@owney/sdk 0.5.3 → 0.5.5-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -32,6 +32,8 @@ module.exports = __toCommonJS(src_exports);
32
32
 
33
33
  // src/agents/zyfai/zyfai.agent.ts
34
34
  var import_sdk = require("@zyfai/sdk");
35
+ var import_viem = require("viem");
36
+ var import_chains = require("viem/chains");
35
37
 
36
38
  // src/types/config.ts
37
39
  var P_SUPPORTED_CHAINS = {
@@ -268,6 +270,14 @@ function mapApyHistory(raw) {
268
270
  apyByChainAndAsset: mapWeightedApyByChain(raw.weightedApyAfterFeeByChain)
269
271
  };
270
272
  }
273
+ function splitOpportunity(opportunity) {
274
+ const match = opportunity.match(/^(.*?)\s*\(([^)]*)\)\s*$/);
275
+ if (match) {
276
+ const pool = match[2].trim();
277
+ return { protocol: match[1].trim(), pool: pool || void 0 };
278
+ }
279
+ return { protocol: opportunity.trim() };
280
+ }
271
281
  function mapEntries(rawEntries, chainId) {
272
282
  const actionMap = {
273
283
  "Top up wallet": "Top up",
@@ -287,21 +297,26 @@ function mapEntries(rawEntries, chainId) {
287
297
  );
288
298
  oldApy = Number(rawLog.oldApy);
289
299
  newApy = Number(rawLog.newApy);
300
+ const from = splitOpportunity(rawLog.oldOpportunity);
301
+ const to = splitOpportunity(rawLog.newOpportunity);
290
302
  rebalanceLog = [
291
303
  {
292
- fromProtocol: rawLog.oldOpportunity.replace(/\s*\(.*?\)/g, "").trim(),
293
- toProtocol: rawLog.newOpportunity.replace(/\s*\(.*?\)/g, "").trim(),
304
+ fromProtocol: from.protocol,
305
+ toProtocol: to.protocol,
306
+ fromPool: from.pool,
307
+ toPool: to.pool,
294
308
  tokenSymbol: matched?.token_symbol ?? "",
295
309
  amount: matched ? String(hexToDecimal(matched.amount || "0x")) : "",
296
310
  status: "success"
297
311
  }
298
312
  ];
299
313
  }
314
+ const mappedAction = (entry.action ? actionMap[entry.action] : void 0) ?? entry.action;
300
315
  const rawAmount = entry?.positions?.[0]?.amount;
301
- const hasAmount = typeof rawAmount === "string" && rawAmount !== "" && rawAmount !== "0x" && rawAmount !== "0x0" && rawAmount !== "0";
316
+ const hasAmount = mappedAction !== "Withdraw" && typeof rawAmount === "string" && rawAmount !== "" && rawAmount !== "0x" && rawAmount !== "0x0" && rawAmount !== "0";
302
317
  return {
303
318
  agent: "zyfai",
304
- action: (entry.action ? actionMap[entry.action] : void 0) ?? entry.action,
319
+ action: mappedAction,
305
320
  date: entry.date || "",
306
321
  newApy: newApy === null ? null : String(newApy),
307
322
  oldApy: oldApy === null ? null : String(oldApy),
@@ -393,6 +408,33 @@ function computeAllocationApy(positions) {
393
408
  return { totalApy, apyByChainAndAsset };
394
409
  }
395
410
 
411
+ // src/agents/zyfai/zyfai.withdraw-amount.ts
412
+ var TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
413
+ function topicToAddress(topic) {
414
+ return `0x${(topic ?? "").slice(-40)}`.toLowerCase();
415
+ }
416
+ function extractWithdrawnAmount(logs, recipient, token, decimals) {
417
+ const wantRecipient = recipient.toLowerCase();
418
+ const wantToken = token.toLowerCase();
419
+ let total = 0n;
420
+ let matched = false;
421
+ for (const log of logs) {
422
+ if (log.address.toLowerCase() !== wantToken)
423
+ continue;
424
+ if ((log.topics[0] ?? "").toLowerCase() !== TRANSFER_TOPIC)
425
+ continue;
426
+ if (topicToAddress(log.topics[2]) !== wantRecipient)
427
+ continue;
428
+ if (!log.data || log.data === "0x")
429
+ continue;
430
+ total += BigInt(log.data);
431
+ matched = true;
432
+ }
433
+ if (!matched)
434
+ return void 0;
435
+ return String(hexToDecimal(`0x${total.toString(16)}`, decimals));
436
+ }
437
+
396
438
  // src/agents/zyfai/zyfai.history-cursor.ts
397
439
  var InvalidHistoryCursorError = class extends Error {
398
440
  code = "invalid_cursor";
@@ -491,6 +533,7 @@ var DEFAULT_ZYFAI_RPC_URLS = {
491
533
  8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
492
534
  42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
493
535
  };
536
+ var VIEM_CHAIN = { 8453: import_chains.base, 42161: import_chains.arbitrum };
494
537
  var ZyfaiAgent = class {
495
538
  id = "zyfai";
496
539
  supportedChainIds = [8453, 42161];
@@ -511,13 +554,83 @@ var ZyfaiAgent = class {
511
554
  connectedChainId = null;
512
555
  smartWalletCache = /* @__PURE__ */ new Map();
513
556
  connectAuthPromise = null;
557
+ rpcUrls;
558
+ publicClients = /* @__PURE__ */ new Map();
559
+ // txHash -> resolved withdrawn amount. Receipts are immutable, so a
560
+ // successful decode is cached forever; failures are NOT cached so a transient
561
+ // RPC error retries on the next fetch.
562
+ withdrawAmountCache = /* @__PURE__ */ new Map();
514
563
  constructor(apiKey, rpcUrls) {
564
+ this.rpcUrls = rpcUrls ?? DEFAULT_ZYFAI_RPC_URLS;
515
565
  this.sdk = new import_sdk.ZyfaiSDK({
516
566
  apiKey,
517
- rpcUrls: rpcUrls ?? DEFAULT_ZYFAI_RPC_URLS
567
+ rpcUrls: this.rpcUrls
518
568
  });
519
569
  this.installAuthCache();
520
570
  }
571
+ getPublicClient(chainId) {
572
+ const cached = this.publicClients.get(chainId);
573
+ if (cached)
574
+ return cached;
575
+ const client = (0, import_viem.createPublicClient)({
576
+ chain: VIEM_CHAIN[chainId],
577
+ transport: (0, import_viem.http)(this.rpcUrls[chainId])
578
+ });
579
+ this.publicClients.set(chainId, client);
580
+ return client;
581
+ }
582
+ /**
583
+ * Fills in the true withdrawn amount for `Withdraw` entries by decoding the
584
+ * USDC `Transfer` that lands in the user's wallet on-chain. The Zyfai history
585
+ * payload only reports the *remaining* position for a partial withdraw (see
586
+ * the note in `mapEntries`), so without this the UI shows the leftover
587
+ * balance instead of the amount withdrawn.
588
+ *
589
+ * Mutates `entries` in place. Runs all lookups concurrently and is tolerant
590
+ * of RPC failures: an entry whose amount can't be resolved keeps
591
+ * `amount: undefined`, which consumers render as a hidden/retryable row
592
+ * rather than a wrong number.
593
+ */
594
+ async resolveWithdrawnAmounts(entries, chainId, recipient) {
595
+ const asset = SupportedAssets.find(
596
+ (a) => a.chainId === chainId && a.symbol === "USDC"
597
+ );
598
+ if (!asset)
599
+ return;
600
+ const withdrawals = entries.filter(
601
+ (e) => e.action === "Withdraw" && e.transactions[0]?.txHashes[0]
602
+ );
603
+ if (withdrawals.length === 0)
604
+ return;
605
+ const client = this.getPublicClient(chainId);
606
+ await Promise.all(
607
+ withdrawals.map(async (entry) => {
608
+ const tx = entry.transactions[0];
609
+ const txHash = tx.txHashes[0];
610
+ const cached = this.withdrawAmountCache.get(txHash);
611
+ if (cached !== void 0) {
612
+ tx.amount = cached;
613
+ return;
614
+ }
615
+ try {
616
+ const receipt = await client.getTransactionReceipt({
617
+ hash: txHash
618
+ });
619
+ const amount = extractWithdrawnAmount(
620
+ receipt.logs,
621
+ recipient,
622
+ asset.address,
623
+ asset.decimals
624
+ );
625
+ if (amount !== void 0) {
626
+ this.withdrawAmountCache.set(txHash, amount);
627
+ tx.amount = amount;
628
+ }
629
+ } catch {
630
+ }
631
+ })
632
+ );
633
+ }
521
634
  /**
522
635
  * Patches `sdk.authenticateUser` so a fresh page can reuse the JWT from a
523
636
  * prior session instead of triggering a new SIWE prompt. Runs inside
@@ -768,8 +881,10 @@ var ZyfaiAgent = class {
768
881
  toDate,
769
882
  rawOffset: firstOvershoot ? firstOvershoot.rawIdx : offset
770
883
  }) : void 0;
884
+ const data = mapEntries(pageData, validChainId);
885
+ await this.resolveWithdrawnAmounts(data, validChainId, state.walletAddress);
771
886
  return {
772
- data: mapEntries(pageData, validChainId),
887
+ data,
773
888
  hasMore,
774
889
  nextCursor
775
890
  };
@@ -860,7 +975,7 @@ async function withFailureReporting(apiKey, agentType, fn) {
860
975
  }
861
976
 
862
977
  // src/lib/helpers/withdraw-helper.ts
863
- var import_viem = require("viem");
978
+ var import_viem2 = require("viem");
864
979
  function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
865
980
  const target = asset.toUpperCase();
866
981
  return agents.map((agent) => {
@@ -870,7 +985,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
870
985
  );
871
986
  if (!tokenBalance)
872
987
  return { agent, balance: 0n };
873
- return { agent, balance: (0, import_viem.parseUnits)(tokenBalance.amount, decimals) };
988
+ return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
874
989
  });
875
990
  }
876
991
  function planProportionalShares(balances, requested, totalAvailable) {
@@ -986,11 +1101,11 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
986
1101
  }
987
1102
 
988
1103
  // src/client.ts
989
- var import_viem3 = require("viem");
990
- var import_chains = require("viem/chains");
1104
+ var import_viem4 = require("viem");
1105
+ var import_chains2 = require("viem/chains");
991
1106
 
992
1107
  // src/lib/transfer-auth.ts
993
- var import_viem2 = require("viem");
1108
+ var import_viem3 = require("viem");
994
1109
  var ERC20_META_ABI = [
995
1110
  { type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
996
1111
  { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
@@ -1022,16 +1137,16 @@ async function readTokenMeta(publicClient, token) {
1022
1137
  function randomAuthNonce() {
1023
1138
  const bytes = new Uint8Array(32);
1024
1139
  globalThis.crypto.getRandomValues(bytes);
1025
- return (0, import_viem2.bytesToHex)(bytes);
1140
+ return (0, import_viem3.bytesToHex)(bytes);
1026
1141
  }
1027
1142
 
1028
1143
  // src/lib/sponsor-client.ts
1029
1144
  var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
1030
1145
  async function postSponsorTransferAuth(input) {
1031
- const base2 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1146
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1032
1147
  let res;
1033
1148
  try {
1034
- res = await fetch(`${base2}/api/v1/sponsor/erc20-transfer-auth`, {
1149
+ res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
1035
1150
  method: "POST",
1036
1151
  headers: { "content-type": "application/json", "x-owney-api-key": input.apiKey },
1037
1152
  body: JSON.stringify(input.body)
@@ -1134,9 +1249,9 @@ var SPONSORED_USDC_BY_CHAIN = {
1134
1249
  8453: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1135
1250
  42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
1136
1251
  };
1137
- var VIEM_CHAIN = {
1138
- 8453: import_chains.base,
1139
- 42161: import_chains.arbitrum
1252
+ var VIEM_CHAIN2 = {
1253
+ 8453: import_chains2.base,
1254
+ 42161: import_chains2.arbitrum
1140
1255
  };
1141
1256
  var OwneySDK = class {
1142
1257
  agents = /* @__PURE__ */ new Map();
@@ -1249,14 +1364,14 @@ var OwneySDK = class {
1249
1364
  // Casts work around viem's chain-narrowed Client vs the generic
1250
1365
  // PublicClient/WalletClient param types — structurally identical at
1251
1366
  // runtime, but the two share a name TS treats as unrelated.
1252
- getPublicClient: (cid) => (0, import_viem3.createPublicClient)({
1253
- chain: VIEM_CHAIN[cid],
1254
- transport: (0, import_viem3.custom)(provider)
1367
+ getPublicClient: (cid) => (0, import_viem4.createPublicClient)({
1368
+ chain: VIEM_CHAIN2[cid],
1369
+ transport: (0, import_viem4.custom)(provider)
1255
1370
  }),
1256
- getWalletClient: (cid) => (0, import_viem3.createWalletClient)({
1371
+ getWalletClient: (cid) => (0, import_viem4.createWalletClient)({
1257
1372
  account: owner,
1258
- chain: VIEM_CHAIN[cid],
1259
- transport: (0, import_viem3.custom)(provider)
1373
+ chain: VIEM_CHAIN2[cid],
1374
+ transport: (0, import_viem4.custom)(provider)
1260
1375
  })
1261
1376
  });
1262
1377
  return this.cachedSponsoredCallback;
@@ -2116,7 +2231,7 @@ var OwneySDK = class {
2116
2231
  };
2117
2232
 
2118
2233
  // src/agents/zyfai/zyfai.siwx.ts
2119
- var import_viem4 = require("viem");
2234
+ var import_viem5 = require("viem");
2120
2235
  var import_siwe = require("siwe");
2121
2236
  var import_sdk2 = require("@zyfai/sdk");
2122
2237
 
@@ -2198,7 +2313,7 @@ function buildSIWXConfig(deps) {
2198
2313
  issuedAt,
2199
2314
  toString() {
2200
2315
  return new import_siwe.SiweMessage({
2201
- address: (0, import_viem4.getAddress)(accountAddress),
2316
+ address: (0, import_viem5.getAddress)(accountAddress),
2202
2317
  chainId: numericChainId(chainId),
2203
2318
  domain,
2204
2319
  uri,
@@ -2274,9 +2389,9 @@ function buildSIWXConfig(deps) {
2274
2389
  }
2275
2390
  function createOwneySIWX(config) {
2276
2391
  const zyfai = new import_sdk2.ZyfaiSDK({ apiKey: config.apiKey });
2277
- const http = zyfai.httpClient;
2392
+ const http2 = zyfai.httpClient;
2278
2393
  return buildSIWXConfig({
2279
- post: (url, data) => http.post(url, data),
2394
+ post: (url, data) => http2.post(url, data),
2280
2395
  referralSource: config.referralSource
2281
2396
  });
2282
2397
  }
package/dist/index.d.cts CHANGED
@@ -114,6 +114,15 @@ interface HistoryTransaction {
114
114
  interface RebalanceLog {
115
115
  fromProtocol: string;
116
116
  toProtocol: string;
117
+ /**
118
+ * Pool/vault sub-label for the source position (e.g. "USDC Core"), when the
119
+ * agent exposes one. Lets consumers disambiguate moves within the same
120
+ * protocol ("Morpho · USDC Core → Morpho · Spark USDC"). Absent when the
121
+ * agent only reports a bare protocol name.
122
+ */
123
+ fromPool?: string;
124
+ /** Pool/vault sub-label for the destination position. See {@link fromPool}. */
125
+ toPool?: string;
117
126
  tokenSymbol: string;
118
127
  amount: string;
119
128
  status: "success" | "failed";
package/dist/index.d.ts CHANGED
@@ -114,6 +114,15 @@ interface HistoryTransaction {
114
114
  interface RebalanceLog {
115
115
  fromProtocol: string;
116
116
  toProtocol: string;
117
+ /**
118
+ * Pool/vault sub-label for the source position (e.g. "USDC Core"), when the
119
+ * agent exposes one. Lets consumers disambiguate moves within the same
120
+ * protocol ("Morpho · USDC Core → Morpho · Spark USDC"). Absent when the
121
+ * agent only reports a bare protocol name.
122
+ */
123
+ fromPool?: string;
124
+ /** Pool/vault sub-label for the destination position. See {@link fromPool}. */
125
+ toPool?: string;
117
126
  tokenSymbol: string;
118
127
  amount: string;
119
128
  status: "success" | "failed";
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  // src/agents/zyfai/zyfai.agent.ts
2
2
  import { ZyfaiSDK } from "@zyfai/sdk";
3
+ import { createPublicClient, http } from "viem";
4
+ import { base, arbitrum } from "viem/chains";
3
5
 
4
6
  // src/types/config.ts
5
7
  var P_SUPPORTED_CHAINS = {
@@ -236,6 +238,14 @@ function mapApyHistory(raw) {
236
238
  apyByChainAndAsset: mapWeightedApyByChain(raw.weightedApyAfterFeeByChain)
237
239
  };
238
240
  }
241
+ function splitOpportunity(opportunity) {
242
+ const match = opportunity.match(/^(.*?)\s*\(([^)]*)\)\s*$/);
243
+ if (match) {
244
+ const pool = match[2].trim();
245
+ return { protocol: match[1].trim(), pool: pool || void 0 };
246
+ }
247
+ return { protocol: opportunity.trim() };
248
+ }
239
249
  function mapEntries(rawEntries, chainId) {
240
250
  const actionMap = {
241
251
  "Top up wallet": "Top up",
@@ -255,21 +265,26 @@ function mapEntries(rawEntries, chainId) {
255
265
  );
256
266
  oldApy = Number(rawLog.oldApy);
257
267
  newApy = Number(rawLog.newApy);
268
+ const from = splitOpportunity(rawLog.oldOpportunity);
269
+ const to = splitOpportunity(rawLog.newOpportunity);
258
270
  rebalanceLog = [
259
271
  {
260
- fromProtocol: rawLog.oldOpportunity.replace(/\s*\(.*?\)/g, "").trim(),
261
- toProtocol: rawLog.newOpportunity.replace(/\s*\(.*?\)/g, "").trim(),
272
+ fromProtocol: from.protocol,
273
+ toProtocol: to.protocol,
274
+ fromPool: from.pool,
275
+ toPool: to.pool,
262
276
  tokenSymbol: matched?.token_symbol ?? "",
263
277
  amount: matched ? String(hexToDecimal(matched.amount || "0x")) : "",
264
278
  status: "success"
265
279
  }
266
280
  ];
267
281
  }
282
+ const mappedAction = (entry.action ? actionMap[entry.action] : void 0) ?? entry.action;
268
283
  const rawAmount = entry?.positions?.[0]?.amount;
269
- const hasAmount = typeof rawAmount === "string" && rawAmount !== "" && rawAmount !== "0x" && rawAmount !== "0x0" && rawAmount !== "0";
284
+ const hasAmount = mappedAction !== "Withdraw" && typeof rawAmount === "string" && rawAmount !== "" && rawAmount !== "0x" && rawAmount !== "0x0" && rawAmount !== "0";
270
285
  return {
271
286
  agent: "zyfai",
272
- action: (entry.action ? actionMap[entry.action] : void 0) ?? entry.action,
287
+ action: mappedAction,
273
288
  date: entry.date || "",
274
289
  newApy: newApy === null ? null : String(newApy),
275
290
  oldApy: oldApy === null ? null : String(oldApy),
@@ -361,6 +376,33 @@ function computeAllocationApy(positions) {
361
376
  return { totalApy, apyByChainAndAsset };
362
377
  }
363
378
 
379
+ // src/agents/zyfai/zyfai.withdraw-amount.ts
380
+ var TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
381
+ function topicToAddress(topic) {
382
+ return `0x${(topic ?? "").slice(-40)}`.toLowerCase();
383
+ }
384
+ function extractWithdrawnAmount(logs, recipient, token, decimals) {
385
+ const wantRecipient = recipient.toLowerCase();
386
+ const wantToken = token.toLowerCase();
387
+ let total = 0n;
388
+ let matched = false;
389
+ for (const log of logs) {
390
+ if (log.address.toLowerCase() !== wantToken)
391
+ continue;
392
+ if ((log.topics[0] ?? "").toLowerCase() !== TRANSFER_TOPIC)
393
+ continue;
394
+ if (topicToAddress(log.topics[2]) !== wantRecipient)
395
+ continue;
396
+ if (!log.data || log.data === "0x")
397
+ continue;
398
+ total += BigInt(log.data);
399
+ matched = true;
400
+ }
401
+ if (!matched)
402
+ return void 0;
403
+ return String(hexToDecimal(`0x${total.toString(16)}`, decimals));
404
+ }
405
+
364
406
  // src/agents/zyfai/zyfai.history-cursor.ts
365
407
  var InvalidHistoryCursorError = class extends Error {
366
408
  code = "invalid_cursor";
@@ -459,6 +501,7 @@ var DEFAULT_ZYFAI_RPC_URLS = {
459
501
  8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
460
502
  42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
461
503
  };
504
+ var VIEM_CHAIN = { 8453: base, 42161: arbitrum };
462
505
  var ZyfaiAgent = class {
463
506
  id = "zyfai";
464
507
  supportedChainIds = [8453, 42161];
@@ -479,13 +522,83 @@ var ZyfaiAgent = class {
479
522
  connectedChainId = null;
480
523
  smartWalletCache = /* @__PURE__ */ new Map();
481
524
  connectAuthPromise = null;
525
+ rpcUrls;
526
+ publicClients = /* @__PURE__ */ new Map();
527
+ // txHash -> resolved withdrawn amount. Receipts are immutable, so a
528
+ // successful decode is cached forever; failures are NOT cached so a transient
529
+ // RPC error retries on the next fetch.
530
+ withdrawAmountCache = /* @__PURE__ */ new Map();
482
531
  constructor(apiKey, rpcUrls) {
532
+ this.rpcUrls = rpcUrls ?? DEFAULT_ZYFAI_RPC_URLS;
483
533
  this.sdk = new ZyfaiSDK({
484
534
  apiKey,
485
- rpcUrls: rpcUrls ?? DEFAULT_ZYFAI_RPC_URLS
535
+ rpcUrls: this.rpcUrls
486
536
  });
487
537
  this.installAuthCache();
488
538
  }
539
+ getPublicClient(chainId) {
540
+ const cached = this.publicClients.get(chainId);
541
+ if (cached)
542
+ return cached;
543
+ const client = createPublicClient({
544
+ chain: VIEM_CHAIN[chainId],
545
+ transport: http(this.rpcUrls[chainId])
546
+ });
547
+ this.publicClients.set(chainId, client);
548
+ return client;
549
+ }
550
+ /**
551
+ * Fills in the true withdrawn amount for `Withdraw` entries by decoding the
552
+ * USDC `Transfer` that lands in the user's wallet on-chain. The Zyfai history
553
+ * payload only reports the *remaining* position for a partial withdraw (see
554
+ * the note in `mapEntries`), so without this the UI shows the leftover
555
+ * balance instead of the amount withdrawn.
556
+ *
557
+ * Mutates `entries` in place. Runs all lookups concurrently and is tolerant
558
+ * of RPC failures: an entry whose amount can't be resolved keeps
559
+ * `amount: undefined`, which consumers render as a hidden/retryable row
560
+ * rather than a wrong number.
561
+ */
562
+ async resolveWithdrawnAmounts(entries, chainId, recipient) {
563
+ const asset = SupportedAssets.find(
564
+ (a) => a.chainId === chainId && a.symbol === "USDC"
565
+ );
566
+ if (!asset)
567
+ return;
568
+ const withdrawals = entries.filter(
569
+ (e) => e.action === "Withdraw" && e.transactions[0]?.txHashes[0]
570
+ );
571
+ if (withdrawals.length === 0)
572
+ return;
573
+ const client = this.getPublicClient(chainId);
574
+ await Promise.all(
575
+ withdrawals.map(async (entry) => {
576
+ const tx = entry.transactions[0];
577
+ const txHash = tx.txHashes[0];
578
+ const cached = this.withdrawAmountCache.get(txHash);
579
+ if (cached !== void 0) {
580
+ tx.amount = cached;
581
+ return;
582
+ }
583
+ try {
584
+ const receipt = await client.getTransactionReceipt({
585
+ hash: txHash
586
+ });
587
+ const amount = extractWithdrawnAmount(
588
+ receipt.logs,
589
+ recipient,
590
+ asset.address,
591
+ asset.decimals
592
+ );
593
+ if (amount !== void 0) {
594
+ this.withdrawAmountCache.set(txHash, amount);
595
+ tx.amount = amount;
596
+ }
597
+ } catch {
598
+ }
599
+ })
600
+ );
601
+ }
489
602
  /**
490
603
  * Patches `sdk.authenticateUser` so a fresh page can reuse the JWT from a
491
604
  * prior session instead of triggering a new SIWE prompt. Runs inside
@@ -736,8 +849,10 @@ var ZyfaiAgent = class {
736
849
  toDate,
737
850
  rawOffset: firstOvershoot ? firstOvershoot.rawIdx : offset
738
851
  }) : void 0;
852
+ const data = mapEntries(pageData, validChainId);
853
+ await this.resolveWithdrawnAmounts(data, validChainId, state.walletAddress);
739
854
  return {
740
- data: mapEntries(pageData, validChainId),
855
+ data,
741
856
  hasMore,
742
857
  nextCursor
743
858
  };
@@ -955,11 +1070,11 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
955
1070
 
956
1071
  // src/client.ts
957
1072
  import {
958
- createPublicClient,
1073
+ createPublicClient as createPublicClient2,
959
1074
  createWalletClient,
960
1075
  custom
961
1076
  } from "viem";
962
- import { base, arbitrum } from "viem/chains";
1077
+ import { base as base2, arbitrum as arbitrum2 } from "viem/chains";
963
1078
 
964
1079
  // src/lib/transfer-auth.ts
965
1080
  import { bytesToHex } from "viem";
@@ -1000,10 +1115,10 @@ function randomAuthNonce() {
1000
1115
  // src/lib/sponsor-client.ts
1001
1116
  var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
1002
1117
  async function postSponsorTransferAuth(input) {
1003
- const base2 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1118
+ const base3 = input.baseUrl ?? ROUTING_API_BASE_URL3;
1004
1119
  let res;
1005
1120
  try {
1006
- res = await fetch(`${base2}/api/v1/sponsor/erc20-transfer-auth`, {
1121
+ res = await fetch(`${base3}/api/v1/sponsor/erc20-transfer-auth`, {
1007
1122
  method: "POST",
1008
1123
  headers: { "content-type": "application/json", "x-owney-api-key": input.apiKey },
1009
1124
  body: JSON.stringify(input.body)
@@ -1106,9 +1221,9 @@ var SPONSORED_USDC_BY_CHAIN = {
1106
1221
  8453: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1107
1222
  42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
1108
1223
  };
1109
- var VIEM_CHAIN = {
1110
- 8453: base,
1111
- 42161: arbitrum
1224
+ var VIEM_CHAIN2 = {
1225
+ 8453: base2,
1226
+ 42161: arbitrum2
1112
1227
  };
1113
1228
  var OwneySDK = class {
1114
1229
  agents = /* @__PURE__ */ new Map();
@@ -1221,13 +1336,13 @@ var OwneySDK = class {
1221
1336
  // Casts work around viem's chain-narrowed Client vs the generic
1222
1337
  // PublicClient/WalletClient param types — structurally identical at
1223
1338
  // runtime, but the two share a name TS treats as unrelated.
1224
- getPublicClient: (cid) => createPublicClient({
1225
- chain: VIEM_CHAIN[cid],
1339
+ getPublicClient: (cid) => createPublicClient2({
1340
+ chain: VIEM_CHAIN2[cid],
1226
1341
  transport: custom(provider)
1227
1342
  }),
1228
1343
  getWalletClient: (cid) => createWalletClient({
1229
1344
  account: owner,
1230
- chain: VIEM_CHAIN[cid],
1345
+ chain: VIEM_CHAIN2[cid],
1231
1346
  transport: custom(provider)
1232
1347
  })
1233
1348
  });
@@ -2246,9 +2361,9 @@ function buildSIWXConfig(deps) {
2246
2361
  }
2247
2362
  function createOwneySIWX(config) {
2248
2363
  const zyfai = new ZyfaiSDK2({ apiKey: config.apiKey });
2249
- const http = zyfai.httpClient;
2364
+ const http2 = zyfai.httpClient;
2250
2365
  return buildSIWXConfig({
2251
- post: (url, data) => http.post(url, data),
2366
+ post: (url, data) => http2.post(url, data),
2252
2367
  referralSource: config.referralSource
2253
2368
  });
2254
2369
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.5.3",
3
+ "version": "0.5.5-beta.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",