@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 +95 -10
- package/dist/index.cjs +111 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +96 -4
- package/dist/index.d.ts +96 -4
- package/dist/index.js +108 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
##
|
|
27
|
+
## Setup
|
|
28
28
|
|
|
29
|
-
|
|
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
|
-
|
|
31
|
+
### Option A: Dashboard Setup
|
|
32
32
|
|
|
33
|
-
|
|
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
|
-
|
|
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
|
-
|
|
41
|
+
Everything can be done from code — no dashboard needed. An agent can bootstrap its own vault end-to-end.
|
|
40
42
|
|
|
41
|
-
|
|
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
|
-
|
|
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,
|
|
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
|