PerpV2LeverageModule

The PerpV2LeverageModule enables leveraged trading using the PerpV2 Protocol. It allows a SetToken to manage a Perp account as a positive equity external position that has value equal to the net Perp account denominated in USDC. The Perp account can manage multiple long or short positions simultaneously using any virtual assets supported by Perp.

Notes:

  • Unlike other more common position types, this module does not EXACTLY replicate issuance and redemption positions since a trade is necessary to enter/exit the position on behalf of the issuer/redeemer.

  • The cost of entering/exiting the position (slippage) is carried by the issuer/redeemer. Any pending funding costs or PnL is carried by the current token holders

  • For safety (to prevent sandwich attacks), this module MUST issue using the SlippageIssuanceModule which allows users to specify slippage thresholds for issuance and redemption.

  • The external position unit is only updated on an as-needed basis during issuance/redemption. It does not reflect the current value of the Set's perpetual position. To retrieve a dynamically calculated value for a SetToken with a Perp position you can call SlippageIssuanceModule's getRequiredComponentIssuanceUnitsOffChain method as a static call using the ethers javascript library.

  • Important: Due to low Optimism block gas limit, and high Perp V2 gas costs for trading, currently SetTokens are restricted by governance to two open perp positions per account. This is to ensure that Sets can be minted and redeemed using the Slippage Issuance Module with no issues of hitting the block gas limit. Therefore to manage multiple positions, you can create multiple SetTokens each with a maximum of two positions - effectively enabling isolated and dual markets only. This restriction can be loosened as Optimism raises its max gas and Perpetual Protocol optimizes gas for trading.

    • Examples of allowed: SOL1x, SOL-2x, LUNA5x, SOL1x+BTC2x

    • Examples of not allowed: SOL1x+BTC2x+ETH-1x

Structs

PositionNotionalInfo

struct PositionNotionalInfo {
  address baseToken;   // Virtual token minted by the Perp protocol
  int256 baseBalance;  // Base position notional quantity in 10**18 decimals. When negative, position is short
  int256 quoteBalance; // vUSDC "debt" notional quantity minted to open position. When positive, position is short
}

PositionUnitInfo

struct PositionUnitInfo {
  address baseToken; // Virtual token minted by the Perp protocol
  int256 baseUnit;   // Base position unit. When negative, position is short
  int256 quoteUnit;  // vUSDC "debt" position unit. When positive, position is short
}

AccountInfo

NOTE: when pendingFundingPayments is positive it will be credited to account on settlement, when negative it's a debt owed that will be repaid on settlement. (PerpProtocol.Exchange returns the value with the opposite meaning, e.g positively signed payments are owed by account to system).

struct AccountInfo {
  int256 collateralBalance;       // Quantity of collateral deposited in Perp vault in 10**18 decimals
  int256 owedRealizedPnl;         // USDC quantity of profit and loss in 10**18 decimals not yet settled to vault
  int256 pendingFundingPayments;  // USDC quantity of pending funding payments in 10**18 decimals
  int256 netQuoteBalance;         // USDC quantity of net quote balance for all open positions in Perp account
}

Select Methods

trade()

function trade(
    ISetToken _setToken,
    address _baseToken,
    int256 _baseQuantityUnits,
    uint256 _quoteBoundQuantityUnits
)

MANAGER ONLY: Allows manager to buy or sell virtual positions to change exposure to the underlying baseToken. Providing a positive value for _baseQuantityUnits buys baseToken on UniswapV3 via Perp's ClearingHouse, Providing a negative value sells the token. _quoteBoundQuantityUnits defines a min-receive-like slippage bound for the amount of vUSDC quote asset the trade will either pay or receive as a result of the action.

NOTE: This method doesn't update the externalPositionUnit because it is a function of UniswapV3 virtual token market prices and needs to be generated on the fly to be meaningful.

As a user when levering, e.g increasing the magnitude of your position, you'd trade as below:

As a user when delevering, e.g decreasing the magnitude of your position, you'd trade as below:

Parameters

deposit()

MANAGER ONLY: Deposits default position collateral token (USDC) into the PerpV2 Vault, increasing the size of the Perp account external position. This method is useful for establishing initial collateralization ratios, e.g the flow when setting up a 2X external position would be to deposit 100 units of USDC and execute a lever trade for ~200 vUSDC worth of vBaseToken with the difference between these made up as automatically "issued" margin debt in the PerpV2 system.

function deposit(
    ISetToken _setToken,
    uint256 _collateralQuantityUnits
)

Parameters

withdraw()

