@owney/sdk 0.7.21-beta.3 → 0.7.21-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -32,6 +32,26 @@ __export(index_exports, {
32
32
  });
33
33
  module.exports = __toCommonJS(index_exports);
34
34
 
35
+ // src/lib/deposit-batch-callback.ts
36
+ var batchCallbacks = /* @__PURE__ */ new WeakMap();
37
+ var getDepositBatchTransfer = (callback) => callback ? batchCallbacks.get(callback) : void 0;
38
+ function toBatchTransfer(to, amount, verification) {
39
+ return {
40
+ to,
41
+ amount,
42
+ ...verification ? {
43
+ yieldseeker: {
44
+ signature: verification.signature,
45
+ userId: verification.userId,
46
+ agentId: verification.yieldseekerAgentId
47
+ }
48
+ } : {}
49
+ };
50
+ }
51
+ function registerDepositBatch(callback, transfer) {
52
+ batchCallbacks.set(callback, transfer);
53
+ }
54
+
35
55
  // src/errors.ts
36
56
  var OwneyError = class extends Error {
37
57
  code;
@@ -2098,39 +2118,6 @@ async function ensureWalletOnChain(pub, wallet, expected) {
2098
2118
 
2099
2119
  // src/lib/transfer-auth.ts
2100
2120
  var import_viem2 = require("viem");
2101
- var ERC20_META_ABI = [
2102
- { type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
2103
- { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
2104
- ];
2105
- function buildTransferWithAuthorizationTypedData(input) {
2106
- return {
2107
- domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
2108
- types: {
2109
- TransferWithAuthorization: [
2110
- { name: "from", type: "address" },
2111
- { name: "to", type: "address" },
2112
- { name: "value", type: "uint256" },
2113
- { name: "validAfter", type: "uint256" },
2114
- { name: "validBefore", type: "uint256" },
2115
- { name: "nonce", type: "bytes32" }
2116
- ]
2117
- },
2118
- primaryType: "TransferWithAuthorization",
2119
- message: input.message
2120
- };
2121
- }
2122
- async function readTokenMeta(publicClient, token) {
2123
- const [tokenName, tokenVersion] = await Promise.all([
2124
- publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
2125
- publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
2126
- ]);
2127
- return { tokenName, tokenVersion };
2128
- }
2129
- function randomAuthNonce() {
2130
- const bytes = new Uint8Array(32);
2131
- globalThis.crypto.getRandomValues(bytes);
2132
- return (0, import_viem2.bytesToHex)(bytes);
2133
- }
2134
2121
 
2135
2122
  // src/lib/sponsor-client.ts
2136
2123
  var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
@@ -2173,65 +2160,21 @@ async function postPaymasterIntent(input) {
2173
2160
  }
2174
2161
  return parsed.data;
2175
2162
  }
2176
- async function postSponsorTransferAuth(input) {
2163
+ async function getSponsorRelayerAddress(input) {
2177
2164
  const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2178
2165
  let res;
2179
2166
  try {
2180
- res = await fetch(`${base5}/api/v1/sponsor/erc20-transfer-auth`, {
2181
- method: "POST",
2182
- headers: {
2183
- "content-type": "application/json",
2184
- "x-owney-api-key": input.apiKey,
2185
- ...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
2186
- },
2187
- body: JSON.stringify(input.body)
2188
- });
2189
- } catch (networkError) {
2190
- throw new OwneyError(
2191
- "SPONSOR_REQUEST_FAILED",
2192
- `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2193
- { cause: String(networkError) }
2194
- );
2195
- }
2196
- const text = await res.text();
2197
- let parsed = null;
2198
- try {
2199
- parsed = JSON.parse(text);
2200
- } catch {
2201
- }
2202
- if (!res.ok || !parsed?.success || !parsed.data) {
2203
- throw new OwneyError(
2204
- "SPONSOR_REQUEST_FAILED",
2205
- `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2167
+ res = await fetch(
2168
+ `${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
2206
2169
  {
2207
- statusCode: res.status,
2208
- responseBody: text.slice(0, 500),
2209
- // 503 SPONSOR_UNAVAILABLE = relayer out of gas; the tx was rejected
2210
- // before broadcast, so it is safe to fall back to a user-paid deposit.
2211
- safeToFallback: res.status === 503
2170
+ headers: { "x-owney-api-key": input.apiKey }
2212
2171
  }
2213
2172
  );
2214
- }
2215
- return parsed.data;
2216
- }
2217
- async function postSponsorPermit2Transfer(input) {
2218
- const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2219
- let res;
2220
- try {
2221
- res = await fetch(`${base5}/api/v1/sponsor/permit2-transfer`, {
2222
- method: "POST",
2223
- headers: {
2224
- "content-type": "application/json",
2225
- "x-owney-api-key": input.apiKey,
2226
- ...input.yieldseekerSignature ? { Authorization: `Signature ${input.yieldseekerSignature}` } : {}
2227
- },
2228
- body: JSON.stringify(input.body)
2229
- });
2230
2173
  } catch (networkError) {
2231
2174
  throw new OwneyError(
2232
2175
  "SPONSOR_REQUEST_FAILED",
2233
2176
  `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2234
- { cause: String(networkError), safeToFallback: false }
2177
+ { cause: String(networkError), safeToFallback: true }
2235
2178
  );
2236
2179
  }
2237
2180
  const text = await res.text();
@@ -2240,54 +2183,53 @@ async function postSponsorPermit2Transfer(input) {
2240
2183
  parsed = JSON.parse(text);
2241
2184
  } catch {
2242
2185
  }
2243
- if (!res.ok || !parsed?.success || !parsed.data) {
2186
+ if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
2244
2187
  throw new OwneyError(
2245
2188
  "SPONSOR_REQUEST_FAILED",
2246
2189
  `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2247
2190
  {
2248
2191
  statusCode: res.status,
2249
2192
  responseBody: text.slice(0, 500),
2250
- safeToFallback: res.status >= 400 && res.status < 500 || res.status === 503
2193
+ safeToFallback: true
2251
2194
  }
2252
2195
  );
2253
2196
  }
2254
- return parsed.data;
2197
+ return parsed.data.relayer;
2255
2198
  }
2256
- async function getSponsorRelayerAddress(input) {
2257
- const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
2199
+ async function postSponsorBatchTransfer(input) {
2258
2200
  let res;
2259
2201
  try {
2260
2202
  res = await fetch(
2261
- `${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
2203
+ `${input.baseUrl ?? ROUTING_API_BASE_URL}/api/v1/sponsor/permit2-batch`,
2262
2204
  {
2263
- headers: { "x-owney-api-key": input.apiKey }
2205
+ method: "POST",
2206
+ headers: {
2207
+ "content-type": "application/json",
2208
+ "x-owney-api-key": input.apiKey
2209
+ },
2210
+ body: JSON.stringify(input.body)
2264
2211
  }
2265
2212
  );
2266
- } catch (networkError) {
2213
+ } catch {
2267
2214
  throw new OwneyError(
2268
2215
  "SPONSOR_REQUEST_FAILED",
2269
- `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
2270
- { cause: String(networkError), safeToFallback: true }
2216
+ "Deposit status is unknown. Retry the same amount to check it.",
2217
+ { safeToFallback: false }
2271
2218
  );
2272
2219
  }
2273
- const text = await res.text();
2274
- let parsed = null;
2275
- try {
2276
- parsed = JSON.parse(text);
2277
- } catch {
2278
- }
2279
- if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
2220
+ const parsed = await res.json().catch(() => null);
2221
+ if (!res.ok || !parsed?.success || !/^0x[0-9a-fA-F]{64}$/.test(parsed.data?.txHash ?? "")) {
2280
2222
  throw new OwneyError(
2281
2223
  "SPONSOR_REQUEST_FAILED",
2282
- `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
2224
+ "Deposit could not be confirmed. Retry the same amount to check its status.",
2283
2225
  {
2284
2226
  statusCode: res.status,
2285
- responseBody: text.slice(0, 500),
2286
- safeToFallback: true
2227
+ safeToFallback: false,
2228
+ notSubmitted: parsed?.notSubmitted === true || parsed?.error?.notSubmitted === true || parsed?.error?.details?.notSubmitted === true
2287
2229
  }
2288
2230
  );
2289
2231
  }
2290
- return parsed.data.relayer;
2232
+ return parsed.data;
2291
2233
  }
2292
2234
 
2293
2235
  // src/lib/permit2.ts
@@ -2323,29 +2265,6 @@ var ERC20_ALLOWANCE_ABI = [
2323
2265
  outputs: [{ name: "", type: "uint256" }]
2324
2266
  }
2325
2267
  ];
2326
- function buildPermitTransferFromTypedData(input) {
2327
- return {
2328
- domain: {
2329
- name: "Permit2",
2330
- chainId: input.chainId,
2331
- verifyingContract: PERMIT2_ADDRESS
2332
- },
2333
- types: {
2334
- PermitTransferFrom: [
2335
- { name: "permitted", type: "TokenPermissions" },
2336
- { name: "spender", type: "address" },
2337
- { name: "nonce", type: "uint256" },
2338
- { name: "deadline", type: "uint256" }
2339
- ],
2340
- TokenPermissions: [
2341
- { name: "token", type: "address" },
2342
- { name: "amount", type: "uint256" }
2343
- ]
2344
- },
2345
- primaryType: "PermitTransferFrom",
2346
- message: input.message
2347
- };
2348
- }
2349
2268
  function randomPermit2Nonce() {
2350
2269
  const bytes = new Uint8Array(32);
2351
2270
  globalThis.crypto.getRandomValues(bytes);
@@ -2388,88 +2307,6 @@ function makeVerificationAwareDepositCallback(implementation) {
2388
2307
  });
2389
2308
  return callback;
2390
2309
  }
2391
- function makeSponsoredDepositCallback(deps) {
2392
- const post = deps.httpPost ?? postSponsorTransferAuth;
2393
- return makeVerificationAwareDepositCallback(
2394
- async (smartWallet, chainId, amount, verification) => {
2395
- const cid = chainId;
2396
- const token = deps.tokenAddressByChain[cid];
2397
- if (!token) {
2398
- throw new OwneyError(
2399
- "CHAIN_UNSUPPORTED",
2400
- `No sponsored token configured for chain ${chainId}`
2401
- );
2402
- }
2403
- const pub = deps.getPublicClient(cid);
2404
- const wallet = deps.getWalletClient(cid);
2405
- await ensureWalletOnChain(pub, wallet, cid);
2406
- try {
2407
- const balance = await readErc20Balance(pub, token, deps.ownerAddress);
2408
- if (balance < BigInt(amount)) {
2409
- throw new OwneyError(
2410
- "DEPOSIT_INSUFFICIENT_BALANCE",
2411
- "Insufficient balance for this deposit.",
2412
- { token, chainId: cid, balance: balance.toString(), amount }
2413
- );
2414
- }
2415
- } catch (err) {
2416
- if (err instanceof OwneyError) throw err;
2417
- console.warn(
2418
- "[owney-sdk] Deposit balance pre-check failed (non-fatal):",
2419
- err instanceof Error ? err.message : String(err)
2420
- );
2421
- }
2422
- const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
2423
- const validAfter = 0n;
2424
- const validBefore = BigInt(
2425
- Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
2426
- );
2427
- const nonce = randomAuthNonce();
2428
- const typedData = buildTransferWithAuthorizationTypedData({
2429
- token,
2430
- chainId: cid,
2431
- tokenName,
2432
- tokenVersion,
2433
- message: {
2434
- from: deps.ownerAddress,
2435
- to: smartWallet,
2436
- value: BigInt(amount),
2437
- validAfter,
2438
- validBefore,
2439
- nonce
2440
- }
2441
- });
2442
- const authSignature = await wallet.signTypedData({
2443
- account: deps.ownerAddress,
2444
- ...typedData
2445
- });
2446
- deps.onApproved?.();
2447
- const result = await post({
2448
- baseUrl: deps.baseUrl,
2449
- apiKey: deps.apiKey,
2450
- ...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
2451
- body: {
2452
- chainId: cid,
2453
- token,
2454
- from: deps.ownerAddress,
2455
- to: smartWallet,
2456
- value: amount,
2457
- validAfter: validAfter.toString(),
2458
- validBefore: validBefore.toString(),
2459
- nonce,
2460
- authSignature,
2461
- tokenName,
2462
- tokenVersion,
2463
- ...verification?.agentId === "yieldseeker" ? {
2464
- yieldseekerUserId: verification.userId,
2465
- yieldseekerAgentId: verification.yieldseekerAgentId
2466
- } : {}
2467
- }
2468
- });
2469
- return result.txHash;
2470
- }
2471
- );
2472
- }
2473
2310
 
2474
2311
  // src/agents/yieldseeker/yieldseeker.auth.ts
2475
2312
  var import_siwe = require("siwe");
@@ -4325,105 +4162,305 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
4325
4162
  }
4326
4163
 
4327
4164
  // src/client.ts
4328
- var import_viem9 = require("viem");
4165
+ var import_viem11 = require("viem");
4329
4166
  var import_chains4 = require("viem/chains");
4330
4167
 
4331
- // src/lib/sponsored-weth-deposit.ts
4332
- var PERMIT_WINDOW_SECONDS = 15 * 60;
4333
- function makeSponsoredWethCallback(deps) {
4334
- const get = deps.httpGet ?? getSponsorRelayerAddress;
4335
- const post = deps.httpPost ?? postSponsorPermit2Transfer;
4336
- return makeVerificationAwareDepositCallback(
4337
- async (smartWallet, chainId, amount, verification) => {
4338
- const cid = chainId;
4339
- const token = deps.tokenAddressByChain[cid];
4340
- if (!token) {
4341
- throw new OwneyError(
4342
- "CHAIN_UNSUPPORTED",
4343
- `No sponsored WETH configured for chain ${chainId}`
4344
- );
4345
- }
4346
- const amountWei = BigInt(amount);
4347
- const pub = deps.getPublicClient(cid);
4348
- const wallet = deps.getWalletClient(cid);
4349
- await ensureWalletOnChain(pub, wallet, cid);
4350
- try {
4351
- const balance = await readErc20Balance(pub, token, deps.ownerAddress);
4352
- if (balance < amountWei) {
4353
- throw new OwneyError(
4354
- "DEPOSIT_INSUFFICIENT_BALANCE",
4355
- "Insufficient WETH balance for this deposit.",
4356
- { token, chainId: cid, balance: balance.toString(), amount }
4357
- );
4358
- }
4359
- } catch (err) {
4360
- if (err instanceof OwneyError) throw err;
4361
- console.warn(
4362
- "[owney-sdk] WETH balance pre-check failed (non-fatal):",
4363
- err instanceof Error ? err.message : String(err)
4364
- );
4365
- }
4366
- const allowance = await readPermit2Allowance(
4367
- pub,
4368
- token,
4369
- deps.ownerAddress
4168
+ // src/lib/sponsored-token-batch.ts
4169
+ var import_viem9 = require("viem");
4170
+
4171
+ // src/lib/permit2-batch.ts
4172
+ var import_viem8 = require("viem");
4173
+ var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
4174
+ var PERMIT_BATCH_TYPES = {
4175
+ PermitBatchWitnessTransferFrom: [
4176
+ { name: "permitted", type: "TokenPermissions[]" },
4177
+ { name: "spender", type: "address" },
4178
+ { name: "nonce", type: "uint256" },
4179
+ { name: "deadline", type: "uint256" },
4180
+ { name: "witness", type: "Deposit" }
4181
+ ],
4182
+ Deposit: [{ name: "recipients", type: "address[]" }],
4183
+ TokenPermissions: [
4184
+ { name: "token", type: "address" },
4185
+ { name: "amount", type: "uint256" }
4186
+ ]
4187
+ };
4188
+ var PERMIT2_BATCH_ABI = (0, import_viem8.parseAbi)([
4189
+ "struct TokenPermissions { address token; uint256 amount; }",
4190
+ "struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
4191
+ "struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
4192
+ "function permitWitnessTransferFrom(PermitBatchTransferFrom permit,SignatureTransferDetails[] transferDetails,address owner,bytes32 witness,string witnessTypeString,bytes signature)",
4193
+ "function nonceBitmap(address owner,uint256 wordPos) view returns (uint256)"
4194
+ ]);
4195
+ function batchPermit(b) {
4196
+ return {
4197
+ permitted: b.transfers.map((t) => ({
4198
+ token: b.token,
4199
+ amount: BigInt(t.amount)
4200
+ })),
4201
+ nonce: BigInt(b.nonce),
4202
+ deadline: BigInt(b.deadline)
4203
+ };
4204
+ }
4205
+ function batchTypedData(b, spender) {
4206
+ return {
4207
+ domain: {
4208
+ name: "Permit2",
4209
+ chainId: b.chainId,
4210
+ verifyingContract: BATCH_PERMIT2_ADDRESS
4211
+ },
4212
+ types: PERMIT_BATCH_TYPES,
4213
+ primaryType: "PermitBatchWitnessTransferFrom",
4214
+ message: {
4215
+ ...batchPermit(b),
4216
+ spender,
4217
+ witness: { recipients: b.transfers.map((t) => t.to) }
4218
+ }
4219
+ };
4220
+ }
4221
+
4222
+ // src/lib/sponsored-token-batch.ts
4223
+ var memory = /* @__PURE__ */ new Map();
4224
+ var inflight = /* @__PURE__ */ new Map();
4225
+ var planOf = (transfers) => JSON.stringify(
4226
+ transfers.map((t) => ({
4227
+ to: t.to.toLowerCase(),
4228
+ amount: BigInt(t.amount).toString()
4229
+ }))
4230
+ );
4231
+ function read(key2) {
4232
+ return typeof window === "undefined" ? memory.get(key2) : window.localStorage.getItem(key2);
4233
+ }
4234
+ function save(key2, body) {
4235
+ const value = JSON.stringify({
4236
+ ...body,
4237
+ transfers: body.transfers.map(({ to, amount }) => ({ to, amount }))
4238
+ });
4239
+ if (typeof window === "undefined") memory.set(key2, value);
4240
+ else window.localStorage.setItem(key2, value);
4241
+ }
4242
+ function clear(key2) {
4243
+ if (typeof window === "undefined") memory.delete(key2);
4244
+ else window.localStorage.removeItem(key2);
4245
+ }
4246
+ function sponsorTokenBatch(i) {
4247
+ 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()}`;
4248
+ const plan = planOf(i.transfers);
4249
+ const active = inflight.get(key2);
4250
+ if (active) {
4251
+ if (active.plan !== plan)
4252
+ return Promise.reject(
4253
+ new Error(
4254
+ "A token deposit is already in progress. Wait for its result before depositing again."
4255
+ )
4370
4256
  );
4371
- if (allowance < amountWei) {
4372
- throw new OwneyError(
4373
- "PERMIT2_APPROVAL_REQUIRED",
4374
- "WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
4375
- { token, chainId: cid, allowance: allowance.toString(), amount }
4376
- );
4257
+ return active.promise;
4258
+ }
4259
+ const promise = execute(i, key2, plan).finally(() => inflight.delete(key2));
4260
+ inflight.set(key2, { plan, promise });
4261
+ return promise;
4262
+ }
4263
+ async function execute(i, key2, plan) {
4264
+ if (!i.transfers.length || i.transfers.length > 16 || i.transfers.some(
4265
+ (t) => BigInt(t.amount) <= 0n || BigInt(t.amount) >= 1n << 256n
4266
+ ) || new Set(i.transfers.map((t) => t.to.toLowerCase())).size !== i.transfers.length)
4267
+ throw new Error("Invalid token deposit shares.");
4268
+ const send = async (initial) => {
4269
+ let body = initial;
4270
+ save(key2, body);
4271
+ try {
4272
+ if (!body.serializedTransaction) {
4273
+ const prepared = await postSponsorBatchTransfer({
4274
+ apiKey: i.apiKey,
4275
+ baseUrl: i.baseUrl,
4276
+ body
4277
+ });
4278
+ if (!prepared.serializedTransaction || (0, import_viem9.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
4279
+ throw new Error(
4280
+ "Sponsorship API did not return a valid prepared transaction."
4281
+ );
4282
+ body = {
4283
+ ...body,
4284
+ serializedTransaction: prepared.serializedTransaction
4285
+ };
4286
+ save(key2, body);
4377
4287
  }
4378
- const relayer = await get({
4379
- baseUrl: deps.baseUrl,
4380
- apiKey: deps.apiKey,
4381
- chainId: cid
4382
- });
4383
- const nonce = randomPermit2Nonce();
4384
- const deadline = BigInt(
4385
- Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
4386
- );
4387
- const typedData = buildPermitTransferFromTypedData({
4388
- chainId: cid,
4389
- message: {
4390
- permitted: { token, amount: amountWei },
4391
- spender: relayer,
4392
- nonce,
4393
- deadline
4394
- }
4395
- });
4396
- const signature = await wallet.signTypedData({
4397
- account: deps.ownerAddress,
4398
- ...typedData
4399
- });
4400
- deps.onApproved?.();
4401
- const result = await post({
4402
- baseUrl: deps.baseUrl,
4403
- apiKey: deps.apiKey,
4404
- ...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
4405
- body: {
4406
- chainId: cid,
4407
- token,
4408
- from: deps.ownerAddress,
4409
- to: smartWallet,
4410
- amount,
4411
- nonce: nonce.toString(),
4412
- deadline: deadline.toString(),
4413
- signature,
4414
- ...verification?.agentId === "yieldseeker" ? {
4415
- yieldseekerUserId: verification.userId,
4416
- yieldseekerAgentId: verification.yieldseekerAgentId
4417
- } : {}
4418
- }
4288
+ const result = await postSponsorBatchTransfer({
4289
+ apiKey: i.apiKey,
4290
+ baseUrl: i.baseUrl,
4291
+ body
4419
4292
  });
4293
+ if (result.txHash !== (0, import_viem9.keccak256)(body.serializedTransaction))
4294
+ throw new Error(
4295
+ "Sponsorship receipt does not match the pending transaction."
4296
+ );
4297
+ clear(key2);
4420
4298
  return result.txHash;
4299
+ } catch (error) {
4300
+ if (!body.serializedTransaction || error instanceof OwneyError && error.details?.notSubmitted === true)
4301
+ clear(key2);
4302
+ throw error;
4421
4303
  }
4304
+ };
4305
+ const saved = read(key2);
4306
+ if (saved) {
4307
+ const previous = JSON.parse(saved);
4308
+ 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)
4309
+ throw new Error(
4310
+ "Retry the previous token deposit and agent split first to reconcile its status."
4311
+ );
4312
+ i.onApproved?.();
4313
+ return send({ ...previous, transfers: i.transfers });
4314
+ }
4315
+ const total = i.transfers.reduce((sum, t) => sum + BigInt(t.amount), 0n);
4316
+ const [balance, allowance] = await Promise.all([
4317
+ readErc20Balance(i.pub, i.token, i.owner),
4318
+ readPermit2Allowance(i.pub, i.token, i.owner)
4319
+ ]);
4320
+ if (balance < total)
4321
+ throw new OwneyError(
4322
+ "DEPOSIT_INSUFFICIENT_BALANCE",
4323
+ "Insufficient token balance for this deposit."
4324
+ );
4325
+ if (allowance < total)
4326
+ throw new OwneyError(
4327
+ "PERMIT2_APPROVAL_REQUIRED",
4328
+ "token deposits need a one-time Permit2 approval."
4329
+ );
4330
+ const relayer = await getSponsorRelayerAddress({
4331
+ apiKey: i.apiKey,
4332
+ baseUrl: i.baseUrl,
4333
+ chainId: i.chainId
4334
+ });
4335
+ const now = (await i.pub.getBlock()).timestamp;
4336
+ const unsigned = {
4337
+ chainId: i.chainId,
4338
+ token: i.token,
4339
+ from: i.owner,
4340
+ transfers: i.transfers,
4341
+ nonce: randomPermit2Nonce().toString(),
4342
+ deadline: (now + 900n).toString()
4343
+ };
4344
+ const signature = await i.wallet.signTypedData({
4345
+ account: i.owner,
4346
+ ...batchTypedData(unsigned, relayer)
4347
+ });
4348
+ i.onApproved?.();
4349
+ return send({ ...unsigned, signature });
4350
+ }
4351
+
4352
+ // src/lib/sponsored-token-deposit.ts
4353
+ function makeSponsoredTokenCallback(deps) {
4354
+ const batch = async (chainId, transfers) => {
4355
+ if (chainId !== 8453 && chainId !== 42161 && chainId !== 1)
4356
+ throw new OwneyError(
4357
+ "CHAIN_UNSUPPORTED",
4358
+ `No sponsored token configured for chain ${chainId}`
4359
+ );
4360
+ const token = deps.tokenAddressByChain[chainId];
4361
+ if (!token)
4362
+ throw new OwneyError(
4363
+ "CHAIN_UNSUPPORTED",
4364
+ `No sponsored token configured for chain ${chainId}`
4365
+ );
4366
+ const pub = deps.getPublicClient(chainId), wallet = deps.getWalletClient(chainId);
4367
+ await ensureWalletOnChain(pub, wallet, chainId);
4368
+ return sponsorTokenBatch({
4369
+ apiKey: deps.apiKey,
4370
+ baseUrl: deps.baseUrl,
4371
+ owner: deps.ownerAddress,
4372
+ token,
4373
+ chainId,
4374
+ transfers,
4375
+ pub,
4376
+ wallet,
4377
+ onApproved: deps.onApproved
4378
+ });
4379
+ };
4380
+ const callback = makeVerificationAwareDepositCallback(
4381
+ (to, chainId, amount, verification) => batch(chainId, [toBatchTransfer(to, amount, verification)])
4422
4382
  );
4383
+ registerDepositBatch(callback, batch);
4384
+ return callback;
4385
+ }
4386
+
4387
+ // src/lib/agent-deposit-batch.ts
4388
+ function deferred() {
4389
+ let resolve, reject;
4390
+ const promise = new Promise((yes, no) => {
4391
+ resolve = yes;
4392
+ reject = no;
4393
+ });
4394
+ void promise.catch(() => {
4395
+ });
4396
+ return { promise, resolve, reject };
4397
+ }
4398
+ async function runAgentDepositBatch(chainId, legs, transfer) {
4399
+ const funding = deferred();
4400
+ const tasks = [];
4401
+ const transfers = [];
4402
+ try {
4403
+ for (const leg of legs) {
4404
+ const ready = deferred();
4405
+ let entered = false;
4406
+ const callback = makeVerificationAwareDepositCallback(
4407
+ (to, cid, amount, verification) => {
4408
+ if (entered || cid !== chainId || BigInt(amount) !== BigInt(leg.amount)) {
4409
+ const error = new Error(
4410
+ "Agent changed its prepared deposit share."
4411
+ );
4412
+ ready.reject(error);
4413
+ throw error;
4414
+ }
4415
+ entered = true;
4416
+ ready.resolve(toBatchTransfer(to, amount, verification));
4417
+ return funding.promise;
4418
+ }
4419
+ );
4420
+ const task = Promise.resolve().then(() => leg.run(callback));
4421
+ tasks.push(task);
4422
+ void task.then(
4423
+ () => {
4424
+ if (!entered)
4425
+ ready.reject(
4426
+ new Error("Agent did not prepare a deposit transfer.")
4427
+ );
4428
+ },
4429
+ (error) => ready.reject(error)
4430
+ );
4431
+ transfers.push(await ready.promise);
4432
+ }
4433
+ const txHash = await transfer(chainId, transfers);
4434
+ funding.resolve(txHash);
4435
+ const settled = await Promise.allSettled(tasks);
4436
+ const agentResults = {};
4437
+ const failures = [];
4438
+ for (const [index, result] of settled.entries()) {
4439
+ if (result.status === "fulfilled")
4440
+ agentResults[legs[index].id] = result.value;
4441
+ else failures.push(legs[index].id);
4442
+ }
4443
+ if (failures.length)
4444
+ throw new OwneyError(
4445
+ "DEPOSIT_PARTIAL_FAILURE",
4446
+ "The deposit was sent to all agents, but some agent updates could not be confirmed. Check activity before depositing again.",
4447
+ {
4448
+ txHash,
4449
+ fundsSubmitted: true,
4450
+ agentResults,
4451
+ failedAgentIds: failures
4452
+ }
4453
+ );
4454
+ return { agentResults };
4455
+ } catch (error) {
4456
+ funding.reject(error);
4457
+ await Promise.allSettled(tasks);
4458
+ throw error;
4459
+ }
4423
4460
  }
4424
4461
 
4425
4462
  // src/lib/sponsored-calls-deposit.ts
4426
- var import_viem8 = require("viem");
4463
+ var import_viem10 = require("viem");
4427
4464
  var DEFAULT_POLL_INTERVAL_MS = 1500;
4428
4465
  var DEFAULT_MAX_POLLS = 30;
4429
4466
  async function paymasterSupported(provider, owner, chainId) {
@@ -4431,7 +4468,7 @@ async function paymasterSupported(provider, owner, chainId) {
4431
4468
  method: "wallet_getCapabilities",
4432
4469
  params: [owner]
4433
4470
  });
4434
- const forChain = caps?.[(0, import_viem8.toHex)(chainId)] ?? caps?.[String(chainId)];
4471
+ const forChain = caps?.[(0, import_viem10.toHex)(chainId)] ?? caps?.[String(chainId)];
4435
4472
  return Boolean(forChain?.paymasterService?.supported);
4436
4473
  }
4437
4474
  function makeSponsoredCallsCallback(deps) {
@@ -4449,7 +4486,7 @@ function makeSponsoredCallsCallback(deps) {
4449
4486
  }
4450
4487
  return new URL(configured, origin).toString();
4451
4488
  };
4452
- return makeVerificationAwareDepositCallback(async (smartWallet, chainId, amount, verification) => {
4489
+ const batch = async (chainId, transfers) => {
4453
4490
  const cid = chainId;
4454
4491
  const token = deps.tokenAddressByChain[cid];
4455
4492
  if (!token) {
@@ -4465,19 +4502,24 @@ function makeSponsoredCallsCallback(deps) {
4465
4502
  { chainId }
4466
4503
  );
4467
4504
  }
4468
- const data = (0, import_viem8.encodeFunctionData)({
4469
- abi: import_viem8.erc20Abi,
4470
- functionName: "transfer",
4471
- args: [smartWallet, BigInt(amount)]
4472
- });
4505
+ const calls = transfers.map((transfer) => ({
4506
+ to: token,
4507
+ value: "0x0",
4508
+ data: (0, import_viem10.encodeFunctionData)({
4509
+ abi: import_viem10.erc20Abi,
4510
+ functionName: "transfer",
4511
+ args: [transfer.to, BigInt(transfer.amount)]
4512
+ })
4513
+ }));
4473
4514
  let paymasterUrl = absolutePaymasterUrl();
4474
- if (verification?.agentId === "yieldseeker") {
4475
- if (chainId !== 8453) {
4515
+ for (const transfer of transfers) {
4516
+ const verification = transfer.yieldseeker;
4517
+ if (!verification) continue;
4518
+ if (chainId !== 8453)
4476
4519
  throw new OwneyError(
4477
4520
  "CHAIN_UNSUPPORTED",
4478
- `Yieldseeker Base Account sponsorship is not available on chain ${chainId}.`
4521
+ `Yieldseeker sponsorship is not available on chain ${chainId}.`
4479
4522
  );
4480
- }
4481
4523
  const { intent } = await postPaymasterIntent({
4482
4524
  baseUrl: deps.routingApiBaseUrl,
4483
4525
  apiKey: deps.apiKey,
@@ -4486,14 +4528,14 @@ function makeSponsoredCallsCallback(deps) {
4486
4528
  chainId,
4487
4529
  token,
4488
4530
  from: deps.ownerAddress,
4489
- to: smartWallet,
4490
- amount,
4531
+ to: transfer.to,
4532
+ amount: transfer.amount,
4491
4533
  yieldseekerUserId: verification.userId,
4492
- yieldseekerAgentId: verification.yieldseekerAgentId
4534
+ yieldseekerAgentId: verification.agentId
4493
4535
  }
4494
4536
  });
4495
4537
  const url = new URL(paymasterUrl);
4496
- url.searchParams.set("owneyIntent", intent);
4538
+ url.searchParams.append("owneyIntent", intent);
4497
4539
  paymasterUrl = url.toString();
4498
4540
  }
4499
4541
  const sendResult = await deps.provider.request({
@@ -4502,9 +4544,9 @@ function makeSponsoredCallsCallback(deps) {
4502
4544
  {
4503
4545
  version: "2.0.0",
4504
4546
  from: deps.ownerAddress,
4505
- chainId: (0, import_viem8.toHex)(chainId),
4506
- atomicRequired: false,
4507
- calls: [{ to: token, value: "0x0", data }],
4547
+ chainId: (0, import_viem10.toHex)(chainId),
4548
+ atomicRequired: transfers.length > 1,
4549
+ calls,
4508
4550
  capabilities: {
4509
4551
  paymasterService: { url: paymasterUrl }
4510
4552
  }
@@ -4526,7 +4568,24 @@ function makeSponsoredCallsCallback(deps) {
4526
4568
  params: [callsId]
4527
4569
  });
4528
4570
  const txHash = status?.receipts?.[0]?.transactionHash;
4529
- if (txHash) return txHash;
4571
+ if (status?.receipts?.some((receipt) => receipt.status === "0x0") || typeof status?.status === "number" && status.status >= 400) {
4572
+ throw new OwneyError(
4573
+ "SPONSOR_REQUEST_FAILED",
4574
+ "The sponsored deposit did not complete successfully.",
4575
+ { chainId, callsId, safeToFallback: false }
4576
+ );
4577
+ }
4578
+ if (txHash && (transfers.length === 1 || status?.status === 200 || status?.status === "CONFIRMED")) {
4579
+ if (status?.receipts?.some(
4580
+ (receipt) => receipt.transactionHash !== txHash
4581
+ ))
4582
+ throw new OwneyError(
4583
+ "SPONSORED_CALLS_NO_RECEIPT",
4584
+ "The wallet returned multiple transactions for an atomic deposit. Check activity before trying again.",
4585
+ { chainId, callsId }
4586
+ );
4587
+ return txHash;
4588
+ }
4530
4589
  if (pollIntervalMs > 0) {
4531
4590
  await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
4532
4591
  }
@@ -4536,7 +4595,12 @@ function makeSponsoredCallsCallback(deps) {
4536
4595
  `No receipt for calls ${callsId} after ${maxPolls} polls; the deposit may still settle.`,
4537
4596
  { chainId, callsId }
4538
4597
  );
4539
- });
4598
+ };
4599
+ const callback = makeVerificationAwareDepositCallback(
4600
+ (to, chainId, amount, verification) => batch(chainId, [toBatchTransfer(to, amount, verification)])
4601
+ );
4602
+ registerDepositBatch(callback, batch);
4603
+ return callback;
4540
4604
  }
4541
4605
 
4542
4606
  // src/client.ts
@@ -4575,6 +4639,11 @@ var SPONSORED_WETH_BY_CHAIN = {
4575
4639
  42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
4576
4640
  1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
4577
4641
  };
4642
+ var SPONSORED_TOKENS_BY_ASSET = { USDC: SPONSORED_USDC_BY_CHAIN, WETH: SPONSORED_WETH_BY_CHAIN };
4643
+ function sponsoredTokensFor(asset) {
4644
+ if (!Object.prototype.hasOwnProperty.call(SPONSORED_TOKENS_BY_ASSET, asset)) throw new OwneyError("ASSET_UNSUPPORTED", `No sponsored token configured for ${asset}`);
4645
+ return SPONSORED_TOKENS_BY_ASSET[asset];
4646
+ }
4578
4647
  function shouldFallbackToUserPaid(error, asset, appCallback) {
4579
4648
  return (asset === "WETH" || asset === "USDC") && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
4580
4649
  }
@@ -4709,18 +4778,13 @@ var OwneySDK = class {
4709
4778
  }
4710
4779
  return this.state.provider;
4711
4780
  }
4712
- /**
4713
- * Lazily builds (and caches) the default EIP-3009 sponsored deposit callback
4714
- * used when the caller omits `depositCallback`. Wraps the connected EIP-1193
4715
- * provider with viem `custom(provider)` to read token meta and sign the
4716
- * `TransferWithAuthorization`, then POSTs to the sponsor API.
4717
- */
4781
+ /** Builds the default USDC batch callback for the connected wallet. */
4718
4782
  getDefaultSponsoredCallback(onApproved) {
4719
4783
  if (!onApproved && this.cachedSponsoredCallback)
4720
4784
  return this.cachedSponsoredCallback;
4721
4785
  const provider = this.requireConnectedProvider();
4722
4786
  const owner = this.state.walletAddress;
4723
- const callback = makeSponsoredDepositCallback({
4787
+ const callback = makeSponsoredTokenCallback({
4724
4788
  apiKey: this.apiKey,
4725
4789
  baseUrl: this.routingApiBaseUrl,
4726
4790
  ownerAddress: owner,
@@ -4729,25 +4793,20 @@ var OwneySDK = class {
4729
4793
  // Casts work around viem's chain-narrowed Client vs the generic
4730
4794
  // PublicClient/WalletClient param types — structurally identical at
4731
4795
  // runtime, but the two share a name TS treats as unrelated.
4732
- getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
4796
+ getPublicClient: (cid) => (0, import_viem11.createPublicClient)({
4733
4797
  chain: VIEM_CHAIN2[cid],
4734
- transport: (0, import_viem9.custom)(provider)
4798
+ transport: (0, import_viem11.custom)(provider)
4735
4799
  }),
4736
- getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
4800
+ getWalletClient: (cid) => (0, import_viem11.createWalletClient)({
4737
4801
  account: owner,
4738
4802
  chain: VIEM_CHAIN2[cid],
4739
- transport: (0, import_viem9.custom)(provider)
4803
+ transport: (0, import_viem11.custom)(provider)
4740
4804
  })
4741
4805
  });
4742
4806
  if (!onApproved) this.cachedSponsoredCallback = callback;
4743
4807
  return callback;
4744
4808
  }
4745
- /**
4746
- * Lazily builds (and caches) the default Permit2 sponsored WETH deposit
4747
- * callback used when the caller omits `depositCallback` for a WETH
4748
- * deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
4749
- * `PermitTransferFrom` instead of an EIP-3009 authorization.
4750
- */
4809
+ /** Builds the wallet-native sponsored calls callback for compatible paymasters. */
4751
4810
  getDefaultSponsoredCallsCallback(asset, onApproved) {
4752
4811
  const cached = this.cachedSponsoredCallsCallbacks.get(asset);
4753
4812
  if (!onApproved && cached) return cached;
@@ -4759,7 +4818,7 @@ var OwneySDK = class {
4759
4818
  ownerAddress: this.state.walletAddress,
4760
4819
  paymasterServiceUrl: this.paymasterServiceUrl,
4761
4820
  onApproved,
4762
- tokenAddressByChain: asset === "WETH" ? SPONSORED_WETH_BY_CHAIN : SPONSORED_USDC_BY_CHAIN
4821
+ tokenAddressByChain: sponsoredTokensFor(asset)
4763
4822
  });
4764
4823
  if (!onApproved) this.cachedSponsoredCallsCallbacks.set(asset, callback);
4765
4824
  return callback;
@@ -4768,14 +4827,14 @@ var OwneySDK = class {
4768
4827
  * Lazily builds (and caches) the default Permit2 sponsored WETH deposit
4769
4828
  * callback used when the caller omits `depositCallback` for a WETH deposit.
4770
4829
  * Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
4771
- * `PermitTransferFrom` instead of an EIP-3009 authorization.
4830
+ * single-use batch authorization instead of an EIP-3009 authorization.
4772
4831
  */
4773
4832
  getDefaultWethSponsoredCallback(onApproved) {
4774
4833
  if (!onApproved && this.cachedWethSponsoredCallback)
4775
4834
  return this.cachedWethSponsoredCallback;
4776
4835
  const provider = this.requireConnectedProvider();
4777
4836
  const owner = this.state.walletAddress;
4778
- const callback = makeSponsoredWethCallback({
4837
+ const callback = makeSponsoredTokenCallback({
4779
4838
  apiKey: this.apiKey,
4780
4839
  baseUrl: this.routingApiBaseUrl,
4781
4840
  ownerAddress: owner,
@@ -4784,14 +4843,14 @@ var OwneySDK = class {
4784
4843
  // Casts work around viem's chain-narrowed Client vs the generic
4785
4844
  // PublicClient/WalletClient param types — structurally identical at
4786
4845
  // runtime, but the two share a name TS treats as unrelated.
4787
- getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
4846
+ getPublicClient: (cid) => (0, import_viem11.createPublicClient)({
4788
4847
  chain: VIEM_CHAIN2[cid],
4789
- transport: (0, import_viem9.custom)(provider)
4848
+ transport: (0, import_viem11.custom)(provider)
4790
4849
  }),
4791
- getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
4850
+ getWalletClient: (cid) => (0, import_viem11.createWalletClient)({
4792
4851
  account: owner,
4793
4852
  chain: VIEM_CHAIN2[cid],
4794
- transport: (0, import_viem9.custom)(provider)
4853
+ transport: (0, import_viem11.custom)(provider)
4795
4854
  })
4796
4855
  });
4797
4856
  if (!onApproved) this.cachedWethSponsoredCallback = callback;
@@ -5027,10 +5086,9 @@ var OwneySDK = class {
5027
5086
  * Serializing costs no real wall-clock: the user can only approve one prompt
5028
5087
  * at a time anyway.
5029
5088
  *
5030
- * Every agent is attempted even if an earlier one fails, so one declined
5031
- * signature can't deny the remaining agents their turn. Once all agents have
5032
- * had a chance, a partial failure identifies the agents that still need a
5033
- * retry; if none activated, the original provider error is preserved.
5089
+ * Stop at the first failure so a canceled sign-in does not open another
5090
+ * agent's wallet prompt. Report any earlier successes for diagnostics; the
5091
+ * app discards the session when the complete sign-in does not succeed.
5034
5092
  */
5035
5093
  async activateAgentsInTurn(agents, state, chainId, asset) {
5036
5094
  let firstError = null;
@@ -5058,6 +5116,7 @@ var OwneySDK = class {
5058
5116
  } else {
5059
5117
  console.error(`activateAgent(${agent.id}) failed:`, error);
5060
5118
  }
5119
+ break;
5061
5120
  }
5062
5121
  }
5063
5122
  if (firstError === null) return;
@@ -5081,7 +5140,8 @@ var OwneySDK = class {
5081
5140
  * @param options.asset - Asset symbol to deposit (e.g. "USDC")
5082
5141
  * @param options.depositCallback - Callback that performs the token transfer and returns a tx hash.
5083
5142
  * When agentId is omitted, this callback is invoked once per eligible agent with that agent's
5084
- * split amount and smart wallet address — expect multiple wallet prompts.
5143
+ * split amount and smart wallet address. Default sponsored deposits batch
5144
+ * all shares into one signature; custom callbacks still run once per agent.
5085
5145
  * @param options.agentId - Optional explicit target. Otherwise split equally,
5086
5146
  * or fund remaining agents when a recovery deposit cannot meet every minimum.
5087
5147
  * @returns {OwneyDepositResult} for a single agent, or {OwneyMultiDepositResult} with per-agent results
@@ -5166,6 +5226,32 @@ var OwneySDK = class {
5166
5226
  }
5167
5227
  );
5168
5228
  }
5229
+ const batchTransfer = getDepositBatchTransfer(effectiveCallback);
5230
+ if (!depositCallback && batchTransfer) {
5231
+ return runAgentDepositBatch(
5232
+ chainId,
5233
+ agentAmounts.map(({ agent, amount: amount2 }) => ({
5234
+ id: agent.id,
5235
+ amount: amount2,
5236
+ run: (callback) => withFailureReporting(
5237
+ this.apiKey,
5238
+ agent.id,
5239
+ () => agent.deposit(state, chainId, amount2, asset, callback),
5240
+ this.routingApiBaseUrl
5241
+ )
5242
+ })),
5243
+ async (cid, transfers) => {
5244
+ try {
5245
+ return await batchTransfer(cid, transfers);
5246
+ } catch (error) {
5247
+ if (!(error instanceof OwneyError) || error.code !== "PERMIT2_APPROVAL_REQUIRED")
5248
+ throw error;
5249
+ await this.approvePermit2(asset);
5250
+ return batchTransfer(cid, transfers);
5251
+ }
5252
+ }
5253
+ );
5254
+ }
5169
5255
  const agentResults = {};
5170
5256
  for (const [
5171
5257
  index,
@@ -5205,7 +5291,7 @@ var OwneySDK = class {
5205
5291
  *
5206
5292
  * 1. Missing Permit2 allowance: when the app did not supply its own
5207
5293
  * callback and the attempt fails with `PERMIT2_APPROVAL_REQUIRED` on a
5208
- * WETH deposit, this is the wallet's first gasless WETH deposit. We send
5294
+ * token deposit, this is the wallet's first Permit2 deposit for that token. We send
5209
5295
  * the one-time (user-paid) Permit2 approval via `approvePermit2()` and
5210
5296
  * retry the SAME sponsored attempt once. Bounded to one approval attempt
5211
5297
  * per call so a wallet/agent that keeps reporting the allowance as
@@ -5242,12 +5328,12 @@ var OwneySDK = class {
5242
5328
  try {
5243
5329
  return await attempt(effectiveCallback);
5244
5330
  } catch (error) {
5245
- if (!approvalAttempted && appCallback === void 0 && asset === "WETH" && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
5331
+ if (!approvalAttempted && appCallback === void 0 && (asset === "WETH" || asset === "USDC") && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
5246
5332
  approvalAttempted = true;
5247
5333
  console.warn(
5248
- "[owney-sdk] First WETH deposit: sending one-time Permit2 approval..."
5334
+ "[owney-sdk] First token deposit: sending one-time Permit2 approval..."
5249
5335
  );
5250
- await this.approvePermit2();
5336
+ await this.approvePermit2(asset);
5251
5337
  continue;
5252
5338
  }
5253
5339
  if (!shouldFallbackToUserPaid(error, asset, appCallback)) throw error;
@@ -5900,30 +5986,29 @@ var OwneySDK = class {
5900
5986
  return pending;
5901
5987
  }
5902
5988
  /**
5903
- * One-time, user-paid approval of Permit2 on the sponsored WETH token for
5904
- * the active chain. Required once per wallet per chain before gasless WETH
5989
+ * One-time, user-paid approval of Permit2 on the selected token for
5990
+ * the active chain. Required once per wallet, token and chain before sponsored
5905
5991
  * deposits; afterwards deposit() is signature-only. Resolves only after the
5906
5992
  * approval transaction is mined (1 confirmation), so a subsequent deposit()
5907
5993
  * will see the new allowance; throws if the transaction reverted.
5908
5994
  * @returns the approval transaction hash.
5909
5995
  */
5910
5996
  async approvePermit2(asset = "WETH") {
5911
- void asset;
5912
5997
  const state = this.requireState();
5913
5998
  const chainId = this.requireChainId();
5914
- this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
5915
- const token = SPONSORED_WETH_BY_CHAIN[chainId];
5999
+ this.getEligibleAgents(chainId, asset, { excludeDisabled: true });
6000
+ const token = sponsoredTokensFor(asset)[chainId];
5916
6001
  if (!token) {
5917
6002
  throw new OwneyError(
5918
6003
  "CHAIN_UNSUPPORTED",
5919
- `No sponsored WETH on chain ${chainId}`
6004
+ `No sponsored token on chain ${chainId}`
5920
6005
  );
5921
6006
  }
5922
6007
  const provider = this.requireConnectedProvider();
5923
- const wallet = (0, import_viem9.createWalletClient)({
6008
+ const wallet = (0, import_viem11.createWalletClient)({
5924
6009
  account: state.walletAddress,
5925
6010
  chain: VIEM_CHAIN2[chainId],
5926
- transport: (0, import_viem9.custom)(provider)
6011
+ transport: (0, import_viem11.custom)(provider)
5927
6012
  });
5928
6013
  const hash = await wallet.writeContract({
5929
6014
  address: token,
@@ -5933,9 +6018,9 @@ var OwneySDK = class {
5933
6018
  account: state.walletAddress,
5934
6019
  chain: VIEM_CHAIN2[chainId]
5935
6020
  });
5936
- const publicClient = (0, import_viem9.createPublicClient)({
6021
+ const publicClient = (0, import_viem11.createPublicClient)({
5937
6022
  chain: VIEM_CHAIN2[chainId],
5938
- transport: (0, import_viem9.custom)(provider)
6023
+ transport: (0, import_viem11.custom)(provider)
5939
6024
  });
5940
6025
  const receipt = await publicClient.waitForTransactionReceipt({
5941
6026
  hash,
@@ -6049,7 +6134,7 @@ var OwneySDK = class {
6049
6134
  };
6050
6135
 
6051
6136
  // src/agents/zyfai/zyfai.siwx.ts
6052
- var import_viem10 = require("viem");
6137
+ var import_viem12 = require("viem");
6053
6138
  var import_siwe2 = require("siwe");
6054
6139
  var import_sdk2 = require("@zyfai/sdk");
6055
6140
 
@@ -6176,7 +6261,7 @@ function buildSIWXConfig(deps) {
6176
6261
  issuedAt,
6177
6262
  toString() {
6178
6263
  return new import_siwe2.SiweMessage({
6179
- address: (0, import_viem10.getAddress)(accountAddress),
6264
+ address: (0, import_viem12.getAddress)(accountAddress),
6180
6265
  chainId: numericChainId(chainId),
6181
6266
  domain,
6182
6267
  uri,