@symbiome-forge/cow-sdk-wasm 0.1.0-alpha.4 → 0.1.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +176 -223
  2. package/dist/default/callbacks.d.ts +18 -0
  3. package/dist/default/index.cjs +3 -0
  4. package/dist/default/index.d.ts +1 -0
  5. package/dist/default/index.mjs +3 -0
  6. package/dist/default/internal.cjs +3 -2
  7. package/dist/default/internal.js +3 -2
  8. package/dist/orderbook/callbacks.d.ts +18 -0
  9. package/dist/orderbook/internal.cjs +3 -2
  10. package/dist/orderbook/internal.js +3 -2
  11. package/dist/raw/default-bundler/cow_sdk_wasm.d.ts +63 -15
  12. package/dist/raw/default-bundler/cow_sdk_wasm_bg.js +88 -37
  13. package/dist/raw/default-bundler/cow_sdk_wasm_bg.wasm +0 -0
  14. package/dist/raw/default-bundler/cow_sdk_wasm_bg.wasm.d.ts +4 -3
  15. package/dist/raw/default-nodejs/cow_sdk_wasm.cjs +88 -37
  16. package/dist/raw/default-nodejs/cow_sdk_wasm.d.ts +63 -15
  17. package/dist/raw/default-nodejs/cow_sdk_wasm_bg.wasm.d.ts +4 -3
  18. package/dist/raw/orderbook-bundler/cow_sdk_wasm.d.ts +18 -6
  19. package/dist/raw/orderbook-bundler/cow_sdk_wasm_bg.js +28 -16
  20. package/dist/raw/orderbook-bundler/cow_sdk_wasm_bg.wasm +0 -0
  21. package/dist/raw/orderbook-nodejs/cow_sdk_wasm.cjs +28 -16
  22. package/dist/raw/orderbook-nodejs/cow_sdk_wasm.d.ts +18 -6
  23. package/dist/raw/signing-bundler/cow_sdk_wasm_bg.js +10 -10
  24. package/dist/raw/signing-bundler/cow_sdk_wasm_bg.wasm +0 -0
  25. package/dist/raw/signing-bundler/cow_sdk_wasm_bg.wasm.d.ts +3 -3
  26. package/dist/raw/signing-nodejs/cow_sdk_wasm.cjs +10 -10
  27. package/dist/raw/signing-nodejs/cow_sdk_wasm_bg.wasm.d.ts +3 -3
  28. package/dist/raw/trading-bundler/cow_sdk_wasm.d.ts +63 -15
  29. package/dist/raw/trading-bundler/cow_sdk_wasm_bg.js +88 -37
  30. package/dist/raw/trading-bundler/cow_sdk_wasm_bg.wasm +0 -0
  31. package/dist/raw/trading-bundler/cow_sdk_wasm_bg.wasm.d.ts +4 -3
  32. package/dist/raw/trading-nodejs/cow_sdk_wasm.cjs +88 -37
  33. package/dist/raw/trading-nodejs/cow_sdk_wasm.d.ts +63 -15
  34. package/dist/raw/trading-nodejs/cow_sdk_wasm_bg.wasm.d.ts +4 -3
  35. package/dist/raw/trading-web/cow_sdk_wasm.d.ts +67 -18
  36. package/dist/raw/trading-web/cow_sdk_wasm.js +88 -37
  37. package/dist/raw/trading-web/cow_sdk_wasm_bg.wasm.d.ts +4 -3
  38. package/dist/signing/callbacks.d.ts +18 -0
  39. package/dist/signing/internal.cjs +3 -2
  40. package/dist/signing/internal.js +3 -2
  41. package/dist/trading/callbacks.d.ts +18 -0
  42. package/dist/trading/edge.d.ts +1 -0
  43. package/dist/trading/edge.mjs +3 -0
  44. package/dist/trading/index.cjs +3 -0
  45. package/dist/trading/index.d.ts +1 -0
  46. package/dist/trading/index.mjs +3 -0
  47. package/dist/trading/internal.cjs +3 -2
  48. package/dist/trading/internal.js +3 -2
  49. package/package.json +1 -1
package/README.md CHANGED
@@ -1,287 +1,240 @@
1
1
  # @symbiome-forge/cow-sdk-wasm
2
2
 
