@owney/sdk 0.7.28-beta.7 → 0.7.28

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/README.md CHANGED
@@ -134,6 +134,13 @@ Deposit funds into a specific agent, or split equally across all agents if `agen
134
134
  | `options.depositCallback` | `DepositCallback` | Callback that performs the token transfer and returns a tx hash |
135
135
  | `options.agentId` | `AgentId` (optional) | Target agent. Omit to split equally across all agents. |
136
136
 
137
+ Zyfai's native transfer path uses `sendDeposit()`. It returns after the token
138
+ transfer confirms and the deposit is registered, while balance credit may still
139
+ be queued. Balance reads reflect subsequent credit and allocation; the deposit
140
+ call does not wait for `waitForDepositCredit()`.
141
+ A successful deposit result confirms the transfer; it does not guarantee that
142
+ the funds are already credited or earning yield.
143
+
137
144
  Returns: `OwneyDepositResult` (single agent) or `OwneyMultiDepositResult` (all agents)
138
145
 
139
146
  ```typescript
package/dist/index.cjs CHANGED
@@ -351,6 +351,9 @@ var NAME_TO_CHAIN_ID = Object.entries(
351
351
  acc[name.toLowerCase()] = Number(id);
352
352
  return acc;
353
353
  }, {});
354
+ NAME_TO_CHAIN_ID.mainnet = 1;
355
+ NAME_TO_CHAIN_ID["ethereum mainnet"] = 1;
356
+ NAME_TO_CHAIN_ID["arbitrum one"] = 42161;
354
357
  function resolveChainId(chain) {
355
358
  const asNum = Number(chain);
356
359
  if (Number.isFinite(asNum) && asNum > 0) return asNum;
@@ -1441,7 +1444,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
1441
1444
  * chain/account switch fires the provider's `accountsChanged` →
1442
1445
  * `disconnectAccount` (nulls walletClient + authenticatedUserId) while our
1443
1446
  * own `connectAuth` guard still believes this chain is connected and
1444
- * early-returns. Without this, authenticated calls (depositFunds,
1447
+ * early-returns. Without this, authenticated calls (sendDeposit,
1445
1448
  * withdrawFunds) fail with "No account connected".
1446
1449
  */
1447
1450
  async ensureConnected(state, chainId) {
@@ -2924,9 +2927,24 @@ async function wrapNative(deps, options) {
2924
2927
  message: error instanceof Error ? error.message : String(error)
2925
2928
  });
2926
2929
  }
2930
+ let approvalPending = true;
2931
+ if (options.spender && deps.readAllowance) {
2932
+ try {
2933
+ const allowance = await deps.readAllowance(wrapper, options.spender);
2934
+ approvalPending = allowance < requested;
2935
+ } catch (error) {
2936
+ debugLog(
2937
+ "owney-sdk",
2938
+ "wrap: allowance read failed, assuming an approve",
2939
+ {
2940
+ message: error instanceof Error ? error.message : String(error)
2941
+ }
2942
+ );
2943
+ }
2944
+ }
2927
2945
  const measured = await deps.getMaxFeePerGas();
2928
2946
  const gasPrice = measured > MIN_WALLET_GAS_PRICE ? measured : MIN_WALLET_GAS_PRICE;
2929
- const reserve = (wrapGas + APPROVE_GAS) * gasPrice * GAS_SAFETY;
2947
+ const reserve = (wrapGas + (approvalPending ? APPROVE_GAS : 0n)) * gasPrice * GAS_SAFETY;
2930
2948
  const spendable = balance > reserve ? balance - reserve : 0n;
2931
2949
  const wrapped = requested < spendable ? requested : spendable;
2932
2950
  debugLog("owney-sdk", "wrap: sizing", {
@@ -2934,6 +2952,7 @@ async function wrapNative(deps, options) {
2934
2952
  requested: requested.toString(),
2935
2953
  measuredGasPrice: measured.toString(),
2936
2954
  reservedAtGasPrice: gasPrice.toString(),
2955
+ approvalPending,
2937
2956
  reserve: reserve.toString(),
2938
2957
  wrapped: wrapped.toString()
2939
2958
  });
@@ -3483,7 +3502,14 @@ async function executeSwap(deps, options) {
3483
3502
  try {
3484
3503
  result = quote.rail === "classic" ? await runClassic(deps, options) : quote.rail === "fusion" ? await runSameChainFusion(deps, options, walletAddress) : await runFusion(deps, options, walletAddress);
3485
3504
  } catch (error) {
3486
- if (!stoppedWatching(error)) throw error;
3505
+ if (!stoppedWatching(error)) {
3506
+ if (quote.rail !== "fusion") throw error;
3507
+ const now = await deps.readTargetBalance().catch(() => before);
3508
+ if (now <= before) throw error;
3509
+ debugLog("owney-sdk", "swap: rail said no but the funds are here", {
3510
+ message: error instanceof Error ? error.message : String(error)
3511
+ });
3512
+ }
3487
3513
  railError = error;
3488
3514
  debugLog("owney-sdk", "swap: rail failed, checking whether funds landed", {
3489
3515
  message: error instanceof Error ? error.message : String(error)
@@ -3572,7 +3598,7 @@ async function runSameChainFusion(deps, options, walletAddress) {
3572
3598
  const isNativeSource = quote.src.address.toLowerCase().startsWith("0xeeee");
3573
3599
  const amount = isNativeSource ? BigInt(quote.src.amount) : await affordableAmount(deps, BigInt(quote.src.amount));
3574
3600
  const permit = await authoriseSpend(deps, options, amount, isNativeSource);
3575
- onStage?.("quoting");
3601
+ onStage?.("building");
3576
3602
  debugLog("owney-sdk", "swap: building same-chain fusion order");
3577
3603
  const built = await deps.api.buildOrder({
3578
3604
  from: {
@@ -3654,7 +3680,7 @@ async function runFusion(deps, options, walletAddress) {
3654
3680
  const amount = await affordableAmount(deps, BigInt(quote.src.amount));
3655
3681
  const permit = await authoriseSpend(deps, options, amount, false);
3656
3682
  const { secrets, secretHashes } = mintSecrets(quote.secretsCount ?? 1);
3657
- onStage?.("quoting");
3683
+ onStage?.("building");
3658
3684
  debugLog("owney-sdk", "swap: building fusion order", {
3659
3685
  secrets: secretHashes.length
3660
3686
  });
@@ -5562,13 +5588,25 @@ var OwneySDK = class {
5562
5588
  }
5563
5589
  return hash;
5564
5590
  },
5565
- ensureChain: (id) => this.ensureSwapChain(id)
5591
+ ensureChain: (id) => this.ensureSwapChain(id),
5592
+ // Read through the same public client as every other sizing input, so
5593
+ // a wallet sitting on the wrong chain cannot answer for this one.
5594
+ readAllowance: (token, spender) => publicClient.readContract({
5595
+ address: token,
5596
+ abi: ERC20_ALLOWANCE_ABI,
5597
+ functionName: "allowance",
5598
+ args: [walletAddress, spender]
5599
+ })
5566
5600
  },
5567
5601
  {
5568
5602
  chainId,
5569
5603
  wrapper: wrapper.address,
5570
5604
  nativeSymbol: quote.src.symbol,
5571
5605
  requested: BigInt(quote.src.amount),
5606
+ // Lets the reserve skip the approve's gas when one is already in
5607
+ // place. Absent on a quote that names no spender, which keeps the
5608
+ // full reserve (ROUT-242, comment 16234).
5609
+ ...quote.spender ? { spender: quote.spender } : {},
5572
5610
  onSubmitted: () => onStage?.("wrapped")
5573
5611
  }
5574
5612
  );
package/dist/index.d.cts CHANGED
@@ -220,7 +220,7 @@ type SwapOrderStatus = "pending" | "executed" | "expired" | "cancelled" | "refun
220
220
  * of the flow and the one place a user would otherwise think nothing is
221
221
  * happening.
222
222
  */
223
- type SwapStage = "withdrawing" | "withdrawn" | "quoting" | "wrapping" | "wrapped" | "approving" | "signing" | "swapping" | "swapped" | "depositing" | "refunding" | "refunded";
223
+ type SwapStage = "withdrawing" | "withdrawn" | "quoting" | "wrapping" | "wrapped" | "approving" | "building" | "signing" | "swapping" | "swapped" | "depositing" | "refunding" | "refunded";
224
224
  /**
225
225
  * Which way the funds travel, which decides what each side may be.
226
226
  *
package/dist/index.d.ts CHANGED
@@ -220,7 +220,7 @@ type SwapOrderStatus = "pending" | "executed" | "expired" | "cancelled" | "refun
220
220
  * of the flow and the one place a user would otherwise think nothing is
221
221
  * happening.
222
222
  */
223
- type SwapStage = "withdrawing" | "withdrawn" | "quoting" | "wrapping" | "wrapped" | "approving" | "signing" | "swapping" | "swapped" | "depositing" | "refunding" | "refunded";
223
+ type SwapStage = "withdrawing" | "withdrawn" | "quoting" | "wrapping" | "wrapped" | "approving" | "building" | "signing" | "swapping" | "swapped" | "depositing" | "refunding" | "refunded";
224
224
  /**
225
225
  * Which way the funds travel, which decides what each side may be.
226
226
  *
package/dist/index.js CHANGED
@@ -311,6 +311,9 @@ var NAME_TO_CHAIN_ID = Object.entries(
311
311
  acc[name.toLowerCase()] = Number(id);
312
312
  return acc;
313
313
  }, {});
314
+ NAME_TO_CHAIN_ID.mainnet = 1;
315
+ NAME_TO_CHAIN_ID["ethereum mainnet"] = 1;
316
+ NAME_TO_CHAIN_ID["arbitrum one"] = 42161;
314
317
  function resolveChainId(chain) {
315
318
  const asNum = Number(chain);
316
319
  if (Number.isFinite(asNum) && asNum > 0) return asNum;
@@ -1401,7 +1404,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
1401
1404
  * chain/account switch fires the provider's `accountsChanged` →
1402
1405
  * `disconnectAccount` (nulls walletClient + authenticatedUserId) while our
1403
1406
  * own `connectAuth` guard still believes this chain is connected and
1404
- * early-returns. Without this, authenticated calls (depositFunds,
1407
+ * early-returns. Without this, authenticated calls (sendDeposit,
1405
1408
  * withdrawFunds) fail with "No account connected".
1406
1409
  */
1407
1410
  async ensureConnected(state, chainId) {
@@ -2884,9 +2887,24 @@ async function wrapNative(deps, options) {
2884
2887
  message: error instanceof Error ? error.message : String(error)
2885
2888
  });
2886
2889
  }
2890
+ let approvalPending = true;
2891
+ if (options.spender && deps.readAllowance) {
2892
+ try {
2893
+ const allowance = await deps.readAllowance(wrapper, options.spender);
2894
+ approvalPending = allowance < requested;
2895
+ } catch (error) {
2896
+ debugLog(
2897
+ "owney-sdk",
2898
+ "wrap: allowance read failed, assuming an approve",
2899
+ {
2900
+ message: error instanceof Error ? error.message : String(error)
2901
+ }
2902
+ );
2903
+ }
2904
+ }
2887
2905
  const measured = await deps.getMaxFeePerGas();
2888
2906
  const gasPrice = measured > MIN_WALLET_GAS_PRICE ? measured : MIN_WALLET_GAS_PRICE;
2889
- const reserve = (wrapGas + APPROVE_GAS) * gasPrice * GAS_SAFETY;
2907
+ const reserve = (wrapGas + (approvalPending ? APPROVE_GAS : 0n)) * gasPrice * GAS_SAFETY;
2890
2908
  const spendable = balance > reserve ? balance - reserve : 0n;
2891
2909
  const wrapped = requested < spendable ? requested : spendable;
2892
2910
  debugLog("owney-sdk", "wrap: sizing", {
@@ -2894,6 +2912,7 @@ async function wrapNative(deps, options) {
2894
2912
  requested: requested.toString(),
2895
2913
  measuredGasPrice: measured.toString(),
2896
2914
  reservedAtGasPrice: gasPrice.toString(),
2915
+ approvalPending,
2897
2916
  reserve: reserve.toString(),
2898
2917
  wrapped: wrapped.toString()
2899
2918
  });
@@ -3453,7 +3472,14 @@ async function executeSwap(deps, options) {
3453
3472
  try {
3454
3473
  result = quote.rail === "classic" ? await runClassic(deps, options) : quote.rail === "fusion" ? await runSameChainFusion(deps, options, walletAddress) : await runFusion(deps, options, walletAddress);
3455
3474
  } catch (error) {
3456
- if (!stoppedWatching(error)) throw error;
3475
+ if (!stoppedWatching(error)) {
3476
+ if (quote.rail !== "fusion") throw error;
3477
+ const now = await deps.readTargetBalance().catch(() => before);
3478
+ if (now <= before) throw error;
3479
+ debugLog("owney-sdk", "swap: rail said no but the funds are here", {
3480
+ message: error instanceof Error ? error.message : String(error)
3481
+ });
3482
+ }
3457
3483
  railError = error;
3458
3484
  debugLog("owney-sdk", "swap: rail failed, checking whether funds landed", {
3459
3485
  message: error instanceof Error ? error.message : String(error)
@@ -3542,7 +3568,7 @@ async function runSameChainFusion(deps, options, walletAddress) {
3542
3568
  const isNativeSource = quote.src.address.toLowerCase().startsWith("0xeeee");
3543
3569
  const amount = isNativeSource ? BigInt(quote.src.amount) : await affordableAmount(deps, BigInt(quote.src.amount));
3544
3570
  const permit = await authoriseSpend(deps, options, amount, isNativeSource);
3545
- onStage?.("quoting");
3571
+ onStage?.("building");
3546
3572
  debugLog("owney-sdk", "swap: building same-chain fusion order");
3547
3573
  const built = await deps.api.buildOrder({
3548
3574
  from: {
@@ -3624,7 +3650,7 @@ async function runFusion(deps, options, walletAddress) {
3624
3650
  const amount = await affordableAmount(deps, BigInt(quote.src.amount));
3625
3651
  const permit = await authoriseSpend(deps, options, amount, false);
3626
3652
  const { secrets, secretHashes } = mintSecrets(quote.secretsCount ?? 1);
3627
- onStage?.("quoting");
3653
+ onStage?.("building");
3628
3654
  debugLog("owney-sdk", "swap: building fusion order", {
3629
3655
  secrets: secretHashes.length
3630
3656
  });
@@ -5543,13 +5569,25 @@ var OwneySDK = class {
5543
5569
  }
5544
5570
  return hash;
5545
5571
  },
5546
- ensureChain: (id) => this.ensureSwapChain(id)
5572
+ ensureChain: (id) => this.ensureSwapChain(id),
5573
+ // Read through the same public client as every other sizing input, so
5574
+ // a wallet sitting on the wrong chain cannot answer for this one.
5575
+ readAllowance: (token, spender) => publicClient.readContract({
5576
+ address: token,
5577
+ abi: ERC20_ALLOWANCE_ABI,
5578
+ functionName: "allowance",
5579
+ args: [walletAddress, spender]
5580
+ })
5547
5581
  },
5548
5582
  {
5549
5583
  chainId,
5550
5584
  wrapper: wrapper.address,
5551
5585
  nativeSymbol: quote.src.symbol,
5552
5586
  requested: BigInt(quote.src.amount),
5587
+ // Lets the reserve skip the approve's gas when one is already in
5588
+ // place. Absent on a quote that names no spender, which keeps the
5589
+ // full reserve (ROUT-242, comment 16234).
5590
+ ...quote.spender ? { spender: quote.spender } : {},
5553
5591
  onSubmitted: () => onStage?.("wrapped")
5554
5592
  }
5555
5593
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.7.28-beta.7",
3
+ "version": "0.7.28",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",