@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 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
- ...input.fromToken !== void 0 && { fromToken: input.fromToken },
3355
- ...input.maxFromAmount !== void 0 && { maxFromAmount: input.maxFromAmount.toString() },
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
- ...input.fromToken !== void 0 && { fromToken: input.fromToken },
3378
- ...input.maxFromAmount !== void 0 && { maxFromAmount: input.maxFromAmount.toString() },
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;