@t2000/sdk 3.2.0 → 4.0.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 CHANGED
@@ -1,287 +1,197 @@
1
1
  # @t2000/sdk
2
2
 
3
- The complete TypeScript SDK for Agentic Wallets on Sui. Send USDC, earn yield via NAVI, and borrow against collateral — all from a single class. USDC in, USDC out.
3
+ The TypeScript SDK for Agent Wallets on Sui. Send USDC + USDsui gasless, swap via Cetus Aggregator, and pay MPP-protected APIs — all from a single class.
4
4
 
5
- In Audric, this SDK powers **Audric Passport** (wallet, signing), **Audric Finance** (NAVI lending/borrowing builders, Cetus swap), and **Audric Pay** (USDC transfers, payment links), and is wrapped by `@t2000/engine` to implement **Audric Intelligence**'s Agent Harness.
5
+ In [Audric](https://audric.ai), this SDK powers **Audric Passport** (wallet, signing), **Audric Finance** (NAVI lending/borrowing builders, Cetus swap), and **Audric Pay** (USDC transfers, payment links), and is wrapped by [`@t2000/engine`](https://www.npmjs.com/package/@t2000/engine) to implement **Audric Intelligence**'s Agent Harness.
6
6
 
7
- [![npm](https://img.shields.io/npm/v/@t2000/sdk)](https://www.npmjs.com/package/@t2000/sdk)
8
- [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
9
-
10
- **[Website](https://t2000.ai)** · **[GitHub](https://github.com/mission69b/t2000)** · **[CLI](https://www.npmjs.com/package/@t2000/cli)** · **[MPP](https://www.npmjs.com/package/@suimpp/mpp)** · **[MCP](https://www.npmjs.com/package/@t2000/mcp)**
7
+ [![npm @t2000/sdk](https://img.shields.io/npm/v/@t2000/sdk?label=%40t2000%2Fsdk)](https://www.npmjs.com/package/@t2000/sdk)
8
+ [![npm @t2000/cli](https://img.shields.io/npm/v/@t2000/cli?label=%40t2000%2Fcli)](https://www.npmjs.com/package/@t2000/cli)
9
+ [![docs](https://img.shields.io/badge/docs-t2000.ai-00D395)](https://t2000.ai)
10
+ [![license](https://img.shields.io/badge/license-MIT-blue)](https://github.com/mission69b/t2000/blob/main/LICENSE)
11
11
 
12
12
  ## Installation
13
13
 
14
14
  ```bash
15
- npm install @t2000/sdk
16
- # or
17
- pnpm add @t2000/sdk
18
- # or
19
- yarn add @t2000/sdk
15
+ npm install @t2000/sdk # or pnpm add / yarn add
20
16
  ```
21
17
 
22
- **Requirements:** Node.js 18+ · TypeScript 5+ (optional but recommended)
18
+ **Requires** Node.js 18+ · TypeScript 5+ (recommended).
23
19
 
24
20
  ## Quick Start
25
21
 
26
22
  ```typescript
27
23
  import { T2000 } from '@t2000/sdk';
28
24
 
29
- // Create a new Agentic Wallet
30
- const { agent, address } = await T2000.init({ pin: 'my-secret' });
25
+ // Create a new wallet (plain Bech32, 0o600 perms, no PIN)
26
+ const { agent, address } = await T2000.init();
27
+
28
+ // Or load an existing wallet from ~/.t2000/wallet.key
29
+ const agent = await T2000.create();
31
30
 
32
- // Or load an existing one
33
- const agent = await T2000.create({ pin: 'my-secret' });
31
+ // Or from a Bech32 secret in memory (no file)
32
+ const agent = T2000.fromPrivateKey('suiprivkey1…');
34
33
 
35
- // Check balance
34
+ // Inspect
36
35
  const balance = await agent.balance();
37
36
  console.log(`$${balance.available} USDC available`);
38
37
 
39
- // Send USDC recipient can be a 0x address, a SuiNS name, or a saved contact alias
40
- await agent.send({ to: '0x...', amount: 10 });
41
- await agent.send({ to: 'alex.sui', amount: 10 }); // SuiNS — resolved on-chain
42
- // Legacy contact aliases (~/.t2000/contacts.json) still work but emit a deprecation
43
- // warning on first use per process. Sunset target: next major SDK release.
38
+ // Send — asset is REQUIRED; USDC + USDsui are gasless via 0x2::balance::send_funds
39
+ await agent.send({ to: 'alice.sui', amount: 5, asset: 'USDC' });
40
+ await agent.send({ to: '0x8b3e…', amount: 5, asset: 'USDsui' });
44
41
 
45
- // Save (earn yield auto-selects best rate via NAVI)
46
- await agent.save({ amount: 50 });
42
+ // Swap Cetus Aggregator V3 across 20+ Sui DEXs. Requires SUI for gas.
43
+ await agent.swap({ from: 'USDC', to: 'SUI', amount: 100 });
47
44
 
48
- // Borrow USDC against your collateral
49
- await agent.borrow({ amount: 25 });
50
-
51
- // Withdraw from savings (USDC)
52
- await agent.withdraw({ amount: 25 });
45
+ // Pay — MPP-protected API. Gasless USDC; handles HTTP 402 transparently.
46
+ const result = await agent.pay({
47
+ url: 'https://mpp.t2000.ai/openai/v1/chat/completions',
48
+ method: 'POST',
49
+ body: JSON.stringify({ model: 'gpt-4o-mini', messages: […] }),
50
+ maxPrice: 0.10,
51
+ });
53
52
  ```
54
53
 
55
- ## API Reference
54
+ ## Factory Methods
56
55
 
57
- ### `T2000.init(options)` Create a new wallet
56
+ | Static | Returns | Use when |
57
+ |---|---|---|
58
+ | `T2000.init({ keyPath?, name? })` | `{ agent, address }` | Generating a brand-new wallet. Writes a plain Bech32 JSON file to `~/.t2000/wallet.key` (override with `keyPath`). |
59
+ | `T2000.create({ keyPath?, rpcUrl? })` | `T2000` | Loading the existing wallet from disk. Throws `WALLET_NOT_FOUND` if the file is missing, `WALLET_CORRUPT` if it's a v3 PIN-encrypted file or otherwise malformed. |
60
+ | `T2000.fromPrivateKey(secret, { network?, rpcUrl? })` | `T2000` | Synchronous in-memory load from a `suiprivkey1…` Bech32 or hex secret. No filesystem read or write. |
58
61
 
59
- Creates a new Agentic Wallet (generates keypair, encrypts, and saves to disk). Fund the returned address with a small amount of SUI for gas (Mercuryo: https://exchange.mercuryo.io/?widget_id=89960d1a-8db7-49e5-8823-4c5e01c1cea2) plus USDC to transact.
62
+ > **v3 v4.** `T2000.create({ pin })` is gone. The `pin` and `passphrase` fields are accepted (back-compat) but **ignored** — v4 wallets are plain Bech32 JSON with `0o600` perms. If you're upgrading a v3 PIN-encrypted wallet: export the secret from the v3 binary, then `T2000.init` followed by replacing the generated file with one carrying that secret (or just use [`@t2000/cli`](https://www.npmjs.com/package/@t2000/cli)'s `t2 init --import` flow).
60
63
 
61
- ```typescript
62
- const { agent, address } = await T2000.init({
63
- pin: 'my-secret', // Required — encrypts the key
64
- keyPath: '~/.t2000/wallet.key', // Optional — custom key file path
65
- });
66
- ```
64
+ ## Agent Wallet API
67
65
 
68
- ### `T2000.create(options)`Load an existing wallet
66
+ These methods cover the v4 Agent Wallet brand sending USDC, receiving, swapping, paying for MPP APIs. They mirror the [`@t2000/cli`](https://www.npmjs.com/package/@t2000/cli) surface 1:1.
69
67
 
70
- Loads an existing Agentic Wallet from an encrypted key file. Throws `WALLET_NOT_FOUND` if no wallet exists.
68
+ | Method | Returns | Notes |
69
+ |---|---|---|
70
+ | `agent.address()` | `string` | Sui address. |
71
+ | `agent.balance()` | `BalanceResponse` | USDC / USDsui / SUI + gas reserve + total USD. |
72
+ | `agent.history({ limit? })` | `TransactionRecord[]` | Sends / swaps / MPP payments with Suiscan digests. |
73
+ | `agent.send({ to, amount, asset })` | `SendResult` | **`asset` is required** (`'USDC'` / `'USDsui'` / `'SUI'`). USDC + USDsui are gasless via Sui foundation's `0x2::balance::send_funds` sponsor; SUI uses standard gas. `to` resolves in priority order: hex address > SuiNS name (`alice.sui`) > `@audric` handle > saved contact. |
74
+ | `agent.resolveRecipient(input)` | `{ address, suinsName?, contactName? }` | Public resolver — same lookup `send` uses. Handy for dry-run previews. |
75
+ | `agent.swap({ from, to, amount, slippage? })` | `SwapResult` | Cetus Aggregator V3 (20+ DEXs). User-friendly names (`'USDC'`, `'SUI'`, `'CETUS'`, …) or full coin types. Default slippage 1%, max 5%. **Requires SUI for gas** — Cetus is not in the gasless allowlist. |
76
+ | `agent.swapQuote({ from, to, amount, slippage? })` | `SwapQuoteResult` | Preview route + output + price impact (no execution). |
77
+ | `agent.pay(options)` | `PayResult` | MPP-protected paid API. Handles 402 → quote → USDC payment → retry. USDC transfer is gasless. `options.maxPrice` caps spend (default 1 USDC). |
78
+ | `agent.receive({ amount?, currency?, memo?, label? })` | `PaymentRequest` | Builds a Payment Kit `sui:pay?…` URI with a unique nonce. Scannable by any Sui wallet. |
79
+ | `agent.exportKey()` | `string` | Print the Bech32 (`suiprivkey1…`) secret for the underlying keypair. |
80
+
81
+ ### Events
71
82
 
72
83
  ```typescript
73
- const agent = await T2000.create({
74
- pin: 'my-secret', // Required decrypts the key
75
- keyPath: '~/.t2000/wallet.key', // Optional custom key file path
76
- rpcUrl: 'https://...', // Optional custom Sui RPC endpoint
77
- });
84
+ agent.on('balanceChange', (e) => { /* asset, previous, current, cause, tx? */ });
85
+ agent.on('healthWarning', (e) => { /* healthFactor, threshold */ });
86
+ agent.on('healthCritical', (e) => { /* healthFactor < 1.2 */ });
87
+ agent.on('yield', (e) => { /* earned, total, apy, timestamp */ });
88
+ agent.on('error', (e) => { /* T2000Error */ });
78
89
  ```
79
90
 
80
- ### `T2000.fromPrivateKey(key, options?)` — Load from raw key
91
+ ### Exposed Internals
81
92
 
82
- Synchronous factory that creates an agent from a raw private key (bech32 `suiprivkey1...` or hex).
93
+ For integrations (`@suimpp/mpp`, `@t2000/engine`, audric web-v2):
83
94
 
84
95
  ```typescript
85
- const agent = T2000.fromPrivateKey('suiprivkey1q...');
96
+ agent.suiClient; // SuiJsonRpcClient
97
+ agent.signer; // TransactionSigner (works for keypair + zkLogin)
98
+ agent.keypair; // Ed25519Keypair (throws for zkLogin instances)
86
99
  ```
87
100
 
88
- ### Core Methods
89
-
90
- | Method | Description | Returns |
91
- |--------|-------------|---------|
92
- | `agent.address()` | Wallet Sui address | `string` |
93
- | `agent.balance()` | Available USDC + savings + gas reserve | `BalanceResponse` |
94
- | `agent.send({ to, amount, asset? })` | Transfer USDC (or other supported asset) to a 0x address, SuiNS name (`alex.sui`), or saved contact alias. Priority: hex > SuiNS > contact. `SendResult.suinsName` / `.contactName` flag which path resolved. | `SendResult` |
95
- | `agent.resolveRecipient(input)` | Public resolver — same hex / SuiNS / contact priority used by `send()`. Useful for dryRun previews where you need the resolved address before committing. | `{ address, suinsName?, contactName? }` |
96
- | `agent.receive({ amount?, currency?, memo?, label? })` | Generate payment request with Payment Kit URI (`sui:pay?...`), nonce for duplicate prevention | `PaymentRequest` |
97
- | `agent.save({ amount, asset?, protocol? })` | Deposit **USDC or USDsui** to NAVI savings (v0.51.0+). `asset` defaults to `'USDC'`. Auto-selects best rate or specify `protocol`. `amount` can be `'all'`. | `SaveResult` |
98
- | `agent.withdraw({ amount, asset? })` | Withdraw from savings. `amount` can be `'all'`. Optional `asset` (default: USDC; also supports USDsui plus legacy USDe / SUI). | `WithdrawResult` |
99
- | `agent.borrow({ amount, asset? })` | Borrow **USDC or USDsui** against collateral (v0.51.0+). `asset` defaults to `'USDC'`. | `BorrowResult` |
100
- | `agent.repay({ amount, asset? })` | Repay outstanding **USDC or USDsui** debt (v0.51.1+). Pass `asset` to target a specific debt; omit for highest-APY repay. **Symmetry enforced:** USDsui debt is repaid with USDsui coins (and USDC with USDC). `amount` can be `'all'`. | `RepayResult` |
101
- | `agent.swap({ from, to, amount, slippage? })` | Swap tokens via Cetus Aggregator (20+ DEXs). User-friendly names or full coin types. | `SwapResult` |
102
- | `agent.stakeVSui({ amount })` | Stake SUI for vSUI via VOLO liquid staking (min 1 SUI) | `StakeVSuiResult` |
103
- | `agent.unstakeVSui({ amount })` | Unstake vSUI back to SUI. `amount` can be `'all'`. | `UnstakeVSuiResult` |
104
- | `agent.exportKey()` | Export private key (bech32 format) | `string` |
105
-
106
- ### Query Methods
107
-
108
- | Method | Description | Returns |
109
- |--------|-------------|---------|
110
- | `agent.healthFactor()` | Lending health factor | `HealthFactorResult` |
111
- | `agent.earnings()` | Yield earned to date | `EarningsResult` |
112
- | `agent.rates()` | Best save/borrow APYs across protocols | `RatesResult` |
113
- | `agent.allRatesAcrossAssets()` | Per-protocol rate data across assets | `Array<{ protocol, asset, rates }>` |
114
- | `agent.positions()` | All open DeFi positions | `PositionsResult` |
115
- | `agent.fundStatus()` | Complete savings summary | `FundStatusResult` |
116
- | `agent.maxWithdraw()` | Max safe withdrawal amount | `MaxWithdrawResult` |
117
- | `agent.maxBorrow()` | Max safe borrow amount | `MaxBorrowResult` |
118
- | `agent.deposit()` | Wallet address + funding instructions | `DepositInfo` |
119
- | `agent.history({ limit? })` | Transaction history (default: all) | `TransactionRecord[]` |
120
- | `agent.swapQuote({ from, to, amount })` | Preview swap route, output amount, and price impact (no execution) | `SwapQuoteResult` |
121
-
122
- ### Contacts Methods
123
-
124
- | Method | Description | Returns |
125
- |--------|-------------|---------|
126
- | `agent.contacts.add(name, address)` | Save a named contact | `void` |
127
- | `agent.contacts.remove(name)` | Remove a contact | `void` |
128
- | `agent.contacts.list()` | List all saved contacts | `Contact[]` |
129
- | `agent.contacts.get(name)` | Get a contact by name | `Contact` |
130
- | `agent.contacts.resolve(nameOrAddress)` | Resolve name to address (passthrough if already an address) | `string` |
131
-
132
- ### Safeguards (Enforcer)
133
-
134
- | Method | Description | Returns |
135
- |--------|-------------|---------|
136
- | `agent.enforcer.getConfig()` | Get safeguard settings | `SafeguardConfig` |
137
- | `agent.enforcer.set({ maxPerTx?, maxDailySend? })` | Set per-transaction and/or daily send limits | `void` |
138
- | `agent.enforcer.lock()` | Lock agent (freeze all operations) | `void` |
139
- | `agent.enforcer.unlock(pin)` | Unlock agent | `void` |
140
- | `agent.enforcer.check(amount)` | Check if amount is allowed under limits | `void` (throws `SafeguardError` if not) |
141
- | `agent.enforcer.recordUsage(amount)` | Record send for daily limit tracking | `void` |
142
- | `agent.enforcer.isConfigured()` | Whether safeguards are set up | `boolean` |
143
-
144
- **Types:** `SafeguardConfig` — `{ maxPerTx?, maxDailySend?, locked? }` · `SafeguardError` — thrown when limits exceeded or agent locked
145
-
146
- ### Key Management
147
-
148
- ```typescript
149
- import {
150
- generateKeypair,
151
- keypairFromPrivateKey,
152
- exportPrivateKey,
153
- getAddress,
154
- saveKey,
155
- loadKey,
156
- walletExists,
157
- } from '@t2000/sdk';
101
+ ## Programmatic DeFi API (no CLI alias)
158
102
 
159
- // Generate a new keypair
160
- const keypair = generateKeypair();
103
+ These methods power [Audric Finance](https://audric.ai) — save (NAVI lend), borrow (NAVI), repay, withdraw, plus the supporting read tools. **They have no `t2` CLI alias in v4** by design: the CLI is a focused Agent Wallet (send / swap / pay); DeFi flows live in consumer apps and the `@t2000/engine` Agent Harness. They are still load-bearing for `@t2000/engine` 4.x and any consumer app building on the SDK.
161
104
 
162
- // Import from private key (bech32 or hex)
163
- const imported = keypairFromPrivateKey('suiprivkey1...');
105
+ | Method | Notes |
106
+ |---|---|
107
+ | `agent.save({ amount, asset?, protocol? })` | Deposit **USDC or USDsui** to NAVI savings (default `'USDC'`). `amount` can be `'all'`. |
108
+ | `agent.withdraw({ amount, asset?, protocol? })` | Withdraw from savings (default `'USDC'`; also supports USDsui + legacy USDe / SUI positions). `amount` can be `'all'`. |
109
+ | `agent.borrow({ amount, asset?, protocol? })` | Borrow USDC or USDsui against collateral (default `'USDC'`). |
110
+ | `agent.repay({ amount, asset?, protocol? })` | Repay outstanding debt. **Symmetry enforced** — USDsui debt is repaid with USDsui (USDC with USDC). `amount` can be `'all'`. |
111
+ | `agent.claimRewards()` | Claim pending NAVI rewards. |
112
+ | `agent.healthFactor()` | NAVI lending health factor. |
113
+ | `agent.maxWithdraw()` · `agent.maxBorrow()` | Safe limits without breaching health factor. |
114
+ | `agent.positions()` | Open save + borrow positions across protocols. |
115
+ | `agent.rates()` | Best save/borrow APYs across protocols. |
116
+ | `agent.earnings()` | Yield earned to date. |
117
+ | `agent.fundStatus()` | Complete savings summary. |
118
+ | `agent.deposit()` · `agent.fund()` | Wallet address + funding instructions (used by Audric's deposit UI). |
164
119
 
165
- // Export private key (bech32 format)
166
- const privkey = exportPrivateKey(keypair);
167
-
168
- // Get the Sui address
169
- const address = getAddress(keypair);
170
-
171
- // Check if wallet exists on disk
172
- const exists = await walletExists();
173
-
174
- // Save/load encrypted key
175
- await saveKey(keypair, 'my-pin');
176
- const loaded = await loadKey('my-pin');
177
- ```
178
-
179
- ### Events
120
+ ## Utility Exports
180
121
 
181
122
  ```typescript
182
- agent.on('balanceChange', (e) => {
183
- console.log(`${e.cause}: ${e.asset} ${e.previous} → ${e.current}`);
184
- });
123
+ import {
124
+ // Key management
125
+ generateKeypair, keypairFromPrivateKey, exportPrivateKey, getAddress,
126
+ saveKey, loadKey, walletExists,
185
127
 
186
- agent.on('healthWarning', (e) => {
187
- console.log(`Health factor: ${e.healthFactor} (warning)`);
188
- });
128
+ // Token data
129
+ COIN_REGISTRY, TOKEN_MAP, SUI_TYPE, USDC_TYPE,
130
+ isTier1, isTier2, isSupported, getTier,
131
+ getDecimalsForCoinType, resolveSymbol, resolveTokenType,
189
132
 
190
- agent.on('healthCritical', (e) => {
191
- console.log(`Health factor: ${e.healthFactor} (critical — below 1.2)`);
192
- });
133
+ // Asset allowlist
134
+ SUPPORTED_ASSETS, SENDABLE_ASSETS, assertAllowedAsset,
135
+ GASLESS_MIN_STABLE_AMOUNT, GASLESS_STABLE_TYPES,
193
136
 
194
- agent.on('yield', (e) => {
195
- console.log(`Earned: $${e.earned}, total: $${e.total}`);
196
- });
137
+ // Numbers + formatting
138
+ mistToSui, suiToMist, usdcToRaw, rawToUsdc,
139
+ formatUsd, formatSui, truncateAddress, validateAddress,
197
140
 
198
- agent.on('error', (e) => {
199
- console.error(`Error: ${e.code} — ${e.message}`);
200
- });
141
+ // Sui clients
142
+ getSuiClient, getSuiGrpcClient, DEFAULT_GRPC_URL,
143
+
144
+ // Fees (consumer apps only — SDK + CLI are fee-free)
145
+ addFeeTransfer, T2000_OVERLAY_FEE_WALLET,
146
+ } from '@t2000/sdk';
201
147
  ```
202
148
 
203
- ### Utility Functions
149
+ ## Supported Assets
204
150
 
205
- ```typescript
206
- import {
207
- mistToSui,
208
- suiToMist,
209
- usdcToRaw,
210
- rawToUsdc,
211
- formatUsd,
212
- formatSui,
213
- validateAddress,
214
- truncateAddress,
215
- } from '@t2000/sdk';
151
+ Token metadata + tiers live in `COIN_REGISTRY` (`packages/sdk/src/token-registry.ts`). **19 tokens** total.
216
152
 
217
- mistToSui(1_000_000_000n); // 1.0
218
- usdcToRaw(10.50); // 10_500_000n
219
- formatUsd(1234.5); // "$1234.50"
220
- truncateAddress('0xabcdef...1234'); // "0xabcd...1234"
221
- validateAddress('0x...'); // throws if invalid
222
- ```
153
+ - **Tier 1 — financial layer (1):** USDC. Save / borrow / receive / yield, marketplace, MPP.
154
+ - **Tier 2 — swap assets (15):** SUI, wBTC, ETH, GOLD, DEEP, WAL, NS, IKA, CETUS, NAVX, vSUI, haSUI, afSUI, LOFI, MANIFEST. Hold + swap + send only.
155
+ - **Legacy — no tier, display only (3):** USDT, USDe, USDsui. Kept so existing NAVI positions still render accurately.
223
156
 
224
- ### Advanced: Exposed Internals
157
+ > **Strategic exception.** `OPERATION_ASSETS.save` and `OPERATION_ASSETS.borrow` accept **both** USDC and USDsui — USDsui is no-tier in the registry but saveable/borrowable via the allowlist (NAVI runs a separate USDsui pool, often at a different APY than the USDC pool, since v0.51.0). Repay symmetry is enforced: USDsui debt must be repaid with USDsui (USDC debt with USDC).
225
158
 
226
- For integrations (like `@suimpp/mpp`), the agent exposes:
159
+ **`OPERATION_ASSETS`** constrains each write:
227
160
 
228
161
  ```typescript
229
- agent.suiClient; // SuiJsonRpcClient instance
230
- agent.signer; // Ed25519Keypair
231
- ```
162
+ import { OPERATION_ASSETS, assertAllowedAsset } from '@t2000/sdk';
232
163
 
233
- ## Gas
164
+ OPERATION_ASSETS.send; // ['USDC', 'USDsui', 'SUI']
165
+ OPERATION_ASSETS.save; // ['USDC', 'USDsui']
166
+ OPERATION_ASSETS.borrow; // ['USDC', 'USDsui']
234
167
 
235
- Every transaction is self-funded by the agent's wallet. Keep at least ~0.05 SUI on hand. If gas runs out the SDK throws `INSUFFICIENT_GAS` top up via Mercuryo (https://exchange.mercuryo.io/?widget_id=89960d1a-8db7-49e5-8823-4c5e01c1cea2) or any Sui exchange.
236
-
237
- > **Audric web app exception:** Audric web users transact under Enoki gas sponsorship (zkLogin), so `INSUFFICIENT_GAS` is not a user-facing concern there. The SDK itself is sponsorship-agnostic — sponsorship is wired in at the host layer (Audric web), not inside `@t2000/sdk`.
168
+ assertAllowedAsset('send', 'USDY'); // throws — not in the allowlist
169
+ ```
238
170
 
239
- **Architecture:** Each protocol operation (NAVI, send) exposes both `buildXxxTx()` (standalone transaction) and `addXxxToTx()` (composable fragment) functions. Multi-step flows compose multiple protocol calls into a single atomic Payment Intent (a Sui Programmable Transaction Block under the hood). If any step within a Payment Intent fails, the entire transaction reverts — no funds left in intermediate states.
171
+ ## Gasless
240
172
 
241
- ## Configuration
173
+ USDC + USDsui sends and MPP USDC payments are gasless. Build path goes through `SuiGrpcClient` so the SDK's gasless-eligibility resolver detects the `0x2::balance::send_funds` Move call at build time and zeroes out `gasPrice` / `gasBudget` / `gasPayment` automatically. Submission still goes through the JSON-RPC client (hybrid pattern documented at [`docs.sui.io`](https://docs.sui.io/develop/transaction-payment/gasless-stablecoin-transfers)).
242
174
 
243
- | Environment Variable | Description | Default |
244
- |---------------------|-------------|---------|
245
- | `T2000_API_URL` | t2000 API base URL | `https://api.t2000.ai` |
175
+ Other writes (SUI sends, Cetus swaps, NAVI save / borrow / withdraw / repay / claim) require gas. Keep ~0.05 SUI on hand. The SDK throws `INSUFFICIENT_GAS` if you run dry.
246
176
 
247
- Options like `pin`, `keyPath`, and `rpcUrl` are passed directly to `T2000.create()` or `T2000.init()`. The CLI handles env vars like `T2000_PIN` see the [CLI README](https://www.npmjs.com/package/@t2000/cli).
177
+ > **Consumer apps:** sponsored gas via Enoki / zkLogin is the host's responsibility. The SDK is sponsorship-agnostic Audric wires Enoki at the host layer (`audric/apps/web-v2`); the SDK doesn't know or care. See `audric/.cursor/rules/audric-transaction-flow.mdc` in the audric repo.
248
178
 
249
- ## Supported Assets
179
+ ## Fees
250
180
 
251
- Token metadata and **tiers** live in `token-registry.ts` (`COIN_REGISTRY`). **17 tokens** total:
181
+ The SDK + CLI are **fee-free by design** (`@t2000/sdk@1.1.0+`). No t2000 protocol fees on any operation.
252
182
 
253
- - **Tier 1 (saveable / borrowable):** USDC, USDsui save, borrow, send, swap. USDsui is a strategic exception (v0.51.0+) because NAVI runs a separate USDsui pool that often quotes a different APY than USDC. Repay symmetry is enforced (USDsui debt → USDsui repay).
254
- - **Tier 2 (15):** SUI, wBTC, ETH, GOLD, DEEP, WAL, NS, IKA, CETUS, NAVX, vSUI, haSUI, afSUI, LOFI, MANIFEST — send and swap only (not for new save/borrow deposits).
255
- - **Legacy (no tier):** USDT, USDe, USDSUI — display and withdraw of existing positions; still send/swap where applicable.
183
+ Network gas (SUI) and third-party fees (Cetus routing, NAVI lending spread) still apply at on-chain rates.
256
184
 
257
- Six tokens were removed from the registry (FDUSD, AUSD, BUCK, BLUB, SCA, TURBOS). `STABLE_ASSETS = ['USDC']` (the canonical USD unit for balance aggregation), but **`OPERATION_ASSETS.save` and `OPERATION_ASSETS.borrow` accept both `'USDC'` and `'USDsui'`** (v0.51.0+). Use `isAllowedAsset(op, asset)` / `assertAllowedAsset(op, asset)` to validate.
185
+ Consumer apps that want to charge an overlay fee Audric does this on save / borrow / swap call `addFeeTransfer(tx, paymentCoin, FEE_BPS, receiverAddress, amount)` inside the same PTB. The SDK never assumes a t2000 treasury; pass any receiver.
258
186
 
259
- ```typescript
260
- import {
261
- COIN_REGISTRY,
262
- TOKEN_MAP,
263
- isTier1,
264
- isTier2,
265
- isSupported,
266
- getTier,
267
- getDecimalsForCoinType,
268
- resolveSymbol,
269
- resolveTokenType,
270
- SUI_TYPE,
271
- USDC_TYPE,
272
- USDT_TYPE,
273
- IKA_TYPE,
274
- LOFI_TYPE,
275
- MANIFEST_TYPE,
276
- } from '@t2000/sdk';
187
+ ## Configuration
277
188
 
278
- isTier1(USDC_TYPE); // true
279
- isTier2(SUI_TYPE); // true
280
- isSupported(USDT_TYPE); // false (legacy no tier)
281
- getTier(SUI_TYPE); // 2
282
- ```
189
+ | Env var | Effect |
190
+ |---|---|
191
+ | `T2000_RPC_URL` | Custom Sui JSON-RPC endpoint. |
192
+ | `T2000_GRPC_URL` | Custom Sui gRPC endpoint (defaults to `fullnode.mainnet.sui.io`). Used during gasless USDC/USDsui send + pay build paths. |
283
193
 
284
- Swap uses Cetus Aggregator V3. Per-call `overlayFee` is opt-in — the SDK and CLI never charge fees by default. Consumer apps that want to charge an overlay (e.g. Audric) pass `overlayFee: { rate: 10n, receiver: T2000_OVERLAY_FEE_WALLET }` to `findSwapRoute` / `buildSwapTx`. Use `COIN_REGISTRY`, `getDecimalsForCoinType()`, `resolveSymbol()`, and `resolveTokenType()` for token data.
194
+ Per-call options like `keyPath` and `rpcUrl` are passed to `T2000.create()` / `T2000.init()`.
285
195
 
286
196
  ## Error Handling
287
197
 
@@ -289,59 +199,38 @@ Swap uses Cetus Aggregator V3. Per-call `overlayFee` is opt-in — the SDK and C
289
199
  import { T2000Error } from '@t2000/sdk';
290
200
 
291
201
  try {
292
- await agent.send({ to: 'alex.sui', amount: 1000 });
202
+ await agent.send({ to: 'alice.sui', amount: 1000, asset: 'USDC' });
293
203
  } catch (e) {
294
204
  if (e instanceof T2000Error) {
295
- console.log(e.code);
296
- // 'INSUFFICIENT_BALANCE' | 'SUINS_NOT_REGISTERED' | 'CONTACT_NOT_FOUND' | …
297
- console.log(e.message); // Human-readable message
205
+ // e.code + e.message
298
206
  }
299
207
  }
300
208
  ```
301
209
 
302
- Common error codes: `INSUFFICIENT_BALANCE` · `INVALID_ADDRESS` · `INVALID_AMOUNT` · `INVALID_ASSET` · `HEALTH_FACTOR_TOO_LOW` · `NO_COLLATERAL` · `WALLET_NOT_FOUND` · `WALLET_LOCKED` · `WALLET_EXISTS` · `SIMULATION_FAILED` · `TRANSACTION_FAILED` · `PROTOCOL_PAUSED` · `INSUFFICIENT_GAS` · `WITHDRAW_WOULD_LIQUIDATE` · `SWAP_NO_ROUTE` · `SWAP_FAILED`
210
+ Common codes:
303
211
 
304
- ## Protocol Integration
212
+ `WALLET_NOT_FOUND` · `WALLET_CORRUPT` · `INVALID_KEY` · `INSUFFICIENT_BALANCE` · `INSUFFICIENT_GAS` · `INVALID_ADDRESS` · `INVALID_AMOUNT` · `INVALID_ASSET` · `ASSET_NOT_SUPPORTED` · `SUINS_NOT_REGISTERED` · `CONTACT_NOT_FOUND` · `SWAP_NO_ROUTE` · `SWAP_FAILED` · `HEALTH_FACTOR_TOO_LOW` · `NO_COLLATERAL` · `WITHDRAW_WOULD_LIQUIDATE` · `PROTOCOL_PAUSED` · `SIMULATION_FAILED` · `TRANSACTION_FAILED`
305
213
 
306
- t2000 uses an MCP-first integration model: NAVI MCP for reads, thin transaction builders for writes. No protocol SDK dependencies needed.
214
+ ## Architecture
215
+
216
+ t2000 uses an MCP-first model for DeFi reads + thin transaction builders for writes. No protocol SDK dependencies needed in user code.
307
217
 
308
218
  | Protocol | Integration | Used for |
309
- |----------|------------|----------|
310
- | NAVI | MCP (reads) + thin tx builders (writes) | Lending positions, deposits, withdrawals, borrows, rewards |
311
- | Cetus Aggregator V3 | `@cetusprotocol/aggregator-sdk` (isolated) | Multi-DEX swap routing — overlay fee on swaps (`cetus-swap.ts`) |
312
- | VOLO | Thin tx builders (direct Move calls) | Stake SUI vSUI, unstake vSUI SUI |
219
+ |---|---|---|
220
+ | Sui foundation gasless | `0x2::balance::send_funds` Move call (built via `SuiGrpcClient`) | USDC + USDsui transfers, MPP USDC payments |
221
+ | Cetus Aggregator V3 | `@cetusprotocol/aggregator-sdk` (isolated to `protocols/cetus-swap.ts`) | Multi-DEX swap routing |
222
+ | NAVI | NAVI MCP (reads) + thin tx builders (writes) | Save / borrow / withdraw / repay / rewards / positions / rates |
223
+ | MPP | `mppx` + `@suimpp/mpp/client` | Paid API access — 40+ services on `mpp.t2000.ai` |
224
+
225
+ Each NAVI op exposes both `buildXxxTx()` (standalone) and `addXxxToTx()` (composable fragment) so multi-step flows compose atomically as a single Programmable Transaction Block — any step failing reverts the whole bundle.
313
226
 
314
227
  ## Testing
315
228
 
316
229
  ```bash
317
- # Run all SDK unit tests
318
- pnpm --filter @t2000/sdk test
319
-
320
- # Run smoke tests against mainnet RPC (read-only, no transactions)
321
- SMOKE=1 pnpm --filter @t2000/sdk test -- src/__smoke__
230
+ pnpm --filter @t2000/sdk test # unit
231
+ SMOKE=1 pnpm --filter @t2000/sdk test -- src/__smoke__ # read-only mainnet smokes
322
232
  ```
323
233
 
324
- ## Protocol Fees
325
-
326
- > **The SDK and CLI are fee-free by design (as of `@t2000/sdk@1.1.0`, B5 v2 / 2026-04-30).** Direct SDK / CLI calls — `t2000 save`, `t2000 borrow`, `t2000 swap`, plus `T2000.save()` / `T2000.borrow()` / `swapExecute()` from any third-party integrator — never charge a t2000 protocol fee. The CLI is dev-focused tooling and intentionally has no monetization.
327
-
328
- Fees only apply when the **Audric** consumer app calls these primitives. Audric layers a fee transfer step (`addFeeTransfer`) inside the same PTB and routes the USDC to `T2000_OVERLAY_FEE_WALLET`.
329
-
330
- | Operation | Audric fee | SDK / CLI fee | Notes |
331
- |-----------|-----------|--------------|-------|
332
- | Save (deposit) | 0.10% | Free | USDC only; USDsui save is free in Audric too |
333
- | Borrow | 0.05% | Free | USDC only; USDsui borrow is free in Audric too |
334
- | Swap | 0.10% | Free | Audric passes Cetus `overlayFee`. CLI omits it. Cetus Aggregator network fees still apply both ways. |
335
- | Withdraw / Repay / Send / Receive / Stake / Unstake / Pay (MPP) | Free | Free | No surcharge anywhere. |
336
-
337
- How Audric collects fees: `prepare/route.ts` calls `addFeeTransfer(tx, paymentCoin, FEE_BPS, T2000_OVERLAY_FEE_WALLET, amount)` for save/borrow and passes `overlayFee.receiver = T2000_OVERLAY_FEE_WALLET` for swaps. Both flows produce a USDC transfer to the treasury wallet inside the same atomic Payment Intent. The t2000 server-side indexer detects the on-chain USDC inflow and records a `ProtocolFeeLedger` row — no off-chain submission is involved.
338
-
339
- Need to charge an overlay fee in your own consumer app? Import `addFeeTransfer` and `T2000_OVERLAY_FEE_WALLET` from `@t2000/sdk` (or use your own receiver address — the SDK never assumes the t2000 treasury).
340
-
341
- ## MCP Server
342
-
343
- The SDK powers the [`@t2000/mcp`](https://www.npmjs.com/package/@t2000/mcp) server for Claude Desktop, Cursor, and any MCP-compatible AI platform. Run `t2000 mcp` to start.
344
-
345
234
  ## License
346
235
 
347
- MIT — see [LICENSE](https://github.com/mission69b/t2000/blob/main/LICENSE)
236
+ MIT — see [LICENSE](https://github.com/mission69b/t2000/blob/main/LICENSE).