@owney/sdk 0.6.4 → 0.6.5-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 +428 -22
- package/dist/index.d.cts +119 -61
- package/dist/index.d.ts +119 -61
- package/dist/index.js +421 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ var P_SUPPORTED_CHAINS = {
|
|
|
8
8
|
chainIds: [8453, 42161],
|
|
9
9
|
chains: ["BASE", "ARBITRUM"]
|
|
10
10
|
};
|
|
11
|
-
var SUPPORTED_TOKENS = ["USDC", "USDT"];
|
|
11
|
+
var SUPPORTED_TOKENS = ["USDC", "USDT", "WETH"];
|
|
12
12
|
var SUPPORTED_CHAIN_IDS = P_SUPPORTED_CHAINS.chainIds;
|
|
13
13
|
var SUPPORTED_CHAINS = P_SUPPORTED_CHAINS.chains;
|
|
14
14
|
var SupportedAssets = [
|
|
@@ -29,6 +29,18 @@ var SupportedAssets = [
|
|
|
29
29
|
symbol: "USDT",
|
|
30
30
|
chainId: 42161,
|
|
31
31
|
decimals: 6
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
address: `0x4200000000000000000000000000000000000006`,
|
|
35
|
+
symbol: "WETH",
|
|
36
|
+
chainId: 8453,
|
|
37
|
+
decimals: 18
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
address: `0x82aF49447D8a07e3bd95BD0d56f35241523fBab1`,
|
|
41
|
+
symbol: "WETH",
|
|
42
|
+
chainId: 42161,
|
|
43
|
+
decimals: 18
|
|
32
44
|
}
|
|
33
45
|
];
|
|
34
46
|
|
|
@@ -513,11 +525,26 @@ var clearSession = (address, chainId) => {
|
|
|
513
525
|
}
|
|
514
526
|
};
|
|
515
527
|
|
|
528
|
+
// src/agents/zyfai/autoSelect.ts
|
|
529
|
+
function buildAutoSelectUpdate(current, asset) {
|
|
530
|
+
if (current.autoSelectProtocols === true)
|
|
531
|
+
return null;
|
|
532
|
+
return {
|
|
533
|
+
asset,
|
|
534
|
+
autoSelectProtocols: true,
|
|
535
|
+
...current.chains ? { chains: current.chains } : {}
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
|
|
516
539
|
// src/agents/zyfai/zyfai.agent.ts
|
|
517
540
|
var DEFAULT_ZYFAI_RPC_URLS = {
|
|
518
541
|
8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
|
|
519
542
|
42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
|
|
520
543
|
};
|
|
544
|
+
var WETH_ADDRESS_BY_CHAIN = {
|
|
545
|
+
8453: "0x4200000000000000000000000000000000000006",
|
|
546
|
+
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"
|
|
547
|
+
};
|
|
521
548
|
var VIEM_CHAIN = { 8453: base, 42161: arbitrum };
|
|
522
549
|
var ZyfaiAgent = class {
|
|
523
550
|
id = "zyfai";
|
|
@@ -526,12 +553,18 @@ var ZyfaiAgent = class {
|
|
|
526
553
|
{
|
|
527
554
|
chainId: 8453,
|
|
528
555
|
chain: "BASE",
|
|
529
|
-
assets: [
|
|
556
|
+
assets: [
|
|
557
|
+
{ symbol: "USDC", minDepositAmount: "1500000" },
|
|
558
|
+
{ symbol: "WETH", minDepositAmount: "500000000000000" }
|
|
559
|
+
]
|
|
530
560
|
},
|
|
531
561
|
{
|
|
532
562
|
chainId: 42161,
|
|
533
563
|
chain: "ARBITRUM",
|
|
534
|
-
assets: [
|
|
564
|
+
assets: [
|
|
565
|
+
{ symbol: "USDC", minDepositAmount: "1500000" },
|
|
566
|
+
{ symbol: "WETH", minDepositAmount: "500000000000000" }
|
|
567
|
+
]
|
|
535
568
|
}
|
|
536
569
|
];
|
|
537
570
|
sdk;
|
|
@@ -726,6 +759,22 @@ var ZyfaiAgent = class {
|
|
|
726
759
|
this.smartWalletCache.set(`${address}_${validChainId}`, wallet.address);
|
|
727
760
|
if (!wallet.isDeployed) {
|
|
728
761
|
await this.sdk.deploySafe(address, validChainId, "aggressive", true);
|
|
762
|
+
try {
|
|
763
|
+
if (validChainId === 42161) {
|
|
764
|
+
await this.sdk.updateUserProfile({
|
|
765
|
+
asset: "WETH",
|
|
766
|
+
chains: [validChainId],
|
|
767
|
+
protocols: ["c24a33fa-4a87-4376-b756-93bd3957d3ea"],
|
|
768
|
+
// AAVE protocol ID
|
|
769
|
+
autoSelectProtocols: false
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
} catch (err) {
|
|
773
|
+
console.warn(
|
|
774
|
+
"Failed to configure AAVE protocol after Safe deployment:",
|
|
775
|
+
err instanceof Error ? err.message : String(err)
|
|
776
|
+
);
|
|
777
|
+
}
|
|
729
778
|
}
|
|
730
779
|
const user = await this.sdk.getUserDetails();
|
|
731
780
|
if (!user.hasActiveSessionKey) {
|
|
@@ -767,7 +816,7 @@ var ZyfaiAgent = class {
|
|
|
767
816
|
throw error;
|
|
768
817
|
}
|
|
769
818
|
}
|
|
770
|
-
async deposit(state, chainId, amount, depositCallback) {
|
|
819
|
+
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
771
820
|
try {
|
|
772
821
|
await this.connectFull(state, chainId);
|
|
773
822
|
const validChainId = isValidChainId(chainId);
|
|
@@ -781,13 +830,15 @@ var ZyfaiAgent = class {
|
|
|
781
830
|
validChainId,
|
|
782
831
|
amount
|
|
783
832
|
);
|
|
784
|
-
|
|
833
|
+
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
834
|
+
await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
|
|
785
835
|
return { txHash, smartWallet: wallet.address, amount };
|
|
786
836
|
}
|
|
787
837
|
const raw = await this.sdk.depositFunds(
|
|
788
838
|
this.getAddress(),
|
|
789
839
|
validChainId,
|
|
790
|
-
amount
|
|
840
|
+
amount,
|
|
841
|
+
asset
|
|
791
842
|
);
|
|
792
843
|
return mapDeposit(raw);
|
|
793
844
|
} catch (error) {
|
|
@@ -883,6 +934,21 @@ var ZyfaiAgent = class {
|
|
|
883
934
|
}
|
|
884
935
|
return mapUserProfile(raw, this.connectedAddress);
|
|
885
936
|
}
|
|
937
|
+
async ensureAutoSelectProtocols(state, chainId, asset) {
|
|
938
|
+
await this.connectAuth(state, chainId);
|
|
939
|
+
const current = await this.sdk.getUserDetails(asset);
|
|
940
|
+
const update = buildAutoSelectUpdate(
|
|
941
|
+
{
|
|
942
|
+
autoSelectProtocols: current.autoSelectProtocols,
|
|
943
|
+
chains: current.chains
|
|
944
|
+
},
|
|
945
|
+
asset
|
|
946
|
+
);
|
|
947
|
+
if (!update)
|
|
948
|
+
return false;
|
|
949
|
+
await this.sdk.updateUserProfile(update);
|
|
950
|
+
return true;
|
|
951
|
+
}
|
|
886
952
|
// --- IAgent: Discovery (no wallet required) ---
|
|
887
953
|
async getAgentApy(days, options) {
|
|
888
954
|
const raw = await this.sdk.getAPYPerStrategy(
|
|
@@ -1162,6 +1228,70 @@ async function postSponsorTransferAuth(input) {
|
|
|
1162
1228
|
}
|
|
1163
1229
|
return parsed.data;
|
|
1164
1230
|
}
|
|
1231
|
+
async function postSponsorPermit2Transfer(input) {
|
|
1232
|
+
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
1233
|
+
let res;
|
|
1234
|
+
try {
|
|
1235
|
+
res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
|
|
1236
|
+
method: "POST",
|
|
1237
|
+
headers: { "content-type": "application/json", "x-owney-api-key": input.apiKey },
|
|
1238
|
+
body: JSON.stringify(input.body)
|
|
1239
|
+
});
|
|
1240
|
+
} catch (networkError) {
|
|
1241
|
+
throw new OwneyError(
|
|
1242
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1243
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
1244
|
+
{ cause: String(networkError), safeToFallback: false }
|
|
1245
|
+
);
|
|
1246
|
+
}
|
|
1247
|
+
const text = await res.text();
|
|
1248
|
+
let parsed = null;
|
|
1249
|
+
try {
|
|
1250
|
+
parsed = JSON.parse(text);
|
|
1251
|
+
} catch {
|
|
1252
|
+
}
|
|
1253
|
+
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
1254
|
+
throw new OwneyError(
|
|
1255
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1256
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
1257
|
+
{
|
|
1258
|
+
statusCode: res.status,
|
|
1259
|
+
responseBody: text.slice(0, 500),
|
|
1260
|
+
safeToFallback: res.status >= 400 && res.status < 500
|
|
1261
|
+
}
|
|
1262
|
+
);
|
|
1263
|
+
}
|
|
1264
|
+
return parsed.data;
|
|
1265
|
+
}
|
|
1266
|
+
async function getSponsorRelayerAddress(input) {
|
|
1267
|
+
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
1268
|
+
let res;
|
|
1269
|
+
try {
|
|
1270
|
+
res = await fetch(`${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`, {
|
|
1271
|
+
headers: { "x-owney-api-key": input.apiKey }
|
|
1272
|
+
});
|
|
1273
|
+
} catch (networkError) {
|
|
1274
|
+
throw new OwneyError(
|
|
1275
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1276
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
1277
|
+
{ cause: String(networkError), safeToFallback: true }
|
|
1278
|
+
);
|
|
1279
|
+
}
|
|
1280
|
+
const text = await res.text();
|
|
1281
|
+
let parsed = null;
|
|
1282
|
+
try {
|
|
1283
|
+
parsed = JSON.parse(text);
|
|
1284
|
+
} catch {
|
|
1285
|
+
}
|
|
1286
|
+
if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
|
|
1287
|
+
throw new OwneyError(
|
|
1288
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1289
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
1290
|
+
{ statusCode: res.status, responseBody: text.slice(0, 500), safeToFallback: true }
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1293
|
+
return parsed.data.relayer;
|
|
1294
|
+
}
|
|
1165
1295
|
|
|
1166
1296
|
// src/lib/sponsored-deposit.ts
|
|
1167
1297
|
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
@@ -1213,6 +1343,114 @@ function makeSponsoredDepositCallback(deps) {
|
|
|
1213
1343
|
};
|
|
1214
1344
|
}
|
|
1215
1345
|
|
|
1346
|
+
// src/lib/permit2.ts
|
|
1347
|
+
import { bytesToHex as bytesToHex2 } from "viem";
|
|
1348
|
+
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
1349
|
+
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
1350
|
+
var ERC20_ALLOWANCE_ABI = [
|
|
1351
|
+
{
|
|
1352
|
+
type: "function",
|
|
1353
|
+
name: "allowance",
|
|
1354
|
+
stateMutability: "view",
|
|
1355
|
+
inputs: [{ name: "owner", type: "address" }, { name: "spender", type: "address" }],
|
|
1356
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
1357
|
+
},
|
|
1358
|
+
{
|
|
1359
|
+
type: "function",
|
|
1360
|
+
name: "approve",
|
|
1361
|
+
stateMutability: "nonpayable",
|
|
1362
|
+
inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }],
|
|
1363
|
+
outputs: [{ name: "", type: "bool" }]
|
|
1364
|
+
}
|
|
1365
|
+
];
|
|
1366
|
+
function buildPermitTransferFromTypedData(input) {
|
|
1367
|
+
return {
|
|
1368
|
+
domain: { name: "Permit2", chainId: input.chainId, verifyingContract: PERMIT2_ADDRESS },
|
|
1369
|
+
types: {
|
|
1370
|
+
PermitTransferFrom: [
|
|
1371
|
+
{ name: "permitted", type: "TokenPermissions" },
|
|
1372
|
+
{ name: "spender", type: "address" },
|
|
1373
|
+
{ name: "nonce", type: "uint256" },
|
|
1374
|
+
{ name: "deadline", type: "uint256" }
|
|
1375
|
+
],
|
|
1376
|
+
TokenPermissions: [
|
|
1377
|
+
{ name: "token", type: "address" },
|
|
1378
|
+
{ name: "amount", type: "uint256" }
|
|
1379
|
+
]
|
|
1380
|
+
},
|
|
1381
|
+
primaryType: "PermitTransferFrom",
|
|
1382
|
+
message: input.message
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
function randomPermit2Nonce() {
|
|
1386
|
+
const bytes = new Uint8Array(32);
|
|
1387
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
1388
|
+
return BigInt(bytesToHex2(bytes));
|
|
1389
|
+
}
|
|
1390
|
+
async function readPermit2Allowance(publicClient, token, owner) {
|
|
1391
|
+
return publicClient.readContract({
|
|
1392
|
+
address: token,
|
|
1393
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
1394
|
+
functionName: "allowance",
|
|
1395
|
+
args: [owner, PERMIT2_ADDRESS]
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
// src/lib/sponsored-weth-deposit.ts
|
|
1400
|
+
var PERMIT_WINDOW_SECONDS = 15 * 60;
|
|
1401
|
+
function makeSponsoredWethCallback(deps) {
|
|
1402
|
+
const get = deps.httpGet ?? getSponsorRelayerAddress;
|
|
1403
|
+
const post = deps.httpPost ?? postSponsorPermit2Transfer;
|
|
1404
|
+
return async (smartWallet, chainId, amount) => {
|
|
1405
|
+
const cid = chainId;
|
|
1406
|
+
const token = deps.tokenAddressByChain[cid];
|
|
1407
|
+
if (!token) {
|
|
1408
|
+
throw new OwneyError("CHAIN_UNSUPPORTED", `No sponsored WETH configured for chain ${chainId}`);
|
|
1409
|
+
}
|
|
1410
|
+
const amountWei = BigInt(amount);
|
|
1411
|
+
const pub = deps.getPublicClient(cid);
|
|
1412
|
+
const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
|
|
1413
|
+
if (allowance < amountWei) {
|
|
1414
|
+
throw new OwneyError(
|
|
1415
|
+
"PERMIT2_APPROVAL_REQUIRED",
|
|
1416
|
+
"WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
|
|
1417
|
+
{ token, chainId: cid, allowance: allowance.toString(), amount }
|
|
1418
|
+
);
|
|
1419
|
+
}
|
|
1420
|
+
const relayer = await get({ baseUrl: deps.baseUrl, apiKey: deps.apiKey, chainId: cid });
|
|
1421
|
+
const nonce = randomPermit2Nonce();
|
|
1422
|
+
const deadline = BigInt(Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS);
|
|
1423
|
+
const typedData = buildPermitTransferFromTypedData({
|
|
1424
|
+
chainId: cid,
|
|
1425
|
+
message: {
|
|
1426
|
+
permitted: { token, amount: amountWei },
|
|
1427
|
+
spender: relayer,
|
|
1428
|
+
nonce,
|
|
1429
|
+
deadline
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
const wallet = deps.getWalletClient(cid);
|
|
1433
|
+
const signature = await wallet.signTypedData(
|
|
1434
|
+
{ account: deps.ownerAddress, ...typedData }
|
|
1435
|
+
);
|
|
1436
|
+
const result = await post({
|
|
1437
|
+
baseUrl: deps.baseUrl,
|
|
1438
|
+
apiKey: deps.apiKey,
|
|
1439
|
+
body: {
|
|
1440
|
+
chainId: cid,
|
|
1441
|
+
token,
|
|
1442
|
+
from: deps.ownerAddress,
|
|
1443
|
+
to: smartWallet,
|
|
1444
|
+
amount,
|
|
1445
|
+
nonce: nonce.toString(),
|
|
1446
|
+
deadline: deadline.toString(),
|
|
1447
|
+
signature
|
|
1448
|
+
}
|
|
1449
|
+
});
|
|
1450
|
+
return result.txHash;
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1216
1454
|
// src/client.ts
|
|
1217
1455
|
function encodeMultiAgentCursor(map) {
|
|
1218
1456
|
return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
|
|
@@ -1242,6 +1480,13 @@ var VIEM_CHAIN2 = {
|
|
|
1242
1480
|
8453: base2,
|
|
1243
1481
|
42161: arbitrum2
|
|
1244
1482
|
};
|
|
1483
|
+
var SPONSORED_WETH_BY_CHAIN = {
|
|
1484
|
+
8453: "0x4200000000000000000000000000000000000006",
|
|
1485
|
+
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"
|
|
1486
|
+
};
|
|
1487
|
+
function shouldFallbackToUserPaid(error, asset, appCallback) {
|
|
1488
|
+
return asset === "WETH" && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
|
|
1489
|
+
}
|
|
1245
1490
|
var OwneySDK = class {
|
|
1246
1491
|
agents = /* @__PURE__ */ new Map();
|
|
1247
1492
|
activeAgents = /* @__PURE__ */ new Set();
|
|
@@ -1255,6 +1500,7 @@ var OwneySDK = class {
|
|
|
1255
1500
|
apiKey;
|
|
1256
1501
|
zyfaiRpcUrls;
|
|
1257
1502
|
cachedSponsoredCallback = null;
|
|
1503
|
+
cachedWethSponsoredCallback = null;
|
|
1258
1504
|
initializingAgentsPromise = null;
|
|
1259
1505
|
constructor(config) {
|
|
1260
1506
|
this.apiKey = config.apiKey;
|
|
@@ -1279,6 +1525,7 @@ var OwneySDK = class {
|
|
|
1279
1525
|
}
|
|
1280
1526
|
this.state = { provider, walletAddress, chainId: null };
|
|
1281
1527
|
this.cachedSponsoredCallback = null;
|
|
1528
|
+
this.cachedWethSponsoredCallback = null;
|
|
1282
1529
|
}
|
|
1283
1530
|
/**
|
|
1284
1531
|
* Disconnect from all agents and clear connection state.
|
|
@@ -1292,6 +1539,7 @@ var OwneySDK = class {
|
|
|
1292
1539
|
this.disabledAgents.clear();
|
|
1293
1540
|
this.state = null;
|
|
1294
1541
|
this.cachedSponsoredCallback = null;
|
|
1542
|
+
this.cachedWethSponsoredCallback = null;
|
|
1295
1543
|
}
|
|
1296
1544
|
isAgentDisabled(agentId) {
|
|
1297
1545
|
return this.disabledAgents.has(agentId);
|
|
@@ -1365,6 +1613,36 @@ var OwneySDK = class {
|
|
|
1365
1613
|
});
|
|
1366
1614
|
return this.cachedSponsoredCallback;
|
|
1367
1615
|
}
|
|
1616
|
+
/**
|
|
1617
|
+
* Lazily builds (and caches) the default Permit2 sponsored WETH deposit
|
|
1618
|
+
* callback used when the caller omits `depositCallback` for a WETH
|
|
1619
|
+
* deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
1620
|
+
* `PermitTransferFrom` instead of an EIP-3009 authorization.
|
|
1621
|
+
*/
|
|
1622
|
+
getDefaultWethSponsoredCallback() {
|
|
1623
|
+
if (this.cachedWethSponsoredCallback)
|
|
1624
|
+
return this.cachedWethSponsoredCallback;
|
|
1625
|
+
const provider = this.requireConnectedProvider();
|
|
1626
|
+
const owner = this.state.walletAddress;
|
|
1627
|
+
this.cachedWethSponsoredCallback = makeSponsoredWethCallback({
|
|
1628
|
+
apiKey: this.apiKey,
|
|
1629
|
+
ownerAddress: owner,
|
|
1630
|
+
tokenAddressByChain: SPONSORED_WETH_BY_CHAIN,
|
|
1631
|
+
// Casts work around viem's chain-narrowed Client vs the generic
|
|
1632
|
+
// PublicClient/WalletClient param types — structurally identical at
|
|
1633
|
+
// runtime, but the two share a name TS treats as unrelated.
|
|
1634
|
+
getPublicClient: (cid) => createPublicClient2({
|
|
1635
|
+
chain: VIEM_CHAIN2[cid],
|
|
1636
|
+
transport: custom(provider)
|
|
1637
|
+
}),
|
|
1638
|
+
getWalletClient: (cid) => createWalletClient({
|
|
1639
|
+
account: owner,
|
|
1640
|
+
chain: VIEM_CHAIN2[cid],
|
|
1641
|
+
transport: custom(provider)
|
|
1642
|
+
})
|
|
1643
|
+
});
|
|
1644
|
+
return this.cachedWethSponsoredCallback;
|
|
1645
|
+
}
|
|
1368
1646
|
getAgent(agentId) {
|
|
1369
1647
|
const agent = this.agents.get(agentId);
|
|
1370
1648
|
if (!agent) {
|
|
@@ -1518,7 +1796,7 @@ var OwneySDK = class {
|
|
|
1518
1796
|
}
|
|
1519
1797
|
const state = this.requireState();
|
|
1520
1798
|
const chainId = this.requireChainId();
|
|
1521
|
-
const effectiveCallback = depositCallback ?? this.getDefaultSponsoredCallback();
|
|
1799
|
+
const effectiveCallback = depositCallback ?? (asset === "WETH" ? this.getDefaultWethSponsoredCallback() : this.getDefaultSponsoredCallback());
|
|
1522
1800
|
if (agentId) {
|
|
1523
1801
|
if (this.isAgentDisabled(agentId)) {
|
|
1524
1802
|
throw new OwneyError(
|
|
@@ -1530,10 +1808,14 @@ var OwneySDK = class {
|
|
|
1530
1808
|
const agent = this.getAgent(agentId);
|
|
1531
1809
|
this.validateAssetSupport(agent, chainId, asset);
|
|
1532
1810
|
await this.validateMinDepositAmount(agent, state, chainId, asset, amount);
|
|
1533
|
-
return
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1811
|
+
return this.depositWithFallback(
|
|
1812
|
+
agent,
|
|
1813
|
+
state,
|
|
1814
|
+
chainId,
|
|
1815
|
+
amount,
|
|
1816
|
+
asset,
|
|
1817
|
+
effectiveCallback,
|
|
1818
|
+
depositCallback
|
|
1537
1819
|
);
|
|
1538
1820
|
}
|
|
1539
1821
|
const totalAmount = BigInt(amount);
|
|
@@ -1564,14 +1846,78 @@ var OwneySDK = class {
|
|
|
1564
1846
|
}
|
|
1565
1847
|
const agentResults = {};
|
|
1566
1848
|
for (const { agent, amount: agentAmount } of agentAmounts) {
|
|
1567
|
-
agentResults[agent.id] = await
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1849
|
+
agentResults[agent.id] = await this.depositWithFallback(
|
|
1850
|
+
agent,
|
|
1851
|
+
state,
|
|
1852
|
+
chainId,
|
|
1853
|
+
agentAmount,
|
|
1854
|
+
asset,
|
|
1855
|
+
effectiveCallback,
|
|
1856
|
+
depositCallback
|
|
1571
1857
|
);
|
|
1572
1858
|
}
|
|
1573
1859
|
return { agentResults };
|
|
1574
1860
|
}
|
|
1861
|
+
/**
|
|
1862
|
+
* Invokes `agent.deposit` with the resolved sponsored callback, composing
|
|
1863
|
+
* two independent auto-recovery mechanisms:
|
|
1864
|
+
*
|
|
1865
|
+
* 1. Missing Permit2 allowance: when the app did not supply its own
|
|
1866
|
+
* callback and the attempt fails with `PERMIT2_APPROVAL_REQUIRED` on a
|
|
1867
|
+
* WETH deposit, this is the wallet's first gasless WETH deposit. We send
|
|
1868
|
+
* the one-time (user-paid) Permit2 approval via `approvePermit2()` and
|
|
1869
|
+
* retry the SAME sponsored attempt once. Bounded to one approval attempt
|
|
1870
|
+
* per call so a wallet/agent that keeps reporting the allowance as
|
|
1871
|
+
* missing can't loop forever. If `approvePermit2()` itself throws (e.g.
|
|
1872
|
+
* the user rejects the wallet prompt), that error propagates as-is —
|
|
1873
|
+
* the user said no to a transaction, so we must not turn around and ask
|
|
1874
|
+
* them to pay for a different one via the fallback below.
|
|
1875
|
+
* 2. `shouldFallbackToUserPaid`: the SDK's own WETH sponsor path failed at
|
|
1876
|
+
* the infrastructure layer in a provably safe-to-retry way, so we warn
|
|
1877
|
+
* and retry once with `undefined`, falling through to a user-paid
|
|
1878
|
+
* native deposit. This still applies after a successful Permit2
|
|
1879
|
+
* approval retry (the retried sponsored attempt can itself hit a safe
|
|
1880
|
+
* sponsor failure).
|
|
1881
|
+
*
|
|
1882
|
+
* Any other error — an app-supplied callback's own failure in particular —
|
|
1883
|
+
* propagates unchanged. Scoped per agent invocation so one agent's failure
|
|
1884
|
+
* doesn't force a retry of sibling agents in a multi-agent split.
|
|
1885
|
+
*/
|
|
1886
|
+
async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback) {
|
|
1887
|
+
const attempt = (callback) => withFailureReporting(
|
|
1888
|
+
this.apiKey,
|
|
1889
|
+
agent.id,
|
|
1890
|
+
() => agent.deposit(
|
|
1891
|
+
state,
|
|
1892
|
+
chainId,
|
|
1893
|
+
amount,
|
|
1894
|
+
asset,
|
|
1895
|
+
callback
|
|
1896
|
+
)
|
|
1897
|
+
);
|
|
1898
|
+
let approvalAttempted = false;
|
|
1899
|
+
for (; ; ) {
|
|
1900
|
+
try {
|
|
1901
|
+
return await attempt(effectiveCallback);
|
|
1902
|
+
} catch (error) {
|
|
1903
|
+
if (!approvalAttempted && appCallback === void 0 && asset === "WETH" && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
|
|
1904
|
+
approvalAttempted = true;
|
|
1905
|
+
console.warn(
|
|
1906
|
+
"[owney-sdk] First WETH deposit: sending one-time Permit2 approval..."
|
|
1907
|
+
);
|
|
1908
|
+
await this.approvePermit2();
|
|
1909
|
+
continue;
|
|
1910
|
+
}
|
|
1911
|
+
if (!shouldFallbackToUserPaid(error, asset, appCallback))
|
|
1912
|
+
throw error;
|
|
1913
|
+
console.warn(
|
|
1914
|
+
"[owney-sdk] WETH sponsorship unavailable, falling back to user-paid deposit:",
|
|
1915
|
+
error
|
|
1916
|
+
);
|
|
1917
|
+
return attempt(void 0);
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1575
1921
|
getMinDepositAmount(agent, chainId, asset) {
|
|
1576
1922
|
const chainAssets = agent.supportedAssets.find(
|
|
1577
1923
|
(sa) => sa.chainId === chainId
|
|
@@ -2059,6 +2405,66 @@ var OwneySDK = class {
|
|
|
2059
2405
|
agentUserProfile: results
|
|
2060
2406
|
};
|
|
2061
2407
|
}
|
|
2408
|
+
/**
|
|
2409
|
+
* Ensure the given asset uses backend protocol auto-selection for the active
|
|
2410
|
+
* chain's account. No-op (returns false) for agents that don't support it.
|
|
2411
|
+
* @param asset - "USDC" or "WETH".
|
|
2412
|
+
* @param agentId - Agent to target. Defaults to "zyfai".
|
|
2413
|
+
* @returns whether a profile write occurred.
|
|
2414
|
+
*/
|
|
2415
|
+
async ensureAutoSelectProtocols(asset, agentId = "zyfai") {
|
|
2416
|
+
const state = this.requireState();
|
|
2417
|
+
const chainId = this.requireChainId();
|
|
2418
|
+
const agent = this.getAgent(agentId);
|
|
2419
|
+
if (typeof agent.ensureAutoSelectProtocols !== "function")
|
|
2420
|
+
return false;
|
|
2421
|
+
return agent.ensureAutoSelectProtocols(state, chainId, asset);
|
|
2422
|
+
}
|
|
2423
|
+
/**
|
|
2424
|
+
* One-time, user-paid approval of Permit2 on the sponsored WETH token for
|
|
2425
|
+
* the active chain. Required once per wallet per chain before gasless WETH
|
|
2426
|
+
* deposits; afterwards deposit() is signature-only. Resolves only after the
|
|
2427
|
+
* approval transaction is mined (1 confirmation), so a subsequent deposit()
|
|
2428
|
+
* will see the new allowance; throws if the transaction reverted.
|
|
2429
|
+
* @returns the approval transaction hash.
|
|
2430
|
+
*/
|
|
2431
|
+
async approvePermit2(asset = "WETH") {
|
|
2432
|
+
const state = this.requireState();
|
|
2433
|
+
const chainId = this.requireChainId();
|
|
2434
|
+
const token = SPONSORED_WETH_BY_CHAIN[chainId];
|
|
2435
|
+
if (!token) {
|
|
2436
|
+
throw new OwneyError(
|
|
2437
|
+
"CHAIN_UNSUPPORTED",
|
|
2438
|
+
`No sponsored WETH on chain ${chainId}`
|
|
2439
|
+
);
|
|
2440
|
+
}
|
|
2441
|
+
const provider = this.requireConnectedProvider();
|
|
2442
|
+
const wallet = createWalletClient({
|
|
2443
|
+
account: state.walletAddress,
|
|
2444
|
+
chain: VIEM_CHAIN2[chainId],
|
|
2445
|
+
transport: custom(provider)
|
|
2446
|
+
});
|
|
2447
|
+
const hash = await wallet.writeContract({
|
|
2448
|
+
address: token,
|
|
2449
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
2450
|
+
functionName: "approve",
|
|
2451
|
+
args: [PERMIT2_ADDRESS, MAX_UINT256],
|
|
2452
|
+
account: state.walletAddress,
|
|
2453
|
+
chain: VIEM_CHAIN2[chainId]
|
|
2454
|
+
});
|
|
2455
|
+
const publicClient = createPublicClient2({
|
|
2456
|
+
chain: VIEM_CHAIN2[chainId],
|
|
2457
|
+
transport: custom(provider)
|
|
2458
|
+
});
|
|
2459
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
2460
|
+
hash,
|
|
2461
|
+
confirmations: 1
|
|
2462
|
+
});
|
|
2463
|
+
if (receipt.status !== "success") {
|
|
2464
|
+
throw new Error(`Permit2 approval reverted (tx ${hash})`);
|
|
2465
|
+
}
|
|
2466
|
+
return hash;
|
|
2467
|
+
}
|
|
2062
2468
|
// --- Discovery (no wallet required) ---
|
|
2063
2469
|
/**
|
|
2064
2470
|
* Get the agent's average APY performance over a time period. Does not require a wallet connection.
|