@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/README.md +20 -4
- package/dist/{chunk-HOTFZYSW.js → chunk-2DGCZD5K.js} +214 -62
- package/dist/chunk-2DGCZD5K.js.map +1 -0
- package/dist/{chunk-RW4XZWNC.js → chunk-4MKR2SUZ.js} +2 -2
- package/dist/chunk-JWYDIW4V.js +37 -0
- package/dist/chunk-JWYDIW4V.js.map +1 -0
- package/dist/index.cjs +241 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -2
- package/dist/protocol.cjs +108 -52
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.d.cts +1 -1
- package/dist/protocol.d.ts +1 -1
- package/dist/protocol.js +3 -2
- package/dist/{provider-BBG2zhpu.d.cts → provider-CcCZB-Cj.d.cts} +15 -5
- package/dist/{provider-BBG2zhpu.d.ts → provider-CcCZB-Cj.d.ts} +15 -5
- package/dist/provider.cjs +241 -61
- package/dist/provider.cjs.map +1 -1
- package/dist/provider.d.cts +1 -1
- package/dist/provider.d.ts +1 -1
- package/dist/provider.js +2 -1
- package/dist/validators.d.cts +1 -1
- package/dist/validators.d.ts +1 -1
- package/dist/validators.js +5 -34
- package/dist/validators.js.map +1 -1
- package/package.json +5 -5
- package/dist/chunk-HOTFZYSW.js.map +0 -1
- /package/dist/{chunk-RW4XZWNC.js.map → chunk-4MKR2SUZ.js.map} +0 -0
package/dist/provider.cjs
CHANGED
|
@@ -429,6 +429,38 @@ function sameJetton(a, b) {
|
|
|
429
429
|
}
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
+
// src/validators/settlement.ts
|
|
433
|
+
var AUDITED_OMNISTON_SETTLEMENT_CONTRACTS = {
|
|
434
|
+
"ton:-239": ["EQAbWJ3Y1HgIIvcMq1prG1anlDC0T3cZlAU7luPT6LmTpmrZ"],
|
|
435
|
+
"eip155:1": ["0xafCF681272AF10959736f45616eeCBC22D7095E2"],
|
|
436
|
+
"eip155:56": ["0xafCF681272AF10959736f45616eeCBC22D7095E2"],
|
|
437
|
+
"eip155:137": ["0x41A392062A4307a6961d48e30FF20bF6823715B8"],
|
|
438
|
+
"eip155:4663": ["0xafCF681272AF10959736f45616eeCBC22D7095E2"],
|
|
439
|
+
"eip155:8453": ["0xACa3E2F9b61a44892926bC6c3e104192E7D1c3cd"],
|
|
440
|
+
"eip155:42161": ["0xafCF681272AF10959736f45616eeCBC22D7095E2"],
|
|
441
|
+
"eip155:43114": ["0xafCF681272AF10959736f45616eeCBC22D7095E2"],
|
|
442
|
+
// 2026-10-20: one factory address on X Layer, HyperEVM and Plasma. Runtime
|
|
443
|
+
// bytecode equals Ethereum's 0xafCF… byte for byte except the immutable slots
|
|
444
|
+
// (escrow implementations, its order protocol, domain hash); it references
|
|
445
|
+
// the order protocol trusted below on the same chain.
|
|
446
|
+
"eip155:196": ["0xd602079AFa5c38F550510a2a988CbFa698Fa0189"],
|
|
447
|
+
"eip155:999": ["0xd602079AFa5c38F550510a2a988CbFa698Fa0189"],
|
|
448
|
+
"eip155:9745": ["0xd602079AFa5c38F550510a2a988CbFa698Fa0189"]
|
|
449
|
+
};
|
|
450
|
+
var AUDITED_OMNISTON_EVM_ORDER_PROTOCOLS = {
|
|
451
|
+
"eip155:1": ["0xb5810aCfD45D85Eaa938bF90a5857295513DA7E9"],
|
|
452
|
+
"eip155:56": ["0xb5810aCfD45D85Eaa938bF90a5857295513DA7E9"],
|
|
453
|
+
"eip155:137": ["0xafCF681272AF10959736f45616eeCBC22D7095E2"],
|
|
454
|
+
"eip155:8453": ["0xe7405fAcDEDB2512e91F2886BDff13231343aF5E"],
|
|
455
|
+
"eip155:42161": ["0xb5810aCfD45D85Eaa938bF90a5857295513DA7E9"],
|
|
456
|
+
"eip155:43114": ["0xb5810aCfD45D85Eaa938bF90a5857295513DA7E9"],
|
|
457
|
+
// 2026-10-20: bytecode equals Ethereum's 0xb581… except immutables (wrapped
|
|
458
|
+
// native, own address, chain id 196 / 9745 / 999 at the chain-id slot).
|
|
459
|
+
"eip155:196": ["0xb5810aCfD45D85Eaa938bF90a5857295513DA7E9"],
|
|
460
|
+
"eip155:9745": ["0xb5810aCfD45D85Eaa938bF90a5857295513DA7E9"],
|
|
461
|
+
"eip155:999": ["0xD84778bbeEC5D2a8d17c6424249C438EC0b1ABE3"]
|
|
462
|
+
};
|
|
463
|
+
|
|
432
464
|
// src/provider.ts
|
|
433
465
|
var import_core4 = require("@anywalletkit/core");
|
|
434
466
|
var EVM_CASE = {
|
|
@@ -507,18 +539,36 @@ function toChainAddress(caip2, address) {
|
|
|
507
539
|
if (!$case) return null;
|
|
508
540
|
return { chain: { $case, value: (0, import_viem.getAddress)(address) } };
|
|
509
541
|
}
|
|
510
|
-
|
|
511
|
-
|
|
542
|
+
var QUOTE_DIGEST_SKIP = /* @__PURE__ */ new Set(["rfqId", "resolverName", "estimatedSettlementDuration", "estimatedGasConsumption"]);
|
|
543
|
+
function canonicalJson(value) {
|
|
544
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
545
|
+
if (value && typeof value === "object") {
|
|
546
|
+
return `{${Object.keys(value).sort().map((k) => `${JSON.stringify(k)}:${canonicalJson(value[k])}`).join(",")}}`;
|
|
547
|
+
}
|
|
548
|
+
return JSON.stringify(value) ?? "null";
|
|
549
|
+
}
|
|
550
|
+
function quoteDigest(q) {
|
|
551
|
+
const terms = JSON.parse(JSON.stringify(q));
|
|
552
|
+
for (const key of QUOTE_DIGEST_SKIP) delete terms[key];
|
|
553
|
+
return (0, import_viem.sha256)(new TextEncoder().encode(canonicalJson(terms))).slice(2, 34);
|
|
554
|
+
}
|
|
555
|
+
var QUOTE_DIGEST = /^[0-9a-f]{32}$/;
|
|
556
|
+
function encodeIntent(quoteId, trader, dest, srcChain, kind, digest) {
|
|
557
|
+
return [INTENT_PREFIX, quoteId, trader, `${dest}#${digest}`, srcChain, ...kind === "swap" ? ["swap"] : []].map(encodeURIComponent).join("|");
|
|
512
558
|
}
|
|
513
559
|
var QUOTE_ID = /^[0-9a-fA-F]{48,64}$/;
|
|
514
560
|
function decodeIntent(intentId) {
|
|
515
561
|
try {
|
|
516
562
|
const parts = intentId.split("|");
|
|
517
563
|
if (parts.length !== 5 && parts.length !== 6 || parts[0] !== INTENT_PREFIX) return null;
|
|
518
|
-
const [, quoteId = "", trader = "",
|
|
564
|
+
const [, quoteId = "", trader = "", destPart = "", srcChain = "", kindPart = ""] = parts.map(decodeURIComponent);
|
|
565
|
+
const mark = destPart.lastIndexOf("#");
|
|
566
|
+
const dest = mark < 0 ? destPart : destPart.slice(0, mark);
|
|
567
|
+
const digest = mark < 0 ? void 0 : destPart.slice(mark + 1);
|
|
568
|
+
if (digest !== void 0 && !QUOTE_DIGEST.test(digest)) return null;
|
|
519
569
|
if (!QUOTE_ID.test(quoteId) || !trader || !dest || !srcChain) return null;
|
|
520
570
|
if (parts.length === 6 && kindPart !== "swap") return null;
|
|
521
|
-
return { quoteId, trader, dest, srcChain, kind: parts.length === 6 ? "swap" : "order" };
|
|
571
|
+
return { quoteId, trader, dest, srcChain, kind: parts.length === 6 ? "swap" : "order", ...digest ? { digest } : {} };
|
|
522
572
|
} catch {
|
|
523
573
|
return null;
|
|
524
574
|
}
|
|
@@ -639,9 +689,9 @@ function validEvmAddress(value) {
|
|
|
639
689
|
}
|
|
640
690
|
}
|
|
641
691
|
var EVM_DEST_PROTOCOL_FEE_RECIPIENTS = /* @__PURE__ */ new Set(["0x076268146b4ec6d21fc49c27586cfa91b9079a00"]);
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
692
|
+
var EVM_AUCTION_POINTS = [[800000n, 100n], [700000n, 100n], [600000n, 100n], [500000n, 100n], [400000n, 100n]];
|
|
693
|
+
var EVM_TIMELOCKS = [1n, 120n, 240n, 300n, 1n, 120n, 180n, 0n].reduce((packed, value, index) => packed | value << BigInt(index * 32), 0n);
|
|
694
|
+
function readEvmOrderExtension(extension) {
|
|
645
695
|
if (extension.length < 32) throw new Error("omniston: malformed EVM order extension");
|
|
646
696
|
const view = new DataView(extension.buffer, extension.byteOffset, extension.byteLength);
|
|
647
697
|
const fields = [];
|
|
@@ -661,54 +711,48 @@ async function validateEvmToTonOrderExtension(extension, expected) {
|
|
|
661
711
|
}
|
|
662
712
|
let target;
|
|
663
713
|
for (const index of [2, 3, 7]) {
|
|
664
|
-
const
|
|
665
|
-
if (
|
|
666
|
-
const address = (0, import_viem.getAddress)(`0x${bytesToHex(
|
|
667
|
-
if (
|
|
714
|
+
const field2 = fields[index];
|
|
715
|
+
if (field2.length <= 20) throw new Error("omniston: incomplete EVM order extension program");
|
|
716
|
+
const address = (0, import_viem.getAddress)(`0x${bytesToHex(field2.slice(0, 20))}`);
|
|
717
|
+
if (target !== void 0 && (0, import_viem.getAddress)(target) !== address) {
|
|
668
718
|
throw new Error("omniston: EVM order extension target is not independently trusted");
|
|
669
719
|
}
|
|
670
720
|
target = address;
|
|
671
721
|
}
|
|
672
|
-
const
|
|
722
|
+
const field = fields[2];
|
|
673
723
|
let cursor = 20;
|
|
674
|
-
const gasBump = readBigEndian(
|
|
724
|
+
const gasBump = readBigEndian(field, cursor, 3);
|
|
675
725
|
cursor += 3;
|
|
676
|
-
const gasPrice = readBigEndian(
|
|
726
|
+
const gasPrice = readBigEndian(field, cursor, 4);
|
|
677
727
|
cursor += 4;
|
|
678
|
-
const auctionStart = readBigEndian(
|
|
728
|
+
const auctionStart = readBigEndian(field, cursor, 4);
|
|
679
729
|
cursor += 4;
|
|
680
|
-
const duration = readBigEndian(
|
|
730
|
+
const duration = readBigEndian(field, cursor, 3);
|
|
681
731
|
cursor += 3;
|
|
682
|
-
const initialRateBump = readBigEndian(
|
|
732
|
+
const initialRateBump = readBigEndian(field, cursor, 3);
|
|
683
733
|
cursor += 3;
|
|
684
|
-
const pointCount = (0, import_core4.bigintToNumber)(readBigEndian(
|
|
734
|
+
const pointCount = (0, import_core4.bigintToNumber)(readBigEndian(field, cursor, 1));
|
|
685
735
|
cursor += 1;
|
|
686
736
|
if (pointCount !== 5) throw new Error("omniston: unsupported EVM auction profile");
|
|
687
737
|
const points = [];
|
|
688
738
|
for (let index = 0; index < pointCount; index++) {
|
|
689
|
-
points.push([readBigEndian(
|
|
739
|
+
points.push([readBigEndian(field, cursor, 3), readBigEndian(field, cursor + 3, 2)]);
|
|
690
740
|
cursor += 5;
|
|
691
741
|
}
|
|
692
|
-
const integratorFee = readBigEndian(
|
|
742
|
+
const integratorFee = readBigEndian(field, cursor, 2);
|
|
693
743
|
cursor += 2;
|
|
694
|
-
const integratorShare = readBigEndian(
|
|
744
|
+
const integratorShare = readBigEndian(field, cursor, 1);
|
|
695
745
|
cursor += 1;
|
|
696
|
-
const resolverFee = readBigEndian(
|
|
746
|
+
const resolverFee = readBigEndian(field, cursor, 2);
|
|
697
747
|
cursor += 2;
|
|
698
|
-
const whitelistDiscount = readBigEndian(
|
|
748
|
+
const whitelistDiscount = readBigEndian(field, cursor, 1);
|
|
699
749
|
cursor += 1;
|
|
700
|
-
const getterWhitelistCount = (0, import_core4.bigintToNumber)(readBigEndian(
|
|
750
|
+
const getterWhitelistCount = (0, import_core4.bigintToNumber)(readBigEndian(field, cursor, 1));
|
|
701
751
|
cursor += 1;
|
|
702
|
-
if (getterWhitelistCount !== 1 || cursor + getterWhitelistCount * 10 !==
|
|
752
|
+
if (getterWhitelistCount !== 1 || cursor + getterWhitelistCount * 10 !== field.length) {
|
|
703
753
|
throw new Error("omniston: malformed EVM amount-getter whitelist");
|
|
704
754
|
}
|
|
705
|
-
const
|
|
706
|
-
const expectedPoints = [[800000n, 100n], [700000n, 100n], [600000n, 100n], [500000n, 100n], [400000n, 100n]];
|
|
707
|
-
const quoteResolverFee = expected.protocolFeePips % 10 === 0 ? BigInt(expected.protocolFeePips / 10) : -1n;
|
|
708
|
-
const resolverLow80 = BigInt((0, import_viem.getAddress)(expected.initialResolverAddress)) & (1n << 80n) - 1n;
|
|
709
|
-
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) {
|
|
710
|
-
throw new Error("omniston: EVM amount-getter terms do not match the pinned v1beta8 profile");
|
|
711
|
-
}
|
|
755
|
+
const getter = { gasBump, gasPrice, auctionStart, duration, initialRateBump, points, integratorFee, integratorShare, resolverFee, whitelistDiscount, resolver: readBigEndian(field, cursor, 10) };
|
|
712
756
|
const post = fields[7];
|
|
713
757
|
cursor = 20;
|
|
714
758
|
const integratorRecipient = post.slice(cursor, cursor + 20);
|
|
@@ -734,28 +778,51 @@ async function validateEvmToTonOrderExtension(extension, expected) {
|
|
|
734
778
|
cursor += 10;
|
|
735
779
|
const resolverTimeDelta = readBigEndian(post, cursor, 2);
|
|
736
780
|
cursor += 2;
|
|
737
|
-
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) {
|
|
738
|
-
throw new Error("omniston: EVM post-interaction terms do not match the pinned v1beta8 profile");
|
|
739
|
-
}
|
|
740
781
|
const extra = Array.from({ length: 5 }, () => {
|
|
741
782
|
const value = readBigEndian(post, cursor, 32);
|
|
742
783
|
cursor += 32;
|
|
743
784
|
return value;
|
|
744
785
|
});
|
|
786
|
+
const customData = extension.slice(32 + begin);
|
|
787
|
+
if (customData.length !== 131) throw new Error("omniston: malformed EVM order extension custom data");
|
|
788
|
+
return {
|
|
789
|
+
target,
|
|
790
|
+
getter,
|
|
791
|
+
post: { integratorRecipient, protocolRecipient, integratorFee: postIntegratorFee, integratorShare: postIntegratorShare, resolverFee: postResolverFee, discount: postDiscount, allowedTime, resolver: postResolver, resolverTimeDelta },
|
|
792
|
+
extra,
|
|
793
|
+
customData
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
async function validateEvmToTonOrderExtension(extension, expected) {
|
|
797
|
+
const evmDest = expected.dstChainId !== void 0;
|
|
798
|
+
if (evmDest && (!Number.isSafeInteger(expected.dstChainId) || expected.dstChainId <= 0)) throw new Error("omniston: invalid EVM destination chain id");
|
|
799
|
+
const { target, getter, post, extra, customData } = readEvmOrderExtension(extension);
|
|
800
|
+
if (!expected.trustedTarget(target)) throw new Error("omniston: EVM order extension target is not independently trusted");
|
|
801
|
+
const quoteResolverFee = expected.protocolFeePips % 10 === 0 ? BigInt(expected.protocolFeePips / 10) : -1n;
|
|
802
|
+
const resolverLow80 = BigInt((0, import_viem.getAddress)(expected.initialResolverAddress)) & (1n << 80n) - 1n;
|
|
803
|
+
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) {
|
|
804
|
+
throw new Error("omniston: EVM amount-getter terms do not match the pinned v1beta8 profile");
|
|
805
|
+
}
|
|
806
|
+
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) {
|
|
807
|
+
throw new Error("omniston: EVM post-interaction terms do not match the pinned v1beta8 profile");
|
|
808
|
+
}
|
|
745
809
|
const hashlock2 = readBigEndian(expected.secretHash, 0, expected.secretHash.length);
|
|
746
810
|
const dstToken = evmDest ? BigInt((0, import_viem.getAddress)(expected.outputTokenAddress)) : BigInt((0, import_viem.getAddress)(expected.takerAsset));
|
|
747
811
|
const deposits = BigInt(expected.srcSecurityDepositUnits) << 128n | BigInt(expected.dstSecurityDepositUnits);
|
|
748
|
-
|
|
749
|
-
if (expected.secretHash.length !== 32 || extra[0] !== hashlock2 || extra[1] !== (evmDest ? BigInt(expected.dstChainId) : 0n) || extra[2] !== dstToken || extra[3] !== deposits || extra[4] !== timelocks) {
|
|
812
|
+
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) {
|
|
750
813
|
throw new Error("omniston: signed EVM HTLC terms do not match the validated order details");
|
|
751
814
|
}
|
|
752
|
-
|
|
753
|
-
if (
|
|
754
|
-
|
|
815
|
+
await checkOrderCustomData(customData, { evmDest, outputToken: expected.outputTokenAddress, traderDst: expected.traderDstAddress, quoteId: expected.quoteId });
|
|
816
|
+
if (expected.tradeStartDeadline < expected.quoteTimestamp) {
|
|
817
|
+
throw new Error("omniston: invalid EVM order quote deadline");
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
async function checkOrderCustomData(customData, expected) {
|
|
821
|
+
if (expected.evmDest) {
|
|
755
822
|
if (!zeroBytes(customData.slice(0, 66))) throw new Error("omniston: unexpected TON addresses in an EVM-destination order");
|
|
756
823
|
} else {
|
|
757
824
|
const { Address: Address3 } = await import("@ton/core");
|
|
758
|
-
const expectedTon = [expected.
|
|
825
|
+
const expectedTon = [expected.outputToken, expected.traderDst].map((value) => Address3.parse(value));
|
|
759
826
|
for (let index = 0; index < expectedTon.length; index++) {
|
|
760
827
|
const offset = index * 33;
|
|
761
828
|
const workchain = customData[offset] === 255 ? -1 : customData[offset];
|
|
@@ -771,9 +838,6 @@ async function validateEvmToTonOrderExtension(extension, expected) {
|
|
|
771
838
|
if (!QUOTE_ID.test(quoteId) || !sliceEqual(customData.slice(99), hexToBytes(quoteId.padStart(64, "0")))) {
|
|
772
839
|
throw new Error("omniston: EVM order extension custom data is not bound to the quote id");
|
|
773
840
|
}
|
|
774
|
-
if (expected.tradeStartDeadline < expected.quoteTimestamp) {
|
|
775
|
-
throw new Error("omniston: invalid EVM order quote deadline");
|
|
776
|
-
}
|
|
777
841
|
}
|
|
778
842
|
function validateEvmTypedOrder(td, expected) {
|
|
779
843
|
const value = td;
|
|
@@ -1225,8 +1289,101 @@ function omnistonPins(trustedEvmOrderProtocols) {
|
|
|
1225
1289
|
const byChain = new Map(Object.entries(trustedEvmOrderProtocols).map(([chain, list]) => [chain, list.map((a) => (0, import_viem.getAddress)(a))]));
|
|
1226
1290
|
return Object.freeze({ callTargets: () => [], spenders: (chain) => byChain.get(chain) ?? [] });
|
|
1227
1291
|
}
|
|
1292
|
+
var MAX_EVM_ORDER_LIFETIME_S = 900;
|
|
1293
|
+
async function checkRemoteEvmOrder(quote, tx, params, trusted) {
|
|
1294
|
+
const chainId = evmRef2(params.sourceChain);
|
|
1295
|
+
if (chainId === null || !params.sourceChain.startsWith("eip155:")) return "a typed order needs an EVM source";
|
|
1296
|
+
const intent = decodeIntent(quote.intentId.replace(/~[0-9a-f]{32}$/, ""));
|
|
1297
|
+
if (!intent || intent.kind !== "order" || intent.srcChain !== params.sourceChain) return "the quote's intent is not an order from this chain";
|
|
1298
|
+
if (!tx.orderExtension || !/^0x(?:[0-9a-fA-F]{2})+$/.test(tx.orderExtension)) return "the build carries no order extension to check";
|
|
1299
|
+
const source = (0, import_swap2_assets.ccAsset)(params.sourceChain, params.sourceToken);
|
|
1300
|
+
if (!source?.contract) return "unknown source token";
|
|
1301
|
+
const tonDest = isTon(params.destChain);
|
|
1302
|
+
const destChainId = tonDest ? null : evmRef2(params.destChain);
|
|
1303
|
+
if (!tonDest && (destChainId === null || !params.destChain.startsWith("eip155:"))) return "the destination is neither TON nor an EVM chain";
|
|
1304
|
+
const outputToken = (0, import_swap2_assets.ccAssetByToken)(params.destChain, params.destToken)?.contract ?? params.destToken;
|
|
1305
|
+
const listed = (table, chain, address) => typeof address === "string" && validEvmAddress(address) && (table[chain] ?? []).some((a) => validEvmAddress(a) && (0, import_viem.getAddress)(a) === (0, import_viem.getAddress)(address));
|
|
1306
|
+
let td;
|
|
1307
|
+
try {
|
|
1308
|
+
td = JSON.parse(tx.typedData ?? "");
|
|
1309
|
+
} catch {
|
|
1310
|
+
return "the typed data is not JSON";
|
|
1311
|
+
}
|
|
1312
|
+
const domain = td.domain ?? {};
|
|
1313
|
+
const contract = domain.verifyingContract;
|
|
1314
|
+
if (domain.name !== "1inch Limit Order Protocol" || domain.version !== "4") return "the domain is not the 1inch Limit Order Protocol v4";
|
|
1315
|
+
if (!listed(trusted.orderProtocols, params.sourceChain, contract) || !tx.approvalAddress || (0, import_viem.getAddress)(tx.approvalAddress) !== (0, import_viem.getAddress)(String(contract))) {
|
|
1316
|
+
return `${String(contract)} is not a trusted order protocol on ${params.sourceChain}`;
|
|
1317
|
+
}
|
|
1318
|
+
if (Number(domain.chainId) !== chainId || tx.chainId !== void 0 && tx.chainId !== chainId) return "the domain chainId is not the source chain";
|
|
1319
|
+
const order = td.types?.Order;
|
|
1320
|
+
const message = td.message;
|
|
1321
|
+
if (td.primaryType !== "Order" || !Array.isArray(order) || order.length !== ORDER_FIELDS.length || !order.every((field, i) => field.name === ORDER_FIELDS[i][0] && field.type === ORDER_FIELDS[i][1]) || !message || Object.keys(message).length !== ORDER_FIELDS.length || !ORDER_FIELDS.every(([name]) => Object.hasOwn(message, name)) || !ORDER_FIELDS.every(([name, type]) => type === "address" ? validEvmAddress(message[name]) : validUint256(message[name]))) {
|
|
1322
|
+
return "the typed data is not exactly a v4 Order";
|
|
1323
|
+
}
|
|
1324
|
+
if (!validEvmAddress(intent.trader) || (0, import_viem.getAddress)(String(message.maker)) !== (0, import_viem.getAddress)(intent.trader)) return "the maker is not the trader";
|
|
1325
|
+
if ((0, import_viem.getAddress)(String(message.makerAsset)) !== (0, import_viem.getAddress)(source.contract)) return "the makerAsset is not the source token";
|
|
1326
|
+
if (BigInt(message.makingAmount) !== BigInt(quote.sourceAmount)) return "the makingAmount is not the input";
|
|
1327
|
+
if ((0, import_viem.getAddress)(String(message.takerAsset)) === (0, import_viem.getAddress)(String(message.makerAsset))) return "the takerAsset is the makerAsset";
|
|
1328
|
+
const floor = [quote.minDestAmount, params.minAmountOut ?? "0"].filter((v) => /^\d{1,80}$/.test(v)).map((v) => BigInt(v)).reduce((a, b) => a > b ? a : b, 0n);
|
|
1329
|
+
if (floor <= 0n || BigInt(message.takingAmount) < floor) return `takingAmount ${String(message.takingAmount)} is below the quote's minimum ${floor}`;
|
|
1330
|
+
const traits = BigInt(message.makerTraits);
|
|
1331
|
+
const flags = traits & (1n << 56n) - 1n << 200n;
|
|
1332
|
+
const baseFlags = 1n << 251n | 1n << 249n;
|
|
1333
|
+
if (flags !== baseFlags && flags !== (baseFlags | 1n << 255n)) return "the makerTraits flags are not a single-fill order with its extension";
|
|
1334
|
+
if ((traits & (1n << 80n) - 1n) !== 0n || (traits >> 160n & (1n << 40n) - 1n) !== 0n) return "the makerTraits restrict the taker or the series";
|
|
1335
|
+
const now = BigInt(Math.floor(Date.now() / 1e3));
|
|
1336
|
+
const expiration = traits >> 80n & (1n << 40n) - 1n;
|
|
1337
|
+
if (expiration <= now || expiration > now + BigInt(MAX_EVM_ORDER_LIFETIME_S)) return "the order's expiry is not minutes away";
|
|
1338
|
+
const extension = (0, import_viem.hexToBytes)(tx.orderExtension);
|
|
1339
|
+
const mask = (1n << 160n) - 1n;
|
|
1340
|
+
if ((BigInt(message.salt) & mask) !== (BigInt((0, import_viem.keccak256)(extension)) & mask)) return "the salt does not commit the order extension";
|
|
1341
|
+
let parsed;
|
|
1342
|
+
try {
|
|
1343
|
+
parsed = readEvmOrderExtension(extension);
|
|
1344
|
+
} catch (e) {
|
|
1345
|
+
return e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e);
|
|
1346
|
+
}
|
|
1347
|
+
const { target, getter, post, extra, customData } = parsed;
|
|
1348
|
+
if (!listed(trusted.factories, params.sourceChain, target)) return `the extension targets ${target}, not a trusted escrow factory on ${params.sourceChain}`;
|
|
1349
|
+
const receiverLow80 = BigInt((0, import_viem.getAddress)(String(message.receiver))) & (1n << 80n) - 1n;
|
|
1350
|
+
if (getter.resolver !== receiverLow80 || post.resolver !== receiverLow80) return "the order's receiver is not the resolver its extension whitelists";
|
|
1351
|
+
if (getter.gasBump !== 0n || getter.gasPrice !== 0n || getter.duration !== 1800n || getter.initialRateBump !== 900000n || getter.points.some((point, index) => point[0] !== EVM_AUCTION_POINTS[index][0] || point[1] !== EVM_AUCTION_POINTS[index][1]) || getter.integratorFee !== 0n || getter.integratorShare !== 100n || getter.whitelistDiscount !== 50n || !within(getter.auctionStart, (0, import_core4.bigintToNumber)(now), MAX_EVM_ORDER_LIFETIME_S, 60)) {
|
|
1352
|
+
return "the amount-getter terms do not match the pinned v1beta8 profile";
|
|
1353
|
+
}
|
|
1354
|
+
if (!zeroBytes(post.integratorRecipient) || !(zeroBytes(post.protocolRecipient) || !tonDest && EVM_DEST_PROTOCOL_FEE_RECIPIENTS.has(`0x${bytesToHex(post.protocolRecipient)}`)) || post.integratorFee !== 0n || post.integratorShare !== 100n || post.resolverFee !== getter.resolverFee || post.discount !== 50n || post.resolverTimeDelta !== 0n || !within(post.allowedTime, (0, import_core4.bigintToNumber)(getter.auctionStart), 60, 5)) {
|
|
1355
|
+
return "the post-interaction terms do not match the pinned v1beta8 profile";
|
|
1356
|
+
}
|
|
1357
|
+
let dstToken;
|
|
1358
|
+
try {
|
|
1359
|
+
dstToken = tonDest ? BigInt((0, import_viem.getAddress)(String(message.takerAsset))) : BigInt((0, import_viem.getAddress)(outputToken));
|
|
1360
|
+
} catch {
|
|
1361
|
+
return "the destination token is not an EVM address";
|
|
1362
|
+
}
|
|
1363
|
+
if (extra[1] !== (tonDest ? 0n : BigInt(destChainId)) || extra[2] !== dstToken || extra[4] !== EVM_TIMELOCKS) {
|
|
1364
|
+
return "the signed HTLC terms name another destination chain or token, or other timelocks";
|
|
1365
|
+
}
|
|
1366
|
+
try {
|
|
1367
|
+
await checkOrderCustomData(customData, { evmDest: !tonDest, outputToken, traderDst: params.destAddress, quoteId: intent.quoteId });
|
|
1368
|
+
} catch (e) {
|
|
1369
|
+
return e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e);
|
|
1370
|
+
}
|
|
1371
|
+
return null;
|
|
1372
|
+
}
|
|
1228
1373
|
function omnistonVerifyBuild(hooks) {
|
|
1374
|
+
const trusted = {
|
|
1375
|
+
orderProtocols: hooks.trustedEvmOrderProtocols ?? AUDITED_OMNISTON_EVM_ORDER_PROTOCOLS,
|
|
1376
|
+
factories: hooks.trustedSettlementContracts ?? AUDITED_OMNISTON_SETTLEMENT_CONTRACTS
|
|
1377
|
+
};
|
|
1229
1378
|
return async (quote, tx, params) => {
|
|
1379
|
+
if (tx.typedData !== void 0) {
|
|
1380
|
+
if (!params) return "the request this quote answered is unknown";
|
|
1381
|
+
try {
|
|
1382
|
+
return await checkRemoteEvmOrder(quote, tx, params, trusted);
|
|
1383
|
+
} catch {
|
|
1384
|
+
return "the typed order could not be checked";
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1230
1387
|
if (tx.bodyBoc === void 0) return null;
|
|
1231
1388
|
if (!params) return "the request this quote answered is unknown";
|
|
1232
1389
|
if (quote.providerData?.settlement !== "swap") {
|
|
@@ -1287,16 +1444,15 @@ function omnistonVerifyBuild(hooks) {
|
|
|
1287
1444
|
} catch {
|
|
1288
1445
|
return "the swap target could not be checked";
|
|
1289
1446
|
}
|
|
1290
|
-
if (hooks.validateTonSwapLeg)
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
}
|
|
1298
|
-
if (!ok) return `${leg.kind} v${leg.version} leg not verified`;
|
|
1447
|
+
if (!hooks.validateTonSwapLeg) return "a TON swap body needs validateTonSwapLeg (createOmnistonTonValidators)";
|
|
1448
|
+
for (const leg of checked.legs) {
|
|
1449
|
+
let ok = false;
|
|
1450
|
+
try {
|
|
1451
|
+
ok = await hooks.validateTonSwapLeg(leg);
|
|
1452
|
+
} catch {
|
|
1453
|
+
ok = false;
|
|
1299
1454
|
}
|
|
1455
|
+
if (!ok) return `${leg.kind} v${leg.version} leg not verified`;
|
|
1300
1456
|
}
|
|
1301
1457
|
return null;
|
|
1302
1458
|
};
|
|
@@ -1433,7 +1589,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1433
1589
|
return true;
|
|
1434
1590
|
}
|
|
1435
1591
|
const a = existing.bound, b = entry.bound;
|
|
1436
|
-
const same = !!a && !!b && a.kind === b.kind && a.srcChain === b.srcChain && sameIntentAddress(a.trader, b.trader) && sameIntentAddress(a.dest, b.dest);
|
|
1592
|
+
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);
|
|
1437
1593
|
if (!same) return false;
|
|
1438
1594
|
await quotes.save(quoteId, json);
|
|
1439
1595
|
return true;
|
|
@@ -1486,10 +1642,19 @@ function omnistonProvider(opts = {}) {
|
|
|
1486
1642
|
throw new Error(`omniston: intent id does not match the quote it was issued for (${bound.field})`);
|
|
1487
1643
|
}
|
|
1488
1644
|
if (bound.legacy) trace("legacy-intent", { where });
|
|
1645
|
+
if (!decoded.digest) {
|
|
1646
|
+
trace("intent-mismatch", { where, field: "digest-missing" });
|
|
1647
|
+
throw new Error("omniston: this quote was issued before an update \u2014 request a fresh quote");
|
|
1648
|
+
}
|
|
1649
|
+
if (decoded.digest !== quoteDigest(entry.quote)) {
|
|
1650
|
+
trace("intent-mismatch", { where, field: "digest" });
|
|
1651
|
+
throw new Error("omniston: quote was replaced \u2014 request a fresh quote");
|
|
1652
|
+
}
|
|
1489
1653
|
return bound.ref;
|
|
1490
1654
|
}
|
|
1491
1655
|
const bindKey = (quoteId) => `${quoteId}#bind`;
|
|
1492
1656
|
const disclosedKey = (quoteId) => `${quoteId}#disclosed`;
|
|
1657
|
+
const disclosedIndicesKey = (quoteId) => `${quoteId}#disclosed-indices`;
|
|
1493
1658
|
function orderBinding(ref, q) {
|
|
1494
1659
|
return (0, import_viem.sha256)(new TextEncoder().encode(JSON.stringify([
|
|
1495
1660
|
ref.quoteId,
|
|
@@ -1506,6 +1671,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1506
1671
|
await store.clear(quoteId);
|
|
1507
1672
|
await store.clear(bindKey(quoteId));
|
|
1508
1673
|
await store.clear(disclosedKey(quoteId));
|
|
1674
|
+
await store.clear(disclosedIndicesKey(quoteId));
|
|
1509
1675
|
}
|
|
1510
1676
|
async function secretsFor(ref, q, count) {
|
|
1511
1677
|
const quoteId = ref.quoteId;
|
|
@@ -1534,7 +1700,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1534
1700
|
return !!existing && existing.length > 0;
|
|
1535
1701
|
}
|
|
1536
1702
|
const SWAP_SLIPPAGE_PIPS_DEFAULT = 5e3;
|
|
1537
|
-
const sameChainTonReady = () => typeof opts.validateTonJettonWallet === "function" && typeof opts.validateTonSwapTarget === "function";
|
|
1703
|
+
const sameChainTonReady = () => typeof opts.validateTonJettonWallet === "function" && typeof opts.validateTonSwapTarget === "function" && (typeof opts.validateTonSwapLeg === "function" || opts.tonSwapPayloadCheck === "report");
|
|
1538
1704
|
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";
|
|
1539
1705
|
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";
|
|
1540
1706
|
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";
|
|
@@ -1613,7 +1779,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1613
1779
|
const feeUsd = stableOut ? feeUnits / 10 ** dest.decimals : 0;
|
|
1614
1780
|
return {
|
|
1615
1781
|
provider: "omniston",
|
|
1616
|
-
intentId: encodeIntent(q.quoteId, trader, params.destAddress, params.sourceChain, "swap"),
|
|
1782
|
+
intentId: encodeIntent(q.quoteId, trader, params.destAddress, params.sourceChain, "swap", quoteDigest(q)),
|
|
1617
1783
|
depositAddress: "",
|
|
1618
1784
|
// swap settlement — the built message pays the DEX directly
|
|
1619
1785
|
depositMemo: null,
|
|
@@ -1646,7 +1812,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1646
1812
|
if (!isTon(ref.srcChain) || q.inputAsset.chain.$case !== "ton" || q.outputAsset.chain.$case !== "ton") {
|
|
1647
1813
|
throw new Error("omniston: swap quote is not a TON same-chain swap");
|
|
1648
1814
|
}
|
|
1649
|
-
if (!sameChainTonReady()) throw new Error("omniston: TON same-chain swaps need independent jetton-wallet and swap-
|
|
1815
|
+
if (!sameChainTonReady()) throw new Error("omniston: TON same-chain swaps need independent jetton-wallet, swap-target and swap-leg validators");
|
|
1650
1816
|
const owner = { chain: { $case: "ton", value: ref.trader } };
|
|
1651
1817
|
const receiver = { chain: { $case: "ton", value: ref.dest } };
|
|
1652
1818
|
const omni = await getClient();
|
|
@@ -1745,7 +1911,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1745
1911
|
}
|
|
1746
1912
|
if (checked) {
|
|
1747
1913
|
if (typeof opts.validateTonSwapLeg !== "function") {
|
|
1748
|
-
|
|
1914
|
+
payloadRefusal("legs-unverified", `no validateTonSwapLeg hook for ${checked.legs.length} leg(s)`);
|
|
1749
1915
|
} else {
|
|
1750
1916
|
for (const leg of checked.legs) {
|
|
1751
1917
|
let ok = false;
|
|
@@ -1916,7 +2082,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1916
2082
|
}
|
|
1917
2083
|
});
|
|
1918
2084
|
const orderProtocol = (0, import_viem.getAddress)(protocol.chain.value);
|
|
1919
|
-
return { chain: srcChain, to: orderProtocol, value: "0", chainId, approvalAddress: orderProtocol, typedData };
|
|
2085
|
+
return { chain: srcChain, to: orderProtocol, value: "0", chainId, approvalAddress: orderProtocol, typedData, orderExtension: `0x${bytesToHex(payload.orderExtension)}` };
|
|
1920
2086
|
}
|
|
1921
2087
|
return {
|
|
1922
2088
|
name: "omniston",
|
|
@@ -2025,13 +2191,13 @@ function omnistonProvider(opts = {}) {
|
|
|
2025
2191
|
if (outputScaled * 2n < inputScaled || outputScaled > inputScaled * 2n) return refuse2("quoted output is outside 0.5x\u20132x of the input");
|
|
2026
2192
|
if (await quoteIdInUse(q.quoteId)) return refuse2("the platform reused a quote id that already belongs to an order");
|
|
2027
2193
|
if (!await claimBuilt(q.quoteId, { quote: q, at: Date.now(), bound: { trader, dest: params.destAddress, srcChain: params.sourceChain, kind: "order" } })) {
|
|
2028
|
-
return refuse2("the platform reused a quote id that already holds another
|
|
2194
|
+
return refuse2("the platform reused a quote id that already holds another quote");
|
|
2029
2195
|
}
|
|
2030
2196
|
const feeUnits = Number(q.protocolFeeUnits || "0") + Number(q.integratorFeeUnits || "0");
|
|
2031
2197
|
const feeUsd = feeUnits / 10 ** dest.decimals;
|
|
2032
2198
|
return {
|
|
2033
2199
|
provider: "omniston",
|
|
2034
|
-
intentId: encodeIntent(q.quoteId, trader, params.destAddress, params.sourceChain),
|
|
2200
|
+
intentId: encodeIntent(q.quoteId, trader, params.destAddress, params.sourceChain, "order", quoteDigest(q)),
|
|
2035
2201
|
depositAddress: "",
|
|
2036
2202
|
// order settlement — no static deposit address
|
|
2037
2203
|
depositMemo: null,
|
|
@@ -2203,6 +2369,7 @@ function omnistonProvider(opts = {}) {
|
|
|
2203
2369
|
);
|
|
2204
2370
|
if (!order) return { state: "pending" };
|
|
2205
2371
|
let secretsHex = await store.load(decoded.quoteId);
|
|
2372
|
+
const storedSecrets = secretsHex?.length ?? 0;
|
|
2206
2373
|
if (!secretsHex || cached?.quote.settlementData.$case !== "order") trace("disclosure-skipped", { gate: "context", secrets: !!secretsHex, cached: !!cached, feedStatus: order.status });
|
|
2207
2374
|
if (secretsHex && cached?.quote.settlementData.$case === "order") {
|
|
2208
2375
|
const [lockedTo] = await store.load(bindKey(decoded.quoteId)) ?? [];
|
|
@@ -2211,6 +2378,7 @@ function omnistonProvider(opts = {}) {
|
|
|
2211
2378
|
secretsHex = void 0;
|
|
2212
2379
|
}
|
|
2213
2380
|
}
|
|
2381
|
+
const disclosedNow = /* @__PURE__ */ new Set();
|
|
2214
2382
|
if (secretsHex && cached?.quote.settlementData.$case === "order") {
|
|
2215
2383
|
const q = cached.quote;
|
|
2216
2384
|
const settlement = q.settlementData.value;
|
|
@@ -2309,11 +2477,15 @@ function omnistonProvider(opts = {}) {
|
|
|
2309
2477
|
}
|
|
2310
2478
|
await omni.orderDiscloseHtlcSecret({ quoteId: decoded.quoteId, executionIndex: i, secret: secretBytes }).then(async () => {
|
|
2311
2479
|
trace("secret-disclosed", { index: i });
|
|
2480
|
+
disclosedNow.add(i);
|
|
2312
2481
|
await store.save(disclosedKey(decoded.quoteId), ["1"]);
|
|
2313
2482
|
}, (error) => trace("disclose-failed", { index: i, error: error instanceof Error ? error.message : String(error) }));
|
|
2314
2483
|
})
|
|
2315
2484
|
);
|
|
2316
2485
|
}
|
|
2486
|
+
const disclosedBefore = (await store.load(disclosedIndicesKey(decoded.quoteId)) ?? []).map(Number).filter(Number.isSafeInteger);
|
|
2487
|
+
const disclosedAll = /* @__PURE__ */ new Set([...disclosedBefore, ...disclosedNow]);
|
|
2488
|
+
if (disclosedNow.size > 0) await store.save(disclosedIndicesKey(decoded.quoteId), [...disclosedAll].sort((a, b) => a - b).map(String));
|
|
2317
2489
|
const status = mapOrderStatus(order);
|
|
2318
2490
|
const delivered = order.executions[0]?.outputPositionAddress;
|
|
2319
2491
|
const outCase = cached?.quote.outputAsset.chain.$case;
|
|
@@ -2338,6 +2510,14 @@ function omnistonProvider(opts = {}) {
|
|
|
2338
2510
|
}).catch(() => null);
|
|
2339
2511
|
if (hash) status.destTxHash = hash;
|
|
2340
2512
|
}
|
|
2513
|
+
if (status.state === "completed" && storedSecrets > 0 && !status.destTxHash) {
|
|
2514
|
+
const legacyDisclosed = disclosedBefore.length === 0 && storedSecrets === 1 && (await store.load(disclosedKey(decoded.quoteId)) ?? []).length > 0;
|
|
2515
|
+
const allDisclosed = legacyDisclosed || (order.executions.length > 0 ? order.executions.every((execution) => disclosedAll.has(execution.index)) : disclosedAll.size > 0);
|
|
2516
|
+
if (!allDisclosed) {
|
|
2517
|
+
trace("completion-unproven", { feedStatus: order.status, executions: order.executions.length, disclosed: disclosedAll.size });
|
|
2518
|
+
return { state: "pending", depositReceived: order.executions.length > 0 || disclosedAll.size > 0 };
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2341
2521
|
if (status.state === "completed") {
|
|
2342
2522
|
await clearSecrets(decoded.quoteId);
|
|
2343
2523
|
await quotes.clear(decoded.quoteId);
|