OrderRouter
Inherits: Ownable2Step, Pausable, OrderEscrowAccounting
Title: OrderRouter (The MEV Shield)
Manages Commit-Reveal, MEV protection, and the un-brickable FIFO queue.
Holds only non-trader-owned keeper execution reserves. Trader collateral remains in MarginClearinghouse.
Note: security-contact: contact@plether.com
Constants
PANIC_SELECTOR
bytes4 internal constant PANIC_SELECTOR = 0x4e487b71
TYPED_ORDER_FAILURE_SELECTOR
bytes4 internal constant TYPED_ORDER_FAILURE_SELECTOR = ICfdEngine.CfdEngine__TypedOrderFailure.selector
MARK_PRICE_OUT_OF_ORDER_SELECTOR
bytes4 internal constant MARK_PRICE_OUT_OF_ORDER_SELECTOR = ICfdEngine.CfdEngine__MarkPriceOutOfOrder.selector
vault
ICfdVault internal immutable vault
TIMELOCK_DELAY
uint256 public constant TIMELOCK_DELAY = 48 hours
MIN_ENGINE_GAS
uint256 internal constant MIN_ENGINE_GAS = 600_000
MIN_MEV_PUBLISH_DELAY
uint256 internal constant MIN_MEV_PUBLISH_DELAY = 5
DEFAULT_MAX_ORDER_AGE
uint256 internal constant DEFAULT_MAX_ORDER_AGE = 60
MAX_EXPIRED_ORDER_SKIPS_PER_CALL
uint256 internal constant MAX_EXPIRED_ORDER_SKIPS_PER_CALL = 32
MAX_BATCH_ORDER_SCANS
uint256 internal constant MAX_BATCH_ORDER_SCANS = 64
MAX_PRUNE_ORDERS_PER_CALL
uint256 internal constant MAX_PRUNE_ORDERS_PER_CALL = 64
RETRYABLE_SKIP_COOLDOWN
uint64 internal constant RETRYABLE_SKIP_COOLDOWN = 5
OPEN_ORDER_EXECUTION_BOUNTY_BPS
uint256 internal constant OPEN_ORDER_EXECUTION_BOUNTY_BPS = 1
MIN_OPEN_ORDER_EXECUTION_BOUNTY_USDC
uint256 internal constant MIN_OPEN_ORDER_EXECUTION_BOUNTY_USDC = 50_000
MAX_OPEN_ORDER_EXECUTION_BOUNTY_USDC
uint256 internal constant MAX_OPEN_ORDER_EXECUTION_BOUNTY_USDC = DecimalConstants.ONE_USDC
CLOSE_ORDER_EXECUTION_BOUNTY_USDC
uint256 internal constant CLOSE_ORDER_EXECUTION_BOUNTY_USDC = DecimalConstants.ONE_USDC
MAX_PENDING_ORDERS
uint256 public constant MAX_PENDING_ORDERS = 5
State Variables
pyth
IPyth public pyth
pythFeedIds
bytes32[] public pythFeedIds
quantities
uint256[] public quantities
basePrices
uint256[] public basePrices
inversions
bool[] public inversions
nextCommitId
uint64 public nextCommitId = 1
nextExecuteId
uint64 public nextExecuteId = 1
maxOrderAge
uint256 public maxOrderAge
pendingMaxOrderAge
uint256 public pendingMaxOrderAge
maxOrderAgeActivationTime
uint256 public maxOrderAgeActivationTime
orderExecutionStalenessLimit
uint256 public orderExecutionStalenessLimit = 60
pendingOrderExecutionStalenessLimit
uint256 public pendingOrderExecutionStalenessLimit
orderExecutionStalenessActivationTime
uint256 public orderExecutionStalenessActivationTime
liquidationStalenessLimit
uint256 public liquidationStalenessLimit = 15
pendingLiquidationStalenessLimit
uint256 public pendingLiquidationStalenessLimit
liquidationStalenessActivationTime
uint256 public liquidationStalenessActivationTime
claimableEth
mapping(address => uint256) public claimableEth
pendingHeadOrderId
mapping(bytes32 => uint64) public pendingHeadOrderId
pendingTailOrderId
mapping(bytes32 => uint64) public pendingTailOrderId
globalTailOrderId
uint64 public globalTailOrderId
Functions
onlyEngine
modifier onlyEngine() ;
constructor
constructor(
address _engine,
address _vault,
address _pyth,
bytes32[] memory _feedIds,
uint256[] memory _quantities,
uint256[] memory _basePrices,
bool[] memory _inversions
) Ownable(msg.sender) OrderEscrowAccounting(_engine);
Parameters
| Name | Type | Description |
|---|---|---|
_engine | address | CfdEngine that processes trades and liquidations |
_vault | address | CfdVault used for vault depth queries and liquidation bounty payouts |
_pyth | address | Pyth oracle contract (address(0) enables mock mode on Anvil) |
_feedIds | bytes32[] | Pyth price feed IDs for each basket component |
_quantities | uint256[] | Weight of each component (must sum to 1e18) |
_basePrices | uint256[] | Base price per component for normalization (8 decimals) |
_inversions | bool[] | Whether to invert each feed (e.g. USD/JPY -> JPY/USD) |
proposeMaxOrderAge
Proposes a new maxOrderAge value, subject to 48h timelock.
function proposeMaxOrderAge(
uint256 _maxOrderAge
) external onlyOwner;
finalizeMaxOrderAge
Finalizes the pending maxOrderAge after timelock expires.
function finalizeMaxOrderAge() external onlyOwner;
cancelMaxOrderAgeProposal
Cancels the pending maxOrderAge proposal.
function cancelMaxOrderAgeProposal() external onlyOwner;
proposeOrderExecutionStalenessLimit
function proposeOrderExecutionStalenessLimit(
uint256 limit
) external onlyOwner;
finalizeOrderExecutionStalenessLimit
function finalizeOrderExecutionStalenessLimit() external onlyOwner;
cancelOrderExecutionStalenessLimitProposal
function cancelOrderExecutionStalenessLimitProposal() external onlyOwner;
proposeLiquidationStalenessLimit
function proposeLiquidationStalenessLimit(
uint256 limit
) external onlyOwner;
finalizeLiquidationStalenessLimit
function finalizeLiquidationStalenessLimit() external onlyOwner;
cancelLiquidationStalenessLimitProposal
function cancelLiquidationStalenessLimitProposal() external onlyOwner;
pause
function pause() external onlyOwner;
unpause
function unpause() external onlyOwner;
commitOrder
Submits a trade intent to the FIFO queue. Margin and the order’s execution bounty are reserved immediately.
function commitOrder(
CfdTypes.Side side,
uint256 sizeDelta,
uint256 marginDelta,
uint256 targetPrice,
bool isClose
) external;
Parameters
| Name | Type | Description |
|---|---|---|
side | CfdTypes.Side | BULL or BEAR |
sizeDelta | uint256 | Position size change (18 decimals) |
marginDelta | uint256 | Margin to add or remove (6 decimals, USDC) |
targetPrice | uint256 | Slippage limit price (8 decimals, 0 = market order) |
isClose | bool | True to allow execution even when paused or in FAD close-only mode |
quoteOpenOrderExecutionBountyUsdc
Quotes the reserved USDC execution bounty for a new open order using the latest engine mark price.
Falls back to 1.00 USD if the engine has not observed a mark yet. Result is floored at 0.05 USDC and capped at 1 USDC for non-close intents.
function quoteOpenOrderExecutionBountyUsdc(
uint256 sizeDelta
) external view returns (uint256 executionBountyUsdc);
Parameters
| Name | Type | Description |
|---|---|---|
sizeDelta | uint256 | Order size in 18-decimal notional units |
Returns
| Name | Type | Description |
|---|---|---|
executionBountyUsdc | uint256 | Reserved execution bounty in 6-decimal USDC units |
quoteCloseOrderExecutionBountyUsdc
Quotes the flat reserved USDC execution bounty for a close order.
function quoteCloseOrderExecutionBountyUsdc() external pure returns (uint256 executionBountyUsdc);
orders
function orders(
uint64 orderId
) external view returns (bytes32, uint256, uint256, uint256, uint64, uint64, uint64, CfdTypes.Side, bool);
committedMargins
function committedMargins(
uint64 orderId
) external view returns (uint256);
executionBountyReserves
function executionBountyReserves(
uint64 orderId
) external view returns (uint256);
isInMarginQueue
function isInMarginQueue(
uint64 orderId
) external view returns (bool);
getOrderRecord
function getOrderRecord(
uint64 orderId
) external view returns (OrderRecord memory);
syncMarginQueue
Returns the total queued escrow state for an account across all pending orders.
function syncMarginQueue(
bytes32 accountId
) external onlyEngine;
getPendingOrdersForAccount
function getPendingOrdersForAccount(
bytes32 accountId
) external view returns (IOrderRouterAccounting.PendingOrderView[] memory pending);
executeOrder
Keeper executes the current global queue head. Validates oracle freshness and publish-time ordering against the order commit, checks slippage, then delegates to CfdEngine. Terminal invalid/expired orders pay from router-custodied execution bounty, while retryable slippage misses are requeued to the global tail with cooldown so keepers cannot burn out-of-market intents or pin the FIFO head.
function executeOrder(
uint64 orderId,
bytes[] calldata pythUpdateData
) external payable;
Parameters
| Name | Type | Description |
|---|---|---|
orderId | uint64 | Must equal the current global queue head (expired orders are auto-skipped) |
pythUpdateData | bytes[] | Pyth price update blobs; attach ETH to cover the Pyth fee |
executeOrderBatch
Executes queued pending orders against a single Pyth price tick. Updates Pyth once, then loops through the FIFO queue. Aggregates reserved USDC execution bounties across processed orders and refunds excess ETH in a single transfer.
function executeOrderBatch(
uint64 maxOrderId,
bytes[] calldata pythUpdateData
) external payable;
Parameters
| Name | Type | Description |
|---|---|---|
maxOrderId | uint64 | Inclusive upper bound on committed order ids the batch may begin processing from |
pythUpdateData | bytes[] | Pyth price update blobs; attach ETH to cover the Pyth fee |
_skipStaleOrders
function _skipStaleOrders(
uint64 upToId,
uint256 maxSkips
) internal returns (uint256 skipped);
_currentRouterExecutionContext
function _currentRouterExecutionContext() internal view returns (RouterExecutionContext memory context);
_routedCloseOnlyFailure
function _routedCloseOnlyFailure(
CfdTypes.Order memory order,
bool oracleFrozen,
bool isFadWindow,
bool degradedMode,
bool closeOnly
) internal pure returns (OrderFailurePolicyLib.FailureContext memory failure);
_processTypedOrderExecution
function _processTypedOrderExecution(
CfdTypes.Order memory order,
uint256 executionPrice,
uint256 vaultDepth,
uint64 oraclePublishTime,
bool oracleFrozen,
bool isFadWindow,
bool degradedMode
)
internal
returns (
bool success,
OrderFailReason failureReason,
OrderFailurePolicyLib.FailedOrderBountyPolicy failureBountyPolicy
);
pruneExpiredOrders
function pruneExpiredOrders(
uint64 upToId,
uint256 maxPrunes
) external;
_resolveOraclePrice
function _resolveOraclePrice(
bytes[] calldata pythUpdateData,
uint256 mockFallbackPrice
) internal returns (uint256 price, uint64 publishTime, uint256 pythFee);
_sendEth
function _sendEth(
address to,
uint256 amount
) internal;
_pendingOrder
function _pendingOrder(
uint64 orderId
) internal view returns (OrderRecord storage record, CfdTypes.Order memory order);
_getQueuedPositionView
function _getQueuedPositionView(
bytes32 accountId
) internal view returns (QueuedPositionView memory queuedPosition);
_failedOrderBountyPolicy
function _failedOrderBountyPolicy(
OrderFailurePolicyLib.FailureContext memory context
) internal pure returns (OrderFailurePolicyLib.FailedOrderBountyPolicy);
_decodeTypedOrderFailure
function _decodeTypedOrderFailure(
bytes memory revertData
)
internal
pure
returns (CfdEnginePlanTypes.ExecutionFailurePolicyCategory failureCategory, uint8 failureCode, bool isClose);
_cleanupOrder
function _cleanupOrder(
uint64 orderId,
bool success,
OrderFailurePolicyLib.FailedOrderBountyPolicy failedPolicy
) internal returns (uint256 executionBountyUsdc);
_finalizeExecution
function _finalizeExecution(
uint64 orderId,
uint256 pythFee,
bool success,
OrderFailurePolicyLib.FailedOrderBountyPolicy failedPolicy
) internal;
_routedExpiryFailure
function _routedExpiryFailure(
CfdTypes.Order memory order
) internal view returns (OrderFailurePolicyLib.FailureContext memory context);
_routedRouterPolicyFailure
function _routedRouterPolicyFailure(
CfdTypes.Order memory order,
OrderFailurePolicyLib.RouterFailureCode failureCode,
bool oracleFrozen,
bool isFad,
bool degradedMode,
bool closeOnly
) internal pure returns (OrderFailurePolicyLib.FailureContext memory context);
_routedFailureFromEngineRevert
function _routedFailureFromEngineRevert(
CfdTypes.Order memory order,
bytes memory revertData,
bool oracleFrozen,
bool isFad,
bool degradedMode
) internal pure returns (OrderFailurePolicyLib.FailureContext memory context);
_payOrDeferLiquidationBounty
Immediate liquidation bounties still pay directly to the executing keeper wallet.
If immediate payment is unavailable or direct transfer fails, the bounty is deferred and later
settles as clearinghouse credit via claimDeferredClearerBounty().
function _payOrDeferLiquidationBounty(
uint256 liquidationBountyUsdc
) internal;
_forfeitEscrowedOrderBountiesOnLiquidation
function _forfeitEscrowedOrderBountiesOnLiquidation(
bytes32 accountId
) internal;
_clearLiquidatedAccountOrders
function _clearLiquidatedAccountOrders(
bytes32 accountId
) internal;
_quoteOpenOrderExecutionBountyUsdc
function _quoteOpenOrderExecutionBountyUsdc(
uint256 sizeDelta,
uint256 price
) internal pure returns (uint256);
_quoteCloseOrderExecutionBountyUsdc
function _quoteCloseOrderExecutionBountyUsdc() internal pure returns (uint256);
_commitReferencePrice
function _commitReferencePrice() internal view returns (uint256 price);
_canUseCommitMarkForOpenPrefilter
function _canUseCommitMarkForOpenPrefilter() internal view returns (bool);
_linkPendingOrder
function _linkPendingOrder(
bytes32 accountId,
uint64 orderId
) internal;
_linkGlobalOrder
function _linkGlobalOrder(
uint64 orderId
) internal;
_unlinkGlobalOrder
function _unlinkGlobalOrder(
uint64 orderId
) internal;
_skipRetryableOrder
function _skipRetryableOrder(
uint64 orderId,
OrderFailReason reason
) internal;
_unlinkPendingOrder
function _unlinkPendingOrder(
bytes32 accountId,
uint64 orderId
) internal;
_reserveCloseExecutionBounty
function _reserveCloseExecutionBounty(
bytes32 accountId,
uint256 executionBountyUsdc
) internal override;
_deleteOrder
function _deleteOrder(
uint64 orderId,
bool advanceHead,
IOrderRouterAccounting.OrderStatus terminalStatus
) internal;
_releaseCommittedMarginForExecution
function _releaseCommittedMarginForExecution(
uint64 orderId
) internal;
claimEth
Claims ETH stuck from failed refund transfers.
function claimEth() external;
claimUsdc
Claims USDC bounty refunds that could not be pushed during failed-order cleanup.
function claimUsdc() external;
_computeBasketPrice
function _computeBasketPrice() internal view returns (uint256 basketPrice, uint256 minPublishTime);
_checkSlippage
Opens vs closes have opposing slippage directions: BULL open wants HIGH entry (more room for drop) → exec >= target BULL close wants LOW exit (lock in profit) → exec <= target BEAR open wants LOW entry (more room for rise) → exec <= target BEAR close wants HIGH exit (lock in profit) → exec >= target targetPrice == 0 disables the check (market order).
function _checkSlippage(
CfdTypes.Order memory order,
uint256 executionPrice
) internal pure returns (bool);
_isOracleFrozen
Returns true only when FX markets are actually closed and Pyth feeds have stopped publishing. Distinct from isFadWindow() which starts 3 hours earlier for margin purposes. Uses Friday 22:00 UTC (conservative vs 21:00 EDT summer) to guarantee zero latency arbitrage.
function _isOracleFrozen() internal view returns (bool);
_isCloseOnlyWindow
function _isCloseOnlyWindow() internal view returns (bool);
_invertPythPrice
Inverts a Pyth price (e.g. USD/JPY → JPY/USD) and returns 8-decimal output. Formula: 10^(8 - expo) / price
function _invertPythPrice(
int64 price,
int32 expo
) internal pure returns (uint256);
_normalizePythPrice
Converts a Pyth price to 8-decimal format. Scales up/down based on exponent difference from -8.
function _normalizePythPrice(
int64 price,
int32 expo
) internal pure returns (uint256);
updateMarkPrice
Push a fresh mark price to the engine without processing an order. Required before LP deposits/withdrawals when mark is stale.
function updateMarkPrice(
bytes[] calldata pythUpdateData
) external payable;
Parameters
| Name | Type | Description |
|---|---|---|
pythUpdateData | bytes[] | Pyth price update blobs; attach ETH to cover the Pyth fee |
executeLiquidation
Keeper-triggered liquidation using the canonical live-market staleness policy. Forfeits any queued-order execution escrow to the vault instead of crediting it back to trader settlement, then pays the liquidation keeper bounty in USDC directly from the vault.
function executeLiquidation(
bytes32 accountId,
bytes[] calldata pythUpdateData
) external payable;
Parameters
| Name | Type | Description |
|---|---|---|
accountId | bytes32 | The account to liquidate (bytes32-encoded address) |
pythUpdateData | bytes[] | Pyth price update blobs; attach ETH to cover the Pyth fee |
_pendingHeadOrderId
function _pendingHeadOrderId(
bytes32 accountId
) internal view override returns (uint64);
_revertInsufficientFreeEquity
function _revertInsufficientFreeEquity() internal pure override;
_revertMarginOrderLinkCorrupted
function _revertMarginOrderLinkCorrupted() internal pure override;
Events
OrderCommitted
event OrderCommitted(uint64 indexed orderId, bytes32 indexed accountId, CfdTypes.Side side);
OrderExecuted
event OrderExecuted(uint64 indexed orderId, uint256 executionPrice);
OrderFailed
event OrderFailed(uint64 indexed orderId, OrderFailReason reason);
OrderSkipped
event OrderSkipped(uint64 indexed orderId, OrderFailReason reason, uint64 retryAfterTimestamp);
Errors
OrderRouter__ZeroSize
error OrderRouter__ZeroSize();
OrderRouter__CloseMarginDeltaNotAllowed
error OrderRouter__CloseMarginDeltaNotAllowed();
OrderRouter__TimelockNotReady
error OrderRouter__TimelockNotReady();
OrderRouter__NoProposal
error OrderRouter__NoProposal();
OrderRouter__FIFOViolation
error OrderRouter__FIFOViolation();
OrderRouter__OrderNotPending
error OrderRouter__OrderNotPending();
OrderRouter__InsufficientPythFee
error OrderRouter__InsufficientPythFee();
OrderRouter__MockModeDisabled
error OrderRouter__MockModeDisabled();
OrderRouter__NoOrdersToExecute
error OrderRouter__NoOrdersToExecute();
OrderRouter__MaxOrderIdNotCommitted
error OrderRouter__MaxOrderIdNotCommitted();
OrderRouter__OraclePriceTooStale
error OrderRouter__OraclePriceTooStale();
OrderRouter__NothingToClaim
error OrderRouter__NothingToClaim();
OrderRouter__EthTransferFailed
error OrderRouter__EthTransferFailed();
OrderRouter__OraclePriceNegative
error OrderRouter__OraclePriceNegative();
OrderRouter__MevOraclePriceTooStale
error OrderRouter__MevOraclePriceTooStale();
OrderRouter__LengthMismatch
error OrderRouter__LengthMismatch();
OrderRouter__InvalidWeights
error OrderRouter__InvalidWeights();
OrderRouter__InvalidBasePrice
error OrderRouter__InvalidBasePrice();
OrderRouter__EmptyFeeds
error OrderRouter__EmptyFeeds();
OrderRouter__MevDetected
error OrderRouter__MevDetected();
OrderRouter__MissingPythUpdateData
error OrderRouter__MissingPythUpdateData();
OrderRouter__OracleFrozen
error OrderRouter__OracleFrozen();
OrderRouter__InsufficientGas
error OrderRouter__InsufficientGas();
OrderRouter__NoOpenPosition
error OrderRouter__NoOpenPosition();
OrderRouter__CloseSideMismatch
error OrderRouter__CloseSideMismatch();
OrderRouter__CloseSizeExceedsPosition
error OrderRouter__CloseSizeExceedsPosition();
OrderRouter__InsufficientFreeEquity
error OrderRouter__InsufficientFreeEquity();
OrderRouter__MarginOrderLinkCorrupted
error OrderRouter__MarginOrderLinkCorrupted();
OrderRouter__PendingOrderLinkCorrupted
error OrderRouter__PendingOrderLinkCorrupted();
OrderRouter__Unauthorized
error OrderRouter__Unauthorized();
OrderRouter__TooManyPendingOrders
error OrderRouter__TooManyPendingOrders();
OrderRouter__DegradedMode
error OrderRouter__DegradedMode();
OrderRouter__CloseOnlyMode
error OrderRouter__CloseOnlyMode();
OrderRouter__SeedLifecycleIncomplete
error OrderRouter__SeedLifecycleIncomplete();
OrderRouter__TradingNotActive
error OrderRouter__TradingNotActive();
OrderRouter__RetryCooldownActive
error OrderRouter__RetryCooldownActive();
OrderRouter__OraclePublishTimeOutOfOrder
error OrderRouter__OraclePublishTimeOutOfOrder();
OrderRouter__InvalidStalenessLimit
error OrderRouter__InvalidStalenessLimit();
OrderRouter__PredictableOpenInvalid
error OrderRouter__PredictableOpenInvalid(uint8 code);
Structs
QueuedPositionView
struct QueuedPositionView {
bool exists;
CfdTypes.Side side;
uint256 size;
}
RouterExecutionContext
struct RouterExecutionContext {
bool oracleFrozen;
bool isFadWindow;
bool degradedMode;
OrderOraclePolicyLib.OracleExecutionPolicy policy;
}
Enums
OrderFailReason
enum OrderFailReason {
Expired,
CloseOnlyOracleFrozen,
CloseOnlyFad,
SlippageExceeded,
EnginePanic,
AccountLiquidated,
EngineRevert
}