@symbiome-forge/cow-sdk-wasm 0.1.0-alpha.3 → 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 (79) hide show
  1. package/README.md +177 -216
  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/{cloudflare/raw/cloudflare.cjs → default/raw/trading-web.cjs} +1 -1
  9. package/dist/{cloudflare/raw/cloudflare.d.ts → default/raw/trading-web.d.ts} +2 -2
  10. package/dist/{cloudflare/raw/cloudflare.js → default/raw/trading-web.js} +1 -1
  11. package/dist/orderbook/callbacks.d.ts +18 -0
  12. package/dist/orderbook/internal.cjs +3 -2
  13. package/dist/orderbook/internal.js +3 -2
  14. package/dist/orderbook/raw/trading-web.cjs +65 -0
  15. package/dist/orderbook/raw/trading-web.d.ts +30 -0
  16. package/dist/orderbook/raw/trading-web.js +29 -0
  17. package/dist/raw/default-bundler/cow_sdk_wasm.d.ts +63 -15
  18. package/dist/raw/default-bundler/cow_sdk_wasm_bg.js +87 -36
  19. package/dist/raw/default-bundler/cow_sdk_wasm_bg.wasm +0 -0
  20. package/dist/raw/default-bundler/cow_sdk_wasm_bg.wasm.d.ts +4 -3
  21. package/dist/raw/default-nodejs/cow_sdk_wasm.cjs +87 -36
  22. package/dist/raw/default-nodejs/cow_sdk_wasm.d.ts +63 -15
  23. package/dist/raw/default-nodejs/cow_sdk_wasm_bg.wasm.d.ts +4 -3
  24. package/dist/raw/orderbook-bundler/cow_sdk_wasm.d.ts +18 -6
  25. package/dist/raw/orderbook-bundler/cow_sdk_wasm_bg.js +37 -25
  26. package/dist/raw/orderbook-bundler/cow_sdk_wasm_bg.wasm +0 -0
  27. package/dist/raw/orderbook-bundler/cow_sdk_wasm_bg.wasm.d.ts +3 -3
  28. package/dist/raw/orderbook-nodejs/cow_sdk_wasm.cjs +37 -25
  29. package/dist/raw/orderbook-nodejs/cow_sdk_wasm.d.ts +18 -6
  30. package/dist/raw/orderbook-nodejs/cow_sdk_wasm_bg.wasm.d.ts +3 -3
  31. package/dist/raw/signing-bundler/cow_sdk_wasm_bg.js +10 -10
  32. package/dist/raw/signing-bundler/cow_sdk_wasm_bg.wasm +0 -0
  33. package/dist/raw/signing-bundler/cow_sdk_wasm_bg.wasm.d.ts +3 -3
  34. package/dist/raw/signing-nodejs/cow_sdk_wasm.cjs +10 -10
  35. package/dist/raw/signing-nodejs/cow_sdk_wasm_bg.wasm.d.ts +3 -3
  36. package/dist/raw/trading-bundler/cow_sdk_wasm.d.ts +2977 -0
  37. package/dist/raw/trading-bundler/cow_sdk_wasm.js +9 -0
  38. package/dist/raw/trading-bundler/cow_sdk_wasm_bg.js +2175 -0
  39. package/dist/raw/{cloudflare-web → trading-bundler}/cow_sdk_wasm_bg.wasm +0 -0
  40. package/dist/raw/{cloudflare-web → trading-bundler}/cow_sdk_wasm_bg.wasm.d.ts +4 -3
  41. package/dist/raw/trading-nodejs/cow_sdk_wasm.cjs +2206 -0
  42. package/dist/raw/trading-nodejs/cow_sdk_wasm.d.ts +2977 -0
  43. package/dist/raw/trading-nodejs/cow_sdk_wasm_bg.wasm.d.ts +68 -0
  44. package/dist/raw/{cloudflare-web → trading-web}/cow_sdk_wasm.d.ts +67 -18
  45. package/dist/raw/{cloudflare-web → trading-web}/cow_sdk_wasm.js +87 -36
  46. package/dist/raw/trading-web/cow_sdk_wasm_bg.wasm.d.ts +68 -0
  47. package/dist/signing/callbacks.d.ts +18 -0
  48. package/dist/signing/internal.cjs +3 -2
  49. package/dist/signing/internal.js +3 -2
  50. package/dist/signing/raw/trading-web.cjs +65 -0
  51. package/dist/signing/raw/trading-web.d.ts +30 -0
  52. package/dist/signing/raw/trading-web.js +29 -0
  53. package/dist/{cloudflare → trading}/callbacks.d.ts +18 -0
  54. package/dist/trading/edge.d.ts +92 -0
  55. package/dist/trading/edge.mjs +243 -0
  56. package/dist/{cloudflare → trading}/index.cjs +15 -1
  57. package/dist/{cloudflare → trading}/index.d.ts +15 -3
  58. package/dist/{cloudflare → trading}/index.mjs +15 -1
  59. package/dist/{cloudflare → trading}/internal.cjs +3 -2
  60. package/dist/{cloudflare → trading}/internal.js +3 -2
  61. package/dist/trading/raw/trading-web.d.ts +30 -0
  62. package/dist/trading/raw/trading-web.js +29 -0
  63. package/dist/trading/raw/trading.cjs +72 -0
  64. package/dist/trading/raw/trading.d.ts +33 -0
  65. package/dist/trading/raw/trading.js +35 -0
  66. package/package.json +38 -8
  67. /package/dist/{cloudflare → trading}/callbacks.cjs +0 -0
  68. /package/dist/{cloudflare → trading}/callbacks.js +0 -0
  69. /package/dist/{cloudflare → trading}/envelope.cjs +0 -0
  70. /package/dist/{cloudflare → trading}/envelope.d.ts +0 -0
  71. /package/dist/{cloudflare → trading}/envelope.js +0 -0
  72. /package/dist/{cloudflare → trading}/errors.cjs +0 -0
  73. /package/dist/{cloudflare → trading}/errors.d.ts +0 -0
  74. /package/dist/{cloudflare → trading}/errors.js +0 -0
  75. /package/dist/{cloudflare → trading}/index.js +0 -0
  76. /package/dist/{cloudflare → trading}/internal.d.ts +0 -0
  77. /package/dist/{cloudflare → trading}/options.cjs +0 -0
  78. /package/dist/{cloudflare → trading}/options.d.ts +0 -0
  79. /package/dist/{cloudflare → trading}/options.js +0 -0
