@anywalletkit/protocol-swap2-omniston 0.5.6 → 0.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -511,6 +511,7 @@ var AUDITED_OMNISTON_EVM_ORDER_PROTOCOLS = {
511
511
  };
512
512
 
513
513
  // src/provider.ts
514
+ var import_errors = require("@anywalletkit/protocol-swap2-core/errors");
514
515
  var import_core4 = require("@anywalletkit/core");
515
516
  var EVM_CASE = {
516
517
  1: "ethereum",
@@ -648,6 +649,54 @@ function encodeTypedMessage(td) {
648
649
  (0, import_viem.encodeAbiParameters)([paramFor(td.primaryType)], [td.message])
649
650
  );
650
651
  }
652
+ function venueReason(error) {
653
+ const details = typeof error === "object" && error !== null ? error.details : void 0;
654
+ const info = typeof details === "object" && details !== null ? details.error_info : void 0;
655
+ if (typeof info !== "object" || info === null) return "upstream_unavailable";
656
+ const { reason, metadata } = info;
657
+ const meta = typeof metadata === "object" && metadata !== null ? metadata : {};
658
+ const type = typeof meta.type === "string" ? meta.type : "";
659
+ switch (reason) {
660
+ case "INVALID_ARGUMENT":
661
+ case "INVALID_PARAMETERS":
662
+ return void 0;
663
+ case "NOT_FOUND":
664
+ return typeof meta.object_type === "string" && meta.object_type.toLowerCase() === "quote" ? "quote_expired" : "upstream_unavailable";
665
+ case "UNSUPPORTED":
666
+ return "unsupported_pair";
667
+ case "SWAP_LIMIT_EXCEEDED":
668
+ return "amount_too_large";
669
+ case "QUOTE_VALIDATION_FAILED":
670
+ if (type === "EMULATION_UNSUPPORTED_TOKEN") return "unsupported_pair";
671
+ if (type === "EMULATION_WRONG_OUTPUT_AMOUNT" || type === "EMULATION_REFUND_SLIPPAGE") return "price_check_failed";
672
+ return type.startsWith("EMULATION_") ? "simulation_failed" : "upstream_unavailable";
673
+ default:
674
+ return "upstream_unavailable";
675
+ }
676
+ }
677
+ function venueFailure(error) {
678
+ if (!(error instanceof Error)) return error;
679
+ const reason = venueReason(error);
680
+ return reason ? (0, import_errors.withReason)(error, reason) : error;
681
+ }
682
+ function mismatchReason(field) {
683
+ return field === "trader" || field === "dest" || field === "owner" ? "recipient_mismatch" : "policy_refused";
684
+ }
685
+ function tonSwapPayloadReason(code) {
686
+ switch (code) {
687
+ case "receiver-not-destination":
688
+ case "refund-not-trader":
689
+ case "excess-not-trader":
690
+ return "recipient_mismatch";
691
+ case "min-out-zero":
692
+ case "min-out-below-quote":
693
+ return "price_check_failed";
694
+ case "leg-unverified":
695
+ return "vault_mismatch";
696
+ default:
697
+ return "policy_refused";
698
+ }
699
+ }
651
700
  function generateSecret() {
652
701
  return crypto.getRandomValues(new Uint8Array(32));
653
702
  }
@@ -658,7 +707,7 @@ function hashlock(secret, hashingFunction) {
658
707
  case "HASHING_FUNCTION_SHA256":
659
708
  return (0, import_viem.sha256)(secret, "bytes");
660
709
  default:
661
- throw new Error(`omniston: unsupported HTLC hashing function ${String(hashingFunction)}`);
710
+ throw (0, import_errors.refusal)("policy_refused", `omniston: unsupported HTLC hashing function ${String(hashingFunction)}`);
662
711
  }
663
712
  }
