@anywalletkit/protocol-swap2-relay 0.1.1 → 0.1.3
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.
- package/README.md +1 -1
- package/dist/{chunk-WRIKAUWI.js → chunk-C3I2XYSW.js} +92 -84
- package/dist/chunk-C3I2XYSW.js.map +1 -0
- package/dist/{chunk-CCMNAZZB.js → chunk-JTPYU26F.js} +72 -29
- package/dist/chunk-JTPYU26F.js.map +1 -0
- package/dist/{chunk-UVHDMGM5.js → chunk-MDXHT7HY.js} +5 -3
- package/dist/chunk-MDXHT7HY.js.map +1 -0
- package/dist/{chunk-7LPLU3MB.js → chunk-TBFD7BPZ.js} +2 -2
- package/dist/index.cjs +163 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -4
- package/dist/order.cjs +4 -2
- package/dist/order.cjs.map +1 -1
- package/dist/order.d.cts +9 -1
- package/dist/order.d.ts +9 -1
- package/dist/order.js +1 -1
- package/dist/protocol.cjs +163 -110
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.js +4 -4
- package/dist/provider.cjs +163 -110
- package/dist/provider.cjs.map +1 -1
- package/dist/provider.js +3 -3
- package/dist/verify.cjs +94 -84
- package/dist/verify.cjs.map +1 -1
- package/dist/verify.d.cts +2 -1
- package/dist/verify.d.ts +2 -1
- package/dist/verify.js +2 -2
- package/package.json +6 -6
- package/dist/chunk-CCMNAZZB.js.map +0 -1
- package/dist/chunk-UVHDMGM5.js.map +0 -1
- package/dist/chunk-WRIKAUWI.js.map +0 -1
- /package/dist/{chunk-7LPLU3MB.js.map → chunk-TBFD7BPZ.js.map} +0 -0
package/dist/provider.cjs
CHANGED
|
@@ -25,6 +25,7 @@ __export(provider_exports, {
|
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(provider_exports);
|
|
27
27
|
var import_util2 = require("@anywalletkit/protocol-swap2-core/util");
|
|
28
|
+
var import_errors3 = require("@anywalletkit/protocol-swap2-core/errors");
|
|
28
29
|
|
|
29
30
|
// src/activity.ts
|
|
30
31
|
var import_activity = require("@anywalletkit/protocol-swap2-core/activity");
|
|
@@ -100,13 +101,15 @@ function describeRelayActivity(tx, ctx) {
|
|
|
100
101
|
|
|
101
102
|
// src/order.ts
|
|
102
103
|
var import_secp256k1 = require("@noble/curves/secp256k1.js");
|
|
104
|
+
var import_errors = require("@anywalletkit/protocol-swap2-core/errors");
|
|
103
105
|
var import_viem = require("viem");
|
|
104
106
|
var RelayRefusal = class extends Error {
|
|
105
107
|
code;
|
|
106
|
-
constructor(code, detail) {
|
|
108
|
+
constructor(code, detail, reason) {
|
|
107
109
|
super(`relay: ${detail} [${code}]`);
|
|
108
110
|
this.name = "RelayRefusal";
|
|
109
111
|
this.code = code;
|
|
112
|
+
if (reason !== void 0) (0, import_errors.withReason)(this, reason);
|
|
110
113
|
}
|
|
111
114
|
};
|
|
112
115
|
var RELAY_ORDER_TYPES = {
|
|
@@ -163,7 +166,7 @@ var MAX_BYTES_HEX = 2 + 2 * 1024;
|
|
|
163
166
|
var MAX_TEXT = 64;
|
|
164
167
|
var U256_MAX = (1n << 256n) - 1n;
|
|
165
168
|
var bad = (what) => {
|
|
166
|
-
throw new RelayRefusal("order-malformed", `the order's ${what}
|
|
169
|
+
throw new RelayRefusal("order-malformed", `the order's ${what}`, "upstream_unavailable");
|
|
167
170
|
};
|
|
168
171
|
function rec(v, what) {
|
|
169
172
|
if (v === null || typeof v !== "object" || Array.isArray(v)) bad(`${what} is not an object`);
|
|
@@ -282,6 +285,7 @@ function relayOrderSigner(id, signature) {
|
|
|
282
285
|
|
|
283
286
|
// src/verify.ts
|
|
284
287
|
var import_viem2 = require("viem");
|
|
288
|
+
var import_errors2 = require("@anywalletkit/protocol-swap2-core/errors");
|
|
285
289
|
var import_util = require("@anywalletkit/protocol-swap2-core/util");
|
|
286
290
|
|
|
287
291
|
// src/cleared.ts
|
|
@@ -291,10 +295,10 @@ var clearedOrders = /* @__PURE__ */ new Map();
|
|
|
291
295
|
function relayClearOrder(id, stage, nowMs = Date.now()) {
|
|
292
296
|
const key = `${stage}:${id.toLowerCase()}`;
|
|
293
297
|
const until = clearedOrders.get(key);
|
|
294
|
-
if (until !== void 0 && until > nowMs) throw new RelayRefusal("order-reused", `order ${id} was already cleared for a deposit \u2014 quote again
|
|
298
|
+
if (until !== void 0 && until > nowMs) throw new RelayRefusal("order-reused", `order ${id} was already cleared for a deposit \u2014 quote again`, "policy_refused");
|
|
295
299
|
if (clearedOrders.size >= CLEARED_MAX) {
|
|
296
300
|
for (const [k, t] of clearedOrders) if (t <= nowMs) clearedOrders.delete(k);
|
|
297
|
-
if (clearedOrders.size >= CLEARED_MAX) throw new RelayRefusal("order-reused", "too many deposits cleared in the last minutes \u2014 try again later");
|
|
301
|
+
if (clearedOrders.size >= CLEARED_MAX) throw new RelayRefusal("order-reused", "too many deposits cleared in the last minutes \u2014 try again later", "policy_refused");
|
|
298
302
|
}
|
|
299
303
|
clearedOrders.set(key, nowMs + CLEARED_TTL_MS);
|
|
300
304
|
}
|
|
@@ -336,24 +340,24 @@ function buildRelayDeposit(source, depositor, token, amount, id) {
|
|
|
336
340
|
};
|
|
337
341
|
}
|
|
338
342
|
function checkRequest(r) {
|
|
339
|
-
const invalid = (what) => {
|
|
340
|
-
throw new RelayRefusal("request-invalid", what);
|
|
343
|
+
const invalid = (what, reason) => {
|
|
344
|
+
throw new RelayRefusal("request-invalid", what, reason);
|
|
341
345
|
};
|
|
342
346
|
if (r === null || typeof r !== "object") invalid("no request to check the order against");
|
|
343
347
|
const source = relayChain(r.sourceChain);
|
|
344
348
|
const dest = relayChain(r.destChain);
|
|
345
|
-
if (!source) throw new RelayRefusal("chain-unsupported", `source chain ${String(r.sourceChain)} is not a pinned Relay chain
|
|
346
|
-
if (!dest) throw new RelayRefusal("chain-unsupported", `destination chain ${String(r.destChain)} is not a pinned Relay chain
|
|
347
|
-
if (source === dest) throw new RelayRefusal("chain-unsupported", "source and destination are the same chain");
|
|
349
|
+
if (!source) throw new RelayRefusal("chain-unsupported", `source chain ${String(r.sourceChain)} is not a pinned Relay chain`, "unsupported_pair");
|
|
350
|
+
if (!dest) throw new RelayRefusal("chain-unsupported", `destination chain ${String(r.destChain)} is not a pinned Relay chain`, "unsupported_pair");
|
|
351
|
+
if (source === dest) throw new RelayRefusal("chain-unsupported", "source and destination are the same chain", "unsupported_pair");
|
|
348
352
|
for (const [what, v] of [["input token", r.sourceToken], ["output token", r.destToken], ["depositor", r.depositor], ["recipient", r.recipient]]) {
|
|
349
353
|
if (typeof v !== "string" || !ADDRESS.test(v)) invalid(`the ${what} is not an address`);
|
|
350
354
|
}
|
|
351
|
-
if (/^0x0{40}$/.test(r.depositor)) invalid("the depositor is the zero address");
|
|
352
|
-
if (/^0x0{40}$/.test(r.recipient)) invalid("the recipient is the zero address");
|
|
355
|
+
if (/^0x0{40}$/.test(r.depositor)) invalid("the depositor is the zero address", "recipient_mismatch");
|
|
356
|
+
if (/^0x0{40}$/.test(r.recipient)) invalid("the recipient is the zero address", "recipient_mismatch");
|
|
353
357
|
if (typeof r.amount !== "bigint" || r.amount <= 0n || r.amount > U256_MAX2) invalid("the amount is not a positive integer");
|
|
354
358
|
if (r.minAmountOut !== void 0 && (typeof r.minAmountOut !== "bigint" || r.minAmountOut < 0n)) invalid("the output floor is not an unsigned integer");
|
|
355
359
|
if (r.slippageBps !== void 0 && (!Number.isInteger(r.slippageBps) || r.slippageBps < 0 || r.slippageBps > RELAY_MAX_SLIPPAGE_BPS)) {
|
|
356
|
-
invalid(`slippage must be a whole number of basis points up to ${RELAY_MAX_SLIPPAGE_BPS}
|
|
360
|
+
invalid(`slippage must be a whole number of basis points up to ${RELAY_MAX_SLIPPAGE_BPS}`, "policy_refused");
|
|
357
361
|
}
|
|
358
362
|
const nowMs = r.nowMs ?? Date.now();
|
|
359
363
|
if (!Number.isFinite(nowMs) || nowMs <= 0) invalid("the clock is not a time");
|
|
@@ -363,143 +367,143 @@ var isAddr = (bytes2, addr) => bytes2.length === 42 && bytes2 === lower(addr);
|
|
|
363
367
|
function checkDeadline(code, what, deadline, nowS) {
|
|
364
368
|
const left = deadline - nowS;
|
|
365
369
|
if (left > RELAY_ORDER_LIFETIME_S + RELAY_CLOCK_SKEW_S) {
|
|
366
|
-
throw new RelayRefusal(code, `${what} is ${left} s away, past the ${RELAY_ORDER_LIFETIME_S} s an order lives
|
|
370
|
+
throw new RelayRefusal(code, `${what} is ${left} s away, past the ${RELAY_ORDER_LIFETIME_S} s an order lives`, "policy_refused");
|
|
367
371
|
}
|
|
368
372
|
if (left < RELAY_ORDER_LIFETIME_S - RELAY_MAX_ORDER_AGE_S) {
|
|
369
|
-
throw new RelayRefusal(code, `${what} is ${left} s away: the order is older than ${RELAY_MAX_ORDER_AGE_S} s or already past it
|
|
373
|
+
throw new RelayRefusal(code, `${what} is ${left} s away: the order is older than ${RELAY_MAX_ORDER_AGE_S} s or already past it`, "quote_expired");
|
|
370
374
|
}
|
|
371
375
|
}
|
|
372
376
|
function wordAddress(word) {
|
|
373
377
|
return /^0{24}/.test(word) ? `0x${word.slice(24)}` : null;
|
|
374
378
|
}
|
|
375
379
|
function checkDepositTarget(tx, source) {
|
|
376
|
-
if (tx === null || typeof tx !== "object") throw new RelayRefusal("tx-calldata", "there is no deposit transaction");
|
|
380
|
+
if (tx === null || typeof tx !== "object") throw new RelayRefusal("tx-calldata", "there is no deposit transaction", "policy_refused");
|
|
377
381
|
if (tx.chainId !== source.chainId) {
|
|
378
|
-
throw new RelayRefusal("tx-chain", `the deposit is for chain ${String(tx.chainId)}, not ${source.chainId}
|
|
382
|
+
throw new RelayRefusal("tx-chain", `the deposit is for chain ${String(tx.chainId)}, not ${source.chainId}`, "vault_mismatch");
|
|
379
383
|
}
|
|
380
384
|
if (typeof tx.to !== "string" || !ADDRESS.test(tx.to) || lower(tx.to) !== source.depository) {
|
|
381
|
-
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
|
|
385
|
+
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`, "vault_mismatch");
|
|
382
386
|
}
|
|
383
387
|
}
|
|
384
388
|
function checkDepositTx(tx, source, request, order, id) {
|
|
385
389
|
checkDepositTarget(tx, source);
|
|
386
390
|
if (typeof tx.data !== "string" || !/^0x(?:[0-9a-fA-F]{2})*$/.test(tx.data) || tx.data.length < 10) {
|
|
387
|
-
throw new RelayRefusal("tx-calldata", "the deposit has no calldata");
|
|
391
|
+
throw new RelayRefusal("tx-calldata", "the deposit has no calldata", "policy_refused");
|
|
388
392
|
}
|
|
389
393
|
const data = lower(tx.data);
|
|
390
394
|
const selector = data.slice(0, 10);
|
|
391
395
|
const input = order.inputs[0].payment;
|
|
392
396
|
const native = isNative(input.currency);
|
|
393
397
|
if (selector === RELAY_DEPOSIT_ERC20_ALLOWANCE) {
|
|
394
|
-
throw new RelayRefusal("tx-selector", "the deposit uses depositErc20 without an amount, which takes the whole allowance");
|
|
398
|
+
throw new RelayRefusal("tx-selector", "the deposit uses depositErc20 without an amount, which takes the whole allowance", "policy_refused");
|
|
395
399
|
}
|
|
396
400
|
if (selector !== RELAY_DEPOSIT_NATIVE && selector !== RELAY_DEPOSIT_ERC20) {
|
|
397
|
-
throw new RelayRefusal("tx-selector", `the deposit calls ${selector}, not a known deposit function
|
|
401
|
+
throw new RelayRefusal("tx-selector", `the deposit calls ${selector}, not a known deposit function`, "policy_refused");
|
|
398
402
|
}
|
|
399
403
|
if (selector === RELAY_DEPOSIT_NATIVE !== native) {
|
|
400
|
-
throw new RelayRefusal("tx-selector", `the deposit calls ${native ? "depositErc20" : "depositNative"} for an order whose input is ${native ? "the native coin" : "a token"}
|
|
404
|
+
throw new RelayRefusal("tx-selector", `the deposit calls ${native ? "depositErc20" : "depositNative"} for an order whose input is ${native ? "the native coin" : "a token"}`, "policy_refused");
|
|
401
405
|
}
|
|
402
406
|
const words = native ? 2 : 4;
|
|
403
407
|
if (data.length !== 10 + 64 * words) {
|
|
404
|
-
throw new RelayRefusal("tx-calldata", `the deposit calldata is ${(data.length - 2) / 2} bytes, not ${4 + 32 * words}
|
|
408
|
+
throw new RelayRefusal("tx-calldata", `the deposit calldata is ${(data.length - 2) / 2} bytes, not ${4 + 32 * words}`, "policy_refused");
|
|
405
409
|
}
|
|
406
410
|
const word = (i) => data.slice(10 + 64 * i, 74 + 64 * i);
|
|
407
411
|
const depositor = wordAddress(word(0));
|
|
408
|
-
if (depositor === null) throw new RelayRefusal("tx-calldata", "the depositor argument is not a padded address");
|
|
412
|
+
if (depositor === null) throw new RelayRefusal("tx-calldata", "the depositor argument is not a padded address", "policy_refused");
|
|
409
413
|
if (depositor !== lower(request.depositor)) {
|
|
410
|
-
throw new RelayRefusal("tx-depositor", `the deposit is credited to ${depositor}, not to the sender ${lower(request.depositor)}
|
|
414
|
+
throw new RelayRefusal("tx-depositor", `the deposit is credited to ${depositor}, not to the sender ${lower(request.depositor)}`, "recipient_mismatch");
|
|
411
415
|
}
|
|
412
416
|
if (!native) {
|
|
413
417
|
const token = wordAddress(word(1));
|
|
414
|
-
if (token === null) throw new RelayRefusal("tx-calldata", "the token argument is not a padded address");
|
|
415
|
-
if (token !== input.currency) throw new RelayRefusal("tx-token", `the deposit moves ${token}, the order's input is ${input.currency}
|
|
418
|
+
if (token === null) throw new RelayRefusal("tx-calldata", "the token argument is not a padded address", "policy_refused");
|
|
419
|
+
if (token !== input.currency) throw new RelayRefusal("tx-token", `the deposit moves ${token}, the order's input is ${input.currency}`, "policy_refused");
|
|
416
420
|
const amount = BigInt(`0x${word(2)}`);
|
|
417
|
-
if (amount !== input.amount) throw new RelayRefusal("tx-amount", `the deposit moves ${amount}, the order's input is ${input.amount}
|
|
421
|
+
if (amount !== input.amount) throw new RelayRefusal("tx-amount", `the deposit moves ${amount}, the order's input is ${input.amount}`, "policy_refused");
|
|
418
422
|
}
|
|
419
423
|
const txId = `0x${word(native ? 1 : 3)}`;
|
|
420
|
-
if (txId !== id) throw new RelayRefusal("tx-id", `the deposit names order ${txId}, the order received hashes to ${id}
|
|
424
|
+
if (txId !== id) throw new RelayRefusal("tx-id", `the deposit names order ${txId}, the order received hashes to ${id}`, "recipient_unverifiable");
|
|
421
425
|
const value = typeof tx.value === "string" && /^(?:0|[1-9]\d{0,77})$/.test(tx.value) ? BigInt(tx.value) : null;
|
|
422
426
|
const wantValue = native ? input.amount : 0n;
|
|
423
427
|
if (value === null || value !== wantValue) {
|
|
424
|
-
throw new RelayRefusal("tx-value", `the deposit sends ${String(tx.value)} wei, it must send ${wantValue}
|
|
428
|
+
throw new RelayRefusal("tx-value", `the deposit sends ${String(tx.value)} wei, it must send ${wantValue}`, "policy_refused");
|
|
425
429
|
}
|
|
426
430
|
}
|
|
427
431
|
function verifyRelayOrder(request, protocol, deposit) {
|
|
428
432
|
const { source, dest, nowS } = checkRequest(request);
|
|
429
433
|
if (deposit !== void 0) checkDepositTarget(deposit, source);
|
|
430
434
|
if (protocol === null || typeof protocol !== "object") {
|
|
431
|
-
throw new RelayRefusal("protocol-data-missing", "the quote carries no order (protocol.v2) \u2014 nothing to check the deposit against");
|
|
435
|
+
throw new RelayRefusal("protocol-data-missing", "the quote carries no order (protocol.v2) \u2014 nothing to check the deposit against", "upstream_unavailable");
|
|
432
436
|
}
|
|
433
437
|
if (protocol.orderData === void 0 || protocol.orderData === null) {
|
|
434
|
-
throw new RelayRefusal("protocol-data-missing", "the quote carries no order data \u2014 nothing to check the deposit against");
|
|
438
|
+
throw new RelayRefusal("protocol-data-missing", "the quote carries no order data \u2014 nothing to check the deposit against", "upstream_unavailable");
|
|
435
439
|
}
|
|
436
440
|
if (protocol.hubType !== "onchain") {
|
|
437
|
-
throw new RelayRefusal("hub-type", `the order settles through a "${String(protocol.hubType)}" hub, only "onchain" is read
|
|
441
|
+
throw new RelayRefusal("hub-type", `the order settles through a "${String(protocol.hubType)}" hub, only "onchain" is read`, "unsupported_pair");
|
|
438
442
|
}
|
|
439
443
|
const order = parseRelayOrder(protocol.orderData);
|
|
440
|
-
if (order.version !== RELAY_ORDER_VERSION) throw new RelayRefusal("order-version", `order version "${order.version}" is not ${RELAY_ORDER_VERSION}
|
|
441
|
-
if (order.inputs.length !== 1) throw new RelayRefusal("order-inputs", `the order has ${order.inputs.length} inputs, exactly one is signed for
|
|
442
|
-
if (order.output.payments.length !== 1) throw new RelayRefusal("order-payments", `the order pays ${order.output.payments.length} outputs, exactly one is accepted
|
|
443
|
-
if (order.output.calls.length !== 0) throw new RelayRefusal("order-calls", "the order runs calls on the destination \u2014 only plain payments are accepted");
|
|
444
|
-
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");
|
|
444
|
+
if (order.version !== RELAY_ORDER_VERSION) throw new RelayRefusal("order-version", `order version "${order.version}" is not ${RELAY_ORDER_VERSION}`, "upstream_unavailable");
|
|
445
|
+
if (order.inputs.length !== 1) throw new RelayRefusal("order-inputs", `the order has ${order.inputs.length} inputs, exactly one is signed for`, "policy_refused");
|
|
446
|
+
if (order.output.payments.length !== 1) throw new RelayRefusal("order-payments", `the order pays ${order.output.payments.length} outputs, exactly one is accepted`, "policy_refused");
|
|
447
|
+
if (order.output.calls.length !== 0) throw new RelayRefusal("order-calls", "the order runs calls on the destination \u2014 only plain payments are accepted", "policy_refused");
|
|
448
|
+
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", "policy_refused");
|
|
445
449
|
const input = order.inputs[0];
|
|
446
450
|
const pay = order.output.payments[0];
|
|
447
|
-
if (input.payment.weight === 0n) throw new RelayRefusal("input-weight", "the order's input has zero weight");
|
|
451
|
+
if (input.payment.weight === 0n) throw new RelayRefusal("input-weight", "the order's input has zero weight", "policy_refused");
|
|
448
452
|
if (!RELAY_SOLVERS.some((s) => s.address === order.solver && s.chainId === order.solverChainId)) {
|
|
449
|
-
throw new RelayRefusal("solver-not-pinned", `solver ${order.solver} on "${order.solverChainId}" is not a pinned Relay solver
|
|
453
|
+
throw new RelayRefusal("solver-not-pinned", `solver ${order.solver} on "${order.solverChainId}" is not a pinned Relay solver`, "recipient_unverifiable");
|
|
450
454
|
}
|
|
451
|
-
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}")
|
|
452
|
-
if (!isAddr(pay.recipient, request.recipient)) throw new RelayRefusal("output-recipient", `the order pays ${pay.recipient}, not the recipient ${lower(request.recipient)}
|
|
453
|
-
if (!isAddr(pay.currency, request.destToken)) throw new RelayRefusal("output-currency", `the order pays in ${pay.currency}, the request is for ${lower(request.destToken)}
|
|
454
|
-
if (pay.minimumAmount <= 0n) throw new RelayRefusal("output-minimum", "the order's minimum output is zero");
|
|
455
|
+
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}")`, "recipient_mismatch");
|
|
456
|
+
if (!isAddr(pay.recipient, request.recipient)) throw new RelayRefusal("output-recipient", `the order pays ${pay.recipient}, not the recipient ${lower(request.recipient)}`, "recipient_mismatch");
|
|
457
|
+
if (!isAddr(pay.currency, request.destToken)) throw new RelayRefusal("output-currency", `the order pays in ${pay.currency}, the request is for ${lower(request.destToken)}`, "price_check_failed");
|
|
458
|
+
if (pay.minimumAmount <= 0n) throw new RelayRefusal("output-minimum", "the order's minimum output is zero", "price_check_failed");
|
|
455
459
|
if (request.minAmountOut !== void 0 && pay.minimumAmount < request.minAmountOut) {
|
|
456
|
-
throw new RelayRefusal("output-minimum", `the order's minimum output ${pay.minimumAmount} is under the caller's floor ${request.minAmountOut}
|
|
460
|
+
throw new RelayRefusal("output-minimum", `the order's minimum output ${pay.minimumAmount} is under the caller's floor ${request.minAmountOut}`, "price_check_failed");
|
|
457
461
|
}
|
|
458
|
-
if (pay.expectedAmount < pay.minimumAmount) throw new RelayRefusal("output-expected", `the order expects ${pay.expectedAmount}, less than its own minimum ${pay.minimumAmount}
|
|
462
|
+
if (pay.expectedAmount < pay.minimumAmount) throw new RelayRefusal("output-expected", `the order expects ${pay.expectedAmount}, less than its own minimum ${pay.minimumAmount}`, "price_check_failed");
|
|
459
463
|
const slippage = BigInt(request.slippageBps ?? RELAY_DEFAULT_SLIPPAGE_BPS);
|
|
460
464
|
const floor = pay.expectedAmount * (10000n - slippage) / 10000n;
|
|
461
465
|
if (pay.minimumAmount < floor) {
|
|
462
|
-
throw new RelayRefusal("output-minimum", `the order's minimum output ${pay.minimumAmount} is more than ${slippage} bps under its expected ${pay.expectedAmount}
|
|
466
|
+
throw new RelayRefusal("output-minimum", `the order's minimum output ${pay.minimumAmount} is more than ${slippage} bps under its expected ${pay.expectedAmount}`, "price_check_failed");
|
|
463
467
|
}
|
|
464
468
|
checkDeadline("output-deadline", "the output deadline", order.output.deadline, nowS);
|
|
465
|
-
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}")
|
|
466
|
-
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)}
|
|
467
|
-
if (input.payment.amount !== request.amount) throw new RelayRefusal("input-amount", `the order is funded with ${input.payment.amount}, the request sends ${request.amount}
|
|
469
|
+
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}")`, "policy_refused");
|
|
470
|
+
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)}`, "policy_refused");
|
|
471
|
+
if (input.payment.amount !== request.amount) throw new RelayRefusal("input-amount", `the order is funded with ${input.payment.amount}, the request sends ${request.amount}`, "policy_refused");
|
|
468
472
|
let originRefund = false;
|
|
469
473
|
for (const refund of input.refunds) {
|
|
470
474
|
if (refund.chainId === source.slug) {
|
|
471
|
-
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)}
|
|
472
|
-
if (refund.currency !== input.payment.currency) throw new RelayRefusal("refund-currency", `a source-chain refund is in ${refund.currency}, not the token sent
|
|
475
|
+
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)}`, "recipient_mismatch");
|
|
476
|
+
if (refund.currency !== input.payment.currency) throw new RelayRefusal("refund-currency", `a source-chain refund is in ${refund.currency}, not the token sent`, "policy_refused");
|
|
473
477
|
originRefund = true;
|
|
474
478
|
} else if (refund.chainId === dest.slug) {
|
|
475
479
|
if (!isAddr(refund.recipient, request.recipient) && !isAddr(refund.recipient, request.depositor)) {
|
|
476
|
-
throw new RelayRefusal("refund-recipient", `a destination-chain refund goes to ${refund.recipient}, neither the recipient nor the sender
|
|
480
|
+
throw new RelayRefusal("refund-recipient", `a destination-chain refund goes to ${refund.recipient}, neither the recipient nor the sender`, "recipient_mismatch");
|
|
477
481
|
}
|
|
478
|
-
if (refund.currency.length !== 42) throw new RelayRefusal("refund-currency", "a destination-chain refund names no token");
|
|
482
|
+
if (refund.currency.length !== 42) throw new RelayRefusal("refund-currency", "a destination-chain refund names no token", "policy_refused");
|
|
479
483
|
} else {
|
|
480
|
-
throw new RelayRefusal("refund-chain", `a refund is payable on "${refund.chainId}", neither the source nor the destination chain
|
|
484
|
+
throw new RelayRefusal("refund-chain", `a refund is payable on "${refund.chainId}", neither the source nor the destination chain`, "recipient_mismatch");
|
|
481
485
|
}
|
|
482
486
|
checkDeadline("refund-deadline", "a refund deadline", refund.deadline, nowS);
|
|
483
487
|
}
|
|
484
|
-
if (!originRefund) throw new RelayRefusal("refund-missing", "the order has no refund to the sender on the source chain");
|
|
488
|
+
if (!originRefund) throw new RelayRefusal("refund-missing", "the order has no refund to the sender on the source chain", "policy_refused");
|
|
485
489
|
const id = relayOrderId(order);
|
|
486
490
|
if (typeof protocol.orderId !== "string" || lower(protocol.orderId) !== id) {
|
|
487
|
-
throw new RelayRefusal("order-id-mismatch", `the quote names order ${String(protocol.orderId)}, the order data hashes to ${id}
|
|
491
|
+
throw new RelayRefusal("order-id-mismatch", `the quote names order ${String(protocol.orderId)}, the order data hashes to ${id}`, "recipient_unverifiable");
|
|
488
492
|
}
|
|
489
493
|
const signer = relayOrderSigner(id, protocol.orderSignature);
|
|
490
494
|
if (signer === null || signer !== order.solver) {
|
|
491
|
-
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}
|
|
495
|
+
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}`, "recipient_unverifiable");
|
|
492
496
|
}
|
|
493
497
|
const pd = protocol.paymentDetails;
|
|
494
|
-
if (pd === null || typeof pd !== "object") throw new RelayRefusal("payment-details", "the quote has no payment details");
|
|
498
|
+
if (pd === null || typeof pd !== "object") throw new RelayRefusal("payment-details", "the quote has no payment details", "upstream_unavailable");
|
|
495
499
|
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)) {
|
|
496
|
-
throw new RelayRefusal("payment-details", "the quote's payment details do not describe the order's input on the pinned depository");
|
|
500
|
+
throw new RelayRefusal("payment-details", "the quote's payment details do not describe the order's input on the pinned depository", "vault_mismatch");
|
|
497
501
|
}
|
|
498
502
|
const built = buildRelayDeposit(source, request.depositor, input.payment.currency, input.payment.amount, id);
|
|
499
503
|
if (deposit !== void 0) {
|
|
500
504
|
checkDepositTx(deposit, source, request, order, id);
|
|
501
505
|
if (lower(deposit.data) !== built.data || lower(deposit.to) !== built.to || deposit.value !== built.value) {
|
|
502
|
-
throw new RelayRefusal("tx-calldata", "the deposit is not the call this module builds for the order");
|
|
506
|
+
throw new RelayRefusal("tx-calldata", "the deposit is not the call this module builds for the order", "policy_refused");
|
|
503
507
|
}
|
|
504
508
|
}
|
|
505
509
|
return { order, id, deposit: built, expectedAmount: pay.expectedAmount, minimumAmount: pay.minimumAmount, source, dest };
|
|
@@ -509,22 +513,22 @@ function relayRefusalReason(err) {
|
|
|
509
513
|
throw err;
|
|
510
514
|
}
|
|
511
515
|
function relayRequestOf(params, opts = {}) {
|
|
512
|
-
const invalid = (what) => {
|
|
513
|
-
throw new RelayRefusal("request-invalid", what);
|
|
516
|
+
const invalid = (what, reason) => {
|
|
517
|
+
throw new RelayRefusal("request-invalid", what, reason);
|
|
514
518
|
};
|
|
515
|
-
if (params.flex) invalid("the amount is sealed into the order (no flexible deposits)");
|
|
516
|
-
if (!relayChain(params.sourceChain)) throw new RelayRefusal("chain-unsupported", `source chain ${params.sourceChain} is not a pinned Relay chain
|
|
517
|
-
if (!relayChain(params.destChain)) throw new RelayRefusal("chain-unsupported", `destination chain ${params.destChain} is not a pinned Relay chain
|
|
518
|
-
if (params.sourceChain === params.destChain) throw new RelayRefusal("chain-unsupported", "same-chain swaps are not served through the depository");
|
|
519
|
+
if (params.flex) invalid("the amount is sealed into the order (no flexible deposits)", "unsupported_pair");
|
|
520
|
+
if (!relayChain(params.sourceChain)) throw new RelayRefusal("chain-unsupported", `source chain ${params.sourceChain} is not a pinned Relay chain`, "unsupported_pair");
|
|
521
|
+
if (!relayChain(params.destChain)) throw new RelayRefusal("chain-unsupported", `destination chain ${params.destChain} is not a pinned Relay chain`, "unsupported_pair");
|
|
522
|
+
if (params.sourceChain === params.destChain) throw new RelayRefusal("chain-unsupported", "same-chain swaps are not served through the depository", "unsupported_pair");
|
|
519
523
|
let sourceToken;
|
|
520
524
|
if (typeof params.sourceToken === "string" && ADDRESS.test(params.sourceToken)) sourceToken = params.sourceToken;
|
|
521
525
|
else {
|
|
522
526
|
const asset = (0, import_util.sourceAssetOf)(params);
|
|
523
|
-
if (!asset) invalid(`source token ${String(params.sourceToken)} is not known on ${params.sourceChain}
|
|
527
|
+
if (!asset) invalid(`source token ${String(params.sourceToken)} is not known on ${params.sourceChain}`, "unsupported_pair");
|
|
524
528
|
sourceToken = asset.contract ?? RELAY_NATIVE;
|
|
525
|
-
if (!ADDRESS.test(sourceToken)) invalid(`source token ${String(params.sourceToken)} has no EVM contract
|
|
529
|
+
if (!ADDRESS.test(sourceToken)) invalid(`source token ${String(params.sourceToken)} has no EVM contract`, "unsupported_pair");
|
|
526
530
|
}
|
|
527
|
-
if (typeof params.destToken !== "string" || !ADDRESS.test(params.destToken)) invalid("the destination token must be an EVM address (zero for the native coin)");
|
|
531
|
+
if (typeof params.destToken !== "string" || !ADDRESS.test(params.destToken)) invalid("the destination token must be an EVM address (zero for the native coin)", "unsupported_pair");
|
|
528
532
|
if (typeof params.amountIn !== "string" || !/^[1-9]\d{0,77}$/.test(params.amountIn)) invalid("amount must be a positive base-unit integer");
|
|
529
533
|
if (params.minAmountOut !== void 0 && !/^(?:0|[1-9]\d{0,77})$/.test(params.minAmountOut)) invalid("the output floor must be a base-unit integer");
|
|
530
534
|
const recipient = params.destAddress || opts.placeholder || "";
|
|
@@ -560,57 +564,63 @@ function relayProtocolOf(quote) {
|
|
|
560
564
|
return { orderId: pd.orderId, hubType: pd.hubType, orderData, orderSignature: pd.orderSignature, paymentDetails };
|
|
561
565
|
}
|
|
562
566
|
function checkRelayQuote(request, quote, tx) {
|
|
563
|
-
if (quote === null || typeof quote !== "object" || quote.provider !== "relay") throw new RelayRefusal("quote-shape", "not a Relay quote");
|
|
567
|
+
if (quote === null || typeof quote !== "object" || quote.provider !== "relay") throw new RelayRefusal("quote-shape", "not a Relay quote", "policy_refused");
|
|
564
568
|
const t = tx ?? quote.txRequest;
|
|
565
|
-
if (!t) throw new RelayRefusal("quote-shape", "the quote has no transaction");
|
|
566
|
-
if (quote.depositAddress) throw new RelayRefusal("quote-shape", "a Relay quote pays the depository through its calldata, never a deposit address");
|
|
569
|
+
if (!t) throw new RelayRefusal("quote-shape", "the quote has no transaction", "policy_refused");
|
|
570
|
+
if (quote.depositAddress) throw new RelayRefusal("quote-shape", "a Relay quote pays the depository through its calldata, never a deposit address", "vault_mismatch");
|
|
567
571
|
if (quote.txRequest?.serializedTx !== void 0 || quote.txRequest?.nativeFee !== void 0) {
|
|
568
|
-
throw new RelayRefusal("quote-shape", "a Relay deposit carries no extra native fee and no serialized transaction");
|
|
572
|
+
throw new RelayRefusal("quote-shape", "a Relay deposit carries no extra native fee and no serialized transaction", "policy_refused");
|
|
569
573
|
}
|
|
570
574
|
const v = verifyRelayOrder(request, relayProtocolOf(quote), t);
|
|
571
575
|
const approval = t.approvalAddress;
|
|
572
576
|
if (v.deposit.approvalAddress === void 0 ? approval !== void 0 && approval !== "" : typeof approval !== "string" || lower(approval) !== v.deposit.approvalAddress) {
|
|
573
|
-
throw new RelayRefusal("quote-approval", `the quote approves ${String(approval)}, the order needs ${v.deposit.approvalAddress ?? "no approval"}
|
|
577
|
+
throw new RelayRefusal("quote-approval", `the quote approves ${String(approval)}, the order needs ${v.deposit.approvalAddress ?? "no approval"}`, "vault_mismatch");
|
|
574
578
|
}
|
|
575
579
|
if (quote.sourceAmount !== request.amount.toString(10) || quote.minDestAmount !== v.minimumAmount.toString(10) || quote.destAmount !== v.expectedAmount.toString(10)) {
|
|
576
|
-
throw new RelayRefusal("quote-amounts", "the amounts the quote shows are not the order's");
|
|
580
|
+
throw new RelayRefusal("quote-amounts", "the amounts the quote shows are not the order's", "price_check_failed");
|
|
577
581
|
}
|
|
578
582
|
const gas = t.gasLimit;
|
|
579
583
|
if (gas !== void 0) {
|
|
580
584
|
const floor = v.deposit.approvalAddress === void 0 ? RELAY_GAS_FLOOR_NATIVE : RELAY_GAS_FLOOR_ERC20;
|
|
581
585
|
if (typeof gas !== "string" || !/^[1-9]\d{0,10}$/.test(gas) || BigInt(gas) < floor || BigInt(gas) > RELAY_GAS_CAP) {
|
|
582
|
-
throw new RelayRefusal("quote-gas", `the gas limit ${String(gas).slice(0, 40)} is outside ${floor}\u2013${RELAY_GAS_CAP}
|
|
586
|
+
throw new RelayRefusal("quote-gas", `the gas limit ${String(gas).slice(0, 40)} is outside ${floor}\u2013${RELAY_GAS_CAP}`, "policy_refused");
|
|
583
587
|
}
|
|
584
588
|
}
|
|
585
589
|
const now = request.nowMs ?? Date.now();
|
|
586
590
|
if (typeof quote.validUntilMs !== "number" || !Number.isFinite(quote.validUntilMs) || quote.validUntilMs > now + RELAY_MAX_QUOTE_LIFE_MS) {
|
|
587
|
-
throw new RelayRefusal("quote-expiry", "the quote claims to live longer than a Relay quote may");
|
|
591
|
+
throw new RelayRefusal("quote-expiry", "the quote claims to live longer than a Relay quote may", "policy_refused");
|
|
588
592
|
}
|
|
589
593
|
if (typeof quote.intentId !== "string" || quote.intentId !== `relay:${String(quote.providerData?.requestId)}` || !/^relay:0x[0-9a-f]{64}$/.test(quote.intentId)) {
|
|
590
|
-
throw new RelayRefusal("quote-shape", "the quote's id is not its Relay request id");
|
|
594
|
+
throw new RelayRefusal("quote-shape", "the quote's id is not its Relay request id", "policy_refused");
|
|
591
595
|
}
|
|
592
596
|
return v;
|
|
593
597
|
}
|
|
598
|
+
var pinNotes = (0, import_errors2.refusalNotes)();
|
|
599
|
+
function notedReason(err) {
|
|
600
|
+
const text2 = relayRefusalReason(err);
|
|
601
|
+
const info = (0, import_errors2.refusalOf)(err);
|
|
602
|
+
return info ? pinNotes.note(text2, info) : text2;
|
|
603
|
+
}
|
|
594
604
|
function relayVerifyQuote(params, quote) {
|
|
595
605
|
try {
|
|
596
606
|
checkRelayQuote(relayRequestOf(params), quote);
|
|
597
607
|
return null;
|
|
598
608
|
} catch (err) {
|
|
599
|
-
return
|
|
609
|
+
return notedReason(err);
|
|
600
610
|
}
|
|
601
611
|
}
|
|
602
612
|
function relayVerifyBuild(quote, tx, params) {
|
|
603
613
|
try {
|
|
604
|
-
if (!params) throw new RelayRefusal("request-invalid", "the request this quote answered is not known on this side \u2014 quote again");
|
|
605
|
-
if (tx.chain !== params.sourceChain) throw new RelayRefusal("tx-chain", `the build is for ${tx.chain}, not ${params.sourceChain}
|
|
614
|
+
if (!params) throw new RelayRefusal("request-invalid", "the request this quote answered is not known on this side \u2014 quote again", "policy_refused");
|
|
615
|
+
if (tx.chain !== params.sourceChain) throw new RelayRefusal("tx-chain", `the build is for ${tx.chain}, not ${params.sourceChain}`, "vault_mismatch");
|
|
606
616
|
if (tx.serializedTx !== void 0 || tx.memo !== void 0 || tx.typedData !== void 0 || tx.bodyBoc !== void 0) {
|
|
607
|
-
throw new RelayRefusal("quote-shape", "a Relay deposit is one EVM call");
|
|
617
|
+
throw new RelayRefusal("quote-shape", "a Relay deposit is one EVM call", "policy_refused");
|
|
608
618
|
}
|
|
609
619
|
const v = checkRelayQuote(relayRequestOf(params), quote, tx);
|
|
610
620
|
relayClearOrder(v.id, "build");
|
|
611
621
|
return null;
|
|
612
622
|
} catch (err) {
|
|
613
|
-
return
|
|
623
|
+
return notedReason(err);
|
|
614
624
|
}
|
|
615
625
|
}
|
|
616
626
|
var relayPins = Object.freeze({
|
|
@@ -623,7 +633,8 @@ var relayPins = Object.freeze({
|
|
|
623
633
|
return c2 ? [c2.depository] : [];
|
|
624
634
|
},
|
|
625
635
|
verifyQuote: relayVerifyQuote,
|
|
626
|
-
verifyBuild: relayVerifyBuild
|
|
636
|
+
verifyBuild: relayVerifyBuild,
|
|
637
|
+
reasonOf: pinNotes.reasonOf
|
|
627
638
|
});
|
|
628
639
|
|
|
629
640
|
// src/provider.ts
|
|
@@ -647,28 +658,69 @@ var RELAY_STATUS_MAP = Object.freeze({
|
|
|
647
658
|
refunded: "refunded",
|
|
648
659
|
failure: "refund-pending"
|
|
649
660
|
});
|
|
661
|
+
var RELAY_ERROR_REASONS = Object.freeze(Object.assign(/* @__PURE__ */ Object.create(null), {
|
|
662
|
+
AMOUNT_TOO_LOW: "amount_too_small",
|
|
663
|
+
AMOUNT_TOO_HIGH: "amount_too_large",
|
|
664
|
+
INSUFFICIENT_LIQUIDITY: "insufficient_liquidity",
|
|
665
|
+
NO_QUOTES: "no_route",
|
|
666
|
+
NO_SWAP_ROUTES_FOUND: "no_route",
|
|
667
|
+
NO_INTERNAL_SWAP_ROUTES_FOUND: "no_route",
|
|
668
|
+
// Relay's own failures: a third-party pricing failure, and the transient codes it says to retry with backoff.
|
|
669
|
+
SWAP_QUOTE_FAILED: "upstream_unavailable",
|
|
670
|
+
REQUEST_TIMED_OUT: "upstream_unavailable",
|
|
671
|
+
RPC_HTTP_ERROR: "upstream_unavailable",
|
|
672
|
+
PRICE_FETCH_FAILED: "upstream_unavailable",
|
|
673
|
+
SERVICE_UNAVAILABLE: "upstream_unavailable",
|
|
674
|
+
// The recipient resolves to a token contract, which cannot receive.
|
|
675
|
+
INVALID_RECIPIENT: "recipient_mismatch",
|
|
676
|
+
UNSUPPORTED_CHAIN: "unsupported_pair",
|
|
677
|
+
UNSUPPORTED_CURRENCY: "unsupported_pair",
|
|
678
|
+
UNSUPPORTED_ROUTE: "unsupported_pair",
|
|
679
|
+
UNSUPPORTED_EXECUTION_TYPE: "unsupported_pair",
|
|
680
|
+
INVALID_INPUT_CURRENCY: "unsupported_pair",
|
|
681
|
+
INVALID_OUTPUT_CURRENCY: "unsupported_pair",
|
|
682
|
+
CHAIN_DISABLED: "chain_halted",
|
|
683
|
+
ROUTE_TEMPORARILY_RESTRICTED: "chain_halted",
|
|
684
|
+
SWAP_IMPACT_TOO_HIGH: "price_check_failed",
|
|
685
|
+
UNAUTHORIZED: "key_rejected",
|
|
686
|
+
UNAUTHORIZED_QUOTE: "key_rejected",
|
|
687
|
+
FORBIDDEN: "key_rejected"
|
|
688
|
+
}));
|
|
689
|
+
function quoteHttpRefusal(res, parsed, aborted) {
|
|
690
|
+
const code = parsed?.errorCode;
|
|
691
|
+
const byCode = typeof code === "string" && Object.prototype.hasOwnProperty.call(RELAY_ERROR_REASONS, code) ? RELAY_ERROR_REASONS[code] : void 0;
|
|
692
|
+
const byStatus = (0, import_errors3.httpRefusal)(res.status, res.headers?.get?.("retry-after"));
|
|
693
|
+
if (byStatus?.reason === "rate_limited") return byStatus;
|
|
694
|
+
if (byCode) {
|
|
695
|
+
if (byStatus?.reason === byCode) return byStatus;
|
|
696
|
+
return byCode === "upstream_unavailable" || byCode === "key_rejected" ? { reason: byCode, httpStatus: res.status } : { reason: byCode };
|
|
697
|
+
}
|
|
698
|
+
if (byStatus) return byStatus;
|
|
699
|
+
if (parsed !== null && typeof parsed === "object") return { reason: "no_route" };
|
|
700
|
+
return aborted ? void 0 : { reason: "upstream_unavailable", httpStatus: res.status };
|
|
701
|
+
}
|
|
650
702
|
function clip(text2, max = 240) {
|
|
651
703
|
const t = (0, import_util2.redactUrls)(text2).replace(/\s+/g, " ").trim();
|
|
652
704
|
return t.length > max ? `${t.slice(0, max)}\u2026` : t;
|
|
653
705
|
}
|
|
654
706
|
function depositStep(body) {
|
|
655
707
|
const steps = body.steps;
|
|
656
|
-
if (!Array.isArray(steps) || steps.length === 0 || steps.length > 8) throw new RelayRefusal("quote-steps", "the quote has no steps");
|
|
708
|
+
if (!Array.isArray(steps) || steps.length === 0 || steps.length > 8) throw new RelayRefusal("quote-steps", "the quote has no steps", "upstream_unavailable");
|
|
657
709
|
let found = null;
|
|
658
710
|
for (const s of steps) {
|
|
659
|
-
if (s === null || typeof s !== "object") throw new RelayRefusal("quote-steps", "the quote has a malformed step");
|
|
711
|
+
if (s === null || typeof s !== "object") throw new RelayRefusal("quote-steps", "the quote has a malformed step", "upstream_unavailable");
|
|
660
712
|
if (s.id === "approve") continue;
|
|
661
|
-
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
|
|
662
|
-
if (found) throw new RelayRefusal("quote-steps", "the quote has more than one deposit step");
|
|
713
|
+
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`, "unsupported_pair");
|
|
714
|
+
if (found) throw new RelayRefusal("quote-steps", "the quote has more than one deposit step", "unsupported_pair");
|
|
663
715
|
found = s;
|
|
664
716
|
}
|
|
665
|
-
if (!found) throw new RelayRefusal("quote-steps", "the quote has no deposit step");
|
|
717
|
+
if (!found) throw new RelayRefusal("quote-steps", "the quote has no deposit step", "upstream_unavailable");
|
|
666
718
|
const items = found.items;
|
|
667
|
-
if (!Array.isArray(items) || items.length !== 1) throw new RelayRefusal("quote-steps", "the deposit step is not a single transaction");
|
|
719
|
+
if (!Array.isArray(items) || items.length !== 1) throw new RelayRefusal("quote-steps", "the deposit step is not a single transaction", "unsupported_pair");
|
|
668
720
|
const data = items[0]?.data;
|
|
669
|
-
if (data === null || typeof data !== "object") throw new RelayRefusal("quote-steps", "the deposit step has no transaction");
|
|
721
|
+
if (data === null || typeof data !== "object") throw new RelayRefusal("quote-steps", "the deposit step has no transaction", "upstream_unavailable");
|
|
670
722
|
const requestId = typeof found.requestId === "string" ? found.requestId.toLowerCase() : "";
|
|
671
|
-
if (!REQUEST_ID.test(requestId)) throw new RelayRefusal("quote-steps", "the quote has no request id to track the deposit by");
|
|
723
|
+
if (!REQUEST_ID.test(requestId)) throw new RelayRefusal("quote-steps", "the quote has no request id to track the deposit by", "upstream_unavailable");
|
|
672
724
|
return { requestId, tx: data };
|
|
673
725
|
}
|
|
674
726
|
function relayProvider(config = {}) {
|
|
@@ -704,12 +756,12 @@ function relayProvider(config = {}) {
|
|
|
704
756
|
.../^\d{1,5}$/.test(pd.slippageBps ?? "") ? { slippageBps: Number(pd.slippageBps) } : {}
|
|
705
757
|
};
|
|
706
758
|
if (sealed.depositor.toLowerCase() === EVM_PLACEHOLDER.toLowerCase() || sealed.recipient.toLowerCase() === EVM_PLACEHOLDER.toLowerCase()) {
|
|
707
|
-
throw new RelayRefusal("request-invalid", "a quote priced without the real addresses is never built or signed \u2014 quote again with them");
|
|
759
|
+
throw new RelayRefusal("request-invalid", "a quote priced without the real addresses is never built or signed \u2014 quote again with them", "policy_refused");
|
|
708
760
|
}
|
|
709
761
|
const mine = issued.get(quote.intentId);
|
|
710
762
|
if (!mine) return sealed;
|
|
711
763
|
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();
|
|
712
|
-
if (!same) throw new RelayRefusal("request-invalid", "the quote no longer says what was asked when it was issued \u2014 quote again");
|
|
764
|
+
if (!same) throw new RelayRefusal("request-invalid", "the quote no longer says what was asked when it was issued \u2014 quote again", "policy_refused");
|
|
713
765
|
return mine;
|
|
714
766
|
}
|
|
715
767
|
async function readJson(res) {
|
|
@@ -725,8 +777,8 @@ function relayProvider(config = {}) {
|
|
|
725
777
|
supportsChainPair: (from, to) => relay.supports(from, to),
|
|
726
778
|
supportsRoute: (from, to) => relay.supports(from.chain, to.chain),
|
|
727
779
|
async getQuote(params, signal) {
|
|
728
|
-
const refuse = (reason) => {
|
|
729
|
-
params.onRefusal?.(reason);
|
|
780
|
+
const refuse = (reason, info) => {
|
|
781
|
+
params.onRefusal?.(reason, info);
|
|
730
782
|
return null;
|
|
731
783
|
};
|
|
732
784
|
const dry = params.dry !== false;
|
|
@@ -735,7 +787,7 @@ function relayProvider(config = {}) {
|
|
|
735
787
|
try {
|
|
736
788
|
request = relayRequestOf(params, dry ? { placeholder: EVM_PLACEHOLDER } : {});
|
|
737
789
|
} catch (err) {
|
|
738
|
-
if (err instanceof RelayRefusal) return refuse(err.message);
|
|
790
|
+
if (err instanceof RelayRefusal) return refuse(err.message, (0, import_errors3.refusalOf)(err));
|
|
739
791
|
throw err;
|
|
740
792
|
}
|
|
741
793
|
const src = relayChain(request.sourceChain);
|
|
@@ -770,20 +822,21 @@ function relayProvider(config = {}) {
|
|
|
770
822
|
if (!res.ok) {
|
|
771
823
|
const e = parsed ?? {};
|
|
772
824
|
const why = typeof e.message === "string" ? clip(e.message) : `HTTP ${res.status}`;
|
|
773
|
-
return refuse(`relay: no quote \u2014 ${typeof e.errorCode === "string" ? `${clip(e.errorCode, 60)}: ` : ""}${why}
|
|
825
|
+
return refuse(`relay: no quote \u2014 ${typeof e.errorCode === "string" ? `${clip(e.errorCode, 60)}: ` : ""}${why}`, quoteHttpRefusal(res, parsed, signal?.aborted === true));
|
|
774
826
|
}
|
|
775
|
-
if (parsed === null || typeof parsed !== "object") return refuse("relay: the quote response is not JSON");
|
|
827
|
+
if (parsed === null || typeof parsed !== "object") return refuse("relay: the quote response is not JSON", signal?.aborted ? void 0 : { reason: "upstream_unavailable" });
|
|
776
828
|
body = parsed;
|
|
777
829
|
} catch (err) {
|
|
778
|
-
|
|
830
|
+
const info = (0, import_errors3.refusalOf)(err) ?? (signal?.aborted ? void 0 : { reason: "upstream_unavailable" });
|
|
831
|
+
return refuse(`relay: quote request failed \u2014 ${clip(err instanceof Error ? err.message : String(err))}`, info);
|
|
779
832
|
}
|
|
780
833
|
try {
|
|
781
834
|
const step = depositStep(body);
|
|
782
835
|
const proto = body.protocol?.v2 ?? null;
|
|
783
836
|
const v = verifyRelayOrder(request, proto, step.tx);
|
|
784
837
|
const extra = (x) => x !== void 0 && x !== null && x !== "0" && x !== 0;
|
|
785
|
-
if (extra(body.fees?.app?.amount)) throw new RelayRefusal("app-fee", "the quote charges an app fee this module did not ask for");
|
|
786
|
-
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");
|
|
838
|
+
if (extra(body.fees?.app?.amount)) throw new RelayRefusal("app-fee", "the quote charges an app fee this module did not ask for", "policy_refused");
|
|
839
|
+
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", "policy_refused");
|
|
787
840
|
const native = v.deposit.approvalAddress === void 0;
|
|
788
841
|
const floor = native ? RELAY_GAS_FLOOR_NATIVE : RELAY_GAS_FLOOR_ERC20;
|
|
789
842
|
const apiGas = typeof step.tx.gas === "string" && /^[1-9]\d{0,9}$/.test(step.tx.gas) ? BigInt(step.tx.gas) * 2n : 0n;
|
|
@@ -838,15 +891,15 @@ function relayProvider(config = {}) {
|
|
|
838
891
|
}
|
|
839
892
|
};
|
|
840
893
|
} catch (err) {
|
|
841
|
-
if (err instanceof RelayRefusal) return refuse(err.message);
|
|
842
|
-
return refuse(`relay: unreadable quote \u2014 ${clip(err instanceof Error ? err.message : String(err))}
|
|
894
|
+
if (err instanceof RelayRefusal) return refuse(err.message, (0, import_errors3.refusalOf)(err));
|
|
895
|
+
return refuse(`relay: unreadable quote \u2014 ${clip(err instanceof Error ? err.message : String(err))}`, (0, import_errors3.refusalOf)(err) ?? { reason: "upstream_unavailable" });
|
|
843
896
|
}
|
|
844
897
|
},
|
|
845
898
|
/** The quote's own transaction — after the order it carries has been verified once more. */
|
|
846
899
|
async buildDeposit(quote) {
|
|
847
900
|
const tx = quote.txRequest;
|
|
848
|
-
if (!tx || !quote.intentId.startsWith(INTENT_PREFIX)) throw
|
|
849
|
-
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");
|
|
901
|
+
if (!tx || !quote.intentId.startsWith(INTENT_PREFIX)) throw (0, import_errors3.refusal)("policy_refused", "swap2: Relay quote has no transaction request");
|
|
902
|
+
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", "policy_refused");
|
|
850
903
|
const request = requestOf(quote);
|
|
851
904
|
const v = checkRelayQuote(request, quote);
|
|
852
905
|
return {
|
|
@@ -864,15 +917,15 @@ function relayProvider(config = {}) {
|
|
|
864
917
|
* and the exact transaction about to be signed — and each order is cleared for one deposit only.
|
|
865
918
|
*/
|
|
866
919
|
async preflightDeposit(quote, tx) {
|
|
867
|
-
if (quote.providerData?.placeholder === "1") throw new RelayRefusal("request-invalid", "a quote priced without the real addresses is never signed");
|
|
920
|
+
if (quote.providerData?.placeholder === "1") throw new RelayRefusal("request-invalid", "a quote priced without the real addresses is never signed", "policy_refused");
|
|
868
921
|
const request = requestOf(quote);
|
|
869
|
-
if (tx.chain !== request.sourceChain) throw new RelayRefusal("tx-chain", `the deposit is for ${tx.chain}, not ${request.sourceChain}
|
|
922
|
+
if (tx.chain !== request.sourceChain) throw new RelayRefusal("tx-chain", `the deposit is for ${tx.chain}, not ${request.sourceChain}`, "vault_mismatch");
|
|
870
923
|
if (tx.serializedTx !== void 0 || tx.memo !== void 0 || tx.typedData !== void 0 || tx.bodyBoc !== void 0) {
|
|
871
|
-
throw new RelayRefusal("quote-shape", "a Relay deposit is one EVM call");
|
|
924
|
+
throw new RelayRefusal("quote-shape", "a Relay deposit is one EVM call", "policy_refused");
|
|
872
925
|
}
|
|
873
926
|
const v = checkRelayQuote(request, quote, tx);
|
|
874
927
|
const store = config.clearedStore;
|
|
875
|
-
if (store && await store.has(v.id)) throw new RelayRefusal("order-reused", `order ${v.id} was already cleared for a deposit \u2014 quote again
|
|
928
|
+
if (store && await store.has(v.id)) throw new RelayRefusal("order-reused", `order ${v.id} was already cleared for a deposit \u2014 quote again`, "policy_refused");
|
|
876
929
|
relayClearOrder(v.id, "sign");
|
|
877
930
|
if (store) await store.add(v.id, v.order.output.deadline * 1e3);
|
|
878
931
|
},
|