package/README.md CHANGED
@@ -1,279 +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 with viem, ethers, wagmi, or an EIP-1193 wallet | `@symbiome-forge/cow-sdk-wasm` | Wallet stack stays outside the package behind typed callbacks |
19
- | Browser dapp with a smaller orderbook bundle target | `@symbiome-forge/cow-sdk-wasm/orderbook` | Orderbook and signing subset with a smaller raw wasm budget |
20
- | Node.js 22 or 24 LTS backend | `@symbiome-forge/cow-sdk-wasm` | Node target works without browser polyfills when transport is configured |
21
- | Cloudflare Worker proxying CoW orderbook calls | `@symbiome-forge/cow-sdk-wasm/cloudflare` | Worker-compatible web target and 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
- | Native Rust service, bot, solver, or treasury automation | `cow-sdk` | Avoids wasm-bindgen and npm packaging entirely |
24
- | 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 |
25
-
26
- ## Not in this crate
27
-
28
- Use the upstream TypeScript SDK packages until these capability families ship
29
- in `cow-rs`:
30
-
31
- - TWAP and composable orders.
32
- - Cross-chain bridging.
33
- - Cow Shed account abstraction.
34
- - Flash-loan helpers.
35
- - Weiroll command planning.
36
- - Hardware wallet adapters.
37
- - On-chain transaction submission; this package emits typed data or
38
- transaction requests and lets the caller's wallet submit.
39
- - WASI, WebAssembly components, TinyGo, Blazor, AssemblyScript guests, and
40
- `no_std` embedded targets.
41
-
42
- ## Quickstart
43
-
44
- ### 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.
45
64
 
46
65
  ```ts
47
- 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) });
48
72
 
49
73
  const trading = new TradingClient({
50
74
  chainId: 1,
51
75
  env: "prod",
52
- appCode: "my-node-service",
53
- transport: { kind: "fetch" },
54
- transportPolicy: {
55
- retryPolicy: { maxAttempts: 3, baseDelayMs: 200 },
56
- userAgent: "my-node-service/1.0"
57
- }
76
+ appCode: "my-dapp",
77
+ transport: { kind: "fetch" }
58
78
  });
59
79
 
60
- // `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.
61
82
  const quote = await trading.getQuote({
62
83
  kind: "sell",
63
- sellToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
64
- buyToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
84
+ owner,
85
+ sellToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
86
+ buyToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
65
87
  amount: "1000000000000000000"
66
88
  });
67
89
 
