Skip to main content

Simulations

The ExchangeRouter and GlvRouter contracts expose public simulation functions that let you dry-run an execution against a set of supplied prices before submitting a real request for keeper execution. Running a simulation catches validation errors — such as price impact limits or insufficient output amounts — without spending gas on a failed transaction.

For the full CreateOrderParams and CreateDepositParams structures, see ExchangeRouter. For contract addresses, see Contract addresses.

How simulations work

Simulation functions use the withSimulatedOraclePrices modifier in OracleModule. The modifier injects synthetic price data into the oracle, executes the handler logic through the router/controller path, then unconditionally reverts with EndOfOracleSimulation. Because the transaction always reverts, no state changes are persisted.

The caller interprets the revert reason: if the error is EndOfOracleSimulation, the simulation succeeded and the supplied prices passed validation. Any other revert reason indicates an error that would also occur on-chain. In practice, this is a preflight tool for users and integrators; actual request execution is still performed by keeper-only handler functions.

In practice, simulations are usually called in the same multicall flow that creates the request. You create the deposit, withdrawal, or order first, then call the corresponding simulateExecuteLatest... function with the prices you want to test against.

Example (TypeScript / ethers):

const currentTimestamp = (await provider.getBlock("latest")).timestamp + 2;

await expect(
exchangeRouter.multicall(
[
exchangeRouter.interface.encodeFunctionData("sendWnt", [depositVault.address, executionFee]),
exchangeRouter.interface.encodeFunctionData("sendTokens", [usdc.address, depositVault.address, shortTokenAmount]),
exchangeRouter.interface.encodeFunctionData("createDeposit", [depositParams]),
exchangeRouter.interface.encodeFunctionData("simulateExecuteLatestDeposit", [
{
primaryTokens: [wnt.address, usdc.address],
primaryPrices: [
{ min: wethPrice, max: wethPrice },
{ min: usdcPrice, max: usdcPrice },
],
minTimestamp: currentTimestamp,
maxTimestamp: currentTimestamp,
},
]),
],
{ value: executionFee }
)
).to.be.revertedWithCustomError(errorsContract, "EndOfOracleSimulation");

If the call reverts with EndOfOracleSimulation, the simulated execution passed. If it reverts with any other error, treat that as the actual validation error for the supplied prices.

Available simulation functions

The latest-request helpers use the latest created request key. In practice, call the simulation immediately after creating the request in the same multicall, so it targets the action you just created.

ExchangeRouter functions

FunctionAction type
simulateExecuteLatestDeposit(SimulatePricesParams)GM deposit
simulateExecuteLatestWithdrawal(SimulatePricesParams, SwapPricingType)GM withdrawal
simulateExecuteLatestShift(SimulatePricesParams)GM pool shift
simulateExecuteLatestOrder(SimulatePricesParams)Perpetual or swap order
simulateExecuteLatestJitOrder(GlvShiftUtils.CreateGlvShiftParams[], SimulatePricesParams)JIT order

GlvRouter functions

FunctionAction type
simulateExecuteGlvDeposit(bytes32, SimulatePricesParams)GLV deposit by explicit key
simulateExecuteLatestGlvDeposit(SimulatePricesParams)Latest GLV deposit
simulateExecuteGlvWithdrawal(bytes32, SimulatePricesParams)GLV withdrawal by explicit key
simulateExecuteLatestGlvWithdrawal(SimulatePricesParams)Latest GLV withdrawal

SimulatePricesParams

struct SimulatePricesParams {
address[] primaryTokens; // token addresses to price
Price.Props[] primaryPrices; // { min, max } price for each token
uint256 minTimestamp; // lower bound of the price window
uint256 maxTimestamp; // upper bound of the price window
}

Supply current oracle prices for all tokens involved in the action. The minTimestamp and maxTimestamp fields must bracket the expected execution timestamp; a common pattern is to set both to block.timestamp + 120.

Field notes

FieldDescription
primaryTokensToken addresses to assign simulated prices to. Include every token the action depends on.
primaryPricesSimulated oracle prices for each token, in the same order as primaryTokens. Each price uses { min, max }.
minTimestampLower bound for the simulated oracle timestamp window.
maxTimestampUpper bound for the simulated oracle timestamp window.