Skip to main content

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.

warning

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:

ParameterTypeDescription
paramsIBaseOrderUtils.CreateOrderParamsStruct containing order configuration

CreateOrderParams

The order struct is split into address fields, numeric fields, and a small set of enums and flags.

FieldDescription
addressesGroup of address fields listed in CreateOrderParamsAddresses.
numbersGroup of numeric fields listed in CreateOrderParamsNumbers.
orderTypeOrder type enum listed in OrderType.
decreasePositionSwapTypeSwap behavior for decrease orders listed in DecreasePositionSwapType.
isLongtrue for a long position, false for a short position.
shouldUnwrapNativeTokenWhether 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.
autoCancelFor LimitDecrease and StopLossDecrease orders, whether the order is cancelled automatically when the position closes. Ignored for all other order types.
referralCodeReferral code to set for the trader alongside order creation. If the trader already has a referral code set, this parameter has no effect.
dataListArray of additional bytes32 data. Pass an empty array if no extra data is needed.

CreateOrderParamsAddresses

FieldDescription
receiverAddress that receives any output amounts.
cancellationReceiverIf the order is cancelled, collateral and the network fee gas are sent to this address if it is not the zero address.
callbackContractContract to call on order execution or cancellation.
uiFeeReceiverAddress that receives the UI fee.
marketMarket to trade in.
initialCollateralTokenInitial collateral token transferred into the contract.
swapPathArray 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

FieldDescription
sizeDeltaUsdPosition size to increase or decrease.
initialCollateralDeltaAmountAmount of input collateral sent to the OrderVault for swap and increase orders, or collateral amount to withdraw for decrease orders.
triggerPriceTrigger price for LimitIncrease, LimitDecrease, and StopLossDecrease orders. The keeper attempts execution when price reaches this level.
acceptablePricePrice 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.
executionFeeAmount 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.
callbackGasLimitGas limit passed to the callback contract on order execution or cancellation.
minOutputAmountFor 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.
validFromTimeTimestamp 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

ValueDescription
MarketSwapExecute a swap at the current market price.
LimitSwapExecute a swap when minOutputAmount can be filled.
MarketIncreaseOpen or increase a long or short position at market price.
LimitIncreaseOpen or increase a position when the trigger price is reached.
MarketDecreaseClose or reduce a position at market price.
LimitDecreaseClose or reduce a position when the trigger price is reached.
StopLossDecreaseClose or reduce a position when price falls to the trigger level.
LiquidationForced position closure triggered by the protocol when margin requirements are no longer met. Not user-callable.
StopIncreaseOpen or increase a position when the stop price is reached.

DecreasePositionSwapType

ValueDescription
NoSwapNo swap is performed.
SwapPnlTokenToCollateralTokenThe profit token is swapped to the collateral token, if possible.
SwapCollateralTokenToPnlTokenThe 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.

warning

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:

ParameterTypeDescription
paramsIDepositUtils.CreateDepositParamsStruct containing deposit configuration

CreateDepositParams

FieldDescription
receiverAddress that receives the GM tokens.
callbackContractContract to call on deposit execution or cancellation.
uiFeeReceiverAddress that receives the UI fee.
marketMarket to deposit into.
initialLongTokenLong token transferred into the contract.
initialShortTokenShort token transferred into the contract.
longTokenSwapPathArray of market addresses to swap initialLongToken through before depositing.
shortTokenSwapPathArray of market addresses to swap initialShortToken through before depositing.
minMarketTokensMinimum acceptable amount of GM tokens to receive.
shouldUnwrapNativeTokenWhether 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.
executionFeeAmount 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.
callbackGasLimitGas limit passed to the callback contract on deposit execution or cancellation.
dataListArray 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:

ParameterTypeDescription
paramsIWithdrawalUtils.CreateWithdrawalParamsStruct 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.

