@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/protocol.cjs CHANGED
@@ -426,6 +426,7 @@ function sameJetton(a, b) {
426
426
  }
427
427
 
428
428
  // src/provider.ts
429
+ var import_errors = require("@anywalletkit/protocol-swap2-core/errors");
429
430
  var import_core4 = require("@anywalletkit/core");
430
431
  var EVM_CASE = {
431
432
  1: "ethereum",
@@ -563,6 +564,54 @@ function encodeTypedMessage(td) {
563
564
  (0, import_viem.encodeAbiParameters)([paramFor(td.primaryType)], [td.message])
564
565
  );
565
566
  }
567
+ function venueReason(error) {
568
+ const details = typeof error === "object" && error !== null ? error.details : void 0;
569
+ const info = typeof details === "object" && details !== null ? details.error_info : void 0;
570
+ if (typeof info !== "object" || info === null) return "upstream_unavailable";
571
+ const { reason, metadata } = info;
572
+ const meta = typeof metadata === "object" && metadata !== null ? metadata : {};
573
+ const type = typeof meta.type === "string" ? meta.type : "";
574
+ switch (reason) {
575
+ case "INVALID_ARGUMENT":
576
+ case "INVALID_PARAMETERS":
577
+ return void 0;
578
+ case "NOT_FOUND":
579
+ return typeof meta.object_type === "string" && meta.object_type.toLowerCase() === "quote" ? "quote_expired" : "upstream_unavailable";
580
+ case "UNSUPPORTED":
581
+ return "unsupported_pair";
582
+ case "SWAP_LIMIT_EXCEEDED":
583
+ return "amount_too_large";
584
+ case "QUOTE_VALIDATION_FAILED":
585
+ if (type === "EMULATION_UNSUPPORTED_TOKEN") return "unsupported_pair";
586
+ if (type === "EMULATION_WRONG_OUTPUT_AMOUNT" || type === "EMULATION_REFUND_SLIPPAGE") return "price_check_failed";
587
+ return type.startsWith("EMULATION_") ? "simulation_failed" : "upstream_unavailable";
588
+ default:
589
+ return "upstream_unavailable";
590
+ }
591
+ }
592
+ function venueFailure(error) {
593
+ if (!(error instanceof Error)) return error;
594
+ const reason = venueReason(error);
595
+ return reason ? (0, import_errors.withReason)(error, reason) : error;
596
+ }
597
+ function mismatchReason(field) {
598
+ return field === "trader" || field === "dest" || field === "owner" ? "recipient_mismatch" : "policy_refused";
599
+ }
600
+ function tonSwapPayloadReason(code) {
601
+ switch (code) {
602
+ case "receiver-not-destination":
603
+ case "refund-not-trader":
604
+ case "excess-not-trader":
605
+ return "recipient_mismatch";
606
+ case "min-out-zero":
607
+ case "min-out-below-quote":
608
+ return "price_check_failed";
609
+ case "leg-unverified":
610
+ return "vault_mismatch";
611
+ default:
612
+ return "policy_refused";
613
+ }
614
+ }
566
615
  function generateSecret() {
567
616
  return crypto.getRandomValues(new Uint8Array(32));
568
617
  }
@@ -573,7 +622,7 @@ function hashlock(secret, hashingFunction) {
573
622
  case "HASHING_FUNCTION_SHA256":
574
623
  return (0, import_viem.sha256)(secret, "bytes");
575
624
  default:
576
- throw new Error(`omniston: unsupported HTLC hashing function ${String(hashingFunction)}`);
625
+ throw (0, import_errors.refusal)("policy_refused", `omniston: unsupported HTLC hashing function ${String(hashingFunction)}`);
577
626
  }
578
627
  }
