@owney/sdk 0.7.26-beta.1 → 0.7.26-beta.3
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/index.cjs +350 -68
- package/dist/index.d.cts +18 -2
- package/dist/index.d.ts +18 -2
- package/dist/index.js +320 -33
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2208,7 +2208,21 @@ var CHAIN_NAMES = {
|
|
|
2208
2208
|
function chainName(chainId) {
|
|
2209
2209
|
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
2210
2210
|
}
|
|
2211
|
-
|
|
2211
|
+
var SETTLE_TIMEOUT_MS = 8e3;
|
|
2212
|
+
var POLL_INTERVAL_MS = 200;
|
|
2213
|
+
var wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
2214
|
+
async function readChainId(pub) {
|
|
2215
|
+
try {
|
|
2216
|
+
return await pub.getChainId();
|
|
2217
|
+
} catch {
|
|
2218
|
+
return void 0;
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
async function ensureWalletOnChain(pub, wallet, expected, options = {}) {
|
|
2222
|
+
const {
|
|
2223
|
+
settleTimeoutMs = SETTLE_TIMEOUT_MS,
|
|
2224
|
+
pollIntervalMs = POLL_INTERVAL_MS
|
|
2225
|
+
} = options;
|
|
2212
2226
|
const actual = await pub.getChainId();
|
|
2213
2227
|
if (actual === expected) return;
|
|
2214
2228
|
try {
|
|
@@ -2224,14 +2238,18 @@ async function ensureWalletOnChain(pub, wallet, expected) {
|
|
|
2224
2238
|
}
|
|
2225
2239
|
);
|
|
2226
2240
|
}
|
|
2227
|
-
const
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2241
|
+
const deadline = Date.now() + settleTimeoutMs;
|
|
2242
|
+
let after = await readChainId(pub);
|
|
2243
|
+
while (after !== expected && Date.now() < deadline) {
|
|
2244
|
+
await wait(pollIntervalMs);
|
|
2245
|
+
after = await readChainId(pub);
|
|
2246
|
+
}
|
|
2247
|
+
if (after === expected) return;
|
|
2248
|
+
throw new OwneyError(
|
|
2249
|
+
"CHAIN_MISMATCH",
|
|
2250
|
+
`Your wallet is still on ${chainName(after ?? actual)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2251
|
+
{ expectedChainId: expected, actualChainId: after ?? actual }
|
|
2252
|
+
);
|
|
2235
2253
|
}
|
|
2236
2254
|
|
|
2237
2255
|
// src/lib/swap/swap-api.ts
|
|
@@ -2348,9 +2366,10 @@ function createSwapApi(baseUrl, apiKey) {
|
|
|
2348
2366
|
dstSymbol: params.to.symbol,
|
|
2349
2367
|
amount: params.from.amount,
|
|
2350
2368
|
walletAddress: params.walletAddress,
|
|
2351
|
-
secretHashes: params.secretHashes,
|
|
2369
|
+
...params.secretHashes ? { secretHashes: params.secretHashes } : {},
|
|
2352
2370
|
...params.direction ? { direction: params.direction } : {},
|
|
2353
|
-
...params.receiver ? { receiver: params.receiver } : {}
|
|
2371
|
+
...params.receiver ? { receiver: params.receiver } : {},
|
|
2372
|
+
...params.permit ? { permit: params.permit } : {}
|
|
2354
2373
|
}
|
|
2355
2374
|
}),
|
|
2356
2375
|
submitOrder: (body) => request(baseUrl, apiKey, "/order", { method: "POST", body }),
|
|
@@ -2363,7 +2382,16 @@ function createSwapApi(baseUrl, apiKey) {
|
|
|
2363
2382
|
method: "POST",
|
|
2364
2383
|
body: { orderHash, secret }
|
|
2365
2384
|
}),
|
|
2366
|
-
|
|
2385
|
+
/**
|
|
2386
|
+
* @param chainId present only for a SAME-CHAIN order. That product keys its
|
|
2387
|
+
* orders per chain and the Fusion+ endpoint does not know them, so asking
|
|
2388
|
+
* without it answers 404 — which reads as an order that vanished.
|
|
2389
|
+
*/
|
|
2390
|
+
orderStatus: (orderHash, chainId) => request(
|
|
2391
|
+
baseUrl,
|
|
2392
|
+
apiKey,
|
|
2393
|
+
chainId === void 0 ? `/order/${orderHash}` : `/order/${orderHash}?chainId=${chainId}`
|
|
2394
|
+
),
|
|
2367
2395
|
readyForSecrets: (orderHash) => request(
|
|
2368
2396
|
baseUrl,
|
|
2369
2397
|
apiKey,
|
|
@@ -2506,7 +2534,12 @@ var SWAP_TERMINAL_STATUSES = [
|
|
|
2506
2534
|
"executed",
|
|
2507
2535
|
"expired",
|
|
2508
2536
|
"cancelled",
|
|
2509
|
-
"refunded"
|
|
2537
|
+
"refunded",
|
|
2538
|
+
"filled",
|
|
2539
|
+
"false-predicate",
|
|
2540
|
+
"not-enough-balance-or-allowance",
|
|
2541
|
+
"wrong-permit",
|
|
2542
|
+
"invalid-signature"
|
|
2510
2543
|
];
|
|
2511
2544
|
var isSwapTerminal = (status) => SWAP_TERMINAL_STATUSES.includes(status);
|
|
2512
2545
|
|
|
@@ -2584,6 +2617,71 @@ async function runFusionOrder(deps, options) {
|
|
|
2584
2617
|
}
|
|
2585
2618
|
}
|
|
2586
2619
|
|
|
2620
|
+
// src/lib/swap/swap.same-chain-runner.ts
|
|
2621
|
+
var DEFAULT_POLL_MS2 = 5e3;
|
|
2622
|
+
var MAX_BACKOFF_MS2 = 3e4;
|
|
2623
|
+
var backoffFor2 = (failures, base3) => Math.min(base3 * 2 ** Math.min(failures, 5), MAX_BACKOFF_MS2);
|
|
2624
|
+
var DEFAULT_TIMEOUT_MS2 = 3 * 60 * 1e3;
|
|
2625
|
+
function explain(status) {
|
|
2626
|
+
switch (status) {
|
|
2627
|
+
case "expired":
|
|
2628
|
+
return "No one filled the swap in time, so nothing was exchanged. Your funds never left your wallet.";
|
|
2629
|
+
case "cancelled":
|
|
2630
|
+
return "The swap was cancelled before anyone filled it. Your funds never left your wallet.";
|
|
2631
|
+
case "not-enough-balance-or-allowance":
|
|
2632
|
+
return "The swap could not be filled because the balance or approval had changed since it was signed. Nothing was exchanged.";
|
|
2633
|
+
case "wrong-permit":
|
|
2634
|
+
return "The approval signed with this swap was not accepted. Nothing was exchanged \u2014 try again, approving in your wallet if you are asked to.";
|
|
2635
|
+
case "invalid-signature":
|
|
2636
|
+
return "The swap signature was rejected. Nothing was exchanged.";
|
|
2637
|
+
case "false-predicate":
|
|
2638
|
+
return "The swap's conditions no longer held when it was filled. Nothing was exchanged.";
|
|
2639
|
+
default:
|
|
2640
|
+
return "The swap did not complete. Nothing was exchanged.";
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
async function runSameChainOrder(deps, options) {
|
|
2644
|
+
const {
|
|
2645
|
+
orderHash,
|
|
2646
|
+
onStage,
|
|
2647
|
+
pollIntervalMs = DEFAULT_POLL_MS2,
|
|
2648
|
+
timeoutMs = DEFAULT_TIMEOUT_MS2
|
|
2649
|
+
} = options;
|
|
2650
|
+
const deadline = deps.now() + timeoutMs;
|
|
2651
|
+
let failures = 0;
|
|
2652
|
+
onStage?.("swapping");
|
|
2653
|
+
for (; ; ) {
|
|
2654
|
+
if (deps.now() >= deadline) {
|
|
2655
|
+
throw new OwneyError(
|
|
2656
|
+
"SWAP_ORDER_EXPIRED",
|
|
2657
|
+
"No one filled the swap in time. Nothing was exchanged and your funds are still in your wallet.",
|
|
2658
|
+
{ orderHash }
|
|
2659
|
+
);
|
|
2660
|
+
}
|
|
2661
|
+
let status;
|
|
2662
|
+
try {
|
|
2663
|
+
({ status } = await deps.orderStatus(orderHash));
|
|
2664
|
+
failures = 0;
|
|
2665
|
+
} catch {
|
|
2666
|
+
failures += 1;
|
|
2667
|
+
await deps.sleep(backoffFor2(failures, pollIntervalMs));
|
|
2668
|
+
continue;
|
|
2669
|
+
}
|
|
2670
|
+
if (isSwapTerminal(status)) {
|
|
2671
|
+
if (status === "filled" || status === "executed") {
|
|
2672
|
+
onStage?.("swapped");
|
|
2673
|
+
return { status, filled: true };
|
|
2674
|
+
}
|
|
2675
|
+
throw new OwneyError(
|
|
2676
|
+
status === "cancelled" ? "SWAP_ORDER_CANCELLED" : "SWAP_ORDER_EXPIRED",
|
|
2677
|
+
explain(status),
|
|
2678
|
+
{ orderHash, status }
|
|
2679
|
+
);
|
|
2680
|
+
}
|
|
2681
|
+
await deps.sleep(pollIntervalMs);
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
|
|
2587
2685
|
// src/lib/swap/swap.secret-store.ts
|
|
2588
2686
|
var KEY_PREFIX2 = "owney.swap.order";
|
|
2589
2687
|
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -2667,7 +2765,7 @@ async function executeSwap(deps, options) {
|
|
|
2667
2765
|
});
|
|
2668
2766
|
const before = await deps.readTargetBalance();
|
|
2669
2767
|
debugLog("owney-sdk", "swap: target balance before", before.toString());
|
|
2670
|
-
const result = quote.rail === "classic" ? await runClassic(deps, options) : await runFusion(deps, options, walletAddress);
|
|
2768
|
+
const result = quote.rail === "classic" ? await runClassic(deps, options) : quote.rail === "fusion" ? await runSameChainFusion(deps, options, walletAddress) : await runFusion(deps, options, walletAddress);
|
|
2671
2769
|
const after = await deps.readTargetBalance();
|
|
2672
2770
|
const received = after - before;
|
|
2673
2771
|
debugLog("owney-sdk", "swap: target balance after", {
|
|
@@ -2738,25 +2836,75 @@ async function runClassic(deps, options) {
|
|
|
2738
2836
|
onStage?.("swapped");
|
|
2739
2837
|
return { txHash };
|
|
2740
2838
|
}
|
|
2741
|
-
async function
|
|
2839
|
+
async function runSameChainFusion(deps, options, walletAddress) {
|
|
2742
2840
|
const { quote, direction, onStage } = options;
|
|
2743
2841
|
const isNativeSource = quote.src.address.toLowerCase().startsWith("0xeeee");
|
|
2744
2842
|
const amount = isNativeSource ? BigInt(quote.src.amount) : await affordableAmount(deps, BigInt(quote.src.amount));
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2843
|
+
const permit = await authoriseSpend(deps, options, amount, isNativeSource);
|
|
2844
|
+
onStage?.("quoting");
|
|
2845
|
+
debugLog("owney-sdk", "swap: building same-chain fusion order");
|
|
2846
|
+
const built = await deps.api.buildOrder({
|
|
2847
|
+
from: {
|
|
2848
|
+
chainId: quote.src.chainId,
|
|
2849
|
+
symbol: quote.src.symbol,
|
|
2850
|
+
amount: amount.toString()
|
|
2851
|
+
},
|
|
2852
|
+
to: { chainId: quote.dst.chainId, symbol: quote.dst.symbol },
|
|
2853
|
+
walletAddress,
|
|
2854
|
+
...direction ? { direction } : {},
|
|
2855
|
+
...permit ? { permit } : {}
|
|
2856
|
+
});
|
|
2857
|
+
debugLog("owney-sdk", "swap: same-chain order built", {
|
|
2858
|
+
orderHash: built.orderHash
|
|
2859
|
+
});
|
|
2860
|
+
onStage?.("signing");
|
|
2861
|
+
await deps.ensureChain(quote.src.chainId);
|
|
2862
|
+
const signature = await deps.signTypedData(built.typedData);
|
|
2863
|
+
debugLog("owney-sdk", "swap: signed, submitting same-chain order");
|
|
2864
|
+
await deps.api.submitOrder({
|
|
2865
|
+
// Without this the routing API hands the order to the Fusion+ relayer,
|
|
2866
|
+
// which does not know it and answers with an error that names nothing.
|
|
2867
|
+
rail: "fusion",
|
|
2868
|
+
srcChainId: quote.src.chainId,
|
|
2869
|
+
order: built.order,
|
|
2870
|
+
signature,
|
|
2871
|
+
quoteId: built.quoteId,
|
|
2872
|
+
...built.extension ? { extension: built.extension } : {}
|
|
2873
|
+
});
|
|
2874
|
+
await runSameChainOrder(deps.sameChainRunner ?? deps.runner, {
|
|
2875
|
+
orderHash: built.orderHash,
|
|
2876
|
+
...onStage ? { onStage } : {}
|
|
2877
|
+
});
|
|
2878
|
+
return { orderHash: built.orderHash };
|
|
2879
|
+
}
|
|
2880
|
+
async function authoriseSpend(deps, options, amount, isNativeSource) {
|
|
2881
|
+
const { quote, onStage } = options;
|
|
2882
|
+
if (!quote.spender || isNativeSource) return void 0;
|
|
2883
|
+
const current = await deps.readAllowance(quote.spender);
|
|
2884
|
+
debugLog("owney-sdk", "swap: fusion allowance", {
|
|
2885
|
+
spender: quote.spender,
|
|
2886
|
+
current: current.toString(),
|
|
2887
|
+
needed: amount.toString()
|
|
2888
|
+
});
|
|
2889
|
+
if (current >= amount) return void 0;
|
|
2890
|
+
onStage?.("approving");
|
|
2891
|
+
await deps.ensureChain(quote.src.chainId);
|
|
2892
|
+
const permit = quote.permitSupported ? await deps.buildPermit?.(quote.spender, MAX_UINT256) ?? void 0 : void 0;
|
|
2893
|
+
if (permit) {
|
|
2894
|
+
debugLog("owney-sdk", "swap: permitting limit order protocol", {
|
|
2895
|
+
bytes: (permit.length - 2) / 2
|
|
2752
2896
|
});
|
|
2753
|
-
|
|
2754
|
-
onStage?.("approving");
|
|
2755
|
-
await deps.ensureChain(quote.src.chainId);
|
|
2756
|
-
await deps.approve(quote.spender, MAX_UINT256);
|
|
2757
|
-
debugLog("owney-sdk", "swap: approved limit order protocol");
|
|
2758
|
-
}
|
|
2897
|
+
return permit;
|
|
2759
2898
|
}
|
|
2899
|
+
await deps.approve(quote.spender, MAX_UINT256);
|
|
2900
|
+
debugLog("owney-sdk", "swap: approved limit order protocol");
|
|
2901
|
+
return void 0;
|
|
2902
|
+
}
|
|
2903
|
+
async function runFusion(deps, options, walletAddress) {
|
|
2904
|
+
const { quote, direction, onStage } = options;
|
|
2905
|
+
const isNativeSource = quote.src.address.toLowerCase().startsWith("0xeeee");
|
|
2906
|
+
const amount = isNativeSource ? BigInt(quote.src.amount) : await affordableAmount(deps, BigInt(quote.src.amount));
|
|
2907
|
+
const permit = await authoriseSpend(deps, options, amount, isNativeSource);
|
|
2760
2908
|
const { secrets, secretHashes } = mintSecrets(quote.secretsCount ?? 1);
|
|
2761
2909
|
onStage?.("quoting");
|
|
2762
2910
|
debugLog("owney-sdk", "swap: building fusion order", {
|
|
@@ -2772,7 +2920,8 @@ async function runFusion(deps, options, walletAddress) {
|
|
|
2772
2920
|
to: { chainId: quote.dst.chainId, symbol: quote.dst.symbol },
|
|
2773
2921
|
walletAddress,
|
|
2774
2922
|
secretHashes,
|
|
2775
|
-
...direction ? { direction } : {}
|
|
2923
|
+
...direction ? { direction } : {},
|
|
2924
|
+
...permit ? { permit } : {}
|
|
2776
2925
|
});
|
|
2777
2926
|
saveOrder({
|
|
2778
2927
|
orderHash: built.orderHash,
|
|
@@ -2823,16 +2972,114 @@ async function runFusion(deps, options, walletAddress) {
|
|
|
2823
2972
|
return { orderHash: built.orderHash };
|
|
2824
2973
|
}
|
|
2825
2974
|
|
|
2975
|
+
// src/lib/swap/swap.permit.ts
|
|
2976
|
+
var import_viem5 = require("viem");
|
|
2977
|
+
var PERMIT_ABI = (0, import_viem5.parseAbi)([
|
|
2978
|
+
"function nonces(address owner) view returns (uint256)",
|
|
2979
|
+
"function name() view returns (string)",
|
|
2980
|
+
"function version() view returns (string)",
|
|
2981
|
+
"function DOMAIN_SEPARATOR() view returns (bytes32)"
|
|
2982
|
+
]);
|
|
2983
|
+
var PERMIT_TYPES = {
|
|
2984
|
+
Permit: [
|
|
2985
|
+
{ name: "owner", type: "address" },
|
|
2986
|
+
{ name: "spender", type: "address" },
|
|
2987
|
+
{ name: "value", type: "uint256" },
|
|
2988
|
+
{ name: "nonce", type: "uint256" },
|
|
2989
|
+
{ name: "deadline", type: "uint256" }
|
|
2990
|
+
]
|
|
2991
|
+
};
|
|
2992
|
+
var VERSION_CANDIDATES = ["1", "2"];
|
|
2993
|
+
var PERMIT_TTL_SECONDS = 3600;
|
|
2994
|
+
async function tryRead(read) {
|
|
2995
|
+
try {
|
|
2996
|
+
return await read();
|
|
2997
|
+
} catch {
|
|
2998
|
+
return null;
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
function matchDomain(name, versions, chainId, token, onChainSeparator) {
|
|
3002
|
+
for (const version of versions) {
|
|
3003
|
+
const domain = { name, version, chainId, verifyingContract: token };
|
|
3004
|
+
if ((0, import_viem5.domainSeparator)({ domain }) === onChainSeparator) return domain;
|
|
3005
|
+
}
|
|
3006
|
+
return null;
|
|
3007
|
+
}
|
|
3008
|
+
async function buildErc2612Permit(reads, input) {
|
|
3009
|
+
const { token, owner, spender, value, chainId } = input;
|
|
3010
|
+
const call = (functionName, args) => reads.readContract({
|
|
3011
|
+
address: token,
|
|
3012
|
+
abi: PERMIT_ABI,
|
|
3013
|
+
functionName,
|
|
3014
|
+
...args ? { args } : {}
|
|
3015
|
+
});
|
|
3016
|
+
const nonce = await tryRead(() => call("nonces", [owner]));
|
|
3017
|
+
if (nonce === null) {
|
|
3018
|
+
debugLog("owney-sdk", "permit: token has no nonces(), using approval", {
|
|
3019
|
+
token
|
|
3020
|
+
});
|
|
3021
|
+
return null;
|
|
3022
|
+
}
|
|
3023
|
+
const separator = await tryRead(
|
|
3024
|
+
() => call("DOMAIN_SEPARATOR")
|
|
3025
|
+
);
|
|
3026
|
+
const name = await tryRead(() => call("name"));
|
|
3027
|
+
if (!separator || !name) {
|
|
3028
|
+
debugLog("owney-sdk", "permit: domain unverifiable, using approval", {
|
|
3029
|
+
token,
|
|
3030
|
+
hasSeparator: Boolean(separator),
|
|
3031
|
+
hasName: Boolean(name)
|
|
3032
|
+
});
|
|
3033
|
+
return null;
|
|
3034
|
+
}
|
|
3035
|
+
const declared = await tryRead(() => call("version"));
|
|
3036
|
+
const domain = matchDomain(
|
|
3037
|
+
name,
|
|
3038
|
+
declared ? [declared, ...VERSION_CANDIDATES] : VERSION_CANDIDATES,
|
|
3039
|
+
chainId,
|
|
3040
|
+
token,
|
|
3041
|
+
separator
|
|
3042
|
+
);
|
|
3043
|
+
if (!domain) {
|
|
3044
|
+
debugLog("owney-sdk", "permit: no domain matched, using approval", {
|
|
3045
|
+
token,
|
|
3046
|
+
declared
|
|
3047
|
+
});
|
|
3048
|
+
return null;
|
|
3049
|
+
}
|
|
3050
|
+
const deadline = BigInt(Math.floor((input.now?.() ?? Date.now()) / 1e3)) + BigInt(PERMIT_TTL_SECONDS);
|
|
3051
|
+
const signature = await input.signTypedData({
|
|
3052
|
+
domain,
|
|
3053
|
+
types: PERMIT_TYPES,
|
|
3054
|
+
primaryType: "Permit",
|
|
3055
|
+
message: { owner, spender, value, nonce, deadline }
|
|
3056
|
+
});
|
|
3057
|
+
const { r, s, v, yParity } = (0, import_viem5.parseSignature)(signature);
|
|
3058
|
+
const recoveryV = v ?? BigInt(yParity + 27);
|
|
3059
|
+
return (0, import_viem5.encodeAbiParameters)(
|
|
3060
|
+
[
|
|
3061
|
+
{ type: "address" },
|
|
3062
|
+
{ type: "address" },
|
|
3063
|
+
{ type: "uint256" },
|
|
3064
|
+
{ type: "uint256" },
|
|
3065
|
+
{ type: "uint8" },
|
|
3066
|
+
{ type: "bytes32" },
|
|
3067
|
+
{ type: "bytes32" }
|
|
3068
|
+
],
|
|
3069
|
+
[owner, spender, value, deadline, Number(recoveryV), r, s]
|
|
3070
|
+
);
|
|
3071
|
+
}
|
|
3072
|
+
|
|
2826
3073
|
// src/lib/swap/swap.arrival.ts
|
|
2827
|
-
var
|
|
2828
|
-
var
|
|
3074
|
+
var DEFAULT_TIMEOUT_MS3 = 18e4;
|
|
3075
|
+
var DEFAULT_POLL_MS3 = 4e3;
|
|
2829
3076
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
2830
3077
|
async function awaitWithdrawalArrival(options) {
|
|
2831
3078
|
const {
|
|
2832
3079
|
readBalance,
|
|
2833
3080
|
baseline,
|
|
2834
|
-
timeoutMs =
|
|
2835
|
-
pollMs =
|
|
3081
|
+
timeoutMs = DEFAULT_TIMEOUT_MS3,
|
|
3082
|
+
pollMs = DEFAULT_POLL_MS3
|
|
2836
3083
|
} = options;
|
|
2837
3084
|
const deadline = Date.now() + timeoutMs;
|
|
2838
3085
|
debugLog("owney-sdk", "withdraw: waiting for funds to land", {
|
|
@@ -2907,7 +3154,7 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
|
2907
3154
|
}
|
|
2908
3155
|
|
|
2909
3156
|
// src/lib/helpers/withdraw-helper.ts
|
|
2910
|
-
var
|
|
3157
|
+
var import_viem6 = require("viem");
|
|
2911
3158
|
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
2912
3159
|
const target = asset.toUpperCase();
|
|
2913
3160
|
return agents.map((agent) => {
|
|
@@ -2916,7 +3163,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
2916
3163
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
2917
3164
|
);
|
|
2918
3165
|
if (!tokenBalance) return { agent, balance: 0n };
|
|
2919
|
-
return { agent, balance: (0,
|
|
3166
|
+
return { agent, balance: (0, import_viem6.parseUnits)(tokenBalance.amount, decimals) };
|
|
2920
3167
|
});
|
|
2921
3168
|
}
|
|
2922
3169
|
function planProportionalShares(balances, requested, totalAvailable) {
|
|
@@ -3063,11 +3310,11 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
3063
3310
|
}
|
|
3064
3311
|
|
|
3065
3312
|
// src/client.ts
|
|
3066
|
-
var
|
|
3313
|
+
var import_viem9 = require("viem");
|
|
3067
3314
|
var import_chains2 = require("viem/chains");
|
|
3068
3315
|
|
|
3069
3316
|
// src/lib/transfer-auth.ts
|
|
3070
|
-
var
|
|
3317
|
+
var import_viem7 = require("viem");
|
|
3071
3318
|
var ERC20_META_ABI = [
|
|
3072
3319
|
{ type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
|
|
3073
3320
|
{ type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
|
|
@@ -3099,7 +3346,7 @@ async function readTokenMeta(publicClient, token) {
|
|
|
3099
3346
|
function randomAuthNonce() {
|
|
3100
3347
|
const bytes = new Uint8Array(32);
|
|
3101
3348
|
globalThis.crypto.getRandomValues(bytes);
|
|
3102
|
-
return (0,
|
|
3349
|
+
return (0, import_viem7.bytesToHex)(bytes);
|
|
3103
3350
|
}
|
|
3104
3351
|
|
|
3105
3352
|
// src/lib/sponsor-client.ts
|
|
@@ -3381,7 +3628,7 @@ function makeSponsoredWethCallback(deps) {
|
|
|
3381
3628
|
}
|
|
3382
3629
|
|
|
3383
3630
|
// src/lib/sponsored-calls-deposit.ts
|
|
3384
|
-
var
|
|
3631
|
+
var import_viem8 = require("viem");
|
|
3385
3632
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
3386
3633
|
var DEFAULT_MAX_POLLS = 30;
|
|
3387
3634
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -3389,7 +3636,7 @@ async function paymasterSupported(provider, owner, chainId) {
|
|
|
3389
3636
|
method: "wallet_getCapabilities",
|
|
3390
3637
|
params: [owner]
|
|
3391
3638
|
});
|
|
3392
|
-
const forChain = caps?.[(0,
|
|
3639
|
+
const forChain = caps?.[(0, import_viem8.toHex)(chainId)] ?? caps?.[String(chainId)];
|
|
3393
3640
|
return Boolean(forChain?.paymasterService?.supported);
|
|
3394
3641
|
}
|
|
3395
3642
|
function makeSponsoredCallsCallback(deps) {
|
|
@@ -3423,8 +3670,8 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
3423
3670
|
{ chainId }
|
|
3424
3671
|
);
|
|
3425
3672
|
}
|
|
3426
|
-
const data = (0,
|
|
3427
|
-
abi:
|
|
3673
|
+
const data = (0, import_viem8.encodeFunctionData)({
|
|
3674
|
+
abi: import_viem8.erc20Abi,
|
|
3428
3675
|
functionName: "transfer",
|
|
3429
3676
|
args: [smartWallet, BigInt(amount)]
|
|
3430
3677
|
});
|
|
@@ -3434,7 +3681,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
3434
3681
|
{
|
|
3435
3682
|
version: "2.0.0",
|
|
3436
3683
|
from: deps.ownerAddress,
|
|
3437
|
-
chainId: (0,
|
|
3684
|
+
chainId: (0, import_viem8.toHex)(chainId),
|
|
3438
3685
|
atomicRequired: false,
|
|
3439
3686
|
calls: [{ to: token, value: "0x0", data }],
|
|
3440
3687
|
capabilities: {
|
|
@@ -3656,14 +3903,14 @@ var OwneySDK = class {
|
|
|
3656
3903
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
3657
3904
|
// PublicClient/WalletClient param types — structurally identical at
|
|
3658
3905
|
// runtime, but the two share a name TS treats as unrelated.
|
|
3659
|
-
getPublicClient: (cid) => (0,
|
|
3906
|
+
getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
|
|
3660
3907
|
chain: VIEM_CHAIN2[cid],
|
|
3661
|
-
transport: (0,
|
|
3908
|
+
transport: (0, import_viem9.custom)(provider)
|
|
3662
3909
|
}),
|
|
3663
|
-
getWalletClient: (cid) => (0,
|
|
3910
|
+
getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
|
|
3664
3911
|
account: owner,
|
|
3665
3912
|
chain: VIEM_CHAIN2[cid],
|
|
3666
|
-
transport: (0,
|
|
3913
|
+
transport: (0, import_viem9.custom)(provider)
|
|
3667
3914
|
})
|
|
3668
3915
|
});
|
|
3669
3916
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
@@ -3709,14 +3956,14 @@ var OwneySDK = class {
|
|
|
3709
3956
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
3710
3957
|
// PublicClient/WalletClient param types — structurally identical at
|
|
3711
3958
|
// runtime, but the two share a name TS treats as unrelated.
|
|
3712
|
-
getPublicClient: (cid) => (0,
|
|
3959
|
+
getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
|
|
3713
3960
|
chain: VIEM_CHAIN2[cid],
|
|
3714
|
-
transport: (0,
|
|
3961
|
+
transport: (0, import_viem9.custom)(provider)
|
|
3715
3962
|
}),
|
|
3716
|
-
getWalletClient: (cid) => (0,
|
|
3963
|
+
getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
|
|
3717
3964
|
account: owner,
|
|
3718
3965
|
chain: VIEM_CHAIN2[cid],
|
|
3719
|
-
transport: (0,
|
|
3966
|
+
transport: (0, import_viem9.custom)(provider)
|
|
3720
3967
|
})
|
|
3721
3968
|
});
|
|
3722
3969
|
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
@@ -4298,11 +4545,11 @@ var OwneySDK = class {
|
|
|
4298
4545
|
);
|
|
4299
4546
|
}
|
|
4300
4547
|
await ensureWalletOnChain(
|
|
4301
|
-
(0,
|
|
4302
|
-
(0,
|
|
4548
|
+
(0, import_viem9.createPublicClient)({ chain, transport: (0, import_viem9.custom)(provider) }),
|
|
4549
|
+
(0, import_viem9.createWalletClient)({
|
|
4303
4550
|
account: state.walletAddress,
|
|
4304
4551
|
chain,
|
|
4305
|
-
transport: (0,
|
|
4552
|
+
transport: (0, import_viem9.custom)(provider)
|
|
4306
4553
|
}),
|
|
4307
4554
|
chainId
|
|
4308
4555
|
);
|
|
@@ -4318,16 +4565,16 @@ var OwneySDK = class {
|
|
|
4318
4565
|
const provider = this.requireConnectedProvider();
|
|
4319
4566
|
const srcChain = VIEM_CHAIN2[quote.src.chainId];
|
|
4320
4567
|
const dstChain = VIEM_CHAIN2[quote.dst.chainId];
|
|
4321
|
-
const wallet = (0,
|
|
4568
|
+
const wallet = (0, import_viem9.createWalletClient)({
|
|
4322
4569
|
account: state.walletAddress,
|
|
4323
4570
|
chain: srcChain,
|
|
4324
|
-
transport: (0,
|
|
4571
|
+
transport: (0, import_viem9.custom)(provider)
|
|
4325
4572
|
});
|
|
4326
|
-
const srcPublic = (0,
|
|
4573
|
+
const srcPublic = (0, import_viem9.createPublicClient)({
|
|
4327
4574
|
chain: srcChain,
|
|
4328
4575
|
transport: swapReadTransport(quote.src.chainId, this.zyfaiRpcUrls)
|
|
4329
4576
|
});
|
|
4330
|
-
const dstPublic = (0,
|
|
4577
|
+
const dstPublic = (0, import_viem9.createPublicClient)({
|
|
4331
4578
|
chain: dstChain,
|
|
4332
4579
|
transport: swapReadTransport(quote.dst.chainId, this.zyfaiRpcUrls)
|
|
4333
4580
|
});
|
|
@@ -4344,7 +4591,7 @@ var OwneySDK = class {
|
|
|
4344
4591
|
}
|
|
4345
4592
|
return dstPublic.readContract({
|
|
4346
4593
|
address: dst,
|
|
4347
|
-
abi:
|
|
4594
|
+
abi: import_viem9.erc20Abi,
|
|
4348
4595
|
functionName: "balanceOf",
|
|
4349
4596
|
args: [state.walletAddress]
|
|
4350
4597
|
});
|
|
@@ -4412,6 +4659,41 @@ var OwneySDK = class {
|
|
|
4412
4659
|
return hash;
|
|
4413
4660
|
},
|
|
4414
4661
|
ensureChain: (chainId) => this.ensureSwapChain(chainId),
|
|
4662
|
+
/**
|
|
4663
|
+
* Sign the approval instead of paying for it. Null means the token
|
|
4664
|
+
* cannot be permitted (WETH has no `permit`) or its domain could not be
|
|
4665
|
+
* verified, and the executor sends an ordinary approval instead.
|
|
4666
|
+
*
|
|
4667
|
+
* Reads go through srcPublic like every other read here — the domain is
|
|
4668
|
+
* chain-scoped, and reading a nonce off whatever chain the wallet
|
|
4669
|
+
* happens to sit on would sign a permit the token rejects.
|
|
4670
|
+
*/
|
|
4671
|
+
buildPermit: (spender, amount) => buildErc2612Permit(
|
|
4672
|
+
{
|
|
4673
|
+
readContract: (args) => srcPublic.readContract(args)
|
|
4674
|
+
},
|
|
4675
|
+
{
|
|
4676
|
+
token: quote.src.address,
|
|
4677
|
+
owner: state.walletAddress,
|
|
4678
|
+
spender,
|
|
4679
|
+
value: amount,
|
|
4680
|
+
chainId: quote.src.chainId,
|
|
4681
|
+
signTypedData: (payload) => wallet.signTypedData({
|
|
4682
|
+
account: state.walletAddress,
|
|
4683
|
+
...payload
|
|
4684
|
+
})
|
|
4685
|
+
}
|
|
4686
|
+
),
|
|
4687
|
+
/**
|
|
4688
|
+
* Same-chain polling, with the chain bound in. That product keys its
|
|
4689
|
+
* orders per chain; asking the Fusion+ endpoint for one answers 404,
|
|
4690
|
+
* which would read as an order that vanished mid-swap.
|
|
4691
|
+
*/
|
|
4692
|
+
sameChainRunner: {
|
|
4693
|
+
orderStatus: (h) => this.swapApi().orderStatus(h, quote.src.chainId),
|
|
4694
|
+
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
4695
|
+
now: () => Date.now()
|
|
4696
|
+
},
|
|
4415
4697
|
runner: {
|
|
4416
4698
|
readyForSecrets: (h) => this.swapApi().readyForSecrets(h),
|
|
4417
4699
|
submitSecret: (h, secret) => this.swapApi().submitSecret(h, secret),
|
|
@@ -4557,13 +4839,13 @@ var OwneySDK = class {
|
|
|
4557
4839
|
);
|
|
4558
4840
|
}
|
|
4559
4841
|
const srcChain = VIEM_CHAIN2[options.from.chainId];
|
|
4560
|
-
const srcPublic = (0,
|
|
4842
|
+
const srcPublic = (0, import_viem9.createPublicClient)({
|
|
4561
4843
|
chain: srcChain,
|
|
4562
4844
|
transport: swapReadTransport(options.from.chainId, this.zyfaiRpcUrls)
|
|
4563
4845
|
});
|
|
4564
4846
|
const readWalletBalance = () => srcPublic.readContract({
|
|
4565
4847
|
address: asset.address,
|
|
4566
|
-
abi:
|
|
4848
|
+
abi: import_viem9.erc20Abi,
|
|
4567
4849
|
functionName: "balanceOf",
|
|
4568
4850
|
args: [state.walletAddress]
|
|
4569
4851
|
});
|
|
@@ -4575,7 +4857,7 @@ var OwneySDK = class {
|
|
|
4575
4857
|
options.onSwapProgress?.("withdrawing");
|
|
4576
4858
|
const withdraw = await this.withdraw({
|
|
4577
4859
|
asset: options.from.symbol,
|
|
4578
|
-
...options.amount === void 0 ? {} : { amount: (0,
|
|
4860
|
+
...options.amount === void 0 ? {} : { amount: (0, import_viem9.parseUnits)(options.amount, asset.decimals).toString() },
|
|
4579
4861
|
...options.agentId ? { agentId: options.agentId } : {}
|
|
4580
4862
|
});
|
|
4581
4863
|
const arrived = await awaitWithdrawalArrival({
|
|
@@ -5211,10 +5493,10 @@ var OwneySDK = class {
|
|
|
5211
5493
|
);
|
|
5212
5494
|
}
|
|
5213
5495
|
const provider = this.requireConnectedProvider();
|
|
5214
|
-
const wallet = (0,
|
|
5496
|
+
const wallet = (0, import_viem9.createWalletClient)({
|
|
5215
5497
|
account: state.walletAddress,
|
|
5216
5498
|
chain: VIEM_CHAIN2[chainId],
|
|
5217
|
-
transport: (0,
|
|
5499
|
+
transport: (0, import_viem9.custom)(provider)
|
|
5218
5500
|
});
|
|
5219
5501
|
const hash = await wallet.writeContract({
|
|
5220
5502
|
address: token,
|
|
@@ -5224,9 +5506,9 @@ var OwneySDK = class {
|
|
|
5224
5506
|
account: state.walletAddress,
|
|
5225
5507
|
chain: VIEM_CHAIN2[chainId]
|
|
5226
5508
|
});
|
|
5227
|
-
const publicClient = (0,
|
|
5509
|
+
const publicClient = (0, import_viem9.createPublicClient)({
|
|
5228
5510
|
chain: VIEM_CHAIN2[chainId],
|
|
5229
|
-
transport: (0,
|
|
5511
|
+
transport: (0, import_viem9.custom)(provider)
|
|
5230
5512
|
});
|
|
5231
5513
|
const receipt = await publicClient.waitForTransactionReceipt({
|
|
5232
5514
|
hash,
|
|
@@ -5352,7 +5634,7 @@ var OwneySDK = class {
|
|
|
5352
5634
|
};
|
|
5353
5635
|
|
|
5354
5636
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
5355
|
-
var
|
|
5637
|
+
var import_viem10 = require("viem");
|
|
5356
5638
|
var import_siwe = require("siwe");
|
|
5357
5639
|
var import_sdk2 = require("@zyfai/sdk");
|
|
5358
5640
|
|
|
@@ -5479,7 +5761,7 @@ function buildSIWXConfig(deps) {
|
|
|
5479
5761
|
issuedAt,
|
|
5480
5762
|
toString() {
|
|
5481
5763
|
return new import_siwe.SiweMessage({
|
|
5482
|
-
address: (0,
|
|
5764
|
+
address: (0, import_viem10.getAddress)(accountAddress),
|
|
5483
5765
|
chainId: numericChainId(chainId),
|
|
5484
5766
|
domain,
|
|
5485
5767
|
uri,
|
package/dist/index.d.cts
CHANGED
|
@@ -13,11 +13,16 @@ import { SIWXConfig } from '@reown/appkit-controllers';
|
|
|
13
13
|
* `classic` — same chain. One atomic transaction: it either completes or
|
|
14
14
|
* nothing moved.
|
|
15
15
|
*
|
|
16
|
+
* `fusion` — same chain, filled by a resolver against a signed intent. No
|
|
17
|
+
* transaction from the user, so no native gas: that is the entire reason it
|
|
18
|
+
* exists. The trade is certainty — the order sits in an auction and can expire
|
|
19
|
+
* unfilled, where a classic swap resolves immediately either way.
|
|
20
|
+
*
|
|
16
21
|
* `fusion-plus` — crossing chains. The user's funds sit in an escrow while a
|
|
17
22
|
* resolver fills the other side, so the order has a lifecycle and can end in
|
|
18
23
|
* `expired` → `refunding` → `refunded` without ever depositing.
|
|
19
24
|
*/
|
|
20
|
-
type SwapRail = "classic" | "fusion-plus";
|
|
25
|
+
type SwapRail = "classic" | "fusion" | "fusion-plus";
|
|
21
26
|
type SwapTokenInfo = {
|
|
22
27
|
readonly symbol: string;
|
|
23
28
|
readonly address: string;
|
|
@@ -110,6 +115,17 @@ type SwapQuote = {
|
|
|
110
115
|
* calldata instead.
|
|
111
116
|
*/
|
|
112
117
|
spender?: string;
|
|
118
|
+
/**
|
|
119
|
+
* Cross-chain only. True when the routing API will accept an EIP-2612 permit
|
|
120
|
+
* on the order, so the approval is performed by the resolver inside the fill
|
|
121
|
+
* and the user needs no native ETH. False or absent means send an approval
|
|
122
|
+
* transaction, which is what every swap did before ROUT-242.
|
|
123
|
+
*
|
|
124
|
+
* It has to arrive on the QUOTE rather than being tried and recovered from:
|
|
125
|
+
* the client decides whether to spend the user's gas before the order it
|
|
126
|
+
* would be attached to exists.
|
|
127
|
+
*/
|
|
128
|
+
permitSupported?: boolean;
|
|
113
129
|
/**
|
|
114
130
|
* True only for a cross-chain swap FROM native ETH, which needs an on-chain
|
|
115
131
|
* order creation carrying the full amount as msg.value. The user's funds
|
|
@@ -155,7 +171,7 @@ type SwapQuote = {
|
|
|
155
171
|
* `refunding` is the window the returning-funds screen renders: the order has
|
|
156
172
|
* failed and the money is on its way back, but is not back yet.
|
|
157
173
|
*/
|
|
158
|
-
type SwapOrderStatus = "pending" | "executed" | "expired" | "cancelled" | "refunding" | "refunded" | "unpublished";
|
|
174
|
+
type SwapOrderStatus = "pending" | "executed" | "expired" | "cancelled" | "refunding" | "refunded" | "unpublished" | "filled" | "partially-filled" | "false-predicate" | "not-enough-balance-or-allowance" | "wrong-permit" | "invalid-signature";
|
|
159
175
|
/**
|
|
160
176
|
* Stage reported to the UI while a swap runs, in either direction.
|
|
161
177
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -13,11 +13,16 @@ import { SIWXConfig } from '@reown/appkit-controllers';
|
|
|
13
13
|
* `classic` — same chain. One atomic transaction: it either completes or
|
|
14
14
|
* nothing moved.
|
|
15
15
|
*
|
|
16
|
+
* `fusion` — same chain, filled by a resolver against a signed intent. No
|
|
17
|
+
* transaction from the user, so no native gas: that is the entire reason it
|
|
18
|
+
* exists. The trade is certainty — the order sits in an auction and can expire
|
|
19
|
+
* unfilled, where a classic swap resolves immediately either way.
|
|
20
|
+
*
|
|
16
21
|
* `fusion-plus` — crossing chains. The user's funds sit in an escrow while a
|
|
17
22
|
* resolver fills the other side, so the order has a lifecycle and can end in
|
|
18
23
|
* `expired` → `refunding` → `refunded` without ever depositing.
|
|
19
24
|
*/
|
|
20
|
-
type SwapRail = "classic" | "fusion-plus";
|
|
25
|
+
type SwapRail = "classic" | "fusion" | "fusion-plus";
|
|
21
26
|
type SwapTokenInfo = {
|
|
22
27
|
readonly symbol: string;
|
|
23
28
|
readonly address: string;
|
|
@@ -110,6 +115,17 @@ type SwapQuote = {
|
|
|
110
115
|
* calldata instead.
|
|
111
116
|
*/
|
|
112
117
|
spender?: string;
|
|
118
|
+
/**
|
|
119
|
+
* Cross-chain only. True when the routing API will accept an EIP-2612 permit
|
|
120
|
+
* on the order, so the approval is performed by the resolver inside the fill
|
|
121
|
+
* and the user needs no native ETH. False or absent means send an approval
|
|
122
|
+
* transaction, which is what every swap did before ROUT-242.
|
|
123
|
+
*
|
|
124
|
+
* It has to arrive on the QUOTE rather than being tried and recovered from:
|
|
125
|
+
* the client decides whether to spend the user's gas before the order it
|
|
126
|
+
* would be attached to exists.
|
|
127
|
+
*/
|
|
128
|
+
permitSupported?: boolean;
|
|
113
129
|
/**
|
|
114
130
|
* True only for a cross-chain swap FROM native ETH, which needs an on-chain
|
|
115
131
|
* order creation carrying the full amount as msg.value. The user's funds
|
|
@@ -155,7 +171,7 @@ type SwapQuote = {
|
|
|
155
171
|
* `refunding` is the window the returning-funds screen renders: the order has
|
|
156
172
|
* failed and the money is on its way back, but is not back yet.
|
|
157
173
|
*/
|
|
158
|
-
type SwapOrderStatus = "pending" | "executed" | "expired" | "cancelled" | "refunding" | "refunded" | "unpublished";
|
|
174
|
+
type SwapOrderStatus = "pending" | "executed" | "expired" | "cancelled" | "refunding" | "refunded" | "unpublished" | "filled" | "partially-filled" | "false-predicate" | "not-enough-balance-or-allowance" | "wrong-permit" | "invalid-signature";
|
|
159
175
|
/**
|
|
160
176
|
* Stage reported to the UI while a swap runs, in either direction.
|
|
161
177
|
*
|
package/dist/index.js
CHANGED
|
@@ -2174,7 +2174,21 @@ var CHAIN_NAMES = {
|
|
|
2174
2174
|
function chainName(chainId) {
|
|
2175
2175
|
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
2176
2176
|
}
|
|
2177
|
-
|
|
2177
|
+
var SETTLE_TIMEOUT_MS = 8e3;
|
|
2178
|
+
var POLL_INTERVAL_MS = 200;
|
|
2179
|
+
var wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
2180
|
+
async function readChainId(pub) {
|
|
2181
|
+
try {
|
|
2182
|
+
return await pub.getChainId();
|
|
2183
|
+
} catch {
|
|
2184
|
+
return void 0;
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
async function ensureWalletOnChain(pub, wallet, expected, options = {}) {
|
|
2188
|
+
const {
|
|
2189
|
+
settleTimeoutMs = SETTLE_TIMEOUT_MS,
|
|
2190
|
+
pollIntervalMs = POLL_INTERVAL_MS
|
|
2191
|
+
} = options;
|
|
2178
2192
|
const actual = await pub.getChainId();
|
|
2179
2193
|
if (actual === expected) return;
|
|
2180
2194
|
try {
|
|
@@ -2190,14 +2204,18 @@ async function ensureWalletOnChain(pub, wallet, expected) {
|
|
|
2190
2204
|
}
|
|
2191
2205
|
);
|
|
2192
2206
|
}
|
|
2193
|
-
const
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2207
|
+
const deadline = Date.now() + settleTimeoutMs;
|
|
2208
|
+
let after = await readChainId(pub);
|
|
2209
|
+
while (after !== expected && Date.now() < deadline) {
|
|
2210
|
+
await wait(pollIntervalMs);
|
|
2211
|
+
after = await readChainId(pub);
|
|
2212
|
+
}
|
|
2213
|
+
if (after === expected) return;
|
|
2214
|
+
throw new OwneyError(
|
|
2215
|
+
"CHAIN_MISMATCH",
|
|
2216
|
+
`Your wallet is still on ${chainName(after ?? actual)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2217
|
+
{ expectedChainId: expected, actualChainId: after ?? actual }
|
|
2218
|
+
);
|
|
2201
2219
|
}
|
|
2202
2220
|
|
|
2203
2221
|
// src/lib/swap/swap-api.ts
|
|
@@ -2314,9 +2332,10 @@ function createSwapApi(baseUrl, apiKey) {
|
|
|
2314
2332
|
dstSymbol: params.to.symbol,
|
|
2315
2333
|
amount: params.from.amount,
|
|
2316
2334
|
walletAddress: params.walletAddress,
|
|
2317
|
-
secretHashes: params.secretHashes,
|
|
2335
|
+
...params.secretHashes ? { secretHashes: params.secretHashes } : {},
|
|
2318
2336
|
...params.direction ? { direction: params.direction } : {},
|
|
2319
|
-
...params.receiver ? { receiver: params.receiver } : {}
|
|
2337
|
+
...params.receiver ? { receiver: params.receiver } : {},
|
|
2338
|
+
...params.permit ? { permit: params.permit } : {}
|
|
2320
2339
|
}
|
|
2321
2340
|
}),
|
|
2322
2341
|
submitOrder: (body) => request(baseUrl, apiKey, "/order", { method: "POST", body }),
|
|
@@ -2329,7 +2348,16 @@ function createSwapApi(baseUrl, apiKey) {
|
|
|
2329
2348
|
method: "POST",
|
|
2330
2349
|
body: { orderHash, secret }
|
|
2331
2350
|
}),
|
|
2332
|
-
|
|
2351
|
+
/**
|
|
2352
|
+
* @param chainId present only for a SAME-CHAIN order. That product keys its
|
|
2353
|
+
* orders per chain and the Fusion+ endpoint does not know them, so asking
|
|
2354
|
+
* without it answers 404 — which reads as an order that vanished.
|
|
2355
|
+
*/
|
|
2356
|
+
orderStatus: (orderHash, chainId) => request(
|
|
2357
|
+
baseUrl,
|
|
2358
|
+
apiKey,
|
|
2359
|
+
chainId === void 0 ? `/order/${orderHash}` : `/order/${orderHash}?chainId=${chainId}`
|
|
2360
|
+
),
|
|
2333
2361
|
readyForSecrets: (orderHash) => request(
|
|
2334
2362
|
baseUrl,
|
|
2335
2363
|
apiKey,
|
|
@@ -2472,7 +2500,12 @@ var SWAP_TERMINAL_STATUSES = [
|
|
|
2472
2500
|
"executed",
|
|
2473
2501
|
"expired",
|
|
2474
2502
|
"cancelled",
|
|
2475
|
-
"refunded"
|
|
2503
|
+
"refunded",
|
|
2504
|
+
"filled",
|
|
2505
|
+
"false-predicate",
|
|
2506
|
+
"not-enough-balance-or-allowance",
|
|
2507
|
+
"wrong-permit",
|
|
2508
|
+
"invalid-signature"
|
|
2476
2509
|
];
|
|
2477
2510
|
var isSwapTerminal = (status) => SWAP_TERMINAL_STATUSES.includes(status);
|
|
2478
2511
|
|
|
@@ -2550,6 +2583,71 @@ async function runFusionOrder(deps, options) {
|
|
|
2550
2583
|
}
|
|
2551
2584
|
}
|
|
2552
2585
|
|
|
2586
|
+
// src/lib/swap/swap.same-chain-runner.ts
|
|
2587
|
+
var DEFAULT_POLL_MS2 = 5e3;
|
|
2588
|
+
var MAX_BACKOFF_MS2 = 3e4;
|
|
2589
|
+
var backoffFor2 = (failures, base3) => Math.min(base3 * 2 ** Math.min(failures, 5), MAX_BACKOFF_MS2);
|
|
2590
|
+
var DEFAULT_TIMEOUT_MS2 = 3 * 60 * 1e3;
|
|
2591
|
+
function explain(status) {
|
|
2592
|
+
switch (status) {
|
|
2593
|
+
case "expired":
|
|
2594
|
+
return "No one filled the swap in time, so nothing was exchanged. Your funds never left your wallet.";
|
|
2595
|
+
case "cancelled":
|
|
2596
|
+
return "The swap was cancelled before anyone filled it. Your funds never left your wallet.";
|
|
2597
|
+
case "not-enough-balance-or-allowance":
|
|
2598
|
+
return "The swap could not be filled because the balance or approval had changed since it was signed. Nothing was exchanged.";
|
|
2599
|
+
case "wrong-permit":
|
|
2600
|
+
return "The approval signed with this swap was not accepted. Nothing was exchanged \u2014 try again, approving in your wallet if you are asked to.";
|
|
2601
|
+
case "invalid-signature":
|
|
2602
|
+
return "The swap signature was rejected. Nothing was exchanged.";
|
|
2603
|
+
case "false-predicate":
|
|
2604
|
+
return "The swap's conditions no longer held when it was filled. Nothing was exchanged.";
|
|
2605
|
+
default:
|
|
2606
|
+
return "The swap did not complete. Nothing was exchanged.";
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
async function runSameChainOrder(deps, options) {
|
|
2610
|
+
const {
|
|
2611
|
+
orderHash,
|
|
2612
|
+
onStage,
|
|
2613
|
+
pollIntervalMs = DEFAULT_POLL_MS2,
|
|
2614
|
+
timeoutMs = DEFAULT_TIMEOUT_MS2
|
|
2615
|
+
} = options;
|
|
2616
|
+
const deadline = deps.now() + timeoutMs;
|
|
2617
|
+
let failures = 0;
|
|
2618
|
+
onStage?.("swapping");
|
|
2619
|
+
for (; ; ) {
|
|
2620
|
+
if (deps.now() >= deadline) {
|
|
2621
|
+
throw new OwneyError(
|
|
2622
|
+
"SWAP_ORDER_EXPIRED",
|
|
2623
|
+
"No one filled the swap in time. Nothing was exchanged and your funds are still in your wallet.",
|
|
2624
|
+
{ orderHash }
|
|
2625
|
+
);
|
|
2626
|
+
}
|
|
2627
|
+
let status;
|
|
2628
|
+
try {
|
|
2629
|
+
({ status } = await deps.orderStatus(orderHash));
|
|
2630
|
+
failures = 0;
|
|
2631
|
+
} catch {
|
|
2632
|
+
failures += 1;
|
|
2633
|
+
await deps.sleep(backoffFor2(failures, pollIntervalMs));
|
|
2634
|
+
continue;
|
|
2635
|
+
}
|
|
2636
|
+
if (isSwapTerminal(status)) {
|
|
2637
|
+
if (status === "filled" || status === "executed") {
|
|
2638
|
+
onStage?.("swapped");
|
|
2639
|
+
return { status, filled: true };
|
|
2640
|
+
}
|
|
2641
|
+
throw new OwneyError(
|
|
2642
|
+
status === "cancelled" ? "SWAP_ORDER_CANCELLED" : "SWAP_ORDER_EXPIRED",
|
|
2643
|
+
explain(status),
|
|
2644
|
+
{ orderHash, status }
|
|
2645
|
+
);
|
|
2646
|
+
}
|
|
2647
|
+
await deps.sleep(pollIntervalMs);
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2553
2651
|
// src/lib/swap/swap.secret-store.ts
|
|
2554
2652
|
var KEY_PREFIX2 = "owney.swap.order";
|
|
2555
2653
|
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -2633,7 +2731,7 @@ async function executeSwap(deps, options) {
|
|
|
2633
2731
|
});
|
|
2634
2732
|
const before = await deps.readTargetBalance();
|
|
2635
2733
|
debugLog("owney-sdk", "swap: target balance before", before.toString());
|
|
2636
|
-
const result = quote.rail === "classic" ? await runClassic(deps, options) : await runFusion(deps, options, walletAddress);
|
|
2734
|
+
const result = quote.rail === "classic" ? await runClassic(deps, options) : quote.rail === "fusion" ? await runSameChainFusion(deps, options, walletAddress) : await runFusion(deps, options, walletAddress);
|
|
2637
2735
|
const after = await deps.readTargetBalance();
|
|
2638
2736
|
const received = after - before;
|
|
2639
2737
|
debugLog("owney-sdk", "swap: target balance after", {
|
|
@@ -2704,25 +2802,75 @@ async function runClassic(deps, options) {
|
|
|
2704
2802
|
onStage?.("swapped");
|
|
2705
2803
|
return { txHash };
|
|
2706
2804
|
}
|
|
2707
|
-
async function
|
|
2805
|
+
async function runSameChainFusion(deps, options, walletAddress) {
|
|
2708
2806
|
const { quote, direction, onStage } = options;
|
|
2709
2807
|
const isNativeSource = quote.src.address.toLowerCase().startsWith("0xeeee");
|
|
2710
2808
|
const amount = isNativeSource ? BigInt(quote.src.amount) : await affordableAmount(deps, BigInt(quote.src.amount));
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2809
|
+
const permit = await authoriseSpend(deps, options, amount, isNativeSource);
|
|
2810
|
+
onStage?.("quoting");
|
|
2811
|
+
debugLog("owney-sdk", "swap: building same-chain fusion order");
|
|
2812
|
+
const built = await deps.api.buildOrder({
|
|
2813
|
+
from: {
|
|
2814
|
+
chainId: quote.src.chainId,
|
|
2815
|
+
symbol: quote.src.symbol,
|
|
2816
|
+
amount: amount.toString()
|
|
2817
|
+
},
|
|
2818
|
+
to: { chainId: quote.dst.chainId, symbol: quote.dst.symbol },
|
|
2819
|
+
walletAddress,
|
|
2820
|
+
...direction ? { direction } : {},
|
|
2821
|
+
...permit ? { permit } : {}
|
|
2822
|
+
});
|
|
2823
|
+
debugLog("owney-sdk", "swap: same-chain order built", {
|
|
2824
|
+
orderHash: built.orderHash
|
|
2825
|
+
});
|
|
2826
|
+
onStage?.("signing");
|
|
2827
|
+
await deps.ensureChain(quote.src.chainId);
|
|
2828
|
+
const signature = await deps.signTypedData(built.typedData);
|
|
2829
|
+
debugLog("owney-sdk", "swap: signed, submitting same-chain order");
|
|
2830
|
+
await deps.api.submitOrder({
|
|
2831
|
+
// Without this the routing API hands the order to the Fusion+ relayer,
|
|
2832
|
+
// which does not know it and answers with an error that names nothing.
|
|
2833
|
+
rail: "fusion",
|
|
2834
|
+
srcChainId: quote.src.chainId,
|
|
2835
|
+
order: built.order,
|
|
2836
|
+
signature,
|
|
2837
|
+
quoteId: built.quoteId,
|
|
2838
|
+
...built.extension ? { extension: built.extension } : {}
|
|
2839
|
+
});
|
|
2840
|
+
await runSameChainOrder(deps.sameChainRunner ?? deps.runner, {
|
|
2841
|
+
orderHash: built.orderHash,
|
|
2842
|
+
...onStage ? { onStage } : {}
|
|
2843
|
+
});
|
|
2844
|
+
return { orderHash: built.orderHash };
|
|
2845
|
+
}
|
|
2846
|
+
async function authoriseSpend(deps, options, amount, isNativeSource) {
|
|
2847
|
+
const { quote, onStage } = options;
|
|
2848
|
+
if (!quote.spender || isNativeSource) return void 0;
|
|
2849
|
+
const current = await deps.readAllowance(quote.spender);
|
|
2850
|
+
debugLog("owney-sdk", "swap: fusion allowance", {
|
|
2851
|
+
spender: quote.spender,
|
|
2852
|
+
current: current.toString(),
|
|
2853
|
+
needed: amount.toString()
|
|
2854
|
+
});
|
|
2855
|
+
if (current >= amount) return void 0;
|
|
2856
|
+
onStage?.("approving");
|
|
2857
|
+
await deps.ensureChain(quote.src.chainId);
|
|
2858
|
+
const permit = quote.permitSupported ? await deps.buildPermit?.(quote.spender, MAX_UINT256) ?? void 0 : void 0;
|
|
2859
|
+
if (permit) {
|
|
2860
|
+
debugLog("owney-sdk", "swap: permitting limit order protocol", {
|
|
2861
|
+
bytes: (permit.length - 2) / 2
|
|
2718
2862
|
});
|
|
2719
|
-
|
|
2720
|
-
onStage?.("approving");
|
|
2721
|
-
await deps.ensureChain(quote.src.chainId);
|
|
2722
|
-
await deps.approve(quote.spender, MAX_UINT256);
|
|
2723
|
-
debugLog("owney-sdk", "swap: approved limit order protocol");
|
|
2724
|
-
}
|
|
2863
|
+
return permit;
|
|
2725
2864
|
}
|
|
2865
|
+
await deps.approve(quote.spender, MAX_UINT256);
|
|
2866
|
+
debugLog("owney-sdk", "swap: approved limit order protocol");
|
|
2867
|
+
return void 0;
|
|
2868
|
+
}
|
|
2869
|
+
async function runFusion(deps, options, walletAddress) {
|
|
2870
|
+
const { quote, direction, onStage } = options;
|
|
2871
|
+
const isNativeSource = quote.src.address.toLowerCase().startsWith("0xeeee");
|
|
2872
|
+
const amount = isNativeSource ? BigInt(quote.src.amount) : await affordableAmount(deps, BigInt(quote.src.amount));
|
|
2873
|
+
const permit = await authoriseSpend(deps, options, amount, isNativeSource);
|
|
2726
2874
|
const { secrets, secretHashes } = mintSecrets(quote.secretsCount ?? 1);
|
|
2727
2875
|
onStage?.("quoting");
|
|
2728
2876
|
debugLog("owney-sdk", "swap: building fusion order", {
|
|
@@ -2738,7 +2886,8 @@ async function runFusion(deps, options, walletAddress) {
|
|
|
2738
2886
|
to: { chainId: quote.dst.chainId, symbol: quote.dst.symbol },
|
|
2739
2887
|
walletAddress,
|
|
2740
2888
|
secretHashes,
|
|
2741
|
-
...direction ? { direction } : {}
|
|
2889
|
+
...direction ? { direction } : {},
|
|
2890
|
+
...permit ? { permit } : {}
|
|
2742
2891
|
});
|
|
2743
2892
|
saveOrder({
|
|
2744
2893
|
orderHash: built.orderHash,
|
|
@@ -2789,16 +2938,119 @@ async function runFusion(deps, options, walletAddress) {
|
|
|
2789
2938
|
return { orderHash: built.orderHash };
|
|
2790
2939
|
}
|
|
2791
2940
|
|
|
2941
|
+
// src/lib/swap/swap.permit.ts
|
|
2942
|
+
import {
|
|
2943
|
+
domainSeparator,
|
|
2944
|
+
encodeAbiParameters,
|
|
2945
|
+
parseAbi as parseAbi2,
|
|
2946
|
+
parseSignature
|
|
2947
|
+
} from "viem";
|
|
2948
|
+
var PERMIT_ABI = parseAbi2([
|
|
2949
|
+
"function nonces(address owner) view returns (uint256)",
|
|
2950
|
+
"function name() view returns (string)",
|
|
2951
|
+
"function version() view returns (string)",
|
|
2952
|
+
"function DOMAIN_SEPARATOR() view returns (bytes32)"
|
|
2953
|
+
]);
|
|
2954
|
+
var PERMIT_TYPES = {
|
|
2955
|
+
Permit: [
|
|
2956
|
+
{ name: "owner", type: "address" },
|
|
2957
|
+
{ name: "spender", type: "address" },
|
|
2958
|
+
{ name: "value", type: "uint256" },
|
|
2959
|
+
{ name: "nonce", type: "uint256" },
|
|
2960
|
+
{ name: "deadline", type: "uint256" }
|
|
2961
|
+
]
|
|
2962
|
+
};
|
|
2963
|
+
var VERSION_CANDIDATES = ["1", "2"];
|
|
2964
|
+
var PERMIT_TTL_SECONDS = 3600;
|
|
2965
|
+
async function tryRead(read) {
|
|
2966
|
+
try {
|
|
2967
|
+
return await read();
|
|
2968
|
+
} catch {
|
|
2969
|
+
return null;
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
function matchDomain(name, versions, chainId, token, onChainSeparator) {
|
|
2973
|
+
for (const version of versions) {
|
|
2974
|
+
const domain = { name, version, chainId, verifyingContract: token };
|
|
2975
|
+
if (domainSeparator({ domain }) === onChainSeparator) return domain;
|
|
2976
|
+
}
|
|
2977
|
+
return null;
|
|
2978
|
+
}
|
|
2979
|
+
async function buildErc2612Permit(reads, input) {
|
|
2980
|
+
const { token, owner, spender, value, chainId } = input;
|
|
2981
|
+
const call = (functionName, args) => reads.readContract({
|
|
2982
|
+
address: token,
|
|
2983
|
+
abi: PERMIT_ABI,
|
|
2984
|
+
functionName,
|
|
2985
|
+
...args ? { args } : {}
|
|
2986
|
+
});
|
|
2987
|
+
const nonce = await tryRead(() => call("nonces", [owner]));
|
|
2988
|
+
if (nonce === null) {
|
|
2989
|
+
debugLog("owney-sdk", "permit: token has no nonces(), using approval", {
|
|
2990
|
+
token
|
|
2991
|
+
});
|
|
2992
|
+
return null;
|
|
2993
|
+
}
|
|
2994
|
+
const separator = await tryRead(
|
|
2995
|
+
() => call("DOMAIN_SEPARATOR")
|
|
2996
|
+
);
|
|
2997
|
+
const name = await tryRead(() => call("name"));
|
|
2998
|
+
if (!separator || !name) {
|
|
2999
|
+
debugLog("owney-sdk", "permit: domain unverifiable, using approval", {
|
|
3000
|
+
token,
|
|
3001
|
+
hasSeparator: Boolean(separator),
|
|
3002
|
+
hasName: Boolean(name)
|
|
3003
|
+
});
|
|
3004
|
+
return null;
|
|
3005
|
+
}
|
|
3006
|
+
const declared = await tryRead(() => call("version"));
|
|
3007
|
+
const domain = matchDomain(
|
|
3008
|
+
name,
|
|
3009
|
+
declared ? [declared, ...VERSION_CANDIDATES] : VERSION_CANDIDATES,
|
|
3010
|
+
chainId,
|
|
3011
|
+
token,
|
|
3012
|
+
separator
|
|
3013
|
+
);
|
|
3014
|
+
if (!domain) {
|
|
3015
|
+
debugLog("owney-sdk", "permit: no domain matched, using approval", {
|
|
3016
|
+
token,
|
|
3017
|
+
declared
|
|
3018
|
+
});
|
|
3019
|
+
return null;
|
|
3020
|
+
}
|
|
3021
|
+
const deadline = BigInt(Math.floor((input.now?.() ?? Date.now()) / 1e3)) + BigInt(PERMIT_TTL_SECONDS);
|
|
3022
|
+
const signature = await input.signTypedData({
|
|
3023
|
+
domain,
|
|
3024
|
+
types: PERMIT_TYPES,
|
|
3025
|
+
primaryType: "Permit",
|
|
3026
|
+
message: { owner, spender, value, nonce, deadline }
|
|
3027
|
+
});
|
|
3028
|
+
const { r, s, v, yParity } = parseSignature(signature);
|
|
3029
|
+
const recoveryV = v ?? BigInt(yParity + 27);
|
|
3030
|
+
return encodeAbiParameters(
|
|
3031
|
+
[
|
|
3032
|
+
{ type: "address" },
|
|
3033
|
+
{ type: "address" },
|
|
3034
|
+
{ type: "uint256" },
|
|
3035
|
+
{ type: "uint256" },
|
|
3036
|
+
{ type: "uint8" },
|
|
3037
|
+
{ type: "bytes32" },
|
|
3038
|
+
{ type: "bytes32" }
|
|
3039
|
+
],
|
|
3040
|
+
[owner, spender, value, deadline, Number(recoveryV), r, s]
|
|
3041
|
+
);
|
|
3042
|
+
}
|
|
3043
|
+
|
|
2792
3044
|
// src/lib/swap/swap.arrival.ts
|
|
2793
|
-
var
|
|
2794
|
-
var
|
|
3045
|
+
var DEFAULT_TIMEOUT_MS3 = 18e4;
|
|
3046
|
+
var DEFAULT_POLL_MS3 = 4e3;
|
|
2795
3047
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
2796
3048
|
async function awaitWithdrawalArrival(options) {
|
|
2797
3049
|
const {
|
|
2798
3050
|
readBalance,
|
|
2799
3051
|
baseline,
|
|
2800
|
-
timeoutMs =
|
|
2801
|
-
pollMs =
|
|
3052
|
+
timeoutMs = DEFAULT_TIMEOUT_MS3,
|
|
3053
|
+
pollMs = DEFAULT_POLL_MS3
|
|
2802
3054
|
} = options;
|
|
2803
3055
|
const deadline = Date.now() + timeoutMs;
|
|
2804
3056
|
debugLog("owney-sdk", "withdraw: waiting for funds to land", {
|
|
@@ -4384,6 +4636,41 @@ var OwneySDK = class {
|
|
|
4384
4636
|
return hash;
|
|
4385
4637
|
},
|
|
4386
4638
|
ensureChain: (chainId) => this.ensureSwapChain(chainId),
|
|
4639
|
+
/**
|
|
4640
|
+
* Sign the approval instead of paying for it. Null means the token
|
|
4641
|
+
* cannot be permitted (WETH has no `permit`) or its domain could not be
|
|
4642
|
+
* verified, and the executor sends an ordinary approval instead.
|
|
4643
|
+
*
|
|
4644
|
+
* Reads go through srcPublic like every other read here — the domain is
|
|
4645
|
+
* chain-scoped, and reading a nonce off whatever chain the wallet
|
|
4646
|
+
* happens to sit on would sign a permit the token rejects.
|
|
4647
|
+
*/
|
|
4648
|
+
buildPermit: (spender, amount) => buildErc2612Permit(
|
|
4649
|
+
{
|
|
4650
|
+
readContract: (args) => srcPublic.readContract(args)
|
|
4651
|
+
},
|
|
4652
|
+
{
|
|
4653
|
+
token: quote.src.address,
|
|
4654
|
+
owner: state.walletAddress,
|
|
4655
|
+
spender,
|
|
4656
|
+
value: amount,
|
|
4657
|
+
chainId: quote.src.chainId,
|
|
4658
|
+
signTypedData: (payload) => wallet.signTypedData({
|
|
4659
|
+
account: state.walletAddress,
|
|
4660
|
+
...payload
|
|
4661
|
+
})
|
|
4662
|
+
}
|
|
4663
|
+
),
|
|
4664
|
+
/**
|
|
4665
|
+
* Same-chain polling, with the chain bound in. That product keys its
|
|
4666
|
+
* orders per chain; asking the Fusion+ endpoint for one answers 404,
|
|
4667
|
+
* which would read as an order that vanished mid-swap.
|
|
4668
|
+
*/
|
|
4669
|
+
sameChainRunner: {
|
|
4670
|
+
orderStatus: (h) => this.swapApi().orderStatus(h, quote.src.chainId),
|
|
4671
|
+
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
4672
|
+
now: () => Date.now()
|
|
4673
|
+
},
|
|
4387
4674
|
runner: {
|
|
4388
4675
|
readyForSecrets: (h) => this.swapApi().readyForSecrets(h),
|
|
4389
4676
|
submitSecret: (h, secret) => this.swapApi().submitSecret(h, secret),
|