Perp v2 Integration Guide
Which contracts should I be interacting with?
There are three main contracts:
Vault: where all users' funds are stored, including USDC and non-USDC collateralsClearingHouse:- the main component that manages all markets of Perp v2
- As a taker, one can open or close positions
- As a maker, one can add or remove liquidity
- As a liquidator, one can liquidate someone's position that is close to or already bankrupt and get liquidation fees as the reward
AccountBalance: where most of the information of a trader can be queried, such as position size, position value, etc
Install Curie npm package
npm install @perp/curie-contract
Node version: 12 (or 16 for M1 CPU)
Vault
This contract is mainly used for depositing and withdrawing collaterals.
Deposit
Vault.deposit
Deposit collateral
function deposit(address token, uint256 amount) external;
Parameters:
token: the address of the collateralamount: the amount to be deposited
Example:
IVault(VAULT_ADDR).deposit(TOKEN_ADDR, AMOUNT)
Withdraw
When withdrawing collaterals, one can withdraw the amount up to one's freeCollateral. This ensures that one's positions are always sufficiently collateralized.
Vault.getFreeCollateral
How many collaterals a trader can withdraw
function getFreeCollateral(address trader) external view returns (uint256);
Parameter:
trader: the address of the trader
Vault.withdraw
Withdraw collaterals of the specified amount
function withdraw(address token, uint256 amount) external;
Parameters:
token: the address of the collateralamount: the amount to be withdrawn, which should not exceedfreeCollateral
Example:
IVault vault = IVault(VAULT_ADDR);
uint256 freeCollateral = vault.getFreeCollateral(TRADER_ADDR);
vault.withdraw(TOKEN_ADDR, AMOUNT);
ClearingHouse
ClearingHouse manages all markets of Perp v2.
For each market, we deploy a pair of two virtual tokens (with no real value) and initiate a new Uniswap V3 pool to provide liquidity to.
- Base token: the virtual underlying asset users are trading for, such as vETH, vBTC
- Quote token: the counter currency of base token, which is always vUSDC for any base token
Open Position
ClearingHouse.openPosition
Open a new position or adjust the position size of an existing one
struct OpenPositionParams {
address baseToken;
bool isBaseToQuote;
bool isExactInput;
uint256 amount;
uint256 oppositeAmountBound;
uint256 deadline;
uint160 sqrtPriceLimitX96;
bytes32 referralCode;
}
function openPosition(OpenPositionParams memory params) external returns (uint256 base, uint256 quote);
Parameters:
baseToken: the address of the base token, which suggests the market to trade inisBaseToQuote:truefor shorting the base token asset andfalsefor longingisExactInput: for specifyingexactInputorexactOutput; similar to UniSwap V2's specsamount: the amount specified. Depending on theisExactInputparameter, this can be either the input amount or output amount.oppositeAmountBound: the restriction on how many token to receive/pay, depending onisBaseToQuote&isExactInputisBaseToQuote&&isExactInput: want more output quote as possible, so we set a lower bound of output quoteisBaseToQuote&&!isExactInput: want less input base as possible, so we set a upper bound of input base!isBaseToQuote&&isExactInput: want more output base as possible, so we set a lower bound of output base!isBaseToQuote&&!isExactInput: want less input quote as possible, so we set a upper bound of input quote
deadline: the restriction on when the tx should be executed; otherwise, tx will get revertedsqrtPriceLimitX96: the restriction on the ending price after the swap;0for no limit. This is the same assqrtPriceLimitX96in the UniSwap V3 contract.referralCode: the referral code for partners
Return values:
base: the amount of base token exchangedquote: the amount of quote token exchanged
Example:
- Long 1 vETH
ClearingHouse clearingHouse = ClearingHouse(CH_ADDR);
IClearingHouse.OpenPositionParams params = IClearingHouse.OpenPositionParams({
baseToken: VETH_ADDR,
isBaseToQuote: false, // false for longing
isExactInput: false, // false for specifying the output vETH amount
amount: 1 ether,
oppositeAmountBound: 0, // no amount limit
sqrtPriceLimitX96: 0 // no price limit
deadline: block.timestamp + 900, // take 15 minutes for example
referralCode: 0x0000000000000000000000000000000000000000000000000000000000000000 // no referral code
})
// quote is the amount of quote token the taker pays
// base is the amount of base token the taker gets
(uint256 base, uint256 quote) = clearingHouse.openPosition(params)
Close Position
Close an existing position
ClearingHouse.closePosition
struct ClosePositionParams {
address baseToken;
uint160 sqrtPriceLimitX96;
uint256 oppositeAmountBound;
uint256 deadline;
bytes32 referralCode;
}
function closePosition(ClosePositionParams calldata params) external returns (uint256 base, uint256 quote);
The params are pretty much the same as openPosition.
Example:
- Close the 1 vETH long position in the above example of
openPosition
ClearingHouse clearingHouse = ClearingHouse(CH_ADDR);
IClearingHouse.ClosePositionParams params = IClearingHouse.ClosePositionParams({
baseToken: VETH_ADDR,
sqrtPriceLimitX96: 0,
oppositeAmountBound: 0,
deadline: block.timestamp + 900,
referralCode: 0x0000000000000000000000000000000000000000000000000000000000000000
})
(uint256 base, uint256 quote) = clearingHouse.closePosition(params)
Add Liquidity
ClearingHouse.addLiquidity
Provide liquidity
struct AddLiquidityParams {
address baseToken;
uint256 base;
uint256 quote;
int24 lowerTick;
int24 upperTick;
uint256 minBase;
uint256 minQuote;
uint256 deadline;
}
struct AddLiquidityResponse {
uint256 base;
uint256 quote;
uint256 fee;
uint256 liquidity;
}
function addLiquidity(AddLiquidityParams calldata params) external returns (AddLiquidityResponse memory)
Parameters:
baseToken: the base token addressbase: the amount of base token you want to providequote: the amount of quote token you want to providelowerTick: lower tick of liquidity range, same as UniSwap V3upperTick: upper tick of liquidity range, same as UniSwap V3minBase: the minimum amount of base token you'd like to provideminQuote: the minimum amount of quote token you'd like to providedeadline: a time after which the transaction can no longer be executed
Return values:
base: the amount of base token added to the poolquote: the amount of quote token added to the poolfee: the amount of fee collected if there is anyliquidity: the amount of liquidity added to the pool, derived frombase"e
Example:
- Provide liquidity to vETH/vUSDC pair with 2 vETH and 100 vUSDC, in the tick range [50000, 51000)
- The range for liquidity on Perp V2 and Uniswap V3 is always expressed in tick
ClearingHouse clearingHouse = ClearingHouse(CH_ADDR);
IClearingHouse.AddLiquidityParams params = ClearingHouse.AddLiquidityParams({
baseToken: VETH_ADDR,
base: 2 ether,
quote: 100 ether,
lowerTick: 50000,
upperTick: 51000,
minBase: 0,
minQuote: 0,
deadline: block.timestamp
})
IClearingHouse.AddLiquidityResponse memory response = clearingHouse.addLiquidity(params);
Remove Liquidity
ClearingHouse.removeLiquidity
struct RemoveLiquidityParams {
address baseToken;
int24 lowerTick;
int24 upperTick;
uint128 liquidity;
uint256 minBase;
uint256 minQuote;
uint256 deadline;
}
struct RemoveLiquidityResponse {
uint256 base;
uint256 quote;
uint256 fee;
}
function removeLiquidity(RemoveLiquidityParams calldata params) external returns (RemoveLiquidityResponse memory)
Parameters:
baseToken: the address of base tokenlowerTick: lower tick of liquidity range, same as UniSwap V3upperTick: upper tick of liquidity range, same as UniSwap V3liquidity: how much liquidity you want to remove, same as UniSwap V3minBase: the minimum amount of base token you want to removeminQuote: the minimum amount of quote token you want to removedeadline: a time after which the transaction can no longer be executed
Return values:
base: the amount of base token removed from poolquote: the amount of quote token removed from poolfee: the amount of fee collected if there is any
Example:
- Remove 12 units of liquidity from vETH/vUSDC pair, in the tick range [50000, 51000) with a minimum requirement of 1 ETH that should be successfully removed
ClearingHouse clearingHouse = ClearingHouse(CH_ADDR);
IClearingHouse.RemoveLiquidityParams params = ClearingHouse.RemoveLiquidityParams({
baseToken: VETH_ADDR,
lowerTick: 50000,
upperTick: 51000,
liquidity: 12,
minBase: 1 ether,
minQuote: 0,
deadline: block.timestamp
})
RemoveLiquidityResponse memory response = clearingHouse.removeLiquidity(params);
- Collect maker's fees by removing zero liquidity
ClearingHouse clearingHouse = ClearingHouse(CH_ADDR);
IClearingHouse.RemoveLiquidityParams params = ClearingHouse.RemoveLiquidityParams({
baseToken: VETH_ADDR,
lowerTick: 50000,
upperTick: 51000,
liquidity: 0, // removing 0 liquidity is interpreted as to collect the accumulated swapping fees since last collection
minBase: 0,
minQuote: 0,
deadline: block.timestamp
})
// response.fee is the fees a maker gets
RemoveLiquidityResponse memory response = clearingHouse.addLiquidity(params)
Get Account Value
ClearingHouse.getAccountValue
Get the total worth of one's positions denominated in USDC
function getAccountValue(address trader) public view returns (int256);
Parameter:
account: the address of the trader