@anywalletkit/protocol-swap2-core 0.4.1 → 0.6.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 -1
- package/dist/{chunk-Y3VUEWYI.js → chunk-IGTMUTNJ.js} +106 -28
- package/dist/chunk-IGTMUTNJ.js.map +1 -0
- package/dist/{chunk-GDRKGFLH.js → chunk-JFGMF6FO.js} +2 -2
- package/dist/{chunk-SKV5BVXK.js → chunk-MNJTMSFN.js} +8 -2
- package/dist/chunk-MNJTMSFN.js.map +1 -0
- package/dist/{chunk-4H2JVYXT.js → chunk-ZMWK3RQ4.js} +4 -3
- package/dist/chunk-ZMWK3RQ4.js.map +1 -0
- package/dist/index.cjs +119 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +7 -5
- package/dist/remote.cjs +3 -1
- package/dist/remote.cjs.map +1 -1
- package/dist/remote.d.cts +6 -0
- package/dist/remote.d.ts +6 -0
- package/dist/remote.js +2 -2
- package/dist/server.cjs +8 -4
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +7 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.js +8 -5
- package/dist/server.js.map +1 -1
- package/dist/swap2/base.cjs +101 -26
- package/dist/swap2/base.cjs.map +1 -1
- package/dist/swap2/base.d.cts +21 -1
- package/dist/swap2/base.d.ts +21 -1
- package/dist/swap2/base.js +1 -1
- package/dist/swap2/single.cjs +101 -26
- package/dist/swap2/single.cjs.map +1 -1
- package/dist/swap2/single.d.cts +1 -0
- package/dist/swap2/single.d.ts +1 -0
- package/dist/swap2/single.js +2 -2
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +13 -0
- package/dist/types.d.ts +13 -0
- package/dist/util.cjs +6 -0
- package/dist/util.cjs.map +1 -1
- package/dist/util.d.cts +12 -1
- package/dist/util.d.ts +12 -1
- package/dist/util.js +3 -1
- package/package.json +7 -7
- package/dist/chunk-4H2JVYXT.js.map +0 -1
- package/dist/chunk-SKV5BVXK.js.map +0 -1
- package/dist/chunk-Y3VUEWYI.js.map +0 -1
- /package/dist/{chunk-GDRKGFLH.js.map → chunk-JFGMF6FO.js.map} +0 -0
package/dist/swap2/base.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Swap2Protocol, Swap2ProtocolConfig, Swap2Options, Swap2Fee, Swap2Quote, Swap2Result, Swap2StatusOptions, Swap2StatusResult, BridgeOptions, BridgeResult, SwapOptions, SwapResult, Swap2SupportedChain, Swap2SupportedTokensOptions, Swap2SupportedToken } from '@anywalletkit/core';
|
|
2
|
+
import { CcAsset } from '@anywalletkit/swap2-assets';
|
|
2
3
|
import { QuoteParams, Quote, CrossChainProvider } from '../types.cjs';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -89,6 +90,15 @@ interface Swap2EngineConfig extends Swap2ProtocolConfig {
|
|
|
89
90
|
* called again with `requoted: true`, and throwing then revokes the allowance.
|
|
90
91
|
*/
|
|
91
92
|
reviewExecution?: (plan: Swap2ExecutionPlan) => Promise<void> | void;
|
|
93
|
+
/**
|
|
94
|
+
* Accept tokens the swap catalog does not list, named by their contract (EVM) or mint (Solana):
|
|
95
|
+
* tokens the user added at their own risk. Off by default. The source token's decimals are read
|
|
96
|
+
* with the account's `getTokenInfo` (a `fromTokenDecimals` that disagrees is refused); an account
|
|
97
|
+
* without it must be given `fromTokenDecimals`. Only providers that trade any token (LI.FI,
|
|
98
|
+
* Uniswap, Balancer, Fynd, Jupiter) quote them, and they have no USD value: the router cannot
|
|
99
|
+
* judge their price, so show the user the amounts.
|
|
100
|
+
*/
|
|
101
|
+
allowUnlistedTokens?: boolean;
|
|
92
102
|
}
|
|
93
103
|
/** What `swap2()` is about to sign, handed to {@link Swap2EngineConfig.reviewExecution}. */
|
|
94
104
|
interface Swap2ExecutionPlan {
|
|
@@ -202,8 +212,18 @@ declare abstract class BaseSwap2Protocol extends Swap2Protocol {
|
|
|
202
212
|
protected protocolEnabled(): boolean;
|
|
203
213
|
/** Pair-level capability used when discovery specifies both endpoints. */
|
|
204
214
|
protected pairSupported(fromChain: string, toChain: string): boolean;
|
|
215
|
+
private unlistedAllowed;
|
|
216
|
+
/** The source asset the options name: the catalog's, or (allowUnlistedTokens) an unlisted one with its decimals. */
|
|
217
|
+
protected sourceAsset(options: Swap2Options): CcAsset | undefined;
|
|
218
|
+
private readonly unlistedDecimals;
|
|
219
|
+
/**
|
|
220
|
+
* Options ready to quote: with `allowUnlistedTokens`, an unlisted source token gets its decimals,
|
|
221
|
+
* read from the chain through the account (cached per token) — a caller's `fromTokenDecimals` that
|
|
222
|
+
* disagrees is refused. Listed tokens and every option otherwise pass through unchanged.
|
|
223
|
+
*/
|
|
224
|
+
protected prepareOptions(options: Swap2Options): Promise<Swap2Options>;
|
|
205
225
|
/** Swap2Options → the engine's QuoteParams (exact-in only). */
|
|
206
|
-
protected toParams(
|
|
226
|
+
protected toParams(given: Swap2Options, dry: boolean): QuoteParams;
|
|
207
227
|
private assertAccountChain;
|
|
208
228
|
protected assertFeeConfig(config?: Swap2ProtocolConfig): void;
|
|
209
229
|
protected assertFeeCaps(fees: readonly Swap2Fee[] | undefined, sourceAmount: bigint, sourceToken: string, config?: Swap2ProtocolConfig): void;
|
package/dist/swap2/base.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Swap2Protocol, Swap2ProtocolConfig, Swap2Options, Swap2Fee, Swap2Quote, Swap2Result, Swap2StatusOptions, Swap2StatusResult, BridgeOptions, BridgeResult, SwapOptions, SwapResult, Swap2SupportedChain, Swap2SupportedTokensOptions, Swap2SupportedToken } from '@anywalletkit/core';
|
|
2
|
+
import { CcAsset } from '@anywalletkit/swap2-assets';
|
|
2
3
|
import { QuoteParams, Quote, CrossChainProvider } from '../types.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -89,6 +90,15 @@ interface Swap2EngineConfig extends Swap2ProtocolConfig {
|
|
|
89
90
|
* called again with `requoted: true`, and throwing then revokes the allowance.
|
|
90
91
|
*/
|
|
91
92
|
reviewExecution?: (plan: Swap2ExecutionPlan) => Promise<void> | void;
|
|
93
|
+
/**
|
|
94
|
+
* Accept tokens the swap catalog does not list, named by their contract (EVM) or mint (Solana):
|
|
95
|
+
* tokens the user added at their own risk. Off by default. The source token's decimals are read
|
|
96
|
+
* with the account's `getTokenInfo` (a `fromTokenDecimals` that disagrees is refused); an account
|
|
97
|
+
* without it must be given `fromTokenDecimals`. Only providers that trade any token (LI.FI,
|
|
98
|
+
* Uniswap, Balancer, Fynd, Jupiter) quote them, and they have no USD value: the router cannot
|
|
99
|
+
* judge their price, so show the user the amounts.
|
|
100
|
+
*/
|
|
101
|
+
allowUnlistedTokens?: boolean;
|
|
92
102
|
}
|
|
93
103
|
/** What `swap2()` is about to sign, handed to {@link Swap2EngineConfig.reviewExecution}. */
|
|
94
104
|
interface Swap2ExecutionPlan {
|
|
@@ -202,8 +212,18 @@ declare abstract class BaseSwap2Protocol extends Swap2Protocol {
|
|
|
202
212
|
protected protocolEnabled(): boolean;
|
|
203
213
|
/** Pair-level capability used when discovery specifies both endpoints. */
|
|
204
214
|
protected pairSupported(fromChain: string, toChain: string): boolean;
|
|
215
|
+
private unlistedAllowed;
|
|
216
|
+
/** The source asset the options name: the catalog's, or (allowUnlistedTokens) an unlisted one with its decimals. */
|
|
217
|
+
protected sourceAsset(options: Swap2Options): CcAsset | undefined;
|
|
218
|
+
private readonly unlistedDecimals;
|
|
219
|
+
/**
|
|
220
|
+
* Options ready to quote: with `allowUnlistedTokens`, an unlisted source token gets its decimals,
|
|
221
|
+
* read from the chain through the account (cached per token) — a caller's `fromTokenDecimals` that
|
|
222
|
+
* disagrees is refused. Listed tokens and every option otherwise pass through unchanged.
|
|
223
|
+
*/
|
|
224
|
+
protected prepareOptions(options: Swap2Options): Promise<Swap2Options>;
|
|
205
225
|
/** Swap2Options → the engine's QuoteParams (exact-in only). */
|
|
206
|
-
protected toParams(
|
|
226
|
+
protected toParams(given: Swap2Options, dry: boolean): QuoteParams;
|
|
207
227
|
private assertAccountChain;
|
|
208
228
|
protected assertFeeConfig(config?: Swap2ProtocolConfig): void;
|
|
209
229
|
protected assertFeeCaps(fees: readonly Swap2Fee[] | undefined, sourceAmount: bigint, sourceToken: string, config?: Swap2ProtocolConfig): void;
|
package/dist/swap2/base.js
CHANGED
package/dist/swap2/single.cjs
CHANGED
|
@@ -111,12 +111,14 @@ var inFlightKey = (fromChain, o) => JSON.stringify(
|
|
|
111
111
|
[fromChain, o.fromToken, o.toToken, o.toChain, o.recipient, o.refundAddress, o.fromTokenAmount, o.toTokenAmount, o.minAmountOut, o.slippage],
|
|
112
112
|
(_k, v) => typeof v === "bigint" ? v.toString() : v
|
|
113
113
|
);
|
|
114
|
+
var LOST_ANSWER = /socket hang up|ECONNRESET|ECONNABORTED|ETIMEDOUT|EPIPE|ENETUNREACH|EHOSTUNREACH|EAI_AGAIN|UND_ERR_[A-Z_]+|timed? ?out|TimeoutError|request aborted|AbortError|connection (?:closed|reset)|fetch failed|failed to fetch|network request failed|other side closed/i;
|
|
114
115
|
function maybeSent(error) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
const chain = [];
|
|
117
|
+
for (let e = error, i = 0; e && typeof e === "object" && i < 6; e = e.cause, i++) chain.push(e);
|
|
118
|
+
for (const e of chain) if (e.maybeSent === true) return typeof e.hash === "string" && e.hash ? { hash: e.hash } : {};
|
|
119
|
+
if (chain.some((e) => e.answered === true)) return null;
|
|
120
|
+
const texts = chain.length ? chain.map((e) => [e.message, e.code, e.name].filter((x) => typeof x === "string").join(" ")) : [String(error)];
|
|
121
|
+
return texts.some((t) => LOST_ANSWER.test(t)) ? {} : null;
|
|
120
122
|
}
|
|
121
123
|
var NATIVE_DECIMALS = Object.freeze({
|
|
122
124
|
"eip155:5042": 18
|
|
@@ -200,22 +202,24 @@ function reviewedIdentity(plan) {
|
|
|
200
202
|
return JSON.stringify(pinned, (_k, v) => typeof v === "bigint" ? v.toString() : v);
|
|
201
203
|
}
|
|
202
204
|
var SWAP2_ID_PREFIX = "awk1";
|
|
203
|
-
function encodeId(provider, intentId, sourceTxHash) {
|
|
205
|
+
function encodeId(provider, intentId, sourceTxHash, destTxHash) {
|
|
204
206
|
const enc = (v) => encodeURIComponent(v).replace(/\./g, "%2E");
|
|
205
|
-
|
|
207
|
+
const parts = [provider, intentId, ...sourceTxHash != null ? [sourceTxHash, ...destTxHash != null ? [destTxHash] : []] : []];
|
|
208
|
+
return `${SWAP2_ID_PREFIX}.${parts.map(enc).join(".")}`;
|
|
206
209
|
}
|
|
207
210
|
function decodeId(id) {
|
|
208
211
|
try {
|
|
209
212
|
if (!id.startsWith(`${SWAP2_ID_PREFIX}.`)) throw new Error("unsupported id version");
|
|
210
213
|
const parts = id.slice(SWAP2_ID_PREFIX.length + 1).split(".");
|
|
211
|
-
if (parts.length < 2 || parts.length >
|
|
214
|
+
if (parts.length < 2 || parts.length > 4 || !parts[0] || !parts[1]) throw new Error("invalid id shape");
|
|
212
215
|
const provider = decodeURIComponent(parts[0]);
|
|
213
216
|
const intentId = decodeURIComponent(parts[1]);
|
|
214
217
|
const sourceTxHash = parts[2] ? decodeURIComponent(parts[2]) : void 0;
|
|
215
|
-
|
|
218
|
+
const destTxHash = parts[3] ? decodeURIComponent(parts[3]) : void 0;
|
|
219
|
+
if (!/^[A-Za-z0-9_-]+$/.test(provider) || !intentId || parts[2] !== void 0 && !sourceTxHash || parts[3] !== void 0 && !destTxHash) {
|
|
216
220
|
throw new Error("invalid id contents");
|
|
217
221
|
}
|
|
218
|
-
return { provider, intentId, ...sourceTxHash ? { sourceTxHash } : {} };
|
|
222
|
+
return { provider, intentId, ...sourceTxHash ? { sourceTxHash } : {}, ...destTxHash ? { destTxHash } : {} };
|
|
219
223
|
} catch {
|
|
220
224
|
throw new Swap2ValidationError("swap2: malformed swap2 status id");
|
|
221
225
|
}
|
|
@@ -267,16 +271,23 @@ function toCaip2(chain, fallback) {
|
|
|
267
271
|
}
|
|
268
272
|
return swap2ChainId(chain);
|
|
269
273
|
}
|
|
274
|
+
function nonEmptyRef(ref, side) {
|
|
275
|
+
if (typeof ref !== "string" || ref.trim() === "") throw new Swap2ValidationError(`swap2: ${side} token must not be empty (pass the symbol or the contract address)`);
|
|
276
|
+
return ref;
|
|
277
|
+
}
|
|
270
278
|
function toSymbol(caip2, ref) {
|
|
279
|
+
nonEmptyRef(ref, "source");
|
|
271
280
|
const byToken = ref.startsWith("0x") || ref.length > 20 ? (0, import_swap2_assets.ccAssetByToken)(caip2, ref) : void 0;
|
|
272
281
|
if (byToken) return byToken.symbol;
|
|
273
282
|
if ((0, import_swap2_assets.ccAsset)(caip2, ref)) return ref;
|
|
274
283
|
return ref;
|
|
275
284
|
}
|
|
276
|
-
function toDestAddress(caip2, ref) {
|
|
285
|
+
function toDestAddress(caip2, ref, allowUnlisted = false) {
|
|
286
|
+
nonEmptyRef(ref, "destination");
|
|
277
287
|
if (ref.startsWith("0x")) return ref;
|
|
278
288
|
const asset = (0, import_swap2_assets.ccAssetByToken)(caip2, ref) ?? (0, import_swap2_assets.ccAsset)(caip2, ref);
|
|
279
289
|
if (!asset) {
|
|
290
|
+
if (allowUnlisted && (0, import_swap2_assets.isTokenAddress)(caip2, ref)) return ref;
|
|
280
291
|
throw new Error(`swap2: unknown destination token '${ref}' on ${caip2}`);
|
|
281
292
|
}
|
|
282
293
|
return asset.contract ?? ZERO;
|
|
@@ -358,6 +369,15 @@ function mapStatus(s) {
|
|
|
358
369
|
const status = s.state === "completed" ? "completed" : s.state === "action-required" ? "action-required" : s.state === "refund-pending" ? "refund-pending" : s.state === "partial" ? "partial" : s.state === "failed" ? "failed" : s.state === "refunded" ? "refunded" : s.state === "cancelled" ? "cancelled" : s.state === "expired" ? "expired" : "pending";
|
|
359
370
|
return s.destTxHash ? { status, transactions: [{ hash: s.destTxHash, type: "destination" }] } : { status };
|
|
360
371
|
}
|
|
372
|
+
function blankToUndefined(v) {
|
|
373
|
+
return typeof v === "string" && v.trim() === "" ? void 0 : v;
|
|
374
|
+
}
|
|
375
|
+
function withoutBlankAddresses(o) {
|
|
376
|
+
const { recipient, refundAddress, ...rest } = o;
|
|
377
|
+
const r = blankToUndefined(recipient);
|
|
378
|
+
const f = blankToUndefined(refundAddress);
|
|
379
|
+
return { ...rest, ...r !== void 0 ? { recipient: r } : {}, ...f !== void 0 ? { refundAddress: f } : {} };
|
|
380
|
+
}
|
|
361
381
|
var BaseSwap2Protocol = class extends import_core.Swap2Protocol {
|
|
362
382
|
fromChain;
|
|
363
383
|
constructor(account, config) {
|
|
@@ -390,19 +410,61 @@ var BaseSwap2Protocol = class extends import_core.Swap2Protocol {
|
|
|
390
410
|
pairSupported(fromChain, toChain) {
|
|
391
411
|
return this.chainSupported(fromChain) && this.chainSupported(toChain);
|
|
392
412
|
}
|
|
413
|
+
unlistedAllowed() {
|
|
414
|
+
return this._config.allowUnlistedTokens === true;
|
|
415
|
+
}
|
|
416
|
+
/** The source asset the options name: the catalog's, or (allowUnlistedTokens) an unlisted one with its decimals. */
|
|
417
|
+
sourceAsset(options) {
|
|
418
|
+
const decimals = options.fromTokenDecimals;
|
|
419
|
+
return (0, import_swap2_assets.resolveAsset)(this.fromChain, toSymbol(this.fromChain, options.fromToken), {
|
|
420
|
+
allowUnlisted: this.unlistedAllowed(),
|
|
421
|
+
...decimals !== void 0 ? { decimals } : {}
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
unlistedDecimals = /* @__PURE__ */ new Map();
|
|
425
|
+
/**
|
|
426
|
+
* Options ready to quote: with `allowUnlistedTokens`, an unlisted source token gets its decimals,
|
|
427
|
+
* read from the chain through the account (cached per token) — a caller's `fromTokenDecimals` that
|
|
428
|
+
* disagrees is refused. Listed tokens and every option otherwise pass through unchanged.
|
|
429
|
+
*/
|
|
430
|
+
async prepareOptions(options) {
|
|
431
|
+
if (!this.unlistedAllowed()) return options;
|
|
432
|
+
const ref = toSymbol(this.fromChain, options.fromToken);
|
|
433
|
+
if ((0, import_swap2_assets.resolveAsset)(this.fromChain, ref) || !(0, import_swap2_assets.isTokenAddress)(this.fromChain, ref)) return options;
|
|
434
|
+
const key = ref.startsWith("0x") ? ref.toLowerCase() : ref;
|
|
435
|
+
let decimals = this.unlistedDecimals.get(key);
|
|
436
|
+
if (decimals === void 0) {
|
|
437
|
+
const reader = this._account;
|
|
438
|
+
if (typeof reader?.getTokenInfo !== "function") {
|
|
439
|
+
if (options.fromTokenDecimals === void 0) {
|
|
440
|
+
throw new Swap2ValidationError(`swap2: '${ref}' is not in the catalog \u2014 pass fromTokenDecimals, or use an account that can read token info`);
|
|
441
|
+
}
|
|
442
|
+
return options;
|
|
443
|
+
}
|
|
444
|
+
decimals = (await reader.getTokenInfo(ref)).decimals;
|
|
445
|
+
this.unlistedDecimals.set(key, decimals);
|
|
446
|
+
}
|
|
447
|
+
if (options.fromTokenDecimals !== void 0 && options.fromTokenDecimals !== decimals) {
|
|
448
|
+
throw new Swap2ValidationError(`swap2: fromTokenDecimals ${options.fromTokenDecimals} does not match the token's ${decimals}`);
|
|
449
|
+
}
|
|
450
|
+
return snapshotOptions({ ...options, fromTokenDecimals: decimals });
|
|
451
|
+
}
|
|
393
452
|
/** Swap2Options → the engine's QuoteParams (exact-in only). */
|
|
394
|
-
toParams(
|
|
453
|
+
toParams(given, dry) {
|
|
454
|
+
const options = withoutBlankAddresses(given);
|
|
395
455
|
if (options.fromTokenAmount == null) {
|
|
396
456
|
throw new Swap2UnsupportedOperationError("swap2: exact-out swap2 is not supported by the engine");
|
|
397
457
|
}
|
|
398
458
|
const destChain = toCaip2(options.toChain, this.fromChain);
|
|
399
459
|
const constraints = callerConstraints(options);
|
|
400
460
|
const amountIn = exactPositiveAmount(options.fromTokenAmount, "fromTokenAmount");
|
|
461
|
+
const source = this.sourceAsset(options);
|
|
401
462
|
const params = {
|
|
402
463
|
sourceChain: this.fromChain,
|
|
403
|
-
sourceToken: toSymbol(this.fromChain, options.fromToken),
|
|
464
|
+
sourceToken: (0, import_swap2_assets.isUnlisted)(source) ? source.contract : toSymbol(this.fromChain, options.fromToken),
|
|
465
|
+
...(0, import_swap2_assets.isUnlisted)(source) ? { sourceDecimals: source.decimals } : {},
|
|
404
466
|
destChain,
|
|
405
|
-
destToken: toDestAddress(destChain, options.toToken),
|
|
467
|
+
destToken: toDestAddress(destChain, options.toToken, this.unlistedAllowed()),
|
|
406
468
|
amountIn: amountIn.toString(),
|
|
407
469
|
destAddress: options.recipient ?? "",
|
|
408
470
|
dry,
|
|
@@ -458,7 +520,7 @@ var BaseSwap2Protocol = class extends import_core.Swap2Protocol {
|
|
|
458
520
|
}
|
|
459
521
|
}
|
|
460
522
|
assertQuoteFeeCaps(q, options, config) {
|
|
461
|
-
const source =
|
|
523
|
+
const source = this.sourceAsset(options);
|
|
462
524
|
if (!source) throw new Error(`swap2: unknown source token '${options.fromToken}' on ${this.fromChain}`);
|
|
463
525
|
const token = source.contract ?? source.assetId;
|
|
464
526
|
const fees = q.fees?.map((fee) => ({ ...fee, amount: BigInt(fee.amount) }));
|
|
@@ -503,7 +565,7 @@ var BaseSwap2Protocol = class extends import_core.Swap2Protocol {
|
|
|
503
565
|
* gave one.
|
|
504
566
|
*/
|
|
505
567
|
async quoteSwap2(options) {
|
|
506
|
-
const snapshot = snapshotOptions(options);
|
|
568
|
+
const snapshot = await this.prepareOptions(snapshotOptions(options));
|
|
507
569
|
const q = await this.resolveQuote(this.toParams(snapshot, true));
|
|
508
570
|
if (!q) {
|
|
509
571
|
const detail = this.noRouteDetail();
|
|
@@ -545,9 +607,9 @@ var BaseSwap2Protocol = class extends import_core.Swap2Protocol {
|
|
|
545
607
|
this.assertFeeConfig(configSnapshot);
|
|
546
608
|
await this.assertAccountChain(signer);
|
|
547
609
|
const selfAddress = await signer.getAddress();
|
|
548
|
-
const recipient = snapshot.recipient ?? selfAddress;
|
|
549
|
-
const refundAddress = snapshot.refundAddress ?? selfAddress;
|
|
550
|
-
const executionOptions = snapshotOptions({ ...snapshot, recipient, refundAddress });
|
|
610
|
+
const recipient = blankToUndefined(snapshot.recipient) ?? selfAddress;
|
|
611
|
+
const refundAddress = blankToUndefined(snapshot.refundAddress) ?? selfAddress;
|
|
612
|
+
const executionOptions = await this.prepareOptions(snapshotOptions({ ...snapshot, recipient, refundAddress }));
|
|
551
613
|
const params = this.toParams(executionOptions, false);
|
|
552
614
|
const resolved = await this.resolveQuote(params);
|
|
553
615
|
if (!resolved) {
|
|
@@ -589,7 +651,7 @@ var BaseSwap2Protocol = class extends import_core.Swap2Protocol {
|
|
|
589
651
|
throw new Error("swap2: provider build does not match the validated quote transaction");
|
|
590
652
|
}
|
|
591
653
|
} else if (!unsigned.bodyBoc && !unsigned.typedData) {
|
|
592
|
-
if (!q.depositAddress || unsigned.to !== q.depositAddress || (unsigned.memo ?? null) !== q.depositMemo) {
|
|
654
|
+
if (!q.depositAddress || unsigned.to !== q.depositAddress || (unsigned.memo ?? null) !== q.depositMemo || unsigned.data !== void 0 || unsigned.approvalAddress !== void 0 || unsigned.serializedTx !== void 0) {
|
|
593
655
|
throw new Error("swap2: provider build does not match the validated quote deposit");
|
|
594
656
|
}
|
|
595
657
|
}
|
|
@@ -628,7 +690,7 @@ var BaseSwap2Protocol = class extends import_core.Swap2Protocol {
|
|
|
628
690
|
}
|
|
629
691
|
let tokenId;
|
|
630
692
|
if (!unsigned.data && !unsigned.bodyBoc && !unsigned.typedData && !serialized) {
|
|
631
|
-
const src =
|
|
693
|
+
const src = this.sourceAsset(executionOptions);
|
|
632
694
|
if (src?.contract) {
|
|
633
695
|
if (this.fromChain.startsWith("eip155:")) {
|
|
634
696
|
unsigned = {
|
|
@@ -654,7 +716,7 @@ var BaseSwap2Protocol = class extends import_core.Swap2Protocol {
|
|
|
654
716
|
if (tokenId && (cellBody !== void 0 || unsigned.memo !== void 0 && !tronSource && !nearSource && !tonSource)) {
|
|
655
717
|
throw new Error("swap2: token deposit with memo/cell payload is not implemented");
|
|
656
718
|
}
|
|
657
|
-
const source =
|
|
719
|
+
const source = this.sourceAsset(executionOptions);
|
|
658
720
|
if (!source) throw new Error(`swap2: unknown source token '${executionOptions.fromToken}' on ${this.fromChain}`);
|
|
659
721
|
const executionValue = BigInt(unsigned.value);
|
|
660
722
|
if (serialized) {
|
|
@@ -944,10 +1006,10 @@ var BaseSwap2Protocol = class extends import_core.Swap2Protocol {
|
|
|
944
1006
|
}
|
|
945
1007
|
/** Status of a swap by the id `swap2()` returned, asked from the provider that executed it. */
|
|
946
1008
|
async getSwap2Status(id, _options) {
|
|
947
|
-
const { provider: name, intentId, sourceTxHash } = decodeId(id);
|
|
1009
|
+
const { provider: name, intentId, sourceTxHash, destTxHash } = decodeId(id);
|
|
948
1010
|
const provider = this.providerFor(name);
|
|
949
1011
|
if (!provider) throw new Error(`swap2: no swap2 found for id '${id}'`);
|
|
950
|
-
return mapStatus(await provider.trackIntent(sourceTxHash
|
|
1012
|
+
return mapStatus(await provider.trackIntent({ intentId, ...sourceTxHash != null ? { sourceTxHash } : {}, ...destTxHash != null ? { destTxHash } : {} }));
|
|
951
1013
|
}
|
|
952
1014
|
/**
|
|
953
1015
|
* Two-phase routes (CCTP-style burn-and-mint): once `getSwap2Status` reports
|
|
@@ -993,6 +1055,17 @@ var BaseSwap2Protocol = class extends import_core.Swap2Protocol {
|
|
|
993
1055
|
if (unsigned.chainId !== void 0 && String(unsigned.chainId) !== expectedId) {
|
|
994
1056
|
throw new Error(`swap2: claim chainId ${unsigned.chainId} does not match ${unsigned.chain}`);
|
|
995
1057
|
}
|
|
1058
|
+
if (typeof provider.claimTargets !== "function") {
|
|
1059
|
+
throw new Swap2UnsupportedOperationError(`swap2: provider '${name}' declares no claimTargets \u2014 a claim may only call contracts the provider names for its chain`);
|
|
1060
|
+
}
|
|
1061
|
+
const targets = provider.claimTargets(unsigned.chain).map((a) => a.toLowerCase());
|
|
1062
|
+
if (!targets.includes(unsigned.to.toLowerCase())) {
|
|
1063
|
+
throw new Swap2ValidationError(`swap2: claim target ${unsigned.to} is not a contract '${name}' declares for ${unsigned.chain} \u2014 refused`);
|
|
1064
|
+
}
|
|
1065
|
+
const claimCall = (0, import_tx_preview_evm.decodeCall)({ to: unsigned.to, data: unsigned.data, value: 0n });
|
|
1066
|
+
if (claimCall.transfer || claimCall.approval) {
|
|
1067
|
+
throw new Swap2ValidationError(`swap2: claim calldata (${claimCall.method}) moves tokens or grants an allowance \u2014 refused`);
|
|
1068
|
+
}
|
|
996
1069
|
const actual = await readAccountChain(signer);
|
|
997
1070
|
if (actual == null) throw new Error(`swap2: destination account chain identity is unavailable for ${unsigned.chain}`);
|
|
998
1071
|
const actualText = typeof actual === "string" ? actual : String(actual);
|
|
@@ -1008,7 +1081,7 @@ var BaseSwap2Protocol = class extends import_core.Swap2Protocol {
|
|
|
1008
1081
|
});
|
|
1009
1082
|
if (!sent.hash) throw new Swap2ExecutionError("swap2: account did not return a claim transaction hash");
|
|
1010
1083
|
this.emitPhase("broadcast", { hash: sent.hash });
|
|
1011
|
-
return { hash: sent.hash, chain: unsigned.chain, id };
|
|
1084
|
+
return { hash: sent.hash, chain: unsigned.chain, id: encodeId(name, intentId, sourceTxHash, sent.hash) };
|
|
1012
1085
|
}
|
|
1013
1086
|
/** The inherited bridge() has no floor field: quote first, then bind execution
|
|
1014
1087
|
* to that quote's minimum so the inherited convenience API stays safe. */
|
|
@@ -1068,7 +1141,9 @@ var BaseSwap2Protocol = class extends import_core.Swap2Protocol {
|
|
|
1068
1141
|
if ((wantFrom || wantTo) && c.caip2 !== wantFrom && c.caip2 !== wantTo) continue;
|
|
1069
1142
|
for (const a of c.assets) {
|
|
1070
1143
|
out.push({
|
|
1071
|
-
|
|
1144
|
+
// What quoteSwap2 takes back: the contract, or the symbol of the native coin — never the
|
|
1145
|
+
// provider-specific assetId, which is "" for most catalog entries (round 3, M25).
|
|
1146
|
+
token: a.contract ?? a.symbol,
|
|
1072
1147
|
chain: c.caip2,
|
|
1073
1148
|
symbol: a.symbol,
|
|
1074
1149
|
decimals: a.decimals,
|