@anywalletkit/protocol-swap2-relay 0.1.0 → 0.1.2
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 +4 -4
- 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/index.cjs
CHANGED
|
@@ -129,13 +129,15 @@ var relay = {
|
|
|
129
129
|
|
|
130
130
|
// src/order.ts
|
|
131
131
|
var import_secp256k1 = require("@noble/curves/secp256k1.js");
|
|
132
|
+
var import_errors = require("@anywalletkit/protocol-swap2-core/errors");
|
|
132
133
|
var import_viem = require("viem");
|
|
133
134
|
var RelayRefusal = class extends Error {
|
|
134
135
|
code;
|
|
135
|
-
constructor(code, detail) {
|
|
136
|
+
constructor(code, detail, reason) {
|
|
136
137
|
super(`relay: ${detail} [${code}]`);
|
|
137
138
|
this.name = "RelayRefusal";
|
|
138
139
|
this.code = code;
|
|
140
|
+
if (reason !== void 0) (0, import_errors.withReason)(this, reason);
|
|
139
141
|
}
|
|
140
142
|
};
|
|
141
143
|
var RELAY_ORDER_TYPES = {
|
|
@@ -192,7 +194,7 @@ var MAX_BYTES_HEX = 2 + 2 * 1024;
|
|
|
192
194
|
var MAX_TEXT = 64;
|
|
193
195
|
var U256_MAX = (1n << 256n) - 1n;
|
|
194
196
|
var bad = (what) => {
|
|
195
|
-
throw new RelayRefusal("order-malformed", `the order's ${what}
|
|
197
|
+
throw new RelayRefusal("order-malformed", `the order's ${what}`, "upstream_unavailable");
|
|
196
198
|
};
|
|
197
199
|
function rec(v, what) {
|
|
198
200
|
if (v === null || typeof v !== "object" || Array.isArray(v)) bad(`${what} is not an object`);
|
|
@@ -311,6 +313,7 @@ function relayOrderSigner(id, signature) {
|
|
|
311
313
|
|
|
312
314
|
// src/verify.ts
|
|
313
315
|
var import_viem2 = require("viem");
|
|
316
|
+
var import_errors2 = require("@anywalletkit/protocol-swap2-core/errors");
|
|
314
317
|
var import_util = require("@anywalletkit/protocol-swap2-core/util");
|
|
315
318
|
|
|
316
319
|
// src/cleared.ts
|
|
@@ -320,10 +323,10 @@ var clearedOrders = /* @__PURE__ */ new Map();
|
|
|
320
323
|
function relayClearOrder(id, stage, nowMs = Date.now()) {
|
|
321
324
|
const key = `${stage}:${id.toLowerCase()}`;
|
|
322
325
|
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
|
|
326
|
+
if (until !== void 0 && until > nowMs) throw new RelayRefusal("order-reused", `order ${id} was already cleared for a deposit \u2014 quote again`, "policy_refused");
|
|
324
327
|
if (clearedOrders.size >= CLEARED_MAX) {
|
|
325
328
|
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");
|
|
329
|
+
if (clearedOrders.size >= CLEARED_MAX) throw new RelayRefusal("order-reused", "too many deposits cleared in the last minutes \u2014 try again later", "policy_refused");
|
|
327
330
|
}
|
|
328
331
|
clearedOrders.set(key, nowMs + CLEARED_TTL_MS);
|
|
329
332
|
}
|
|
@@ -365,24 +368,24 @@ function buildRelayDeposit(source, depositor, token, amount, id) {
|
|
|
365
368
|
};
|
|
366
369
|
}
|
|
367
370
|
function checkRequest(r) {
|
|
368
|
-
const invalid = (what) => {
|
|
369
|
-
throw new RelayRefusal("request-invalid", what);
|
|
371
|
+
const invalid = (what, reason) => {
|
|
372
|
+
throw new RelayRefusal("request-invalid", what, reason);
|
|
370
373
|
};
|
|
371
374
|
if (r === null || typeof r !== "object") invalid("no request to check the order against");
|
|
372
375
|
const source = relayChain(r.sourceChain);
|
|
373
376
|
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
|
+
if (!source) throw new RelayRefusal("chain-unsupported", `source chain ${String(r.sourceChain)} is not a pinned Relay chain`, "unsupported_pair");
|
|
378
|
+
if (!dest) throw new RelayRefusal("chain-unsupported", `destination chain ${String(r.destChain)} is not a pinned Relay chain`, "unsupported_pair");
|
|
379
|
+
if (source === dest) throw new RelayRefusal("chain-unsupported", "source and destination are the same chain", "unsupported_pair");
|
|
377
380
|
for (const [what, v] of [["input token", r.sourceToken], ["output token", r.destToken], ["depositor", r.depositor], ["recipient", r.recipient]]) {
|
|
378
381
|
if (typeof v !== "string" || !ADDRESS.test(v)) invalid(`the ${what} is not an address`);
|
|
379
382
|
}
|
|
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");
|
|
383
|
+
if (/^0x0{40}$/.test(r.depositor)) invalid("the depositor is the zero address", "recipient_mismatch");
|
|
384
|
+
if (/^0x0{40}$/.test(r.recipient)) invalid("the recipient is the zero address", "recipient_mismatch");
|
|
382
385
|
if (typeof r.amount !== "bigint" || r.amount <= 0n || r.amount > U256_MAX2) invalid("the amount is not a positive integer");
|
|
383
386
|
if (r.minAmountOut !== void 0 && (typeof r.minAmountOut !== "bigint" || r.minAmountOut < 0n)) invalid("the output floor is not an unsigned integer");
|
|
384
387
|
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}
|
|
388
|
+
invalid(`slippage must be a whole number of basis points up to ${RELAY_MAX_SLIPPAGE_BPS}`, "policy_refused");
|
|
386
389
|
}
|
|
387
390
|
const nowMs = r.nowMs ?? Date.now();
|
|
388
391
|
if (!Number.isFinite(nowMs) || nowMs <= 0) invalid("the clock is not a time");
|
|
@@ -392,143 +395,143 @@ var isAddr = (bytes2, addr) => bytes2.length === 42 && bytes2 === lower(addr);
|
|
|
392
395
|
function checkDeadline(code, what, deadline, nowS) {
|
|
393
396
|
const left = deadline - nowS;
|
|
394
397
|
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
|
|
398
|
+
throw new RelayRefusal(code, `${what} is ${left} s away, past the ${RELAY_ORDER_LIFETIME_S} s an order lives`, "policy_refused");
|
|
396
399
|
}
|
|
397
400
|
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
|
|
401
|
+
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");
|
|
399
402
|
}
|
|
400
403
|
}
|
|
401
404
|
function wordAddress(word) {
|
|
402
405
|
return /^0{24}/.test(word) ? `0x${word.slice(24)}` : null;
|
|
403
406
|
}
|
|
404
407
|
function checkDepositTarget(tx, source) {
|
|
405
|
-
if (tx === null || typeof tx !== "object") throw new RelayRefusal("tx-calldata", "there is no deposit transaction");
|
|
408
|
+
if (tx === null || typeof tx !== "object") throw new RelayRefusal("tx-calldata", "there is no deposit transaction", "policy_refused");
|
|
406
409
|
if (tx.chainId !== source.chainId) {
|
|
407
|
-
throw new RelayRefusal("tx-chain", `the deposit is for chain ${String(tx.chainId)}, not ${source.chainId}
|
|
410
|
+
throw new RelayRefusal("tx-chain", `the deposit is for chain ${String(tx.chainId)}, not ${source.chainId}`, "vault_mismatch");
|
|
408
411
|
}
|
|
409
412
|
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
|
|
413
|
+
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");
|
|
411
414
|
}
|
|
412
415
|
}
|
|
413
416
|
function checkDepositTx(tx, source, request, order, id) {
|
|
414
417
|
checkDepositTarget(tx, source);
|
|
415
418
|
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");
|
|
419
|
+
throw new RelayRefusal("tx-calldata", "the deposit has no calldata", "policy_refused");
|
|
417
420
|
}
|
|
418
421
|
const data = lower(tx.data);
|
|
419
422
|
const selector = data.slice(0, 10);
|
|
420
423
|
const input = order.inputs[0].payment;
|
|
421
424
|
const native = isNative(input.currency);
|
|
422
425
|
if (selector === RELAY_DEPOSIT_ERC20_ALLOWANCE) {
|
|
423
|
-
throw new RelayRefusal("tx-selector", "the deposit uses depositErc20 without an amount, which takes the whole allowance");
|
|
426
|
+
throw new RelayRefusal("tx-selector", "the deposit uses depositErc20 without an amount, which takes the whole allowance", "policy_refused");
|
|
424
427
|
}
|
|
425
428
|
if (selector !== RELAY_DEPOSIT_NATIVE && selector !== RELAY_DEPOSIT_ERC20) {
|
|
426
|
-
throw new RelayRefusal("tx-selector", `the deposit calls ${selector}, not a known deposit function
|
|
429
|
+
throw new RelayRefusal("tx-selector", `the deposit calls ${selector}, not a known deposit function`, "policy_refused");
|
|
427
430
|
}
|
|
428
431
|
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"}
|
|
432
|
+
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");
|
|
430
433
|
}
|
|
431
434
|
const words = native ? 2 : 4;
|
|
432
435
|
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}
|
|
436
|
+
throw new RelayRefusal("tx-calldata", `the deposit calldata is ${(data.length - 2) / 2} bytes, not ${4 + 32 * words}`, "policy_refused");
|
|
434
437
|
}
|
|
435
438
|
const word = (i) => data.slice(10 + 64 * i, 74 + 64 * i);
|
|
436
439
|
const depositor = wordAddress(word(0));
|
|
437
|
-
if (depositor === null) throw new RelayRefusal("tx-calldata", "the depositor argument is not a padded address");
|
|
440
|
+
if (depositor === null) throw new RelayRefusal("tx-calldata", "the depositor argument is not a padded address", "policy_refused");
|
|
438
441
|
if (depositor !== lower(request.depositor)) {
|
|
439
|
-
throw new RelayRefusal("tx-depositor", `the deposit is credited to ${depositor}, not to the sender ${lower(request.depositor)}
|
|
442
|
+
throw new RelayRefusal("tx-depositor", `the deposit is credited to ${depositor}, not to the sender ${lower(request.depositor)}`, "recipient_mismatch");
|
|
440
443
|
}
|
|
441
444
|
if (!native) {
|
|
442
445
|
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}
|
|
446
|
+
if (token === null) throw new RelayRefusal("tx-calldata", "the token argument is not a padded address", "policy_refused");
|
|
447
|
+
if (token !== input.currency) throw new RelayRefusal("tx-token", `the deposit moves ${token}, the order's input is ${input.currency}`, "policy_refused");
|
|
445
448
|
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}
|
|
449
|
+
if (amount !== input.amount) throw new RelayRefusal("tx-amount", `the deposit moves ${amount}, the order's input is ${input.amount}`, "policy_refused");
|
|
447
450
|
}
|
|
448
451
|
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}
|
|
452
|
+
if (txId !== id) throw new RelayRefusal("tx-id", `the deposit names order ${txId}, the order received hashes to ${id}`, "recipient_unverifiable");
|
|
450
453
|
const value = typeof tx.value === "string" && /^(?:0|[1-9]\d{0,77})$/.test(tx.value) ? BigInt(tx.value) : null;
|
|
451
454
|
const wantValue = native ? input.amount : 0n;
|
|
452
455
|
if (value === null || value !== wantValue) {
|
|
453
|
-
throw new RelayRefusal("tx-value", `the deposit sends ${String(tx.value)} wei, it must send ${wantValue}
|
|
456
|
+
throw new RelayRefusal("tx-value", `the deposit sends ${String(tx.value)} wei, it must send ${wantValue}`, "policy_refused");
|
|
454
457
|
}
|
|
455
458
|
}
|
|
456
459
|
function verifyRelayOrder(request, protocol, deposit) {
|
|
457
460
|
const { source, dest, nowS } = checkRequest(request);
|
|
458
461
|
if (deposit !== void 0) checkDepositTarget(deposit, source);
|
|
459
462
|
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");
|
|
463
|
+
throw new RelayRefusal("protocol-data-missing", "the quote carries no order (protocol.v2) \u2014 nothing to check the deposit against", "upstream_unavailable");
|
|
461
464
|
}
|
|
462
465
|
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");
|
|
466
|
+
throw new RelayRefusal("protocol-data-missing", "the quote carries no order data \u2014 nothing to check the deposit against", "upstream_unavailable");
|
|
464
467
|
}
|
|
465
468
|
if (protocol.hubType !== "onchain") {
|
|
466
|
-
throw new RelayRefusal("hub-type", `the order settles through a "${String(protocol.hubType)}" hub, only "onchain" is read
|
|
469
|
+
throw new RelayRefusal("hub-type", `the order settles through a "${String(protocol.hubType)}" hub, only "onchain" is read`, "unsupported_pair");
|
|
467
470
|
}
|
|
468
471
|
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");
|
|
472
|
+
if (order.version !== RELAY_ORDER_VERSION) throw new RelayRefusal("order-version", `order version "${order.version}" is not ${RELAY_ORDER_VERSION}`, "upstream_unavailable");
|
|
473
|
+
if (order.inputs.length !== 1) throw new RelayRefusal("order-inputs", `the order has ${order.inputs.length} inputs, exactly one is signed for`, "policy_refused");
|
|
474
|
+
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");
|
|
475
|
+
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");
|
|
476
|
+
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");
|
|
474
477
|
const input = order.inputs[0];
|
|
475
478
|
const pay = order.output.payments[0];
|
|
476
|
-
if (input.payment.weight === 0n) throw new RelayRefusal("input-weight", "the order's input has zero weight");
|
|
479
|
+
if (input.payment.weight === 0n) throw new RelayRefusal("input-weight", "the order's input has zero weight", "policy_refused");
|
|
477
480
|
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
|
|
481
|
+
throw new RelayRefusal("solver-not-pinned", `solver ${order.solver} on "${order.solverChainId}" is not a pinned Relay solver`, "recipient_unverifiable");
|
|
479
482
|
}
|
|
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");
|
|
483
|
+
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");
|
|
484
|
+
if (!isAddr(pay.recipient, request.recipient)) throw new RelayRefusal("output-recipient", `the order pays ${pay.recipient}, not the recipient ${lower(request.recipient)}`, "recipient_mismatch");
|
|
485
|
+
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");
|
|
486
|
+
if (pay.minimumAmount <= 0n) throw new RelayRefusal("output-minimum", "the order's minimum output is zero", "price_check_failed");
|
|
484
487
|
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}
|
|
488
|
+
throw new RelayRefusal("output-minimum", `the order's minimum output ${pay.minimumAmount} is under the caller's floor ${request.minAmountOut}`, "price_check_failed");
|
|
486
489
|
}
|
|
487
|
-
if (pay.expectedAmount < pay.minimumAmount) throw new RelayRefusal("output-expected", `the order expects ${pay.expectedAmount}, less than its own minimum ${pay.minimumAmount}
|
|
490
|
+
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");
|
|
488
491
|
const slippage = BigInt(request.slippageBps ?? RELAY_DEFAULT_SLIPPAGE_BPS);
|
|
489
492
|
const floor = pay.expectedAmount * (10000n - slippage) / 10000n;
|
|
490
493
|
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}
|
|
494
|
+
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");
|
|
492
495
|
}
|
|
493
496
|
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
|
+
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");
|
|
498
|
+
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");
|
|
499
|
+
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");
|
|
497
500
|
let originRefund = false;
|
|
498
501
|
for (const refund of input.refunds) {
|
|
499
502
|
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
|
|
503
|
+
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");
|
|
504
|
+
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");
|
|
502
505
|
originRefund = true;
|
|
503
506
|
} else if (refund.chainId === dest.slug) {
|
|
504
507
|
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
|
|
508
|
+
throw new RelayRefusal("refund-recipient", `a destination-chain refund goes to ${refund.recipient}, neither the recipient nor the sender`, "recipient_mismatch");
|
|
506
509
|
}
|
|
507
|
-
if (refund.currency.length !== 42) throw new RelayRefusal("refund-currency", "a destination-chain refund names no token");
|
|
510
|
+
if (refund.currency.length !== 42) throw new RelayRefusal("refund-currency", "a destination-chain refund names no token", "policy_refused");
|
|
508
511
|
} else {
|
|
509
|
-
throw new RelayRefusal("refund-chain", `a refund is payable on "${refund.chainId}", neither the source nor the destination chain
|
|
512
|
+
throw new RelayRefusal("refund-chain", `a refund is payable on "${refund.chainId}", neither the source nor the destination chain`, "recipient_mismatch");
|
|
510
513
|
}
|
|
511
514
|
checkDeadline("refund-deadline", "a refund deadline", refund.deadline, nowS);
|
|
512
515
|
}
|
|
513
|
-
if (!originRefund) throw new RelayRefusal("refund-missing", "the order has no refund to the sender on the source chain");
|
|
516
|
+
if (!originRefund) throw new RelayRefusal("refund-missing", "the order has no refund to the sender on the source chain", "policy_refused");
|
|
514
517
|
const id = relayOrderId(order);
|
|
515
518
|
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}
|
|
519
|
+
throw new RelayRefusal("order-id-mismatch", `the quote names order ${String(protocol.orderId)}, the order data hashes to ${id}`, "recipient_unverifiable");
|
|
517
520
|
}
|
|
518
521
|
const signer = relayOrderSigner(id, protocol.orderSignature);
|
|
519
522
|
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}
|
|
523
|
+
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");
|
|
521
524
|
}
|
|
522
525
|
const pd = protocol.paymentDetails;
|
|
523
|
-
if (pd === null || typeof pd !== "object") throw new RelayRefusal("payment-details", "the quote has no payment details");
|
|
526
|
+
if (pd === null || typeof pd !== "object") throw new RelayRefusal("payment-details", "the quote has no payment details", "upstream_unavailable");
|
|
524
527
|
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");
|
|
528
|
+
throw new RelayRefusal("payment-details", "the quote's payment details do not describe the order's input on the pinned depository", "vault_mismatch");
|
|
526
529
|
}
|
|
527
530
|
const built = buildRelayDeposit(source, request.depositor, input.payment.currency, input.payment.amount, id);
|
|
528
531
|
if (deposit !== void 0) {
|
|
529
532
|
checkDepositTx(deposit, source, request, order, id);
|
|
530
533
|
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");
|
|
534
|
+
throw new RelayRefusal("tx-calldata", "the deposit is not the call this module builds for the order", "policy_refused");
|
|
532
535
|
}
|
|
533
536
|
}
|
|
534
537
|
return { order, id, deposit: built, expectedAmount: pay.expectedAmount, minimumAmount: pay.minimumAmount, source, dest };
|
|
@@ -538,22 +541,22 @@ function relayRefusalReason(err) {
|
|
|
538
541
|
throw err;
|
|
539
542
|
}
|
|
540
543
|
function relayRequestOf(params, opts = {}) {
|
|
541
|
-
const invalid = (what) => {
|
|
542
|
-
throw new RelayRefusal("request-invalid", what);
|
|
544
|
+
const invalid = (what, reason) => {
|
|
545
|
+
throw new RelayRefusal("request-invalid", what, reason);
|
|
543
546
|
};
|
|
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");
|
|
547
|
+
if (params.flex) invalid("the amount is sealed into the order (no flexible deposits)", "unsupported_pair");
|
|
548
|
+
if (!relayChain(params.sourceChain)) throw new RelayRefusal("chain-unsupported", `source chain ${params.sourceChain} is not a pinned Relay chain`, "unsupported_pair");
|
|
549
|
+
if (!relayChain(params.destChain)) throw new RelayRefusal("chain-unsupported", `destination chain ${params.destChain} is not a pinned Relay chain`, "unsupported_pair");
|
|
550
|
+
if (params.sourceChain === params.destChain) throw new RelayRefusal("chain-unsupported", "same-chain swaps are not served through the depository", "unsupported_pair");
|
|
548
551
|
let sourceToken;
|
|
549
552
|
if (typeof params.sourceToken === "string" && ADDRESS.test(params.sourceToken)) sourceToken = params.sourceToken;
|
|
550
553
|
else {
|
|
551
554
|
const asset = (0, import_util.sourceAssetOf)(params);
|
|
552
|
-
if (!asset) invalid(`source token ${String(params.sourceToken)} is not known on ${params.sourceChain}
|
|
555
|
+
if (!asset) invalid(`source token ${String(params.sourceToken)} is not known on ${params.sourceChain}`, "unsupported_pair");
|
|
553
556
|
sourceToken = asset.contract ?? RELAY_NATIVE;
|
|
554
|
-
if (!ADDRESS.test(sourceToken)) invalid(`source token ${String(params.sourceToken)} has no EVM contract
|
|
557
|
+
if (!ADDRESS.test(sourceToken)) invalid(`source token ${String(params.sourceToken)} has no EVM contract`, "unsupported_pair");
|
|
555
558
|
}
|
|
556
|
-
if (typeof params.destToken !== "string" || !ADDRESS.test(params.destToken)) invalid("the destination token must be an EVM address (zero for the native coin)");
|
|
559
|
+
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");
|
|
557
560
|
if (typeof params.amountIn !== "string" || !/^[1-9]\d{0,77}$/.test(params.amountIn)) invalid("amount must be a positive base-unit integer");
|
|
558
561
|
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
562
|
const recipient = params.destAddress || opts.placeholder || "";
|
|
@@ -589,57 +592,63 @@ function relayProtocolOf(quote) {
|
|
|
589
592
|
return { orderId: pd.orderId, hubType: pd.hubType, orderData, orderSignature: pd.orderSignature, paymentDetails };
|
|
590
593
|
}
|
|
591
594
|
function checkRelayQuote(request, quote, tx) {
|
|
592
|
-
if (quote === null || typeof quote !== "object" || quote.provider !== "relay") throw new RelayRefusal("quote-shape", "not a Relay quote");
|
|
595
|
+
if (quote === null || typeof quote !== "object" || quote.provider !== "relay") throw new RelayRefusal("quote-shape", "not a Relay quote", "policy_refused");
|
|
593
596
|
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");
|
|
597
|
+
if (!t) throw new RelayRefusal("quote-shape", "the quote has no transaction", "policy_refused");
|
|
598
|
+
if (quote.depositAddress) throw new RelayRefusal("quote-shape", "a Relay quote pays the depository through its calldata, never a deposit address", "vault_mismatch");
|
|
596
599
|
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");
|
|
600
|
+
throw new RelayRefusal("quote-shape", "a Relay deposit carries no extra native fee and no serialized transaction", "policy_refused");
|
|
598
601
|
}
|
|
599
602
|
const v = verifyRelayOrder(request, relayProtocolOf(quote), t);
|
|
600
603
|
const approval = t.approvalAddress;
|
|
601
604
|
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"}
|
|
605
|
+
throw new RelayRefusal("quote-approval", `the quote approves ${String(approval)}, the order needs ${v.deposit.approvalAddress ?? "no approval"}`, "vault_mismatch");
|
|
603
606
|
}
|
|
604
607
|
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");
|
|
608
|
+
throw new RelayRefusal("quote-amounts", "the amounts the quote shows are not the order's", "price_check_failed");
|
|
606
609
|
}
|
|
607
610
|
const gas = t.gasLimit;
|
|
608
611
|
if (gas !== void 0) {
|
|
609
612
|
const floor = v.deposit.approvalAddress === void 0 ? RELAY_GAS_FLOOR_NATIVE : RELAY_GAS_FLOOR_ERC20;
|
|
610
613
|
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}
|
|
614
|
+
throw new RelayRefusal("quote-gas", `the gas limit ${String(gas).slice(0, 40)} is outside ${floor}\u2013${RELAY_GAS_CAP}`, "policy_refused");
|
|
612
615
|
}
|
|
613
616
|
}
|
|
614
617
|
const now = request.nowMs ?? Date.now();
|
|
615
618
|
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");
|
|
619
|
+
throw new RelayRefusal("quote-expiry", "the quote claims to live longer than a Relay quote may", "policy_refused");
|
|
617
620
|
}
|
|
618
621
|
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");
|
|
622
|
+
throw new RelayRefusal("quote-shape", "the quote's id is not its Relay request id", "policy_refused");
|
|
620
623
|
}
|
|
621
624
|
return v;
|
|
622
625
|
}
|
|
626
|
+
var pinNotes = (0, import_errors2.refusalNotes)();
|
|
627
|
+
function notedReason(err) {
|
|
628
|
+
const text2 = relayRefusalReason(err);
|
|
629
|
+
const info = (0, import_errors2.refusalOf)(err);
|
|
630
|
+
return info ? pinNotes.note(text2, info) : text2;
|
|
631
|
+
}
|
|
623
632
|
function relayVerifyQuote(params, quote) {
|
|
624
633
|
try {
|
|
625
634
|
checkRelayQuote(relayRequestOf(params), quote);
|
|
626
635
|
return null;
|
|
627
636
|
} catch (err) {
|
|
628
|
-
return
|
|
637
|
+
return notedReason(err);
|
|
629
638
|
}
|
|
630
639
|
}
|
|
631
640
|
function relayVerifyBuild(quote, tx, params) {
|
|
632
641
|
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}
|
|
642
|
+
if (!params) throw new RelayRefusal("request-invalid", "the request this quote answered is not known on this side \u2014 quote again", "policy_refused");
|
|
643
|
+
if (tx.chain !== params.sourceChain) throw new RelayRefusal("tx-chain", `the build is for ${tx.chain}, not ${params.sourceChain}`, "vault_mismatch");
|
|
635
644
|
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");
|
|
645
|
+
throw new RelayRefusal("quote-shape", "a Relay deposit is one EVM call", "policy_refused");
|
|
637
646
|
}
|
|
638
647
|
const v = checkRelayQuote(relayRequestOf(params), quote, tx);
|
|
639
648
|
relayClearOrder(v.id, "build");
|
|
640
649
|
return null;
|
|
641
650
|
} catch (err) {
|
|
642
|
-
return
|
|
651
|
+
return notedReason(err);
|
|
643
652
|
}
|
|
644
653
|
}
|
|
645
654
|
var relayPins = Object.freeze({
|
|
@@ -652,11 +661,13 @@ var relayPins = Object.freeze({
|
|
|
652
661
|
return c2 ? [c2.depository] : [];
|
|
653
662
|
},
|
|
654
663
|
verifyQuote: relayVerifyQuote,
|
|
655
|
-
verifyBuild: relayVerifyBuild
|
|
664
|
+
verifyBuild: relayVerifyBuild,
|
|
665
|
+
reasonOf: pinNotes.reasonOf
|
|
656
666
|
});
|
|
657
667
|
|
|
658
668
|
// src/provider.ts
|
|
659
669
|
var import_util2 = require("@anywalletkit/protocol-swap2-core/util");
|
|
670
|
+
var import_errors3 = require("@anywalletkit/protocol-swap2-core/errors");
|
|
660
671
|
|
|
661
672
|
// src/activity.ts
|
|
662
673
|
var import_activity = require("@anywalletkit/protocol-swap2-core/activity");
|
|
@@ -689,28 +700,69 @@ var RELAY_STATUS_MAP = Object.freeze({
|
|
|
689
700
|
refunded: "refunded",
|
|
690
701
|
failure: "refund-pending"
|
|
691
702
|
});
|
|
703
|
+
var RELAY_ERROR_REASONS = Object.freeze(Object.assign(/* @__PURE__ */ Object.create(null), {
|
|
704
|
+
AMOUNT_TOO_LOW: "amount_too_small",
|
|
705
|
+
AMOUNT_TOO_HIGH: "amount_too_large",
|
|
706
|
+
INSUFFICIENT_LIQUIDITY: "insufficient_liquidity",
|
|
707
|
+
NO_QUOTES: "no_route",
|
|
708
|
+
NO_SWAP_ROUTES_FOUND: "no_route",
|
|
709
|
+
NO_INTERNAL_SWAP_ROUTES_FOUND: "no_route",
|
|
710
|
+
// Relay's own failures: a third-party pricing failure, and the transient codes it says to retry with backoff.
|
|
711
|
+
SWAP_QUOTE_FAILED: "upstream_unavailable",
|
|
712
|
+
REQUEST_TIMED_OUT: "upstream_unavailable",
|
|
713
|
+
RPC_HTTP_ERROR: "upstream_unavailable",
|
|
714
|
+
PRICE_FETCH_FAILED: "upstream_unavailable",
|
|
715
|
+
SERVICE_UNAVAILABLE: "upstream_unavailable",
|
|
716
|
+
// The recipient resolves to a token contract, which cannot receive.
|
|
717
|
+
INVALID_RECIPIENT: "recipient_mismatch",
|
|
718
|
+
UNSUPPORTED_CHAIN: "unsupported_pair",
|
|
719
|
+
UNSUPPORTED_CURRENCY: "unsupported_pair",
|
|
720
|
+
UNSUPPORTED_ROUTE: "unsupported_pair",
|
|
721
|
+
UNSUPPORTED_EXECUTION_TYPE: "unsupported_pair",
|
|
722
|
+
INVALID_INPUT_CURRENCY: "unsupported_pair",
|
|
723
|
+
INVALID_OUTPUT_CURRENCY: "unsupported_pair",
|
|
724
|
+
CHAIN_DISABLED: "chain_halted",
|
|
725
|
+
ROUTE_TEMPORARILY_RESTRICTED: "chain_halted",
|
|
726
|
+
SWAP_IMPACT_TOO_HIGH: "price_check_failed",
|
|
727
|
+
UNAUTHORIZED: "key_rejected",
|
|
728
|
+
UNAUTHORIZED_QUOTE: "key_rejected",
|
|
729
|
+
FORBIDDEN: "key_rejected"
|
|
730
|
+
}));
|
|
731
|
+
function quoteHttpRefusal(res, parsed, aborted) {
|
|
732
|
+
const code = parsed?.errorCode;
|
|
733
|
+
const byCode = typeof code === "string" && Object.prototype.hasOwnProperty.call(RELAY_ERROR_REASONS, code) ? RELAY_ERROR_REASONS[code] : void 0;
|
|
734
|
+
const byStatus = (0, import_errors3.httpRefusal)(res.status, res.headers?.get?.("retry-after"));
|
|
735
|
+
if (byStatus?.reason === "rate_limited") return byStatus;
|
|
736
|
+
if (byCode) {
|
|
737
|
+
if (byStatus?.reason === byCode) return byStatus;
|
|
738
|
+
return byCode === "upstream_unavailable" || byCode === "key_rejected" ? { reason: byCode, httpStatus: res.status } : { reason: byCode };
|
|
739
|
+
}
|
|
740
|
+
if (byStatus) return byStatus;
|
|
741
|
+
if (parsed !== null && typeof parsed === "object") return { reason: "no_route" };
|
|
742
|
+
return aborted ? void 0 : { reason: "upstream_unavailable", httpStatus: res.status };
|
|
743
|
+
}
|
|
692
744
|
function clip(text2, max = 240) {
|
|
693
745
|
const t = (0, import_util2.redactUrls)(text2).replace(/\s+/g, " ").trim();
|
|
694
746
|
return t.length > max ? `${t.slice(0, max)}\u2026` : t;
|
|
695
747
|
}
|
|
696
748
|
function depositStep(body) {
|
|
697
749
|
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");
|
|
750
|
+
if (!Array.isArray(steps) || steps.length === 0 || steps.length > 8) throw new RelayRefusal("quote-steps", "the quote has no steps", "upstream_unavailable");
|
|
699
751
|
let found = null;
|
|
700
752
|
for (const s of steps) {
|
|
701
|
-
if (s === null || typeof s !== "object") throw new RelayRefusal("quote-steps", "the quote has a malformed step");
|
|
753
|
+
if (s === null || typeof s !== "object") throw new RelayRefusal("quote-steps", "the quote has a malformed step", "upstream_unavailable");
|
|
702
754
|
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");
|
|
755
|
+
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");
|
|
756
|
+
if (found) throw new RelayRefusal("quote-steps", "the quote has more than one deposit step", "unsupported_pair");
|
|
705
757
|
found = s;
|
|
706
758
|
}
|
|
707
|
-
if (!found) throw new RelayRefusal("quote-steps", "the quote has no deposit step");
|
|
759
|
+
if (!found) throw new RelayRefusal("quote-steps", "the quote has no deposit step", "upstream_unavailable");
|
|
708
760
|
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");
|
|
761
|
+
if (!Array.isArray(items) || items.length !== 1) throw new RelayRefusal("quote-steps", "the deposit step is not a single transaction", "unsupported_pair");
|
|
710
762
|
const data = items[0]?.data;
|
|
711
|
-
if (data === null || typeof data !== "object") throw new RelayRefusal("quote-steps", "the deposit step has no transaction");
|
|
763
|
+
if (data === null || typeof data !== "object") throw new RelayRefusal("quote-steps", "the deposit step has no transaction", "upstream_unavailable");
|
|
712
764
|
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");
|
|
765
|
+
if (!REQUEST_ID.test(requestId)) throw new RelayRefusal("quote-steps", "the quote has no request id to track the deposit by", "upstream_unavailable");
|
|
714
766
|
return { requestId, tx: data };
|
|
715
767
|
}
|
|
716
768
|
function relayProvider(config = {}) {
|
|
@@ -746,12 +798,12 @@ function relayProvider(config = {}) {
|
|
|
746
798
|
.../^\d{1,5}$/.test(pd.slippageBps ?? "") ? { slippageBps: Number(pd.slippageBps) } : {}
|
|
747
799
|
};
|
|
748
800
|
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");
|
|
801
|
+
throw new RelayRefusal("request-invalid", "a quote priced without the real addresses is never built or signed \u2014 quote again with them", "policy_refused");
|
|
750
802
|
}
|
|
751
803
|
const mine = issued.get(quote.intentId);
|
|
752
804
|
if (!mine) return sealed;
|
|
753
805
|
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");
|
|
806
|
+
if (!same) throw new RelayRefusal("request-invalid", "the quote no longer says what was asked when it was issued \u2014 quote again", "policy_refused");
|
|
755
807
|
return mine;
|
|
756
808
|
}
|
|
757
809
|
async function readJson(res) {
|
|
@@ -767,8 +819,8 @@ function relayProvider(config = {}) {
|
|
|
767
819
|
supportsChainPair: (from, to) => relay.supports(from, to),
|
|
768
820
|
supportsRoute: (from, to) => relay.supports(from.chain, to.chain),
|
|
769
821
|
async getQuote(params, signal) {
|
|
770
|
-
const refuse = (reason) => {
|
|
771
|
-
params.onRefusal?.(reason);
|
|
822
|
+
const refuse = (reason, info) => {
|
|
823
|
+
params.onRefusal?.(reason, info);
|
|
772
824
|
return null;
|
|
773
825
|
};
|
|
774
826
|
const dry = params.dry !== false;
|
|
@@ -777,7 +829,7 @@ function relayProvider(config = {}) {
|
|
|
777
829
|
try {
|
|
778
830
|
request = relayRequestOf(params, dry ? { placeholder: EVM_PLACEHOLDER } : {});
|
|
779
831
|
} catch (err) {
|
|
780
|
-
if (err instanceof RelayRefusal) return refuse(err.message);
|
|
832
|
+
if (err instanceof RelayRefusal) return refuse(err.message, (0, import_errors3.refusalOf)(err));
|
|
781
833
|
throw err;
|
|
782
834
|
}
|
|
783
835
|
const src = relayChain(request.sourceChain);
|
|
@@ -812,20 +864,21 @@ function relayProvider(config = {}) {
|
|
|
812
864
|
if (!res.ok) {
|
|
813
865
|
const e = parsed ?? {};
|
|
814
866
|
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}
|
|
867
|
+
return refuse(`relay: no quote \u2014 ${typeof e.errorCode === "string" ? `${clip(e.errorCode, 60)}: ` : ""}${why}`, quoteHttpRefusal(res, parsed, signal?.aborted === true));
|
|
816
868
|
}
|
|
817
|
-
if (parsed === null || typeof parsed !== "object") return refuse("relay: the quote response is not JSON");
|
|
869
|
+
if (parsed === null || typeof parsed !== "object") return refuse("relay: the quote response is not JSON", signal?.aborted ? void 0 : { reason: "upstream_unavailable" });
|
|
818
870
|
body = parsed;
|
|
819
871
|
} catch (err) {
|
|
820
|
-
|
|
872
|
+
const info = (0, import_errors3.refusalOf)(err) ?? (signal?.aborted ? void 0 : { reason: "upstream_unavailable" });
|
|
873
|
+
return refuse(`relay: quote request failed \u2014 ${clip(err instanceof Error ? err.message : String(err))}`, info);
|
|
821
874
|
}
|
|
822
875
|
try {
|
|
823
876
|
const step = depositStep(body);
|
|
824
877
|
const proto = body.protocol?.v2 ?? null;
|
|
825
878
|
const v = verifyRelayOrder(request, proto, step.tx);
|
|
826
879
|
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");
|
|
880
|
+
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");
|
|
881
|
+
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");
|
|
829
882
|
const native = v.deposit.approvalAddress === void 0;
|
|
830
883
|
const floor = native ? RELAY_GAS_FLOOR_NATIVE : RELAY_GAS_FLOOR_ERC20;
|
|
831
884
|
const apiGas = typeof step.tx.gas === "string" && /^[1-9]\d{0,9}$/.test(step.tx.gas) ? BigInt(step.tx.gas) * 2n : 0n;
|
|
@@ -880,15 +933,15 @@ function relayProvider(config = {}) {
|
|
|
880
933
|
}
|
|
881
934
|
};
|
|
882
935
|
} 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))}
|
|
936
|
+
if (err instanceof RelayRefusal) return refuse(err.message, (0, import_errors3.refusalOf)(err));
|
|
937
|
+
return refuse(`relay: unreadable quote \u2014 ${clip(err instanceof Error ? err.message : String(err))}`, (0, import_errors3.refusalOf)(err) ?? { reason: "upstream_unavailable" });
|
|
885
938
|
}
|
|
886
939
|
},
|
|
887
940
|
/** The quote's own transaction — after the order it carries has been verified once more. */
|
|
888
941
|
async buildDeposit(quote) {
|
|
889
942
|
const tx = quote.txRequest;
|
|
890
|
-
if (!tx || !quote.intentId.startsWith(INTENT_PREFIX)) throw
|
|
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");
|
|
943
|
+
if (!tx || !quote.intentId.startsWith(INTENT_PREFIX)) throw (0, import_errors3.refusal)("policy_refused", "swap2: Relay quote has no transaction request");
|
|
944
|
+
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");
|
|
892
945
|
const request = requestOf(quote);
|
|
893
946
|
const v = checkRelayQuote(request, quote);
|
|
894
947
|
return {
|
|
@@ -906,15 +959,15 @@ function relayProvider(config = {}) {
|
|
|
906
959
|
* and the exact transaction about to be signed — and each order is cleared for one deposit only.
|
|
907
960
|
*/
|
|
908
961
|
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");
|
|
962
|
+
if (quote.providerData?.placeholder === "1") throw new RelayRefusal("request-invalid", "a quote priced without the real addresses is never signed", "policy_refused");
|
|
910
963
|
const request = requestOf(quote);
|
|
911
|
-
if (tx.chain !== request.sourceChain) throw new RelayRefusal("tx-chain", `the deposit is for ${tx.chain}, not ${request.sourceChain}
|
|
964
|
+
if (tx.chain !== request.sourceChain) throw new RelayRefusal("tx-chain", `the deposit is for ${tx.chain}, not ${request.sourceChain}`, "vault_mismatch");
|
|
912
965
|
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");
|
|
966
|
+
throw new RelayRefusal("quote-shape", "a Relay deposit is one EVM call", "policy_refused");
|
|
914
967
|
}
|
|
915
968
|
const v = checkRelayQuote(request, quote, tx);
|
|
916
969
|
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
|
|
970
|
+
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");
|
|
918
971
|
relayClearOrder(v.id, "sign");
|
|
919
972
|
if (store) await store.add(v.id, v.order.output.deadline * 1e3);
|
|
920
973
|
},
|