664
713
  function sameAsset(a, b) {
@@ -696,7 +745,7 @@ function stepJetton(asset) {
696
745
  }
697
746
  function readBigEndian(bytes, offset, length) {
698
747
  if (!Number.isInteger(offset) || !Number.isInteger(length) || length < 1 || offset < 0 || offset + length > bytes.length) {
699
- throw new Error("omniston: truncated EVM order extension");
748
+ throw (0, import_errors.refusal)("policy_refused", "omniston: truncated EVM order extension");
700
749
  }
701
750
  let value = 0n;
702
751
  for (let index = offset; index < offset + length; index++) value = value << 8n | BigInt(bytes[index]);
@@ -741,30 +790,30 @@ var EVM_DEST_PROTOCOL_FEE_RECIPIENTS = /* @__PURE__ */ new Set(["0x076268146b4ec
741
790
  var EVM_AUCTION_POINTS = [[800000n, 100n], [700000n, 100n], [600000n, 100n], [500000n, 100n], [400000n, 100n]];
742
791
  var EVM_TIMELOCKS = [1n, 120n, 240n, 300n, 1n, 120n, 180n, 0n].reduce((packed, value, index) => packed | value << BigInt(index * 32), 0n);
743
792
  function readEvmOrderExtension(extension) {
744
- if (extension.length < 32) throw new Error("omniston: malformed EVM order extension");
793
+ if (extension.length < 32) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed EVM order extension");
745
794
  const view = new DataView(extension.buffer, extension.byteOffset, extension.byteLength);
746
795
  const fields = [];
747
796
  let begin = 0;
748
797
  const concatLength = extension.length - 32;
749
798
  for (let index = 0; index < 8; index++) {
750
799
  const end2 = view.getUint32(28 - index * 4, false);
751
- if (end2 < begin || end2 > concatLength) throw new Error("omniston: malformed EVM order extension offsets");
800
+ if (end2 < begin || end2 > concatLength) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed EVM order extension offsets");
752
801
  fields.push(extension.slice(32 + begin, 32 + end2));
753
802
  begin = end2;
754
803
  }
755
804
  for (const index of [0, 1, 4, 5, 6]) {
756
- if (fields[index].length !== 0) throw new Error("omniston: unsupported EVM order extension field");
805
+ if (fields[index].length !== 0) throw (0, import_errors.refusal)("policy_refused", "omniston: unsupported EVM order extension field");
757
806
  }
758
807
  if (!sliceEqual(fields[2], fields[3])) {
759
- throw new Error("omniston: making/taking amount getters use different programs");
808
+ throw (0, import_errors.refusal)("policy_refused", "omniston: making/taking amount getters use different programs");
760
809
  }
761
810
  let target;
762
811
  for (const index of [2, 3, 7]) {
763
812
  const field2 = fields[index];
764
- if (field2.length <= 20) throw new Error("omniston: incomplete EVM order extension program");
813
+ if (field2.length <= 20) throw (0, import_errors.refusal)("policy_refused", "omniston: incomplete EVM order extension program");
765
814
  const address = (0, import_viem.getAddress)(`0x${bytesToHex(field2.slice(0, 20))}`);
766
815
  if (target !== void 0 && (0, import_viem.getAddress)(target) !== address) {
767
- throw new Error("omniston: EVM order extension target is not independently trusted");
816
+ throw (0, import_errors.refusal)("vault_mismatch", "omniston: EVM order extension target is not independently trusted");
768
817
  }
769
818
  target = address;
770
819
  }
@@ -782,7 +831,7 @@ function readEvmOrderExtension(extension) {
782
831
  cursor += 3;
783
832
  const pointCount = (0, import_core4.bigintToNumber)(readBigEndian(field, cursor, 1));
784
833
  cursor += 1;
785
- if (pointCount !== 5) throw new Error("omniston: unsupported EVM auction profile");
834
+ if (pointCount !== 5) throw (0, import_errors.refusal)("policy_refused", "omniston: unsupported EVM auction profile");
786
835
  const points = [];
787
836
  for (let index = 0; index < pointCount; index++) {
788
837
  points.push([readBigEndian(field, cursor, 3), readBigEndian(field, cursor + 3, 2)]);
@@ -799,7 +848,7 @@ function readEvmOrderExtension(extension) {
799
848
  const getterWhitelistCount = (0, import_core4.bigintToNumber)(readBigEndian(field, cursor, 1));
800
849
  cursor += 1;
801
850
  if (getterWhitelistCount !== 1 || cursor + getterWhitelistCount * 10 !== field.length) {
802
- throw new Error("omniston: malformed EVM amount-getter whitelist");
851
+ throw (0, import_errors.refusal)("policy_refused", "omniston: malformed EVM amount-getter whitelist");
803
852
  }
804
853
  const getter = { gasBump, gasPrice, auctionStart, duration, initialRateBump, points, integratorFee, integratorShare, resolverFee, whitelistDiscount, resolver: readBigEndian(field, cursor, 10) };
805
854
  const post = fields[7];
@@ -821,7 +870,7 @@ function readEvmOrderExtension(extension) {
821
870
  const whitelistCount = (0, import_core4.bigintToNumber)(readBigEndian(post, cursor, 1));
822
871
  cursor += 1;
823
872
  if (whitelistCount !== 1 || cursor + whitelistCount * 12 + 160 !== post.length) {
824
- throw new Error("omniston: malformed EVM post-interaction whitelist");
873
+ throw (0, import_errors.refusal)("policy_refused", "omniston: malformed EVM post-interaction whitelist");
825
874
  }
826
875
  const postResolver = readBigEndian(post, cursor, 10);
827
876
  cursor += 10;
@@ -833,7 +882,7 @@ function readEvmOrderExtension(extension) {
833
882
  return value;
834
883
  });
835
884
  const customData = extension.slice(32 + begin);
836
- if (customData.length !== 131) throw new Error("omniston: malformed EVM order extension custom data");
885
+ if (customData.length !== 131) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed EVM order extension custom data");
837
886
  return {
838
887
  target,
839
888
  getter,
@@ -846,29 +895,29 @@ async function validateEvmToTonOrderExtension(extension, expected) {
846
895
  const evmDest = expected.dstChainId !== void 0;
847
896
  if (evmDest && (!Number.isSafeInteger(expected.dstChainId) || expected.dstChainId <= 0)) throw new Error("omniston: invalid EVM destination chain id");
848
897
  const { target, getter, post, extra, customData } = readEvmOrderExtension(extension);
849
- if (!expected.trustedTarget(target)) throw new Error("omniston: EVM order extension target is not independently trusted");
898
+ if (!expected.trustedTarget(target)) throw (0, import_errors.refusal)("vault_mismatch", "omniston: EVM order extension target is not independently trusted");
850
899
  const quoteResolverFee = expected.protocolFeePips % 10 === 0 ? BigInt(expected.protocolFeePips / 10) : -1n;
851
900
  const resolverLow80 = BigInt((0, import_viem.getAddress)(expected.initialResolverAddress)) & (1n << 80n) - 1n;
852
901
  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) {
853
- throw new Error("omniston: EVM amount-getter terms do not match the pinned v1beta8 profile");
902
+ throw (0, import_errors.refusal)("policy_refused", "omniston: EVM amount-getter terms do not match the pinned v1beta8 profile");
854
903
  }
855
904
  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) {
856
- throw new Error("omniston: EVM post-interaction terms do not match the pinned v1beta8 profile");
905
+ throw (0, import_errors.refusal)("policy_refused", "omniston: EVM post-interaction terms do not match the pinned v1beta8 profile");
857
906
  }
858
907
  const hashlock2 = readBigEndian(expected.secretHash, 0, expected.secretHash.length);
859
908
  const dstToken = evmDest ? BigInt((0, import_viem.getAddress)(expected.outputTokenAddress)) : BigInt((0, import_viem.getAddress)(expected.takerAsset));
860
909
  const deposits = BigInt(expected.srcSecurityDepositUnits) << 128n | BigInt(expected.dstSecurityDepositUnits);
861
910
  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) {
862
- throw new Error("omniston: signed EVM HTLC terms do not match the validated order details");
911
+ throw (0, import_errors.refusal)("policy_refused", "omniston: signed EVM HTLC terms do not match the validated order details");
863
912
  }
864
913
  await checkOrderCustomData(customData, { evmDest, outputToken: expected.outputTokenAddress, traderDst: expected.traderDstAddress, quoteId: expected.quoteId });
865
914
  if (expected.tradeStartDeadline < expected.quoteTimestamp) {
866
- throw new Error("omniston: invalid EVM order quote deadline");
915
+ throw (0, import_errors.refusal)("quote_expired", "omniston: invalid EVM order quote deadline");
867
916
  }
868
917
  }
869
918
  async function checkOrderCustomData(customData, expected) {
870
919
  if (expected.evmDest) {
871
- if (!zeroBytes(customData.slice(0, 66))) throw new Error("omniston: unexpected TON addresses in an EVM-destination order");
920
+ if (!zeroBytes(customData.slice(0, 66))) throw (0, import_errors.refusal)("recipient_mismatch", "omniston: unexpected TON addresses in an EVM-destination order");
872
921
  } else {
873
922
  const { Address: Address3 } = await import("@ton/core");
874
923
  const expectedTon = [expected.outputToken, expected.traderDst].map((value) => Address3.parse(value));
@@ -876,16 +925,16 @@ async function checkOrderCustomData(customData, expected) {
876
925
  const offset = index * 33;
877
926
  const workchain = customData[offset] === 255 ? -1 : customData[offset];
878
927
  if (workchain !== expectedTon[index].workChain || !sliceEqual(customData.slice(offset + 1, offset + 33), expectedTon[index].hash)) {
879
- throw new Error("omniston: signed EVM\u2192TON address does not match the validated request");
928
+ throw (0, import_errors.refusal)("recipient_mismatch", "omniston: signed EVM\u2192TON address does not match the validated request");
880
929
  }
881
930
  }
882
931
  }
883
932
  if (!zeroBytes(customData.slice(66, 99))) {
884
- throw new Error("omniston: unexpected private TON disclosure address");
933
+ throw (0, import_errors.refusal)("policy_refused", "omniston: unexpected private TON disclosure address");
885
934
  }
886
935
  const quoteId = expected.quoteId.replace(/^0x/, "");
887
936
  if (!QUOTE_ID.test(quoteId) || !sliceEqual(customData.slice(99), hexToBytes(quoteId.padStart(64, "0")))) {
888
- throw new Error("omniston: EVM order extension custom data is not bound to the quote id");
937
+ throw (0, import_errors.refusal)("policy_refused", "omniston: EVM order extension custom data is not bound to the quote id");
889
938
  }
890
939
  }
891
940
  function validateEvmTypedOrder(td, expected) {
@@ -907,7 +956,7 @@ function validateEvmTypedOrder(td, expected) {
907
956
  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
908
957
  // skew between the quote and the order builder), never later.
909
958
  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)) {
910
- throw new Error("omniston: EVM order fields do not match the validated quote/request");
959
+ throw (0, import_errors.refusal)("policy_refused", "omniston: EVM order fields do not match the validated quote/request");
911
960
  }
912
961
  }
913
962
  async function validateTonEscrowPayload(message, expected, validateForwardPayload) {
@@ -917,9 +966,9 @@ async function validateTonEscrowPayload(message, expected, validateForwardPayloa
917
966
  try {
918
967
  roots = Cell2.fromBoc(import_buffer2.Buffer.from(boc));
919
968
  } catch {
920
- throw new Error("omniston: malformed TON escrow BoC");
969
+ throw (0, import_errors.refusal)("policy_refused", "omniston: malformed TON escrow BoC");
921
970
  }
922
- if (roots.length !== 1) throw new Error("omniston: TON escrow must contain exactly one root cell");
971
+ if (roots.length !== 1) throw (0, import_errors.refusal)("policy_refused", "omniston: TON escrow must contain exactly one root cell");
923
972
  const slice = roots[0].beginParse();
924
973
  const op = slice.loadUint(32);
925
974
  slice.loadUintBig(64);
@@ -930,7 +979,7 @@ async function validateTonEscrowPayload(message, expected, validateForwardPayloa
930
979
  const forwardAmount = slice.loadCoins();
931
980
  const payloadByRef = slice.loadBit();
932
981
  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) {
933
- throw new Error("omniston: TON jetton transfer does not match the validated quote/request");
982
+ throw (0, import_errors.refusal)("policy_refused", "omniston: TON jetton transfer does not match the validated quote/request");
934
983
  }
935
984
  const forwardPayload = slice.loadRef();
936
985
  const verified = await validateForwardPayload({
@@ -950,7 +999,7 @@ async function validateTonEscrowPayload(message, expected, validateForwardPayloa
950
999
  ...expected.hashingFunction ? { hashingFunction: expected.hashingFunction } : {},
951
1000
  exclusivityTimeout: expected.exclusivityTimeout
952
1001
  });
953
- if (!verified) throw new Error("omniston: TON escrow forward payload failed independent semantic validation");
1002
+ if (!verified) throw (0, import_errors.refusal)("policy_refused", "omniston: TON escrow forward payload failed independent semantic validation");
954
1003
  }
955
1004
  function feeToken(asset) {
956
1005
  return asset.assetId || asset.contract || asset.symbol;
@@ -980,24 +1029,24 @@ var ProtoReader = class {
980
1029
  const byte = this.bytes[this.pos++];
981
1030
  out += (byte & 127) * 2 ** shift;
982
1031
  if ((byte & 128) === 0) {
983
- if (!Number.isSafeInteger(out)) throw new Error("omniston: unsafe protobuf varint");
1032
+ if (!Number.isSafeInteger(out)) throw (0, import_errors.refusal)("policy_refused", "omniston: unsafe protobuf varint");
984
1033
  return out;
985
1034
  }
986
1035
  shift += 7;
987
1036
  }
988
- throw new Error("omniston: malformed protobuf varint");
1037
+ throw (0, import_errors.refusal)("policy_refused", "omniston: malformed protobuf varint");
989
1038
  }
990
1039
  field() {
991
1040
  const tag = this.varint();
992
1041
  const number = Math.floor(tag / 8);
993
1042
  const wire = tag & 7;
994
- if (number < 1) throw new Error("omniston: malformed protobuf field");
1043
+ if (number < 1) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed protobuf field");
995
1044
  return { number, wire };
996
1045
  }
997
1046
  lengthDelimited() {
998
1047
  const length = this.varint();
999
1048
  const end2 = this.pos + length;
1000
- if (length < 0 || end2 > this.bytes.length) throw new Error("omniston: truncated protobuf payload");
1049
+ if (length < 0 || end2 > this.bytes.length) throw (0, import_errors.refusal)("policy_refused", "omniston: truncated protobuf payload");
1001
1050
  const value = this.bytes.slice(this.pos, end2);
1002
1051
  this.pos = end2;
1003
1052
  return value;
@@ -1019,10 +1068,10 @@ var ProtoReader = class {
1019
1068
  this.advance(4);
1020
1069
  return;
1021
1070
  }
1022
- throw new Error("omniston: unsupported protobuf wire type");
1071
+ throw (0, import_errors.refusal)("policy_refused", "omniston: unsupported protobuf wire type");
1023
1072
  }
1024
1073
  advance(length) {
1025
- if (this.pos + length > this.bytes.length) throw new Error("omniston: truncated protobuf payload");
1074
+ if (this.pos + length > this.bytes.length) throw (0, import_errors.refusal)("policy_refused", "omniston: truncated protobuf payload");
1026
1075
  this.pos += length;
1027
1076
  }
1028
1077
  };
@@ -1049,10 +1098,10 @@ function decodeChainAddress(bytes) {
1049
1098
  while (!reader.done) {
1050
1099
  const { number, wire } = reader.field();
1051
1100
  const $case = PROTO_CASE_BY_FIELD[number];
1052
- if (!$case || wire !== 2 || address) throw new Error("omniston: malformed chain address in order details");
1101
+ if (!$case || wire !== 2 || address) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed chain address in order details");
1053
1102
  address = { chain: { $case, value: utf8.decode(reader.lengthDelimited()) } };
1054
1103
  }
1055
- if (!address?.chain.value) throw new Error("omniston: empty chain address in order details");
1104
+ if (!address?.chain.value) throw (0, import_errors.refusal)("policy_refused", "omniston: empty chain address in order details");
1056
1105
  return address;
1057
1106
  }
1058
1107
  function decodeAssetId(bytes) {
@@ -1061,27 +1110,27 @@ function decodeAssetId(bytes) {
1061
1110
  while (!reader.done) {
1062
1111
  const { number, wire } = reader.field();
1063
1112
  const $case = PROTO_CASE_BY_FIELD[number];
1064
- if (!$case || wire !== 2 || asset) throw new Error("omniston: malformed asset id in order details");
1113
+ if (!$case || wire !== 2 || asset) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed asset id in order details");
1065
1114
  const kindReader = new ProtoReader(reader.lengthDelimited());
1066
1115
  const kindField = kindReader.field();
1067
1116
  if (kindField.wire !== 2 || kindField.number !== 1 && kindField.number !== 2) {
1068
- throw new Error("omniston: unsupported asset kind in order details");
1117
+ throw (0, import_errors.refusal)("policy_refused", "omniston: unsupported asset kind in order details");
1069
1118
  }
1070
1119
  const kindCase = kindField.number === 1 ? "native" : $case === "ton" ? "jetton" : $case === "tron" ? "trc20" : "erc20";
1071
1120
  const value = kindField.number === 1 ? {} : utf8.decode(kindReader.lengthDelimited());
1072
- if (!kindReader.done || kindField.number === 2 && !value) throw new Error("omniston: malformed asset kind in order details");
1121
+ if (!kindReader.done || kindField.number === 2 && !value) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed asset kind in order details");
1073
1122
  asset = { chain: { $case, value: { kind: { $case: kindCase, value } } } };
1074
1123
  }
1075
- if (!asset) throw new Error("omniston: empty asset id in order details");
1124
+ if (!asset) throw (0, import_errors.refusal)("policy_refused", "omniston: empty asset id in order details");
1076
1125
  return asset;
1077
1126
  }
1078
1127
  function validateSerializedOrderDetails(bytes, expected) {
1079
- if (!bytes?.length) throw new Error("omniston: EVM HTLC order payload has no serialized details");
1128
+ if (!bytes?.length) throw (0, import_errors.refusal)("policy_refused", "omniston: EVM HTLC order payload has no serialized details");
1080
1129
  const wrapper = new ProtoReader(bytes);
1081
1130
  const outer = wrapper.field();
1082
- if (outer.number !== 1 || outer.wire !== 2) throw new Error("omniston: unsupported HTLC order-details version");
1131
+ if (outer.number !== 1 || outer.wire !== 2) throw (0, import_errors.refusal)("policy_refused", "omniston: unsupported HTLC order-details version");
1083
1132
  const detailsBytes = wrapper.lengthDelimited();
1084
- if (!wrapper.done) throw new Error("omniston: ambiguous HTLC order-details wrapper");
1133
+ if (!wrapper.done) throw (0, import_errors.refusal)("policy_refused", "omniston: ambiguous HTLC order-details wrapper");
1085
1134
  const reader = new ProtoReader(detailsBytes);
1086
1135
  let quoteId;
1087
1136
  let outputAsset;
@@ -1089,16 +1138,16 @@ function validateSerializedOrderDetails(bytes, expected) {
1089
1138
  const hashes = [];
1090
1139
  while (!reader.done) {
1091
1140
  const { number, wire } = reader.field();
1092
- if (wire !== 2) throw new Error("omniston: malformed HTLC order details");
1141
+ if (wire !== 2) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed HTLC order details");
1093
1142
  const value = reader.lengthDelimited();
1094
1143
  if (number === 1 && quoteId === void 0) quoteId = utf8.decode(value);
1095
1144
  else if (number === 2 && outputAsset === void 0) outputAsset = decodeAssetId(value);
1096
1145
  else if (number === 3 && traderDstAddress === void 0) traderDstAddress = decodeChainAddress(value);
1097
1146
  else if (number === 5) hashes.push(value);
1098
- else throw new Error("omniston: duplicate or unexpected HTLC order-details field");
1147
+ else throw (0, import_errors.refusal)("policy_refused", "omniston: duplicate or unexpected HTLC order-details field");
1099
1148
  }
1100
1149
  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]))) {
1101
- throw new Error("omniston: resolver order payload does not match the validated quote/request");
1150
+ throw (0, import_errors.refusal)("policy_refused", "omniston: resolver order payload does not match the validated quote/request");
1102
1151
  }
1103
1152
  }
1104
1153
  function isPositiveInteger(value) {
@@ -1129,7 +1178,7 @@ function bytesToHex(bytes) {
1129
1178
  function hexToBytes(hex) {
1130
1179
  const clean = hex.replace(/^0x/, "");
1131
1180
  if (!clean || !/^(?:[0-9a-fA-F]{2})+$/.test(clean)) {
1132
- throw new Error("omniston: malformed hex payload");
1181
+ throw (0, import_errors.refusal)("policy_refused", "omniston: malformed hex payload");
1133
1182
  }
1134
1183
  const out = new Uint8Array(clean.length / 2);
1135
1184
  for (let i = 0; i < out.length; i++) out[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
@@ -1163,6 +1212,14 @@ function swapProtocols(v) {
1163
1212
  for (const route of v.routes) for (const step of route.steps) for (const chunk of step.chunks) names.add(chunk.protocol);
1164
1213
  return [...names];
1165
1214
  }
1215
+ function rejectionReason(rejection) {
1216
+ return rejection === "quote timestamp out of range" ? "quote_expired" : "upstream_unavailable";
1217
+ }
1218
+ function rfqReason(outcome) {
1219
+ if (outcome.error) return outcome.errorReason;
1220
+ if (outcome.rejected) return rejectionReason(outcome.rejected);
1221
+ return outcome.answered ? "no_route" : "upstream_unavailable";
1222
+ }
1166
1223
  function quoteRejection(q, update, acknowledgedRfqId, request, settlement) {
1167
1224
  if (!q) return "empty quote";
1168
1225
  if (!acknowledgedRfqId || update.rfqId !== acknowledgedRfqId || q.rfqId !== acknowledgedRfqId) return "quote for another RFQ";
@@ -1202,6 +1259,7 @@ function awaitQuote(stream, request, signal, settleMs = QUOTE_SETTLE_MS, settlem
1202
1259
  const max = setTimeout(() => finish(latest), QUOTE_MAX_MS);
1203
1260
  sub = stream.subscribe({
1204
1261
  next: (event) => {
1262
+ if (event.$case === "ack" || event.$case === "quoteUpdated" || event.$case === "noQuote") outcome.answered = true;
1205
1263
  if (event.$case === "ack") {
1206
1264
  acknowledgedRfqId = event.value.rfqId || null;
1207
1265
  } else if (event.$case === "quoteUpdated") {
@@ -1221,6 +1279,7 @@ function awaitQuote(stream, request, signal, settleMs = QUOTE_SETTLE_MS, settlem
1221
1279
  },
1222
1280
  error: (err) => {
1223
1281
  outcome.error = String(err?.message ?? err).slice(0, 200);
1282
+ outcome.errorReason = venueReason(err);
1224
1283
  finish(null);
1225
1284
  },
1226
1285
  complete: () => finish(latest)
@@ -1262,9 +1321,9 @@ function awaitSwapProgress(stream) {
1262
1321
  }
1263
1322
  var JETTON_TRANSFER_OP = 260734629;
1264
1323
  function parseSwapPayload(payloadHex) {
1265
- if (!/^(?:[0-9a-fA-F]{2})+$/.test(payloadHex)) throw new Error("omniston: malformed swap payload hex");
1324
+ if (!/^(?:[0-9a-fA-F]{2})+$/.test(payloadHex)) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed swap payload hex");
1266
1325
  const roots = import_core3.Cell.fromBoc(import_buffer2.Buffer.from(payloadHex, "hex"));
1267
- if (roots.length !== 1) throw new Error("omniston: swap payload must have one root");
1326
+ if (roots.length !== 1) throw (0, import_errors.refusal)("policy_refused", "omniston: swap payload must have one root");
1268
1327
  const s = roots[0].beginParse();
1269
1328
  const op = s.loadUint(32);
1270
1329
  if (op !== JETTON_TRANSFER_OP) return { op, jettonTransfer: null };
@@ -1336,86 +1395,108 @@ function mapOrderStatus(order) {
1336
1395
  }
1337
1396
  function omnistonPins(trustedEvmOrderProtocols) {
1338
1397
  const byChain = new Map(Object.entries(trustedEvmOrderProtocols).map(([chain, list]) => [chain, list.map((a) => (0, import_viem.getAddress)(a))]));
1339
- return Object.freeze({ callTargets: () => [], spenders: (chain) => byChain.get(chain) ?? [] });
1398
+ return Object.freeze({
1399
+ callTargets: () => [],
1400
+ spenders: (chain) => byChain.get(chain) ?? [],
1401
+ // The reason behind a text omnistonVerifyBuild returned, so the remote stub's refusal carries it.
1402
+ reasonOf: verifyNotes.reasonOf
1403
+ });
1404
+ }
1405
+ var VERIFY_NOTES = /* @__PURE__ */ Symbol.for("@anywalletkit/protocol-swap2-omniston/verify-notes@1");
1406
+ var verifyNotes = (() => {
1407
+ const shared = globalThis[VERIFY_NOTES];
1408
+ if (shared && typeof shared.note === "function" && typeof shared.reasonOf === "function") return shared;
1409
+ const notes = (0, import_errors.refusalNotes)();
1410
+ try {
1411
+ Object.defineProperty(globalThis, VERIFY_NOTES, { value: notes });
1412
+ } catch {
1413
+ }
1414
+ return notes;
1415
+ })();
1416
+ var said = verifyNotes.note;
1417
+ function tonEscrowBuildReason(text) {
1418
+ if (text.startsWith("the escrow transfer pays ")) return "vault_mismatch";
1419
+ if (text.startsWith("the resolver is bound to deliver ")) return "price_check_failed";
1420
+ return "policy_refused";
1340
1421
  }
1341
1422
  var MAX_EVM_ORDER_LIFETIME_S = 900;
1342
1423
  async function checkRemoteEvmOrder(quote, tx, params, trusted) {
1343
1424
  const chainId = evmRef2(params.sourceChain);
1344
- if (chainId === null || !params.sourceChain.startsWith("eip155:")) return "a typed order needs an EVM source";
1425
+ if (chainId === null || !params.sourceChain.startsWith("eip155:")) return said("a typed order needs an EVM source", "policy_refused");
1345
1426
  const intent = decodeIntent(quote.intentId.replace(/~[0-9a-f]{32}$/, ""));
1346
- if (!intent || intent.kind !== "order" || intent.srcChain !== params.sourceChain) return "the quote's intent is not an order from this chain";
1347
- if (!tx.orderExtension || !/^0x(?:[0-9a-fA-F]{2})+$/.test(tx.orderExtension)) return "the build carries no order extension to check";
1427
+ if (!intent || intent.kind !== "order" || intent.srcChain !== params.sourceChain) return said("the quote's intent is not an order from this chain", "policy_refused");
1428
+ if (!tx.orderExtension || !/^0x(?:[0-9a-fA-F]{2})+$/.test(tx.orderExtension)) return said("the build carries no order extension to check", "policy_refused");
1348
1429
  const source = (0, import_swap2_assets.ccAsset)(params.sourceChain, params.sourceToken);
1349
- if (!source?.contract) return "unknown source token";
1430
+ if (!source?.contract) return said("unknown source token", "unsupported_pair");
1350
1431
  const tonDest = isTon(params.destChain);
1351
1432
  const destChainId = tonDest ? null : evmRef2(params.destChain);
1352
- if (!tonDest && (destChainId === null || !params.destChain.startsWith("eip155:"))) return "the destination is neither TON nor an EVM chain";
1433
+ if (!tonDest && (destChainId === null || !params.destChain.startsWith("eip155:"))) return said("the destination is neither TON nor an EVM chain", "unsupported_pair");
1353
1434
  const outputToken = (0, import_swap2_assets.ccAssetByToken)(params.destChain, params.destToken)?.contract ?? params.destToken;
1354
1435
  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));
1355
1436
  let td;
1356
1437
  try {
1357
1438
  td = JSON.parse(tx.typedData ?? "");
1358
1439
  } catch {
1359
- return "the typed data is not JSON";
1440
+ return said("the typed data is not JSON", "policy_refused");
1360
1441
  }
1361
1442
  const domain = td.domain ?? {};
1362
1443
  const contract = domain.verifyingContract;
1363
- if (domain.name !== "1inch Limit Order Protocol" || domain.version !== "4") return "the domain is not the 1inch Limit Order Protocol v4";
1444
+ if (domain.name !== "1inch Limit Order Protocol" || domain.version !== "4") return said("the domain is not the 1inch Limit Order Protocol v4", "policy_refused");
1364
1445
  if (!listed(trusted.orderProtocols, params.sourceChain, contract) || !tx.approvalAddress || (0, import_viem.getAddress)(tx.approvalAddress) !== (0, import_viem.getAddress)(String(contract))) {
1365
- return `${String(contract)} is not a trusted order protocol on ${params.sourceChain}`;
1446
+ return said(`${String(contract)} is not a trusted order protocol on ${params.sourceChain}`, "vault_mismatch");
1366
1447
  }
1367
- if (Number(domain.chainId) !== chainId || tx.chainId !== void 0 && tx.chainId !== chainId) return "the domain chainId is not the source chain";
1448
+ if (Number(domain.chainId) !== chainId || tx.chainId !== void 0 && tx.chainId !== chainId) return said("the domain chainId is not the source chain", "policy_refused");
1368
1449
  const order = td.types?.Order;
1369
1450
  const message = td.message;
1370
1451
  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]))) {
1371
- return "the typed data is not exactly a v4 Order";
1452
+ return said("the typed data is not exactly a v4 Order", "policy_refused");
1372
1453
  }
1373
- if (!validEvmAddress(intent.trader) || (0, import_viem.getAddress)(String(message.maker)) !== (0, import_viem.getAddress)(intent.trader)) return "the maker is not the trader";
1374
- if ((0, import_viem.getAddress)(String(message.makerAsset)) !== (0, import_viem.getAddress)(source.contract)) return "the makerAsset is not the source token";
1375
- if (BigInt(message.makingAmount) !== BigInt(quote.sourceAmount)) return "the makingAmount is not the input";
1376
- if ((0, import_viem.getAddress)(String(message.takerAsset)) === (0, import_viem.getAddress)(String(message.makerAsset))) return "the takerAsset is the makerAsset";
1454
+ 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");
1455
+ 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");
1456
+ if (BigInt(message.makingAmount) !== BigInt(quote.sourceAmount)) return said("the makingAmount is not the input", "policy_refused");
1457
+ if ((0, import_viem.getAddress)(String(message.takerAsset)) === (0, import_viem.getAddress)(String(message.makerAsset))) return said("the takerAsset is the makerAsset", "policy_refused");
1377
1458
  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);
1378
- if (floor <= 0n || BigInt(message.takingAmount) < floor) return `takingAmount ${String(message.takingAmount)} is below the quote's minimum ${floor}`;
1459
+ if (floor <= 0n || BigInt(message.takingAmount) < floor) return said(`takingAmount ${String(message.takingAmount)} is below the quote's minimum ${floor}`, "price_check_failed");
1379
1460
  const traits = BigInt(message.makerTraits);
1380
1461
  const flags = traits & (1n << 56n) - 1n << 200n;
1381
1462
  const baseFlags = 1n << 251n | 1n << 249n;
1382
- if (flags !== baseFlags && flags !== (baseFlags | 1n << 255n)) return "the makerTraits flags are not a single-fill order with its extension";
1383
- if ((traits & (1n << 80n) - 1n) !== 0n || (traits >> 160n & (1n << 40n) - 1n) !== 0n) return "the makerTraits restrict the taker or the series";
1463
+ if (flags !== baseFlags && flags !== (baseFlags | 1n << 255n)) return said("the makerTraits flags are not a single-fill order with its extension", "policy_refused");
1464
+ if ((traits & (1n << 80n) - 1n) !== 0n || (traits >> 160n & (1n << 40n) - 1n) !== 0n) return said("the makerTraits restrict the taker or the series", "policy_refused");
1384
1465
  const now = BigInt(Math.floor(Date.now() / 1e3));
1385
1466
  const expiration = traits >> 80n & (1n << 40n) - 1n;
1386
- if (expiration <= now || expiration > now + BigInt(MAX_EVM_ORDER_LIFETIME_S)) return "the order's expiry is not minutes away";
1467
+ 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");
1387
1468
  const extension = (0, import_viem.hexToBytes)(tx.orderExtension);
1388
1469
  const mask = (1n << 160n) - 1n;
1389
- if ((BigInt(message.salt) & mask) !== (BigInt((0, import_viem.keccak256)(extension)) & mask)) return "the salt does not commit the order extension";
1470
+ if ((BigInt(message.salt) & mask) !== (BigInt((0, import_viem.keccak256)(extension)) & mask)) return said("the salt does not commit the order extension", "policy_refused");
1390
1471
  let parsed;
1391
1472
  try {
1392
1473
  parsed = readEvmOrderExtension(extension);
1393
1474
  } catch (e) {
1394
- return e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e);
1475
+ return said(e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e), (0, import_errors.refusalOf)(e) ?? "policy_refused");
1395
1476
  }
1396
1477
  const { target, getter, post, extra, customData } = parsed;
1397
- if (!listed(trusted.factories, params.sourceChain, target)) return `the extension targets ${target}, not a trusted escrow factory on ${params.sourceChain}`;
1478
+ if (!listed(trusted.factories, params.sourceChain, target)) return said(`the extension targets ${target}, not a trusted escrow factory on ${params.sourceChain}`, "vault_mismatch");
1398
1479
  const receiverLow80 = BigInt((0, import_viem.getAddress)(String(message.receiver))) & (1n << 80n) - 1n;
1399
- if (getter.resolver !== receiverLow80 || post.resolver !== receiverLow80) return "the order's receiver is not the resolver its extension whitelists";
1480
+ if (getter.resolver !== receiverLow80 || post.resolver !== receiverLow80) return said("the order's receiver is not the resolver its extension whitelists", "recipient_mismatch");
1400
1481
  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)) {
1401
- return "the amount-getter terms do not match the pinned v1beta8 profile";
1482
+ return said("the amount-getter terms do not match the pinned v1beta8 profile", "policy_refused");
1402
1483
  }
1403
1484
  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)) {
1404
- return "the post-interaction terms do not match the pinned v1beta8 profile";
1485
+ return said("the post-interaction terms do not match the pinned v1beta8 profile", "policy_refused");
1405
1486
  }
1406
1487
  let dstToken;
1407
1488
  try {
1408
1489
  dstToken = tonDest ? BigInt((0, import_viem.getAddress)(String(message.takerAsset))) : BigInt((0, import_viem.getAddress)(outputToken));
1409
1490
  } catch {
1410
- return "the destination token is not an EVM address";
1491
+ return said("the destination token is not an EVM address", "policy_refused");
1411
1492
  }
1412
1493
  if (extra[1] !== (tonDest ? 0n : BigInt(destChainId)) || extra[2] !== dstToken || extra[4] !== EVM_TIMELOCKS) {
1413
- return "the signed HTLC terms name another destination chain or token, or other timelocks";
1494
+ return said("the signed HTLC terms name another destination chain or token, or other timelocks", "policy_refused");
1414
1495
  }
1415
1496
  try {
1416
1497
  await checkOrderCustomData(customData, { evmDest: !tonDest, outputToken, traderDst: params.destAddress, quoteId: intent.quoteId });
1417
1498
  } catch (e) {
1418
- return e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e);
1499
+ return said(e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e), (0, import_errors.refusalOf)(e) ?? "policy_refused");
1419
1500
  }
1420
1501
  return null;
1421
1502
  }
