Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
cow-rs
The Rust SDK for the CoW Protocol.
cow-rs is the idiomatic Rust client for trading on CoW Protocol:
build and sign orders, talk to the orderbook, decode on-chain
settlement events. Built on alloy;
ports the canonical types from
cowprotocol/services; locks
every protocol-critical path byte-for-byte against
@cowprotocol/cow-sdk,
cowdao-grants/cow-py and
ethers.
At a glance
- Full order lifecycle: quote, sign, submit, look up, cancel.
- All four signing schemes: EIP-712, EthSign, EIP-1271, pre-sign.
- All eleven chains: Mainnet, BNB, Gnosis, Polygon, Base, Plasma, Arbitrum One, Avalanche, Ink, Linea, Sepolia, plus their barn staging endpoints where the orderbook team publishes them.
- Conformance-locked: 219 native tests (180 lib + 26 wiremock + 5
schema-drift + 3 source-lock + 1 trading-mock + 4 doctests) plus 12
headless-Firefox wasm-bindgen cases, with byte-exact goldens
cross-checked against
cowprotocol/services,cowprotocol/contracts, ethers, cow-sdk and cow-py. - Hostile-orderbook hardened: every quote response is bound to the
originating
QuoteRequestbefore the SDK produces signable bytes, app-data digests round-trip on get/put, fee-math fails closed viachecked_*(no saturation), andEthFlowOrder::receiveris non-zero by construction. - Sync core, async client: hashing, signing and contract decoding are pure-compute and need no runtime; the HTTP client is async-tokio and the only piece that depends on one.
- WASM-ready: compiles cleanly to
wasm32-unknown-unknownand has an in-browser end-to-end harness (seetest-harness/) that exercises the live orderbook from the browser; the poll helper is runtime-agnostic so you can drop ingloo_timers::future::sleep.
Install
[]
= "1.0.0-alpha.1"
The crate is published as cowprotocol on crates.io (the cow-rs name was already taken on
crates.io by an unrelated publisher before this SDK existed); the source lives at
cowdao-grants/cow-rs.
MSRV 1.91, edition 2024.
Quick start: quote, sign, submit
use ;
use ;
use PrivateKeySigner;
# async
See examples/post_order.rs
for the same flow on Sepolia, runnable with a private key in the
environment.
No-async core
Every protocol-critical primitive is synchronous and runtime-free:
OrderData::hash_struct, OrderData::uid, EcdsaSignature::sign,
Signature::recover, DomainSeparator::new, the sol!-generated
contract bindings. You can use cow-rs in a Postgres extension
(pgrx), an FFI shim,
an embedded context, or anywhere else a tokio reactor is hostile,
without pulling in reqwest or tokio.
use ;
use ;
let order = new
.sell_amount
.buy_amount
.valid_to
.kind
.build;
let domain = new;
let owner = address!;
let uid = order.uid;
assert_eq!;
Modules
| Module | What it exposes |
|---|---|
order |
OrderData (12-field signed payload), OrderBuilder, OrderUid, OrderKind, SellTokenSource, BuyTokenDestination, BUY_ETH_ADDRESS, plus the full GET-orders Order, OrderStatus, OrderClass |
order_book |
OrderBookApi with quote / submit / lookup / status / cancel, trades, native price, account orders, app-data PUT / GET (with digest round-trip), version, total surplus; the runtime-agnostic poll_until helper and the tokio-bound wait_for_order_fulfilled convenience |
trading |
TradingClient::post_swap_order, the one-call quote → bind → sign → put-app-data → submit facade. Mirrors TradingSdk.postSwapOrder in @cowprotocol/cow-sdk |
quote_amounts |
compute() (the partner-fee + protocol-fee + slippage composition the TS SDK uses, byte-for-byte against cow-sdk PR #867); fail-closed via Error::QuoteFeeMathOverflow { stage } on every intermediate |
signature |
Signature (all four schemes), EcdsaSignature, Recovered, SignatureError |
domain |
DomainSeparator, hashed_eip712_message, hashed_ethsign_message |
chain |
Chain (eleven networks) with orderbook_base_url, orderbook_barn_url, settlement, vault_relayer, subgraph_studio_url |
cancellation |
OrderCancellation (single), OrderCancellations (collection), SignedOrderCancellations |
app_data |
AppDataHash, AppDataDoc (canonical JSON + keccak digest), AppDataCid (IPFS CIDv1 derivation), AppDataDoc::sdk_attribution for the SDK's appCode tag |
eth_flow |
EthFlowOrder (non-zero receiver enforced at construction), ETH_FLOW_PRODUCTION, ETH_FLOW_STAGING |
composable |
ConditionalOrderParams, Proof, PollOutcome, ComposableCoW events, TwapData + TwapStaticInput, plus deployment addresses |
multiplexer |
OZ-style commutative double-hashed merkle leaves, watch-tower-side proof verification |
contracts |
GPv2Settlement (settle + events), CoWSwapOnchainOrders (ETH-flow events), ERC20, WETH9, GPV2_SETTLEMENT, GPV2_VAULT_RELAYER |
subgraph |
SubgraphClient typed access to CoW's subgraph; totals, daily / hourly volume; opt-in bearer-token auth for the gateway URL |
Everything is re-exported at the crate root: use cowprotocol::....
WASM and JavaScript
cow-rs targets wasm32-unknown-unknown:
- Reqwest's browser fetch backend kicks in automatically on wasm.
OrderBookApi::poll_untilis runtime-agnostic; pair it withgloo_timers::future::sleepinstead oftokio::time::sleep.wait_for_order_fulfilled(the tokio-bound convenience) is non-wasm only.- CI gates
cargo check --target wasm32-unknown-unknownon every push. crates/cow-sdk-wasm/ships a#[wasm_bindgen]shim published to npm as@cowdao-grants/cow-sdk-wasm;test-harness/index.htmlexercises it end-to-end against the live orderbook from a real browser. Run withjust wasm-harness.
JavaScript quick-start
Two signing flows. Pick one.
In-shim signing (tests, scripts, fast iteration): the wasm crate
holds the private key and signs inside linear memory. Requires the
in_shim_signing cargo feature at build time (off by default).
import init from '@cowdao-grants/cow-sdk-wasm';
await ;
// 1. Quote (network).
const = await ;
// 2. Sign in-shim.
const sig = ;
// 3. Submit (network).
const creation = ;
const uid = await ;
console.log;
External signing (production, Safe / WalletConnect / browser
wallets): the wasm crate never sees the private key. The shim's
eip712_payload(orderData, chain) returns a ready-to-use
{ domain, primaryType, types, message } object — the exact shape
both viem and ethers's signTypedData accept. Works against the
default (no-feature) build.
import init from '@cowdao-grants/cow-sdk-wasm';
await ;
const = await ;
const payload = ;
Hand payload to whichever wallet you have. Split the resulting
65-byte signature into (r, s, v) and feed it back through
build_order_creation.
// viem
import from 'viem';
const signatureHex = await walletClient.;
// ethers v6
const signatureHex = await ethersSigner.;
// raw EIP-1193 (window.ethereum, WalletConnect, Safe SDK): viem and
// ethers both throw if `types` contains an `EIP712Domain` entry, so
// the shim deliberately omits it. The raw `eth_signTypedData_v4` RPC
// needs it, so inject before stringifying:
const v4 = ;
const signatureHex = await window..;
// any path → (r, s, v)
const bytes = ; // or ethers.getBytes
const sig = ;
const creation = ;
const uid = await ;
Conformance. eip712_payload produces the digest
OrderData::hash_struct computes server-side; the Rust test suite
already locks that hash byte-for-byte against ethers's
TypedDataEncoder on all eleven chains. The in-browser harness
(test-harness/index.html, panel 3) re-asserts the equality at
runtime across the shim, viem, and ethers. @cowprotocol/cow-sdk's
own typed-data path delegates to ethers's TypedDataEncoder, so
parity with cow-sdk follows transitively.
For Safe wallets, replace signTypedData with the Safe SDK's
signMessage flow and use build_order_creation_eip1271 instead;
the shim wraps the bytes into a Signature::Eip1271 envelope.
See crates/cow-sdk-wasm/README.md
for the full exported function list, the in_shim_signing feature
trade-off, and the npm publish flow for maintainers.
Build targets and bundle size
wasm-pack produces a different JS glue per target, with the same
underlying .wasm binary. The release recipes are:
| Target | Recipe | Output dir | Consumers |
|---|---|---|---|
| web | just wasm-build-web |
pkg-web/ |
Browser ES modules. |
| bundler | just wasm-build-bundler |
pkg-bundler/ |
webpack / Vite / Rollup. |
| nodejs | just wasm-build-nodejs |
pkg-nodejs/ |
Node 18+, CommonJS. |
just wasm-build-all builds all three; just wasm-size reports the
post-wasm-opt .wasm byte counts. The wasm binary is byte-identical
across targets, so an eventual npm package can ship one .wasm plus
three JS glues with an exports map.
Size knobs applied (crates/cow-sdk-wasm/Cargo.toml +
workspace [profile.release]):
wasm-opt -Ozover the default-O: binaryen biases for binary size (~30% smaller).[profile.release]:lto = "fat",opt-level = "z",panic = "abort",strip = true. Workspace-only — crates.io consumers use their own profile.cowprotocol = { default-features = false }: drops thesubgraphGraphQL client; not reachable from JS.lol_allocglobal allocator (~5 KB vs dlmalloc's ~10 KB). Active by default —mod allocator;is wired in at the top of the wasm crate'slib.rs.in_shim_signingcargo feature, default-off: gatesalloy-signer+alloy-signer-localso the default build ships hash builders only. Saves ~68 KB; integrators sign with viem / ethers / Safe and submit the (r, s, v) bag back throughbuild_order_creation.- No reqwest in the wasm output: HTTP-touching exports
(
get_quote,post_order, etc.) call the JSfetchglobal directly viajs_sys::Reflect, side-stepping reqwest's 150 KB bundle. Withlto = "fat"the linker drops reqwest from the wasm binary because no wasm-bindgen export reaches it. Cowprotocol stays unchanged — the same crate continues to ship reqwest-backedOrderBookApifor native consumers.
Current .wasm after wasm-opt -Oz:
default features 584 KB
+ --features in_shim_signing ~650 KB
(Down from 652 KB / 720 KB before lever 1.)
Conformance
cow-rs locks byte-exact equivalence on every protocol-critical path:
- ethers
TypedDataEncoderfor all eleven chains: signed UID, struct hash, domain separator, six order-shape permutations cowprotocol/servicesfor signature recovery and cancellation struct hashescowprotocol/contractsfor the canonicalTYPE_HASHderivation,packOrderUidParamslayout, and event topic hashes- ethers
Wallet.signTypedDatafor the ECDSA(r, s, v)golden - cow-py for the
ConditionalOrderleaf-id derivation - The empty-document app-data digest
keccak256("{}")
Regenerate the cross-implementation vectors with:
&& &&
Status
1.0.0-alpha: the public API is locked unless a critical conformance
issue forces a break. Patch releases (1.0.0-alpha.N) bring additive
features and bug fixes; breaking changes only on minor or major bumps.
Production readiness:
- ✅ Byte-conformance with services / contracts / cow-sdk / cow-py
- ✅ All eleven documented chains
- ✅ All four signing schemes
- ✅ Mock-server integration coverage for every
OrderBookApimethod - ✅ WASM compilation gate in CI plus an in-browser e2e harness
- ✅
cargo clippy -- -Dwarnings, nounsafe, noanyhowin lib code - ✅ Published to crates.io as
cowprotocol
Building
just build # cargo build --all-targets --all-features --workspace
just test # cargo test --all-targets --all-features --workspace
just clippy # cargo clippy ... -- -Dwarnings
just fmt-check
just wasm-check # cargo check --target wasm32-unknown-unknown ...
just wasm-harness # build cow-sdk-wasm and serve test-harness/ on :8765
just doc # cargo doc with -D warnings
Layout
crates/cowprotocol/ # Library crate; everything re-exported from the root
crates/cowprotocol/examples/ # get_quote.rs, post_order.rs
crates/cow-sdk-wasm/ # #[wasm_bindgen] shim driving the in-browser harness (unpublished)
test-harness/ # Static HTML harness; `just wasm-harness` to run
tools/vector-gen/ # Node.js golden-vector generator (ethers reference)
Contributing
See CONTRIBUTING.md. Briefly: Oxford English in
prose, no em dashes, Conventional Commits, AI-assistance disclosure
in the PR body (never in commits), PRs ≤ 1,500 LoC against develop.
Licence
GPL-3.0-or-later; see LICENSE. Portions adapted from
cowprotocol/services
under MIT / Apache-2.0 with attribution in each affected file.