@anywalletkit/protocol-swap2-relay 0.0.0-stage → 0.1.0

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 (58) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +116 -2
  3. package/dist/activity.cjs +82 -0
  4. package/dist/activity.cjs.map +1 -0
  5. package/dist/activity.d.cts +15 -0
  6. package/dist/activity.d.ts +15 -0
  7. package/dist/activity.js +10 -0
  8. package/dist/activity.js.map +1 -0
  9. package/dist/chunk-7LPLU3MB.js +27 -0
  10. package/dist/chunk-7LPLU3MB.js.map +1 -0
  11. package/dist/chunk-CCMNAZZB.js +307 -0
  12. package/dist/chunk-CCMNAZZB.js.map +1 -0
  13. package/dist/chunk-JQAN7HB5.js +80 -0
  14. package/dist/chunk-JQAN7HB5.js.map +1 -0
  15. package/dist/chunk-K5F3BEUO.js +22 -0
  16. package/dist/chunk-K5F3BEUO.js.map +1 -0
  17. package/dist/chunk-UVHDMGM5.js +190 -0
  18. package/dist/chunk-UVHDMGM5.js.map +1 -0
  19. package/dist/chunk-WRIKAUWI.js +384 -0
  20. package/dist/chunk-WRIKAUWI.js.map +1 -0
  21. package/dist/config.cjs +100 -0
  22. package/dist/config.cjs.map +1 -0
  23. package/dist/config.d.cts +58 -0
  24. package/dist/config.d.ts +58 -0
  25. package/dist/config.js +35 -0
  26. package/dist/config.js.map +1 -0
  27. package/dist/index.cjs +965 -0
  28. package/dist/index.cjs.map +1 -0
  29. package/dist/index.d.cts +11 -0
  30. package/dist/index.d.ts +11 -0
  31. package/dist/index.js +98 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/order.cjs +210 -0
  34. package/dist/order.cjs.map +1 -0
  35. package/dist/order.d.cts +178 -0
  36. package/dist/order.d.ts +178 -0
  37. package/dist/order.js +15 -0
  38. package/dist/order.js.map +1 -0
  39. package/dist/protocol.cjs +922 -0
  40. package/dist/protocol.cjs.map +1 -0
  41. package/dist/protocol.d.cts +16 -0
  42. package/dist/protocol.d.ts +16 -0
  43. package/dist/protocol.js +12 -0
  44. package/dist/protocol.js.map +1 -0
  45. package/dist/provider.cjs +907 -0
  46. package/dist/provider.cjs.map +1 -0
  47. package/dist/provider.d.cts +31 -0
  48. package/dist/provider.d.ts +31 -0
  49. package/dist/provider.js +13 -0
  50. package/dist/provider.js.map +1 -0
  51. package/dist/verify.cjs +617 -0
  52. package/dist/verify.cjs.map +1 -0
  53. package/dist/verify.d.cts +151 -0
  54. package/dist/verify.d.ts +151 -0
  55. package/dist/verify.js +39 -0
  56. package/dist/verify.js.map +1 -0
  57. package/module.json +42 -0
  58. package/package.json +99 -3
