OrderBook

Git Source

Inherits: IOrderBookV3, ReentrancyGuard, Multicall, OrderBookV3FlashLender, DeployerDiscoverableMetaV3

State Variables

sOrders

All hashes of all active orders. There's nothing interesting in the value it's just nonzero if the order is live. The key is the hash of the order. Removing an order sets the value back to zero so it is identical to the order never existing. The order hash includes its owner so there's no need to build a multi level mapping, each order hash MUST uniquely identify the order globally. order hash => order is live

mapping(bytes32 orderHash => uint256 liveness) internal sOrders;

sVaultBalances

Vault balances are stored in a mapping of owner => token => vault ID This gives 1:1 parity with the IOrderBookV1 interface but keeping the sFoo naming convention for storage variables.

mapping(address owner => mapping(address token => mapping(uint256 vaultId => uint256 balance))) internal sVaultBalances;

Functions

constructor

Initializes the orderbook upon construction for compatibility with Open Zeppelin upgradeable contracts. Orderbook itself does NOT support factory deployments as each order is a unique expression deployment rather than needing to wrap up expressions with proxies.

constructor(DeployerDiscoverableMetaV3ConstructionConfig memory config)
    DeployerDiscoverableMetaV3(CALLER_META_HASH, config);

vaultBalance

Get the current balance of a vault for a given owner, token and vault ID.

function vaultBalance(address owner, address token, uint256 vaultId) external view override returns (uint256);

Parameters

NameTypeDescription
owneraddressThe owner of the vault.
tokenaddressThe token the vault is for.
vaultIduint256

Returns

NameTypeDescription
<none>uint256balance The current balance of the vault.

orderExists

Returns true if the order exists, false otherwise.

function orderExists(bytes32 orderHash) external view override returns (bool);

Parameters

NameTypeDescription
orderHashbytes32The hash of the order to check.

Returns

NameTypeDescription
<none>boolexists True if the order exists, false otherwise.

deposit

msg.sender deposits tokens according to config. The config specifies the vault to deposit tokens under. Delegated depositing is NOT supported. Depositing DOES NOT mint shares (unlike ERC4626) so the overall vaulted experience is much simpler as there is always a 1:1 relationship between deposited assets and vault balances globally and individually. This mitigates rounding/dust issues, speculative behaviour on derived assets, possible regulatory issues re: whether a vault share is a security, code bloat on the vault, complex mint/deposit/withdraw/redeem 4-way logic, the need for preview functions, etc. etc. At the same time, allowing vault IDs to be specified by the depositor allows much more granular and direct control over token movements within Orderbook than either ERC4626 vault shares or mere contract-level ERC20 allowances can facilitate. Vault IDs are namespaced by the token address so there is no risk of collision between tokens. For example, vault ID 0 for token A is completely different to vault ID 0 for token B. 0 amount deposits are unsupported as underlying token contracts handle 0 value transfers differently and this would be a source of confusion. The order book MUST revert with ZeroDepositAmount if the amount is zero.

function deposit(address token, uint256 vaultId, uint256 amount) external nonReentrant;

Parameters

NameTypeDescription
tokenaddressThe token to deposit.
vaultIduint256The vault ID to deposit under.
amountuint256The amount of tokens to deposit.

withdraw

Allows the sender to withdraw any tokens from their own vaults. If the withrawer has an active flash loan debt denominated in the same token being withdrawn then Orderbook will merely reduce the debt and NOT send the amount of tokens repaid to the flashloan debt. MUST revert if the amount requested to withdraw is zero. The withdrawal MAY still not move any tokens (without revert) if the vault balance is zero, or the withdrawal is used to repay a flash loan, or due to any other internal accounting.

function withdraw(address token, uint256 vaultId, uint256 targetAmount) external nonReentrant;

Parameters

NameTypeDescription
tokenaddressThe token to withdraw.
vaultIduint256The vault ID to withdraw from.
targetAmountuint256The amount of tokens to attempt to withdraw. MAY result in fewer tokens withdrawn if the vault balance is lower than the target amount. MAY NOT be zero, the order book MUST revert with ZeroWithdrawTargetAmount if the amount is zero.

addOrder