68
- // Reuse the quote to sign and post in one call; `quote.value` is the
69
- // `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.
70
92
  const result = await trading.postSwapOrderFromQuote(
71
93
  quote.value,
72
- "0x1111111111111111111111111111111111111111",
73
- async (envelope) => walletClient.signTypedData(envelope),
74
- { walletConfig: { timeoutMs: 15_000 } }
75
- );
76
- // `result.value.orderId` is the posted order UID.
77
- ```
78
-
79
- ### Browser with `window.ethereum`
80
-
81
- ```ts
82
- import { signOrderWithEip1193 } from "@symbiome-forge/cow-sdk-wasm";
83
-
84
- const ethereum = window.ethereum;
85
- const [owner] = await ethereum.request({ method: "eth_requestAccounts" });
86
- const abortController = new AbortController();
87
-
88
- // The order to sign: build it yourself or map it from a fetched quote.
89
- const order = {
90
- sellToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
91
- buyToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
92
- receiver: owner,
93
- sellAmount: "1000000000000000000",
94
- buyAmount: "3500000000",
95
- validTo: Math.floor(Date.now() / 1000) + 3_600,
96
- appData: "0x0000000000000000000000000000000000000000000000000000000000000000",
97
- feeAmount: "0",
98
- kind: "sell",
99
- partiallyFillable: false,
100
- sellTokenBalance: "erc20",
101
- buyTokenBalance: "erc20"
102
- };
103
-
104
- const signed = await signOrderWithEip1193(
105
- order,
106
- 1,
107
94
  owner,
108
- (rpc) => ethereum.request(rpc),
109
- { 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 } }
110
109
  );
111
- // `signed.value` is the SignedOrderDto.
112
- ```
113
110
 
114
- ### Browser with MetaMask `eth_signTypedData_v4`
115
-
116
- When the wallet exposes the typed-data JSON-RPC method directly, callers can
117
- pass the envelope to `eth_signTypedData_v4` from inside the typed-data signer
118
- callback. The helper hands the callback a typed-data envelope — plain `domain`,
119
- `types`, `primaryType`, and `message` objects — that the callback serializes and
120
- returns the signature string for.
121
-
122
- ```ts
123
- import { signOrderWithTypedDataSigner } from "@symbiome-forge/cow-sdk-wasm";
111
+ console.log(`https://explorer.cow.fi/mainnet/orders/${result.value.orderId}`);
112
+ trading.dispose();
113
+ ```
124
114
 
125
- const [owner] = await window.ethereum.request({ method: "eth_requestAccounts" });
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.
126
118
 
127
- const signed = await signOrderWithTypedDataSigner(order, 1, owner, async (envelope) => {
128
- const signature = await window.ethereum.request({
129
- method: "eth_signTypedData_v4",
130
- params: [owner, JSON.stringify(envelope)]
131
- });
132
- if (typeof signature !== "string") {
133
- throw new Error("wallet did not return a signature");
134
- }
135
- return signature;
136
- });
137
- ```
119
+ ### Cloudflare Worker (edge)
138
120
 
139
- ### 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`.
140
123
 
141
124
  ```ts
142
- import initialize, {
143
- OrderBookClient
144
- } from "@symbiome-forge/cow-sdk-wasm/cloudflare";
145
- import wasmModule from "@symbiome-forge/cow-sdk-wasm/cloudflare/wasm";
125
+ import initialize, { OrderBookClient } from "@symbiome-forge/cow-sdk-wasm/trading/edge";
126
+ import wasmModule from "@symbiome-forge/cow-sdk-wasm/trading/edge/wasm";
146
127
 
147
128
  export default {
148
129
  async fetch(request: Request, env: Env): Promise<Response> {
149
130
  await initialize(wasmModule);
150
-
151
131
  const client = new OrderBookClient({
152
132
  chainId: 1,
153
133
  env: "prod",
154
134
  apiKey: env.COW_PARTNER_API_KEY ?? null,
155
- transport: { kind: "fetch" },
156
- transportPolicy: { userAgent: "my-worker/1.0" }
157
- });
158
-
159
- const quote = await client.getQuote(await request.json(), {
160
- timeoutMs: 8_000
135
+ transport: { kind: "fetch" }
161
136
  });
137
+ const quote = await client.getQuote(await request.json(), { timeoutMs: 8_000 });
162
138
  client.dispose();
163
-
164
139
  return Response.json(quote);
165
140
  }
166
141
  };
167
142
  ```
168
143
 
169
- ## Choosing your import
144
+ ### Lower-level signing
170
145
 
