@t2000/sdk 0.5.6 → 0.6.2
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/dist/adapters/index.cjs +100 -39
- package/dist/adapters/index.cjs.map +1 -1
- package/dist/adapters/index.js +100 -39
- package/dist/adapters/index.js.map +1 -1
- package/dist/index.cjs +82 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +82 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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];
|
|
@@ -129,28 +151,6 @@ var T2000_TREASURY_ID = process.env.T2000_TREASURY_ID ?? "0x3bb501b8300125dca590
|
|
|
129
151
|
process.env.T2000_API_URL ?? "https://api.t2000.ai";
|
|
130
152
|
var CETUS_USDC_SUI_POOL = "0x51e883ba7c0b566a26cbc8a94cd33eb0abd418a77cc1e60ad22fd9b1f29cd2ab";
|
|
131
153
|
|
|
132
|
-
// src/errors.ts
|
|
133
|
-
var T2000Error = class extends Error {
|
|
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
|
-
|
|
154
154
|
// src/utils/format.ts
|
|
155
155
|
function usdcToRaw(amount) {
|
|
156
156
|
return BigInt(Math.round(amount * 10 ** USDC_DECIMALS));
|
|
@@ -193,6 +193,8 @@ var SUI_SYSTEM_STATE = "0x05";
|
|
|
193
193
|
var NAVI_BALANCE_DECIMALS = 9;
|
|
194
194
|
var CONFIG_API = "https://open-api.naviprotocol.io/api/navi/config?env=prod";
|
|
195
195
|
var POOLS_API = "https://open-api.naviprotocol.io/api/navi/pools?env=prod";
|
|
196
|
+
var PACKAGE_API = "https://open-api.naviprotocol.io/api/package";
|
|
197
|
+
var packageCache = null;
|
|
196
198
|
function toBigInt(v) {
|
|
197
199
|
if (typeof v === "bigint") return v;
|
|
198
200
|
return BigInt(String(v));
|
|
@@ -217,9 +219,22 @@ async function fetchJson(url) {
|
|
|
217
219
|
const json = await res.json();
|
|
218
220
|
return json.data ?? json;
|
|
219
221
|
}
|
|
222
|
+
async function getLatestPackageId() {
|
|
223
|
+
if (packageCache && Date.now() - packageCache.ts < CACHE_TTL) return packageCache.id;
|
|
224
|
+
const res = await fetch(PACKAGE_API);
|
|
225
|
+
if (!res.ok) throw new T2000Error("PROTOCOL_UNAVAILABLE", `NAVI package API error: ${res.status}`);
|
|
226
|
+
const json = await res.json();
|
|
227
|
+
if (!json.packageId) throw new T2000Error("PROTOCOL_UNAVAILABLE", "NAVI package API returned no packageId");
|
|
228
|
+
packageCache = { id: json.packageId, ts: Date.now() };
|
|
229
|
+
return json.packageId;
|
|
230
|
+
}
|
|
220
231
|
async function getConfig(fresh = false) {
|
|
221
232
|
if (configCache && !fresh && Date.now() - configCache.ts < CACHE_TTL) return configCache.data;
|
|
222
|
-
const data = await
|
|
233
|
+
const [data, latestPkg] = await Promise.all([
|
|
234
|
+
fetchJson(CONFIG_API),
|
|
235
|
+
getLatestPackageId()
|
|
236
|
+
]);
|
|
237
|
+
data.package = latestPkg;
|
|
223
238
|
configCache = { data, ts: Date.now() };
|
|
224
239
|
return data;
|
|
225
240
|
}
|
|
@@ -237,6 +252,24 @@ async function getUsdcPool() {
|
|
|
237
252
|
if (!usdc) throw new T2000Error("PROTOCOL_UNAVAILABLE", "USDC pool not found on NAVI");
|
|
238
253
|
return usdc;
|
|
239
254
|
}
|
|
255
|
+
function addOracleUpdate(tx, config, pool) {
|
|
256
|
+
const feed = config.oracle.feeds?.find((f2) => f2.assetId === pool.id);
|
|
257
|
+
if (!feed) {
|
|
258
|
+
throw new T2000Error("PROTOCOL_UNAVAILABLE", `Oracle feed not found for asset ${pool.token?.symbol ?? pool.id}`);
|
|
259
|
+
}
|
|
260
|
+
tx.moveCall({
|
|
261
|
+
target: `${config.oracle.packageId}::oracle_pro::update_single_price_v2`,
|
|
262
|
+
arguments: [
|
|
263
|
+
tx.object(CLOCK),
|
|
264
|
+
tx.object(config.oracle.oracleConfig),
|
|
265
|
+
tx.object(config.oracle.priceOracle),
|
|
266
|
+
tx.object(config.oracle.supraOracleHolder),
|
|
267
|
+
tx.object(feed.pythPriceInfoObject),
|
|
268
|
+
tx.object(config.oracle.switchboardAggregator),
|
|
269
|
+
tx.pure.address(feed.feedId)
|
|
270
|
+
]
|
|
271
|
+
});
|
|
272
|
+
}
|
|
240
273
|
function rateToApy(rawRate) {
|
|
241
274
|
if (!rawRate || rawRate === "0") return 0;
|
|
242
275
|
return Number(BigInt(rawRate)) / 10 ** RATE_DECIMALS * 100;
|
|
@@ -259,7 +292,7 @@ function compoundBalance(rawBalance, currentIndex) {
|
|
|
259
292
|
if (!rawBalance || !currentIndex || currentIndex === "0") return 0;
|
|
260
293
|
const scale = BigInt("1" + "0".repeat(RATE_DECIMALS));
|
|
261
294
|
const half = scale / 2n;
|
|
262
|
-
const result = (rawBalance *
|
|
295
|
+
const result = (rawBalance * BigInt(currentIndex) + half) / scale;
|
|
263
296
|
return Number(result) / 10 ** NAVI_BALANCE_DECIMALS;
|
|
264
297
|
}
|
|
265
298
|
async function getUserState(client, address) {
|
|
@@ -293,23 +326,25 @@ async function fetchCoins(client, owner, coinType) {
|
|
|
293
326
|
}
|
|
294
327
|
return all;
|
|
295
328
|
}
|
|
296
|
-
function
|
|
329
|
+
function mergeCoins(tx, coins) {
|
|
297
330
|
if (coins.length === 0) throw new T2000Error("INSUFFICIENT_BALANCE", "No coins to merge");
|
|
298
331
|
const primary = tx.object(coins[0].coinObjectId);
|
|
299
332
|
if (coins.length > 1) {
|
|
300
333
|
tx.mergeCoins(primary, coins.slice(1).map((c) => tx.object(c.coinObjectId)));
|
|
301
334
|
}
|
|
302
|
-
|
|
303
|
-
return split;
|
|
335
|
+
return primary;
|
|
304
336
|
}
|
|
305
337
|
async function buildSaveTx(client, address, amount, options = {}) {
|
|
338
|
+
if (!amount || amount <= 0 || !Number.isFinite(amount)) {
|
|
339
|
+
throw new T2000Error("INVALID_AMOUNT", "Save amount must be a positive number");
|
|
340
|
+
}
|
|
306
341
|
const rawAmount = Number(usdcToRaw(amount));
|
|
307
342
|
const [config, pool] = await Promise.all([getConfig(), getUsdcPool()]);
|
|
308
343
|
const coins = await fetchCoins(client, address, USDC_TYPE);
|
|
309
344
|
if (coins.length === 0) throw new T2000Error("INSUFFICIENT_BALANCE", "No USDC coins found");
|
|
310
345
|
const tx = new transactions.Transaction();
|
|
311
346
|
tx.setSender(address);
|
|
312
|
-
const coinObj =
|
|
347
|
+
const coinObj = mergeCoins(tx, coins);
|
|
313
348
|
if (options.collectFee) {
|
|
314
349
|
addCollectFeeToTx(tx, coinObj, "save");
|
|
315
350
|
}
|
|
@@ -343,8 +378,9 @@ async function buildWithdrawTx(client, address, amount) {
|
|
|
343
378
|
const rawAmount = Number(usdcToRaw(effectiveAmount));
|
|
344
379
|
const tx = new transactions.Transaction();
|
|
345
380
|
tx.setSender(address);
|
|
346
|
-
tx
|
|
347
|
-
|
|
381
|
+
addOracleUpdate(tx, config, pool);
|
|
382
|
+
const [balance] = tx.moveCall({
|
|
383
|
+
target: `${config.package}::incentive_v3::withdraw_v2`,
|
|
348
384
|
arguments: [
|
|
349
385
|
tx.object(CLOCK),
|
|
350
386
|
tx.object(config.oracle.priceOracle),
|
|
@@ -355,17 +391,28 @@ async function buildWithdrawTx(client, address, amount) {
|
|
|
355
391
|
tx.object(config.incentiveV2),
|
|
356
392
|
tx.object(config.incentiveV3),
|
|
357
393
|
tx.object(SUI_SYSTEM_STATE)
|
|
358
|
-
]
|
|
394
|
+
],
|
|
395
|
+
typeArguments: [pool.suiCoinType]
|
|
396
|
+
});
|
|
397
|
+
const [coin] = tx.moveCall({
|
|
398
|
+
target: "0x2::coin::from_balance",
|
|
399
|
+
arguments: [balance],
|
|
400
|
+
typeArguments: [pool.suiCoinType]
|
|
359
401
|
});
|
|
402
|
+
tx.transferObjects([coin], address);
|
|
360
403
|
return { tx, effectiveAmount };
|
|
361
404
|
}
|
|
362
405
|
async function buildBorrowTx(client, address, amount, options = {}) {
|
|
406
|
+
if (!amount || amount <= 0 || !Number.isFinite(amount)) {
|
|
407
|
+
throw new T2000Error("INVALID_AMOUNT", "Borrow amount must be a positive number");
|
|
408
|
+
}
|
|
363
409
|
const rawAmount = Number(usdcToRaw(amount));
|
|
364
410
|
const [config, pool] = await Promise.all([getConfig(), getUsdcPool()]);
|
|
365
411
|
const tx = new transactions.Transaction();
|
|
366
412
|
tx.setSender(address);
|
|
367
|
-
tx
|
|
368
|
-
|
|
413
|
+
addOracleUpdate(tx, config, pool);
|
|
414
|
+
const [balance] = tx.moveCall({
|
|
415
|
+
target: `${config.package}::incentive_v3::borrow_v2`,
|
|
369
416
|
arguments: [
|
|
370
417
|
tx.object(CLOCK),
|
|
371
418
|
tx.object(config.oracle.priceOracle),
|
|
@@ -376,18 +423,32 @@ async function buildBorrowTx(client, address, amount, options = {}) {
|
|
|
376
423
|
tx.object(config.incentiveV2),
|
|
377
424
|
tx.object(config.incentiveV3),
|
|
378
425
|
tx.object(SUI_SYSTEM_STATE)
|
|
379
|
-
]
|
|
426
|
+
],
|
|
427
|
+
typeArguments: [pool.suiCoinType]
|
|
380
428
|
});
|
|
429
|
+
const [borrowedCoin] = tx.moveCall({
|
|
430
|
+
target: "0x2::coin::from_balance",
|
|
431
|
+
arguments: [balance],
|
|
432
|
+
typeArguments: [pool.suiCoinType]
|
|
433
|
+
});
|
|
434
|
+
if (options.collectFee) {
|
|
435
|
+
addCollectFeeToTx(tx, borrowedCoin, "borrow");
|
|
436
|
+
}
|
|
437
|
+
tx.transferObjects([borrowedCoin], address);
|
|
381
438
|
return tx;
|
|
382
439
|
}
|
|
383
440
|
async function buildRepayTx(client, address, amount) {
|
|
441
|
+
if (!amount || amount <= 0 || !Number.isFinite(amount)) {
|
|
442
|
+
throw new T2000Error("INVALID_AMOUNT", "Repay amount must be a positive number");
|
|
443
|
+
}
|
|
384
444
|
const rawAmount = Number(usdcToRaw(amount));
|
|
385
445
|
const [config, pool] = await Promise.all([getConfig(), getUsdcPool()]);
|
|
386
446
|
const coins = await fetchCoins(client, address, USDC_TYPE);
|
|
387
447
|
if (coins.length === 0) throw new T2000Error("INSUFFICIENT_BALANCE", "No USDC coins to repay with");
|
|
388
448
|
const tx = new transactions.Transaction();
|
|
389
449
|
tx.setSender(address);
|
|
390
|
-
|
|
450
|
+
addOracleUpdate(tx, config, pool);
|
|
451
|
+
const coinObj = mergeCoins(tx, coins);
|
|
391
452
|
tx.moveCall({
|
|
392
453
|
target: `${config.package}::incentive_v3::entry_repay`,
|
|
393
454
|
arguments: [
|
|
@@ -543,7 +604,7 @@ var NaviAdapter = class {
|
|
|
543
604
|
const rates = await getRates(this.client);
|
|
544
605
|
const key = asset.toUpperCase();
|
|
545
606
|
const r = rates[key];
|
|
546
|
-
if (!r) throw new
|
|
607
|
+
if (!r) throw new T2000Error("ASSET_NOT_SUPPORTED", `NAVI does not support ${asset}`);
|
|
547
608
|
return { asset, saveApy: r.saveApy, borrowApy: r.borrowApy };
|
|
548
609
|
}
|
|
549
610
|
async getPositions(address) {
|