@owney/sdk 0.6.8-beta.1 → 0.6.8-beta.3

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
@@ -1051,7 +1051,14 @@ var ZyfaiAgent = class {
1051
1051
  amount
1052
1052
  );
1053
1053
  const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
1054
- await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
1054
+ try {
1055
+ await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
1056
+ } catch (logError) {
1057
+ console.warn(
1058
+ "[owney-sdk] Deposit landed on-chain but logDeposit failed (non-fatal):",
1059
+ logError
1060
+ );
1061
+ }
1055
1062
  return { txHash, smartWallet: wallet.address, amount };
1056
1063
  }
1057
1064
  const raw = await this.sdk.depositFunds(
@@ -1516,56 +1523,6 @@ async function getSponsorRelayerAddress(input) {
1516
1523
  return parsed.data.relayer;
1517
1524
  }
1518
1525
 
1519
- // src/lib/sponsored-deposit.ts
1520
- var AUTH_WINDOW_SECONDS = 15 * 60;
1521
- function makeSponsoredDepositCallback(deps) {
1522
- const post = deps.httpPost ?? postSponsorTransferAuth;
1523
- return async (smartWallet, chainId, amount) => {
1524
- const cid = chainId;
1525
- const token = deps.tokenAddressByChain[cid];
1526
- if (!token) {
1527
- throw new OwneyError(
1528
- "CHAIN_UNSUPPORTED",
1529
- `No sponsored token configured for chain ${chainId}`
1530
- );
1531
- }
1532
- const pub = deps.getPublicClient(cid);
1533
- const wallet = deps.getWalletClient(cid);
1534
- const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
1535
- const validAfter = 0n;
1536
- const validBefore = BigInt(Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS);
1537
- const nonce = randomAuthNonce();
1538
- const typedData = buildTransferWithAuthorizationTypedData({
1539
- token,
1540
- chainId: cid,
1541
- tokenName,
1542
- tokenVersion,
1543
- message: { from: deps.ownerAddress, to: smartWallet, value: BigInt(amount), validAfter, validBefore, nonce }
1544
- });
1545
- const authSignature = await wallet.signTypedData(
1546
- { account: deps.ownerAddress, ...typedData }
1547
- );
1548
- const result = await post({
1549
- baseUrl: deps.baseUrl,
1550
- apiKey: deps.apiKey,
1551
- body: {
1552
- chainId: cid,
1553
- token,
1554
- from: deps.ownerAddress,
1555
- to: smartWallet,
1556
- value: amount,
1557
- validAfter: validAfter.toString(),
1558
- validBefore: validBefore.toString(),
1559
- nonce,
1560
- authSignature,
1561
- tokenName,
1562
- tokenVersion
1563
- }
1564
- });
1565
- return result.txHash;
1566
- };
1567
- }
1568
-
1569
1526
  // src/lib/permit2.ts
1570
1527
  var import_viem4 = require("viem");
1571
1528
  var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
@@ -1575,20 +1532,37 @@ var ERC20_ALLOWANCE_ABI = [
1575
1532
  type: "function",
1576
1533
  name: "allowance",
1577
1534
  stateMutability: "view",
1578
- inputs: [{ name: "owner", type: "address" }, { name: "spender", type: "address" }],
1535
+ inputs: [
1536
+ { name: "owner", type: "address" },
1537
+ { name: "spender", type: "address" }
1538
+ ],
1579
1539
  outputs: [{ name: "", type: "uint256" }]
1580
1540
  },
1581
1541
  {
1582
1542
  type: "function",
1583
1543
  name: "approve",
1584
1544
  stateMutability: "nonpayable",
1585
- inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }],
1545
+ inputs: [
1546
+ { name: "spender", type: "address" },
1547
+ { name: "amount", type: "uint256" }
1548
+ ],
1586
1549
  outputs: [{ name: "", type: "bool" }]
1550
+ },
1551
+ {
1552
+ type: "function",
1553
+ name: "balanceOf",
1554
+ stateMutability: "view",
1555
+ inputs: [{ name: "account", type: "address" }],
1556
+ outputs: [{ name: "", type: "uint256" }]
1587
1557
  }
1588
1558
  ];