@@ -1426,22 +1507,22 @@ function omnistonVerifyBuild(hooks) {
1426
1507
  };
1427
1508
  return async (quote, tx, params) => {
1428
1509
  if (tx.typedData !== void 0) {
1429
- if (!params) return "the request this quote answered is unknown";
1510
+ if (!params) return said("the request this quote answered is unknown", "policy_refused");
1430
1511
  try {
1431
1512
  return await checkRemoteEvmOrder(quote, tx, params, trusted);
1432
1513
  } catch {
1433
- return "the typed order could not be checked";
1514
+ return said("the typed order could not be checked", "policy_refused");
1434
1515
  }
1435
1516
  }
1436
1517
  if (tx.bodyBoc === void 0) return null;
1437
- if (!params) return "the request this quote answered is unknown";
1518
+ if (!params) return said("the request this quote answered is unknown", "policy_refused");
1438
1519
  if (quote.providerData?.settlement !== "swap") {
1439
1520
  if (params.sourceChain.startsWith("ton:") && params.destChain.startsWith("eip155:")) {
1440
- if (!hooks.verifyTonEscrowBuild) return "a TON \u2192 EVM escrow body needs verifyTonEscrowBuild (createOmnistonTonValidators)";
1441
- if (!params.refundAddress) return "the trader's TON address (refundAddress) is unknown";
1521
+ if (!hooks.verifyTonEscrowBuild) return said("a TON \u2192 EVM escrow body needs verifyTonEscrowBuild (createOmnistonTonValidators)", "unsupported_pair");
1522
+ if (!params.refundAddress) return said("the trader's TON address (refundAddress) is unknown", "recipient_unverifiable");
1442
1523
  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);
1443
1524
  try {
1444
- return await hooks.verifyTonEscrowBuild({
1525
+ const text = await hooks.verifyTonEscrowBuild({
1445
1526
  payloadBocHex: import_buffer2.Buffer.from(tx.bodyBoc, "base64").toString("hex"),
1446
1527
  ownerAddress: params.refundAddress,
1447
1528
  inputUnits: quote.sourceAmount,
@@ -1450,22 +1531,23 @@ function omnistonVerifyBuild(hooks) {
1450
1531
  outputTokenAddress: params.destToken,
1451
1532
  minResolverSendsUnits: floor.toString()
1452
1533
  });
1534
+ return text ? said(text, tonEscrowBuildReason(text)) : text;
1453
1535
  } catch {
1454
- return "the escrow body could not be checked";
1536
+ return said("the escrow body could not be checked", "policy_refused");
1455
1537
  }
1456
1538
  }
1457
- return "only TON swap and TON \u2192 EVM escrow bodies can be checked on the client";
1539
+ return said("only TON swap and TON \u2192 EVM escrow bodies can be checked on the client", "unsupported_pair");
1458
1540
  }
1459
1541
  let steps;
1460
1542
  try {
1461
1543
  steps = JSON.parse(quote.providerData.routeSteps ?? "");
1462
1544
  } catch {
1463
- return "the quote carries no route steps";
1545
+ return said("the quote carries no route steps", "policy_refused");
1464
1546
  }
1465
- if (!Array.isArray(steps) || steps.length === 0) return "the quote carries no route steps";
1547
+ if (!Array.isArray(steps) || steps.length === 0) return said("the quote carries no route steps", "policy_refused");
1466
1548
  const source = (0, import_swap2_assets.ccAsset)(params.sourceChain, params.sourceToken);
1467
1549
  const dest = (0, import_swap2_assets.ccAssetByToken)(params.destChain, params.destToken);
1468
- if (!source || !dest) return "unknown source or destination token";
1550
+ if (!source || !dest) return said("unknown source or destination token", "unsupported_pair");
1469
1551
  const protocols = (quote.providerData.protocols ?? "").split(",").filter(Boolean);
1470
1552
  let checked;
1471
1553
  try {
@@ -1483,17 +1565,17 @@ function omnistonVerifyBuild(hooks) {
1483
1565
  integratorAddress: hooks.integratorAddress ?? null
1484
1566
  });
1485
1567
  } catch (e) {
1486
- return e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e);
1568
+ return said(e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e), e instanceof TonSwapPayloadError ? tonSwapPayloadReason(e.reason) : "policy_refused");
1487
1569
  }
