@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.js
CHANGED
|
@@ -3,6 +3,28 @@ import { bcs } from '@mysten/sui/bcs';
|
|
|
3
3
|
import { AggregatorClient, Env } from '@cetusprotocol/aggregator-sdk';
|
|
4
4
|
import { normalizeStructTag } from '@mysten/sui/utils';
|
|
5
5
|
|
|
6
|
+
// src/errors.ts
|
|
7
|
+
var T2000Error = class extends Error {
|
|
8
|
+
code;
|
|
9
|
+
data;
|
|
10
|
+
retryable;
|
|
11
|
+
constructor(code, message, data, retryable = false) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "T2000Error";
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.data = data;
|
|
16
|
+
this.retryable = retryable;
|
|
17
|
+
}
|
|
18
|
+
toJSON() {
|
|
19
|
+
return {
|
|
20
|
+
error: this.code,
|
|
21
|
+
message: this.message,
|
|
22
|
+
...this.data && { data: this.data },
|
|
23
|
+
retryable: this.retryable
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
6
28
|
// src/adapters/registry.ts
|
|
7
29
|
var ProtocolRegistry = class {
|
|
8
30
|
lending = /* @__PURE__ */ new Map();
|
|
@@ -25,7 +47,7 @@ var ProtocolRegistry = class {
|
|
|
25
47
|
}
|
|
26
48
|
}
|
|
27
49
|
if (candidates.length === 0) {
|
|
28
|
-
throw new
|
|
50
|
+
throw new T2000Error("ASSET_NOT_SUPPORTED", `No lending adapter supports saving ${asset}`);
|
|
29
51
|
}
|
|
30
52
|
candidates.sort((a, b) => b.rate.saveApy - a.rate.saveApy);
|
|
31
53
|
return candidates[0];
|
|
@@ -43,7 +65,7 @@ var ProtocolRegistry = class {
|
|
|
43
65
|
}
|
|
44
66
|
}
|
|
45
67
|
if (candidates.length === 0) {
|
|
46
|
-
throw new
|
|
68
|
+
throw new T2000Error("ASSET_NOT_SUPPORTED", `No lending adapter supports borrowing ${asset}`);
|
|
47
69
|
}
|
|
48
70
|
candidates.sort((a, b) => a.rate.borrowApy - b.rate.borrowApy);
|
|
49
71
|
return candidates[0];
|
|
@@ -60,7 +82,7 @@ var ProtocolRegistry = class {
|
|
|
60
82
|
}
|
|
61
83
|
}
|
|
62
84
|
if (candidates.length === 0) {
|
|
63
|
-
throw new
|
|
85
|
+
throw new T2000Error("ASSET_NOT_SUPPORTED", `No swap adapter supports ${from} \u2192 ${to}`);
|
|
64
86
|
}
|
|
65
87
|
candidates.sort((a, b) => b.quote.expectedOutput - a.quote.expectedOutput);
|
|
66
88
|
return candidates[0];
|
|
@@ -127,28 +149,6 @@ var T2000_TREASURY_ID = process.env.T2000_TREASURY_ID ?? "0x3bb501b8300125dca590
|
|
|
127
149
|
process.env.T2000_API_URL ?? "https://api.t2000.ai";
|
|
128
150
|
var CETUS_USDC_SUI_POOL = "0x51e883ba7c0b566a26cbc8a94cd33eb0abd418a77cc1e60ad22fd9b1f29cd2ab";
|
|
129
151
|
|
|
130
|
-
// src/errors.ts
|
|
131
|
-
var T2000Error = class extends Error {
|
|
132
|
-
code;
|
|
133
|
-
data;
|
|
134
|
-
retryable;
|
|
135
|
-
constructor(code, message, data, retryable = false) {
|
|
136
|
-
super(message);
|
|
137
|
-
this.name = "T2000Error";
|
|
138
|
-
this.code = code;
|
|
139
|
-
this.data = data;
|
|
140
|
-
this.retryable = retryable;
|
|
141
|
-
}
|
|
142
|
-
toJSON() {
|
|
143
|
-
return {
|
|
144
|
-
error: this.code,
|
|
145
|
-
message: this.message,
|
|
146
|
-
...this.data && { data: this.data },
|
|
147
|
-
retryable: this.retryable
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
|
|
152
152
|
// src/utils/format.ts
|
|
153
153
|
function usdcToRaw(amount) {
|
|
154
154
|
return BigInt(Math.round(amount * 10 ** USDC_DECIMALS));
|
|
@@ -191,6 +191,8 @@ var SUI_SYSTEM_STATE = "0x05";
|
|
|
191
191
|
var NAVI_BALANCE_DECIMALS = 9;
|
|
192
192
|
var CONFIG_API = "https://open-api.naviprotocol.io/api/navi/config?env=prod";
|
|
193
193
|
var POOLS_API = "https://open-api.naviprotocol.io/api/navi/pools?env=prod";
|
|
194
|
+
var PACKAGE_API = "https://open-api.naviprotocol.io/api/package";
|
|
195
|
+
var packageCache = null;
|
|
194
196
|
function toBigInt(v) {
|
|
195
197
|
if (typeof v === "bigint") return v;
|
|
196
198
|
return BigInt(String(v));
|
|
@@ -215,9 +217,22 @@ async function fetchJson(url) {
|
|
|
215
217
|
const json = await res.json();
|
|
216
218
|
return json.data ?? json;
|
|
217
219
|
}
|
|
220
|
+
async function getLatestPackageId() {
|
|
221
|
+
if (packageCache && Date.now() - packageCache.ts < CACHE_TTL) return packageCache.id;
|
|
222
|
+
const res = await fetch(PACKAGE_API);
|
|
223
|
+
if (!res.ok) throw new T2000Error("PROTOCOL_UNAVAILABLE", `NAVI package API error: ${res.status}`);
|
|
224
|
+
const json = await res.json();
|
|
225
|
+
if (!json.packageId) throw new T2000Error("PROTOCOL_UNAVAILABLE", "NAVI package API returned no packageId");
|
|
226
|
+
packageCache = { id: json.packageId, ts: Date.now() };
|
|
227
|
+
return json.packageId;
|
|
228
|
+
}
|
|
218
229
|
async function getConfig(fresh = false) {
|
|
219
230
|
if (configCache && !fresh && Date.now() - configCache.ts < CACHE_TTL) return configCache.data;
|
|
220
|
-
const data = await
|
|
231
|
+
const [data, latestPkg] = await Promise.all([
|
|
232
|
+
fetchJson(CONFIG_API),
|
|
233
|
+
getLatestPackageId()
|
|
234
|
+
]);
|
|
235
|
+
data.package = latestPkg;
|
|
221
236
|
configCache = { data, ts: Date.now() };
|
|
222
237
|
return data;
|
|
223
238
|
}
|
|
@@ -235,6 +250,24 @@ async function getUsdcPool() {
|
|
|
235
250
|
if (!usdc) throw new T2000Error("PROTOCOL_UNAVAILABLE", "USDC pool not found on NAVI");
|
|
236
251
|
return usdc;
|
|
237
252
|
}
|
|
253
|
+
function addOracleUpdate(tx, config, pool) {
|
|
254
|
+
const feed = config.oracle.feeds?.find((f2) => f2.assetId === pool.id);
|
|
255
|
+
if (!feed) {
|
|
256
|
+
throw new T2000Error("PROTOCOL_UNAVAILABLE", `Oracle feed not found for asset ${pool.token?.symbol ?? pool.id}`);
|
|
257
|
+
}
|
|
258
|
+
tx.moveCall({
|
|
259
|
+
target: `${config.oracle.packageId}::oracle_pro::update_single_price_v2`,
|
|
260
|
+
arguments: [
|
|
261
|
+
tx.object(CLOCK),
|
|
262
|
+
tx.object(config.oracle.oracleConfig),
|
|
263
|
+
tx.object(config.oracle.priceOracle),
|
|
264
|
+
tx.object(config.oracle.supraOracleHolder),
|
|
265
|
+
tx.object(feed.pythPriceInfoObject),
|
|
266
|
+
tx.object(config.oracle.switchboardAggregator),
|
|
267
|
+
tx.pure.address(feed.feedId)
|
|
268
|
+
]
|
|
269
|
+
});
|
|
270
|
+
}
|
|
238
271
|
function rateToApy(rawRate) {
|
|
239
272
|
if (!rawRate || rawRate === "0") return 0;
|
|
240
273
|
return Number(BigInt(rawRate)) / 10 ** RATE_DECIMALS * 100;
|
|
@@ -257,7 +290,7 @@ function compoundBalance(rawBalance, currentIndex) {
|
|
|
257
290
|
if (!rawBalance || !currentIndex || currentIndex === "0") return 0;
|
|
258
291
|
const scale = BigInt("1" + "0".repeat(RATE_DECIMALS));
|
|
259
292
|
const half = scale / 2n;
|
|
260
|
-
const result = (rawBalance *
|
|
293
|
+
const result = (rawBalance * BigInt(currentIndex) + half) / scale;
|
|
261
294
|
return Number(result) / 10 ** NAVI_BALANCE_DECIMALS;
|
|
262
295
|
}
|
|
263
296
|
async function getUserState(client, address) {
|
|
@@ -291,23 +324,25 @@ async function fetchCoins(client, owner, coinType) {
|
|
|
291
324
|
}
|
|
292
325
|
return all;
|
|
293
326
|
}
|
|
294
|
-
function
|
|
327
|
+
function mergeCoins(tx, coins) {
|
|
295
328
|
if (coins.length === 0) throw new T2000Error("INSUFFICIENT_BALANCE", "No coins to merge");
|
|
296
329
|
const primary = tx.object(coins[0].coinObjectId);
|
|
297
330
|
if (coins.length > 1) {
|
|
298
331
|
tx.mergeCoins(primary, coins.slice(1).map((c) => tx.object(c.coinObjectId)));
|
|
299
332
|
}
|
|
300
|
-
|
|
301
|
-
return split;
|
|
333
|
+
return primary;
|
|
302
334
|
}
|
|
303
335
|
async function buildSaveTx(client, address, amount, options = {}) {
|
|
336
|
+
if (!amount || amount <= 0 || !Number.isFinite(amount)) {
|
|
337
|
+
throw new T2000Error("INVALID_AMOUNT", "Save amount must be a positive number");
|
|
338
|
+
}
|
|
304
339
|
const rawAmount = Number(usdcToRaw(amount));
|
|
305
340
|
const [config, pool] = await Promise.all([getConfig(), getUsdcPool()]);
|
|
306
341
|
const coins = await fetchCoins(client, address, USDC_TYPE);
|
|
307
342
|
if (coins.length === 0) throw new T2000Error("INSUFFICIENT_BALANCE", "No USDC coins found");
|
|
308
343
|
const tx = new Transaction();
|
|
309
344
|
tx.setSender(address);
|
|
310
|
-
const coinObj =
|
|
345
|
+
const coinObj = mergeCoins(tx, coins);
|
|
311
346
|
if (options.collectFee) {
|
|
312
347
|
addCollectFeeToTx(tx, coinObj, "save");
|
|
313
348
|
}
|
|
@@ -341,8 +376,9 @@ async function buildWithdrawTx(client, address, amount) {
|
|
|
341
376
|
const rawAmount = Number(usdcToRaw(effectiveAmount));
|
|
342
377
|
const tx = new Transaction();
|
|
343
378
|
tx.setSender(address);
|
|
344
|
-
tx
|
|
345
|
-
|
|
379
|
+
addOracleUpdate(tx, config, pool);
|
|
380
|
+
const [balance] = tx.moveCall({
|
|
381
|
+
target: `${config.package}::incentive_v3::withdraw_v2`,
|
|
346
382
|
arguments: [
|
|
347
383
|
tx.object(CLOCK),
|
|
348
384
|
tx.object(config.oracle.priceOracle),
|
|
@@ -353,17 +389,28 @@ async function buildWithdrawTx(client, address, amount) {
|
|
|
353
389
|
tx.object(config.incentiveV2),
|
|
354
390
|
tx.object(config.incentiveV3),
|
|
355
391
|
tx.object(SUI_SYSTEM_STATE)
|
|
356
|
-
]
|
|
392
|
+
],
|
|
393
|
+
typeArguments: [pool.suiCoinType]
|
|
394
|
+
});
|
|
395
|
+
const [coin] = tx.moveCall({
|
|
396
|
+
target: "0x2::coin::from_balance",
|
|
397
|
+
arguments: [balance],
|
|
398
|
+
typeArguments: [pool.suiCoinType]
|
|
357
399
|
});
|
|
400
|
+
tx.transferObjects([coin], address);
|
|
358
401
|
return { tx, effectiveAmount };
|
|
359
402
|
}
|
|
360
403
|
async function buildBorrowTx(client, address, amount, options = {}) {
|
|
404
|
+
if (!amount || amount <= 0 || !Number.isFinite(amount)) {
|
|
405
|
+
throw new T2000Error("INVALID_AMOUNT", "Borrow amount must be a positive number");
|
|
406
|
+
}
|
|
361
407
|
const rawAmount = Number(usdcToRaw(amount));
|
|
362
408
|
const [config, pool] = await Promise.all([getConfig(), getUsdcPool()]);
|
|
363
409
|
const tx = new Transaction();
|
|
364
410
|
tx.setSender(address);
|
|
365
|
-
tx
|
|
366
|
-
|
|
411
|
+
addOracleUpdate(tx, config, pool);
|
|
412
|
+
const [balance] = tx.moveCall({
|
|
413
|
+
target: `${config.package}::incentive_v3::borrow_v2`,
|
|
367
414
|
arguments: [
|
|
368
415
|
tx.object(CLOCK),
|
|
369
416
|
tx.object(config.oracle.priceOracle),
|
|
@@ -374,18 +421,32 @@ async function buildBorrowTx(client, address, amount, options = {}) {
|
|
|
374
421
|
tx.object(config.incentiveV2),
|
|
375
422
|
tx.object(config.incentiveV3),
|
|
376
423
|
tx.object(SUI_SYSTEM_STATE)
|
|
377
|
-
]
|
|
424
|
+
],
|
|
425
|
+
typeArguments: [pool.suiCoinType]
|
|
378
426
|
});
|
|
427
|
+
const [borrowedCoin] = tx.moveCall({
|
|
428
|
+
target: "0x2::coin::from_balance",
|
|
429
|
+
arguments: [balance],
|
|
430
|
+
typeArguments: [pool.suiCoinType]
|
|
431
|
+
});
|
|
432
|
+
if (options.collectFee) {
|
|
433
|
+
addCollectFeeToTx(tx, borrowedCoin, "borrow");
|
|
434
|
+
}
|
|
435
|
+
tx.transferObjects([borrowedCoin], address);
|
|
379
436
|
return tx;
|
|
380
437
|
}
|
|
381
438
|
async function buildRepayTx(client, address, amount) {
|
|
439
|
+
if (!amount || amount <= 0 || !Number.isFinite(amount)) {
|
|
440
|
+
throw new T2000Error("INVALID_AMOUNT", "Repay amount must be a positive number");
|
|
441
|
+
}
|
|
382
442
|
const rawAmount = Number(usdcToRaw(amount));
|
|
383
443
|
const [config, pool] = await Promise.all([getConfig(), getUsdcPool()]);
|
|
384
444
|
const coins = await fetchCoins(client, address, USDC_TYPE);
|
|
385
445
|
if (coins.length === 0) throw new T2000Error("INSUFFICIENT_BALANCE", "No USDC coins to repay with");
|
|
386
446
|
const tx = new Transaction();
|
|
387
447
|
tx.setSender(address);
|
|
388
|
-
|
|
448
|
+
addOracleUpdate(tx, config, pool);
|
|
449
|
+
const coinObj = mergeCoins(tx, coins);
|
|
389
450
|
tx.moveCall({
|
|
390
451
|
target: `${config.package}::incentive_v3::entry_repay`,
|
|
391
452
|
arguments: [
|
|
@@ -541,7 +602,7 @@ var NaviAdapter = class {
|
|
|
541
602
|
const rates = await getRates(this.client);
|
|
542
603
|
const key = asset.toUpperCase();
|
|
543
604
|
const r = rates[key];
|
|
544
|
-
if (!r) throw new
|
|
605
|
+
if (!r) throw new T2000Error("ASSET_NOT_SUPPORTED", `NAVI does not support ${asset}`);
|
|
545
606
|
return { asset, saveApy: r.saveApy, borrowApy: r.borrowApy };
|
|
546
607
|
}
|
|
547
608
|
async getPositions(address) {
|