ExchangeRouter
The ExchangeRouter contract exposes the main protocol functions for creating orders, deposits, and withdrawals.
For an overview of the contract architecture and execution model, see Architecture.
Creating an order
To create a swap, increase-position, or decrease-position order, you must first transfer the required tokens to the OrderVault, then call ExchangeRouter.createOrder in the same transaction.
The token transfer and the ExchangeRouter.createOrder call must occur in a single transaction. If they are separated, other users may withdraw the transferred tokens before your order is created.
See the ExchangeRouter tests for a complete example.
ExchangeRouter.createOrder
function createOrder(IBaseOrderUtils.CreateOrderParams calldata params) returns (bytes32)
Use this to create swap, increase, or decrease orders through the router. For increase and swap orders, transfer the execution fee and any input collateral to the OrderVault in the same transaction, usually via multicall.
Example (TypeScript / ethers):
await usdc.approve(router.address, collateralAmount);
await exchangeRouter.multicall(
[
exchangeRouter.interface.encodeFunctionData("sendWnt", [orderVault.address, executionFee]),
exchangeRouter.interface.encodeFunctionData("sendTokens", [usdc.address, orderVault.address, collateralAmount]),
exchangeRouter.interface.encodeFunctionData("createOrder", [
{
addresses: {
receiver: trader.address,
cancellationReceiver: trader.address,
callbackContract: ethers.constants.AddressZero,
uiFeeReceiver: ethers.constants.AddressZero,
market: marketToken,
initialCollateralToken: usdc.address,
swapPath: [],
},
numbers: {
sizeDeltaUsd,
initialCollateralDeltaAmount: collateralAmount,
triggerPrice: 0,
acceptablePrice,
executionFee,
callbackGasLimit: 0,
minOutputAmount: 0,
validFromTime: 0,
},
orderType: OrderType.MarketIncrease,
decreasePositionSwapType: DecreasePositionSwapType.NoSwap,
isLong: true,
shouldUnwrapNativeToken: false,
autoCancel: false,
referralCode: ethers.constants.HashZero,
dataList: [],
},
]),
],
{ value: executionFee }
);
Parameters:
| Parameter | Type | Description |
|---|---|---|
params | IBaseOrderUtils.CreateOrderParams | Struct containing order configuration |
CreateOrderParams
The order struct is split into address fields, numeric fields, and a small set of enums and flags.
| Field | Description |
|---|---|
addresses | Group of address fields listed in CreateOrderParamsAddresses. |
numbers | Group of numeric fields listed in CreateOrderParamsNumbers. |
orderType | Order type enum listed in OrderType. |
decreasePositionSwapType | Swap behavior for decrease orders listed in DecreasePositionSwapType. |
isLong | true for a long position, false for a short position. |
shouldUnwrapNativeToken | Whether to unwrap the native token on output. For example, if the output token is WETH and this is true, the contract converts WETH to ETH before sending. |
autoCancel | For LimitDecrease and StopLossDecrease orders, whether the order is cancelled automatically when the position closes. Ignored for all other order types. |
referralCode | Referral code to set for the trader alongside order creation. If the trader already has a referral code set, this parameter has no effect. |
dataList | Array of additional bytes32 data. Pass an empty array if no extra data is needed. |
CreateOrderParamsAddresses
| Field | Description |
|---|---|
receiver | Address that receives any output amounts. |
cancellationReceiver | If the order is cancelled, collateral and the network fee gas are sent to this address if it is not the zero address. |
callbackContract | Contract to call on order execution or cancellation. |
uiFeeReceiver | Address that receives the UI fee. |
market | Market to trade in. |
initialCollateralToken | Initial collateral token transferred into the contract. |
swapPath | Array of market addresses to swap initialCollateralToken through. For increase and swap orders, the token is swapped before entering the position. For decrease orders, the output is swapped through these markets. |
CreateOrderParamsNumbers
| Field | Description |
|---|---|
sizeDeltaUsd | Position size to increase or decrease. |
initialCollateralDeltaAmount | Amount of input collateral sent to the OrderVault for swap and increase orders, or collateral amount to withdraw for decrease orders. |
triggerPrice | Trigger price for LimitIncrease, LimitDecrease, and StopLossDecrease orders. The keeper attempts execution when price reaches this level. |
acceptablePrice | Price at which the order can execute. For market orders, the order is cancelled if it cannot execute at this price. For limit and stop-loss orders, execution is skipped if the trigger price is reached but the acceptable price cannot be filled. |
executionFee | Amount of native token (for example, ETH on Arbitrum) included as the execution fee. This is the maximum fee keepers can use to execute the order. Any excess is returned to the order account. See Execution Fee for details. |
callbackGasLimit | Gas limit passed to the callback contract on order execution or cancellation. |
minOutputAmount | For swap orders, the minimum token output amount. For increase orders, the minimum token amount after initialCollateralDeltaAmount is swapped through swapPath. For decrease orders, this is the minimum USD value, because decrease orders can produce two output tokens: the profit token and the withdrawn collateral token. |
validFromTime | Timestamp from which the order becomes valid for execution. The keeper skips the order until block.timestamp reaches this value. Pass 0 for immediate validity. |
OrderType
| Value | Description |
|---|---|
MarketSwap | Execute a swap at the current market price. |
LimitSwap | Execute a swap when minOutputAmount can be filled. |
MarketIncrease | Open or increase a long or short position at market price. |
LimitIncrease | Open or increase a position when the trigger price is reached. |
MarketDecrease | Close or reduce a position at market price. |
LimitDecrease | Close or reduce a position when the trigger price is reached. |
StopLossDecrease | Close or reduce a position when price falls to the trigger level. |
Liquidation | Forced position closure triggered by the protocol when margin requirements are no longer met. Not user-callable. |
StopIncrease | Open or increase a position when the stop price is reached. |
DecreasePositionSwapType
| Value | Description |
|---|---|
NoSwap | No swap is performed. |
SwapPnlTokenToCollateralToken | The profit token is swapped to the collateral token, if possible. |
SwapCollateralTokenToPnlToken | The withdrawn collateral is swapped to the profit token, if possible. |
Creating a deposit
To create a deposit, you must first transfer tokens to the DepositVault, then call ExchangeRouter.createDeposit in the same transaction.
The token transfer and the ExchangeRouter.createDeposit call must occur in a single transaction. If they are separated, other users may withdraw the transferred tokens before your deposit is created.
If this transaction reverts, the token transfer also reverts, so no funds remain in the DepositVault. If the deposit request is created successfully and is later cancelled, the initial deposit tokens are returned to the account that created the deposit, and any unused execution fee is refunded separately.
See the ExchangeRouter tests for a complete example.
ExchangeRouter.createDeposit
function createDeposit(IDepositUtils.CreateDepositParams calldata params) returns (bytes32)
Use this to add liquidity to a GM market. Transfer the execution fee and any long / short input tokens to the DepositVault in the same transaction, usually via multicall.
Example (TypeScript / ethers):
await usdc.approve(router.address, shortTokenAmount);
await exchangeRouter.multicall(
[
exchangeRouter.interface.encodeFunctionData("sendWnt", [depositVault.address, executionFee]),
exchangeRouter.interface.encodeFunctionData("sendTokens", [usdc.address, depositVault.address, shortTokenAmount]),
exchangeRouter.interface.encodeFunctionData("createDeposit", [
{
addresses: {
receiver: liquidityProvider.address,
callbackContract: ethers.constants.AddressZero,
uiFeeReceiver: ethers.constants.AddressZero,
market: marketToken,
initialLongToken: longToken,
initialShortToken: usdc.address,
longTokenSwapPath: [],
shortTokenSwapPath: [],
},
minMarketTokens,
shouldUnwrapNativeToken: false,
executionFee,
callbackGasLimit: 0,
dataList: [],
},
]),
],
{ value: executionFee }
);
Parameters:
| Parameter | Type | Description |
|---|---|---|
params | IDepositUtils.CreateDepositParams | Struct containing deposit configuration |
CreateDepositParams
| Field | Description |
|---|---|
receiver | Address that receives the GM tokens. |
callbackContract | Contract to call on deposit execution or cancellation. |
uiFeeReceiver | Address that receives the UI fee. |
market | Market to deposit into. |
initialLongToken | Long token transferred into the contract. |
initialShortToken | Short token transferred into the contract. |
longTokenSwapPath | Array of market addresses to swap initialLongToken through before depositing. |
shortTokenSwapPath | Array of market addresses to swap initialShortToken through before depositing. |
minMarketTokens | Minimum acceptable amount of GM tokens to receive. |
shouldUnwrapNativeToken | Whether to unwrap the native token if the deposit is cancelled. For example, if initialLongToken is WETH and this is true, the contract converts WETH to ETH before refunding. |
executionFee | Amount of native token (for example, ETH on Arbitrum) included as the execution fee. This is the maximum fee keepers can use to execute the deposit. Any excess is returned to the deposit account. See Execution Fee for details. |
callbackGasLimit | Gas limit passed to the callback contract on deposit execution or cancellation. |
dataList | Array of additional bytes32 data. Pass an empty array if no extra data is needed. |
Creating a withdrawal
To create a withdrawal through ExchangeRouter, send the GM market tokens and execution fee to the WithdrawalVault and call ExchangeRouter.createWithdrawal in the same transaction, usually via multicall.
ExchangeRouter.createWithdrawal
function createWithdrawal(IWithdrawalUtils.CreateWithdrawalParams calldata params) returns (bytes32)
Use this to create an asynchronous withdrawal request. Transfer the GM market tokens and execution fee to the WithdrawalVault in the same transaction, usually via multicall. The withdrawal size is determined by the GM market token amount sent to WithdrawalVault with sendTokens.
Example (TypeScript / ethers):
await marketToken.approve(router.address, marketTokenAmount);
await exchangeRouter.multicall(
[
exchangeRouter.interface.encodeFunctionData("sendWnt", [withdrawalVault.address, executionFee]),
exchangeRouter.interface.encodeFunctionData("sendTokens", [
marketToken.address,
withdrawalVault.address,
marketTokenAmount,
]),
exchangeRouter.interface.encodeFunctionData("createWithdrawal", [
{
addresses: {
receiver: trader.address,
callbackContract: ethers.constants.AddressZero,
uiFeeReceiver: ethers.constants.AddressZero,
market: marketToken.address,
longTokenSwapPath: [],
shortTokenSwapPath: [],
},
minLongTokenAmount,
minShortTokenAmount,
shouldUnwrapNativeToken: false,
executionFee,
callbackGasLimit: 0,
dataList: [],
},
]),
],
{ value: executionFee }
);
Parameters:
| Parameter | Type | Description |
|---|---|---|
params | IWithdrawalUtils.CreateWithdrawalParams | Struct containing withdrawal configuration |
CreateWithdrawalParams
The GM market token amount to withdraw is not part of CreateWithdrawalParams. It is set by the amount sent to WithdrawalVault in the preceding sendTokens call.
| Field | Description |
|---|---|
receiver | Address that receives the withdrawn tokens. |
callbackContract | Contract to call on withdrawal execution or cancellation. |
uiFeeReceiver | Address that receives the UI fee. |
market | Market to withdraw from. |
longTokenSwapPath | Array of market addresses to swap the withdrawn long token through. |
shortTokenSwapPath | Array of market addresses to swap the withdrawn short token through. |
minLongTokenAmount | Minimum output amount of long token after swapping through longTokenSwapPath. For example, if WETH is swapped to BTC, this specifies the minimum BTC amount. |
minShortTokenAmount | Minimum output amount of short token after swapping through shortTokenSwapPath. |
shouldUnwrapNativeToken | Whether to unwrap the native token on output. For example, if the output token is WETH and this is true, the contract converts WETH to ETH before sending. |
executionFee | Amount of native token (for example, ETH on Arbitrum) included as the execution fee. This is the maximum fee keepers can use to execute the withdrawal. Any excess is returned to the withdrawal account. See Execution Fee for details. |
callbackGasLimit | Gas limit passed to the callback contract on withdrawal execution or cancellation. |
dataList | Array of additional bytes32 data. Pass an empty array if no extra data is needed. |
Creating an atomic withdrawal
An atomic withdrawal executes synchronously without waiting for a keeper. It uses on-chain Chainlink price feeds rather than off-chain oracle data, so only tokens with a registered ChainlinkPriceFeedProvider are supported.
ExchangeRouter.executeAtomicWithdrawal
function executeAtomicWithdrawal(
IWithdrawalUtils.CreateWithdrawalParams calldata params,
OracleUtils.SetPricesParams calldata oracleParams
)
Use this to withdraw synchronously using on-chain Chainlink prices instead of the normal keeper flow. Swaps are not supported, so both swap paths must be empty. As with standard withdrawals, the withdrawal size is determined by the GM market token amount sent to WithdrawalVault.
Example (TypeScript / ethers):
await marketToken.approve(router.address, marketTokenAmount);
await exchangeRouter.multicall(
[
exchangeRouter.interface.encodeFunctionData("sendWnt", [withdrawalVault.address, executionFee]),
exchangeRouter.interface.encodeFunctionData("sendTokens", [
marketToken.address,
withdrawalVault.address,
marketTokenAmount,
]),
exchangeRouter.interface.encodeFunctionData("executeAtomicWithdrawal", [
{
addresses: {
receiver: trader.address,
callbackContract: ethers.constants.AddressZero,
uiFeeReceiver: ethers.constants.AddressZero,
market: marketToken.address,
longTokenSwapPath: [],
shortTokenSwapPath: [],
},
minLongTokenAmount,
minShortTokenAmount,
shouldUnwrapNativeToken: false,
executionFee,
callbackGasLimit: 0,
dataList: [],
},
{
tokens: [indexToken.address, longToken.address, shortToken.address],
providers: [
chainlinkPriceFeedProvider.address,
chainlinkPriceFeedProvider.address,
chainlinkPriceFeedProvider.address,
],
data: ["0x", "0x", "0x"],
},
]),
],
{ value: executionFee }
);
Parameters:
| Parameter | Type | Description |
|---|---|---|
params | IWithdrawalUtils.CreateWithdrawalParams | Withdrawal configuration, using the same fields as a standard withdrawal with the constraints listed in CreateWithdrawalParams for atomic withdrawal. |
oracleParams | OracleUtils.SetPricesParams | Oracle price inputs required for atomic execution. |
CreateWithdrawalParams for atomic withdrawal
| Field | Description |
|---|---|
longTokenSwapPath | Must be empty. Swaps are not supported for atomic withdrawals. |
shortTokenSwapPath | Must be empty. Swaps are not supported for atomic withdrawals. |
| All other fields | Same as CreateWithdrawalParams. |
OracleUtils.SetPricesParams
| Field | Description |
|---|---|
tokens | Array of token addresses: the index token, long token, and short token of the market. |
providers | Array of price providers for each token. Use the ChainlinkPriceFeedProvider address found in the deployments folder. To check whether a token is supported, call dataStore.getAddress(Keys.priceFeedKey(token)) and verify the result is not the zero address. |
data | Array of provider data. Pass an array of 0x empty bytes for Chainlink feeds. |
Creating a shift
A shift moves liquidity from one GM market to another in a single request. Instead of withdrawing from the source market and depositing into the destination market separately, you can use createShift to do both atomically. Transfer the GM market tokens from the source market and the execution fee to the ShiftVault before calling ExchangeRouter.createShift in the same transaction.
The token transfer and the ExchangeRouter.createShift call must occur in a single transaction. If they are separated, other users may withdraw the transferred tokens before your shift is created.
ExchangeRouter.createShift
function createShift(IShiftUtils.CreateShiftParams calldata params) returns (bytes32)
Use this to move GM liquidity from one market to another. Transfer the source market's GM tokens and the execution fee to the ShiftVault in the same transaction, usually via multicall. The shift amount is determined by the GM token amount sent to ShiftVault with sendTokens.
Example (TypeScript / ethers):
await sourceMarketToken.approve(router.address, marketTokenAmount);
await exchangeRouter.multicall(
[
exchangeRouter.interface.encodeFunctionData("sendWnt", [shiftVault.address, executionFee]),
exchangeRouter.interface.encodeFunctionData("sendTokens", [
sourceMarketToken.address,
shiftVault.address,
marketTokenAmount,
]),
exchangeRouter.interface.encodeFunctionData("createShift", [
{
addresses: {
receiver: account.address,
callbackContract: ethers.constants.AddressZero,
uiFeeReceiver: ethers.constants.AddressZero,
fromMarket: sourceMarketToken.address,
toMarket: destinationMarketToken.address,
},
minMarketTokens,
executionFee,
callbackGasLimit: 0,
dataList: [],
},
]),
],
{ value: executionFee }
);
Parameters:
| Parameter | Type | Description |
|---|---|---|
params | IShiftUtils.CreateShiftParams | Struct containing shift configuration |
Returns: bytes32 -- the unique key identifying the shift request.
CreateShiftParams
The GM market token amount to shift is not part of CreateShiftParams. It is set by the amount sent to ShiftVault in the preceding sendTokens call.
| Field | Description |
|---|---|
receiver | Address that receives the destination market's GM tokens. |
callbackContract | Contract to call on shift execution or cancellation. |
uiFeeReceiver | Address that receives the UI fee. |
fromMarket | Source GM market to withdraw liquidity from. |
toMarket | Destination GM market to deposit liquidity into. |
minMarketTokens | Minimum acceptable amount of destination GM tokens to receive. |
executionFee | Amount of native token (for example, ETH on Arbitrum) included as the execution fee. This is the maximum fee keepers can use to execute the shift. Any excess is returned to the shift account. See Execution Fee for details. |
callbackGasLimit | Gas limit passed to the callback contract on shift execution or cancellation. |
dataList | Array of additional bytes32 data. Pass an empty array if no extra data is needed. |
Updating an order
You can modify the parameters of an existing pending order by calling updateOrder. Only the order's creator can update it, and market orders can't be updated. Any additional WNT transferred to the contract during the update is added to the order's execution fee.
ExchangeRouter.updateOrder
function updateOrder(
bytes32 key,
uint256 sizeDeltaUsd,
uint256 acceptablePrice,
uint256 triggerPrice,
uint256 minOutputAmount,
uint256 validFromTime,
bool autoCancel
)
Use this to change the parameters of a pending limit or trigger order. The order is also unfrozen if it was previously frozen due to an execution error.
Parameters:
| Parameter | Type | Description |
|---|---|---|
key | bytes32 | Unique key of the order to update, returned by createOrder. |
sizeDeltaUsd | uint256 | New position size delta in USD. |
acceptablePrice | uint256 | New acceptable execution price. For long increase orders, the order is cancelled if the execution price is greater than acceptablePrice. For short increase orders, the order is cancelled if the execution price is less than acceptablePrice. |
triggerPrice | uint256 | New trigger price for limit and stop-loss orders. |
minOutputAmount | uint256 | New minimum output amount for swap orders or minimum USD value for decrease orders. |
validFromTime | uint256 | New timestamp from which the order becomes valid. Pass 0 for immediate validity. |
autoCancel | bool | Whether to automatically cancel the order when the associated position closes. Only applies to LimitDecrease and StopLossDecrease orders. |
Cancelling requests
You can cancel pending orders, deposits, withdrawals, and shifts that you created. When a request is cancelled, the deposited funds and any unused execution fee are returned to the creator. Cancellation is subject to a delay period -- the request must have been pending for at least the configured requestExpirationTime before it can be cancelled. If you attempt to cancel too early, the transaction reverts with RequestNotYetCancellable.
ExchangeRouter.cancelOrder
function cancelOrder(bytes32 key)
Cancels a pending order. Only the order's creator can cancel it.
Parameters:
| Parameter | Type | Description |
|---|---|---|
key | bytes32 | Unique key of the order to cancel, returned by createOrder. |
ExchangeRouter.cancelDeposit
function cancelDeposit(bytes32 key)
Cancels a pending deposit. Only the deposit's creator can cancel it. The initial deposit tokens are returned to the creator's account.
Parameters:
| Parameter | Type | Description |
|---|---|---|
key | bytes32 | Unique key of the deposit to cancel, returned by createDeposit. |
ExchangeRouter.cancelWithdrawal
function cancelWithdrawal(bytes32 key)
Cancels a pending withdrawal. Only the withdrawal's creator can cancel it. The GM market tokens are returned to the creator's account.
Parameters:
| Parameter | Type | Description |
|---|---|---|
key | bytes32 | Unique key of the withdrawal to cancel, returned by createWithdrawal. |
ExchangeRouter.cancelShift
function cancelShift(bytes32 key)
Cancels a pending shift. Only the shift's creator can cancel it. The source market's GM tokens are returned to the creator's account.
Parameters:
| Parameter | Type | Description |
|---|---|---|
key | bytes32 | Unique key of the shift to cancel, returned by createShift. |
Setting a callback contract
You can set a persistent callback contract for a specific market. The protocol uses this saved callback contract for liquidations and auto-deleveraging (ADL), where no user-initiated order exists to specify a callback. This lets your contract receive notifications when your position is liquidated or reduced by ADL.
ExchangeRouter.setSavedCallbackContract
function setSavedCallbackContract(address market, address callbackContract)
Saves a callback contract for the caller's account in the given market. The protocol calls this contract during liquidation and ADL events for that account and market.
Parameters:
| Parameter | Type | Description |
|---|---|---|
market | address | Market address to associate the callback contract with. |
callbackContract | address | Contract address to call on liquidation or ADL. Pass the zero address to remove it. |
Claiming fees and rewards
These functions let you claim accumulated fees and rewards across multiple markets in a single transaction. The markets and tokens arrays must have the same length -- each index pairs a market with the token to claim.
ExchangeRouter.claimFundingFees
function claimFundingFees(
address[] memory markets,
address[] memory tokens,
address receiver
) returns (uint256[] memory)
Claims accumulated positive funding fees for the caller across the specified market-token pairs. Funding fees accrue when you hold a position on the less-crowded side of a market.
Parameters:
| Parameter | Type | Description |
|---|---|---|
markets | address[] | Array of market addresses to claim funding fees from. |
tokens | address[] | Array of token addresses, one per market, specifying which token to claim. |
receiver | address | Address that receives the claimed funding fees. |
Returns: uint256[] -- the amount claimed for each market-token pair.
ExchangeRouter.claimCollateral
function claimCollateral(
address[] memory markets,
address[] memory tokens,
uint256[] memory timeKeys,
address receiver
) returns (uint256[] memory)
Claims collateral that was capped due to negative price impact. When a position's price impact is capped, the excess amount is stored and becomes claimable over time as the price impact factor recovers. Each claim is keyed by the timestamp when the capped amount was recorded.
Parameters:
| Parameter | Type | Description |
|---|---|---|
markets | address[] | Array of market addresses to claim collateral from. |
tokens | address[] | Array of token addresses, one per market, specifying which token to claim. |
timeKeys | uint256[] | Array of timestamps identifying the specific capped collateral entries to claim. |
receiver | address | Address that receives the claimed collateral. |
Returns: uint256[] -- the amount claimed for each entry.
ExchangeRouter.claimAffiliateRewards
function claimAffiliateRewards(
address[] memory markets,
address[] memory tokens,
address receiver
) returns (uint256[] memory)
Claims accumulated affiliate (referral) rewards for the caller across the specified market-token pairs. Rewards accrue when traders you referred generate trading fees.
Parameters:
| Parameter | Type | Description |
|---|---|---|
markets | address[] | Array of market addresses to claim affiliate rewards from. |
tokens | address[] | Array of token addresses, one per market, specifying which token to claim. |
receiver | address | Address that receives the claimed affiliate rewards. |
Returns: uint256[] -- the amount claimed for each market-token pair.
UI fee configuration
These functions let an integration configure its UI fee factor and claim accumulated UI fees.
ExchangeRouter.setUiFeeFactor
function setUiFeeFactor(uint256 uiFeeFactor)
Sets the UI fee factor for the caller's account. The factor is stored in the protocol configuration and is applied when users submit actions with your address as uiFeeReceiver.
Parameters:
| Parameter | Type | Description |
|---|---|---|
uiFeeFactor | uint256 | UI fee percentage in 30-decimal precision. The value must not exceed MAX_UI_FEE_FACTOR. |
ExchangeRouter.claimUiFees
function claimUiFees(
address[] memory markets,
address[] memory tokens,
address receiver
) returns (uint256[] memory)
Claims accumulated UI fees for the caller across the specified market-token pairs. The caller address (msg.sender) is treated as the uiFeeReceiver whose balance is being claimed.
Parameters:
| Parameter | Type | Description |
|---|---|---|
markets | address[] | Array of market addresses to claim UI fees from. |
tokens | address[] | Array of token addresses, one per market, specifying which token to claim. |
receiver | address | Address that receives the claimed UI fees. |
Returns: uint256[] -- the amount claimed for each market-token pair.