import { Address } from "../types" ; import { Account } from "../test/types" ; import { BigNumber } from "ethers" ; import { ether , preciseDiv , preciseMul } from "../index" ; import { TWO , ZERO , ONE_DAY_IN_SECONDS } from "../constants" ; import { PerpV2LeverageModule , SetToken } from "../contracts" ; import { PerpV2Fixture } from "../fixtures" ; export function toUSDCDecimals ( quantity ) { return quantity . div ( BigNumber . from ( 10 ) . pow ( 12 ) ) ; } export async function leverUp ( setToken , module , fixture , owner , baseToken , leverageRatio , slippagePercentage , isLong ) { const spotPrice = await fixture . getSpotPrice ( baseToken ) ; const totalSupply = await setToken . totalSupply ( ) ; const collateralBalance = ( await module . getAccountInfo ( setToken . address ) ) . collateralBalance ; const baseTradeQuantityNotional = preciseDiv ( collateralBalance . mul ( leverageRatio ) , spotPrice ) ; const baseTradeQuantityUnit = ( isLong ) ? preciseDiv ( baseTradeQuantityNotional , totalSupply ) : preciseDiv ( baseTradeQuantityNotional , totalSupply ) . mul ( - 1 ) ; const estimatedQuoteQuantityNotional = preciseMul ( baseTradeQuantityNotional , spotPrice ) . abs ( ) ; const allowedSlippage = preciseMul ( estimatedQuoteQuantityNotional , ether ( .02 ) ) ; const slippageAdjustedQuoteQuanitityNotional = ( isLong ) ? estimatedQuoteQuantityNotional . add ( allowedSlippage ) : estimatedQuoteQuantityNotional . sub ( allowedSlippage ) ; const receiveQuoteQuantityUnit = preciseDiv ( slippageAdjustedQuoteQuanitityNotional , totalSupply ) ; await module . connect ( owner . wallet ) . trade ( setToken . address , baseToken , baseTradeQuantityUnit , receiveQuoteQuantityUnit ) ; return baseTradeQuantityUnit ; } export async function calculateUSDCTransferIn ( setToken , setQuantity , module , fixture ) { return toUSDCDecimals ( await calculateUSDCTransferInPreciseUnits ( setToken , setQuantity , module , fixture ) ) ; } export async function calculateUSDCTransferInPreciseUnits ( setToken , setQuantity , module , fixture , includeFunding = true ) { const accountInfo = await module . getAccountInfo ( setToken . address ) ; let totalCollateralValue = accountInfo . collateralBalance . add ( accountInfo . owedRealizedPnl ) . add ( accountInfo . netQuoteBalance ) ; if ( includeFunding ) { totalCollateralValue = totalCollateralValue . add ( accountInfo . pendingFundingPayments ) ; } const totalSupply = await setToken . totalSupply ( ) ; let usdcAmountIn = preciseMul ( preciseDiv ( totalCollateralValue , totalSupply ) , setQuantity ) ; const allPositionInfo = await module . getPositionUnitInfo ( setToken . address ) ; for ( const positionInfo of allPositionInfo ) { const baseTradeQuantityNotional = preciseMul ( positionInfo . baseUnit , setQuantity ) ; const isLong = ( baseTradeQuantityNotional . gte ( ZERO ) ) ; const { deltaQuote } = await fixture . getSwapQuote ( positionInfo . baseToken , baseTradeQuantityNotional . abs ( ) , isLong ) ; const idealQuote = preciseMul ( baseTradeQuantityNotional , await fixture . getSpotPrice ( positionInfo . baseToken ) ) ; const expectedSlippage = isLong ? deltaQuote . sub ( idealQuote ) : idealQuote . abs ( ) . sub ( deltaQuote ) ; usdcAmountIn = usdcAmountIn . add ( idealQuote ) . add ( expectedSlippage ) ; } return usdcAmountIn ; } export async function calculateUSDCTransferOut ( setToken , setQuantity , module , fixture ) { return toUSDCDecimals ( await calculateUSDCTransferOutPreciseUnits ( setToken , setQuantity , module , fixture ) ) ; } export async function calculateUSDCTransferOutPreciseUnits ( setToken , setQuantity , module , fixture , includeFunding = true ) { let totalRealizedPnl = BigNumber . from ( 0 ) ; const allPositionInfo = await module . getPositionNotionalInfo ( setToken . address ) ; const accountInfo = await module . getAccountInfo ( setToken . address ) ; let totalCollateralBalance = accountInfo . collateralBalance . add ( accountInfo . owedRealizedPnl ) ; if ( includeFunding ) { totalCollateralBalance = totalCollateralBalance . add ( accountInfo . pendingFundingPayments ) ; } const collateralPositionUnit = preciseDiv ( totalCollateralBalance , await setToken . totalSupply ( ) ) ; const collateralQuantityNotional = preciseMul ( collateralPositionUnit , setQuantity ) ; for ( const positionInfo of allPositionInfo ) { const basePositionUnit = preciseDiv ( positionInfo . baseBalance , await setToken . totalSupply ( ) ) ; const baseTradeQuantityNotional = preciseMul ( basePositionUnit , setQuantity ) ; const isLong = ( basePositionUnit . gte ( ZERO ) ) ; const closeRatio = preciseDiv ( baseTradeQuantityNotional . abs ( ) , positionInfo . baseBalance . abs ( ) ) ; const reducedOpenNotional = preciseMul ( positionInfo . quoteBalance , closeRatio ) ; const { deltaQuote } = await fixture . getSwapQuote ( positionInfo . baseToken , baseTradeQuantityNotional . abs ( ) , ! isLong ) ; const realizedPnl = ( isLong ) ? reducedOpenNotional . add ( deltaQuote ) : reducedOpenNotional . sub ( deltaQuote ) ; totalRealizedPnl = totalRealizedPnl . add ( realizedPnl ) ; } return collateralQuantityNotional . add ( totalRealizedPnl ) . abs ( ) ; } export async function calculateExternalPositionUnit ( setToken , module , fixture ) { let totalPositionValue = BigNumber . from ( 0 ) ; const allPositionInfo = await module . getPositionNotionalInfo ( setToken . address ) ; for ( const positionInfo of allPositionInfo ) { const spotPrice = await fixture . getSpotPrice ( positionInfo . baseToken ) ; totalPositionValue = totalPositionValue . add ( preciseMul ( positionInfo . baseBalance , spotPrice ) ) ; } const { collateralBalance , pendingFundingPayments , owedRealizedPnl , netQuoteBalance , } = await module . getAccountInfo ( setToken . address ) ; const numerator = totalPositionValue . add ( collateralBalance ) . add ( netQuoteBalance ) . add ( pendingFundingPayments ) . add ( owedRealizedPnl ) ; return toUSDCDecimals ( preciseDiv ( numerator , await setToken . totalSupply ( ) ) ) ; } export async function getUSDCDeltaDueToFundingGrowth ( setToken , setQuantity , baseToken , baseBalance , fixture ) { const [ fundingGrowthGlobal , markTwap , indexTwap ] = await fixture . exchange . getFundingGrowthGlobalAndTwaps ( baseToken ) ; const twPremium = fundingGrowthGlobal . twPremiumX96 . mul ( ether ( 1 ) ) . div ( TWO . pow ( BigNumber . from ( 96 ) ) ) ; const fundingGrowth = preciseMul ( baseBalance , twPremium . div ( ONE_DAY_IN_SECONDS ) ) . abs ( ) ; const isLong = baseBalance . gt ( ZERO ) ; let netFundingGrowth ; if ( markTwap . lt ( indexTwap ) ) { netFundingGrowth = isLong ? fundingGrowth : fundingGrowth . mul ( - 1 ) ; } else { netFundingGrowth = isLong ? fundingGrowth . mul ( - 1 ) : fundingGrowth ; } const totalSupply = await setToken . totalSupply ( ) ; const usdcAmountDelta = preciseMul ( preciseDiv ( netFundingGrowth , totalSupply ) , setQuantity ) ; return usdcAmountDelta ; }