1488
1570
  const payload = parseSwapPayload(import_buffer2.Buffer.from(tx.bodyBoc, "base64").toString("hex"));
1489
1571
  const target = source.contract ? payload.jettonTransfer?.destination : tx.to;
1490
- if (!target) return "the body names no swap destination";
1572
+ if (!target) return said("the body names no swap destination", "policy_refused");
1491
1573
  try {
1492
- if (!await hooks.validateTonSwapTarget({ address: target, protocols })) return `${target} is not a recognized DEX contract for ${protocols.join("+")}`;
1574
+ if (!await hooks.validateTonSwapTarget({ address: target, protocols })) return said(`${target} is not a recognized DEX contract for ${protocols.join("+")}`, "vault_mismatch");
1493
1575
  } catch {
1494
- return "the swap target could not be checked";
1576
+ return said("the swap target could not be checked", "policy_refused");
1495
1577
  }
1496
- if (!hooks.validateTonSwapLeg) return "a TON swap body needs validateTonSwapLeg (createOmnistonTonValidators)";
1578
+ if (!hooks.validateTonSwapLeg) return said("a TON swap body needs validateTonSwapLeg (createOmnistonTonValidators)", "unsupported_pair");
1497
1579
  for (const leg of checked.legs) {
1498
1580
  let ok = false;
1499
1581
  try {
@@ -1501,7 +1583,7 @@ function omnistonVerifyBuild(hooks) {
1501
1583
  } catch {
1502
1584
  ok = false;
1503
1585
  }
1504
- if (!ok) return `${leg.kind} v${leg.version} leg not verified`;
1586
+ if (!ok) return said(`${leg.kind} v${leg.version} leg not verified`, "vault_mismatch");
1505
1587
  }
1506
1588
  return null;
1507
1589
  };