Given an order config, deploys the expression and builds the full Order for the config, then records it as an active order. Delegated adding an order is NOT supported. The msg.sender that adds an order is ALWAYS the owner and all resulting vault movements are their own. MUST revert with OrderNoSources if the order has no associated calculation and OrderNoHandleIO if the order has no handle IO entrypoint. The calculation MUST return at least two values from evaluation, the maximum amount and the IO ratio. The handle IO entrypoint SHOULD return zero values from evaluation. Either MAY revert during evaluation on the interpreter, which MUST prevent the order from clearing. MUST revert with OrderNoInputs if the order has no inputs. MUST revert with OrderNoOutputs if the order has no outputs. If the order already exists, the order book MUST NOT change state, which includes not emitting an event. Instead it MUST return false. If the order book modifies state it MUST emit an AddOrder event and return true.

function addOrder(OrderConfigV2 calldata config) external nonReentrant returns (bool stateChanged);

Parameters

NameTypeDescription
configOrderConfigV2All config required to build an Order.

Returns

NameTypeDescription
stateChangedboolTrue if the order was added, false if it already existed.

removeOrder

Order owner can remove their own orders. Delegated order removal is NOT supported and will revert. Removing an order multiple times or removing an order that never existed are valid, the event will be emitted and the transaction will complete with that order hash definitely, redundantly not live.

function removeOrder(OrderV2 calldata order) external nonReentrant returns (bool stateChanged);

Parameters

NameTypeDescription
orderOrderV2The Order data exactly as it was added.

Returns

NameTypeDescription
stateChangedboolTrue if the order was removed, false if it did not exist.

takeOrders

Allows msg.sender to attempt to fill a list of orders in sequence without needing to place their own order and clear them. This works like a market buy but against a specific set of orders. Every order will looped over and calculated individually then filled maximally until the request input is reached for the msg.sender. The msg.sender is responsible for selecting the best orders at the time according to their criteria and MAY specify a maximum IO ratio to guard against an order spiking the ratio beyond what the msg.sender expected and is comfortable with. As orders may be removed and calculate their ratios dynamically, all issues fulfilling an order other than misconfiguration by the msg.sender are no-ops and DO NOT revert the transaction. This allows the msg.sender to optimistically provide a list of orders that they aren't sure will completely fill at a good price, and fallback to more reliable orders further down their list. Misconfiguration such as token mismatches are errors that revert as this is known and static at all times to the msg.sender so MUST be provided correctly. msg.sender MAY specify a minimum input that MUST be reached across all orders in the list, otherwise the transaction will revert, this MAY be set to zero. Exactly like withdraw, if there is an active flash loan for msg.sender they will have their outstanding loan reduced by the final input amount preferentially before sending any tokens. Notably this allows arb bots implemented as flash loan borrowers to connect orders against external liquidity directly by paying back the loan with a takeOrders call and outputting the result of the external trade. Rounding errors always favour the order never the msg.sender.

function takeOrders(TakeOrdersConfigV2 calldata config)
    external
    nonReentrant
    returns (uint256 totalTakerInput, uint256 totalTakerOutput);

Parameters

NameTypeDescription
configTakeOrdersConfigV2The constraints and list of orders to take, orders are processed sequentially in order as provided, there is NO ATTEMPT onchain to predict/filter/sort these orders other than evaluating them as provided. Inputs and outputs are from the perspective of msg.sender except for values specified by the orders themselves which are the from the perspective of that order.

Returns

NameTypeDescription
totalTakerInputuint256totalInput Total tokens sent to msg.sender, taken from order vaults processed.
totalTakerOutputuint256totalOutput Total tokens taken from msg.sender and distributed between vaults.

clear