package/dist/index.cjs ADDED
@@ -0,0 +1,965 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ RELAY_ALLOCATOR: () => RELAY_ALLOCATOR,
24
+ RELAY_API: () => RELAY_API,
25
+ RELAY_CHAINS: () => RELAY_CHAINS,
26
+ RELAY_CLOCK_SKEW_S: () => RELAY_CLOCK_SKEW_S,
27
+ RELAY_DEFAULT_SLIPPAGE_BPS: () => RELAY_DEFAULT_SLIPPAGE_BPS,
28
+ RELAY_DEPOSIT_ERC20: () => RELAY_DEPOSIT_ERC20,
29
+ RELAY_DEPOSIT_ERC20_ALLOWANCE: () => RELAY_DEPOSIT_ERC20_ALLOWANCE,
30
+ RELAY_DEPOSIT_NATIVE: () => RELAY_DEPOSIT_NATIVE,
31
+ RELAY_GAS_CAP: () => RELAY_GAS_CAP,
32
+ RELAY_GAS_FLOOR_ERC20: () => RELAY_GAS_FLOOR_ERC20,
33
+ RELAY_GAS_FLOOR_NATIVE: () => RELAY_GAS_FLOOR_NATIVE,
34
+ RELAY_MAX_ORDER_AGE_S: () => RELAY_MAX_ORDER_AGE_S,
35
+ RELAY_MAX_QUOTE_LIFE_MS: () => RELAY_MAX_QUOTE_LIFE_MS,
36
+ RELAY_MAX_SLIPPAGE_BPS: () => RELAY_MAX_SLIPPAGE_BPS,
37
+ RELAY_MODULE: () => RELAY_MODULE,
38
+ RELAY_NATIVE: () => RELAY_NATIVE,
39
+ RELAY_ORDER_LIFETIME_S: () => RELAY_ORDER_LIFETIME_S,
40
+ RELAY_ORDER_TYPES: () => RELAY_ORDER_TYPES,
41
+ RELAY_ORDER_VERSION: () => RELAY_ORDER_VERSION,
42
+ RELAY_OWNER: () => RELAY_OWNER,
43
+ RELAY_SOLVERS: () => RELAY_SOLVERS,
44
+ RELAY_STATUS_MAP: () => RELAY_STATUS_MAP,
45
+ RelayRefusal: () => RelayRefusal,
46
+ RelaySwap2Protocol: () => RelaySwap2Protocol,
47
+ buildRelayDeposit: () => buildRelayDeposit,
48
+ checkRelayQuote: () => checkRelayQuote,
49
+ default: () => RelaySwap2Protocol,
50
+ describeRelayActivity: () => describeRelayActivity,
51
+ parseRelayOrder: () => parseRelayOrder,
52
+ relay: () => relay,
53
+ relayChain: () => relayChain,
54
+ relayClearOrder: () => relayClearOrder,
55
+ relayOrderId: () => relayOrderId,
56
+ relayOrderSigner: () => relayOrderSigner,
57
+ relayPins: () => relayPins,
58
+ relayProtocolOf: () => relayProtocolOf,
59
+ relayProvider: () => relayProvider,
60
+ relayRefusalReason: () => relayRefusalReason,
61
+ relayRequestOf: () => relayRequestOf,
62
+ relayVerifyBuild: () => relayVerifyBuild,
63
+ relayVerifyQuote: () => relayVerifyQuote,
64
+ verifyRelayOrder: () => verifyRelayOrder
65
+ });
66
+ module.exports = __toCommonJS(src_exports);
67
+
68
+ // src/config.ts
69
+ var DEPOSITORY = "0x4cd00e387622c35bddb9b4c962c136462338bc31";
70
+ var DEPOSITORY_B = "0x59916da825d2d2ec1bf878d71c88826f6633ecca";
71
+ var c = (chainId, slug, name, depository = DEPOSITORY) => Object.freeze({ caip2: `eip155:${chainId}`, chainId, slug, name, depository });
72
+ var RELAY_CHAINS = Object.freeze(Object.assign(/* @__PURE__ */ Object.create(null), Object.fromEntries([
73
+ c(1, "ethereum", "Ethereum"),
74
+ c(10, "optimism", "OP Mainnet"),
75
+ c(56, "bnb", "BNB Chain"),
76
+ c(100, "gnosis", "Gnosis"),
77
+ c(130, "unichain", "Unichain"),
78
+ c(137, "polygon", "Polygon"),
79
+ c(143, "monad", "Monad"),
80
+ c(146, "sonic", "Sonic"),
81
+ c(196, "xlayer", "X Layer"),
82
+ c(324, "zksync", "zkSync Era"),
83
+ c(480, "worldchain", "World Chain"),
84
+ c(999, "hyperevm", "HyperEVM"),
85
+ c(2818, "morph", "Morph"),
86
+ c(4326, "megaeth", "MegaETH"),
87
+ c(4663, "robinhood", "Robinhood Chain"),
88
+ c(5e3, "mantle", "Mantle", DEPOSITORY_B),
89
+ c(5042, "arc", "Arc"),
90
+ c(8453, "base", "Base"),
91
+ c(9745, "plasma", "Plasma"),
92
+ c(42161, "arbitrum", "Arbitrum"),
93
+ c(42220, "celo", "Celo"),
94
+ c(43114, "avalanche", "Avalanche"),
95
+ c(57073, "ink", "Ink"),
96
+ c(59144, "linea", "Linea", DEPOSITORY_B),
97
+ c(80094, "berachain", "Berachain"),
98
+ c(98866, "plume", "Plume"),
99
+ c(99901, "elysium", "Elysium"),
100
+ c(534352, "scroll", "Scroll"),
101
+ c(747474, "katana", "Katana")
102
+ ].map((x) => [x.caip2, x]))));
103
+ function relayChain(caip2) {
104
+ return typeof caip2 === "string" && Object.prototype.hasOwnProperty.call(RELAY_CHAINS, caip2) ? RELAY_CHAINS[caip2] : void 0;
105
+ }
106
+ var RELAY_SOLVERS = Object.freeze([
107
+ Object.freeze({ chainId: "base", address: "0xf70da97812cb96acdf810712aa562db8dfa3dbef" })
108
+ ]);
109
+ var RELAY_ALLOCATOR = "0x63c1d3e9c646184529c5694630a01c00df171b56";
110
+ var RELAY_OWNER = "0xf61a305199fa1135d76ffab3752d42f55cbd775a";
111
+ var RELAY_API = "https://api.relay.link";
112
+ var RELAY_NATIVE = "0x0000000000000000000000000000000000000000";
113
+ var RELAY_DEPOSIT_NATIVE = "0x49290c1c";
114
+ var RELAY_DEPOSIT_ERC20 = "0xe8017952";
115
+ var RELAY_DEPOSIT_ERC20_ALLOWANCE = "0x5a1ee3ac";
116
+ var RELAY_ORDER_VERSION = "v1";
117
+ var RELAY_ORDER_LIFETIME_S = 604800;
118
+ var RELAY_MAX_ORDER_AGE_S = 600;
119
+ var RELAY_CLOCK_SKEW_S = 300;
120
+ var relay = {
121
+ supportsChain(chain) {
122
+ return relayChain(chain) !== void 0;
123
+ },
124
+ /** Two different pinned EVM chains. */
125
+ supports(from, to) {
126
+ return from !== to && relayChain(from) !== void 0 && relayChain(to) !== void 0;
127
+ }
128
+ };
129
+
130
+ // src/order.ts
131
+ var import_secp256k1 = require("@noble/curves/secp256k1.js");
132
+ var import_viem = require("viem");
133
+ var RelayRefusal = class extends Error {
134
+ code;
135
+ constructor(code, detail) {
136
+ super(`relay: ${detail} [${code}]`);
137
+ this.name = "RelayRefusal";
138
+ this.code = code;
139
+ }
140
+ };
141
+ var RELAY_ORDER_TYPES = {
142
+ Order: [
143
+ { name: "version", type: "string" },
144
+ { name: "solverChainId", type: "string" },
145
+ { name: "solver", type: "address" },
146
+ { name: "salt", type: "uint256" },
147
+ { name: "inputs", type: "Input[]" },
148
+ { name: "output", type: "Output" },
149
+ { name: "fees", type: "Fee[]" }
150
+ ],
151
+ Input: [
152
+ { name: "payment", type: "InputPayment" },
153
+ { name: "refunds", type: "InputRefund[]" }
154
+ ],
155
+ InputPayment: [
156
+ { name: "chainId", type: "string" },
157
+ { name: "currency", type: "bytes" },
158
+ { name: "amount", type: "uint256" },
159
+ { name: "weight", type: "uint256" }
160
+ ],
161
+ InputRefund: [
162
+ { name: "chainId", type: "string" },
163
+ { name: "recipient", type: "bytes" },
164
+ { name: "currency", type: "bytes" },
165
+ { name: "minimumAmount", type: "uint256" },
166
+ { name: "deadline", type: "uint32" },
167
+ { name: "extraData", type: "bytes" }
168
+ ],
169
+ Output: [
170
+ { name: "chainId", type: "string" },
171
+ { name: "payments", type: "OutputPayment[]" },
172
+ { name: "deadline", type: "uint32" },
173
+ { name: "calls", type: "bytes[]" },
174
+ { name: "extraData", type: "bytes" }
175
+ ],
176
+ OutputPayment: [
177
+ { name: "recipient", type: "bytes" },
178
+ { name: "currency", type: "bytes" },
179
+ { name: "minimumAmount", type: "uint256" },
180
+ { name: "expectedAmount", type: "uint256" }
181
+ ],
182
+ Fee: [
183
+ { name: "recipientChainId", type: "string" },
184
+ { name: "recipient", type: "bytes" },
185
+ { name: "currencyChainId", type: "string" },
186
+ { name: "currency", type: "bytes" },
187
+ { name: "amount", type: "uint256" }
188
+ ]
189
+ };
190
+ var MAX_LIST = 8;
191
+ var MAX_BYTES_HEX = 2 + 2 * 1024;
192
+ var MAX_TEXT = 64;
193
+ var U256_MAX = (1n << 256n) - 1n;
194
+ var bad = (what) => {
195
+ throw new RelayRefusal("order-malformed", `the order's ${what}`);
196
+ };
197
+ function rec(v, what) {
198
+ if (v === null || typeof v !== "object" || Array.isArray(v)) bad(`${what} is not an object`);
199
+ return v;
200
+ }
201
+ function list(v, what) {
202
+ if (!Array.isArray(v)) bad(`${what} is not a list`);
203
+ if (v.length > MAX_LIST) bad(`${what} has more than ${MAX_LIST} entries`);
204
+ return v;
205
+ }
206
+ function own(o, key) {
207
+ return Object.prototype.hasOwnProperty.call(o, key) ? o[key] : void 0;
208
+ }
209
+ function text(v, what) {
210
+ if (typeof v !== "string" || v.length === 0 || v.length > MAX_TEXT) bad(`${what} is not a short string`);
211
+ return v;
212
+ }
213
+ function bytes(v, what) {
214
+ if (typeof v !== "string" || v.length > MAX_BYTES_HEX || !/^0x(?:[0-9a-fA-F]{2})*$/.test(v)) bad(`${what} is not hex bytes`);
215
+ return v.toLowerCase();
216
+ }
217
+ function uint(v, what, max = U256_MAX) {
218
+ let n = null;
219
+ if (typeof v === "string" && (/^(?:0|[1-9]\d{0,77})$/.test(v) || /^0x[0-9a-fA-F]{1,64}$/.test(v))) n = BigInt(v);
220
+ else if (typeof v === "number" && Number.isSafeInteger(v) && v >= 0) n = BigInt(v);
221
+ if (n === null || n > max) bad(`${what} is not an unsigned integer in range`);
222
+ return n;
223
+ }
224
+ function uint32(v, what) {
225
+ return parseInt(uint(v, what, 0xffffffffn).toString(10), 10);
226
+ }
227
+ function address(v, what) {
228
+ if (typeof v !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(v)) bad(`${what} is not an address`);
229
+ return v.toLowerCase();
230
+ }
231
+ function parseRelayOrder(raw) {
232
+ const o = rec(raw, "body");
233
+ const output = rec(own(o, "output"), "output");
234
+ return {
235
+ version: text(own(o, "version"), "version"),
236
+ solverChainId: text(own(o, "solverChainId"), "solver chain"),
237
+ solver: address(own(o, "solver"), "solver"),
238
+ salt: uint(own(o, "salt"), "salt"),
239
+ inputs: list(own(o, "inputs"), "inputs").map((x, i) => {
240
+ const input = rec(x, `input ${i}`);
241
+ const p = rec(own(input, "payment"), `input ${i} payment`);
242
+ return {
243
+ payment: {
244
+ chainId: text(own(p, "chainId"), `input ${i} chain`),
245
+ currency: bytes(own(p, "currency"), `input ${i} currency`),
246
+ amount: uint(own(p, "amount"), `input ${i} amount`),
247
+ weight: uint(own(p, "weight"), `input ${i} weight`)
248
+ },
249
+ refunds: list(own(input, "refunds"), `input ${i} refunds`).map((y, j) => {
250
+ const r = rec(y, `refund ${j}`);
251
+ return {
252
+ chainId: text(own(r, "chainId"), `refund ${j} chain`),
253
+ recipient: bytes(own(r, "recipient"), `refund ${j} recipient`),
254
+ currency: bytes(own(r, "currency"), `refund ${j} currency`),
255
+ minimumAmount: uint(own(r, "minimumAmount"), `refund ${j} minimum`),
256
+ deadline: uint32(own(r, "deadline"), `refund ${j} deadline`),
257
+ extraData: bytes(own(r, "extraData"), `refund ${j} extra data`)
258
+ };
259
+ })
260
+ };
261
+ }),
262
+ output: {
263
+ chainId: text(own(output, "chainId"), "output chain"),
264
+ payments: list(own(output, "payments"), "output payments").map((x, i) => {
265
+ const p = rec(x, `output payment ${i}`);
266
+ return {
267
+ recipient: bytes(own(p, "recipient"), `output payment ${i} recipient`),
268
+ currency: bytes(own(p, "currency"), `output payment ${i} currency`),
269
+ minimumAmount: uint(own(p, "minimumAmount"), `output payment ${i} minimum`),
270
+ expectedAmount: uint(own(p, "expectedAmount"), `output payment ${i} expected amount`)
271
+ };
272
+ }),
273
+ deadline: uint32(own(output, "deadline"), "output deadline"),
274
+ calls: list(own(output, "calls"), "output calls").map((x, i) => bytes(x, `output call ${i}`)),
275
+ extraData: bytes(own(output, "extraData"), "output extra data")
276
+ },
277
+ fees: list(own(o, "fees"), "fees").map((x, i) => {
278
+ const f = rec(x, `fee ${i}`);
279
+ return {
280
+ recipientChainId: text(own(f, "recipientChainId"), `fee ${i} recipient chain`),
281
+ recipient: bytes(own(f, "recipient"), `fee ${i} recipient`),
282
+ currencyChainId: text(own(f, "currencyChainId"), `fee ${i} currency chain`),
283
+ currency: bytes(own(f, "currency"), `fee ${i} currency`),
284
+ amount: uint(own(f, "amount"), `fee ${i} amount`)
285
+ };
286
+ })
287
+ };
288
+ }
289
+ function relayOrderId(order) {
290
+ return (0, import_viem.hashStruct)({ types: RELAY_ORDER_TYPES, primaryType: "Order", data: order });
291
+ }
292
+ function relayOrderSigner(id, signature) {
293
+ if (typeof signature !== "string" || !/^0x[0-9a-fA-F]{130}$/.test(signature)) return null;
294
+ const sig = (0, import_viem.hexToBytes)(signature);
295
+ const v = sig[64];
296
+ const recovery = v === 27 || v === 28 ? v - 27 : v === 0 || v === 1 ? v : -1;
297
+ if (recovery < 0) return null;
298
+ try {
299
+ const recoverable = new Uint8Array(65);
300
+ recoverable[0] = recovery;
301
+ recoverable.set(sig.subarray(0, 64), 1);
302
+ const digest = (0, import_viem.hexToBytes)((0, import_viem.hashMessage)({ raw: id }));
303
+ const key = import_secp256k1.secp256k1.recoverPublicKey(recoverable, digest, { prehash: false });
304
+ if (!import_secp256k1.secp256k1.verify(sig.subarray(0, 64), digest, key, { prehash: false })) return null;
305
+ const uncompressed = import_secp256k1.secp256k1.Point.fromBytes(key).toBytes(false);
306
+ return `0x${(0, import_viem.keccak256)(uncompressed.subarray(1)).slice(-40)}`.toLowerCase();
307
+ } catch {
308
+ return null;
309
+ }
310
+ }
311
+
312
+ // src/verify.ts
313
+ var import_viem2 = require("viem");
314
+ var import_util = require("@anywalletkit/protocol-swap2-core/util");
315
+
316
+ // src/cleared.ts
317
+ var CLEARED_TTL_MS = (RELAY_MAX_ORDER_AGE_S + 2 * RELAY_CLOCK_SKEW_S) * 1e3;
318
+ var CLEARED_MAX = 4096;
319
+ var clearedOrders = /* @__PURE__ */ new Map();
320
+ function relayClearOrder(id, stage, nowMs = Date.now()) {
321
+ const key = `${stage}:${id.toLowerCase()}`;
322
+ const until = clearedOrders.get(key);
323
+ if (until !== void 0 && until > nowMs) throw new RelayRefusal("order-reused", `order ${id} was already cleared for a deposit \u2014 quote again`);
324
+ if (clearedOrders.size >= CLEARED_MAX) {
325
+ for (const [k, t] of clearedOrders) if (t <= nowMs) clearedOrders.delete(k);
326
+ if (clearedOrders.size >= CLEARED_MAX) throw new RelayRefusal("order-reused", "too many deposits cleared in the last minutes \u2014 try again later");
327
+ }
328
+ clearedOrders.set(key, nowMs + CLEARED_TTL_MS);
329
+ }
330
+
331
+ // src/verify.ts
332
+ var DEPOSITORY_ABI = (0, import_viem2.parseAbi)([
333
+ "function depositNative(address depositor, bytes32 id) payable",
334
+ "function depositErc20(address depositor, address token, uint256 amount, bytes32 id)"
335
+ ]);
336
+ var RELAY_MAX_SLIPPAGE_BPS = 5e3;
337
+ var RELAY_DEFAULT_SLIPPAGE_BPS = 200;
338
+ var RELAY_GAS_FLOOR_NATIVE = 90000n;
339
+ var RELAY_GAS_FLOOR_ERC20 = 180000n;
340
+ var RELAY_GAS_CAP = 60000000n;
341
+ var RELAY_MAX_QUOTE_LIFE_MS = 3e5 + RELAY_CLOCK_SKEW_S * 1e3;
342
+ var ADDRESS = /^0x[0-9a-fA-F]{40}$/;
343
+ var U256_MAX2 = (1n << 256n) - 1n;
344
+ var lower = (s) => s.toLowerCase();
345
+ var isNative = (token) => lower(token) === RELAY_NATIVE;
346
+ function buildRelayDeposit(source, depositor, token, amount, id) {
347
+ if (!ADDRESS.test(depositor) || /^0x0{40}$/.test(depositor)) throw new RelayRefusal("request-invalid", "the depositor is not an address");
348
+ if (!ADDRESS.test(token)) throw new RelayRefusal("request-invalid", "the input token is not an address");
349
+ if (amount <= 0n || amount > U256_MAX2) throw new RelayRefusal("request-invalid", "the amount is not a positive integer");
350
+ if (!/^0x[0-9a-f]{64}$/.test(id)) throw new RelayRefusal("order-id-mismatch", "the order id is not 32 bytes");
351
+ if (isNative(token)) {
352
+ return {
353
+ to: source.depository,
354
+ data: (0, import_viem2.encodeFunctionData)({ abi: DEPOSITORY_ABI, functionName: "depositNative", args: [lower(depositor), id] }),
355
+ value: amount.toString(10),
356
+ chainId: source.chainId
357
+ };
358
+ }
359
+ return {
360
+ to: source.depository,
361
+ data: (0, import_viem2.encodeFunctionData)({ abi: DEPOSITORY_ABI, functionName: "depositErc20", args: [lower(depositor), lower(token), amount, id] }),
362
+ value: "0",
363
+ chainId: source.chainId,
364
+ approvalAddress: source.depository
365
+ };
366
+ }
367
+ function checkRequest(r) {
368
+ const invalid = (what) => {
369
+ throw new RelayRefusal("request-invalid", what);
370
+ };
371
+ if (r === null || typeof r !== "object") invalid("no request to check the order against");
372
+ const source = relayChain(r.sourceChain);
373
+ const dest = relayChain(r.destChain);
374
+ if (!source) throw new RelayRefusal("chain-unsupported", `source chain ${String(r.sourceChain)} is not a pinned Relay chain`);
375
+ if (!dest) throw new RelayRefusal("chain-unsupported", `destination chain ${String(r.destChain)} is not a pinned Relay chain`);
376
+ if (source === dest) throw new RelayRefusal("chain-unsupported", "source and destination are the same chain");
377
+ for (const [what, v] of [["input token", r.sourceToken], ["output token", r.destToken], ["depositor", r.depositor], ["recipient", r.recipient]]) {
378
+ if (typeof v !== "string" || !ADDRESS.test(v)) invalid(`the ${what} is not an address`);
379
+ }
380
+ if (/^0x0{40}$/.test(r.depositor)) invalid("the depositor is the zero address");
381
+ if (/^0x0{40}$/.test(r.recipient)) invalid("the recipient is the zero address");
382
+ if (typeof r.amount !== "bigint" || r.amount <= 0n || r.amount > U256_MAX2) invalid("the amount is not a positive integer");
383
+ if (r.minAmountOut !== void 0 && (typeof r.minAmountOut !== "bigint" || r.minAmountOut < 0n)) invalid("the output floor is not an unsigned integer");
384
+ if (r.slippageBps !== void 0 && (!Number.isInteger(r.slippageBps) || r.slippageBps < 0 || r.slippageBps > RELAY_MAX_SLIPPAGE_BPS)) {
385
+ invalid(`slippage must be a whole number of basis points up to ${RELAY_MAX_SLIPPAGE_BPS}`);
386
+ }
387
+ const nowMs = r.nowMs ?? Date.now();
388
+ if (!Number.isFinite(nowMs) || nowMs <= 0) invalid("the clock is not a time");
389
+ return { source, dest, nowS: Math.floor(nowMs / 1e3) };
390
+ }
391
+ var isAddr = (bytes2, addr) => bytes2.length === 42 && bytes2 === lower(addr);
392
+ function checkDeadline(code, what, deadline, nowS) {
393
+ const left = deadline - nowS;
394
+ if (left > RELAY_ORDER_LIFETIME_S + RELAY_CLOCK_SKEW_S) {
395
+ throw new RelayRefusal(code, `${what} is ${left} s away, past the ${RELAY_ORDER_LIFETIME_S} s an order lives`);
396
+ }
397
+ if (left < RELAY_ORDER_LIFETIME_S - RELAY_MAX_ORDER_AGE_S) {
398
+ throw new RelayRefusal(code, `${what} is ${left} s away: the order is older than ${RELAY_MAX_ORDER_AGE_S} s or already past it`);
399
+ }
400
+ }
401
+ function wordAddress(word) {
402
+ return /^0{24}/.test(word) ? `0x${word.slice(24)}` : null;
403
+ }
404
+ function checkDepositTarget(tx, source) {
405
+ if (tx === null || typeof tx !== "object") throw new RelayRefusal("tx-calldata", "there is no deposit transaction");
406
+ if (tx.chainId !== source.chainId) {
407
+ throw new RelayRefusal("tx-chain", `the deposit is for chain ${String(tx.chainId)}, not ${source.chainId}`);
408
+ }
409
+ if (typeof tx.to !== "string" || !ADDRESS.test(tx.to) || lower(tx.to) !== source.depository) {
410
+ throw new RelayRefusal("tx-target", `the deposit calls ${String(tx.to)}, not the ${source.name} depository ${source.depository} \u2014 routes that first swap on the source chain go through a router and are not signed by this module`);
411
+ }
412
+ }
413
+ function checkDepositTx(tx, source, request, order, id) {
414
+ checkDepositTarget(tx, source);
415
+ if (typeof tx.data !== "string" || !/^0x(?:[0-9a-fA-F]{2})*$/.test(tx.data) || tx.data.length < 10) {
416
+ throw new RelayRefusal("tx-calldata", "the deposit has no calldata");
417
+ }
418
+ const data = lower(tx.data);
419
+ const selector = data.slice(0, 10);
420
+ const input = order.inputs[0].payment;
421
+ const native = isNative(input.currency);
422
+ if (selector === RELAY_DEPOSIT_ERC20_ALLOWANCE) {
423
+ throw new RelayRefusal("tx-selector", "the deposit uses depositErc20 without an amount, which takes the whole allowance");
424
+ }
425
+ if (selector !== RELAY_DEPOSIT_NATIVE && selector !== RELAY_DEPOSIT_ERC20) {
426
+ throw new RelayRefusal("tx-selector", `the deposit calls ${selector}, not a known deposit function`);
427
+ }
428
+ if (selector === RELAY_DEPOSIT_NATIVE !== native) {
429
+ throw new RelayRefusal("tx-selector", `the deposit calls ${native ? "depositErc20" : "depositNative"} for an order whose input is ${native ? "the native coin" : "a token"}`);
430
+ }
431
+ const words = native ? 2 : 4;
432
+ if (data.length !== 10 + 64 * words) {
433
+ throw new RelayRefusal("tx-calldata", `the deposit calldata is ${(data.length - 2) / 2} bytes, not ${4 + 32 * words}`);
434
+ }
435
+ const word = (i) => data.slice(10 + 64 * i, 74 + 64 * i);
436
+ const depositor = wordAddress(word(0));
437
+ if (depositor === null) throw new RelayRefusal("tx-calldata", "the depositor argument is not a padded address");
438
+ if (depositor !== lower(request.depositor)) {
439
+ throw new RelayRefusal("tx-depositor", `the deposit is credited to ${depositor}, not to the sender ${lower(request.depositor)}`);
440
+ }
441
+ if (!native) {
442
+ const token = wordAddress(word(1));
443
+ if (token === null) throw new RelayRefusal("tx-calldata", "the token argument is not a padded address");
444
+ if (token !== input.currency) throw new RelayRefusal("tx-token", `the deposit moves ${token}, the order's input is ${input.currency}`);
445
+ const amount = BigInt(`0x${word(2)}`);
446
+ if (amount !== input.amount) throw new RelayRefusal("tx-amount", `the deposit moves ${amount}, the order's input is ${input.amount}`);
447
+ }
448
+ const txId = `0x${word(native ? 1 : 3)}`;
449
+ if (txId !== id) throw new RelayRefusal("tx-id", `the deposit names order ${txId}, the order received hashes to ${id}`);
450
+ const value = typeof tx.value === "string" && /^(?:0|[1-9]\d{0,77})$/.test(tx.value) ? BigInt(tx.value) : null;
451
+ const wantValue = native ? input.amount : 0n;
452
+ if (value === null || value !== wantValue) {
453
+ throw new RelayRefusal("tx-value", `the deposit sends ${String(tx.value)} wei, it must send ${wantValue}`);
454
+ }
455
+ }
456
+ function verifyRelayOrder(request, protocol, deposit) {
457
+ const { source, dest, nowS } = checkRequest(request);
458
+ if (deposit !== void 0) checkDepositTarget(deposit, source);
459
+ if (protocol === null || typeof protocol !== "object") {
460
+ throw new RelayRefusal("protocol-data-missing", "the quote carries no order (protocol.v2) \u2014 nothing to check the deposit against");
461
+ }
462
+ if (protocol.orderData === void 0 || protocol.orderData === null) {
463
+ throw new RelayRefusal("protocol-data-missing", "the quote carries no order data \u2014 nothing to check the deposit against");
464
+ }
465
+ if (protocol.hubType !== "onchain") {
466
+ throw new RelayRefusal("hub-type", `the order settles through a "${String(protocol.hubType)}" hub, only "onchain" is read`);
467
+ }
468
+ const order = parseRelayOrder(protocol.orderData);
469
+ if (order.version !== RELAY_ORDER_VERSION) throw new RelayRefusal("order-version", `order version "${order.version}" is not ${RELAY_ORDER_VERSION}`);
470
+ if (order.inputs.length !== 1) throw new RelayRefusal("order-inputs", `the order has ${order.inputs.length} inputs, exactly one is signed for`);
471
+ if (order.output.payments.length !== 1) throw new RelayRefusal("order-payments", `the order pays ${order.output.payments.length} outputs, exactly one is accepted`);
472
+ if (order.output.calls.length !== 0) throw new RelayRefusal("order-calls", "the order runs calls on the destination \u2014 only plain payments are accepted");
473
+ if (order.fees.length !== 0) throw new RelayRefusal("order-fees", "the order carries order-level fees, which this module does not show and does not accept");
474
+ const input = order.inputs[0];
475
+ const pay = order.output.payments[0];
476
+ if (input.payment.weight === 0n) throw new RelayRefusal("input-weight", "the order's input has zero weight");
477
+ if (!RELAY_SOLVERS.some((s) => s.address === order.solver && s.chainId === order.solverChainId)) {
478
+ throw new RelayRefusal("solver-not-pinned", `solver ${order.solver} on "${order.solverChainId}" is not a pinned Relay solver`);
479
+ }
480
+ if (order.output.chainId !== dest.slug) throw new RelayRefusal("output-chain", `the order pays on "${order.output.chainId}", the request is for ${dest.name} ("${dest.slug}")`);
481
+ if (!isAddr(pay.recipient, request.recipient)) throw new RelayRefusal("output-recipient", `the order pays ${pay.recipient}, not the recipient ${lower(request.recipient)}`);
482
+ if (!isAddr(pay.currency, request.destToken)) throw new RelayRefusal("output-currency", `the order pays in ${pay.currency}, the request is for ${lower(request.destToken)}`);
483
+ if (pay.minimumAmount <= 0n) throw new RelayRefusal("output-minimum", "the order's minimum output is zero");
484
+ if (request.minAmountOut !== void 0 && pay.minimumAmount < request.minAmountOut) {
485
+ throw new RelayRefusal("output-minimum", `the order's minimum output ${pay.minimumAmount} is under the caller's floor ${request.minAmountOut}`);
486
+ }
487
+ if (pay.expectedAmount < pay.minimumAmount) throw new RelayRefusal("output-expected", `the order expects ${pay.expectedAmount}, less than its own minimum ${pay.minimumAmount}`);
488
+ const slippage = BigInt(request.slippageBps ?? RELAY_DEFAULT_SLIPPAGE_BPS);
489
+ const floor = pay.expectedAmount * (10000n - slippage) / 10000n;
490
+ if (pay.minimumAmount < floor) {
491
+ throw new RelayRefusal("output-minimum", `the order's minimum output ${pay.minimumAmount} is more than ${slippage} bps under its expected ${pay.expectedAmount}`);
492
+ }
493
+ checkDeadline("output-deadline", "the output deadline", order.output.deadline, nowS);
494
+ if (input.payment.chainId !== source.slug) throw new RelayRefusal("input-chain", `the order is funded on "${input.payment.chainId}", the request sends on ${source.name} ("${source.slug}")`);
495
+ if (!isAddr(input.payment.currency, request.sourceToken)) throw new RelayRefusal("input-currency", `the order is funded in ${input.payment.currency}, the request sends ${lower(request.sourceToken)}`);
496
+ if (input.payment.amount !== request.amount) throw new RelayRefusal("input-amount", `the order is funded with ${input.payment.amount}, the request sends ${request.amount}`);
497
+ let originRefund = false;
498
+ for (const refund of input.refunds) {
499
+ if (refund.chainId === source.slug) {
500
+ if (!isAddr(refund.recipient, request.depositor)) throw new RelayRefusal("refund-recipient", `a source-chain refund goes to ${refund.recipient}, not the sender ${lower(request.depositor)}`);
501
+ if (refund.currency !== input.payment.currency) throw new RelayRefusal("refund-currency", `a source-chain refund is in ${refund.currency}, not the token sent`);
502
+ originRefund = true;
503
+ } else if (refund.chainId === dest.slug) {
504
+ if (!isAddr(refund.recipient, request.recipient) && !isAddr(refund.recipient, request.depositor)) {
505
+ throw new RelayRefusal("refund-recipient", `a destination-chain refund goes to ${refund.recipient}, neither the recipient nor the sender`);
506
+ }
507
+ if (refund.currency.length !== 42) throw new RelayRefusal("refund-currency", "a destination-chain refund names no token");
508
+ } else {
509
+ throw new RelayRefusal("refund-chain", `a refund is payable on "${refund.chainId}", neither the source nor the destination chain`);
510
+ }
511
+ checkDeadline("refund-deadline", "a refund deadline", refund.deadline, nowS);
512
+ }
513
+ if (!originRefund) throw new RelayRefusal("refund-missing", "the order has no refund to the sender on the source chain");
514
+ const id = relayOrderId(order);
515
+ if (typeof protocol.orderId !== "string" || lower(protocol.orderId) !== id) {
516
+ throw new RelayRefusal("order-id-mismatch", `the quote names order ${String(protocol.orderId)}, the order data hashes to ${id}`);
517
+ }
518
+ const signer = relayOrderSigner(id, protocol.orderSignature);
519
+ if (signer === null || signer !== order.solver) {
520
+ throw new RelayRefusal("signature-invalid", signer === null ? "the order signature is not a valid signature" : `the order is signed by ${signer}, not by its solver ${order.solver}`);
521
+ }
522
+ const pd = protocol.paymentDetails;
523
+ if (pd === null || typeof pd !== "object") throw new RelayRefusal("payment-details", "the quote has no payment details");
524
+ if (pd.chainId !== source.slug || typeof pd.depository !== "string" || lower(pd.depository) !== source.depository || typeof pd.currency !== "string" || lower(pd.currency) !== input.payment.currency || typeof pd.amount !== "string" || pd.amount !== input.payment.amount.toString(10)) {
525
+ throw new RelayRefusal("payment-details", "the quote's payment details do not describe the order's input on the pinned depository");
526
+ }
527
+ const built = buildRelayDeposit(source, request.depositor, input.payment.currency, input.payment.amount, id);
528
+ if (deposit !== void 0) {
529
+ checkDepositTx(deposit, source, request, order, id);
530
+ if (lower(deposit.data) !== built.data || lower(deposit.to) !== built.to || deposit.value !== built.value) {
531
+ throw new RelayRefusal("tx-calldata", "the deposit is not the call this module builds for the order");
532
+ }
533
+ }
534
+ return { order, id, deposit: built, expectedAmount: pay.expectedAmount, minimumAmount: pay.minimumAmount, source, dest };
535
+ }
536
+ function relayRefusalReason(err) {
537
+ if (err instanceof RelayRefusal) return err.message;
538
+ throw err;
539
+ }
540
+ function relayRequestOf(params, opts = {}) {
541
+ const invalid = (what) => {
542
+ throw new RelayRefusal("request-invalid", what);
543
+ };
544
+ if (params.flex) invalid("the amount is sealed into the order (no flexible deposits)");
545
+ if (!relayChain(params.sourceChain)) throw new RelayRefusal("chain-unsupported", `source chain ${params.sourceChain} is not a pinned Relay chain`);
546
+ if (!relayChain(params.destChain)) throw new RelayRefusal("chain-unsupported", `destination chain ${params.destChain} is not a pinned Relay chain`);
547
+ if (params.sourceChain === params.destChain) throw new RelayRefusal("chain-unsupported", "same-chain swaps are not served through the depository");
548
+ let sourceToken;
549
+ if (typeof params.sourceToken === "string" && ADDRESS.test(params.sourceToken)) sourceToken = params.sourceToken;
550
+ else {
551
+ const asset = (0, import_util.sourceAssetOf)(params);
552
+ if (!asset) invalid(`source token ${String(params.sourceToken)} is not known on ${params.sourceChain}`);
553
+ sourceToken = asset.contract ?? RELAY_NATIVE;
554
+ if (!ADDRESS.test(sourceToken)) invalid(`source token ${String(params.sourceToken)} has no EVM contract`);
555
+ }
556
+ if (typeof params.destToken !== "string" || !ADDRESS.test(params.destToken)) invalid("the destination token must be an EVM address (zero for the native coin)");
557
+ if (typeof params.amountIn !== "string" || !/^[1-9]\d{0,77}$/.test(params.amountIn)) invalid("amount must be a positive base-unit integer");
558
+ if (params.minAmountOut !== void 0 && !/^(?:0|[1-9]\d{0,77})$/.test(params.minAmountOut)) invalid("the output floor must be a base-unit integer");
559
+ const recipient = params.destAddress || opts.placeholder || "";
560
+ if (!recipient) invalid("a live quote needs a recipient");
561
+ if (!ADDRESS.test(recipient)) invalid("the recipient must be an EVM address");
562
+ const depositor = params.refundAddress || opts.placeholder || "";
563
+ if (!depositor) invalid("a live quote needs the sender's address (refundAddress)");
564
+ if (!ADDRESS.test(depositor)) invalid("the sender's address must be an EVM address");
565
+ return {
566
+ sourceChain: params.sourceChain,
567
+ destChain: params.destChain,
568
+ sourceToken,
569
+ destToken: params.destToken,
570
+ amount: BigInt(params.amountIn),
571
+ depositor,
572
+ recipient,
573
+ ...params.minAmountOut !== void 0 ? { minAmountOut: BigInt(params.minAmountOut) } : {},
574
+ ...params.slippageBps !== void 0 ? { slippageBps: params.slippageBps } : {},
575
+ ...opts.nowMs !== void 0 ? { nowMs: opts.nowMs } : {}
576
+ };
577
+ }
578
+ function relayProtocolOf(quote) {
579
+ const pd = quote.providerData;
580
+ if (!pd || typeof pd.orderData !== "string") return null;
581
+ let orderData;
582
+ let paymentDetails;
583
+ try {
584
+ orderData = JSON.parse(pd.orderData);
585
+ paymentDetails = typeof pd.paymentDetails === "string" ? JSON.parse(pd.paymentDetails) : void 0;
586
+ } catch {
587
+ return null;
588
+ }
589
+ return { orderId: pd.orderId, hubType: pd.hubType, orderData, orderSignature: pd.orderSignature, paymentDetails };
590
+ }
591
+ function checkRelayQuote(request, quote, tx) {
592
+ if (quote === null || typeof quote !== "object" || quote.provider !== "relay") throw new RelayRefusal("quote-shape", "not a Relay quote");
593
+ const t = tx ?? quote.txRequest;
594
+ if (!t) throw new RelayRefusal("quote-shape", "the quote has no transaction");
595
+ if (quote.depositAddress) throw new RelayRefusal("quote-shape", "a Relay quote pays the depository through its calldata, never a deposit address");
596
+ if (quote.txRequest?.serializedTx !== void 0 || quote.txRequest?.nativeFee !== void 0) {
597
+ throw new RelayRefusal("quote-shape", "a Relay deposit carries no extra native fee and no serialized transaction");
598
+ }
599
+ const v = verifyRelayOrder(request, relayProtocolOf(quote), t);
600
+ const approval = t.approvalAddress;
601
+ if (v.deposit.approvalAddress === void 0 ? approval !== void 0 && approval !== "" : typeof approval !== "string" || lower(approval) !== v.deposit.approvalAddress) {
602
+ throw new RelayRefusal("quote-approval", `the quote approves ${String(approval)}, the order needs ${v.deposit.approvalAddress ?? "no approval"}`);
603
+ }
604
+ if (quote.sourceAmount !== request.amount.toString(10) || quote.minDestAmount !== v.minimumAmount.toString(10) || quote.destAmount !== v.expectedAmount.toString(10)) {
605
+ throw new RelayRefusal("quote-amounts", "the amounts the quote shows are not the order's");
606
+ }
607
+ const gas = t.gasLimit;
608
+ if (gas !== void 0) {
609
+ const floor = v.deposit.approvalAddress === void 0 ? RELAY_GAS_FLOOR_NATIVE : RELAY_GAS_FLOOR_ERC20;
610
+ if (typeof gas !== "string" || !/^[1-9]\d{0,10}$/.test(gas) || BigInt(gas) < floor || BigInt(gas) > RELAY_GAS_CAP) {
611
+ throw new RelayRefusal("quote-gas", `the gas limit ${String(gas).slice(0, 40)} is outside ${floor}\u2013${RELAY_GAS_CAP}`);
612
+ }
613
+ }
614
+ const now = request.nowMs ?? Date.now();
615
+ if (typeof quote.validUntilMs !== "number" || !Number.isFinite(quote.validUntilMs) || quote.validUntilMs > now + RELAY_MAX_QUOTE_LIFE_MS) {
616
+ throw new RelayRefusal("quote-expiry", "the quote claims to live longer than a Relay quote may");
617
+ }
618
+ if (typeof quote.intentId !== "string" || quote.intentId !== `relay:${String(quote.providerData?.requestId)}` || !/^relay:0x[0-9a-f]{64}$/.test(quote.intentId)) {
619
+ throw new RelayRefusal("quote-shape", "the quote's id is not its Relay request id");
620
+ }
621
+ return v;
622
+ }
623
+ function relayVerifyQuote(params, quote) {
624
+ try {
625
+ checkRelayQuote(relayRequestOf(params), quote);
626
+ return null;
627
+ } catch (err) {
628
+ return relayRefusalReason(err);
629
+ }
630
+ }
631
+ function relayVerifyBuild(quote, tx, params) {
632
+ try {
633
+ if (!params) throw new RelayRefusal("request-invalid", "the request this quote answered is not known on this side \u2014 quote again");
634
+ if (tx.chain !== params.sourceChain) throw new RelayRefusal("tx-chain", `the build is for ${tx.chain}, not ${params.sourceChain}`);
635
+ if (tx.serializedTx !== void 0 || tx.memo !== void 0 || tx.typedData !== void 0 || tx.bodyBoc !== void 0) {
636
+ throw new RelayRefusal("quote-shape", "a Relay deposit is one EVM call");
637
+ }
638
+ const v = checkRelayQuote(relayRequestOf(params), quote, tx);
639
+ relayClearOrder(v.id, "build");
640
+ return null;
641
+ } catch (err) {
642
+ return relayRefusalReason(err);
643
+ }
644
+ }
645
+ var relayPins = Object.freeze({
646
+ callTargets: (chain) => {
647
+ const c2 = relayChain(chain);
648
+ return c2 ? [c2.depository] : [];
649
+ },
650
+ spenders: (chain) => {
651
+ const c2 = relayChain(chain);
652
+ return c2 ? [c2.depository] : [];
653
+ },
654
+ verifyQuote: relayVerifyQuote,
655
+ verifyBuild: relayVerifyBuild
656
+ });
657
+
658
+ // src/provider.ts
659
+ var import_util2 = require("@anywalletkit/protocol-swap2-core/util");
660
+
661
+ // src/activity.ts
662
+ var import_activity = require("@anywalletkit/protocol-swap2-core/activity");
663
+ var RELAY_MODULE = "@anywalletkit/protocol-swap2-relay";
664
+ function describeRelayActivity(tx, ctx) {
665
+ const chain = relayChain(tx.chain);
666
+ if (!chain) return null;
667
+ if (!(0, import_activity.matchesCall)(tx, [chain.depository], [RELAY_DEPOSIT_NATIVE, RELAY_DEPOSIT_ERC20, RELAY_DEPOSIT_ERC20_ALLOWANCE])) return null;
668
+ return (0, import_activity.describeSwapLike)(tx, ctx, { protocol: RELAY_MODULE, subtitle: `Relay \xB7 ${(0, import_activity.chainLabel)(tx.chain)}`, kind: "bridge", outVerb: "Bridged" });
669
+ }
670
+
671
+ // src/provider.ts
672
+ var EVM_PLACEHOLDER = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
673
+ var DEFAULT_QUOTE_TTL_MS = 6e4;
674
+ var QUOTE_TIMEOUT_MS = 2e4;
675
+ var STATUS_TIMEOUT_MS = 12e3;
676
+ var INTENT_PREFIX = "relay:";
677
+ var REQUEST_ID = /^0x[0-9a-f]{64}$/;
678
+ var MAX_BODY = 512 * 1024;
679
+ var MAX_REMEMBERED = 256;
680
+ var RELAY_STATUS_MAP = Object.freeze({
681
+ waiting: "pending",
682
+ depositing: "pending",
683
+ pending: "pending",
684
+ submitted: "pending",
685
+ delayed: "pending",
686
+ unknown: "pending",
687
+ success: "completed",
688
+ refund: "refunded",
689
+ refunded: "refunded",
690
+ failure: "refund-pending"
691
+ });
692
+ function clip(text2, max = 240) {
693
+ const t = (0, import_util2.redactUrls)(text2).replace(/\s+/g, " ").trim();
694
+ return t.length > max ? `${t.slice(0, max)}\u2026` : t;
695
+ }
696
+ function depositStep(body) {
697
+ const steps = body.steps;
698
+ if (!Array.isArray(steps) || steps.length === 0 || steps.length > 8) throw new RelayRefusal("quote-steps", "the quote has no steps");
699
+ let found = null;
700
+ for (const s of steps) {
701
+ if (s === null || typeof s !== "object") throw new RelayRefusal("quote-steps", "the quote has a malformed step");
702
+ if (s.id === "approve") continue;
703
+ if (s.id !== "deposit") throw new RelayRefusal("quote-steps", `the quote needs a "${clip(String(s.id), 40)}" step, only a single deposit is signed`);
704
+ if (found) throw new RelayRefusal("quote-steps", "the quote has more than one deposit step");
705
+ found = s;
706
+ }
707
+ if (!found) throw new RelayRefusal("quote-steps", "the quote has no deposit step");
708
+ const items = found.items;
709
+ if (!Array.isArray(items) || items.length !== 1) throw new RelayRefusal("quote-steps", "the deposit step is not a single transaction");
710
+ const data = items[0]?.data;
711
+ if (data === null || typeof data !== "object") throw new RelayRefusal("quote-steps", "the deposit step has no transaction");
712
+ const requestId = typeof found.requestId === "string" ? found.requestId.toLowerCase() : "";
713
+ if (!REQUEST_ID.test(requestId)) throw new RelayRefusal("quote-steps", "the quote has no request id to track the deposit by");
714
+ return { requestId, tx: data };
715
+ }
716
+ function relayProvider(config = {}) {
717
+ config = Object.freeze({ ...config });
718
+ const base = (config.baseUrl ?? RELAY_API).replace(/\/+$/, "");
719
+ if (!/^https?:\/\//.test(base)) throw new Error("relay: baseUrl must use http or https");
720
+ const quoteTtlMs = config.quoteTtlMs ?? DEFAULT_QUOTE_TTL_MS;
721
+ if (!Number.isInteger(quoteTtlMs) || quoteTtlMs <= 0 || quoteTtlMs > 3e5) throw new Error("relay: quoteTtlMs must be an integer between 1 and 300000");
722
+ if (config.apiKey !== void 0 && (typeof config.apiKey !== "string" || !/^[\x21-\x7e]{1,256}$/.test(config.apiKey))) throw new Error("relay: apiKey must be a printable string");
723
+ const doFetch = (input, init) => (config.fetchImpl ?? fetch)(input, init);
724
+ const headers = (json) => ({
725
+ accept: "application/json",
726
+ ...json ? { "content-type": "application/json" } : {},
727
+ ...config.apiKey ? { "x-api-key": config.apiKey } : {}
728
+ });
729
+ const issued = /* @__PURE__ */ new Map();
730
+ const remember = (m, k, v) => {
731
+ m.delete(k);
732
+ m.set(k, v);
733
+ if (m.size > MAX_REMEMBERED) m.delete(m.keys().next().value);
734
+ };
735
+ function requestOf(quote) {
736
+ const pd = quote.providerData ?? {};
737
+ const sealed = {
738
+ sourceChain: pd.sourceChain ?? "",
739
+ destChain: pd.destChain ?? "",
740
+ sourceToken: pd.sourceToken ?? "",
741
+ destToken: pd.destToken ?? "",
742
+ amount: /^[1-9]\d{0,77}$/.test(pd.amount ?? "") ? BigInt(pd.amount) : 0n,
743
+ depositor: pd.depositor ?? "",
744
+ recipient: pd.recipient ?? "",
745
+ .../^\d{1,78}$/.test(pd.minAmountOut ?? "") ? { minAmountOut: BigInt(pd.minAmountOut) } : {},
746
+ .../^\d{1,5}$/.test(pd.slippageBps ?? "") ? { slippageBps: Number(pd.slippageBps) } : {}
747
+ };
748
+ if (sealed.depositor.toLowerCase() === EVM_PLACEHOLDER.toLowerCase() || sealed.recipient.toLowerCase() === EVM_PLACEHOLDER.toLowerCase()) {
749
+ throw new RelayRefusal("request-invalid", "a quote priced without the real addresses is never built or signed \u2014 quote again with them");
750
+ }
751
+ const mine = issued.get(quote.intentId);
752
+ if (!mine) return sealed;
753
+ const same = mine.sourceChain === sealed.sourceChain && mine.destChain === sealed.destChain && mine.amount === sealed.amount && mine.sourceToken.toLowerCase() === sealed.sourceToken.toLowerCase() && mine.destToken.toLowerCase() === sealed.destToken.toLowerCase() && mine.depositor.toLowerCase() === sealed.depositor.toLowerCase() && mine.recipient.toLowerCase() === sealed.recipient.toLowerCase();
754
+ if (!same) throw new RelayRefusal("request-invalid", "the quote no longer says what was asked when it was issued \u2014 quote again");
755
+ return mine;
756
+ }
757
+ async function readJson(res) {
758
+ const text2 = await res.text();
759
+ if (text2.length > MAX_BODY) throw new Error("relay: response too large");
760
+ return JSON.parse(text2);
761
+ }
762
+ return {
763
+ name: "relay",
764
+ describeActivity: describeRelayActivity,
765
+ enabled: () => config.enabled ?? true,
766
+ supportsChain: (chain) => relay.supportsChain(chain),
767
+ supportsChainPair: (from, to) => relay.supports(from, to),
768
+ supportsRoute: (from, to) => relay.supports(from.chain, to.chain),
769
+ async getQuote(params, signal) {
770
+ const refuse = (reason) => {
771
+ params.onRefusal?.(reason);
772
+ return null;
773
+ };
774
+ const dry = params.dry !== false;
775
+ const placeholder = dry && (!params.destAddress || !params.refundAddress);
776
+ let request;
777
+ try {
778
+ request = relayRequestOf(params, dry ? { placeholder: EVM_PLACEHOLDER } : {});
779
+ } catch (err) {
780
+ if (err instanceof RelayRefusal) return refuse(err.message);
781
+ throw err;
782
+ }
783
+ const src = relayChain(request.sourceChain);
784
+ const dst = relayChain(request.destChain);
785
+ let body;
786
+ try {
787
+ const res = await doFetch(`${base}/quote/v2`, {
788
+ method: "POST",
789
+ headers: headers(true),
790
+ body: JSON.stringify({
791
+ user: request.depositor,
792
+ recipient: request.recipient,
793
+ refundTo: request.depositor,
794
+ originChainId: src.chainId,
795
+ destinationChainId: dst.chainId,
796
+ originCurrency: request.sourceToken.toLowerCase(),
797
+ destinationCurrency: request.destToken.toLowerCase(),
798
+ amount: request.amount.toString(10),
799
+ tradeType: "EXACT_INPUT",
800
+ includeProtocolData: true,
801
+ // Always named: Relay's "auto" would leave the distance between expected and minimum to the answering side.
802
+ slippageTolerance: String(request.slippageBps ?? RELAY_DEFAULT_SLIPPAGE_BPS)
803
+ }),
804
+ signal: signal ? (0, import_util2.anySignal)([signal, (0, import_util2.timeoutSignal)(QUOTE_TIMEOUT_MS)]) : (0, import_util2.timeoutSignal)(QUOTE_TIMEOUT_MS)
805
+ });
806
+ let parsed;
807
+ try {
808
+ parsed = await readJson(res);
809
+ } catch {
810
+ parsed = null;
811
+ }
812
+ if (!res.ok) {
813
+ const e = parsed ?? {};
814
+ const why = typeof e.message === "string" ? clip(e.message) : `HTTP ${res.status}`;
815
+ return refuse(`relay: no quote \u2014 ${typeof e.errorCode === "string" ? `${clip(e.errorCode, 60)}: ` : ""}${why}`);
816
+ }
817
+ if (parsed === null || typeof parsed !== "object") return refuse("relay: the quote response is not JSON");
818
+ body = parsed;
819
+ } catch (err) {
820
+ return refuse(`relay: quote request failed \u2014 ${clip(err instanceof Error ? err.message : String(err))}`);
821
+ }
822
+ try {
823
+ const step = depositStep(body);
824
+ const proto = body.protocol?.v2 ?? null;
825
+ const v = verifyRelayOrder(request, proto, step.tx);
826
+ const extra = (x) => x !== void 0 && x !== null && x !== "0" && x !== 0;
827
+ if (extra(body.fees?.app?.amount)) throw new RelayRefusal("app-fee", "the quote charges an app fee this module did not ask for");
828
+ if (extra(body.details?.currencyGasTopup?.amount)) throw new RelayRefusal("gas-topup", "the quote converts part of the output into a gas top-up this module did not ask for");
829
+ const native = v.deposit.approvalAddress === void 0;
830
+ const floor = native ? RELAY_GAS_FLOOR_NATIVE : RELAY_GAS_FLOOR_ERC20;
831
+ const apiGas = typeof step.tx.gas === "string" && /^[1-9]\d{0,9}$/.test(step.tx.gas) ? BigInt(step.tx.gas) * 2n : 0n;
832
+ const gasLimit = apiGas > RELAY_GAS_CAP ? RELAY_GAS_CAP : apiGas > floor ? apiGas : floor;
833
+ const relayer = body.fees?.relayer;
834
+ const usd = typeof relayer?.amountUsd === "string" && /^\d{1,12}(?:\.\d{1,18})?$/.test(relayer.amountUsd) ? parseFloat(relayer.amountUsd) : 0;
835
+ const feeToken = relayer?.currency?.address;
836
+ const fees = typeof relayer?.amount === "string" && /^[1-9]\d{0,77}$/.test(relayer.amount) && typeof feeToken === "string" && /^0x[0-9a-fA-F]{40}$/.test(feeToken) ? [{ type: "protocol", amount: relayer.amount, token: feeToken.toLowerCase(), included: true, description: "Relay fee" }] : [];
837
+ const eta = body.details?.timeEstimate;
838
+ const etaSeconds = typeof eta === "number" && Number.isFinite(eta) && eta > 0 ? Math.min(Math.ceil(eta), 86400) : 60;
839
+ const intentId = `${INTENT_PREFIX}${step.requestId}`;
840
+ if (!placeholder) remember(issued, intentId, request);
841
+ return {
842
+ provider: "relay",
843
+ intentId,
844
+ depositAddress: "",
845
+ depositMemo: null,
846
+ txRequest: {
847
+ to: v.deposit.to,
848
+ data: v.deposit.data,
849
+ value: v.deposit.value,
850
+ chainId: v.deposit.chainId,
851
+ gasLimit: gasLimit.toString(10),
852
+ ...v.deposit.approvalAddress ? { approvalAddress: v.deposit.approvalAddress } : {}
853
+ },
854
+ sourceAmount: request.amount.toString(10),
855
+ destAmount: v.expectedAmount.toString(10),
856
+ minDestAmount: v.minimumAmount.toString(10),
857
+ feeUsd: usd,
858
+ fees,
859
+ etaSeconds,
860
+ reliability: 0.97,
861
+ validUntilMs: Date.now() + quoteTtlMs,
862
+ providerData: {
863
+ requestId: step.requestId,
864
+ orderId: v.id,
865
+ hubType: "onchain",
866
+ // The order as received, re-read by the same parser before a build and before a signature.
867
+ orderData: JSON.stringify(proto.orderData),
868
+ orderSignature: String(proto.orderSignature).toLowerCase(),
869
+ paymentDetails: JSON.stringify(proto.paymentDetails),
870
+ sourceChain: request.sourceChain,
871
+ destChain: request.destChain,
872
+ sourceToken: request.sourceToken.toLowerCase(),
873
+ destToken: request.destToken.toLowerCase(),
874
+ amount: request.amount.toString(10),
875
+ depositor: request.depositor.toLowerCase(),
876
+ recipient: request.recipient.toLowerCase(),
877
+ ...request.minAmountOut !== void 0 ? { minAmountOut: request.minAmountOut.toString(10) } : {},
878
+ ...request.slippageBps !== void 0 ? { slippageBps: String(request.slippageBps) } : {},
879
+ ...placeholder ? { placeholder: "1" } : {}
880
+ }
881
+ };
882
+ } catch (err) {
883
+ if (err instanceof RelayRefusal) return refuse(err.message);
884
+ return refuse(`relay: unreadable quote \u2014 ${clip(err instanceof Error ? err.message : String(err))}`);
885
+ }
886
+ },
887
+ /** The quote's own transaction — after the order it carries has been verified once more. */
888
+ async buildDeposit(quote) {
889
+ const tx = quote.txRequest;
890
+ if (!tx || !quote.intentId.startsWith(INTENT_PREFIX)) throw new Error("swap2: Relay quote has no transaction request");
891
+ if (quote.providerData?.placeholder === "1") throw new RelayRefusal("request-invalid", "a quote priced without the real addresses is never built \u2014 quote again with them");
892
+ const request = requestOf(quote);
893
+ const v = checkRelayQuote(request, quote);
894
+ return {
895
+ chain: request.sourceChain,
896
+ to: v.deposit.to,
897
+ value: v.deposit.value,
898
+ data: v.deposit.data,
899
+ chainId: v.deposit.chainId,
900
+ ...tx.gasLimit ? { gasLimit: tx.gasLimit } : {},
901
+ ...v.deposit.approvalAddress ? { approvalAddress: v.deposit.approvalAddress } : {}
902
+ };
903
+ },
904
+ /**
905
+ * Right before signing: the whole verification again, against the request this instance issued the quote for
906
+ * and the exact transaction about to be signed — and each order is cleared for one deposit only.
907
+ */
908
+ async preflightDeposit(quote, tx) {
909
+ if (quote.providerData?.placeholder === "1") throw new RelayRefusal("request-invalid", "a quote priced without the real addresses is never signed");
910
+ const request = requestOf(quote);
911
+ if (tx.chain !== request.sourceChain) throw new RelayRefusal("tx-chain", `the deposit is for ${tx.chain}, not ${request.sourceChain}`);
912
+ if (tx.serializedTx !== void 0 || tx.memo !== void 0 || tx.typedData !== void 0 || tx.bodyBoc !== void 0) {
913
+ throw new RelayRefusal("quote-shape", "a Relay deposit is one EVM call");
914
+ }
915
+ const v = checkRelayQuote(request, quote, tx);
916
+ const store = config.clearedStore;
917
+ if (store && await store.has(v.id)) throw new RelayRefusal("order-reused", `order ${v.id} was already cleared for a deposit \u2014 quote again`);
918
+ relayClearOrder(v.id, "sign");
919
+ if (store) await store.add(v.id, v.order.output.deadline * 1e3);
920
+ },
921
+ async trackIntent(ref) {
922
+ if (typeof ref.intentId !== "string" || !ref.intentId.startsWith(INTENT_PREFIX)) return { state: "failed" };
923
+ const requestId = ref.intentId.slice(INTENT_PREFIX.length);
924
+ if (!REQUEST_ID.test(requestId)) return { state: "failed" };
925
+ if (!ref.sourceTxHash) return { state: "pending" };
926
+ try {
927
+ const res = await doFetch(`${base}/intents/status/v3?requestId=${requestId}`, { headers: headers(false), signal: (0, import_util2.timeoutSignal)(STATUS_TIMEOUT_MS) });
928
+ if (!res.ok) return { state: "pending" };
929
+ const s = await readJson(res);
930
+ const status = typeof s?.status === "string" ? s.status.toLowerCase() : "";
931
+ const state = Object.prototype.hasOwnProperty.call(RELAY_STATUS_MAP, status) ? RELAY_STATUS_MAP[status] : "pending";
932
+ const seen = status !== "waiting" && status !== "unknown" && status !== "";
933
+ if (state === "completed") {
934
+ const hashes = Array.isArray(s?.txHashes) ? s.txHashes.filter((h) => typeof h === "string" && /^0x[0-9a-fA-F]{64}$/.test(h)) : [];
935
+ const dest = hashes[hashes.length - 1] ?? ref.destTxHash ?? void 0;
936
+ return { state, depositReceived: true, ...dest ? { destTxHash: dest } : {} };
937
+ }
938
+ if (status === "failure") {
939
+ const why = typeof s?.refundFailReason === "string" ? s.refundFailReason.trim() : "";
940
+ return { state: why !== "" && why.toUpperCase() !== "N/A" ? "failed" : "refund-pending", depositReceived: true };
941
+ }
942
+ return { state, ...seen ? { depositReceived: true } : {} };
943
+ } catch {
944
+ return { state: "pending" };
945
+ }
946
+ }
947
+ };
948
+ }
949
+
950
+ // src/protocol.ts
951
+ var import_single = require("@anywalletkit/protocol-swap2-core/swap2/single");
952
+ var RelaySwap2Protocol = class extends import_single.SingleProviderSwap2 {
953
+ provider;
954
+ constructor(account, config) {
955
+ super(account, config);
956
+ this.provider = relayProvider(config);
957
+ }
958
+ chainSupported(caip2) {
959
+ return relay.supportsChain(caip2);
960
+ }
961
+ pairSupported(fromChain, toChain) {
962
+ return relay.supports(fromChain, toChain);
963
+ }
964
+ };
965
+ //# sourceMappingURL=index.cjs.map