@anywalletkit/protocol-swap2-relay 0.1.1 → 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/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 relayRefusalReason(err);
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 relayRefusalReason(err);
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
- return refuse(`relay: quote request failed \u2014 ${clip(err instanceof Error ? err.message : String(err))}`);
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 new Error("swap2: Relay quote has no transaction request");
891
- if (quote.providerData?.placeholder === "1") throw new RelayRefusal("request-invalid", "a quote priced without the real addresses is never built \u2014 quote again with them");
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
  },