579
628
  function sameAsset(a, b) {
@@ -611,7 +660,7 @@ function stepJetton(asset) {
611
660
  }
612
661
  function readBigEndian(bytes, offset, length) {
613
662
  if (!Number.isInteger(offset) || !Number.isInteger(length) || length < 1 || offset < 0 || offset + length > bytes.length) {
614
- throw new Error("omniston: truncated EVM order extension");
663
+ throw (0, import_errors.refusal)("policy_refused", "omniston: truncated EVM order extension");
615
664
  }
616
665
  let value = 0n;
617
666
  for (let index = offset; index < offset + length; index++) value = value << 8n | BigInt(bytes[index]);
@@ -656,30 +705,30 @@ var EVM_DEST_PROTOCOL_FEE_RECIPIENTS = /* @__PURE__ */ new Set(["0x076268146b4ec
656
705
  var EVM_AUCTION_POINTS = [[800000n, 100n], [700000n, 100n], [600000n, 100n], [500000n, 100n], [400000n, 100n]];
657
706
  var EVM_TIMELOCKS = [1n, 120n, 240n, 300n, 1n, 120n, 180n, 0n].reduce((packed, value, index) => packed | value << BigInt(index * 32), 0n);
658
707
  function readEvmOrderExtension(extension) {
659
- if (extension.length < 32) throw new Error("omniston: malformed EVM order extension");
708
+ if (extension.length < 32) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed EVM order extension");
660
709
  const view = new DataView(extension.buffer, extension.byteOffset, extension.byteLength);
661
710
  const fields = [];
662
711
  let begin = 0;
663
712
  const concatLength = extension.length - 32;
664
713
  for (let index = 0; index < 8; index++) {
665
714
  const end2 = view.getUint32(28 - index * 4, false);
666
- if (end2 < begin || end2 > concatLength) throw new Error("omniston: malformed EVM order extension offsets");
715
+ if (end2 < begin || end2 > concatLength) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed EVM order extension offsets");
667
716
  fields.push(extension.slice(32 + begin, 32 + end2));
668
717
  begin = end2;
669
718
  }
670
719
  for (const index of [0, 1, 4, 5, 6]) {
671
- if (fields[index].length !== 0) throw new Error("omniston: unsupported EVM order extension field");
720
+ if (fields[index].length !== 0) throw (0, import_errors.refusal)("policy_refused", "omniston: unsupported EVM order extension field");
672
721
  }
673
722
  if (!sliceEqual(fields[2], fields[3])) {
674
- throw new Error("omniston: making/taking amount getters use different programs");
723
+ throw (0, import_errors.refusal)("policy_refused", "omniston: making/taking amount getters use different programs");
675
724
  }
676
725
  let target;
677
726
  for (const index of [2, 3, 7]) {
678
727
  const field2 = fields[index];
679
- if (field2.length <= 20) throw new Error("omniston: incomplete EVM order extension program");
728
+ if (field2.length <= 20) throw (0, import_errors.refusal)("policy_refused", "omniston: incomplete EVM order extension program");
680
729
  const address = (0, import_viem.getAddress)(`0x${bytesToHex(field2.slice(0, 20))}`);
681
730
  if (target !== void 0 && (0, import_viem.getAddress)(target) !== address) {
682
- throw new Error("omniston: EVM order extension target is not independently trusted");
731
+ throw (0, import_errors.refusal)("vault_mismatch", "omniston: EVM order extension target is not independently trusted");
683
732
  }
684
733
  target = address;
685
734
  }
@@ -697,7 +746,7 @@ function readEvmOrderExtension(extension) {
697
746
  cursor += 3;
698
747
  const pointCount = (0, import_core4.bigintToNumber)(readBigEndian(field, cursor, 1));
699
748
  cursor += 1;
700
- if (pointCount !== 5) throw new Error("omniston: unsupported EVM auction profile");
749
+ if (pointCount !== 5) throw (0, import_errors.refusal)("policy_refused", "omniston: unsupported EVM auction profile");
701
750
  const points = [];
702
751
  for (let index = 0; index < pointCount; index++) {
703
752
  points.push([readBigEndian(field, cursor, 3), readBigEndian(field, cursor + 3, 2)]);
@@ -714,7 +763,7 @@ function readEvmOrderExtension(extension) {
714
763
  const getterWhitelistCount = (0, import_core4.bigintToNumber)(readBigEndian(field, cursor, 1));
715
764
  cursor += 1;
716
765
  if (getterWhitelistCount !== 1 || cursor + getterWhitelistCount * 10 !== field.length) {
717
- throw new Error("omniston: malformed EVM amount-getter whitelist");
766
+ throw (0, import_errors.refusal)("policy_refused", "omniston: malformed EVM amount-getter whitelist");
718
767
  }
719
768
  const getter = { gasBump, gasPrice, auctionStart, duration, initialRateBump, points, integratorFee, integratorShare, resolverFee, whitelistDiscount, resolver: readBigEndian(field, cursor, 10) };
720
769
  const post = fields[7];
@@ -736,7 +785,7 @@ function readEvmOrderExtension(extension) {
736
785
  const whitelistCount = (0, import_core4.bigintToNumber)(readBigEndian(post, cursor, 1));
737
786
  cursor += 1;
738
787
  if (whitelistCount !== 1 || cursor + whitelistCount * 12 + 160 !== post.length) {
739
- throw new Error("omniston: malformed EVM post-interaction whitelist");
788
+ throw (0, import_errors.refusal)("policy_refused", "omniston: malformed EVM post-interaction whitelist");
740
789
  }
741
790
  const postResolver = readBigEndian(post, cursor, 10);
742
791
  cursor += 10;
@@ -748,7 +797,7 @@ function readEvmOrderExtension(extension) {
748
797
  return value;
749
798
  });
750
799
  const customData = extension.slice(32 + begin);
751
- if (customData.length !== 131) throw new Error("omniston: malformed EVM order extension custom data");
800
+ if (customData.length !== 131) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed EVM order extension custom data");
752
801
  return {
753
802
  target,
754
803
  getter,
@@ -761,29 +810,29 @@ async function validateEvmToTonOrderExtension(extension, expected) {
761
810
  const evmDest = expected.dstChainId !== void 0;
762
811
  if (evmDest && (!Number.isSafeInteger(expected.dstChainId) || expected.dstChainId <= 0)) throw new Error("omniston: invalid EVM destination chain id");
763
812
  const { target, getter, post, extra, customData } = readEvmOrderExtension(extension);
764
- if (!expected.trustedTarget(target)) throw new Error("omniston: EVM order extension target is not independently trusted");
813
+ if (!expected.trustedTarget(target)) throw (0, import_errors.refusal)("vault_mismatch", "omniston: EVM order extension target is not independently trusted");
765
814
  const quoteResolverFee = expected.protocolFeePips % 10 === 0 ? BigInt(expected.protocolFeePips / 10) : -1n;
766
815
  const resolverLow80 = BigInt((0, import_viem.getAddress)(expected.initialResolverAddress)) & (1n << 80n) - 1n;
767
816
  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) {
768
- throw new Error("omniston: EVM amount-getter terms do not match the pinned v1beta8 profile");
817
+ throw (0, import_errors.refusal)("policy_refused", "omniston: EVM amount-getter terms do not match the pinned v1beta8 profile");
769
818
  }
770
819
  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) {
771
- throw new Error("omniston: EVM post-interaction terms do not match the pinned v1beta8 profile");
820
+ throw (0, import_errors.refusal)("policy_refused", "omniston: EVM post-interaction terms do not match the pinned v1beta8 profile");
772
821
  }
773
822
  const hashlock2 = readBigEndian(expected.secretHash, 0, expected.secretHash.length);
774
823
  const dstToken = evmDest ? BigInt((0, import_viem.getAddress)(expected.outputTokenAddress)) : BigInt((0, import_viem.getAddress)(expected.takerAsset));
775
824
  const deposits = BigInt(expected.srcSecurityDepositUnits) << 128n | BigInt(expected.dstSecurityDepositUnits);
776
825
  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) {
777
- throw new Error("omniston: signed EVM HTLC terms do not match the validated order details");
826
+ throw (0, import_errors.refusal)("policy_refused", "omniston: signed EVM HTLC terms do not match the validated order details");
778
827
  }
779
828
  await checkOrderCustomData(customData, { evmDest, outputToken: expected.outputTokenAddress, traderDst: expected.traderDstAddress, quoteId: expected.quoteId });
780
829
  if (expected.tradeStartDeadline < expected.quoteTimestamp) {
781
- throw new Error("omniston: invalid EVM order quote deadline");
830
+ throw (0, import_errors.refusal)("quote_expired", "omniston: invalid EVM order quote deadline");
782
831
  }
783
832
  }
784
833
  async function checkOrderCustomData(customData, expected) {
785
834
  if (expected.evmDest) {
786
- if (!zeroBytes(customData.slice(0, 66))) throw new Error("omniston: unexpected TON addresses in an EVM-destination order");
835
+ if (!zeroBytes(customData.slice(0, 66))) throw (0, import_errors.refusal)("recipient_mismatch", "omniston: unexpected TON addresses in an EVM-destination order");
787
836
  } else {
788
837
  const { Address: Address3 } = await import("@ton/core");
789
838
  const expectedTon = [expected.outputToken, expected.traderDst].map((value) => Address3.parse(value));
@@ -791,16 +840,16 @@ async function checkOrderCustomData(customData, expected) {
791
840
  const offset = index * 33;
792
841
  const workchain = customData[offset] === 255 ? -1 : customData[offset];
793
842
  if (workchain !== expectedTon[index].workChain || !sliceEqual(customData.slice(offset + 1, offset + 33), expectedTon[index].hash)) {
794
- throw new Error("omniston: signed EVM\u2192TON address does not match the validated request");
843
+ throw (0, import_errors.refusal)("recipient_mismatch", "omniston: signed EVM\u2192TON address does not match the validated request");
795
844
  }
796
845
  }
797
846
  }
798
847
  if (!zeroBytes(customData.slice(66, 99))) {
799
- throw new Error("omniston: unexpected private TON disclosure address");
848
+ throw (0, import_errors.refusal)("policy_refused", "omniston: unexpected private TON disclosure address");
800
849
  }
801
850
  const quoteId = expected.quoteId.replace(/^0x/, "");
802
851
  if (!QUOTE_ID.test(quoteId) || !sliceEqual(customData.slice(99), hexToBytes(quoteId.padStart(64, "0")))) {
803
- throw new Error("omniston: EVM order extension custom data is not bound to the quote id");
852
+ throw (0, import_errors.refusal)("policy_refused", "omniston: EVM order extension custom data is not bound to the quote id");
804
853
  }
805
854
  }
806
855
  function validateEvmTypedOrder(td, expected) {
@@ -822,7 +871,7 @@ function validateEvmTypedOrder(td, expected) {
822
871
  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
823
872
  // skew between the quote and the order builder), never later.
824
873
  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)) {
825
- throw new Error("omniston: EVM order fields do not match the validated quote/request");
874
+ throw (0, import_errors.refusal)("policy_refused", "omniston: EVM order fields do not match the validated quote/request");
826
875
  }
827
876
  }
828
877
  async function validateTonEscrowPayload(message, expected, validateForwardPayload) {
@@ -832,9 +881,9 @@ async function validateTonEscrowPayload(message, expected, validateForwardPayloa
832
881
  try {
833
882
  roots = Cell2.fromBoc(import_buffer2.Buffer.from(boc));
834
883
  } catch {
835
- throw new Error("omniston: malformed TON escrow BoC");
884
+ throw (0, import_errors.refusal)("policy_refused", "omniston: malformed TON escrow BoC");
836
885
  }
837
- if (roots.length !== 1) throw new Error("omniston: TON escrow must contain exactly one root cell");
886
+ if (roots.length !== 1) throw (0, import_errors.refusal)("policy_refused", "omniston: TON escrow must contain exactly one root cell");
838
887
  const slice = roots[0].beginParse();
839
888
  const op = slice.loadUint(32);
840
889
  slice.loadUintBig(64);
@@ -845,7 +894,7 @@ async function validateTonEscrowPayload(message, expected, validateForwardPayloa
845
894
  const forwardAmount = slice.loadCoins();
846
895
  const payloadByRef = slice.loadBit();
847
896
  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) {
848
- throw new Error("omniston: TON jetton transfer does not match the validated quote/request");
897
+ throw (0, import_errors.refusal)("policy_refused", "omniston: TON jetton transfer does not match the validated quote/request");
849
898
  }
850
899
  const forwardPayload = slice.loadRef();
851
900
  const verified = await validateForwardPayload({
@@ -865,7 +914,7 @@ async function validateTonEscrowPayload(message, expected, validateForwardPayloa
865
914
  ...expected.hashingFunction ? { hashingFunction: expected.hashingFunction } : {},
866
915
  exclusivityTimeout: expected.exclusivityTimeout
867
916
  });
868
- if (!verified) throw new Error("omniston: TON escrow forward payload failed independent semantic validation");
917
+ if (!verified) throw (0, import_errors.refusal)("policy_refused", "omniston: TON escrow forward payload failed independent semantic validation");
869
918
  }
870
919
  function feeToken(asset) {
871
920
  return asset.assetId || asset.contract || asset.symbol;
@@ -895,24 +944,24 @@ var ProtoReader = class {
895
944
  const byte = this.bytes[this.pos++];
896
945
  out += (byte & 127) * 2 ** shift;
897
946
  if ((byte & 128) === 0) {
898
- if (!Number.isSafeInteger(out)) throw new Error("omniston: unsafe protobuf varint");
947
+ if (!Number.isSafeInteger(out)) throw (0, import_errors.refusal)("policy_refused", "omniston: unsafe protobuf varint");
899
948
  return out;
900
949
  }
901
950
  shift += 7;
902
951
  }
903
- throw new Error("omniston: malformed protobuf varint");
952
+ throw (0, import_errors.refusal)("policy_refused", "omniston: malformed protobuf varint");
904
953
  }
905
954
  field() {
906
955
  const tag = this.varint();
907
956
  const number = Math.floor(tag / 8);
908
957
  const wire = tag & 7;
909
- if (number < 1) throw new Error("omniston: malformed protobuf field");
958
+ if (number < 1) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed protobuf field");
910
959
  return { number, wire };
911
960
  }
912
961
  lengthDelimited() {
913
962
  const length = this.varint();
914
963
  const end2 = this.pos + length;
915
- if (length < 0 || end2 > this.bytes.length) throw new Error("omniston: truncated protobuf payload");
964
+ if (length < 0 || end2 > this.bytes.length) throw (0, import_errors.refusal)("policy_refused", "omniston: truncated protobuf payload");
916
965
  const value = this.bytes.slice(this.pos, end2);
917
966
  this.pos = end2;
918
967
  return value;
@@ -934,10 +983,10 @@ var ProtoReader = class {
934
983
  this.advance(4);
935
984
  return;
936
985
  }
937
- throw new Error("omniston: unsupported protobuf wire type");
986
+ throw (0, import_errors.refusal)("policy_refused", "omniston: unsupported protobuf wire type");
938
987
  }
939
988
  advance(length) {
940
- if (this.pos + length > this.bytes.length) throw new Error("omniston: truncated protobuf payload");
989
+ if (this.pos + length > this.bytes.length) throw (0, import_errors.refusal)("policy_refused", "omniston: truncated protobuf payload");
941
990
  this.pos += length;
942
991
  }
943
992
  };
@@ -964,10 +1013,10 @@ function decodeChainAddress(bytes) {
964
1013
  while (!reader.done) {
965
1014
  const { number, wire } = reader.field();
966
1015
  const $case = PROTO_CASE_BY_FIELD[number];
967
- if (!$case || wire !== 2 || address) throw new Error("omniston: malformed chain address in order details");
1016
+ if (!$case || wire !== 2 || address) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed chain address in order details");
968
1017
  address = { chain: { $case, value: utf8.decode(reader.lengthDelimited()) } };
969
1018
  }
970
- if (!address?.chain.value) throw new Error("omniston: empty chain address in order details");
1019
+ if (!address?.chain.value) throw (0, import_errors.refusal)("policy_refused", "omniston: empty chain address in order details");
971
1020
  return address;
972
1021
  }
973
1022
  function decodeAssetId(bytes) {
@@ -976,27 +1025,27 @@ function decodeAssetId(bytes) {
976
1025
  while (!reader.done) {
977
1026
  const { number, wire } = reader.field();
978
1027
  const $case = PROTO_CASE_BY_FIELD[number];
979
- if (!$case || wire !== 2 || asset) throw new Error("omniston: malformed asset id in order details");
1028
+ if (!$case || wire !== 2 || asset) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed asset id in order details");
980
1029
  const kindReader = new ProtoReader(reader.lengthDelimited());
981
1030
  const kindField = kindReader.field();
982
1031
  if (kindField.wire !== 2 || kindField.number !== 1 && kindField.number !== 2) {
983
- throw new Error("omniston: unsupported asset kind in order details");
1032
+ throw (0, import_errors.refusal)("policy_refused", "omniston: unsupported asset kind in order details");
984
1033
  }
985
1034
  const kindCase = kindField.number === 1 ? "native" : $case === "ton" ? "jetton" : $case === "tron" ? "trc20" : "erc20";
986
1035
  const value = kindField.number === 1 ? {} : utf8.decode(kindReader.lengthDelimited());
987
- if (!kindReader.done || kindField.number === 2 && !value) throw new Error("omniston: malformed asset kind in order details");
1036
+ if (!kindReader.done || kindField.number === 2 && !value) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed asset kind in order details");
988
1037
  asset = { chain: { $case, value: { kind: { $case: kindCase, value } } } };
989
1038
  }
990
- if (!asset) throw new Error("omniston: empty asset id in order details");
1039
+ if (!asset) throw (0, import_errors.refusal)("policy_refused", "omniston: empty asset id in order details");
991
1040
  return asset;
992
1041
  }
993
1042
  function validateSerializedOrderDetails(bytes, expected) {
994
- if (!bytes?.length) throw new Error("omniston: EVM HTLC order payload has no serialized details");
1043
+ if (!bytes?.length) throw (0, import_errors.refusal)("policy_refused", "omniston: EVM HTLC order payload has no serialized details");
995
1044
  const wrapper = new ProtoReader(bytes);
996
1045
  const outer = wrapper.field();
997
- if (outer.number !== 1 || outer.wire !== 2) throw new Error("omniston: unsupported HTLC order-details version");
1046
+ if (outer.number !== 1 || outer.wire !== 2) throw (0, import_errors.refusal)("policy_refused", "omniston: unsupported HTLC order-details version");
998
1047
  const detailsBytes = wrapper.lengthDelimited();
999
- if (!wrapper.done) throw new Error("omniston: ambiguous HTLC order-details wrapper");
1048
+ if (!wrapper.done) throw (0, import_errors.refusal)("policy_refused", "omniston: ambiguous HTLC order-details wrapper");
1000
1049
  const reader = new ProtoReader(detailsBytes);
1001
1050
  let quoteId;
1002
1051
  let outputAsset;
@@ -1004,16 +1053,16 @@ function validateSerializedOrderDetails(bytes, expected) {
1004
1053
  const hashes = [];
1005
1054
  while (!reader.done) {
1006
1055
  const { number, wire } = reader.field();
1007
- if (wire !== 2) throw new Error("omniston: malformed HTLC order details");
1056
+ if (wire !== 2) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed HTLC order details");
1008
1057
  const value = reader.lengthDelimited();
1009
1058
  if (number === 1 && quoteId === void 0) quoteId = utf8.decode(value);
1010
1059
  else if (number === 2 && outputAsset === void 0) outputAsset = decodeAssetId(value);
1011
1060
  else if (number === 3 && traderDstAddress === void 0) traderDstAddress = decodeChainAddress(value);
1012
1061
  else if (number === 5) hashes.push(value);
1013
- else throw new Error("omniston: duplicate or unexpected HTLC order-details field");
1062
+ else throw (0, import_errors.refusal)("policy_refused", "omniston: duplicate or unexpected HTLC order-details field");
1014
1063
  }
1015
1064
  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]))) {
1016
- throw new Error("omniston: resolver order payload does not match the validated quote/request");
1065
+ throw (0, import_errors.refusal)("policy_refused", "omniston: resolver order payload does not match the validated quote/request");
1017
1066
  }
1018
1067
  }
1019
1068
  function isPositiveInteger(value) {
@@ -1044,7 +1093,7 @@ function bytesToHex(bytes) {
1044
1093
  function hexToBytes(hex) {
1045
1094
  const clean = hex.replace(/^0x/, "");
1046
1095
  if (!clean || !/^(?:[0-9a-fA-F]{2})+$/.test(clean)) {
1047
- throw new Error("omniston: malformed hex payload");
1096
+ throw (0, import_errors.refusal)("policy_refused", "omniston: malformed hex payload");
1048
1097
  }
1049
1098
  const out = new Uint8Array(clean.length / 2);
1050
1099
  for (let i = 0; i < out.length; i++) out[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
@@ -1078,6 +1127,14 @@ function swapProtocols(v) {
1078
1127
  for (const route of v.routes) for (const step of route.steps) for (const chunk of step.chunks) names.add(chunk.protocol);
1079
1128
  return [...names];
1080
1129
  }
1130
+ function rejectionReason(rejection) {
1131
+ return rejection === "quote timestamp out of range" ? "quote_expired" : "upstream_unavailable";
1132
+ }
1133
+ function rfqReason(outcome) {
1134
+ if (outcome.error) return outcome.errorReason;
1135
+ if (outcome.rejected) return rejectionReason(outcome.rejected);
1136
+ return outcome.answered ? "no_route" : "upstream_unavailable";
1137
+ }
1081
1138
  function quoteRejection(q, update, acknowledgedRfqId, request, settlement) {
1082
1139
  if (!q) return "empty quote";
1083
1140
  if (!acknowledgedRfqId || update.rfqId !== acknowledgedRfqId || q.rfqId !== acknowledgedRfqId) return "quote for another RFQ";
@@ -1117,6 +1174,7 @@ function awaitQuote(stream, request, signal, settleMs = QUOTE_SETTLE_MS, settlem
1117
1174
  const max = setTimeout(() => finish(latest), QUOTE_MAX_MS);
1118
1175
  sub = stream.subscribe({
1119
1176
  next: (event) => {
1177
+ if (event.$case === "ack" || event.$case === "quoteUpdated" || event.$case === "noQuote") outcome.answered = true;
1120
1178
  if (event.$case === "ack") {
1121
1179
  acknowledgedRfqId = event.value.rfqId || null;
1122
1180
  } else if (event.$case === "quoteUpdated") {
@@ -1136,6 +1194,7 @@ function awaitQuote(stream, request, signal, settleMs = QUOTE_SETTLE_MS, settlem
1136
1194
  },
1137
1195
  error: (err) => {
1138
1196
  outcome.error = String(err?.message ?? err).slice(0, 200);
1197
+ outcome.errorReason = venueReason(err);
1139
1198
  finish(null);
1140
1199
  },
1141
1200
  complete: () => finish(latest)
@@ -1177,9 +1236,9 @@ function awaitSwapProgress(stream) {
1177
1236
  }
1178
1237
  var JETTON_TRANSFER_OP = 260734629;
1179
1238
  function parseSwapPayload(payloadHex) {
1180
- if (!/^(?:[0-9a-fA-F]{2})+$/.test(payloadHex)) throw new Error("omniston: malformed swap payload hex");
1239
+ if (!/^(?:[0-9a-fA-F]{2})+$/.test(payloadHex)) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed swap payload hex");
1181
1240
  const roots = import_core3.Cell.fromBoc(import_buffer2.Buffer.from(payloadHex, "hex"));
1182
- if (roots.length !== 1) throw new Error("omniston: swap payload must have one root");
1241
+ if (roots.length !== 1) throw (0, import_errors.refusal)("policy_refused", "omniston: swap payload must have one root");
1183
1242
  const s = roots[0].beginParse();
1184
1243
  const op = s.loadUint(32);
1185
1244
  if (op !== JETTON_TRANSFER_OP) return { op, jettonTransfer: null };
@@ -1249,6 +1308,18 @@ function mapOrderStatus(order) {
1249
1308
  return { state: "pending", depositReceived };
1250
1309
  }
1251
1310
  }
1311
+ var VERIFY_NOTES = /* @__PURE__ */ Symbol.for("@anywalletkit/protocol-swap2-omniston/verify-notes@1");
1312
+ var verifyNotes = (() => {
1313
+ const shared = globalThis[VERIFY_NOTES];
1314
+ if (shared && typeof shared.note === "function" && typeof shared.reasonOf === "function") return shared;
1315
+ const notes = (0, import_errors.refusalNotes)();
1316
+ try {
1317
+ Object.defineProperty(globalThis, VERIFY_NOTES, { value: notes });
1318
+ } catch {
1319
+ }
1320
+ return notes;
1321
+ })();
1322
+ var said = verifyNotes.note;
1252
1323
  function omnistonProvider(opts = {}) {
1253
1324
  opts = Object.freeze({ ...opts });
1254
1325
  const apiUrl = opts.apiUrl ?? "wss://omni-ws.ston.fi";
@@ -1428,19 +1499,19 @@ function omnistonProvider(opts = {}) {
1428
1499
  if (!bound.ok) {
1429
1500
  if (bound.reason === "legacy-expired") {
1430
1501
  trace("legacy-intent-expired", { where });
1431
- throw new Error("omniston: quote expired \u2014 request a fresh quote");
1502
+ throw (0, import_errors.refusal)("quote_expired", "omniston: quote expired \u2014 request a fresh quote");
1432
1503
  }
1433
1504
  trace("intent-mismatch", { where, field: bound.field });
1434
- throw new Error(`omniston: intent id does not match the quote it was issued for (${bound.field})`);
1505
+ throw (0, import_errors.refusal)(mismatchReason(bound.field), `omniston: intent id does not match the quote it was issued for (${bound.field})`);
1435
1506
  }
1436
1507
  if (bound.legacy) trace("legacy-intent", { where });
1437
1508
  if (!decoded.digest) {
1438
1509
  trace("intent-mismatch", { where, field: "digest-missing" });
1439
- throw new Error("omniston: this quote was issued before an update \u2014 request a fresh quote");
1510
+ throw (0, import_errors.refusal)("quote_expired", "omniston: this quote was issued before an update \u2014 request a fresh quote");
1440
1511
  }
1441
1512
  if (decoded.digest !== quoteDigest(entry.quote)) {
1442
1513
  trace("intent-mismatch", { where, field: "digest" });
1443
- throw new Error("omniston: quote was replaced \u2014 request a fresh quote");
1514
+ throw (0, import_errors.refusal)("policy_refused", "omniston: quote was replaced \u2014 request a fresh quote");
1444
1515
  }
1445
1516
  return bound.ref;
1446
1517
  }
@@ -1474,10 +1545,10 @@ function omnistonProvider(opts = {}) {
1474
1545
  const disclosed = (await store.load(disclosedKey(quoteId)) ?? []).length > 0;
1475
1546
  if (boundTo !== binding || disclosed) {
1476
1547
  traceFor(quoteId)("secret-reuse-refused", { disclosed, bound: boundTo !== void 0 });
1477
- throw new Error("omniston: this quote id already belongs to another order \u2014 request a fresh quote");
1548
+ throw (0, import_errors.refusal)("policy_refused", "omniston: this quote id already belongs to another order \u2014 request a fresh quote");
1478
1549
  }
1479
1550
  if (existing.length !== count || !existing.every((s) => /^[0-9a-fA-F]{64}$/.test(s))) {
1480
- throw new Error("omniston: stored HTLC secrets for this quote do not fit the order \u2014 request a fresh quote");
1551
+ throw (0, import_errors.refusal)("policy_refused", "omniston: stored HTLC secrets for this quote do not fit the order \u2014 request a fresh quote");
1481
1552
  }
1482
1553
  traceFor(quoteId)("secret-reused", { count });
1483
1554
  return { secrets: existing.map(hexToBytes), created: false };
@@ -1536,7 +1607,10 @@ function omnistonProvider(opts = {}) {
1536
1607
  let q = await rfq(slippagePips);
1537
1608
  if (!q) {
1538
1609
  if (!signal?.aborted) {
1539
- params.onRefusal?.(`omniston: ${outcome.error ? `RFQ failed: ${outcome.error}` : outcome.rejected ? `swap quote rejected: ${outcome.rejected}` : "no TON DEX route quoted this pair/amount"}`);
1610
+ const text = `omniston: ${outcome.error ? `RFQ failed: ${outcome.error}` : outcome.rejected ? `swap quote rejected: ${outcome.rejected}` : "no TON DEX route quoted this pair/amount"}`;
1611
+ const reason = rfqReason(outcome);
1612
+ if (reason) params.onRefusal?.(text, { reason });
1613
+ else params.onRefusal?.(text);
1540
1614
  }
1541
1615
  return null;
1542
1616
  }
@@ -1602,27 +1676,32 @@ function omnistonProvider(opts = {}) {
1602
1676
  async function buildSwapDeposit(ref, q, swap) {
1603
1677
  if (!ref.trader) throw new Error("omniston: quote was requested without a refund/source address");
1604
1678
  if (!isTon(ref.srcChain) || q.inputAsset.chain.$case !== "ton" || q.outputAsset.chain.$case !== "ton") {
1605
- throw new Error("omniston: swap quote is not a TON same-chain swap");
1679
+ throw (0, import_errors.refusal)("policy_refused", "omniston: swap quote is not a TON same-chain swap");
1606
1680
  }
1607
- if (!sameChainTonReady()) throw new Error("omniston: TON same-chain swaps need independent jetton-wallet, swap-target and swap-leg validators");
1681
+ if (!sameChainTonReady()) throw (0, import_errors.refusal)("unsupported_pair", "omniston: TON same-chain swaps need independent jetton-wallet, swap-target and swap-leg validators");
1608
1682
  const owner = { chain: { $case: "ton", value: ref.trader } };
1609
1683
  const receiver = { chain: { $case: "ton", value: ref.dest } };
1610
1684
  const omni = await getClient();
1611
- if (typeof omni.tonBuildSwap !== "function") throw new Error("omniston: client does not support swap settlement (tonBuildSwap)");
1612
- const tx = await omni.tonBuildSwap({
1613
- quoteId: ref.quoteId,
1614
- transferSrcAddress: owner,
1615
- refundSrcAddress: owner,
1616
- gasExcessAddress: owner,
1617
- traderDstAddress: receiver,
1618
- // Build at the slippage the quote was requested with: its
1619
- // minOutputAmount is the minimum this module reported to the router.
1620
- useRecommendedSlippage: false
1621
- });
1685
+ if (typeof omni.tonBuildSwap !== "function") throw (0, import_errors.refusal)("unsupported_pair", "omniston: client does not support swap settlement (tonBuildSwap)");
1686
+ let tx;
1687
+ try {
1688
+ tx = await omni.tonBuildSwap({
1689
+ quoteId: ref.quoteId,
1690
+ transferSrcAddress: owner,
1691
+ refundSrcAddress: owner,
1692
+ gasExcessAddress: owner,
1693
+ traderDstAddress: receiver,
1694
+ // Build at the slippage the quote was requested with: its
1695
+ // minOutputAmount is the minimum this module reported to the router.
1696
+ useRecommendedSlippage: false
1697
+ });
1698
+ } catch (error) {
1699
+ throw venueFailure(error);
1700
+ }
1622
1701
  const [message, ...rest] = tx.messages;
1623
- if (!message || rest.length > 0) throw new Error(`omniston: expected exactly one swap message, got ${tx.messages.length}`);
1624
- if (message.jettonWalletStateInit) throw new Error("omniston: deploying a resolver-provided jetton wallet is not supported");
1625
- if (!isPositiveInteger(message.sendAmount)) throw new Error("omniston: malformed swap message value");
1702
+ if (!message || rest.length > 0) throw (0, import_errors.refusal)("policy_refused", `omniston: expected exactly one swap message, got ${tx.messages.length}`);
1703
+ if (message.jettonWalletStateInit) throw (0, import_errors.refusal)("policy_refused", "omniston: deploying a resolver-provided jetton wallet is not supported");
1704
+ if (!isPositiveInteger(message.sendAmount)) throw (0, import_errors.refusal)("policy_refused", "omniston: malformed swap message value");
1626
1705
  const sendAmount = BigInt(message.sendAmount);
1627
1706
  const gasCap = BigInt(maxTonExecutionNano);
1628
1707
  const gasBudget = q.gasBudget && isPositiveInteger(q.gasBudget) ? BigInt(q.gasBudget) : gasCap;
@@ -1634,53 +1713,53 @@ function omnistonProvider(opts = {}) {
1634
1713
  const ptonV1Native = !inputJetton && !!payload.jettonTransfer && singleV1;
1635
1714
  if (inputJetton) {
1636
1715
  const transfer = payload.jettonTransfer;
1637
- if (!transfer) throw new Error("omniston: jetton-input swap message is not a jetton transfer");
1638
- 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("+")})`);
1639
- if (transfer.amount !== BigInt(q.inputUnits)) throw new Error("omniston: swap message moves a different amount than quoted");
1716
+ if (!transfer) throw (0, import_errors.refusal)("policy_refused", "omniston: jetton-input swap message is not a jetton transfer");
1717
+ 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("+")})`);
1718
+ if (transfer.amount !== BigInt(q.inputUnits)) throw (0, import_errors.refusal)("policy_refused", "omniston: swap message moves a different amount than quoted");
1640
1719
  if (!transfer.responseDestination || !sameTonAddress(transfer.responseDestination, ref.trader)) {
1641
- throw new Error("omniston: swap message returns excess to a wallet other than the trader");
1720
+ throw (0, import_errors.refusal)("recipient_mismatch", "omniston: swap message returns excess to a wallet other than the trader");
1642
1721
  }
1643
- if (transfer.forwardTon >= sendAmount) throw new Error("omniston: swap message forwards more TON than it carries");
1722
+ if (transfer.forwardTon >= sendAmount) throw (0, import_errors.refusal)("policy_refused", "omniston: swap message forwards more TON than it carries");
1644
1723
  const walletValid = await opts.validateTonJettonWallet({
1645
1724
  ownerAddress: ref.trader,
1646
1725
  jettonMasterAddress: inputJetton,
1647
1726
  walletAddress: message.targetAddress
1648
1727
  });
1649
- if (!walletValid) throw new Error("omniston: TON jetton wallet was not independently validated");
1728
+ if (!walletValid) throw (0, import_errors.refusal)("vault_mismatch", "omniston: TON jetton wallet was not independently validated");
1650
1729
  const targetValid = await opts.validateTonSwapTarget({ address: transfer.destination, protocols });
1651
- if (!targetValid) throw new Error("omniston: swap destination is not a recognized DEX contract for the quoted protocol");
1730
+ if (!targetValid) throw (0, import_errors.refusal)("vault_mismatch", "omniston: swap destination is not a recognized DEX contract for the quoted protocol");
1652
1731
  } else if (ptonV1Native) {
1653
1732
  const transfer = payload.jettonTransfer;
1654
1733
  const input = BigInt(q.inputUnits);
1655
- if (transfer.amount !== input) throw new Error("omniston: swap message moves a different amount than quoted");
1656
- if (sendAmount < input) throw new Error("omniston: swap message carries less than the quoted input");
1734
+ if (transfer.amount !== input) throw (0, import_errors.refusal)("policy_refused", "omniston: swap message moves a different amount than quoted");
1735
+ if (sendAmount < input) throw (0, import_errors.refusal)("policy_refused", "omniston: swap message carries less than the quoted input");
1657
1736
  const gas = sendAmount - input;
1658
- 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("+")})`);
1659
- if (transfer.forwardTon > gas) throw new Error("omniston: swap message forwards more TON than its gas");
1737
+ 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("+")})`);
1738
+ if (transfer.forwardTon > gas) throw (0, import_errors.refusal)("policy_refused", "omniston: swap message forwards more TON than its gas");
1660
1739
  if (!transfer.responseDestination || !sameTonAddress(transfer.responseDestination, ref.trader)) {
1661
- throw new Error("omniston: swap message returns excess to a wallet other than the trader");
1740
+ throw (0, import_errors.refusal)("recipient_mismatch", "omniston: swap message returns excess to a wallet other than the trader");
1662
1741
  }
1663
- if (typeof opts.validateTonSwapLeg !== "function") throw new Error("omniston: STON.fi v1 native input needs the validateTonSwapLeg validator (pTON v1 wallet ownership)");
1742
+ 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)");
1664
1743
  const targetValid = await opts.validateTonSwapTarget({ address: message.targetAddress, protocols });
1665
- if (!targetValid) throw new Error("omniston: swap target is not a recognized DEX contract for the quoted protocol");
1744
+ if (!targetValid) throw (0, import_errors.refusal)("vault_mismatch", "omniston: swap target is not a recognized DEX contract for the quoted protocol");
1666
1745
  const routerValid = await opts.validateTonSwapTarget({ address: transfer.destination, protocols });
1667
- if (!routerValid) throw new Error("omniston: swap destination is not a recognized DEX contract for the quoted protocol");
1746
+ if (!routerValid) throw (0, import_errors.refusal)("vault_mismatch", "omniston: swap destination is not a recognized DEX contract for the quoted protocol");
1668
1747
  } else {
1669
1748
  if (payload.jettonTransfer) {
1670
- 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})`);
1749
+ 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})`);
1671
1750
  }
1672
1751
  const input = BigInt(q.inputUnits);
1673
- if (sendAmount < input) throw new Error("omniston: swap message carries less than the quoted input");
1752
+ if (sendAmount < input) throw (0, import_errors.refusal)("policy_refused", "omniston: swap message carries less than the quoted input");
1674
1753
  const gas = sendAmount - input;
1675
- 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("+")})`);
1754
+ 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("+")})`);
1676
1755
  const targetValid = await opts.validateTonSwapTarget({ address: message.targetAddress, protocols });
1677
- if (!targetValid) throw new Error("omniston: swap target is not a recognized DEX contract for the quoted protocol");
1756
+ if (!targetValid) throw (0, import_errors.refusal)("vault_mismatch", "omniston: swap target is not a recognized DEX contract for the quoted protocol");
1678
1757
  }
1679
1758
  const mode = ptonV1Native ? "enforce" : opts.tonSwapPayloadCheck ?? "enforce";
1680
1759
  const trace = traceFor(ref.quoteId);
1681
1760
  const payloadRefusal = (reason, detail) => {
1682
1761
  trace("ton-swap-payload", { reason, detail, mode, protocols: protocols.join("+") });
1683
- if (mode === "enforce") throw new Error(`omniston: ton-swap-payload ${reason} \u2014 ${detail}`);
1762
+ if (mode === "enforce") throw (0, import_errors.refusal)(tonSwapPayloadReason(reason), `omniston: ton-swap-payload ${reason} \u2014 ${detail}`);
1684
1763
  };
1685
1764
  let checked = null;
1686
1765
  try {
@@ -1754,33 +1833,33 @@ function omnistonProvider(opts = {}) {
1754
1833
  const srcChain = ref.srcChain;
1755
1834
  const chainId = evmRef2(srcChain);
1756
1835
  const srcCase = q.inputAsset.chain.$case;
1757
- if (chainId === null || EVM_CASE[chainId] !== srcCase) throw new Error("omniston: source chain does not match the EVM quote");
1836
+ if (chainId === null || EVM_CASE[chainId] !== srcCase) throw (0, import_errors.refusal)("policy_refused", "omniston: source chain does not match the EVM quote");
1758
1837
  const destCase = q.outputAsset.chain.$case;
1759
1838
  const destChain = caipForChainCase(destCase);
1760
1839
  const tonDest = destCase === "ton";
1761
- if (!destChain || destChain === srcChain) throw new Error("omniston: EVM-source order has no supported destination chain");
1840
+ if (!destChain || destChain === srcChain) throw (0, import_errors.refusal)("unsupported_pair", "omniston: EVM-source order has no supported destination chain");
1762
1841
  if (!trustedOrderProtocols.has(srcChain) || typeof opts.validateEvmInputPosition !== "function" || typeof (tonDest ? opts.validateTonOutputPosition : opts.validateEvmOutputPosition) !== "function") {
1763
- throw new Error(`omniston: EVM\u2192${tonDest ? "TON" : "EVM"} needs a trusted order protocol and both position validators`);
1842
+ throw (0, import_errors.refusal)("unsupported_pair", `omniston: EVM\u2192${tonDest ? "TON" : "EVM"} needs a trusted order protocol and both position validators`);
1764
1843
  }
1765
1844
  let owner;
1766
1845
  try {
1767
1846
  owner = (0, import_viem.getAddress)(ref.trader);
1768
1847
  } catch {
1769
- throw new Error("omniston: trader is not an EVM address");
1848
+ throw (0, import_errors.refusal)("recipient_mismatch", "omniston: trader is not an EVM address");
1770
1849
  }
1771
1850
  let dest;
1772
1851
  if (tonDest) {
1773
1852
  try {
1774
1853
  import_core3.Address.parse(ref.dest);
1775
1854
  } catch {
1776
- throw new Error("omniston: destination is not a TON address");
1855
+ throw (0, import_errors.refusal)("recipient_mismatch", "omniston: destination is not a TON address");
1777
1856
  }
1778
1857
  dest = ref.dest;
1779
1858
  } else {
1780
1859
  try {
1781
1860
  dest = (0, import_viem.getAddress)(ref.dest);
1782
1861
  } catch {
1783
- throw new Error("omniston: destination is not an EVM address");
1862
+ throw (0, import_errors.refusal)("recipient_mismatch", "omniston: destination is not an EVM address");
1784
1863
  }
1785
1864
  }
1786
1865
  const traderDst = { chain: { $case: destCase, value: dest } };
@@ -1791,9 +1870,9 @@ function omnistonProvider(opts = {}) {
1791
1870
  const outputToken = assetContract(q.outputAsset);
1792
1871
  const destChainId = tonDest ? null : evmRef2(destChain);
1793
1872
  if (factory?.chain.$case === destCase && !isTrusted(destChain, factory.chain.value)) {
1794
- throw new Error(`omniston: untrusted ${destChain} settlement contract ${factory.chain.value}`);
1873
+ throw (0, import_errors.refusal)("vault_mismatch", `omniston: untrusted ${destChain} settlement contract ${factory.chain.value}`);
1795
1874
  }
1796
- 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");
1875
+ 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");
1797
1876
  const { secrets, created } = await secretsFor(ref, q, 1);
1798
1877
  const hashes = secrets.map((secret) => hashlock(secret, settlement.htlcHashingFunction));
1799
1878
  const forget = async () => {
@@ -1810,14 +1889,14 @@ function omnistonProvider(opts = {}) {
1810
1889
  });
1811
1890
  } catch (error) {
1812
1891
  await forget();
1813
- throw error;
1892
+ throw venueFailure(error);
1814
1893
  }
1815
1894
  let td;
1816
1895
  try {
1817
1896
  try {
1818
1897
  td = JSON.parse(payload.typedData);
1819
1898
  } catch {
1820
- throw new Error("omniston: malformed EVM order typed data");
1899
+ throw (0, import_errors.refusal)("policy_refused", "omniston: malformed EVM order typed data");
1821
1900
  }
1822
1901
  validateSerializedOrderDetails(payload.serializedOrderDetails, {
1823
1902
  quoteId: ref.quoteId,
@@ -1900,45 +1979,46 @@ function omnistonProvider(opts = {}) {
1900
1979
  async getQuote(params, signal) {
1901
1980
  if (params.flex) return null;
1902
1981
  if (isTon(params.sourceChain) && isTon(params.destChain)) return await getSwapQuote(params, signal);
1903
- const refuse2 = (reason) => {
1904
- params.onRefusal?.(`omniston: ${reason}`);
1982
+ const refuse2 = (reason, why) => {
1983
+ if (why) params.onRefusal?.(`omniston: ${reason}`, { reason: why });
1984
+ else params.onRefusal?.(`omniston: ${reason}`);
1905
1985
  return null;
1906
1986
  };
1907
1987
  const evmSource = !isTon(params.sourceChain);
1908
1988
  const tonDest = isTon(params.destChain);
1909
- if (!omniston.supports(params.sourceChain, params.destChain)) return refuse2("route is not an Omniston order corridor");
1989
+ if (!omniston.supports(params.sourceChain, params.destChain)) return refuse2("route is not an Omniston order corridor", "unsupported_pair");
1910
1990
  if (!trustedContracts.has(params.sourceChain) || !trustedContracts.has(params.destChain)) {
1911
- return refuse2(`no trusted settlement contract configured for ${trustedContracts.has(params.sourceChain) ? params.destChain : params.sourceChain}`);
1991
+ return refuse2(`no trusted settlement contract configured for ${trustedContracts.has(params.sourceChain) ? params.destChain : params.sourceChain}`, "unsupported_pair");
1912
1992
  }
1913
1993
  if (evmSource) {
1914
- if (!trustedOrderProtocols.has(params.sourceChain)) return refuse2(`no trusted EVM order protocol configured for ${params.sourceChain}`);
1994
+ if (!trustedOrderProtocols.has(params.sourceChain)) return refuse2(`no trusted EVM order protocol configured for ${params.sourceChain}`, "unsupported_pair");
1915
1995
  if (typeof opts.validateEvmInputPosition !== "function" || typeof (tonDest ? opts.validateTonOutputPosition : opts.validateEvmOutputPosition) !== "function") {
1916
- 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");
1996
+ 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");
1917
1997
  }
1918
1998
  if (params.dry === false) {
1919
1999
  if (tonDest) {
1920
2000
  try {
1921
2001
  import_core3.Address.parse(params.destAddress);
1922
2002
  } catch {
1923
- return refuse2("destination is not a TON address");
2003
+ return refuse2("destination is not a TON address", "recipient_mismatch");
1924
2004
  }
1925
- } else if (!validEvmAddress(params.destAddress)) return refuse2("destination is not an EVM address");
2005
+ } else if (!validEvmAddress(params.destAddress)) return refuse2("destination is not an EVM address", "recipient_mismatch");
1926
2006
  }
1927
- } 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");
2007
+ } 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");
1928
2008
  const source = (0, import_swap2_assets.ccAsset)(params.sourceChain, params.sourceToken);
1929
2009
  const dest = (0, import_swap2_assets.ccAssetByToken)(params.destChain, params.destToken);
1930
- if (!source || !dest) return refuse2("asset is not in the catalog");
2010
+ if (!source || !dest) return refuse2("asset is not in the catalog", "unsupported_pair");
1931
2011
  const inputAsset = toAssetId(params.sourceChain, source);
1932
2012
  const outputAsset = toAssetId(params.destChain, dest);
1933
2013
  if (!inputAsset || !outputAsset) {
1934
- return refuse2(`cross-chain orders are token corridors only (no ${!inputAsset ? source.symbol : dest.symbol} native leg)`);
2014
+ return refuse2(`cross-chain orders are token corridors only (no ${!inputAsset ? source.symbol : dest.symbol} native leg)`, "unsupported_pair");
1935
2015
  }
1936
2016
  const maxUnits = decimalToUnits(maxUsdText, source.decimals);
1937
2017
  if (!maxUnits || !isPositiveInteger(params.amountIn)) return refuse2("invalid amount");
1938
- if (BigInt(params.amountIn) > maxUnits) return refuse2(`amount above the ${maxUsdText} USD per-swap cap`);
2018
+ if (BigInt(params.amountIn) > maxUnits) return refuse2(`amount above the ${maxUsdText} USD per-swap cap`, "amount_too_large");
1939
2019
  const trader = params.refundAddress ?? "";
1940
2020
  if (!trader && !params.dry) return refuse2(`execution quote needs the trader's ${evmSource ? "EVM" : "TON"} address (refundAddress)`);
1941
- if (evmSource && trader && !validEvmAddress(trader)) return refuse2("refundAddress is not an EVM address");
2021
+ if (evmSource && trader && !validEvmAddress(trader)) return refuse2("refundAddress is not an EVM address", "recipient_mismatch");
1942
2022
  const request = {
1943
2023
  inputAsset,
1944
2024
  outputAsset,
@@ -1966,27 +2046,28 @@ function omnistonProvider(opts = {}) {
1966
2046
  if (!q) {
1967
2047
  if (signal?.aborted) return null;
1968
2048
  return refuse2(
1969
- rfq.error ? `RFQ failed: ${rfq.error}` : rfq.rejected ? `resolver quote rejected: ${rfq.rejected}` : "no resolver quoted this route/amount"
2049
+ rfq.error ? `RFQ failed: ${rfq.error}` : rfq.rejected ? `resolver quote rejected: ${rfq.rejected}` : "no resolver quoted this route/amount",
2050
+ rfqReason(rfq)
1970
2051
  );
1971
2052
  }
1972
2053
  const orderData = q.settlementData.$case === "order" ? q.settlementData.value : null;
1973
- if (!orderData) return refuse2("resolver answered without order settlement");
2054
+ if (!orderData) return refuse2("resolver answered without order settlement", "upstream_unavailable");
1974
2055
  const srcContract = orderData.srcProtocolContractAddress.chain.value;
1975
2056
  if (evmSource ? !isTrustedOrderProtocol(params.sourceChain, srcContract) : !isTrusted(params.sourceChain, srcContract)) {
1976
- return refuse2(evmSource ? `untrusted ${params.sourceChain} order protocol ${srcContract}` : `untrusted TON settlement contract ${srcContract}`);
2057
+ return refuse2(evmSource ? `untrusted ${params.sourceChain} order protocol ${srcContract}` : `untrusted TON settlement contract ${srcContract}`, "vault_mismatch");
1977
2058
  }
1978
2059
  if (!orderData.dstProtocolContractAddress || !isTrusted(params.destChain, orderData.dstProtocolContractAddress.chain.value)) {
1979
- return refuse2(`untrusted ${params.destChain} settlement contract ${orderData.dstProtocolContractAddress?.chain.value ?? "(none)"}`);
2060
+ return refuse2(`untrusted ${params.destChain} settlement contract ${orderData.dstProtocolContractAddress?.chain.value ?? "(none)"}`, "vault_mismatch");
1980
2061
  }
1981
2062
  const deadlineMs = orderData.tradeStartDeadline * 1e3;
1982
- if (!Number.isFinite(deadlineMs) || deadlineMs <= Date.now()) return refuse2("quote trade-start deadline already passed");
1983
- if (params.dry === false && deadlineMs - Date.now() < MIN_START_HEADROOM_MS) return refuse2("quote trade-start deadline too close to execute");
2063
+ if (!Number.isFinite(deadlineMs) || deadlineMs <= Date.now()) return refuse2("quote trade-start deadline already passed", "quote_expired");
2064
+ if (params.dry === false && deadlineMs - Date.now() < MIN_START_HEADROOM_MS) return refuse2("quote trade-start deadline too close to execute", "quote_expired");
1984
2065
  const inputScaled = BigInt(q.inputUnits) * 10n ** BigInt(dest.decimals);
1985
2066
  const outputScaled = BigInt(q.outputUnits) * 10n ** BigInt(source.decimals);
1986
- if (outputScaled * 2n < inputScaled || outputScaled > inputScaled * 2n) return refuse2("quoted output is outside 0.5x\u20132x of the input");
1987
- if (await quoteIdInUse(q.quoteId)) return refuse2("the platform reused a quote id that already belongs to an order");
2067
+ if (outputScaled * 2n < inputScaled || outputScaled > inputScaled * 2n) return refuse2("quoted output is outside 0.5x\u20132x of the input", "price_check_failed");
2068
+ if (await quoteIdInUse(q.quoteId)) return refuse2("the platform reused a quote id that already belongs to an order", "policy_refused");
1988
2069
  if (!await claimBuilt(q.quoteId, { quote: q, at: Date.now(), bound: { trader, dest: params.destAddress, srcChain: params.sourceChain, kind: "order" } })) {
1989
- return refuse2("the platform reused a quote id that already holds another quote");
2070
+ return refuse2("the platform reused a quote id that already holds another quote", "policy_refused");
1990
2071
  }
1991
2072
  const feeUnits = Number(q.protocolFeeUnits || "0") + Number(q.integratorFeeUnits || "0");
1992
2073
  const feeUsd = feeUnits / 10 ** dest.decimals;
@@ -2017,29 +2098,29 @@ function omnistonProvider(opts = {}) {
2017
2098
  */
2018
2099
  async buildDeposit(quote) {
2019
2100
  if (!Number.isFinite(quote.validUntilMs) || quote.validUntilMs <= Date.now()) {
2020
- throw new Error("omniston: quote expired \u2014 request a fresh quote");
2101
+ throw (0, import_errors.refusal)("quote_expired", "omniston: quote expired \u2014 request a fresh quote");
2021
2102
  }
2022
2103
  const decoded = decodeIntent(quote.intentId);
2023
2104
  if (!decoded) throw new Error("omniston: malformed intent id");
2024
2105
  const cached = await loadBuilt(decoded.quoteId);
2025
- if (!cached) throw new Error("omniston: quote expired \u2014 request a fresh quote");
2106
+ if (!cached) throw (0, import_errors.refusal)("quote_expired", "omniston: quote expired \u2014 request a fresh quote");
2026
2107
  const ref = requireBinding(decoded, cached, "buildDeposit");
2027
2108
  const q = cached.quote;
2028
2109
  if (q.settlementData.$case === "swap") return await buildSwapDeposit(ref, q, q.settlementData.value);
2029
2110
  if (q.settlementData.$case !== "order" || q.settlementData.value.tradeStartDeadline * 1e3 <= Date.now()) {
2030
- throw new Error("omniston: quote trade deadline expired \u2014 request a fresh quote");
2111
+ throw (0, import_errors.refusal)("quote_expired", "omniston: quote trade deadline expired \u2014 request a fresh quote");
2031
2112
  }
2032
2113
  if (!ref.trader) throw new Error("omniston: quote was requested without a refund/source address");
2033
2114
  const srcCase = q.inputAsset.chain.$case;
2034
2115
  if (srcCase !== "ton") return await buildEvmOrder(ref, q, q.settlementData.value, cached.bound);
2035
2116
  const srcChain = ref.srcChain;
2036
- if (!isTon(srcChain)) throw new Error("omniston: source chain does not match TON quote");
2117
+ if (!isTon(srcChain)) throw (0, import_errors.refusal)("policy_refused", "omniston: source chain does not match TON quote");
2037
2118
  const destCase = q.outputAsset.chain.$case;
2038
2119
  const traderDst = destCase === "ton" ? null : { chain: { $case: destCase, value: (0, import_viem.getAddress)(ref.dest) } };
2039
- if (!traderDst) throw new Error("omniston: TON-destination orders are not supported");
2120
+ if (!traderDst) throw (0, import_errors.refusal)("unsupported_pair", "omniston: TON-destination orders are not supported");
2040
2121
  const minter = q.settlementData.value.srcProtocolContractAddress;
2041
2122
  if (minter.chain.$case !== "ton" || !isTrusted(srcChain, minter.chain.value)) {
2042
- throw new Error(`omniston: untrusted TON settlement contract ${minter.chain.value}`);
2123
+ throw (0, import_errors.refusal)("vault_mismatch", `omniston: untrusted TON settlement contract ${minter.chain.value}`);
2043
2124
  }
2044
2125
  const settlement = q.settlementData;
2045
2126
  const hashingFunction = settlement.$case === "order" ? settlement.value.htlcHashingFunction : void 0;
@@ -2063,27 +2144,27 @@ function omnistonProvider(opts = {}) {
2063
2144
  });
2064
2145
  } catch (error) {
2065
2146
  await forget();
2066
- throw error;
2147
+ throw venueFailure(error);
2067
2148
  }
2068
2149
  const [message, ...rest] = tx.messages;
2069
2150
  if (!message || rest.length > 0) {
2070
2151
  await forget();
2071
- throw new Error(`omniston: expected exactly one escrow message, got ${tx.messages.length}`);
2152
+ throw (0, import_errors.refusal)("policy_refused", `omniston: expected exactly one escrow message, got ${tx.messages.length}`);
2072
2153
  }
2073
2154
  if (!isPositiveInteger(message.sendAmount) || BigInt(message.sendAmount) > BigInt(maxTonExecutionNano)) {
2074
2155
  await forget();
2075
- throw new Error("omniston: resolver TON execution value exceeds the configured safety cap");
2156
+ throw (0, import_errors.refusal)("policy_refused", "omniston: resolver TON execution value exceeds the configured safety cap");
2076
2157
  }
2077
2158
  const expectedProtocol = q.settlementData.value.srcProtocolContractAddress;
2078
2159
  const initialResolver = q.settlementData.value.srcInitialResolverAddress;
2079
2160
  const jettonMaster = assetContract(q.inputAsset);
2080
2161
  if (expectedProtocol.chain.$case !== "ton" || initialResolver?.chain.$case !== "ton" || !jettonMaster) {
2081
2162
  await forget();
2082
- throw new Error("omniston: incomplete TON settlement binding in quote");
2163
+ throw (0, import_errors.refusal)("policy_refused", "omniston: incomplete TON settlement binding in quote");
2083
2164
  }
2084
2165
  if (message.jettonWalletStateInit) {
2085
2166
  await forget();
2086
- throw new Error("omniston: deploying a resolver-provided jetton wallet is not supported");
2167
+ throw (0, import_errors.refusal)("policy_refused", "omniston: deploying a resolver-provided jetton wallet is not supported");
2087
2168
  }
2088
2169
  let walletValid = false;
2089
2170
  try {
@@ -2098,11 +2179,11 @@ function omnistonProvider(opts = {}) {
2098
2179
  }
2099
2180
  if (!walletValid) {
2100
2181
  await forget();
2101
- throw new Error("omniston: TON jetton wallet was not independently validated");
2182
+ throw (0, import_errors.refusal)("vault_mismatch", "omniston: TON jetton wallet was not independently validated");
2102
2183
  }
2103
2184
  try {
2104
2185
  if (!opts.validateTonEscrowPayload) {
2105
- throw new Error("omniston: TON source requires an independent escrow-payload validator");
2186
+ throw (0, import_errors.refusal)("unsupported_pair", "omniston: TON source requires an independent escrow-payload validator");
2106
2187
  }
2107
2188
  await validateTonEscrowPayload(message, {
2108
2189
  quoteId: ref.quoteId,
@@ -2149,7 +2230,7 @@ function omnistonProvider(opts = {}) {
2149
2230
  const result = bindIntent(decoded, stored);
2150
2231
  if (!result.ok && result.reason === "mismatch") {
2151
2232
  trace("intent-mismatch", { where: "trackIntent", field: result.field });
2152
- throw new Error(`omniston: intent id does not match the quote it was issued for (${result.field})`);
2233
+ throw (0, import_errors.refusal)(mismatchReason(result.field), `omniston: intent id does not match the quote it was issued for (${result.field})`);
2153
2234
  }
2154
2235
  if (result.ok) {
2155
2236
  if (result.legacy) trace("legacy-intent", { where: "trackIntent" });
@@ -2329,12 +2410,12 @@ function omnistonProvider(opts = {}) {
2329
2410
  if (!ref) throw new Error("omniston: malformed intent id");
2330
2411
  const cached = await loadBuilt(ref.quoteId);
2331
2412
  const order = cached?.evmOrder;
2332
- if (!order) throw new Error("omniston: no signed-order context for this quote \u2014 build the order again");
2413
+ if (!order) throw (0, import_errors.refusal)("quote_expired", "omniston: no signed-order context for this quote \u2014 build the order again");
2333
2414
  requireBinding(ref, cached, "registerSignedOrder");
2334
2415
  if (!/^0x[0-9a-fA-F]{130}$/.test(signature)) throw new Error("omniston: malformed order signature");
2335
2416
  const td = JSON.parse(order.typedData);
2336
2417
  const omni = await getClient();
2337
- await omni.orderRegisterSignedOrder({
2418
+ const request = {
2338
2419
  quoteId: ref.quoteId,
2339
2420
  ownerSrcAddress: { chain: { $case: order.chainCase, value: order.owner } },
2340
2421
  signedOrder: { order: { $case: "evmV1", value: {
@@ -2343,7 +2424,12 @@ function omnistonProvider(opts = {}) {
2343
2424
  orderExtension: hexToBytes(order.orderExtension)
2344
2425
  } } },
2345
2426
  ...order.serializedOrderDetails ? { serializedOrderDetails: hexToBytes(order.serializedOrderDetails) } : {}
2346
- });
2427
+ };
2428
+ try {
2429
+ await omni.orderRegisterSignedOrder(request);
2430
+ } catch (error) {
2431
+ throw venueFailure(error);
2432
+ }
2347
2433
  },
2348
2434
  close() {
2349
2435
  clientGeneration += 1;