@anywalletkit/protocol-swap2-core 0.8.0 → 0.10.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 +2 -2
- package/dist/{chunk-FHEU7E5A.js → chunk-AY7DVYAN.js} +3 -3
- package/dist/{chunk-5FPDKWBG.js → chunk-F5TRF52T.js} +12 -3
- package/dist/chunk-F5TRF52T.js.map +1 -0
- package/dist/{chunk-6MTZNYKM.js → chunk-KB2GX6R2.js} +2 -1
- package/dist/{chunk-6MTZNYKM.js.map → chunk-KB2GX6R2.js.map} +1 -1
- package/dist/{chunk-CRCDWV3L.js → chunk-PN76KVPT.js} +59 -12
- package/dist/chunk-PN76KVPT.js.map +1 -0
- package/dist/index.cjs +67 -13
- 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 +8 -4
- package/dist/remote.cjs +1 -0
- package/dist/remote.cjs.map +1 -1
- package/dist/remote.js +1 -1
- package/dist/server.cjs +13 -3
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +8 -2
- package/dist/server.js.map +1 -1
- package/dist/swap2/base.cjs +58 -11
- package/dist/swap2/base.cjs.map +1 -1
- package/dist/swap2/base.d.cts +18 -0
- package/dist/swap2/base.d.ts +18 -0
- package/dist/swap2/base.js +2 -1
- package/dist/swap2/single.cjs +119 -68
- package/dist/swap2/single.cjs.map +1 -1
- package/dist/swap2/single.js +3 -3
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +18 -0
- package/dist/types.d.ts +18 -0
- package/dist/validate.cjs +11 -2
- package/dist/validate.cjs.map +1 -1
- package/dist/validate.d.cts +13 -3
- package/dist/validate.d.ts +13 -3
- package/dist/validate.js +5 -1
- package/package.json +6 -6
- package/dist/chunk-5FPDKWBG.js.map +0 -1
- package/dist/chunk-CRCDWV3L.js.map +0 -1
- /package/dist/{chunk-FHEU7E5A.js.map → chunk-AY7DVYAN.js.map} +0 -0
package/dist/swap2/base.d.cts
CHANGED
|
@@ -190,6 +190,24 @@ interface SerializedExecutionExpectation {
|
|
|
190
190
|
token: string;
|
|
191
191
|
maxAmount: string;
|
|
192
192
|
};
|
|
193
|
+
/**
|
|
194
|
+
* Bridges: the destination chain (CAIP-2) and the reviewed recipient there. Value leaving the account may only go into
|
|
195
|
+
* the bridge (plus the provider's declared fee), and a bridge instruction whose recipient the account can read must
|
|
196
|
+
* name this one; an account that cannot prove where the funds go refuses the transaction. The engine also names the
|
|
197
|
+
* reviewed destination token (zero address = native), the quoted minimum in its base units with its decimals (the
|
|
198
|
+
* catalog's, else the caller's verified `toTokenDecimals`; never the quote's alone), and the slippage the plan allows, so a bridge
|
|
199
|
+
* order's own token and minimum can be held to them.
|
|
200
|
+
* Set on every cross-chain serialized leg. A Solana account maps the whole expectation with wallet-solana's
|
|
201
|
+
* `serializedGuardFromExecution`; a hand mapping that drops `bridgeOut` leaves the leg checked against the SOL ceiling only.
|
|
202
|
+
*/
|
|
203
|
+
bridgeOut?: {
|
|
204
|
+
destChain: string;
|
|
205
|
+
recipient: string;
|
|
206
|
+
destToken: string;
|
|
207
|
+
minAmountOut: string;
|
|
208
|
+
destDecimals?: number;
|
|
209
|
+
slippageBps?: number;
|
|
210
|
+
};
|
|
193
211
|
}
|
|
194
212
|
/**
|
|
195
213
|
* A bound wallet account, accepted opaquely: the write surface we need is
|
package/dist/swap2/base.d.ts
CHANGED
|
@@ -190,6 +190,24 @@ interface SerializedExecutionExpectation {
|
|
|
190
190
|
token: string;
|
|
191
191
|
maxAmount: string;
|
|
192
192
|
};
|
|
193
|
+
/**
|
|
194
|
+
* Bridges: the destination chain (CAIP-2) and the reviewed recipient there. Value leaving the account may only go into
|
|
195
|
+
* the bridge (plus the provider's declared fee), and a bridge instruction whose recipient the account can read must
|
|
196
|
+
* name this one; an account that cannot prove where the funds go refuses the transaction. The engine also names the
|
|
197
|
+
* reviewed destination token (zero address = native), the quoted minimum in its base units with its decimals (the
|
|
198
|
+
* catalog's, else the caller's verified `toTokenDecimals`; never the quote's alone), and the slippage the plan allows, so a bridge
|
|
199
|
+
* order's own token and minimum can be held to them.
|
|
200
|
+
* Set on every cross-chain serialized leg. A Solana account maps the whole expectation with wallet-solana's
|
|
201
|
+
* `serializedGuardFromExecution`; a hand mapping that drops `bridgeOut` leaves the leg checked against the SOL ceiling only.
|
|
202
|
+
*/
|
|
203
|
+
bridgeOut?: {
|
|
204
|
+
destChain: string;
|
|
205
|
+
recipient: string;
|
|
206
|
+
destToken: string;
|
|
207
|
+
minAmountOut: string;
|
|
208
|
+
destDecimals?: number;
|
|
209
|
+
slippageBps?: number;
|
|
210
|
+
};
|
|
193
211
|
}
|
|
194
212
|
/**
|
|
195
213
|
* A bound wallet account, accepted opaquely: the write surface we need is
|
package/dist/swap2/base.js
CHANGED
|
@@ -7,8 +7,9 @@ import {
|
|
|
7
7
|
nativeUnits,
|
|
8
8
|
normalizeTypedDomain,
|
|
9
9
|
swap2ChainId
|
|
10
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-PN76KVPT.js";
|
|
11
11
|
import "../chunk-IVR3SREQ.js";
|
|
12
|
+
import "../chunk-F5TRF52T.js";
|
|
12
13
|
import "../chunk-2KC734UX.js";
|
|
13
14
|
export {
|
|
14
15
|
BaseSwap2Protocol,
|
package/dist/swap2/single.cjs
CHANGED
|
@@ -81,6 +81,70 @@ function tonRawAddress(address) {
|
|
|
81
81
|
return `${wc}:${Array.from(bytes.subarray(2, 34), (b) => b.toString(16).padStart(2, "0")).join("")}`;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
// src/validate.ts
|
|
85
|
+
var SLIPPAGE_ROUNDING_BPS = 1;
|
|
86
|
+
function slippageFloor(dest, slippageBps) {
|
|
87
|
+
const keep = 1e4 - slippageBps - SLIPPAGE_ROUNDING_BPS;
|
|
88
|
+
return keep <= 0 ? 0n : dest * BigInt(keep) / 10000n;
|
|
89
|
+
}
|
|
90
|
+
function validQuote(q, params, provider) {
|
|
91
|
+
try {
|
|
92
|
+
if (q.provider !== provider || q.sourceAmount !== params.amountIn) return false;
|
|
93
|
+
if (typeof q.intentId !== "string" || !q.intentId || typeof q.depositAddress !== "string" || q.depositMemo !== null && typeof q.depositMemo !== "string") return false;
|
|
94
|
+
if (![q.sourceAmount, q.destAmount, q.minDestAmount].every((value) => /^[0-9]+$/.test(value))) return false;
|
|
95
|
+
const source = BigInt(q.sourceAmount);
|
|
96
|
+
const dest = BigInt(q.destAmount);
|
|
97
|
+
const min = BigInt(q.minDestAmount);
|
|
98
|
+
if (source <= 0n || dest <= 0n || min <= 0n || min > dest) return false;
|
|
99
|
+
if (params.minAmountOut !== void 0) {
|
|
100
|
+
if (!/^[0-9]+$/.test(params.minAmountOut) || BigInt(params.minAmountOut) <= 0n || min < BigInt(params.minAmountOut)) return false;
|
|
101
|
+
}
|
|
102
|
+
if (params.slippageBps !== void 0) {
|
|
103
|
+
if (!Number.isInteger(params.slippageBps) || params.slippageBps < 0 || params.slippageBps >= 1e4) return false;
|
|
104
|
+
if (min < slippageFloor(dest, params.slippageBps)) return false;
|
|
105
|
+
}
|
|
106
|
+
if (!Number.isFinite(q.etaSeconds) || q.etaSeconds < 0) return false;
|
|
107
|
+
if (!Number.isFinite(q.reliability) || q.reliability < 0 || q.reliability > 1) return false;
|
|
108
|
+
if (!Number.isFinite(q.feeUsd) || q.feeUsd < 0) return false;
|
|
109
|
+
if (q.fees && (!Array.isArray(q.fees) || !q.fees.every(
|
|
110
|
+
(fee) => ["network", "protocol", "affiliate", "other"].includes(fee.type) && /^[0-9]+$/.test(fee.amount) && typeof fee.token === "string" && fee.token.length > 0 && typeof fee.included === "boolean" && (fee.description === void 0 || typeof fee.description === "string")
|
|
111
|
+
))) return false;
|
|
112
|
+
if (q.providerData && (Array.isArray(q.providerData) || typeof q.providerData !== "object" || !Object.values(q.providerData).every((value) => typeof value === "string"))) return false;
|
|
113
|
+
const tx = q.txRequest;
|
|
114
|
+
if (tx) {
|
|
115
|
+
if (tx.value !== void 0 && !/^[0-9]+$/.test(tx.value)) return false;
|
|
116
|
+
if (tx.gasLimit !== void 0 && !/^[0-9]+$/.test(tx.gasLimit)) return false;
|
|
117
|
+
if (tx.chainId !== void 0 && (!Number.isSafeInteger(tx.chainId) || tx.chainId <= 0)) return false;
|
|
118
|
+
if ([tx.to, tx.data, tx.approvalAddress, tx.serializedTx].some((value) => value !== void 0 && typeof value !== "string")) return false;
|
|
119
|
+
}
|
|
120
|
+
if (!Number.isFinite(q.validUntilMs) || q.validUntilMs <= Date.now()) return false;
|
|
121
|
+
return true;
|
|
122
|
+
} catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function remotePinRefusal(provider, q, chain) {
|
|
127
|
+
if (provider.remote !== true) return null;
|
|
128
|
+
const tx = q.txRequest;
|
|
129
|
+
const plain = tx ? tx.data === void 0 && tx.serializedTx === void 0 : typeof q.depositAddress === "string" && q.depositAddress !== "";
|
|
130
|
+
if (plain && provider.depositAddressRoutes !== true) {
|
|
131
|
+
return `${provider.name}: the route pays ${tx?.to || q.depositAddress}, an address the backend named, and the app did not allow deposit-address routes for this provider (remoteProvider depositAddressRoutes)`;
|
|
132
|
+
}
|
|
133
|
+
if (!chain.startsWith("eip155:")) return null;
|
|
134
|
+
if (!tx || tx.serializedTx !== void 0) return null;
|
|
135
|
+
const pinned = (list, address) => (list ?? []).some((a) => typeof a === "string" && a.toLowerCase() === address.toLowerCase());
|
|
136
|
+
if (tx.approvalAddress && !pinned(provider.spenders?.(chain), tx.approvalAddress)) {
|
|
137
|
+
return `${provider.name}: the route approves ${tx.approvalAddress}, which the app did not pin as a spender on ${chain} (remoteProvider spenders)`;
|
|
138
|
+
}
|
|
139
|
+
if (tx.data !== void 0 && tx.to && !pinned(provider.callTargets?.(chain), tx.to)) {
|
|
140
|
+
return `${provider.name}: the route calls ${tx.to}, which the app did not pin as a call target on ${chain} (remoteProvider callTargets)`;
|
|
141
|
+
}
|
|
142
|
+
if (tx.data !== void 0 && provider.verifiesQuotes !== true && provider.verifiesBuilds !== true) {
|
|
143
|
+
return `${provider.name}: the route's calldata is not checked by the app (remoteProvider verifyQuote / verifyBuild) \u2014 a pinned target alone does not say whom it pays`;
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
84
148
|
// src/errors.ts
|
|
85
149
|
var Swap2Error = class extends Error {
|
|
86
150
|
constructor(message, code, options) {
|
|
@@ -310,16 +374,18 @@ function reviewedIdentity(plan) {
|
|
|
310
374
|
return JSON.stringify(pinned, (_k, v) => typeof v === "bigint" ? v.toString() : v);
|
|
311
375
|
}
|
|
312
376
|
var SWAP2_ID_PREFIX = "awk1";
|
|
313
|
-
|
|
377
|
+
var CONFIRMED_SEGMENT = "ok";
|
|
378
|
+
function encodeId(provider, intentId, sourceTxHash, destTxHash, destTxConfirmed = false) {
|
|
314
379
|
const enc = (v) => encodeURIComponent(v).replace(/\./g, "%2E");
|
|
315
|
-
const
|
|
380
|
+
const dest = destTxHash != null ? [destTxHash, ...destTxConfirmed ? [CONFIRMED_SEGMENT] : []] : [];
|
|
381
|
+
const parts = [provider, intentId, ...sourceTxHash != null ? [sourceTxHash, ...dest] : []];
|
|
316
382
|
return `${SWAP2_ID_PREFIX}.${parts.map(enc).join(".")}`;
|
|
317
383
|
}
|
|
318
384
|
function decodeId(id) {
|
|
319
385
|
try {
|
|
320
386
|
if (!id.startsWith(`${SWAP2_ID_PREFIX}.`)) throw new Error("unsupported id version");
|
|
321
387
|
const parts = id.slice(SWAP2_ID_PREFIX.length + 1).split(".");
|
|
322
|
-
if (parts.length < 2 || parts.length >
|
|
388
|
+
if (parts.length < 2 || parts.length > 5 || !parts[0] || !parts[1]) throw new Error("invalid id shape");
|
|
323
389
|
const provider = decodeURIComponent(parts[0]);
|
|
324
390
|
const intentId = decodeURIComponent(parts[1]);
|
|
325
391
|
const sourceTxHash = parts[2] ? decodeURIComponent(parts[2]) : void 0;
|
|
@@ -327,7 +393,8 @@ function decodeId(id) {
|
|
|
327
393
|
if (!/^[A-Za-z0-9_-]+$/.test(provider) || !intentId || parts[2] !== void 0 && !sourceTxHash || parts[3] !== void 0 && !destTxHash) {
|
|
328
394
|
throw new Error("invalid id contents");
|
|
329
395
|
}
|
|
330
|
-
|
|
396
|
+
if (parts[4] !== void 0 && parts[4] !== CONFIRMED_SEGMENT) throw new Error("invalid id contents");
|
|
397
|
+
return { provider, intentId, ...sourceTxHash ? { sourceTxHash } : {}, ...destTxHash ? { destTxHash } : {}, ...parts[4] !== void 0 ? { destTxConfirmed: true } : {} };
|
|
331
398
|
} catch {
|
|
332
399
|
throw new Swap2ValidationError("swap2: malformed swap2 status id");
|
|
333
400
|
}
|
|
@@ -460,13 +527,30 @@ function assertQuoteConstraints(q, options) {
|
|
|
460
527
|
}
|
|
461
528
|
if (options.slippage !== void 0) {
|
|
462
529
|
const scale = 1000000000n;
|
|
463
|
-
const permitted = BigInt(floorScaled(options.slippage, 1e9));
|
|
464
|
-
const required = nominal * (scale - permitted) / scale;
|
|
530
|
+
const permitted = BigInt(floorScaled(options.slippage, 1e9)) + BigInt(SLIPPAGE_ROUNDING_BPS) * 100000n;
|
|
531
|
+
const required = permitted >= scale ? 0n : nominal * (scale - permitted) / scale;
|
|
465
532
|
if (guaranteed < required) {
|
|
466
533
|
throw new Error(`swap2: quote exceeds caller slippage (${options.slippage})`);
|
|
467
534
|
}
|
|
468
535
|
}
|
|
469
536
|
}
|
|
537
|
+
function bridgeOutOf(params, q, toTokenDecimals) {
|
|
538
|
+
let decimals = (0, import_swap2_assets2.resolveAsset)(params.destChain, params.destToken)?.decimals;
|
|
539
|
+
if (decimals === void 0 && toTokenDecimals !== void 0) {
|
|
540
|
+
if (q.destDecimals !== void 0 && q.destDecimals !== toTokenDecimals) {
|
|
541
|
+
throw new Swap2ValidationError(`swap2: the quote's destDecimals ${q.destDecimals} do not match toTokenDecimals ${toTokenDecimals}`);
|
|
542
|
+
}
|
|
543
|
+
decimals = toTokenDecimals;
|
|
544
|
+
}
|
|
545
|
+
return {
|
|
546
|
+
destChain: params.destChain,
|
|
547
|
+
recipient: params.destAddress,
|
|
548
|
+
destToken: params.destToken,
|
|
549
|
+
minAmountOut: q.minDestAmount,
|
|
550
|
+
...decimals !== void 0 ? { destDecimals: decimals } : {},
|
|
551
|
+
...params.slippageBps !== void 0 ? { slippageBps: params.slippageBps } : {}
|
|
552
|
+
};
|
|
553
|
+
}
|
|
470
554
|
function requireExecutionFloor(options) {
|
|
471
555
|
if (options.minAmountOut === void 0) {
|
|
472
556
|
throw new Error("swap2: swap2() requires caller-supplied minAmountOut");
|
|
@@ -565,6 +649,10 @@ var BaseSwap2Protocol = class extends import_core.Swap2Protocol {
|
|
|
565
649
|
}
|
|
566
650
|
const destChain = toCaip2(options.toChain, this.fromChain);
|
|
567
651
|
const constraints = callerConstraints(options);
|
|
652
|
+
const toDecimals = options.toTokenDecimals;
|
|
653
|
+
if (toDecimals !== void 0 && (!Number.isInteger(toDecimals) || toDecimals < 0 || toDecimals > 36)) {
|
|
654
|
+
throw new Swap2ValidationError("swap2: toTokenDecimals must be whole decimals in 0..36");
|
|
655
|
+
}
|
|
568
656
|
const amountIn = exactPositiveAmount(options.fromTokenAmount, "fromTokenAmount");
|
|
569
657
|
const source = this.sourceAsset(options);
|
|
570
658
|
const params = {
|
|
@@ -907,6 +995,8 @@ var BaseSwap2Protocol = class extends import_core.Swap2Protocol {
|
|
|
907
995
|
}
|
|
908
996
|
} else if (remote && !pinnedTargets.includes(unsigned.to.toLowerCase())) {
|
|
909
997
|
throw new Swap2ValidationError(`swap2: remote provider '${q.provider}' calls ${(0, import_viem.getAddress)(unsigned.to)} on ${this.fromChain}, which the app did not pin as a call target (remoteProvider callTargets) \u2014 refused`);
|
|
998
|
+
} else if (remote && provider.verifiesQuotes !== true && provider.verifiesBuilds !== true) {
|
|
999
|
+
throw new Swap2ValidationError(`swap2: remote provider '${q.provider}' calls ${(0, import_viem.getAddress)(unsigned.to)} with calldata the app does not check (remoteProvider verifyQuote / verifyBuild) \u2014 refused`);
|
|
910
1000
|
}
|
|
911
1001
|
}
|
|
912
1002
|
const needsApproval = !!unsigned.approvalAddress && !!source.contract;
|
|
@@ -1092,7 +1182,8 @@ var BaseSwap2Protocol = class extends import_core.Swap2Protocol {
|
|
|
1092
1182
|
maxValueOut: ((source.contract ? 0n : BigInt(q.sourceAmount)) + allowance).toString(),
|
|
1093
1183
|
// Only a same-chain swap lands in this account; a bridge delivers elsewhere.
|
|
1094
1184
|
...params.destChain === this.fromChain ? { tokenIn: { token: params.destToken, minAmount: q.minDestAmount } } : {},
|
|
1095
|
-
...source.contract ? { source: { token: source.contract, maxAmount: q.sourceAmount } } : {}
|
|
1185
|
+
...source.contract ? { source: { token: source.contract, maxAmount: q.sourceAmount } } : {},
|
|
1186
|
+
...params.destChain !== this.fromChain ? { bridgeOut: bridgeOutOf(params, q, executionOptions.toTokenDecimals) } : {}
|
|
1096
1187
|
};
|
|
1097
1188
|
sent = await signer.sendSerializedTransaction(unsigned.serializedTx, expect);
|
|
1098
1189
|
} else if (tokenId) {
|
|
@@ -1176,10 +1267,10 @@ var BaseSwap2Protocol = class extends import_core.Swap2Protocol {
|
|
|
1176
1267
|
}
|
|
1177
1268
|
/** Status of a swap by the id `swap2()` returned, asked from the provider that executed it. */
|
|
1178
1269
|
async getSwap2Status(id, _options) {
|
|
1179
|
-
const { provider: name, intentId, sourceTxHash, destTxHash } = decodeId(id);
|
|
1270
|
+
const { provider: name, intentId, sourceTxHash, destTxHash, destTxConfirmed } = decodeId(id);
|
|
1180
1271
|
const provider = this.providerFor(name);
|
|
1181
1272
|
if (!provider) throw new Error(`swap2: no swap2 found for id '${id}'`);
|
|
1182
|
-
return mapStatus(await provider.trackIntent({ intentId, ...sourceTxHash != null ? { sourceTxHash } : {}, ...destTxHash != null ? { destTxHash } : {} }));
|
|
1273
|
+
return mapStatus(await provider.trackIntent({ intentId, ...sourceTxHash != null ? { sourceTxHash } : {}, ...destTxHash != null ? { destTxHash, ...destTxConfirmed ? { destTxConfirmed } : {} } : {} }));
|
|
1183
1274
|
}
|
|
1184
1275
|
/**
|
|
1185
1276
|
* Two-phase routes (CCTP-style burn-and-mint): once `getSwap2Status` reports
|
|
@@ -1252,7 +1343,21 @@ var BaseSwap2Protocol = class extends import_core.Swap2Protocol {
|
|
|
1252
1343
|
});
|
|
1253
1344
|
if (!sent.hash) throw new Swap2ExecutionError("swap2: account did not return a claim transaction hash");
|
|
1254
1345
|
this.emitPhase("broadcast", { hash: sent.hash });
|
|
1255
|
-
|
|
1346
|
+
let confirmed = false;
|
|
1347
|
+
if (typeof signer.waitForTransaction === "function") {
|
|
1348
|
+
try {
|
|
1349
|
+
await signer.waitForTransaction(sent.hash);
|
|
1350
|
+
confirmed = true;
|
|
1351
|
+
} catch (e) {
|
|
1352
|
+
const err = e;
|
|
1353
|
+
const timedOut = /timeout|timed out/i.test(String(err?.name ?? "")) || /timed out|timeout/i.test(String(err?.message ?? ""));
|
|
1354
|
+
const reverted = err?.code === "reverted" || !timedOut && /\breverted\b/i.test(String(err?.message ?? ""));
|
|
1355
|
+
if (reverted) {
|
|
1356
|
+
throw new Swap2ExecutionError(`swap2: the claim transaction ${sent.hash} on ${unsigned.chain} reverted \u2014 poll the status of '${id}' and claim again if it is still action-required`, { cause: e });
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
return { hash: sent.hash, chain: unsigned.chain, id: encodeId(name, intentId, sourceTxHash, sent.hash, confirmed) };
|
|
1256
1361
|
}
|
|
1257
1362
|
/** The inherited bridge() has no floor field: quote first, then bind execution
|
|
1258
1363
|
* to that quote's minimum so the inherited convenience API stays safe. */
|
|
@@ -1329,7 +1434,10 @@ function structuredCloneSafe(value) {
|
|
|
1329
1434
|
if (value === null || typeof value !== "object") return value;
|
|
1330
1435
|
if (Array.isArray(value)) return value.map((v) => structuredCloneSafe(v));
|
|
1331
1436
|
const out = {};
|
|
1332
|
-
for (const [k, v] of Object.entries(value))
|
|
1437
|
+
for (const [k, v] of Object.entries(value)) {
|
|
1438
|
+
if (k === "__proto__") Object.defineProperty(out, k, { value: structuredCloneSafe(v), writable: true, enumerable: true, configurable: true });
|
|
1439
|
+
else out[k] = structuredCloneSafe(v);
|
|
1440
|
+
}
|
|
1333
1441
|
return out;
|
|
1334
1442
|
}
|
|
1335
1443
|
function deepFreeze(value) {
|
|
@@ -1340,63 +1448,6 @@ function deepFreeze(value) {
|
|
|
1340
1448
|
return value;
|
|
1341
1449
|
}
|
|
1342
1450
|
|
|
1343
|
-
// src/validate.ts
|
|
1344
|
-
function validQuote(q, params, provider) {
|
|
1345
|
-
try {
|
|
1346
|
-
if (q.provider !== provider || q.sourceAmount !== params.amountIn) return false;
|
|
1347
|
-
if (typeof q.intentId !== "string" || !q.intentId || typeof q.depositAddress !== "string" || q.depositMemo !== null && typeof q.depositMemo !== "string") return false;
|
|
1348
|
-
if (![q.sourceAmount, q.destAmount, q.minDestAmount].every((value) => /^[0-9]+$/.test(value))) return false;
|
|
1349
|
-
const source = BigInt(q.sourceAmount);
|
|
1350
|
-
const dest = BigInt(q.destAmount);
|
|
1351
|
-
const min = BigInt(q.minDestAmount);
|
|
1352
|
-
if (source <= 0n || dest <= 0n || min <= 0n || min > dest) return false;
|
|
1353
|
-
if (params.minAmountOut !== void 0) {
|
|
1354
|
-
if (!/^[0-9]+$/.test(params.minAmountOut) || BigInt(params.minAmountOut) <= 0n || min < BigInt(params.minAmountOut)) return false;
|
|
1355
|
-
}
|
|
1356
|
-
if (params.slippageBps !== void 0) {
|
|
1357
|
-
if (!Number.isInteger(params.slippageBps) || params.slippageBps < 0 || params.slippageBps >= 1e4) return false;
|
|
1358
|
-
const required = dest * BigInt(1e4 - params.slippageBps) / 10000n;
|
|
1359
|
-
if (min < required) return false;
|
|
1360
|
-
}
|
|
1361
|
-
if (!Number.isFinite(q.etaSeconds) || q.etaSeconds < 0) return false;
|
|
1362
|
-
if (!Number.isFinite(q.reliability) || q.reliability < 0 || q.reliability > 1) return false;
|
|
1363
|
-
if (!Number.isFinite(q.feeUsd) || q.feeUsd < 0) return false;
|
|
1364
|
-
if (q.fees && (!Array.isArray(q.fees) || !q.fees.every(
|
|
1365
|
-
(fee) => ["network", "protocol", "affiliate", "other"].includes(fee.type) && /^[0-9]+$/.test(fee.amount) && typeof fee.token === "string" && fee.token.length > 0 && typeof fee.included === "boolean" && (fee.description === void 0 || typeof fee.description === "string")
|
|
1366
|
-
))) return false;
|
|
1367
|
-
if (q.providerData && (Array.isArray(q.providerData) || typeof q.providerData !== "object" || !Object.values(q.providerData).every((value) => typeof value === "string"))) return false;
|
|
1368
|
-
const tx = q.txRequest;
|
|
1369
|
-
if (tx) {
|
|
1370
|
-
if (tx.value !== void 0 && !/^[0-9]+$/.test(tx.value)) return false;
|
|
1371
|
-
if (tx.gasLimit !== void 0 && !/^[0-9]+$/.test(tx.gasLimit)) return false;
|
|
1372
|
-
if (tx.chainId !== void 0 && (!Number.isSafeInteger(tx.chainId) || tx.chainId <= 0)) return false;
|
|
1373
|
-
if ([tx.to, tx.data, tx.approvalAddress, tx.serializedTx].some((value) => value !== void 0 && typeof value !== "string")) return false;
|
|
1374
|
-
}
|
|
1375
|
-
if (!Number.isFinite(q.validUntilMs) || q.validUntilMs <= Date.now()) return false;
|
|
1376
|
-
return true;
|
|
1377
|
-
} catch {
|
|
1378
|
-
return false;
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
function remotePinRefusal(provider, q, chain) {
|
|
1382
|
-
if (provider.remote !== true) return null;
|
|
1383
|
-
const tx = q.txRequest;
|
|
1384
|
-
const plain = tx ? tx.data === void 0 && tx.serializedTx === void 0 : typeof q.depositAddress === "string" && q.depositAddress !== "";
|
|
1385
|
-
if (plain && provider.depositAddressRoutes !== true) {
|
|
1386
|
-
return `${provider.name}: the route pays ${tx?.to || q.depositAddress}, an address the backend named, and the app did not allow deposit-address routes for this provider (remoteProvider depositAddressRoutes)`;
|
|
1387
|
-
}
|
|
1388
|
-
if (!chain.startsWith("eip155:")) return null;
|
|
1389
|
-
if (!tx || tx.serializedTx !== void 0) return null;
|
|
1390
|
-
const pinned = (list, address) => (list ?? []).some((a) => typeof a === "string" && a.toLowerCase() === address.toLowerCase());
|
|
1391
|
-
if (tx.approvalAddress && !pinned(provider.spenders?.(chain), tx.approvalAddress)) {
|
|
1392
|
-
return `${provider.name}: the route approves ${tx.approvalAddress}, which the app did not pin as a spender on ${chain} (remoteProvider spenders)`;
|
|
1393
|
-
}
|
|
1394
|
-
if (tx.data !== void 0 && tx.to && !pinned(provider.callTargets?.(chain), tx.to)) {
|
|
1395
|
-
return `${provider.name}: the route calls ${tx.to}, which the app did not pin as a call target on ${chain} (remoteProvider callTargets)`;
|
|
1396
|
-
}
|
|
1397
|
-
return null;
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1400
1451
|
// src/swap2/single.ts
|
|
1401
1452
|
var SingleProviderSwap2 = class extends BaseSwap2Protocol {
|
|
1402
1453
|
/** Why the last resolveQuote came back empty — the provider's own reason, like the router's. */
|