@owney/sdk 0.7.24-beta.0 → 0.7.25-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1070 -164
- package/dist/index.d.cts +319 -2
- package/dist/index.d.ts +319 -2
- package/dist/index.js +1052 -146
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -27,6 +27,7 @@ __export(index_exports, {
|
|
|
27
27
|
OwneyError: () => OwneyError,
|
|
28
28
|
OwneySDK: () => OwneySDK,
|
|
29
29
|
createOwneySIWX: () => createOwneySIWX,
|
|
30
|
+
listPendingSwaps: () => listOrders,
|
|
30
31
|
setOwneyDebug: () => setOwneyDebug
|
|
31
32
|
});
|
|
32
33
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -2198,6 +2199,653 @@ async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL) {
|
|
|
2198
2199
|
return json.data;
|
|
2199
2200
|
}
|
|
2200
2201
|
|
|
2202
|
+
// src/lib/chain-guard.ts
|
|
2203
|
+
var CHAIN_NAMES = {
|
|
2204
|
+
1: "Ethereum",
|
|
2205
|
+
8453: "Base",
|
|
2206
|
+
42161: "Arbitrum"
|
|
2207
|
+
};
|
|
2208
|
+
function chainName(chainId) {
|
|
2209
|
+
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
2210
|
+
}
|
|
2211
|
+
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
2212
|
+
const actual = await pub.getChainId();
|
|
2213
|
+
if (actual === expected) return;
|
|
2214
|
+
try {
|
|
2215
|
+
await wallet.switchChain({ id: expected });
|
|
2216
|
+
} catch (error) {
|
|
2217
|
+
throw new OwneyError(
|
|
2218
|
+
"CHAIN_MISMATCH",
|
|
2219
|
+
`Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2220
|
+
{
|
|
2221
|
+
expectedChainId: expected,
|
|
2222
|
+
actualChainId: actual,
|
|
2223
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2224
|
+
}
|
|
2225
|
+
);
|
|
2226
|
+
}
|
|
2227
|
+
const after = await pub.getChainId();
|
|
2228
|
+
if (after !== expected) {
|
|
2229
|
+
throw new OwneyError(
|
|
2230
|
+
"CHAIN_MISMATCH",
|
|
2231
|
+
`Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2232
|
+
{ expectedChainId: expected, actualChainId: after }
|
|
2233
|
+
);
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
// src/lib/swap/swap-api.ts
|
|
2238
|
+
async function request(baseUrl, apiKey, path, init) {
|
|
2239
|
+
const url = `${baseUrl}/api/v1/swap${path}`;
|
|
2240
|
+
const res = await fetch(url, {
|
|
2241
|
+
method: init?.method ?? "GET",
|
|
2242
|
+
headers: {
|
|
2243
|
+
"Content-Type": "application/json",
|
|
2244
|
+
"x-owney-api-key": apiKey
|
|
2245
|
+
},
|
|
2246
|
+
...init ? { body: JSON.stringify(init.body) } : {}
|
|
2247
|
+
});
|
|
2248
|
+
if (!res.ok) {
|
|
2249
|
+
const text = await res.text().catch(() => "");
|
|
2250
|
+
if (res.status === 429) {
|
|
2251
|
+
throw new OwneyError(
|
|
2252
|
+
"SWAP_RATE_LIMITED",
|
|
2253
|
+
"Swap provider is rate limiting, retry shortly",
|
|
2254
|
+
{ statusCode: res.status }
|
|
2255
|
+
);
|
|
2256
|
+
}
|
|
2257
|
+
if (res.status === 403) {
|
|
2258
|
+
throw new OwneyError(
|
|
2259
|
+
"SWAP_DISABLED",
|
|
2260
|
+
"Swap is not enabled for this organization",
|
|
2261
|
+
{ statusCode: res.status }
|
|
2262
|
+
);
|
|
2263
|
+
}
|
|
2264
|
+
throw new OwneyError(
|
|
2265
|
+
"SWAP_REQUEST_FAILED",
|
|
2266
|
+
`Swap API error ${res.status}: ${text}`,
|
|
2267
|
+
{ statusCode: res.status, responseBody: text }
|
|
2268
|
+
);
|
|
2269
|
+
}
|
|
2270
|
+
const json = await res.json();
|
|
2271
|
+
if (!json.success) {
|
|
2272
|
+
throw new OwneyError(
|
|
2273
|
+
"SWAP_REQUEST_FAILED",
|
|
2274
|
+
`Swap API request failed: ${json.message ?? "unknown error"}`,
|
|
2275
|
+
{ message: json.message }
|
|
2276
|
+
);
|
|
2277
|
+
}
|
|
2278
|
+
return json.data;
|
|
2279
|
+
}
|
|
2280
|
+
function createSwapApi(baseUrl, apiKey) {
|
|
2281
|
+
return {
|
|
2282
|
+
/** Source assets the user may pay with, and each chain's deposit targets. */
|
|
2283
|
+
listTokens: () => request(baseUrl, apiKey, "/tokens"),
|
|
2284
|
+
/**
|
|
2285
|
+
* `walletAddress` is required even though the routing API could not infer
|
|
2286
|
+
* it: the Fusion+ quoter binds a quote to whoever will sign the order and
|
|
2287
|
+
* rejects the request without it.
|
|
2288
|
+
*/
|
|
2289
|
+
quote: (params) => request(baseUrl, apiKey, "/quote", {
|
|
2290
|
+
method: "POST",
|
|
2291
|
+
body: {
|
|
2292
|
+
srcChainId: params.from.chainId,
|
|
2293
|
+
srcSymbol: params.from.symbol,
|
|
2294
|
+
dstChainId: params.to.chainId,
|
|
2295
|
+
dstSymbol: params.to.symbol,
|
|
2296
|
+
amount: params.from.amount,
|
|
2297
|
+
walletAddress: params.walletAddress,
|
|
2298
|
+
...params.direction ? { direction: params.direction } : {}
|
|
2299
|
+
}
|
|
2300
|
+
}),
|
|
2301
|
+
/** Ready-to-send calldata for a same-chain swap. */
|
|
2302
|
+
swapTx: (params) => request(baseUrl, apiKey, "/tx", {
|
|
2303
|
+
method: "POST",
|
|
2304
|
+
body: {
|
|
2305
|
+
srcChainId: params.from.chainId,
|
|
2306
|
+
srcSymbol: params.from.symbol,
|
|
2307
|
+
dstChainId: params.to.chainId,
|
|
2308
|
+
dstSymbol: params.to.symbol,
|
|
2309
|
+
amount: params.from.amount,
|
|
2310
|
+
walletAddress: params.walletAddress,
|
|
2311
|
+
slippage: params.slippage,
|
|
2312
|
+
...params.direction ? { direction: params.direction } : {}
|
|
2313
|
+
}
|
|
2314
|
+
}),
|
|
2315
|
+
/**
|
|
2316
|
+
* Builds a Fusion+ order server-side and returns EIP-712 typed data.
|
|
2317
|
+
*
|
|
2318
|
+
* Only HASHES go over the wire. The preimages never leave the browser —
|
|
2319
|
+
* see swap.secrets.
|
|
2320
|
+
*/
|
|
2321
|
+
buildOrder: (params) => request(baseUrl, apiKey, "/order/build", {
|
|
2322
|
+
method: "POST",
|
|
2323
|
+
body: {
|
|
2324
|
+
srcChainId: params.from.chainId,
|
|
2325
|
+
srcSymbol: params.from.symbol,
|
|
2326
|
+
dstChainId: params.to.chainId,
|
|
2327
|
+
dstSymbol: params.to.symbol,
|
|
2328
|
+
amount: params.from.amount,
|
|
2329
|
+
walletAddress: params.walletAddress,
|
|
2330
|
+
secretHashes: params.secretHashes,
|
|
2331
|
+
...params.direction ? { direction: params.direction } : {},
|
|
2332
|
+
...params.receiver ? { receiver: params.receiver } : {}
|
|
2333
|
+
}
|
|
2334
|
+
}),
|
|
2335
|
+
submitOrder: (body) => request(baseUrl, apiKey, "/order", { method: "POST", body }),
|
|
2336
|
+
/**
|
|
2337
|
+
* Only call once `readyForSecrets` reports the escrow deployed. Publishing
|
|
2338
|
+
* earlier hands a resolver the preimage while the user's funds are locked
|
|
2339
|
+
* and nothing has been posted on the destination chain.
|
|
2340
|
+
*/
|
|
2341
|
+
submitSecret: (orderHash, secret) => request(baseUrl, apiKey, "/order/secret", {
|
|
2342
|
+
method: "POST",
|
|
2343
|
+
body: { orderHash, secret }
|
|
2344
|
+
}),
|
|
2345
|
+
orderStatus: (orderHash) => request(baseUrl, apiKey, `/order/${orderHash}`),
|
|
2346
|
+
readyForSecrets: (orderHash) => request(
|
|
2347
|
+
baseUrl,
|
|
2348
|
+
apiKey,
|
|
2349
|
+
`/order/${orderHash}/ready-for-secrets`
|
|
2350
|
+
)
|
|
2351
|
+
};
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
// src/lib/swap/swap.rpc.ts
|
|
2355
|
+
var import_viem2 = require("viem");
|
|
2356
|
+
var DEFAULT_RPC_URLS = {
|
|
2357
|
+
1: ["https://cloudflare-eth.com", "https://ethereum-rpc.publicnode.com"],
|
|
2358
|
+
8453: ["https://mainnet.base.org", "https://base-rpc.publicnode.com"],
|
|
2359
|
+
42161: [
|
|
2360
|
+
"https://arb1.arbitrum.io/rpc",
|
|
2361
|
+
"https://arbitrum-one-rpc.publicnode.com"
|
|
2362
|
+
]
|
|
2363
|
+
};
|
|
2364
|
+
function swapReadTransport(chainId, overrides) {
|
|
2365
|
+
const override = overrides?.[chainId];
|
|
2366
|
+
if (override) return (0, import_viem2.http)(override);
|
|
2367
|
+
const urls = DEFAULT_RPC_URLS[chainId];
|
|
2368
|
+
if (!urls || urls.length === 0) return (0, import_viem2.http)();
|
|
2369
|
+
return (0, import_viem2.fallback)(urls.map((url) => (0, import_viem2.http)(url)));
|
|
2370
|
+
}
|
|
2371
|
+
function receiptTimeoutMs(chainId) {
|
|
2372
|
+
return chainId === 1 ? 6e5 : 18e4;
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
// src/lib/permit2.ts
|
|
2376
|
+
var import_viem3 = require("viem");
|
|
2377
|
+
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2378
|
+
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2379
|
+
var ERC20_ALLOWANCE_ABI = [
|
|
2380
|
+
{
|
|
2381
|
+
type: "function",
|
|
2382
|
+
name: "allowance",
|
|
2383
|
+
stateMutability: "view",
|
|
2384
|
+
inputs: [
|
|
2385
|
+
{ name: "owner", type: "address" },
|
|
2386
|
+
{ name: "spender", type: "address" }
|
|
2387
|
+
],
|
|
2388
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
2389
|
+
},
|
|
2390
|
+
{
|
|
2391
|
+
type: "function",
|
|
2392
|
+
name: "approve",
|
|
2393
|
+
stateMutability: "nonpayable",
|
|
2394
|
+
inputs: [
|
|
2395
|
+
{ name: "spender", type: "address" },
|
|
2396
|
+
{ name: "amount", type: "uint256" }
|
|
2397
|
+
],
|
|
2398
|
+
outputs: [{ name: "", type: "bool" }]
|
|
2399
|
+
},
|
|
2400
|
+
{
|
|
2401
|
+
type: "function",
|
|
2402
|
+
name: "balanceOf",
|
|
2403
|
+
stateMutability: "view",
|
|
2404
|
+
inputs: [{ name: "account", type: "address" }],
|
|
2405
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
2406
|
+
}
|
|
2407
|
+
];
|
|
2408
|
+
function buildPermitTransferFromTypedData(input) {
|
|
2409
|
+
return {
|
|
2410
|
+
domain: {
|
|
2411
|
+
name: "Permit2",
|
|
2412
|
+
chainId: input.chainId,
|
|
2413
|
+
verifyingContract: PERMIT2_ADDRESS
|
|
2414
|
+
},
|
|
2415
|
+
types: {
|
|
2416
|
+
PermitTransferFrom: [
|
|
2417
|
+
{ name: "permitted", type: "TokenPermissions" },
|
|
2418
|
+
{ name: "spender", type: "address" },
|
|
2419
|
+
{ name: "nonce", type: "uint256" },
|
|
2420
|
+
{ name: "deadline", type: "uint256" }
|
|
2421
|
+
],
|
|
2422
|
+
TokenPermissions: [
|
|
2423
|
+
{ name: "token", type: "address" },
|
|
2424
|
+
{ name: "amount", type: "uint256" }
|
|
2425
|
+
]
|
|
2426
|
+
},
|
|
2427
|
+
primaryType: "PermitTransferFrom",
|
|
2428
|
+
message: input.message
|
|
2429
|
+
};
|
|
2430
|
+
}
|
|
2431
|
+
function randomPermit2Nonce() {
|
|
2432
|
+
const bytes = new Uint8Array(32);
|
|
2433
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
2434
|
+
return BigInt((0, import_viem3.bytesToHex)(bytes));
|
|
2435
|
+
}
|
|
2436
|
+
async function readPermit2Allowance(publicClient, token, owner) {
|
|
2437
|
+
return publicClient.readContract({
|
|
2438
|
+
address: token,
|
|
2439
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
2440
|
+
functionName: "allowance",
|
|
2441
|
+
args: [owner, PERMIT2_ADDRESS]
|
|
2442
|
+
});
|
|
2443
|
+
}
|
|
2444
|
+
async function readErc20Balance(publicClient, token, owner) {
|
|
2445
|
+
return publicClient.readContract({
|
|
2446
|
+
address: token,
|
|
2447
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
2448
|
+
functionName: "balanceOf",
|
|
2449
|
+
args: [owner]
|
|
2450
|
+
});
|
|
2451
|
+
}
|
|
2452
|
+
|
|
2453
|
+
// src/lib/swap/swap.secrets.ts
|
|
2454
|
+
var import_viem4 = require("viem");
|
|
2455
|
+
var SECRET_BYTES = 32;
|
|
2456
|
+
function randomBytes(length) {
|
|
2457
|
+
const bytes = new Uint8Array(length);
|
|
2458
|
+
const cryptoObj = typeof globalThis !== "undefined" ? globalThis.crypto : void 0;
|
|
2459
|
+
if (!cryptoObj?.getRandomValues) {
|
|
2460
|
+
throw new Error(
|
|
2461
|
+
"[owney-sdk] Secure randomness is unavailable, so a swap secret cannot be generated safely."
|
|
2462
|
+
);
|
|
2463
|
+
}
|
|
2464
|
+
cryptoObj.getRandomValues(bytes);
|
|
2465
|
+
return bytes;
|
|
2466
|
+
}
|
|
2467
|
+
function mintSecrets(count) {
|
|
2468
|
+
if (!Number.isInteger(count) || count < 1) {
|
|
2469
|
+
throw new Error(
|
|
2470
|
+
`[owney-sdk] A swap needs at least one secret, got ${String(count)}.`
|
|
2471
|
+
);
|
|
2472
|
+
}
|
|
2473
|
+
const secrets = [];
|
|
2474
|
+
const secretHashes = [];
|
|
2475
|
+
for (let i = 0; i < count; i++) {
|
|
2476
|
+
const secret = (0, import_viem4.toHex)(randomBytes(SECRET_BYTES));
|
|
2477
|
+
secrets.push(secret);
|
|
2478
|
+
secretHashes.push((0, import_viem4.keccak256)(secret));
|
|
2479
|
+
}
|
|
2480
|
+
return { secrets, secretHashes };
|
|
2481
|
+
}
|
|
2482
|
+
|
|
2483
|
+
// src/lib/swap/swap.types.ts
|
|
2484
|
+
var SWAP_TERMINAL_STATUSES = [
|
|
2485
|
+
"executed",
|
|
2486
|
+
"expired",
|
|
2487
|
+
"cancelled",
|
|
2488
|
+
"refunded"
|
|
2489
|
+
];
|
|
2490
|
+
var isSwapTerminal = (status) => SWAP_TERMINAL_STATUSES.includes(status);
|
|
2491
|
+
|
|
2492
|
+
// src/lib/swap/swap.order-runner.ts
|
|
2493
|
+
var DEFAULT_POLL_MS = 5e3;
|
|
2494
|
+
var MAX_BACKOFF_MS = 3e4;
|
|
2495
|
+
var backoffFor = (failures, base3) => Math.min(base3 * 2 ** Math.min(failures, 5), MAX_BACKOFF_MS);
|
|
2496
|
+
var DEFAULT_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
2497
|
+
async function runFusionOrder(deps, options) {
|
|
2498
|
+
const {
|
|
2499
|
+
orderHash,
|
|
2500
|
+
secrets,
|
|
2501
|
+
onStage,
|
|
2502
|
+
pollIntervalMs = DEFAULT_POLL_MS,
|
|
2503
|
+
timeoutMs = DEFAULT_TIMEOUT_MS
|
|
2504
|
+
} = options;
|
|
2505
|
+
const deadline = deps.now() + timeoutMs;
|
|
2506
|
+
let failures = 0;
|
|
2507
|
+
const published = /* @__PURE__ */ new Set();
|
|
2508
|
+
onStage?.("swapping");
|
|
2509
|
+
for (; ; ) {
|
|
2510
|
+
if (deps.now() >= deadline) {
|
|
2511
|
+
throw new OwneyError(
|
|
2512
|
+
"SWAP_REQUEST_FAILED",
|
|
2513
|
+
"Timed out waiting for the swap to settle. It may still complete \u2014 check the order status before retrying.",
|
|
2514
|
+
{ orderHash }
|
|
2515
|
+
);
|
|
2516
|
+
}
|
|
2517
|
+
let ready;
|
|
2518
|
+
try {
|
|
2519
|
+
ready = await deps.readyForSecrets(orderHash);
|
|
2520
|
+
} catch {
|
|
2521
|
+
ready = {};
|
|
2522
|
+
}
|
|
2523
|
+
for (const fill of ready.fills ?? []) {
|
|
2524
|
+
if (published.has(fill.idx)) continue;
|
|
2525
|
+
const secret = secrets[fill.idx];
|
|
2526
|
+
if (secret === void 0) {
|
|
2527
|
+
throw new OwneyError(
|
|
2528
|
+
"SWAP_REQUEST_FAILED",
|
|
2529
|
+
`Swap needs a secret for fill ${fill.idx} that this session does not have. The order will refund once its timelock expires.`,
|
|
2530
|
+
{ orderHash, fillIndex: fill.idx }
|
|
2531
|
+
);
|
|
2532
|
+
}
|
|
2533
|
+
try {
|
|
2534
|
+
await deps.submitSecret(orderHash, secret);
|
|
2535
|
+
published.add(fill.idx);
|
|
2536
|
+
} catch {
|
|
2537
|
+
failures += 1;
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
let status;
|
|
2541
|
+
try {
|
|
2542
|
+
({ status } = await deps.orderStatus(orderHash));
|
|
2543
|
+
failures = 0;
|
|
2544
|
+
} catch {
|
|
2545
|
+
failures += 1;
|
|
2546
|
+
await deps.sleep(backoffFor(failures, pollIntervalMs));
|
|
2547
|
+
continue;
|
|
2548
|
+
}
|
|
2549
|
+
if (status === "refunding") onStage?.("refunding");
|
|
2550
|
+
if (isSwapTerminal(status)) {
|
|
2551
|
+
if (status === "executed") {
|
|
2552
|
+
onStage?.("swapped");
|
|
2553
|
+
return { status, filled: true };
|
|
2554
|
+
}
|
|
2555
|
+
if (status === "refunded") onStage?.("refunded");
|
|
2556
|
+
throw new OwneyError(
|
|
2557
|
+
status === "refunded" ? "SWAP_ORDER_REFUNDED" : status === "cancelled" ? "SWAP_ORDER_CANCELLED" : "SWAP_ORDER_EXPIRED",
|
|
2558
|
+
status === "refunded" ? "The swap did not complete and your funds have been returned." : "The swap did not complete in time. Your funds will be returned once the timelock expires.",
|
|
2559
|
+
{ orderHash, status }
|
|
2560
|
+
);
|
|
2561
|
+
}
|
|
2562
|
+
await deps.sleep(pollIntervalMs);
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
// src/lib/swap/swap.secret-store.ts
|
|
2567
|
+
var KEY_PREFIX2 = "owney.swap.order";
|
|
2568
|
+
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
2569
|
+
var storage2 = () => {
|
|
2570
|
+
if (typeof window === "undefined") return null;
|
|
2571
|
+
try {
|
|
2572
|
+
return window.localStorage;
|
|
2573
|
+
} catch {
|
|
2574
|
+
return null;
|
|
2575
|
+
}
|
|
2576
|
+
};
|
|
2577
|
+
var keyFor = (orderHash) => `${KEY_PREFIX2}.${orderHash}`;
|
|
2578
|
+
function saveOrder(order) {
|
|
2579
|
+
const store = storage2();
|
|
2580
|
+
if (!store) return;
|
|
2581
|
+
try {
|
|
2582
|
+
store.setItem(keyFor(order.orderHash), JSON.stringify(order));
|
|
2583
|
+
} catch {
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
function clearOrder(orderHash) {
|
|
2587
|
+
const store = storage2();
|
|
2588
|
+
if (!store) return;
|
|
2589
|
+
try {
|
|
2590
|
+
store.removeItem(keyFor(orderHash));
|
|
2591
|
+
} catch {
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
function listOrders(now = Date.now()) {
|
|
2595
|
+
const store = storage2();
|
|
2596
|
+
if (!store) return [];
|
|
2597
|
+
const out = [];
|
|
2598
|
+
try {
|
|
2599
|
+
const keys = [];
|
|
2600
|
+
for (let i = 0; i < store.length; i++) {
|
|
2601
|
+
const key2 = store.key(i);
|
|
2602
|
+
if (key2?.startsWith(`${KEY_PREFIX2}.`)) keys.push(key2);
|
|
2603
|
+
}
|
|
2604
|
+
for (const key2 of keys) {
|
|
2605
|
+
const raw = store.getItem(key2);
|
|
2606
|
+
if (!raw) continue;
|
|
2607
|
+
try {
|
|
2608
|
+
const parsed = JSON.parse(raw);
|
|
2609
|
+
if (now - parsed.createdAt > MAX_AGE_MS) {
|
|
2610
|
+
store.removeItem(key2);
|
|
2611
|
+
continue;
|
|
2612
|
+
}
|
|
2613
|
+
if (Array.isArray(parsed.secrets) && parsed.secrets.length > 0) {
|
|
2614
|
+
out.push(parsed);
|
|
2615
|
+
}
|
|
2616
|
+
} catch {
|
|
2617
|
+
store.removeItem(key2);
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
} catch {
|
|
2621
|
+
return out;
|
|
2622
|
+
}
|
|
2623
|
+
return out.sort((a, b) => b.createdAt - a.createdAt);
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
// src/lib/swap/swap.executor.ts
|
|
2627
|
+
var DEFAULT_SLIPPAGE = 1;
|
|
2628
|
+
async function affordableAmount(deps, quoted) {
|
|
2629
|
+
const balance = await deps.readSourceBalance();
|
|
2630
|
+
if (balance >= quoted) return quoted;
|
|
2631
|
+
debugLog("owney-sdk", "swap: trimming to the current source balance", {
|
|
2632
|
+
quoted: quoted.toString(),
|
|
2633
|
+
balance: balance.toString(),
|
|
2634
|
+
short: (quoted - balance).toString()
|
|
2635
|
+
});
|
|
2636
|
+
return balance;
|
|
2637
|
+
}
|
|
2638
|
+
async function executeSwap(deps, options) {
|
|
2639
|
+
const { quote, walletAddress, onStage } = options;
|
|
2640
|
+
debugLog("owney-sdk", "swap: start", {
|
|
2641
|
+
rail: quote.rail,
|
|
2642
|
+
from: `${quote.src.amount} ${quote.src.symbol} on ${quote.src.chainId}`,
|
|
2643
|
+
to: `${quote.dst.symbol} on ${quote.dst.chainId}`,
|
|
2644
|
+
expected: quote.dst.amount,
|
|
2645
|
+
floor: quote.dstAmountMin
|
|
2646
|
+
});
|
|
2647
|
+
const before = await deps.readTargetBalance();
|
|
2648
|
+
debugLog("owney-sdk", "swap: target balance before", before.toString());
|
|
2649
|
+
const result = quote.rail === "classic" ? await runClassic(deps, options) : await runFusion(deps, options, walletAddress);
|
|
2650
|
+
const after = await deps.readTargetBalance();
|
|
2651
|
+
const received = after - before;
|
|
2652
|
+
debugLog("owney-sdk", "swap: target balance after", {
|
|
2653
|
+
after: after.toString(),
|
|
2654
|
+
received: received.toString()
|
|
2655
|
+
});
|
|
2656
|
+
if (received <= 0n) {
|
|
2657
|
+
throw new OwneyError(
|
|
2658
|
+
"SWAP_REQUEST_FAILED",
|
|
2659
|
+
"The swap completed but no funds arrived in the wallet. Check the transaction before retrying.",
|
|
2660
|
+
{ rail: quote.rail, ...result }
|
|
2661
|
+
);
|
|
2662
|
+
}
|
|
2663
|
+
return { received: received.toString(), ...result };
|
|
2664
|
+
}
|
|
2665
|
+
async function runClassic(deps, options) {
|
|
2666
|
+
const {
|
|
2667
|
+
quote,
|
|
2668
|
+
walletAddress,
|
|
2669
|
+
slippage = DEFAULT_SLIPPAGE,
|
|
2670
|
+
direction,
|
|
2671
|
+
onStage
|
|
2672
|
+
} = options;
|
|
2673
|
+
const isNativeSource = quote.src.address.toLowerCase().startsWith("0xeeee");
|
|
2674
|
+
const amount = isNativeSource ? BigInt(quote.src.amount) : await affordableAmount(deps, BigInt(quote.src.amount));
|
|
2675
|
+
onStage?.("quoting");
|
|
2676
|
+
debugLog("owney-sdk", "swap: fetching classic calldata");
|
|
2677
|
+
const { tx } = await deps.api.swapTx({
|
|
2678
|
+
from: {
|
|
2679
|
+
chainId: quote.src.chainId,
|
|
2680
|
+
symbol: quote.src.symbol,
|
|
2681
|
+
amount: amount.toString()
|
|
2682
|
+
},
|
|
2683
|
+
to: { chainId: quote.dst.chainId, symbol: quote.dst.symbol },
|
|
2684
|
+
walletAddress,
|
|
2685
|
+
slippage,
|
|
2686
|
+
...direction ? { direction } : {}
|
|
2687
|
+
});
|
|
2688
|
+
const isNative = BigInt(tx.value ?? "0") > 0n;
|
|
2689
|
+
if (!isNative) {
|
|
2690
|
+
const needed = amount;
|
|
2691
|
+
const current = await deps.readAllowance(tx.to);
|
|
2692
|
+
debugLog("owney-sdk", "swap: allowance", {
|
|
2693
|
+
spender: tx.to,
|
|
2694
|
+
current: current.toString(),
|
|
2695
|
+
needed: needed.toString()
|
|
2696
|
+
});
|
|
2697
|
+
if (current < needed) {
|
|
2698
|
+
onStage?.("approving");
|
|
2699
|
+
await deps.ensureChain(quote.src.chainId);
|
|
2700
|
+
await deps.approve(tx.to, MAX_UINT256);
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
onStage?.("signing");
|
|
2704
|
+
await deps.ensureChain(quote.src.chainId);
|
|
2705
|
+
debugLog("owney-sdk", "swap: sending classic swap tx", { to: tx.to });
|
|
2706
|
+
const txHash = await deps.sendTransaction({
|
|
2707
|
+
to: tx.to,
|
|
2708
|
+
data: tx.data,
|
|
2709
|
+
value: tx.value ?? "0"
|
|
2710
|
+
});
|
|
2711
|
+
onStage?.("swapped");
|
|
2712
|
+
return { txHash };
|
|
2713
|
+
}
|
|
2714
|
+
async function runFusion(deps, options, walletAddress) {
|
|
2715
|
+
const { quote, direction, onStage } = options;
|
|
2716
|
+
const isNativeSource = quote.src.address.toLowerCase().startsWith("0xeeee");
|
|
2717
|
+
const amount = isNativeSource ? BigInt(quote.src.amount) : await affordableAmount(deps, BigInt(quote.src.amount));
|
|
2718
|
+
if (quote.spender && !isNativeSource) {
|
|
2719
|
+
const needed = amount;
|
|
2720
|
+
const current = await deps.readAllowance(quote.spender);
|
|
2721
|
+
debugLog("owney-sdk", "swap: fusion allowance", {
|
|
2722
|
+
spender: quote.spender,
|
|
2723
|
+
current: current.toString(),
|
|
2724
|
+
needed: needed.toString()
|
|
2725
|
+
});
|
|
2726
|
+
if (current < needed) {
|
|
2727
|
+
onStage?.("approving");
|
|
2728
|
+
await deps.ensureChain(quote.src.chainId);
|
|
2729
|
+
await deps.approve(quote.spender, MAX_UINT256);
|
|
2730
|
+
debugLog("owney-sdk", "swap: approved limit order protocol");
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
const { secrets, secretHashes } = mintSecrets(quote.secretsCount ?? 1);
|
|
2734
|
+
onStage?.("quoting");
|
|
2735
|
+
debugLog("owney-sdk", "swap: building fusion order", {
|
|
2736
|
+
secrets: secretHashes.length
|
|
2737
|
+
});
|
|
2738
|
+
const built = await deps.api.buildOrder({
|
|
2739
|
+
from: {
|
|
2740
|
+
chainId: quote.src.chainId,
|
|
2741
|
+
symbol: quote.src.symbol,
|
|
2742
|
+
// The trimmed amount — the order is re-quoted at this size server-side.
|
|
2743
|
+
amount: amount.toString()
|
|
2744
|
+
},
|
|
2745
|
+
to: { chainId: quote.dst.chainId, symbol: quote.dst.symbol },
|
|
2746
|
+
walletAddress,
|
|
2747
|
+
secretHashes,
|
|
2748
|
+
...direction ? { direction } : {}
|
|
2749
|
+
});
|
|
2750
|
+
saveOrder({
|
|
2751
|
+
orderHash: built.orderHash,
|
|
2752
|
+
secrets,
|
|
2753
|
+
srcChainId: quote.src.chainId,
|
|
2754
|
+
srcSymbol: quote.src.symbol,
|
|
2755
|
+
dstChainId: quote.dst.chainId,
|
|
2756
|
+
dstSymbol: quote.dst.symbol,
|
|
2757
|
+
amount: amount.toString(),
|
|
2758
|
+
createdAt: Date.now()
|
|
2759
|
+
});
|
|
2760
|
+
debugLog("owney-sdk", "swap: order built", { orderHash: built.orderHash });
|
|
2761
|
+
onStage?.("signing");
|
|
2762
|
+
await deps.ensureChain(quote.src.chainId);
|
|
2763
|
+
debugLog("owney-sdk", "swap: awaiting signature in wallet", {
|
|
2764
|
+
signingOnChain: quote.src.chainId
|
|
2765
|
+
});
|
|
2766
|
+
const signature = await deps.signTypedData(built.typedData);
|
|
2767
|
+
debugLog("owney-sdk", "swap: signed, submitting to relayer");
|
|
2768
|
+
await deps.api.submitOrder({
|
|
2769
|
+
srcChainId: quote.src.chainId,
|
|
2770
|
+
// The ORDER STRUCT, not the typed-data envelope we just signed. Sending
|
|
2771
|
+
// the envelope here gets a bare 500 from the relayer.
|
|
2772
|
+
order: built.order,
|
|
2773
|
+
signature,
|
|
2774
|
+
quoteId: built.quoteId,
|
|
2775
|
+
// Single-fill orders must NOT carry secretHashes — the relayer rejects
|
|
2776
|
+
// them with SECRET_HASHES_NOT_REQUIRED. The one hash is already inside the
|
|
2777
|
+
// order's hashlock, so repeating it here is redundant, and only a
|
|
2778
|
+
// multi-fill order (a Merkle tree of hashes) needs them listed.
|
|
2779
|
+
...secretHashes.length > 1 ? { secretHashes } : {},
|
|
2780
|
+
...built.extension ? { extension: built.extension } : {}
|
|
2781
|
+
});
|
|
2782
|
+
debugLog("owney-sdk", "swap: order submitted, polling escrows");
|
|
2783
|
+
try {
|
|
2784
|
+
await runFusionOrder(deps.runner, {
|
|
2785
|
+
orderHash: built.orderHash,
|
|
2786
|
+
secrets,
|
|
2787
|
+
...onStage ? { onStage } : {}
|
|
2788
|
+
});
|
|
2789
|
+
} catch (error) {
|
|
2790
|
+
if (error instanceof OwneyError && (error.code === "SWAP_ORDER_REFUNDED" || error.code === "SWAP_ORDER_CANCELLED")) {
|
|
2791
|
+
clearOrder(built.orderHash);
|
|
2792
|
+
}
|
|
2793
|
+
throw error;
|
|
2794
|
+
}
|
|
2795
|
+
clearOrder(built.orderHash);
|
|
2796
|
+
return { orderHash: built.orderHash };
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2799
|
+
// src/lib/swap/swap.arrival.ts
|
|
2800
|
+
var DEFAULT_TIMEOUT_MS2 = 18e4;
|
|
2801
|
+
var DEFAULT_POLL_MS2 = 4e3;
|
|
2802
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
2803
|
+
async function awaitWithdrawalArrival(options) {
|
|
2804
|
+
const {
|
|
2805
|
+
readBalance,
|
|
2806
|
+
baseline,
|
|
2807
|
+
timeoutMs = DEFAULT_TIMEOUT_MS2,
|
|
2808
|
+
pollMs = DEFAULT_POLL_MS2
|
|
2809
|
+
} = options;
|
|
2810
|
+
const deadline = Date.now() + timeoutMs;
|
|
2811
|
+
debugLog("owney-sdk", "withdraw: waiting for funds to land", {
|
|
2812
|
+
baseline: baseline.toString(),
|
|
2813
|
+
timeoutMs
|
|
2814
|
+
});
|
|
2815
|
+
let lastError;
|
|
2816
|
+
for (; ; ) {
|
|
2817
|
+
try {
|
|
2818
|
+
const balance = await readBalance();
|
|
2819
|
+
if (balance > baseline) {
|
|
2820
|
+
const arrived = balance - baseline;
|
|
2821
|
+
debugLog("owney-sdk", "withdraw: funds landed", {
|
|
2822
|
+
arrived: arrived.toString()
|
|
2823
|
+
});
|
|
2824
|
+
return arrived;
|
|
2825
|
+
}
|
|
2826
|
+
} catch (error) {
|
|
2827
|
+
lastError = error;
|
|
2828
|
+
debugLog("owney-sdk", "withdraw: balance read failed, retrying", {
|
|
2829
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2830
|
+
});
|
|
2831
|
+
}
|
|
2832
|
+
if (Date.now() >= deadline) {
|
|
2833
|
+
throw new OwneyError(
|
|
2834
|
+
"WITHDRAW_ARRIVAL_TIMEOUT",
|
|
2835
|
+
"The withdrawal was accepted but the funds had not arrived in time to swap them. They are on their way to your wallet in the original asset.",
|
|
2836
|
+
{
|
|
2837
|
+
baseline: baseline.toString(),
|
|
2838
|
+
waitedMs: timeoutMs,
|
|
2839
|
+
...lastError ? {
|
|
2840
|
+
lastReadError: lastError instanceof Error ? lastError.message : String(lastError)
|
|
2841
|
+
} : {}
|
|
2842
|
+
}
|
|
2843
|
+
);
|
|
2844
|
+
}
|
|
2845
|
+
await sleep(pollMs);
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
|
|
2201
2849
|
// src/lib/health-report.ts
|
|
2202
2850
|
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2203
2851
|
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL2) {
|
|
@@ -2232,7 +2880,7 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
|
2232
2880
|
}
|
|
2233
2881
|
|
|
2234
2882
|
// src/lib/helpers/withdraw-helper.ts
|
|
2235
|
-
var
|
|
2883
|
+
var import_viem5 = require("viem");
|
|
2236
2884
|
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
2237
2885
|
const target = asset.toUpperCase();
|
|
2238
2886
|
return agents.map((agent) => {
|
|
@@ -2241,7 +2889,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
2241
2889
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
2242
2890
|
);
|
|
2243
2891
|
if (!tokenBalance) return { agent, balance: 0n };
|
|
2244
|
-
return { agent, balance: (0,
|
|
2892
|
+
return { agent, balance: (0, import_viem5.parseUnits)(tokenBalance.amount, decimals) };
|
|
2245
2893
|
});
|
|
2246
2894
|
}
|
|
2247
2895
|
function planProportionalShares(balances, requested, totalAvailable) {
|
|
@@ -2388,11 +3036,11 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
2388
3036
|
}
|
|
2389
3037
|
|
|
2390
3038
|
// src/client.ts
|
|
2391
|
-
var
|
|
3039
|
+
var import_viem8 = require("viem");
|
|
2392
3040
|
var import_chains2 = require("viem/chains");
|
|
2393
3041
|
|
|
2394
3042
|
// src/lib/transfer-auth.ts
|
|
2395
|
-
var
|
|
3043
|
+
var import_viem6 = require("viem");
|
|
2396
3044
|
var ERC20_META_ABI = [
|
|
2397
3045
|
{ type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
|
|
2398
3046
|
{ type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
|
|
@@ -2424,7 +3072,7 @@ async function readTokenMeta(publicClient, token) {
|
|
|
2424
3072
|
function randomAuthNonce() {
|
|
2425
3073
|
const bytes = new Uint8Array(32);
|
|
2426
3074
|
globalThis.crypto.getRandomValues(bytes);
|
|
2427
|
-
return (0,
|
|
3075
|
+
return (0, import_viem6.bytesToHex)(bytes);
|
|
2428
3076
|
}
|
|
2429
3077
|
|
|
2430
3078
|
// src/lib/sponsor-client.ts
|
|
@@ -2544,119 +3192,6 @@ async function getSponsorRelayerAddress(input) {
|
|
|
2544
3192
|
return parsed.data.relayer;
|
|
2545
3193
|
}
|
|
2546
3194
|
|
|
2547
|
-
// src/lib/permit2.ts
|
|
2548
|
-
var import_viem4 = require("viem");
|
|
2549
|
-
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2550
|
-
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2551
|
-
var ERC20_ALLOWANCE_ABI = [
|
|
2552
|
-
{
|
|
2553
|
-
type: "function",
|
|
2554
|
-
name: "allowance",
|
|
2555
|
-
stateMutability: "view",
|
|
2556
|
-
inputs: [
|
|
2557
|
-
{ name: "owner", type: "address" },
|
|
2558
|
-
{ name: "spender", type: "address" }
|
|
2559
|
-
],
|
|
2560
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
2561
|
-
},
|
|
2562
|
-
{
|
|
2563
|
-
type: "function",
|
|
2564
|
-
name: "approve",
|
|
2565
|
-
stateMutability: "nonpayable",
|
|
2566
|
-
inputs: [
|
|
2567
|
-
{ name: "spender", type: "address" },
|
|
2568
|
-
{ name: "amount", type: "uint256" }
|
|
2569
|
-
],
|
|
2570
|
-
outputs: [{ name: "", type: "bool" }]
|
|
2571
|
-
},
|
|
2572
|
-
{
|
|
2573
|
-
type: "function",
|
|
2574
|
-
name: "balanceOf",
|
|
2575
|
-
stateMutability: "view",
|
|
2576
|
-
inputs: [{ name: "account", type: "address" }],
|
|
2577
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
2578
|
-
}
|
|
2579
|
-
];
|
|
2580
|
-
function buildPermitTransferFromTypedData(input) {
|
|
2581
|
-
return {
|
|
2582
|
-
domain: {
|
|
2583
|
-
name: "Permit2",
|
|
2584
|
-
chainId: input.chainId,
|
|
2585
|
-
verifyingContract: PERMIT2_ADDRESS
|
|
2586
|
-
},
|
|
2587
|
-
types: {
|
|
2588
|
-
PermitTransferFrom: [
|
|
2589
|
-
{ name: "permitted", type: "TokenPermissions" },
|
|
2590
|
-
{ name: "spender", type: "address" },
|
|
2591
|
-
{ name: "nonce", type: "uint256" },
|
|
2592
|
-
{ name: "deadline", type: "uint256" }
|
|
2593
|
-
],
|
|
2594
|
-
TokenPermissions: [
|
|
2595
|
-
{ name: "token", type: "address" },
|
|
2596
|
-
{ name: "amount", type: "uint256" }
|
|
2597
|
-
]
|
|
2598
|
-
},
|
|
2599
|
-
primaryType: "PermitTransferFrom",
|
|
2600
|
-
message: input.message
|
|
2601
|
-
};
|
|
2602
|
-
}
|
|
2603
|
-
function randomPermit2Nonce() {
|
|
2604
|
-
const bytes = new Uint8Array(32);
|
|
2605
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
2606
|
-
return BigInt((0, import_viem4.bytesToHex)(bytes));
|
|
2607
|
-
}
|
|
2608
|
-
async function readPermit2Allowance(publicClient, token, owner) {
|
|
2609
|
-
return publicClient.readContract({
|
|
2610
|
-
address: token,
|
|
2611
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2612
|
-
functionName: "allowance",
|
|
2613
|
-
args: [owner, PERMIT2_ADDRESS]
|
|
2614
|
-
});
|
|
2615
|
-
}
|
|
2616
|
-
async function readErc20Balance(publicClient, token, owner) {
|
|
2617
|
-
return publicClient.readContract({
|
|
2618
|
-
address: token,
|
|
2619
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2620
|
-
functionName: "balanceOf",
|
|
2621
|
-
args: [owner]
|
|
2622
|
-
});
|
|
2623
|
-
}
|
|
2624
|
-
|
|
2625
|
-
// src/lib/chain-guard.ts
|
|
2626
|
-
var CHAIN_NAMES = {
|
|
2627
|
-
1: "Ethereum",
|
|
2628
|
-
8453: "Base",
|
|
2629
|
-
42161: "Arbitrum"
|
|
2630
|
-
};
|
|
2631
|
-
function chainName(chainId) {
|
|
2632
|
-
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
2633
|
-
}
|
|
2634
|
-
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
2635
|
-
const actual = await pub.getChainId();
|
|
2636
|
-
if (actual === expected) return;
|
|
2637
|
-
try {
|
|
2638
|
-
await wallet.switchChain({ id: expected });
|
|
2639
|
-
} catch (error) {
|
|
2640
|
-
throw new OwneyError(
|
|
2641
|
-
"CHAIN_MISMATCH",
|
|
2642
|
-
`Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2643
|
-
{
|
|
2644
|
-
expectedChainId: expected,
|
|
2645
|
-
actualChainId: actual,
|
|
2646
|
-
cause: error instanceof Error ? error.message : String(error)
|
|
2647
|
-
}
|
|
2648
|
-
);
|
|
2649
|
-
}
|
|
2650
|
-
const after = await pub.getChainId();
|
|
2651
|
-
if (after !== expected) {
|
|
2652
|
-
throw new OwneyError(
|
|
2653
|
-
"CHAIN_MISMATCH",
|
|
2654
|
-
`Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2655
|
-
{ expectedChainId: expected, actualChainId: after }
|
|
2656
|
-
);
|
|
2657
|
-
}
|
|
2658
|
-
}
|
|
2659
|
-
|
|
2660
3195
|
// src/lib/sponsored-deposit.ts
|
|
2661
3196
|
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
2662
3197
|
function makeSponsoredDepositCallback(deps) {
|
|
@@ -2819,7 +3354,7 @@ function makeSponsoredWethCallback(deps) {
|
|
|
2819
3354
|
}
|
|
2820
3355
|
|
|
2821
3356
|
// src/lib/sponsored-calls-deposit.ts
|
|
2822
|
-
var
|
|
3357
|
+
var import_viem7 = require("viem");
|
|
2823
3358
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
2824
3359
|
var DEFAULT_MAX_POLLS = 30;
|
|
2825
3360
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -2827,7 +3362,7 @@ async function paymasterSupported(provider, owner, chainId) {
|
|
|
2827
3362
|
method: "wallet_getCapabilities",
|
|
2828
3363
|
params: [owner]
|
|
2829
3364
|
});
|
|
2830
|
-
const forChain = caps?.[(0,
|
|
3365
|
+
const forChain = caps?.[(0, import_viem7.toHex)(chainId)] ?? caps?.[String(chainId)];
|
|
2831
3366
|
return Boolean(forChain?.paymasterService?.supported);
|
|
2832
3367
|
}
|
|
2833
3368
|
function makeSponsoredCallsCallback(deps) {
|
|
@@ -2861,8 +3396,8 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2861
3396
|
{ chainId }
|
|
2862
3397
|
);
|
|
2863
3398
|
}
|
|
2864
|
-
const data = (0,
|
|
2865
|
-
abi:
|
|
3399
|
+
const data = (0, import_viem7.encodeFunctionData)({
|
|
3400
|
+
abi: import_viem7.erc20Abi,
|
|
2866
3401
|
functionName: "transfer",
|
|
2867
3402
|
args: [smartWallet, BigInt(amount)]
|
|
2868
3403
|
});
|
|
@@ -2872,7 +3407,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2872
3407
|
{
|
|
2873
3408
|
version: "2.0.0",
|
|
2874
3409
|
from: deps.ownerAddress,
|
|
2875
|
-
chainId: (0,
|
|
3410
|
+
chainId: (0, import_viem7.toHex)(chainId),
|
|
2876
3411
|
atomicRequired: false,
|
|
2877
3412
|
calls: [{ to: token, value: "0x0", data }],
|
|
2878
3413
|
capabilities: {
|
|
@@ -3094,14 +3629,14 @@ var OwneySDK = class {
|
|
|
3094
3629
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
3095
3630
|
// PublicClient/WalletClient param types — structurally identical at
|
|
3096
3631
|
// runtime, but the two share a name TS treats as unrelated.
|
|
3097
|
-
getPublicClient: (cid) => (0,
|
|
3632
|
+
getPublicClient: (cid) => (0, import_viem8.createPublicClient)({
|
|
3098
3633
|
chain: VIEM_CHAIN2[cid],
|
|
3099
|
-
transport: (0,
|
|
3634
|
+
transport: (0, import_viem8.custom)(provider)
|
|
3100
3635
|
}),
|
|
3101
|
-
getWalletClient: (cid) => (0,
|
|
3636
|
+
getWalletClient: (cid) => (0, import_viem8.createWalletClient)({
|
|
3102
3637
|
account: owner,
|
|
3103
3638
|
chain: VIEM_CHAIN2[cid],
|
|
3104
|
-
transport: (0,
|
|
3639
|
+
transport: (0, import_viem8.custom)(provider)
|
|
3105
3640
|
})
|
|
3106
3641
|
});
|
|
3107
3642
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
@@ -3147,14 +3682,14 @@ var OwneySDK = class {
|
|
|
3147
3682
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
3148
3683
|
// PublicClient/WalletClient param types — structurally identical at
|
|
3149
3684
|
// runtime, but the two share a name TS treats as unrelated.
|
|
3150
|
-
getPublicClient: (cid) => (0,
|
|
3685
|
+
getPublicClient: (cid) => (0, import_viem8.createPublicClient)({
|
|
3151
3686
|
chain: VIEM_CHAIN2[cid],
|
|
3152
|
-
transport: (0,
|
|
3687
|
+
transport: (0, import_viem8.custom)(provider)
|
|
3153
3688
|
}),
|
|
3154
|
-
getWalletClient: (cid) => (0,
|
|
3689
|
+
getWalletClient: (cid) => (0, import_viem8.createWalletClient)({
|
|
3155
3690
|
account: owner,
|
|
3156
3691
|
chain: VIEM_CHAIN2[cid],
|
|
3157
|
-
transport: (0,
|
|
3692
|
+
transport: (0, import_viem8.custom)(provider)
|
|
3158
3693
|
})
|
|
3159
3694
|
});
|
|
3160
3695
|
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
@@ -3185,12 +3720,10 @@ var OwneySDK = class {
|
|
|
3185
3720
|
this.orgAgentConfigPromise = fetchOrgAgentConfig(
|
|
3186
3721
|
this.apiKey,
|
|
3187
3722
|
this.routingApiBaseUrl
|
|
3188
|
-
).then(
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
}
|
|
3193
|
-
);
|
|
3723
|
+
).then((config) => {
|
|
3724
|
+
this.orgAgentConfig = config;
|
|
3725
|
+
return config;
|
|
3726
|
+
});
|
|
3194
3727
|
}
|
|
3195
3728
|
return this.orgAgentConfigPromise;
|
|
3196
3729
|
}
|
|
@@ -3709,6 +4242,330 @@ var OwneySDK = class {
|
|
|
3709
4242
|
return eligible;
|
|
3710
4243
|
}
|
|
3711
4244
|
// --- Fund operations ---
|
|
4245
|
+
// --- Swap to yield (ROUT-242) ---
|
|
4246
|
+
/** Lazily built so an app that never swaps pays nothing for it. */
|
|
4247
|
+
swapApiClient;
|
|
4248
|
+
swapApi() {
|
|
4249
|
+
this.swapApiClient ??= createSwapApi(
|
|
4250
|
+
this.routingApiBaseUrl ?? ROUTING_API_BASE_URL,
|
|
4251
|
+
this.apiKey
|
|
4252
|
+
);
|
|
4253
|
+
return this.swapApiClient;
|
|
4254
|
+
}
|
|
4255
|
+
/**
|
|
4256
|
+
* Put the wallet on `chainId`, or fail with something actionable.
|
|
4257
|
+
*
|
|
4258
|
+
* Reuses the same guard the deposit rail uses, which re-reads the chain after
|
|
4259
|
+
* switching — some wallets resolve wallet_switchEthereumChain before the
|
|
4260
|
+
* network has actually changed.
|
|
4261
|
+
*/
|
|
4262
|
+
async ensureSwapChain(chainId) {
|
|
4263
|
+
const provider = this.requireConnectedProvider();
|
|
4264
|
+
const state = this.requireState();
|
|
4265
|
+
const chain = VIEM_CHAIN2[chainId];
|
|
4266
|
+
if (!chain) {
|
|
4267
|
+
throw new OwneyError(
|
|
4268
|
+
"CHAIN_UNSUPPORTED",
|
|
4269
|
+
`Chain ${chainId} is not supported`,
|
|
4270
|
+
{ chainId }
|
|
4271
|
+
);
|
|
4272
|
+
}
|
|
4273
|
+
await ensureWalletOnChain(
|
|
4274
|
+
(0, import_viem8.createPublicClient)({ chain, transport: (0, import_viem8.custom)(provider) }),
|
|
4275
|
+
(0, import_viem8.createWalletClient)({
|
|
4276
|
+
account: state.walletAddress,
|
|
4277
|
+
chain,
|
|
4278
|
+
transport: (0, import_viem8.custom)(provider)
|
|
4279
|
+
}),
|
|
4280
|
+
chainId
|
|
4281
|
+
);
|
|
4282
|
+
}
|
|
4283
|
+
/**
|
|
4284
|
+
* Binds the executor's abstract deps to this client's wallet.
|
|
4285
|
+
*
|
|
4286
|
+
* Kept as a builder rather than baked into the executor so the whole swap
|
|
4287
|
+
* flow stays testable without a provider — the executor never imports viem.
|
|
4288
|
+
*/
|
|
4289
|
+
buildSwapDeps(quote) {
|
|
4290
|
+
const state = this.requireState();
|
|
4291
|
+
const provider = this.requireConnectedProvider();
|
|
4292
|
+
const srcChain = VIEM_CHAIN2[quote.src.chainId];
|
|
4293
|
+
const dstChain = VIEM_CHAIN2[quote.dst.chainId];
|
|
4294
|
+
const wallet = (0, import_viem8.createWalletClient)({
|
|
4295
|
+
account: state.walletAddress,
|
|
4296
|
+
chain: srcChain,
|
|
4297
|
+
transport: (0, import_viem8.custom)(provider)
|
|
4298
|
+
});
|
|
4299
|
+
const srcPublic = (0, import_viem8.createPublicClient)({
|
|
4300
|
+
chain: srcChain,
|
|
4301
|
+
transport: swapReadTransport(quote.src.chainId, this.zyfaiRpcUrls)
|
|
4302
|
+
});
|
|
4303
|
+
const dstPublic = (0, import_viem8.createPublicClient)({
|
|
4304
|
+
chain: dstChain,
|
|
4305
|
+
transport: swapReadTransport(quote.dst.chainId, this.zyfaiRpcUrls)
|
|
4306
|
+
});
|
|
4307
|
+
return {
|
|
4308
|
+
api: this.swapApi(),
|
|
4309
|
+
// Native-aware, like readSourceBalance below. Native ETH is never a
|
|
4310
|
+
// DEPOSIT target, so this only ever mattered once withdrawal shipped —
|
|
4311
|
+
// and there it is the headline case. balanceOf() on the 0xEeee sentinel
|
|
4312
|
+
// reverts, which would have read as "the swap landed nothing".
|
|
4313
|
+
readTargetBalance: async () => {
|
|
4314
|
+
const dst = quote.dst.address;
|
|
4315
|
+
if (dst.toLowerCase().startsWith("0xeeee")) {
|
|
4316
|
+
return dstPublic.getBalance({ address: state.walletAddress });
|
|
4317
|
+
}
|
|
4318
|
+
return dstPublic.readContract({
|
|
4319
|
+
address: dst,
|
|
4320
|
+
abi: import_viem8.erc20Abi,
|
|
4321
|
+
functionName: "balanceOf",
|
|
4322
|
+
args: [state.walletAddress]
|
|
4323
|
+
});
|
|
4324
|
+
},
|
|
4325
|
+
sendTransaction: async (tx) => {
|
|
4326
|
+
const hash = await wallet.sendTransaction({
|
|
4327
|
+
to: tx.to,
|
|
4328
|
+
data: tx.data,
|
|
4329
|
+
value: BigInt(tx.value || "0"),
|
|
4330
|
+
account: state.walletAddress,
|
|
4331
|
+
chain: srcChain
|
|
4332
|
+
});
|
|
4333
|
+
const receipt = await srcPublic.waitForTransactionReceipt({
|
|
4334
|
+
timeout: receiptTimeoutMs(quote.src.chainId),
|
|
4335
|
+
hash,
|
|
4336
|
+
confirmations: 1
|
|
4337
|
+
});
|
|
4338
|
+
if (receipt.status !== "success") {
|
|
4339
|
+
throw new OwneyError(
|
|
4340
|
+
"SWAP_REQUEST_FAILED",
|
|
4341
|
+
`Swap transaction reverted (tx ${hash})`,
|
|
4342
|
+
{ hash }
|
|
4343
|
+
);
|
|
4344
|
+
}
|
|
4345
|
+
return hash;
|
|
4346
|
+
},
|
|
4347
|
+
signTypedData: (typedData) => wallet.signTypedData({
|
|
4348
|
+
account: state.walletAddress,
|
|
4349
|
+
...typedData
|
|
4350
|
+
}),
|
|
4351
|
+
// Chain-bound like every other read here: the wallet provider's chain is
|
|
4352
|
+
// not ours to rely on mid-swap.
|
|
4353
|
+
readSourceBalance: async () => {
|
|
4354
|
+
const src = quote.src.address;
|
|
4355
|
+
if (src.toLowerCase().startsWith("0xeeee")) {
|
|
4356
|
+
return srcPublic.getBalance({ address: state.walletAddress });
|
|
4357
|
+
}
|
|
4358
|
+
return srcPublic.readContract({
|
|
4359
|
+
address: src,
|
|
4360
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
4361
|
+
functionName: "balanceOf",
|
|
4362
|
+
args: [state.walletAddress]
|
|
4363
|
+
});
|
|
4364
|
+
},
|
|
4365
|
+
readAllowance: (spender) => srcPublic.readContract({
|
|
4366
|
+
address: quote.src.address,
|
|
4367
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
4368
|
+
functionName: "allowance",
|
|
4369
|
+
args: [state.walletAddress, spender]
|
|
4370
|
+
}),
|
|
4371
|
+
approve: async (spender, amount) => {
|
|
4372
|
+
const hash = await wallet.writeContract({
|
|
4373
|
+
address: quote.src.address,
|
|
4374
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
4375
|
+
functionName: "approve",
|
|
4376
|
+
args: [spender, amount],
|
|
4377
|
+
account: state.walletAddress,
|
|
4378
|
+
chain: srcChain
|
|
4379
|
+
});
|
|
4380
|
+
await srcPublic.waitForTransactionReceipt({
|
|
4381
|
+
hash,
|
|
4382
|
+
confirmations: 1,
|
|
4383
|
+
timeout: receiptTimeoutMs(quote.src.chainId)
|
|
4384
|
+
});
|
|
4385
|
+
return hash;
|
|
4386
|
+
},
|
|
4387
|
+
ensureChain: (chainId) => this.ensureSwapChain(chainId),
|
|
4388
|
+
runner: {
|
|
4389
|
+
readyForSecrets: (h) => this.swapApi().readyForSecrets(h),
|
|
4390
|
+
submitSecret: (h, secret) => this.swapApi().submitSecret(h, secret),
|
|
4391
|
+
orderStatus: (h) => this.swapApi().orderStatus(h),
|
|
4392
|
+
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
4393
|
+
now: () => Date.now()
|
|
4394
|
+
}
|
|
4395
|
+
};
|
|
4396
|
+
}
|
|
4397
|
+
/**
|
|
4398
|
+
* Assets the user may pay with, and what each chain deposits into.
|
|
4399
|
+
*
|
|
4400
|
+
* The source list is deliberately wider than the deposit list: it includes
|
|
4401
|
+
* native ETH and USDT, which Owney never holds but users often do.
|
|
4402
|
+
*/
|
|
4403
|
+
async getSwapTokens() {
|
|
4404
|
+
return this.swapApi().listTokens();
|
|
4405
|
+
}
|
|
4406
|
+
/**
|
|
4407
|
+
* Price a swap without committing to it.
|
|
4408
|
+
*
|
|
4409
|
+
* `dstAmountMin` is the number to validate against a deposit minimum —
|
|
4410
|
+
* `dst.amount` is an estimate that a decaying auction or slippage can undercut,
|
|
4411
|
+
* and a swap landing below the floor leaves the user swapped but not
|
|
4412
|
+
* deposited.
|
|
4413
|
+
*/
|
|
4414
|
+
async getSwapQuote(params) {
|
|
4415
|
+
const state = this.requireState();
|
|
4416
|
+
return this.swapApi().quote({
|
|
4417
|
+
...params,
|
|
4418
|
+
walletAddress: state.walletAddress
|
|
4419
|
+
});
|
|
4420
|
+
}
|
|
4421
|
+
/**
|
|
4422
|
+
* Swap an asset the user holds into a deposit asset, then deposit it.
|
|
4423
|
+
*
|
|
4424
|
+
* Kept separate from `deposit()` rather than bolted on as an option: the
|
|
4425
|
+
* return shape differs, the staging callback is meaningless on the plain
|
|
4426
|
+
* path, and integrators who never swap should not have to reason about any
|
|
4427
|
+
* of it.
|
|
4428
|
+
*
|
|
4429
|
+
* The deposit runs on the MEASURED arrival, not the quote. A quote is an
|
|
4430
|
+
* estimate, so depositing the quoted figure would either strand dust or try
|
|
4431
|
+
* to move funds that never came.
|
|
4432
|
+
*
|
|
4433
|
+
* Failure modes differ in a way callers must respect. A same-chain swap is
|
|
4434
|
+
* atomic — if it fails, nothing moved. A cross-chain swap escrows the user's
|
|
4435
|
+
* funds first, so SWAP_ORDER_EXPIRED / REFUNDED / CANCELLED all mean the
|
|
4436
|
+
* money left the wallet. Only the former can honestly say "nothing has left
|
|
4437
|
+
* your wallet".
|
|
4438
|
+
*/
|
|
4439
|
+
async swapAndDeposit(options) {
|
|
4440
|
+
const state = this.requireState();
|
|
4441
|
+
const api = this.swapApi();
|
|
4442
|
+
const quote = await api.quote({
|
|
4443
|
+
from: options.from,
|
|
4444
|
+
to: options.to,
|
|
4445
|
+
walletAddress: state.walletAddress
|
|
4446
|
+
});
|
|
4447
|
+
await this.ensureSwapChain(quote.src.chainId);
|
|
4448
|
+
const swap = await executeSwap(this.buildSwapDeps(quote), {
|
|
4449
|
+
quote,
|
|
4450
|
+
walletAddress: state.walletAddress,
|
|
4451
|
+
...options.slippage === void 0 ? {} : { slippage: options.slippage },
|
|
4452
|
+
...options.onSwapProgress ? { onStage: options.onSwapProgress } : {}
|
|
4453
|
+
});
|
|
4454
|
+
options.onSwapProgress?.("depositing");
|
|
4455
|
+
await this.ensureSwapChain(quote.dst.chainId);
|
|
4456
|
+
const deposit = await this.deposit({
|
|
4457
|
+
amount: swap.received,
|
|
4458
|
+
asset: options.to.symbol,
|
|
4459
|
+
...options.agentId ? { agentId: options.agentId } : {}
|
|
4460
|
+
});
|
|
4461
|
+
return { swap, deposit };
|
|
4462
|
+
}
|
|
4463
|
+
/**
|
|
4464
|
+
* Withdraw from an agent and swap the proceeds into whatever the user wants
|
|
4465
|
+
* to hold, delivered to their own wallet.
|
|
4466
|
+
*
|
|
4467
|
+
* The mirror of `swapAndDeposit()`, with one structural difference that
|
|
4468
|
+
* drives the whole implementation: a deposit swap starts from funds already
|
|
4469
|
+
* sitting in the wallet, but a withdrawal has to wait for them. The agent's
|
|
4470
|
+
* provider acknowledges a withdrawal and *then* queues the on-chain transfer
|
|
4471
|
+
* to the EOA, so `withdraw()` resolving means "accepted", not "arrived".
|
|
4472
|
+
* Quoting before the tokens land would size the swap against a balance that
|
|
4473
|
+
* is not there yet.
|
|
4474
|
+
*
|
|
4475
|
+
* The swap is therefore sized from the MEASURED arrival, exactly as the
|
|
4476
|
+
* deposit path sizes its deposit from the measured swap output. On a full
|
|
4477
|
+
* withdrawal there is no other number available — "MAX" has no figure until
|
|
4478
|
+
* the agent picks one.
|
|
4479
|
+
*
|
|
4480
|
+
* **Failure here is not symmetrical with the deposit path.** A failed
|
|
4481
|
+
* deposit-swap leaves the user holding what they started with. A failed
|
|
4482
|
+
* withdrawal-swap leaves them holding the AGENT'S asset in their own wallet:
|
|
4483
|
+
* the money is out, safe, and in the wrong denomination. Both
|
|
4484
|
+
* `WITHDRAW_ARRIVAL_TIMEOUT` and `WITHDRAW_SWAP_FAILED` carry `withdrawn` for
|
|
4485
|
+
* that reason — the UI has to tell the user where their money actually is,
|
|
4486
|
+
* and must never present either as a lost withdrawal.
|
|
4487
|
+
*/
|
|
4488
|
+
async withdrawAndSwap(options) {
|
|
4489
|
+
const state = this.requireState();
|
|
4490
|
+
const activeChainId = this.requireChainId();
|
|
4491
|
+
if (options.from.chainId !== activeChainId) {
|
|
4492
|
+
throw new OwneyError(
|
|
4493
|
+
"CHAIN_MISMATCH",
|
|
4494
|
+
`Cannot withdraw from chain ${options.from.chainId} while the active chain is ${activeChainId}. Activate on that chain first.`,
|
|
4495
|
+
{ requested: options.from.chainId, active: activeChainId }
|
|
4496
|
+
);
|
|
4497
|
+
}
|
|
4498
|
+
const asset = SupportedAssets.find(
|
|
4499
|
+
(a) => a.chainId === options.from.chainId && a.symbol === options.from.symbol.toUpperCase()
|
|
4500
|
+
);
|
|
4501
|
+
if (!asset) {
|
|
4502
|
+
throw new OwneyError(
|
|
4503
|
+
"WITHDRAW_NO_PERMITTED_TOKENS",
|
|
4504
|
+
`${options.from.symbol} on chain ${options.from.chainId} is not an asset Owney holds`,
|
|
4505
|
+
{ ...options.from }
|
|
4506
|
+
);
|
|
4507
|
+
}
|
|
4508
|
+
const srcChain = VIEM_CHAIN2[options.from.chainId];
|
|
4509
|
+
const srcPublic = (0, import_viem8.createPublicClient)({
|
|
4510
|
+
chain: srcChain,
|
|
4511
|
+
transport: swapReadTransport(options.from.chainId, this.zyfaiRpcUrls)
|
|
4512
|
+
});
|
|
4513
|
+
const readWalletBalance = () => srcPublic.readContract({
|
|
4514
|
+
address: asset.address,
|
|
4515
|
+
abi: import_viem8.erc20Abi,
|
|
4516
|
+
functionName: "balanceOf",
|
|
4517
|
+
args: [state.walletAddress]
|
|
4518
|
+
});
|
|
4519
|
+
const baseline = await readWalletBalance();
|
|
4520
|
+
debugLog("owney-sdk", "withdrawAndSwap: baseline", {
|
|
4521
|
+
asset: `${asset.symbol}@${asset.chainId}`,
|
|
4522
|
+
baseline: baseline.toString()
|
|
4523
|
+
});
|
|
4524
|
+
options.onSwapProgress?.("withdrawing");
|
|
4525
|
+
const withdraw = await this.withdraw({
|
|
4526
|
+
asset: options.from.symbol,
|
|
4527
|
+
...options.amount === void 0 ? {} : { amount: options.amount },
|
|
4528
|
+
...options.agentId ? { agentId: options.agentId } : {}
|
|
4529
|
+
});
|
|
4530
|
+
const arrived = await awaitWithdrawalArrival({
|
|
4531
|
+
readBalance: readWalletBalance,
|
|
4532
|
+
baseline,
|
|
4533
|
+
...options.arrivalTimeoutMs === void 0 ? {} : { timeoutMs: options.arrivalTimeoutMs }
|
|
4534
|
+
});
|
|
4535
|
+
const withdrawn = arrived.toString();
|
|
4536
|
+
options.onSwapProgress?.("withdrawn");
|
|
4537
|
+
try {
|
|
4538
|
+
const quote = await this.swapApi().quote({
|
|
4539
|
+
from: { ...options.from, amount: withdrawn },
|
|
4540
|
+
to: options.to,
|
|
4541
|
+
direction: "withdraw",
|
|
4542
|
+
walletAddress: state.walletAddress
|
|
4543
|
+
});
|
|
4544
|
+
await this.ensureSwapChain(quote.src.chainId);
|
|
4545
|
+
const swap = await executeSwap(this.buildSwapDeps(quote), {
|
|
4546
|
+
quote,
|
|
4547
|
+
walletAddress: state.walletAddress,
|
|
4548
|
+
direction: "withdraw",
|
|
4549
|
+
...options.slippage === void 0 ? {} : { slippage: options.slippage },
|
|
4550
|
+
...options.onSwapProgress ? { onStage: options.onSwapProgress } : {}
|
|
4551
|
+
});
|
|
4552
|
+
return { withdraw, withdrawn, swap };
|
|
4553
|
+
} catch (error) {
|
|
4554
|
+
throw new OwneyError(
|
|
4555
|
+
"WITHDRAW_SWAP_FAILED",
|
|
4556
|
+
`Withdrew ${withdrawn} ${asset.symbol} to your wallet, but the swap to ${options.to.symbol} did not complete. The funds are in your wallet as ${asset.symbol}.`,
|
|
4557
|
+
{
|
|
4558
|
+
withdrawn,
|
|
4559
|
+
asset: asset.symbol,
|
|
4560
|
+
chainId: asset.chainId,
|
|
4561
|
+
intendedSymbol: options.to.symbol,
|
|
4562
|
+
intendedChainId: options.to.chainId,
|
|
4563
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
4564
|
+
...error instanceof OwneyError ? { causeCode: error.code } : {}
|
|
4565
|
+
}
|
|
4566
|
+
);
|
|
4567
|
+
}
|
|
4568
|
+
}
|
|
3712
4569
|
/**
|
|
3713
4570
|
* Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
|
|
3714
4571
|
* Validates that the asset is supported by the target agent(s) on the active chain.
|
|
@@ -3879,7 +4736,11 @@ var OwneySDK = class {
|
|
|
3879
4736
|
const chainId = this.requireChainId();
|
|
3880
4737
|
if (agentId) {
|
|
3881
4738
|
const agent = this.getAgent(agentId);
|
|
3882
|
-
const result = await this.readAgent(
|
|
4739
|
+
const result = await this.readAgent(
|
|
4740
|
+
agent,
|
|
4741
|
+
"balances",
|
|
4742
|
+
() => agent.getBalances(state, chainId)
|
|
4743
|
+
);
|
|
3883
4744
|
return result;
|
|
3884
4745
|
}
|
|
3885
4746
|
let totalBalance = 0;
|
|
@@ -3887,7 +4748,11 @@ var OwneySDK = class {
|
|
|
3887
4748
|
const entries = [...this.getActiveAgents().entries()];
|
|
3888
4749
|
const balanceResults = await Promise.allSettled(
|
|
3889
4750
|
entries.map(async ([id, agent]) => {
|
|
3890
|
-
const b = await this.readAgent(
|
|
4751
|
+
const b = await this.readAgent(
|
|
4752
|
+
agent,
|
|
4753
|
+
"balances",
|
|
4754
|
+
() => agent.getBalances(state, chainId)
|
|
4755
|
+
);
|
|
3891
4756
|
return [id, b];
|
|
3892
4757
|
})
|
|
3893
4758
|
);
|
|
@@ -3908,7 +4773,8 @@ var OwneySDK = class {
|
|
|
3908
4773
|
const reason = settledResult.reason;
|
|
3909
4774
|
agentFailures.push(reason);
|
|
3910
4775
|
const retryDelay = rateLimitDelay(reason);
|
|
3911
|
-
if (retryDelay !== void 0)
|
|
4776
|
+
if (retryDelay !== void 0)
|
|
4777
|
+
agentRetryAt[agentId2] = Date.now() + retryDelay;
|
|
3912
4778
|
agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
|
|
3913
4779
|
}
|
|
3914
4780
|
if (successCount === 0) {
|
|
@@ -3935,14 +4801,22 @@ var OwneySDK = class {
|
|
|
3935
4801
|
const chainId = this.requireChainId();
|
|
3936
4802
|
if (agentId) {
|
|
3937
4803
|
const agent = this.getAgent(agentId);
|
|
3938
|
-
return this.readAgent(
|
|
4804
|
+
return this.readAgent(
|
|
4805
|
+
agent,
|
|
4806
|
+
"earnings",
|
|
4807
|
+
() => agent.getEarnings(state, chainId)
|
|
4808
|
+
);
|
|
3939
4809
|
}
|
|
3940
4810
|
let totalEarnings = 0;
|
|
3941
4811
|
const results = {};
|
|
3942
4812
|
const entries = [...this.getActiveAgents().entries()];
|
|
3943
4813
|
const earningsResults = await Promise.all(
|
|
3944
4814
|
entries.map(async ([id, agent]) => {
|
|
3945
|
-
const e = await this.readAgent(
|
|
4815
|
+
const e = await this.readAgent(
|
|
4816
|
+
agent,
|
|
4817
|
+
"earnings",
|
|
4818
|
+
() => agent.getEarnings(state, chainId)
|
|
4819
|
+
);
|
|
3946
4820
|
return [id, e];
|
|
3947
4821
|
})
|
|
3948
4822
|
);
|
|
@@ -4087,7 +4961,11 @@ var OwneySDK = class {
|
|
|
4087
4961
|
),
|
|
4088
4962
|
Promise.all(
|
|
4089
4963
|
entries.map(async ([id, agent]) => {
|
|
4090
|
-
const b = await this.readAgent(
|
|
4964
|
+
const b = await this.readAgent(
|
|
4965
|
+
agent,
|
|
4966
|
+
"balances",
|
|
4967
|
+
() => agent.getBalances(state, chainId)
|
|
4968
|
+
);
|
|
4091
4969
|
return [id, balanceForApyScope(b, chainId, tokenSymbol)];
|
|
4092
4970
|
})
|
|
4093
4971
|
)
|
|
@@ -4147,7 +5025,12 @@ var OwneySDK = class {
|
|
|
4147
5025
|
const { agentId, filters } = options ?? {};
|
|
4148
5026
|
if (agentId) {
|
|
4149
5027
|
const agent = this.getAgent(agentId);
|
|
4150
|
-
return this.readAgent(
|
|
5028
|
+
return this.readAgent(
|
|
5029
|
+
agent,
|
|
5030
|
+
"history",
|
|
5031
|
+
() => agent.getHistory(state, chainId, filters),
|
|
5032
|
+
filters
|
|
5033
|
+
);
|
|
4151
5034
|
}
|
|
4152
5035
|
const activeAgents = [...this.getActiveAgents().values()];
|
|
4153
5036
|
const cursorMap = filters?.cursor ? decodeMultiAgentCursor(filters.cursor) : {};
|
|
@@ -4204,13 +5087,21 @@ var OwneySDK = class {
|
|
|
4204
5087
|
const chainId = this.requireChainId();
|
|
4205
5088
|
if (agentId) {
|
|
4206
5089
|
const agent = this.getAgent(agentId);
|
|
4207
|
-
return this.readAgent(
|
|
5090
|
+
return this.readAgent(
|
|
5091
|
+
agent,
|
|
5092
|
+
"profile",
|
|
5093
|
+
() => agent.getUserProfile(state, chainId)
|
|
5094
|
+
);
|
|
4208
5095
|
}
|
|
4209
5096
|
const results = {};
|
|
4210
5097
|
const entries = [...this.getActiveAgents().entries()];
|
|
4211
5098
|
const profileResults = await Promise.all(
|
|
4212
5099
|
entries.map(async ([id, agent]) => {
|
|
4213
|
-
const p = await this.readAgent(
|
|
5100
|
+
const p = await this.readAgent(
|
|
5101
|
+
agent,
|
|
5102
|
+
"profile",
|
|
5103
|
+
() => agent.getUserProfile(state, chainId)
|
|
5104
|
+
);
|
|
4214
5105
|
return [id, p];
|
|
4215
5106
|
})
|
|
4216
5107
|
);
|
|
@@ -4269,10 +5160,10 @@ var OwneySDK = class {
|
|
|
4269
5160
|
);
|
|
4270
5161
|
}
|
|
4271
5162
|
const provider = this.requireConnectedProvider();
|
|
4272
|
-
const wallet = (0,
|
|
5163
|
+
const wallet = (0, import_viem8.createWalletClient)({
|
|
4273
5164
|
account: state.walletAddress,
|
|
4274
5165
|
chain: VIEM_CHAIN2[chainId],
|
|
4275
|
-
transport: (0,
|
|
5166
|
+
transport: (0, import_viem8.custom)(provider)
|
|
4276
5167
|
});
|
|
4277
5168
|
const hash = await wallet.writeContract({
|
|
4278
5169
|
address: token,
|
|
@@ -4282,9 +5173,9 @@ var OwneySDK = class {
|
|
|
4282
5173
|
account: state.walletAddress,
|
|
4283
5174
|
chain: VIEM_CHAIN2[chainId]
|
|
4284
5175
|
});
|
|
4285
|
-
const publicClient = (0,
|
|
5176
|
+
const publicClient = (0, import_viem8.createPublicClient)({
|
|
4286
5177
|
chain: VIEM_CHAIN2[chainId],
|
|
4287
|
-
transport: (0,
|
|
5178
|
+
transport: (0, import_viem8.custom)(provider)
|
|
4288
5179
|
});
|
|
4289
5180
|
const receipt = await publicClient.waitForTransactionReceipt({
|
|
4290
5181
|
hash,
|
|
@@ -4318,13 +5209,23 @@ var OwneySDK = class {
|
|
|
4318
5209
|
const agentOptions = { tokenSymbol, chainId };
|
|
4319
5210
|
if (agentId) {
|
|
4320
5211
|
const agent = this.getAgent(agentId);
|
|
4321
|
-
return this.readAgent(
|
|
5212
|
+
return this.readAgent(
|
|
5213
|
+
agent,
|
|
5214
|
+
"agentApy",
|
|
5215
|
+
() => agent.getAgentApy(days, agentOptions),
|
|
5216
|
+
{ days, ...agentOptions }
|
|
5217
|
+
);
|
|
4322
5218
|
}
|
|
4323
5219
|
const results = {};
|
|
4324
5220
|
const agentEntries = [...this.agents.entries()];
|
|
4325
5221
|
const apyResults = await Promise.all(
|
|
4326
5222
|
agentEntries.map(async ([id, agent]) => {
|
|
4327
|
-
const apy = await this.readAgent(
|
|
5223
|
+
const apy = await this.readAgent(
|
|
5224
|
+
agent,
|
|
5225
|
+
"agentApy",
|
|
5226
|
+
() => agent.getAgentApy(days, agentOptions),
|
|
5227
|
+
{ days, ...agentOptions }
|
|
5228
|
+
);
|
|
4328
5229
|
return [id, apy];
|
|
4329
5230
|
})
|
|
4330
5231
|
);
|
|
@@ -4351,7 +5252,11 @@ var OwneySDK = class {
|
|
|
4351
5252
|
const entries = [...activeAgents.entries()];
|
|
4352
5253
|
const balanceResults = await Promise.allSettled(
|
|
4353
5254
|
entries.map(async ([id, agent]) => {
|
|
4354
|
-
const b = await this.readAgent(
|
|
5255
|
+
const b = await this.readAgent(
|
|
5256
|
+
agent,
|
|
5257
|
+
"balances",
|
|
5258
|
+
() => agent.getBalances(state, chainId)
|
|
5259
|
+
);
|
|
4355
5260
|
return [id, b.positions ?? []];
|
|
4356
5261
|
})
|
|
4357
5262
|
);
|
|
@@ -4396,13 +5301,13 @@ var OwneySDK = class {
|
|
|
4396
5301
|
};
|
|
4397
5302
|
|
|
4398
5303
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
4399
|
-
var
|
|
5304
|
+
var import_viem9 = require("viem");
|
|
4400
5305
|
var import_siwe = require("siwe");
|
|
4401
5306
|
var import_sdk2 = require("@zyfai/sdk");
|
|
4402
5307
|
|
|
4403
5308
|
// src/agents/zyfai/zyfai.siwx-cache.ts
|
|
4404
|
-
var
|
|
4405
|
-
var
|
|
5309
|
+
var KEY_PREFIX3 = "owney.siwx.session";
|
|
5310
|
+
var storage3 = () => {
|
|
4406
5311
|
if (typeof window === "undefined") return null;
|
|
4407
5312
|
try {
|
|
4408
5313
|
return window.localStorage;
|
|
@@ -4410,8 +5315,8 @@ var storage2 = () => {
|
|
|
4410
5315
|
return null;
|
|
4411
5316
|
}
|
|
4412
5317
|
};
|
|
4413
|
-
var buildKey2 = (address) => `${
|
|
4414
|
-
var legacyKeyPrefix2 = (address) => `${
|
|
5318
|
+
var buildKey2 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}`;
|
|
5319
|
+
var legacyKeyPrefix2 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}:`;
|
|
4415
5320
|
var memorySiwxSessions = /* @__PURE__ */ new Map();
|
|
4416
5321
|
var readLegacySiwxSession = (store, address) => {
|
|
4417
5322
|
if (!store) return null;
|
|
@@ -4443,7 +5348,7 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
4443
5348
|
var readSiwxSession = (address, chainId) => {
|
|
4444
5349
|
if (typeof window === "undefined") return null;
|
|
4445
5350
|
const key2 = buildKey2(address);
|
|
4446
|
-
const store =
|
|
5351
|
+
const store = storage3();
|
|
4447
5352
|
let raw = null;
|
|
4448
5353
|
try {
|
|
4449
5354
|
raw = store?.getItem(key2) ?? null;
|
|
@@ -4473,7 +5378,7 @@ var writeSiwxSession = (address, _chainId, session) => {
|
|
|
4473
5378
|
if (typeof window === "undefined") return;
|
|
4474
5379
|
const key2 = buildKey2(address);
|
|
4475
5380
|
memorySiwxSessions.set(key2, session);
|
|
4476
|
-
const store =
|
|
5381
|
+
const store = storage3();
|
|
4477
5382
|
try {
|
|
4478
5383
|
store?.setItem(key2, JSON.stringify(session));
|
|
4479
5384
|
} catch {
|
|
@@ -4482,7 +5387,7 @@ var writeSiwxSession = (address, _chainId, session) => {
|
|
|
4482
5387
|
var clearSiwxSession = (address, _chainId) => {
|
|
4483
5388
|
const key2 = buildKey2(address);
|
|
4484
5389
|
memorySiwxSessions.delete(key2);
|
|
4485
|
-
const store =
|
|
5390
|
+
const store = storage3();
|
|
4486
5391
|
try {
|
|
4487
5392
|
store?.removeItem(key2);
|
|
4488
5393
|
} catch {
|
|
@@ -4523,7 +5428,7 @@ function buildSIWXConfig(deps) {
|
|
|
4523
5428
|
issuedAt,
|
|
4524
5429
|
toString() {
|
|
4525
5430
|
return new import_siwe.SiweMessage({
|
|
4526
|
-
address: (0,
|
|
5431
|
+
address: (0, import_viem9.getAddress)(accountAddress),
|
|
4527
5432
|
chainId: numericChainId(chainId),
|
|
4528
5433
|
domain,
|
|
4529
5434
|
uri,
|
|
@@ -4601,9 +5506,9 @@ function buildSIWXConfig(deps) {
|
|
|
4601
5506
|
}
|
|
4602
5507
|
function createOwneySIWX(config) {
|
|
4603
5508
|
const zyfai = new import_sdk2.ZyfaiSDK({ apiKey: config.apiKey });
|
|
4604
|
-
const
|
|
5509
|
+
const http4 = zyfai.httpClient;
|
|
4605
5510
|
return buildSIWXConfig({
|
|
4606
|
-
post: (url, data) =>
|
|
5511
|
+
post: (url, data) => http4.post(url, data),
|
|
4607
5512
|
referralSource: config.referralSource
|
|
4608
5513
|
});
|
|
4609
5514
|
}
|
|
@@ -4616,5 +5521,6 @@ function createOwneySIWX(config) {
|
|
|
4616
5521
|
OwneyError,
|
|
4617
5522
|
OwneySDK,
|
|
4618
5523
|
createOwneySIWX,
|
|
5524
|
+
listPendingSwaps,
|
|
4619
5525
|
setOwneyDebug
|
|
4620
5526
|
});
|