SakePerp.fi is a decentralized perpetual contracts platform based on the Ethereum virtual machine. This doc introduces the interface and call method of the SakePerp contracts. Users are able to interact with the contract directly through languages such as Javascript/ Typescript/ Go and develop their own advanced trading strategy.
Contract Addresses and abi
Smart Contract Addresses(BSC - Mainnet)
url: https://bsc-graphnode-api.sakeperp.fi/subgraphs/name/sakeperp/sakeperp-subgraph
Request this url by post, and set the data {"query":"{contractLists {isExchange name addr exchangeStateAddr insuranceFundAddr highRiskLPTokenAddr quoteAssetName quoteAssetAddr maxLeverage}}"}
The return value is in json format
{
"data": {
"contractLists": [
{
"addr": "0x97dc089519d0b341D401a4fBaC26B02a7DCd7f6b", // Contract Address
"exchangeStateAddr": "0xB04Af392cF7c2d09A6dC8a812DEDCe02Ef4D2092", // state contract address of the corresponding trading pool
"highRiskLPTokenAddr": "0xa1e38c2710b71cad2e2e6afa25eeae26dd3b34bb", // LPToken contract address of the corresponding trading pool
"insuranceFundAddr": "0x7395DE44d09E2c691F1bd946F660BbFCf5718275", // Insurance Fund contract address of the corresponding trading pool
"isExchange": true, // Is it a trading pool address?
"maxLeverage": 15, // Maximum leverage supported by the trading pool
"name": "ETH/BUSD", // Contract name
"quoteAssetAddr": "0xe9e7cea3dedca5984780bafc599bd69add087d56", // Contract address of the asset used for a quote
"quoteAssetName": "BUSD" // The name of the asset used for a quote
},
{
"addr": "0xCFADd8b52B0737401d8e0aFDc8Aa9194bd40BFc3",
"exchangeStateAddr": "0x79366156125D61883d09036d9b57d36d897F9895",
"highRiskLPTokenAddr": "0xeca8f31532b8b51f2185210f22fbe3c33a786b1b",
"insuranceFundAddr": "0xCAe9E683864E5892DA9d258cae4C4d029BD10620",
"isExchange": true,
"maxLeverage": 15,
"name": "BTC/BUSD",
"quoteAssetAddr": "0xe9e7cea3dedca5984780bafc599bd69add087d56",
"quoteAssetName": "BUSD"
},
{
"addr": "0xeCf9e5c123a1E251E4f9E3B9800EC8ab3E5033Ed", // Contract address
"exchangeStateAddr": "",
"highRiskLPTokenAddr": "",
"insuranceFundAddr": "",
"isExchange": false,
"maxLeverage": 0,
"name": "SakePerp", // Contract name
"quoteAssetAddr": "",
"quoteAssetName": ""
},
......
]
}
}
Abi for all contracts are available from npm
npm install @sakeperp/artifact
Contract Interface and Calls
Users need an account on BSC before the smart contract interation. User can initialise an account with a private key or a mnemonic. Please make sure that there's enough Gas and BUSD in the account.
const { Contract,providers,Wallet } =require("ethers")constbscNodeUrl="https://bsc-dataseed1.defibit.io/"constprovider=newproviders.JsonRpcProvider(bscNodeUrl)constwallet=newWallet(privateKey, provider) // use privatekey// const wallet = Wallet.fromMnemonic(mnemonic).connect(provider) // use mnemonic
Users can start making contract calls when one has the BSC account and related smart contract information.
Before the contract call, we need to unify different tokens into the same decimal place when we do mathematical calculations to avoid errors.
In order to avoid such errors, the Sake dev team has developed a set of libraries in the contract to perform these kinds of mathematical calculations. Some contract interface entries will require a javascript object to be inputted. There is only one d attribute. All d values require 18 decimal places. For example, if we want a leverage multiple of 1.5, then we need to input 15000000000000000000000.
const { parseUnits } =require("ethers/lib/utils")constconverted= { d:parseUnits("1.5",18) } // d is 1500000000000000000
Position Operation and Enquiry
SakePerp provides interfaces to open, close, add and remove margins.
approve
Before opening a position, users need to authorise the SakePerp contract to have access to their account funds.
openPosition( IExchange _exchange, Side _side,Decimal.decimal calldata _quoteAssetAmount,Decimal.decimal calldata _leverage,Decimal.decimal calldata _baseAssetAmountLimit ) _exchange:address of the trading pool _side:0:Long 1:Short _quoteAssetAmount:Margin amount _leverage:leverage _baseAssetAmountLimit:The minimum position requirement is set to avoid excessive slippage. If the actual position is less than the mininimum position, the trade will fail. 0 min position means that any position size is acceptable.
const { parseUnits } =require("ethers/lib/utils")constDEFAULT_DECIMALS=18constside=1// ShortconstquoteAssetAmount= { d:parseUnits("1000",DEFAULT_DECIMALS) }constleverage= { d:parseUnits("1.5",DEFAULT_DECIMALS) }constminBaseAssetAmount= { d:"0" }constoptions= { gasLimit:2_500_000 } consttx=awaitsakePerp.openPosition( exchangeAddr, side, quoteAssetAmount, leverage, minBaseAssetAmount, options, )awaittx.wait()
closePosition(Close Position)
closePosition() will close all the positions of the account in the specified trading pool. If users wants to close part of the positions, he can open a position opposite to the original position through openPosition()
closePosition(IExchange _exchange,Decimal.decimal calldata _quoteAssetAmountLimit) _exchange:Trading pool address _quoteAssetAmountLimit:The minimum funds requirement. The transaction will fail if below the value. Setting it to 0 means accepting any amount of funds.
const { parseUnits } =require("ethers/lib/utils")constDEFAULT_DECIMALS=18constminQuoteAssetAmount= { d:"0" }constoptions= { gasLimit:2_500_000 } consttx=awaitsakePerp.closePosition( exchangeAddr, minQuoteAssetAmount, options, )awaittx.wait()
addMargin(Add Margin)
addMargin(IExchange _exchange,Decimal.decimal calldata _addedMargin) _exchange:Trading pool address _addedMargin:The amount of margin to add
removeMargin(Remove Margin)
removeMargin(IExchange _exchange,Decimal.decimal calldata _removedMargin) _exchange:Trading pool address _removedMargin:The amount of margin to be removed. After removing the margin, the margin ratio should still be greater than the maintenance margin ratio.
SakePerpViewer Contract provides a position enquiry interface.
getUnrealizedPnl(Query the user's unrealized profit and loss)
getUnrealizedPnl(
IExchange _exchange,
address _trader,
ISakePerp.PnlCalcOption _pnlCalcOption
)
_exchange:Trading pool address
_trader: User's address
_pnlCalcOption:Fixed with 0
getPersonalBalanceWithFundingPayment (Query the user's balance in all trading pools)
getPersonalBalanceWithFundingPayment(IERC20Upgradeable _quoteToken, address _trader)
_quoteToken: The token address of the quoted asset. Currently it's the contract address of BUSD.
_trader:Users's address
getPersonalPositionWithFundingPayment (Query the user's position information in the specified trading pool)
getPersonalPositionWithFundingPayment(IExchange _exchange, address _trader)
_exchange: Trading pool address
_trader: User's address
Return Value
struct Position {
SignedDecimal.signedDecimal size;
Decimal.decimal margin;
Decimal.decimal openNotional;
SignedDecimal.signedDecimal lastUpdatedCumulativePremiumFraction;
Decimal.decimal lastUpdatedCumulativeOvernightFeeRate;
uint256 liquidityHistoryIndex;
uint256 blockNumber;
}
size: Position size
margin: Margin
openNotional: Total position value
getMarginRatio (Query the user's margin ratio in the specified trading pool)
When the margin ratio of the non-stablecoin trading pool is less than 3%, and the margin ratio of the stablecoin trading pool is less than 1%, it will be automatically liquidated.
getMarginRatio(IExchange _exchange, address _trader)
_exchange: Trading pool address
_trader: User's address
MM Liquidity Operation and Enquiry
SakePerpVault provides interfaces for adding, removing, and querying liquidity to MMs.
approve
Before an operation, users need to authorise the SakePerp contract to have access to the account funds.
User (MM) will receive a certain amount of lpToken after adding liquidity.addLiquidity( IExchange _exchange, Risk _risk,Decimal.decimal memory _quoteAssetAmount)_exchange: Trading pool address_risk: Fixed as0_quoteAssetAmount: the amount user wants to addconst { parseUnits } =require("ethers/lib/utils")constDEFAULT_DECIMALS=18constquoteAssetAmount= { d:parseUnits("10000",DEFAULT_DECIMALS) }constoptions= { gasLimit:2_500_000 } consttx=awaitsakePerpVault.addLiquidity( exchangeAddr,0, quoteAssetAmount,)awaittx.wait()
removeLiquidity (Remove Liquidity)
When the liquidity is removed, the user's lpToken will be destroyed, and the user's funds will be returned according to the profit and loss of the trading pool.
removeLiquidity( IExchange _exchange, Risk _risk,Decimal.decimal memory _lpTokenAmount)_exchange: Trading pool address_risk: Fixed as0_lpTokenAmount: The amount of LPToken which will be destoryedconst { parseUnits } =require("ethers/lib/utils")constDEFAULT_DECIMALS=18constlpTokenAmount= { d:parseUnits("100.111",DEFAULT_DECIMALS) }constoptions= { gasLimit:2_500_000 } consttx=awaitsakePerpVault.removeLiquidity( exchangeAddr,0, lpTokenAmount,)awaittx.wait()
getTotalLpUnrealizedPNL (Get the total profit and loss of the trading pool)
getTotalLpUnrealizedPNL(IExchange _exchange)
_exchange: Trading pool address
getLpTokenPrice (Get the LPToken price of the trading pool)
getLpTokenPrice(IExchange _exchange, Risk _risk)
_exchange: Trading pool address
_risk: Fixed as 0
getTotalMMLiquidity (Get the total liquidity of the trading pool)
getTotalMMLiquidity(address _exchange)
_exchange:Trading pool address
getTotalMMAvailableLiquidity (Get the available liquidity of the trading pool)
To protect MM, only part of the funds invested by MM is available.
getTotalMMAvailableLiquidity(address _exchange)
_exchange:Trading pool address
Trading Pool Status Enquiry
The exchange contract provides an interface for querying status information of the trading pool.
getUnderlyingPrice(Return the oracle price, also known as indexPrice)
getUnderlyingTwapPrice(Return the time-weighted oracle price over a period of time)
getSpotPrice(Return the contract price, also known as markPrice)
getTwapPrice(Return the time-weighted contract price over a period of time)
getReserve(Return the x and y values of the vAMM curve)
getMaxHoldingBaseAsset(Return the maximum position size for a single user, a value of 0 means no limit)
initMarginRatio(Return the initial margin ratio, which determines the maximum leverage)
maintenanceMarginRatio(Return the maintenance margin ratio below which the position will be liquidated)
spreadRatio(Return to the spread)
liquidationFeeRatio(Return to the liquidation fee ratio)
maxLiquidationFee(Return to the max. liquidation fee)
getTotalPositionSize(Return to the net position of the trading pool)
getPositionSize(Return the size of long and short positions respectively)