Skip to main content

Overview

Matches a taker order against an array of maker orders. This function enables sophisticated order matching including complementary fills (buy vs sell), minting (two buys), and merging (two sells). The exchange acts as an intermediary to facilitate complex trades.
function matchOrders(
    Order memory takerOrder,
    Order[] memory makerOrders,
    uint256 takerFillAmount,
    uint256[] memory makerFillAmounts
) external nonReentrant onlyOperator notPaused

Parameters

takerOrder
Order
required
The active order to be matched (taker side). See Order Structure for details.This order is filled using the exchange as the counterparty, with the exchange then filling the maker orders to satisfy the taker.
makerOrders
Order[]
required
Array of passive orders to be matched against the taker order.Requirements:
  • Each order must be compatible with the taker order
  • For complementary matches: must have the same token ID but opposite sides
  • For mint matches (two buys): must be for complementary tokens
  • For merge matches (two sells): must be for complementary tokens
  • Orders must be crossing (prices must allow profitable execution)
takerFillAmount
uint256
required
The amount to fill on the taker order, denominated in terms of the taker’s maker amount.This represents how much of the taker order to execute in this match.
makerFillAmounts
uint256[]
required
Array of amounts to fill for each maker order, denominated in terms of each maker’s maker amount.Requirements:
  • Array length must match the makerOrders array length
  • Total fill amounts must be sufficient to satisfy the taker order
  • Each amount must not exceed the corresponding order’s remaining amount

Requirements

  • Caller must be an authorized operator (onlyOperator)
  • Trading must not be paused (notPaused)
  • Function must not be re-entered (nonReentrant)
  • Taker order must pass all validation checks
  • Each maker order must pass all validation checks
  • Orders must be crossing (economically viable to match)
  • Token IDs must be compatible based on match type
  • Sufficient balances must exist for all asset transfers

Match Types

The function automatically determines the match type based on the order sides:

Complementary (Buy vs Sell)

Matches a buy order with a sell order for the same token. Example: YES buy at 60¢ matched with YES sell at 50¢
  • No CTF operations needed
  • Direct asset transfers between parties
  • Token IDs must match

Mint (Two Buys)

Matches two buy orders for complementary tokens by minting new outcome tokens. Example: YES buy matched with NO buy
  • Exchange mints new outcome token pairs using collateral
  • Requires complementary token IDs
  • Buys must “cross” (priceYes + priceNo > 1)

Merge (Two Sells)

Matches two sell orders for complementary tokens by merging outcome tokens into collateral. Example: YES sell matched with NO sell
  • Exchange merges outcome tokens into collateral
  • Requires complementary token IDs
  • Sells must “cross” (priceYes + priceNo < 1)

Behavior

Execution Flow

  1. Taker Order Setup:
    • Validate taker order
    • Calculate required taking amount
    • Transfer taker’s maker asset to the exchange
  2. Maker Order Processing:
    • For each maker order:
      • Validate the order
      • Determine match type (complementary, mint, or merge)
      • Validate orders are crossing
      • Execute CTF operations if needed (mint/merge)
      • Transfer assets between maker and exchange
      • Charge fees to maker
  3. Taker Order Settlement:
    • Calculate actual taker proceeds (may include price improvement)
    • Transfer proceeds to taker (minus fees)
    • Charge fees to taker
    • Refund any unused maker assets to taker

Price Improvement

If maker orders provide better pricing than the taker order’s limit price, the taker receives the surplus. Fees are calculated on the actual fill price, including any price improvement.

Fee Collection

Fees are charged to both taker and maker(s) based on their respective feeRateBps. The operator receives all fees explicitly through transfers.

Events

OrderFilled
event
Emitted for the taker order and each maker order that is filled.
event OrderFilled(
    bytes32 indexed orderHash,
    address indexed maker,
    address indexed taker,
    uint256 makerAssetId,
    uint256 takerAssetId,
    uint256 makerAmountFilled,
    uint256 takerAmountFilled,
    uint256 fee
)
  • For taker order: taker is address(this) (the exchange)
  • For maker orders: taker is the taker order’s maker
OrdersMatched
event
Emitted once after all orders are successfully matched.
event OrdersMatched(
    bytes32 indexed takerOrderHash,
    address indexed takerOrderMaker,
    uint256 makerAssetId,
    uint256 takerAssetId,
    uint256 makerAmountFilled,
    uint256 takerAmountFilled
)
  • takerOrderHash: Hash of the taker order
  • takerOrderMaker: Maker of the taker order
  • makerAssetId: Token ID of asset provided by taker
  • takerAssetId: Token ID of asset received by taker
  • makerAmountFilled: Amount provided by taker
  • takerAmountFilled: Amount received by taker
FeeCharged
event
Emitted for each fee charged to taker and maker(s).
event FeeCharged(
    address indexed receiver,
    uint256 indexed tokenId,
    uint256 amount
)

