@axonfi/sdk 0.4.4 → 0.5.1

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
@@ -24,23 +24,108 @@ Your agents pay. You stay in control.
24
24
  npm install @axonfi/sdk
25
25
  ```
26
26
 
27
- ## Prerequisites
27
+ ## Setup
28
28
 
29
- Before using the SDK, you need an Axon vault with a registered bot:
29
+ There are two ways to set up an Axon vault: through the **dashboard** (UI) or entirely through the **SDK** (programmatic). Both produce the same on-chain result.
30
30
 
31
- 1. **Deploy a vault** — Go to [app.axonfi.xyz](https://app.axonfi.xyz), connect your wallet, and deploy a vault on your target chain. The vault is a non-custodial smart contract — only you (the owner) can withdraw funds.
31
+ ### Option A: Dashboard Setup
32
32
 
33
- 2. **Fund the vault** — Send USDC (or any ERC-20) to your vault address. Anyone can deposit directly to the contract.
33
+ 1. Go to [app.axonfi.xyz](https://app.axonfi.xyz), connect your wallet, deploy a vault
34
+ 2. Fund the vault — send USDC, ETH, or any ERC-20 to the vault address
35
+ 3. Register a bot — generate a keypair or bring your own key
36
+ 4. Configure policies — per-tx caps, daily limits, AI threshold
37
+ 5. Give the bot key to your agent
34
38
 
35
- 3. **Register a bot** In the dashboard, go to your vault → Bots → Add Bot. You can either:
36
- - **Generate a new keypair** (recommended) — the dashboard creates a key and downloads an encrypted keystore JSON file. You set the passphrase.
37
- - **Bring your own key** — paste an existing public key if you manage keys externally.
39
+ ### Option B: Full SDK Setup (Programmatic)
38
40
 
39
- 4. **Configure policies** Set per-transaction caps, daily spending limits, velocity windows, and destination whitelists. The bot can only operate within these bounds.
41
+ Everything can be done from code no dashboard needed. An agent can bootstrap its own vault end-to-end.
40
42
 
41
- 5. **Get the bot key** — Your agent needs the bot's private key to sign payment intents. Use the keystore file + passphrase (recommended) or export the raw private key for quick testing.
43
+ ```typescript
44
+ import {
45
+ AxonClient, deployVault, addBot, deposit,
46
+ createAxonPublicClient, createAxonWalletClient,
47
+ NATIVE_ETH, USDC, WINDOW, Chain,
48
+ } from '@axonfi/sdk';
49
+ import { parseUnits } from 'viem';
50
+ import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
51
+
52
+ // ── 1. Owner wallet (funded with ETH for gas) ─────────────────────
53
+ const ownerKey = '0x...'; // or generate: generatePrivateKey()
54
+ const chainId = Chain.BaseSepolia;
55
+ const ownerWallet = createAxonWalletClient(ownerKey, chainId);
56
+ const publicClient = createAxonPublicClient(chainId, 'https://sepolia.base.org');
57
+
58
+ // ── 2. Deploy vault (on-chain tx, ~0.001 ETH gas) ─────────────────
59
+ const vaultAddress = await deployVault(ownerWallet, publicClient);
60
+ console.log('Vault deployed:', vaultAddress);
61
+
62
+ // ── 3. Generate a bot keypair ──────────────────────────────────────
63
+ const botKey = generatePrivateKey();
64
+ const botAddress = privateKeyToAccount(botKey).address;
65
+
66
+ // ── 4. Accept Terms of Service (wallet signature, no gas) ─────────
67
+ const axon = new AxonClient({ vaultAddress, chainId, botPrivateKey: botKey });
68
+ await axon.acceptTos(ownerWallet, ownerWallet.account!.address);
69
+
70
+ // ── 5. Register the bot on the vault (on-chain tx, ~0.0005 ETH gas)
71
+ await addBot(ownerWallet, publicClient, vaultAddress, botAddress, {
72
+ maxPerTxAmount: 100, // $100 hard cap per tx
73
+ maxRebalanceAmount: 0, // no rebalance cap
74
+ spendingLimits: [{
75
+ amount: 1000, // $1,000/day rolling limit
76
+ maxCount: 0, // no tx count limit
77
+ windowSeconds: WINDOW.ONE_DAY,
78
+ }],
79
+ aiTriggerThreshold: 50, // AI scan above $50
80
+ requireAiVerification: false,
81
+ });
82
+
83
+ // ── 6. Deposit funds (on-chain tx, ~0.0005 ETH gas) ───────────────
84
+ // Option A: Deposit ETH (vault accepts native ETH directly)
85
+ await deposit(ownerWallet, publicClient, vaultAddress,
86
+ NATIVE_ETH, parseUnits('0.1', 18));
87
+
88
+ // Option B: Deposit USDC (SDK handles approve + deposit)
89
+ await deposit(ownerWallet, publicClient, vaultAddress,
90
+ USDC[chainId], parseUnits('500', 6));
91
+
92
+ // ── 7. Bot is ready — gasless from here ────────────────────────────
93
+ // Save botKey securely. The bot never needs ETH.
94
+ ```
95
+
96
+ ### What Needs Gas vs. What's Gasless
97
+
98
+ | Step | Who pays gas | Notes |
99
+ |------|-------------|-------|
100
+ | Deploy vault | Owner | ~0.001 ETH. One-time. |
101
+ | Accept ToS | Owner | Wallet signature only (no gas). |
102
+ | Register bot | Owner | ~0.0005 ETH. One per bot. |
103
+ | Configure bot | Owner | ~0.0003 ETH. Only when changing limits. |
104
+ | Deposit ETH | Depositor | Anyone can deposit. ETH sent directly. |
105
+ | Deposit ERC-20 | Depositor | Anyone can deposit. SDK handles approve + deposit. |
106
+ | **Pay** | **Free (relayer)** | **Bot signs EIP-712 intent. Axon pays gas.** |
107
+ | **Execute (DeFi)** | **Free (relayer)** | **Bot signs intent. Axon pays gas.** |
108
+ | **Swap (rebalance)** | **Free (relayer)** | **Bot signs intent. Axon pays gas.** |
109
+ | Pause/unpause | Owner | ~0.0002 ETH. Emergency only. |
110
+ | Withdraw | Owner | ~0.0003 ETH. Owner-only. |
111
+
112
+ **The key insight:** Setup operations (deploy, add bot, deposit) require gas from the owner. Once setup is complete, all bot operations (payments, DeFi, swaps) are gasless — the bot never needs ETH. The relayer pays all execution gas.
113
+
114
+ ### Depositing ETH
115
+
116
+ Vaults accept native ETH directly — no wrapping needed. You can start a vault with only ETH:
117
+
118
+ ```typescript
119
+ // Deploy vault + deposit ETH — no USDC needed
120
+ const vault = await deployVault(ownerWallet, publicClient, factory);
121
+ await addBot(ownerWallet, publicClient, vault, botAddress, config);
122
+ await deposit(ownerWallet, publicClient, vault, NATIVE_ETH, parseUnits('0.5', 18));
123
+
124
+ // Bot can now pay in any token — the relayer swaps ETH → USDC automatically
125
+ await axon.pay({ to: '0x...', token: 'USDC', amount: 10 });
126
+ ```
42
127
 
43
- The vault owner's wallet stays secure the bot key can only sign intents within the policies you configure, and can be revoked instantly from the dashboard.
128
+ When a bot pays in a token the vault doesn't hold directly (e.g., USDC when the vault only has ETH), the relayer automatically routes through a swap. The bot doesn't need to know or care what tokens are in the vault.
44
129
 
45
130
  ## Quick Start
46
131
 
package/dist/index.cjs CHANGED
@@ -2546,8 +2546,6 @@ var AxonVaultFactoryAbi = [
2546
2546
  "inputs": []
2547
2547
  }
2548
2548
  ];
2549
-
2550
- // src/vault.ts
2551
2549
  function getChain(chainId) {
2552
2550
  switch (chainId) {
2553
2551
  case 8453:
@@ -2579,6 +2577,33 @@ function createAxonWalletClient(privateKey, chainId) {
2579
2577
  // signing is local — transport is unused but required by viem
2580
2578
  });
2581
2579
  }
2580
+ var USDC_DECIMALS = 6n;
2581
+ var USDC_UNIT = 10n ** USDC_DECIMALS;
2582
+ function toBotConfigParams(input) {
2583
+ return {
2584
+ maxPerTxAmount: BigInt(Math.round(input.maxPerTxAmount * Number(USDC_UNIT))),
2585
+ maxRebalanceAmount: BigInt(Math.round(input.maxRebalanceAmount * Number(USDC_UNIT))),
2586
+ spendingLimits: input.spendingLimits.map((sl) => ({
2587
+ amount: BigInt(Math.round(sl.amount * Number(USDC_UNIT))),
2588
+ maxCount: BigInt(sl.maxCount),
2589
+ windowSeconds: BigInt(sl.windowSeconds)
2590
+ })),
2591
+ aiTriggerThreshold: BigInt(Math.round(input.aiTriggerThreshold * Number(USDC_UNIT))),
2592
+ requireAiVerification: input.requireAiVerification
2593
+ };
2594
+ }
2595
+ var DEFAULT_RELAYER_URL = "https://relay.axonfi.xyz";
2596
+ async function getFactoryAddress(chainId, relayerUrl) {
2597
+ const base2 = relayerUrl ?? DEFAULT_RELAYER_URL;
2598
+ const resp = await fetch(`${base2}/v1/chains`);
2599
+ if (!resp.ok) throw new Error(`Failed to fetch chain config from relayer [${resp.status}]`);
2600
+ const data = await resp.json();
2601
+ const chain = data.chains?.find((c) => c.chainId === chainId);
2602
+ if (!chain?.factoryAddress) {
2603
+ throw new Error(`No factory address available for chainId ${chainId}. Check the relayer's chain configuration.`);
2604
+ }
2605
+ return chain.factoryAddress;
2606
+ }
2582
2607
  async function getBotConfig(publicClient, vaultAddress, botAddress) {
2583
2608
  const result = await publicClient.readContract({
2584
2609
  address: vaultAddress,
@@ -2727,10 +2752,13 @@ async function isRebalanceTokenWhitelisted(publicClient, vaultAddress, token) {
2727
2752
  args: [token]
2728
2753
  });
2729
2754
  }
2730
- async function deployVault(walletClient, publicClient, factoryAddress) {
2755
+ async function deployVault(walletClient, publicClient, relayerUrl) {
2731
2756
  if (!walletClient.account) {
2732
2757
  throw new Error("walletClient has no account attached");
2733
2758
  }
2759
+ const chainId = walletClient.chain?.id;
2760
+ if (!chainId) throw new Error("walletClient has no chain configured");
2761
+ const factoryAddress = await getFactoryAddress(chainId, relayerUrl);
2734
2762
  const hash = await walletClient.writeContract({
2735
2763
  address: factoryAddress,
2736
2764
  abi: AxonVaultFactoryAbi,
@@ -2751,6 +2779,81 @@ async function deployVault(walletClient, publicClient, factoryAddress) {
2751
2779
  }
2752
2780
  throw new Error("VaultDeployed event not found in transaction receipt");
2753
2781
  }
2782
+ async function addBot(walletClient, publicClient, vaultAddress, botAddress, config) {
2783
+ if (!walletClient.account) {
2784
+ throw new Error("walletClient has no account attached");
2785
+ }
2786
+ const params = toBotConfigParams(config);
2787
+ const hash = await walletClient.writeContract({
2788
+ address: vaultAddress,
2789
+ abi: AxonVaultAbi,
2790
+ functionName: "addBot",
2791
+ args: [botAddress, params],
2792
+ account: walletClient.account,
2793
+ chain: walletClient.chain ?? null
2794
+ });
2795
+ await publicClient.waitForTransactionReceipt({ hash });
2796
+ return hash;
2797
+ }
2798
+ async function updateBotConfig(walletClient, publicClient, vaultAddress, botAddress, config) {
2799
+ if (!walletClient.account) {
2800
+ throw new Error("walletClient has no account attached");
2801
+ }
2802
+ const params = toBotConfigParams(config);
2803
+ const hash = await walletClient.writeContract({
2804
+ address: vaultAddress,
2805
+ abi: AxonVaultAbi,
2806
+ functionName: "updateBotConfig",
2807
+ args: [botAddress, params],
2808
+ account: walletClient.account,
2809
+ chain: walletClient.chain ?? null
2810
+ });
2811
+ await publicClient.waitForTransactionReceipt({ hash });
2812
+ return hash;
2813
+ }
2814
+ async function removeBot(walletClient, publicClient, vaultAddress, botAddress) {
2815
+ if (!walletClient.account) {
2816
+ throw new Error("walletClient has no account attached");
2817
+ }
2818
+ const hash = await walletClient.writeContract({
2819
+ address: vaultAddress,
2820
+ abi: AxonVaultAbi,
2821
+ functionName: "removeBot",
2822
+ args: [botAddress],
2823
+ account: walletClient.account,
2824
+ chain: walletClient.chain ?? null
2825
+ });
2826
+ await publicClient.waitForTransactionReceipt({ hash });
2827
+ return hash;
2828
+ }
2829
+ async function deposit(walletClient, publicClient, vaultAddress, token, amount, ref = "0x0000000000000000000000000000000000000000000000000000000000000000") {
2830
+ if (!walletClient.account) {
2831
+ throw new Error("walletClient has no account attached");
2832
+ }
2833
+ const isEth = token.toLowerCase() === NATIVE_ETH.toLowerCase();
2834
+ if (!isEth) {
2835
+ const approveTx = await walletClient.writeContract({
2836
+ address: token,
2837
+ abi: viem.erc20Abi,
2838
+ functionName: "approve",
2839
+ args: [vaultAddress, amount],
2840
+ account: walletClient.account,
2841
+ chain: walletClient.chain ?? null
2842
+ });
2843
+ await publicClient.waitForTransactionReceipt({ hash: approveTx });
2844
+ }
2845
+ const hash = await walletClient.writeContract({
2846
+ address: vaultAddress,
2847
+ abi: AxonVaultAbi,
2848
+ functionName: "deposit",
2849
+ args: [token, amount, ref],
2850
+ account: walletClient.account,
2851
+ chain: walletClient.chain ?? null,
2852
+ ...isEth ? { value: amount } : {}
2853
+ });
2854
+ await publicClient.waitForTransactionReceipt({ hash });
2855
+ return hash;
2856
+ }
2754
2857
 
2755
2858
  // src/tokens.ts
2756
2859
  var Token = /* @__PURE__ */ ((Token2) => {
@@ -4402,10 +4505,12 @@ exports.USDC_EIP712_DOMAIN = USDC_EIP712_DOMAIN;
4402
4505
  exports.WINDOW = WINDOW;
4403
4506
  exports.WITNESS_TYPE_STRING = WITNESS_TYPE_STRING;
4404
4507
  exports.X402_PROXY_ADDRESS = X402_PROXY_ADDRESS;
4508
+ exports.addBot = addBot;
4405
4509
  exports.createAxonPublicClient = createAxonPublicClient;
4406
4510
  exports.createAxonWalletClient = createAxonWalletClient;
4407
4511
  exports.decryptKeystore = decryptKeystore;
4408
4512
  exports.deployVault = deployVault;
4513
+ exports.deposit = deposit;
4409
4514
  exports.encodeRef = encodeRef;
4410
4515
  exports.encryptKeystore = encryptKeystore;
4411
4516
  exports.extractX402Metadata = extractX402Metadata;
@@ -4432,6 +4537,7 @@ exports.parseChainId = parseChainId;
4432
4537
  exports.parsePaymentRequired = parsePaymentRequired;
4433
4538
  exports.randomNonce = randomNonce;
4434
4539
  exports.randomPermit2Nonce = randomPermit2Nonce;
4540
+ exports.removeBot = removeBot;
4435
4541
  exports.resolveToken = resolveToken;
4436
4542
  exports.resolveTokenDecimals = resolveTokenDecimals;
4437
4543
  exports.signExecuteIntent = signExecuteIntent;
@@ -4439,5 +4545,7 @@ exports.signPayment = signPayment;
4439
4545
  exports.signPermit2WitnessTransfer = signPermit2WitnessTransfer;
4440
4546
  exports.signSwapIntent = signSwapIntent;
4441
4547
  exports.signTransferWithAuthorization = signTransferWithAuthorization;
4548
+ exports.toBotConfigParams = toBotConfigParams;
4549
+ exports.updateBotConfig = updateBotConfig;
4442
4550
  //# sourceMappingURL=index.cjs.map
4443
4551
  //# sourceMappingURL=index.cjs.map