@t2000/sdk 0.6.0 → 0.7.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 +6 -6
- package/dist/adapters/index.cjs +127 -27
- package/dist/adapters/index.cjs.map +1 -1
- package/dist/adapters/index.d.cts +2 -1
- package/dist/adapters/index.d.ts +2 -1
- package/dist/adapters/index.js +123 -28
- package/dist/adapters/index.js.map +1 -1
- package/dist/{index-DMDq8uxe.d.cts → index-rT0oHn8M.d.cts} +52 -1
- package/dist/{index-DMDq8uxe.d.ts → index-rT0oHn8M.d.ts} +52 -1
- package/dist/index.cjs +107 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -16
- package/dist/index.d.ts +3 -16
- package/dist/index.js +103 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @t2000/sdk
|
|
2
2
|
|
|
3
|
-
The complete TypeScript SDK for AI agent bank accounts on Sui. Send USDC, earn yield via NAVI
|
|
3
|
+
The complete TypeScript SDK for AI agent bank accounts on Sui. Send USDC, earn yield via NAVI + Suilend, swap on Cetus DEX, borrow against collateral — all from a single class.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@t2000/sdk)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -37,7 +37,7 @@ console.log(`$${balance.available} USDC available`);
|
|
|
37
37
|
// Send USDC
|
|
38
38
|
await agent.send({ to: '0x...', amount: 10 });
|
|
39
39
|
|
|
40
|
-
// Save (earn yield
|
|
40
|
+
// Save (earn yield — auto-selects best rate across NAVI + Suilend)
|
|
41
41
|
await agent.save({ amount: 50, asset: 'USDC' });
|
|
42
42
|
|
|
43
43
|
// Swap USDC → SUI (via Cetus DEX)
|
|
@@ -89,7 +89,7 @@ const agent = T2000.fromPrivateKey('suiprivkey1q...');
|
|
|
89
89
|
| `agent.address()` | Wallet Sui address | `string` |
|
|
90
90
|
| `agent.balance()` | Available USDC + savings + gas reserve | `BalanceResponse` |
|
|
91
91
|
| `agent.send({ to, amount, asset? })` | Transfer USDC to any Sui address | `SendResult` |
|
|
92
|
-
| `agent.save({ amount, asset })` | Deposit USDC to
|
|
92
|
+
| `agent.save({ amount, asset, protocol? })` | Deposit USDC to savings (earn APY). Auto-selects best rate or specify `protocol`. `amount` can be `'all'`. | `SaveResult` |
|
|
93
93
|
| `agent.withdraw({ amount, asset })` | Withdraw USDC from savings. `amount` can be `'all'`. | `WithdrawResult` |
|
|
94
94
|
| `agent.swap({ from, to, amount, maxSlippage? })` | Swap via Cetus CLMM DEX. `maxSlippage` in % (default: 3). | `SwapResult` |
|
|
95
95
|
| `agent.swapQuote({ from, to, amount })` | Get swap quote without executing | `SwapQuote` |
|
|
@@ -103,7 +103,7 @@ const agent = T2000.fromPrivateKey('suiprivkey1q...');
|
|
|
103
103
|
|--------|-------------|---------|
|
|
104
104
|
| `agent.healthFactor()` | Lending health factor | `HealthFactorResult` |
|
|
105
105
|
| `agent.earnings()` | Yield earned to date | `EarningsResult` |
|
|
106
|
-
| `agent.rates()` |
|
|
106
|
+
| `agent.rates()` | Best save/borrow APYs across protocols | `RatesResult` |
|
|
107
107
|
| `agent.positions()` | All open DeFi positions | `PositionsResult` |
|
|
108
108
|
| `agent.fundStatus()` | Complete savings summary | `FundStatusResult` |
|
|
109
109
|
| `agent.maxWithdraw()` | Max safe withdrawal amount | `MaxWithdrawResult` |
|
|
@@ -227,7 +227,7 @@ Every operation (send, save, borrow, repay, withdraw, swap) routes through a 3-s
|
|
|
227
227
|
|
|
228
228
|
Every transaction result includes a `gasMethod` field (`'self-funded'` | `'auto-topup'` | `'sponsored'`) indicating which strategy was used.
|
|
229
229
|
|
|
230
|
-
**Architecture:** Each protocol operation (NAVI, Cetus, send) exposes a `buildXxxTx()` function that returns a `Transaction` without executing it. `executeWithGas()` then handles execution with the fallback chain. This separation ensures gas management is consistent across all operations.
|
|
230
|
+
**Architecture:** Each protocol operation (NAVI, Suilend, Cetus, send) exposes a `buildXxxTx()` function that returns a `Transaction` without executing it. `executeWithGas()` then handles execution with the fallback chain. This separation ensures gas management is consistent across all operations.
|
|
231
231
|
|
|
232
232
|
## Configuration
|
|
233
233
|
|
|
@@ -264,7 +264,7 @@ Common error codes: `INSUFFICIENT_BALANCE` · `INVALID_ADDRESS` · `INVALID_AMOU
|
|
|
264
264
|
## Testing
|
|
265
265
|
|
|
266
266
|
```bash
|
|
267
|
-
# Run all SDK unit tests (
|
|
267
|
+
# Run all SDK unit tests (262 tests)
|
|
268
268
|
pnpm --filter @t2000/sdk test
|
|
269
269
|
```
|
|
270
270
|
|
package/dist/adapters/index.cjs
CHANGED
|
@@ -5,6 +5,28 @@ var bcs = require('@mysten/sui/bcs');
|
|
|
5
5
|
var aggregatorSdk = require('@cetusprotocol/aggregator-sdk');
|
|
6
6
|
var utils = require('@mysten/sui/utils');
|
|
7
7
|
|
|
8
|
+
// src/errors.ts
|
|
9
|
+
var T2000Error = class extends Error {
|
|
10
|
+
code;
|
|
11
|
+
data;
|
|
12
|
+
retryable;
|
|
13
|
+
constructor(code, message, data, retryable = false) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "T2000Error";
|
|
16
|
+
this.code = code;
|
|
17
|
+
this.data = data;
|
|
18
|
+
this.retryable = retryable;
|
|
19
|
+
}
|
|
20
|
+
toJSON() {
|
|
21
|
+
return {
|
|
22
|
+
error: this.code,
|
|
23
|
+
message: this.message,
|
|
24
|
+
...this.data && { data: this.data },
|
|
25
|
+
retryable: this.retryable
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
8
30
|
// src/adapters/registry.ts
|
|
9
31
|
var ProtocolRegistry = class {
|
|
10
32
|
lending = /* @__PURE__ */ new Map();
|
|
@@ -27,7 +49,7 @@ var ProtocolRegistry = class {
|
|
|
27
49
|
}
|
|
28
50
|
}
|
|
29
51
|
if (candidates.length === 0) {
|
|
30
|
-
throw new
|
|
52
|
+
throw new T2000Error("ASSET_NOT_SUPPORTED", `No lending adapter supports saving ${asset}`);
|
|
31
53
|
}
|
|
32
54
|
candidates.sort((a, b) => b.rate.saveApy - a.rate.saveApy);
|
|
33
55
|
return candidates[0];
|
|
@@ -45,7 +67,7 @@ var ProtocolRegistry = class {
|
|
|
45
67
|
}
|
|
46
68
|
}
|
|
47
69
|
if (candidates.length === 0) {
|
|
48
|
-
throw new
|
|
70
|
+
throw new T2000Error("ASSET_NOT_SUPPORTED", `No lending adapter supports borrowing ${asset}`);
|
|
49
71
|
}
|
|
50
72
|
candidates.sort((a, b) => a.rate.borrowApy - b.rate.borrowApy);
|
|
51
73
|
return candidates[0];
|
|
@@ -62,7 +84,7 @@ var ProtocolRegistry = class {
|
|
|
62
84
|
}
|
|
63
85
|
}
|
|
64
86
|
if (candidates.length === 0) {
|
|
65
|
-
throw new
|
|
87
|
+
throw new T2000Error("ASSET_NOT_SUPPORTED", `No swap adapter supports ${from} \u2192 ${to}`);
|
|
66
88
|
}
|
|
67
89
|
candidates.sort((a, b) => b.quote.expectedOutput - a.quote.expectedOutput);
|
|
68
90
|
return candidates[0];
|
|
@@ -128,28 +150,9 @@ var T2000_CONFIG_ID = process.env.T2000_CONFIG_ID ?? "0x408add9aa9322f93cfd87523
|
|
|
128
150
|
var T2000_TREASURY_ID = process.env.T2000_TREASURY_ID ?? "0x3bb501b8300125dca59019247941a42af6b292a150ce3cfcce9449456be2ec91";
|
|
129
151
|
process.env.T2000_API_URL ?? "https://api.t2000.ai";
|
|
130
152
|
var CETUS_USDC_SUI_POOL = "0x51e883ba7c0b566a26cbc8a94cd33eb0abd418a77cc1e60ad22fd9b1f29cd2ab";
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
code;
|
|
135
|
-
data;
|
|
136
|
-
retryable;
|
|
137
|
-
constructor(code, message, data, retryable = false) {
|
|
138
|
-
super(message);
|
|
139
|
-
this.name = "T2000Error";
|
|
140
|
-
this.code = code;
|
|
141
|
-
this.data = data;
|
|
142
|
-
this.retryable = retryable;
|
|
143
|
-
}
|
|
144
|
-
toJSON() {
|
|
145
|
-
return {
|
|
146
|
-
error: this.code,
|
|
147
|
-
message: this.message,
|
|
148
|
-
...this.data && { data: this.data },
|
|
149
|
-
retryable: this.retryable
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
+
var CETUS_PACKAGE = "0x1eabed72c53feb3805120a081dc15963c204dc8d091542592abaf7a35689b2fb";
|
|
154
|
+
var SENTINEL = {
|
|
155
|
+
PACKAGE: "0x88b83f36dafcd5f6dcdcf1d2cb5889b03f61264ab3cee9cae35db7aa940a21b7"};
|
|
153
156
|
|
|
154
157
|
// src/utils/format.ts
|
|
155
158
|
function usdcToRaw(amount) {
|
|
@@ -252,6 +255,24 @@ async function getUsdcPool() {
|
|
|
252
255
|
if (!usdc) throw new T2000Error("PROTOCOL_UNAVAILABLE", "USDC pool not found on NAVI");
|
|
253
256
|
return usdc;
|
|
254
257
|
}
|
|
258
|
+
function addOracleUpdate(tx, config, pool) {
|
|
259
|
+
const feed = config.oracle.feeds?.find((f2) => f2.assetId === pool.id);
|
|
260
|
+
if (!feed) {
|
|
261
|
+
throw new T2000Error("PROTOCOL_UNAVAILABLE", `Oracle feed not found for asset ${pool.token?.symbol ?? pool.id}`);
|
|
262
|
+
}
|
|
263
|
+
tx.moveCall({
|
|
264
|
+
target: `${config.oracle.packageId}::oracle_pro::update_single_price_v2`,
|
|
265
|
+
arguments: [
|
|
266
|
+
tx.object(CLOCK),
|
|
267
|
+
tx.object(config.oracle.oracleConfig),
|
|
268
|
+
tx.object(config.oracle.priceOracle),
|
|
269
|
+
tx.object(config.oracle.supraOracleHolder),
|
|
270
|
+
tx.object(feed.pythPriceInfoObject),
|
|
271
|
+
tx.object(config.oracle.switchboardAggregator),
|
|
272
|
+
tx.pure.address(feed.feedId)
|
|
273
|
+
]
|
|
274
|
+
});
|
|
275
|
+
}
|
|
255
276
|
function rateToApy(rawRate) {
|
|
256
277
|
if (!rawRate || rawRate === "0") return 0;
|
|
257
278
|
return Number(BigInt(rawRate)) / 10 ** RATE_DECIMALS * 100;
|
|
@@ -274,7 +295,7 @@ function compoundBalance(rawBalance, currentIndex) {
|
|
|
274
295
|
if (!rawBalance || !currentIndex || currentIndex === "0") return 0;
|
|
275
296
|
const scale = BigInt("1" + "0".repeat(RATE_DECIMALS));
|
|
276
297
|
const half = scale / 2n;
|
|
277
|
-
const result = (rawBalance *
|
|
298
|
+
const result = (rawBalance * BigInt(currentIndex) + half) / scale;
|
|
278
299
|
return Number(result) / 10 ** NAVI_BALANCE_DECIMALS;
|
|
279
300
|
}
|
|
280
301
|
async function getUserState(client, address) {
|
|
@@ -317,6 +338,9 @@ function mergeCoins(tx, coins) {
|
|
|
317
338
|
return primary;
|
|
318
339
|
}
|
|
319
340
|
async function buildSaveTx(client, address, amount, options = {}) {
|
|
341
|
+
if (!amount || amount <= 0 || !Number.isFinite(amount)) {
|
|
342
|
+
throw new T2000Error("INVALID_AMOUNT", "Save amount must be a positive number");
|
|
343
|
+
}
|
|
320
344
|
const rawAmount = Number(usdcToRaw(amount));
|
|
321
345
|
const [config, pool] = await Promise.all([getConfig(), getUsdcPool()]);
|
|
322
346
|
const coins = await fetchCoins(client, address, USDC_TYPE);
|
|
@@ -357,6 +381,7 @@ async function buildWithdrawTx(client, address, amount) {
|
|
|
357
381
|
const rawAmount = Number(usdcToRaw(effectiveAmount));
|
|
358
382
|
const tx = new transactions.Transaction();
|
|
359
383
|
tx.setSender(address);
|
|
384
|
+
addOracleUpdate(tx, config, pool);
|
|
360
385
|
const [balance] = tx.moveCall({
|
|
361
386
|
target: `${config.package}::incentive_v3::withdraw_v2`,
|
|
362
387
|
arguments: [
|
|
@@ -381,10 +406,14 @@ async function buildWithdrawTx(client, address, amount) {
|
|
|
381
406
|
return { tx, effectiveAmount };
|
|
382
407
|
}
|
|
383
408
|
async function buildBorrowTx(client, address, amount, options = {}) {
|
|
409
|
+
if (!amount || amount <= 0 || !Number.isFinite(amount)) {
|
|
410
|
+
throw new T2000Error("INVALID_AMOUNT", "Borrow amount must be a positive number");
|
|
411
|
+
}
|
|
384
412
|
const rawAmount = Number(usdcToRaw(amount));
|
|
385
413
|
const [config, pool] = await Promise.all([getConfig(), getUsdcPool()]);
|
|
386
414
|
const tx = new transactions.Transaction();
|
|
387
415
|
tx.setSender(address);
|
|
416
|
+
addOracleUpdate(tx, config, pool);
|
|
388
417
|
const [balance] = tx.moveCall({
|
|
389
418
|
target: `${config.package}::incentive_v3::borrow_v2`,
|
|
390
419
|
arguments: [
|
|
@@ -405,16 +434,23 @@ async function buildBorrowTx(client, address, amount, options = {}) {
|
|
|
405
434
|
arguments: [balance],
|
|
406
435
|
typeArguments: [pool.suiCoinType]
|
|
407
436
|
});
|
|
437
|
+
if (options.collectFee) {
|
|
438
|
+
addCollectFeeToTx(tx, borrowedCoin, "borrow");
|
|
439
|
+
}
|
|
408
440
|
tx.transferObjects([borrowedCoin], address);
|
|
409
441
|
return tx;
|
|
410
442
|
}
|
|
411
443
|
async function buildRepayTx(client, address, amount) {
|
|
444
|
+
if (!amount || amount <= 0 || !Number.isFinite(amount)) {
|
|
445
|
+
throw new T2000Error("INVALID_AMOUNT", "Repay amount must be a positive number");
|
|
446
|
+
}
|
|
412
447
|
const rawAmount = Number(usdcToRaw(amount));
|
|
413
448
|
const [config, pool] = await Promise.all([getConfig(), getUsdcPool()]);
|
|
414
449
|
const coins = await fetchCoins(client, address, USDC_TYPE);
|
|
415
450
|
if (coins.length === 0) throw new T2000Error("INSUFFICIENT_BALANCE", "No USDC coins to repay with");
|
|
416
451
|
const tx = new transactions.Transaction();
|
|
417
452
|
tx.setSender(address);
|
|
453
|
+
addOracleUpdate(tx, config, pool);
|
|
418
454
|
const coinObj = mergeCoins(tx, coins);
|
|
419
455
|
tx.moveCall({
|
|
420
456
|
target: `${config.package}::incentive_v3::entry_repay`,
|
|
@@ -553,6 +589,22 @@ async function maxBorrowAmount(client, addressOrKeypair) {
|
|
|
553
589
|
}
|
|
554
590
|
|
|
555
591
|
// src/adapters/navi.ts
|
|
592
|
+
var descriptor = {
|
|
593
|
+
id: "navi",
|
|
594
|
+
name: "NAVI Protocol",
|
|
595
|
+
packages: [],
|
|
596
|
+
dynamicPackageId: true,
|
|
597
|
+
actionMap: {
|
|
598
|
+
"incentive_v3::entry_deposit": "save",
|
|
599
|
+
"incentive_v3::deposit": "save",
|
|
600
|
+
"incentive_v3::withdraw_v2": "withdraw",
|
|
601
|
+
"incentive_v3::entry_withdraw": "withdraw",
|
|
602
|
+
"incentive_v3::borrow_v2": "borrow",
|
|
603
|
+
"incentive_v3::entry_borrow": "borrow",
|
|
604
|
+
"incentive_v3::entry_repay": "repay",
|
|
605
|
+
"incentive_v3::repay": "repay"
|
|
606
|
+
}
|
|
607
|
+
};
|
|
556
608
|
var NaviAdapter = class {
|
|
557
609
|
id = "navi";
|
|
558
610
|
name = "NAVI Protocol";
|
|
@@ -571,7 +623,7 @@ var NaviAdapter = class {
|
|
|
571
623
|
const rates = await getRates(this.client);
|
|
572
624
|
const key = asset.toUpperCase();
|
|
573
625
|
const r = rates[key];
|
|
574
|
-
if (!r) throw new
|
|
626
|
+
if (!r) throw new T2000Error("ASSET_NOT_SUPPORTED", `NAVI does not support ${asset}`);
|
|
575
627
|
return { asset, saveApy: r.saveApy, borrowApy: r.borrowApy };
|
|
576
628
|
}
|
|
577
629
|
async getPositions(address) {
|
|
@@ -697,6 +749,18 @@ function fallbackQuote(fromAsset, amount, poolPrice) {
|
|
|
697
749
|
}
|
|
698
750
|
|
|
699
751
|
// src/adapters/cetus.ts
|
|
752
|
+
var descriptor2 = {
|
|
753
|
+
id: "cetus",
|
|
754
|
+
name: "Cetus DEX",
|
|
755
|
+
packages: [CETUS_PACKAGE],
|
|
756
|
+
actionMap: {
|
|
757
|
+
"router::swap": "swap",
|
|
758
|
+
"router::swap_ab_bc": "swap",
|
|
759
|
+
"router::swap_ab_cb": "swap",
|
|
760
|
+
"router::swap_ba_bc": "swap",
|
|
761
|
+
"router::swap_ba_cb": "swap"
|
|
762
|
+
}
|
|
763
|
+
};
|
|
700
764
|
var CetusAdapter = class {
|
|
701
765
|
id = "cetus";
|
|
702
766
|
name = "Cetus";
|
|
@@ -751,6 +815,20 @@ var LENDING_MARKET_TYPE = "0xf95b06141ed4a174f239417323bde3f209b972f5930d8521ea3
|
|
|
751
815
|
var SUILEND_PACKAGE = "0xf95b06141ed4a174f239417323bde3f209b972f5930d8521ea38a52aff3a6ddf";
|
|
752
816
|
var UPGRADE_CAP_ID = "0x3d4ef1859c3ee9fc72858f588b56a09da5466e64f8cc4e90a7b3b909fba8a7ae";
|
|
753
817
|
var FALLBACK_PUBLISHED_AT = "0xd2a67633ccb8de063163e25bcfca242929caf5cf1a26c2929dab519ee0b8f331";
|
|
818
|
+
var descriptor3 = {
|
|
819
|
+
id: "suilend",
|
|
820
|
+
name: "Suilend",
|
|
821
|
+
packages: [SUILEND_PACKAGE],
|
|
822
|
+
actionMap: {
|
|
823
|
+
"lending_market::deposit_liquidity_and_mint_ctokens": "save",
|
|
824
|
+
"lending_market::deposit_ctokens_into_obligation": "save",
|
|
825
|
+
"lending_market::create_obligation": "save",
|
|
826
|
+
"lending_market::withdraw_ctokens": "withdraw",
|
|
827
|
+
"lending_market::redeem_ctokens_and_withdraw_liquidity": "withdraw",
|
|
828
|
+
"lending_market::borrow": "borrow",
|
|
829
|
+
"lending_market::repay": "repay"
|
|
830
|
+
}
|
|
831
|
+
};
|
|
754
832
|
function interpolateRate(utilBreakpoints, aprBreakpoints, utilizationPct) {
|
|
755
833
|
if (utilBreakpoints.length === 0) return 0;
|
|
756
834
|
if (utilizationPct <= utilBreakpoints[0]) return aprBreakpoints[0];
|
|
@@ -1128,10 +1206,32 @@ var SuilendAdapter = class {
|
|
|
1128
1206
|
return all;
|
|
1129
1207
|
}
|
|
1130
1208
|
};
|
|
1209
|
+
var descriptor4 = {
|
|
1210
|
+
id: "sentinel",
|
|
1211
|
+
name: "Sui Sentinel",
|
|
1212
|
+
packages: [SENTINEL.PACKAGE],
|
|
1213
|
+
actionMap: {
|
|
1214
|
+
"sentinel::request_attack": "sentinel_attack",
|
|
1215
|
+
"sentinel::consume_prompt": "sentinel_settle"
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
|
|
1219
|
+
// src/adapters/index.ts
|
|
1220
|
+
var allDescriptors = [
|
|
1221
|
+
descriptor,
|
|
1222
|
+
descriptor3,
|
|
1223
|
+
descriptor2,
|
|
1224
|
+
descriptor4
|
|
1225
|
+
];
|
|
1131
1226
|
|
|
1132
1227
|
exports.CetusAdapter = CetusAdapter;
|
|
1133
1228
|
exports.NaviAdapter = NaviAdapter;
|
|
1134
1229
|
exports.ProtocolRegistry = ProtocolRegistry;
|
|
1135
1230
|
exports.SuilendAdapter = SuilendAdapter;
|
|
1231
|
+
exports.allDescriptors = allDescriptors;
|
|
1232
|
+
exports.cetusDescriptor = descriptor2;
|
|
1233
|
+
exports.naviDescriptor = descriptor;
|
|
1234
|
+
exports.sentinelDescriptor = descriptor4;
|
|
1235
|
+
exports.suilendDescriptor = descriptor3;
|
|
1136
1236
|
//# sourceMappingURL=index.cjs.map
|
|
1137
1237
|
//# sourceMappingURL=index.cjs.map
|