@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.
package/dist/provider.cjs CHANGED
@@ -502,6 +502,7 @@ var AUDITED_OMNISTON_EVM_ORDER_PROTOCOLS = {
502
502
  };
503
503
 
504
504
  // src/provider.ts
505
+ var import_errors = require("@anywalletkit/protocol-swap2-core/errors");
505
506
  var import_core4 = require("@anywalletkit/core");
506
507
  var EVM_CASE = {
507
508
  1: "ethereum",
@@ -639,6 +640,54 @@ function encodeTypedMessage(td) {
639
640
  (0, import_viem.encodeAbiParameters)([paramFor(td.primaryType)], [td.message])
640
641
  );
641
642
  }
643
+ function venueReason(error) {
644
+ const details = typeof error === "object" && error !== null ? error.details : void 0;
645
+ const info = typeof details === "object" && details !== null ? details.error_info : void 0;
646
+ if (typeof info !== "object" || info === null) return "upstream_unavailable";
647
+ const { reason, metadata } = info;
648
+ const meta = typeof metadata === "object" && metadata !== null ? metadata : {};
649
+ const type = typeof meta.type === "string" ? meta.type : "";
650
+ switch (reason) {
651
+ case "INVALID_ARGUMENT":
652
+ case "INVALID_PARAMETERS":
653
+ return void 0;
654
+ case "NOT_FOUND":
655
+ return typeof meta.object_type === "string" && meta.object_type.toLowerCase() === "quote" ? "quote_expired" : "upstream_unavailable";
656
+ case "UNSUPPORTED":
657
+ return "unsupported_pair";
658
+ case "SWAP_LIMIT_EXCEEDED":
659
+ return "amount_too_large";
660
+ case "QUOTE_VALIDATION_FAILED":
661
+ if (type === "EMULATION_UNSUPPORTED_TOKEN") return "unsupported_pair";
662
+ if (type === "EMULATION_WRONG_OUTPUT_AMOUNT" || type === "EMULATION_REFUND_SLIPPAGE") return "price_check_failed";
663
+ return type.startsWith("EMULATION_") ? "simulation_failed" : "upstream_unavailable";
664
+ default:
665
+ return "upstream_unavailable";
666
+ }
667
+ }
668
+ function venueFailure(error) {
669
+ if (!(error instanceof Error)) return error;
670
+ const reason = venueReason(error);
671
+ return reason ? (0, import_errors.withReason)(error, reason) : error;
672
+ }
673
+ function mismatchReason(field) {
674
+ return field === "trader" || field === "dest" || field === "owner" ? "recipient_mismatch" : "policy_refused";
675
+ }
676
+ function tonSwapPayloadReason(code) {
677
+ switch (code) {
678
+ case "receiver-not-destination":
679
+ case "refund-not-trader":
680
+ case "excess-not-trader":
681
+ return "recipient_mismatch";
682
+ case "min-out-zero":
683
+ case "min-out-below-quote":
684
+ return "price_check_failed";
685
+ case "leg-unverified":
686
+ return "vault_mismatch";
687
+ default:
688
+ return "policy_refused";
689
+ }
690
+ }
642
691
  function generateSecret() {
643
692
  return crypto.getRandomValues(new Uint8Array(32));
644
693
  }
@@ -649,7 +698,7 @@ function hashlock(secret, hashingFunction) {
649
698
  case "HASHING_FUNCTION_SHA256":
650
699
  return (0, import_viem.sha256)(secret, "bytes");
651
700
  default:
652
- throw new Error(`omniston: unsupported HTLC hashing function ${String(hashingFunction)}`);
701
+ throw (0, import_errors.refusal)("policy_refused", `omniston: unsupported HTLC hashing function ${String(hashingFunction)}`);
653
702
  }
654
703
  }
