@owney/sdk 0.7.7-beta.3 → 0.7.7-beta.5
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 +146 -186
- package/dist/index.d.cts +21 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +131 -171
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -879,12 +879,6 @@ var ZyfaiAgent = class {
|
|
|
879
879
|
connectedAddress = null;
|
|
880
880
|
connectedChainId = null;
|
|
881
881
|
smartWalletCache = /* @__PURE__ */ new Map();
|
|
882
|
-
/**
|
|
883
|
-
* `${eoa}:${chainId}` keys we have already tried createSessionKey for. Guards
|
|
884
|
-
* against re-running it on every activation/deposit when Zyfai's
|
|
885
|
-
* `hasActiveSessionKey` does not flip to true — see ensureSessionKey.
|
|
886
|
-
*/
|
|
887
|
-
sessionKeyAttempts = /* @__PURE__ */ new Set();
|
|
888
882
|
connectAuthPromise = null;
|
|
889
883
|
rpcUrls;
|
|
890
884
|
publicClients = /* @__PURE__ */ new Map();
|
|
@@ -1342,74 +1336,10 @@ var ZyfaiAgent = class {
|
|
|
1342
1336
|
* recoverable (ensureWalletDeployed tries the other deploy call), but
|
|
1343
1337
|
* failing the deposit outright is not.
|
|
1344
1338
|
*/
|
|
1345
|
-
/**
|
|
1346
|
-
* Zyfai's own answer to "what kind of wallet is this, and can the agent act on
|
|
1347
|
-
* it", read from the positions payload (`GET /data/position`, which
|
|
1348
|
-
* `getPositions` wraps).
|
|
1349
|
-
*
|
|
1350
|
-
* `predeployed` and `hasActiveSessionKey` are both returned at runtime but
|
|
1351
|
-
* declared on neither `Portfolio` nor their docs, hence the cast. Verified
|
|
1352
|
-
* live: an old wallet reports `{predeployed: false, hasActiveSessionKey:
|
|
1353
|
-
* false}` while a pool wallet reports `{true, true}`.
|
|
1354
|
-
*
|
|
1355
|
-
* Preferred over the SDK's private `isPredeployed`, which is cached from the
|
|
1356
|
-
* login response and can go stale mid-session. Returns an empty object on any
|
|
1357
|
-
* failure so every caller degrades to its own fallback.
|
|
1358
|
-
*/
|
|
1359
|
-
async readWalletState(ownerAddress) {
|
|
1360
|
-
try {
|
|
1361
|
-
const { portfolio } = await this.sdk.getPositions(ownerAddress);
|
|
1362
|
-
const raw = portfolio;
|
|
1363
|
-
debugLog("zyfai:onboard", "wallet state from getPositions", {
|
|
1364
|
-
predeployed: raw?.predeployed,
|
|
1365
|
-
hasActiveSessionKey: raw?.hasActiveSessionKey
|
|
1366
|
-
});
|
|
1367
|
-
return {
|
|
1368
|
-
predeployed: raw?.predeployed,
|
|
1369
|
-
hasActiveSessionKey: raw?.hasActiveSessionKey
|
|
1370
|
-
};
|
|
1371
|
-
} catch (error) {
|
|
1372
|
-
console.warn(
|
|
1373
|
-
"[zyfai] getPositions wallet-state read failed (non-fatal):",
|
|
1374
|
-
error instanceof Error ? error.message : String(error)
|
|
1375
|
-
);
|
|
1376
|
-
return {};
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
1339
|
async isPredeployedPoolWallet(ownerAddress, smartWallet) {
|
|
1380
|
-
const { predeployed } = await this.readWalletState(ownerAddress);
|
|
1381
|
-
if (typeof predeployed === "boolean") {
|
|
1382
|
-
debugLog(
|
|
1383
|
-
"zyfai:onboard",
|
|
1384
|
-
`wallet class: ${predeployed ? "PREDEPLOYED (new flow)" : "OLD (legacy flow)"}`,
|
|
1385
|
-
{ source: "getPositions.predeployed", predeployed }
|
|
1386
|
-
);
|
|
1387
|
-
return predeployed;
|
|
1388
|
-
}
|
|
1389
|
-
const internals = this.sdk;
|
|
1390
|
-
if (typeof internals.isPredeployed === "boolean") {
|
|
1391
|
-
debugLog(
|
|
1392
|
-
"zyfai:onboard",
|
|
1393
|
-
`wallet class: ${internals.isPredeployed ? "PREDEPLOYED (new flow)" : "OLD (legacy flow)"}`,
|
|
1394
|
-
{
|
|
1395
|
-
source: "login response (cached)",
|
|
1396
|
-
predeployed: internals.isPredeployed
|
|
1397
|
-
}
|
|
1398
|
-
);
|
|
1399
|
-
return internals.isPredeployed;
|
|
1400
|
-
}
|
|
1401
1340
|
try {
|
|
1402
1341
|
const byEoa = await this.sdk.getSmartWalletByEOA(ownerAddress);
|
|
1403
|
-
|
|
1404
|
-
debugLog(
|
|
1405
|
-
"zyfai:onboard",
|
|
1406
|
-
`wallet class: ${isPool ? "PREDEPLOYED (new flow)" : "OLD (legacy flow)"}`,
|
|
1407
|
-
{
|
|
1408
|
-
source: "getSmartWalletByEOA fallback",
|
|
1409
|
-
poolRecord: byEoa?.smartWallet
|
|
1410
|
-
}
|
|
1411
|
-
);
|
|
1412
|
-
return isPool;
|
|
1342
|
+
return typeof byEoa?.smartWallet === "string" && byEoa.smartWallet.toLowerCase() === smartWallet.toLowerCase();
|
|
1413
1343
|
} catch (error) {
|
|
1414
1344
|
console.warn(
|
|
1415
1345
|
"[zyfai] getSmartWalletByEOA failed; assuming a legacy wallet:",
|
|
@@ -1447,24 +1377,8 @@ var ZyfaiAgent = class {
|
|
|
1447
1377
|
ownerAddress,
|
|
1448
1378
|
chainId
|
|
1449
1379
|
);
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
isDeployed: wallet.isDeployed
|
|
1453
|
-
});
|
|
1454
|
-
if (!wallet.isDeployed) {
|
|
1455
|
-
debugLog(
|
|
1456
|
-
"zyfai:onboard",
|
|
1457
|
-
"Safe not deployed on this chain; activation does nothing (deposit owns deploys)"
|
|
1458
|
-
);
|
|
1459
|
-
return;
|
|
1460
|
-
}
|
|
1461
|
-
const { hasActiveSessionKey } = await this.readWalletState(ownerAddress);
|
|
1462
|
-
await this.ensureSessionKey(
|
|
1463
|
-
ownerAddress,
|
|
1464
|
-
chainId,
|
|
1465
|
-
wallet.address,
|
|
1466
|
-
hasActiveSessionKey
|
|
1467
|
-
);
|
|
1380
|
+
if (!wallet.isDeployed) return;
|
|
1381
|
+
await this.ensureSessionKey(ownerAddress, chainId, wallet.address);
|
|
1468
1382
|
} catch (error) {
|
|
1469
1383
|
console.warn(
|
|
1470
1384
|
"[zyfai] session-key check at activation failed (non-fatal):",
|
|
@@ -1472,51 +1386,20 @@ var ZyfaiAgent = class {
|
|
|
1472
1386
|
);
|
|
1473
1387
|
}
|
|
1474
1388
|
}
|
|
1475
|
-
async ensureSessionKey(ownerAddress, chainId, smartWallet
|
|
1476
|
-
const attemptKey = `${ownerAddress.toLowerCase()}:${chainId}`;
|
|
1477
|
-
if (this.sessionKeyAttempts.has(attemptKey)) {
|
|
1478
|
-
debugLog(
|
|
1479
|
-
"zyfai:onboard",
|
|
1480
|
-
"session key: already attempted this session, skipping",
|
|
1481
|
-
{ chainId }
|
|
1482
|
-
);
|
|
1483
|
-
return;
|
|
1484
|
-
}
|
|
1389
|
+
async ensureSessionKey(ownerAddress, chainId, smartWallet) {
|
|
1485
1390
|
try {
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
if (hasActiveSessionKey === void 0) {
|
|
1495
|
-
const installed = await this.readSmartSessionsInstalled(
|
|
1496
|
-
chainId,
|
|
1497
|
-
smartWallet
|
|
1498
|
-
);
|
|
1499
|
-
debugLog(
|
|
1500
|
-
"zyfai:onboard",
|
|
1501
|
-
`session key: Zyfai state unavailable; on-chain validator ${installed ? "INSTALLED" : "MISSING"}`,
|
|
1502
|
-
{ smartWallet, chainId }
|
|
1503
|
-
);
|
|
1504
|
-
if (installed) return;
|
|
1505
|
-
}
|
|
1506
|
-
this.sessionKeyAttempts.add(attemptKey);
|
|
1391
|
+
const installed = await this.getPublicClient(chainId).readContract({
|
|
1392
|
+
address: smartWallet,
|
|
1393
|
+
abi: ERC7579_IS_MODULE_INSTALLED_ABI,
|
|
1394
|
+
functionName: "isModuleInstalled",
|
|
1395
|
+
// moduleTypeId 1 = validator (ERC-7579)
|
|
1396
|
+
args: [1n, import_sdk.SMART_SESSIONS_VALIDATOR, "0x"]
|
|
1397
|
+
});
|
|
1398
|
+
if (installed) return;
|
|
1507
1399
|
console.warn(
|
|
1508
|
-
`[zyfai]
|
|
1400
|
+
`[zyfai] Safe ${smartWallet} has no smart-sessions validator on chain ${chainId}; creating a session key so the agent can allocate.`
|
|
1509
1401
|
);
|
|
1510
|
-
debugLog("zyfai:onboard", "session key: calling createSessionKey", {
|
|
1511
|
-
chainId,
|
|
1512
|
-
smartWallet,
|
|
1513
|
-
hasActiveSessionKey
|
|
1514
|
-
});
|
|
1515
1402
|
await this.sdk.createSessionKey(ownerAddress, chainId);
|
|
1516
|
-
debugLog("zyfai:onboard", "session key: createSessionKey succeeded", {
|
|
1517
|
-
chainId,
|
|
1518
|
-
smartWallet
|
|
1519
|
-
});
|
|
1520
1403
|
} catch (error) {
|
|
1521
1404
|
console.warn(
|
|
1522
1405
|
"[zyfai] createSessionKey skipped (non-fatal):",
|
|
@@ -1524,16 +1407,6 @@ var ZyfaiAgent = class {
|
|
|
1524
1407
|
);
|
|
1525
1408
|
}
|
|
1526
1409
|
}
|
|
1527
|
-
/** Ground truth for "can Zyfai's automation act on this Safe, on this chain". */
|
|
1528
|
-
async readSmartSessionsInstalled(chainId, smartWallet) {
|
|
1529
|
-
return this.getPublicClient(chainId).readContract({
|
|
1530
|
-
address: smartWallet,
|
|
1531
|
-
abi: ERC7579_IS_MODULE_INSTALLED_ABI,
|
|
1532
|
-
functionName: "isModuleInstalled",
|
|
1533
|
-
// moduleTypeId 1 = validator (ERC-7579)
|
|
1534
|
-
args: [1n, import_sdk.SMART_SESSIONS_VALIDATOR, "0x"]
|
|
1535
|
-
});
|
|
1536
|
-
}
|
|
1537
1410
|
/**
|
|
1538
1411
|
* Guarantee the user's Safe has code on `chainId` before anything transfers
|
|
1539
1412
|
* funds into it, and return its address.
|
|
@@ -1554,24 +1427,8 @@ var ZyfaiAgent = class {
|
|
|
1554
1427
|
*/
|
|
1555
1428
|
async ensureWalletDeployed(ownerAddress, chainId) {
|
|
1556
1429
|
const wallet = await this.sdk.getSmartWalletAddress(ownerAddress, chainId);
|
|
1557
|
-
debugLog("zyfai:onboard", `--- deposit onboarding, chain ${chainId} ---`, {
|
|
1558
|
-
eoa: ownerAddress,
|
|
1559
|
-
smartWallet: wallet.address,
|
|
1560
|
-
isDeployed: wallet.isDeployed,
|
|
1561
|
-
isOwner: wallet.isOwner
|
|
1562
|
-
});
|
|
1563
1430
|
if (wallet.isDeployed) {
|
|
1564
|
-
|
|
1565
|
-
"zyfai:onboard",
|
|
1566
|
-
"Safe already has code -> no deploy needed, checking the session key"
|
|
1567
|
-
);
|
|
1568
|
-
const { hasActiveSessionKey } = await this.readWalletState(ownerAddress);
|
|
1569
|
-
await this.ensureSessionKey(
|
|
1570
|
-
ownerAddress,
|
|
1571
|
-
chainId,
|
|
1572
|
-
wallet.address,
|
|
1573
|
-
hasActiveSessionKey
|
|
1574
|
-
);
|
|
1431
|
+
await this.ensureSessionKey(ownerAddress, chainId, wallet.address);
|
|
1575
1432
|
return wallet.address;
|
|
1576
1433
|
}
|
|
1577
1434
|
const poolFirst = await this.isPredeployedPoolWallet(
|
|
@@ -1587,27 +1444,14 @@ var ZyfaiAgent = class {
|
|
|
1587
1444
|
this.sdk.deploySafe(ownerAddress, chainId, "aggressive", true)
|
|
1588
1445
|
);
|
|
1589
1446
|
const [first, second] = poolFirst ? [deployAsPool, deployAsLegacy] : [deployAsLegacy, deployAsPool];
|
|
1590
|
-
debugLog(
|
|
1591
|
-
"zyfai:onboard",
|
|
1592
|
-
`Safe has no code on chain ${chainId} -> deploying via ${poolFirst ? "deployOnChains (pool path)" : "deploySafe(createSessionKey=true) (legacy path)"}`
|
|
1593
|
-
);
|
|
1594
1447
|
try {
|
|
1595
1448
|
await first();
|
|
1596
|
-
debugLog(
|
|
1597
|
-
"zyfai:onboard",
|
|
1598
|
-
`${poolFirst ? "deployOnChains" : "deploySafe"} succeeded`
|
|
1599
|
-
);
|
|
1600
1449
|
} catch (error) {
|
|
1601
1450
|
console.warn(
|
|
1602
1451
|
`[zyfai] ${poolFirst ? "deployOnChains" : "deploySafe"} failed on chain ${chainId}; trying the other deploy path:`,
|
|
1603
1452
|
error instanceof Error ? error.message : String(error)
|
|
1604
1453
|
);
|
|
1605
|
-
debugLog(
|
|
1606
|
-
"zyfai:onboard",
|
|
1607
|
-
`falling back to ${poolFirst ? "deploySafe (legacy path)" : "deployOnChains (pool path)"}`
|
|
1608
|
-
);
|
|
1609
1454
|
await second();
|
|
1610
|
-
debugLog("zyfai:onboard", "fallback deploy path succeeded");
|
|
1611
1455
|
}
|
|
1612
1456
|
const redeployed = await this.sdk.getSmartWalletAddress(
|
|
1613
1457
|
ownerAddress,
|
|
@@ -1995,7 +1839,7 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
1995
1839
|
}
|
|
1996
1840
|
|
|
1997
1841
|
// src/client.ts
|
|
1998
|
-
var
|
|
1842
|
+
var import_viem6 = require("viem");
|
|
1999
1843
|
var import_chains2 = require("viem/chains");
|
|
2000
1844
|
|
|
2001
1845
|
// src/lib/transfer-auth.ts
|
|
@@ -2425,6 +2269,97 @@ function makeSponsoredWethCallback(deps) {
|
|
|
2425
2269
|
};
|
|
2426
2270
|
}
|
|
2427
2271
|
|
|
2272
|
+
// src/lib/sponsored-calls-deposit.ts
|
|
2273
|
+
var import_viem5 = require("viem");
|
|
2274
|
+
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
2275
|
+
var DEFAULT_MAX_POLLS = 30;
|
|
2276
|
+
async function paymasterSupported(provider, owner, chainId) {
|
|
2277
|
+
const caps = await provider.request({
|
|
2278
|
+
method: "wallet_getCapabilities",
|
|
2279
|
+
params: [owner]
|
|
2280
|
+
});
|
|
2281
|
+
const forChain = caps?.[(0, import_viem5.toHex)(chainId)] ?? caps?.[String(chainId)];
|
|
2282
|
+
return Boolean(forChain?.paymasterService?.supported);
|
|
2283
|
+
}
|
|
2284
|
+
function makeSponsoredCallsCallback(deps) {
|
|
2285
|
+
const pollIntervalMs = deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
2286
|
+
const maxPolls = deps.maxPolls ?? DEFAULT_MAX_POLLS;
|
|
2287
|
+
const absolutePaymasterUrl = () => {
|
|
2288
|
+
const configured = deps.paymasterServiceUrl;
|
|
2289
|
+
if (/^https?:\/\//i.test(configured)) return configured;
|
|
2290
|
+
const origin = globalThis.location?.origin;
|
|
2291
|
+
if (!origin) {
|
|
2292
|
+
throw new OwneyError(
|
|
2293
|
+
"SPONSORED_CALLS_UNSUPPORTED",
|
|
2294
|
+
`paymasterServiceUrl "${configured}" is relative and no page origin is available to resolve it.`
|
|
2295
|
+
);
|
|
2296
|
+
}
|
|
2297
|
+
return new URL(configured, origin).toString();
|
|
2298
|
+
};
|
|
2299
|
+
return async (smartWallet, chainId, amount) => {
|
|
2300
|
+
const cid = chainId;
|
|
2301
|
+
const token = deps.tokenAddressByChain[cid];
|
|
2302
|
+
if (!token) {
|
|
2303
|
+
throw new OwneyError(
|
|
2304
|
+
"CHAIN_UNSUPPORTED",
|
|
2305
|
+
`No sponsored token configured for chain ${chainId}`
|
|
2306
|
+
);
|
|
2307
|
+
}
|
|
2308
|
+
if (!await paymasterSupported(deps.provider, deps.ownerAddress, chainId)) {
|
|
2309
|
+
throw new OwneyError(
|
|
2310
|
+
"SPONSORED_CALLS_UNSUPPORTED",
|
|
2311
|
+
`Wallet does not advertise paymasterService on chain ${chainId}.`,
|
|
2312
|
+
{ chainId }
|
|
2313
|
+
);
|
|
2314
|
+
}
|
|
2315
|
+
const data = (0, import_viem5.encodeFunctionData)({
|
|
2316
|
+
abi: import_viem5.erc20Abi,
|
|
2317
|
+
functionName: "transfer",
|
|
2318
|
+
args: [smartWallet, BigInt(amount)]
|
|
2319
|
+
});
|
|
2320
|
+
const sendResult = await deps.provider.request({
|
|
2321
|
+
method: "wallet_sendCalls",
|
|
2322
|
+
params: [
|
|
2323
|
+
{
|
|
2324
|
+
version: "2.0.0",
|
|
2325
|
+
from: deps.ownerAddress,
|
|
2326
|
+
chainId: (0, import_viem5.toHex)(chainId),
|
|
2327
|
+
atomicRequired: false,
|
|
2328
|
+
calls: [{ to: token, value: "0x0", data }],
|
|
2329
|
+
capabilities: {
|
|
2330
|
+
paymasterService: { url: absolutePaymasterUrl() }
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
]
|
|
2334
|
+
});
|
|
2335
|
+
deps.onApproved?.();
|
|
2336
|
+
const callsId = typeof sendResult === "string" ? sendResult : sendResult?.id;
|
|
2337
|
+
if (!callsId) {
|
|
2338
|
+
throw new OwneyError(
|
|
2339
|
+
"SPONSORED_CALLS_NO_ID",
|
|
2340
|
+
"Wallet accepted wallet_sendCalls but returned no calls id.",
|
|
2341
|
+
{ chainId }
|
|
2342
|
+
);
|
|
2343
|
+
}
|
|
2344
|
+
for (let attempt = 0; attempt < maxPolls; attempt += 1) {
|
|
2345
|
+
const status = await deps.provider.request({
|
|
2346
|
+
method: "wallet_getCallsStatus",
|
|
2347
|
+
params: [callsId]
|
|
2348
|
+
});
|
|
2349
|
+
const txHash = status?.receipts?.[0]?.transactionHash;
|
|
2350
|
+
if (txHash) return txHash;
|
|
2351
|
+
if (pollIntervalMs > 0) {
|
|
2352
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
throw new OwneyError(
|
|
2356
|
+
"SPONSORED_CALLS_NO_RECEIPT",
|
|
2357
|
+
`No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
|
|
2358
|
+
{ chainId, callsId }
|
|
2359
|
+
);
|
|
2360
|
+
};
|
|
2361
|
+
}
|
|
2362
|
+
|
|
2428
2363
|
// src/client.ts
|
|
2429
2364
|
function encodeMultiAgentCursor(map) {
|
|
2430
2365
|
return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
|
|
@@ -2486,12 +2421,15 @@ var OwneySDK = class {
|
|
|
2486
2421
|
referralSource;
|
|
2487
2422
|
cachedSponsoredCallback = null;
|
|
2488
2423
|
cachedWethSponsoredCallback = null;
|
|
2424
|
+
paymasterServiceUrl;
|
|
2425
|
+
cachedSponsoredCallsCallbacks = /* @__PURE__ */ new Map();
|
|
2489
2426
|
initializingAgentsPromise = null;
|
|
2490
2427
|
constructor(config) {
|
|
2491
2428
|
this.apiKey = config.apiKey;
|
|
2492
2429
|
if (config.debug) setOwneyDebug(true);
|
|
2493
2430
|
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
2494
2431
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
2432
|
+
this.paymasterServiceUrl = config.paymasterServiceUrl;
|
|
2495
2433
|
this.referralSource = config.referralSource;
|
|
2496
2434
|
}
|
|
2497
2435
|
/**
|
|
@@ -2514,6 +2452,7 @@ var OwneySDK = class {
|
|
|
2514
2452
|
this.state = { provider, walletAddress, chainId: null };
|
|
2515
2453
|
this.cachedSponsoredCallback = null;
|
|
2516
2454
|
this.cachedWethSponsoredCallback = null;
|
|
2455
|
+
this.cachedSponsoredCallsCallbacks.clear();
|
|
2517
2456
|
}
|
|
2518
2457
|
/**
|
|
2519
2458
|
* Disconnect from all agents and clear connection state.
|
|
@@ -2528,6 +2467,7 @@ var OwneySDK = class {
|
|
|
2528
2467
|
this.state = null;
|
|
2529
2468
|
this.cachedSponsoredCallback = null;
|
|
2530
2469
|
this.cachedWethSponsoredCallback = null;
|
|
2470
|
+
this.cachedSponsoredCallsCallbacks.clear();
|
|
2531
2471
|
}
|
|
2532
2472
|
isAgentDisabled(agentId) {
|
|
2533
2473
|
return this.disabledAgents.has(agentId);
|
|
@@ -2590,14 +2530,14 @@ var OwneySDK = class {
|
|
|
2590
2530
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
2591
2531
|
// PublicClient/WalletClient param types — structurally identical at
|
|
2592
2532
|
// runtime, but the two share a name TS treats as unrelated.
|
|
2593
|
-
getPublicClient: (cid) => (0,
|
|
2533
|
+
getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
|
|
2594
2534
|
chain: VIEM_CHAIN2[cid],
|
|
2595
|
-
transport: (0,
|
|
2535
|
+
transport: (0, import_viem6.custom)(provider)
|
|
2596
2536
|
}),
|
|
2597
|
-
getWalletClient: (cid) => (0,
|
|
2537
|
+
getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
|
|
2598
2538
|
account: owner,
|
|
2599
2539
|
chain: VIEM_CHAIN2[cid],
|
|
2600
|
-
transport: (0,
|
|
2540
|
+
transport: (0, import_viem6.custom)(provider)
|
|
2601
2541
|
})
|
|
2602
2542
|
});
|
|
2603
2543
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
@@ -2609,6 +2549,26 @@ var OwneySDK = class {
|
|
|
2609
2549
|
* deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
2610
2550
|
* `PermitTransferFrom` instead of an EIP-3009 authorization.
|
|
2611
2551
|
*/
|
|
2552
|
+
getDefaultSponsoredCallsCallback(asset, onApproved) {
|
|
2553
|
+
const cached = this.cachedSponsoredCallsCallbacks.get(asset);
|
|
2554
|
+
if (!onApproved && cached) return cached;
|
|
2555
|
+
const provider = this.requireConnectedProvider();
|
|
2556
|
+
const callback = makeSponsoredCallsCallback({
|
|
2557
|
+
provider,
|
|
2558
|
+
ownerAddress: this.state.walletAddress,
|
|
2559
|
+
paymasterServiceUrl: this.paymasterServiceUrl,
|
|
2560
|
+
onApproved,
|
|
2561
|
+
tokenAddressByChain: asset === "WETH" ? SPONSORED_WETH_BY_CHAIN : SPONSORED_USDC_BY_CHAIN
|
|
2562
|
+
});
|
|
2563
|
+
if (!onApproved) this.cachedSponsoredCallsCallbacks.set(asset, callback);
|
|
2564
|
+
return callback;
|
|
2565
|
+
}
|
|
2566
|
+
/**
|
|
2567
|
+
* Lazily builds (and caches) the default Permit2 sponsored WETH deposit
|
|
2568
|
+
* callback used when the caller omits `depositCallback` for a WETH deposit.
|
|
2569
|
+
* Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
2570
|
+
* `PermitTransferFrom` instead of an EIP-3009 authorization.
|
|
2571
|
+
*/
|
|
2612
2572
|
getDefaultWethSponsoredCallback(onApproved) {
|
|
2613
2573
|
if (!onApproved && this.cachedWethSponsoredCallback)
|
|
2614
2574
|
return this.cachedWethSponsoredCallback;
|
|
@@ -2623,14 +2583,14 @@ var OwneySDK = class {
|
|
|
2623
2583
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
2624
2584
|
// PublicClient/WalletClient param types — structurally identical at
|
|
2625
2585
|
// runtime, but the two share a name TS treats as unrelated.
|
|
2626
|
-
getPublicClient: (cid) => (0,
|
|
2586
|
+
getPublicClient: (cid) => (0, import_viem6.createPublicClient)({
|
|
2627
2587
|
chain: VIEM_CHAIN2[cid],
|
|
2628
|
-
transport: (0,
|
|
2588
|
+
transport: (0, import_viem6.custom)(provider)
|
|
2629
2589
|
}),
|
|
2630
|
-
getWalletClient: (cid) => (0,
|
|
2590
|
+
getWalletClient: (cid) => (0, import_viem6.createWalletClient)({
|
|
2631
2591
|
account: owner,
|
|
2632
2592
|
chain: VIEM_CHAIN2[cid],
|
|
2633
|
-
transport: (0,
|
|
2593
|
+
transport: (0, import_viem6.custom)(provider)
|
|
2634
2594
|
})
|
|
2635
2595
|
});
|
|
2636
2596
|
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
@@ -2831,7 +2791,7 @@ var OwneySDK = class {
|
|
|
2831
2791
|
}
|
|
2832
2792
|
const state = this.requireState();
|
|
2833
2793
|
const chainId = this.requireChainId();
|
|
2834
|
-
const effectiveCallback = depositCallback ?? (asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved) : this.getDefaultSponsoredCallback(onApproved));
|
|
2794
|
+
const effectiveCallback = depositCallback ?? (this.paymasterServiceUrl ? this.getDefaultSponsoredCallsCallback(asset, onApproved) : asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved) : this.getDefaultSponsoredCallback(onApproved));
|
|
2835
2795
|
if (agentId) {
|
|
2836
2796
|
if (this.isAgentDisabled(agentId)) {
|
|
2837
2797
|
throw new OwneyError(
|
|
@@ -3474,10 +3434,10 @@ var OwneySDK = class {
|
|
|
3474
3434
|
);
|
|
3475
3435
|
}
|
|
3476
3436
|
const provider = this.requireConnectedProvider();
|
|
3477
|
-
const wallet = (0,
|
|
3437
|
+
const wallet = (0, import_viem6.createWalletClient)({
|
|
3478
3438
|
account: state.walletAddress,
|
|
3479
3439
|
chain: VIEM_CHAIN2[chainId],
|
|
3480
|
-
transport: (0,
|
|
3440
|
+
transport: (0, import_viem6.custom)(provider)
|
|
3481
3441
|
});
|
|
3482
3442
|
const hash = await wallet.writeContract({
|
|
3483
3443
|
address: token,
|
|
@@ -3487,9 +3447,9 @@ var OwneySDK = class {
|
|
|
3487
3447
|
account: state.walletAddress,
|
|
3488
3448
|
chain: VIEM_CHAIN2[chainId]
|
|
3489
3449
|
});
|
|
3490
|
-
const publicClient = (0,
|
|
3450
|
+
const publicClient = (0, import_viem6.createPublicClient)({
|
|
3491
3451
|
chain: VIEM_CHAIN2[chainId],
|
|
3492
|
-
transport: (0,
|
|
3452
|
+
transport: (0, import_viem6.custom)(provider)
|
|
3493
3453
|
});
|
|
3494
3454
|
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3495
3455
|
hash,
|
|
@@ -3598,7 +3558,7 @@ var OwneySDK = class {
|
|
|
3598
3558
|
};
|
|
3599
3559
|
|
|
3600
3560
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
3601
|
-
var
|
|
3561
|
+
var import_viem7 = require("viem");
|
|
3602
3562
|
var import_siwe = require("siwe");
|
|
3603
3563
|
var import_sdk2 = require("@zyfai/sdk");
|
|
3604
3564
|
|
|
@@ -3725,7 +3685,7 @@ function buildSIWXConfig(deps) {
|
|
|
3725
3685
|
issuedAt,
|
|
3726
3686
|
toString() {
|
|
3727
3687
|
return new import_siwe.SiweMessage({
|
|
3728
|
-
address: (0,
|
|
3688
|
+
address: (0, import_viem7.getAddress)(accountAddress),
|
|
3729
3689
|
chainId: numericChainId(chainId),
|
|
3730
3690
|
domain,
|
|
3731
3691
|
uri,
|
package/dist/index.d.cts
CHANGED
|
@@ -30,6 +30,17 @@ interface OwneySDKConfig {
|
|
|
30
30
|
* already-deployed app: set `globalThis.__OWNEY_DEBUG__ = true` in devtools.
|
|
31
31
|
*/
|
|
32
32
|
debug?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Absolute URL of an ERC-7677 paymaster service. When set, deposits go out as
|
|
35
|
+
* an EIP-5792 `wallet_sendCalls` with `capabilities.paymasterService`, so a
|
|
36
|
+
* wallet that advertises sponsorship pays no native gas — the case EIP-3009
|
|
37
|
+
* cannot serve, because USDC verifies it with `ecrecover` and a smart account
|
|
38
|
+
* signs via ERC-1271.
|
|
39
|
+
*
|
|
40
|
+
* MUST be absolute: the wallet fetches this URL itself, so a relative path is
|
|
41
|
+
* unresolvable. Omit it and every rail behaves exactly as before.
|
|
42
|
+
*/
|
|
43
|
+
paymasterServiceUrl?: string;
|
|
33
44
|
}
|
|
34
45
|
interface ConnectionState {
|
|
35
46
|
provider: any;
|
|
@@ -393,6 +404,8 @@ declare class OwneySDK {
|
|
|
393
404
|
private referralSource?;
|
|
394
405
|
private cachedSponsoredCallback;
|
|
395
406
|
private cachedWethSponsoredCallback;
|
|
407
|
+
private paymasterServiceUrl?;
|
|
408
|
+
private cachedSponsoredCallsCallbacks;
|
|
396
409
|
private initializingAgentsPromise;
|
|
397
410
|
constructor(config: OwneySDKConfig);
|
|
398
411
|
/**
|
|
@@ -433,6 +446,13 @@ declare class OwneySDK {
|
|
|
433
446
|
* deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
434
447
|
* `PermitTransferFrom` instead of an EIP-3009 authorization.
|
|
435
448
|
*/
|
|
449
|
+
private getDefaultSponsoredCallsCallback;
|
|
450
|
+
/**
|
|
451
|
+
* Lazily builds (and caches) the default Permit2 sponsored WETH deposit
|
|
452
|
+
* callback used when the caller omits `depositCallback` for a WETH deposit.
|
|
453
|
+
* Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
454
|
+
* `PermitTransferFrom` instead of an EIP-3009 authorization.
|
|
455
|
+
*/
|
|
436
456
|
private getDefaultWethSponsoredCallback;
|
|
437
457
|
private getAgent;
|
|
438
458
|
private getActiveAgents;
|
|
@@ -609,7 +629,7 @@ declare class OwneySDK {
|
|
|
609
629
|
getAllocationApy({ agentId, }?: AllocationApyOptions): Promise<OwneyAllocationApy>;
|
|
610
630
|
}
|
|
611
631
|
|
|
612
|
-
type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "WALLET_NOT_DEPLOYED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "AGENT_DISABLED" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "CHAIN_MISMATCH" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "DEPOSIT_INSUFFICIENT_BALANCE" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "SPONSOR_REQUEST_FAILED" | "PERMIT2_APPROVAL_REQUIRED" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
|
|
632
|
+
type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "WALLET_NOT_DEPLOYED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "AGENT_DISABLED" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "CHAIN_MISMATCH" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "DEPOSIT_INSUFFICIENT_BALANCE" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "SPONSOR_REQUEST_FAILED" | "PERMIT2_APPROVAL_REQUIRED" | "SPONSORED_CALLS_UNSUPPORTED" | "SPONSORED_CALLS_NO_ID" | "SPONSORED_CALLS_NO_RECEIPT" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
|
|
613
633
|
declare class OwneyError extends Error {
|
|
614
634
|
readonly code: OwneyErrorCode;
|
|
615
635
|
readonly details?: Record<string, unknown>;
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,17 @@ interface OwneySDKConfig {
|
|
|
30
30
|
* already-deployed app: set `globalThis.__OWNEY_DEBUG__ = true` in devtools.
|
|
31
31
|
*/
|
|
32
32
|
debug?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Absolute URL of an ERC-7677 paymaster service. When set, deposits go out as
|
|
35
|
+
* an EIP-5792 `wallet_sendCalls` with `capabilities.paymasterService`, so a
|
|
36
|
+
* wallet that advertises sponsorship pays no native gas — the case EIP-3009
|
|
37
|
+
* cannot serve, because USDC verifies it with `ecrecover` and a smart account
|
|
38
|
+
* signs via ERC-1271.
|
|
39
|
+
*
|
|
40
|
+
* MUST be absolute: the wallet fetches this URL itself, so a relative path is
|
|
41
|
+
* unresolvable. Omit it and every rail behaves exactly as before.
|
|
42
|
+
*/
|
|
43
|
+
paymasterServiceUrl?: string;
|
|
33
44
|
}
|
|
34
45
|
interface ConnectionState {
|
|
35
46
|
provider: any;
|
|
@@ -393,6 +404,8 @@ declare class OwneySDK {
|
|
|
393
404
|
private referralSource?;
|
|
394
405
|
private cachedSponsoredCallback;
|
|
395
406
|
private cachedWethSponsoredCallback;
|
|
407
|
+
private paymasterServiceUrl?;
|
|
408
|
+
private cachedSponsoredCallsCallbacks;
|
|
396
409
|
private initializingAgentsPromise;
|
|
397
410
|
constructor(config: OwneySDKConfig);
|
|
398
411
|
/**
|
|
@@ -433,6 +446,13 @@ declare class OwneySDK {
|
|
|
433
446
|
* deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
434
447
|
* `PermitTransferFrom` instead of an EIP-3009 authorization.
|
|
435
448
|
*/
|
|
449
|
+
private getDefaultSponsoredCallsCallback;
|
|
450
|
+
/**
|
|
451
|
+
* Lazily builds (and caches) the default Permit2 sponsored WETH deposit
|
|
452
|
+
* callback used when the caller omits `depositCallback` for a WETH deposit.
|
|
453
|
+
* Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
454
|
+
* `PermitTransferFrom` instead of an EIP-3009 authorization.
|
|
455
|
+
*/
|
|
436
456
|
private getDefaultWethSponsoredCallback;
|
|
437
457
|
private getAgent;
|
|
438
458
|
private getActiveAgents;
|
|
@@ -609,7 +629,7 @@ declare class OwneySDK {
|
|
|
609
629
|
getAllocationApy({ agentId, }?: AllocationApyOptions): Promise<OwneyAllocationApy>;
|
|
610
630
|
}
|
|
611
631
|
|
|
612
|
-
type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "WALLET_NOT_DEPLOYED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "AGENT_DISABLED" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "CHAIN_MISMATCH" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "DEPOSIT_INSUFFICIENT_BALANCE" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "SPONSOR_REQUEST_FAILED" | "PERMIT2_APPROVAL_REQUIRED" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
|
|
632
|
+
type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "WALLET_NOT_DEPLOYED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "AGENT_DISABLED" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "CHAIN_MISMATCH" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "DEPOSIT_INSUFFICIENT_BALANCE" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "SPONSOR_REQUEST_FAILED" | "PERMIT2_APPROVAL_REQUIRED" | "SPONSORED_CALLS_UNSUPPORTED" | "SPONSORED_CALLS_NO_ID" | "SPONSORED_CALLS_NO_RECEIPT" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
|
|
613
633
|
declare class OwneyError extends Error {
|
|
614
634
|
readonly code: OwneyErrorCode;
|
|
615
635
|
readonly details?: Record<string, unknown>;
|
package/dist/index.js
CHANGED
|
@@ -846,12 +846,6 @@ var ZyfaiAgent = class {
|
|
|
846
846
|
connectedAddress = null;
|
|
847
847
|
connectedChainId = null;
|
|
848
848
|
smartWalletCache = /* @__PURE__ */ new Map();
|
|
849
|
-
/**
|
|
850
|
-
* `${eoa}:${chainId}` keys we have already tried createSessionKey for. Guards
|
|
851
|
-
* against re-running it on every activation/deposit when Zyfai's
|
|
852
|
-
* `hasActiveSessionKey` does not flip to true — see ensureSessionKey.
|
|
853
|
-
*/
|
|
854
|
-
sessionKeyAttempts = /* @__PURE__ */ new Set();
|
|
855
849
|
connectAuthPromise = null;
|
|
856
850
|
rpcUrls;
|
|
857
851
|
publicClients = /* @__PURE__ */ new Map();
|
|
@@ -1309,74 +1303,10 @@ var ZyfaiAgent = class {
|
|
|
1309
1303
|
* recoverable (ensureWalletDeployed tries the other deploy call), but
|
|
1310
1304
|
* failing the deposit outright is not.
|
|
1311
1305
|
*/
|
|
1312
|
-
/**
|
|
1313
|
-
* Zyfai's own answer to "what kind of wallet is this, and can the agent act on
|
|
1314
|
-
* it", read from the positions payload (`GET /data/position`, which
|
|
1315
|
-
* `getPositions` wraps).
|
|
1316
|
-
*
|
|
1317
|
-
* `predeployed` and `hasActiveSessionKey` are both returned at runtime but
|
|
1318
|
-
* declared on neither `Portfolio` nor their docs, hence the cast. Verified
|
|
1319
|
-
* live: an old wallet reports `{predeployed: false, hasActiveSessionKey:
|
|
1320
|
-
* false}` while a pool wallet reports `{true, true}`.
|
|
1321
|
-
*
|
|
1322
|
-
* Preferred over the SDK's private `isPredeployed`, which is cached from the
|
|
1323
|
-
* login response and can go stale mid-session. Returns an empty object on any
|
|
1324
|
-
* failure so every caller degrades to its own fallback.
|
|
1325
|
-
*/
|
|
1326
|
-
async readWalletState(ownerAddress) {
|
|
1327
|
-
try {
|
|
1328
|
-
const { portfolio } = await this.sdk.getPositions(ownerAddress);
|
|
1329
|
-
const raw = portfolio;
|
|
1330
|
-
debugLog("zyfai:onboard", "wallet state from getPositions", {
|
|
1331
|
-
predeployed: raw?.predeployed,
|
|
1332
|
-
hasActiveSessionKey: raw?.hasActiveSessionKey
|
|
1333
|
-
});
|
|
1334
|
-
return {
|
|
1335
|
-
predeployed: raw?.predeployed,
|
|
1336
|
-
hasActiveSessionKey: raw?.hasActiveSessionKey
|
|
1337
|
-
};
|
|
1338
|
-
} catch (error) {
|
|
1339
|
-
console.warn(
|
|
1340
|
-
"[zyfai] getPositions wallet-state read failed (non-fatal):",
|
|
1341
|
-
error instanceof Error ? error.message : String(error)
|
|
1342
|
-
);
|
|
1343
|
-
return {};
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
1306
|
async isPredeployedPoolWallet(ownerAddress, smartWallet) {
|
|
1347
|
-
const { predeployed } = await this.readWalletState(ownerAddress);
|
|
1348
|
-
if (typeof predeployed === "boolean") {
|
|
1349
|
-
debugLog(
|
|
1350
|
-
"zyfai:onboard",
|
|
1351
|
-
`wallet class: ${predeployed ? "PREDEPLOYED (new flow)" : "OLD (legacy flow)"}`,
|
|
1352
|
-
{ source: "getPositions.predeployed", predeployed }
|
|
1353
|
-
);
|
|
1354
|
-
return predeployed;
|
|
1355
|
-
}
|
|
1356
|
-
const internals = this.sdk;
|
|
1357
|
-
if (typeof internals.isPredeployed === "boolean") {
|
|
1358
|
-
debugLog(
|
|
1359
|
-
"zyfai:onboard",
|
|
1360
|
-
`wallet class: ${internals.isPredeployed ? "PREDEPLOYED (new flow)" : "OLD (legacy flow)"}`,
|
|
1361
|
-
{
|
|
1362
|
-
source: "login response (cached)",
|
|
1363
|
-
predeployed: internals.isPredeployed
|
|
1364
|
-
}
|
|
1365
|
-
);
|
|
1366
|
-
return internals.isPredeployed;
|
|
1367
|
-
}
|
|
1368
1307
|
try {
|
|
1369
1308
|
const byEoa = await this.sdk.getSmartWalletByEOA(ownerAddress);
|
|
1370
|
-
|
|
1371
|
-
debugLog(
|
|
1372
|
-
"zyfai:onboard",
|
|
1373
|
-
`wallet class: ${isPool ? "PREDEPLOYED (new flow)" : "OLD (legacy flow)"}`,
|
|
1374
|
-
{
|
|
1375
|
-
source: "getSmartWalletByEOA fallback",
|
|
1376
|
-
poolRecord: byEoa?.smartWallet
|
|
1377
|
-
}
|
|
1378
|
-
);
|
|
1379
|
-
return isPool;
|
|
1309
|
+
return typeof byEoa?.smartWallet === "string" && byEoa.smartWallet.toLowerCase() === smartWallet.toLowerCase();
|
|
1380
1310
|
} catch (error) {
|
|
1381
1311
|
console.warn(
|
|
1382
1312
|
"[zyfai] getSmartWalletByEOA failed; assuming a legacy wallet:",
|
|
@@ -1414,24 +1344,8 @@ var ZyfaiAgent = class {
|
|
|
1414
1344
|
ownerAddress,
|
|
1415
1345
|
chainId
|
|
1416
1346
|
);
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
isDeployed: wallet.isDeployed
|
|
1420
|
-
});
|
|
1421
|
-
if (!wallet.isDeployed) {
|
|
1422
|
-
debugLog(
|
|
1423
|
-
"zyfai:onboard",
|
|
1424
|
-
"Safe not deployed on this chain; activation does nothing (deposit owns deploys)"
|
|
1425
|
-
);
|
|
1426
|
-
return;
|
|
1427
|
-
}
|
|
1428
|
-
const { hasActiveSessionKey } = await this.readWalletState(ownerAddress);
|
|
1429
|
-
await this.ensureSessionKey(
|
|
1430
|
-
ownerAddress,
|
|
1431
|
-
chainId,
|
|
1432
|
-
wallet.address,
|
|
1433
|
-
hasActiveSessionKey
|
|
1434
|
-
);
|
|
1347
|
+
if (!wallet.isDeployed) return;
|
|
1348
|
+
await this.ensureSessionKey(ownerAddress, chainId, wallet.address);
|
|
1435
1349
|
} catch (error) {
|
|
1436
1350
|
console.warn(
|
|
1437
1351
|
"[zyfai] session-key check at activation failed (non-fatal):",
|
|
@@ -1439,51 +1353,20 @@ var ZyfaiAgent = class {
|
|
|
1439
1353
|
);
|
|
1440
1354
|
}
|
|
1441
1355
|
}
|
|
1442
|
-
async ensureSessionKey(ownerAddress, chainId, smartWallet
|
|
1443
|
-
const attemptKey = `${ownerAddress.toLowerCase()}:${chainId}`;
|
|
1444
|
-
if (this.sessionKeyAttempts.has(attemptKey)) {
|
|
1445
|
-
debugLog(
|
|
1446
|
-
"zyfai:onboard",
|
|
1447
|
-
"session key: already attempted this session, skipping",
|
|
1448
|
-
{ chainId }
|
|
1449
|
-
);
|
|
1450
|
-
return;
|
|
1451
|
-
}
|
|
1356
|
+
async ensureSessionKey(ownerAddress, chainId, smartWallet) {
|
|
1452
1357
|
try {
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
if (hasActiveSessionKey === void 0) {
|
|
1462
|
-
const installed = await this.readSmartSessionsInstalled(
|
|
1463
|
-
chainId,
|
|
1464
|
-
smartWallet
|
|
1465
|
-
);
|
|
1466
|
-
debugLog(
|
|
1467
|
-
"zyfai:onboard",
|
|
1468
|
-
`session key: Zyfai state unavailable; on-chain validator ${installed ? "INSTALLED" : "MISSING"}`,
|
|
1469
|
-
{ smartWallet, chainId }
|
|
1470
|
-
);
|
|
1471
|
-
if (installed) return;
|
|
1472
|
-
}
|
|
1473
|
-
this.sessionKeyAttempts.add(attemptKey);
|
|
1358
|
+
const installed = await this.getPublicClient(chainId).readContract({
|
|
1359
|
+
address: smartWallet,
|
|
1360
|
+
abi: ERC7579_IS_MODULE_INSTALLED_ABI,
|
|
1361
|
+
functionName: "isModuleInstalled",
|
|
1362
|
+
// moduleTypeId 1 = validator (ERC-7579)
|
|
1363
|
+
args: [1n, SMART_SESSIONS_VALIDATOR, "0x"]
|
|
1364
|
+
});
|
|
1365
|
+
if (installed) return;
|
|
1474
1366
|
console.warn(
|
|
1475
|
-
`[zyfai]
|
|
1367
|
+
`[zyfai] Safe ${smartWallet} has no smart-sessions validator on chain ${chainId}; creating a session key so the agent can allocate.`
|
|
1476
1368
|
);
|
|
1477
|
-
debugLog("zyfai:onboard", "session key: calling createSessionKey", {
|
|
1478
|
-
chainId,
|
|
1479
|
-
smartWallet,
|
|
1480
|
-
hasActiveSessionKey
|
|
1481
|
-
});
|
|
1482
1369
|
await this.sdk.createSessionKey(ownerAddress, chainId);
|
|
1483
|
-
debugLog("zyfai:onboard", "session key: createSessionKey succeeded", {
|
|
1484
|
-
chainId,
|
|
1485
|
-
smartWallet
|
|
1486
|
-
});
|
|
1487
1370
|
} catch (error) {
|
|
1488
1371
|
console.warn(
|
|
1489
1372
|
"[zyfai] createSessionKey skipped (non-fatal):",
|
|
@@ -1491,16 +1374,6 @@ var ZyfaiAgent = class {
|
|
|
1491
1374
|
);
|
|
1492
1375
|
}
|
|
1493
1376
|
}
|
|
1494
|
-
/** Ground truth for "can Zyfai's automation act on this Safe, on this chain". */
|
|
1495
|
-
async readSmartSessionsInstalled(chainId, smartWallet) {
|
|
1496
|
-
return this.getPublicClient(chainId).readContract({
|
|
1497
|
-
address: smartWallet,
|
|
1498
|
-
abi: ERC7579_IS_MODULE_INSTALLED_ABI,
|
|
1499
|
-
functionName: "isModuleInstalled",
|
|
1500
|
-
// moduleTypeId 1 = validator (ERC-7579)
|
|
1501
|
-
args: [1n, SMART_SESSIONS_VALIDATOR, "0x"]
|
|
1502
|
-
});
|
|
1503
|
-
}
|
|
1504
1377
|
/**
|
|
1505
1378
|
* Guarantee the user's Safe has code on `chainId` before anything transfers
|
|
1506
1379
|
* funds into it, and return its address.
|
|
@@ -1521,24 +1394,8 @@ var ZyfaiAgent = class {
|
|
|
1521
1394
|
*/
|
|
1522
1395
|
async ensureWalletDeployed(ownerAddress, chainId) {
|
|
1523
1396
|
const wallet = await this.sdk.getSmartWalletAddress(ownerAddress, chainId);
|
|
1524
|
-
debugLog("zyfai:onboard", `--- deposit onboarding, chain ${chainId} ---`, {
|
|
1525
|
-
eoa: ownerAddress,
|
|
1526
|
-
smartWallet: wallet.address,
|
|
1527
|
-
isDeployed: wallet.isDeployed,
|
|
1528
|
-
isOwner: wallet.isOwner
|
|
1529
|
-
});
|
|
1530
1397
|
if (wallet.isDeployed) {
|
|
1531
|
-
|
|
1532
|
-
"zyfai:onboard",
|
|
1533
|
-
"Safe already has code -> no deploy needed, checking the session key"
|
|
1534
|
-
);
|
|
1535
|
-
const { hasActiveSessionKey } = await this.readWalletState(ownerAddress);
|
|
1536
|
-
await this.ensureSessionKey(
|
|
1537
|
-
ownerAddress,
|
|
1538
|
-
chainId,
|
|
1539
|
-
wallet.address,
|
|
1540
|
-
hasActiveSessionKey
|
|
1541
|
-
);
|
|
1398
|
+
await this.ensureSessionKey(ownerAddress, chainId, wallet.address);
|
|
1542
1399
|
return wallet.address;
|
|
1543
1400
|
}
|
|
1544
1401
|
const poolFirst = await this.isPredeployedPoolWallet(
|
|
@@ -1554,27 +1411,14 @@ var ZyfaiAgent = class {
|
|
|
1554
1411
|
this.sdk.deploySafe(ownerAddress, chainId, "aggressive", true)
|
|
1555
1412
|
);
|
|
1556
1413
|
const [first, second] = poolFirst ? [deployAsPool, deployAsLegacy] : [deployAsLegacy, deployAsPool];
|
|
1557
|
-
debugLog(
|
|
1558
|
-
"zyfai:onboard",
|
|
1559
|
-
`Safe has no code on chain ${chainId} -> deploying via ${poolFirst ? "deployOnChains (pool path)" : "deploySafe(createSessionKey=true) (legacy path)"}`
|
|
1560
|
-
);
|
|
1561
1414
|
try {
|
|
1562
1415
|
await first();
|
|
1563
|
-
debugLog(
|
|
1564
|
-
"zyfai:onboard",
|
|
1565
|
-
`${poolFirst ? "deployOnChains" : "deploySafe"} succeeded`
|
|
1566
|
-
);
|
|
1567
1416
|
} catch (error) {
|
|
1568
1417
|
console.warn(
|
|
1569
1418
|
`[zyfai] ${poolFirst ? "deployOnChains" : "deploySafe"} failed on chain ${chainId}; trying the other deploy path:`,
|
|
1570
1419
|
error instanceof Error ? error.message : String(error)
|
|
1571
1420
|
);
|
|
1572
|
-
debugLog(
|
|
1573
|
-
"zyfai:onboard",
|
|
1574
|
-
`falling back to ${poolFirst ? "deploySafe (legacy path)" : "deployOnChains (pool path)"}`
|
|
1575
|
-
);
|
|
1576
1421
|
await second();
|
|
1577
|
-
debugLog("zyfai:onboard", "fallback deploy path succeeded");
|
|
1578
1422
|
}
|
|
1579
1423
|
const redeployed = await this.sdk.getSmartWalletAddress(
|
|
1580
1424
|
ownerAddress,
|
|
@@ -2396,6 +2240,97 @@ function makeSponsoredWethCallback(deps) {
|
|
|
2396
2240
|
};
|
|
2397
2241
|
}
|
|
2398
2242
|
|
|
2243
|
+
// src/lib/sponsored-calls-deposit.ts
|
|
2244
|
+
import { encodeFunctionData, erc20Abi, toHex } from "viem";
|
|
2245
|
+
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
2246
|
+
var DEFAULT_MAX_POLLS = 30;
|
|
2247
|
+
async function paymasterSupported(provider, owner, chainId) {
|
|
2248
|
+
const caps = await provider.request({
|
|
2249
|
+
method: "wallet_getCapabilities",
|
|
2250
|
+
params: [owner]
|
|
2251
|
+
});
|
|
2252
|
+
const forChain = caps?.[toHex(chainId)] ?? caps?.[String(chainId)];
|
|
2253
|
+
return Boolean(forChain?.paymasterService?.supported);
|
|
2254
|
+
}
|
|
2255
|
+
function makeSponsoredCallsCallback(deps) {
|
|
2256
|
+
const pollIntervalMs = deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
2257
|
+
const maxPolls = deps.maxPolls ?? DEFAULT_MAX_POLLS;
|
|
2258
|
+
const absolutePaymasterUrl = () => {
|
|
2259
|
+
const configured = deps.paymasterServiceUrl;
|
|
2260
|
+
if (/^https?:\/\//i.test(configured)) return configured;
|
|
2261
|
+
const origin = globalThis.location?.origin;
|
|
2262
|
+
if (!origin) {
|
|
2263
|
+
throw new OwneyError(
|
|
2264
|
+
"SPONSORED_CALLS_UNSUPPORTED",
|
|
2265
|
+
`paymasterServiceUrl "${configured}" is relative and no page origin is available to resolve it.`
|
|
2266
|
+
);
|
|
2267
|
+
}
|
|
2268
|
+
return new URL(configured, origin).toString();
|
|
2269
|
+
};
|
|
2270
|
+
return async (smartWallet, chainId, amount) => {
|
|
2271
|
+
const cid = chainId;
|
|
2272
|
+
const token = deps.tokenAddressByChain[cid];
|
|
2273
|
+
if (!token) {
|
|
2274
|
+
throw new OwneyError(
|
|
2275
|
+
"CHAIN_UNSUPPORTED",
|
|
2276
|
+
`No sponsored token configured for chain ${chainId}`
|
|
2277
|
+
);
|
|
2278
|
+
}
|
|
2279
|
+
if (!await paymasterSupported(deps.provider, deps.ownerAddress, chainId)) {
|
|
2280
|
+
throw new OwneyError(
|
|
2281
|
+
"SPONSORED_CALLS_UNSUPPORTED",
|
|
2282
|
+
`Wallet does not advertise paymasterService on chain ${chainId}.`,
|
|
2283
|
+
{ chainId }
|
|
2284
|
+
);
|
|
2285
|
+
}
|
|
2286
|
+
const data = encodeFunctionData({
|
|
2287
|
+
abi: erc20Abi,
|
|
2288
|
+
functionName: "transfer",
|
|
2289
|
+
args: [smartWallet, BigInt(amount)]
|
|
2290
|
+
});
|
|
2291
|
+
const sendResult = await deps.provider.request({
|
|
2292
|
+
method: "wallet_sendCalls",
|
|
2293
|
+
params: [
|
|
2294
|
+
{
|
|
2295
|
+
version: "2.0.0",
|
|
2296
|
+
from: deps.ownerAddress,
|
|
2297
|
+
chainId: toHex(chainId),
|
|
2298
|
+
atomicRequired: false,
|
|
2299
|
+
calls: [{ to: token, value: "0x0", data }],
|
|
2300
|
+
capabilities: {
|
|
2301
|
+
paymasterService: { url: absolutePaymasterUrl() }
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
]
|
|
2305
|
+
});
|
|
2306
|
+
deps.onApproved?.();
|
|
2307
|
+
const callsId = typeof sendResult === "string" ? sendResult : sendResult?.id;
|
|
2308
|
+
if (!callsId) {
|
|
2309
|
+
throw new OwneyError(
|
|
2310
|
+
"SPONSORED_CALLS_NO_ID",
|
|
2311
|
+
"Wallet accepted wallet_sendCalls but returned no calls id.",
|
|
2312
|
+
{ chainId }
|
|
2313
|
+
);
|
|
2314
|
+
}
|
|
2315
|
+
for (let attempt = 0; attempt < maxPolls; attempt += 1) {
|
|
2316
|
+
const status = await deps.provider.request({
|
|
2317
|
+
method: "wallet_getCallsStatus",
|
|
2318
|
+
params: [callsId]
|
|
2319
|
+
});
|
|
2320
|
+
const txHash = status?.receipts?.[0]?.transactionHash;
|
|
2321
|
+
if (txHash) return txHash;
|
|
2322
|
+
if (pollIntervalMs > 0) {
|
|
2323
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
throw new OwneyError(
|
|
2327
|
+
"SPONSORED_CALLS_NO_RECEIPT",
|
|
2328
|
+
`No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
|
|
2329
|
+
{ chainId, callsId }
|
|
2330
|
+
);
|
|
2331
|
+
};
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2399
2334
|
// src/client.ts
|
|
2400
2335
|
function encodeMultiAgentCursor(map) {
|
|
2401
2336
|
return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
|
|
@@ -2457,12 +2392,15 @@ var OwneySDK = class {
|
|
|
2457
2392
|
referralSource;
|
|
2458
2393
|
cachedSponsoredCallback = null;
|
|
2459
2394
|
cachedWethSponsoredCallback = null;
|
|
2395
|
+
paymasterServiceUrl;
|
|
2396
|
+
cachedSponsoredCallsCallbacks = /* @__PURE__ */ new Map();
|
|
2460
2397
|
initializingAgentsPromise = null;
|
|
2461
2398
|
constructor(config) {
|
|
2462
2399
|
this.apiKey = config.apiKey;
|
|
2463
2400
|
if (config.debug) setOwneyDebug(true);
|
|
2464
2401
|
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
2465
2402
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
2403
|
+
this.paymasterServiceUrl = config.paymasterServiceUrl;
|
|
2466
2404
|
this.referralSource = config.referralSource;
|
|
2467
2405
|
}
|
|
2468
2406
|
/**
|
|
@@ -2485,6 +2423,7 @@ var OwneySDK = class {
|
|
|
2485
2423
|
this.state = { provider, walletAddress, chainId: null };
|
|
2486
2424
|
this.cachedSponsoredCallback = null;
|
|
2487
2425
|
this.cachedWethSponsoredCallback = null;
|
|
2426
|
+
this.cachedSponsoredCallsCallbacks.clear();
|
|
2488
2427
|
}
|
|
2489
2428
|
/**
|
|
2490
2429
|
* Disconnect from all agents and clear connection state.
|
|
@@ -2499,6 +2438,7 @@ var OwneySDK = class {
|
|
|
2499
2438
|
this.state = null;
|
|
2500
2439
|
this.cachedSponsoredCallback = null;
|
|
2501
2440
|
this.cachedWethSponsoredCallback = null;
|
|
2441
|
+
this.cachedSponsoredCallsCallbacks.clear();
|
|
2502
2442
|
}
|
|
2503
2443
|
isAgentDisabled(agentId) {
|
|
2504
2444
|
return this.disabledAgents.has(agentId);
|
|
@@ -2580,6 +2520,26 @@ var OwneySDK = class {
|
|
|
2580
2520
|
* deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
2581
2521
|
* `PermitTransferFrom` instead of an EIP-3009 authorization.
|
|
2582
2522
|
*/
|
|
2523
|
+
getDefaultSponsoredCallsCallback(asset, onApproved) {
|
|
2524
|
+
const cached = this.cachedSponsoredCallsCallbacks.get(asset);
|
|
2525
|
+
if (!onApproved && cached) return cached;
|
|
2526
|
+
const provider = this.requireConnectedProvider();
|
|
2527
|
+
const callback = makeSponsoredCallsCallback({
|
|
2528
|
+
provider,
|
|
2529
|
+
ownerAddress: this.state.walletAddress,
|
|
2530
|
+
paymasterServiceUrl: this.paymasterServiceUrl,
|
|
2531
|
+
onApproved,
|
|
2532
|
+
tokenAddressByChain: asset === "WETH" ? SPONSORED_WETH_BY_CHAIN : SPONSORED_USDC_BY_CHAIN
|
|
2533
|
+
});
|
|
2534
|
+
if (!onApproved) this.cachedSponsoredCallsCallbacks.set(asset, callback);
|
|
2535
|
+
return callback;
|
|
2536
|
+
}
|
|
2537
|
+
/**
|
|
2538
|
+
* Lazily builds (and caches) the default Permit2 sponsored WETH deposit
|
|
2539
|
+
* callback used when the caller omits `depositCallback` for a WETH deposit.
|
|
2540
|
+
* Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
2541
|
+
* `PermitTransferFrom` instead of an EIP-3009 authorization.
|
|
2542
|
+
*/
|
|
2583
2543
|
getDefaultWethSponsoredCallback(onApproved) {
|
|
2584
2544
|
if (!onApproved && this.cachedWethSponsoredCallback)
|
|
2585
2545
|
return this.cachedWethSponsoredCallback;
|
|
@@ -2802,7 +2762,7 @@ var OwneySDK = class {
|
|
|
2802
2762
|
}
|
|
2803
2763
|
const state = this.requireState();
|
|
2804
2764
|
const chainId = this.requireChainId();
|
|
2805
|
-
const effectiveCallback = depositCallback ?? (asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved) : this.getDefaultSponsoredCallback(onApproved));
|
|
2765
|
+
const effectiveCallback = depositCallback ?? (this.paymasterServiceUrl ? this.getDefaultSponsoredCallsCallback(asset, onApproved) : asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved) : this.getDefaultSponsoredCallback(onApproved));
|
|
2806
2766
|
if (agentId) {
|
|
2807
2767
|
if (this.isAgentDisabled(agentId)) {
|
|
2808
2768
|
throw new OwneyError(
|