1589
1559
  function buildPermitTransferFromTypedData(input) {
1590
1560
  return {
1591
- domain: { name: "Permit2", chainId: input.chainId, verifyingContract: PERMIT2_ADDRESS },
1561
+ domain: {
1562
+ name: "Permit2",
1563
+ chainId: input.chainId,
1564
+ verifyingContract: PERMIT2_ADDRESS
1565
+ },
1592
1566
  types: {
1593
1567
  PermitTransferFrom: [
1594
1568
  { name: "permitted", type: "TokenPermissions" },
@@ -1618,6 +1592,87 @@ async function readPermit2Allowance(publicClient, token, owner) {
1618
1592
  args: [owner, PERMIT2_ADDRESS]
1619
1593
  });
1620
1594
  }
1595
+ async function readErc20Balance(publicClient, token, owner) {
1596
+ return publicClient.readContract({
1597
+ address: token,
1598
+ abi: ERC20_ALLOWANCE_ABI,
1599
+ functionName: "balanceOf",
1600
+ args: [owner]
1601
+ });
1602
+ }
1603
+
1604
+ // src/lib/sponsored-deposit.ts
1605
+ var AUTH_WINDOW_SECONDS = 15 * 60;
1606
+ function makeSponsoredDepositCallback(deps) {
1607
+ const post = deps.httpPost ?? postSponsorTransferAuth;
1608
+ return async (smartWallet, chainId, amount) => {
1609
+ const cid = chainId;
1610
+ const token = deps.tokenAddressByChain[cid];
1611
+ if (!token) {
1612
+ throw new OwneyError(
1613
+ "CHAIN_UNSUPPORTED",
1614
+ `No sponsored token configured for chain ${chainId}`
1615
+ );
1616
+ }
1617
+ const pub = deps.getPublicClient(cid);
1618
+ const wallet = deps.getWalletClient(cid);
1619
+ try {
1620
+ const balance = await readErc20Balance(pub, token, deps.ownerAddress);
1621
+ if (balance < BigInt(amount)) {
1622
+ throw new OwneyError(
1623
+ "DEPOSIT_INSUFFICIENT_BALANCE",
1624
+ "Insufficient balance for this deposit.",
1625
+ { token, chainId: cid, balance: balance.toString(), amount }
1626
+ );
1627
+ }
1628
+ } catch (err) {
1629
+ if (err instanceof OwneyError) throw err;
1630
+ }
1631
+ const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
1632
+ const validAfter = 0n;
1633
+ const validBefore = BigInt(
1634
+ Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
1635
+ );
1636
+ const nonce = randomAuthNonce();
1637
+ const typedData = buildTransferWithAuthorizationTypedData({
1638
+ token,
1639
+ chainId: cid,
1640
+ tokenName,
1641
+ tokenVersion,
1642
+ message: {
1643
+ from: deps.ownerAddress,
1644
+ to: smartWallet,
1645
+ value: BigInt(amount),
1646
+ validAfter,
1647
+ validBefore,
1648
+ nonce
1649
+ }
1650
+ });
1651
+ const authSignature = await wallet.signTypedData({
1652
+ account: deps.ownerAddress,
1653
+ ...typedData
1654
+ });
1655
+ deps.onApproved?.();
1656
+ const result = await post({
1657
+ baseUrl: deps.baseUrl,
1658
+ apiKey: deps.apiKey,
1659
+ body: {
1660
+ chainId: cid,
1661
+ token,
1662
+ from: deps.ownerAddress,
1663
+ to: smartWallet,
1664
+ value: amount,
1665
+ validAfter: validAfter.toString(),
1666
+ validBefore: validBefore.toString(),
1667
+ nonce,
1668
+ authSignature,
1669
+ tokenName,
1670
+ tokenVersion
1671
+ }
1672
+ });
1673
+ return result.txHash;
1674
+ };
1675
+ }
1621
1676
 
1622
1677
  // src/lib/sponsored-weth-deposit.ts
1623
1678
  var PERMIT_WINDOW_SECONDS = 15 * 60;
@@ -1628,10 +1683,25 @@ function makeSponsoredWethCallback(deps) {
1628
1683
  const cid = chainId;
1629
1684
  const token = deps.tokenAddressByChain[cid];
1630
1685
  if (!token) {
1631
- throw new OwneyError("CHAIN_UNSUPPORTED", `No sponsored WETH configured for chain ${chainId}`);
1686
+ throw new OwneyError(
1687
+ "CHAIN_UNSUPPORTED",
1688
+ `No sponsored WETH configured for chain ${chainId}`
1689
+ );
1632
1690
  }
1633
1691
  const amountWei = BigInt(amount);
1634
1692
  const pub = deps.getPublicClient(cid);
1693
+ try {
1694
+ const balance = await readErc20Balance(pub, token, deps.ownerAddress);
1695
+ if (balance < amountWei) {
1696
+ throw new OwneyError(
1697
+ "DEPOSIT_INSUFFICIENT_BALANCE",
1698
+ "Insufficient WETH balance for this deposit.",
1699
+ { token, chainId: cid, balance: balance.toString(), amount }
1700
+ );
1701
+ }
1702
+ } catch (err) {
1703
+ if (err instanceof OwneyError) throw err;
1704
+ }
1635
1705
  const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
1636
1706
  if (allowance < amountWei) {
1637
1707
  throw new OwneyError(
@@ -1640,9 +1710,15 @@ function makeSponsoredWethCallback(deps) {
1640
1710
  { token, chainId: cid, allowance: allowance.toString(), amount }
1641
1711
  );
1642
1712
  }
1643
- const relayer = await get({ baseUrl: deps.baseUrl, apiKey: deps.apiKey, chainId: cid });
1713
+ const relayer = await get({
1714
+ baseUrl: deps.baseUrl,
1715
+ apiKey: deps.apiKey,
1716
+ chainId: cid
1717
+ });
1644
1718
  const nonce = randomPermit2Nonce();
1645
- const deadline = BigInt(Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS);
1719
+ const deadline = BigInt(
1720
+ Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
1721
+ );
1646
1722
  const typedData = buildPermitTransferFromTypedData({
1647
1723
  chainId: cid,
1648
1724
  message: {
@@ -1653,9 +1729,11 @@ function makeSponsoredWethCallback(deps) {
1653
1729
  }
1654
1730
  });
1655
1731
  const wallet = deps.getWalletClient(cid);
1656
- const signature = await wallet.signTypedData(
1657
- { account: deps.ownerAddress, ...typedData }
1658
- );
1732
+ const signature = await wallet.signTypedData({
1733
+ account: deps.ownerAddress,
1734
+ ...typedData
1735
+ });
1736
+ deps.onApproved?.();
1659
1737
  const result = await post({
1660
1738
  baseUrl: deps.baseUrl,
1661
1739
  apiKey: deps.apiKey,
@@ -1818,14 +1896,16 @@ var OwneySDK = class {
1818
1896
  * provider with viem `custom(provider)` to read token meta and sign the
1819
1897
  * `TransferWithAuthorization`, then POSTs to the sponsor API.
1820
1898
  */
1821
- getDefaultSponsoredCallback() {
1822
- if (this.cachedSponsoredCallback) return this.cachedSponsoredCallback;
1899
+ getDefaultSponsoredCallback(onApproved) {
1900
+ if (!onApproved && this.cachedSponsoredCallback)
1901
+ return this.cachedSponsoredCallback;
1823
1902
  const provider = this.requireConnectedProvider();
1824
1903
  const owner = this.state.walletAddress;
1825
- this.cachedSponsoredCallback = makeSponsoredDepositCallback({
1904
+ const callback = makeSponsoredDepositCallback({
1826
1905
  apiKey: this.apiKey,
1827
1906
  baseUrl: this.routingApiBaseUrl,
1828
1907
  ownerAddress: owner,
1908
+ onApproved,
1829
1909
  tokenAddressByChain: SPONSORED_USDC_BY_CHAIN,
1830
1910
  // Casts work around viem's chain-narrowed Client vs the generic
1831
1911
  // PublicClient/WalletClient param types — structurally identical at
@@ -1840,7 +1920,8 @@ var OwneySDK = class {
1840
1920
  transport: (0, import_viem5.custom)(provider)
1841
1921
  })
1842
1922
  });
1843
- return this.cachedSponsoredCallback;
1923
+ if (!onApproved) this.cachedSponsoredCallback = callback;
1924
+ return callback;
1844
1925
  }
1845
1926
  /**
1846
1927
  * Lazily builds (and caches) the default Permit2 sponsored WETH deposit
@@ -1848,15 +1929,16 @@ var OwneySDK = class {
1848
1929
  * deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
1849
1930
  * `PermitTransferFrom` instead of an EIP-3009 authorization.
1850
1931
  */
1851
- getDefaultWethSponsoredCallback() {
1852
- if (this.cachedWethSponsoredCallback)
1932
+ getDefaultWethSponsoredCallback(onApproved) {
1933
+ if (!onApproved && this.cachedWethSponsoredCallback)
1853
1934
  return this.cachedWethSponsoredCallback;
1854
1935
  const provider = this.requireConnectedProvider();
1855
1936
  const owner = this.state.walletAddress;
1856
- this.cachedWethSponsoredCallback = makeSponsoredWethCallback({
1937
+ const callback = makeSponsoredWethCallback({
1857
1938
  apiKey: this.apiKey,
1858
1939
  baseUrl: this.routingApiBaseUrl,
1859
1940
  ownerAddress: owner,
1941
+ onApproved,
1860
1942
  tokenAddressByChain: SPONSORED_WETH_BY_CHAIN,
1861
1943
  // Casts work around viem's chain-narrowed Client vs the generic
1862
1944
  // PublicClient/WalletClient param types — structurally identical at
@@ -1871,7 +1953,8 @@ var OwneySDK = class {
1871
1953
  transport: (0, import_viem5.custom)(provider)
1872
1954
  })
1873
1955
  });
1874
- return this.cachedWethSponsoredCallback;
1956
+ if (!onApproved) this.cachedWethSponsoredCallback = callback;
1957
+ return callback;
1875
1958
  }
1876
1959
  getAgent(agentId) {
1877
1960
  const agent = this.agents.get(agentId);
@@ -2017,13 +2100,13 @@ var OwneySDK = class {
2017
2100
  * @returns {OwneyDepositResult} for a single agent, or {OwneyMultiDepositResult} with per-agent results
2018
2101
  */
2019
2102
  async deposit(options) {
2020
- const { amount, asset, depositCallback, agentId } = options;
2103
+ const { amount, asset, depositCallback, agentId, onApproved } = options;
2021
2104
  if (BigInt(amount) === 0n) {
2022
2105
  return { agentResults: {} };
2023
2106
  }
2024
2107
  const state = this.requireState();
2025
2108
  const chainId = this.requireChainId();
2026
- const effectiveCallback = depositCallback ?? (asset === "WETH" ? this.getDefaultWethSponsoredCallback() : this.getDefaultSponsoredCallback());
2109
+ const effectiveCallback = depositCallback ?? (asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved) : this.getDefaultSponsoredCallback(onApproved));
2027
2110
  if (agentId) {
2028
2111
  if (this.isAgentDisabled(agentId)) {
2029
2112
  throw new OwneyError(
package/dist/index.d.cts CHANGED
@@ -98,6 +98,7 @@ type DepositOptions = {
98
98
  asset: Asset;
99
99
  depositCallback?: DepositCallback;
100
100
  agentId?: AgentId;
101
+ onApproved?: () => void;
101
102
  };
102
103
 
103
104
  interface OwneyDepositResult {
@@ -524,7 +525,7 @@ declare class OwneySDK {
524
525
  getAllocationApy({ agentId, }?: AllocationApyOptions): Promise<OwneyAllocationApy>;
525
526
  }
526
527
 
527
- type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "AGENT_DISABLED" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "SPONSOR_REQUEST_FAILED" | "PERMIT2_APPROVAL_REQUIRED" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
528
+ type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "AGENT_DISABLED" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "DEPOSIT_INSUFFICIENT_BALANCE" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "SPONSOR_REQUEST_FAILED" | "PERMIT2_APPROVAL_REQUIRED" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
528
529
  declare class OwneyError extends Error {
529
530
  readonly code: OwneyErrorCode;
530
531
  readonly details?: Record<string, unknown>;
package/dist/index.d.ts CHANGED
@@ -98,6 +98,7 @@ type DepositOptions = {
98
98
  asset: Asset;
99
99
  depositCallback?: DepositCallback;
100
100
  agentId?: AgentId;
101
+ onApproved?: () => void;
101
102
  };
102
103
 
103
104
  interface OwneyDepositResult {
@@ -524,7 +525,7 @@ declare class OwneySDK {
524
525
  getAllocationApy({ agentId, }?: AllocationApyOptions): Promise<OwneyAllocationApy>;
525
526
  }
526
527
 
527
- type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "AGENT_DISABLED" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "SPONSOR_REQUEST_FAILED" | "PERMIT2_APPROVAL_REQUIRED" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
528
+ type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "AGENT_DISABLED" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "DEPOSIT_INSUFFICIENT_BALANCE" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "SPONSOR_REQUEST_FAILED" | "PERMIT2_APPROVAL_REQUIRED" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
528
529
  declare class OwneyError extends Error {
529
530
  readonly code: OwneyErrorCode;
530
531
  readonly details?: Record<string, unknown>;
package/dist/index.js CHANGED
@@ -1019,7 +1019,14 @@ var ZyfaiAgent = class {
1019
1019
  amount
1020
1020
  );
1021
1021
  const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
1022
- await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
1022
+ try {
1023
+ await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
1024
+ } catch (logError) {
1025
+ console.warn(
1026
+ "[owney-sdk] Deposit landed on-chain but logDeposit failed (non-fatal):",
1027
+ logError
1028
+ );
1029
+ }
1023
1030
  return { txHash, smartWallet: wallet.address, amount };
1024
1031
  }
1025
1032
  const raw = await this.sdk.depositFunds(
@@ -1488,56 +1495,6 @@ async function getSponsorRelayerAddress(input) {
1488
1495
  return parsed.data.relayer;
1489
1496
  }
1490
1497
 
1491
- // src/lib/sponsored-deposit.ts
1492
- var AUTH_WINDOW_SECONDS = 15 * 60;
1493
- function makeSponsoredDepositCallback(deps) {
1494
- const post = deps.httpPost ?? postSponsorTransferAuth;
1495
- return async (smartWallet, chainId, amount) => {
1496
- const cid = chainId;
1497
- const token = deps.tokenAddressByChain[cid];
1498
- if (!token) {
1499
- throw new OwneyError(
1500
- "CHAIN_UNSUPPORTED",
1501
- `No sponsored token configured for chain ${chainId}`
1502
- );
1503
- }
1504
- const pub = deps.getPublicClient(cid);
1505
- const wallet = deps.getWalletClient(cid);
1506
- const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
1507
- const validAfter = 0n;
1508
- const validBefore = BigInt(Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS);
1509
- const nonce = randomAuthNonce();
1510
- const typedData = buildTransferWithAuthorizationTypedData({
1511
- token,
1512
- chainId: cid,
1513
- tokenName,
1514
- tokenVersion,
1515
- message: { from: deps.ownerAddress, to: smartWallet, value: BigInt(amount), validAfter, validBefore, nonce }
1516
- });
1517
- const authSignature = await wallet.signTypedData(
1518
- { account: deps.ownerAddress, ...typedData }
1519
- );
1520
- const result = await post({
1521
- baseUrl: deps.baseUrl,
1522
- apiKey: deps.apiKey,
1523
- body: {
1524
- chainId: cid,
1525
- token,
1526
- from: deps.ownerAddress,
1527
- to: smartWallet,
1528
- value: amount,
1529
- validAfter: validAfter.toString(),
1530
- validBefore: validBefore.toString(),
1531
- nonce,
1532
- authSignature,
1533
- tokenName,
1534
- tokenVersion
1535
- }
1536
- });
1537
- return result.txHash;
1538
- };
1539
- }
1540
-
1541
1498
  // src/lib/permit2.ts
1542
1499
  import { bytesToHex as bytesToHex2 } from "viem";
1543
1500
  var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
@@ -1547,20 +1504,37 @@ var ERC20_ALLOWANCE_ABI = [
1547
1504
  type: "function",
1548
1505
  name: "allowance",
1549
1506
  stateMutability: "view",
1550
- inputs: [{ name: "owner", type: "address" }, { name: "spender", type: "address" }],
1507
+ inputs: [
1508
+ { name: "owner", type: "address" },
1509
+ { name: "spender", type: "address" }
1510
+ ],
1551
1511
  outputs: [{ name: "", type: "uint256" }]
1552
1512
  },
1553
1513
  {
1554
1514
  type: "function",
1555
1515
  name: "approve",
1556
1516
  stateMutability: "nonpayable",
1557
- inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }],
1517
+ inputs: [
1518
+ { name: "spender", type: "address" },
1519
+ { name: "amount", type: "uint256" }
1520
+ ],
1558
1521
  outputs: [{ name: "", type: "bool" }]
1522
+ },
1523
+ {
1524
+ type: "function",
1525
+ name: "balanceOf",
1526
+ stateMutability: "view",
1527
+ inputs: [{ name: "account", type: "address" }],
1528
+ outputs: [{ name: "", type: "uint256" }]
1559
1529
  }
1560
1530
  ];
1561
1531
  function buildPermitTransferFromTypedData(input) {
1562
1532
  return {
1563
- domain: { name: "Permit2", chainId: input.chainId, verifyingContract: PERMIT2_ADDRESS },
1533
+ domain: {
1534
+ name: "Permit2",
1535
+ chainId: input.chainId,
1536
+ verifyingContract: PERMIT2_ADDRESS
1537
+ },
1564
1538
  types: {
1565
1539
  PermitTransferFrom: [
1566
1540
  { name: "permitted", type: "TokenPermissions" },
@@ -1590,6 +1564,87 @@ async function readPermit2Allowance(publicClient, token, owner) {
1590
1564
  args: [owner, PERMIT2_ADDRESS]
1591
1565
  });
1592
1566
  }
1567
+ async function readErc20Balance(publicClient, token, owner) {
1568
+ return publicClient.readContract({
1569
+ address: token,
1570
+ abi: ERC20_ALLOWANCE_ABI,
1571
+ functionName: "balanceOf",
1572
+ args: [owner]
1573
+ });
1574
+ }
1575
+
1576
+ // src/lib/sponsored-deposit.ts
1577
+ var AUTH_WINDOW_SECONDS = 15 * 60;
1578
+ function makeSponsoredDepositCallback(deps) {
1579
+ const post = deps.httpPost ?? postSponsorTransferAuth;
1580
+ return async (smartWallet, chainId, amount) => {
1581
+ const cid = chainId;
1582
+ const token = deps.tokenAddressByChain[cid];
1583
+ if (!token) {
1584
+ throw new OwneyError(
1585
+ "CHAIN_UNSUPPORTED",
1586
+ `No sponsored token configured for chain ${chainId}`
1587
+ );
1588
+ }
1589
+ const pub = deps.getPublicClient(cid);
1590
+ const wallet = deps.getWalletClient(cid);
1591
+ try {
1592
+ const balance = await readErc20Balance(pub, token, deps.ownerAddress);
1593
+ if (balance < BigInt(amount)) {
1594
+ throw new OwneyError(
1595
+ "DEPOSIT_INSUFFICIENT_BALANCE",
1596
+ "Insufficient balance for this deposit.",
1597
+ { token, chainId: cid, balance: balance.toString(), amount }
1598
+ );
1599
+ }
1600
+ } catch (err) {
1601
+ if (err instanceof OwneyError) throw err;
1602
+ }
1603
+ const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
1604
+ const validAfter = 0n;
1605
+ const validBefore = BigInt(
1606
+ Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
1607
+ );
1608
+ const nonce = randomAuthNonce();
1609
+ const typedData = buildTransferWithAuthorizationTypedData({
1610
+ token,
1611
+ chainId: cid,
1612
+ tokenName,
1613
+ tokenVersion,
1614
+ message: {
1615
+ from: deps.ownerAddress,
1616
+ to: smartWallet,
1617
+ value: BigInt(amount),
1618
+ validAfter,
1619
+ validBefore,
1620
+ nonce
1621
+ }
1622
+ });
1623
+ const authSignature = await wallet.signTypedData({
1624
+ account: deps.ownerAddress,
1625
+ ...typedData
1626
+ });
1627
+ deps.onApproved?.();
1628
+ const result = await post({
1629
+ baseUrl: deps.baseUrl,
1630
+ apiKey: deps.apiKey,
1631
+ body: {
1632
+ chainId: cid,
1633
+ token,
1634
+ from: deps.ownerAddress,
1635
+ to: smartWallet,
1636
+ value: amount,
1637
+ validAfter: validAfter.toString(),
1638
+ validBefore: validBefore.toString(),
1639
+ nonce,
1640
+ authSignature,
1641
+ tokenName,
1642
+ tokenVersion
1643
+ }
1644
+ });
1645
+ return result.txHash;
1646
+ };
1647
+ }
1593
1648
 
1594
1649
  // src/lib/sponsored-weth-deposit.ts
1595
1650
  var PERMIT_WINDOW_SECONDS = 15 * 60;
@@ -1600,10 +1655,25 @@ function makeSponsoredWethCallback(deps) {
1600
1655
  const cid = chainId;
1601
1656
  const token = deps.tokenAddressByChain[cid];
1602
1657
  if (!token) {
1603
- throw new OwneyError("CHAIN_UNSUPPORTED", `No sponsored WETH configured for chain ${chainId}`);
1658
+ throw new OwneyError(
1659
+ "CHAIN_UNSUPPORTED",
1660
+ `No sponsored WETH configured for chain ${chainId}`
1661
+ );
1604
1662
  }
1605
1663
  const amountWei = BigInt(amount);
1606
1664
  const pub = deps.getPublicClient(cid);
1665
+ try {
1666
+ const balance = await readErc20Balance(pub, token, deps.ownerAddress);
1667
+ if (balance < amountWei) {
1668
+ throw new OwneyError(
1669
+ "DEPOSIT_INSUFFICIENT_BALANCE",
1670
+ "Insufficient WETH balance for this deposit.",
1671
+ { token, chainId: cid, balance: balance.toString(), amount }
1672
+ );
1673
+ }
1674
+ } catch (err) {
1675
+ if (err instanceof OwneyError) throw err;
1676
+ }
1607
1677
  const allowance = await readPermit2Allowance(pub, token, deps.ownerAddress);
1608
1678
  if (allowance < amountWei) {
1609
1679
  throw new OwneyError(
@@ -1612,9 +1682,15 @@ function makeSponsoredWethCallback(deps) {
1612
1682
  { token, chainId: cid, allowance: allowance.toString(), amount }
1613
1683
  );
1614
1684
  }
1615
- const relayer = await get({ baseUrl: deps.baseUrl, apiKey: deps.apiKey, chainId: cid });
1685
+ const relayer = await get({
1686
+ baseUrl: deps.baseUrl,
1687
+ apiKey: deps.apiKey,
1688
+ chainId: cid
1689
+ });
1616
1690
  const nonce = randomPermit2Nonce();
1617
- const deadline = BigInt(Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS);
1691
+ const deadline = BigInt(
1692
+ Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
1693
+ );
1618
1694
  const typedData = buildPermitTransferFromTypedData({
1619
1695
  chainId: cid,
1620
1696
  message: {
@@ -1625,9 +1701,11 @@ function makeSponsoredWethCallback(deps) {
1625
1701
  }
1626
1702
  });
1627
1703
  const wallet = deps.getWalletClient(cid);
1628
- const signature = await wallet.signTypedData(
1629
- { account: deps.ownerAddress, ...typedData }
1630
- );
1704
+ const signature = await wallet.signTypedData({
1705
+ account: deps.ownerAddress,
1706
+ ...typedData
1707
+ });
1708
+ deps.onApproved?.();
1631
1709
  const result = await post({
1632
1710
  baseUrl: deps.baseUrl,
1633
1711
  apiKey: deps.apiKey,
@@ -1790,14 +1868,16 @@ var OwneySDK = class {
1790
1868
  * provider with viem `custom(provider)` to read token meta and sign the
1791
1869
  * `TransferWithAuthorization`, then POSTs to the sponsor API.
1792
1870
  */
1793
- getDefaultSponsoredCallback() {
1794
- if (this.cachedSponsoredCallback) return this.cachedSponsoredCallback;
1871
+ getDefaultSponsoredCallback(onApproved) {
1872
+ if (!onApproved && this.cachedSponsoredCallback)
1873
+ return this.cachedSponsoredCallback;
1795
1874
  const provider = this.requireConnectedProvider();
1796
1875
  const owner = this.state.walletAddress;
1797
- this.cachedSponsoredCallback = makeSponsoredDepositCallback({
1876
+ const callback = makeSponsoredDepositCallback({
1798
1877
  apiKey: this.apiKey,
1799
1878
  baseUrl: this.routingApiBaseUrl,
1800
1879
  ownerAddress: owner,
1880
+ onApproved,
1801
1881
  tokenAddressByChain: SPONSORED_USDC_BY_CHAIN,
1802
1882
  // Casts work around viem's chain-narrowed Client vs the generic
1803
1883
  // PublicClient/WalletClient param types — structurally identical at
@@ -1812,7 +1892,8 @@ var OwneySDK = class {
1812
1892
  transport: custom(provider)
1813
1893
  })
1814
1894
  });
1815
- return this.cachedSponsoredCallback;
1895
+ if (!onApproved) this.cachedSponsoredCallback = callback;
1896
+ return callback;
1816
1897
  }
1817
1898
  /**
1818
1899
  * Lazily builds (and caches) the default Permit2 sponsored WETH deposit
@@ -1820,15 +1901,16 @@ var OwneySDK = class {
1820
1901
  * deposit. Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
1821
1902
  * `PermitTransferFrom` instead of an EIP-3009 authorization.
1822
1903
  */
1823
- getDefaultWethSponsoredCallback() {
1824
- if (this.cachedWethSponsoredCallback)
1904
+ getDefaultWethSponsoredCallback(onApproved) {
1905
+ if (!onApproved && this.cachedWethSponsoredCallback)
1825
1906
  return this.cachedWethSponsoredCallback;
1826
1907
  const provider = this.requireConnectedProvider();
1827
1908
  const owner = this.state.walletAddress;
1828
- this.cachedWethSponsoredCallback = makeSponsoredWethCallback({
1909
+ const callback = makeSponsoredWethCallback({
1829
1910
  apiKey: this.apiKey,
1830
1911
  baseUrl: this.routingApiBaseUrl,
1831
1912
  ownerAddress: owner,
1913
+ onApproved,
1832
1914
  tokenAddressByChain: SPONSORED_WETH_BY_CHAIN,
1833
1915
  // Casts work around viem's chain-narrowed Client vs the generic
1834
1916
  // PublicClient/WalletClient param types — structurally identical at
@@ -1843,7 +1925,8 @@ var OwneySDK = class {
1843
1925
  transport: custom(provider)
1844
1926
  })
1845
1927
  });
1846
- return this.cachedWethSponsoredCallback;
1928
+ if (!onApproved) this.cachedWethSponsoredCallback = callback;
1929
+ return callback;
1847
1930
  }
1848
1931
  getAgent(agentId) {
1849
1932
  const agent = this.agents.get(agentId);
@@ -1989,13 +2072,13 @@ var OwneySDK = class {
1989
2072
  * @returns {OwneyDepositResult} for a single agent, or {OwneyMultiDepositResult} with per-agent results
1990
2073
  */
1991
2074
  async deposit(options) {
1992
- const { amount, asset, depositCallback, agentId } = options;
2075
+ const { amount, asset, depositCallback, agentId, onApproved } = options;
1993
2076
  if (BigInt(amount) === 0n) {
1994
2077
  return { agentResults: {} };
1995
2078
  }
1996
2079
  const state = this.requireState();
1997
2080
  const chainId = this.requireChainId();
1998
- const effectiveCallback = depositCallback ?? (asset === "WETH" ? this.getDefaultWethSponsoredCallback() : this.getDefaultSponsoredCallback());
2081
+ const effectiveCallback = depositCallback ?? (asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved) : this.getDefaultSponsoredCallback(onApproved));
1999
2082
  if (agentId) {
2000
2083
  if (this.isAgentDisabled(agentId)) {
2001
2084
  throw new OwneyError(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.6.8-beta.1",
3
+ "version": "0.6.8-beta.3",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",