655
704
  function sameAsset(a, b) {
@@ -687,7 +736,7 @@ function stepJetton(asset) {
687
736
  }
688
737
  function readBigEndian(bytes, offset, length) {
689
738
  if (!Number.isInteger(offset) || !Number.isInteger(length) || length < 1 || offset < 0 || offset + length > bytes.length) {
690
- throw new Error("omniston: truncated EVM order extension");
739
+ throw (0, import_errors.refusal)("policy_refused", "omniston: truncated EVM order extension");
691
740
  }
692
741
  let value = 0n;
693
742
  for (let index = offset; index < offset + length; index++) value = value << 8n | BigInt(bytes[index]);
@@ -732,30 +781,30 @@ var EVM_DEST_PROTOCOL_FEE_RECIPIENTS = /* @__PURE__ */ new Set(["0x076268146b4ec
732
781
  var EVM_AUCTION_POINTS = [[800000n, 100n], [700000n, 100n], [600000n, 100n], [500000n, 100n], [400000n, 100n]];
733
782
  var EVM_TIMELOCKS = [1n, 120n, 240n, 300n, 1n, 120n, 180n, 0n].reduce((packed, value, index) => packed | value << BigInt(index * 32), 0n);
734
783
  function readEvmOrderExtension(extension) {
735
- if (extension.length < 32) throw new Error("omniston: malformed EVM order extension");
784
+ if (extension.length < 32) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed EVM order extension");
736
785
  const view = new DataView(extension.buffer, extension.byteOffset, extension.byteLength);
737
786
  const fields = [];
738
787
  let begin = 0;
739
788
  const concatLength = extension.length - 32;
740
789
  for (let index = 0; index < 8; index++) {
741
790
  const end2 = view.getUint32(28 - index * 4, false);
742
- if (end2 < begin || end2 > concatLength) throw new Error("omniston: malformed EVM order extension offsets");
791
+ if (end2 < begin || end2 > concatLength) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed EVM order extension offsets");
743
792
  fields.push(extension.slice(32 + begin, 32 + end2));
744
793
  begin = end2;
745
794
  }
746
795
  for (const index of [0, 1, 4, 5, 6]) {
747
- if (fields[index].length !== 0) throw new Error("omniston: unsupported EVM order extension field");
796
+ if (fields[index].length !== 0) throw (0, import_errors.refusal)("policy_refused", "omniston: unsupported EVM order extension field");
748
797
  }
749
798
  if (!sliceEqual(fields[2], fields[3])) {
750
- throw new Error("omniston: making/taking amount getters use different programs");
799
+ throw (0, import_errors.refusal)("policy_refused", "omniston: making/taking amount getters use different programs");
751
800
  }
752
801
  let target;
753
802
  for (const index of [2, 3, 7]) {
754
803
  const field2 = fields[index];
755
- if (field2.length <= 20) throw new Error("omniston: incomplete EVM order extension program");
804
+ if (field2.length <= 20) throw (0, import_errors.refusal)("policy_refused", "omniston: incomplete EVM order extension program");
756
805
  const address = (0, import_viem.getAddress)(`0x${bytesToHex(field2.slice(0, 20))}`);
757
806
  if (target !== void 0 && (0, import_viem.getAddress)(target) !== address) {
758
- throw new Error("omniston: EVM order extension target is not independently trusted");
807
+ throw (0, import_errors.refusal)("vault_mismatch", "omniston: EVM order extension target is not independently trusted");
759
808
  }
760
809
  target = address;
761
810
  }
@@ -773,7 +822,7 @@ function readEvmOrderExtension(extension) {
773
822
  cursor += 3;
774
823
  const pointCount = (0, import_core4.bigintToNumber)(readBigEndian(field, cursor, 1));
775
824
  cursor += 1;
776
- if (pointCount !== 5) throw new Error("omniston: unsupported EVM auction profile");
825
+ if (pointCount !== 5) throw (0, import_errors.refusal)("policy_refused", "omniston: unsupported EVM auction profile");
777
826
  const points = [];
778
827
  for (let index = 0; index < pointCount; index++) {
779
828
  points.push([readBigEndian(field, cursor, 3), readBigEndian(field, cursor + 3, 2)]);
@@ -790,7 +839,7 @@ function readEvmOrderExtension(extension) {
790
839
  const getterWhitelistCount = (0, import_core4.bigintToNumber)(readBigEndian(field, cursor, 1));
791
840
  cursor += 1;
792
841
  if (getterWhitelistCount !== 1 || cursor + getterWhitelistCount * 10 !== field.length) {
793
- throw new Error("omniston: malformed EVM amount-getter whitelist");
842
+ throw (0, import_errors.refusal)("policy_refused", "omniston: malformed EVM amount-getter whitelist");
794
843
  }
795
844
  const getter = { gasBump, gasPrice, auctionStart, duration, initialRateBump, points, integratorFee, integratorShare, resolverFee, whitelistDiscount, resolver: readBigEndian(field, cursor, 10) };
796
845
  const post = fields[7];
@@ -812,7 +861,7 @@ function readEvmOrderExtension(extension) {
812
861
  const whitelistCount = (0, import_core4.bigintToNumber)(readBigEndian(post, cursor, 1));
813
862
  cursor += 1;
814
863
  if (whitelistCount !== 1 || cursor + whitelistCount * 12 + 160 !== post.length) {
815
- throw new Error("omniston: malformed EVM post-interaction whitelist");
864
+ throw (0, import_errors.refusal)("policy_refused", "omniston: malformed EVM post-interaction whitelist");
816
865
  }
817
866
  const postResolver = readBigEndian(post, cursor, 10);
818
867
  cursor += 10;
@@ -824,7 +873,7 @@ function readEvmOrderExtension(extension) {
824
873
  return value;
825
874
  });
826
875
  const customData = extension.slice(32 + begin);
827
- if (customData.length !== 131) throw new Error("omniston: malformed EVM order extension custom data");
876
+ if (customData.length !== 131) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed EVM order extension custom data");
828
877
  return {
829
878
  target,
830
879
  getter,
@@ -837,29 +886,29 @@ async function validateEvmToTonOrderExtension(extension, expected) {
837
886
  const evmDest = expected.dstChainId !== void 0;
838
887
  if (evmDest && (!Number.isSafeInteger(expected.dstChainId) || expected.dstChainId <= 0)) throw new Error("omniston: invalid EVM destination chain id");
839
888
  const { target, getter, post, extra, customData } = readEvmOrderExtension(extension);
840
- if (!expected.trustedTarget(target)) throw new Error("omniston: EVM order extension target is not independently trusted");
889
+ if (!expected.trustedTarget(target)) throw (0, import_errors.refusal)("vault_mismatch", "omniston: EVM order extension target is not independently trusted");
841
890
  const quoteResolverFee = expected.protocolFeePips % 10 === 0 ? BigInt(expected.protocolFeePips / 10) : -1n;
842
891
  const resolverLow80 = BigInt((0, import_viem.getAddress)(expected.initialResolverAddress)) & (1n << 80n) - 1n;
843
892
  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) {
844
- throw new Error("omniston: EVM amount-getter terms do not match the pinned v1beta8 profile");
893
+ throw (0, import_errors.refusal)("policy_refused", "omniston: EVM amount-getter terms do not match the pinned v1beta8 profile");
845
894
  }
846
895
  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) {
847
- throw new Error("omniston: EVM post-interaction terms do not match the pinned v1beta8 profile");
896
+ throw (0, import_errors.refusal)("policy_refused", "omniston: EVM post-interaction terms do not match the pinned v1beta8 profile");
848
897
  }
849
898
  const hashlock2 = readBigEndian(expected.secretHash, 0, expected.secretHash.length);
850
899
  const dstToken = evmDest ? BigInt((0, import_viem.getAddress)(expected.outputTokenAddress)) : BigInt((0, import_viem.getAddress)(expected.takerAsset));
851
900
  const deposits = BigInt(expected.srcSecurityDepositUnits) << 128n | BigInt(expected.dstSecurityDepositUnits);
852
901
  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) {
853
- throw new Error("omniston: signed EVM HTLC terms do not match the validated order details");
902
+ throw (0, import_errors.refusal)("policy_refused", "omniston: signed EVM HTLC terms do not match the validated order details");
854
903
  }
855
904
  await checkOrderCustomData(customData, { evmDest, outputToken: expected.outputTokenAddress, traderDst: expected.traderDstAddress, quoteId: expected.quoteId });
856
905
  if (expected.tradeStartDeadline < expected.quoteTimestamp) {
857
- throw new Error("omniston: invalid EVM order quote deadline");
906
+ throw (0, import_errors.refusal)("quote_expired", "omniston: invalid EVM order quote deadline");
858
907
  }
859
908
  }
860
909
  async function checkOrderCustomData(customData, expected) {
861
910
  if (expected.evmDest) {
862
- if (!zeroBytes(customData.slice(0, 66))) throw new Error("omniston: unexpected TON addresses in an EVM-destination order");
911
+ if (!zeroBytes(customData.slice(0, 66))) throw (0, import_errors.refusal)("recipient_mismatch", "omniston: unexpected TON addresses in an EVM-destination order");
863
912
  } else {
864
913
  const { Address: Address3 } = await import("@ton/core");
865
914
  const expectedTon = [expected.outputToken, expected.traderDst].map((value) => Address3.parse(value));
@@ -867,16 +916,16 @@ async function checkOrderCustomData(customData, expected) {
867
916
  const offset = index * 33;
868
917
  const workchain = customData[offset] === 255 ? -1 : customData[offset];
869
918
  if (workchain !== expectedTon[index].workChain || !sliceEqual(customData.slice(offset + 1, offset + 33), expectedTon[index].hash)) {
870
- throw new Error("omniston: signed EVM\u2192TON address does not match the validated request");
919
+ throw (0, import_errors.refusal)("recipient_mismatch", "omniston: signed EVM\u2192TON address does not match the validated request");
871
920
  }
872
921
  }
873
922
  }
874
923
  if (!zeroBytes(customData.slice(66, 99))) {
875
- throw new Error("omniston: unexpected private TON disclosure address");
924
+ throw (0, import_errors.refusal)("policy_refused", "omniston: unexpected private TON disclosure address");
876
925
  }
877
926
  const quoteId = expected.quoteId.replace(/^0x/, "");
878
927
  if (!QUOTE_ID.test(quoteId) || !sliceEqual(customData.slice(99), hexToBytes(quoteId.padStart(64, "0")))) {
879
- throw new Error("omniston: EVM order extension custom data is not bound to the quote id");
928
+ throw (0, import_errors.refusal)("policy_refused", "omniston: EVM order extension custom data is not bound to the quote id");
880
929
  }
881
930
  }
882
931
  function validateEvmTypedOrder(td, expected) {
@@ -898,7 +947,7 @@ function validateEvmTypedOrder(td, expected) {
898
947
  if (value.domain?.name !== "1inch Limit Order Protocol" || value.domain.version !== "4" || typeof value.domain.verifyingContract !== "string" || (0, import_viem.getAddress)(value.domain.verifyingContract) !== (0, import_viem.getAddress)(expected.settlementContract) || Number(value.domain.chainId) !== expected.chainId || value.primaryType !== "Order" || !exactSchema || !message || !exactMessage || (0, import_viem.getAddress)(String(message.maker)) !== (0, import_viem.getAddress)(expected.owner) || (0, import_viem.getAddress)(String(message.receiver)) !== (0, import_viem.getAddress)(expected.receiver) || (0, import_viem.getAddress)(String(message.makerAsset)) !== (0, import_viem.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
899
948
  // skew between the quote and the order builder), never later.
900
949
  traits < 0n || !flagsOk || allowedSender !== 0n || series !== 0n || !within(expiration, expected.tradeStartDeadline, 15, 15) || expiration <= BigInt(expected.quoteTimestamp) || !validEvmAddress(message.takerAsset) || (0, import_viem.getAddress)(String(message.takerAsset)) === (0, import_viem.getAddress)(expected.makerAsset) || !validUint256(message.makingAmount) || BigInt(message.makingAmount) !== BigInt(expected.makingAmount) || !validUint256(message.takingAmount) || BigInt(message.takingAmount) !== BigInt(expected.takingAmount)) {
901
- throw new Error("omniston: EVM order fields do not match the validated quote/request");
950
+ throw (0, import_errors.refusal)("policy_refused", "omniston: EVM order fields do not match the validated quote/request");
902
951
  }
903
952
  }
904
953
  async function validateTonEscrowPayload(message, expected, validateForwardPayload) {
@@ -908,9 +957,9 @@ async function validateTonEscrowPayload(message, expected, validateForwardPayloa
908
957
  try {
909
958
  roots = Cell2.fromBoc(import_buffer2.Buffer.from(boc));
910
959
  } catch {
911
- throw new Error("omniston: malformed TON escrow BoC");
960
+ throw (0, import_errors.refusal)("policy_refused", "omniston: malformed TON escrow BoC");
912
961
  }
913
- if (roots.length !== 1) throw new Error("omniston: TON escrow must contain exactly one root cell");
962
+ if (roots.length !== 1) throw (0, import_errors.refusal)("policy_refused", "omniston: TON escrow must contain exactly one root cell");
914
963
  const slice = roots[0].beginParse();
915
964
  const op = slice.loadUint(32);
916
965
  slice.loadUintBig(64);
@@ -921,7 +970,7 @@ async function validateTonEscrowPayload(message, expected, validateForwardPayloa
921
970
  const forwardAmount = slice.loadCoins();
922
971
  const payloadByRef = slice.loadBit();
923
972
  if (op !== 260734629 || amount !== BigInt(expected.inputUnits) || !(destination instanceof Address3) || !destination.equals(Address3.parse(expected.protocolAddress)) || response !== null && (!(response instanceof Address3) || !response.equals(Address3.parse(expected.ownerAddress))) || customPayload !== null || forwardAmount <= 0n || forwardAmount > BigInt(message.sendAmount) || !payloadByRef || slice.remainingBits !== 0 || slice.remainingRefs !== 1) {
924
- throw new Error("omniston: TON jetton transfer does not match the validated quote/request");
973
+ throw (0, import_errors.refusal)("policy_refused", "omniston: TON jetton transfer does not match the validated quote/request");
925
974
  }
926
975
  const forwardPayload = slice.loadRef();
927
976
  const verified = await validateForwardPayload({
@@ -941,7 +990,7 @@ async function validateTonEscrowPayload(message, expected, validateForwardPayloa
941
990
  ...expected.hashingFunction ? { hashingFunction: expected.hashingFunction } : {},
942
991
  exclusivityTimeout: expected.exclusivityTimeout
943
992
  });
944
- if (!verified) throw new Error("omniston: TON escrow forward payload failed independent semantic validation");
993
+ if (!verified) throw (0, import_errors.refusal)("policy_refused", "omniston: TON escrow forward payload failed independent semantic validation");
945
994
  }
946
995
  function feeToken(asset) {
947
996
  return asset.assetId || asset.contract || asset.symbol;
@@ -971,24 +1020,24 @@ var ProtoReader = class {
971
1020
  const byte = this.bytes[this.pos++];
972
1021
  out += (byte & 127) * 2 ** shift;
973
1022
  if ((byte & 128) === 0) {
974
- if (!Number.isSafeInteger(out)) throw new Error("omniston: unsafe protobuf varint");
1023
+ if (!Number.isSafeInteger(out)) throw (0, import_errors.refusal)("policy_refused", "omniston: unsafe protobuf varint");
975
1024
  return out;
976
1025
  }
977
1026
  shift += 7;
978
1027
  }
979
- throw new Error("omniston: malformed protobuf varint");
1028
+ throw (0, import_errors.refusal)("policy_refused", "omniston: malformed protobuf varint");
980
1029
  }
981
1030
  field() {
982
1031
  const tag = this.varint();
983
1032
  const number = Math.floor(tag / 8);
984
1033
  const wire = tag & 7;
985
- if (number < 1) throw new Error("omniston: malformed protobuf field");
1034
+ if (number < 1) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed protobuf field");
986
1035
  return { number, wire };
987
1036
  }
988
1037
  lengthDelimited() {
989
1038
  const length = this.varint();
990
1039
  const end2 = this.pos + length;
991
- if (length < 0 || end2 > this.bytes.length) throw new Error("omniston: truncated protobuf payload");
1040
+ if (length < 0 || end2 > this.bytes.length) throw (0, import_errors.refusal)("policy_refused", "omniston: truncated protobuf payload");
992
1041
  const value = this.bytes.slice(this.pos, end2);
993
1042
  this.pos = end2;
994
1043
  return value;
@@ -1010,10 +1059,10 @@ var ProtoReader = class {
1010
1059
  this.advance(4);
1011
1060
  return;
1012
1061
  }
1013
- throw new Error("omniston: unsupported protobuf wire type");
1062
+ throw (0, import_errors.refusal)("policy_refused", "omniston: unsupported protobuf wire type");
1014
1063
  }
1015
1064
  advance(length) {
1016
- if (this.pos + length > this.bytes.length) throw new Error("omniston: truncated protobuf payload");
1065
+ if (this.pos + length > this.bytes.length) throw (0, import_errors.refusal)("policy_refused", "omniston: truncated protobuf payload");
1017
1066
  this.pos += length;
1018
1067
  }
1019
1068
  };
@@ -1040,10 +1089,10 @@ function decodeChainAddress(bytes) {
1040
1089
  while (!reader.done) {
1041
1090
  const { number, wire } = reader.field();
1042
1091
  const $case = PROTO_CASE_BY_FIELD[number];
1043
- if (!$case || wire !== 2 || address) throw new Error("omniston: malformed chain address in order details");
1092
+ if (!$case || wire !== 2 || address) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed chain address in order details");
1044
1093
  address = { chain: { $case, value: utf8.decode(reader.lengthDelimited()) } };
1045
1094
  }
1046
- if (!address?.chain.value) throw new Error("omniston: empty chain address in order details");
1095
+ if (!address?.chain.value) throw (0, import_errors.refusal)("policy_refused", "omniston: empty chain address in order details");
1047
1096
  return address;
1048
1097
  }
1049
1098
  function decodeAssetId(bytes) {
@@ -1052,27 +1101,27 @@ function decodeAssetId(bytes) {
1052
1101
  while (!reader.done) {
1053
1102
  const { number, wire } = reader.field();
1054
1103
  const $case = PROTO_CASE_BY_FIELD[number];
1055
- if (!$case || wire !== 2 || asset) throw new Error("omniston: malformed asset id in order details");
1104
+ if (!$case || wire !== 2 || asset) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed asset id in order details");
1056
1105
  const kindReader = new ProtoReader(reader.lengthDelimited());
1057
1106
  const kindField = kindReader.field();
1058
1107
  if (kindField.wire !== 2 || kindField.number !== 1 && kindField.number !== 2) {
1059
- throw new Error("omniston: unsupported asset kind in order details");
1108
+ throw (0, import_errors.refusal)("policy_refused", "omniston: unsupported asset kind in order details");
1060
1109
  }
1061
1110
  const kindCase = kindField.number === 1 ? "native" : $case === "ton" ? "jetton" : $case === "tron" ? "trc20" : "erc20";
1062
1111
  const value = kindField.number === 1 ? {} : utf8.decode(kindReader.lengthDelimited());
1063
- if (!kindReader.done || kindField.number === 2 && !value) throw new Error("omniston: malformed asset kind in order details");
1112
+ if (!kindReader.done || kindField.number === 2 && !value) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed asset kind in order details");
1064
1113
  asset = { chain: { $case, value: { kind: { $case: kindCase, value } } } };
1065
1114
  }
1066
- if (!asset) throw new Error("omniston: empty asset id in order details");
1115
+ if (!asset) throw (0, import_errors.refusal)("policy_refused", "omniston: empty asset id in order details");
1067
1116
  return asset;
1068
1117
  }
1069
1118
  function validateSerializedOrderDetails(bytes, expected) {
1070
- if (!bytes?.length) throw new Error("omniston: EVM HTLC order payload has no serialized details");
1119
+ if (!bytes?.length) throw (0, import_errors.refusal)("policy_refused", "omniston: EVM HTLC order payload has no serialized details");
1071
1120
  const wrapper = new ProtoReader(bytes);
1072
1121
  const outer = wrapper.field();
1073
- if (outer.number !== 1 || outer.wire !== 2) throw new Error("omniston: unsupported HTLC order-details version");
1122
+ if (outer.number !== 1 || outer.wire !== 2) throw (0, import_errors.refusal)("policy_refused", "omniston: unsupported HTLC order-details version");
1074
1123
  const detailsBytes = wrapper.lengthDelimited();
1075
- if (!wrapper.done) throw new Error("omniston: ambiguous HTLC order-details wrapper");
1124
+ if (!wrapper.done) throw (0, import_errors.refusal)("policy_refused", "omniston: ambiguous HTLC order-details wrapper");
1076
1125
  const reader = new ProtoReader(detailsBytes);
1077
1126
  let quoteId;
1078
1127
  let outputAsset;
@@ -1080,16 +1129,16 @@ function validateSerializedOrderDetails(bytes, expected) {
1080
1129
  const hashes = [];
1081
1130
  while (!reader.done) {
1082
1131
  const { number, wire } = reader.field();
1083
- if (wire !== 2) throw new Error("omniston: malformed HTLC order details");
1132
+ if (wire !== 2) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed HTLC order details");
1084
1133
  const value = reader.lengthDelimited();
1085
1134
  if (number === 1 && quoteId === void 0) quoteId = utf8.decode(value);
1086
1135
  else if (number === 2 && outputAsset === void 0) outputAsset = decodeAssetId(value);
1087
1136
  else if (number === 3 && traderDstAddress === void 0) traderDstAddress = decodeChainAddress(value);
1088
1137
  else if (number === 5) hashes.push(value);
1089
- else throw new Error("omniston: duplicate or unexpected HTLC order-details field");
1138
+ else throw (0, import_errors.refusal)("policy_refused", "omniston: duplicate or unexpected HTLC order-details field");
1090
1139
  }
1091
1140
  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]))) {
1092
- throw new Error("omniston: resolver order payload does not match the validated quote/request");
1141
+ throw (0, import_errors.refusal)("policy_refused", "omniston: resolver order payload does not match the validated quote/request");
1093
1142
  }
1094
1143
  }
1095
1144
  function isPositiveInteger(value) {
@@ -1120,7 +1169,7 @@ function bytesToHex(bytes) {
1120
1169
  function hexToBytes(hex) {
1121
1170
  const clean = hex.replace(/^0x/, "");
1122
1171
  if (!clean || !/^(?:[0-9a-fA-F]{2})+$/.test(clean)) {
1123
- throw new Error("omniston: malformed hex payload");
1172
+ throw (0, import_errors.refusal)("policy_refused", "omniston: malformed hex payload");
1124
1173
  }
1125
1174
  const out = new Uint8Array(clean.length / 2);
1126
1175
  for (let i = 0; i < out.length; i++) out[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
@@ -1154,6 +1203,14 @@ function swapProtocols(v) {
1154
1203
  for (const route of v.routes) for (const step of route.steps) for (const chunk of step.chunks) names.add(chunk.protocol);
1155
1204
  return [...names];
1156
1205
  }
1206
+ function rejectionReason(rejection) {
1207
+ return rejection === "quote timestamp out of range" ? "quote_expired" : "upstream_unavailable";
1208
+ }
1209
+ function rfqReason(outcome) {
1210
+ if (outcome.error) return outcome.errorReason;
1211
+ if (outcome.rejected) return rejectionReason(outcome.rejected);
1212
+ return outcome.answered ? "no_route" : "upstream_unavailable";
1213
+ }
1157
1214
  function quoteRejection(q, update, acknowledgedRfqId, request, settlement) {
1158
1215
  if (!q) return "empty quote";
1159
1216
  if (!acknowledgedRfqId || update.rfqId !== acknowledgedRfqId || q.rfqId !== acknowledgedRfqId) return "quote for another RFQ";
@@ -1193,6 +1250,7 @@ function awaitQuote(stream, request, signal, settleMs = QUOTE_SETTLE_MS, settlem
1193
1250
  const max = setTimeout(() => finish(latest), QUOTE_MAX_MS);
1194
1251
  sub = stream.subscribe({
1195
1252
  next: (event) => {
1253
+ if (event.$case === "ack" || event.$case === "quoteUpdated" || event.$case === "noQuote") outcome.answered = true;
1196
1254
  if (event.$case === "ack") {
1197
1255
  acknowledgedRfqId = event.value.rfqId || null;
1198
1256
  } else if (event.$case === "quoteUpdated") {
@@ -1212,6 +1270,7 @@ function awaitQuote(stream, request, signal, settleMs = QUOTE_SETTLE_MS, settlem
1212
1270
  },
1213
1271
  error: (err) => {
1214
1272
  outcome.error = String(err?.message ?? err).slice(0, 200);
1273
+ outcome.errorReason = venueReason(err);
1215
1274
  finish(null);
1216
1275
  },
1217
1276
  complete: () => finish(latest)
@@ -1253,9 +1312,9 @@ function awaitSwapProgress(stream) {
1253
1312
  }
1254
1313
  var JETTON_TRANSFER_OP = 260734629;
1255
1314
  function parseSwapPayload(payloadHex) {
1256
- if (!/^(?:[0-9a-fA-F]{2})+$/.test(payloadHex)) throw new Error("omniston: malformed swap payload hex");
1315
+ if (!/^(?:[0-9a-fA-F]{2})+$/.test(payloadHex)) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed swap payload hex");
1257
1316
  const roots = import_core3.Cell.fromBoc(import_buffer2.Buffer.from(payloadHex, "hex"));
1258
- if (roots.length !== 1) throw new Error("omniston: swap payload must have one root");
1317
+ if (roots.length !== 1) throw (0, import_errors.refusal)("policy_refused", "omniston: swap payload must have one root");
1259
1318
  const s = roots[0].beginParse();
1260
1319
  const op = s.loadUint(32);
1261
1320
  if (op !== JETTON_TRANSFER_OP) return { op, jettonTransfer: null };
@@ -1327,86 +1386,108 @@ function mapOrderStatus(order) {
1327
1386
  }
1328
1387
  function omnistonPins(trustedEvmOrderProtocols) {
1329
1388
  const byChain = new Map(Object.entries(trustedEvmOrderProtocols).map(([chain, list]) => [chain, list.map((a) => (0, import_viem.getAddress)(a))]));
1330
- return Object.freeze({ callTargets: () => [], spenders: (chain) => byChain.get(chain) ?? [] });
1389
+ return Object.freeze({
1390
+ callTargets: () => [],
1391
+ spenders: (chain) => byChain.get(chain) ?? [],
1392
+ // The reason behind a text omnistonVerifyBuild returned, so the remote stub's refusal carries it.
1393
+ reasonOf: verifyNotes.reasonOf
1394
+ });
1395
+ }
1396
+ var VERIFY_NOTES = /* @__PURE__ */ Symbol.for("@anywalletkit/protocol-swap2-omniston/verify-notes@1");
1397
+ var verifyNotes = (() => {
1398
+ const shared = globalThis[VERIFY_NOTES];
1399
+ if (shared && typeof shared.note === "function" && typeof shared.reasonOf === "function") return shared;
1400
+ const notes = (0, import_errors.refusalNotes)();
1401
+ try {
1402
+ Object.defineProperty(globalThis, VERIFY_NOTES, { value: notes });
1403
+ } catch {
1404
+ }
1405
+ return notes;
1406
+ })();
1407
+ var said = verifyNotes.note;
1408
+ function tonEscrowBuildReason(text) {
1409
+ if (text.startsWith("the escrow transfer pays ")) return "vault_mismatch";
1410
+ if (text.startsWith("the resolver is bound to deliver ")) return "price_check_failed";
1411
+ return "policy_refused";
1331
1412
  }
1332
1413
  var MAX_EVM_ORDER_LIFETIME_S = 900;
1333
1414
  async function checkRemoteEvmOrder(quote, tx, params, trusted) {
1334
1415
  const chainId = evmRef2(params.sourceChain);
1335
- if (chainId === null || !params.sourceChain.startsWith("eip155:")) return "a typed order needs an EVM source";
1416
+ if (chainId === null || !params.sourceChain.startsWith("eip155:")) return said("a typed order needs an EVM source", "policy_refused");
1336
1417
  const intent = decodeIntent(quote.intentId.replace(/~[0-9a-f]{32}$/, ""));
1337
- if (!intent || intent.kind !== "order" || intent.srcChain !== params.sourceChain) return "the quote's intent is not an order from this chain";
1338
- if (!tx.orderExtension || !/^0x(?:[0-9a-fA-F]{2})+$/.test(tx.orderExtension)) return "the build carries no order extension to check";
1418
+ if (!intent || intent.kind !== "order" || intent.srcChain !== params.sourceChain) return said("the quote's intent is not an order from this chain", "policy_refused");
1419
+ if (!tx.orderExtension || !/^0x(?:[0-9a-fA-F]{2})+$/.test(tx.orderExtension)) return said("the build carries no order extension to check", "policy_refused");
1339
1420
  const source = (0, import_swap2_assets.ccAsset)(params.sourceChain, params.sourceToken);
1340
- if (!source?.contract) return "unknown source token";
1421
+ if (!source?.contract) return said("unknown source token", "unsupported_pair");
1341
1422
  const tonDest = isTon(params.destChain);
1342
1423
  const destChainId = tonDest ? null : evmRef2(params.destChain);
1343
- if (!tonDest && (destChainId === null || !params.destChain.startsWith("eip155:"))) return "the destination is neither TON nor an EVM chain";
1424
+ if (!tonDest && (destChainId === null || !params.destChain.startsWith("eip155:"))) return said("the destination is neither TON nor an EVM chain", "unsupported_pair");
1344
1425
  const outputToken = (0, import_swap2_assets.ccAssetByToken)(params.destChain, params.destToken)?.contract ?? params.destToken;
1345
1426
  const listed = (table, chain, address) => typeof address === "string" && validEvmAddress(address) && (table[chain] ?? []).some((a) => validEvmAddress(a) && (0, import_viem.getAddress)(a) === (0, import_viem.getAddress)(address));
1346
1427
  let td;
1347
1428
  try {
1348
1429
  td = JSON.parse(tx.typedData ?? "");
1349
1430
  } catch {
1350
- return "the typed data is not JSON";
1431
+ return said("the typed data is not JSON", "policy_refused");
1351
1432
  }
1352
1433
  const domain = td.domain ?? {};
1353
1434
  const contract = domain.verifyingContract;
1354
- if (domain.name !== "1inch Limit Order Protocol" || domain.version !== "4") return "the domain is not the 1inch Limit Order Protocol v4";
1435
+ if (domain.name !== "1inch Limit Order Protocol" || domain.version !== "4") return said("the domain is not the 1inch Limit Order Protocol v4", "policy_refused");
1355
1436
  if (!listed(trusted.orderProtocols, params.sourceChain, contract) || !tx.approvalAddress || (0, import_viem.getAddress)(tx.approvalAddress) !== (0, import_viem.getAddress)(String(contract))) {
1356
- return `${String(contract)} is not a trusted order protocol on ${params.sourceChain}`;
1437
+ return said(`${String(contract)} is not a trusted order protocol on ${params.sourceChain}`, "vault_mismatch");
1357
1438
  }
1358
- if (Number(domain.chainId) !== chainId || tx.chainId !== void 0 && tx.chainId !== chainId) return "the domain chainId is not the source chain";
1439
+ if (Number(domain.chainId) !== chainId || tx.chainId !== void 0 && tx.chainId !== chainId) return said("the domain chainId is not the source chain", "policy_refused");
1359
1440
  const order = td.types?.Order;
1360
1441
  const message = td.message;
1361
1442
  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]))) {
1362
- return "the typed data is not exactly a v4 Order";
1443
+ return said("the typed data is not exactly a v4 Order", "policy_refused");
1363
1444
  }
1364
- if (!validEvmAddress(intent.trader) || (0, import_viem.getAddress)(String(message.maker)) !== (0, import_viem.getAddress)(intent.trader)) return "the maker is not the trader";
1365
- if ((0, import_viem.getAddress)(String(message.makerAsset)) !== (0, import_viem.getAddress)(source.contract)) return "the makerAsset is not the source token";
1366
- if (BigInt(message.makingAmount) !== BigInt(quote.sourceAmount)) return "the makingAmount is not the input";
1367
- if ((0, import_viem.getAddress)(String(message.takerAsset)) === (0, import_viem.getAddress)(String(message.makerAsset))) return "the takerAsset is the makerAsset";
1445
+ if (!validEvmAddress(intent.trader) || (0, import_viem.getAddress)(String(message.maker)) !== (0, import_viem.getAddress)(intent.trader)) return said("the maker is not the trader", "policy_refused");
1446
+ if ((0, import_viem.getAddress)(String(message.makerAsset)) !== (0, import_viem.getAddress)(source.contract)) return said("the makerAsset is not the source token", "policy_refused");
1447
+ if (BigInt(message.makingAmount) !== BigInt(quote.sourceAmount)) return said("the makingAmount is not the input", "policy_refused");
1448
+ if ((0, import_viem.getAddress)(String(message.takerAsset)) === (0, import_viem.getAddress)(String(message.makerAsset))) return said("the takerAsset is the makerAsset", "policy_refused");
1368
1449
  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);
1369
- if (floor <= 0n || BigInt(message.takingAmount) < floor) return `takingAmount ${String(message.takingAmount)} is below the quote's minimum ${floor}`;
1450
+ if (floor <= 0n || BigInt(message.takingAmount) < floor) return said(`takingAmount ${String(message.takingAmount)} is below the quote's minimum ${floor}`, "price_check_failed");
1370
1451
  const traits = BigInt(message.makerTraits);
1371
1452
  const flags = traits & (1n << 56n) - 1n << 200n;
1372
1453
  const baseFlags = 1n << 251n | 1n << 249n;
1373
- if (flags !== baseFlags && flags !== (baseFlags | 1n << 255n)) return "the makerTraits flags are not a single-fill order with its extension";
1374
- if ((traits & (1n << 80n) - 1n) !== 0n || (traits >> 160n & (1n << 40n) - 1n) !== 0n) return "the makerTraits restrict the taker or the series";
1454
+ if (flags !== baseFlags && flags !== (baseFlags | 1n << 255n)) return said("the makerTraits flags are not a single-fill order with its extension", "policy_refused");
1455
+ if ((traits & (1n << 80n) - 1n) !== 0n || (traits >> 160n & (1n << 40n) - 1n) !== 0n) return said("the makerTraits restrict the taker or the series", "policy_refused");
1375
1456
  const now = BigInt(Math.floor(Date.now() / 1e3));
1376
1457
  const expiration = traits >> 80n & (1n << 40n) - 1n;
1377
- if (expiration <= now || expiration > now + BigInt(MAX_EVM_ORDER_LIFETIME_S)) return "the order's expiry is not minutes away";
1458
+ 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");
1378
1459
  const extension = (0, import_viem.hexToBytes)(tx.orderExtension);
1379
1460
  const mask = (1n << 160n) - 1n;
1380
- if ((BigInt(message.salt) & mask) !== (BigInt((0, import_viem.keccak256)(extension)) & mask)) return "the salt does not commit the order extension";
1461
+ if ((BigInt(message.salt) & mask) !== (BigInt((0, import_viem.keccak256)(extension)) & mask)) return said("the salt does not commit the order extension", "policy_refused");
1381
1462
  let parsed;
1382
1463
  try {
1383
1464
  parsed = readEvmOrderExtension(extension);
1384
1465
  } catch (e) {
1385
- return e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e);
1466
+ return said(e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e), (0, import_errors.refusalOf)(e) ?? "policy_refused");
1386
1467
  }
1387
1468
  const { target, getter, post, extra, customData } = parsed;
1388
- if (!listed(trusted.factories, params.sourceChain, target)) return `the extension targets ${target}, not a trusted escrow factory on ${params.sourceChain}`;
1469
+ if (!listed(trusted.factories, params.sourceChain, target)) return said(`the extension targets ${target}, not a trusted escrow factory on ${params.sourceChain}`, "vault_mismatch");
1389
1470
  const receiverLow80 = BigInt((0, import_viem.getAddress)(String(message.receiver))) & (1n << 80n) - 1n;
1390
- if (getter.resolver !== receiverLow80 || post.resolver !== receiverLow80) return "the order's receiver is not the resolver its extension whitelists";
1471
+ if (getter.resolver !== receiverLow80 || post.resolver !== receiverLow80) return said("the order's receiver is not the resolver its extension whitelists", "recipient_mismatch");
1391
1472
  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, (0, import_core4.bigintToNumber)(now), MAX_EVM_ORDER_LIFETIME_S, 60)) {
1392
- return "the amount-getter terms do not match the pinned v1beta8 profile";
1473
+ return said("the amount-getter terms do not match the pinned v1beta8 profile", "policy_refused");
1393
1474
  }
1394
1475
  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, (0, import_core4.bigintToNumber)(getter.auctionStart), 60, 5)) {
1395
- return "the post-interaction terms do not match the pinned v1beta8 profile";
1476
+ return said("the post-interaction terms do not match the pinned v1beta8 profile", "policy_refused");
1396
1477
  }
1397
1478
  let dstToken;
1398
1479
  try {
1399
1480
  dstToken = tonDest ? BigInt((0, import_viem.getAddress)(String(message.takerAsset))) : BigInt((0, import_viem.getAddress)(outputToken));
1400
1481
  } catch {
1401
- return "the destination token is not an EVM address";
1482
+ return said("the destination token is not an EVM address", "policy_refused");
1402
1483
  }
1403
1484
  if (extra[1] !== (tonDest ? 0n : BigInt(destChainId)) || extra[2] !== dstToken || extra[4] !== EVM_TIMELOCKS) {
1404
- return "the signed HTLC terms name another destination chain or token, or other timelocks";
1485
+ return said("the signed HTLC terms name another destination chain or token, or other timelocks", "policy_refused");
1405
1486
  }
1406
1487
  try {
1407
1488
  await checkOrderCustomData(customData, { evmDest: !tonDest, outputToken, traderDst: params.destAddress, quoteId: intent.quoteId });
1408
1489
  } catch (e) {
1409
- return e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e);
1490
+ return said(e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e), (0, import_errors.refusalOf)(e) ?? "policy_refused");
1410
1491
  }
1411
1492
  return null;
1412
1493
  }
@@ -1417,22 +1498,22 @@ function omnistonVerifyBuild(hooks) {
1417
1498
  };
1418
1499
  return async (quote, tx, params) => {
1419
1500
  if (tx.typedData !== void 0) {
1420
- if (!params) return "the request this quote answered is unknown";
1501
+ if (!params) return said("the request this quote answered is unknown", "policy_refused");
1421
1502
  try {
1422
1503
  return await checkRemoteEvmOrder(quote, tx, params, trusted);
1423
1504
  } catch {
1424
- return "the typed order could not be checked";
1505
+ return said("the typed order could not be checked", "policy_refused");
1425
1506
  }
1426
1507
  }
1427
1508
  if (tx.bodyBoc === void 0) return null;
1428
- if (!params) return "the request this quote answered is unknown";
1509
+ if (!params) return said("the request this quote answered is unknown", "policy_refused");
1429
1510
  if (quote.providerData?.settlement !== "swap") {
1430
1511
  if (params.sourceChain.startsWith("ton:") && params.destChain.startsWith("eip155:")) {
1431
- if (!hooks.verifyTonEscrowBuild) return "a TON \u2192 EVM escrow body needs verifyTonEscrowBuild (createOmnistonTonValidators)";
1432
- if (!params.refundAddress) return "the trader's TON address (refundAddress) is unknown";
1512
+ if (!hooks.verifyTonEscrowBuild) return said("a TON \u2192 EVM escrow body needs verifyTonEscrowBuild (createOmnistonTonValidators)", "unsupported_pair");
1513
+ if (!params.refundAddress) return said("the trader's TON address (refundAddress) is unknown", "recipient_unverifiable");
1433
1514
  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);
1434
1515
  try {
1435
- return await hooks.verifyTonEscrowBuild({
1516
+ const text = await hooks.verifyTonEscrowBuild({
1436
1517
  payloadBocHex: import_buffer2.Buffer.from(tx.bodyBoc, "base64").toString("hex"),
1437
1518
  ownerAddress: params.refundAddress,
1438
1519
  inputUnits: quote.sourceAmount,
@@ -1441,22 +1522,23 @@ function omnistonVerifyBuild(hooks) {
1441
1522
  outputTokenAddress: params.destToken,
1442
1523
  minResolverSendsUnits: floor.toString()
1443
1524
  });
1525
+ return text ? said(text, tonEscrowBuildReason(text)) : text;
1444
1526
  } catch {
1445
- return "the escrow body could not be checked";
1527
+ return said("the escrow body could not be checked", "policy_refused");
1446
1528
  }
1447
1529
  }
1448
- return "only TON swap and TON \u2192 EVM escrow bodies can be checked on the client";
1530
+ return said("only TON swap and TON \u2192 EVM escrow bodies can be checked on the client", "unsupported_pair");
1449
1531
  }
1450
1532
  let steps;
1451
1533
  try {
1452
1534
  steps = JSON.parse(quote.providerData.routeSteps ?? "");
1453
1535
  } catch {
1454
- return "the quote carries no route steps";
1536
+ return said("the quote carries no route steps", "policy_refused");
1455
1537
  }
1456
- if (!Array.isArray(steps) || steps.length === 0) return "the quote carries no route steps";
1538
+ if (!Array.isArray(steps) || steps.length === 0) return said("the quote carries no route steps", "policy_refused");
1457
1539
  const source = (0, import_swap2_assets.ccAsset)(params.sourceChain, params.sourceToken);
1458
1540
  const dest = (0, import_swap2_assets.ccAssetByToken)(params.destChain, params.destToken);
1459
- if (!source || !dest) return "unknown source or destination token";
1541
+ if (!source || !dest) return said("unknown source or destination token", "unsupported_pair");
1460
1542
  const protocols = (quote.providerData.protocols ?? "").split(",").filter(Boolean);
1461
1543
  let checked;
1462
1544
  try {
@@ -1474,17 +1556,17 @@ function omnistonVerifyBuild(hooks) {
1474
1556
  integratorAddress: hooks.integratorAddress ?? null
1475
1557
  });
1476
1558
  } catch (e) {
1477
- return e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e);
1559
+ return said(e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e), e instanceof TonSwapPayloadError ? tonSwapPayloadReason(e.reason) : "policy_refused");
1478
1560
  }
1479
1561
  const payload = parseSwapPayload(import_buffer2.Buffer.from(tx.bodyBoc, "base64").toString("hex"));
1480
1562
  const target = source.contract ? payload.jettonTransfer?.destination : tx.to;
1481
- if (!target) return "the body names no swap destination";
1563
+ if (!target) return said("the body names no swap destination", "policy_refused");
1482
1564
  try {
1483
- if (!await hooks.validateTonSwapTarget({ address: target, protocols })) return `${target} is not a recognized DEX contract for ${protocols.join("+")}`;
1565
+ if (!await hooks.validateTonSwapTarget({ address: target, protocols })) return said(`${target} is not a recognized DEX contract for ${protocols.join("+")}`, "vault_mismatch");
1484
1566
  } catch {
1485
- return "the swap target could not be checked";
1567
+ return said("the swap target could not be checked", "policy_refused");
1486
1568
  }
1487
- if (!hooks.validateTonSwapLeg) return "a TON swap body needs validateTonSwapLeg (createOmnistonTonValidators)";
1569
+ if (!hooks.validateTonSwapLeg) return said("a TON swap body needs validateTonSwapLeg (createOmnistonTonValidators)", "unsupported_pair");
1488
1570
  for (const leg of checked.legs) {
1489
1571
  let ok = false;
1490
1572
  try {
@@ -1492,7 +1574,7 @@ function omnistonVerifyBuild(hooks) {
1492
1574
  } catch {
1493
1575
  ok = false;
1494
1576
  }
1495
- if (!ok) return `${leg.kind} v${leg.version} leg not verified`;
1577
+ if (!ok) return said(`${leg.kind} v${leg.version} leg not verified`, "vault_mismatch");
1496
1578
  }
1497
1579
  return null;
1498
1580
  };
@@ -1676,19 +1758,19 @@ function omnistonProvider(opts = {}) {
1676
1758
  if (!bound.ok) {
1677
1759
  if (bound.reason === "legacy-expired") {
1678
1760
  trace("legacy-intent-expired", { where });
1679
- throw new Error("omniston: quote expired \u2014 request a fresh quote");
1761
+ throw (0, import_errors.refusal)("quote_expired", "omniston: quote expired \u2014 request a fresh quote");
1680
1762
  }
1681
1763
  trace("intent-mismatch", { where, field: bound.field });
1682
- throw new Error(`omniston: intent id does not match the quote it was issued for (${bound.field})`);
1764
+ throw (0, import_errors.refusal)(mismatchReason(bound.field), `omniston: intent id does not match the quote it was issued for (${bound.field})`);
1683
1765
  }
1684
1766
  if (bound.legacy) trace("legacy-intent", { where });
1685
1767
  if (!decoded.digest) {
1686
1768
  trace("intent-mismatch", { where, field: "digest-missing" });
1687
- throw new Error("omniston: this quote was issued before an update \u2014 request a fresh quote");
1769
+ throw (0, import_errors.refusal)("quote_expired", "omniston: this quote was issued before an update \u2014 request a fresh quote");
1688
1770
  }
1689
1771
  if (decoded.digest !== quoteDigest(entry.quote)) {
1690
1772
  trace("intent-mismatch", { where, field: "digest" });
1691
- throw new Error("omniston: quote was replaced \u2014 request a fresh quote");
1773
+ throw (0, import_errors.refusal)("policy_refused", "omniston: quote was replaced \u2014 request a fresh quote");
1692
1774
  }
1693
1775
  return bound.ref;
1694
1776
  }
@@ -1722,10 +1804,10 @@ function omnistonProvider(opts = {}) {
1722
1804
  const disclosed = (await store.load(disclosedKey(quoteId)) ?? []).length > 0;
1723
1805
  if (boundTo !== binding || disclosed) {
1724
1806
  traceFor(quoteId)("secret-reuse-refused", { disclosed, bound: boundTo !== void 0 });
1725
- throw new Error("omniston: this quote id already belongs to another order \u2014 request a fresh quote");
1807
+ throw (0, import_errors.refusal)("policy_refused", "omniston: this quote id already belongs to another order \u2014 request a fresh quote");
1726
1808
  }
1727
1809
  if (existing.length !== count || !existing.every((s) => /^[0-9a-fA-F]{64}$/.test(s))) {
1728
- throw new Error("omniston: stored HTLC secrets for this quote do not fit the order \u2014 request a fresh quote");
1810
+ throw (0, import_errors.refusal)("policy_refused", "omniston: stored HTLC secrets for this quote do not fit the order \u2014 request a fresh quote");
1729
1811
  }
1730
1812
  traceFor(quoteId)("secret-reused", { count });
1731
1813
  return { secrets: existing.map(hexToBytes), created: false };
@@ -1784,7 +1866,10 @@ function omnistonProvider(opts = {}) {
1784
1866
  let q = await rfq(slippagePips);
1785
1867
  if (!q) {
1786
1868
  if (!signal?.aborted) {
1787
- params.onRefusal?.(`omniston: ${outcome.error ? `RFQ failed: ${outcome.error}` : outcome.rejected ? `swap quote rejected: ${outcome.rejected}` : "no TON DEX route quoted this pair/amount"}`);
1869
+ const text = `omniston: ${outcome.error ? `RFQ failed: ${outcome.error}` : outcome.rejected ? `swap quote rejected: ${outcome.rejected}` : "no TON DEX route quoted this pair/amount"}`;
1870
+ const reason = rfqReason(outcome);
1871
+ if (reason) params.onRefusal?.(text, { reason });
1872
+ else params.onRefusal?.(text);
1788
1873
  }
1789
1874
  return null;
1790
1875
  }
@@ -1850,27 +1935,32 @@ function omnistonProvider(opts = {}) {
1850
1935
  async function buildSwapDeposit(ref, q, swap) {
1851
1936
  if (!ref.trader) throw new Error("omniston: quote was requested without a refund/source address");
1852
1937
  if (!isTon(ref.srcChain) || q.inputAsset.chain.$case !== "ton" || q.outputAsset.chain.$case !== "ton") {
1853
- throw new Error("omniston: swap quote is not a TON same-chain swap");
1938
+ throw (0, import_errors.refusal)("policy_refused", "omniston: swap quote is not a TON same-chain swap");
1854
1939
  }
1855
- if (!sameChainTonReady()) throw new Error("omniston: TON same-chain swaps need independent jetton-wallet, swap-target and swap-leg validators");
1940
+ if (!sameChainTonReady()) throw (0, import_errors.refusal)("unsupported_pair", "omniston: TON same-chain swaps need independent jetton-wallet, swap-target and swap-leg validators");
1856
1941
  const owner = { chain: { $case: "ton", value: ref.trader } };
1857
1942
  const receiver = { chain: { $case: "ton", value: ref.dest } };
1858
1943
  const omni = await getClient();
1859
- if (typeof omni.tonBuildSwap !== "function") throw new Error("omniston: client does not support swap settlement (tonBuildSwap)");
1860
- const tx = await omni.tonBuildSwap({
1861
- quoteId: ref.quoteId,
1862
- transferSrcAddress: owner,
1863
- refundSrcAddress: owner,
1864
- gasExcessAddress: owner,
1865
- traderDstAddress: receiver,
1866
- // Build at the slippage the quote was requested with: its
1867
- // minOutputAmount is the minimum this module reported to the router.
1868
- useRecommendedSlippage: false
1869
- });
1944
+ if (typeof omni.tonBuildSwap !== "function") throw (0, import_errors.refusal)("unsupported_pair", "omniston: client does not support swap settlement (tonBuildSwap)");
1945
+ let tx;
1946
+ try {
1947
+ tx = await omni.tonBuildSwap({
1948
+ quoteId: ref.quoteId,
1949
+ transferSrcAddress: owner,
1950
+ refundSrcAddress: owner,
1951
+ gasExcessAddress: owner,
1952
+ traderDstAddress: receiver,
1953
+ // Build at the slippage the quote was requested with: its
1954
+ // minOutputAmount is the minimum this module reported to the router.
1955
+ useRecommendedSlippage: false
1956
+ });
1957
+ } catch (error) {
1958
+ throw venueFailure(error);
1959
+ }
1870
1960
  const [message, ...rest] = tx.messages;
1871
- if (!message || rest.length > 0) throw new Error(`omniston: expected exactly one swap message, got ${tx.messages.length}`);
1872
- if (message.jettonWalletStateInit) throw new Error("omniston: deploying a resolver-provided jetton wallet is not supported");
1873
- if (!isPositiveInteger(message.sendAmount)) throw new Error("omniston: malformed swap message value");
1961
+ if (!message || rest.length > 0) throw (0, import_errors.refusal)("policy_refused", `omniston: expected exactly one swap message, got ${tx.messages.length}`);
1962
+ if (message.jettonWalletStateInit) throw (0, import_errors.refusal)("policy_refused", "omniston: deploying a resolver-provided jetton wallet is not supported");
1963
+ if (!isPositiveInteger(message.sendAmount)) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed swap message value");
1874
1964
  const sendAmount = BigInt(message.sendAmount);
1875
1965
  const gasCap = BigInt(maxTonExecutionNano);
1876
1966
  const gasBudget = q.gasBudget && isPositiveInteger(q.gasBudget) ? BigInt(q.gasBudget) : gasCap;
@@ -1882,53 +1972,53 @@ function omnistonProvider(opts = {}) {
1882
1972
  const ptonV1Native = !inputJetton && !!payload.jettonTransfer && singleV1;
1883
1973
  if (inputJetton) {
1884
1974
  const transfer = payload.jettonTransfer;
1885
- if (!transfer) throw new Error("omniston: jetton-input swap message is not a jetton transfer");
1886
- 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("+")})`);
1887
- if (transfer.amount !== BigInt(q.inputUnits)) throw new Error("omniston: swap message moves a different amount than quoted");
1975
+ if (!transfer) throw (0, import_errors.refusal)("policy_refused", "omniston: jetton-input swap message is not a jetton transfer");
1976
+ if (sendAmount > gasCap || sendAmount > gasBudget) throw (0, import_errors.refusal)("policy_refused", `omniston: swap message attaches more TON than the quoted gas budget (${sendAmount} > ${gasBudget} nanoTON, cap ${gasCap}, ${protocols.join("+")})`);
1977
+ if (transfer.amount !== BigInt(q.inputUnits)) throw (0, import_errors.refusal)("policy_refused", "omniston: swap message moves a different amount than quoted");
1888
1978
  if (!transfer.responseDestination || !sameTonAddress(transfer.responseDestination, ref.trader)) {
1889
- throw new Error("omniston: swap message returns excess to a wallet other than the trader");
1979
+ throw (0, import_errors.refusal)("recipient_mismatch", "omniston: swap message returns excess to a wallet other than the trader");
1890
1980
  }
1891
- if (transfer.forwardTon >= sendAmount) throw new Error("omniston: swap message forwards more TON than it carries");
1981
+ if (transfer.forwardTon >= sendAmount) throw (0, import_errors.refusal)("policy_refused", "omniston: swap message forwards more TON than it carries");
1892
1982
  const walletValid = await opts.validateTonJettonWallet({
1893
1983
  ownerAddress: ref.trader,
1894
1984
  jettonMasterAddress: inputJetton,
1895
1985
  walletAddress: message.targetAddress
1896
1986
  });
1897
- if (!walletValid) throw new Error("omniston: TON jetton wallet was not independently validated");
1987
+ if (!walletValid) throw (0, import_errors.refusal)("vault_mismatch", "omniston: TON jetton wallet was not independently validated");
1898
1988
  const targetValid = await opts.validateTonSwapTarget({ address: transfer.destination, protocols });
1899
- if (!targetValid) throw new Error("omniston: swap destination is not a recognized DEX contract for the quoted protocol");
1989
+ if (!targetValid) throw (0, import_errors.refusal)("vault_mismatch", "omniston: swap destination is not a recognized DEX contract for the quoted protocol");
1900
1990
  } else if (ptonV1Native) {
1901
1991
  const transfer = payload.jettonTransfer;
1902
1992
  const input = BigInt(q.inputUnits);
1903
- if (transfer.amount !== input) throw new Error("omniston: swap message moves a different amount than quoted");
1904
- if (sendAmount < input) throw new Error("omniston: swap message carries less than the quoted input");
1993
+ if (transfer.amount !== input) throw (0, import_errors.refusal)("policy_refused", "omniston: swap message moves a different amount than quoted");
1994
+ if (sendAmount < input) throw (0, import_errors.refusal)("policy_refused", "omniston: swap message carries less than the quoted input");
1905
1995
  const gas = sendAmount - input;
1906
- 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("+")})`);
1907
- if (transfer.forwardTon > gas) throw new Error("omniston: swap message forwards more TON than its gas");
1996
+ if (gas > gasCap || gas > gasBudget) throw (0, import_errors.refusal)("policy_refused", `omniston: swap message attaches more TON than the quoted gas budget (${gas} > ${gasBudget} nanoTON, cap ${gasCap}, ${protocols.join("+")})`);
1997
+ if (transfer.forwardTon > gas) throw (0, import_errors.refusal)("policy_refused", "omniston: swap message forwards more TON than its gas");
1908
1998
  if (!transfer.responseDestination || !sameTonAddress(transfer.responseDestination, ref.trader)) {
1909
- throw new Error("omniston: swap message returns excess to a wallet other than the trader");
1999
+ throw (0, import_errors.refusal)("recipient_mismatch", "omniston: swap message returns excess to a wallet other than the trader");
1910
2000
  }
1911
- if (typeof opts.validateTonSwapLeg !== "function") throw new Error("omniston: STON.fi v1 native input needs the validateTonSwapLeg validator (pTON v1 wallet ownership)");
2001
+ if (typeof opts.validateTonSwapLeg !== "function") throw (0, import_errors.refusal)("unsupported_pair", "omniston: STON.fi v1 native input needs the validateTonSwapLeg validator (pTON v1 wallet ownership)");
1912
2002
  const targetValid = await opts.validateTonSwapTarget({ address: message.targetAddress, protocols });
1913
- if (!targetValid) throw new Error("omniston: swap target is not a recognized DEX contract for the quoted protocol");
2003
+ if (!targetValid) throw (0, import_errors.refusal)("vault_mismatch", "omniston: swap target is not a recognized DEX contract for the quoted protocol");
1914
2004
  const routerValid = await opts.validateTonSwapTarget({ address: transfer.destination, protocols });
1915
- if (!routerValid) throw new Error("omniston: swap destination is not a recognized DEX contract for the quoted protocol");
2005
+ if (!routerValid) throw (0, import_errors.refusal)("vault_mismatch", "omniston: swap destination is not a recognized DEX contract for the quoted protocol");
1916
2006
  } else {
1917
2007
  if (payload.jettonTransfer) {
1918
- 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})`);
2008
+ throw (0, import_errors.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})`);
1919
2009
  }
1920
2010
  const input = BigInt(q.inputUnits);
1921
- if (sendAmount < input) throw new Error("omniston: swap message carries less than the quoted input");
2011
+ if (sendAmount < input) throw (0, import_errors.refusal)("policy_refused", "omniston: swap message carries less than the quoted input");
1922
2012
  const gas = sendAmount - input;
1923
- 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("+")})`);
2013
+ if (gas > gasCap || gas > gasBudget) throw (0, import_errors.refusal)("policy_refused", `omniston: swap message attaches more TON than the quoted gas budget (${gas} > ${gasBudget} nanoTON, cap ${gasCap}, ${protocols.join("+")})`);
1924
2014
  const targetValid = await opts.validateTonSwapTarget({ address: message.targetAddress, protocols });
1925
- if (!targetValid) throw new Error("omniston: swap target is not a recognized DEX contract for the quoted protocol");
2015
+ if (!targetValid) throw (0, import_errors.refusal)("vault_mismatch", "omniston: swap target is not a recognized DEX contract for the quoted protocol");
1926
2016
  }
1927
2017
  const mode = ptonV1Native ? "enforce" : opts.tonSwapPayloadCheck ?? "enforce";
1928
2018
  const trace = traceFor(ref.quoteId);
1929
2019
  const payloadRefusal = (reason, detail) => {
1930
2020
  trace("ton-swap-payload", { reason, detail, mode, protocols: protocols.join("+") });
1931
- if (mode === "enforce") throw new Error(`omniston: ton-swap-payload ${reason} \u2014 ${detail}`);
2021
+ if (mode === "enforce") throw (0, import_errors.refusal)(tonSwapPayloadReason(reason), `omniston: ton-swap-payload ${reason} \u2014 ${detail}`);
1932
2022
  };
1933
2023
  let checked = null;
1934
2024
  try {
@@ -2002,33 +2092,33 @@ function omnistonProvider(opts = {}) {
2002
2092
  const srcChain = ref.srcChain;
2003
2093
  const chainId = evmRef2(srcChain);
2004
2094
  const srcCase = q.inputAsset.chain.$case;
2005
- if (chainId === null || EVM_CASE[chainId] !== srcCase) throw new Error("omniston: source chain does not match the EVM quote");
2095
+ if (chainId === null || EVM_CASE[chainId] !== srcCase) throw (0, import_errors.refusal)("policy_refused", "omniston: source chain does not match the EVM quote");
2006
2096
  const destCase = q.outputAsset.chain.$case;
2007
2097
  const destChain = caipForChainCase(destCase);
2008
2098
  const tonDest = destCase === "ton";
2009
- if (!destChain || destChain === srcChain) throw new Error("omniston: EVM-source order has no supported destination chain");
2099
+ if (!destChain || destChain === srcChain) throw (0, import_errors.refusal)("unsupported_pair", "omniston: EVM-source order has no supported destination chain");
2010
2100
  if (!trustedOrderProtocols.has(srcChain) || typeof opts.validateEvmInputPosition !== "function" || typeof (tonDest ? opts.validateTonOutputPosition : opts.validateEvmOutputPosition) !== "function") {
2011
- throw new Error(`omniston: EVM\u2192${tonDest ? "TON" : "EVM"} needs a trusted order protocol and both position validators`);
2101
+ throw (0, import_errors.refusal)("unsupported_pair", `omniston: EVM\u2192${tonDest ? "TON" : "EVM"} needs a trusted order protocol and both position validators`);
2012
2102
  }
2013
2103
  let owner;
2014
2104
  try {
2015
2105
  owner = (0, import_viem.getAddress)(ref.trader);
2016
2106
  } catch {
2017
- throw new Error("omniston: trader is not an EVM address");
2107
+ throw (0, import_errors.refusal)("recipient_mismatch", "omniston: trader is not an EVM address");
2018
2108
  }
2019
2109
  let dest;
2020
2110
  if (tonDest) {
2021
2111
  try {
2022
2112
  import_core3.Address.parse(ref.dest);
2023
2113
  } catch {
2024
- throw new Error("omniston: destination is not a TON address");
2114
+ throw (0, import_errors.refusal)("recipient_mismatch", "omniston: destination is not a TON address");
2025
2115
  }
2026
2116
  dest = ref.dest;
2027
2117
  } else {
2028
2118
  try {
2029
2119
  dest = (0, import_viem.getAddress)(ref.dest);
2030
2120
  } catch {
2031
- throw new Error("omniston: destination is not an EVM address");
2121
+ throw (0, import_errors.refusal)("recipient_mismatch", "omniston: destination is not an EVM address");
2032
2122
  }
2033
2123
  }
2034
2124
  const traderDst = { chain: { $case: destCase, value: dest } };
@@ -2039,9 +2129,9 @@ function omnistonProvider(opts = {}) {
2039
2129
  const outputToken = assetContract(q.outputAsset);
2040
2130
  const destChainId = tonDest ? null : evmRef2(destChain);
2041
2131
  if (factory?.chain.$case === destCase && !isTrusted(destChain, factory.chain.value)) {
2042
- throw new Error(`omniston: untrusted ${destChain} settlement contract ${factory.chain.value}`);
2132
+ throw (0, import_errors.refusal)("vault_mismatch", `omniston: untrusted ${destChain} settlement contract ${factory.chain.value}`);
2043
2133
  }
2044
- 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");
2134
+ 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 (0, import_errors.refusal)("policy_refused", "omniston: incomplete EVM order settlement binding in quote");
2045
2135
  const { secrets, created } = await secretsFor(ref, q, 1);
2046
2136
  const hashes = secrets.map((secret) => hashlock(secret, settlement.htlcHashingFunction));
2047
2137
  const forget = async () => {
@@ -2058,14 +2148,14 @@ function omnistonProvider(opts = {}) {
2058
2148
  });
2059
2149
  } catch (error) {
2060
2150
  await forget();
2061
- throw error;
2151
+ throw venueFailure(error);
2062
2152
  }
2063
2153
  let td;
2064
2154
  try {
2065
2155
  try {
2066
2156
  td = JSON.parse(payload.typedData);
2067
2157
  } catch {
2068
- throw new Error("omniston: malformed EVM order typed data");
2158
+ throw (0, import_errors.refusal)("policy_refused", "omniston: malformed EVM order typed data");
2069
2159
  }
2070
2160
  validateSerializedOrderDetails(payload.serializedOrderDetails, {
2071
2161
  quoteId: ref.quoteId,
@@ -2148,45 +2238,46 @@ function omnistonProvider(opts = {}) {
2148
2238
  async getQuote(params, signal) {
2149
2239
  if (params.flex) return null;
2150
2240
  if (isTon(params.sourceChain) && isTon(params.destChain)) return await getSwapQuote(params, signal);
2151
- const refuse2 = (reason) => {
2152
- params.onRefusal?.(`omniston: ${reason}`);
2241
+ const refuse2 = (reason, why) => {
2242
+ if (why) params.onRefusal?.(`omniston: ${reason}`, { reason: why });
2243
+ else params.onRefusal?.(`omniston: ${reason}`);
2153
2244
  return null;
2154
2245
  };
2155
2246
  const evmSource = !isTon(params.sourceChain);
2156
2247
  const tonDest = isTon(params.destChain);
2157
- if (!omniston.supports(params.sourceChain, params.destChain)) return refuse2("route is not an Omniston order corridor");
2248
+ if (!omniston.supports(params.sourceChain, params.destChain)) return refuse2("route is not an Omniston order corridor", "unsupported_pair");
2158
2249
  if (!trustedContracts.has(params.sourceChain) || !trustedContracts.has(params.destChain)) {
2159
- return refuse2(`no trusted settlement contract configured for ${trustedContracts.has(params.sourceChain) ? params.destChain : params.sourceChain}`);
2250
+ return refuse2(`no trusted settlement contract configured for ${trustedContracts.has(params.sourceChain) ? params.destChain : params.sourceChain}`, "unsupported_pair");
2160
2251
  }
2161
2252
  if (evmSource) {
2162
- if (!trustedOrderProtocols.has(params.sourceChain)) return refuse2(`no trusted EVM order protocol configured for ${params.sourceChain}`);
2253
+ if (!trustedOrderProtocols.has(params.sourceChain)) return refuse2(`no trusted EVM order protocol configured for ${params.sourceChain}`, "unsupported_pair");
2163
2254
  if (typeof opts.validateEvmInputPosition !== "function" || typeof (tonDest ? opts.validateTonOutputPosition : opts.validateEvmOutputPosition) !== "function") {
2164
- 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");
2255
+ 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");
2165
2256
  }
2166
2257
  if (params.dry === false) {
2167
2258
  if (tonDest) {
2168
2259
  try {
2169
2260
  import_core3.Address.parse(params.destAddress);
2170
2261
  } catch {
2171
- return refuse2("destination is not a TON address");
2262
+ return refuse2("destination is not a TON address", "recipient_mismatch");
2172
2263
  }
2173
- } else if (!validEvmAddress(params.destAddress)) return refuse2("destination is not an EVM address");
2264
+ } else if (!validEvmAddress(params.destAddress)) return refuse2("destination is not an EVM address", "recipient_mismatch");
2174
2265
  }
2175
- } 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");
2266
+ } 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");
2176
2267
  const source = (0, import_swap2_assets.ccAsset)(params.sourceChain, params.sourceToken);
2177
2268
  const dest = (0, import_swap2_assets.ccAssetByToken)(params.destChain, params.destToken);
2178
- if (!source || !dest) return refuse2("asset is not in the catalog");
2269
+ if (!source || !dest) return refuse2("asset is not in the catalog", "unsupported_pair");
2179
2270
  const inputAsset = toAssetId(params.sourceChain, source);
2180
2271
  const outputAsset = toAssetId(params.destChain, dest);
2181
2272
  if (!inputAsset || !outputAsset) {
2182
- return refuse2(`cross-chain orders are token corridors only (no ${!inputAsset ? source.symbol : dest.symbol} native leg)`);
2273
+ return refuse2(`cross-chain orders are token corridors only (no ${!inputAsset ? source.symbol : dest.symbol} native leg)`, "unsupported_pair");
2183
2274
  }
2184
2275
  const maxUnits = decimalToUnits(maxUsdText, source.decimals);
2185
2276
  if (!maxUnits || !isPositiveInteger(params.amountIn)) return refuse2("invalid amount");
2186
- if (BigInt(params.amountIn) > maxUnits) return refuse2(`amount above the ${maxUsdText} USD per-swap cap`);
2277
+ if (BigInt(params.amountIn) > maxUnits) return refuse2(`amount above the ${maxUsdText} USD per-swap cap`, "amount_too_large");
2187
2278
  const trader = params.refundAddress ?? "";
2188
2279
  if (!trader && !params.dry) return refuse2(`execution quote needs the trader's ${evmSource ? "EVM" : "TON"} address (refundAddress)`);
2189
- if (evmSource && trader && !validEvmAddress(trader)) return refuse2("refundAddress is not an EVM address");
2280
+ if (evmSource && trader && !validEvmAddress(trader)) return refuse2("refundAddress is not an EVM address", "recipient_mismatch");
2190
2281
  const request = {
2191
2282
  inputAsset,
2192
2283
  outputAsset,
@@ -2214,27 +2305,28 @@ function omnistonProvider(opts = {}) {
2214
2305
  if (!q) {
2215
2306
  if (signal?.aborted) return null;
2216
2307
  return refuse2(
2217
- rfq.error ? `RFQ failed: ${rfq.error}` : rfq.rejected ? `resolver quote rejected: ${rfq.rejected}` : "no resolver quoted this route/amount"
2308
+ rfq.error ? `RFQ failed: ${rfq.error}` : rfq.rejected ? `resolver quote rejected: ${rfq.rejected}` : "no resolver quoted this route/amount",
2309
+ rfqReason(rfq)
2218
2310
  );
2219
2311
  }
2220
2312
  const orderData = q.settlementData.$case === "order" ? q.settlementData.value : null;
2221
- if (!orderData) return refuse2("resolver answered without order settlement");
2313
+ if (!orderData) return refuse2("resolver answered without order settlement", "upstream_unavailable");
2222
2314
  const srcContract = orderData.srcProtocolContractAddress.chain.value;
2223
2315
  if (evmSource ? !isTrustedOrderProtocol(params.sourceChain, srcContract) : !isTrusted(params.sourceChain, srcContract)) {
2224
- return refuse2(evmSource ? `untrusted ${params.sourceChain} order protocol ${srcContract}` : `untrusted TON settlement contract ${srcContract}`);
2316
+ return refuse2(evmSource ? `untrusted ${params.sourceChain} order protocol ${srcContract}` : `untrusted TON settlement contract ${srcContract}`, "vault_mismatch");
2225
2317
  }
2226
2318
  if (!orderData.dstProtocolContractAddress || !isTrusted(params.destChain, orderData.dstProtocolContractAddress.chain.value)) {
2227
- return refuse2(`untrusted ${params.destChain} settlement contract ${orderData.dstProtocolContractAddress?.chain.value ?? "(none)"}`);
2319
+ return refuse2(`untrusted ${params.destChain} settlement contract ${orderData.dstProtocolContractAddress?.chain.value ?? "(none)"}`, "vault_mismatch");
2228
2320
  }
2229
2321
  const deadlineMs = orderData.tradeStartDeadline * 1e3;
2230
- if (!Number.isFinite(deadlineMs) || deadlineMs <= Date.now()) return refuse2("quote trade-start deadline already passed");
2231
- if (params.dry === false && deadlineMs - Date.now() < MIN_START_HEADROOM_MS) return refuse2("quote trade-start deadline too close to execute");
2322
+ if (!Number.isFinite(deadlineMs) || deadlineMs <= Date.now()) return refuse2("quote trade-start deadline already passed", "quote_expired");
2323
+ if (params.dry === false && deadlineMs - Date.now() < MIN_START_HEADROOM_MS) return refuse2("quote trade-start deadline too close to execute", "quote_expired");
2232
2324
  const inputScaled = BigInt(q.inputUnits) * 10n ** BigInt(dest.decimals);
2233
2325
  const outputScaled = BigInt(q.outputUnits) * 10n ** BigInt(source.decimals);
2234
- if (outputScaled * 2n < inputScaled || outputScaled > inputScaled * 2n) return refuse2("quoted output is outside 0.5x\u20132x of the input");
2235
- if (await quoteIdInUse(q.quoteId)) return refuse2("the platform reused a quote id that already belongs to an order");
2326
+ if (outputScaled * 2n < inputScaled || outputScaled > inputScaled * 2n) return refuse2("quoted output is outside 0.5x\u20132x of the input", "price_check_failed");
2327
+ if (await quoteIdInUse(q.quoteId)) return refuse2("the platform reused a quote id that already belongs to an order", "policy_refused");
2236
2328
  if (!await claimBuilt(q.quoteId, { quote: q, at: Date.now(), bound: { trader, dest: params.destAddress, srcChain: params.sourceChain, kind: "order" } })) {
2237
- return refuse2("the platform reused a quote id that already holds another quote");
2329
+ return refuse2("the platform reused a quote id that already holds another quote", "policy_refused");
2238
2330
  }
2239
2331
  const feeUnits = Number(q.protocolFeeUnits || "0") + Number(q.integratorFeeUnits || "0");
2240
2332
  const feeUsd = feeUnits / 10 ** dest.decimals;
@@ -2265,29 +2357,29 @@ function omnistonProvider(opts = {}) {
2265
2357
  */
2266
2358
  async buildDeposit(quote) {
2267
2359
  if (!Number.isFinite(quote.validUntilMs) || quote.validUntilMs <= Date.now()) {
2268
- throw new Error("omniston: quote expired \u2014 request a fresh quote");
2360
+ throw (0, import_errors.refusal)("quote_expired", "omniston: quote expired \u2014 request a fresh quote");
2269
2361
  }
2270
2362
  const decoded = decodeIntent(quote.intentId);
2271
2363
  if (!decoded) throw new Error("omniston: malformed intent id");
2272
2364
  const cached = await loadBuilt(decoded.quoteId);
2273
- if (!cached) throw new Error("omniston: quote expired \u2014 request a fresh quote");
2365
+ if (!cached) throw (0, import_errors.refusal)("quote_expired", "omniston: quote expired \u2014 request a fresh quote");
2274
2366
  const ref = requireBinding(decoded, cached, "buildDeposit");
2275
2367
  const q = cached.quote;
2276
2368
  if (q.settlementData.$case === "swap") return await buildSwapDeposit(ref, q, q.settlementData.value);
2277
2369
  if (q.settlementData.$case !== "order" || q.settlementData.value.tradeStartDeadline * 1e3 <= Date.now()) {
2278
- throw new Error("omniston: quote trade deadline expired \u2014 request a fresh quote");
2370
+ throw (0, import_errors.refusal)("quote_expired", "omniston: quote trade deadline expired \u2014 request a fresh quote");
2279
2371
  }
2280
2372
  if (!ref.trader) throw new Error("omniston: quote was requested without a refund/source address");
2281
2373
  const srcCase = q.inputAsset.chain.$case;
2282
2374
  if (srcCase !== "ton") return await buildEvmOrder(ref, q, q.settlementData.value, cached.bound);
2283
2375
  const srcChain = ref.srcChain;
2284
- if (!isTon(srcChain)) throw new Error("omniston: source chain does not match TON quote");
2376
+ if (!isTon(srcChain)) throw (0, import_errors.refusal)("policy_refused", "omniston: source chain does not match TON quote");
2285
2377
  const destCase = q.outputAsset.chain.$case;
2286
2378
  const traderDst = destCase === "ton" ? null : { chain: { $case: destCase, value: (0, import_viem.getAddress)(ref.dest) } };
2287
- if (!traderDst) throw new Error("omniston: TON-destination orders are not supported");
2379
+ if (!traderDst) throw (0, import_errors.refusal)("unsupported_pair", "omniston: TON-destination orders are not supported");
2288
2380
  const minter = q.settlementData.value.srcProtocolContractAddress;
2289
2381
  if (minter.chain.$case !== "ton" || !isTrusted(srcChain, minter.chain.value)) {
2290
- throw new Error(`omniston: untrusted TON settlement contract ${minter.chain.value}`);
2382
+ throw (0, import_errors.refusal)("vault_mismatch", `omniston: untrusted TON settlement contract ${minter.chain.value}`);
2291
2383
  }
2292
2384
  const settlement = q.settlementData;
2293
2385
  const hashingFunction = settlement.$case === "order" ? settlement.value.htlcHashingFunction : void 0;
@@ -2311,27 +2403,27 @@ function omnistonProvider(opts = {}) {
2311
2403
  });
2312
2404
  } catch (error) {
2313
2405
  await forget();
2314
- throw error;
2406
+ throw venueFailure(error);
2315
2407
  }
2316
2408
  const [message, ...rest] = tx.messages;
2317
2409
  if (!message || rest.length > 0) {
2318
2410
  await forget();
2319
- throw new Error(`omniston: expected exactly one escrow message, got ${tx.messages.length}`);
2411
+ throw (0, import_errors.refusal)("policy_refused", `omniston: expected exactly one escrow message, got ${tx.messages.length}`);
2320
2412
  }
2321
2413
  if (!isPositiveInteger(message.sendAmount) || BigInt(message.sendAmount) > BigInt(maxTonExecutionNano)) {
2322
2414
  await forget();
2323
- throw new Error("omniston: resolver TON execution value exceeds the configured safety cap");
2415
+ throw (0, import_errors.refusal)("policy_refused", "omniston: resolver TON execution value exceeds the configured safety cap");
2324
2416
  }
2325
2417
  const expectedProtocol = q.settlementData.value.srcProtocolContractAddress;
2326
2418
  const initialResolver = q.settlementData.value.srcInitialResolverAddress;
2327
2419
  const jettonMaster = assetContract(q.inputAsset);
2328
2420
  if (expectedProtocol.chain.$case !== "ton" || initialResolver?.chain.$case !== "ton" || !jettonMaster) {
2329
2421
  await forget();
2330
- throw new Error("omniston: incomplete TON settlement binding in quote");
2422
+ throw (0, import_errors.refusal)("policy_refused", "omniston: incomplete TON settlement binding in quote");
2331
2423
  }
2332
2424
  if (message.jettonWalletStateInit) {
2333
2425
  await forget();
2334
- throw new Error("omniston: deploying a resolver-provided jetton wallet is not supported");
2426
+ throw (0, import_errors.refusal)("policy_refused", "omniston: deploying a resolver-provided jetton wallet is not supported");
2335
2427
  }
2336
2428
  let walletValid = false;
2337
2429
  try {
@@ -2346,11 +2438,11 @@ function omnistonProvider(opts = {}) {
2346
2438
  }
2347
2439
  if (!walletValid) {
2348
2440
  await forget();
2349
- throw new Error("omniston: TON jetton wallet was not independently validated");
2441
+ throw (0, import_errors.refusal)("vault_mismatch", "omniston: TON jetton wallet was not independently validated");
2350
2442
  }
2351
2443
  try {
2352
2444
  if (!opts.validateTonEscrowPayload) {
2353
- throw new Error("omniston: TON source requires an independent escrow-payload validator");
2445
+ throw (0, import_errors.refusal)("unsupported_pair", "omniston: TON source requires an independent escrow-payload validator");
2354
2446
  }
2355
2447
  await validateTonEscrowPayload(message, {
2356
2448
  quoteId: ref.quoteId,
@@ -2397,7 +2489,7 @@ function omnistonProvider(opts = {}) {
2397
2489
  const result = bindIntent(decoded, stored);
2398
2490
  if (!result.ok && result.reason === "mismatch") {
2399
2491
  trace("intent-mismatch", { where: "trackIntent", field: result.field });
2400
- throw new Error(`omniston: intent id does not match the quote it was issued for (${result.field})`);
2492
+ throw (0, import_errors.refusal)(mismatchReason(result.field), `omniston: intent id does not match the quote it was issued for (${result.field})`);
2401
2493
  }
2402
2494
  if (result.ok) {
2403
2495
  if (result.legacy) trace("legacy-intent", { where: "trackIntent" });
@@ -2577,12 +2669,12 @@ function omnistonProvider(opts = {}) {
2577
2669
  if (!ref) throw new Error("omniston: malformed intent id");
2578
2670
  const cached = await loadBuilt(ref.quoteId);
2579
2671
  const order = cached?.evmOrder;
2580
- if (!order) throw new Error("omniston: no signed-order context for this quote \u2014 build the order again");
2672
+ if (!order) throw (0, import_errors.refusal)("quote_expired", "omniston: no signed-order context for this quote \u2014 build the order again");
2581
2673
  requireBinding(ref, cached, "registerSignedOrder");
2582
2674
  if (!/^0x[0-9a-fA-F]{130}$/.test(signature)) throw new Error("omniston: malformed order signature");
2583
2675
  const td = JSON.parse(order.typedData);
2584
2676
  const omni = await getClient();
2585
- await omni.orderRegisterSignedOrder({
2677
+ const request = {
2586
2678
  quoteId: ref.quoteId,
2587
2679
  ownerSrcAddress: { chain: { $case: order.chainCase, value: order.owner } },
2588
2680
  signedOrder: { order: { $case: "evmV1", value: {
@@ -2591,7 +2683,12 @@ function omnistonProvider(opts = {}) {
2591
2683
  orderExtension: hexToBytes(order.orderExtension)
2592
2684
  } } },
2593
2685
  ...order.serializedOrderDetails ? { serializedOrderDetails: hexToBytes(order.serializedOrderDetails) } : {}
2594
- });
2686
+ };
2687
+ try {
2688
+ await omni.orderRegisterSignedOrder(request);
2689
+ } catch (error) {
2690
+ throw venueFailure(error);
2691
+ }
2595
2692
  },
2596
2693
  close() {
2597
2694
  clientGeneration += 1;