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