FieldDescription
receiverAddress that receives the withdrawn tokens.
callbackContractContract to call on withdrawal execution or cancellation.
uiFeeReceiverAddress that receives the UI fee.
marketMarket to withdraw from.
longTokenSwapPathArray of market addresses to swap the withdrawn long token through.
shortTokenSwapPathArray of market addresses to swap the withdrawn short token through.
minLongTokenAmountMinimum output amount of long token after swapping through longTokenSwapPath. For example, if WETH is swapped to BTC, this specifies the minimum BTC amount.
minShortTokenAmountMinimum output amount of short token after swapping through shortTokenSwapPath.
shouldUnwrapNativeTokenWhether 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.
executionFeeAmount 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.
callbackGasLimitGas limit passed to the callback contract on withdrawal execution or cancellation.
dataListArray 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:

ParameterTypeDescription
paramsIWithdrawalUtils.CreateWithdrawalParamsWithdrawal configuration, using the same fields as a standard withdrawal with the constraints listed in CreateWithdrawalParams for atomic withdrawal.
oracleParamsOracleUtils.SetPricesParamsOracle price inputs required for atomic execution.

CreateWithdrawalParams for atomic withdrawal

FieldDescription
longTokenSwapPathMust be empty. Swaps are not supported for atomic withdrawals.
shortTokenSwapPathMust be empty. Swaps are not supported for atomic withdrawals.
All other fieldsSame as CreateWithdrawalParams.

OracleUtils.SetPricesParams

FieldDescription
tokensArray of token addresses: the index token, long token, and short token of the market.
providersArray 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.
dataArray 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.

warning

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:

ParameterTypeDescription
paramsIShiftUtils.CreateShiftParamsStruct 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.

FieldDescription
receiverAddress that receives the destination market's GM tokens.
callbackContractContract to call on shift execution or cancellation.
uiFeeReceiverAddress that receives the UI fee.
fromMarketSource GM market to withdraw liquidity from.
toMarketDestination GM market to deposit liquidity into.
minMarketTokensMinimum acceptable amount of destination GM tokens to receive.
executionFeeAmount 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.
callbackGasLimitGas limit passed to the callback contract on shift execution or cancellation.
dataListArray 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:

ParameterTypeDescription
keybytes32Unique key of the order to update, returned by createOrder.
sizeDeltaUsduint256New position size delta in USD.
acceptablePriceuint256New 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.
triggerPriceuint256New trigger price for limit and stop-loss orders.
minOutputAmountuint256New minimum output amount for swap orders or minimum USD value for decrease orders.
validFromTimeuint256New timestamp from which the order becomes valid. Pass 0 for immediate validity.
autoCancelboolWhether 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:

ParameterTypeDescription
keybytes32Unique 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:

ParameterTypeDescription
keybytes32Unique 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:

ParameterTypeDescription
keybytes32Unique 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:

ParameterTypeDescription
keybytes32Unique 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:

ParameterTypeDescription
marketaddressMarket address to associate the callback contract with.
callbackContractaddressContract 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:

ParameterTypeDescription
marketsaddress[]Array of market addresses to claim funding fees from.
tokensaddress[]Array of token addresses, one per market, specifying which token to claim.
receiveraddressAddress 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:

ParameterTypeDescription
marketsaddress[]Array of market addresses to claim collateral from.
tokensaddress[]Array of token addresses, one per market, specifying which token to claim.
timeKeysuint256[]Array of timestamps identifying the specific capped collateral entries to claim.
receiveraddressAddress 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:

ParameterTypeDescription
marketsaddress[]Array of market addresses to claim affiliate rewards from.
tokensaddress[]Array of token addresses, one per market, specifying which token to claim.
receiveraddressAddress 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:

ParameterTypeDescription
uiFeeFactoruint256UI 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:

ParameterTypeDescription
marketsaddress[]Array of market addresses to claim UI fees from.
tokensaddress[]Array of token addresses, one per market, specifying which token to claim.
receiveraddressAddress that receives the claimed UI fees.

Returns: uint256[] -- the amount claimed for each market-token pair.