@owney/sdk 0.6.0-beta.0 → 0.6.0-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 CHANGED
@@ -38,7 +38,7 @@ var P_SUPPORTED_CHAINS = {
38
38
  chainIds: [8453, 42161],
39
39
  chains: ["BASE", "ARBITRUM"]
40
40
  };
41
- var SUPPORTED_TOKENS = ["USDC", "USDT"];
41
+ var SUPPORTED_TOKENS = ["USDC", "USDT", "WETH"];
42
42
  var SUPPORTED_CHAIN_IDS = P_SUPPORTED_CHAINS.chainIds;
43
43
  var SUPPORTED_CHAINS = P_SUPPORTED_CHAINS.chains;
44
44
  var SupportedAssets = [
@@ -59,6 +59,18 @@ var SupportedAssets = [
59
59
  symbol: "USDT",
60
60
  chainId: 42161,
61
61
  decimals: 6
62
+ },
63
+ {
64
+ address: `0x4200000000000000000000000000000000000006`,
65
+ symbol: "WETH",
66
+ chainId: 8453,
67
+ decimals: 18
68
+ },
69
+ {
70
+ address: `0x82aF49447D8a07e3bd95BD0d56f35241523fBab1`,
71
+ symbol: "WETH",
72
+ chainId: 42161,
73
+ decimals: 18
62
74
  }
63
75
  ];
64
76
 
@@ -157,6 +169,18 @@ var CHAIN_ID_TO_NAME = {
157
169
  8453: "BASE",
158
170
  42161: "ARBITRUM"
159
171
  };
172
+ var NAME_TO_CHAIN_ID = Object.entries(
173
+ CHAIN_ID_TO_NAME
174
+ ).reduce((acc, [id, name]) => {
175
+ acc[name.toLowerCase()] = Number(id);
176
+ return acc;
177
+ }, {});
178
+ function resolveChainId(chain) {
179
+ const asNum = Number(chain);
180
+ if (Number.isFinite(asNum) && asNum > 0)
181
+ return asNum;
182
+ return NAME_TO_CHAIN_ID[chain.trim().toLowerCase()] ?? NaN;
183
+ }
160
184
  function mapBalances(raw, _chainId, smartWallet) {
161
185
  const portfolio = raw.portfolio;
162
186
  const portfolioByChain = portfolio.portfolioByChain ?? {};
@@ -177,10 +201,27 @@ function mapBalances(raw, _chainId, smartWallet) {
177
201
  });
178
202
  }
179
203
  }
204
+ const positions = (portfolio.positions ?? []).map((p) => ({
205
+ chain: p.chain ?? "",
206
+ protocol: p.protocol_name ?? p.protocol_id ?? "",
207
+ protocolId: p.protocol_id,
208
+ pool: p.pool,
209
+ asset: p.token_symbol ?? "",
210
+ // Position amount is hex-encoded base units (like the token balances
211
+ // above). Supported assets (USDC/USDT) are 6-decimal — hexToDecimal's
212
+ // default — so no per-token decimals lookup is needed.
213
+ amount: String(hexToDecimal(p.amount || "0")),
214
+ apy: p.pool_apy,
215
+ tvl: p.pool_tvl,
216
+ // PositionSlot has no `liquidity` field yet; Zyfai will add it. Narrow
217
+ // structural read keeps it undefined today and auto-populates later.
218
+ liquidity: p.liquidity
219
+ }));
180
220
  return {
181
221
  smartWallet,
182
222
  totalBalance: String(totalBalance),
183
- tokens
223
+ tokens,
224
+ positions
184
225
  };
185
226
  }
186
227
  function sumTokenValues(tokens) {
@@ -288,13 +329,6 @@ function mapEntries(rawEntries, chainId) {
288
329
  };
289
330
  });
290
331
  }