171
- | Import | Surface | Use when |
172
- | --- | --- | --- |
173
- | `@symbiome-forge/cow-sdk-wasm` | Default facade with orderbook, signing, app-data, IPFS, trading, and subgraph | General TypeScript or Node use |
174
- | `@symbiome-forge/cow-sdk-wasm/orderbook` | Orderbook client, cancellation helpers, and signing helpers | Browser dapps that do not need trading or subgraph clients |
175
- | `@symbiome-forge/cow-sdk-wasm/signing` | Signing, UID, EIP-1271, deployment, and version helpers | Signer services and HSM-facing adapters |
176
- | `@symbiome-forge/cow-sdk-wasm/cloudflare` | Worker-compatible orderbook and trading facade | Cloudflare Workers |
177
- | `@symbiome-forge/cow-sdk-wasm/cloudflare/wasm` | Raw Worker wasm module asset | Pass to the Cloudflare `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`.
178
150
 
179
- Do not import from `dist/raw` or generated wasm-pack target directories. Raw
180
- wasm-bindgen output is package-internal; public imports go through the facade
181
- subpaths above.
151
+ ## The callback boundary
182
152
 
183
- ## 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:
184
155
 
185
- The package is built with release-size settings and a `wasm-opt -Oz` post-pass.
186
- 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.
187
165
 
188
- | Flavor | Raw wasm | Brotli | Gzip | Gate |
189
- | --- | ---: | ---: | ---: | --- |
190
- | default | 1.63 MiB | 511 KiB | 688 KiB | 3.3 MiB raw / 900 KiB brotli |
191
- | orderbook | 1.03 MiB | 341 KiB | 447 KiB | 1.5 MiB raw / 500 KiB brotli |
192
- | signing | 0.31 MiB | 119 KiB | 142 KiB | 0.9 MiB raw / 300 KiB brotli |
193
- | cloudflare | 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.
194
169
 
195
- The cloudflare flavor's gzip-compressed artifact is below the current
196
- Cloudflare Workers Free compressed-size limit at the time of measurement.
197
- Full Workers support still requires release-bundle verification and Worker
198
- startup measurement; the release pipeline enforces the gzip byte budget on
199
- every build, but Wrangler deployment and `startup_time_ms` telemetry are
200
- separate operational gates.
170
+ ## Cancellation and timeouts
201
171
 
202
- Cloudflare Workers cold starts are runtime-sensitive. The package treats
203
- 300 ms as the warning threshold, 500 ms as the release gate, and 1 second as
204
- 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.
205
178
 
206
- ## Transport configuration
179
+ ## Transport
207
180
 
208
- Every client accepts one transport:
181
+ Every client takes one transport:
209
182
 
210
183
  ```ts
211
- transport: { kind: "fetch" }
212
- transport: { kind: "fetch", fetch: customFetch }
213
- 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
214
187
  ```
215
188
 
216
- Use `fetch` for browser, Node, and Worker runtimes that expose a standards
217
- compatible `fetch`. Use `callback` when the host must own request dispatch,
218
- 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.
219
192
 
220
- Every client also accepts optional `transportPolicy` settings for retry,
221
- rate-limit, jitter, and user-agent behavior.
193
+ ## Errors
222
194
 
223
- ## 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
224
204
 
