Match Types
Most order books have one kind of match: a buyer meets a seller. GoMarket has three, because outcome tokens can be created and destroyed at par.
The three types
Section titled “The three types”| Type | The two orders | Mechanism |
|---|---|---|
| COMPLEMENTARY | Opposite sides of the same token | Direct swap |
| MINT | Both BUYs, opposite outcomes | Create a YES+NO pair from their collateral |
| MERGE | Both SELLs, opposite outcomes | Destroy a YES+NO pair back into collateral |
COMPLEMENTARY
Section titled “COMPLEMENTARY”The familiar case. One trader buys YES, another sells YES.
Taker BUY 100 YES @ 0.50 Maker SELL 100 YES @ 0.50 │ │ │ 50 USDC ──────────────────────▶ │ │ ◀────────────────────── 100 YES │Tokens change hands directly. Nothing is created or destroyed, and the market vault is untouched.
MINT — two buyers
Section titled “MINT — two buyers”Two traders both want to buy, on opposite outcomes. Neither can sell to the other — but together they’ve put up a full dollar per share.
Taker BUY 100 YES @ 0.60 Maker BUY 100 NO @ 0.40 │ │ │ 60 USDC 40 USDC │ └──────────┐ ┌────────────┘ ▼ ▼ ┌─────────────────────┐ │ market vault │ 100 USDC in │ mint_tokens │ 100 YES + 100 NO out └─────┬─────────┬─────┘ │ │ 100 YES▼ ▼100 NO Taker MakerThe prices must sum to $1 — exactly the collateral needed to mint the pair. Both traders get the side they wanted; the vault ends fully collateralised.
This is how a market bootstraps. Nobody needs to hold inventory to create two-sided liquidity. The first two participants in a brand-new market can be two buyers.
MERGE — two sellers
Section titled “MERGE — two sellers”The mirror image. Two traders both want to sell, on opposite outcomes.
Taker SELL 100 YES @ 0.60 Maker SELL 100 NO @ 0.40 │ │ │ 100 YES 100 NO │ └──────────┐ ┌───────────┘ ▼ ▼ ┌─────────────────────┐ │ market vault │ 100 YES + 100 NO in │ merge_tokens │ 100 USDC out └─────┬─────────┬─────┘ │ │ 60 USDC▼ ▼40 USDC Taker MakerBoth exit their positions in one transaction, against each other, without needing a buyer at all.
How they settle
Section titled “How they settle”All three settle through the same instruction. execute_trade handles one
maker leg of a match, and the maker leg is identical in every case — a
direct swap between maker and taker.
The difference is the residual. In a MINT or MERGE the taker is left holding something that must be converted:
COMPLEMENTARY [ execute_trade ]
MINT [ mint_tokens ] [ execute_trade ] [ execute_trade ] …MERGE [ merge_tokens ] [ execute_trade ] [ execute_trade ] …The executor prepends the conversion instruction in the same transaction, so the whole sequence is atomic. Either the pair is created and both legs settle, or nothing happens.
Two practical consequences:
- MINT/MERGE transactions carry more accounts and overflow Solana’s
1232-byte legacy message limit. They compile to v0 messages with an
address lookup table when
ADDRESS_LOOKUP_TABLEis configured. - Mint and merge conversions are not counted as volume in the market’s
total_volumeaccumulator. Only settled fills are. → CLOB Markets as Legs
Turning it off
Section titled “Turning it off”Same-side matching is on by default:
CLOB_ENABLE_MINT_MERGE_MATCHING=1 # defaultCLOB_ENABLE_MINT_MERGE_MATCHING=0 # complementary onlyWith it disabled, takers match only against complementary makers. Market
makers who need to mint or merge directly then use the Market Makers API
(packages/mkt-makers-api), which exposes MINT/MERGE as a REST operation.
That service is only meaningful in this configuration.
What a taker sees
Section titled “What a taker sees”The match type is an implementation detail of how the fill was sourced — you don’t request one. You post an order; the engine walks the book and uses whichever mechanism each maker leg requires.
A single taker order can produce a mix: some legs complementary, some minted.
The resulting TradeSummary lists every maker leg in maker_orders, each
with its own matched_amount and price.
Matching rules that still apply
Section titled “Matching rules that still apply”Regardless of type:
- Price-time priority — best price first, earliest first at a price, order-ID as the final tiebreak.
- Self-trade filter — your own resting liquidity is excluded from the walk.
- Price-distortion guard — the walk stops if the fill price drifts past a
configured bound from the reference (
0.02in the demo config), so a market order can’t sweep a thin book to an absurd level. - Expiry check — expired makers are skipped and ejected rather than filled.