@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/dist/protocol.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as _anywalletkit_protocol_swap2_core_types from '@anywalletkit/protocol-swap2-core/types';
|
|
2
2
|
import { SingleProviderSwap2 } from '@anywalletkit/protocol-swap2-core/swap2/single';
|
|
3
3
|
import { Swap2EngineConfig, Swap2Account } from '@anywalletkit/protocol-swap2-core/swap2/base';
|
|
4
|
-
import { b as OmnistonProviderOptions } from './provider-
|
|
4
|
+
import { b as OmnistonProviderOptions } from './provider-CcCZB-Cj.cjs';
|
|
5
5
|
|
|
6
6
|
interface OmnistonSwap2Config extends Swap2EngineConfig, OmnistonProviderOptions {
|
|
7
7
|
}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as _anywalletkit_protocol_swap2_core_types from '@anywalletkit/protocol-swap2-core/types';
|
|
2
2
|
import { SingleProviderSwap2 } from '@anywalletkit/protocol-swap2-core/swap2/single';
|
|
3
3
|
import { Swap2EngineConfig, Swap2Account } from '@anywalletkit/protocol-swap2-core/swap2/base';
|
|
4
|
-
import { b as OmnistonProviderOptions } from './provider-
|
|
4
|
+
import { b as OmnistonProviderOptions } from './provider-CcCZB-Cj.js';
|
|
5
5
|
|
|
6
6
|
interface OmnistonSwap2Config extends Swap2EngineConfig, OmnistonProviderOptions {
|
|
7
7
|
}
|
package/dist/protocol.js
CHANGED
|
@@ -530,10 +530,12 @@ interface OmnistonProviderOptions {
|
|
|
530
530
|
* swap body depends on — that a STON.fi hop's ask wallet is its router's
|
|
531
531
|
* wallet of the quoted output (and, for native input, that the pTON wallet
|
|
532
532
|
* the message pays belongs to that router), or that a DeDust pool trades the
|
|
533
|
-
* quoted pair. `createOmnistonTonValidators` supplies it.
|
|
534
|
-
*
|
|
535
|
-
*
|
|
536
|
-
*
|
|
533
|
+
* quoted pair. `createOmnistonTonValidators` supplies it. Required, with
|
|
534
|
+
* `validateTonSwapTarget`, to advertise same-chain TON swaps (scan
|
|
535
|
+
* 2026-10-30 F8: without it a genuine DEX body can pay out a token other
|
|
536
|
+
* than the quoted one). Only `tonSwapPayloadCheck: "report"` lets a host run
|
|
537
|
+
* without it — each unverified leg is then traced as `ton-swap-payload
|
|
538
|
+
* legs-unverified`; STON.fi v1 native input (pTON v1) is refused regardless.
|
|
537
539
|
*/
|
|
538
540
|
validateTonSwapLeg?: (leg: TonSwapLeg) => Promise<boolean> | boolean;
|
|
539
541
|
/**
|
|
@@ -761,6 +763,7 @@ declare function omnistonVerifyBuild(hooks: {
|
|
|
761
763
|
address: string;
|
|
762
764
|
protocols: readonly string[];
|
|
763
765
|
}) => Promise<boolean> | boolean;
|
|
766
|
+
/** The DEX pays out the quoted token (`createOmnistonTonValidators().validateTonSwapLeg`). Without it a TON swap body is refused. */
|
|
764
767
|
validateTonSwapLeg?: (leg: TonSwapLeg) => Promise<boolean> | boolean;
|
|
765
768
|
/**
|
|
766
769
|
* The app's check of a TON → EVM escrow body (`createOmnistonTonValidators().verifyTonEscrowBuild`). Without it a
|
|
@@ -530,10 +530,12 @@ interface OmnistonProviderOptions {
|
|
|
530
530
|
* swap body depends on — that a STON.fi hop's ask wallet is its router's
|
|
531
531
|
* wallet of the quoted output (and, for native input, that the pTON wallet
|
|
532
532
|
* the message pays belongs to that router), or that a DeDust pool trades the
|
|
533
|
-
* quoted pair. `createOmnistonTonValidators` supplies it.
|
|
534
|
-
*
|
|
535
|
-
*
|
|
536
|
-
*
|
|
533
|
+
* quoted pair. `createOmnistonTonValidators` supplies it. Required, with
|
|
534
|
+
* `validateTonSwapTarget`, to advertise same-chain TON swaps (scan
|
|
535
|
+
* 2026-10-30 F8: without it a genuine DEX body can pay out a token other
|
|
536
|
+
* than the quoted one). Only `tonSwapPayloadCheck: "report"` lets a host run
|
|
537
|
+
* without it — each unverified leg is then traced as `ton-swap-payload
|
|
538
|
+
* legs-unverified`; STON.fi v1 native input (pTON v1) is refused regardless.
|
|
537
539
|
*/
|
|
538
540
|
validateTonSwapLeg?: (leg: TonSwapLeg) => Promise<boolean> | boolean;
|
|
539
541
|
/**
|
|
@@ -761,6 +763,7 @@ declare function omnistonVerifyBuild(hooks: {
|
|
|
761
763
|
address: string;
|
|
762
764
|
protocols: readonly string[];
|
|
763
765
|
}) => Promise<boolean> | boolean;
|
|
766
|
+
/** The DEX pays out the quoted token (`createOmnistonTonValidators().validateTonSwapLeg`). Without it a TON swap body is refused. */
|
|
764
767
|
validateTonSwapLeg?: (leg: TonSwapLeg) => Promise<boolean> | boolean;
|
|
765
768
|
/**
|
|
766
769
|
* The app's check of a TON → EVM escrow body (`createOmnistonTonValidators().verifyTonEscrowBuild`). Without it a
|
package/dist/provider.cjs
CHANGED
|
@@ -539,18 +539,36 @@ function toChainAddress(caip2, address) {
|
|
|
539
539
|
if (!$case) return null;
|
|
540
540
|
return { chain: { $case, value: (0, import_viem.getAddress)(address) } };
|
|
541
541
|
}
|
|
542
|
-
|
|
543
|
-
|
|
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("|");
|
|
544
558
|
}
|
|
545
559
|
var QUOTE_ID = /^[0-9a-fA-F]{48,64}$/;
|
|
546
560
|
function decodeIntent(intentId) {
|
|
547
561
|
try {
|
|
548
562
|
const parts = intentId.split("|");
|
|
549
563
|
if (parts.length !== 5 && parts.length !== 6 || parts[0] !== INTENT_PREFIX) return null;
|
|
550
|
-
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;
|
|
551
569
|
if (!QUOTE_ID.test(quoteId) || !trader || !dest || !srcChain) return null;
|
|
552
570
|
if (parts.length === 6 && kindPart !== "swap") return null;
|
|
553
|
-
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 } : {} };
|
|
554
572
|
} catch {
|
|
555
573
|
return null;
|
|
556
574
|
}
|
|
@@ -1426,16 +1444,15 @@ function omnistonVerifyBuild(hooks) {
|
|
|
1426
1444
|
} catch {
|
|
1427
1445
|
return "the swap target could not be checked";
|
|
1428
1446
|
}
|
|
1429
|
-
if (hooks.validateTonSwapLeg)
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
}
|
|
1437
|
-
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;
|
|
1438
1454
|
}
|
|
1455
|
+
if (!ok) return `${leg.kind} v${leg.version} leg not verified`;
|
|
1439
1456
|
}
|
|
1440
1457
|
return null;
|
|
1441
1458
|
};
|
|
@@ -1572,7 +1589,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1572
1589
|
return true;
|
|
1573
1590
|
}
|
|
1574
1591
|
const a = existing.bound, b = entry.bound;
|
|
1575
|
-
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);
|
|
1576
1593
|
if (!same) return false;
|
|
1577
1594
|
await quotes.save(quoteId, json);
|
|
1578
1595
|
return true;
|
|
@@ -1625,10 +1642,19 @@ function omnistonProvider(opts = {}) {
|
|
|
1625
1642
|
throw new Error(`omniston: intent id does not match the quote it was issued for (${bound.field})`);
|
|
1626
1643
|
}
|
|
1627
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
|
+
}
|
|
1628
1653
|
return bound.ref;
|
|
1629
1654
|
}
|
|
1630
1655
|
const bindKey = (quoteId) => `${quoteId}#bind`;
|
|
1631
1656
|
const disclosedKey = (quoteId) => `${quoteId}#disclosed`;
|
|
1657
|
+
const disclosedIndicesKey = (quoteId) => `${quoteId}#disclosed-indices`;
|
|
1632
1658
|
function orderBinding(ref, q) {
|
|
1633
1659
|
return (0, import_viem.sha256)(new TextEncoder().encode(JSON.stringify([
|
|
1634
1660
|
ref.quoteId,
|
|
@@ -1645,6 +1671,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1645
1671
|
await store.clear(quoteId);
|
|
1646
1672
|
await store.clear(bindKey(quoteId));
|
|
1647
1673
|
await store.clear(disclosedKey(quoteId));
|
|
1674
|
+
await store.clear(disclosedIndicesKey(quoteId));
|
|
1648
1675
|
}
|
|
1649
1676
|
async function secretsFor(ref, q, count) {
|
|
1650
1677
|
const quoteId = ref.quoteId;
|
|
@@ -1673,7 +1700,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1673
1700
|
return !!existing && existing.length > 0;
|
|
1674
1701
|
}
|
|
1675
1702
|
const SWAP_SLIPPAGE_PIPS_DEFAULT = 5e3;
|
|
1676
|
-
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");
|
|
1677
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";
|
|
1678
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";
|
|
1679
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";
|
|
@@ -1752,7 +1779,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1752
1779
|
const feeUsd = stableOut ? feeUnits / 10 ** dest.decimals : 0;
|
|
1753
1780
|
return {
|
|
1754
1781
|
provider: "omniston",
|
|
1755
|
-
intentId: encodeIntent(q.quoteId, trader, params.destAddress, params.sourceChain, "swap"),
|
|
1782
|
+
intentId: encodeIntent(q.quoteId, trader, params.destAddress, params.sourceChain, "swap", quoteDigest(q)),
|
|
1756
1783
|
depositAddress: "",
|
|
1757
1784
|
// swap settlement — the built message pays the DEX directly
|
|
1758
1785
|
depositMemo: null,
|
|
@@ -1785,7 +1812,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1785
1812
|
if (!isTon(ref.srcChain) || q.inputAsset.chain.$case !== "ton" || q.outputAsset.chain.$case !== "ton") {
|
|
1786
1813
|
throw new Error("omniston: swap quote is not a TON same-chain swap");
|
|
1787
1814
|
}
|
|
1788
|
-
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");
|
|
1789
1816
|
const owner = { chain: { $case: "ton", value: ref.trader } };
|
|
1790
1817
|
const receiver = { chain: { $case: "ton", value: ref.dest } };
|
|
1791
1818
|
const omni = await getClient();
|
|
@@ -1884,7 +1911,7 @@ function omnistonProvider(opts = {}) {
|
|
|
1884
1911
|
}
|
|
1885
1912
|
if (checked) {
|
|
1886
1913
|
if (typeof opts.validateTonSwapLeg !== "function") {
|
|
1887
|
-
|
|
1914
|
+
payloadRefusal("legs-unverified", `no validateTonSwapLeg hook for ${checked.legs.length} leg(s)`);
|
|
1888
1915
|
} else {
|
|
1889
1916
|
for (const leg of checked.legs) {
|
|
1890
1917
|
let ok = false;
|
|
@@ -2164,13 +2191,13 @@ function omnistonProvider(opts = {}) {
|
|
|
2164
2191
|
if (outputScaled * 2n < inputScaled || outputScaled > inputScaled * 2n) return refuse2("quoted output is outside 0.5x\u20132x of the input");
|
|
2165
2192
|
if (await quoteIdInUse(q.quoteId)) return refuse2("the platform reused a quote id that already belongs to an order");
|
|
2166
2193
|
if (!await claimBuilt(q.quoteId, { quote: q, at: Date.now(), bound: { trader, dest: params.destAddress, srcChain: params.sourceChain, kind: "order" } })) {
|
|
2167
|
-
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");
|
|
2168
2195
|
}
|
|
2169
2196
|
const feeUnits = Number(q.protocolFeeUnits || "0") + Number(q.integratorFeeUnits || "0");
|
|
2170
2197
|
const feeUsd = feeUnits / 10 ** dest.decimals;
|
|
2171
2198
|
return {
|
|
2172
2199
|
provider: "omniston",
|
|
2173
|
-
intentId: encodeIntent(q.quoteId, trader, params.destAddress, params.sourceChain),
|
|
2200
|
+
intentId: encodeIntent(q.quoteId, trader, params.destAddress, params.sourceChain, "order", quoteDigest(q)),
|
|
2174
2201
|
depositAddress: "",
|
|
2175
2202
|
// order settlement — no static deposit address
|
|
2176
2203
|
depositMemo: null,
|
|
@@ -2342,6 +2369,7 @@ function omnistonProvider(opts = {}) {
|
|
|
2342
2369
|
);
|
|
2343
2370
|
if (!order) return { state: "pending" };
|
|
2344
2371
|
let secretsHex = await store.load(decoded.quoteId);
|
|
2372
|
+
const storedSecrets = secretsHex?.length ?? 0;
|
|
2345
2373
|
if (!secretsHex || cached?.quote.settlementData.$case !== "order") trace("disclosure-skipped", { gate: "context", secrets: !!secretsHex, cached: !!cached, feedStatus: order.status });
|
|
2346
2374
|
if (secretsHex && cached?.quote.settlementData.$case === "order") {
|
|
2347
2375
|
const [lockedTo] = await store.load(bindKey(decoded.quoteId)) ?? [];
|
|
@@ -2350,6 +2378,7 @@ function omnistonProvider(opts = {}) {
|
|
|
2350
2378
|
secretsHex = void 0;
|
|
2351
2379
|
}
|
|
2352
2380
|
}
|
|
2381
|
+
const disclosedNow = /* @__PURE__ */ new Set();
|
|
2353
2382
|
if (secretsHex && cached?.quote.settlementData.$case === "order") {
|
|
2354
2383
|
const q = cached.quote;
|
|
2355
2384
|
const settlement = q.settlementData.value;
|
|
@@ -2448,11 +2477,15 @@ function omnistonProvider(opts = {}) {
|
|
|
2448
2477
|
}
|
|
2449
2478
|
await omni.orderDiscloseHtlcSecret({ quoteId: decoded.quoteId, executionIndex: i, secret: secretBytes }).then(async () => {
|
|
2450
2479
|
trace("secret-disclosed", { index: i });
|
|
2480
|
+
disclosedNow.add(i);
|
|
2451
2481
|
await store.save(disclosedKey(decoded.quoteId), ["1"]);
|
|
2452
2482
|
}, (error) => trace("disclose-failed", { index: i, error: error instanceof Error ? error.message : String(error) }));
|
|
2453
2483
|
})
|
|
2454
2484
|
);
|
|
2455
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));
|
|
2456
2489
|
const status = mapOrderStatus(order);
|
|
2457
2490
|
const delivered = order.executions[0]?.outputPositionAddress;
|
|
2458
2491
|
const outCase = cached?.quote.outputAsset.chain.$case;
|
|
@@ -2477,6 +2510,14 @@ function omnistonProvider(opts = {}) {
|
|
|
2477
2510
|
}).catch(() => null);
|
|
2478
2511
|
if (hash) status.destTxHash = hash;
|
|
2479
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
|
+
}
|
|
2480
2521
|
if (status.state === "completed") {
|
|
2481
2522
|
await clearSecrets(decoded.quoteId);
|
|
2482
2523
|
await quotes.clear(decoded.quoteId);
|