291
- function mapAgentStatus(raw) {
292
- return {
293
- success: raw.success,
294
- strategy: raw.strategy,
295
- protocols: raw.protocols
296
- };
297
- }
298
332
  function mapUserProfile(raw, userAddress) {
299
333
  return {
300
334
  address: userAddress,
@@ -330,6 +364,46 @@ function mapApyByStrategy(raw) {
330
364
  detailedApys: hasDetails ? { apyPerAsset } : void 0
331
365
  };
332
366
  }
367
+ function computeAllocationApy(positions) {
368
+ let weightedSum = 0;
369
+ let totalValue = 0;
370
+ const buckets = {};
371
+ for (const p of positions) {
372
+ const apy = p.apy;
373
+ const value = Number(p.amount);
374
+ if (typeof apy !== "number" || !Number.isFinite(value) || value <= 0) {
375
+ continue;
376
+ }
377
+ weightedSum += apy * value;
378
+ totalValue += value;
379
+ const chainId = resolveChainId(p.chain);
380
+ const token = p.asset;
381
+ if (!SUPPORTED_CHAIN_IDS.includes(chainId))
382
+ continue;
383
+ if (!SUPPORTED_TOKENS.includes(token))
384
+ continue;
385
+ const perAsset = buckets[chainId] ?? {};
386
+ const cell = perAsset[token] ?? { weightedSum: 0, value: 0 };
387
+ cell.weightedSum += apy * value;
388
+ cell.value += value;
389
+ perAsset[token] = cell;
390
+ buckets[chainId] = perAsset;
391
+ }
392
+ const apyByChainAndAsset = {};
393
+ for (const [chainKey, perAsset] of Object.entries(buckets)) {
394
+ const chainId = Number(chainKey);
395
+ const out = {};
396
+ for (const [token, cell] of Object.entries(perAsset ?? {})) {
397
+ if (cell && cell.value > 0) {
398
+ out[token] = cell.weightedSum / cell.value;
399
+ }
400
+ }
401
+ if (Object.keys(out).length > 0)
402
+ apyByChainAndAsset[chainId] = out;
403
+ }
404
+ const totalApy = totalValue > 0 ? String(weightedSum / totalValue) : "0";
405
+ return { totalApy, apyByChainAndAsset };
406
+ }
333
407
 
334
408
  // src/agents/zyfai/zyfai.history-cursor.ts
335
409
  var InvalidHistoryCursorError = class extends Error {
@@ -429,6 +503,10 @@ var DEFAULT_ZYFAI_RPC_URLS = {
429
503
  8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
430
504
  42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
431
505
  };
506
+ var WETH_ADDRESS_BY_CHAIN = {
507
+ 8453: "0x4200000000000000000000000000000000000006",
508
+ 42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"
509
+ };
432
510
  var ZyfaiAgent = class {
433
511
  id = "zyfai";
434
512
  supportedChainIds = [8453, 42161];
@@ -436,12 +514,18 @@ var ZyfaiAgent = class {
436
514
  {
437
515
  chainId: 8453,
438
516
  chain: "BASE",
439
- assets: [{ symbol: "USDC", minDepositAmount: "1500000" }]
517
+ assets: [
518
+ { symbol: "USDC", minDepositAmount: "1500000" },
519
+ { symbol: "WETH", minDepositAmount: "500000000000000" }
520
+ ]
440
521
  },
441
522
  {
442
523
  chainId: 42161,
443
524
  chain: "ARBITRUM",
444
- assets: [{ symbol: "USDC", minDepositAmount: "1500000" }]
525
+ assets: [
526
+ { symbol: "USDC", minDepositAmount: "1500000" },
527
+ { symbol: "WETH", minDepositAmount: "500000000000000" }
528
+ ]
445
529
  }
446
530
  ];
447
531
  sdk;
@@ -607,7 +691,7 @@ var ZyfaiAgent = class {
607
691
  throw error;
608
692
  }
609
693
  }
610
- async deposit(state, chainId, amount, depositCallback) {
694
+ async deposit(state, chainId, amount, asset, depositCallback) {
611
695
  try {
612
696
  await this.connectFull(state, chainId);
613
697
  const validChainId = isValidChainId(chainId);
@@ -621,13 +705,15 @@ var ZyfaiAgent = class {
621
705
  validChainId,
622
706
  amount
623
707
  );
624
- await this.sdk.logDeposit(validChainId, txHash, amount);
708
+ const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
709
+ await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
625
710
  return { txHash, smartWallet: wallet.address, amount };
626
711
  }
627
712
  const raw = await this.sdk.depositFunds(
628
713
  this.getAddress(),
629
714
  validChainId,
630
- amount
715
+ amount,
716
+ asset
631
717
  );
632
718
  return mapDeposit(raw);
633
719
  } catch (error) {
@@ -712,17 +798,6 @@ var ZyfaiAgent = class {
712
798
  nextCursor
713
799
  };
714
800
  }
715
- // --- IAgent: Agent control ---
716
- async pause(state, chainId) {
717
- await this.connectAuth(state, chainId);
718
- const raw = await this.sdk.pauseAgent();
719
- return mapAgentStatus(raw);
720
- }
721
- async resume(state, chainId) {
722
- await this.connectAuth(state, chainId);
723
- const raw = await this.sdk.resumeAgent();
724
- return mapAgentStatus(raw);
725
- }
726
801
  async getUserProfile(state, chainId) {
727
802
  await this.connectAuth(state, chainId);
728
803
  const validChainId = isValidChainId(chainId);
@@ -934,6 +1009,130 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
934
1009
  return out;
935
1010
  }
936
1011
 
1012
+ // src/client.ts
1013
+ var import_viem3 = require("viem");
1014
+ var import_chains = require("viem/chains");
1015
+
1016
+ // src/lib/transfer-auth.ts
1017
+ var import_viem2 = require("viem");
1018
+ var ERC20_META_ABI = [
1019
+ { type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
1020
+ { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
1021
+ ];
1022
+ function buildTransferWithAuthorizationTypedData(input) {
1023
+ return {
1024
+ domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
1025
+ types: {
1026
+ TransferWithAuthorization: [
1027
+ { name: "from", type: "address" },
1028
+ { name: "to", type: "address" },
1029
+ { name: "value", type: "uint256" },
1030
+ { name: "validAfter", type: "uint256" },
1031
+ { name: "validBefore", type: "uint256" },
1032
+ { name: "nonce", type: "bytes32" }
1033
+ ]
1034
+ },
1035
+ primaryType: "TransferWithAuthorization",
1036
+ message: input.message
1037
+ };
1038
+ }
1039
+ async function readTokenMeta(publicClient, token) {
1040
+ const [tokenName, tokenVersion] = await Promise.all([
1041
+ publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
1042
+ publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
1043
+ ]);
1044
+ return { tokenName, tokenVersion };
1045
+ }
1046
+ function randomAuthNonce() {
1047
+ const bytes = new Uint8Array(32);
1048
+ globalThis.crypto.getRandomValues(bytes);
1049
+ return (0, import_viem2.bytesToHex)(bytes);
1050
+ }
1051
+
1052
+ // src/lib/sponsor-client.ts
1053
+ var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
1054
+ async function postSponsorTransferAuth(input) {
1055
+ const base2 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1056
+ let res;
1057
+ try {
1058
+ res = await fetch(`${base2}/api/v1/sponsor/erc20-transfer-auth`, {
1059
+ method: "POST",
1060
+ headers: { "content-type": "application/json", "x-owney-api-key": input.apiKey },
1061
+ body: JSON.stringify(input.body)
1062
+ });
1063
+ } catch (networkError) {
1064
+ throw new OwneyError(
1065
+ "SPONSOR_REQUEST_FAILED",
1066
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
1067
+ { cause: String(networkError) }
1068
+ );
1069
+ }
1070
+ const text = await res.text();
1071
+ let parsed = null;
1072
+ try {
1073
+ parsed = JSON.parse(text);
1074
+ } catch {
1075
+ }
1076
+ if (!res.ok || !parsed?.success || !parsed.data) {
1077
+ throw new OwneyError(
1078
+ "SPONSOR_REQUEST_FAILED",
1079
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
1080
+ { statusCode: res.status, responseBody: text.slice(0, 500) }
1081
+ );
1082
+ }
1083
+ return parsed.data;
1084
+ }
1085
+
1086
+ // src/lib/sponsored-deposit.ts
1087
+ var AUTH_WINDOW_SECONDS = 15 * 60;
1088
+ function makeSponsoredDepositCallback(deps) {
1089
+ const post = deps.httpPost ?? postSponsorTransferAuth;
1090
+ return async (smartWallet, chainId, amount) => {
1091
+ const cid = chainId;
1092
+ const token = deps.tokenAddressByChain[cid];
1093
+ if (!token) {
1094
+ throw new OwneyError(
1095
+ "CHAIN_UNSUPPORTED",
1096
+ `No sponsored token configured for chain ${chainId}`
1097
+ );
1098
+ }
1099
+ const pub = deps.getPublicClient(cid);
1100
+ const wallet = deps.getWalletClient(cid);
1101
+ const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
1102
+ const validAfter = 0n;
1103
+ const validBefore = BigInt(Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS);
1104
+ const nonce = randomAuthNonce();
1105
+ const typedData = buildTransferWithAuthorizationTypedData({
1106
+ token,
1107
+ chainId: cid,
1108
+ tokenName,
1109
+ tokenVersion,
1110
+ message: { from: deps.ownerAddress, to: smartWallet, value: BigInt(amount), validAfter, validBefore, nonce }
1111
+ });
1112
+ const authSignature = await wallet.signTypedData(
1113
+ { account: deps.ownerAddress, ...typedData }
1114
+ );
1115
+ const result = await post({
1116
+ baseUrl: deps.baseUrl,
1117
+ apiKey: deps.apiKey,
1118
+ body: {
1119
+ chainId: cid,
1120
+ token,
1121
+ from: deps.ownerAddress,
1122
+ to: smartWallet,
1123
+ value: amount,
1124
+ validAfter: validAfter.toString(),
1125
+ validBefore: validBefore.toString(),
1126
+ nonce,
1127
+ authSignature,
1128
+ tokenName,
1129
+ tokenVersion
1130
+ }
1131
+ });
1132
+ return result.txHash;
1133
+ };
1134
+ }
1135
+
937
1136
  // src/client.ts
938
1137
  function encodeMultiAgentCursor(map) {
939
1138
  return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
@@ -955,6 +1154,14 @@ function decodeMultiAgentCursor(token) {
955
1154
  }
956
1155
  return parsed;
957
1156
  }
1157
+ var SPONSORED_USDC_BY_CHAIN = {
1158
+ 8453: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1159
+ 42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
1160
+ };
1161
+ var VIEM_CHAIN = {
1162
+ 8453: import_chains.base,
1163
+ 42161: import_chains.arbitrum
1164
+ };
958
1165
  var OwneySDK = class {
959
1166
  agents = /* @__PURE__ */ new Map();
960
1167
  activeAgents = /* @__PURE__ */ new Set();
@@ -967,6 +1174,7 @@ var OwneySDK = class {
967
1174
  state = null;
968
1175
  apiKey;
969
1176
  zyfaiRpcUrls;
1177
+ cachedSponsoredCallback = null;
970
1178
  initializingAgentsPromise = null;
971
1179
  constructor(config) {
972
1180
  this.apiKey = config.apiKey;
@@ -990,6 +1198,7 @@ var OwneySDK = class {
990
1198
  );
991
1199
  }
992
1200
  this.state = { provider, walletAddress, chainId: null };
1201
+ this.cachedSponsoredCallback = null;
993
1202
  }
994
1203
  /**
995
1204
  * Disconnect from all agents and clear connection state.
@@ -1002,6 +1211,7 @@ var OwneySDK = class {
1002
1211
  this.activeAgents.clear();
1003
1212
  this.disabledAgents.clear();
1004
1213
  this.state = null;
1214
+ this.cachedSponsoredCallback = null;
1005
1215
  }
1006
1216
  isAgentDisabled(agentId) {
1007
1217
  return this.disabledAgents.has(agentId);
@@ -1035,6 +1245,46 @@ var OwneySDK = class {
1035
1245
  }
1036
1246
  return chainId;
1037
1247
  }
1248
+ requireConnectedProvider() {
1249
+ if (!this.state?.provider) {
1250
+ throw new OwneyError(
1251
+ "NOT_CONNECTED",
1252
+ "connect(provider) must be called before deposit",
1253
+ {}
1254
+ );
1255
+ }
1256
+ return this.state.provider;
1257
+ }
1258
+ /**
1259
+ * Lazily builds (and caches) the default EIP-3009 sponsored deposit callback
1260
+ * used when the caller omits `depositCallback`. Wraps the connected EIP-1193
1261
+ * provider with viem `custom(provider)` to read token meta and sign the
1262
+ * `TransferWithAuthorization`, then POSTs to the sponsor API.
1263
+ */
1264
+ getDefaultSponsoredCallback() {
1265
+ if (this.cachedSponsoredCallback)
1266
+ return this.cachedSponsoredCallback;
1267
+ const provider = this.requireConnectedProvider();
1268
+ const owner = this.state.walletAddress;
1269
+ this.cachedSponsoredCallback = makeSponsoredDepositCallback({
1270
+ apiKey: this.apiKey,
1271
+ ownerAddress: owner,
1272
+ tokenAddressByChain: SPONSORED_USDC_BY_CHAIN,
1273
+ // Casts work around viem's chain-narrowed Client vs the generic
1274
+ // PublicClient/WalletClient param types — structurally identical at
1275
+ // runtime, but the two share a name TS treats as unrelated.
1276
+ getPublicClient: (cid) => (0, import_viem3.createPublicClient)({
1277
+ chain: VIEM_CHAIN[cid],
1278
+ transport: (0, import_viem3.custom)(provider)
1279
+ }),
1280
+ getWalletClient: (cid) => (0, import_viem3.createWalletClient)({
1281
+ account: owner,
1282
+ chain: VIEM_CHAIN[cid],
1283
+ transport: (0, import_viem3.custom)(provider)
1284
+ })
1285
+ });
1286
+ return this.cachedSponsoredCallback;
1287
+ }
1038
1288
  getAgent(agentId) {
1039
1289
  const agent = this.agents.get(agentId);
1040
1290
  if (!agent) {
@@ -1188,6 +1438,7 @@ var OwneySDK = class {
1188
1438
  }
1189
1439
  const state = this.requireState();
1190
1440
  const chainId = this.requireChainId();
1441
+ const effectiveCallback = depositCallback ?? (asset === "WETH" ? void 0 : this.getDefaultSponsoredCallback());
1191
1442
  if (agentId) {
1192
1443
  if (this.isAgentDisabled(agentId)) {
1193
1444
  throw new OwneyError(
@@ -1202,7 +1453,13 @@ var OwneySDK = class {
1202
1453
  return withFailureReporting(
1203
1454
  this.apiKey,
1204
1455
  agent.id,
1205
- () => agent.deposit(state, chainId, amount, depositCallback)
1456
+ () => agent.deposit(
1457
+ state,
1458
+ chainId,
1459
+ amount,
1460
+ asset,
1461
+ effectiveCallback
1462
+ )
1206
1463
  );
1207
1464
  }
1208
1465
  const totalAmount = BigInt(amount);
@@ -1236,7 +1493,13 @@ var OwneySDK = class {
1236
1493
  agentResults[agent.id] = await withFailureReporting(
1237
1494
  this.apiKey,
1238
1495
  agent.id,
1239
- () => agent.deposit(state, chainId, agentAmount, depositCallback)
1496
+ () => agent.deposit(
1497
+ state,
1498
+ chainId,
1499
+ agentAmount,
1500
+ asset,
1501
+ effectiveCallback
1502
+ )
1240
1503
  );
1241
1504
  }
1242
1505
  return { agentResults };
@@ -1430,10 +1693,7 @@ var OwneySDK = class {
1430
1693
  disabledBalances,
1431
1694
  requested
1432
1695
  );
1433
- const enabledTotal = enabledBalances.reduce(
1434
- (s, b) => s + b.balance,
1435
- 0n
1436
- );
1696
+ const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
1437
1697
  const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
1438
1698
  agent,
1439
1699
  balance,
@@ -1704,27 +1964,6 @@ var OwneySDK = class {
1704
1964
  nextCursor: hasMore ? encodeMultiAgentCursor(nextCursorMap) : void 0
1705
1965
  };
1706
1966
  }
1707
- // --- Agent control ---
1708
- /**
1709
- * Pause an agent's automated operations. The agent will stop rebalancing until resumed.
1710
- * @param agentId - The agent to pause (e.g. "zyfai")
1711
- * @returns OwneyAgentStatus with success flag
1712
- */
1713
- async pauseAgent(agentId) {
1714
- const state = this.requireState();
1715
- const chainId = this.requireChainId();
1716
- return this.getAgent(agentId).pause(state, chainId);
1717
- }
1718
- /**
1719
- * Resume an agent's automated operations.
1720
- * @param agentId - The agent to resume (e.g. "zyfai")
1721
- * @returns OwneyAgentStatus with success flag
1722
- */
1723
- async resumeAgent(agentId) {
1724
- const state = this.requireState();
1725
- const chainId = this.requireChainId();
1726
- return this.getAgent(agentId).resume(state, chainId);
1727
- }
1728
1967
  /**
1729
1968
  * Get the user's profile for a specific agent, or all agents.
1730
1969
  * @param agentId - Optional. Agent to query. Omit for all agents.
@@ -1799,12 +2038,13 @@ var OwneySDK = class {
1799
2038
  const chainId = this.requireChainId();
1800
2039
  const activeAgents = this.getActiveAgents();
1801
2040
  const balances = {};
2041
+ const positionsByAgent = {};
1802
2042
  let totalBalance = 0;
1803
2043
  const entries = [...activeAgents.entries()];
1804
2044
  const balanceResults = await Promise.allSettled(
1805
2045
  entries.map(async ([id, agent]) => {
1806
2046
  const b = await agent.getBalances(state, chainId);
1807
- return [id, Number(b.totalBalance)];
2047
+ return [id, Number(b.totalBalance), b.positions ?? []];
1808
2048
  })
1809
2049
  );
1810
2050
  let successCount = 0;
@@ -1813,8 +2053,9 @@ var OwneySDK = class {
1813
2053
  const settledResult = balanceResults[i];
1814
2054
  const [agentId] = entries[i];
1815
2055
  if (settledResult.status === "fulfilled") {
1816
- const [id, bal] = settledResult.value;
2056
+ const [id, bal, agentPositions] = settledResult.value;
1817
2057
  balances[id] = bal;
2058
+ positionsByAgent[id] = agentPositions;
1818
2059
  totalBalance += bal;
1819
2060
  successCount += 1;
1820
2061
  } else {
@@ -1848,13 +2089,70 @@ var OwneySDK = class {
1848
2089
  }
1849
2090
  }
1850
2091
  return {
1851
- agentAllocation: allocation
2092
+ agentAllocation: allocation,
2093
+ agentPositions: positionsByAgent
2094
+ };
2095
+ }
2096
+ /**
2097
+ * Allocation-weighted APY from the current portfolio: each pool's APY
2098
+ * weighted by the user's position value in it. More accurate per-user than
2099
+ * the historical 30D APY. (ROUT-228)
2100
+ * @param options.agentId - Optional. Scope to one agent. Omit to aggregate
2101
+ * positions across all active agents.
2102
+ */
2103
+ async getAllocationApy({
2104
+ agentId
2105
+ } = {}) {
2106
+ const state = this.requireState();
2107
+ const chainId = this.requireChainId();
2108
+ const activeAgents = agentId ? /* @__PURE__ */ new Map([[agentId, this.getAgent(agentId)]]) : this.getActiveAgents();
2109
+ const entries = [...activeAgents.entries()];
2110
+ const balanceResults = await Promise.allSettled(
2111
+ entries.map(async ([id, agent]) => {
2112
+ const b = await agent.getBalances(state, chainId);
2113
+ return [id, b.positions ?? []];
2114
+ })
2115
+ );
2116
+ const agentApy = {};
2117
+ const allPositions = [];
2118
+ let successCount = 0;
2119
+ const agentErrors = {};
2120
+ for (let i = 0; i < balanceResults.length; i++) {
2121
+ const settled = balanceResults[i];
2122
+ const [aid] = entries[i];
2123
+ if (settled.status === "fulfilled") {
2124
+ const [id, positions] = settled.value;
2125
+ const per = computeAllocationApy(positions);
2126
+ agentApy[id] = {
2127
+ totalApy: per.totalApy,
2128
+ apyByChainAndAsset: per.apyByChainAndAsset
2129
+ };
2130
+ allPositions.push(...positions);
2131
+ successCount += 1;
2132
+ } else {
2133
+ const reason = settled.reason;
2134
+ agentErrors[aid] = reason instanceof Error ? reason.message : String(reason);
2135
+ console.error(`getAllocationApy agent "${aid}" failed:`, reason);
2136
+ }
2137
+ }
2138
+ if (successCount === 0) {
2139
+ throw new OwneyError(
2140
+ "ALLOCATION_ALL_FAILED",
2141
+ "Failed to fetch allocation APY: all agents failed.",
2142
+ { agentErrors }
2143
+ );
2144
+ }
2145
+ const overall = computeAllocationApy(allPositions);
2146
+ return {
2147
+ totalApy: overall.totalApy,
2148
+ apyByChainAndAsset: overall.apyByChainAndAsset,
2149
+ agentApy
1852
2150
  };
1853
2151
  }
1854
2152
  };
1855
2153
 
1856
2154
  // src/agents/zyfai/zyfai.siwx.ts
1857
- var import_viem2 = require("viem");
2155
+ var import_viem4 = require("viem");
1858
2156
  var import_siwe = require("siwe");
1859
2157
  var import_sdk2 = require("@zyfai/sdk");
1860
2158
 
@@ -1936,7 +2234,7 @@ function buildSIWXConfig(deps) {
1936
2234
  issuedAt,
1937
2235
  toString() {
1938
2236
  return new import_siwe.SiweMessage({
1939
- address: (0, import_viem2.getAddress)(accountAddress),
2237
+ address: (0, import_viem4.getAddress)(accountAddress),
1940
2238
  chainId: numericChainId(chainId),
1941
2239
  domain,
1942
2240
  uri,