Allows msg.sender to match two live orders placed earlier by non-interactive parties and claim a bounty in the process. The clearer is free to select any two live orders on the order book for matching and as long as they have compatible tokens, ratios and amounts, the orders will clear. Clearing the orders DOES NOT remove them from the orderbook, they remain live until explicitly removed by their owner. Even if the input vault balances are completely emptied, the orders remain live until removed. This allows order owners to deploy a strategy over a long period of time and periodically top up the input vaults. Clearing two orders from the same owner is disallowed. Any mismatch in the ratios between the two orders will cause either more inputs than there are available outputs (transaction will revert) or less inputs than there are available outputs. In the latter case the excess outputs are given to the msg.sender of clear, to the vaults they specify in the clear config. This not only incentivises "automatic" clear calls for both alice and bob, but incentivises prioritising greater ratio differences with a larger bounty. The second point is important because it implicitly prioritises orders that are further from the current market price, thus putting constant increasing pressure on the entire system the further it drifts from the norm, no matter how esoteric the individual order expressions and sizings might be. All else equal there are several factors that would impact how reliably some order clears relative to the wider market, such as:

  • Bounties are effectively percentages of cleared amounts so larger orders have larger bounties and cover gas costs more easily
  • High gas on the network means that orders are harder to clear profitably so the negative spread of the ratios will need to be larger
  • Complex and stateful expressions cost more gas to evalulate so the negative spread will need to be larger
  • Erratic behavior of the order owner could reduce the willingness of third parties to interact if it could result in wasted gas due to orders suddently being removed before clearance etc.
  • Dynamic and highly volatile words used in the expression could be ignored or low priority by clearers who want to be sure that they can accurately predict the ratios that they include in their clearance
  • Geopolitical issues such as sanctions and regulatory restrictions could cause issues for certain owners and clearers
function clear(
    OrderV2 memory aliceOrder,
    OrderV2 memory bobOrder,
    ClearConfig calldata clearConfig,
    SignedContextV1[] memory aliceSignedContext,
    SignedContextV1[] memory bobSignedContext
) external nonReentrant;

Parameters

NameTypeDescription
aliceOrderOrderV2
bobOrderOrderV2
clearConfigClearConfigAdditional configuration for the clearance such as how to handle the bounty payment for the msg.sender.
aliceSignedContextSignedContextV1[]Optional signed context that is relevant to A.
bobSignedContextSignedContextV1[]Optional signed context that is relevant to B.

calculateOrderIO

Main entrypoint into an order calculates the amount and IO ratio. Both are always treated as 18 decimal fixed point values and then rescaled according to the order's definition of each token's actual fixed point decimals.

function calculateOrderIO(
    OrderV2 memory order,
    uint256 inputIOIndex,
    uint256 outputIOIndex,
    address counterparty,
    SignedContextV1[] memory signedContext
) internal view returns (OrderIOCalculationV2 memory);

Parameters

NameTypeDescription
orderOrderV2The order to evaluate.
inputIOIndexuint256The index of the input token being calculated for.
outputIOIndexuint256The index of the output token being calculated for.
counterpartyaddressThe counterparty of the order as it is currently being cleared against.
signedContextSignedContextV1[]Any signed context provided by the clearer/taker that the order may need for its calculations.

recordVaultIO

Given an order, final input and output amounts and the IO calculation verbatim from _calculateOrderIO, dispatch the handle IO entrypoint if it exists and update the order owner's vault balances.

function recordVaultIO(uint256 input, uint256 output, OrderIOCalculationV2 memory orderIOCalculation) internal;

Parameters

NameTypeDescription
inputuint256The exact token input amount to move into the owner's vault.
outputuint256The exact token output amount to move out of the owner's vault.
orderIOCalculationOrderIOCalculationV2The verbatim order IO calculation returned by _calculateOrderIO.

handleIO

function handleIO(OrderIOCalculationV2 memory orderIOCalculation) internal;

calculateClearStateChange

Calculates the clear state change given both order calculations for order alice and order bob. The input of each is their output multiplied by their IO ratio and the output of each is the smaller of their maximum output and the counterparty IO * max output.

function calculateClearStateChange(
    OrderIOCalculationV2 memory aliceOrderIOCalculation,
    OrderIOCalculationV2 memory bobOrderIOCalculation
) internal pure returns (ClearStateChange memory clearStateChange);

Parameters

NameTypeDescription
aliceOrderIOCalculationOrderIOCalculationV2Order calculation for Alice.
bobOrderIOCalculationOrderIOCalculationV2Order calculation for Bob.

Returns

NameTypeDescription
clearStateChangeClearStateChangeThe clear state change with absolute inputs and outputs for Alice and Bob.

calculateClearStateAlice

function calculateClearStateAlice(
    OrderIOCalculationV2 memory aliceOrderIOCalculation,
    OrderIOCalculationV2 memory bobOrderIOCalculation
) internal pure returns (uint256 aliceInput, uint256 aliceOutput);

_calculateOrderDispatch

function _calculateOrderDispatch(address expression_) internal pure returns (EncodedDispatch);

_handleIODispatch

function _handleIODispatch(address expression_) internal pure returns (EncodedDispatch);