@owney/sdk 0.6.4 → 0.6.5-beta.1
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 +487 -35
- package/dist/index.d.cts +119 -61
- package/dist/index.d.ts +119 -61
- package/dist/index.js +480 -28
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -40,7 +40,7 @@ var P_SUPPORTED_CHAINS = {
|
|
|
40
40
|
chainIds: [8453, 42161],
|
|
41
41
|
chains: ["BASE", "ARBITRUM"]
|
|
42
42
|
};
|
|
43
|
-
var SUPPORTED_TOKENS = ["USDC", "USDT"];
|
|
43
|
+
var SUPPORTED_TOKENS = ["USDC", "USDT", "WETH"];
|
|
44
44
|
var SUPPORTED_CHAIN_IDS = P_SUPPORTED_CHAINS.chainIds;
|
|
45
45
|
var SUPPORTED_CHAINS = P_SUPPORTED_CHAINS.chains;
|
|
46
46
|
var SupportedAssets = [
|
|
@@ -61,6 +61,18 @@ var SupportedAssets = [
|
|
|
61
61
|
symbol: "USDT",
|
|
62
62
|
chainId: 42161,
|
|
63
63
|
decimals: 6
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
address: `0x4200000000000000000000000000000000000006`,
|
|
67
|
+
symbol: "WETH",
|
|
68
|
+
chainId: 8453,
|
|
69
|
+
decimals: 18
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
address: `0x82aF49447D8a07e3bd95BD0d56f35241523fBab1`,
|
|
73
|
+
symbol: "WETH",
|
|
74
|
+
chainId: 42161,
|
|
75
|
+
decimals: 18
|
|
64
76
|
}
|
|
65
77
|
];
|
|
66
78
|
|
|
@@ -141,6 +153,20 @@ var DayFilterMapping = {
|
|
|
141
153
|
};
|
|
142
154
|
|
|
143
155
|
// src/agents/zyfai/zyfai.mapper.ts
|
|
156
|
+
var DECIMALS_BY_SYMBOL = {
|
|
157
|
+
usdc: 6,
|
|
158
|
+
usdt: 6,
|
|
159
|
+
weth: 18,
|
|
160
|
+
eth: 18,
|
|
161
|
+
cbeth: 18,
|
|
162
|
+
wbtc: 8,
|
|
163
|
+
cbbtc: 8
|
|
164
|
+
};
|
|
165
|
+
function tokenDecimals(symbol, explicit) {
|
|
166
|
+
if (typeof explicit === "number" && Number.isFinite(explicit))
|
|
167
|
+
return explicit;
|
|
168
|
+
return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
|
|
169
|
+
}
|
|
144
170
|
function mapDeposit(raw) {
|
|
145
171
|
return {
|
|
146
172
|
txHash: raw.txHash,
|
|
@@ -198,9 +224,16 @@ function mapBalances(raw, _chainId, smartWallet) {
|
|
|
198
224
|
pool: p.pool,
|
|
199
225
|
asset: p.token_symbol ?? "",
|
|
200
226
|
// Position amount is hex-encoded base units (like the token balances
|
|
201
|
-
// above).
|
|
202
|
-
//
|
|
203
|
-
|
|
227
|
+
// above). `PositionSlot` doesn't type a `decimals` field, but runtime
|
|
228
|
+
// getBalances payloads often carry one (e.g. 18 for WETH) — prefer that
|
|
229
|
+
// when present, otherwise fall back to the per-symbol lookup so
|
|
230
|
+
// non-6-decimal assets like WETH don't render 1e12 too large.
|
|
231
|
+
amount: String(
|
|
232
|
+
hexToDecimal(
|
|
233
|
+
p.amount || "0",
|
|
234
|
+
tokenDecimals(p.token_symbol, p.decimals)
|
|
235
|
+
)
|
|
236
|
+
),
|
|
204
237
|
apy: p.pool_apy,
|
|
205
238
|
tvl: p.pool_tvl,
|
|
206
239
|
// PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
|
|
@@ -323,7 +356,15 @@ function mapEntries(rawEntries, chainId) {
|
|
|
323
356
|
fromPool: from.pool,
|
|
324
357
|
toPool: to.pool,
|
|
325
358
|
tokenSymbol: matched?.token_symbol ?? "",
|
|
326
|
-
amount: matched ? String(
|
|
359
|
+
amount: matched ? String(
|
|
360
|
+
hexToDecimal(
|
|
361
|
+
matched.amount || "0x",
|
|
362
|
+
tokenDecimals(
|
|
363
|
+
matched.token_symbol,
|
|
364
|
+
matched.decimals
|
|
365
|
+
)
|
|
366
|
+
)
|
|
367
|
+
) : "",
|
|
327
368
|
status: "success"
|
|
328
369
|
}
|
|
329
370
|
];
|
|
@@ -342,7 +383,12 @@ function mapEntries(rawEntries, chainId) {
|
|
|
342
383
|
txHashes: entry.transactionHash ? [entry.transactionHash] : [],
|
|
343
384
|
chainId: entry.chainId,
|
|
344
385
|
tokenSymbol: entry?.positions?.[0]?.token_symbol,
|
|
345
|
-
amount: hasAmount ? String(
|
|
386
|
+
amount: hasAmount ? String(
|
|
387
|
+
hexToDecimal(
|
|
388
|
+
rawAmount,
|
|
389
|
+
tokenDecimals(entry?.positions?.[0]?.token_symbol)
|
|
390
|
+
)
|
|
391
|
+
) : void 0
|
|
346
392
|
}
|
|
347
393
|
],
|
|
348
394
|
rebalanceLog
|
|
@@ -545,11 +591,26 @@ var clearSession = (address, chainId) => {
|
|
|
545
591
|
}
|
|
546
592
|
};
|
|
547
593
|
|
|
594
|
+
// src/agents/zyfai/autoSelect.ts
|
|
595
|
+
function buildAutoSelectUpdate(current, asset) {
|
|
596
|
+
if (current.autoSelectProtocols === true)
|
|
597
|
+
return null;
|
|
598
|
+
return {
|
|
599
|
+
asset,
|
|
600
|
+
autoSelectProtocols: true,
|
|
601
|
+
...current.chains ? { chains: current.chains } : {}
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
|
|
548
605
|
// src/agents/zyfai/zyfai.agent.ts
|
|
549
606
|
var DEFAULT_ZYFAI_RPC_URLS = {
|
|
550
607
|
8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
|
|
551
608
|
42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
|
|
552
609
|
};
|
|
610
|
+
var WETH_ADDRESS_BY_CHAIN = {
|
|
611
|
+
8453: "0x4200000000000000000000000000000000000006",
|
|
612
|
+
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"
|
|
613
|
+
};
|
|
553
614
|
var VIEM_CHAIN = { 8453: import_chains.base, 42161: import_chains.arbitrum };
|
|
554
615
|
var ZyfaiAgent = class {
|
|
555
616
|
id = "zyfai";
|
|
@@ -558,12 +619,18 @@ var ZyfaiAgent = class {
|
|
|
558
619
|
{
|
|
559
620
|
chainId: 8453,
|
|
560
621
|
chain: "BASE",
|
|
561
|
-
assets: [
|
|
622
|
+
assets: [
|
|
623
|
+
{ symbol: "USDC", minDepositAmount: "1500000" },
|
|
624
|
+
{ symbol: "WETH", minDepositAmount: "500000000000000" }
|
|
625
|
+
]
|
|
562
626
|
},
|
|
563
627
|
{
|
|
564
628
|
chainId: 42161,
|
|
565
629
|
chain: "ARBITRUM",
|
|
566
|
-
assets: [
|
|
630
|
+
assets: [
|
|
631
|
+
{ symbol: "USDC", minDepositAmount: "1500000" },
|
|
632
|
+
{ symbol: "WETH", minDepositAmount: "500000000000000" }
|
|
633
|
+
]
|
|
567
634
|
}
|
|
568
635
|
];
|
|
569
636
|
sdk;
|
|
@@ -598,22 +665,24 @@ var ZyfaiAgent = class {
|
|
|
598
665
|
}
|
|
599
666
|
/**
|
|
600
667
|
* Fills in the true withdrawn amount for `Withdraw` entries by decoding the
|
|
601
|
-
*
|
|
668
|
+
* on-chain `Transfer` that lands in the user's wallet. The Zyfai history
|
|
602
669
|
* payload only reports the *remaining* position for a partial withdraw (see
|
|
603
670
|
* the note in `mapEntries`), so without this the UI shows the leftover
|
|
604
671
|
* balance instead of the amount withdrawn.
|
|
605
672
|
*
|
|
673
|
+
* The withdrawn asset varies per entry (USDC or WETH), so it's resolved
|
|
674
|
+
* from `tx.tokenSymbol` (set by `mapEntries` from the entry's own
|
|
675
|
+
* position) rather than assumed to always be USDC. Zyfai sometimes
|
|
676
|
+
* denominates WETH as lowercase "eth" — that's normalized to WETH.
|
|
677
|
+
* Entries with no symbol (older payload shapes) fall back to USDC to
|
|
678
|
+
* preserve prior behavior.
|
|
679
|
+
*
|
|
606
680
|
* Mutates `entries` in place. Runs all lookups concurrently and is tolerant
|
|
607
681
|
* of RPC failures: an entry whose amount can't be resolved keeps
|
|
608
682
|
* `amount: undefined`, which consumers render as a hidden/retryable row
|
|
609
683
|
* rather than a wrong number.
|
|
610
684
|
*/
|
|
611
685
|
async resolveWithdrawnAmounts(entries, chainId, recipient) {
|
|
612
|
-
const asset = SupportedAssets.find(
|
|
613
|
-
(a) => a.chainId === chainId && a.symbol === "USDC"
|
|
614
|
-
);
|
|
615
|
-
if (!asset)
|
|
616
|
-
return;
|
|
617
686
|
const withdrawals = entries.filter(
|
|
618
687
|
(e) => e.action === "Withdraw" && e.transactions[0]?.txHashes[0]
|
|
619
688
|
);
|
|
@@ -624,7 +693,17 @@ var ZyfaiAgent = class {
|
|
|
624
693
|
withdrawals.map(async (entry) => {
|
|
625
694
|
const tx = entry.transactions[0];
|
|
626
695
|
const txHash = tx.txHashes[0];
|
|
627
|
-
const
|
|
696
|
+
const rawSymbol = tx.tokenSymbol;
|
|
697
|
+
const normalizedSymbol = rawSymbol ? rawSymbol.toLowerCase() === "eth" ? "WETH" : rawSymbol.toUpperCase() : "USDC";
|
|
698
|
+
const asset = SupportedAssets.find(
|
|
699
|
+
(a) => a.chainId === chainId && a.symbol === normalizedSymbol
|
|
700
|
+
) ?? SupportedAssets.find(
|
|
701
|
+
(a) => a.chainId === chainId && a.symbol === "USDC"
|
|
702
|
+
);
|
|
703
|
+
if (!asset)
|
|
704
|
+
return;
|
|
705
|
+
const cacheKey = `${txHash}:${asset.address}`;
|
|
706
|
+
const cached = this.withdrawAmountCache.get(cacheKey);
|
|
628
707
|
if (cached !== void 0) {
|
|
629
708
|
tx.amount = cached;
|
|
630
709
|
return;
|
|
@@ -640,7 +719,7 @@ var ZyfaiAgent = class {
|
|
|
640
719
|
asset.decimals
|
|
641
720
|
);
|
|
642
721
|
if (amount !== void 0) {
|
|
643
|
-
this.withdrawAmountCache.set(
|
|
722
|
+
this.withdrawAmountCache.set(cacheKey, amount);
|
|
644
723
|
tx.amount = amount;
|
|
645
724
|
}
|
|
646
725
|
} catch {
|
|
@@ -758,6 +837,22 @@ var ZyfaiAgent = class {
|
|
|
758
837
|
this.smartWalletCache.set(`${address}_${validChainId}`, wallet.address);
|
|
759
838
|
if (!wallet.isDeployed) {
|
|
760
839
|
await this.sdk.deploySafe(address, validChainId, "aggressive", true);
|
|
840
|
+
try {
|
|
841
|
+
if (validChainId === 42161) {
|
|
842
|
+
await this.sdk.updateUserProfile({
|
|
843
|
+
asset: "WETH",
|
|
844
|
+
chains: [validChainId],
|
|
845
|
+
protocols: ["c24a33fa-4a87-4376-b756-93bd3957d3ea"],
|
|
846
|
+
// AAVE protocol ID
|
|
847
|
+
autoSelectProtocols: false
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
} catch (err) {
|
|
851
|
+
console.warn(
|
|
852
|
+
"Failed to configure AAVE protocol after Safe deployment:",
|
|
853
|
+
err instanceof Error ? err.message : String(err)
|
|
854
|
+
);
|
|
855
|
+
}
|
|
761
856
|
}
|
|
762
857
|
const user = await this.sdk.getUserDetails();
|
|
763
858
|
if (!user.hasActiveSessionKey) {
|
|
@@ -799,7 +894,7 @@ var ZyfaiAgent = class {
|
|
|
799
894
|
throw error;
|
|
800
895
|
}
|
|
801
896
|
}
|
|
802
|
-
async deposit(state, chainId, amount, depositCallback) {
|
|
897
|
+
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
803
898
|
try {
|
|
804
899
|
await this.connectFull(state, chainId);
|
|
805
900
|
const validChainId = isValidChainId(chainId);
|
|
@@ -813,13 +908,15 @@ var ZyfaiAgent = class {
|
|
|
813
908
|
validChainId,
|
|
814
909
|
amount
|
|
815
910
|
);
|
|
816
|
-
|
|
911
|
+
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
912
|
+
await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
|
|
817
913
|
return { txHash, smartWallet: wallet.address, amount };
|
|
818
914
|
}
|
|
819
915
|
const raw = await this.sdk.depositFunds(
|
|
820
916
|
this.getAddress(),
|
|
821
917
|
validChainId,
|
|
822
|
-
amount
|
|
918
|
+
amount,
|
|
919
|
+
asset
|
|
823
920
|
);
|
|
824
921
|
return mapDeposit(raw);
|
|
825
922
|
} catch (error) {
|
|
@@ -915,6 +1012,21 @@ var ZyfaiAgent = class {
|
|
|
915
1012
|
}
|
|
916
1013
|
return mapUserProfile(raw, this.connectedAddress);
|
|
917
1014
|
}
|
|
1015
|
+
async ensureAutoSelectProtocols(state, chainId, asset) {
|
|
1016
|
+
await this.connectAuth(state, chainId);
|
|
1017
|
+
const current = await this.sdk.getUserDetails(asset);
|
|
1018
|
+
const update = buildAutoSelectUpdate(
|
|
1019
|
+
{
|
|
1020
|
+
autoSelectProtocols: current.autoSelectProtocols,
|
|
1021
|
+
chains: current.chains
|
|
1022
|
+
},
|
|
1023
|
+
asset
|
|
1024
|
+
);
|
|
1025
|
+
if (!update)
|
|
1026
|
+
return false;
|
|
1027
|
+
await this.sdk.updateUserProfile(update);
|
|
1028
|
+
return true;
|
|
1029
|
+
}
|
|
918
1030
|
// --- IAgent: Discovery (no wallet required) ---
|
|
919
1031
|
async getAgentApy(days, options) {
|
|
920
1032
|
const raw = await this.sdk.getAPYPerStrategy(
|
|
@@ -1118,7 +1230,7 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
1118
1230
|
}
|
|
1119
1231
|
|
|
1120
1232
|
// src/client.ts
|
|
1121
|
-
var
|
|
1233
|
+
var import_viem5 = require("viem");
|
|
1122
1234
|
var import_chains2 = require("viem/chains");
|
|
1123
1235
|
|
|
1124
1236
|
// src/lib/transfer-auth.ts
|
|
@@ -1190,6 +1302,70 @@ async function postSponsorTransferAuth(input) {
|
|
|
1190
1302
|
}
|
|
1191
1303
|
return parsed.data;
|
|
1192
1304
|
}
|
|
1305
|
+
async function postSponsorPermit2Transfer(input) {
|
|
1306
|
+
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
1307
|
+
let res;
|
|
1308
|
+
try {
|
|
1309
|
+
res = await fetch(`${base3}/api/v1/sponsor/permit2-transfer`, {
|
|
1310
|
+
method: "POST",
|
|
1311
|
+
headers: { "content-type": "application/json", "x-owney-api-key": input.apiKey },
|
|
1312
|
+
body: JSON.stringify(input.body)
|
|
1313
|
+
});
|
|
1314
|
+
} catch (networkError) {
|
|
1315
|
+
throw new OwneyError(
|
|
1316
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1317
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
1318
|
+
{ cause: String(networkError), safeToFallback: false }
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
const text = await res.text();
|
|
1322
|
+
let parsed = null;
|
|
1323
|
+
try {
|
|
1324
|
+
parsed = JSON.parse(text);
|
|
1325
|
+
} catch {
|
|
1326
|
+
}
|
|
1327
|
+
if (!res.ok || !parsed?.success || !parsed.data) {
|
|
1328
|
+
throw new OwneyError(
|
|
1329
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1330
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
1331
|
+
{
|
|
1332
|
+
statusCode: res.status,
|
|
1333
|
+
responseBody: text.slice(0, 500),
|
|
1334
|
+
safeToFallback: res.status >= 400 && res.status < 500
|
|
1335
|
+
}
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
return parsed.data;
|
|
1339
|
+
}
|
|
1340
|
+
async function getSponsorRelayerAddress(input) {
|
|
1341
|
+
const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
|
|
1342
|
+
let res;
|
|
1343
|
+
try {
|
|
1344
|
+
res = await fetch(`${base3}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`, {
|
|
1345
|
+
headers: { "x-owney-api-key": input.apiKey }
|
|
1346
|
+
});
|
|
1347
|
+
} catch (networkError) {
|
|
1348
|
+
throw new OwneyError(
|
|
1349
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1350
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
1351
|
+
{ cause: String(networkError), safeToFallback: true }
|
|
1352
|
+
);
|
|
1353
|
+
}
|
|
1354
|
+
const text = await res.text();
|
|
1355
|
+
let parsed = null;
|
|
1356
|
+
try {
|
|
1357
|
+
parsed = JSON.parse(text);
|
|
1358
|
+
} catch {
|
|
1359
|
+
}
|
|
1360
|
+
if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
|
|
1361
|
+
throw new OwneyError(
|
|
1362
|
+
"SPONSOR_REQUEST_FAILED",
|
|
1363
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
1364
|
+
{ statusCode: res.status, responseBody: text.slice(0, 500), safeToFallback: true }
|
|
1365
|
+
);
|
|
1366
|
+
}
|
|
1367
|
+
return parsed.data.relayer;
|
|
1368
|
+
}
|
|
1193
1369
|
|
|
1194
1370
|
// src/lib/sponsored-deposit.ts
|
|
1195
1371
|
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
@@ -1241,6 +1417,114 @@ function makeSponsoredDepositCallback(deps) {
|
|
|
1241
1417
|
};
|
|
1242
1418
|
}
|
|
1243
1419
|
|
|
1420
|
+
// src/lib/permit2.ts
|
|
1421
|
+
var import_viem4 = require("viem");
|
|
1422
|
+
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
1423
|
+
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
1424
|
+
var ERC20_ALLOWANCE_ABI = [
|
|
1425
|
+
{
|
|
1426
|
+
type: "function",
|
|
1427
|
+
name: "allowance",
|
|
1428
|
+
stateMutability: "view",
|
|
1429
|
+
inputs: [{ name: "owner", type: "address" }, { name: "spender", type: "address" }],
|
|
1430
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
1431
|
+
},
|
|
1432
|
+
{
|
|
1433
|
+
type: "function",
|
|
1434
|
+
name: "approve",
|
|
1435
|
+
stateMutability: "nonpayable",
|
|
1436
|
+
inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }],
|
|
1437
|
+
outputs: [{ name: "", type: "bool" }]
|
|
1438
|
+
}
|
|
1439
|
+
];
|
|
1440
|
+
function buildPermitTransferFromTypedData(input) {
|
|
1441
|
+
return {
|
|
1442
|
+
domain: { name: "Permit2", chainId: input.chainId, verifyingContract: PERMIT2_ADDRESS },
|
|
1443
|
+
types: {
|
|
1444
|
+
PermitTransferFrom: [
|
|
1445
|
+
{ name: "permitted", type: "TokenPermissions" },
|
|
1446
|
+
{ name: "spender", type: "address" },
|
|
1447
|
+
{ name: "nonce", type: "uint256" },
|
|
1448
|
+
{ name: "deadline", type: "uint256" }
|
|
1449
|
+
],
|
|
1450
|
+
TokenPermissions: [
|
|
1451
|
+
{ name: "token", type: "address" },
|
|
1452
|
+
{ name: "amount", type: "uint256" }
|
|
1453
|
+
]
|
|
1454
|
+
},
|
|
1455
|
+
primaryType: "PermitTransferFrom",
|
|
1456
|
+
message: input.message
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
function randomPermit2Nonce() {
|
|
1460
|
+
const bytes = new Uint8Array(32);
|
|
1461
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
1462
|
+
return BigInt((0, import_viem4.bytesToHex)(bytes));
|
|
1463
|
+
}
|
|
1464
|
+
async function readPermit2Allowance(publicClient, token, owner) {
|
|
1465
|
+
return publicClient.readContract({
|
|
1466
|
+
address: token,
|
|
1467
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
1468
|
+
functionName: "allowance",
|
|
1469
|
+
args: [owner, PERMIT2_ADDRESS]
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
// src/lib/sponsored-weth-deposit.ts
|
|
1474
|
+
var PERMIT_WINDOW_SECONDS = 15 * 60;
|
|
1475
|
+
function makeSponsoredWethCallback(deps) {
|
|
1476
|
+
const get = deps.httpGet ?? getSponsorRelayerAddress;
|
|
1477
|
+
const post = deps.httpPost ?? postSponsorPermit2Transfer;
|
|
1478
|
+
return async (smartWallet, chainId, amount) => {
|
|
1479
|
+
const cid = chainId;
|
|
1480
|
+
const token = deps.tokenAddressByChain[cid];
|
|
1481
|
+
if (!token) {
|
|
1482
|
+
throw new OwneyError("CHAIN_UNSUPPORTED", `No sponsored WETH configured for chain ${chainId}`);
|
|
1483
|
+
}
|
|
1484
|
+
const amountWei = BigInt(amount);
|
|
1485
|
+
const pub = deps.getPublicClient(cid);
|
|
1486
|
+
const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
|
|
1487
|
+
if (allowance < amountWei) {
|
|
1488
|
+
throw new OwneyError(
|
|
1489
|
+
"PERMIT2_APPROVAL_REQUIRED",
|
|
1490
|
+
"WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
|
|
1491
|
+
{ token, chainId: cid, allowance: allowance.toString(), amount }
|
|
1492
|
+
);
|
|
1493
|
+
}
|
|
1494
|
+
const relayer = await get({ baseUrl: deps.baseUrl, apiKey: deps.apiKey, chainId: cid });
|
|
1495
|
+
const nonce = randomPermit2Nonce();
|
|
1496
|
+
const deadline = BigInt(Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS);
|
|
1497
|
+
const typedData = buildPermitTransferFromTypedData({
|
|
1498
|
+
chainId: cid,
|
|
1499
|
+
message: {
|
|
1500
|
+
permitted: { token, amount: amountWei },
|
|
1501
|
+
spender: relayer,
|
|
1502
|
+
nonce,
|
|
1503
|
+
deadline
|
|
1504
|
+
}
|
|
1505
|
+
});
|
|
1506
|
+
const wallet = deps.getWalletClient(cid);
|
|
1507
|
+
const signature = await wallet.signTypedData(
|
|
1508
|
+
{ account: deps.ownerAddress, ...typedData }
|
|
1509
|
+
);
|
|
1510
|
+
const result = await post({
|
|
1511
|
+
baseUrl: deps.baseUrl,
|
|
1512
|
+
apiKey: deps.apiKey,
|
|
1513
|
+
body: {
|
|
1514
|
+
chainId: cid,
|
|
1515
|
+
token,
|
|
1516
|
+
from: deps.ownerAddress,
|
|
1517
|
+
to: smartWallet,
|
|
1518
|
+
amount,
|
|
1519
|
+
nonce: nonce.toString(),
|
|
1520
|
+
deadline: deadline.toString(),
|
|
1521
|
+
signature
|
|
1522
|
+
}
|
|
1523
|
+
});
|
|
1524
|
+
return result.txHash;
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1244
1528
|
// src/client.ts
|
|
1245
1529
|
function encodeMultiAgentCursor(map) {
|
|
1246
1530
|
return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
|
|
@@ -1270,6 +1554,13 @@ var VIEM_CHAIN2 = {
|
|
|
1270
1554
|
8453: import_chains2.base,
|
|
1271
1555
|
42161: import_chains2.arbitrum
|
|
1272
1556
|
};
|
|
1557
|
+
var SPONSORED_WETH_BY_CHAIN = {
|
|
1558
|
+
8453: "0x4200000000000000000000000000000000000006",
|
|
1559
|
+
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"
|
|
1560
|
+
};
|
|
1561
|
+
function shouldFallbackToUserPaid(error, asset, appCallback) {
|
|
1562
|
+
return asset === "WETH" && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
|
|
1563
|
+
}
|
|
1273
1564
|
var OwneySDK = class {
|
|
1274
1565
|
agents = /* @__PURE__ */ new Map();
|
|
1275
1566
|
activeAgents = /* @__PURE__ */ new Set();
|
|
@@ -1283,6 +1574,7 @@ var OwneySDK = class {
|
|
|
1283
1574
|
apiKey;
|
|
1284
1575
|
zyfaiRpcUrls;
|
|
1285
1576
|
cachedSponsoredCallback = null;
|
|
1577
|
+
cachedWethSponsoredCallback = null;
|
|
1286
1578
|
initializingAgentsPromise = null;
|
|
1287
1579
|
constructor(config) {
|
|
1288
1580
|
this.apiKey = config.apiKey;
|
|
@@ -1307,6 +1599,7 @@ var OwneySDK = class {
|
|
|
1307
1599
|
}
|
|
1308
1600
|
this.state = { provider, walletAddress, chainId: null };
|
|
1309
1601
|
this.cachedSponsoredCallback = null;
|
|
1602
|
+
this.cachedWethSponsoredCallback = null;
|
|
1310
1603
|
}
|
|
1311
1604
|
/**
|
|
1312
1605
|
* Disconnect from all agents and clear connection state.
|
|
@@ -1320,6 +1613,7 @@ var OwneySDK = class {
|
|
|
1320
1613
|
this.disabledAgents.clear();
|
|
1321
1614
|
this.state = null;
|
|
1322
1615
|
this.cachedSponsoredCallback = null;
|
|
1616
|
+
this.cachedWethSponsoredCallback = null;
|
|
1323
1617
|
}
|
|
1324
1618
|
isAgentDisabled(agentId) {
|
|
1325
1619
|
return this.disabledAgents.has(agentId);
|
|
@@ -1381,18 +1675,48 @@ var OwneySDK = class {
|
|
|
1381
1675
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
1382
1676
|
// PublicClient/WalletClient param types — structurally identical at
|
|
1383
1677
|
// runtime, but the two share a name TS treats as unrelated.
|
|
1384
|
-
getPublicClient: (cid) => (0,
|
|
1678
|
+
getPublicClient: (cid) => (0, import_viem5.createPublicClient)({
|
|
1385
1679
|
chain: VIEM_CHAIN2[cid],
|
|
1386
|
-
transport: (0,
|
|
1680
|
+
transport: (0, import_viem5.custom)(provider)
|
|
1387
1681
|
}),
|
|
1388
|
-
getWalletClient: (cid) => (0,
|
|
1682
|
+
getWalletClient: (cid) => (0, import_viem5.createWalletClient)({
|
|
1389
1683
|
account: owner,
|
|
1390
1684
|
chain: VIEM_CHAIN2[cid],
|
|
1391
|
-
transport: (0,
|
|
1685
|
+
transport: (0, import_viem5.custom)(provider)
|
|
1392
1686
|
})
|
|
1393
1687
|
});
|
|
1394
1688
|
return this.cachedSponsoredCallback;
|
|
1395
1689
|
}
|
|
1690
|
+
/**
|
|
1691
|
+
* Lazily builds (and caches) the default Permit2 sponsored WETH deposit
|
|
1692
|
+
* callback used when the caller omits `depositCallback` for a WETH
|
|
1693
|
+
* deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
1694
|
+
* `PermitTransferFrom` instead of an EIP-3009 authorization.
|
|
1695
|
+
*/
|
|
1696
|
+
getDefaultWethSponsoredCallback() {
|
|
1697
|
+
if (this.cachedWethSponsoredCallback)
|
|
1698
|
+
return this.cachedWethSponsoredCallback;
|
|
1699
|
+
const provider = this.requireConnectedProvider();
|
|
1700
|
+
const owner = this.state.walletAddress;
|
|
1701
|
+
this.cachedWethSponsoredCallback = makeSponsoredWethCallback({
|
|
1702
|
+
apiKey: this.apiKey,
|
|
1703
|
+
ownerAddress: owner,
|
|
1704
|
+
tokenAddressByChain: SPONSORED_WETH_BY_CHAIN,
|
|
1705
|
+
// Casts work around viem's chain-narrowed Client vs the generic
|
|
1706
|
+
// PublicClient/WalletClient param types — structurally identical at
|
|
1707
|
+
// runtime, but the two share a name TS treats as unrelated.
|
|
1708
|
+
getPublicClient: (cid) => (0, import_viem5.createPublicClient)({
|
|
1709
|
+
chain: VIEM_CHAIN2[cid],
|
|
1710
|
+
transport: (0, import_viem5.custom)(provider)
|
|
1711
|
+
}),
|
|
1712
|
+
getWalletClient: (cid) => (0, import_viem5.createWalletClient)({
|
|
1713
|
+
account: owner,
|
|
1714
|
+
chain: VIEM_CHAIN2[cid],
|
|
1715
|
+
transport: (0, import_viem5.custom)(provider)
|
|
1716
|
+
})
|
|
1717
|
+
});
|
|
1718
|
+
return this.cachedWethSponsoredCallback;
|
|
1719
|
+
}
|
|
1396
1720
|
getAgent(agentId) {
|
|
1397
1721
|
const agent = this.agents.get(agentId);
|
|
1398
1722
|
if (!agent) {
|
|
@@ -1546,7 +1870,7 @@ var OwneySDK = class {
|
|
|
1546
1870
|
}
|
|
1547
1871
|
const state = this.requireState();
|
|
1548
1872
|
const chainId = this.requireChainId();
|
|
1549
|
-
const effectiveCallback = depositCallback ?? this.getDefaultSponsoredCallback();
|
|
1873
|
+
const effectiveCallback = depositCallback ?? (asset === "WETH" ? this.getDefaultWethSponsoredCallback() : this.getDefaultSponsoredCallback());
|
|
1550
1874
|
if (agentId) {
|
|
1551
1875
|
if (this.isAgentDisabled(agentId)) {
|
|
1552
1876
|
throw new OwneyError(
|
|
@@ -1558,10 +1882,14 @@ var OwneySDK = class {
|
|
|
1558
1882
|
const agent = this.getAgent(agentId);
|
|
1559
1883
|
this.validateAssetSupport(agent, chainId, asset);
|
|
1560
1884
|
await this.validateMinDepositAmount(agent, state, chainId, asset, amount);
|
|
1561
|
-
return
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1885
|
+
return this.depositWithFallback(
|
|
1886
|
+
agent,
|
|
1887
|
+
state,
|
|
1888
|
+
chainId,
|
|
1889
|
+
amount,
|
|
1890
|
+
asset,
|
|
1891
|
+
effectiveCallback,
|
|
1892
|
+
depositCallback
|
|
1565
1893
|
);
|
|
1566
1894
|
}
|
|
1567
1895
|
const totalAmount = BigInt(amount);
|
|
@@ -1592,14 +1920,78 @@ var OwneySDK = class {
|
|
|
1592
1920
|
}
|
|
1593
1921
|
const agentResults = {};
|
|
1594
1922
|
for (const { agent, amount: agentAmount } of agentAmounts) {
|
|
1595
|
-
agentResults[agent.id] = await
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1923
|
+
agentResults[agent.id] = await this.depositWithFallback(
|
|
1924
|
+
agent,
|
|
1925
|
+
state,
|
|
1926
|
+
chainId,
|
|
1927
|
+
agentAmount,
|
|
1928
|
+
asset,
|
|
1929
|
+
effectiveCallback,
|
|
1930
|
+
depositCallback
|
|
1599
1931
|
);
|
|
1600
1932
|
}
|
|
1601
1933
|
return { agentResults };
|
|
1602
1934
|
}
|
|
1935
|
+
/**
|
|
1936
|
+
* Invokes `agent.deposit` with the resolved sponsored callback, composing
|
|
1937
|
+
* two independent auto-recovery mechanisms:
|
|
1938
|
+
*
|
|
1939
|
+
* 1. Missing Permit2 allowance: when the app did not supply its own
|
|
1940
|
+
* callback and the attempt fails with `PERMIT2_APPROVAL_REQUIRED` on a
|
|
1941
|
+
* WETH deposit, this is the wallet's first gasless WETH deposit. We send
|
|
1942
|
+
* the one-time (user-paid) Permit2 approval via `approvePermit2()` and
|
|
1943
|
+
* retry the SAME sponsored attempt once. Bounded to one approval attempt
|
|
1944
|
+
* per call so a wallet/agent that keeps reporting the allowance as
|
|
1945
|
+
* missing can't loop forever. If `approvePermit2()` itself throws (e.g.
|
|
1946
|
+
* the user rejects the wallet prompt), that error propagates as-is —
|
|
1947
|
+
* the user said no to a transaction, so we must not turn around and ask
|
|
1948
|
+
* them to pay for a different one via the fallback below.
|
|
1949
|
+
* 2. `shouldFallbackToUserPaid`: the SDK's own WETH sponsor path failed at
|
|
1950
|
+
* the infrastructure layer in a provably safe-to-retry way, so we warn
|
|
1951
|
+
* and retry once with `undefined`, falling through to a user-paid
|
|
1952
|
+
* native deposit. This still applies after a successful Permit2
|
|
1953
|
+
* approval retry (the retried sponsored attempt can itself hit a safe
|
|
1954
|
+
* sponsor failure).
|
|
1955
|
+
*
|
|
1956
|
+
* Any other error — an app-supplied callback's own failure in particular —
|
|
1957
|
+
* propagates unchanged. Scoped per agent invocation so one agent's failure
|
|
1958
|
+
* doesn't force a retry of sibling agents in a multi-agent split.
|
|
1959
|
+
*/
|
|
1960
|
+
async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback) {
|
|
1961
|
+
const attempt = (callback) => withFailureReporting(
|
|
1962
|
+
this.apiKey,
|
|
1963
|
+
agent.id,
|
|
1964
|
+
() => agent.deposit(
|
|
1965
|
+
state,
|
|
1966
|
+
chainId,
|
|
1967
|
+
amount,
|
|
1968
|
+
asset,
|
|
1969
|
+
callback
|
|
1970
|
+
)
|
|
1971
|
+
);
|
|
1972
|
+
let approvalAttempted = false;
|
|
1973
|
+
for (; ; ) {
|
|
1974
|
+
try {
|
|
1975
|
+
return await attempt(effectiveCallback);
|
|
1976
|
+
} catch (error) {
|
|
1977
|
+
if (!approvalAttempted && appCallback === void 0 && asset === "WETH" && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
|
|
1978
|
+
approvalAttempted = true;
|
|
1979
|
+
console.warn(
|
|
1980
|
+
"[owney-sdk] First WETH deposit: sending one-time Permit2 approval..."
|
|
1981
|
+
);
|
|
1982
|
+
await this.approvePermit2();
|
|
1983
|
+
continue;
|
|
1984
|
+
}
|
|
1985
|
+
if (!shouldFallbackToUserPaid(error, asset, appCallback))
|
|
1986
|
+
throw error;
|
|
1987
|
+
console.warn(
|
|
1988
|
+
"[owney-sdk] WETH sponsorship unavailable, falling back to user-paid deposit:",
|
|
1989
|
+
error
|
|
1990
|
+
);
|
|
1991
|
+
return attempt(void 0);
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1603
1995
|
getMinDepositAmount(agent, chainId, asset) {
|
|
1604
1996
|
const chainAssets = agent.supportedAssets.find(
|
|
1605
1997
|
(sa) => sa.chainId === chainId
|
|
@@ -2087,6 +2479,66 @@ var OwneySDK = class {
|
|
|
2087
2479
|
agentUserProfile: results
|
|
2088
2480
|
};
|
|
2089
2481
|
}
|
|
2482
|
+
/**
|
|
2483
|
+
* Ensure the given asset uses backend protocol auto-selection for the active
|
|
2484
|
+
* chain's account. No-op (returns false) for agents that don't support it.
|
|
2485
|
+
* @param asset - "USDC" or "WETH".
|
|
2486
|
+
* @param agentId - Agent to target. Defaults to "zyfai".
|
|
2487
|
+
* @returns whether a profile write occurred.
|
|
2488
|
+
*/
|
|
2489
|
+
async ensureAutoSelectProtocols(asset, agentId = "zyfai") {
|
|
2490
|
+
const state = this.requireState();
|
|
2491
|
+
const chainId = this.requireChainId();
|
|
2492
|
+
const agent = this.getAgent(agentId);
|
|
2493
|
+
if (typeof agent.ensureAutoSelectProtocols !== "function")
|
|
2494
|
+
return false;
|
|
2495
|
+
return agent.ensureAutoSelectProtocols(state, chainId, asset);
|
|
2496
|
+
}
|
|
2497
|
+
/**
|
|
2498
|
+
* One-time, user-paid approval of Permit2 on the sponsored WETH token for
|
|
2499
|
+
* the active chain. Required once per wallet per chain before gasless WETH
|
|
2500
|
+
* deposits; afterwards deposit() is signature-only. Resolves only after the
|
|
2501
|
+
* approval transaction is mined (1 confirmation), so a subsequent deposit()
|
|
2502
|
+
* will see the new allowance; throws if the transaction reverted.
|
|
2503
|
+
* @returns the approval transaction hash.
|
|
2504
|
+
*/
|
|
2505
|
+
async approvePermit2(asset = "WETH") {
|
|
2506
|
+
const state = this.requireState();
|
|
2507
|
+
const chainId = this.requireChainId();
|
|
2508
|
+
const token = SPONSORED_WETH_BY_CHAIN[chainId];
|
|
2509
|
+
if (!token) {
|
|
2510
|
+
throw new OwneyError(
|
|
2511
|
+
"CHAIN_UNSUPPORTED",
|
|
2512
|
+
`No sponsored WETH on chain ${chainId}`
|
|
2513
|
+
);
|
|
2514
|
+
}
|
|
2515
|
+
const provider = this.requireConnectedProvider();
|
|
2516
|
+
const wallet = (0, import_viem5.createWalletClient)({
|
|
2517
|
+
account: state.walletAddress,
|
|
2518
|
+
chain: VIEM_CHAIN2[chainId],
|
|
2519
|
+
transport: (0, import_viem5.custom)(provider)
|
|
2520
|
+
});
|
|
2521
|
+
const hash = await wallet.writeContract({
|
|
2522
|
+
address: token,
|
|
2523
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
2524
|
+
functionName: "approve",
|
|
2525
|
+
args: [PERMIT2_ADDRESS, MAX_UINT256],
|
|
2526
|
+
account: state.walletAddress,
|
|
2527
|
+
chain: VIEM_CHAIN2[chainId]
|
|
2528
|
+
});
|
|
2529
|
+
const publicClient = (0, import_viem5.createPublicClient)({
|
|
2530
|
+
chain: VIEM_CHAIN2[chainId],
|
|
2531
|
+
transport: (0, import_viem5.custom)(provider)
|
|
2532
|
+
});
|
|
2533
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
2534
|
+
hash,
|
|
2535
|
+
confirmations: 1
|
|
2536
|
+
});
|
|
2537
|
+
if (receipt.status !== "success") {
|
|
2538
|
+
throw new Error(`Permit2 approval reverted (tx ${hash})`);
|
|
2539
|
+
}
|
|
2540
|
+
return hash;
|
|
2541
|
+
}
|
|
2090
2542
|
// --- Discovery (no wallet required) ---
|
|
2091
2543
|
/**
|
|
2092
2544
|
* Get the agent's average APY performance over a time period. Does not require a wallet connection.
|
|
@@ -2185,7 +2637,7 @@ var OwneySDK = class {
|
|
|
2185
2637
|
};
|
|
2186
2638
|
|
|
2187
2639
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
2188
|
-
var
|
|
2640
|
+
var import_viem6 = require("viem");
|
|
2189
2641
|
var import_siwe = require("siwe");
|
|
2190
2642
|
var import_sdk2 = require("@zyfai/sdk");
|
|
2191
2643
|
|
|
@@ -2267,7 +2719,7 @@ function buildSIWXConfig(deps) {
|
|
|
2267
2719
|
issuedAt,
|
|
2268
2720
|
toString() {
|
|
2269
2721
|
return new import_siwe.SiweMessage({
|
|
2270
|
-
address: (0,
|
|
2722
|
+
address: (0, import_viem6.getAddress)(accountAddress),
|
|
2271
2723
|
chainId: numericChainId(chainId),
|
|
2272
2724
|
domain,
|
|
2273
2725
|
uri,
|