@owney/sdk 0.7.25-beta.4 → 0.7.25-beta.7

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
@@ -159,6 +159,34 @@ function rateLimitDelay(error, now = Date.now()) {
159
159
  }
160
160
 
161
161
  // src/lib/agent-reads.ts
162
+ function rateLimitDiagnostics(error) {
163
+ const seen = /* @__PURE__ */ new Set();
164
+ let details = {};
165
+ function visit(value, depth = 0) {
166
+ if (depth > 6 || !value || typeof value !== "object" || seen.has(value))
167
+ return;
168
+ seen.add(value);
169
+ const record = value;
170
+ for (const key2 of [
171
+ "rpcSource",
172
+ "chainId",
173
+ "rpcMethod",
174
+ "providerRequestId",
175
+ "statusCode"
176
+ ]) {
177
+ const candidate = record[key2];
178
+ if (candidate !== void 0 && details[key2] === void 0)
179
+ details = { ...details, [key2]: candidate };
180
+ }
181
+ for (const key2 of ["details", "cause", "response", "fields", "error"])
182
+ visit(record[key2], depth + 1);
183
+ }
184
+ visit(error);
185
+ return {
186
+ rpcSource: details.rpcSource ?? "agent-api",
187
+ ...details
188
+ };
189
+ }
162
190
  var AgentReads = class {
163
191
  inFlight = /* @__PURE__ */ new Map();
164
192
  cooldowns = /* @__PURE__ */ new Map();
@@ -166,14 +194,15 @@ var AgentReads = class {
166
194
  clearInFlight() {
167
195
  this.inFlight.clear();
168
196
  }
169
- limited(agentId, until) {
197
+ limited(agentId, until, diagnostics) {
170
198
  return new OwneyError(
171
199
  "AGENT_RATE_LIMITED",
172
200
  "Too many requests. Please wait before trying again.",
173
201
  {
174
202
  statusCode: 429,
175
203
  retryAt: until,
176
- retryAfterSeconds: Math.max(0, Math.ceil((until - Date.now()) / 1e3))
204
+ retryAfterSeconds: Math.max(0, Math.ceil((until - Date.now()) / 1e3)),
205
+ ...diagnostics
177
206
  },
178
207
  agentId
179
208
  );
@@ -181,7 +210,9 @@ var AgentReads = class {
181
210
  run(agentId, key2, fetch2) {
182
211
  const cooldown = this.cooldowns.get(agentId);
183
212
  if (cooldown && cooldown.until > Date.now()) {
184
- return Promise.reject(this.limited(agentId, cooldown.until));
213
+ return Promise.reject(
214
+ this.limited(agentId, cooldown.until, cooldown.diagnostics)
215
+ );
185
216
  }
186
217
  const requestKey = JSON.stringify([agentId, key2]);
187
218
  const existing = this.inFlight.get(requestKey);
@@ -202,8 +233,9 @@ var AgentReads = class {
202
233
  Math.min(3e4 * 2 ** (failures - 1), 3e5)
203
234
  );
204
235
  const until = Math.max(previous?.until ?? 0, Date.now() + delay);
205
- this.cooldowns.set(agentId, { until, failures });
206
- throw this.limited(agentId, until);
236
+ const diagnostics = rateLimitDiagnostics(error);
237
+ this.cooldowns.set(agentId, { until, failures, diagnostics });
238
+ throw this.limited(agentId, until, diagnostics);
207
239
  }
208
240
  ).finally(() => {
209
241
  if (this.inFlight.get(requestKey) === promise)
@@ -216,7 +248,7 @@ var AgentReads = class {
216
248
 
217
249
  // src/agents/zyfai/zyfai.agent.ts
218
250
  var import_sdk = require("@zyfai/sdk");
219
- var import_viem = require("viem");
251
+ var import_viem2 = require("viem");
220
252
  var import_chains = require("viem/chains");
221
253
 
222
254
  // src/types/config.ts
@@ -1097,15 +1129,145 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
1097
1129
  return !protocolListsEqual(current.protocols, desiredProtocols);
1098
1130
  }
1099
1131
 
1132
+ // src/lib/paid-rpc.ts
1133
+ var import_viem = require("viem");
1134
+ var PAID_RPC_RETRY_COUNT = 3;
1135
+ var PAID_RPC_RETRY_DELAY_MS = 1e3;
1136
+ function configuredRpcProxyBaseUrl(baseUrl) {
1137
+ const configured = (baseUrl ?? "")?.trim();
1138
+ if (!configured) {
1139
+ throw new Error(
1140
+ "OWNEY_ROUTING_API_BASE_URL is required for RPC proxy requests unless routingApiBaseUrl or all rpcUrls are configured."
1141
+ );
1142
+ }
1143
+ return configured;
1144
+ }
1145
+ function rpcProxyUrl(chainId, apiKey, baseUrl) {
1146
+ const url = new URL(
1147
+ `${configuredRpcProxyBaseUrl(baseUrl).replace(/\/$/, "")}/api/v1/rpc/${chainId}`
1148
+ );
1149
+ if (apiKey) url.searchParams.set("apiKey", apiKey);
1150
+ return url.toString();
1151
+ }
1152
+ function resolveRpcUrl(rpcUrls, chainId, apiKey = "", baseUrl) {
1153
+ const url = rpcUrls?.[chainId]?.trim();
1154
+ if (url) return url;
1155
+ return rpcProxyUrl(chainId, apiKey, baseUrl);
1156
+ }
1157
+ function resolveRpcUrls(rpcUrls, apiKey = "", baseUrl) {
1158
+ return {
1159
+ 1: resolveRpcUrl(rpcUrls, 1, apiKey, baseUrl),
1160
+ 8453: resolveRpcUrl(rpcUrls, 8453, apiKey, baseUrl),
1161
+ 42161: resolveRpcUrl(rpcUrls, 42161, apiKey, baseUrl)
1162
+ };
1163
+ }
1164
+ function createPaidRpcClient(chain, rpcUrls) {
1165
+ const url = resolveRpcUrl(
1166
+ rpcUrls,
1167
+ chain.id
1168
+ );
1169
+ return (0, import_viem.createPublicClient)({
1170
+ chain,
1171
+ transport: (0, import_viem.http)(url, {
1172
+ retryCount: PAID_RPC_RETRY_COUNT,
1173
+ retryDelay: PAID_RPC_RETRY_DELAY_MS
1174
+ })
1175
+ });
1176
+ }
1177
+ function headerValue(error, name) {
1178
+ const seen = /* @__PURE__ */ new Set();
1179
+ let value;
1180
+ function visit(candidate, depth = 0) {
1181
+ if (value || depth > 6 || !candidate || typeof candidate !== "object")
1182
+ return;
1183
+ if (seen.has(candidate)) return;
1184
+ seen.add(candidate);
1185
+ const record = candidate;
1186
+ const headers = record.headers;
1187
+ let found;
1188
+ if (typeof headers?.get === "function") {
1189
+ found = headers.get(name);
1190
+ } else {
1191
+ const headerRecord = headers;
1192
+ found = headerRecord?.[name] ?? headerRecord?.[name.toLowerCase()];
1193
+ }
1194
+ if (typeof found === "string" && found) {
1195
+ value = found;
1196
+ return;
1197
+ }
1198
+ for (const key2 of ["cause", "details", "response", "error"])
1199
+ visit(record[key2], depth + 1);
1200
+ }
1201
+ visit(error);
1202
+ return value;
1203
+ }
1204
+ function statusCode(error) {
1205
+ const seen = /* @__PURE__ */ new Set();
1206
+ let status;
1207
+ function visit(candidate, depth = 0) {
1208
+ if (status || depth > 6 || !candidate || typeof candidate !== "object")
1209
+ return;
1210
+ if (seen.has(candidate)) return;
1211
+ seen.add(candidate);
1212
+ const record = candidate;
1213
+ for (const key2 of ["status", "statusCode"]) {
1214
+ const parsed = Number(record[key2]);
1215
+ if (Number.isInteger(parsed) && parsed >= 100 && parsed <= 599) {
1216
+ status = parsed;
1217
+ return;
1218
+ }
1219
+ }
1220
+ for (const key2 of ["cause", "details", "response", "error"])
1221
+ visit(record[key2], depth + 1);
1222
+ }
1223
+ visit(error);
1224
+ return status;
1225
+ }
1226
+ function paidRpcError(error, chainId, rpcMethod, agentId) {
1227
+ const delay = rateLimitDelay(error);
1228
+ const providerRequestId = headerValue(error, "x-alchemy-request-id") ?? headerValue(error, "x-request-id");
1229
+ if (delay === void 0) {
1230
+ return new OwneyError(
1231
+ "AGENT_API_ERROR",
1232
+ "The blockchain RPC request failed.",
1233
+ {
1234
+ rpcSource: "paid-rpc",
1235
+ chainId,
1236
+ rpcMethod,
1237
+ ...statusCode(error) ? { statusCode: statusCode(error) } : {},
1238
+ ...providerRequestId ? { providerRequestId } : {}
1239
+ },
1240
+ agentId
1241
+ );
1242
+ }
1243
+ const retryAt = Date.now() + delay;
1244
+ return new OwneyError(
1245
+ "AGENT_RATE_LIMITED",
1246
+ "The blockchain RPC is rate limited. Please wait before trying again.",
1247
+ {
1248
+ rpcSource: "paid-rpc",
1249
+ chainId,
1250
+ rpcMethod,
1251
+ statusCode: 429,
1252
+ retryAt,
1253
+ retryAfterSeconds: Math.max(0, Math.ceil(delay / 1e3)),
1254
+ ...providerRequestId ? { providerRequestId } : {}
1255
+ },
1256
+ agentId
1257
+ );
1258
+ }
1259
+ async function withPaidRpcDiagnostics(operation, chainId, rpcMethod, agentId) {
1260
+ try {
1261
+ return await operation();
1262
+ } catch (error) {
1263
+ throw paidRpcError(error, chainId, rpcMethod, agentId);
1264
+ }
1265
+ }
1266
+
1100
1267
  // src/agents/zyfai/zyfai.agent.ts
1101
- var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem.parseAbi)([
1268
+ var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem2.parseAbi)([
1102
1269
  "function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
1103
1270
  ]);
1104
- var DEFAULT_ZYFAI_RPC_URLS = {
1105
- 8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
1106
- 42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
1107
- 1: "https://eth-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
1108
- };
1109
1271
  var WETH_ADDRESS_BY_CHAIN = {
1110
1272
  8453: "0x4200000000000000000000000000000000000006",
1111
1273
  42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
@@ -1171,7 +1333,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
1171
1333
  earningsSnapshot = null;
1172
1334
  earningsGeneration = 0;
1173
1335
  constructor(apiKey, rpcUrls, referralSource) {
1174
- this.rpcUrls = rpcUrls ?? DEFAULT_ZYFAI_RPC_URLS;
1336
+ this.rpcUrls = resolveRpcUrls(rpcUrls);
1175
1337
  this.sdk = new import_sdk.ZyfaiSDK({
1176
1338
  apiKey,
1177
1339
  rpcUrls: this.rpcUrls,
@@ -1185,10 +1347,10 @@ var ZyfaiAgent = class _ZyfaiAgent {
1185
1347
  getPublicClient(chainId) {
1186
1348
  const cached = this.publicClients.get(chainId);
1187
1349
  if (cached) return cached;
1188
- const client = (0, import_viem.createPublicClient)({
1189
- chain: VIEM_CHAIN[chainId],
1190
- transport: (0, import_viem.http)(this.rpcUrls[chainId])
1191
- });
1350
+ const client = createPaidRpcClient(
1351
+ VIEM_CHAIN[chainId],
1352
+ this.rpcUrls
1353
+ );
1192
1354
  this.publicClients.set(chainId, client);
1193
1355
  return client;
1194
1356
  }
@@ -2156,7 +2318,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
2156
2318
  };
2157
2319
 
2158
2320
  // src/agents/yieldseeker/yieldseeker.agent.ts
2159
- var import_viem6 = require("viem");
2321
+ var import_viem7 = require("viem");
2160
2322
  var import_chains3 = require("viem/chains");
2161
2323
 
2162
2324
  // src/lib/chain-guard.ts
@@ -2195,7 +2357,7 @@ async function ensureWalletOnChain(pub, wallet, expected) {
2195
2357
  }
2196
2358
 
2197
2359
  // src/lib/transfer-auth.ts
2198
- var import_viem2 = require("viem");
2360
+ var import_viem3 = require("viem");
2199
2361
 
2200
2362
  // src/lib/sponsor-client.ts
2201
2363
  var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
@@ -2311,7 +2473,7 @@ async function postSponsorBatchTransfer(input) {
2311
2473
  }
2312
2474
 
2313
2475
  // src/lib/permit2.ts
2314
- var import_viem3 = require("viem");
2476
+ var import_viem4 = require("viem");
2315
2477
  var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
2316
2478
  var MAX_UINT256 = 2n ** 256n - 1n;
2317
2479
  function permit2ApprovalAmount(requiredAmount) {
@@ -2352,7 +2514,7 @@ var ERC20_ALLOWANCE_ABI = [
2352
2514
  function randomPermit2Nonce() {
2353
2515
  const bytes = new Uint8Array(32);
2354
2516
  globalThis.crypto.getRandomValues(bytes);
2355
- return BigInt((0, import_viem3.bytesToHex)(bytes));
2517
+ return BigInt((0, import_viem4.bytesToHex)(bytes));
2356
2518
  }
2357
2519
  async function readPermit2Allowance(publicClient, token, owner, blockNumber) {
2358
2520
  return publicClient.readContract({
@@ -2395,7 +2557,7 @@ function makeVerificationAwareDepositCallback(implementation) {
2395
2557
 
2396
2558
  // src/agents/yieldseeker/yieldseeker.auth.ts
2397
2559
  var import_siwe = require("siwe");
2398
- var import_viem4 = require("viem");
2560
+ var import_viem5 = require("viem");
2399
2561
  var import_chains2 = require("viem/chains");
2400
2562
 
2401
2563
  // src/agents/yieldseeker/yieldseeker.auth-cache.ts
@@ -2508,7 +2670,7 @@ function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
2508
2670
  return new import_siwe.SiweMessage({
2509
2671
  scheme: url.protocol.slice(0, -1),
2510
2672
  domain: url.host,
2511
- address: (0, import_viem4.getAddress)(address),
2673
+ address: (0, import_viem5.getAddress)(address),
2512
2674
  uri: url.origin,
2513
2675
  version: "1",
2514
2676
  chainId,
@@ -2594,15 +2756,15 @@ var YieldseekerAuth = class {
2594
2756
  clearYieldseekerSession(state.walletAddress, chainId);
2595
2757
  }
2596
2758
  async sign(state, chainId) {
2597
- const account = (0, import_viem4.getAddress)(state.walletAddress);
2598
- const publicClient = (0, import_viem4.createPublicClient)({
2759
+ const account = (0, import_viem5.getAddress)(state.walletAddress);
2760
+ const publicClient = (0, import_viem5.createPublicClient)({
2599
2761
  chain: import_chains2.base,
2600
- transport: (0, import_viem4.custom)(state.provider)
2762
+ transport: (0, import_viem5.custom)(state.provider)
2601
2763
  });
2602
- const walletClient = (0, import_viem4.createWalletClient)({
2764
+ const walletClient = (0, import_viem5.createWalletClient)({
2603
2765
  account,
2604
2766
  chain: import_chains2.base,
2605
- transport: (0, import_viem4.custom)(state.provider)
2767
+ transport: (0, import_viem5.custom)(state.provider)
2606
2768
  });
2607
2769
  await ensureWalletOnChain(
2608
2770
  publicClient,
@@ -2768,7 +2930,7 @@ var YieldseekerApiClient = class {
2768
2930
  };
2769
2931
 
2770
2932
  // src/agents/yieldseeker/yieldseeker.mapper.ts
2771
- var import_viem5 = require("viem");
2933
+ var import_viem6 = require("viem");
2772
2934
 
2773
2935
  // src/lib/helpers/snapshot-apy.ts
2774
2936
  var DAY_MS = 864e5;
@@ -2835,10 +2997,10 @@ function raw(value, endpoint) {
2835
2997
  return BigInt(value);
2836
2998
  }
2837
2999
  function decimal(value, decimals, endpoint) {
2838
- return (0, import_viem5.formatUnits)(raw(value, endpoint), decimals);
3000
+ return (0, import_viem6.formatUnits)(raw(value, endpoint), decimals);
2839
3001
  }
2840
3002
  function usd(rawAmount, decimals, price) {
2841
- return Number((0, import_viem5.formatUnits)(rawAmount, decimals)) * price;
3003
+ return Number((0, import_viem6.formatUnits)(rawAmount, decimals)) * price;
2842
3004
  }
2843
3005
  function percent(value) {
2844
3006
  const result = Number(value);
@@ -2864,7 +3026,7 @@ function assetAddressValue(record, address) {
2864
3026
  }
2865
3027
  function position(value, asset, baseAssetDecimals) {
2866
3028
  const option = value?.yieldOption;
2867
- if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem5.isAddress)(option.address)) {
3029
+ if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem6.isAddress)(option.address)) {
2868
3030
  return invalid("yield positions", "missing vault metadata");
2869
3031
  }
2870
3032
  return {
@@ -2945,7 +3107,7 @@ function mapYieldseekerEarnings(contexts) {
2945
3107
  chain: "BASE",
2946
3108
  chainId: 8453,
2947
3109
  asset: context.asset,
2948
- amount: (0, import_viem5.formatUnits)(amount, context.snapshot.baseAssetDecimals)
3110
+ amount: (0, import_viem6.formatUnits)(amount, context.snapshot.baseAssetDecimals)
2949
3111
  });
2950
3112
  lifetimeEarnings += usd(
2951
3113
  amount,
@@ -3212,6 +3374,9 @@ var YIELDSEEKER_USERNAME_PREFIX = "owney_";
3212
3374
  var YIELDSEEKER_USERNAME_RANDOM_LENGTH = 14;
3213
3375
  var YIELDSEEKER_USERNAME_CREATE_ATTEMPTS = 3;
3214
3376
  var YIELDSEEKER_YIELD_OPTIONS_CACHE_MS = 6e4;
3377
+ var YIELDSEEKER_AGENT_LIST_CACHE_MS = 6e4;
3378
+ var YIELDSEEKER_PORTFOLIO_CACHE_MS = 3e4;
3379
+ var YIELDSEEKER_ACTIVITY_CACHE_MS = 6e4;
3215
3380
  function generateYieldseekerUsername() {
3216
3381
  const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
3217
3382
  return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
@@ -3255,6 +3420,7 @@ function query(params) {
3255
3420
  var YieldseekerAgent = class {
3256
3421
  id = "yieldseeker";
3257
3422
  balanceComposition = "tokens-plus-positions";
3423
+ withdrawalRequiresWalletApproval = true;
3258
3424
  supportedChainIds = [8453];
3259
3425
  supportedAssets = [
3260
3426
  {
@@ -3268,11 +3434,17 @@ var YieldseekerAgent = class {
3268
3434
  ];
3269
3435
  api;
3270
3436
  auth;
3437
+ rpcUrls;
3438
+ receiptClient;
3271
3439
  transactionExecutor;
3272
3440
  unwindReceiptWaiter;
3273
3441
  agentContexts = /* @__PURE__ */ new Map();
3274
3442
  users = /* @__PURE__ */ new Map();
3275
3443
  pendingAgents = /* @__PURE__ */ new Map();
3444
+ pendingWalletContexts = /* @__PURE__ */ new Map();
3445
+ readCache = /* @__PURE__ */ new Map();
3446
+ pendingReads = /* @__PURE__ */ new Map();
3447
+ readGeneration = 0;
3276
3448
  yieldOptions = /* @__PURE__ */ new Map();
3277
3449
  pendingYieldOptions = /* @__PURE__ */ new Map();
3278
3450
  constructor(owneyApiKey, options = {}) {
@@ -3282,10 +3454,16 @@ var YieldseekerAgent = class {
3282
3454
  options.fetchFn
3283
3455
  );
3284
3456
  this.auth = new YieldseekerAuth(options.auth);
3457
+ this.rpcUrls = options.rpcUrls;
3285
3458
  this.transactionExecutor = options.transactionExecutor;
3286
3459
  this.unwindReceiptWaiter = options.unwindReceiptWaiter;
3287
3460
  }
3461
+ getReceiptClient() {
3462
+ this.receiptClient ??= createPaidRpcClient(import_chains3.base, this.rpcUrls);
3463
+ return this.receiptClient;
3464
+ }
3288
3465
  async disconnect() {
3466
+ this.readGeneration += 1;
3289
3467
  this.auth.clear();
3290
3468
  for (const key2 of this.users.keys()) {
3291
3469
  const [walletAddress, chainId] = key2.split(":");
@@ -3294,6 +3472,9 @@ var YieldseekerAgent = class {
3294
3472
  this.users.clear();
3295
3473
  this.agentContexts.clear();
3296
3474
  this.pendingAgents.clear();
3475
+ this.pendingWalletContexts.clear();
3476
+ this.readCache.clear();
3477
+ this.pendingReads.clear();
3297
3478
  }
3298
3479
  async activateAgent(state, chainId, asset) {
3299
3480
  this.assertChain(chainId);
@@ -3330,12 +3511,12 @@ var YieldseekerAgent = class {
3330
3511
  await this.waitForReceipt(state, chainId, txHash);
3331
3512
  } else {
3332
3513
  txHash = await this.submitTransaction(state, chainId, {
3333
- from: (0, import_viem6.getAddress)(state.walletAddress),
3514
+ from: (0, import_viem7.getAddress)(state.walletAddress),
3334
3515
  to: YIELDSEEKER_ASSET_METADATA[asset].address,
3335
- data: (0, import_viem6.encodeFunctionData)({
3336
- abi: import_viem6.erc20Abi,
3516
+ data: (0, import_viem7.encodeFunctionData)({
3517
+ abi: import_viem7.erc20Abi,
3337
3518
  functionName: "transfer",
3338
- args: [(0, import_viem6.getAddress)(context.wallet.walletAddress), BigInt(amount)]
3519
+ args: [(0, import_viem7.getAddress)(context.wallet.walletAddress), BigInt(amount)]
3339
3520
  }),
3340
3521
  value: "0",
3341
3522
  chainId
@@ -3435,15 +3616,15 @@ var YieldseekerAgent = class {
3435
3616
  remaining: remaining.toString()
3436
3617
  });
3437
3618
  }
3438
- const account = (0, import_viem6.getAddress)(state.walletAddress);
3619
+ const account = (0, import_viem7.getAddress)(state.walletAddress);
3439
3620
  const txHash = await this.submitTransaction(state, chainId, {
3440
3621
  from: account,
3441
- to: (0, import_viem6.getAddress)(context.wallet.walletAddress),
3442
- data: amount === void 0 ? (0, import_viem6.encodeFunctionData)({
3622
+ to: (0, import_viem7.getAddress)(context.wallet.walletAddress),
3623
+ data: amount === void 0 ? (0, import_viem7.encodeFunctionData)({
3443
3624
  abi: YIELDSEEKER_AGENT_WALLET_ABI,
3444
3625
  functionName: "withdrawAllAssetToUser",
3445
3626
  args: [account, metadata.address]
3446
- }) : (0, import_viem6.encodeFunctionData)({
3627
+ }) : (0, import_viem7.encodeFunctionData)({
3447
3628
  abi: YIELDSEEKER_AGENT_WALLET_ABI,
3448
3629
  functionName: "withdrawAssetToUser",
3449
3630
  args: [account, metadata.address, requested]
@@ -3499,7 +3680,7 @@ var YieldseekerAgent = class {
3499
3680
  );
3500
3681
  const vaultAddresses = new Set(
3501
3682
  catalog.flat().filter(
3502
- (yieldOption) => yieldOption.chainId === chainId && (0, import_viem6.isAddress)(yieldOption.address)
3683
+ (yieldOption) => yieldOption.chainId === chainId && (0, import_viem7.isAddress)(yieldOption.address)
3503
3684
  ).map((yieldOption) => yieldOption.address.toLowerCase())
3504
3685
  );
3505
3686
  return mapYieldseekerHistory(contexts, {
@@ -3556,6 +3737,86 @@ var YieldseekerAgent = class {
3556
3737
  contextKey(state, chainId, asset) {
3557
3738
  return `${this.userKey(state, chainId)}:${asset}`;
3558
3739
  }
3740
+ cachedRead(key2, ttlMs, read2) {
3741
+ const cached = this.readCache.get(key2);
3742
+ if (cached && cached.expiresAt > Date.now()) {
3743
+ return Promise.resolve(cached.value);
3744
+ }
3745
+ const pending = this.pendingReads.get(key2);
3746
+ if (pending) return pending;
3747
+ const generation = this.readGeneration;
3748
+ const request = Promise.resolve().then(read2).then((value) => {
3749
+ if (this.readGeneration === generation) {
3750
+ this.readCache.set(key2, {
3751
+ expiresAt: Date.now() + ttlMs,
3752
+ value
3753
+ });
3754
+ }
3755
+ return value;
3756
+ }).finally(() => {
3757
+ if (this.pendingReads.get(key2) === request) {
3758
+ this.pendingReads.delete(key2);
3759
+ }
3760
+ });
3761
+ this.pendingReads.set(key2, request);
3762
+ return request;
3763
+ }
3764
+ agentListKey(state, chainId) {
3765
+ return `agents:${this.userKey(state, chainId)}`;
3766
+ }
3767
+ async listAgents(state, chainId, user) {
3768
+ const response = await this.cachedRead(
3769
+ this.agentListKey(state, chainId),
3770
+ YIELDSEEKER_AGENT_LIST_CACHE_MS,
3771
+ async () => {
3772
+ const response2 = await this.walletRequest(
3773
+ state,
3774
+ chainId,
3775
+ `/users/${user.userId}/agents`
3776
+ );
3777
+ if (!Array.isArray(response2?.agents)) {
3778
+ throw this.invalidResponse("agent list");
3779
+ }
3780
+ return response2;
3781
+ }
3782
+ );
3783
+ return response.agents;
3784
+ }
3785
+ async contextForAgent(state, chainId, user, agent, asset) {
3786
+ const key2 = this.contextKey(state, chainId, asset);
3787
+ const cached = this.agentContexts.get(key2);
3788
+ if (cached?.agent.agentId === agent.agentId) return cached;
3789
+ const pending = this.pendingWalletContexts.get(key2);
3790
+ if (pending) return pending;
3791
+ const generation = this.readGeneration;
3792
+ const request = this.walletRequest(
3793
+ state,
3794
+ chainId,
3795
+ `/users/${user.userId}/agents/${agent.agentId}/wallet`
3796
+ ).then((walletResponse) => {
3797
+ if (!walletResponse?.agentWallet || !(0, import_viem7.isAddress)(walletResponse.agentWallet.walletAddress)) {
3798
+ throw this.invalidResponse("agent wallet");
3799
+ }
3800
+ const context = {
3801
+ user,
3802
+ agent,
3803
+ wallet: walletResponse.agentWallet,
3804
+ asset
3805
+ };
3806
+ if (this.readGeneration === generation) {
3807
+ this.agentContexts.set(key2, context);
3808
+ }
3809
+ return context;
3810
+ });
3811
+ this.pendingWalletContexts.set(key2, request);
3812
+ try {
3813
+ return await request;
3814
+ } finally {
3815
+ if (this.pendingWalletContexts.get(key2) === request) {
3816
+ this.pendingWalletContexts.delete(key2);
3817
+ }
3818
+ }
3819
+ }
3559
3820
  async resolveUser(state, chainId) {
3560
3821
  const key2 = this.userKey(state, chainId);
3561
3822
  const inMemory = this.users.get(key2);
@@ -3565,7 +3826,7 @@ var YieldseekerAgent = class {
3565
3826
  this.users.set(key2, persisted);
3566
3827
  return persisted;
3567
3828
  }
3568
- const walletAddress = (0, import_viem6.getAddress)(state.walletAddress);
3829
+ const walletAddress = (0, import_viem7.getAddress)(state.walletAddress);
3569
3830
  let user = null;
3570
3831
  try {
3571
3832
  const login = await this.providerRequest(
@@ -3652,16 +3913,9 @@ var YieldseekerAgent = class {
3652
3913
  }
3653
3914
  async resolveAgent(state, chainId, asset, createIfMissing) {
3654
3915
  const user = await this.resolveUser(state, chainId);
3655
- const response = await this.walletRequest(
3656
- state,
3657
- chainId,
3658
- `/users/${user.userId}/agents`
3659
- );
3660
- if (!Array.isArray(response?.agents)) {
3661
- throw this.invalidResponse("agent list");
3662
- }
3916
+ const agents = await this.listAgents(state, chainId, user);
3663
3917
  const metadata = YIELDSEEKER_ASSET_METADATA[asset];
3664
- let agent = response.agents.find(
3918
+ let agent = agents.find(
3665
3919
  (candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
3666
3920
  );
3667
3921
  if (!agent && createIfMissing) {
@@ -3682,83 +3936,91 @@ var YieldseekerAgent = class {
3682
3936
  }
3683
3937
  );
3684
3938
  agent = created?.agent;
3939
+ if (agent) {
3940
+ this.readCache.set(this.agentListKey(state, chainId), {
3941
+ expiresAt: Date.now() + YIELDSEEKER_AGENT_LIST_CACHE_MS,
3942
+ value: {
3943
+ agents: [...agents, agent]
3944
+ }
3945
+ });
3946
+ }
3685
3947
  }
3686
3948
  if (!agent) return null;
3687
3949
  this.assertAgent(agent);
3688
- const walletResponse = await this.walletRequest(
3689
- state,
3690
- chainId,
3691
- `/users/${user.userId}/agents/${agent.agentId}/wallet`
3692
- );
3693
- if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
3694
- throw this.invalidResponse("agent wallet");
3695
- }
3696
- return { user, agent, wallet: walletResponse.agentWallet, asset };
3950
+ return this.contextForAgent(state, chainId, user, agent, asset);
3697
3951
  }
3698
3952
  async loadPortfolio(state, chainId, options) {
3699
3953
  const user = await this.resolveUser(state, chainId);
3700
- const response = await this.walletRequest(
3701
- state,
3702
- chainId,
3703
- `/users/${user.userId}/agents`
3704
- );
3705
- if (!Array.isArray(response?.agents)) {
3706
- throw this.invalidResponse("agent list");
3707
- }
3954
+ const agents = await this.listAgents(state, chainId, user);
3708
3955
  const contexts = [];
3709
- for (const agent of response.agents) {
3956
+ for (const agent of agents) {
3710
3957
  const asset = this.assetForAgent(agent);
3711
3958
  if (!this.isOwneyAgent(agent) || !asset || agent.chainId !== chainId || agent.type !== "vault" || options.asset && options.asset !== asset) {
3712
3959
  continue;
3713
3960
  }
3714
3961
  this.assertAgent(agent);
3715
- const walletResponse = await this.walletRequest(
3716
- state,
3717
- chainId,
3718
- `/users/${user.userId}/agents/${agent.agentId}/wallet`
3719
- );
3720
- if (!walletResponse?.agentWallet || !(0, import_viem6.isAddress)(walletResponse.agentWallet.walletAddress)) {
3721
- throw this.invalidResponse("agent wallet");
3722
- }
3723
- const context = {
3724
- user,
3725
- agent,
3726
- wallet: walletResponse.agentWallet,
3727
- asset
3728
- };
3729
- this.agentContexts.set(this.contextKey(state, chainId, asset), context);
3730
- contexts.push(context);
3962
+ contexts.push(this.contextForAgent(state, chainId, user, agent, asset));
3731
3963
  }
3964
+ const resolvedContexts = await Promise.all(contexts);
3732
3965
  return Promise.all(
3733
- contexts.map(
3966
+ resolvedContexts.map(
3734
3967
  (context) => this.loadPortfolioContext(state, chainId, context, options)
3735
3968
  )
3736
3969
  );
3737
3970
  }
3738
3971
  async loadPortfolioContext(state, chainId, context, options = {}) {
3972
+ const contextKey = this.contextKey(state, chainId, context.asset);
3739
3973
  const [snapshot, positions, historic, actions] = await Promise.all([
3740
- this.walletRequest(
3741
- state,
3742
- chainId,
3743
- `${this.agentPath(context, "snapshot")}${query({
3744
- shouldOnlyUseRecentValue: true,
3745
- shouldAllowStaleOnError: true
3746
- })}`
3974
+ this.cachedRead(
3975
+ `snapshot:${contextKey}`,
3976
+ YIELDSEEKER_PORTFOLIO_CACHE_MS,
3977
+ async () => {
3978
+ const response = await this.walletRequest(
3979
+ state,
3980
+ chainId,
3981
+ `${this.agentPath(context, "snapshot")}${query({
3982
+ shouldOnlyUseRecentValue: true,
3983
+ shouldAllowStaleOnError: true
3984
+ })}`
3985
+ );
3986
+ if (!response?.agentSnapshot) {
3987
+ throw this.invalidResponse("agent snapshot");
3988
+ }
3989
+ return response;
3990
+ }
3747
3991
  ),
3748
- this.walletRequest(
3749
- state,
3750
- chainId,
3751
- this.agentPath(context, "yield-positions")
3992
+ this.cachedRead(
3993
+ `positions:${contextKey}`,
3994
+ YIELDSEEKER_PORTFOLIO_CACHE_MS,
3995
+ async () => {
3996
+ const response = await this.walletRequest(
3997
+ state,
3998
+ chainId,
3999
+ this.agentPath(context, "yield-positions")
4000
+ );
4001
+ if (!Array.isArray(response?.yieldPositions)) {
4002
+ throw this.invalidResponse("yield positions");
4003
+ }
4004
+ return response;
4005
+ }
3752
4006
  ),
3753
- options.historic ? this.walletRequest(
3754
- state,
3755
- chainId,
3756
- this.agentPath(context, "wallet/historic-position")
4007
+ options.historic ? this.cachedRead(
4008
+ `historic:${contextKey}`,
4009
+ YIELDSEEKER_ACTIVITY_CACHE_MS,
4010
+ () => this.walletRequest(
4011
+ state,
4012
+ chainId,
4013
+ this.agentPath(context, "wallet/historic-position")
4014
+ )
3757
4015
  ) : Promise.resolve(void 0),
3758
- options.actions ? this.walletRequest(
3759
- state,
3760
- chainId,
3761
- this.agentPath(context, "actions")
4016
+ options.actions ? this.cachedRead(
4017
+ `actions:${contextKey}`,
4018
+ YIELDSEEKER_ACTIVITY_CACHE_MS,
4019
+ () => this.walletRequest(
4020
+ state,
4021
+ chainId,
4022
+ this.agentPath(context, "actions")
4023
+ )
3762
4024
  ) : Promise.resolve(void 0)
3763
4025
  ]);
3764
4026
  if (!snapshot?.agentSnapshot) {
@@ -3784,7 +4046,7 @@ var YieldseekerAgent = class {
3784
4046
  this.agentPath(context, "deploy"),
3785
4047
  { method: "POST", body: {} }
3786
4048
  );
3787
- if (!deployed?.agentWallet || !(0, import_viem6.isAddress)(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
4049
+ if (!deployed?.agentWallet || !(0, import_viem7.isAddress)(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
3788
4050
  throw this.invalidResponse("agent deployment", {
3789
4051
  reason: "Deploy did not return the expected Agent Wallet."
3790
4052
  });
@@ -3792,6 +4054,13 @@ var YieldseekerAgent = class {
3792
4054
  context.wallet = deployed.agentWallet;
3793
4055
  }
3794
4056
  async refreshSnapshotAfterMovement(state, chainId, context, movement) {
4057
+ const contextKey = this.contextKey(state, chainId, context.asset);
4058
+ const invalidatePortfolio = () => {
4059
+ for (const kind of ["snapshot", "positions", "historic", "actions"]) {
4060
+ this.readCache.delete(`${kind}:${contextKey}`);
4061
+ }
4062
+ };
4063
+ invalidatePortfolio();
3795
4064
  try {
3796
4065
  const response = await this.walletRequest(
3797
4066
  state,
@@ -3808,6 +4077,8 @@ var YieldseekerAgent = class {
3808
4077
  `[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
3809
4078
  error
3810
4079
  );
4080
+ } finally {
4081
+ invalidatePortfolio();
3811
4082
  }
3812
4083
  }
3813
4084
  agentPath(context, suffix) {
@@ -3858,6 +4129,7 @@ var YieldseekerAgent = class {
3858
4129
  code,
3859
4130
  `Yieldseeker request failed: ${error.providerCode}.`,
3860
4131
  {
4132
+ rpcSource: "agent-api",
3861
4133
  statusCode: error.status,
3862
4134
  providerCode: error.providerCode,
3863
4135
  ...error.responseFields ? { fields: error.responseFields } : {}
@@ -3870,32 +4142,37 @@ var YieldseekerAgent = class {
3870
4142
  return this.transactionExecutor(state, chainId, transaction);
3871
4143
  }
3872
4144
  this.assertTransaction(transaction, state, chainId);
3873
- const account = (0, import_viem6.getAddress)(state.walletAddress);
3874
- const walletClient = (0, import_viem6.createWalletClient)({
4145
+ const account = (0, import_viem7.getAddress)(state.walletAddress);
4146
+ const walletClient = (0, import_viem7.createWalletClient)({
3875
4147
  account,
3876
4148
  chain: import_chains3.base,
3877
- transport: (0, import_viem6.custom)(state.provider)
4149
+ transport: (0, import_viem7.custom)(state.provider)
3878
4150
  });
3879
- const publicClient = (0, import_viem6.createPublicClient)({
4151
+ const walletChainClient = (0, import_viem7.createPublicClient)({
3880
4152
  chain: import_chains3.base,
3881
- transport: (0, import_viem6.custom)(state.provider)
4153
+ transport: (0, import_viem7.custom)(state.provider)
3882
4154
  });
3883
4155
  await ensureWalletOnChain(
3884
- publicClient,
4156
+ walletChainClient,
3885
4157
  walletClient,
3886
4158
  8453
3887
4159
  );
3888
4160
  const hash = await walletClient.sendTransaction({
3889
4161
  account,
3890
4162
  chain: import_chains3.base,
3891
- to: (0, import_viem6.getAddress)(transaction.to),
4163
+ to: (0, import_viem7.getAddress)(transaction.to),
3892
4164
  data: transaction.data,
3893
4165
  value: BigInt(transaction.value)
3894
4166
  });
3895
- const receipt = await publicClient.waitForTransactionReceipt({
3896
- hash,
3897
- confirmations: 1
3898
- });
4167
+ const receipt = await withPaidRpcDiagnostics(
4168
+ () => this.getReceiptClient().waitForTransactionReceipt({
4169
+ hash,
4170
+ confirmations: 1
4171
+ }),
4172
+ 8453,
4173
+ "eth_getTransactionReceipt",
4174
+ this.id
4175
+ );
3899
4176
  if (receipt.status !== "success") {
3900
4177
  throw new OwneyError(
3901
4178
  "AGENT_TRANSACTION_REVERTED",
@@ -3911,14 +4188,15 @@ var YieldseekerAgent = class {
3911
4188
  await this.unwindReceiptWaiter(state, chainId, transactionHash);
3912
4189
  return;
3913
4190
  }
3914
- const publicClient = (0, import_viem6.createPublicClient)({
3915
- chain: import_chains3.base,
3916
- transport: (0, import_viem6.custom)(state.provider)
3917
- });
3918
- const receipt = await publicClient.waitForTransactionReceipt({
3919
- hash: transactionHash,
3920
- confirmations: 1
3921
- });
4191
+ const receipt = await withPaidRpcDiagnostics(
4192
+ () => this.getReceiptClient().waitForTransactionReceipt({
4193
+ hash: transactionHash,
4194
+ confirmations: 1
4195
+ }),
4196
+ 8453,
4197
+ "eth_getTransactionReceipt",
4198
+ this.id
4199
+ );
3922
4200
  if (receipt.status !== "success") {
3923
4201
  throw new OwneyError(
3924
4202
  "AGENT_TRANSACTION_REVERTED",
@@ -3929,12 +4207,12 @@ var YieldseekerAgent = class {
3929
4207
  }
3930
4208
  }
3931
4209
  assertTransaction(transaction, state, chainId) {
3932
- if (!transaction || typeof transaction.from !== "string" || !(0, import_viem6.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem6.isAddress)(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || (0, import_viem6.getAddress)(transaction.from) !== (0, import_viem6.getAddress)(state.walletAddress)) {
4210
+ if (!transaction || typeof transaction.from !== "string" || !(0, import_viem7.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem7.isAddress)(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || (0, import_viem7.getAddress)(transaction.from) !== (0, import_viem7.getAddress)(state.walletAddress)) {
3933
4211
  throw this.invalidResponse("transaction");
3934
4212
  }
3935
4213
  }
3936
4214
  assertAgent(agent) {
3937
- if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem6.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
4215
+ if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem7.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
3938
4216
  throw this.invalidResponse("agent");
3939
4217
  }
3940
4218
  }
@@ -4087,7 +4365,7 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
4087
4365
  }
4088
4366
 
4089
4367
  // src/lib/helpers/withdraw-helper.ts
4090
- var import_viem7 = require("viem");
4368
+ var import_viem8 = require("viem");
4091
4369
  function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
4092
4370
  const target = asset.toUpperCase();
4093
4371
  return agents.map((agent) => {
@@ -4095,7 +4373,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
4095
4373
  const tokenBalance = agentBalance?.tokens.find(
4096
4374
  (t) => t.chainId === chainId && t.asset.toUpperCase() === target
4097
4375
  );
4098
- let balance = tokenBalance ? (0, import_viem7.parseUnits)(tokenBalance.amount, decimals) : 0n;
4376
+ let balance = tokenBalance ? (0, import_viem8.parseUnits)(tokenBalance.amount, decimals) : 0n;
4099
4377
  if (agent.balanceComposition === "tokens-plus-positions") {
4100
4378
  const chainNameById = {
4101
4379
  1: "ETHEREUM",
@@ -4114,7 +4392,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
4114
4392
  } catch {
4115
4393
  }
4116
4394
  }
4117
- balance += (0, import_viem7.parseUnits)(position2.amount, decimals);
4395
+ balance += (0, import_viem8.parseUnits)(position2.amount, decimals);
4118
4396
  }
4119
4397
  }
4120
4398
  return { agent, balance };
@@ -4273,15 +4551,50 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
4273
4551
  }
4274
4552
 
4275
4553
  // src/client.ts
4276
- var import_viem11 = require("viem");
4554
+ var import_viem12 = require("viem");
4277
4555
  var import_chains4 = require("viem/chains");
4278
4556
 
4279
4557
  // src/lib/sponsored-token-batch.ts
4280
- var import_viem9 = require("viem");
4558
+ var import_viem10 = require("viem");
4281
4559
 
4282
4560
  // src/lib/permit2-batch.ts
4283
- var import_viem8 = require("viem");
4561
+ var import_viem9 = require("viem");
4284
4562
  var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
4563
+ var BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
4564
+ var MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
4565
+ var ERC2612_PERMIT_TYPES = {
4566
+ Permit: [
4567
+ { name: "owner", type: "address" },
4568
+ { name: "spender", type: "address" },
4569
+ { name: "value", type: "uint256" },
4570
+ { name: "nonce", type: "uint256" },
4571
+ { name: "deadline", type: "uint256" }
4572
+ ]
4573
+ };
4574
+ var ERC2612_READ_ABI = (0, import_viem9.parseAbi)([
4575
+ "function name() view returns (string)",
4576
+ "function version() view returns (string)",
4577
+ "function nonces(address owner) view returns (uint256)"
4578
+ ]);
4579
+ function erc2612TypedData(input) {
4580
+ return {
4581
+ domain: {
4582
+ name: input.permit.tokenName,
4583
+ version: input.permit.tokenVersion,
4584
+ chainId: input.chainId,
4585
+ verifyingContract: input.token
4586
+ },
4587
+ types: ERC2612_PERMIT_TYPES,
4588
+ primaryType: "Permit",
4589
+ message: {
4590
+ owner: input.owner,
4591
+ spender: BATCH_PERMIT2_ADDRESS,
4592
+ value: BigInt(input.permit.value),
4593
+ nonce: BigInt(input.permit.nonce),
4594
+ deadline: BigInt(input.permit.deadline)
4595
+ }
4596
+ };
4597
+ }
4285
4598
  var PERMIT_BATCH_TYPES = {
4286
4599
  PermitBatchWitnessTransferFrom: [
4287
4600
  { name: "permitted", type: "TokenPermissions[]" },
@@ -4296,7 +4609,7 @@ var PERMIT_BATCH_TYPES = {
4296
4609
  { name: "amount", type: "uint256" }
4297
4610
  ]
4298
4611
  };
4299
- var PERMIT2_BATCH_ABI = (0, import_viem8.parseAbi)([
4612
+ var PERMIT2_BATCH_ABI = (0, import_viem9.parseAbi)([
4300
4613
  "struct TokenPermissions { address token; uint256 amount; }",
4301
4614
  "struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
4302
4615
  "struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
@@ -4355,7 +4668,7 @@ function clear(key2) {
4355
4668
  else window.localStorage.removeItem(key2);
4356
4669
  }
4357
4670
  function sponsorTokenBatch(i) {
4358
- const key2 = `owney.token-batch.v1:${(0, import_viem9.keccak256)((0, import_viem9.toBytes)(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
4671
+ const key2 = `owney.token-batch.v1:${(0, import_viem10.keccak256)((0, import_viem10.toBytes)(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
4359
4672
  const plan = planOf(i.transfers);
4360
4673
  const active = inflight.get(key2);
4361
4674
  if (active) {
@@ -4386,7 +4699,7 @@ async function execute(i, key2, plan) {
4386
4699
  baseUrl: i.baseUrl,
4387
4700
  body
4388
4701
  });
4389
- if (!prepared.serializedTransaction || (0, import_viem9.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
4702
+ if (!prepared.serializedTransaction || (0, import_viem10.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
4390
4703
  throw new Error(
4391
4704
  "Sponsorship API did not return a valid prepared transaction."
4392
4705
  );
@@ -4401,7 +4714,7 @@ async function execute(i, key2, plan) {
4401
4714
  baseUrl: i.baseUrl,
4402
4715
  body
4403
4716
  });
4404
- if (result.txHash !== (0, import_viem9.keccak256)(body.serializedTransaction))
4717
+ if (result.txHash !== (0, import_viem10.keccak256)(body.serializedTransaction))
4405
4718
  throw new Error(
4406
4719
  "Sponsorship receipt does not match the pending transaction."
4407
4720
  );
@@ -4416,7 +4729,7 @@ async function execute(i, key2, plan) {
4416
4729
  const saved = read(key2);
4417
4730
  if (saved) {
4418
4731
  const previous = JSON.parse(saved);
4419
- if (previous.chainId !== i.chainId || !(0, import_viem9.isAddressEqual)(previous.from, i.owner) || !(0, import_viem9.isAddressEqual)(previous.token, i.token) || planOf(previous.transfers) !== plan)
4732
+ if (previous.chainId !== i.chainId || !(0, import_viem10.isAddressEqual)(previous.from, i.owner) || !(0, import_viem10.isAddressEqual)(previous.token, i.token) || planOf(previous.transfers) !== plan)
4420
4733
  throw new Error(
4421
4734
  "Retry the previous token deposit and agent split first to reconcile its status."
4422
4735
  );
@@ -4433,28 +4746,67 @@ async function execute(i, key2, plan) {
4433
4746
  "DEPOSIT_INSUFFICIENT_BALANCE",
4434
4747
  "Insufficient token balance for this deposit."
4435
4748
  );
4436
- if (allowance < total)
4749
+ if (allowance < total && (i.chainId !== 8453 || !(0, import_viem10.isAddressEqual)(i.token, BASE_USDC_ADDRESS)))
4437
4750
  throw new OwneyError(
4438
4751
  "PERMIT2_APPROVAL_REQUIRED",
4439
4752
  "token deposits need a one-time Permit2 approval."
4440
4753
  );
4441
- const relayer = await getSponsorRelayerAddress({
4754
+ const now = (await i.pub.getBlock()).timestamp;
4755
+ let erc2612Permit;
4756
+ if (allowance < total) {
4757
+ const [tokenName, tokenVersion, permitNonce] = await Promise.all([
4758
+ i.pub.readContract({
4759
+ address: i.token,
4760
+ abi: ERC2612_READ_ABI,
4761
+ functionName: "name"
4762
+ }),
4763
+ i.pub.readContract({
4764
+ address: i.token,
4765
+ abi: ERC2612_READ_ABI,
4766
+ functionName: "version"
4767
+ }),
4768
+ i.pub.readContract({
4769
+ address: i.token,
4770
+ abi: ERC2612_READ_ABI,
4771
+ functionName: "nonces",
4772
+ args: [i.owner]
4773
+ })
4774
+ ]);
4775
+ const unsignedPermit = {
4776
+ value: MAX_UINT256.toString(),
4777
+ nonce: permitNonce.toString(),
4778
+ deadline: (now + 900n).toString(),
4779
+ tokenName,
4780
+ tokenVersion
4781
+ };
4782
+ const signature2 = await i.wallet.signTypedData({
4783
+ account: i.owner,
4784
+ ...erc2612TypedData({
4785
+ chainId: 8453,
4786
+ token: i.token,
4787
+ owner: i.owner,
4788
+ permit: unsignedPermit
4789
+ })
4790
+ });
4791
+ erc2612Permit = { ...unsignedPermit, signature: signature2 };
4792
+ }
4793
+ const spender = erc2612Permit ? MULTICALL3_ADDRESS : await getSponsorRelayerAddress({
4442
4794
  apiKey: i.apiKey,
4443
4795
  baseUrl: i.baseUrl,
4444
4796
  chainId: i.chainId
4445
4797
  });
4446
- const now = (await i.pub.getBlock()).timestamp;
4447
4798
  const unsigned = {
4448
4799
  chainId: i.chainId,
4449
4800
  token: i.token,
4450
4801
  from: i.owner,
4451
4802
  transfers: i.transfers,
4452
4803
  nonce: randomPermit2Nonce().toString(),
4453
- deadline: (now + 900n).toString()
4804
+ deadline: (now + 900n).toString(),
4805
+ ...erc2612Permit ? { erc2612Permit } : {}
4454
4806
  };
4455
4807
  const signature = await i.wallet.signTypedData({
4456
4808
  account: i.owner,
4457
- ...batchTypedData(unsigned, relayer)
4809
+ ...batchTypedData(unsigned, spender)
4458
4810
  });
4459
4811
  i.onApproved?.();
4460
4812
  return send({ ...unsigned, signature });
@@ -4474,8 +4826,8 @@ function makeSponsoredTokenCallback(deps) {
4474
4826
  "CHAIN_UNSUPPORTED",
4475
4827
  `No sponsored token configured for chain ${chainId}`
4476
4828
  );
4477
- const pub = deps.getPublicClient(chainId), wallet = deps.getWalletClient(chainId);
4478
- await ensureWalletOnChain(pub, wallet, chainId);
4829
+ const pub = deps.getPublicClient(chainId), walletChain = deps.getWalletChainClient?.(chainId) ?? pub, wallet = deps.getWalletClient(chainId);
4830
+ await ensureWalletOnChain(walletChain, wallet, chainId);
4479
4831
  return sponsorTokenBatch({
4480
4832
  apiKey: deps.apiKey,
4481
4833
  baseUrl: deps.baseUrl,
@@ -4571,7 +4923,7 @@ async function runAgentDepositBatch(chainId, legs, transfer) {
4571
4923
  }
4572
4924
 
4573
4925
  // src/lib/sponsored-calls-deposit.ts
4574
- var import_viem10 = require("viem");
4926
+ var import_viem11 = require("viem");
4575
4927
  var DEFAULT_POLL_INTERVAL_MS = 1500;
4576
4928
  var DEFAULT_MAX_POLLS = 30;
4577
4929
  async function paymasterSupported(provider, owner, chainId) {
@@ -4579,7 +4931,7 @@ async function paymasterSupported(provider, owner, chainId) {
4579
4931
  method: "wallet_getCapabilities",
4580
4932
  params: [owner]
4581
4933
  });
4582
- const forChain = caps?.[(0, import_viem10.toHex)(chainId)] ?? caps?.[String(chainId)];
4934
+ const forChain = caps?.[(0, import_viem11.toHex)(chainId)] ?? caps?.[String(chainId)];
4583
4935
  return Boolean(forChain?.paymasterService?.supported);
4584
4936
  }
4585
4937
  function makeSponsoredCallsCallback(deps) {
@@ -4616,8 +4968,8 @@ function makeSponsoredCallsCallback(deps) {
4616
4968
  const calls = transfers.map((transfer) => ({
4617
4969
  to: token,
4618
4970
  value: "0x0",
4619
- data: (0, import_viem10.encodeFunctionData)({
4620
- abi: import_viem10.erc20Abi,
4971
+ data: (0, import_viem11.encodeFunctionData)({
4972
+ abi: import_viem11.erc20Abi,
4621
4973
  functionName: "transfer",
4622
4974
  args: [transfer.to, BigInt(transfer.amount)]
4623
4975
  })
@@ -4655,7 +5007,7 @@ function makeSponsoredCallsCallback(deps) {
4655
5007
  {
4656
5008
  version: "2.0.0",
4657
5009
  from: deps.ownerAddress,
4658
- chainId: (0, import_viem10.toHex)(chainId),
5010
+ chainId: (0, import_viem11.toHex)(chainId),
4659
5011
  atomicRequired: transfers.length > 1,
4660
5012
  calls,
4661
5013
  capabilities: {
@@ -4777,6 +5129,7 @@ var OwneySDK = class {
4777
5129
  // leave every user's agent profile alone.
4778
5130
  orgAgentConfig;
4779
5131
  orgAgentConfigPromise = null;
5132
+ rpcUrls;
4780
5133
  zyfaiRpcUrls;
4781
5134
  yieldseekerApiBaseUrl;
4782
5135
  yieldseekerSiweOrigin;
@@ -4801,7 +5154,12 @@ var OwneySDK = class {
4801
5154
  constructor(config) {
4802
5155
  this.apiKey = config.apiKey;
4803
5156
  if (config.debug) setOwneyDebug(true);
4804
- this.zyfaiRpcUrls = config.zyfaiRpcUrls;
5157
+ this.rpcUrls = resolveRpcUrls(
5158
+ config.rpcUrls,
5159
+ config.apiKey,
5160
+ config.routingApiBaseUrl
5161
+ );
5162
+ this.zyfaiRpcUrls = config.rpcUrls ? void 0 : config.zyfaiRpcUrls;
4805
5163
  this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
4806
5164
  this.yieldseekerSiweOrigin = config.yieldseekerSiweOrigin;
4807
5165
  this.routingApiBaseUrl = config.routingApiBaseUrl;
@@ -4891,6 +5249,9 @@ var OwneySDK = class {
4891
5249
  }
4892
5250
  return this.state.provider;
4893
5251
  }
5252
+ getPaidRpcClient(chainId) {
5253
+ return createPaidRpcClient(VIEM_CHAIN2[chainId], this.rpcUrls);
5254
+ }
4894
5255
  /** Builds the default USDC batch callback for the connected wallet. */
4895
5256
  getDefaultSponsoredCallback(onApproved) {
4896
5257
  if (!onApproved && this.cachedSponsoredCallback)
@@ -4906,14 +5267,15 @@ var OwneySDK = class {
4906
5267
  // Casts work around viem's chain-narrowed Client vs the generic
4907
5268
  // PublicClient/WalletClient param types — structurally identical at
4908
5269
  // runtime, but the two share a name TS treats as unrelated.
4909
- getPublicClient: (cid) => (0, import_viem11.createPublicClient)({
5270
+ getPublicClient: (cid) => this.getPaidRpcClient(cid),
5271
+ getWalletChainClient: (cid) => (0, import_viem12.createPublicClient)({
4910
5272
  chain: VIEM_CHAIN2[cid],
4911
- transport: (0, import_viem11.custom)(provider)
5273
+ transport: (0, import_viem12.custom)(provider)
4912
5274
  }),
4913
- getWalletClient: (cid) => (0, import_viem11.createWalletClient)({
5275
+ getWalletClient: (cid) => (0, import_viem12.createWalletClient)({
4914
5276
  account: owner,
4915
5277
  chain: VIEM_CHAIN2[cid],
4916
- transport: (0, import_viem11.custom)(provider)
5278
+ transport: (0, import_viem12.custom)(provider)
4917
5279
  })
4918
5280
  });
4919
5281
  if (!onApproved) this.cachedSponsoredCallback = callback;
@@ -4956,14 +5318,15 @@ var OwneySDK = class {
4956
5318
  // Casts work around viem's chain-narrowed Client vs the generic
4957
5319
  // PublicClient/WalletClient param types — structurally identical at
4958
5320
  // runtime, but the two share a name TS treats as unrelated.
4959
- getPublicClient: (cid) => (0, import_viem11.createPublicClient)({
5321
+ getPublicClient: (cid) => this.getPaidRpcClient(cid),
5322
+ getWalletChainClient: (cid) => (0, import_viem12.createPublicClient)({
4960
5323
  chain: VIEM_CHAIN2[cid],
4961
- transport: (0, import_viem11.custom)(provider)
5324
+ transport: (0, import_viem12.custom)(provider)
4962
5325
  }),
4963
- getWalletClient: (cid) => (0, import_viem11.createWalletClient)({
5326
+ getWalletClient: (cid) => (0, import_viem12.createWalletClient)({
4964
5327
  account: owner,
4965
5328
  chain: VIEM_CHAIN2[cid],
4966
- transport: (0, import_viem11.custom)(provider)
5329
+ transport: (0, import_viem12.custom)(provider)
4967
5330
  })
4968
5331
  });
4969
5332
  if (!onApproved) this.cachedWethSponsoredCallback = callback;
@@ -5069,12 +5432,21 @@ var OwneySDK = class {
5069
5432
  createAgent(agentId, key2) {
5070
5433
  if (agentId === "zyfai") {
5071
5434
  if (!key2) return null;
5072
- return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
5435
+ return new ZyfaiAgent(
5436
+ key2,
5437
+ this.zyfaiRpcUrls ? resolveRpcUrls(
5438
+ this.zyfaiRpcUrls,
5439
+ this.apiKey,
5440
+ this.routingApiBaseUrl
5441
+ ) : this.rpcUrls,
5442
+ this.referralSource
5443
+ );
5073
5444
  }
5074
5445
  if (agentId === "yieldseeker") {
5075
5446
  return new YieldseekerAgent(this.apiKey, {
5076
5447
  auth: { origin: this.yieldseekerSiweOrigin },
5077
- baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
5448
+ baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl),
5449
+ rpcUrls: this.rpcUrls
5078
5450
  });
5079
5451
  }
5080
5452
  return null;
@@ -5410,10 +5782,12 @@ var OwneySDK = class {
5410
5782
  * Invokes `agent.deposit` with the resolved sponsored callback, composing
5411
5783
  * two independent auto-recovery mechanisms:
5412
5784
  *
5413
- * 1. Missing Permit2 allowance: when the app did not supply its own
5414
- * callback and the attempt fails with `PERMIT2_APPROVAL_REQUIRED` on a
5415
- * token deposit, this is the wallet's first Permit2 deposit for that token. We send
5416
- * the one-time (user-paid) Permit2 approval via `approvePermit2()` and
5785
+ * 1. Missing Permit2 allowance outside the atomic Base-USDC path: when the
5786
+ * app did not supply its own callback and the attempt fails with
5787
+ * `PERMIT2_APPROVAL_REQUIRED`, this is the wallet's first Permit2 deposit
5788
+ * for that token. Base USDC bundles a gasless ERC-2612 approval inside
5789
+ * its sponsored deposit and never reaches this branch. Other tokens send
5790
+ * the one-time user-paid Permit2 approval via `approvePermit2()` and
5417
5791
  * retry the SAME sponsored attempt once. Bounded to one approval attempt
5418
5792
  * per call so a wallet/agent that keeps reporting the allowance as
5419
5793
  * missing can't loop forever. If `approvePermit2()` itself throws (e.g.
@@ -5604,6 +5978,32 @@ var OwneySDK = class {
5604
5978
  }
5605
5979
  return eligible;
5606
5980
  }
5981
+ /**
5982
+ * Run owner-approved withdrawals before relayer-only withdrawals. Wallet
5983
+ * approval is the only point at which the user can cancel the aggregate
5984
+ * operation, so no relayer leg should commit before it has completed.
5985
+ */
5986
+ orderAgentsForWithdrawal(agents) {
5987
+ return agents.map((agent, index) => ({ agent, index })).sort((left, right) => {
5988
+ const approvalOrder = Number(Boolean(right.agent.withdrawalRequiresWalletApproval)) - Number(Boolean(left.agent.withdrawalRequiresWalletApproval));
5989
+ return approvalOrder || left.index - right.index;
5990
+ }).map(({ agent }) => agent);
5991
+ }
5992
+ isUserRejectedWithdrawal(error) {
5993
+ let current = error;
5994
+ const seen = /* @__PURE__ */ new Set();
5995
+ while (current && typeof current === "object" && !seen.has(current)) {
5996
+ seen.add(current);
5997
+ const candidate = current;
5998
+ if (candidate.code === 4001 || candidate.code === "4001") return true;
5999
+ const message = [candidate.message, candidate.shortMessage].filter((value) => typeof value === "string").join(" ");
6000
+ if (/user (?:rejected|denied)|rejected by user/i.test(message)) {
6001
+ return true;
6002
+ }
6003
+ current = candidate.cause;
6004
+ }
6005
+ return false;
6006
+ }
5607
6007
  // --- Fund operations ---
5608
6008
  /**
5609
6009
  * Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
@@ -5639,13 +6039,15 @@ var OwneySDK = class {
5639
6039
  );
5640
6040
  }
5641
6041
  const eligibleAgents = this.getEligibleAgents(chainId, asset);
6042
+ const withdrawalAgents = this.orderAgentsForWithdrawal(eligibleAgents);
5642
6043
  if (!amount) {
5643
6044
  const results2 = {};
5644
6045
  const agentErrors2 = {};
5645
- for (const agent of eligibleAgents) {
6046
+ for (const agent of withdrawalAgents) {
5646
6047
  try {
5647
6048
  results2[agent.id] = await agent.withdraw(state, chainId, token);
5648
6049
  } catch (err) {
6050
+ if (this.isUserRejectedWithdrawal(err)) throw err;
5649
6051
  console.error(`withdraw failed for agent "${agent.id}":`, err);
5650
6052
  agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
5651
6053
  const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
@@ -5726,12 +6128,13 @@ var OwneySDK = class {
5726
6128
  planned: 0n
5727
6129
  }));
5728
6130
  const plans = [...disabledPlans, ...enabledPlans];
6131
+ const orderedPlans = this.orderAgentsForWithdrawal(plans.map((p) => p.agent)).map((agent) => plans.find((plan) => plan.agent === agent));
5729
6132
  const results = {};
5730
6133
  const agentErrors = {
5731
6134
  ...aggregated.agentErrors ?? {}
5732
6135
  };
5733
- for (let i = 0; i < plans.length; i++) {
5734
- const p = plans[i];
6136
+ for (let i = 0; i < orderedPlans.length; i++) {
6137
+ const p = orderedPlans[i];
5735
6138
  if (p.planned === 0n) continue;
5736
6139
  try {
5737
6140
  results[p.agent.id] = await p.agent.withdraw(
@@ -5741,6 +6144,7 @@ var OwneySDK = class {
5741
6144
  p.planned.toString()
5742
6145
  );
5743
6146
  } catch (err) {
6147
+ if (this.isUserRejectedWithdrawal(err)) throw err;
5744
6148
  console.error(`withdraw failed for agent "${p.agent.id}":`, err);
5745
6149
  agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
5746
6150
  const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
@@ -5752,7 +6156,7 @@ var OwneySDK = class {
5752
6156
  );
5753
6157
  const failedAmount = p.planned;
5754
6158
  p.planned = 0n;
5755
- redistributeShare(plans, i, failedAmount);
6159
+ redistributeShare(orderedPlans, i, failedAmount);
5756
6160
  }
5757
6161
  }
5758
6162
  if (Object.keys(results).length === 0) {
@@ -6199,17 +6603,18 @@ var OwneySDK = class {
6199
6603
  );
6200
6604
  }
6201
6605
  const provider = this.requireConnectedProvider();
6202
- const publicClient = (0, import_viem11.createPublicClient)({
6606
+ const walletChainClient = (0, import_viem12.createPublicClient)({
6203
6607
  chain: VIEM_CHAIN2[chainId],
6204
- transport: (0, import_viem11.custom)(provider)
6608
+ transport: (0, import_viem12.custom)(provider)
6205
6609
  });
6610
+ const publicClient = this.getPaidRpcClient(chainId);
6206
6611
  const approvalAmount = permit2ApprovalAmount(requiredAmount);
6207
- const wallet = (0, import_viem11.createWalletClient)({
6612
+ const wallet = (0, import_viem12.createWalletClient)({
6208
6613
  account: state.walletAddress,
6209
6614
  chain: VIEM_CHAIN2[chainId],
6210
- transport: (0, import_viem11.custom)(provider)
6615
+ transport: (0, import_viem12.custom)(provider)
6211
6616
  });
6212
- await ensureWalletOnChain(publicClient, wallet, chainId);
6617
+ await ensureWalletOnChain(walletChainClient, wallet, chainId);
6213
6618
  const hash = await wallet.writeContract({
6214
6619
  address: token,
6215
6620
  abi: ERC20_ALLOWANCE_ABI,
@@ -6218,10 +6623,14 @@ var OwneySDK = class {
6218
6623
  account: state.walletAddress,
6219
6624
  chain: VIEM_CHAIN2[chainId]
6220
6625
  });
6221
- const receipt = await publicClient.waitForTransactionReceipt({
6222
- hash,
6223
- confirmations: 1
6224
- });
6626
+ const receipt = await withPaidRpcDiagnostics(
6627
+ () => publicClient.waitForTransactionReceipt({
6628
+ hash,
6629
+ confirmations: 1
6630
+ }),
6631
+ chainId,
6632
+ "eth_getTransactionReceipt"
6633
+ );
6225
6634
  if (receipt.status !== "success") {
6226
6635
  throw new Error(`Permit2 approval reverted (tx ${hash})`);
6227
6636
  }
@@ -6365,7 +6774,7 @@ var OwneySDK = class {
6365
6774
  };
6366
6775
 
6367
6776
  // src/agents/zyfai/zyfai.siwx.ts
6368
- var import_viem12 = require("viem");
6777
+ var import_viem13 = require("viem");
6369
6778
  var import_siwe2 = require("siwe");
6370
6779
  var import_sdk2 = require("@zyfai/sdk");
6371
6780
 
@@ -6492,7 +6901,7 @@ function buildSIWXConfig(deps) {
6492
6901
  issuedAt,
6493
6902
  toString() {
6494
6903
  return new import_siwe2.SiweMessage({
6495
- address: (0, import_viem12.getAddress)(accountAddress),
6904
+ address: (0, import_viem13.getAddress)(accountAddress),
6496
6905
  chainId: numericChainId(chainId),
6497
6906
  domain,
6498
6907
  uri,