For solvers¶
A solver is an off-chain agent that quotes prices and signs commitments. You decide your own pricing model, your own risk limits, and how much capital to post; the protocol's job is to enforce the rules of the trade, not to dictate strategy.
This page covers what a solver does, how to get registered, and the wire protocol you'll need to implement. For the formal model, see Protocol reference.
What a solver does¶
In one sentence: stay connected to the orchestrator, answer quote requests with a signed commitment, post collateral on-chain to back your quotes.
In more detail:
- Maintain a persistent WebSocket connection to the orchestrator.
- For each incoming quote request, decide whether to quote and at what price. Respond within the orchestrator's listening window (sub-second).
- Sign each commitment with the private key registered against your
solverIdon-chain. - Deposit USDC collateral into the market contract; track your own free capital so you only quote what you can back. The contract enforces this as a hard limit, but if you over-commit the user's transaction reverts and you're useless to them.
- Decide your own risk envelope: which ranges, which maturities, which spot-distance limits you're willing to quote.
You compete with other solvers on price. The orchestrator runs a min-price-wins auction with a short listening window; whoever responds first with the best quote takes the trade.
Getting registered¶
Registration is gated by an admin multisig today (this is a centralization point we plan to relax). The flow:
- Generate a fresh signing keypair (this signs commitments; do not reuse a wallet key) and a fresh owner address (this controls your collateral; typically a multisig or hot wallet you trust).
- Talk to the team. We'll review your setup and, on approval, call
registerSolver(signer, owner)on the market contract. - You receive a
solverId(auint32, small integer). This is how the protocol identifies you. - The team also adds your signer address to the orchestrator's allowlist so you can complete the WebSocket handshake. The on-chain registry and the orchestrator's allowlist are separate — both have to be configured.
- Deposit USDC collateral via
depositCollateral(solverId, amount). Anyone can deposit on your behalf, so this can come from your owner or a designated treasurer.
You can be paused (no new commitments accepted; existing positions still resolve normally) or fully removed (retired permanently; existing positions held to maturity, free collateral still withdrawable).
The WebSocket protocol¶
Frames are JSON text frames. There is no envelope type discriminator — message types are identified by a top-level type (handshake messages) or command (quote requests) field, and your responses are identified by direction.
Handshake (EIP-191 challenge / response)¶
When you connect:
- Orchestrator → you:
{"type": "auth_challenge", "nonce": "0x<32 random bytes hex>"} - You → orchestrator:
{"type": "auth_response", "signature": "0x<65-byte EIP-191 sig>"} - Orchestrator → you:
{"type": "auth_ok", "address": "0x..."}on success, or{"type": "auth_reject", "reason": "..."}.
The message you sign is the literal string:
…hashed with the standard EIP-191 personal-sign prefix (\x19Ethereum Signed Message:\n<len>...). The 65-byte signature can use either raw v ∈ {0,1} or personal-sign v ∈ {27,28} — the orchestrator normalizes both.
The handshake must complete within 5 seconds of connection.
Liveness¶
The orchestrator sends a WebSocket PING every 20 seconds. You must respond with a PONG within 60 seconds or the connection is closed (ClosePolicyViolation). Standard gorilla/websocket-style pong handlers work out of the box.
Quote requests¶
Once authenticated, you receive command messages from the orchestrator:
There are four commands:
| Command | Payload | What you return |
|---|---|---|
position_open_quote |
asset, lower_bound_ticks, upper_bound_ticks, maturity_hour | price only |
position_open |
the open-quote payload + buyer address + quantity | price + packed commitment + signature |
position_close_quote |
asset, solver_id, token_id | price only |
position_close |
the close-quote payload + user address + quantity | price + packed commitment + signature |
The _quote variants are price-only; the orchestrator uses them when the UI is just displaying a quote indicatively. The full variants are sent when the user is ready to submit a transaction — you only sign at that point.
Your response¶
{
"request_id": "<echoed>",
"solver_name": "<your name>",
"solver_id": <your registry id>,
"price": <float>,
"commitment_packed": "0x<hex>", // absent on _quote variants
"commitment_signature": "0x<hex>", // absent on _quote variants
"error": "<reason>" // set when declining
}
If you can't quote (request outside your envelope, insufficient capital, malformed input), set error and omit price. The orchestrator simply ignores you for that request.
Commitments¶
A commitment is the EIP-712-signed message that the user submits on-chain alongside their transaction. Two types: BuyCommitment and BuybackCommitment. Schemas and the packed wire format (140 bytes / 160 bytes) are documented on the API docs site; a reference codec lives in contracts/src/Commitments.sol with round-trip tests you can vendor.
Key invariants:
- The price is part of the signed payload. There is no separate price field on the transaction.
- A
BuyCommitmentbinds to a specific buyer address; the smart contract enforcesmsg.sender == buyeronbuy. This means you can quote freely without worrying about commitments being captured by anyone but the intended buyer. - A
BuybackCommitmentsimilarly binds to the user (the seller). - Each commitment carries a nonce that can only be used once per solver. Reuse reverts.
- Each commitment has an expiry timestamp — typically very short (~1 minute). The contract rejects expired commitments.
You're signing a binding offer. Make sure your free capital is reserved before you sign.
Collateral mechanics¶
You have three tracked numbers in the contract:
balance— total USDC currently deposited under your solver ID.frozen— sum of collateral currently backing your open positions. Frozen amount equalsquantityper open position (each unit needs $1 to back its potential payout).costBasis— cumulative deposits minus cumulative principal withdrawals.
Your free capital is balance - frozen. The contract enforces that you can never issue a commitment whose required new collateral exceeds free capital — but again, it does so by reverting the user's transaction, which is bad UX. Self-track so you only sign quotes you can cover.
Withdrawals¶
withdrawCollateral(solverId, amount, to) is only callable by your owner address. The withdrawal is split:
- Fee-free portion up to your remaining principal (
costBasis - principalWithdrawn). Reduces principal 1:1. - Profit portion above that. Subject to a 20% protocol fee (80% to you, 20% to the treasury).
You can withdraw profit even while some collateral is frozen — the frozen amount already covers maximum loss exposure on open positions.
When positions resolve¶
| Event | Effect on your accounts |
|---|---|
You issue a position (buy) |
balance += premium, frozen += quantity |
You buy back your own issue (buyback, isIssuer) |
balance -= amountPaid, frozen -= released collateral |
| A different solver buys you back | Your balances unchanged (you're not a party). The new solver's balance -= amountPaid; their frozen is unchanged because the original collateral still backs the position. |
| Settle, user wins | balance -= payout, frozen -= payout |
| Settle, user loses | frozen -= released, balance unchanged (the premium is realized P&L) |
| Emergency-cancel | frozen -= released (no balance change) |
What you decide vs. what the protocol enforces¶
| You decide | Protocol enforces |
|---|---|
| Pricing model | EIP-712 signature validity, nonce uniqueness, expiry |
| Which assets / ranges / maturities to quote | msg.sender == buyer on buy, user on buyback |
| Spot-distance and inventory limits | Sufficient free collateral at submission time |
| Spread / margin | Per-buy() ETH openFee |
| Whether to participate in a given request at all | Settlement payout routing, premium escrow |
You can be entirely arbitrary about which requests you respond to. Quoting outside your declared envelope or with thin/no margin is your business; the on-chain rules are the only hard backstop.
Next steps
- API surface (REST + WebSocket schemas): api.docs.wenrange.com.
- Full protocol model: Protocol reference.
- For background on what users see: For traders.