chordDocs

Protocol

Settlement and escrow

Gross prefunding, atomic fills, and why a fill can’t happen twice.

Settlement is where Chord’s guarantees live. Everything here is enforced by the program, independent of the collector.

#Commit

commit_batch turns a winning quote into on-chain state. It needs two signatures, the collector’s auctioneer key and the winning solver, and it only runs after the solve window ends, while at least half of the settlement window is still left for fills.

In one transaction it:

  1. writes the price and the sell budgets for A and B to the batch account;
  2. moves the solver’s reserves into the batch vaults, checking they cover every gross output the budgets imply, rounded up:
Text
reserve A ≥ ceil(budget B × denominator / numerator)
reserve B ≥ ceil(budget A × numerator / denominator)
  1. locks one bond from the solver’s account against the batch.

Committing to gross outputs, not just the net, means no trader’s output depends on another trader’s order arriving, or on a trade the solver still has to make.

#Fill

settle_fill(intent, fill_amount, expected_sold) fills one order. Only the collector’s auctioneer key can send it, so the only fills a batch takes are the ones in its winning quote. The instruction just before it must be an Ed25519 verification of the order’s digest by its owner. The program checks that verification’s exact layout, then in a single instruction:

  1. confirms the order matches the batch, its pair, its token accounts and its owner;
  2. checks the order’s receipt: not cancelled, not bound to another order, and its cumulative sold amount equal to expected_sold;
  3. computes your output at the committed price and checks everything you’ve received against your minimum;
  4. transfers your input from your token account to the batch vault, using the allowance;
  5. transfers your output from the vault to your destination account;
  6. updates the receipt and the batch’s budgets.

If either transfer fails, the whole instruction fails and nothing moves.

#Finish

InstructionWhenEffect
finalize_batchBoth sell budgets are fully filledUnlocks the bond and returns leftover reserves to the solver
timeout_batchAfter the deadline, with budget unfilledSlashes the locked bond to the treasury and returns the reserves
expire_uncommittedAfter the deadline, never committedCloses the batch with nothing to slash
claim_refundA terminal batch still holding reservesRetries the reserve refund after a frozen account thaws

All four are permissionless: anyone can call them once the condition holds, so a batch can always reach its end, even if the collector stops.

#Who sends what

The collector’s worker submits commits and fills and pays their fees. It records each signed transaction before it broadcasts it, and only treats a transaction as done once it is finalized. After a restart it rebroadcasts the same bytes instead of signing a new transaction, so a fill is never sent in two different versions.