MANAGER ONLY: Withdraws collateral token from the PerpV2 Vault to a default position on the SetToken. This method is useful when adjusting the overall composition of a Set which has a Perp account external position as one of several components.

NOTE: Within PerpV2, withdraw settles owedRealizedPnl and any pending funding payments to the Perp vault prior to transfer.

function withdraw(
    ISetToken _setToken,
    uint256 _collateralQuantityUnits
)

Parameters

moduleIssueHook()

MODULE ONLY: Hook called prior to issuance. Only callable by valid module. Should only be called ONCE during issue. Trades into current positions and sets the collateralToken's externalPositionUnit so that issuance module can transfer in the right amount of collateral accounting for accrued fees/pnl and slippage incurred during issuance. Any pending funding payments and accrued owedRealizedPnl are attributed to current Set holders.

function moduleIssueHook(
    ISetToken _setToken,
    uint256 _setTokenQuantity
)

Parameters

moduleRedeemHook()

MODULE ONLY: Hook called prior to redemption in the issuance module. Trades out of existing positions to make redemption capital withdraw-able from PerpV2 vault. Sets the externalPositionUnit equal to the realizable value of account in position units (as measured by the trade outcomes for this redemption). Any owedRealizedPnl and pending funding payments are socialized in this step so that redeemer pays/receives their share of them. Should only be called ONCE during redeem.

function moduleRedeemHook(
    ISetToken _setToken,
    uint256 _setTokenQuantity
)

Parameters

componentIssueHook()

MODULE ONLY: Hook called prior to looping through each component on issuance. Deposits collateral into Perp protocol from SetToken default position.

function componentIssueHook(
    ISetToken _setToken,
    uint256 _setTokenQuantity,
    IERC20 _component,
    bool _isEquity
)

Parameters

componentRedeemHook()

MODULE ONLY: Hook called prior to looping through each component on redemption. Withdraws collateral from Perp protocol to SetToken default position without updating the default position unit. Called by issuance module's resolveEquityPositions method which immediately transfers the collateral component from SetToken to redeemer after this hook executes.

function componentRedeemHook(
    ISetToken _setToken,
    uint256 _setTokenQuantity,
    IERC20 _component,
    bool _isEquity
)

Parameters

getIssuanceAdjustments()

Gets the positive equity collateral externalPositionUnit that would be calculated for issuing a quantity of SetToken, representing the amount of collateral that would need to be transferred in per SetToken. Values in the returned arrays map to the same index in the SetToken's components array.

function getIssuanceAdjustments(
    ISetToken _setToken,
    uint256 _setTokenQuantity
)

Parameters

getRedemptionAdjustments()

Gets the positive equity collateral externalPositionUnit that would be calculated for redeeming a quantity of SetToken representing the amount of collateral returned per SetToken. Values in the returned arrays map to the same index in the SetToken's components array.

function getIssuanceAdjustments(
    ISetToken _setToken,
    uint256 _setTokenQuantity
)

Parameters

getPositionNotionalInfo()

Returns a PositionUnitNotionalInfo array representing all positions open for the SetToken. Each element of the array has the following properties:

  • baseToken: address

  • baseBalance: baseToken balance as notional quantity (10**18)

  • quoteBalance: USDC quote asset balance as notional quantity (10**18)

function getPositionNotionalInfo(ISetToken _setToken) view 

Parameters

getPositionUnitInfo()

Returns a PositionUnitUnitInfo array representing all positions open for the SetToken. Each element of the array has the following properties:

  • baseToken: address

  • baseBalance: baseToken balance as position unit (10**18)

  • quoteBalance: USDC quote asset balance as position unit (10**18)

function getPositionUnitInfo(ISetToken _setToken) view

Parameters

getAccountInfo()

Gets Perp account info for SetToken. Returns an AccountInfo struct containing account wide (rather than position specific) balance info.

  • collateralBalance: collateral balance in vault (10^18, regardless of underlying collateral decimals)

  • owedRealizedPnl: owed realized PnL that accrued as a result of trading or liquidation but has not yet settled in the vault as a result of withdrawal (10^18)

  • pendingFundingPayments: funding payments which have not yet accrued to owedRealizedPnl as a result of trading (10^18)

  • netQuoteBalance: the net quote balance of all open positions, long and short (10^18)

function getAccountInfo(ISetToken _setToken) view

Parameters

getAMMSpotPrice()

Gets the mid-point price of a virtual asset from UniswapV3 markets maintained by Perp Protocol.

Returns the price as 10^18.

function getAMMSpotPrice(address _baseToken) view

Parameters

Last updated