Contracts
ExchangeRouter
The main functions should be accessible through the ExchangeRouter contract.
Creating an Order
To create a swap / increase position order, collateral needs to first be transferred to the OrderVault, and ExchangeRouter.createOrder must be called after in the same transaction. The transfer of tokens for collateral and the calling of ExchangeRouter.createOrder must be done in a single transaction, otherwise the tokens may be transferred out by other users.
ExchangeRouter.createOrder(BaseOrderUtils.CreateOrderParams calldata params)
CreateOrderParams:
CreateOrderParamsAddresses:
receiver: The receiver of any output amounts
cancellationReceiver: If the order is cancelled collateral sent for the order as well as gas for the network fee is sent to this address if it is not the zero address
callbackContract: The contract to call on order execution / cancellation
uiFeeReceiver: The receiver of the UI fee
market: The market to trade in
initialCollateralToken: The initial collateral token sent into the contract
swapPath: An array of market addresses to swap the initialCollateralToken through for increase / swap orders, for decrease orders, the output amount will be swapped through these specified markets
CreateOrderParamsNumbers:
sizeDeltaUsd: The position size to increase / decrease
initialCollateralDeltaAmount: The amount of tokens to withdraw for decrease orders
triggerPrice: The trigger price for limit / stop-loss / take-profit orders, the order will be attempted to be executed if price reaches the trigger price
acceptablePrice: The acceptable price at which the order can be executed. For market orders, the order will be cancelled if it cannot be executed at the acceptable price. For limit / stop-loss / take-profit orders, the order will not be executed if the trigger price is reached but the acceptable price cannot be fulfilled.
executionFee: The amount of native token that is included for the execution fee, e.g. on Arbitrum this would be ETH, this is the maximum execution fee that keepers can use to execute the order. When the order is executed, any excess execution fee is sent back to the order's account. Please see the Execution Fee section for more details.
callbackGasLimit: The gas limit to be passed to the callback contract on order execution / cancellation
minOutputAmount: For swap orders this is the minimum token output amount. For increase orders this is the minimum token amount after the initialCollateralDeltaAmount is swapped through the swapPath. NOTE: For decrease orders this is the minimum USD value, USD is used in this case because it is possible for decrease orders to have two output tokens, one being the profit token and the other being the withdrawn collateral token
orderType:
MarketSwap: a market swap order
LimitSwap: a limit swap order, this will be executed if the minOutputAmount can be fulfilled
MarketIncrease: a market order to increase a long / short position
LimitIncrease: a limit order to increase a long / short position
MarketDecrease: a market order to decrease a long / short position
LimitDecrease: a limit order to decrease a long / short position
StopLossDecrease: a stop-loss order to decrease a long / short position
decreasePositionSwapType
NoSwap: no swap will be performed
SwapPnlTokenToCollateralToken: the profit token will be attempted to be swapped to the collateral token
SwapCollateralTokenToPnlToken: the withdrawn collateral will be attempted to be swapped to the profit token
isLong: specifies if this order is for a long or short position
shouldUnwrapNativeToken: specifies whether to unwrap the native token, e.g. if the output token is WETH, setting this to true would convert the WETH token to ETH
autoCancel: for LimitDecrease and StopLossDecrease orders, if this is true then the order will be cancelled when the position is closed, this value is ignored for other order types
referralCode: the referral code to use
Creating a Deposit
To create a deposit, tokens need to first be transferred to the DepositVault, ExchangeRouter.createDeposit can then be called after. The transfer of tokens and the calling of ExchangeRouter.createDeposit should be done in a single transaction, otherwise the tokens may be transferred out by other users.
ExchangeRouter.createDeposit(DepositUtils.CreateDepositParams calldata params)
CreateDepositParams:
receiver: The receiver of PX tokens
callbackContract: The contract to call on deposit execution / cancellation
uiFeeReceiver: The receiver of the UI fee
market: The market to deposit in
initialLongToken: The initial long token sent into the contract
initialShortToken: The initial short token sent into the contract
longTokenSwapPath: An array of market addresses to swap the initialLongToken through for deposits
shortTokenSwapPath: An array of market addresses to swap the initialShortToken through for deposits
minMarketTokens: The minimum output amount of PX token
shouldUnwrapNativeToken: specifies whether to unwrap the native token, e.g. if the deposit is cancelled and the initialLongToken token is WETH, setting this to true would convert the WETH token to ETH before it is refunded
executionFee: The amount of native token that is included for the execution fee, e.g. on Arbitrum this would be ETH, this is the maximum execution fee that keepers can use to execute the deposit. When the deposit is executed, any excess execution fee is sent back to the deposit's account. Please see the Execution Fee section for more details.
callbackGas
Creating a Withdrawal
ExchangeRouter.createWithdrawal(DepositUtils.CreateWithdrawalParams calldata params)
CreateWithdrawalParams:
receiver: The receiver of PX tokens
callbackContract: The contract to call on withdrawal execution / cancellation
uiFeeReceiver: The receiver of the UI fee
market: The market to withdraw from
longTokenSwapPath: An array of market addresses to swap the withdrawn long token through
shortTokenSwapPath: An array of market addresses to swap the withdrawn short token through
minLongTokenAmount: The minimum output amount of long token after it is swapped through longTokenSwapPath. For example, if WETH token is swapped to BTC then minLongTokenAmount specifies minimum amount of BTC tokens
minShortTokenAmount: The minimum output amount of long token after it is swapped through shortTokenSwapPath
shouldUnwrapNativeToken: specifies whether to unwrap the native token, e.g. if the output token is WETH, setting this to true would convert the WETH token to ETH
executionFee: The amount of native token that is included for the execution fee, e.g. on Arbitrum this would be ETH, this is the maximum execution fee that keepers can use to execute the withdrawal. When the withdrawal is executed, any excess execution fee is sent back to the withdrawal's account. Please see the Execution Fee section for more details.
callbackGasLimit: The gas limit to be passed to the callback contract on withdrawal execution / cancellationLimit: The gas limit to be passed to the callback contract on withdrawal execution / cancellation
Creating an Atomic Withdrawal
ExchangeRouter.executeAtomicWithdrawal(WithdrawalUtils.CreateWithdrawalParams calldata params, OracleUtils.SetPricesParams calldata oracleParams)
CreateWithdrawalParams:
receiver: The receiver of PX tokens
callbackContract: The contract to call on withdrawal execution / cancellation
uiFeeReceiver: The receiver of the UI fee
market: The market to withdraw from
longTokenSwapPath: Should be an empty array, swaps are not supported for this atomic action
shortTokenSwapPath: Should be an empty array, swaps are not supported for this atomic action
minLongTokenAmount: The minimum output amount of long token after it is swapped through longTokenSwapPath. For example, if WETH token is swapped to BTC then minLongTokenAmount specifies minimum amount of BTC tokens
minShortTokenAmount: The minimum output amount of long token after it is swapped through shortTokenSwapPath
shouldUnwrapNativeToken: specifies whether to unwrap the native token, e.g. if the output token is WETH, setting this to true would convert the WETH token to ETH
executionFee: The amount of native token that is included for the execution fee, e.g. on Arbitrum this would be ETH, this is the maximum execution fee that keepers can use to execute the withdrawal. When the withdrawal is executed, any excess execution fee is sent back to the withdrawal's account. Please see the Execution Fee section for more details.
callbackGasLimit: The gas limit to be passed to the callback contract on withdrawal execution / cancellationLimit: The gas limit to be passed to the callback contract on withdrawal execution / cancellation
OracleUtils.SetPricesParams
tokens: array of token addresses, these should be the market token addresses (index, long, short)
providers: array of providers, the token providers for each token, these can be the ChainlinkPriceFeedProvider (address can be found in the
deploymentsfolder), note that only tokens supported by theChainlinkPriceFeedProvidercan be used for this call, whether a token is supported can be checked by callingdataStore.getAddress(Keys.priceFeedKey(token))and checking that the value is not the zero addressdata: array of data for the providers, this would be an array of
0x
UI Fee
UI fees will be charged on top of the base fee, the percentage amount for this is based on the amount configured for the passed in uiFeeReceiver address. More info can be found in the Running a Frontend section.
The percentage UI fee to be charged for a uiFeeReceiver can be configured using ExchangeRouter.setUiFeeFactor, the uiFeeFactor is a percentage value over 10^30, e.g. if the uiFeeFactor is 2 x 10^25, then the percentage charged would be (2 x 10^25) / (10^30) = 0.00002 = 0.002%.
The max uiFeeFactor that can be set is based on the configured value of dataStore.getUint(Keys.MAX_UI_FEE_FACTOR).
The uiFeeReceiver value can be passed in for any of the following actions:
Deposits
Withdrawals
Swap Orders
Increase / Decrease Position Orders
Market Orders / Limit Orders / Stop-Loss / Take-Profit Orders
For deposits, withdrawals and swaps the fee will be the percentage of the input amount, for increase / decrease position orders the fee will be the percentage of the increase / decrease size.
UI fees are credited on execution of deposits, withdrawals, orders. ExchangeRouter.claimUiFees can be used to claim the received fees at anytime.
Execution Fee
Creating a deposit, order, withdrawal request requires sending an executionFee.
In the creation transaction, the contracts will verify that the provided executionFee is equal to or more than the estimated execution fee, which is calculated by tx.gasprice * GasUtils.adjustGasLimitForEstimate(datastore, estimatedGasLimit).
To calculate the estimatedGasLimit:
For deposits:
GasUtils.estimateExecuteDepositGasLimitFor orders:
GasUtils.estimateExecuteOrderGasLimitFor withdrawals:
GasUtils.estimateExecuteWithdrawalGasLimit
Note that since the tx.gasprice value may fluctuate based on blockchain usage, some buffer could be added to ensure that the creation transaction does not revert. Upon execution, any excess execution fee will be sent back to the deposit.account / order.account / withdrawal.account.
Funding
An overview of funding can be found in Adaptive Funding.
The parameters for funding can be retrieved from the DataStore, the relevant keys would be:
FUNDING_INCREASE_FACTOR_PER_SECOND
FUNDING_DECREASE_FACTOR_PER_SECOND
MIN_FUNDING_FACTOR_PER_SECOND
MAX_FUNDING_FACTOR_PER_SECOND
THRESHOLD_FOR_STABLE_FUNDING
THRESHOLD_FOR_DECREASE_FUNDING
Reader
Market List
Reader.getMarkets
dataStore: Address of the DataStore contract
start: Start index, can be used for pagination
end: End index, can be used for pagination
Returns array of elements of type Market.Prop
Market.Props:
marketToken: Address of the market token
indexToken: Address of the index token
longToken: Address of the long token
shortToken: Address of the short token
Detailed Market List
Returns markets with borrowing rate, funding rate and flag indicating whether market is disabled
Reader.getMarketInfoList
dataStore: Address of the DataStore contract
marketPricesList: Array of market prices of type MarketUtils.MarketPrices
start: Start index, can be used for pagination
end: End index, can be used for pagination
MarketUtils.MarketPrices:
indexTokenPrice
longTokenPrice
shortTokenPrice
Each price is of type Price.Props
Price.Props:
min
max
Getting PX Token Price
Returns PX token price and information about market pool: pool value, PnL, amount of tokens in the pool, borrowing fees and impact pool
Reader.getMarketTokenPrice
dataStore: Address of the DataStore contract
market: Market data of type Market.Props
indexTokenPrice: Price for index token of type Price.Props
longTokenPrice: Price for long token of type Price.Props
shortTokenPrice: Price for short token of type Price.Props
pnlFactorType: More information can be found here
maximize. Whether to maximize or minimize PX price by using either maximum or minimum price
Position List
Reader.getAccountPositions
dataStore: Address of the DataStore contract
account: The account to get the positions for
start: Start index, can be used for pagination
end: End index, can be used for pagination
Detailed Position List
Reader.getAccountPositionInfoList
dataStore: Address of the DataStore contract
referralStorage: Address of the referralStorage contract
positionKeys: Array of position keys, can be retrieved using the DataStore
prices: Array of market prices of type MarketUtils.MarketPrices
uiFeeReceiver: The uiFeeReceiver that would be used for calculating fees
Example code to retrieve positionKeys can be found here.
Position Information
Returns position, fees, execution price and PnL
Reader.getPositionInfo
dataStore: Address of the DataStore contract
referralStorage: Address of the ReferralStorage contract
positionKey: Key of the position
prices: Prices for index, long and short tokens of type MarketUtils.MarketPrices
sizeDeltaUsd: Used to calculate fees and execution price for decreasing position. Optional, can be 0
uiFeeReceiver: The receiver of the UI fee. Can be zero address
usePositionSizeAsSizeDeltaUsd: Specifies if position size should be used as sizeDeltaUsd
Execution Price for Increasing / Decreasing Position
Reader.getExecutionPrice
dataStore: Address of the DataStore contract
marketKey: Address of the market
indexTokenPrice: Price for index token of type Price.Props
positionSizeInUsd: Size of open position, should be 0 if there is no position
positionSizeInTokens: Size of open position in tokens, should be 0 if there is no position
sizeDeltaUsd: Size delta, positive for increasing position, negative for decreasing position
isLong: Specifies if this is for a long or short position
Output Amount for Swaps
Returns amount of output tokens, impact amount, swap fees
Reader.getSwapAmountOut
dataStore: Address of the DataStore contract
market: Market data of type Market.Props
prices: Prices for index, long and short tokens of type MarketUtils.MarketPrices
tokenIn: Address of token in
amountIn: Amount of token in
uiFeeReceiver: The receiver of the UI fee. Can be zero address
Output amount for Deposits
Returns amount of output PX tokens
Reader.getDepositAmountOut
dataStore: Address of the DataStore contract
market: Market data of type Market.Props
prices: Prices for index, long and short tokens of type MarketUtils.MarketPrices
longTokenAmount: Amount of deposited long token
shortTokenAmount: Amount of deposited short token
uiFeeReceiver: The receiver of the UI fee. Can be zero address
Output Amount for Withdrawals
Returns amounts of output long tokens and short tokens
Reader.getWithdrawalAmountOut
dataStore: Address of the DataStore contract
market: Market data of type Market.Props
prices: Prices for index, long and short tokens of type MarketUtils.MarketPrices
marketTokenAmount: Amount of PX tokens to withdraw
uiFeeReceiver: The receiver of the UI fee. Can be zero address
Simulations
There are simulation functions such as ExchangeRouter.simulateExecuteDeposit, ExchangeRouter.simulateExecuteWithdrawal, ExchangeRouter.simulateExecuteOrder, these can be used to simulate execution to check for any errors before creating a deposit / withdrawal / order.
Event Monitoring
For convenience, all events are emitted on the EventEmitter contract. Each event from the EventEmitter will have an eventName, so events can be monitored just by specifying the EventEmitter address and the eventName to be monitored.
The address of the EventEmitter for each network can be found in the deployments folder.
An example to setup monitoring of the Timelock using OpenZeppelin Defender:
Select the "Sentinel" option
Click "Create Sentinel"
Type in a "Sentinel name"
Select the "Network"
Fill in the EventEmitter address for the "Addresses" field
For "Contract conditions" select "Events" > "EventLog1"
In the field below "EventLog1" fill in
eventName == "SignalPendingAction"Click on "Next" and setup a notification channel
With this setup, you will receive a notification whenever an action is signalled (SignalPendingAction) and executed (ClearPendingAction) on the Timelock.
Note that the specific event to monitor whether it is "EventLog", "EventLog1", "EventLog2", will depend on the event called within the contracts, so the contracts need to be checked to decide which one to use for specific events.
Last updated