@@ -1685,19 +1767,19 @@ function omnistonProvider(opts = {}) {
1685
1767
  if (!bound.ok) {
1686
1768
  if (bound.reason === "legacy-expired") {
1687
1769
  trace("legacy-intent-expired", { where });
1688
- throw new Error("omniston: quote expired \u2014 request a fresh quote");
1770
+ throw (0, import_errors.refusal)("quote_expired", "omniston: quote expired \u2014 request a fresh quote");
1689
1771
  }
1690
1772
  trace("intent-mismatch", { where, field: bound.field });
1691
- throw new Error(`omniston: intent id does not match the quote it was issued for (${bound.field})`);
1773
+ throw (0, import_errors.refusal)(mismatchReason(bound.field), `omniston: intent id does not match the quote it was issued for (${bound.field})`);
1692
1774
  }
1693
1775
  if (bound.legacy) trace("legacy-intent", { where });
1694
1776
  if (!decoded.digest) {
1695
1777
  trace("intent-mismatch", { where, field: "digest-missing" });
1696
- throw new Error("omniston: this quote was issued before an update \u2014 request a fresh quote");
1778
+ throw (0, import_errors.refusal)("quote_expired", "omniston: this quote was issued before an update \u2014 request a fresh quote");
1697
1779
  }
1698
1780
  if (decoded.digest !== quoteDigest(entry.quote)) {
1699
1781
  trace("intent-mismatch", { where, field: "digest" });
1700
- throw new Error("omniston: quote was replaced \u2014 request a fresh quote");
1782
+ throw (0, import_errors.refusal)("policy_refused", "omniston: quote was replaced \u2014 request a fresh quote");
1701
1783
  }
1702
1784
  return bound.ref;
1703
1785
  }
