@owney/sdk 0.7.26-beta.6 → 0.7.26-beta.8

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
@@ -303,6 +303,15 @@ var DayFilterMapping = {
303
303
  "7D": 7
304
304
  };
305
305
 
306
+ // src/lib/helpers/asset-aliases.ts
307
+ function assetAliases(asset) {
308
+ const normalised = asset.toLowerCase();
309
+ return normalised === "weth" ? ["weth", "eth"] : [normalised];
310
+ }
311
+ function matchesAsset(symbol, asset) {
312
+ return assetAliases(asset).includes(symbol.toLowerCase());
313
+ }
314
+
306
315
  // src/agents/zyfai/zyfai.mapper.ts
307
316
  var DECIMALS_BY_SYMBOL = {
308
317
  usdc: 6,
@@ -707,9 +716,8 @@ function computeAllocationApy(positions) {
707
716
  function earningsForToken(bucket, chainId, asset) {
708
717
  const tokens = bucket?.[String(chainId)];
709
718
  if (!tokens) return null;
710
- const wanted = asset.toUpperCase();
711
719
  for (const [symbol, value] of Object.entries(tokens)) {
712
- if (symbol.toUpperCase() !== wanted) continue;
720
+ if (!matchesAsset(symbol, asset)) continue;
713
721
  const amount = Number(value);
714
722
  return Number.isFinite(amount) ? amount : null;
715
723
  }
@@ -3537,11 +3545,10 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
3537
3545
  // src/lib/helpers/withdraw-helper.ts
3538
3546
  var import_viem6 = require("viem");
3539
3547
  function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
3540
- const target = asset.toUpperCase();
3541
3548
  return agents.map((agent) => {
3542
3549
  const agentBalance = aggregated[agent.id];
3543
3550
  const tokenBalance = agentBalance?.tokens.find(
3544
- (t) => t.chainId === chainId && t.asset.toUpperCase() === target
3551
+ (t) => t.chainId === chainId && matchesAsset(t.asset, asset)
3545
3552
  );
3546
3553
  if (!tokenBalance) return { agent, balance: 0n };
3547
3554
  return { agent, balance: (0, import_viem6.parseUnits)(tokenBalance.amount, decimals) };
@@ -4842,15 +4849,14 @@ var OwneySDK = class {
4842
4849
  async hasExistingBalance(agent, state, chainId, asset, requireReliableRead = false) {
4843
4850
  try {
4844
4851
  const balance = await agent.getBalances(state, chainId);
4845
- const target = asset.toLowerCase();
4846
4852
  const token = balance.tokens.find(
4847
- (t) => t.chainId === chainId && t.asset.toLowerCase() === target
4853
+ (t) => t.chainId === chainId && matchesAsset(t.asset, asset)
4848
4854
  );
4849
4855
  const targetChainName = agent.supportedAssets.find((entry) => entry.chainId === chainId)?.chain?.trim().toUpperCase();
4850
4856
  const position = (balance.positions ?? []).find((p) => {
4851
4857
  const positionChain = p.chain.trim().toUpperCase();
4852
4858
  const matchesChain = positionChain === String(chainId) || targetChainName !== void 0 && positionChain === targetChainName;
4853
- return matchesChain && p.asset.toLowerCase() === target && Number(p.amount) > 0;
4859
+ return matchesChain && matchesAsset(p.asset, asset) && Number(p.amount) > 0;
4854
4860
  });
4855
4861
  return !!token && Number(token.amount) > 0 || !!position;
4856
4862
  } catch (error) {
@@ -5193,6 +5199,64 @@ var OwneySDK = class {
5193
5199
  * the deposit's own check is the authority and refusing to even attempt
5194
5200
  * would strand funds we might have been able to move.
5195
5201
  */
5202
+ /** ERC20 balance of the connected wallet, read through the wallet's own node. */
5203
+ async readTokenBalance(chainId, token) {
5204
+ const state = this.requireState();
5205
+ return readErc20Balance(
5206
+ (0, import_viem9.createPublicClient)({
5207
+ chain: swapChain(chainId),
5208
+ transport: (0, import_viem9.custom)(this.requireConnectedProvider())
5209
+ }),
5210
+ token,
5211
+ state.walletAddress
5212
+ );
5213
+ }
5214
+ /** How much of `token` the wallet has already approved Permit2 to move. */
5215
+ async readTokenPermit2Allowance(chainId, token) {
5216
+ const state = this.requireState();
5217
+ return readPermit2Allowance(
5218
+ (0, import_viem9.createPublicClient)({
5219
+ chain: swapChain(chainId),
5220
+ transport: (0, import_viem9.custom)(this.requireConnectedProvider())
5221
+ }),
5222
+ token,
5223
+ state.walletAddress
5224
+ );
5225
+ }
5226
+ /**
5227
+ * How much of a landed swap to actually deposit.
5228
+ *
5229
+ * The swap reports what ARRIVED, and the deposit used to be handed that
5230
+ * figure directly. It is the entire arrival, so there is no slack: anything
5231
+ * that spends the target asset between the two fails the deposit outright.
5232
+ *
5233
+ * Something does. A first WETH deposit needs a one-time Permit2 approval,
5234
+ * and that approval is a transaction. A wallet holding no native gas token -
5235
+ * the normal state of someone who just swapped everything INTO WETH - pays
5236
+ * for it out of the WETH. The deposit then asked for ~2e12 wei more than was
5237
+ * left and refused with DEPOSIT_INSUFFICIENT_BALANCE, under a wrapper saying
5238
+ * the funds were safe but unadded (ROUT-242).
5239
+ *
5240
+ * So the approval happens here, before the amount is settled, and the amount
5241
+ * comes from a read taken after it. Two things this deliberately is not:
5242
+ *
5243
+ * - It is not a balance sweep. `received` is a DELTA over the pre-swap
5244
+ * balance, so a wallet already holding WETH reads far above it; depositing
5245
+ * the balance would move funds the user never offered. The arrival is the
5246
+ * ceiling and the balance is only ever allowed to lower it.
5247
+ * - It does not touch USDC, which moves on signed typed data and so spends
5248
+ * nothing of the user's in between, nor a paymaster-configured client,
5249
+ * where the sponsor covers the transfer and Permit2 never applies.
5250
+ */
5251
+ async settleSwapDepositAmount(chainId, token, symbol, received) {
5252
+ if (symbol !== "WETH" || this.paymasterServiceUrl) {
5253
+ return received.toString();
5254
+ }
5255
+ const allowance = await this.readTokenPermit2Allowance(chainId, token);
5256
+ if (allowance < received) await this.approvePermit2();
5257
+ const balance = await this.readTokenBalance(chainId, token);
5258
+ return (balance < received ? balance : received).toString();
5259
+ }
5196
5260
  async walletSeesBalance(chainId, token, amount) {
5197
5261
  const provider = this.requireConnectedProvider();
5198
5262
  const state = this.requireState();
@@ -5421,7 +5485,7 @@ var OwneySDK = class {
5421
5485
  */
5422
5486
  async getWalletBalances(chainIds) {
5423
5487
  const state = this.requireState();
5424
- const chains = chainIds ?? SUPPORTED_CHAIN_IDS;
5488
+ const chains = chainIds ?? SWAP_CHAIN_IDS;
5425
5489
  const failedChainIds = [];
5426
5490
  const results = await Promise.all(
5427
5491
  chains.map(async (chainId) => {
@@ -5527,12 +5591,18 @@ var OwneySDK = class {
5527
5591
  quote.dst.address,
5528
5592
  BigInt(swap.received)
5529
5593
  );
5594
+ const amount = await this.settleSwapDepositAmount(
5595
+ quote.dst.chainId,
5596
+ quote.dst.address,
5597
+ options.to.symbol,
5598
+ BigInt(swap.received)
5599
+ );
5530
5600
  const deposit = await this.deposit({
5531
- amount: swap.received,
5601
+ amount,
5532
5602
  asset: options.to.symbol,
5533
5603
  ...options.agentId ? { agentId: options.agentId } : {}
5534
5604
  });
5535
- return { swap, deposit };
5605
+ return { swap, deposit, deposited: amount };
5536
5606
  } catch (error) {
5537
5607
  const pending = {
5538
5608
  id: `${quote.dst.chainId}-${options.to.symbol}-${Date.now()}`,
package/dist/index.d.cts CHANGED
@@ -1129,6 +1129,36 @@ declare class OwneySDK {
1129
1129
  * the deposit's own check is the authority and refusing to even attempt
1130
1130
  * would strand funds we might have been able to move.
1131
1131
  */
1132
+ /** ERC20 balance of the connected wallet, read through the wallet's own node. */
1133
+ private readTokenBalance;
1134
+ /** How much of `token` the wallet has already approved Permit2 to move. */
1135
+ private readTokenPermit2Allowance;
1136
+ /**
1137
+ * How much of a landed swap to actually deposit.
1138
+ *
1139
+ * The swap reports what ARRIVED, and the deposit used to be handed that
1140
+ * figure directly. It is the entire arrival, so there is no slack: anything
1141
+ * that spends the target asset between the two fails the deposit outright.
1142
+ *
1143
+ * Something does. A first WETH deposit needs a one-time Permit2 approval,
1144
+ * and that approval is a transaction. A wallet holding no native gas token -
1145
+ * the normal state of someone who just swapped everything INTO WETH - pays
1146
+ * for it out of the WETH. The deposit then asked for ~2e12 wei more than was
1147
+ * left and refused with DEPOSIT_INSUFFICIENT_BALANCE, under a wrapper saying
1148
+ * the funds were safe but unadded (ROUT-242).
1149
+ *
1150
+ * So the approval happens here, before the amount is settled, and the amount
1151
+ * comes from a read taken after it. Two things this deliberately is not:
1152
+ *
1153
+ * - It is not a balance sweep. `received` is a DELTA over the pre-swap
1154
+ * balance, so a wallet already holding WETH reads far above it; depositing
1155
+ * the balance would move funds the user never offered. The arrival is the
1156
+ * ceiling and the balance is only ever allowed to lower it.
1157
+ * - It does not touch USDC, which moves on signed typed data and so spends
1158
+ * nothing of the user's in between, nor a paymaster-configured client,
1159
+ * where the sponsor covers the transfer and Permit2 never applies.
1160
+ */
1161
+ private settleSwapDepositAmount;
1132
1162
  private walletSeesBalance;
1133
1163
  /**
1134
1164
  * Runs a quoted swap. A named seam rather than an inline `executeSwap` call
@@ -1328,6 +1358,12 @@ declare class OwneySDK {
1328
1358
  txHash?: string;
1329
1359
  };
1330
1360
  deposit: OwneyDepositResult | OwneyMultiDepositResult;
1361
+ /**
1362
+ * What was actually deposited, which is not always what the swap
1363
+ * delivered - see `settleSwapDepositAmount`. Callers seeding a headline
1364
+ * want this one; `swap.received` stays what it says it is.
1365
+ */
1366
+ deposited: string;
1331
1367
  }>;
1332
1368
  /**
1333
1369
  * Withdraw from an agent and swap the proceeds into whatever the user wants
package/dist/index.d.ts CHANGED
@@ -1129,6 +1129,36 @@ declare class OwneySDK {
1129
1129
  * the deposit's own check is the authority and refusing to even attempt
1130
1130
  * would strand funds we might have been able to move.
1131
1131
  */
1132
+ /** ERC20 balance of the connected wallet, read through the wallet's own node. */
1133
+ private readTokenBalance;
1134
+ /** How much of `token` the wallet has already approved Permit2 to move. */
1135
+ private readTokenPermit2Allowance;
1136
+ /**
1137
+ * How much of a landed swap to actually deposit.
1138
+ *
1139
+ * The swap reports what ARRIVED, and the deposit used to be handed that
1140
+ * figure directly. It is the entire arrival, so there is no slack: anything
1141
+ * that spends the target asset between the two fails the deposit outright.
1142
+ *
1143
+ * Something does. A first WETH deposit needs a one-time Permit2 approval,
1144
+ * and that approval is a transaction. A wallet holding no native gas token -
1145
+ * the normal state of someone who just swapped everything INTO WETH - pays
1146
+ * for it out of the WETH. The deposit then asked for ~2e12 wei more than was
1147
+ * left and refused with DEPOSIT_INSUFFICIENT_BALANCE, under a wrapper saying
1148
+ * the funds were safe but unadded (ROUT-242).
1149
+ *
1150
+ * So the approval happens here, before the amount is settled, and the amount
1151
+ * comes from a read taken after it. Two things this deliberately is not:
1152
+ *
1153
+ * - It is not a balance sweep. `received` is a DELTA over the pre-swap
1154
+ * balance, so a wallet already holding WETH reads far above it; depositing
1155
+ * the balance would move funds the user never offered. The arrival is the
1156
+ * ceiling and the balance is only ever allowed to lower it.
1157
+ * - It does not touch USDC, which moves on signed typed data and so spends
1158
+ * nothing of the user's in between, nor a paymaster-configured client,
1159
+ * where the sponsor covers the transfer and Permit2 never applies.
1160
+ */
1161
+ private settleSwapDepositAmount;
1132
1162
  private walletSeesBalance;
1133
1163
  /**
1134
1164
  * Runs a quoted swap. A named seam rather than an inline `executeSwap` call
@@ -1328,6 +1358,12 @@ declare class OwneySDK {
1328
1358
  txHash?: string;
1329
1359
  };
1330
1360
  deposit: OwneyDepositResult | OwneyMultiDepositResult;
1361
+ /**
1362
+ * What was actually deposited, which is not always what the swap
1363
+ * delivered - see `settleSwapDepositAmount`. Callers seeding a headline
1364
+ * want this one; `swap.received` stays what it says it is.
1365
+ */
1366
+ deposited: string;
1331
1367
  }>;
1332
1368
  /**
1333
1369
  * Withdraw from an agent and swap the proceeds into whatever the user wants
package/dist/index.js CHANGED
@@ -263,6 +263,15 @@ var DayFilterMapping = {
263
263
  "7D": 7
264
264
  };
265
265
 
266
+ // src/lib/helpers/asset-aliases.ts
267
+ function assetAliases(asset) {
268
+ const normalised = asset.toLowerCase();
269
+ return normalised === "weth" ? ["weth", "eth"] : [normalised];
270
+ }
271
+ function matchesAsset(symbol, asset) {
272
+ return assetAliases(asset).includes(symbol.toLowerCase());
273
+ }
274
+
266
275
  // src/agents/zyfai/zyfai.mapper.ts
267
276
  var DECIMALS_BY_SYMBOL = {
268
277
  usdc: 6,
@@ -667,9 +676,8 @@ function computeAllocationApy(positions) {
667
676
  function earningsForToken(bucket, chainId, asset) {
668
677
  const tokens = bucket?.[String(chainId)];
669
678
  if (!tokens) return null;
670
- const wanted = asset.toUpperCase();
671
679
  for (const [symbol, value] of Object.entries(tokens)) {
672
- if (symbol.toUpperCase() !== wanted) continue;
680
+ if (!matchesAsset(symbol, asset)) continue;
673
681
  const amount = Number(value);
674
682
  return Number.isFinite(amount) ? amount : null;
675
683
  }
@@ -3512,11 +3520,10 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
3512
3520
  // src/lib/helpers/withdraw-helper.ts
3513
3521
  import { parseUnits } from "viem";
3514
3522
  function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
3515
- const target = asset.toUpperCase();
3516
3523
  return agents.map((agent) => {
3517
3524
  const agentBalance = aggregated[agent.id];
3518
3525
  const tokenBalance = agentBalance?.tokens.find(
3519
- (t) => t.chainId === chainId && t.asset.toUpperCase() === target
3526
+ (t) => t.chainId === chainId && matchesAsset(t.asset, asset)
3520
3527
  );
3521
3528
  if (!tokenBalance) return { agent, balance: 0n };
3522
3529
  return { agent, balance: parseUnits(tokenBalance.amount, decimals) };
@@ -4823,15 +4830,14 @@ var OwneySDK = class {
4823
4830
  async hasExistingBalance(agent, state, chainId, asset, requireReliableRead = false) {
4824
4831
  try {
4825
4832
  const balance = await agent.getBalances(state, chainId);
4826
- const target = asset.toLowerCase();
4827
4833
  const token = balance.tokens.find(
4828
- (t) => t.chainId === chainId && t.asset.toLowerCase() === target
4834
+ (t) => t.chainId === chainId && matchesAsset(t.asset, asset)
4829
4835
  );
4830
4836
  const targetChainName = agent.supportedAssets.find((entry) => entry.chainId === chainId)?.chain?.trim().toUpperCase();
4831
4837
  const position = (balance.positions ?? []).find((p) => {
4832
4838
  const positionChain = p.chain.trim().toUpperCase();
4833
4839
  const matchesChain = positionChain === String(chainId) || targetChainName !== void 0 && positionChain === targetChainName;
4834
- return matchesChain && p.asset.toLowerCase() === target && Number(p.amount) > 0;
4840
+ return matchesChain && matchesAsset(p.asset, asset) && Number(p.amount) > 0;
4835
4841
  });
4836
4842
  return !!token && Number(token.amount) > 0 || !!position;
4837
4843
  } catch (error) {
@@ -5174,6 +5180,64 @@ var OwneySDK = class {
5174
5180
  * the deposit's own check is the authority and refusing to even attempt
5175
5181
  * would strand funds we might have been able to move.
5176
5182
  */
5183
+ /** ERC20 balance of the connected wallet, read through the wallet's own node. */
5184
+ async readTokenBalance(chainId, token) {
5185
+ const state = this.requireState();
5186
+ return readErc20Balance(
5187
+ createPublicClient2({
5188
+ chain: swapChain(chainId),
5189
+ transport: custom(this.requireConnectedProvider())
5190
+ }),
5191
+ token,
5192
+ state.walletAddress
5193
+ );
5194
+ }
5195
+ /** How much of `token` the wallet has already approved Permit2 to move. */
5196
+ async readTokenPermit2Allowance(chainId, token) {
5197
+ const state = this.requireState();
5198
+ return readPermit2Allowance(
5199
+ createPublicClient2({
5200
+ chain: swapChain(chainId),
5201
+ transport: custom(this.requireConnectedProvider())
5202
+ }),
5203
+ token,
5204
+ state.walletAddress
5205
+ );
5206
+ }
5207
+ /**
5208
+ * How much of a landed swap to actually deposit.
5209
+ *
5210
+ * The swap reports what ARRIVED, and the deposit used to be handed that
5211
+ * figure directly. It is the entire arrival, so there is no slack: anything
5212
+ * that spends the target asset between the two fails the deposit outright.
5213
+ *
5214
+ * Something does. A first WETH deposit needs a one-time Permit2 approval,
5215
+ * and that approval is a transaction. A wallet holding no native gas token -
5216
+ * the normal state of someone who just swapped everything INTO WETH - pays
5217
+ * for it out of the WETH. The deposit then asked for ~2e12 wei more than was
5218
+ * left and refused with DEPOSIT_INSUFFICIENT_BALANCE, under a wrapper saying
5219
+ * the funds were safe but unadded (ROUT-242).
5220
+ *
5221
+ * So the approval happens here, before the amount is settled, and the amount
5222
+ * comes from a read taken after it. Two things this deliberately is not:
5223
+ *
5224
+ * - It is not a balance sweep. `received` is a DELTA over the pre-swap
5225
+ * balance, so a wallet already holding WETH reads far above it; depositing
5226
+ * the balance would move funds the user never offered. The arrival is the
5227
+ * ceiling and the balance is only ever allowed to lower it.
5228
+ * - It does not touch USDC, which moves on signed typed data and so spends
5229
+ * nothing of the user's in between, nor a paymaster-configured client,
5230
+ * where the sponsor covers the transfer and Permit2 never applies.
5231
+ */
5232
+ async settleSwapDepositAmount(chainId, token, symbol, received) {
5233
+ if (symbol !== "WETH" || this.paymasterServiceUrl) {
5234
+ return received.toString();
5235
+ }
5236
+ const allowance = await this.readTokenPermit2Allowance(chainId, token);
5237
+ if (allowance < received) await this.approvePermit2();
5238
+ const balance = await this.readTokenBalance(chainId, token);
5239
+ return (balance < received ? balance : received).toString();
5240
+ }
5177
5241
  async walletSeesBalance(chainId, token, amount) {
5178
5242
  const provider = this.requireConnectedProvider();
5179
5243
  const state = this.requireState();
@@ -5402,7 +5466,7 @@ var OwneySDK = class {
5402
5466
  */
5403
5467
  async getWalletBalances(chainIds) {
5404
5468
  const state = this.requireState();
5405
- const chains = chainIds ?? SUPPORTED_CHAIN_IDS;
5469
+ const chains = chainIds ?? SWAP_CHAIN_IDS;
5406
5470
  const failedChainIds = [];
5407
5471
  const results = await Promise.all(
5408
5472
  chains.map(async (chainId) => {
@@ -5508,12 +5572,18 @@ var OwneySDK = class {
5508
5572
  quote.dst.address,
5509
5573
  BigInt(swap.received)
5510
5574
  );
5575
+ const amount = await this.settleSwapDepositAmount(
5576
+ quote.dst.chainId,
5577
+ quote.dst.address,
5578
+ options.to.symbol,
5579
+ BigInt(swap.received)
5580
+ );
5511
5581
  const deposit = await this.deposit({
5512
- amount: swap.received,
5582
+ amount,
5513
5583
  asset: options.to.symbol,
5514
5584
  ...options.agentId ? { agentId: options.agentId } : {}
5515
5585
  });
5516
- return { swap, deposit };
5586
+ return { swap, deposit, deposited: amount };
5517
5587
  } catch (error) {
5518
5588
  const pending = {
5519
5589
  id: `${quote.dst.chainId}-${options.to.symbol}-${Date.now()}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.7.26-beta.6",
3
+ "version": "0.7.26-beta.8",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",