225
- Every call accepts an optional `signal` (an `AbortSignal`) and a per-call
226
- `timeoutMs`. Aborting the signal rejects the pending call promptly with a
227
- `cancelled` `CowError`; `timeoutMs` rejects with a `timeout` error. Both resolve
228
- the *awaited call* an already-dispatched HTTP request may keep running in the
229
- background until it completes or the timeout elapses, so treat cancellation as
230
- "stop waiting," not a guarantee that the network request is halted.
231
-
232
- ## Architecture
233
-
234
- The TypeScript facade is the public package contract. It:
235
-
236
- - exposes camelCase TypeScript APIs;
237
- - exposes `dispose()` and `[Symbol.dispose]` (so `using client = new …` works)
238
- while hiding the raw wasm-bindgen `free()` handle;
239
- - maps raw wasm errors into `CowError`;
240
- - adapts `transport: { kind: "fetch" }` into the callback HTTP ABI;
241
- - keeps wallet libraries outside the package behind named callback types.
242
-
243
- ## API reference
244
-
245
- The declaration snapshots under `crates/wasm/snapshots/facade/` show the
246
- public TypeScript surface for each flavor. Key exports include:
247
-
248
- - clients: `OrderBookClient`, `TradingClient`, `SubgraphClient`, `IpfsClient`;
249
- - signing helpers: `signOrderWithTypedDataSigner`, `signOrderWithEip1193`,
250
- `signOrderEthSignDigest`, `signOrderWithEip1271`,
251
- `signOrderWithCustomEip1271`;
252
- - cancellation helpers: `signCancellationWithTypedDataSigner`,
253
- `signCancellationWithEip1193`, `signCancellationEthSignDigest`,
254
- `buildCancelOrderTx`, `buildPresignTx`;
255
- - pure helpers: `domainSeparator`, `orderTypedData`, `computeOrderUid`,
256
- `deploymentAddresses`, `supportedChainIds`, `appDataInfo`,
257
- `validateAppDataDoc`, `appDataDoc`, `appDataHexToCid`,
258
- `cidToAppDataHex`, `wasmVersion`.
259
-
260
- ## When to use this package vs the upstream TypeScript SDK
261
-
262
- For most browser dapps, web apps, and CowSwap-style UIs, the upstream
263
- [`@cowprotocol/cow-sdk`](https://www.npmjs.com/package/@cowprotocol/cow-sdk)
264
- is the recommended choice; it is substantially smaller at equivalent feature
265
- subsets. This package is appropriate for specialized cases:
266
-
267
- - TypeScript services that need byte-for-byte parity with the Rust SDK's
268
- EIP-712 + EIP-1271 signing path.
269
- - Single-source-of-truth Rust + TypeScript embedding (one implementation
270
- across both runtimes).
271
- - Cloudflare Workers (size-compatible with the current Workers Free
272
- compressed-size limit at the time of measurement; the `cloudflare` flavor
273
- is built and tested end-to-end in CI (Workers Vitest), within the Workers
274
- compressed-size budget).
275
- - Embeddable signing helpers (the `./signing` flavor is the smallest).
276
-
277
- The "When to use this SDK" table at the top of this README routes consumers
278
- by use case. The Quickstart sections above show the supported import shapes
279
- for the most common runtimes.
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):
207
+
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");
@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.wasmVersion = exports.validateAppDataDoc = exports.supportedChainIds = exports.signOrderWithTypedDataSigner = exports.signOrderWithEip1271 = exports.signOrderWithEip1193 = exports.signOrderWithCustomEip1271 = exports.signOrderEthSignDigest = exports.signCancellationWithTypedDataSigner = exports.signCancellationWithEip1193 = exports.signCancellationEthSignDigest = exports.orderTypedData = exports.eip1271SignaturePayload = exports.domainSeparator = exports.deploymentAddresses = exports.decodeSettlementLog = exports.decodeEthFlowLog = exports.computeOrderUid = exports.cidToAppDataHex = exports.buildPresignTx = exports.buildCancelOrderTx = exports.appDataInfo = exports.appDataHexToCid = exports.appDataDoc = exports.__cow_sdk_wasm_init = exports.RawTradingClient = exports.RawOrderBookClient = exports.initializeRaw = void 0;
37
- const cow_sdk_wasm_js_1 = __importStar(require("../../raw/cloudflare-web/cow_sdk_wasm.cjs")), wasm = cow_sdk_wasm_js_1;
37
+ const cow_sdk_wasm_js_1 = __importStar(require("../../raw/trading-web/cow_sdk_wasm.cjs")), wasm = cow_sdk_wasm_js_1;
38
38
  exports.initializeRaw = cow_sdk_wasm_js_1.default;
39
39
  exports.RawOrderBookClient = wasm.OrderBookClient;
40
40
  exports.RawTradingClient = wasm.TradingClient;
@@ -1,5 +1,5 @@
1
- import init, * as wasm from "../../raw/cloudflare-web/cow_sdk_wasm.js";
2
- export type * from "../../raw/cloudflare-web/cow_sdk_wasm.js";
1
+ import init, * as wasm from "../../raw/trading-web/cow_sdk_wasm.js";
2
+ export type * from "../../raw/trading-web/cow_sdk_wasm.js";
3
3
  export declare const initializeRaw: typeof init;
4
4
  export declare const RawOrderBookClient: typeof wasm.OrderBookClient;
5
5
  export declare const RawTradingClient: typeof wasm.TradingClient;
@@ -1,4 +1,4 @@
1
- import init, * as wasm from "../../raw/cloudflare-web/cow_sdk_wasm.js";
1
+ import init, * as wasm from "../../raw/trading-web/cow_sdk_wasm.js";
2
2
  export const initializeRaw = init;
3
3
  export const RawOrderBookClient = wasm.OrderBookClient;
4
4
  export const RawTradingClient = wasm.TradingClient;
@@ -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");