Errors

NotOperator
error
Caller is not an authorized operator
Paused
error
Trading is currently paused
NotCrossing
error
Orders do not cross (prices don’t allow profitable execution)
  • For complementary: buy price must be >= sell price
  • For mint: buy prices must sum to > 1
  • For merge: sell prices must sum to < 1
MismatchedTokenIds
error
For complementary matches, taker and maker token IDs don’t match
TooLittleTokensReceived
error
After executing match operations, insufficient tokens were received to fill orders
OrderExpired
error
One or more orders have passed their expiration timestamp
OrderFilledOrCancelled
error
One or more orders have already been fully filled or cancelled
InvalidSignature
error
One or more order signatures are invalid
InvalidNonce
error
One or more order nonces are invalid
MakingGtRemaining
error
One or more fill amounts exceed the remaining unfilled amount
NotTaker
error
Taker order specifies a specific taker and caller is not that address

Example Usage

Complementary Match (Buy vs Sell)

// Taker: BUY YES at 60c
Order memory takerBuy = Order({
    salt: 1,
    maker: bob,
    signer: bob,
    taker: address(0),
    tokenId: yesTokenId,
    makerAmount: 60_000_000,   // 60 USDC
    takerAmount: 100_000_000,  // 100 YES tokens
    expiration: 0,
    nonce: 0,
    feeRateBps: 100,
    side: Side.BUY,
    signatureType: SignatureType.EOA,
    signature: bobSignature
});

// Maker: SELL YES at 50c
Order memory makerSell = Order({
    salt: 2,
    maker: carla,
    signer: carla,
    taker: address(0),
    tokenId: yesTokenId,
    makerAmount: 100_000_000,  // 100 YES tokens
    takerAmount: 50_000_000,   // 50 USDC
    expiration: 0,
    nonce: 0,
    feeRateBps: 100,
    side: Side.SELL,
    signatureType: SignatureType.EOA,
    signature: carlaSignature
});

Order[] memory makerOrders = new Order[](1);
makerOrders[0] = makerSell;

uint256[] memory makerFillAmounts = new uint256[](1);
makerFillAmounts[0] = 100_000_000;  // Fill maker completely

// Match the orders
exchange.matchOrders(
    takerBuy,
    makerOrders,
    60_000_000,  // Fill 60 USDC worth from taker
    makerFillAmounts
);

// Result:
// - Bob pays 60 USDC, receives 100 YES tokens (minus fee)
// - Carla receives 50 USDC (minus fee), provides 100 YES tokens
// - Bob gets price improvement: paid 60¢ but could have paid up to 60¢

Mint Match (Two Buys)

// Taker: BUY YES at 60c
Order memory yesBuy = Order({
    // ... YES buy order at 60c
    tokenId: yesTokenId,
    makerAmount: 60_000_000,
    takerAmount: 100_000_000,
    side: Side.BUY
});

// Maker: BUY NO at 50c
Order memory noBuy = Order({
    // ... NO buy order at 50c
    tokenId: noTokenId,
    makerAmount: 50_000_000,
    takerAmount: 100_000_000,
    side: Side.BUY
});

Order[] memory makerOrders = new Order[](1);
makerOrders[0] = noBuy;

uint256[] memory makerFillAmounts = new uint256[](1);
makerFillAmounts[0] = 50_000_000;

// Match the orders (exchange will mint new tokens)
exchange.matchOrders(
    yesBuy,
    makerOrders,
    60_000_000,
    makerFillAmounts
);

// Result:
// - Exchange mints 100 YES and 100 NO tokens from 100 USDC
// - YES buyer receives YES tokens
// - NO buyer receives NO tokens
// - Orders cross: 60c + 50c = 110c > 100c ✓

Merge Match (Two Sells)

// Taker: SELL YES at 40c
Order memory yesSell = Order({
    // ... YES sell order at 40c
    tokenId: yesTokenId,
    makerAmount: 100_000_000,
    takerAmount: 40_000_000,
    side: Side.SELL
});

// Maker: SELL NO at 50c
Order memory noSell = Order({
    // ... NO sell order at 50c
    tokenId: noTokenId,
    makerAmount: 100_000_000,
    takerAmount: 50_000_000,
    side: Side.SELL
});

Order[] memory makerOrders = new Order[](1);
makerOrders[0] = noSell;

uint256[] memory makerFillAmounts = new uint256[](1);
makerFillAmounts[0] = 100_000_000;

// Match the orders (exchange will merge tokens)
exchange.matchOrders(
    yesSell,
    makerOrders,
    100_000_000,
    makerFillAmounts
);

// Result:
// - Exchange merges 100 YES + 100 NO into 100 USDC
// - YES seller receives USDC
// - NO seller receives USDC
// - Orders cross: 40c + 50c = 90c < 100c ✓

See Also