Skip to main content
Fisherman is the Execution Coordinator. It fans requests out to enabled solvers, validates responses, scores solutions, signs certificates, and notifies the winning solver to settle.

Solver endpoints

Your solver service must expose these HTTP endpoints:
EndpointCalled byPurpose
POST /quoteFishermanReturn a price for one user request.
POST /solveFishermanReturn a settlement plan for a batch of active intents.
POST /revealFishermanReveal solution details (reserved, not required for current flow).
POST /settleFishermanSubmit the winning certificate on-chain.
POST /notifyBothReport won, lost, failed, or settled status.
All endpoints accept application/json and must return JSON. Non-2xx responses are recorded as solver failures.

Registration

Fisherman seeds solver endpoints from SOLVER_URLS. If set to https://solver.example.com, Fisherman calls https://solver.example.com/quote, /solve, etc.
SOLVER_URLS=https://solver-a.example.com,https://solver-b.example.com
SOLVER_TIMEOUT_MS=5000
QUOTE_RESPONSE_WINDOW_MS=1200

Quote endpoint

Request (SolverQuoteRequest)

{
  "requestId": "quote-id",
  "network": "testnet",
  "requestedAtMs": "1780329650000",
  "sellType": "0x2::sui::SUI",
  "buyType": "0x...::wusdc::WUSDC",
  "side": "exactIn",
  "amount": "10000000",
  "partialFillAllowed": true,
  "feeConfig": {
    "volumeFeePpm": 100,
    "correlatedFeePpm": 10,
    "surplusFeePpm": 150000,
    "surplusCapPpm": 1500,
    "maxTotalFeePpm": 2000,
    "solverFeeSharePpm": 500000
  }
}

Response (QuoteResult)

{
  "solver": "0xsolveraddress",
  "requestId": "quote-id",
  "sellType": "0x2::sui::SUI",
  "buyType": "0x...::wusdc::WUSDC",
  "side": "exactIn",
  "sellAmount": "10000000",
  "grossBuyAmount": "8450",
  "estimatedFeeAmount": "8",
  "netBuyAmount": "8442",
  "recommendedMinAmountOut": "8357",
  "priceE9": "845000",
  "validUntilMs": "1780329700000",
  "confidence": "firm",
  "route": { "kind": "deepbook" }
}

Validation

Fisherman rejects a quote if:
  • requestId, sellType, buyType, or side differs from the request
  • configured endpoint address does not match solver
  • estimatedFeeAmount > grossBuyAmount
  • grossBuyAmount - estimatedFeeAmount != netBuyAmount
  • recommendedMinAmountOut > netBuyAmount
  • validUntilMs <= Date.now()
  • side == exactIn and sellAmount != request.amount
  • side == exactOut and netBuyAmount < request.amount

Timeout

Quote timeout per solver is min(endpoint.timeoutMs, QUOTE_RESPONSE_WINDOW_MS). Default timeout is 5000 ms; default window is 1200 ms. Missing the window results in status: "failed".

Solve endpoint

Request (SolveRequest)

{
  "auctionId": "auction-id",
  "sellType": "0x2::sui::SUI",
  "buyType": "0x...::wusdc::WUSDC",
  "epoch": "42",
  "deadlineMs": "1780329700000",
  "maxBatchSize": 10,
  "feeConfig": { ... },
  "intents": [
    {
      "id": "intent-a",
      "owner": "0xowner",
      "sellType": "0x2::sui::SUI",
      "buyType": "0x...::wusdc::WUSDC",
      "sellAmount": "10000000",
      "minAmountOut": "8000",
      "targetEpoch": "42",
      "deadlineMs": "1780329700000",
      "status": "open",
      "createdAt": "2026-06-10T00:00:00.000Z",
      "updatedAt": "2026-06-10T00:00:00.000Z"
    }
  ]
}

Response (SolveResponse)

{
  "solver": "0xsolveraddress",
  "solutionId": "solution-id",
  "sellType": "0x2::sui::SUI",
  "buyType": "0x...::wusdc::WUSDC",
  "epoch": "42",
  "intentIds": ["intent-a"],
  "fills": ["10000000"],
  "grossPayouts": ["8450"],
  "protectedMins": ["8000"],
  "score": "8450",
  "expiresAtMs": "1780329700000",
  "metadata": { "route": "deepbook" }
}

Validation

Fisherman rejects a solution if:
  • no intent is included or length exceeds maxBatchSize
  • token pair or epoch differs from the request
  • parallel arrays have different lengths
  • expiresAtMs <= Date.now()
  • duplicate or unknown intent ids, or intent is not open
  • fill is zero or exceeds the intent sell amount
  • grossPayout is below the protected minimum
  • protected minimum is below intent.minAmountOut
  • score is not a bigint-compatible decimal string

Winner selection

Higher score wins. Ties are broken by lower Fisherman solverEndpointId.

Settle endpoint

Fisherman calls POST /settle on the winning solver with SettleRequest. The solver must:
  1. Verify the certificate belongs to its address and is not expired.
  2. Build a Sui programmable transaction using the certificate epoch.
  3. Submit settlement on-chain.
  4. Return an acknowledgement with the transaction digest.
{
  "accepted": true,
  "txDigest": "0x..."
}
Using the wrong epoch causes EWrongEpoch abort in the contracts.

Notify endpoint

Fisherman sends notification events:
{
  "kind": "won",
  "auctionId": "auction-id",
  "solutionId": "solution-id",
  "certificateId": "certificate-id"
}
Handle notifications idempotently. Losing notifications should release solver-local reservations.

HTTP status guidance

ConditionStatus
Intentional participation200 with valid JSON body
Cannot quote/solve the pairNon-2xx
Degraded or shutting downNon-2xx
Missing liquidityNon-2xx
Returning 200 with invalid data is worse than a clean non-2xx — it is recorded as invalid with a validation error.

Idempotency keys

OperationIdempotency key
QuoterequestId
SolveauctionId
Settlementcertificate.id
Notification(kind, auctionId, solutionId, certificateId)
Do not create duplicate on-chain settlement transactions for the same certificate.

Required checklist

  • Implement POST /quote, /solve, /settle, /notify
  • Use decimal strings for all u64 values
  • Preserve requestId exactly and echo token pair and side
  • Enforce quote expiry and user floors in solve responses
  • Use a deterministic solutionId
  • Return within QUOTE_RESPONSE_WINDOW_MS
  • Add structured logs for requestId, auctionId, solutionId, and certificate.id

Join as a solver

Integration path and requirements.

Solver settlement

On-chain execution after winning.