@axonfi/sdk 0.1.2 → 0.2.0
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 +184 -0
- package/dist/index.cjs +49 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +72 -24
- package/dist/index.d.ts +72 -24
- package/dist/index.js +49 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# @axonfi/sdk
|
|
2
|
+
|
|
3
|
+
Treasury and payment infrastructure for autonomous AI agents. Non-custodial vaults, gasless bots, AI verification.
|
|
4
|
+
|
|
5
|
+
## Why Axon
|
|
6
|
+
|
|
7
|
+
Giving bots funded wallets is risky: scattered keys, no spending controls, one compromised key drains everything. Axon flips this model:
|
|
8
|
+
|
|
9
|
+
- **Non-custodial vaults** — each Principal deploys their own vault. Only the owner can withdraw. Enforced on-chain.
|
|
10
|
+
- **Bounded risk** — per-tx caps, daily limits, velocity windows, destination whitelists. Bots can only operate within the policies you set.
|
|
11
|
+
- **AI verification** — 3-agent LLM consensus (safety, behavioral, reasoning) for flagged transactions. 2/3 consensus required.
|
|
12
|
+
- **Gasless bots** — bots sign EIP-712 intents off-chain. Axon's relayer handles gas, simulation, and on-chain execution.
|
|
13
|
+
- **Multi-chain** — Base, Arbitrum, Optimism, Polygon. USDC as base asset.
|
|
14
|
+
|
|
15
|
+
Your agents pay. You stay in control.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @axonfi/sdk viem
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { AxonClient } from '@axonfi/sdk';
|
|
27
|
+
|
|
28
|
+
const axon = new AxonClient({
|
|
29
|
+
vaultAddress: '0x...',
|
|
30
|
+
chainId: 8453, // Base
|
|
31
|
+
botPrivateKey: '0x...',
|
|
32
|
+
relayerUrl: 'https://relay.axonfi.xyz',
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Pay 5 USDC — SDK handles decimals automatically
|
|
36
|
+
const result = await axon.pay({
|
|
37
|
+
to: '0xRecipient',
|
|
38
|
+
token: 'USDC',
|
|
39
|
+
amount: 5,
|
|
40
|
+
memo: 'API call payment',
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
console.log(result.status, result.txHash);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Human-Friendly Amounts
|
|
47
|
+
|
|
48
|
+
The SDK accepts amounts in three formats:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
// Human-readable number — SDK converts using token decimals
|
|
52
|
+
await axon.pay({ to, token: 'USDC', amount: 5.2 });
|
|
53
|
+
|
|
54
|
+
// Human-readable string — recommended for computed values
|
|
55
|
+
await axon.pay({ to, token: 'USDC', amount: '5.2' });
|
|
56
|
+
|
|
57
|
+
// Raw bigint — base units, passed through as-is
|
|
58
|
+
await axon.pay({ to, token: 'USDC', amount: 5_200_000n });
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Token field accepts addresses, `Token` enum values, or symbol strings:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { Token, USDC } from '@axonfi/sdk';
|
|
65
|
+
|
|
66
|
+
token: 'USDC' // bare symbol string
|
|
67
|
+
token: Token.USDC // type-safe enum
|
|
68
|
+
token: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' // raw address
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Encrypted Bot Keys
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { AxonClient, decryptKeystore } from '@axonfi/sdk';
|
|
75
|
+
import fs from 'fs';
|
|
76
|
+
|
|
77
|
+
const keystore = fs.readFileSync('./axon-bot.json', 'utf8');
|
|
78
|
+
const botPrivateKey = await decryptKeystore(keystore, process.env.BOT_PASSPHRASE!);
|
|
79
|
+
|
|
80
|
+
const axon = new AxonClient({
|
|
81
|
+
vaultAddress: '0x...',
|
|
82
|
+
chainId: 8453,
|
|
83
|
+
botPrivateKey,
|
|
84
|
+
relayerUrl: 'https://relay.axonfi.xyz',
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## API
|
|
89
|
+
|
|
90
|
+
### Payments
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// Send a payment
|
|
94
|
+
const result = await axon.pay({
|
|
95
|
+
to: '0xRecipient',
|
|
96
|
+
token: 'USDC', // or Token.USDC, or an address
|
|
97
|
+
amount: 25, // or '25', or 25_000_000n
|
|
98
|
+
memo: 'Invoice #42',
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Poll async payments
|
|
102
|
+
const status = await axon.poll(result.requestId);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### In-Vault Swaps
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
const result = await axon.swap({
|
|
109
|
+
toToken: 'WETH',
|
|
110
|
+
minToAmount: 0.001,
|
|
111
|
+
memo: 'Rebalance to WETH',
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### DeFi Protocol Execution
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
const result = await axon.execute({
|
|
119
|
+
protocol: '0xUniswapRouter',
|
|
120
|
+
callData: '0x...',
|
|
121
|
+
token: 'USDC',
|
|
122
|
+
amount: 100,
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Vault Reads
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
await axon.getBalance('0xUSDC...'); // vault token balance
|
|
130
|
+
await axon.isActive(); // bot registered + active?
|
|
131
|
+
await axon.isPaused(); // vault paused?
|
|
132
|
+
await axon.getVaultInfo(); // owner, operator, version
|
|
133
|
+
await axon.canPayTo('0xRecipient'); // destination allowed?
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Utilities
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import { parseAmount, resolveTokenDecimals, resolveToken, encodeRef } from '@axonfi/sdk';
|
|
140
|
+
|
|
141
|
+
parseAmount(5.2, 'USDC'); // 5_200_000n
|
|
142
|
+
resolveTokenDecimals('WETH'); // 18
|
|
143
|
+
resolveToken('USDC', 8453); // 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
|
|
144
|
+
encodeRef('invoice-042'); // keccak256 → bytes32
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Response Paths
|
|
148
|
+
|
|
149
|
+
Payments resolve through one of three paths:
|
|
150
|
+
|
|
151
|
+
| Path | Trigger | Timing | Response |
|
|
152
|
+
|------|---------|--------|----------|
|
|
153
|
+
| **Fast** | Below all thresholds | ~2s | `status: "approved"`, `txHash` |
|
|
154
|
+
| **AI Scan** | Exceeds AI threshold | ~30s | `status: "approved"` or routes to review |
|
|
155
|
+
| **Human Review** | No AI consensus | Async | `status: "pending_review"`, poll for result |
|
|
156
|
+
|
|
157
|
+
## Security Model
|
|
158
|
+
|
|
159
|
+
- **Principals** (vault owners) control everything: bot whitelist, spending limits, withdrawal. Hardware wallet recommended.
|
|
160
|
+
- **Bots** only sign payment intents. They never hold ETH, never submit transactions, and can be removed instantly.
|
|
161
|
+
- **Relayer** (Axon) can only execute bot-signed intents within configured limits. Cannot withdraw or modify vault config.
|
|
162
|
+
- **If Axon goes offline**, the Principal retains full withdrawal access directly through the on-chain vault contract.
|
|
163
|
+
|
|
164
|
+
## Chains
|
|
165
|
+
|
|
166
|
+
| Chain | ID | Status |
|
|
167
|
+
|-------|----|--------|
|
|
168
|
+
| Base | 8453 | Live |
|
|
169
|
+
| Arbitrum One | 42161 | Live |
|
|
170
|
+
| Optimism | 10 | Live |
|
|
171
|
+
| Polygon PoS | 137 | Live |
|
|
172
|
+
| Base Sepolia | 84532 | Testnet |
|
|
173
|
+
|
|
174
|
+
## Documentation
|
|
175
|
+
|
|
176
|
+
- [Full SDK Reference](https://axonfi.xyz/docs/sdk/typescript/client)
|
|
177
|
+
- [Quickstart Guide](https://axonfi.xyz/docs/getting-started/quickstart)
|
|
178
|
+
- [How It Works](https://axonfi.xyz/docs/getting-started/how-it-works)
|
|
179
|
+
- [Security Model](https://axonfi.xyz/docs/architecture/security-model)
|
|
180
|
+
- [HTTP 402 Payments](https://axonfi.xyz/docs/guides/http-402)
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -3007,6 +3007,42 @@ function resolveToken(token, chainId) {
|
|
|
3007
3007
|
}
|
|
3008
3008
|
return addr;
|
|
3009
3009
|
}
|
|
3010
|
+
function resolveTokenDecimals(token, chainId) {
|
|
3011
|
+
if (typeof token === "string" && token.startsWith("0x")) {
|
|
3012
|
+
const symbol = getTokenSymbolByAddress(token);
|
|
3013
|
+
if (!symbol) {
|
|
3014
|
+
throw new Error(
|
|
3015
|
+
`Unknown token address ${token} \u2014 cannot determine decimals. Use a bigint amount instead, or pass a known token symbol.`
|
|
3016
|
+
);
|
|
3017
|
+
}
|
|
3018
|
+
const entry2 = KNOWN_TOKENS[symbol];
|
|
3019
|
+
return entry2.decimals;
|
|
3020
|
+
}
|
|
3021
|
+
const entry = KNOWN_TOKENS[token];
|
|
3022
|
+
if (!entry) {
|
|
3023
|
+
throw new Error(
|
|
3024
|
+
`Unknown token symbol "${token}" \u2014 cannot determine decimals. Use a bigint amount instead, or use a known symbol (${Object.keys(KNOWN_TOKENS).join(", ")}).`
|
|
3025
|
+
);
|
|
3026
|
+
}
|
|
3027
|
+
return entry.decimals;
|
|
3028
|
+
}
|
|
3029
|
+
function parseAmount(amount, token, chainId) {
|
|
3030
|
+
if (typeof amount === "bigint") {
|
|
3031
|
+
return amount;
|
|
3032
|
+
}
|
|
3033
|
+
const decimals = resolveTokenDecimals(token);
|
|
3034
|
+
const str = typeof amount === "number" ? amount.toString() : amount;
|
|
3035
|
+
const dotIndex = str.indexOf(".");
|
|
3036
|
+
if (dotIndex !== -1) {
|
|
3037
|
+
const decimalPlaces = str.length - dotIndex - 1;
|
|
3038
|
+
if (decimalPlaces > decimals) {
|
|
3039
|
+
throw new Error(
|
|
3040
|
+
`Amount "${str}" has ${decimalPlaces} decimal places, but ${typeof token === "string" && token.startsWith("0x") ? "this token" : token} only supports ${decimals}. Truncate or round your amount.`
|
|
3041
|
+
);
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
return viem.parseUnits(str, decimals);
|
|
3045
|
+
}
|
|
3010
3046
|
|
|
3011
3047
|
// src/utils.ts
|
|
3012
3048
|
function generateUuid() {
|
|
@@ -3284,7 +3320,7 @@ Timestamp: ${timestamp}`;
|
|
|
3284
3320
|
bot: this.botAddress,
|
|
3285
3321
|
to: input.to,
|
|
3286
3322
|
token: resolveToken(input.token, this.chainId),
|
|
3287
|
-
amount: input.amount,
|
|
3323
|
+
amount: parseAmount(input.amount, input.token, this.chainId),
|
|
3288
3324
|
deadline: input.deadline ?? this._defaultDeadline(),
|
|
3289
3325
|
ref: this._resolveRef(input.memo, input.ref)
|
|
3290
3326
|
};
|
|
@@ -3295,7 +3331,7 @@ Timestamp: ${timestamp}`;
|
|
|
3295
3331
|
protocol: input.protocol,
|
|
3296
3332
|
calldataHash: viem.keccak256(input.callData),
|
|
3297
3333
|
token: resolveToken(input.token, this.chainId),
|
|
3298
|
-
amount: input.amount,
|
|
3334
|
+
amount: parseAmount(input.amount, input.token, this.chainId),
|
|
3299
3335
|
deadline: input.deadline ?? this._defaultDeadline(),
|
|
3300
3336
|
ref: this._resolveRef(input.memo, input.ref)
|
|
3301
3337
|
};
|
|
@@ -3304,7 +3340,7 @@ Timestamp: ${timestamp}`;
|
|
|
3304
3340
|
return {
|
|
3305
3341
|
bot: this.botAddress,
|
|
3306
3342
|
toToken: resolveToken(input.toToken, this.chainId),
|
|
3307
|
-
minToAmount: input.minToAmount,
|
|
3343
|
+
minToAmount: parseAmount(input.minToAmount, input.toToken, this.chainId),
|
|
3308
3344
|
deadline: input.deadline ?? this._defaultDeadline(),
|
|
3309
3345
|
ref: this._resolveRef(input.memo, input.ref)
|
|
3310
3346
|
};
|
|
@@ -3336,6 +3372,8 @@ Timestamp: ${timestamp}`;
|
|
|
3336
3372
|
}
|
|
3337
3373
|
async _submitExecute(intent, signature, input) {
|
|
3338
3374
|
const idempotencyKey = input.idempotencyKey ?? generateUuid();
|
|
3375
|
+
const fromToken = input.fromToken !== void 0 ? resolveToken(input.fromToken, this.chainId) : void 0;
|
|
3376
|
+
const maxFromAmount = input.maxFromAmount !== void 0 ? parseAmount(input.maxFromAmount, input.fromToken ?? input.token, this.chainId) : void 0;
|
|
3339
3377
|
const body = {
|
|
3340
3378
|
chainId: this.chainId,
|
|
3341
3379
|
vaultAddress: this.vaultAddress,
|
|
@@ -3351,8 +3389,8 @@ Timestamp: ${timestamp}`;
|
|
|
3351
3389
|
// Protocol calldata
|
|
3352
3390
|
callData: input.callData,
|
|
3353
3391
|
// Optional pre-swap
|
|
3354
|
-
...
|
|
3355
|
-
...
|
|
3392
|
+
...fromToken !== void 0 && { fromToken },
|
|
3393
|
+
...maxFromAmount !== void 0 && { maxFromAmount: maxFromAmount.toString() },
|
|
3356
3394
|
// Off-chain metadata
|
|
3357
3395
|
idempotencyKey,
|
|
3358
3396
|
...input.memo !== void 0 && { memo: input.memo },
|
|
@@ -3363,6 +3401,8 @@ Timestamp: ${timestamp}`;
|
|
|
3363
3401
|
}
|
|
3364
3402
|
async _submitSwap(intent, signature, input) {
|
|
3365
3403
|
const idempotencyKey = input.idempotencyKey ?? generateUuid();
|
|
3404
|
+
const fromToken = input.fromToken !== void 0 ? resolveToken(input.fromToken, this.chainId) : void 0;
|
|
3405
|
+
const maxFromAmount = input.maxFromAmount !== void 0 ? parseAmount(input.maxFromAmount, input.fromToken ?? input.toToken, this.chainId) : void 0;
|
|
3366
3406
|
const body = {
|
|
3367
3407
|
chainId: this.chainId,
|
|
3368
3408
|
vaultAddress: this.vaultAddress,
|
|
@@ -3374,8 +3414,8 @@ Timestamp: ${timestamp}`;
|
|
|
3374
3414
|
ref: intent.ref,
|
|
3375
3415
|
signature,
|
|
3376
3416
|
// Optional source token
|
|
3377
|
-
...
|
|
3378
|
-
...
|
|
3417
|
+
...fromToken !== void 0 && { fromToken },
|
|
3418
|
+
...maxFromAmount !== void 0 && { maxFromAmount: maxFromAmount.toString() },
|
|
3379
3419
|
// Off-chain metadata
|
|
3380
3420
|
idempotencyKey,
|
|
3381
3421
|
...input.memo !== void 0 && { memo: input.memo }
|
|
@@ -3929,7 +3969,9 @@ exports.isDestinationAllowed = isDestinationAllowed;
|
|
|
3929
3969
|
exports.isRebalanceTokenWhitelisted = isRebalanceTokenWhitelisted;
|
|
3930
3970
|
exports.isVaultPaused = isVaultPaused;
|
|
3931
3971
|
exports.operatorMaxDrainPerDay = operatorMaxDrainPerDay;
|
|
3972
|
+
exports.parseAmount = parseAmount;
|
|
3932
3973
|
exports.resolveToken = resolveToken;
|
|
3974
|
+
exports.resolveTokenDecimals = resolveTokenDecimals;
|
|
3933
3975
|
exports.signExecuteIntent = signExecuteIntent;
|
|
3934
3976
|
exports.signPayment = signPayment;
|
|
3935
3977
|
exports.signSwapIntent = signSwapIntent;
|