@anywalletkit/protocol-swap2-omniston 0.4.0 → 0.5.1
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 +7 -4
- package/dist/{chunk-WL7BSEBR.js → chunk-2E6CVWD6.js} +2 -2
- package/dist/{chunk-R6B2CV5Z.js → chunk-4JOTKQOT.js} +62 -21
- package/dist/chunk-4JOTKQOT.js.map +1 -0
- package/dist/index.cjs +61 -20
- 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 +2 -2
- package/dist/protocol.cjs +53 -11
- 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 +2 -2
- package/dist/{provider-D6RXRgf-.d.cts → provider-CcCZB-Cj.d.cts} +7 -4
- package/dist/{provider-D6RXRgf-.d.ts → provider-CcCZB-Cj.d.ts} +7 -4
- package/dist/provider.cjs +61 -20
- 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 +1 -1
- package/dist/validators.d.cts +1 -1
- package/dist/validators.d.ts +1 -1
- package/package.json +5 -5
- package/dist/chunk-R6B2CV5Z.js.map +0 -1
- /package/dist/{chunk-WL7BSEBR.js.map → chunk-2E6CVWD6.js.map} +0 -0
package/README.md
CHANGED
|
@@ -25,20 +25,23 @@ TRON needs its own order path and is not wired yet.
|
|
|
25
25
|
Every route is fail-closed until its allowlists and validators are configured:
|
|
26
26
|
|
|
27
27
|
- **TON → TON** (DEX swap): `trustedSettlementContracts["ton:-239"]`,
|
|
28
|
-
`validateTonJettonWallet`, `validateTonSwapTarget`. The built message's DEX
|
|
28
|
+
`validateTonJettonWallet`, `validateTonSwapTarget`, `validateTonSwapLeg`. The built message's DEX
|
|
29
29
|
call is decoded in the package (STON.fi v1/v2 incl. pTON and cross-swap,
|
|
30
30
|
DeDust v1, DeDust CPMM v2): receiver = destination, refunds/excess = trader,
|
|
31
31
|
on-chain minimum ≥ the quote's, no foreign referral; unknown layouts and
|
|
32
32
|
split/multi-route quotes are refused by name. `validateTonSwapLeg`
|
|
33
33
|
(supplied by `createOmnistonTonValidators`) confirms the routers' ask
|
|
34
34
|
wallets, pTON wallet ownership and DeDust pool pairs on chain.
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
Without it the decoder cannot tell which token the DEX pays out, so the
|
|
36
|
+
route is not advertised. `tonSwapPayloadCheck: "report"` traces
|
|
37
|
+
`ton-swap-payload <reason>` (incl. `legs-unverified`) instead of refusing
|
|
38
|
+
(default `"enforce"`) — an explicit rollout opt-in.
|
|
37
39
|
Behind `remoteProvider`, the app checks the built body itself:
|
|
38
40
|
`remoteProvider({name: "omniston", baseUrl, verifyBuild: omnistonVerifyBuild({...createOmnistonTonValidators()})})`.
|
|
39
41
|
A TON → TON swap body is opened with `checkTonSwapPayload` against the app's own request (receiver,
|
|
40
42
|
refund, on-chain minimum, input; the quote carries `routeSteps`) and the DEX it pays is confirmed with
|
|
41
|
-
the app's validators
|
|
43
|
+
the app's validators — `validateTonSwapTarget` and `validateTonSwapLeg`; without the leg check a TON swap body
|
|
44
|
+
is refused. A TON → EVM escrow body goes to `verifyTonEscrowBuild`: the jetton amount, the
|
|
42
45
|
trader as owner, the destination chain, address and token and the quote's minimum delivery come from
|
|
43
46
|
the app, the protocol contract from `trustedProtocolAddresses`, and the escrow code, authority key and
|
|
44
47
|
timelocks are the audited ones. Without `verifyBuild` the engine refuses remote cell bodies.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
omnistonProvider
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-4JOTKQOT.js";
|
|
4
4
|
import {
|
|
5
5
|
omniston
|
|
6
6
|
} from "./chunk-QDXFC6BC.js";
|
|
@@ -24,4 +24,4 @@ var OmnistonSwap2Protocol = class extends SingleProviderSwap2 {
|
|
|
24
24
|
export {
|
|
25
25
|
OmnistonSwap2Protocol
|
|
26
26
|
};
|
|
27
|
-
//# sourceMappingURL=chunk-
|
|
27
|
+
//# sourceMappingURL=chunk-2E6CVWD6.js.map
|
|
@@ -394,18 +394,36 @@ function toChainAddress(caip2, address) {
|
|
|
394
394
|
if (!$case) return null;
|
|
395
395
|
return { chain: { $case, value: getAddress(address) } };
|
|
396
396
|
}
|
|
397
|
-
|
|
398
|
-
|
|
397
|
+
var QUOTE_DIGEST_SKIP = /* @__PURE__ */ new Set(["rfqId", "resolverName", "estimatedSettlementDuration", "estimatedGasConsumption"]);
|
|
398
|
+
function canonicalJson(value) {
|
|
399
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
400
|
+
if (value && typeof value === "object") {
|
|
401
|
+
return `{${Object.keys(value).sort().map((k) => `${JSON.stringify(k)}:${canonicalJson(value[k])}`).join(",")}}`;
|
|
402
|
+
}
|
|
403
|
+
return JSON.stringify(value) ?? "null";
|
|
404
|
+
}
|
|
405
|
+
function quoteDigest(q) {
|
|
406
|
+
const terms = JSON.parse(JSON.stringify(q));
|
|
407
|
+
for (const key of QUOTE_DIGEST_SKIP) delete terms[key];
|
|
408
|
+
return sha256(new TextEncoder().encode(canonicalJson(terms))).slice(2, 34);
|
|
409
|
+
}
|
|
410
|
+
var QUOTE_DIGEST = /^[0-9a-f]{32}$/;
|
|
411
|
+
function encodeIntent(quoteId, trader, dest, srcChain, kind, digest) {
|
|
412
|
+
return [INTENT_PREFIX, quoteId, trader, `${dest}#${digest}`, srcChain, ...kind === "swap" ? ["swap"] : []].map(encodeURIComponent).join("|");
|
|
399
413
|
}
|
|
400
414
|
var QUOTE_ID = /^[0-9a-fA-F]{48,64}$/;
|
|
401
415
|
function decodeIntent(intentId) {
|
|
402
416
|
try {
|
|
403
417
|
const parts = intentId.split("|");
|
|
404
418
|
if (parts.length !== 5 && parts.length !== 6 || parts[0] !== INTENT_PREFIX) return null;
|
|
405
|
-
const [, quoteId = "", trader = "",
|
|
419
|
+
const [, quoteId = "", trader = "", destPart = "", srcChain = "", kindPart = ""] = parts.map(decodeURIComponent);
|
|
420
|
+
const mark = destPart.lastIndexOf("#");
|
|
421
|
+
const dest = mark < 0 ? destPart : destPart.slice(0, mark);
|
|
422
|
+
const digest = mark < 0 ? void 0 : destPart.slice(mark + 1);
|
|
423
|
+
if (digest !== void 0 && !QUOTE_DIGEST.test(digest)) return null;
|
|
406
424
|
if (!QUOTE_ID.test(quoteId) || !trader || !dest || !srcChain) return null;
|
|
407
425
|
if (parts.length === 6 && kindPart !== "swap") return null;
|
|
408
|
-
return { quoteId, trader, dest, srcChain, kind: parts.length === 6 ? "swap" : "order" };
|
|
426
|
+
return { quoteId, trader, dest, srcChain, kind: parts.length === 6 ? "swap" : "order", ...digest ? { digest } : {} };
|
|
409
427
|
} catch {
|
|
410
428
|
return null;
|
|
411
429
|
}
|
|
@@ -1281,16 +1299,15 @@ function omnistonVerifyBuild(hooks) {
|
|
|
1281
1299
|
} catch {
|
|
1282
1300
|
return "the swap target could not be checked";
|
|
1283
1301
|
}
|
|
1284
|
-
if (hooks.validateTonSwapLeg)
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
}
|
|
1292
|
-
if (!ok) return `${leg.kind} v${leg.version} leg not verified`;
|
|
1302
|
+
if (!hooks.validateTonSwapLeg) return "a TON swap body needs validateTonSwapLeg (createOmnistonTonValidators)";
|
|
1303
|
+
for (const leg of checked.legs) {
|
|
1304
|
+
let ok = false;
|
|
1305
|
+
try {
|
|
1306
|
+
ok = await hooks.validateTonSwapLeg(leg);
|
|
1307
|
+
} catch {
|
|
1308
|
+
ok = false;
|
|
1293
1309
|
}
|
|
1310
|
+
if (!ok) return `${leg.kind} v${leg.version} leg not verified`;
|
|
1294
1311
|
}
|
|
1295
1312
|
return null;
|
|
1296
1313
|
};
|
|
@@ -1427,7 +1444,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1427
1444
|
return true;
|
|
1428
1445
|
}
|
|
1429
1446
|
const a = existing.bound, b = entry.bound;
|
|
1430
|
-
const same = !!a && !!b && a.kind === b.kind && a.srcChain === b.srcChain && sameIntentAddress(a.trader, b.trader) && sameIntentAddress(a.dest, b.dest);
|
|
1447
|
+
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);
|
|
1431
1448
|
if (!same) return false;
|
|
1432
1449
|
await quotes.save(quoteId, json);
|
|
1433
1450
|
return true;
|
|
@@ -1480,10 +1497,19 @@ function omnistonProvider(opts = {}) {
|
|
|
1480
1497
|
throw new Error(`omniston: intent id does not match the quote it was issued for (${bound.field})`);
|
|
1481
1498
|
}
|
|
1482
1499
|
if (bound.legacy) trace("legacy-intent", { where });
|
|
1500
|
+
if (!decoded.digest) {
|
|
1501
|
+
trace("intent-mismatch", { where, field: "digest-missing" });
|
|
1502
|
+
throw new Error("omniston: this quote was issued before an update \u2014 request a fresh quote");
|
|
1503
|
+
}
|
|
1504
|
+
if (decoded.digest !== quoteDigest(entry.quote)) {
|
|
1505
|
+
trace("intent-mismatch", { where, field: "digest" });
|
|
1506
|
+
throw new Error("omniston: quote was replaced \u2014 request a fresh quote");
|
|
1507
|
+
}
|
|
1483
1508
|
return bound.ref;
|
|
1484
1509
|
}
|
|
1485
1510
|
const bindKey = (quoteId) => `${quoteId}#bind`;
|
|
1486
1511
|
const disclosedKey = (quoteId) => `${quoteId}#disclosed`;
|
|
1512
|
+
const disclosedIndicesKey = (quoteId) => `${quoteId}#disclosed-indices`;
|
|
1487
1513
|
function orderBinding(ref, q) {
|
|
1488
1514
|
return sha256(new TextEncoder().encode(JSON.stringify([
|
|
1489
1515
|
ref.quoteId,
|
|
@@ -1500,6 +1526,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1500
1526
|
await store.clear(quoteId);
|
|
1501
1527
|
await store.clear(bindKey(quoteId));
|
|
1502
1528
|
await store.clear(disclosedKey(quoteId));
|
|
1529
|
+
await store.clear(disclosedIndicesKey(quoteId));
|
|
1503
1530
|
}
|
|
1504
1531
|
async function secretsFor(ref, q, count) {
|
|
1505
1532
|
const quoteId = ref.quoteId;
|
|
@@ -1528,7 +1555,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1528
1555
|
return !!existing && existing.length > 0;
|
|
1529
1556
|
}
|
|
1530
1557
|
const SWAP_SLIPPAGE_PIPS_DEFAULT = 5e3;
|
|
1531
|
-
const sameChainTonReady = () => typeof opts.validateTonJettonWallet === "function" && typeof opts.validateTonSwapTarget === "function";
|
|
1558
|
+
const sameChainTonReady = () => typeof opts.validateTonJettonWallet === "function" && typeof opts.validateTonSwapTarget === "function" && (typeof opts.validateTonSwapLeg === "function" || opts.tonSwapPayloadCheck === "report");
|
|
1532
1559
|
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";
|
|
1533
1560
|
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";
|
|
1534
1561
|
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";
|
|
@@ -1607,7 +1634,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1607
1634
|
const feeUsd = stableOut ? feeUnits / 10 ** dest.decimals : 0;
|
|
1608
1635
|
return {
|
|
1609
1636
|
provider: "omniston",
|
|
1610
|
-
intentId: encodeIntent(q.quoteId, trader, params.destAddress, params.sourceChain, "swap"),
|
|
1637
|
+
intentId: encodeIntent(q.quoteId, trader, params.destAddress, params.sourceChain, "swap", quoteDigest(q)),
|
|
1611
1638
|
depositAddress: "",
|
|
1612
1639
|
// swap settlement — the built message pays the DEX directly
|
|
1613
1640
|
depositMemo: null,
|
|
@@ -1640,7 +1667,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1640
1667
|
if (!isTon(ref.srcChain) || q.inputAsset.chain.$case !== "ton" || q.outputAsset.chain.$case !== "ton") {
|
|
1641
1668
|
throw new Error("omniston: swap quote is not a TON same-chain swap");
|
|
1642
1669
|
}
|
|
1643
|
-
if (!sameChainTonReady()) throw new Error("omniston: TON same-chain swaps need independent jetton-wallet and swap-
|
|
1670
|
+
if (!sameChainTonReady()) throw new Error("omniston: TON same-chain swaps need independent jetton-wallet, swap-target and swap-leg validators");
|
|
1644
1671
|
const owner = { chain: { $case: "ton", value: ref.trader } };
|
|
1645
1672
|
const receiver = { chain: { $case: "ton", value: ref.dest } };
|
|
1646
1673
|
const omni = await getClient();
|
|
@@ -1739,7 +1766,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1739
1766
|
}
|
|
1740
1767
|
if (checked) {
|
|
1741
1768
|
if (typeof opts.validateTonSwapLeg !== "function") {
|
|
1742
|
-
|
|
1769
|
+
payloadRefusal("legs-unverified", `no validateTonSwapLeg hook for ${checked.legs.length} leg(s)`);
|
|
1743
1770
|
} else {
|
|
1744
1771
|
for (const leg of checked.legs) {
|
|
1745
1772
|
let ok = false;
|
|
@@ -2019,13 +2046,13 @@ function omnistonProvider(opts = {}) {
|
|
|
2019
2046
|
if (outputScaled * 2n < inputScaled || outputScaled > inputScaled * 2n) return refuse2("quoted output is outside 0.5x\u20132x of the input");
|
|
2020
2047
|
if (await quoteIdInUse(q.quoteId)) return refuse2("the platform reused a quote id that already belongs to an order");
|
|
2021
2048
|
if (!await claimBuilt(q.quoteId, { quote: q, at: Date.now(), bound: { trader, dest: params.destAddress, srcChain: params.sourceChain, kind: "order" } })) {
|
|
2022
|
-
return refuse2("the platform reused a quote id that already holds another
|
|
2049
|
+
return refuse2("the platform reused a quote id that already holds another quote");
|
|
2023
2050
|
}
|
|
2024
2051
|
const feeUnits = Number(q.protocolFeeUnits || "0") + Number(q.integratorFeeUnits || "0");
|
|
2025
2052
|
const feeUsd = feeUnits / 10 ** dest.decimals;
|
|
2026
2053
|
return {
|
|
2027
2054
|
provider: "omniston",
|
|
2028
|
-
intentId: encodeIntent(q.quoteId, trader, params.destAddress, params.sourceChain),
|
|
2055
|
+
intentId: encodeIntent(q.quoteId, trader, params.destAddress, params.sourceChain, "order", quoteDigest(q)),
|
|
2029
2056
|
depositAddress: "",
|
|
2030
2057
|
// order settlement — no static deposit address
|
|
2031
2058
|
depositMemo: null,
|
|
@@ -2197,6 +2224,7 @@ function omnistonProvider(opts = {}) {
|
|
|
2197
2224
|
);
|
|
2198
2225
|
if (!order) return { state: "pending" };
|
|
2199
2226
|
let secretsHex = await store.load(decoded.quoteId);
|
|
2227
|
+
const storedSecrets = secretsHex?.length ?? 0;
|
|
2200
2228
|
if (!secretsHex || cached?.quote.settlementData.$case !== "order") trace("disclosure-skipped", { gate: "context", secrets: !!secretsHex, cached: !!cached, feedStatus: order.status });
|
|
2201
2229
|
if (secretsHex && cached?.quote.settlementData.$case === "order") {
|
|
2202
2230
|
const [lockedTo] = await store.load(bindKey(decoded.quoteId)) ?? [];
|
|
@@ -2205,6 +2233,7 @@ function omnistonProvider(opts = {}) {
|
|
|
2205
2233
|
secretsHex = void 0;
|
|
2206
2234
|
}
|
|
2207
2235
|
}
|
|
2236
|
+
const disclosedNow = /* @__PURE__ */ new Set();
|
|
2208
2237
|
if (secretsHex && cached?.quote.settlementData.$case === "order") {
|
|
2209
2238
|
const q = cached.quote;
|
|
2210
2239
|
const settlement = q.settlementData.value;
|
|
@@ -2303,11 +2332,15 @@ function omnistonProvider(opts = {}) {
|
|
|
2303
2332
|
}
|
|
2304
2333
|
await omni.orderDiscloseHtlcSecret({ quoteId: decoded.quoteId, executionIndex: i, secret: secretBytes }).then(async () => {
|
|
2305
2334
|
trace("secret-disclosed", { index: i });
|
|
2335
|
+
disclosedNow.add(i);
|
|
2306
2336
|
await store.save(disclosedKey(decoded.quoteId), ["1"]);
|
|
2307
2337
|
}, (error) => trace("disclose-failed", { index: i, error: error instanceof Error ? error.message : String(error) }));
|
|
2308
2338
|
})
|
|
2309
2339
|
);
|
|
2310
2340
|
}
|
|
2341
|
+
const disclosedBefore = (await store.load(disclosedIndicesKey(decoded.quoteId)) ?? []).map(Number).filter(Number.isSafeInteger);
|
|
2342
|
+
const disclosedAll = /* @__PURE__ */ new Set([...disclosedBefore, ...disclosedNow]);
|
|
2343
|
+
if (disclosedNow.size > 0) await store.save(disclosedIndicesKey(decoded.quoteId), [...disclosedAll].sort((a, b) => a - b).map(String));
|
|
2311
2344
|
const status = mapOrderStatus(order);
|
|
2312
2345
|
const delivered = order.executions[0]?.outputPositionAddress;
|
|
2313
2346
|
const outCase = cached?.quote.outputAsset.chain.$case;
|
|
@@ -2332,6 +2365,14 @@ function omnistonProvider(opts = {}) {
|
|
|
2332
2365
|
}).catch(() => null);
|
|
2333
2366
|
if (hash) status.destTxHash = hash;
|
|
2334
2367
|
}
|
|
2368
|
+
if (status.state === "completed" && storedSecrets > 0 && !status.destTxHash) {
|
|
2369
|
+
const legacyDisclosed = disclosedBefore.length === 0 && storedSecrets === 1 && (await store.load(disclosedKey(decoded.quoteId)) ?? []).length > 0;
|
|
2370
|
+
const allDisclosed = legacyDisclosed || (order.executions.length > 0 ? order.executions.every((execution) => disclosedAll.has(execution.index)) : disclosedAll.size > 0);
|
|
2371
|
+
if (!allDisclosed) {
|
|
2372
|
+
trace("completion-unproven", { feedStatus: order.status, executions: order.executions.length, disclosed: disclosedAll.size });
|
|
2373
|
+
return { state: "pending", depositReceived: order.executions.length > 0 || disclosedAll.size > 0 };
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2335
2376
|
if (status.state === "completed") {
|
|
2336
2377
|
await clearSecrets(decoded.quoteId);
|
|
2337
2378
|
await quotes.clear(decoded.quoteId);
|
|
@@ -2384,4 +2425,4 @@ export {
|
|
|
2384
2425
|
omnistonVerifyBuild,
|
|
2385
2426
|
omnistonProvider
|
|
2386
2427
|
};
|
|
2387
|
-
//# sourceMappingURL=chunk-
|
|
2428
|
+
//# sourceMappingURL=chunk-4JOTKQOT.js.map
|