Solvers
Submitting a quote
The candidate format, its signature, and the commit co-signature.
#The candidate
POST /candidates takes one JSON object. Every integer is a canonical decimal string.
{
"batchId": "4247",
"solver": "SoLvEr1111111111111111111111111111111111111",
"solution": {
"priceNumerator": "711",
"priceDenominator": "5000",
"reserveA": "8438818566",
"reserveB": "0",
"surplus": "33566433714",
"surplusScale": "tokenB-at-reference-price",
"policy": "my-solver-v1",
"fills": [
{ "orderId": "9c1e…", "sellAmount": "1200000000", "buyAmount": "8438818565" }
]
},
"signature": "base64…"
}This example quotes a single order: a buy paying 1,200 B with a limit of 143.00, filled at 142.20 for a 9-decimal A and a 6-decimal B.
| Field | Meaning |
|---|---|
batchId | The batch you’re quoting |
solver | Your solver key, base58 |
priceNumerator, priceDenominator | The clearing price, raw B per raw A |
fills[].orderId | The order’s hash, as returned by the collector |
fills[].sellAmount | How much of the order you fill |
fills[].buyAmount | What the order receives: exactly floor(sell × price) |
reserveA, reserveB | What you will escrow at commit; must cover every output, rounded up |
surplus | Your quote’s score, in the units described on Clearing and selection |
policy | A free-form label for how the quote was made |
#Signing
The signed message is the domain tag, the deployment’s identity, then a fixed JSON serialization of the candidate without its signature:
"CHORD_CANDIDATE_V1" ‖ programId (32) ‖ config (32) ‖ chainDomain (32) ‖ JSONThe JSON in the signed message has exactly these keys, in this order: batchId, solver, priceNumerator, priceDenominator, reserveA, reserveB, surplus, surplusScale, policy, fills (each with orderId, sellAmount, buyAmount). Sign it with your solver key’s Ed25519 private key and send the 64-byte signature as base64.
The collector exports the same function it verifies with, candidateMessage, so you can build the bytes exactly as it does.
#When quotes are accepted
| Check | Error |
|---|---|
Signature verifies against solver | BAD_SOLVER_SIGNATURE |
| The batch is known | UNKNOWN_BATCH |
Current slot is in [collectEndSlot, solveEndSlot) | SOLVE_WINDOW_CLOSED |
| Every filled order is open and unchanged on chain | ORDER_INVALIDATED |
| Price, fills, reserves and surplus are consistent | 400 with the reason |
#Co-signing the commit
The commit needs your signature as well as the collector’s. When your quote wins:
GET /batches/:id/solver-signature-requestreturns{ id, wire, lastValidBlockHeight, solver }.wireis the unsigned transaction, base64.- Deserialize it and check it: the commit instruction, your price and budgets, and reserve transfers from your accounts only.
- Sign the message and
POST /transactions/:id/solver-signaturewith{ "signature": "<64 bytes, base64>" }.
The collector verifies your signature against the exact prepared message, adds its own, and broadcasts. If the blockhash expires before you sign, it prepares a fresh request.