Solvers
Run a solver
What a solver does, what it needs, and the loop it runs every batch.
A solver quotes whole batches. It sees every order in a batch once collection closes, proposes one price and the fills that go with it, and if it wins, escrows every output and keeps the net position the batch leaves it with. Market makers, prop AMMs and routers can all solve; anything that can hold inventory and sign a message can compete.
#What you need
| A solver key | An Ed25519 keypair that signs quotes and co-signs commits |
| A bond | SOL deposited to your solver account, at least the config’s bond size |
| A stake | Staked CHORD, at least the minimum set by governance. See Solver stake |
| Inventory | Both tokens of the pair in token accounts owned by your solver key |
| Access | A Solana RPC for the market’s cluster and the collector API |
#Register
Registering creates your solver account and deposits the bond. One bond is locked for every batch you’ve committed and not yet finished, so deposit more than one if you want to win overlapping batches.
import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js";
import { ChordClient } from "@chord/sdk";
const client = new ChordClient(connection);
const register = new Transaction().add(client.registerSolverInstruction(solver.publicKey, 3_000_000_000n));
await sendAndConfirmTransaction(connection, register, [solver]);
// later: add to the bond, or withdraw the part that isn't locked
client.solverFundsInstruction(solver.publicKey, 1_000_000_000n);
client.solverFundsInstruction(solver.publicKey, 1_000_000_000n, true);#The loop
#Watch for batches
Poll GET /market for the current slot and recent batches. Each batch carries its collectEndSlot, solveEndSlot and deadlineSlot.
#Read the orders
Once the processed slot reaches collectEndSlot, fetch GET /batches/:id. Its intents are the signed orders, final for this batch.
#Build and sign a quote
Choose a price, the fills, and the reserves that cover them. The SDK’s referenceSolve is a working starting point, and validateSolution checks your own solution the same way the collector will. Sign the quote with your solver key.
#Submit before the solve window ends
POST /candidates with the signed quote. The collector accepts quotes from collectEndSlot until solveEndSlot and validates each one on arrival.
#Co-sign the commit if you win
When the window closes, the collector prepares the commit transaction. Fetch it from GET /batches/:id/solver-signature-request, check it, sign the exact message, and post the signature to POST /transactions/:id/solver-signature. The collector adds its own signature and sends it.
#Let it settle
The collector’s worker submits every fill against your escrow. When both budgets are filled it finalizes the batch: your bond unlocks and leftover reserves come back to your accounts.
#Managing inventory
Your reserves move into the batch vaults at commit and come back at the end, minus what you sold to traders and plus what they sold to you. Rebalance between batches, never during one: Chord settles only against escrow that is already in the vault.
The SDK includes a direct Raydium CPMM adapter for this. It reads finalized pool state, quotes with the pool’s fees, and builds a single pinned swap_base_input instruction from your own accounts, with bounded slippage.
import { executeRaydiumSwap } from "@chord/sdk";
const route = { network: "mainnet-beta", pool, mintA, mintB } as const;
const signature = await executeRaydiumSwap(
connection,
route,
{ inputAccount, outputAccount, inputMint: mintB, amountIn: 25_000_000_000n, slippageBps: 50 },
solver,
);#Risks you take
- Gross prefunding. You escrow every output, not just the net, so a batch needs more inventory than its net imbalance.
- Token issuers. A frozen account can block a fill or hold your reserves until it thaws.
claim_refundrecovers them afterwards. - Inventory risk. You keep the net position. If the price moves before you rebalance, that’s yours.
See Bonds and slashing for the exact rules.