@@ -1731,10 +1813,10 @@ function omnistonProvider(opts = {}) {
1731
1813
  const disclosed = (await store.load(disclosedKey(quoteId)) ?? []).length > 0;
1732
1814
  if (boundTo !== binding || disclosed) {
1733
1815
  traceFor(quoteId)("secret-reuse-refused", { disclosed, bound: boundTo !== void 0 });
1734
- throw new Error("omniston: this quote id already belongs to another order \u2014 request a fresh quote");
1816
+ throw (0, import_errors.refusal)("policy_refused", "omniston: this quote id already belongs to another order \u2014 request a fresh quote");
1735
1817
  }
1736
1818
  if (existing.length !== count || !existing.every((s) => /^[0-9a-fA-F]{64}$/.test(s))) {
1737
- throw new Error("omniston: stored HTLC secrets for this quote do not fit the order \u2014 request a fresh quote");
1819
+ throw (0, import_errors.refusal)("policy_refused", "omniston: stored HTLC secrets for this quote do not fit the order \u2014 request a fresh quote");
1738
1820
  }
1739
1821
  traceFor(quoteId)("secret-reused", { count });
1740
1822
  return { secrets: existing.map(hexToBytes), created: false };
@@ -1793,7 +1875,10 @@ function omnistonProvider(opts = {}) {
1793
1875
  let q = await rfq(slippagePips);
1794
1876
  if (!q) {
1795
1877
  if (!signal?.aborted) {
1796
- params.onRefusal?.(`omniston: ${outcome.error ? `RFQ failed: ${outcome.error}` : outcome.rejected ? `swap quote rejected: ${outcome.rejected}` : "no TON DEX route quoted this pair/amount"}`);
1878
+ const text = `omniston: ${outcome.error ? `RFQ failed: ${outcome.error}` : outcome.rejected ? `swap quote rejected: ${outcome.rejected}` : "no TON DEX route quoted this pair/amount"}`;
1879
+ const reason = rfqReason(outcome);
1880
+ if (reason) params.onRefusal?.(text, { reason });
1881
+ else params.onRefusal?.(text);
1797
1882
  }
1798
1883
  return null;
1799
1884
  }
@@ -1859,27 +1944,32 @@ function omnistonProvider(opts = {}) {
1859
1944
  async function buildSwapDeposit(ref, q, swap) {
1860
1945
  if (!ref.trader) throw new Error("omniston: quote was requested without a refund/source address");
1861
1946
  if (!isTon(ref.srcChain) || q.inputAsset.chain.$case !== "ton" || q.outputAsset.chain.$case !== "ton") {
1862
- throw new Error("omniston: swap quote is not a TON same-chain swap");
1947
+ throw (0, import_errors.refusal)("policy_refused", "omniston: swap quote is not a TON same-chain swap");
1863
1948
  }
1864
- if (!sameChainTonReady()) throw new Error("omniston: TON same-chain swaps need independent jetton-wallet, swap-target and swap-leg validators");
1949
+ if (!sameChainTonReady()) throw (0, import_errors.refusal)("unsupported_pair", "omniston: TON same-chain swaps need independent jetton-wallet, swap-target and swap-leg validators");
1865
1950
  const owner = { chain: { $case: "ton", value: ref.trader } };
1866
1951
  const receiver = { chain: { $case: "ton", value: ref.dest } };
1867
1952
  const omni = await getClient();
1868
- if (typeof omni.tonBuildSwap !== "function") throw new Error("omniston: client does not support swap settlement (tonBuildSwap)");
1869
- const tx = await omni.tonBuildSwap({
1870
- quoteId: ref.quoteId,
1871
- transferSrcAddress: owner,
1872
- refundSrcAddress: owner,
1873
- gasExcessAddress: owner,
1874
- traderDstAddress: receiver,
1875
- // Build at the slippage the quote was requested with: its
1876
- // minOutputAmount is the minimum this module reported to the router.
1877
- useRecommendedSlippage: false
1878
- });
1953
+ if (typeof omni.tonBuildSwap !== "function") throw (0, import_errors.refusal)("unsupported_pair", "omniston: client does not support swap settlement (tonBuildSwap)");
1954
+ let tx;
1955
+ try {
1956
+ tx = await omni.tonBuildSwap({
1957
+ quoteId: ref.quoteId,
1958
+ transferSrcAddress: owner,
1959
+ refundSrcAddress: owner,
1960
+ gasExcessAddress: owner,
1961
+ traderDstAddress: receiver,
1962
+ // Build at the slippage the quote was requested with: its
1963
+ // minOutputAmount is the minimum this module reported to the router.
1964
+ useRecommendedSlippage: false
1965
+ });
1966
+ } catch (error) {
1967
+ throw venueFailure(error);
1968
+ }
1879
1969
  const [message, ...rest] = tx.messages;
1880
- if (!message || rest.length > 0) throw new Error(`omniston: expected exactly one swap message, got ${tx.messages.length}`);
1881
- if (message.jettonWalletStateInit) throw new Error("omniston: deploying a resolver-provided jetton wallet is not supported");
1882
- if (!isPositiveInteger(message.sendAmount)) throw new Error("omniston: malformed swap message value");
1970
+ if (!message || rest.length > 0) throw (0, import_errors.refusal)("policy_refused", `omniston: expected exactly one swap message, got ${tx.messages.length}`);
1971
+ if (message.jettonWalletStateInit) throw (0, import_errors.refusal)("policy_refused", "omniston: deploying a resolver-provided jetton wallet is not supported");
1972
+ if (!isPositiveInteger(message.sendAmount)) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed swap message value");
1883
1973
  const sendAmount = BigInt(message.sendAmount);
1884
1974
  const gasCap = BigInt(maxTonExecutionNano);
1885
1975
  const gasBudget = q.gasBudget && isPositiveInteger(q.gasBudget) ? BigInt(q.gasBudget) : gasCap;
@@ -1891,53 +1981,53 @@ function omnistonProvider(opts = {}) {
1891
1981
  const ptonV1Native = !inputJetton && !!payload.jettonTransfer && singleV1;
1892
1982
  if (inputJetton) {
1893
1983
  const transfer = payload.jettonTransfer;
1894
- if (!transfer) throw new Error("omniston: jetton-input swap message is not a jetton transfer");
1895
- 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("+")})`);
1896
- if (transfer.amount !== BigInt(q.inputUnits)) throw new Error("omniston: swap message moves a different amount than quoted");
1984
+ if (!transfer) throw (0, import_errors.refusal)("policy_refused", "omniston: jetton-input swap message is not a jetton transfer");
1985
+ 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("+")})`);
1986
+ if (transfer.amount !== BigInt(q.inputUnits)) throw (0, import_errors.refusal)("policy_refused", "omniston: swap message moves a different amount than quoted");
1897
1987
  if (!transfer.responseDestination || !sameTonAddress(transfer.responseDestination, ref.trader)) {
1898
- throw new Error("omniston: swap message returns excess to a wallet other than the trader");
1988
+ throw (0, import_errors.refusal)("recipient_mismatch", "omniston: swap message returns excess to a wallet other than the trader");
1899
1989
  }
1900
- if (transfer.forwardTon >= sendAmount) throw new Error("omniston: swap message forwards more TON than it carries");
1990
+ if (transfer.forwardTon >= sendAmount) throw (0, import_errors.refusal)("policy_refused", "omniston: swap message forwards more TON than it carries");
1901
1991
  const walletValid = await opts.validateTonJettonWallet({
1902
1992
  ownerAddress: ref.trader,
1903
1993
  jettonMasterAddress: inputJetton,
1904
1994
  walletAddress: message.targetAddress
1905
1995
  });
1906
- if (!walletValid) throw new Error("omniston: TON jetton wallet was not independently validated");
1996
+ if (!walletValid) throw (0, import_errors.refusal)("vault_mismatch", "omniston: TON jetton wallet was not independently validated");
1907
1997
  const targetValid = await opts.validateTonSwapTarget({ address: transfer.destination, protocols });
1908
- if (!targetValid) throw new Error("omniston: swap destination is not a recognized DEX contract for the quoted protocol");
1998
+ if (!targetValid) throw (0, import_errors.refusal)("vault_mismatch", "omniston: swap destination is not a recognized DEX contract for the quoted protocol");
1909
1999
  } else if (ptonV1Native) {
1910
2000
  const transfer = payload.jettonTransfer;
1911
2001
  const input = BigInt(q.inputUnits);
1912
- if (transfer.amount !== input) throw new Error("omniston: swap message moves a different amount than quoted");
1913
- if (sendAmount < input) throw new Error("omniston: swap message carries less than the quoted input");
2002
+ if (transfer.amount !== input) throw (0, import_errors.refusal)("policy_refused", "omniston: swap message moves a different amount than quoted");
2003
+ if (sendAmount < input) throw (0, import_errors.refusal)("policy_refused", "omniston: swap message carries less than the quoted input");
1914
2004
  const gas = sendAmount - input;
1915
- 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("+")})`);
1916
- if (transfer.forwardTon > gas) throw new Error("omniston: swap message forwards more TON than its gas");
2005
+ 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("+")})`);
2006
+ if (transfer.forwardTon > gas) throw (0, import_errors.refusal)("policy_refused", "omniston: swap message forwards more TON than its gas");
1917
2007
  if (!transfer.responseDestination || !sameTonAddress(transfer.responseDestination, ref.trader)) {
1918
- throw new Error("omniston: swap message returns excess to a wallet other than the trader");
2008
+ throw (0, import_errors.refusal)("recipient_mismatch", "omniston: swap message returns excess to a wallet other than the trader");
1919
2009
  }
1920
- if (typeof opts.validateTonSwapLeg !== "function") throw new Error("omniston: STON.fi v1 native input needs the validateTonSwapLeg validator (pTON v1 wallet ownership)");
2010
+ 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)");
1921
2011
  const targetValid = await opts.validateTonSwapTarget({ address: message.targetAddress, protocols });
1922
- if (!targetValid) throw new Error("omniston: swap target is not a recognized DEX contract for the quoted protocol");
2012
+ if (!targetValid) throw (0, import_errors.refusal)("vault_mismatch", "omniston: swap target is not a recognized DEX contract for the quoted protocol");
1923
2013
  const routerValid = await opts.validateTonSwapTarget({ address: transfer.destination, protocols });
1924
- if (!routerValid) throw new Error("omniston: swap destination is not a recognized DEX contract for the quoted protocol");
2014
+ if (!routerValid) throw (0, import_errors.refusal)("vault_mismatch", "omniston: swap destination is not a recognized DEX contract for the quoted protocol");
1925
2015
  } else {
1926
2016
  if (payload.jettonTransfer) {
1927
- 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})`);
2017
+ 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})`);
1928
2018
  }
1929
2019
  const input = BigInt(q.inputUnits);
1930
- if (sendAmount < input) throw new Error("omniston: swap message carries less than the quoted input");
2020
+ if (sendAmount < input) throw (0, import_errors.refusal)("policy_refused", "omniston: swap message carries less than the quoted input");
1931
2021
  const gas = sendAmount - input;
1932
- 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("+")})`);
2022
+ 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("+")})`);
1933
2023
  const targetValid = await opts.validateTonSwapTarget({ address: message.targetAddress, protocols });
1934
- if (!targetValid) throw new Error("omniston: swap target is not a recognized DEX contract for the quoted protocol");
2024
+ if (!targetValid) throw (0, import_errors.refusal)("vault_mismatch", "omniston: swap target is not a recognized DEX contract for the quoted protocol");
1935
2025
  }
1936
2026
  const mode = ptonV1Native ? "enforce" : opts.tonSwapPayloadCheck ?? "enforce";
1937
2027
  const trace = traceFor(ref.quoteId);
1938
2028
  const payloadRefusal = (reason, detail) => {
1939
2029
  trace("ton-swap-payload", { reason, detail, mode, protocols: protocols.join("+") });
1940
- if (mode === "enforce") throw new Error(`omniston: ton-swap-payload ${reason} \u2014 ${detail}`);
2030
+ if (mode === "enforce") throw (0, import_errors.refusal)(tonSwapPayloadReason(reason), `omniston: ton-swap-payload ${reason} \u2014 ${detail}`);
1941
2031
  };
1942
2032
  let checked = null;