3
- TypeScript-callable WebAssembly bindings for the CoW Protocol Rust SDK.
3
+ [CoW Protocol](https://cow.fi)'s Rust SDK, compiled to WebAssembly for JavaScript
4
+ and TypeScript. One protocol implementation runs in both runtimes, so the EIP-712
5
+ and EIP-1271 signatures a browser produces are byte-identical to the Rust
6
+ service's — checked against the upstream `cowprotocol/services` and
7
+ `cowprotocol/contracts` fixtures in CI, not asserted in prose.
4
8
 
5
9
  ```sh
6
10
  npm install @symbiome-forge/cow-sdk-wasm@alpha
7
11
  ```
8
12
 
9
- The package exposes a TypeScript facade over deterministic Rust protocol logic.
10
- JavaScript and TypeScript consumers get typed DTOs, explicit wallet and HTTP
11
- callbacks, per-call cancellation, per-call timeouts, and flavor-specific imports
12
- without depending on a specific wallet library.
13
+ A TypeScript facade over deterministic Rust protocol logic: typed DTOs, explicit
14
+ wallet and HTTP callbacks, per-call cancellation and timeouts, and
15
+ flavor-specific imports with no bundled wallet library.
16
+
17
+ ## Why this package
18
+
19
+ - **One source of truth.** Quote echoing, order-UID packing, app-data hashing,
20
+ and the EIP-712 / EIP-1271 signing path are the same Rust code a native
21
+ `cow-sdk` service runs, compiled to wasm — so protocol drift between a Rust
22
+ backend and a TypeScript frontend cannot happen. Every transform is proven
23
+ byte-for-byte against pinned upstream fixtures on each CI run.
24
+ - **No private key ever enters the SDK.** Signing is a callback you supply (viem,
25
+ ethers, an EIP-1193 wallet, or a Safe). There is no code path — not even a
26
+ feature-gated one — that accepts a private key or holds a wallet inside wasm
27
+ memory. The package produces typed data and transaction requests; your wallet
28
+ signs and submits.
29
+ - **The TypeScript surface is locked.** The public `.d.ts` for every flavor is a
30
+ committed snapshot that CI diffs on every build, so a contract change is a
31
+ reviewed diff, never a silent drift — the wasm analog of `cargo-public-api`.
32
+ - **Honest about fit.** For a standard browser dapp where minimal bundle size
33
+ dominates, upstream
34
+ [`@cowprotocol/cow-sdk`](https://www.npmjs.com/package/@cowprotocol/cow-sdk) is
35
+ smaller — use it. This package is for Rust ↔ TypeScript parity, single-source
36
+ embedding, edge runtimes, and embeddable signing.
37
+
38
+ ## Pick your import
13
39
 
14
- ## When to use this SDK
15
-
16
- | You are building... | Choose | Why |
40
+ | Import | Surface | Use when |
17
41
  | --- | --- | --- |
18
- | Browser dApp quote, sign, post, cancel (full order lifecycle) with viem, ethers, wagmi, or an EIP-1193 wallet | `@symbiome-forge/cow-sdk-wasm/trading` | Order lifecycle plus app-data, built for a browser bundler (Vite, webpack); wallet stack stays outside the package behind typed callbacks |
19
- | Browser dApp — orderbook reads and cancellation only | `@symbiome-forge/cow-sdk-wasm/orderbook` | Smaller read-focused subset, no trading or app-data |
20
- | Node.js 22 or 24 LTS backend running order flow | `@symbiome-forge/cow-sdk-wasm/trading` | Same order-lifecycle surface on the Node target, no browser polyfills |
21
- | Edge runtime — Cloudflare Workers, Deno, or Vercel Edge | `@symbiome-forge/cow-sdk-wasm/trading/edge` | The `trading` flavour's web-target build with explicit wasm module initialization |
22
- | Signer service or HSM proxy | `@symbiome-forge/cow-sdk-wasm/signing` | Signing primitives without orderbook, trading, subgraph, or IPFS clients |
23
- | Everything, including subgraph analytics and IPFS app-data | `@symbiome-forge/cow-sdk-wasm` | The full default surface |
24
- | Native Rust service, bot, solver, or treasury automation | `cow-sdk` | Avoids wasm-bindgen and npm packaging entirely |
25
- | Rust app compiled to browser WASM | `cow-sdk` with `cow-sdk-core`'s browser `FetchTransport` (the `wasm32-unknown-unknown` `transport::fetch` module) | Rust-on-wasm path; this package is for JavaScript hosts |
26
-
27
- The same `trading` flavour serves a browser dApp (bundler target), a Node backend
28
- (nodejs target), and an edge runtime (web target) from one feature set — pick the
29
- import by runtime; the package resolves the target through standard conditional
30
- exports, with `./trading/edge` as the explicit entry for Cloudflare Workers.
31
-
32
- ## Not in this crate
33
-
34
- Use the upstream TypeScript SDK packages until these capability families ship
35
- in `cow-rs`:
36
-
37
- - TWAP and composable orders.
38
- - Cross-chain bridging.
39
- - Cow Shed account abstraction.
40
- - Flash-loan helpers.
41
- - Weiroll command planning.
42
- - Hardware wallet adapters.
43
- - On-chain transaction submission; this package emits typed data or
44
- transaction requests and lets the caller's wallet submit.
45
- - WASI, WebAssembly components, TinyGo, Blazor, AssemblyScript guests, and
46
- `no_std` embedded targets.
47
-
48
- ## Quickstart
49
-
50
- ### Node.js 22 or 24 with viem
42
+ | `@symbiome-forge/cow-sdk-wasm/trading` | Full order lifecycle: quote, sign, post, cancel, app-data | A browser dapp, a Node backend, or an edge runtime running order flow one feature set serves all three; pick the runtime by import |
43
+ | `@symbiome-forge/cow-sdk-wasm/trading/edge` | The `trading` flavor's web-target build, with explicit wasm init | Cloudflare Workers, Deno, or Vercel Edge; pair with `…/trading/edge/wasm` for the module asset |
44
+ | `@symbiome-forge/cow-sdk-wasm/orderbook` | Orderbook reads, cancellation, and signing — no trading or app-data | A read-focused dapp that does not post orders |
45
+ | `@symbiome-forge/cow-sdk-wasm/signing` | Signing, UID, EIP-1271, deployment, and version helpers the smallest flavor | A signer service or HSM-facing adapter |
46
+ | `@symbiome-forge/cow-sdk-wasm` | Everything above plus subgraph analytics and IPFS app-data | General use that needs subgraph or IPFS |
47
+
48
+ The `trading` flavor resolves the right target automatically through standard
49
+ conditional exports `node` and `browser` for bundlers and Node, and `workerd` /
50
+ `deno` / `edge-light` / `bun` for edge — with `./trading/edge` as the explicit
51
+ Workers entry. Public imports go through these subpaths; do not import from
52
+ `dist/raw` or generated wasm-pack directories.
53
+
54
+ Building a **native Rust** service, or a Rust app you compile to wasm yourself?
55
+ Use [`cow-sdk`](https://crates.io/crates/cow-sdk) — this package is for
56
+ JavaScript hosts.
57
+
58
+ ## Quickstart a browser swap, end to end
59
+
60
+ Quote, then reuse that quote to sign and post in one call, so the amounts the user
61
+ confirms are the amounts that get posted — no second quote, no drift between
62
+ preview and signature. The wallet signs a typed-data envelope the SDK hands it;
63
+ no key reaches the package.
51
64
 
52
65
  ```ts
53
- import { TradingClient } from "@symbiome-forge/cow-sdk-wasm";
66
+ import { TradingClient } from "@symbiome-forge/cow-sdk-wasm/trading";
67
+ import { createWalletClient, custom } from "viem";
68
+ import { mainnet } from "viem/chains";
69
+
70
+ const [owner] = await window.ethereum.request({ method: "eth_requestAccounts" });
71
+ const wallet = createWalletClient({ chain: mainnet, transport: custom(window.ethereum) });
54
72
 
55
73
  const trading = new TradingClient({
56
74
  chainId: 1,
57
75
  env: "prod",
58
- appCode: "my-node-service",
59
- transport: { kind: "fetch" },
60
- transportPolicy: {
61
- retryPolicy: { maxAttempts: 3, baseDelayMs: 200 },
62
- userAgent: "my-node-service/1.0"
63
- }
76
+ appCode: "my-dapp",
77
+ transport: { kind: "fetch" }
64
78
  });
65
79
 
66
- // `getQuote` returns a fully resolved `QuoteResultsDto` envelope.
80
+ // 1. Quote. `getQuote` returns a fully resolved QuoteResultsDto envelope.
81
+ // `owner` is required for a quote-only call.
67
82
  const quote = await trading.getQuote({
68
83
  kind: "sell",
69
- sellToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
70
- buyToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
84
+ owner,
85
+ sellToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
86
+ buyToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
71
87
  amount: "1000000000000000000"
72
88
  });
73
89
 
74
- // Reuse the quote to sign and post in one call; `quote.value` is the
75
- // `QuoteResultsDto` and the callback receives the EIP-712 envelope.
90
+ // 2. Reuse the quote to sign and post. The callback receives the EIP-712
91
+ // envelope and returns the signature the key stays in the wallet.
76
92
  const result = await trading.postSwapOrderFromQuote(
77
93
  quote.value,
78
- "0x1111111111111111111111111111111111111111",
79
- async (envelope) => walletClient.signTypedData(envelope),
80
- { walletConfig: { timeoutMs: 15_000 } }
81
- );
82
- // `result.value.orderId` is the posted order UID.
83
- ```
84
-
85
- ### Browser with `window.ethereum`
86
-
87
- ```ts
88
- import { signOrderWithEip1193 } from "@symbiome-forge/cow-sdk-wasm";
89
-
90
- const ethereum = window.ethereum;
91
- const [owner] = await ethereum.request({ method: "eth_requestAccounts" });
92
- const abortController = new AbortController();
93
-
94
- // The order to sign: build it yourself or map it from a fetched quote.
95
- const order = {
96
- sellToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
97
- buyToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
98
- receiver: owner,
99
- sellAmount: "1000000000000000000",
100
- buyAmount: "3500000000",
101
- validTo: Math.floor(Date.now() / 1000) + 3_600,
102
- appData: "0x0000000000000000000000000000000000000000000000000000000000000000",
103
- feeAmount: "0",
104
- kind: "sell",
105
- partiallyFillable: false,
106
- sellTokenBalance: "erc20",
107
- buyTokenBalance: "erc20"
108
- };
109
-
110
- const signed = await signOrderWithEip1193(
111
- order,
112
- 1,
113
94
  owner,
114
- (rpc) => ethereum.request(rpc),
115
- { signal: abortController.signal, walletConfig: { timeoutMs: 20_000 } }
95
+ (envelope) => {
96
+ // viem derives EIP712Domain from `domain`; drop it from `types`.
97
+ const types = Object.fromEntries(
98
+ Object.entries(envelope.types).filter(([name]) => name !== "EIP712Domain")
99
+ );
100
+ return wallet.signTypedData({
101
+ account: owner,
102
+ domain: envelope.domain,
103
+ types,
104
+ primaryType: envelope.primaryType,
105
+ message: envelope.message
106
+ });
107
+ },
108
+ { walletConfig: { timeoutMs: 20_000 } }
116
109
  );
117
- // `signed.value` is the SignedOrderDto.
118
- ```
119
-
120
- ### Browser with MetaMask `eth_signTypedData_v4`
121
110
 
122
- When the wallet exposes the typed-data JSON-RPC method directly, callers can
123
- pass the envelope to `eth_signTypedData_v4` from inside the typed-data signer
124
- callback. The helper hands the callback a typed-data envelope — plain `domain`,
125
- `types`, `primaryType`, and `message` objects — that the callback serializes and
126
- returns the signature string for.
111
+ console.log(`https://explorer.cow.fi/mainnet/orders/${result.value.orderId}`);
112
+ trading.dispose();
113
+ ```
127
114
 
128
- ```ts
129
- import { signOrderWithTypedDataSigner } from "@symbiome-forge/cow-sdk-wasm";
115
+ Selling the native asset is the same shape: `getQuote`, then
116
+ `buildSellNativeCurrencyTxFromQuote(quote.value, owner)`, which returns the EthFlow
117
+ transaction request for the wallet to submit.
130
118
 
131
- const [owner] = await window.ethereum.request({ method: "eth_requestAccounts" });
119
+ ### Cloudflare Worker (edge)
132
120
 
133
- const signed = await signOrderWithTypedDataSigner(order, 1, owner, async (envelope) => {
134
- const signature = await window.ethereum.request({
135
- method: "eth_signTypedData_v4",
136
- params: [owner, JSON.stringify(envelope)]
137
- });
138
- if (typeof signature !== "string") {
139
- throw new Error("wallet did not return a signature");
140
- }
141
- return signature;
142
- });
143
- ```
144
-
145
- ### Cloudflare Worker
121
+ Workers cannot compile wasm from bytes at runtime, so the edge build takes the
122
+ statically imported module through an explicit `initialize`.
146
123
 
147
124
  ```ts
148
- import initialize, {
149
- OrderBookClient
150
- } from "@symbiome-forge/cow-sdk-wasm/trading/edge";
125
+ import initialize, { OrderBookClient } from "@symbiome-forge/cow-sdk-wasm/trading/edge";
151
126
  import wasmModule from "@symbiome-forge/cow-sdk-wasm/trading/edge/wasm";
152
127
 
153
128
  export default {
154
129
  async fetch(request: Request, env: Env): Promise<Response> {
155
130
  await initialize(wasmModule);
156
-
157
131
  const client = new OrderBookClient({
158
132
  chainId: 1,
159
133
  env: "prod",
160
134
  apiKey: env.COW_PARTNER_API_KEY ?? null,
161
- transport: { kind: "fetch" },
162
- transportPolicy: { userAgent: "my-worker/1.0" }
163
- });
164
-
165
- const quote = await client.getQuote(await request.json(), {
166
- timeoutMs: 8_000
135
+ transport: { kind: "fetch" }
167
136
  });
137
+ const quote = await client.getQuote(await request.json(), { timeoutMs: 8_000 });
168
138
  client.dispose();
169
-
170
139
  return Response.json(quote);
171
140
  }
172
141
  };
173
142
  ```
174
143
 
175
- ## Choosing your import
144
+ ### Lower-level signing
176
145
 
177
- | Import | Surface | Use when |
178
- | --- | --- | --- |
179
- | `@symbiome-forge/cow-sdk-wasm` | Default facade with orderbook, signing, app-data, IPFS, trading, and subgraph | General TypeScript or Node use that needs subgraph or IPFS |
180
- | `@symbiome-forge/cow-sdk-wasm/trading` | Orderbook, trading, signing, app-data, and cancellation — the full order lifecycle | Browser dApps, Node backends, and edge runtimes running order flow |
181
- | `@symbiome-forge/cow-sdk-wasm/orderbook` | Orderbook client, cancellation helpers, and signing helpers | Read-focused dApps that do not post orders |
182
- | `@symbiome-forge/cow-sdk-wasm/signing` | Signing, UID, EIP-1271, deployment, and version helpers | Signer services and HSM-facing adapters |
183
- | `@symbiome-forge/cow-sdk-wasm/trading/edge` | The `trading` flavour's web-target facade | Edge runtimes (Cloudflare Workers, Deno, Vercel Edge) |
184
- | `@symbiome-forge/cow-sdk-wasm/trading/edge/wasm` | Raw Worker wasm module asset | Pass to the `initialize` helper |
146
+ For control over an order you build yourself, sign through an EIP-1193 wallet with
147
+ `signOrderWithEip1193(order, chainId, owner, (rpc) => ethereum.request(rpc))`, or
148
+ hand a typed-data method directly to `signOrderWithTypedDataSigner`. Both return a
149
+ `SignedOrderDto` you submit with `OrderBookClient.sendOrder`.
185
150
 
186
- Do not import from `dist/raw` or generated wasm-pack target directories. Raw
187
- wasm-bindgen output is package-internal; public imports go through the facade
188
- subpaths above.
151
+ ## The callback boundary
189
152
 
190
- ## Performance and bundle size
153
+ The package names host responsibilities as typed callbacks and never reaches past
154
+ them for a key or a provider:
191
155
 
192
- The package is built with release-size settings and a `wasm-opt -Oz` post-pass.
193
- Measured on the current alpha build:
156
+ - `TypedDataSignerCallback` signs an EIP-712 typed-data envelope.
157
+ - `Eip1193RequestCallback` answers EIP-1193 requests from an injected or hosted
158
+ provider.
159
+ - `DigestSignerCallback` — signs a raw digest for explicit EthSign flows.
160
+ - `CustomEip1271Callback` — returns a smart-account's final EIP-1271 signature.
161
+ - `ContractReadCallback` — performs a read-only `eth_call` and returns the
162
+ ABI-decoded value as a decimal string or number (e.g. viem's `readContract`
163
+ result via `String(value)`).
164
+ - `CowFetchCallback` — dispatches HTTP for Node, Workers, Deno, and custom hosts.
194
165
 
195
- | Flavor | Raw wasm | Brotli | Gzip | Gate |
196
- | --- | ---: | ---: | ---: | --- |
197
- | default | 1.63 MiB | 511 KiB | 689 KiB | 3.3 MiB raw / 900 KiB brotli |
198
- | orderbook | 1.03 MiB | 341 KiB | 447 KiB | 1.5 MiB raw / 500 KiB brotli |
199
- | signing | 0.31 MiB | 120 KiB | 142 KiB | 0.9 MiB raw / 300 KiB brotli |
200
- | trading | 1.54 MiB | 489 KiB | 657 KiB | 3.2 MiB raw / 850 KiB brotli / 3,000,000 B gzip (warn at 2,700,000 B) |
166
+ A callback may return a plain value, a Promise, or a thenable. Clients expose
167
+ `dispose()` and `[Symbol.dispose]` (so `using client = new …` works) and release
168
+ the callbacks they hold on disposal.
201
169
 
202
- The `trading` flavour emits one wasm binary shared across its bundler, nodejs, and
203
- web targets. Its web-target gzip-compressed artifact is below the current
204
- Cloudflare Workers Free compressed-size limit at the time of measurement.
205
- Full Workers support still requires release-bundle verification and Worker
206
- startup measurement; the release pipeline enforces the gzip byte budget on
207
- every build, but Wrangler deployment and `startup_time_ms` telemetry are
208
- separate operational gates.
170
+ ## Cancellation and timeouts
209
171
 
210
- Cloudflare Workers cold starts are runtime-sensitive. The package treats
211
- 300 ms as the warning threshold, 500 ms as the release gate, and 1 second as
212
- the platform-limit budget that Worker consumers should stay well below.
172
+ Every call accepts an optional `signal` (an `AbortSignal`) and `timeoutMs`.
173
+ Aborting the signal rejects the pending call with a `cancelled` `CowError`;
174
+ `timeoutMs` rejects with a `timeout` error. Both resolve the *awaited call* — an
175
+ already-dispatched HTTP request may keep running in the background until it
176
+ completes or the timeout elapses, so treat cancellation as "stop waiting," not a
177
+ guarantee that the network request is halted.
213
178
 
214
- ## Transport configuration
179
+ ## Transport
215
180
 
216
- Every client accepts one transport:
181
+ Every client takes one transport:
217
182
 
218
183
  ```ts
219
- transport: { kind: "fetch" }
220
- transport: { kind: "fetch", fetch: customFetch }
221
- transport: { kind: "callback", callback: customHttpCallback }
184
+ transport: { kind: "fetch" } // standards `fetch` (browser, Node, Workers)
185
+ transport: { kind: "fetch", fetch: customFetch } // a fetch you supply
186
+ transport: { kind: "callback", callback } // you own request dispatch
222
187
  ```
223
188
 
224
- Use `fetch` for browser, Node, and Worker runtimes that expose a standards
225
- compatible `fetch`. Use `callback` when the host must own request dispatch,
226
- fixtures, proxying, custom authentication, or observability.
189
+ Use `callback` when the host must own dispatch for fixtures, proxying, custom
190
+ authentication, or observability. Each client also takes optional
191
+ `transportPolicy` settings for retry, rate-limit, jitter, and user-agent behavior.
227
192
 
228
- Every client also accepts optional `transportPolicy` settings for retry,
229
- rate-limit, jitter, and user-agent behavior.
193
+ ## Errors
230
194
 
231
- ## Cancellation and timeouts
195
+ JavaScript-visible failures are a typed `CowError` discriminated union — transport,
196
+ app-data, signing, orderbook, subgraph, trading, wallet, cancellation, and
197
+ internal variants — with low-cardinality fields visible and URLs, headers, bodies,
198
+ and secret-shaped values redacted. The `orderbook` variant carries `retryable` and
199
+ an optional `retryAfterMs` parsed from the response `Retry-After`, mirroring the
200
+ native `OrderbookError::is_retryable` / `backoff_hint`, so a JavaScript retry loop
201
+ reaches the same verdict as the Rust one.
202
+
203
+ ## Bundle size
204
+
205
+ Built with release-size settings and a `wasm-opt -Oz` pass; measured on the
206
+ current alpha build (gzip is the compressed-transfer figure):
232
207
 
233
- Every call accepts an optional `signal` (an `AbortSignal`) and a per-call
234
- `timeoutMs`. Aborting the signal rejects the pending call promptly with a
235
- `cancelled` `CowError`; `timeoutMs` rejects with a `timeout` error. Both resolve
236
- the *awaited call* an already-dispatched HTTP request may keep running in the
237
- background until it completes or the timeout elapses, so treat cancellation as
238
- "stop waiting," not a guarantee that the network request is halted.
239
-
240
- ## Architecture
241
-
242
- The TypeScript facade is the public package contract. It:
243
-
244
- - exposes camelCase TypeScript APIs;
245
- - exposes `dispose()` and `[Symbol.dispose]` (so `using client = new …` works)
246
- while hiding the raw wasm-bindgen `free()` handle;
247
- - maps raw wasm errors into `CowError`;
248
- - adapts `transport: { kind: "fetch" }` into the callback HTTP ABI;
249
- - keeps wallet libraries outside the package behind named callback types.
250
-
251
- ## API reference
252
-
253
- The declaration snapshots under `crates/wasm/snapshots/facade/` show the
254
- public TypeScript surface for each flavor. Key exports include:
255
-
256
- - clients: `OrderBookClient`, `TradingClient`, `SubgraphClient`, `IpfsClient`;
257
- - signing helpers: `signOrderWithTypedDataSigner`, `signOrderWithEip1193`,
258
- `signOrderEthSignDigest`, `signOrderWithEip1271`,
259
- `signOrderWithCustomEip1271`;
260
- - cancellation helpers: `signCancellationWithTypedDataSigner`,
261
- `signCancellationWithEip1193`, `signCancellationEthSignDigest`,
262
- `buildCancelOrderTx`, `buildPresignTx`;
263
- - pure helpers: `domainSeparator`, `orderTypedData`, `computeOrderUid`,
264
- `deploymentAddresses`, `supportedChainIds`, `appDataInfo`,
265
- `validateAppDataDoc`, `appDataDoc`, `appDataHexToCid`,
266
- `cidToAppDataHex`, `wasmVersion`.
267
-
268
- ## When to use this package vs the upstream TypeScript SDK
269
-
270
- For most browser dapps, web apps, and CowSwap-style UIs, the upstream
271
- [`@cowprotocol/cow-sdk`](https://www.npmjs.com/package/@cowprotocol/cow-sdk)
272
- is the recommended choice; it is substantially smaller at equivalent feature
273
- subsets. This package is appropriate for specialized cases:
274
-
275
- - TypeScript services that need byte-for-byte parity with the Rust SDK's
276
- EIP-712 + EIP-1271 signing path.
277
- - Single-source-of-truth Rust + TypeScript embedding (one implementation
278
- across both runtimes).
279
- - Cloudflare Workers (size-compatible with the current Workers Free
280
- compressed-size limit at the time of measurement; the `trading` flavour's
281
- edge build is built and tested end-to-end in CI (Workers Vitest), within the
282
- Workers compressed-size budget).
283
- - Embeddable signing helpers (the `./signing` flavor is the smallest).
284
-
285
- The "When to use this SDK" table at the top of this README routes consumers
286
- by use case. The Quickstart sections above show the supported import shapes
287
- for the most common runtimes.
208
+ | Flavor | Raw wasm | Brotli | Gzip | Release gate |
209
+ | --- | ---: | ---: | ---: | --- |
210
+ | signing | 0.31 MiB | 120 KiB | 142 KiB | 0.9 MiB raw / 300 KiB brotli |
211
+ | orderbook | 1.02 MiB | 341 KiB | 447 KiB | 1.5 MiB raw / 500 KiB brotli |
212
+ | trading | 1.54 MiB | 490 KiB | 659 KiB | 3.2 MiB raw / 850 KiB brotli |
213
+ | default | 1.63 MiB | 513 KiB | 692 KiB | 3.3 MiB raw / 900 KiB brotli |
214
+
215
+ The `trading` flavor emits one wasm binary across its bundler, Node, and web
216
+ targets; its web-target gzip size is within the current Cloudflare Workers Free
217
+ compressed-size limit, enforced as a byte budget on every build. End-to-end
218
+ Workers support also depends on `wrangler deploy --dry-run` verification and a
219
+ Worker startup-time gate, tracked separately.
220
+
221
+ ## Not in this package
222
+
223
+ Use the upstream TypeScript SDK for these until they ship in `cow-rs`: TWAP and
224
+ composable orders, cross-chain bridging, CoW Shed account abstraction, flash-loan
225
+ helpers, and hardware-wallet adapters. This package emits typed data or
226
+ transaction requests and lets the caller's wallet submit on-chain; it ships no
227
+ WASI, WebAssembly-component, or `no_std` guest target.
228
+
229
+ ## More
230
+
231
+ - The public TypeScript surface for each flavor is the committed declaration
232
+ snapshot under `crates/wasm/snapshots/facade/`.
233
+ - [Architecture](https://github.com/0xSymbiome/cow-rs/blob/main/docs/architecture.md),
234
+ [Observability](https://github.com/0xSymbiome/cow-rs/blob/main/docs/observability.md),
235
+ and the
236
+ [WASM Surface Audit](https://github.com/0xSymbiome/cow-rs/blob/main/docs/audit/wasm-surface-audit.md).
237
+ - Runnable browser, Node, and Worker examples live in the
238
+ [`cow-sdk-examples`](https://github.com/0xSymbiome/cow-sdk-examples) repository.
239
+
240
+ Licensed under GPL-3.0-or-later.
@@ -52,4 +52,22 @@ export type Eip1193RequestCallback = (request: {
52
52
  export type DigestSignerCallback = (digest: string) => Promise<string> | string;
53
53
  export type CowEip1271SignCallback = (request: CowEip1271SignRequest) => Promise<string> | string;
54
54
  export type CustomEip1271Callback = CowEip1271SignCallback;
55
+ /**
56
+ * Performs a read-only contract call on behalf of the SDK and returns the
57
+ * ABI-decoded result.
58
+ *
59
+ * The callback must return the decoded value as a decimal string or number, not
60
+ * the raw `0x`-hex `eth_call` payload. With viem, pass the `readContract`
61
+ * result through `String(value)`:
62
+ *
63
+ * ```ts
64
+ * const readContract: ContractReadCallback = async ({ address, method, abiJson, argsJson }) =>
65
+ * String(await publicClient.readContract({
66
+ * address: address as `0x${string}`,
67
+ * abi: JSON.parse(abiJson),
68
+ * functionName: method,
69
+ * args: JSON.parse(argsJson)
70
+ * }));
71
+ * ```
72
+ */
55
73
  export type ContractReadCallback = (request: ContractCallDto) => Promise<string> | string;
@@ -247,6 +247,9 @@ class TradingClient {
247
247
  async buildSellNativeCurrencyTx(order, quoteId, from, options) {
248
248
  return this.#call((client, merged) => client.buildSellNativeCurrencyTx(order, quoteId, from, merged), options);
249
249
  }
250
+ async buildSellNativeCurrencyTxFromQuote(quoteResults, from, options) {
251
+ return this.#call((client, merged) => client.buildSellNativeCurrencyTxFromQuote(quoteResults, from, merged), options);
252
+ }
250
253
  async getCowProtocolAllowance(params, readContractCallback, options) {
251
254
  return this.#call((client, merged) => client.getCowProtocolAllowance(params, readContractCallback, merged), options);
252
255
  }
@@ -55,6 +55,7 @@ export declare class TradingClient {
55
55
  #private;
56
56
  constructor(config: TradingClientConfig);
57
57
  buildSellNativeCurrencyTx(order: raw.OrderInput, quoteId: number, from: string, options?: SdkClientOptions | null): Promise<WasmEnvelope<raw.BuiltSellNativeCurrencyTxDto>>;
58
+ buildSellNativeCurrencyTxFromQuote(quoteResults: raw.QuoteResultsDto, from: string, options?: SdkClientOptions | null): Promise<WasmEnvelope<raw.BuiltSellNativeCurrencyTxDto>>;
58
59
  getCowProtocolAllowance(params: raw.AllowanceParametersInput, readContractCallback: ContractReadCallback, options?: SdkClientOptions | null): Promise<WasmEnvelope<string>>;
59
60
  buildApprovalTx(params: raw.ApprovalParametersInput, options?: SdkClientOptions | null): Promise<WasmEnvelope<raw.TransactionRequestDto>>;
60
61
  getQuote(params: raw.SwapParametersInput, options?: SdkClientOptions | null): Promise<WasmEnvelope<raw.QuoteResultsDto>>;
@@ -184,6 +184,9 @@ export class TradingClient {
184
184
  async buildSellNativeCurrencyTx(order, quoteId, from, options) {
185
185
  return this.#call((client, merged) => client.buildSellNativeCurrencyTx(order, quoteId, from, merged), options);
186
186
  }
187
+ async buildSellNativeCurrencyTxFromQuote(quoteResults, from, options) {
188
+ return this.#call((client, merged) => client.buildSellNativeCurrencyTxFromQuote(quoteResults, from, merged), options);
189
+ }
187
190
  async getCowProtocolAllowance(params, readContractCallback, options) {
188
191
  return this.#call((client, merged) => client.getCowProtocolAllowance(params, readContractCallback, merged), options);
189
192
  }
@@ -16,8 +16,9 @@ function translateHttpTransport(transport) {
16
16
  return transport;
17
17
  }
18
18
  // An omitted transport (or `{ kind: "fetch" }` without an explicit fetch)
19
- // defaults to the runtime's global `fetch`, matching the Rust builders'
20
- // zero-config default.
19
+ // defaults to the runtime's global `fetch`. This default is a facade
20
+ // affordance: the raw wasm layer has no transport default and requires an
21
+ // explicit callback transport, so the facade injects `globalThis.fetch` here.
21
22
  const fetchFn = transport?.fetch ?? globalThis.fetch;
22
23
  if (typeof fetchFn !== "function") {
23
24
  throw (0, errors_js_1.invalidInput)("transport.fetch", "globalThis.fetch is unavailable; pass an explicit fetch function");
@@ -4,8 +4,9 @@ export function translateHttpTransport(transport) {
4
4
  return transport;
5
5
  }
6
6
  // An omitted transport (or `{ kind: "fetch" }` without an explicit fetch)
7
- // defaults to the runtime's global `fetch`, matching the Rust builders'
8
- // zero-config default.
7
+ // defaults to the runtime's global `fetch`. This default is a facade
8
+ // affordance: the raw wasm layer has no transport default and requires an
9
+ // explicit callback transport, so the facade injects `globalThis.fetch` here.
9
10
  const fetchFn = transport?.fetch ?? globalThis.fetch;
10
11
  if (typeof fetchFn !== "function") {
11
12
  throw invalidInput("transport.fetch", "globalThis.fetch is unavailable; pass an explicit fetch function");
@@ -52,4 +52,22 @@ export type Eip1193RequestCallback = (request: {
52
52
  export type DigestSignerCallback = (digest: string) => Promise<string> | string;
53
53
  export type CowEip1271SignCallback = (request: CowEip1271SignRequest) => Promise<string> | string;
54
54
  export type CustomEip1271Callback = CowEip1271SignCallback;
55
+ /**
56
+ * Performs a read-only contract call on behalf of the SDK and returns the
57
+ * ABI-decoded result.
58
+ *
59
+ * The callback must return the decoded value as a decimal string or number, not
60
+ * the raw `0x`-hex `eth_call` payload. With viem, pass the `readContract`
61
+ * result through `String(value)`:
62
+ *
63
+ * ```ts
64
+ * const readContract: ContractReadCallback = async ({ address, method, abiJson, argsJson }) =>
65
+ * String(await publicClient.readContract({
66
+ * address: address as `0x${string}`,
67
+ * abi: JSON.parse(abiJson),
68
+ * functionName: method,
69
+ * args: JSON.parse(argsJson)
70
+ * }));
71
+ * ```
72
+ */
55
73
  export type ContractReadCallback = (request: ContractCallDto) => Promise<string> | string;
@@ -16,8 +16,9 @@ function translateHttpTransport(transport) {
16
16
  return transport;
17
17
  }
18
18
  // An omitted transport (or `{ kind: "fetch" }` without an explicit fetch)
19
- // defaults to the runtime's global `fetch`, matching the Rust builders'
20
- // zero-config default.
19
+ // defaults to the runtime's global `fetch`. This default is a facade
20
+ // affordance: the raw wasm layer has no transport default and requires an
21
+ // explicit callback transport, so the facade injects `globalThis.fetch` here.
21
22
  const fetchFn = transport?.fetch ?? globalThis.fetch;
22
23
  if (typeof fetchFn !== "function") {
23
24
  throw (0, errors_js_1.invalidInput)("transport.fetch", "globalThis.fetch is unavailable; pass an explicit fetch function");
@@ -4,8 +4,9 @@ export function translateHttpTransport(transport) {
4
4
  return transport;
5
5
  }
6
6
  // An omitted transport (or `{ kind: "fetch" }` without an explicit fetch)
7
- // defaults to the runtime's global `fetch`, matching the Rust builders'
8
- // zero-config default.
7
+ // defaults to the runtime's global `fetch`. This default is a facade
8
+ // affordance: the raw wasm layer has no transport default and requires an
9
+ // explicit callback transport, so the facade injects `globalThis.fetch` here.
9
10
  const fetchFn = transport?.fetch ?? globalThis.fetch;
10
11
  if (typeof fetchFn !== "function") {
11
12
  throw invalidInput("transport.fetch", "globalThis.fetch is unavailable; pass an explicit fetch function");