Skip to content

Repository files navigation

BYOS Service

The off-chain service for Bring Your Own Solver (BYOS): a bonded CoW Protocol solver that sources settlement routes from permissionless external sub-solvers. Sub-solvers submit EIP-712-signed routing proposals against specific order UIDs, collateralized by an on-chain escrow; BYOS scores them, feeds the best into CoW's solver competition through a standard driver, and settles winners through per-sub-solver Trampoline contracts.

Built under a CoW DAO grant answering the BYOS RFP. The on-chain half (Escrow, Trampoline, TrampolineFactory) lives in bleu/byos-contracts.

Status: in progress — the proposal API, scoring/solve engine, background validation, proposal lifecycle and retention, audit trail, escrow operator with Track A debits, and reference sub-solver are implemented; the e2e harness is not. The chain watcher is not needed: settlement outcomes come from the driver's notifications (ADR-0010).

Crates

Crate Description Status
byos The BYOS service: public proposal API + CoW solver engine, one process, two listeners in progress
byos-common Shared contract bindings, EIP-712 schema, and Trampoline calldata encoding in progress
subsolver Reference sub-solver: example proposal-API client, also the e2e-test counterpart implemented
e2e End-to-end tests, two tiers: in-process against plain anvil, and full CoW stack via offline-mode tier-1 chain fixture

Architecture

Sub-solvers discover orders from the public CoW orderbook, compute routes, and POST signed proposals to the public listener. Ingestion verifies the signature and stores the proposal; a background loop then runs the escrow check and the settlement simulation and moves it to active or rejects it. The CoW driver calls /solve on the internal listener; the engine returns the single best proposal per order UID, wrapped in one Trampoline execute call, and the driver competes with it as a normal solver. Background loops re-simulate standing proposals, take settlement outcomes from the driver's /notify, debit escrow on attributable reverts, and sweep terminal proposals past their retention window.

Start with CONTEXT.md for the domain language, then the ADRs:

CoW protocol background (solver auctions, slashing policy, CIPs) is captured in docs/reference/, SLO targets in docs/metrics-reasoning.md.

Development

Prerequisites: stable Rust (via rustup; rust-toolchain.toml pins the channel), a nightly toolchain for rustfmt, just, and cargo-nextest. E2e tests additionally need Foundry's anvil and the offline-mode submodule (git submodule update --init), whose committed anvil-state.json is the tier-1 chain fixture. Running the service (and the DB-backed tests) needs Postgres — docker compose up -d postgres provides one. In production, pass the connection string via the DATABASE_URL env var rather than --database-url: CLI arguments are visible to other users on the host via ps.

just build          # cargo build --workspace
just test-unit      # cargo nextest run
just test-db        # service-level tests against the compose Postgres
just test-e2e       # e2e tier 1: in-process against plain anvil
just test-e2e-full  # e2e tier 2: against a running offline-mode stack
just clippy         # -D warnings, all features and targets
just lint-openapi   # validate + lint crates/byos/openapi.yml (needs node)
just fmt            # cargo +nightly fmt (never stable fmt)

The public proposal API is specified in crates/byos/openapi.yml. Nothing serves it — render it locally with npx @redocly/cli build-docs crates/byos/openapi.yml, which writes a self-contained redoc-static.html. That is the same split cowprotocol/services uses: the spec sits next to the crate, CI validates and lints it, and the rendered docs live outside the repo. The internal listener's /solve and /notify are deliberately absent from it — they implement CoW's own solver-engine spec.

Running byos by hand

just byos-local in one shell, just propose in another. The service boots against the compose Postgres with no chain, so validation is AcceptAll; the helper signs a proposal, submits it, and polls until the background validator flips the status. Watching it go from submitted to active is the point — that is the checkpoint that says the service itself is fine when a full stack around it misbehaves. Both recipes share the chain id, factory, key and ports as just variables, so the two commands cannot disagree on the EIP-712 domain; the signer is anvil account 4.

Three things that look like bugs and are not. A 202 followed by a 404 means the domain disagreement above happened anyway (you overrode a flag): recover_proposer only errors on a malformed signature, so a chain-id or factory mismatch recovers a different address instead of failing, the POST succeeds, and the owner-scoped read 404s the id it just handed you (ADR-0011's anti-existence-oracle rule). active means the validation loop is running, not that anything was checked. And do not copy validUntil from the test fixtures — they all use 1750000000, June 2025, which ingestion rejects as already expired.

Full-stack e2e uses cowdao-grants/offline-mode — the real CoW orderbook/autopilot/driver on a local anvil — with BYOS plugged in as a competing solver. See ADR-0009 for the two-tier design and the shared chain fixture.

License

GPL-3.0-or-later.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages