@anywalletkit/protocol-swap2-omniston 0.3.1 → 0.5.0

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.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { omniston } from './config.cjs';
2
- export { A as AssetId, C as ChainAddress, E as EscrowMessage, H as HtlcSecretStore, O as ObservableLike, a as OmnistonClientLike, b as OmnistonProviderOptions, c as OmnistonQuote, d as OmnistonQuoteStore, e as OrderEvent, f as OrderExecution, g as OrderSettlement, h as OrderSnapshot, Q as QuoteRequest, R as RfqEvent, S as SwapSettlement, i as SwapTrackEvent, T as TON_SWAP_PROTOCOL_LAYOUTS, j as TonSwapCheckInput, k as TonSwapCheckResult, l as TonSwapLeg, m as TonSwapPayloadError, n as checkTonSwapPayload, o as encodeTypedMessage, p as inMemoryQuoteStore, q as inMemorySecretStore, r as omnistonPins, s as omnistonProvider, t as omnistonVerifyBuild, v as validateEvmToTonOrderExtension, u as validateEvmTypedOrder } from './provider-BBG2zhpu.cjs';
2
+ export { A as AssetId, C as ChainAddress, E as EscrowMessage, H as HtlcSecretStore, O as ObservableLike, a as OmnistonClientLike, b as OmnistonProviderOptions, c as OmnistonQuote, d as OmnistonQuoteStore, e as OrderEvent, f as OrderExecution, g as OrderSettlement, h as OrderSnapshot, Q as QuoteRequest, R as RfqEvent, S as SwapSettlement, i as SwapTrackEvent, T as TON_SWAP_PROTOCOL_LAYOUTS, j as TonSwapCheckInput, k as TonSwapCheckResult, l as TonSwapLeg, m as TonSwapPayloadError, n as checkTonSwapPayload, o as encodeTypedMessage, p as inMemoryQuoteStore, q as inMemorySecretStore, r as omnistonPins, s as omnistonProvider, t as omnistonVerifyBuild, v as validateEvmToTonOrderExtension, u as validateEvmTypedOrder } from './provider-CcCZB-Cj.cjs';
3
3
  export { OmnistonSwap2Config, OmnistonSwap2Protocol, OmnistonSwap2Protocol as default } from './protocol.cjs';
4
4
  export { OMNISTON_ESCROWS, OMNISTON_MODULE, STONFI_ROUTERS, describeOmnistonActivity } from './activity.cjs';
5
5
  import '@anywalletkit/protocol-swap2-core/types';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { omniston } from './config.js';
2
- export { A as AssetId, C as ChainAddress, E as EscrowMessage, H as HtlcSecretStore, O as ObservableLike, a as OmnistonClientLike, b as OmnistonProviderOptions, c as OmnistonQuote, d as OmnistonQuoteStore, e as OrderEvent, f as OrderExecution, g as OrderSettlement, h as OrderSnapshot, Q as QuoteRequest, R as RfqEvent, S as SwapSettlement, i as SwapTrackEvent, T as TON_SWAP_PROTOCOL_LAYOUTS, j as TonSwapCheckInput, k as TonSwapCheckResult, l as TonSwapLeg, m as TonSwapPayloadError, n as checkTonSwapPayload, o as encodeTypedMessage, p as inMemoryQuoteStore, q as inMemorySecretStore, r as omnistonPins, s as omnistonProvider, t as omnistonVerifyBuild, v as validateEvmToTonOrderExtension, u as validateEvmTypedOrder } from './provider-BBG2zhpu.js';
2
+ export { A as AssetId, C as ChainAddress, E as EscrowMessage, H as HtlcSecretStore, O as ObservableLike, a as OmnistonClientLike, b as OmnistonProviderOptions, c as OmnistonQuote, d as OmnistonQuoteStore, e as OrderEvent, f as OrderExecution, g as OrderSettlement, h as OrderSnapshot, Q as QuoteRequest, R as RfqEvent, S as SwapSettlement, i as SwapTrackEvent, T as TON_SWAP_PROTOCOL_LAYOUTS, j as TonSwapCheckInput, k as TonSwapCheckResult, l as TonSwapLeg, m as TonSwapPayloadError, n as checkTonSwapPayload, o as encodeTypedMessage, p as inMemoryQuoteStore, q as inMemorySecretStore, r as omnistonPins, s as omnistonProvider, t as omnistonVerifyBuild, v as validateEvmToTonOrderExtension, u as validateEvmTypedOrder } from './provider-CcCZB-Cj.js';
3
3
  export { OmnistonSwap2Config, OmnistonSwap2Protocol, OmnistonSwap2Protocol as default } from './protocol.js';
4
4
  export { OMNISTON_ESCROWS, OMNISTON_MODULE, STONFI_ROUTERS, describeOmnistonActivity } from './activity.js';
5
5
  import '@anywalletkit/protocol-swap2-core/types';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  OmnistonSwap2Protocol
3
- } from "./chunk-RW4XZWNC.js";
3
+ } from "./chunk-4MKR2SUZ.js";
4
4
  import {
5
5
  TON_SWAP_PROTOCOL_LAYOUTS,
6
6
  TonSwapPayloadError,
@@ -13,7 +13,7 @@ import {
13
13
  omnistonVerifyBuild,
14
14
  validateEvmToTonOrderExtension,
15
15
  validateEvmTypedOrder
16
- } from "./chunk-HOTFZYSW.js";
16
+ } from "./chunk-2DGCZD5K.js";
17
17
  import {
18
18
  omniston
19
19
  } from "./chunk-QDXFC6BC.js";
@@ -23,6 +23,7 @@ import {
23
23
  STONFI_ROUTERS,
24
24
  describeOmnistonActivity
25
25
  } from "./chunk-AIWAM7BS.js";
26
+ import "./chunk-JWYDIW4V.js";
26
27
  export {
27
28
  OMNISTON_ESCROWS,
28
29
  OMNISTON_MODULE,
package/dist/protocol.cjs CHANGED
@@ -500,18 +500,36 @@ function toChainAddress(caip2, address) {
500
500
  if (!$case) return null;
501
501
  return { chain: { $case, value: (0, import_viem.getAddress)(address) } };
502
502
  }
503
- function encodeIntent(quoteId, trader, dest, srcChain, kind = "order") {
504
- return [INTENT_PREFIX, quoteId, trader, dest, srcChain, ...kind === "swap" ? ["swap"] : []].map(encodeURIComponent).join("|");
503
+ var QUOTE_DIGEST_SKIP = /* @__PURE__ */ new Set(["rfqId", "resolverName", "estimatedSettlementDuration", "estimatedGasConsumption"]);
504
+ function canonicalJson(value) {
505
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
506
+ if (value && typeof value === "object") {
507
+ return `{${Object.keys(value).sort().map((k) => `${JSON.stringify(k)}:${canonicalJson(value[k])}`).join(",")}}`;
508
+ }
509
+ return JSON.stringify(value) ?? "null";
510
+ }
511
+ function quoteDigest(q) {
512
+ const terms = JSON.parse(JSON.stringify(q));
513
+ for (const key of QUOTE_DIGEST_SKIP) delete terms[key];
514
+ return (0, import_viem.sha256)(new TextEncoder().encode(canonicalJson(terms))).slice(2, 34);
515
+ }
516
+ var QUOTE_DIGEST = /^[0-9a-f]{32}$/;
517
+ function encodeIntent(quoteId, trader, dest, srcChain, kind, digest) {
518
+ return [INTENT_PREFIX, quoteId, trader, `${dest}#${digest}`, srcChain, ...kind === "swap" ? ["swap"] : []].map(encodeURIComponent).join("|");
505
519
  }
506
520
  var QUOTE_ID = /^[0-9a-fA-F]{48,64}$/;
507
521
  function decodeIntent(intentId) {
508
522
  try {
509
523
  const parts = intentId.split("|");
510
524
  if (parts.length !== 5 && parts.length !== 6 || parts[0] !== INTENT_PREFIX) return null;
511
- const [, quoteId = "", trader = "", dest = "", srcChain = "", kindPart = ""] = parts.map(decodeURIComponent);
525
+ const [, quoteId = "", trader = "", destPart = "", srcChain = "", kindPart = ""] = parts.map(decodeURIComponent);
526
+ const mark = destPart.lastIndexOf("#");
527
+ const dest = mark < 0 ? destPart : destPart.slice(0, mark);
528
+ const digest = mark < 0 ? void 0 : destPart.slice(mark + 1);
529
+ if (digest !== void 0 && !QUOTE_DIGEST.test(digest)) return null;
512
530
  if (!QUOTE_ID.test(quoteId) || !trader || !dest || !srcChain) return null;
513
531
  if (parts.length === 6 && kindPart !== "swap") return null;
514
- return { quoteId, trader, dest, srcChain, kind: parts.length === 6 ? "swap" : "order" };
532
+ return { quoteId, trader, dest, srcChain, kind: parts.length === 6 ? "swap" : "order", ...digest ? { digest } : {} };
515
533
  } catch {
516
534
  return null;
517
535
  }
@@ -632,9 +650,9 @@ function validEvmAddress(value) {
632
650
  }
633
651
  }
634
652
  var EVM_DEST_PROTOCOL_FEE_RECIPIENTS = /* @__PURE__ */ new Set(["0x076268146b4ec6d21fc49c27586cfa91b9079a00"]);
635
- async function validateEvmToTonOrderExtension(extension, expected) {
636
- const evmDest = expected.dstChainId !== void 0;
637
- if (evmDest && (!Number.isSafeInteger(expected.dstChainId) || expected.dstChainId <= 0)) throw new Error("omniston: invalid EVM destination chain id");
653
+ var EVM_AUCTION_POINTS = [[800000n, 100n], [700000n, 100n], [600000n, 100n], [500000n, 100n], [400000n, 100n]];
654
+ var EVM_TIMELOCKS = [1n, 120n, 240n, 300n, 1n, 120n, 180n, 0n].reduce((packed, value, index) => packed | value << BigInt(index * 32), 0n);
655
+ function readEvmOrderExtension(extension) {
638
656
  if (extension.length < 32) throw new Error("omniston: malformed EVM order extension");
639
657
  const view = new DataView(extension.buffer, extension.byteOffset, extension.byteLength);
640
658
  const fields = [];
@@ -654,54 +672,48 @@ async function validateEvmToTonOrderExtension(extension, expected) {
654
672
  }
655
673
  let target;
656
674
  for (const index of [2, 3, 7]) {
657
- const field = fields[index];
658
- if (field.length <= 20) throw new Error("omniston: incomplete EVM order extension program");
659
- const address = (0, import_viem.getAddress)(`0x${bytesToHex(field.slice(0, 20))}`);
660
- if (!expected.trustedTarget(address) || target !== void 0 && (0, import_viem.getAddress)(target) !== address) {
675
+ const field2 = fields[index];
676
+ if (field2.length <= 20) throw new Error("omniston: incomplete EVM order extension program");
677
+ const address = (0, import_viem.getAddress)(`0x${bytesToHex(field2.slice(0, 20))}`);
678
+ if (target !== void 0 && (0, import_viem.getAddress)(target) !== address) {
661
679
  throw new Error("omniston: EVM order extension target is not independently trusted");
662
680
  }
663
681
  target = address;
664
682
  }
665
- const getter = fields[2];
683
+ const field = fields[2];
666
684
  let cursor = 20;
667
- const gasBump = readBigEndian(getter, cursor, 3);
685
+ const gasBump = readBigEndian(field, cursor, 3);
668
686
  cursor += 3;
669
- const gasPrice = readBigEndian(getter, cursor, 4);
687
+ const gasPrice = readBigEndian(field, cursor, 4);
670
688
  cursor += 4;
671
- const auctionStart = readBigEndian(getter, cursor, 4);
689
+ const auctionStart = readBigEndian(field, cursor, 4);
672
690
  cursor += 4;
673
- const duration = readBigEndian(getter, cursor, 3);
691
+ const duration = readBigEndian(field, cursor, 3);
674
692
  cursor += 3;
675
- const initialRateBump = readBigEndian(getter, cursor, 3);
693
+ const initialRateBump = readBigEndian(field, cursor, 3);
676
694
  cursor += 3;
677
- const pointCount = (0, import_core4.bigintToNumber)(readBigEndian(getter, cursor, 1));
695
+ const pointCount = (0, import_core4.bigintToNumber)(readBigEndian(field, cursor, 1));
678
696
  cursor += 1;
679
697
  if (pointCount !== 5) throw new Error("omniston: unsupported EVM auction profile");
680
698
  const points = [];
681
699
  for (let index = 0; index < pointCount; index++) {
682
- points.push([readBigEndian(getter, cursor, 3), readBigEndian(getter, cursor + 3, 2)]);
700
+ points.push([readBigEndian(field, cursor, 3), readBigEndian(field, cursor + 3, 2)]);
683
701
  cursor += 5;
684
702
  }
685
- const integratorFee = readBigEndian(getter, cursor, 2);
703
+ const integratorFee = readBigEndian(field, cursor, 2);
686
704
  cursor += 2;
687
- const integratorShare = readBigEndian(getter, cursor, 1);
705
+ const integratorShare = readBigEndian(field, cursor, 1);
688
706
  cursor += 1;
689
- const resolverFee = readBigEndian(getter, cursor, 2);
707
+ const resolverFee = readBigEndian(field, cursor, 2);
690
708
  cursor += 2;
691
- const whitelistDiscount = readBigEndian(getter, cursor, 1);
709
+ const whitelistDiscount = readBigEndian(field, cursor, 1);
692
710
  cursor += 1;
693
- const getterWhitelistCount = (0, import_core4.bigintToNumber)(readBigEndian(getter, cursor, 1));
711
+ const getterWhitelistCount = (0, import_core4.bigintToNumber)(readBigEndian(field, cursor, 1));
694
712
  cursor += 1;
695
- if (getterWhitelistCount !== 1 || cursor + getterWhitelistCount * 10 !== getter.length) {
713
+ if (getterWhitelistCount !== 1 || cursor + getterWhitelistCount * 10 !== field.length) {
696
714
  throw new Error("omniston: malformed EVM amount-getter whitelist");
697
715
  }
698
- const getterResolver = readBigEndian(getter, cursor, 10);
699
- const expectedPoints = [[800000n, 100n], [700000n, 100n], [600000n, 100n], [500000n, 100n], [400000n, 100n]];
700
- const quoteResolverFee = expected.protocolFeePips % 10 === 0 ? BigInt(expected.protocolFeePips / 10) : -1n;
701
- const resolverLow80 = BigInt((0, import_viem.getAddress)(expected.initialResolverAddress)) & (1n << 80n) - 1n;
702
- if (gasBump !== 0n || gasPrice !== 0n || !within(auctionStart, expected.quoteTimestamp, 5, 5) || duration !== 1800n || initialRateBump !== 900000n || points.some((point, index) => point[0] !== expectedPoints[index][0] || point[1] !== expectedPoints[index][1]) || expected.integratorFeePips !== 0 || integratorFee !== 0n || integratorShare !== 100n || quoteResolverFee < 0n || resolverFee !== quoteResolverFee || whitelistDiscount !== 50n || getterResolver !== resolverLow80) {
703
- throw new Error("omniston: EVM amount-getter terms do not match the pinned v1beta8 profile");
704
- }
716
+ const getter = { gasBump, gasPrice, auctionStart, duration, initialRateBump, points, integratorFee, integratorShare, resolverFee, whitelistDiscount, resolver: readBigEndian(field, cursor, 10) };
705
717
  const post = fields[7];
706
718
  cursor = 20;
707
719
  const integratorRecipient = post.slice(cursor, cursor + 20);
@@ -727,28 +739,51 @@ async function validateEvmToTonOrderExtension(extension, expected) {
727
739
  cursor += 10;
728
740
  const resolverTimeDelta = readBigEndian(post, cursor, 2);
729
741
  cursor += 2;
730
- if (!zeroBytes(integratorRecipient) || !(zeroBytes(protocolRecipient) || evmDest && EVM_DEST_PROTOCOL_FEE_RECIPIENTS.has(`0x${bytesToHex(protocolRecipient)}`)) || postIntegratorFee !== 0n || postIntegratorShare !== 100n || postResolverFee !== quoteResolverFee || postDiscount !== 50n || !within(allowedTime, expected.quoteTimestamp, 60, 5) || postResolver !== resolverLow80 || resolverTimeDelta !== 0n) {
731
- throw new Error("omniston: EVM post-interaction terms do not match the pinned v1beta8 profile");
732
- }
733
742
  const extra = Array.from({ length: 5 }, () => {
734
743
  const value = readBigEndian(post, cursor, 32);
735
744
  cursor += 32;
736
745
  return value;
737
746
  });
747
+ const customData = extension.slice(32 + begin);
748
+ if (customData.length !== 131) throw new Error("omniston: malformed EVM order extension custom data");
749
+ return {
750
+ target,
751
+ getter,
752
+ post: { integratorRecipient, protocolRecipient, integratorFee: postIntegratorFee, integratorShare: postIntegratorShare, resolverFee: postResolverFee, discount: postDiscount, allowedTime, resolver: postResolver, resolverTimeDelta },
753
+ extra,
754
+ customData
755
+ };
756
+ }
757
+ async function validateEvmToTonOrderExtension(extension, expected) {
758
+ const evmDest = expected.dstChainId !== void 0;
759
+ if (evmDest && (!Number.isSafeInteger(expected.dstChainId) || expected.dstChainId <= 0)) throw new Error("omniston: invalid EVM destination chain id");
760
+ const { target, getter, post, extra, customData } = readEvmOrderExtension(extension);
761
+ if (!expected.trustedTarget(target)) throw new Error("omniston: EVM order extension target is not independently trusted");
762
+ const quoteResolverFee = expected.protocolFeePips % 10 === 0 ? BigInt(expected.protocolFeePips / 10) : -1n;
763
+ const resolverLow80 = BigInt((0, import_viem.getAddress)(expected.initialResolverAddress)) & (1n << 80n) - 1n;
764
+ 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) {
765
+ throw new Error("omniston: EVM amount-getter terms do not match the pinned v1beta8 profile");
766
+ }
767
+ 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) {
768
+ throw new Error("omniston: EVM post-interaction terms do not match the pinned v1beta8 profile");
769
+ }
738
770
  const hashlock2 = readBigEndian(expected.secretHash, 0, expected.secretHash.length);
739
771
  const dstToken = evmDest ? BigInt((0, import_viem.getAddress)(expected.outputTokenAddress)) : BigInt((0, import_viem.getAddress)(expected.takerAsset));
740
772
  const deposits = BigInt(expected.srcSecurityDepositUnits) << 128n | BigInt(expected.dstSecurityDepositUnits);
741
- const timelocks = [1n, 120n, 240n, 300n, 1n, 120n, 180n, 0n].reduce((packed, value, index) => packed | value << BigInt(index * 32), 0n);
742
- if (expected.secretHash.length !== 32 || extra[0] !== hashlock2 || extra[1] !== (evmDest ? BigInt(expected.dstChainId) : 0n) || extra[2] !== dstToken || extra[3] !== deposits || extra[4] !== timelocks) {
773
+ 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) {
743
774
  throw new Error("omniston: signed EVM HTLC terms do not match the validated order details");
744
775
  }
745
- const customData = extension.slice(32 + begin);
746
- if (customData.length !== 131) throw new Error("omniston: malformed EVM order extension custom data");
747
- if (evmDest) {
776
+ await checkOrderCustomData(customData, { evmDest, outputToken: expected.outputTokenAddress, traderDst: expected.traderDstAddress, quoteId: expected.quoteId });
777
+ if (expected.tradeStartDeadline < expected.quoteTimestamp) {
778
+ throw new Error("omniston: invalid EVM order quote deadline");
779
+ }
780
+ }
781
+ async function checkOrderCustomData(customData, expected) {
782
+ if (expected.evmDest) {
748
783
  if (!zeroBytes(customData.slice(0, 66))) throw new Error("omniston: unexpected TON addresses in an EVM-destination order");
749
784
  } else {
750
785
  const { Address: Address3 } = await import("@ton/core");
751
- const expectedTon = [expected.outputTokenAddress, expected.traderDstAddress].map((value) => Address3.parse(value));
786
+ const expectedTon = [expected.outputToken, expected.traderDst].map((value) => Address3.parse(value));
752
787
  for (let index = 0; index < expectedTon.length; index++) {
753
788
  const offset = index * 33;
754
789
  const workchain = customData[offset] === 255 ? -1 : customData[offset];
@@ -764,9 +799,6 @@ async function validateEvmToTonOrderExtension(extension, expected) {
764
799
  if (!QUOTE_ID.test(quoteId) || !sliceEqual(customData.slice(99), hexToBytes(quoteId.padStart(64, "0")))) {
765
800
  throw new Error("omniston: EVM order extension custom data is not bound to the quote id");
766
801
  }
767
- if (expected.tradeStartDeadline < expected.quoteTimestamp) {
768
- throw new Error("omniston: invalid EVM order quote deadline");
769
- }
770
802
  }
771
803
  function validateEvmTypedOrder(td, expected) {
772
804
  const value = td;
@@ -1346,7 +1378,7 @@ function omnistonProvider(opts = {}) {
1346
1378
  return true;
1347
1379
  }
1348
1380
  const a = existing.bound, b = entry.bound;
1349
- const same = !!a && !!b && a.kind === b.kind && a.srcChain === b.srcChain && sameIntentAddress(a.trader, b.trader) && sameIntentAddress(a.dest, b.dest);
1381
+ const same = !!a && !!b && a.kind === b.kind && a.srcChain === b.srcChain && sameIntentAddress(a.trader, b.trader) && sameIntentAddress(a.dest, b.dest) && quoteDigest(existing.quote) === quoteDigest(entry.quote);
1350
1382
  if (!same) return false;
1351
1383
  await quotes.save(quoteId, json);
1352
1384
  return true;
@@ -1399,10 +1431,19 @@ function omnistonProvider(opts = {}) {
1399
1431
  throw new Error(`omniston: intent id does not match the quote it was issued for (${bound.field})`);
1400
1432
  }
1401
1433
  if (bound.legacy) trace("legacy-intent", { where });
1434
+ if (!decoded.digest) {
1435
+ trace("intent-mismatch", { where, field: "digest-missing" });
1436
+ throw new Error("omniston: this quote was issued before an update \u2014 request a fresh quote");
1437
+ }
1438
+ if (decoded.digest !== quoteDigest(entry.quote)) {
1439
+ trace("intent-mismatch", { where, field: "digest" });
1440
+ throw new Error("omniston: quote was replaced \u2014 request a fresh quote");
1441
+ }
1402
1442
  return bound.ref;
1403
1443
  }
1404
1444
  const bindKey = (quoteId) => `${quoteId}#bind`;
1405
1445
  const disclosedKey = (quoteId) => `${quoteId}#disclosed`;
1446
+ const disclosedIndicesKey = (quoteId) => `${quoteId}#disclosed-indices`;
1406
1447
  function orderBinding(ref, q) {
1407
1448
  return (0, import_viem.sha256)(new TextEncoder().encode(JSON.stringify([
1408
1449
  ref.quoteId,
@@ -1419,6 +1460,7 @@ function omnistonProvider(opts = {}) {
1419
1460
  await store.clear(quoteId);
1420
1461
  await store.clear(bindKey(quoteId));
1421
1462
  await store.clear(disclosedKey(quoteId));
1463
+ await store.clear(disclosedIndicesKey(quoteId));
1422
1464
  }
1423
1465
  async function secretsFor(ref, q, count) {
1424
1466
  const quoteId = ref.quoteId;
@@ -1447,7 +1489,7 @@ function omnistonProvider(opts = {}) {
1447
1489
  return !!existing && existing.length > 0;
1448
1490
  }
1449
1491
  const SWAP_SLIPPAGE_PIPS_DEFAULT = 5e3;
1450
- const sameChainTonReady = () => typeof opts.validateTonJettonWallet === "function" && typeof opts.validateTonSwapTarget === "function";
1492
+ const sameChainTonReady = () => typeof opts.validateTonJettonWallet === "function" && typeof opts.validateTonSwapTarget === "function" && (typeof opts.validateTonSwapLeg === "function" || opts.tonSwapPayloadCheck === "report");
1451
1493
  const tonSourceReady = (from, to) => omniston.supports(from, to) && trustedContracts.has(from) && trustedContracts.has(to) && typeof opts.validateTonJettonWallet === "function" && typeof opts.validateTonEscrowPayload === "function" && typeof opts.validateEvmOutputPosition === "function";
1452
1494
  const evmSourceReady = (from, to) => omniston.supports(from, to) && trustedOrderProtocols.has(from) && trustedContracts.has(from) && trustedContracts.has(to) && typeof opts.validateEvmInputPosition === "function" && typeof opts.validateTonOutputPosition === "function";
1453
1495
  const evmToEvmReady = (from, to) => omniston.supports(from, to) && trustedOrderProtocols.has(from) && trustedContracts.has(from) && trustedContracts.has(to) && typeof opts.validateEvmInputPosition === "function" && typeof opts.validateEvmOutputPosition === "function";
@@ -1526,7 +1568,7 @@ function omnistonProvider(opts = {}) {
1526
1568
  const feeUsd = stableOut ? feeUnits / 10 ** dest.decimals : 0;
1527
1569
  return {
1528
1570
  provider: "omniston",
1529
- intentId: encodeIntent(q.quoteId, trader, params.destAddress, params.sourceChain, "swap"),
1571
+ intentId: encodeIntent(q.quoteId, trader, params.destAddress, params.sourceChain, "swap", quoteDigest(q)),
1530
1572
  depositAddress: "",
1531
1573
  // swap settlement — the built message pays the DEX directly
1532
1574
  depositMemo: null,
@@ -1559,7 +1601,7 @@ function omnistonProvider(opts = {}) {
1559
1601
  if (!isTon(ref.srcChain) || q.inputAsset.chain.$case !== "ton" || q.outputAsset.chain.$case !== "ton") {
1560
1602
  throw new Error("omniston: swap quote is not a TON same-chain swap");
1561
1603
  }
1562
- if (!sameChainTonReady()) throw new Error("omniston: TON same-chain swaps need independent jetton-wallet and swap-target validators");
1604
+ if (!sameChainTonReady()) throw new Error("omniston: TON same-chain swaps need independent jetton-wallet, swap-target and swap-leg validators");
1563
1605
  const owner = { chain: { $case: "ton", value: ref.trader } };
1564
1606
  const receiver = { chain: { $case: "ton", value: ref.dest } };
1565
1607
  const omni = await getClient();
@@ -1658,7 +1700,7 @@ function omnistonProvider(opts = {}) {
1658
1700
  }
1659
1701
  if (checked) {
1660
1702
  if (typeof opts.validateTonSwapLeg !== "function") {
1661
- trace("ton-swap-payload", { reason: "legs-unverified", detail: "no validateTonSwapLeg hook", mode, legs: checked.legs.length });
1703
+ payloadRefusal("legs-unverified", `no validateTonSwapLeg hook for ${checked.legs.length} leg(s)`);
1662
1704
  } else {
1663
1705
  for (const leg of checked.legs) {
1664
1706
  let ok = false;
@@ -1829,7 +1871,7 @@ function omnistonProvider(opts = {}) {
1829
1871
  }
1830
1872
  });
1831
1873
  const orderProtocol = (0, import_viem.getAddress)(protocol.chain.value);
1832
- return { chain: srcChain, to: orderProtocol, value: "0", chainId, approvalAddress: orderProtocol, typedData };
1874
+ return { chain: srcChain, to: orderProtocol, value: "0", chainId, approvalAddress: orderProtocol, typedData, orderExtension: `0x${bytesToHex(payload.orderExtension)}` };
1833
1875
  }
1834
1876
  return {
1835
1877
  name: "omniston",
@@ -1938,13 +1980,13 @@ function omnistonProvider(opts = {}) {
1938
1980
  if (outputScaled * 2n < inputScaled || outputScaled > inputScaled * 2n) return refuse2("quoted output is outside 0.5x\u20132x of the input");
1939
1981
  if (await quoteIdInUse(q.quoteId)) return refuse2("the platform reused a quote id that already belongs to an order");
1940
1982
  if (!await claimBuilt(q.quoteId, { quote: q, at: Date.now(), bound: { trader, dest: params.destAddress, srcChain: params.sourceChain, kind: "order" } })) {
1941
- return refuse2("the platform reused a quote id that already holds another user's quote");
1983
+ return refuse2("the platform reused a quote id that already holds another quote");
1942
1984
  }
1943
1985
  const feeUnits = Number(q.protocolFeeUnits || "0") + Number(q.integratorFeeUnits || "0");
1944
1986
  const feeUsd = feeUnits / 10 ** dest.decimals;
1945
1987
  return {
1946
1988
  provider: "omniston",
1947
- intentId: encodeIntent(q.quoteId, trader, params.destAddress, params.sourceChain),
1989
+ intentId: encodeIntent(q.quoteId, trader, params.destAddress, params.sourceChain, "order", quoteDigest(q)),
1948
1990
  depositAddress: "",
1949
1991
  // order settlement — no static deposit address
1950
1992
  depositMemo: null,
@@ -2116,6 +2158,7 @@ function omnistonProvider(opts = {}) {
2116
2158
  );
2117
2159
  if (!order) return { state: "pending" };
2118
2160
  let secretsHex = await store.load(decoded.quoteId);
2161
+ const storedSecrets = secretsHex?.length ?? 0;
2119
2162
  if (!secretsHex || cached?.quote.settlementData.$case !== "order") trace("disclosure-skipped", { gate: "context", secrets: !!secretsHex, cached: !!cached, feedStatus: order.status });
2120
2163
  if (secretsHex && cached?.quote.settlementData.$case === "order") {
2121
2164
  const [lockedTo] = await store.load(bindKey(decoded.quoteId)) ?? [];
@@ -2124,6 +2167,7 @@ function omnistonProvider(opts = {}) {
2124
2167
  secretsHex = void 0;
2125
2168
  }
2126
2169
  }
2170
+ const disclosedNow = /* @__PURE__ */ new Set();
2127
2171
  if (secretsHex && cached?.quote.settlementData.$case === "order") {
2128
2172
  const q = cached.quote;
2129
2173
  const settlement = q.settlementData.value;
@@ -2222,11 +2266,15 @@ function omnistonProvider(opts = {}) {
2222
2266
  }
2223
2267
  await omni.orderDiscloseHtlcSecret({ quoteId: decoded.quoteId, executionIndex: i, secret: secretBytes }).then(async () => {
2224
2268
  trace("secret-disclosed", { index: i });
2269
+ disclosedNow.add(i);
2225
2270
  await store.save(disclosedKey(decoded.quoteId), ["1"]);
2226
2271
  }, (error) => trace("disclose-failed", { index: i, error: error instanceof Error ? error.message : String(error) }));
2227
2272
  })
2228
2273
  );
2229
2274
  }
2275
+ const disclosedBefore = (await store.load(disclosedIndicesKey(decoded.quoteId)) ?? []).map(Number).filter(Number.isSafeInteger);
2276
+ const disclosedAll = /* @__PURE__ */ new Set([...disclosedBefore, ...disclosedNow]);
2277
+ if (disclosedNow.size > 0) await store.save(disclosedIndicesKey(decoded.quoteId), [...disclosedAll].sort((a, b) => a - b).map(String));
2230
2278
  const status = mapOrderStatus(order);
2231
2279
  const delivered = order.executions[0]?.outputPositionAddress;
2232
2280
  const outCase = cached?.quote.outputAsset.chain.$case;
@@ -2251,6 +2299,14 @@ function omnistonProvider(opts = {}) {
2251
2299
  }).catch(() => null);
2252
2300
  if (hash) status.destTxHash = hash;
2253
2301
  }
2302
+ if (status.state === "completed" && storedSecrets > 0 && !status.destTxHash) {
2303
+ const legacyDisclosed = disclosedBefore.length === 0 && storedSecrets === 1 && (await store.load(disclosedKey(decoded.quoteId)) ?? []).length > 0;
2304
+ const allDisclosed = legacyDisclosed || (order.executions.length > 0 ? order.executions.every((execution) => disclosedAll.has(execution.index)) : disclosedAll.size > 0);
2305
+ if (!allDisclosed) {
2306
+ trace("completion-unproven", { feedStatus: order.status, executions: order.executions.length, disclosed: disclosedAll.size });
2307
+ return { state: "pending", depositReceived: order.executions.length > 0 || disclosedAll.size > 0 };
2308
+ }
2309
+ }
2254
2310
  if (status.state === "completed") {
2255
2311
  await clearSecrets(decoded.quoteId);
2256
2312
  await quotes.clear(decoded.quoteId);