@anywalletkit/protocol-swap2-omniston 0.5.6 → 0.5.7

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.
@@ -317,6 +317,7 @@ function sameJetton(a, b) {
317
317
  }
318
318
 
319
319
  // src/provider.ts
320
+ import { refusal, refusalNotes, refusalOf, withReason } from "@anywalletkit/protocol-swap2-core/errors";
320
321
  import { bigintToNumber } from "@anywalletkit/core";
321
322
  var EVM_CASE = {
322
323
  1: "ethereum",
@@ -454,6 +455,54 @@ function encodeTypedMessage(td) {
454
455
  encodeAbiParameters([paramFor(td.primaryType)], [td.message])
455
456
  );
456
457
  }
458
+ function venueReason(error) {
459
+ const details = typeof error === "object" && error !== null ? error.details : void 0;
460
+ const info = typeof details === "object" && details !== null ? details.error_info : void 0;
461
+ if (typeof info !== "object" || info === null) return "upstream_unavailable";
462
+ const { reason, metadata } = info;
463
+ const meta = typeof metadata === "object" && metadata !== null ? metadata : {};
464
+ const type = typeof meta.type === "string" ? meta.type : "";
465
+ switch (reason) {
466
+ case "INVALID_ARGUMENT":
467
+ case "INVALID_PARAMETERS":
468
+ return void 0;
469
+ case "NOT_FOUND":
470
+ return typeof meta.object_type === "string" && meta.object_type.toLowerCase() === "quote" ? "quote_expired" : "upstream_unavailable";
471
+ case "UNSUPPORTED":
472
+ return "unsupported_pair";
473
+ case "SWAP_LIMIT_EXCEEDED":
474
+ return "amount_too_large";
475
+ case "QUOTE_VALIDATION_FAILED":
476
+ if (type === "EMULATION_UNSUPPORTED_TOKEN") return "unsupported_pair";
477
+ if (type === "EMULATION_WRONG_OUTPUT_AMOUNT" || type === "EMULATION_REFUND_SLIPPAGE") return "price_check_failed";
478
+ return type.startsWith("EMULATION_") ? "simulation_failed" : "upstream_unavailable";
479
+ default:
480
+ return "upstream_unavailable";
481
+ }
482
+ }
483
+ function venueFailure(error) {
484
+ if (!(error instanceof Error)) return error;
485
+ const reason = venueReason(error);
486
+ return reason ? withReason(error, reason) : error;
487
+ }
488
+ function mismatchReason(field) {
489
+ return field === "trader" || field === "dest" || field === "owner" ? "recipient_mismatch" : "policy_refused";
490
+ }
491
+ function tonSwapPayloadReason(code) {
492
+ switch (code) {
493
+ case "receiver-not-destination":
494
+ case "refund-not-trader":
495
+ case "excess-not-trader":
496
+ return "recipient_mismatch";
497
+ case "min-out-zero":
498
+ case "min-out-below-quote":
499
+ return "price_check_failed";
500
+ case "leg-unverified":
501
+ return "vault_mismatch";
502
+ default:
503
+ return "policy_refused";
504
+ }
505
+ }
457
506
  function generateSecret() {
458
507
  return crypto.getRandomValues(new Uint8Array(32));
459
508
  }
@@ -464,7 +513,7 @@ function hashlock(secret, hashingFunction) {
464
513
  case "HASHING_FUNCTION_SHA256":
465
514
  return sha256(secret, "bytes");
466
515
  default:
467
- throw new Error(`omniston: unsupported HTLC hashing function ${String(hashingFunction)}`);
516
+ throw refusal("policy_refused", `omniston: unsupported HTLC hashing function ${String(hashingFunction)}`);
468
517
  }
469
518
  }