1943
2033
  try {
@@ -2011,33 +2101,33 @@ function omnistonProvider(opts = {}) {
2011
2101
  const srcChain = ref.srcChain;
2012
2102
  const chainId = evmRef2(srcChain);
2013
2103
  const srcCase = q.inputAsset.chain.$case;
2014
- if (chainId === null || EVM_CASE[chainId] !== srcCase) throw new Error("omniston: source chain does not match the EVM quote");
2104
+ if (chainId === null || EVM_CASE[chainId] !== srcCase) throw (0, import_errors.refusal)("policy_refused", "omniston: source chain does not match the EVM quote");
2015
2105
  const destCase = q.outputAsset.chain.$case;
2016
2106
  const destChain = caipForChainCase(destCase);
2017
2107
  const tonDest = destCase === "ton";
2018
- if (!destChain || destChain === srcChain) throw new Error("omniston: EVM-source order has no supported destination chain");
2108
+ if (!destChain || destChain === srcChain) throw (0, import_errors.refusal)("unsupported_pair", "omniston: EVM-source order has no supported destination chain");
2019
2109
  if (!trustedOrderProtocols.has(srcChain) || typeof opts.validateEvmInputPosition !== "function" || typeof (tonDest ? opts.validateTonOutputPosition : opts.validateEvmOutputPosition) !== "function") {
2020
- throw new Error(`omniston: EVM\u2192${tonDest ? "TON" : "EVM"} needs a trusted order protocol and both position validators`);
2110
+ throw (0, import_errors.refusal)("unsupported_pair", `omniston: EVM\u2192${tonDest ? "TON" : "EVM"} needs a trusted order protocol and both position validators`);
2021
2111
  }
2022
2112
  let owner;
2023
2113
  try {
2024
2114
  owner = (0, import_viem.getAddress)(ref.trader);
2025
2115
  } catch {
2026
- throw new Error("omniston: trader is not an EVM address");
2116
+ throw (0, import_errors.refusal)("recipient_mismatch", "omniston: trader is not an EVM address");
2027
2117
  }
2028
2118
  let dest;
2029
2119
  if (tonDest) {
2030
2120
  try {
2031
2121
  import_core3.Address.parse(ref.dest);
2032
2122
  } catch {
2033
- throw new Error("omniston: destination is not a TON address");
2123
+ throw (0, import_errors.refusal)("recipient_mismatch", "omniston: destination is not a TON address");
2034
2124
  }
2035
2125
  dest = ref.dest;
2036
2126
  } else {
2037
2127
  try {
2038
2128
  dest = (0, import_viem.getAddress)(ref.dest);
2039
2129
  } catch {
2040
- throw new Error("omniston: destination is not an EVM address");
2130
+ throw (0, import_errors.refusal)("recipient_mismatch", "omniston: destination is not an EVM address");
2041
2131
  }
2042
2132
  }
2043
2133
  const traderDst = { chain: { $case: destCase, value: dest } };
@@ -2048,9 +2138,9 @@ function omnistonProvider(opts = {}) {
2048
2138
  const outputToken = assetContract(q.outputAsset);
2049
2139
  const destChainId = tonDest ? null : evmRef2(destChain);
2050
2140
  if (factory?.chain.$case === destCase && !isTrusted(destChain, factory.chain.value)) {
2051
- throw new Error(`omniston: untrusted ${destChain} settlement contract ${factory.chain.value}`);
2141
+ throw (0, import_errors.refusal)("vault_mismatch", `omniston: untrusted ${destChain} settlement contract ${factory.chain.value}`);
2052
2142
  }
2053
- 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");
2143
+ 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");
2054
2144
  const { secrets, created } = await secretsFor(ref, q, 1);
2055
2145
  const hashes = secrets.map((secret) => hashlock(secret, settlement.htlcHashingFunction));
2056
2146
  const forget = async () => {
@@ -2067,14 +2157,14 @@ function omnistonProvider(opts = {}) {
2067
2157
  });
2068
2158
  } catch (error) {
2069
2159
  await forget();
2070
- throw error;
2160
+ throw venueFailure(error);
2071
2161
  }
2072
2162
  let td;
2073
2163
  try {
2074
2164
  try {
2075
2165
  td = JSON.parse(payload.typedData);
2076
2166
  } catch {
2077
- throw new Error("omniston: malformed EVM order typed data");
2167
+ throw (0, import_errors.refusal)("policy_refused", "omniston: malformed EVM order typed data");
2078
2168
  }
2079
2169
  validateSerializedOrderDetails(payload.serializedOrderDetails, {
2080
2170
  quoteId: ref.quoteId,
@@ -2157,45 +2247,46 @@ function omnistonProvider(opts = {}) {
2157
2247
  async getQuote(params, signal) {
2158
2248
  if (params.flex) return null;
2159
2249
  if (isTon(params.sourceChain) && isTon(params.destChain)) return await getSwapQuote(params, signal);
2160
- const refuse2 = (reason) => {
2161
- params.onRefusal?.(`omniston: ${reason}`);
2250
+ const refuse2 = (reason, why) => {
2251
+ if (why) params.onRefusal?.(`omniston: ${reason}`, { reason: why });
2252
+ else params.onRefusal?.(`omniston: ${reason}`);
2162
2253
  return null;
2163
2254
  };
2164
2255
  const evmSource = !isTon(params.sourceChain);
2165
2256
  const tonDest = isTon(params.destChain);
2166
- if (!omniston.supports(params.sourceChain, params.destChain)) return refuse2("route is not an Omniston order corridor");
2257
+ if (!omniston.supports(params.sourceChain, params.destChain)) return refuse2("route is not an Omniston order corridor", "unsupported_pair");
2167
2258
  if (!trustedContracts.has(params.sourceChain) || !trustedContracts.has(params.destChain)) {
2168
- return refuse2(`no trusted settlement contract configured for ${trustedContracts.has(params.sourceChain) ? params.destChain : params.sourceChain}`);
2259
+ return refuse2(`no trusted settlement contract configured for ${trustedContracts.has(params.sourceChain) ? params.destChain : params.sourceChain}`, "unsupported_pair");
2169
2260
  }
2170
2261
  if (evmSource) {
2171
- if (!trustedOrderProtocols.has(params.sourceChain)) return refuse2(`no trusted EVM order protocol configured for ${params.sourceChain}`);
2262
+ if (!trustedOrderProtocols.has(params.sourceChain)) return refuse2(`no trusted EVM order protocol configured for ${params.sourceChain}`, "unsupported_pair");
2172
2263
  if (typeof opts.validateEvmInputPosition !== "function" || typeof (tonDest ? opts.validateTonOutputPosition : opts.validateEvmOutputPosition) !== "function") {
2173
- 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");
2264
+ 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");
2174
2265
  }
2175
2266
  if (params.dry === false) {
2176
2267
  if (tonDest) {
2177
2268
  try {
2178
2269
  import_core3.Address.parse(params.destAddress);
2179
2270
  } catch {
2180
- return refuse2("destination is not a TON address");
2271
+ return refuse2("destination is not a TON address", "recipient_mismatch");
2181
2272
  }
2182
- } else if (!validEvmAddress(params.destAddress)) return refuse2("destination is not an EVM address");
2273
+ } else if (!validEvmAddress(params.destAddress)) return refuse2("destination is not an EVM address", "recipient_mismatch");
2183
2274
  }
2184
- } 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");
2275
+ } 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");
2185
2276
  const source = (0, import_swap2_assets.ccAsset)(params.sourceChain, params.sourceToken);
2186
2277
  const dest = (0, import_swap2_assets.ccAssetByToken)(params.destChain, params.destToken);
2187
- if (!source || !dest) return refuse2("asset is not in the catalog");
2278
+ if (!source || !dest) return refuse2("asset is not in the catalog", "unsupported_pair");
2188
2279
  const inputAsset = toAssetId(params.sourceChain, source);
2189
2280
  const outputAsset = toAssetId(params.destChain, dest);
2190
2281
  if (!inputAsset || !outputAsset) {
2191
- return refuse2(`cross-chain orders are token corridors only (no ${!inputAsset ? source.symbol : dest.symbol} native leg)`);
2282
+ return refuse2(`cross-chain orders are token corridors only (no ${!inputAsset ? source.symbol : dest.symbol} native leg)`, "unsupported_pair");
2192
2283
  }
2193
2284
  const maxUnits = decimalToUnits(maxUsdText, source.decimals);
2194
2285
  if (!maxUnits || !isPositiveInteger(params.amountIn)) return refuse2("invalid amount");
2195
- if (BigInt(params.amountIn) > maxUnits) return refuse2(`amount above the ${maxUsdText} USD per-swap cap`);
2286
+ if (BigInt(params.amountIn) > maxUnits) return refuse2(`amount above the ${maxUsdText} USD per-swap cap`, "amount_too_large");
2196
2287
  const trader = params.refundAddress ?? "";
2197
2288
  if (!trader && !params.dry) return refuse2(`execution quote needs the trader's ${evmSource ? "EVM" : "TON"} address (refundAddress)`);
2198
- if (evmSource && trader && !validEvmAddress(trader)) return refuse2("refundAddress is not an EVM address");
2289
+ if (evmSource && trader && !validEvmAddress(trader)) return refuse2("refundAddress is not an EVM address", "recipient_mismatch");
2199
2290
  const request = {
2200
2291
  inputAsset,
2201
2292
  outputAsset,
@@ -2223,27 +2314,28 @@ function omnistonProvider(opts = {}) {
2223
2314
  if (!q) {
2224
2315
  if (signal?.aborted) return null;
2225
2316
  return refuse2(
2226
- rfq.error ? `RFQ failed: ${rfq.error}` : rfq.rejected ? `resolver quote rejected: ${rfq.rejected}` : "no resolver quoted this route/amount"
2317
+ rfq.error ? `RFQ failed: ${rfq.error}` : rfq.rejected ? `resolver quote rejected: ${rfq.rejected}` : "no resolver quoted this route/amount",
2318
+ rfqReason(rfq)
2227
2319
  );
2228
2320
  }
2229
2321
  const orderData = q.settlementData.$case === "order" ? q.settlementData.value : null;
2230
- if (!orderData) return refuse2("resolver answered without order settlement");
2322
+ if (!orderData) return refuse2("resolver answered without order settlement", "upstream_unavailable");
2231
2323
  const srcContract = orderData.srcProtocolContractAddress.chain.value;
2232
2324
  if (evmSource ? !isTrustedOrderProtocol(params.sourceChain, srcContract) : !isTrusted(params.sourceChain, srcContract)) {
2233
- return refuse2(evmSource ? `untrusted ${params.sourceChain} order protocol ${srcContract}` : `untrusted TON settlement contract ${srcContract}`);
2325
+ return refuse2(evmSource ? `untrusted ${params.sourceChain} order protocol ${srcContract}` : `untrusted TON settlement contract ${srcContract}`, "vault_mismatch");
2234
2326
  }
2235
2327
  if (!orderData.dstProtocolContractAddress || !isTrusted(params.destChain, orderData.dstProtocolContractAddress.chain.value)) {
2236
- return refuse2(`untrusted ${params.destChain} settlement contract ${orderData.dstProtocolContractAddress?.chain.value ?? "(none)"}`);
2328
+ return refuse2(`untrusted ${params.destChain} settlement contract ${orderData.dstProtocolContractAddress?.chain.value ?? "(none)"}`, "vault_mismatch");
2237
2329
  }
2238
2330
  const deadlineMs = orderData.tradeStartDeadline * 1e3;
2239
- if (!Number.isFinite(deadlineMs) || deadlineMs <= Date.now()) return refuse2("quote trade-start deadline already passed");
2240
- if (params.dry === false && deadlineMs - Date.now() < MIN_START_HEADROOM_MS) return refuse2("quote trade-start deadline too close to execute");
2331
+ if (!Number.isFinite(deadlineMs) || deadlineMs <= Date.now()) return refuse2("quote trade-start deadline already passed", "quote_expired");
2332
+ if (params.dry === false && deadlineMs - Date.now() < MIN_START_HEADROOM_MS) return refuse2("quote trade-start deadline too close to execute", "quote_expired");
2241
2333
  const inputScaled = BigInt(q.inputUnits) * 10n ** BigInt(dest.decimals);
2242
2334
  const outputScaled = BigInt(q.outputUnits) * 10n ** BigInt(source.decimals);
2243
- if (outputScaled * 2n < inputScaled || outputScaled > inputScaled * 2n) return refuse2("quoted output is outside 0.5x\u20132x of the input");
2244
- if (await quoteIdInUse(q.quoteId)) return refuse2("the platform reused a quote id that already belongs to an order");
2335
+ if (outputScaled * 2n < inputScaled || outputScaled > inputScaled * 2n) return refuse2("quoted output is outside 0.5x\u20132x of the input", "price_check_failed");
2336
+ if (await quoteIdInUse(q.quoteId)) return refuse2("the platform reused a quote id that already belongs to an order", "policy_refused");
2245
2337
  if (!await claimBuilt(q.quoteId, { quote: q, at: Date.now(), bound: { trader, dest: params.destAddress, srcChain: params.sourceChain, kind: "order" } })) {
2246
- return refuse2("the platform reused a quote id that already holds another quote");
2338
+ return refuse2("the platform reused a quote id that already holds another quote", "policy_refused");
2247
2339
  }
2248
2340
  const feeUnits = Number(q.protocolFeeUnits || "0") + Number(q.integratorFeeUnits || "0");
2249
2341
  const feeUsd = feeUnits / 10 ** dest.decimals;
@@ -2274,29 +2366,29 @@ function omnistonProvider(opts = {}) {
2274
2366
  */
2275
2367
  async buildDeposit(quote) {
2276
2368
  if (!Number.isFinite(quote.validUntilMs) || quote.validUntilMs <= Date.now()) {
2277
- throw new Error("omniston: quote expired \u2014 request a fresh quote");
2369
+ throw (0, import_errors.refusal)("quote_expired", "omniston: quote expired \u2014 request a fresh quote");
2278
2370
  }
2279
2371
  const decoded = decodeIntent(quote.intentId);
2280
2372
  if (!decoded) throw new Error("omniston: malformed intent id");
2281
2373
  const cached = await loadBuilt(decoded.quoteId);
2282
- if (!cached) throw new Error("omniston: quote expired \u2014 request a fresh quote");
2374
+ if (!cached) throw (0, import_errors.refusal)("quote_expired", "omniston: quote expired \u2014 request a fresh quote");
2283
2375
  const ref = requireBinding(decoded, cached, "buildDeposit");
2284
2376
  const q = cached.quote;
2285
2377
  if (q.settlementData.$case === "swap") return await buildSwapDeposit(ref, q, q.settlementData.value);
2286
2378
  if (q.settlementData.$case !== "order" || q.settlementData.value.tradeStartDeadline * 1e3 <= Date.now()) {
2287
- throw new Error("omniston: quote trade deadline expired \u2014 request a fresh quote");
2379
+ throw (0, import_errors.refusal)("quote_expired", "omniston: quote trade deadline expired \u2014 request a fresh quote");
2288
2380
  }
2289
2381
  if (!ref.trader) throw new Error("omniston: quote was requested without a refund/source address");
2290
2382
  const srcCase = q.inputAsset.chain.$case;
2291
2383
  if (srcCase !== "ton") return await buildEvmOrder(ref, q, q.settlementData.value, cached.bound);
2292
2384
  const srcChain = ref.srcChain;
2293
- if (!isTon(srcChain)) throw new Error("omniston: source chain does not match TON quote");
2385
+ if (!isTon(srcChain)) throw (0, import_errors.refusal)("policy_refused", "omniston: source chain does not match TON quote");
2294
2386
  const destCase = q.outputAsset.chain.$case;
2295
2387
  const traderDst = destCase === "ton" ? null : { chain: { $case: destCase, value: (0, import_viem.getAddress)(ref.dest) } };
2296
- if (!traderDst) throw new Error("omniston: TON-destination orders are not supported");
2388
+ if (!traderDst) throw (0, import_errors.refusal)("unsupported_pair", "omniston: TON-destination orders are not supported");
2297
2389
  const minter = q.settlementData.value.srcProtocolContractAddress;
2298
2390
  if (minter.chain.$case !== "ton" || !isTrusted(srcChain, minter.chain.value)) {
2299
- throw new Error(`omniston: untrusted TON settlement contract ${minter.chain.value}`);
2391
+ throw (0, import_errors.refusal)("vault_mismatch", `omniston: untrusted TON settlement contract ${minter.chain.value}`);
2300
2392
  }
2301
2393
  const settlement = q.settlementData;
2302
2394
  const hashingFunction = settlement.$case === "order" ? settlement.value.htlcHashingFunction : void 0;
@@ -2320,27 +2412,27 @@ function omnistonProvider(opts = {}) {
2320
2412
  });
2321
2413
  } catch (error) {
2322
2414
  await forget();
2323
- throw error;
2415
+ throw venueFailure(error);
2324
2416
  }
2325
2417
  const [message, ...rest] = tx.messages;
2326
2418
  if (!message || rest.length > 0) {
2327
2419
  await forget();
2328
- throw new Error(`omniston: expected exactly one escrow message, got ${tx.messages.length}`);
2420
+ throw (0, import_errors.refusal)("policy_refused", `omniston: expected exactly one escrow message, got ${tx.messages.length}`);
2329
2421
  }
2330
2422
  if (!isPositiveInteger(message.sendAmount) || BigInt(message.sendAmount) > BigInt(maxTonExecutionNano)) {
2331
2423
  await forget();
2332
- throw new Error("omniston: resolver TON execution value exceeds the configured safety cap");
2424
+ throw (0, import_errors.refusal)("policy_refused", "omniston: resolver TON execution value exceeds the configured safety cap");
2333
2425
  }
2334
2426
  const expectedProtocol = q.settlementData.value.srcProtocolContractAddress;
2335
2427
  const initialResolver = q.settlementData.value.srcInitialResolverAddress;
2336
2428
  const jettonMaster = assetContract(q.inputAsset);
2337
2429
  if (expectedProtocol.chain.$case !== "ton" || initialResolver?.chain.$case !== "ton" || !jettonMaster) {
2338
2430
  await forget();
2339
- throw new Error("omniston: incomplete TON settlement binding in quote");
2431
+ throw (0, import_errors.refusal)("policy_refused", "omniston: incomplete TON settlement binding in quote");
2340
2432
  }
2341
2433
  if (message.jettonWalletStateInit) {
2342
2434
  await forget();
2343
- throw new Error("omniston: deploying a resolver-provided jetton wallet is not supported");
2435
+ throw (0, import_errors.refusal)("policy_refused", "omniston: deploying a resolver-provided jetton wallet is not supported");
2344
2436
  }
2345
2437
  let walletValid = false;
2346
2438
  try {
@@ -2355,11 +2447,11 @@ function omnistonProvider(opts = {}) {
2355
2447
  }
2356
2448
  if (!walletValid) {
2357
2449
  await forget();
2358
- throw new Error("omniston: TON jetton wallet was not independently validated");
2450
+ throw (0, import_errors.refusal)("vault_mismatch", "omniston: TON jetton wallet was not independently validated");
2359
2451
  }
2360
2452
  try {
2361
2453
  if (!opts.validateTonEscrowPayload) {
2362
- throw new Error("omniston: TON source requires an independent escrow-payload validator");
2454
+ throw (0, import_errors.refusal)("unsupported_pair", "omniston: TON source requires an independent escrow-payload validator");
2363
2455
  }
2364
2456
  await validateTonEscrowPayload(message, {
2365
2457
  quoteId: ref.quoteId,
@@ -2406,7 +2498,7 @@ function omnistonProvider(opts = {}) {
2406
2498
  const result = bindIntent(decoded, stored);
2407
2499
  if (!result.ok && result.reason === "mismatch") {
2408
2500
  trace("intent-mismatch", { where: "trackIntent", field: result.field });
2409
- throw new Error(`omniston: intent id does not match the quote it was issued for (${result.field})`);
2501
+ throw (0, import_errors.refusal)(mismatchReason(result.field), `omniston: intent id does not match the quote it was issued for (${result.field})`);
2410
2502
  }
2411
2503
  if (result.ok) {
2412
2504
  if (result.legacy) trace("legacy-intent", { where: "trackIntent" });
@@ -2586,12 +2678,12 @@ function omnistonProvider(opts = {}) {
2586
2678
  if (!ref) throw new Error("omniston: malformed intent id");
2587
2679
  const cached = await loadBuilt(ref.quoteId);
2588
2680
  const order = cached?.evmOrder;
2589
- if (!order) throw new Error("omniston: no signed-order context for this quote \u2014 build the order again");
2681
+ if (!order) throw (0, import_errors.refusal)("quote_expired", "omniston: no signed-order context for this quote \u2014 build the order again");
2590
2682
  requireBinding(ref, cached, "registerSignedOrder");
2591
2683
  if (!/^0x[0-9a-fA-F]{130}$/.test(signature)) throw new Error("omniston: malformed order signature");
2592
2684
  const td = JSON.parse(order.typedData);
2593
2685
  const omni = await getClient();
2594
- await omni.orderRegisterSignedOrder({
2686
+ const request = {
2595
2687
  quoteId: ref.quoteId,
2596
2688
  ownerSrcAddress: { chain: { $case: order.chainCase, value: order.owner } },
2597
2689
  signedOrder: { order: { $case: "evmV1", value: {
@@ -2600,7 +2692,12 @@ function omnistonProvider(opts = {}) {
2600
2692
  orderExtension: hexToBytes(order.orderExtension)
2601
2693
  } } },
2602
2694
  ...order.serializedOrderDetails ? { serializedOrderDetails: hexToBytes(order.serializedOrderDetails) } : {}
2603
- });
2695
+ };
2696
+ try {
2697
+ await omni.orderRegisterSignedOrder(request);
2698
+ } catch (error) {
2699
+ throw venueFailure(error);
2700
+ }
2604
2701
  },
2605
2702
  close() {
2606
2703
  clientGeneration += 1;