@anywalletkit/protocol-swap2-omniston 0.2.8 → 0.3.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/protocol.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  OmnistonSwap2Protocol
3
- } from "./chunk-KVDPDEJB.js";
4
- import "./chunk-KNT2XSQU.js";
3
+ } from "./chunk-CRYYP6X3.js";
4
+ import "./chunk-JVFDD7PV.js";
5
5
  import "./chunk-QDXFC6BC.js";
6
6
  import "./chunk-AIWAM7BS.js";
7
7
  export {
package/dist/provider.cjs CHANGED
@@ -36,8 +36,11 @@ __export(provider_exports, {
36
36
  encodeTypedMessage: () => encodeTypedMessage,
37
37
  inMemoryQuoteStore: () => inMemoryQuoteStore,
38
38
  inMemorySecretStore: () => inMemorySecretStore,
39
+ omnistonPins: () => omnistonPins,
39
40
  omnistonProvider: () => omnistonProvider,
40
- validateEvmToTonOrderExtension: () => validateEvmToTonOrderExtension
41
+ omnistonVerifyBuild: () => omnistonVerifyBuild,
42
+ validateEvmToTonOrderExtension: () => validateEvmToTonOrderExtension,
43
+ validateEvmTypedOrder: () => validateEvmTypedOrder
41
44
  });
42
45
  module.exports = __toCommonJS(provider_exports);
43
46
  var import_core3 = require("@ton/core");
@@ -456,7 +459,12 @@ function inMemoryQuoteStore() {
456
459
  return {
457
460
  save: (id, v) => void map.set(id, v),
458
461
  load: (id) => map.get(id),
459
- clear: (id) => void map.delete(id)
462
+ clear: (id) => void map.delete(id),
463
+ claim: (id, v) => {
464
+ if (map.has(id)) return false;
465
+ map.set(id, v);
466
+ return true;
467
+ }
460
468
  };
461
469
  }
462
470
  function inMemorySecretStore() {
@@ -1030,6 +1038,13 @@ function validSwapSettlement(q) {
1030
1038
  const v = q.settlementData.value;
1031
1039
  return Array.isArray(v.routes) && v.routes.length > 0 && v.routes.every((route) => Array.isArray(route?.steps) && route.steps.length > 0 && route.steps.every((step) => Array.isArray(step?.chunks) && step.chunks.length > 0 && step.chunks.every((chunk) => typeof chunk?.protocol === "string" && chunk.protocol.length > 0 && isPositiveInteger(chunk.inputUnits) && isPositiveInteger(chunk.outputUnits)))) && isPositiveInteger(v.minOutputAmount) && BigInt(v.minOutputAmount) <= BigInt(q.outputUnits) && (v.recommendedMinOutputAmount === void 0 || isPositiveInteger(v.recommendedMinOutputAmount) && BigInt(v.recommendedMinOutputAmount) <= BigInt(q.outputUnits)) && (q.gasBudget === void 0 || isPositiveInteger(q.gasBudget));
1032
1040
  }
1041
+ function swapSteps(v) {
1042
+ if (v.routes.length !== 1) return [{ protocol: `${v.routes.length}-routes`, inputJetton: void 0, outputJetton: void 0 }];
1043
+ return v.routes[0].steps.map((step) => {
1044
+ if (step.chunks.length !== 1) return { protocol: `split(${step.chunks.map((c) => c.protocol).join("|")})`, inputJetton: void 0, outputJetton: void 0 };
1045
+ return { protocol: step.chunks[0].protocol, inputJetton: stepJetton(step.inputAsset), outputJetton: stepJetton(step.outputAsset) };
1046
+ });
1047
+ }
1033
1048
  function swapProtocols(v) {
1034
1049
  const names = /* @__PURE__ */ new Set();
1035
1050
  for (const route of v.routes) for (const step of route.steps) for (const chunk of step.chunks) names.add(chunk.protocol);
@@ -1206,6 +1221,66 @@ function mapOrderStatus(order) {
1206
1221
  return { state: "pending", depositReceived };
1207
1222
  }
1208
1223
  }
1224
+ function omnistonPins(trustedEvmOrderProtocols) {
1225
+ const byChain = new Map(Object.entries(trustedEvmOrderProtocols).map(([chain, list]) => [chain, list.map((a) => (0, import_viem.getAddress)(a))]));
1226
+ return Object.freeze({ callTargets: () => [], spenders: (chain) => byChain.get(chain) ?? [] });
1227
+ }
1228
+ function omnistonVerifyBuild(hooks) {
1229
+ return async (quote, tx, params) => {
1230
+ if (tx.bodyBoc === void 0) return null;
1231
+ if (!params) return "the request this quote answered is unknown";
1232
+ if (quote.providerData?.settlement !== "swap") return "only TON swap bodies can be checked on the client";
1233
+ let steps;
1234
+ try {
1235
+ steps = JSON.parse(quote.providerData.routeSteps ?? "");
1236
+ } catch {
1237
+ return "the quote carries no route steps";
1238
+ }
1239
+ if (!Array.isArray(steps) || steps.length === 0) return "the quote carries no route steps";
1240
+ const source = (0, import_swap2_assets.ccAsset)(params.sourceChain, params.sourceToken);
1241
+ const dest = (0, import_swap2_assets.ccAssetByToken)(params.destChain, params.destToken);
1242
+ if (!source || !dest) return "unknown source or destination token";
1243
+ const protocols = (quote.providerData.protocols ?? "").split(",").filter(Boolean);
1244
+ let checked;
1245
+ try {
1246
+ checked = checkTonSwapPayload({
1247
+ payloadHex: import_buffer2.Buffer.from(tx.bodyBoc, "base64").toString("hex"),
1248
+ targetAddress: tx.to,
1249
+ sendAmount: BigInt(tx.value),
1250
+ trader: params.refundAddress ?? "",
1251
+ dest: params.destAddress,
1252
+ inputUnits: quote.sourceAmount,
1253
+ minOutputAmount: quote.minDestAmount,
1254
+ inputJetton: source.contract ?? null,
1255
+ outputJetton: dest.contract ?? null,
1256
+ steps: steps.map((st) => ({ protocol: String(st.protocol), inputJetton: st.inputJetton, outputJetton: st.outputJetton })),
1257
+ integratorAddress: hooks.integratorAddress ?? null
1258
+ });
1259
+ } catch (e) {
1260
+ return e instanceof Error ? e.message.replace(/^omniston: /, "") : String(e);
1261
+ }
1262
+ const payload = parseSwapPayload(import_buffer2.Buffer.from(tx.bodyBoc, "base64").toString("hex"));
1263
+ const target = source.contract ? payload.jettonTransfer?.destination : tx.to;
1264
+ if (!target) return "the body names no swap destination";
1265
+ try {
1266
+ if (!await hooks.validateTonSwapTarget({ address: target, protocols })) return `${target} is not a recognized DEX contract for ${protocols.join("+")}`;
1267
+ } catch {
1268
+ return "the swap target could not be checked";
1269
+ }
1270
+ if (hooks.validateTonSwapLeg) {
1271
+ for (const leg of checked.legs) {
1272
+ let ok = false;
1273
+ try {
1274
+ ok = await hooks.validateTonSwapLeg(leg);
1275
+ } catch {
1276
+ ok = false;
1277
+ }
1278
+ if (!ok) return `${leg.kind} v${leg.version} leg not verified`;
1279
+ }
1280
+ }
1281
+ return null;
1282
+ };
1283
+ }
1209
1284
  function omnistonProvider(opts = {}) {
1210
1285
  opts = Object.freeze({ ...opts });
1211
1286
  const apiUrl = opts.apiUrl ?? "wss://omni-ws.ston.fi";
@@ -1323,6 +1398,26 @@ function omnistonProvider(opts = {}) {
1323
1398
  async function saveBuilt(quoteId, entry) {
1324
1399
  await quotes.save(quoteId, JSON.stringify(entry));
1325
1400
  }
1401
+ async function claimBuilt(quoteId, entry) {
1402
+ const json = JSON.stringify(entry);
1403
+ if (typeof quotes.claim === "function") {
1404
+ if (await quotes.claim(quoteId, json)) return true;
1405
+ } else if (!await quotes.load(quoteId)) {
1406
+ await quotes.save(quoteId, json);
1407
+ return true;
1408
+ }
1409
+ const existing = await loadBuilt(quoteId);
1410
+ if (!existing) {
1411
+ if (typeof quotes.claim === "function") return await quotes.claim(quoteId, json);
1412
+ await quotes.save(quoteId, json);
1413
+ return true;
1414
+ }
1415
+ const a = existing.bound, b = entry.bound;
1416
+ const same = !!a && !!b && a.kind === b.kind && a.srcChain === b.srcChain && sameIntentAddress(a.trader, b.trader) && sameIntentAddress(a.dest, b.dest);
1417
+ if (!same) return false;
1418
+ await quotes.save(quoteId, json);
1419
+ return true;
1420
+ }
1326
1421
  async function loadBuilt(quoteId) {
1327
1422
  const raw2 = await quotes.load(quoteId);
1328
1423
  if (!raw2) return void 0;
@@ -1489,7 +1584,10 @@ function omnistonProvider(opts = {}) {
1489
1584
  if (BigInt(swap.minOutputAmount) < requiredMin(q.outputUnits)) return null;
1490
1585
  if (q.gasBudget !== void 0 && BigInt(q.gasBudget) > BigInt(maxTonExecutionNano)) return null;
1491
1586
  const minOut = swap.minOutputAmount;
1492
- await saveBuilt(q.quoteId, { quote: q, at: Date.now(), bound: { trader, dest: params.destAddress, srcChain: params.sourceChain, kind: "swap" } });
1587
+ if (!await claimBuilt(q.quoteId, { quote: q, at: Date.now(), bound: { trader, dest: params.destAddress, srcChain: params.sourceChain, kind: "swap" } })) {
1588
+ traceFor(q.quoteId)("quote-id-reused", { kind: "swap" });
1589
+ return null;
1590
+ }
1493
1591
  const feeUnits = Number(q.protocolFeeUnits || "0") + Number(q.integratorFeeUnits || "0");
1494
1592
  const stableOut = /^(USDT|USDC|USD₮|jUSDT)$/.test(dest.symbol);
1495
1593
  const feeUsd = stableOut ? feeUnits / 10 ** dest.decimals : 0;
@@ -1516,6 +1614,8 @@ function omnistonProvider(opts = {}) {
1516
1614
  providerData: {
1517
1615
  settlement: "swap",
1518
1616
  protocols: swapProtocols(swap).join(","),
1617
+ // For the app's own check of a remote build (omnistonVerifyBuild): the route the body must follow.
1618
+ routeSteps: JSON.stringify(swapSteps(swap)),
1519
1619
  ...q.gasBudget ? { gasBudget: q.gasBudget } : {},
1520
1620
  ...q.estimatedGasConsumption ? { gasEstimate: q.estimatedGasConsumption } : {}
1521
1621
  }
@@ -1616,10 +1716,7 @@ function omnistonProvider(opts = {}) {
1616
1716
  minOutputAmount: swap.minOutputAmount,
1617
1717
  inputJetton,
1618
1718
  outputJetton: assetContract(q.outputAsset),
1619
- steps: swap.routes.length === 1 ? routeSteps.map((step) => {
1620
- if (step.chunks.length !== 1) return { protocol: `split(${step.chunks.map((c) => c.protocol).join("|")})`, inputJetton: void 0, outputJetton: void 0 };
1621
- return { protocol: step.chunks[0].protocol, inputJetton: stepJetton(step.inputAsset), outputJetton: stepJetton(step.outputAsset) };
1622
- }) : [{ protocol: `${swap.routes.length}-routes`, inputJetton: void 0, outputJetton: void 0 }],
1719
+ steps: swapSteps(swap),
1623
1720
  integratorAddress: q.integratorAddress?.chain.$case === "ton" ? q.integratorAddress.chain.value : null
1624
1721
  });
1625
1722
  } catch (error) {
@@ -1907,7 +2004,9 @@ function omnistonProvider(opts = {}) {
1907
2004
  const outputScaled = BigInt(q.outputUnits) * 10n ** BigInt(source.decimals);
1908
2005
  if (outputScaled * 2n < inputScaled || outputScaled > inputScaled * 2n) return refuse2("quoted output is outside 0.5x\u20132x of the input");
1909
2006
  if (await quoteIdInUse(q.quoteId)) return refuse2("the platform reused a quote id that already belongs to an order");
1910
- await saveBuilt(q.quoteId, { quote: q, at: Date.now(), bound: { trader, dest: params.destAddress, srcChain: params.sourceChain, kind: "order" } });
2007
+ if (!await claimBuilt(q.quoteId, { quote: q, at: Date.now(), bound: { trader, dest: params.destAddress, srcChain: params.sourceChain, kind: "order" } })) {
2008
+ return refuse2("the platform reused a quote id that already holds another user's quote");
2009
+ }
1911
2010
  const feeUnits = Number(q.protocolFeeUnits || "0") + Number(q.integratorFeeUnits || "0");
1912
2011
  const feeUsd = feeUnits / 10 ** dest.decimals;
1913
2012
  return {
@@ -2039,6 +2138,7 @@ function omnistonProvider(opts = {}) {
2039
2138
  await forget();
2040
2139
  throw error;
2041
2140
  }
2141
+ await saveBuilt(ref.quoteId, { quote: q, at: Date.now(), ...cached.bound ? { bound: cached.bound } : {} });
2042
2142
  return {
2043
2143
  chain: srcChain,
2044
2144
  to: message.targetAddress,
@@ -2082,8 +2182,15 @@ function omnistonProvider(opts = {}) {
2082
2182
  })
2083
2183
  );
2084
2184
  if (!order) return { state: "pending" };
2085
- const secretsHex = await store.load(decoded.quoteId);
2185
+ let secretsHex = await store.load(decoded.quoteId);
2086
2186
  if (!secretsHex || cached?.quote.settlementData.$case !== "order") trace("disclosure-skipped", { gate: "context", secrets: !!secretsHex, cached: !!cached, feedStatus: order.status });
2187
+ if (secretsHex && cached?.quote.settlementData.$case === "order") {
2188
+ const [lockedTo] = await store.load(bindKey(decoded.quoteId)) ?? [];
2189
+ if (lockedTo !== orderBinding(bound, cached.quote)) {
2190
+ trace("disclosure-skipped", { gate: "binding", bound: lockedTo !== void 0, feedStatus: order.status });
2191
+ secretsHex = void 0;
2192
+ }
2193
+ }
2087
2194
  if (secretsHex && cached?.quote.settlementData.$case === "order") {
2088
2195
  const q = cached.quote;
2089
2196
  const settlement = q.settlementData.value;