470
519
  function sameAsset(a, b) {
@@ -502,7 +551,7 @@ function stepJetton(asset) {
502
551
  }
503
552
  function readBigEndian(bytes, offset, length) {
504
553
  if (!Number.isInteger(offset) || !Number.isInteger(length) || length < 1 || offset < 0 || offset + length > bytes.length) {
505
- throw new Error("omniston: truncated EVM order extension");
554
+ throw refusal("policy_refused", "omniston: truncated EVM order extension");
506
555
  }
507
556
  let value = 0n;
508
557
  for (let index = offset; index < offset + length; index++) value = value << 8n | BigInt(bytes[index]);
@@ -547,30 +596,30 @@ var EVM_DEST_PROTOCOL_FEE_RECIPIENTS = /* @__PURE__ */ new Set(["0x076268146b4ec
547
596
  var EVM_AUCTION_POINTS = [[800000n, 100n], [700000n, 100n], [600000n, 100n], [500000n, 100n], [400000n, 100n]];
548
597
  var EVM_TIMELOCKS = [1n, 120n, 240n, 300n, 1n, 120n, 180n, 0n].reduce((packed, value, index) => packed | value << BigInt(index * 32), 0n);
549
598
  function readEvmOrderExtension(extension) {
550
- if (extension.length < 32) throw new Error("omniston: malformed EVM order extension");
599
+ if (extension.length < 32) throw refusal("policy_refused", "omniston: malformed EVM order extension");
551
600
  const view = new DataView(extension.buffer, extension.byteOffset, extension.byteLength);
552
601
  const fields = [];
553
602
  let begin = 0;
554
603
  const concatLength = extension.length - 32;
555
604
  for (let index = 0; index < 8; index++) {
556
605
  const end2 = view.getUint32(28 - index * 4, false);
557
- if (end2 < begin || end2 > concatLength) throw new Error("omniston: malformed EVM order extension offsets");
606
+ if (end2 < begin || end2 > concatLength) throw refusal("policy_refused", "omniston: malformed EVM order extension offsets");
558
607
  fields.push(extension.slice(32 + begin, 32 + end2));
559
608
  begin = end2;
560
609
  }
561
610
  for (const index of [0, 1, 4, 5, 6]) {
562
- if (fields[index].length !== 0) throw new Error("omniston: unsupported EVM order extension field");
611
+ if (fields[index].length !== 0) throw refusal("policy_refused", "omniston: unsupported EVM order extension field");
563
612
  }
564
613
  if (!sliceEqual(fields[2], fields[3])) {
565
- throw new Error("omniston: making/taking amount getters use different programs");
614
+ throw refusal("policy_refused", "omniston: making/taking amount getters use different programs");
566
615
  }
567
616
  let target;
568
617
  for (const index of [2, 3, 7]) {
569
618
  const field2 = fields[index];
570
- if (field2.length <= 20) throw new Error("omniston: incomplete EVM order extension program");
619
+ if (field2.length <= 20) throw refusal("policy_refused", "omniston: incomplete EVM order extension program");
571
620
  const address = getAddress(`0x${bytesToHex(field2.slice(0, 20))}`);
572
621
  if (target !== void 0 && getAddress(target) !== address) {
573
- throw new Error("omniston: EVM order extension target is not independently trusted");
622
+ throw refusal("vault_mismatch", "omniston: EVM order extension target is not independently trusted");
574
623
  }
575
624
  target = address;
576
625
  }
@@ -588,7 +637,7 @@ function readEvmOrderExtension(extension) {
588
637
  cursor += 3;
589
638
  const pointCount = bigintToNumber(readBigEndian(field, cursor, 1));
590
639
  cursor += 1;
591
- if (pointCount !== 5) throw new Error("omniston: unsupported EVM auction profile");
640
+ if (pointCount !== 5) throw refusal("policy_refused", "omniston: unsupported EVM auction profile");
592
641
  const points = [];
593
642
  for (let index = 0; index < pointCount; index++) {
594
643
  points.push([readBigEndian(field, cursor, 3), readBigEndian(field, cursor + 3, 2)]);
@@ -605,7 +654,7 @@ function readEvmOrderExtension(extension) {
605
654
  const getterWhitelistCount = bigintToNumber(readBigEndian(field, cursor, 1));
606
655
  cursor += 1;
607
656
  if (getterWhitelistCount !== 1 || cursor + getterWhitelistCount * 10 !== field.length) {
608
- throw new Error("omniston: malformed EVM amount-getter whitelist");
657
+ throw refusal("policy_refused", "omniston: malformed EVM amount-getter whitelist");
609
658
  }
610
659
  const getter = { gasBump, gasPrice, auctionStart, duration, initialRateBump, points, integratorFee, integratorShare, resolverFee, whitelistDiscount, resolver: readBigEndian(field, cursor, 10) };
611
660
  const post = fields[7];
@@ -627,7 +676,7 @@ function readEvmOrderExtension(extension) {
627
676
  const whitelistCount = bigintToNumber(readBigEndian(post, cursor, 1));
628
677
  cursor += 1;
629
678
  if (whitelistCount !== 1 || cursor + whitelistCount * 12 + 160 !== post.length) {
630
- throw new Error("omniston: malformed EVM post-interaction whitelist");
679
+ throw refusal("policy_refused", "omniston: malformed EVM post-interaction whitelist");
631
680
  }
632
681
  const postResolver = readBigEndian(post, cursor, 10);
633
682
  cursor += 10;
@@ -639,7 +688,7 @@ function readEvmOrderExtension(extension) {
639
688
  return value;
640
689
  });
641
690
  const customData = extension.slice(32 + begin);
642
- if (customData.length !== 131) throw new Error("omniston: malformed EVM order extension custom data");
691
+ if (customData.length !== 131) throw refusal("policy_refused", "omniston: malformed EVM order extension custom data");
643
692
  return {
644
693
  target,
645
694
  getter,
@@ -652,29 +701,29 @@ async function validateEvmToTonOrderExtension(extension, expected) {
652
701
  const evmDest = expected.dstChainId !== void 0;
653
702
  if (evmDest && (!Number.isSafeInteger(expected.dstChainId) || expected.dstChainId <= 0)) throw new Error("omniston: invalid EVM destination chain id");
654
703
  const { target, getter, post, extra, customData } = readEvmOrderExtension(extension);
655
- if (!expected.trustedTarget(target)) throw new Error("omniston: EVM order extension target is not independently trusted");
704
+ if (!expected.trustedTarget(target)) throw refusal("vault_mismatch", "omniston: EVM order extension target is not independently trusted");
656
705
  const quoteResolverFee = expected.protocolFeePips % 10 === 0 ? BigInt(expected.protocolFeePips / 10) : -1n;
657
706
  const resolverLow80 = BigInt(getAddress(expected.initialResolverAddress)) & (1n << 80n) - 1n;
658
707
  if (getter.gasBump !== 0n || getter.gasPrice !== 0n || !within(getter.auctionStart, expected.quoteTimestamp, 5, 5) || getter.duration !== 1800n || getter.initialRateBump !== 900000n || getter.points.some((point, index) => point[0] !== EVM_AUCTION_POINTS[index][0] || point[1] !== EVM_AUCTION_POINTS[index][1]) || expected.integratorFeePips !== 0 || getter.integratorFee !== 0n || getter.integratorShare !== 100n || quoteResolverFee < 0n || getter.resolverFee !== quoteResolverFee || getter.whitelistDiscount !== 50n || getter.resolver !== resolverLow80) {
659
- throw new Error("omniston: EVM amount-getter terms do not match the pinned v1beta8 profile");
708
+ throw refusal("policy_refused", "omniston: EVM amount-getter terms do not match the pinned v1beta8 profile");
660
709
  }
661
710
  if (!zeroBytes(post.integratorRecipient) || !(zeroBytes(post.protocolRecipient) || evmDest && EVM_DEST_PROTOCOL_FEE_RECIPIENTS.has(`0x${bytesToHex(post.protocolRecipient)}`)) || post.integratorFee !== 0n || post.integratorShare !== 100n || post.resolverFee !== quoteResolverFee || post.discount !== 50n || !within(post.allowedTime, expected.quoteTimestamp, 60, 5) || post.resolver !== resolverLow80 || post.resolverTimeDelta !== 0n) {
662
- throw new Error("omniston: EVM post-interaction terms do not match the pinned v1beta8 profile");
711
+ throw refusal("policy_refused", "omniston: EVM post-interaction terms do not match the pinned v1beta8 profile");
663
712
  }
664
713
  const hashlock2 = readBigEndian(expected.secretHash, 0, expected.secretHash.length);
665
714
  const dstToken = evmDest ? BigInt(getAddress(expected.outputTokenAddress)) : BigInt(getAddress(expected.takerAsset));
666
715
  const deposits = BigInt(expected.srcSecurityDepositUnits) << 128n | BigInt(expected.dstSecurityDepositUnits);
667
716
  if (expected.secretHash.length !== 32 || extra[0] !== hashlock2 || extra[1] !== (evmDest ? BigInt(expected.dstChainId) : 0n) || extra[2] !== dstToken || extra[3] !== deposits || extra[4] !== EVM_TIMELOCKS) {
668
- throw new Error("omniston: signed EVM HTLC terms do not match the validated order details");
717
+ throw refusal("policy_refused", "omniston: signed EVM HTLC terms do not match the validated order details");
669
718
  }
670
719
  await checkOrderCustomData(customData, { evmDest, outputToken: expected.outputTokenAddress, traderDst: expected.traderDstAddress, quoteId: expected.quoteId });
671
720
  if (expected.tradeStartDeadline < expected.quoteTimestamp) {
672
- throw new Error("omniston: invalid EVM order quote deadline");
721
+ throw refusal("quote_expired", "omniston: invalid EVM order quote deadline");
673
722
  }
674
723
  }
675
724
  async function checkOrderCustomData(customData, expected) {
676
725
  if (expected.evmDest) {
677
- if (!zeroBytes(customData.slice(0, 66))) throw new Error("omniston: unexpected TON addresses in an EVM-destination order");
726
+ if (!zeroBytes(customData.slice(0, 66))) throw refusal("recipient_mismatch", "omniston: unexpected TON addresses in an EVM-destination order");
678
727
  } else {
679
728
  const { Address: Address2 } = await import("@ton/core");
680
729
  const expectedTon = [expected.outputToken, expected.traderDst].map((value) => Address2.parse(value));
@@ -682,16 +731,16 @@ async function checkOrderCustomData(customData, expected) {
682
731
  const offset = index * 33;
683
732
  const workchain = customData[offset] === 255 ? -1 : customData[offset];
684
733
  if (workchain !== expectedTon[index].workChain || !sliceEqual(customData.slice(offset + 1, offset + 33), expectedTon[index].hash)) {
685
- throw new Error("omniston: signed EVM\u2192TON address does not match the validated request");
734
+ throw refusal("recipient_mismatch", "omniston: signed EVM\u2192TON address does not match the validated request");
686
735
  }
687
736
  }
688
737
  }
689
738
  if (!zeroBytes(customData.slice(66, 99))) {
690
- throw new Error("omniston: unexpected private TON disclosure address");
739
+ throw refusal("policy_refused", "omniston: unexpected private TON disclosure address");
691
740
  }
692
741
  const quoteId = expected.quoteId.replace(/^0x/, "");
693
742
  if (!QUOTE_ID.test(quoteId) || !sliceEqual(customData.slice(99), hexToBytes(quoteId.padStart(64, "0")))) {
694
- throw new Error("omniston: EVM order extension custom data is not bound to the quote id");
743
+ throw refusal("policy_refused", "omniston: EVM order extension custom data is not bound to the quote id");
695
744
  }
696
745
  }
697
746
  function validateEvmTypedOrder(td, expected) {
@@ -713,7 +762,7 @@ function validateEvmTypedOrder(td, expected) {
713
762
  if (value.domain?.name !== "1inch Limit Order Protocol" || value.domain.version !== "4" || typeof value.domain.verifyingContract !== "string" || getAddress(value.domain.verifyingContract) !== getAddress(expected.settlementContract) || Number(value.domain.chainId) !== expected.chainId || value.primaryType !== "Order" || !exactSchema || !message || !exactMessage || getAddress(String(message.maker)) !== getAddress(expected.owner) || getAddress(String(message.receiver)) !== getAddress(expected.receiver) || getAddress(String(message.makerAsset)) !== getAddress(expected.makerAsset) || !validUint256(salt) || (BigInt(salt) & (1n << 160n) - 1n) !== (extensionHash & (1n << 160n) - 1n) || // The order expires at the quote's trade-start deadline (±15 s of clock
714
763
  // skew between the quote and the order builder), never later.
715
764
  traits < 0n || !flagsOk || allowedSender !== 0n || series !== 0n || !within(expiration, expected.tradeStartDeadline, 15, 15) || expiration <= BigInt(expected.quoteTimestamp) || !validEvmAddress(message.takerAsset) || getAddress(String(message.takerAsset)) === getAddress(expected.makerAsset) || !validUint256(message.makingAmount) || BigInt(message.makingAmount) !== BigInt(expected.makingAmount) || !validUint256(message.takingAmount) || BigInt(message.takingAmount) !== BigInt(expected.takingAmount)) {
716
- throw new Error("omniston: EVM order fields do not match the validated quote/request");
765
+ throw refusal("policy_refused", "omniston: EVM order fields do not match the validated quote/request");
717
766
  }
718
767
  }
719
768
  async function validateTonEscrowPayload(message, expected, validateForwardPayload) {
@@ -723,9 +772,9 @@ async function validateTonEscrowPayload(message, expected, validateForwardPayloa
723
772
  try {
724
773
  roots = Cell2.fromBoc(Buffer2.from(boc));
725
774
  } catch {
726
- throw new Error("omniston: malformed TON escrow BoC");
775
+ throw refusal("policy_refused", "omniston: malformed TON escrow BoC");
727
776
  }
728
- if (roots.length !== 1) throw new Error("omniston: TON escrow must contain exactly one root cell");
777
+ if (roots.length !== 1) throw refusal("policy_refused", "omniston: TON escrow must contain exactly one root cell");
729
778
  const slice = roots[0].beginParse();
730
779
  const op = slice.loadUint(32);
731
780
  slice.loadUintBig(64);
@@ -736,7 +785,7 @@ async function validateTonEscrowPayload(message, expected, validateForwardPayloa
736
785
  const forwardAmount = slice.loadCoins();
737
786
  const payloadByRef = slice.loadBit();
738
787
  if (op !== 260734629 || amount !== BigInt(expected.inputUnits) || !(destination instanceof Address2) || !destination.equals(Address2.parse(expected.protocolAddress)) || response !== null && (!(response instanceof Address2) || !response.equals(Address2.parse(expected.ownerAddress))) || customPayload !== null || forwardAmount <= 0n || forwardAmount > BigInt(message.sendAmount) || !payloadByRef || slice.remainingBits !== 0 || slice.remainingRefs !== 1) {
739
- throw new Error("omniston: TON jetton transfer does not match the validated quote/request");
788
+ throw refusal("policy_refused", "omniston: TON jetton transfer does not match the validated quote/request");
740
789
  }
741
790
  const forwardPayload = slice.loadRef();
742
791
  const verified = await validateForwardPayload({
@@ -756,7 +805,7 @@ async function validateTonEscrowPayload(message, expected, validateForwardPayloa
756
805
  ...expected.hashingFunction ? { hashingFunction: expected.hashingFunction } : {},
757
806
  exclusivityTimeout: expected.exclusivityTimeout
758
807
  });
759
- if (!verified) throw new Error("omniston: TON escrow forward payload failed independent semantic validation");
808
+ if (!verified) throw refusal("policy_refused", "omniston: TON escrow forward payload failed independent semantic validation");
760
809
  }
761
810
  function feeToken(asset) {
762
811
  return asset.assetId || asset.contract || asset.symbol;
@@ -786,24 +835,24 @@ var ProtoReader = class {
786
835
  const byte = this.bytes[this.pos++];
787
836
  out += (byte & 127) * 2 ** shift;
788
837
  if ((byte & 128) === 0) {
789
- if (!Number.isSafeInteger(out)) throw new Error("omniston: unsafe protobuf varint");
838
+ if (!Number.isSafeInteger(out)) throw refusal("policy_refused", "omniston: unsafe protobuf varint");
790
839
  return out;
791
840
  }
792
841
  shift += 7;
793
842
  }
794
- throw new Error("omniston: malformed protobuf varint");
843
+ throw refusal("policy_refused", "omniston: malformed protobuf varint");
795
844
  }
796
845
  field() {
797
846
  const tag = this.varint();
798
847
  const number = Math.floor(tag / 8);
799
848
  const wire = tag & 7;
800
- if (number < 1) throw new Error("omniston: malformed protobuf field");
849
+ if (number < 1) throw refusal("policy_refused", "omniston: malformed protobuf field");
801
850
  return { number, wire };
802
851
  }
803
852
  lengthDelimited() {
804
853
  const length = this.varint();
805
854
  const end2 = this.pos + length;
806
- if (length < 0 || end2 > this.bytes.length) throw new Error("omniston: truncated protobuf payload");
855
+ if (length < 0 || end2 > this.bytes.length) throw refusal("policy_refused", "omniston: truncated protobuf payload");
807
856
  const value = this.bytes.slice(this.pos, end2);
808
857
  this.pos = end2;
809
858
  return value;
@@ -825,10 +874,10 @@ var ProtoReader = class {
825
874
  this.advance(4);
826
875
  return;
827
876
  }
828
- throw new Error("omniston: unsupported protobuf wire type");
877
+ throw refusal("policy_refused", "omniston: unsupported protobuf wire type");
829
878
  }
830
879
  advance(length) {
831
- if (this.pos + length > this.bytes.length) throw new Error("omniston: truncated protobuf payload");
880
+ if (this.pos + length > this.bytes.length) throw refusal("policy_refused", "omniston: truncated protobuf payload");
832
881
  this.pos += length;
833
882
  }
834
883
  };
@@ -855,10 +904,10 @@ function decodeChainAddress(bytes) {
855
904
  while (!reader.done) {
856
905
  const { number, wire } = reader.field();
857
906
  const $case = PROTO_CASE_BY_FIELD[number];
858
- if (!$case || wire !== 2 || address) throw new Error("omniston: malformed chain address in order details");
907
+ if (!$case || wire !== 2 || address) throw refusal("policy_refused", "omniston: malformed chain address in order details");
859
908
  address = { chain: { $case, value: utf8.decode(reader.lengthDelimited()) } };
860
909
  }
861
- if (!address?.chain.value) throw new Error("omniston: empty chain address in order details");
910
+ if (!address?.chain.value) throw refusal("policy_refused", "omniston: empty chain address in order details");
862
911
  return address;
863
912
  }
864
913
  function decodeAssetId(bytes) {
@@ -867,27 +916,27 @@ function decodeAssetId(bytes) {
867
916
  while (!reader.done) {
868
917
  const { number, wire } = reader.field();
869
918
  const $case = PROTO_CASE_BY_FIELD[number];
870
- if (!$case || wire !== 2 || asset) throw new Error("omniston: malformed asset id in order details");
919
+ if (!$case || wire !== 2 || asset) throw refusal("policy_refused", "omniston: malformed asset id in order details");
871
920
  const kindReader = new ProtoReader(reader.lengthDelimited());
872
921
  const kindField = kindReader.field();
873
922
  if (kindField.wire !== 2 || kindField.number !== 1 && kindField.number !== 2) {
874
- throw new Error("omniston: unsupported asset kind in order details");
923
+ throw refusal("policy_refused", "omniston: unsupported asset kind in order details");
875
924
  }
876
925
  const kindCase = kindField.number === 1 ? "native" : $case === "ton" ? "jetton" : $case === "tron" ? "trc20" : "erc20";
877
926
  const value = kindField.number === 1 ? {} : utf8.decode(kindReader.lengthDelimited());
878
- if (!kindReader.done || kindField.number === 2 && !value) throw new Error("omniston: malformed asset kind in order details");
927
+ if (!kindReader.done || kindField.number === 2 && !value) throw refusal("policy_refused", "omniston: malformed asset kind in order details");
879
928
  asset = { chain: { $case, value: { kind: { $case: kindCase, value } } } };
880
929
  }
881
- if (!asset) throw new Error("omniston: empty asset id in order details");
930
+ if (!asset) throw refusal("policy_refused", "omniston: empty asset id in order details");
882
931
  return asset;
883
932
  }
884
933
  function validateSerializedOrderDetails(bytes, expected) {
885
- if (!bytes?.length) throw new Error("omniston: EVM HTLC order payload has no serialized details");
934
+ if (!bytes?.length) throw refusal("policy_refused", "omniston: EVM HTLC order payload has no serialized details");
886
935
  const wrapper = new ProtoReader(bytes);
887
936
  const outer = wrapper.field();
888
- if (outer.number !== 1 || outer.wire !== 2) throw new Error("omniston: unsupported HTLC order-details version");
937
+ if (outer.number !== 1 || outer.wire !== 2) throw refusal("policy_refused", "omniston: unsupported HTLC order-details version");
889
938
  const detailsBytes = wrapper.lengthDelimited();
890
- if (!wrapper.done) throw new Error("omniston: ambiguous HTLC order-details wrapper");
939
+ if (!wrapper.done) throw refusal("policy_refused", "omniston: ambiguous HTLC order-details wrapper");
891
940
  const reader = new ProtoReader(detailsBytes);
892
941
  let quoteId;
893
942
  let outputAsset;
@@ -895,16 +944,16 @@ function validateSerializedOrderDetails(bytes, expected) {
895
944
  const hashes = [];
896
945
  while (!reader.done) {
897
946
  const { number, wire } = reader.field();
898
- if (wire !== 2) throw new Error("omniston: malformed HTLC order details");
947
+ if (wire !== 2) throw refusal("policy_refused", "omniston: malformed HTLC order details");
899
948
  const value = reader.lengthDelimited();
900
949
  if (number === 1 && quoteId === void 0) quoteId = utf8.decode(value);
901
950
  else if (number === 2 && outputAsset === void 0) outputAsset = decodeAssetId(value);
902
951
  else if (number === 3 && traderDstAddress === void 0) traderDstAddress = decodeChainAddress(value);
903
952
  else if (number === 5) hashes.push(value);
904
- else throw new Error("omniston: duplicate or unexpected HTLC order-details field");
953
+ else throw refusal("policy_refused", "omniston: duplicate or unexpected HTLC order-details field");
905
954
  }
906
955
  if (quoteId !== expected.quoteId || !outputAsset || !sameAsset(outputAsset, expected.outputAsset) || !traderDstAddress || !sameChainAddress(traderDstAddress, expected.traderDstAddress) || hashes.length !== expected.secretHashes.length || hashes.some((hash, i) => !bytesEqual(hash, expected.secretHashes[i]))) {
907
- throw new Error("omniston: resolver order payload does not match the validated quote/request");
956
+ throw refusal("policy_refused", "omniston: resolver order payload does not match the validated quote/request");
908
957
  }
909
958
  }
910
959
  function isPositiveInteger(value) {
@@ -935,7 +984,7 @@ function bytesToHex(bytes) {
935
984
  function hexToBytes(hex) {
936
985
  const clean = hex.replace(/^0x/, "");
937
986
  if (!clean || !/^(?:[0-9a-fA-F]{2})+$/.test(clean)) {
938
- throw new Error("omniston: malformed hex payload");
987
+ throw refusal("policy_refused", "omniston: malformed hex payload");
939
988
  }
940
989
  const out = new Uint8Array(clean.length / 2);
941
990
  for (let i = 0; i < out.length; i++) out[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
@@ -969,6 +1018,14 @@ function swapProtocols(v) {
969
1018
  for (const route of v.routes) for (const step of route.steps) for (const chunk of step.chunks) names.add(chunk.protocol);
970
1019
  return [...names];
971
1020
  }
1021
+ function rejectionReason(rejection) {
1022
+ return rejection === "quote timestamp out of range" ? "quote_expired" : "upstream_unavailable";
1023
+ }
1024
+ function rfqReason(outcome) {
1025
+ if (outcome.error) return outcome.errorReason;
1026
+ if (outcome.rejected) return rejectionReason(outcome.rejected);
1027
+ return outcome.answered ? "no_route" : "upstream_unavailable";
1028
+ }
972
1029
  function quoteRejection(q, update, acknowledgedRfqId, request, settlement) {
973
1030
  if (!q) return "empty quote";
974
1031
  if (!acknowledgedRfqId || update.rfqId !== acknowledgedRfqId || q.rfqId !== acknowledgedRfqId) return "quote for another RFQ";
@@ -1008,6 +1065,7 @@ function awaitQuote(stream, request, signal, settleMs = QUOTE_SETTLE_MS, settlem
1008
1065
  const max = setTimeout(() => finish(latest), QUOTE_MAX_MS);
1009
1066
  sub = stream.subscribe({
1010
1067
  next: (event) => {
1068
+ if (event.$case === "ack" || event.$case === "quoteUpdated" || event.$case === "noQuote") outcome.answered = true;
1011
1069
  if (event.$case === "ack") {
1012
1070
  acknowledgedRfqId = event.value.rfqId || null;
1013
1071
  } else if (event.$case === "quoteUpdated") {
@@ -1027,6 +1085,7 @@ function awaitQuote(stream, request, signal, settleMs = QUOTE_SETTLE_MS, settlem
1027
1085
  },
1028
1086
  error: (err) => {
1029
1087
  outcome.error = String(err?.message ?? err).slice(0, 200);
1088
+ outcome.errorReason = venueReason(err);
1030
1089
  finish(null);
1031
1090
  },
1032
1091
  complete: () => finish(latest)
@@ -1068,9 +1127,9 @@ function awaitSwapProgress(stream) {
1068
1127
  }
1069
1128
  var JETTON_TRANSFER_OP = 260734629;
1070
1129
  function parseSwapPayload(payloadHex) {
1071
- if (!/^(?:[0-9a-fA-F]{2})+$/.test(payloadHex)) throw new Error("omniston: malformed swap payload hex");
1130
+ if (!/^(?:[0-9a-fA-F]{2})+$/.test(payloadHex)) throw refusal("policy_refused", "omniston: malformed swap payload hex");
1072
1131
  const roots = TonCell.fromBoc(Buffer2.from(payloadHex, "hex"));
1073
- if (roots.length !== 1) throw new Error("omniston: swap payload must have one root");
1132
+ if (roots.length !== 1) throw refusal("policy_refused", "omniston: swap payload must have one root");
1074
1133
  const s = roots[0].beginParse();
1075
1134
  const op = s.loadUint(32);
1076
1135
  if (op !== JETTON_TRANSFER_OP) return { op, jettonTransfer: null };
@@ -1142,86 +1201,108 @@ function mapOrderStatus(order) {
1142
1201
  }
1143
1202
  function omnistonPins(trustedEvmOrderProtocols) {
1144
1203
  const byChain = new Map(Object.entries(trustedEvmOrderProtocols).map(([chain, list]) => [chain, list.map((a) => getAddress(a))]));
1145
- return Object.freeze({ callTargets: () => [], spenders: (chain) => byChain.get(chain) ?? [] });
1204
+ return Object.freeze({
1205
+ callTargets: () => [],
1206
+ spenders: (chain) => byChain.get(chain) ?? [],
1207
+ // The reason behind a text omnistonVerifyBuild returned, so the remote stub's refusal carries it.
1208
+ reasonOf: verifyNotes.reasonOf
1209
+ });
1210
+ }
1211
+ var VERIFY_NOTES = /* @__PURE__ */ Symbol.for("@anywalletkit/protocol-swap2-omniston/verify-notes@1");
1212
+ var verifyNotes = (() => {
1213
+ const shared = globalThis[VERIFY_NOTES];
1214
+ if (shared && typeof shared.note === "function" && typeof shared.reasonOf === "function") return shared;
1215
+ const notes = refusalNotes();
1216
+ try {
1217
+ Object.defineProperty(globalThis, VERIFY_NOTES, { value: notes });
1218
+ } catch {
1219
+ }
1220
+ return notes;
1221
+ })();
1222
+ var said = verifyNotes.note;
1223
+ function tonEscrowBuildReason(text) {
1224
+ if (text.startsWith("the escrow transfer pays ")) return "vault_mismatch";
1225
+ if (text.startsWith("the resolver is bound to deliver ")) return "price_check_failed";
1226
+ return "policy_refused";
1146
1227
  }
1147
1228
  var MAX_EVM_ORDER_LIFETIME_S = 900;
1148
1229
  async function checkRemoteEvmOrder(quote, tx, params, trusted) {
1149
1230
  const chainId = evmRef(params.sourceChain);
1150
- if (chainId === null || !params.sourceChain.startsWith("eip155:")) return "a typed order needs an EVM source";
1231
+ if (chainId === null || !params.sourceChain.startsWith("eip155:")) return said("a typed order needs an EVM source", "policy_refused");
1151
1232
  const intent = decodeIntent(quote.intentId.replace(/~[0-9a-f]{32}$/, ""));
1152
- if (!intent || intent.kind !== "order" || intent.srcChain !== params.sourceChain) return "the quote's intent is not an order from this chain";
1153
- if (!tx.orderExtension || !/^0x(?:[0-9a-fA-F]{2})+$/.test(tx.orderExtension)) return "the build carries no order extension to check";
1233
+ if (!intent || intent.kind !== "order" || intent.srcChain !== params.sourceChain) return said("the quote's intent is not an order from this chain", "policy_refused");
1234
+ if (!tx.orderExtension || !/^0x(?:[0-9a-fA-F]{2})+$/.test(tx.orderExtension)) return said("the build carries no order extension to check", "policy_refused");
1154
1235
  const source = ccAsset(params.sourceChain, params.sourceToken);
1155
- if (!source?.contract) return "unknown source token";
1236
+ if (!source?.contract) return said("unknown source token", "unsupported_pair");
1156
1237
  const tonDest = isTon(params.destChain);
1157
1238
  const destChainId = tonDest ? null : evmRef(params.destChain);
1158
- if (!tonDest && (destChainId === null || !params.destChain.startsWith("eip155:"))) return "the destination is neither TON nor an EVM chain";
1239
+ if (!tonDest && (destChainId === null || !params.destChain.startsWith("eip155:"))) return said("the destination is neither TON nor an EVM chain", "unsupported_pair");
1159
1240
  const outputToken = ccAssetByToken(params.destChain, params.destToken)?.contract ?? params.destToken;
1160
1241
  const listed = (table, chain, address) => typeof address === "string" && validEvmAddress(address) && (table[chain] ?? []).some((a) => validEvmAddress(a) && getAddress(a) === getAddress(address));
1161
1242
  let td;
1162
1243
  try {
1163
1244
  td = JSON.parse(tx.typedData ?? "");
1164
1245
  } catch {
1165
- return "the typed data is not JSON";
1246
+ return said("the typed data is not JSON", "policy_refused");
1166
1247
  }
1167
1248
  const domain = td.domain ?? {};
1168
1249
  const contract = domain.verifyingContract;
1169
- if (domain.name !== "1inch Limit Order Protocol" || domain.version !== "4") return "the domain is not the 1inch Limit Order Protocol v4";
1250
+ if (domain.name !== "1inch Limit Order Protocol" || domain.version !== "4") return said("the domain is not the 1inch Limit Order Protocol v4", "policy_refused");
1170
1251
  if (!listed(trusted.orderProtocols, params.sourceChain, contract) || !tx.approvalAddress || getAddress(tx.approvalAddress) !== getAddress(String(contract))) {
1171
- return `${String(contract)} is not a trusted order protocol on ${params.sourceChain}`;
1252
+ return said(`${String(contract)} is not a trusted order protocol on ${params.sourceChain}`, "vault_mismatch");
1172
1253
  }
1173
- if (Number(domain.chainId) !== chainId || tx.chainId !== void 0 && tx.chainId !== chainId) return "the domain chainId is not the source chain";
1254
+ if (Number(domain.chainId) !== chainId || tx.chainId !== void 0 && tx.chainId !== chainId) return said("the domain chainId is not the source chain", "policy_refused");
1174
1255
  const order = td.types?.Order;
1175
1256
  const message = td.message;
1176
1257
  if (td.primaryType !== "Order" || !Array.isArray(order) || order.length !== ORDER_FIELDS.length || !order.every((field, i) => field.name === ORDER_FIELDS[i][0] && field.type === ORDER_FIELDS[i][1]) || !message || Object.keys(message).length !== ORDER_FIELDS.length || !ORDER_FIELDS.every(([name]) => Object.hasOwn(message, name)) || !ORDER_FIELDS.every(([name, type]) => type === "address" ? validEvmAddress(message[name]) : validUint256(message[name]))) {
1177
- return "the typed data is not exactly a v4 Order";
1258
+ return said("the typed data is not exactly a v4 Order", "policy_refused");
1178
1259
  }
1179
- if (!validEvmAddress(intent.trader) || getAddress(String(message.maker)) !== getAddress(intent.trader)) return "the maker is not the trader";
1180
- if (getAddress(String(message.makerAsset)) !== getAddress(source.contract)) return "the makerAsset is not the source token";
1181
- if (BigInt(message.makingAmount) !== BigInt(quote.sourceAmount)) return "the makingAmount is not the input";
1182
- if (getAddress(String(message.takerAsset)) === getAddress(String(message.makerAsset))) return "the takerAsset is the makerAsset";
1260
+ if (!validEvmAddress(intent.trader) || getAddress(String(message.maker)) !== getAddress(intent.trader)) return said("the maker is not the trader", "policy_refused");
1261
+ if (getAddress(String(message.makerAsset)) !== getAddress(source.contract)) return said("the makerAsset is not the source token", "policy_refused");
1262
+ if (BigInt(message.makingAmount) !== BigInt(quote.sourceAmount)) return said("the makingAmount is not the input", "policy_refused");
1263
+ if (getAddress(String(message.takerAsset)) === getAddress(String(message.makerAsset))) return said("the takerAsset is the makerAsset", "policy_refused");
1183
1264
  const floor = [quote.minDestAmount, params.minAmountOut ?? "0"].filter((v) => /^\d{1,80}$/.test(v)).map((v) => BigInt(v)).reduce((a, b) => a > b ? a : b, 0n);
1184
- if (floor <= 0n || BigInt(message.takingAmount) < floor) return `takingAmount ${String(message.takingAmount)} is below the quote's minimum ${floor}`;
1265
+ if (floor <= 0n || BigInt(message.takingAmount) < floor) return said(`takingAmount ${String(message.takingAmount)} is below the quote's minimum ${floor}`, "price_check_failed");
1185
1266
  const traits = BigInt(message.makerTraits);
1186
1267
  const flags = traits & (1n << 56n) - 1n << 200n;
1187
1268
  const baseFlags = 1n << 251n | 1n << 249n;
1188
- if (flags !== baseFlags && flags !== (baseFlags | 1n << 255n)) return "the makerTraits flags are not a single-fill order with its extension";
1189
- if ((traits & (1n << 80n) - 1n) !== 0n || (traits >> 160n & (1n << 40n) - 1n) !== 0n) return "the makerTraits restrict the taker or the series";
1269
+ if (flags !== baseFlags && flags !== (baseFlags | 1n << 255n)) return said("the makerTraits flags are not a single-fill order with its extension", "policy_refused");
1270
+ if ((traits & (1n << 80n) - 1n) !== 0n || (traits >> 160n & (1n << 40n) - 1n) !== 0n) return said("the makerTraits restrict the taker or the series", "policy_refused");
1190
1271
  const now = BigInt(Math.floor(Date.now() / 1e3));
1191
1272
  const expiration = traits >> 80n & (1n << 40n) - 1n;
1192
- if (expiration <= now || expiration > now + BigInt(MAX_EVM_ORDER_LIFETIME_S)) return "the order's expiry is not minutes away";
1273
+ if (expiration <= now || expiration > now + BigInt(MAX_EVM_ORDER_LIFETIME_S)) return said("the order's expiry is not minutes away", expiration <= now ? "quote_expired" : "policy_refused");
1193
1274
  const extension = viemHexToBytes(tx.orderExtension);
1194
1275
  const mask = (1n << 160n) - 1n;
1195
- if ((BigInt(message.salt) & mask) !== (BigInt(keccak256(extension)) & mask)) return "the salt does not commit the order extension";
1276
+ if ((BigInt(message.salt) & mask) !== (BigInt(keccak256(extension)) & mask)) return said("the salt does not commit the order extension", "policy_refused");
1196
1277
  let parsed;
1197
1278
  try {
1198
1279
  parsed = readEvmOrderExtension(extension);
1199
1280
  } catch (e) {
1200
- return e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e);
1281
+ return said(e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e), refusalOf(e) ?? "policy_refused");
1201
1282
  }
1202
1283
  const { target, getter, post, extra, customData } = parsed;
1203
- if (!listed(trusted.factories, params.sourceChain, target)) return `the extension targets ${target}, not a trusted escrow factory on ${params.sourceChain}`;
1284
+ if (!listed(trusted.factories, params.sourceChain, target)) return said(`the extension targets ${target}, not a trusted escrow factory on ${params.sourceChain}`, "vault_mismatch");
1204
1285
  const receiverLow80 = BigInt(getAddress(String(message.receiver))) & (1n << 80n) - 1n;
1205
- if (getter.resolver !== receiverLow80 || post.resolver !== receiverLow80) return "the order's receiver is not the resolver its extension whitelists";
1286
+ if (getter.resolver !== receiverLow80 || post.resolver !== receiverLow80) return said("the order's receiver is not the resolver its extension whitelists", "recipient_mismatch");
1206
1287
  if (getter.gasBump !== 0n || getter.gasPrice !== 0n || getter.duration !== 1800n || getter.initialRateBump !== 900000n || getter.points.some((point, index) => point[0] !== EVM_AUCTION_POINTS[index][0] || point[1] !== EVM_AUCTION_POINTS[index][1]) || getter.integratorFee !== 0n || getter.integratorShare !== 100n || getter.whitelistDiscount !== 50n || !within(getter.auctionStart, bigintToNumber(now), MAX_EVM_ORDER_LIFETIME_S, 60)) {
1207
- return "the amount-getter terms do not match the pinned v1beta8 profile";
1288
+ return said("the amount-getter terms do not match the pinned v1beta8 profile", "policy_refused");
1208
1289
  }
1209
1290
  if (!zeroBytes(post.integratorRecipient) || !(zeroBytes(post.protocolRecipient) || !tonDest && EVM_DEST_PROTOCOL_FEE_RECIPIENTS.has(`0x${bytesToHex(post.protocolRecipient)}`)) || post.integratorFee !== 0n || post.integratorShare !== 100n || post.resolverFee !== getter.resolverFee || post.discount !== 50n || post.resolverTimeDelta !== 0n || !within(post.allowedTime, bigintToNumber(getter.auctionStart), 60, 5)) {
1210
- return "the post-interaction terms do not match the pinned v1beta8 profile";
1291
+ return said("the post-interaction terms do not match the pinned v1beta8 profile", "policy_refused");
1211
1292
  }
1212
1293
  let dstToken;
1213
1294
  try {
1214
1295
  dstToken = tonDest ? BigInt(getAddress(String(message.takerAsset))) : BigInt(getAddress(outputToken));
1215
1296
  } catch {
1216
- return "the destination token is not an EVM address";
1297
+ return said("the destination token is not an EVM address", "policy_refused");
1217
1298
  }
1218
1299
  if (extra[1] !== (tonDest ? 0n : BigInt(destChainId)) || extra[2] !== dstToken || extra[4] !== EVM_TIMELOCKS) {
1219
- return "the signed HTLC terms name another destination chain or token, or other timelocks";
1300
+ return said("the signed HTLC terms name another destination chain or token, or other timelocks", "policy_refused");
1220
1301
  }
1221
1302
  try {
1222
1303
  await checkOrderCustomData(customData, { evmDest: !tonDest, outputToken, traderDst: params.destAddress, quoteId: intent.quoteId });
1223
1304
  } catch (e) {
1224
- return e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e);
1305
+ return said(e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e), refusalOf(e) ?? "policy_refused");
1225
1306
  }
1226
1307
  return null;
1227
1308
  }
@@ -1232,22 +1313,22 @@ function omnistonVerifyBuild(hooks) {
1232
1313
  };
1233
1314
  return async (quote, tx, params) => {
1234
1315
  if (tx.typedData !== void 0) {
1235
- if (!params) return "the request this quote answered is unknown";
1316
+ if (!params) return said("the request this quote answered is unknown", "policy_refused");
1236
1317
  try {
1237
1318
  return await checkRemoteEvmOrder(quote, tx, params, trusted);
1238
1319
  } catch {
1239
- return "the typed order could not be checked";
1320
+ return said("the typed order could not be checked", "policy_refused");
1240
1321
  }
1241
1322
  }
1242
1323
  if (tx.bodyBoc === void 0) return null;
1243
- if (!params) return "the request this quote answered is unknown";
1324
+ if (!params) return said("the request this quote answered is unknown", "policy_refused");
1244
1325
  if (quote.providerData?.settlement !== "swap") {
1245
1326
  if (params.sourceChain.startsWith("ton:") && params.destChain.startsWith("eip155:")) {
1246
- if (!hooks.verifyTonEscrowBuild) return "a TON \u2192 EVM escrow body needs verifyTonEscrowBuild (createOmnistonTonValidators)";
1247
- if (!params.refundAddress) return "the trader's TON address (refundAddress) is unknown";
1327
+ if (!hooks.verifyTonEscrowBuild) return said("a TON \u2192 EVM escrow body needs verifyTonEscrowBuild (createOmnistonTonValidators)", "unsupported_pair");
1328
+ if (!params.refundAddress) return said("the trader's TON address (refundAddress) is unknown", "recipient_unverifiable");
1248
1329
  const floor = [quote.minDestAmount, params.minAmountOut ?? "0"].filter((v) => /^\d{1,80}$/.test(v)).map((v) => BigInt(v)).reduce((a, b) => a > b ? a : b, 0n);
1249
1330
  try {
1250
- return await hooks.verifyTonEscrowBuild({
1331
+ const text = await hooks.verifyTonEscrowBuild({
1251
1332
  payloadBocHex: Buffer2.from(tx.bodyBoc, "base64").toString("hex"),
1252
1333
  ownerAddress: params.refundAddress,
1253
1334
  inputUnits: quote.sourceAmount,
@@ -1256,22 +1337,23 @@ function omnistonVerifyBuild(hooks) {
1256
1337
  outputTokenAddress: params.destToken,
1257
1338
  minResolverSendsUnits: floor.toString()
1258
1339
  });
1340
+ return text ? said(text, tonEscrowBuildReason(text)) : text;
1259
1341
  } catch {
1260
- return "the escrow body could not be checked";
1342
+ return said("the escrow body could not be checked", "policy_refused");
1261
1343
  }
1262
1344
  }
1263
- return "only TON swap and TON \u2192 EVM escrow bodies can be checked on the client";
1345
+ return said("only TON swap and TON \u2192 EVM escrow bodies can be checked on the client", "unsupported_pair");
1264
1346
  }
1265
1347
  let steps;
1266
1348
  try {
1267
1349
  steps = JSON.parse(quote.providerData.routeSteps ?? "");
1268
1350
  } catch {
1269
- return "the quote carries no route steps";
1351
+ return said("the quote carries no route steps", "policy_refused");
1270
1352
  }
1271
- if (!Array.isArray(steps) || steps.length === 0) return "the quote carries no route steps";
1353
+ if (!Array.isArray(steps) || steps.length === 0) return said("the quote carries no route steps", "policy_refused");
1272
1354
  const source = ccAsset(params.sourceChain, params.sourceToken);
1273
1355
  const dest = ccAssetByToken(params.destChain, params.destToken);
1274
- if (!source || !dest) return "unknown source or destination token";
1356
+ if (!source || !dest) return said("unknown source or destination token", "unsupported_pair");
1275
1357
  const protocols = (quote.providerData.protocols ?? "").split(",").filter(Boolean);
1276
1358
  let checked;
1277
1359
  try {
@@ -1289,17 +1371,17 @@ function omnistonVerifyBuild(hooks) {
1289
1371
  integratorAddress: hooks.integratorAddress ?? null
1290
1372
  });
1291
1373
  } catch (e) {
1292
- return e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e);
1374
+ return said(e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e), e instanceof TonSwapPayloadError ? tonSwapPayloadReason(e.reason) : "policy_refused");
1293
1375
  }
1294
1376
  const payload = parseSwapPayload(Buffer2.from(tx.bodyBoc, "base64").toString("hex"));
1295
1377
  const target = source.contract ? payload.jettonTransfer?.destination : tx.to;
1296
- if (!target) return "the body names no swap destination";
1378
+ if (!target) return said("the body names no swap destination", "policy_refused");
1297
1379
  try {
1298
- if (!await hooks.validateTonSwapTarget({ address: target, protocols })) return `${target} is not a recognized DEX contract for ${protocols.join("+")}`;
1380
+ if (!await hooks.validateTonSwapTarget({ address: target, protocols })) return said(`${target} is not a recognized DEX contract for ${protocols.join("+")}`, "vault_mismatch");
1299
1381
  } catch {
1300
- return "the swap target could not be checked";
1382
+ return said("the swap target could not be checked", "policy_refused");
1301
1383
  }
1302
- if (!hooks.validateTonSwapLeg) return "a TON swap body needs validateTonSwapLeg (createOmnistonTonValidators)";
1384
+ if (!hooks.validateTonSwapLeg) return said("a TON swap body needs validateTonSwapLeg (createOmnistonTonValidators)", "unsupported_pair");
1303
1385
  for (const leg of checked.legs) {
1304
1386
  let ok = false;
1305
1387
  try {
@@ -1307,7 +1389,7 @@ function omnistonVerifyBuild(hooks) {
1307
1389
  } catch {
1308
1390
  ok = false;
1309
1391
  }
1310
- if (!ok) return `${leg.kind} v${leg.version} leg not verified`;
1392
+ if (!ok) return said(`${leg.kind} v${leg.version} leg not verified`, "vault_mismatch");
1311
1393
  }
1312
1394
  return null;
1313
1395
  };
@@ -1491,19 +1573,19 @@ function omnistonProvider(opts = {}) {
1491
1573
  if (!bound.ok) {
1492
1574
  if (bound.reason === "legacy-expired") {
1493
1575
  trace("legacy-intent-expired", { where });
1494
- throw new Error("omniston: quote expired \u2014 request a fresh quote");
1576
+ throw refusal("quote_expired", "omniston: quote expired \u2014 request a fresh quote");
1495
1577
  }
1496
1578
  trace("intent-mismatch", { where, field: bound.field });
1497
- throw new Error(`omniston: intent id does not match the quote it was issued for (${bound.field})`);
1579
+ throw refusal(mismatchReason(bound.field), `omniston: intent id does not match the quote it was issued for (${bound.field})`);
1498
1580
  }
1499
1581
  if (bound.legacy) trace("legacy-intent", { where });
1500
1582
  if (!decoded.digest) {
1501
1583
  trace("intent-mismatch", { where, field: "digest-missing" });
1502
- throw new Error("omniston: this quote was issued before an update \u2014 request a fresh quote");
1584
+ throw refusal("quote_expired", "omniston: this quote was issued before an update \u2014 request a fresh quote");
1503
1585
  }
1504
1586
  if (decoded.digest !== quoteDigest(entry.quote)) {
1505
1587
  trace("intent-mismatch", { where, field: "digest" });
1506
- throw new Error("omniston: quote was replaced \u2014 request a fresh quote");
1588
+ throw refusal("policy_refused", "omniston: quote was replaced \u2014 request a fresh quote");
1507
1589
  }
1508
1590
  return bound.ref;
1509
1591
  }
@@ -1537,10 +1619,10 @@ function omnistonProvider(opts = {}) {
1537
1619
  const disclosed = (await store.load(disclosedKey(quoteId)) ?? []).length > 0;
1538
1620
  if (boundTo !== binding || disclosed) {
1539
1621
  traceFor(quoteId)("secret-reuse-refused", { disclosed, bound: boundTo !== void 0 });
1540
- throw new Error("omniston: this quote id already belongs to another order \u2014 request a fresh quote");
1622
+ throw refusal("policy_refused", "omniston: this quote id already belongs to another order \u2014 request a fresh quote");
1541
1623
  }
1542
1624
  if (existing.length !== count || !existing.every((s) => /^[0-9a-fA-F]{64}$/.test(s))) {
1543
- throw new Error("omniston: stored HTLC secrets for this quote do not fit the order \u2014 request a fresh quote");
1625
+ throw refusal("policy_refused", "omniston: stored HTLC secrets for this quote do not fit the order \u2014 request a fresh quote");
1544
1626
  }
1545
1627
  traceFor(quoteId)("secret-reused", { count });
1546
1628
  return { secrets: existing.map(hexToBytes), created: false };
@@ -1599,7 +1681,10 @@ function omnistonProvider(opts = {}) {
1599
1681
  let q = await rfq(slippagePips);
1600
1682
  if (!q) {
1601
1683
  if (!signal?.aborted) {
1602
- params.onRefusal?.(`omniston: ${outcome.error ? `RFQ failed: ${outcome.error}` : outcome.rejected ? `swap quote rejected: ${outcome.rejected}` : "no TON DEX route quoted this pair/amount"}`);
1684
+ const text = `omniston: ${outcome.error ? `RFQ failed: ${outcome.error}` : outcome.rejected ? `swap quote rejected: ${outcome.rejected}` : "no TON DEX route quoted this pair/amount"}`;
1685
+ const reason = rfqReason(outcome);
1686
+ if (reason) params.onRefusal?.(text, { reason });
1687
+ else params.onRefusal?.(text);
1603
1688
  }
1604
1689
  return null;
1605
1690
  }
@@ -1665,27 +1750,32 @@ function omnistonProvider(opts = {}) {
1665
1750
  async function buildSwapDeposit(ref, q, swap) {
1666
1751
  if (!ref.trader) throw new Error("omniston: quote was requested without a refund/source address");
1667
1752
  if (!isTon(ref.srcChain) || q.inputAsset.chain.$case !== "ton" || q.outputAsset.chain.$case !== "ton") {
1668
- throw new Error("omniston: swap quote is not a TON same-chain swap");
1753
+ throw refusal("policy_refused", "omniston: swap quote is not a TON same-chain swap");
1669
1754
  }
1670
- if (!sameChainTonReady()) throw new Error("omniston: TON same-chain swaps need independent jetton-wallet, swap-target and swap-leg validators");
1755
+ if (!sameChainTonReady()) throw refusal("unsupported_pair", "omniston: TON same-chain swaps need independent jetton-wallet, swap-target and swap-leg validators");
1671
1756
  const owner = { chain: { $case: "ton", value: ref.trader } };
1672
1757
  const receiver = { chain: { $case: "ton", value: ref.dest } };
1673
1758
  const omni = await getClient();
1674
- if (typeof omni.tonBuildSwap !== "function") throw new Error("omniston: client does not support swap settlement (tonBuildSwap)");
1675
- const tx = await omni.tonBuildSwap({
1676
- quoteId: ref.quoteId,
1677
- transferSrcAddress: owner,
1678
- refundSrcAddress: owner,
1679
- gasExcessAddress: owner,
1680
- traderDstAddress: receiver,
1681
- // Build at the slippage the quote was requested with: its
1682
- // minOutputAmount is the minimum this module reported to the router.
1683
- useRecommendedSlippage: false
1684
- });
1759
+ if (typeof omni.tonBuildSwap !== "function") throw refusal("unsupported_pair", "omniston: client does not support swap settlement (tonBuildSwap)");
1760
+ let tx;
1761
+ try {
1762
+ tx = await omni.tonBuildSwap({
1763
+ quoteId: ref.quoteId,
1764
+ transferSrcAddress: owner,
1765
+ refundSrcAddress: owner,
1766
+ gasExcessAddress: owner,
1767
+ traderDstAddress: receiver,
1768
+ // Build at the slippage the quote was requested with: its
1769
+ // minOutputAmount is the minimum this module reported to the router.
1770
+ useRecommendedSlippage: false
1771
+ });
1772
+ } catch (error) {
1773
+ throw venueFailure(error);
1774
+ }
1685
1775
  const [message, ...rest] = tx.messages;
1686
- if (!message || rest.length > 0) throw new Error(`omniston: expected exactly one swap message, got ${tx.messages.length}`);
1687
- if (message.jettonWalletStateInit) throw new Error("omniston: deploying a resolver-provided jetton wallet is not supported");
1688
- if (!isPositiveInteger(message.sendAmount)) throw new Error("omniston: malformed swap message value");
1776
+ if (!message || rest.length > 0) throw refusal("policy_refused", `omniston: expected exactly one swap message, got ${tx.messages.length}`);
1777
+ if (message.jettonWalletStateInit) throw refusal("policy_refused", "omniston: deploying a resolver-provided jetton wallet is not supported");
1778
+ if (!isPositiveInteger(message.sendAmount)) throw refusal("policy_refused", "omniston: malformed swap message value");
1689
1779
  const sendAmount = BigInt(message.sendAmount);
1690
1780
  const gasCap = BigInt(maxTonExecutionNano);
1691
1781
  const gasBudget = q.gasBudget && isPositiveInteger(q.gasBudget) ? BigInt(q.gasBudget) : gasCap;
@@ -1697,53 +1787,53 @@ function omnistonProvider(opts = {}) {
1697
1787
  const ptonV1Native = !inputJetton && !!payload.jettonTransfer && singleV1;
1698
1788
  if (inputJetton) {
1699
1789
  const transfer = payload.jettonTransfer;
1700
- if (!transfer) throw new Error("omniston: jetton-input swap message is not a jetton transfer");
1701
- if (sendAmount > gasCap || sendAmount > gasBudget) throw new Error(`omniston: swap message attaches more TON than the quoted gas budget (${sendAmount} > ${gasBudget} nanoTON, cap ${gasCap}, ${protocols.join("+")})`);
1702
- if (transfer.amount !== BigInt(q.inputUnits)) throw new Error("omniston: swap message moves a different amount than quoted");
1790
+ if (!transfer) throw refusal("policy_refused", "omniston: jetton-input swap message is not a jetton transfer");
1791
+ if (sendAmount > gasCap || sendAmount > gasBudget) throw refusal("policy_refused", `omniston: swap message attaches more TON than the quoted gas budget (${sendAmount} > ${gasBudget} nanoTON, cap ${gasCap}, ${protocols.join("+")})`);
1792
+ if (transfer.amount !== BigInt(q.inputUnits)) throw refusal("policy_refused", "omniston: swap message moves a different amount than quoted");
1703
1793
  if (!transfer.responseDestination || !sameTonAddress(transfer.responseDestination, ref.trader)) {
1704
- throw new Error("omniston: swap message returns excess to a wallet other than the trader");
1794
+ throw refusal("recipient_mismatch", "omniston: swap message returns excess to a wallet other than the trader");
1705
1795
  }
1706
- if (transfer.forwardTon >= sendAmount) throw new Error("omniston: swap message forwards more TON than it carries");
1796
+ if (transfer.forwardTon >= sendAmount) throw refusal("policy_refused", "omniston: swap message forwards more TON than it carries");
1707
1797
  const walletValid = await opts.validateTonJettonWallet({
1708
1798
  ownerAddress: ref.trader,
1709
1799
  jettonMasterAddress: inputJetton,
1710
1800
  walletAddress: message.targetAddress
1711
1801
  });
1712
- if (!walletValid) throw new Error("omniston: TON jetton wallet was not independently validated");
1802
+ if (!walletValid) throw refusal("vault_mismatch", "omniston: TON jetton wallet was not independently validated");
1713
1803
  const targetValid = await opts.validateTonSwapTarget({ address: transfer.destination, protocols });
1714
- if (!targetValid) throw new Error("omniston: swap destination is not a recognized DEX contract for the quoted protocol");
1804
+ if (!targetValid) throw refusal("vault_mismatch", "omniston: swap destination is not a recognized DEX contract for the quoted protocol");
1715
1805
  } else if (ptonV1Native) {
1716
1806
  const transfer = payload.jettonTransfer;
1717
1807
  const input = BigInt(q.inputUnits);
1718
- if (transfer.amount !== input) throw new Error("omniston: swap message moves a different amount than quoted");
1719
- if (sendAmount < input) throw new Error("omniston: swap message carries less than the quoted input");
1808
+ if (transfer.amount !== input) throw refusal("policy_refused", "omniston: swap message moves a different amount than quoted");
1809
+ if (sendAmount < input) throw refusal("policy_refused", "omniston: swap message carries less than the quoted input");
1720
1810
  const gas = sendAmount - input;
1721
- if (gas > gasCap || gas > gasBudget) throw new Error(`omniston: swap message attaches more TON than the quoted gas budget (${gas} > ${gasBudget} nanoTON, cap ${gasCap}, ${protocols.join("+")})`);
1722
- if (transfer.forwardTon > gas) throw new Error("omniston: swap message forwards more TON than its gas");
1811
+ if (gas > gasCap || gas > gasBudget) throw refusal("policy_refused", `omniston: swap message attaches more TON than the quoted gas budget (${gas} > ${gasBudget} nanoTON, cap ${gasCap}, ${protocols.join("+")})`);
1812
+ if (transfer.forwardTon > gas) throw refusal("policy_refused", "omniston: swap message forwards more TON than its gas");
1723
1813
  if (!transfer.responseDestination || !sameTonAddress(transfer.responseDestination, ref.trader)) {
1724
- throw new Error("omniston: swap message returns excess to a wallet other than the trader");
1814
+ throw refusal("recipient_mismatch", "omniston: swap message returns excess to a wallet other than the trader");
1725
1815
  }
1726
- if (typeof opts.validateTonSwapLeg !== "function") throw new Error("omniston: STON.fi v1 native input needs the validateTonSwapLeg validator (pTON v1 wallet ownership)");
1816
+ if (typeof opts.validateTonSwapLeg !== "function") throw refusal("unsupported_pair", "omniston: STON.fi v1 native input needs the validateTonSwapLeg validator (pTON v1 wallet ownership)");
1727
1817
  const targetValid = await opts.validateTonSwapTarget({ address: message.targetAddress, protocols });
1728
- if (!targetValid) throw new Error("omniston: swap target is not a recognized DEX contract for the quoted protocol");
1818
+ if (!targetValid) throw refusal("vault_mismatch", "omniston: swap target is not a recognized DEX contract for the quoted protocol");
1729
1819
  const routerValid = await opts.validateTonSwapTarget({ address: transfer.destination, protocols });
1730
- if (!routerValid) throw new Error("omniston: swap destination is not a recognized DEX contract for the quoted protocol");
1820
+ if (!routerValid) throw refusal("vault_mismatch", "omniston: swap destination is not a recognized DEX contract for the quoted protocol");
1731
1821
  } else {
1732
1822
  if (payload.jettonTransfer) {
1733
- throw new Error(`omniston: native-input swap message must not be a jetton transfer (route ${protocols.join("+") || "?"}, target ${message.targetAddress}, transfer ${payload.jettonTransfer.amount} to ${payload.jettonTransfer.destination}, forwardTon ${payload.jettonTransfer.forwardTon}, value ${sendAmount})`);
1823
+ throw refusal("policy_refused", `omniston: native-input swap message must not be a jetton transfer (route ${protocols.join("+") || "?"}, target ${message.targetAddress}, transfer ${payload.jettonTransfer.amount} to ${payload.jettonTransfer.destination}, forwardTon ${payload.jettonTransfer.forwardTon}, value ${sendAmount})`);
1734
1824
  }
1735
1825
  const input = BigInt(q.inputUnits);
1736
- if (sendAmount < input) throw new Error("omniston: swap message carries less than the quoted input");
1826
+ if (sendAmount < input) throw refusal("policy_refused", "omniston: swap message carries less than the quoted input");
1737
1827
  const gas = sendAmount - input;
1738
- if (gas > gasCap || gas > gasBudget) throw new Error(`omniston: swap message attaches more TON than the quoted gas budget (${gas} > ${gasBudget} nanoTON, cap ${gasCap}, ${protocols.join("+")})`);
1828
+ if (gas > gasCap || gas > gasBudget) throw refusal("policy_refused", `omniston: swap message attaches more TON than the quoted gas budget (${gas} > ${gasBudget} nanoTON, cap ${gasCap}, ${protocols.join("+")})`);
1739
1829
  const targetValid = await opts.validateTonSwapTarget({ address: message.targetAddress, protocols });
1740
- if (!targetValid) throw new Error("omniston: swap target is not a recognized DEX contract for the quoted protocol");
1830
+ if (!targetValid) throw refusal("vault_mismatch", "omniston: swap target is not a recognized DEX contract for the quoted protocol");
1741
1831
  }
1742
1832
  const mode = ptonV1Native ? "enforce" : opts.tonSwapPayloadCheck ?? "enforce";
1743
1833
  const trace = traceFor(ref.quoteId);
1744
1834
  const payloadRefusal = (reason, detail) => {
1745
1835
  trace("ton-swap-payload", { reason, detail, mode, protocols: protocols.join("+") });
1746
- if (mode === "enforce") throw new Error(`omniston: ton-swap-payload ${reason} \u2014 ${detail}`);
1836
+ if (mode === "enforce") throw refusal(tonSwapPayloadReason(reason), `omniston: ton-swap-payload ${reason} \u2014 ${detail}`);
1747
1837
  };
1748
1838
  let checked = null;
1749
1839
  try {
@@ -1817,33 +1907,33 @@ function omnistonProvider(opts = {}) {
1817
1907
  const srcChain = ref.srcChain;
1818
1908
  const chainId = evmRef(srcChain);
1819
1909
  const srcCase = q.inputAsset.chain.$case;
1820
- if (chainId === null || EVM_CASE[chainId] !== srcCase) throw new Error("omniston: source chain does not match the EVM quote");
1910
+ if (chainId === null || EVM_CASE[chainId] !== srcCase) throw refusal("policy_refused", "omniston: source chain does not match the EVM quote");
1821
1911
  const destCase = q.outputAsset.chain.$case;
1822
1912
  const destChain = caipForChainCase(destCase);
1823
1913
  const tonDest = destCase === "ton";
1824
- if (!destChain || destChain === srcChain) throw new Error("omniston: EVM-source order has no supported destination chain");
1914
+ if (!destChain || destChain === srcChain) throw refusal("unsupported_pair", "omniston: EVM-source order has no supported destination chain");
1825
1915
  if (!trustedOrderProtocols.has(srcChain) || typeof opts.validateEvmInputPosition !== "function" || typeof (tonDest ? opts.validateTonOutputPosition : opts.validateEvmOutputPosition) !== "function") {
1826
- throw new Error(`omniston: EVM\u2192${tonDest ? "TON" : "EVM"} needs a trusted order protocol and both position validators`);
1916
+ throw refusal("unsupported_pair", `omniston: EVM\u2192${tonDest ? "TON" : "EVM"} needs a trusted order protocol and both position validators`);
1827
1917
  }
1828
1918
  let owner;
1829
1919
  try {
1830
1920
  owner = getAddress(ref.trader);
1831
1921
  } catch {
1832
- throw new Error("omniston: trader is not an EVM address");
1922
+ throw refusal("recipient_mismatch", "omniston: trader is not an EVM address");
1833
1923
  }
1834
1924
  let dest;
1835
1925
  if (tonDest) {
1836
1926
  try {
1837
1927
  TonAddress.parse(ref.dest);
1838
1928
  } catch {
1839
- throw new Error("omniston: destination is not a TON address");
1929
+ throw refusal("recipient_mismatch", "omniston: destination is not a TON address");
1840
1930
  }
1841
1931
  dest = ref.dest;
1842
1932
  } else {
1843
1933
  try {
1844
1934
  dest = getAddress(ref.dest);
1845
1935
  } catch {
1846
- throw new Error("omniston: destination is not an EVM address");
1936
+ throw refusal("recipient_mismatch", "omniston: destination is not an EVM address");
1847
1937
  }
1848
1938
  }
1849
1939
  const traderDst = { chain: { $case: destCase, value: dest } };
@@ -1854,9 +1944,9 @@ function omnistonProvider(opts = {}) {
1854
1944
  const outputToken = assetContract(q.outputAsset);
1855
1945
  const destChainId = tonDest ? null : evmRef(destChain);
1856
1946
  if (factory?.chain.$case === destCase && !isTrusted(destChain, factory.chain.value)) {
1857
- throw new Error(`omniston: untrusted ${destChain} settlement contract ${factory.chain.value}`);
1947
+ throw refusal("vault_mismatch", `omniston: untrusted ${destChain} settlement contract ${factory.chain.value}`);
1858
1948
  }
1859
- if (protocol.chain.$case !== srcCase || !isTrustedOrderProtocol(srcChain, protocol.chain.value) || resolver?.chain.$case !== srcCase || !validEvmAddress(resolver.chain.value) || factory?.chain.$case !== destCase || !isTrusted(destChain, factory.chain.value) || !inputToken || !outputToken || !Number.isSafeInteger(settlement.protocolFeePips) || !tonDest && (destChainId === null || !validEvmAddress(outputToken) || !isPositiveInteger(settlement.dstSecurityDepositUnits ?? ""))) throw new Error("omniston: incomplete EVM order settlement binding in quote");
1949
+ if (protocol.chain.$case !== srcCase || !isTrustedOrderProtocol(srcChain, protocol.chain.value) || resolver?.chain.$case !== srcCase || !validEvmAddress(resolver.chain.value) || factory?.chain.$case !== destCase || !isTrusted(destChain, factory.chain.value) || !inputToken || !outputToken || !Number.isSafeInteger(settlement.protocolFeePips) || !tonDest && (destChainId === null || !validEvmAddress(outputToken) || !isPositiveInteger(settlement.dstSecurityDepositUnits ?? ""))) throw refusal("policy_refused", "omniston: incomplete EVM order settlement binding in quote");
1860
1950
  const { secrets, created } = await secretsFor(ref, q, 1);
1861
1951
  const hashes = secrets.map((secret) => hashlock(secret, settlement.htlcHashingFunction));
1862
1952
  const forget = async () => {
@@ -1873,14 +1963,14 @@ function omnistonProvider(opts = {}) {
1873
1963
  });
1874
1964
  } catch (error) {
1875
1965
  await forget();
1876
- throw error;
1966
+ throw venueFailure(error);
1877
1967
  }
1878
1968
  let td;
1879
1969
  try {
1880
1970
  try {
1881
1971
  td = JSON.parse(payload.typedData);
1882
1972
  } catch {
1883
- throw new Error("omniston: malformed EVM order typed data");
1973
+ throw refusal("policy_refused", "omniston: malformed EVM order typed data");
1884
1974
  }
1885
1975
  validateSerializedOrderDetails(payload.serializedOrderDetails, {
1886
1976
  quoteId: ref.quoteId,
@@ -1963,45 +2053,46 @@ function omnistonProvider(opts = {}) {
1963
2053
  async getQuote(params, signal) {
1964
2054
  if (params.flex) return null;
1965
2055
  if (isTon(params.sourceChain) && isTon(params.destChain)) return await getSwapQuote(params, signal);
1966
- const refuse2 = (reason) => {
1967
- params.onRefusal?.(`omniston: ${reason}`);
2056
+ const refuse2 = (reason, why) => {
2057
+ if (why) params.onRefusal?.(`omniston: ${reason}`, { reason: why });
2058
+ else params.onRefusal?.(`omniston: ${reason}`);
1968
2059
  return null;
1969
2060
  };
1970
2061
  const evmSource = !isTon(params.sourceChain);
1971
2062
  const tonDest = isTon(params.destChain);
1972
- if (!omniston.supports(params.sourceChain, params.destChain)) return refuse2("route is not an Omniston order corridor");
2063
+ if (!omniston.supports(params.sourceChain, params.destChain)) return refuse2("route is not an Omniston order corridor", "unsupported_pair");
1973
2064
  if (!trustedContracts.has(params.sourceChain) || !trustedContracts.has(params.destChain)) {
1974
- return refuse2(`no trusted settlement contract configured for ${trustedContracts.has(params.sourceChain) ? params.destChain : params.sourceChain}`);
2065
+ return refuse2(`no trusted settlement contract configured for ${trustedContracts.has(params.sourceChain) ? params.destChain : params.sourceChain}`, "unsupported_pair");
1975
2066
  }
1976
2067
  if (evmSource) {
1977
- if (!trustedOrderProtocols.has(params.sourceChain)) return refuse2(`no trusted EVM order protocol configured for ${params.sourceChain}`);
2068
+ if (!trustedOrderProtocols.has(params.sourceChain)) return refuse2(`no trusted EVM order protocol configured for ${params.sourceChain}`, "unsupported_pair");
1978
2069
  if (typeof opts.validateEvmInputPosition !== "function" || typeof (tonDest ? opts.validateTonOutputPosition : opts.validateEvmOutputPosition) !== "function") {
1979
- return refuse2(tonDest ? "EVM\u2192TON needs the EVM input-position and TON output-position validators" : "EVM\u2192EVM needs the EVM input-position and EVM output-position validators");
2070
+ return refuse2(tonDest ? "EVM\u2192TON needs the EVM input-position and TON output-position validators" : "EVM\u2192EVM needs the EVM input-position and EVM output-position validators", "unsupported_pair");
1980
2071
  }
1981
2072
  if (params.dry === false) {
1982
2073
  if (tonDest) {
1983
2074
  try {
1984
2075
  TonAddress.parse(params.destAddress);
1985
2076
  } catch {
1986
- return refuse2("destination is not a TON address");
2077
+ return refuse2("destination is not a TON address", "recipient_mismatch");
1987
2078
  }
1988
- } else if (!validEvmAddress(params.destAddress)) return refuse2("destination is not an EVM address");
2079
+ } else if (!validEvmAddress(params.destAddress)) return refuse2("destination is not an EVM address", "recipient_mismatch");
1989
2080
  }
1990
- } else if (typeof opts.validateTonJettonWallet !== "function" || typeof opts.validateTonEscrowPayload !== "function" || typeof opts.validateEvmOutputPosition !== "function") return refuse2("TON\u2192EVM needs the jetton-wallet, escrow-payload and EVM output-position validators");
2081
+ } else if (typeof opts.validateTonJettonWallet !== "function" || typeof opts.validateTonEscrowPayload !== "function" || typeof opts.validateEvmOutputPosition !== "function") return refuse2("TON\u2192EVM needs the jetton-wallet, escrow-payload and EVM output-position validators", "unsupported_pair");
1991
2082
  const source = ccAsset(params.sourceChain, params.sourceToken);
1992
2083
  const dest = ccAssetByToken(params.destChain, params.destToken);
1993
- if (!source || !dest) return refuse2("asset is not in the catalog");
2084
+ if (!source || !dest) return refuse2("asset is not in the catalog", "unsupported_pair");
1994
2085
  const inputAsset = toAssetId(params.sourceChain, source);
1995
2086
  const outputAsset = toAssetId(params.destChain, dest);
1996
2087
  if (!inputAsset || !outputAsset) {
1997
- return refuse2(`cross-chain orders are token corridors only (no ${!inputAsset ? source.symbol : dest.symbol} native leg)`);
2088
+ return refuse2(`cross-chain orders are token corridors only (no ${!inputAsset ? source.symbol : dest.symbol} native leg)`, "unsupported_pair");
1998
2089
  }
1999
2090
  const maxUnits = decimalToUnits(maxUsdText, source.decimals);
2000
2091
  if (!maxUnits || !isPositiveInteger(params.amountIn)) return refuse2("invalid amount");
2001
- if (BigInt(params.amountIn) > maxUnits) return refuse2(`amount above the ${maxUsdText} USD per-swap cap`);
2092
+ if (BigInt(params.amountIn) > maxUnits) return refuse2(`amount above the ${maxUsdText} USD per-swap cap`, "amount_too_large");
2002
2093
  const trader = params.refundAddress ?? "";
2003
2094
  if (!trader && !params.dry) return refuse2(`execution quote needs the trader's ${evmSource ? "EVM" : "TON"} address (refundAddress)`);
2004
- if (evmSource && trader && !validEvmAddress(trader)) return refuse2("refundAddress is not an EVM address");
2095
+ if (evmSource && trader && !validEvmAddress(trader)) return refuse2("refundAddress is not an EVM address", "recipient_mismatch");
2005
2096
  const request = {
2006
2097
  inputAsset,
2007
2098
  outputAsset,
@@ -2029,27 +2120,28 @@ function omnistonProvider(opts = {}) {
2029
2120
  if (!q) {
2030
2121
  if (signal?.aborted) return null;
2031
2122
  return refuse2(
2032
- rfq.error ? `RFQ failed: ${rfq.error}` : rfq.rejected ? `resolver quote rejected: ${rfq.rejected}` : "no resolver quoted this route/amount"
2123
+ rfq.error ? `RFQ failed: ${rfq.error}` : rfq.rejected ? `resolver quote rejected: ${rfq.rejected}` : "no resolver quoted this route/amount",
2124
+ rfqReason(rfq)
2033
2125
  );
2034
2126
  }
2035
2127
  const orderData = q.settlementData.$case === "order" ? q.settlementData.value : null;
2036
- if (!orderData) return refuse2("resolver answered without order settlement");
2128
+ if (!orderData) return refuse2("resolver answered without order settlement", "upstream_unavailable");
2037
2129
  const srcContract = orderData.srcProtocolContractAddress.chain.value;
2038
2130
  if (evmSource ? !isTrustedOrderProtocol(params.sourceChain, srcContract) : !isTrusted(params.sourceChain, srcContract)) {
2039
- return refuse2(evmSource ? `untrusted ${params.sourceChain} order protocol ${srcContract}` : `untrusted TON settlement contract ${srcContract}`);
2131
+ return refuse2(evmSource ? `untrusted ${params.sourceChain} order protocol ${srcContract}` : `untrusted TON settlement contract ${srcContract}`, "vault_mismatch");
2040
2132
  }
2041
2133
  if (!orderData.dstProtocolContractAddress || !isTrusted(params.destChain, orderData.dstProtocolContractAddress.chain.value)) {
2042
- return refuse2(`untrusted ${params.destChain} settlement contract ${orderData.dstProtocolContractAddress?.chain.value ?? "(none)"}`);
2134
+ return refuse2(`untrusted ${params.destChain} settlement contract ${orderData.dstProtocolContractAddress?.chain.value ?? "(none)"}`, "vault_mismatch");
2043
2135
  }
2044
2136
  const deadlineMs = orderData.tradeStartDeadline * 1e3;
2045
- if (!Number.isFinite(deadlineMs) || deadlineMs <= Date.now()) return refuse2("quote trade-start deadline already passed");
2046
- if (params.dry === false && deadlineMs - Date.now() < MIN_START_HEADROOM_MS) return refuse2("quote trade-start deadline too close to execute");
2137
+ if (!Number.isFinite(deadlineMs) || deadlineMs <= Date.now()) return refuse2("quote trade-start deadline already passed", "quote_expired");
2138
+ if (params.dry === false && deadlineMs - Date.now() < MIN_START_HEADROOM_MS) return refuse2("quote trade-start deadline too close to execute", "quote_expired");
2047
2139
  const inputScaled = BigInt(q.inputUnits) * 10n ** BigInt(dest.decimals);
2048
2140
  const outputScaled = BigInt(q.outputUnits) * 10n ** BigInt(source.decimals);
2049
- if (outputScaled * 2n < inputScaled || outputScaled > inputScaled * 2n) return refuse2("quoted output is outside 0.5x\u20132x of the input");
2050
- if (await quoteIdInUse(q.quoteId)) return refuse2("the platform reused a quote id that already belongs to an order");
2141
+ if (outputScaled * 2n < inputScaled || outputScaled > inputScaled * 2n) return refuse2("quoted output is outside 0.5x\u20132x of the input", "price_check_failed");
2142
+ if (await quoteIdInUse(q.quoteId)) return refuse2("the platform reused a quote id that already belongs to an order", "policy_refused");
2051
2143
  if (!await claimBuilt(q.quoteId, { quote: q, at: Date.now(), bound: { trader, dest: params.destAddress, srcChain: params.sourceChain, kind: "order" } })) {
2052
- return refuse2("the platform reused a quote id that already holds another quote");
2144
+ return refuse2("the platform reused a quote id that already holds another quote", "policy_refused");
2053
2145
  }
2054
2146
  const feeUnits = Number(q.protocolFeeUnits || "0") + Number(q.integratorFeeUnits || "0");
2055
2147
  const feeUsd = feeUnits / 10 ** dest.decimals;
@@ -2080,29 +2172,29 @@ function omnistonProvider(opts = {}) {
2080
2172
  */
2081
2173
  async buildDeposit(quote) {
2082
2174
  if (!Number.isFinite(quote.validUntilMs) || quote.validUntilMs <= Date.now()) {
2083
- throw new Error("omniston: quote expired \u2014 request a fresh quote");
2175
+ throw refusal("quote_expired", "omniston: quote expired \u2014 request a fresh quote");
2084
2176
  }
2085
2177
  const decoded = decodeIntent(quote.intentId);
2086
2178
  if (!decoded) throw new Error("omniston: malformed intent id");
2087
2179
  const cached = await loadBuilt(decoded.quoteId);
2088
- if (!cached) throw new Error("omniston: quote expired \u2014 request a fresh quote");
2180
+ if (!cached) throw refusal("quote_expired", "omniston: quote expired \u2014 request a fresh quote");
2089
2181
  const ref = requireBinding(decoded, cached, "buildDeposit");
2090
2182
  const q = cached.quote;
2091
2183
  if (q.settlementData.$case === "swap") return await buildSwapDeposit(ref, q, q.settlementData.value);
2092
2184
  if (q.settlementData.$case !== "order" || q.settlementData.value.tradeStartDeadline * 1e3 <= Date.now()) {
2093
- throw new Error("omniston: quote trade deadline expired \u2014 request a fresh quote");
2185
+ throw refusal("quote_expired", "omniston: quote trade deadline expired \u2014 request a fresh quote");
2094
2186
  }
2095
2187
  if (!ref.trader) throw new Error("omniston: quote was requested without a refund/source address");
2096
2188
  const srcCase = q.inputAsset.chain.$case;
2097
2189
  if (srcCase !== "ton") return await buildEvmOrder(ref, q, q.settlementData.value, cached.bound);
2098
2190
  const srcChain = ref.srcChain;
2099
- if (!isTon(srcChain)) throw new Error("omniston: source chain does not match TON quote");
2191
+ if (!isTon(srcChain)) throw refusal("policy_refused", "omniston: source chain does not match TON quote");
2100
2192
  const destCase = q.outputAsset.chain.$case;
2101
2193
  const traderDst = destCase === "ton" ? null : { chain: { $case: destCase, value: getAddress(ref.dest) } };
2102
- if (!traderDst) throw new Error("omniston: TON-destination orders are not supported");
2194
+ if (!traderDst) throw refusal("unsupported_pair", "omniston: TON-destination orders are not supported");
2103
2195
  const minter = q.settlementData.value.srcProtocolContractAddress;
2104
2196
  if (minter.chain.$case !== "ton" || !isTrusted(srcChain, minter.chain.value)) {
2105
- throw new Error(`omniston: untrusted TON settlement contract ${minter.chain.value}`);
2197
+ throw refusal("vault_mismatch", `omniston: untrusted TON settlement contract ${minter.chain.value}`);
2106
2198
  }
2107
2199
  const settlement = q.settlementData;
2108
2200
  const hashingFunction = settlement.$case === "order" ? settlement.value.htlcHashingFunction : void 0;
@@ -2126,27 +2218,27 @@ function omnistonProvider(opts = {}) {
2126
2218
  });
2127
2219
  } catch (error) {
2128
2220
  await forget();
2129
- throw error;
2221
+ throw venueFailure(error);
2130
2222
  }
2131
2223
  const [message, ...rest] = tx.messages;
2132
2224
  if (!message || rest.length > 0) {
2133
2225
  await forget();
2134
- throw new Error(`omniston: expected exactly one escrow message, got ${tx.messages.length}`);
2226
+ throw refusal("policy_refused", `omniston: expected exactly one escrow message, got ${tx.messages.length}`);
2135
2227
  }
2136
2228
  if (!isPositiveInteger(message.sendAmount) || BigInt(message.sendAmount) > BigInt(maxTonExecutionNano)) {
2137
2229
  await forget();
2138
- throw new Error("omniston: resolver TON execution value exceeds the configured safety cap");
2230
+ throw refusal("policy_refused", "omniston: resolver TON execution value exceeds the configured safety cap");
2139
2231
  }
2140
2232
  const expectedProtocol = q.settlementData.value.srcProtocolContractAddress;
2141
2233
  const initialResolver = q.settlementData.value.srcInitialResolverAddress;
2142
2234
  const jettonMaster = assetContract(q.inputAsset);
2143
2235
  if (expectedProtocol.chain.$case !== "ton" || initialResolver?.chain.$case !== "ton" || !jettonMaster) {
2144
2236
  await forget();
2145
- throw new Error("omniston: incomplete TON settlement binding in quote");
2237
+ throw refusal("policy_refused", "omniston: incomplete TON settlement binding in quote");
2146
2238
  }
2147
2239
  if (message.jettonWalletStateInit) {
2148
2240
  await forget();
2149
- throw new Error("omniston: deploying a resolver-provided jetton wallet is not supported");
2241
+ throw refusal("policy_refused", "omniston: deploying a resolver-provided jetton wallet is not supported");
2150
2242
  }
2151
2243
  let walletValid = false;
2152
2244
  try {
@@ -2161,11 +2253,11 @@ function omnistonProvider(opts = {}) {
2161
2253
  }
2162
2254
  if (!walletValid) {
2163
2255
  await forget();
2164
- throw new Error("omniston: TON jetton wallet was not independently validated");
2256
+ throw refusal("vault_mismatch", "omniston: TON jetton wallet was not independently validated");
2165
2257
  }
2166
2258
  try {
2167
2259
  if (!opts.validateTonEscrowPayload) {
2168
- throw new Error("omniston: TON source requires an independent escrow-payload validator");
2260
+ throw refusal("unsupported_pair", "omniston: TON source requires an independent escrow-payload validator");
2169
2261
  }
2170
2262
  await validateTonEscrowPayload(message, {
2171
2263
  quoteId: ref.quoteId,
@@ -2212,7 +2304,7 @@ function omnistonProvider(opts = {}) {
2212
2304
  const result = bindIntent(decoded, stored);
2213
2305
  if (!result.ok && result.reason === "mismatch") {
2214
2306
  trace("intent-mismatch", { where: "trackIntent", field: result.field });
2215
- throw new Error(`omniston: intent id does not match the quote it was issued for (${result.field})`);
2307
+ throw refusal(mismatchReason(result.field), `omniston: intent id does not match the quote it was issued for (${result.field})`);
2216
2308
  }
2217
2309
  if (result.ok) {
2218
2310
  if (result.legacy) trace("legacy-intent", { where: "trackIntent" });
@@ -2392,12 +2484,12 @@ function omnistonProvider(opts = {}) {
2392
2484
  if (!ref) throw new Error("omniston: malformed intent id");
2393
2485
  const cached = await loadBuilt(ref.quoteId);
2394
2486
  const order = cached?.evmOrder;
2395
- if (!order) throw new Error("omniston: no signed-order context for this quote \u2014 build the order again");
2487
+ if (!order) throw refusal("quote_expired", "omniston: no signed-order context for this quote \u2014 build the order again");
2396
2488
  requireBinding(ref, cached, "registerSignedOrder");
2397
2489
  if (!/^0x[0-9a-fA-F]{130}$/.test(signature)) throw new Error("omniston: malformed order signature");
2398
2490
  const td = JSON.parse(order.typedData);
2399
2491
  const omni = await getClient();
2400
- await omni.orderRegisterSignedOrder({
2492
+ const request = {
2401
2493
  quoteId: ref.quoteId,
2402
2494
  ownerSrcAddress: { chain: { $case: order.chainCase, value: order.owner } },
2403
2495
  signedOrder: { order: { $case: "evmV1", value: {
@@ -2406,7 +2498,12 @@ function omnistonProvider(opts = {}) {
2406
2498
  orderExtension: hexToBytes(order.orderExtension)
2407
2499
  } } },
2408
2500
  ...order.serializedOrderDetails ? { serializedOrderDetails: hexToBytes(order.serializedOrderDetails) } : {}
2409
- });
2501
+ };
2502
+ try {
2503
+ await omni.orderRegisterSignedOrder(request);
2504
+ } catch (error) {
2505
+ throw venueFailure(error);
2506
+ }
2410
2507
  },
2411
2508
  close() {
2412
2509
  clientGeneration += 1;
@@ -2432,4 +2529,4 @@ export {
2432
2529
  omnistonVerifyBuild,
2433
2530
  omnistonProvider
2434
2531
  };
2435
- //# sourceMappingURL=chunk-FDSTD34Z.js.map
2532
+ //# sourceMappingURL=chunk-DMO24BFW.js.map