@t2000/sdk 0.56.2 → 1.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/dist/browser.cjs CHANGED
@@ -100,37 +100,12 @@ function mapMoveAbortCode(code) {
100
100
  };
101
101
  return abortMessages[code] ?? `Move abort code: ${code}`;
102
102
  }
103
- function isMoveAbort(msg) {
104
- return msg.includes("MoveAbort") || msg.includes("MovePrimitiveRuntimeError");
105
- }
106
- function parseMoveAbortMessage(msg) {
107
- const abortMatch = msg.match(/abort code:\s*(\d+)/i) ?? msg.match(/MoveAbort[^,]*,\s*(\d+)/);
108
- if (abortMatch) {
109
- const code = parseInt(abortMatch[1], 10);
110
- const moduleMatch = msg.match(/Identifier\("([^"]+)"\)/) ?? msg.match(/in '([^']+)'/);
111
- const fnMatch = msg.match(/function_name:\s*Some\("([^"]+)"\)/);
112
- const context = `${moduleMatch?.[1] ?? ""}${fnMatch ? `::${fnMatch[1]}` : ""}`.toLowerCase();
113
- const suffix = moduleMatch ? ` [${moduleMatch[1]}${fnMatch ? `::${fnMatch[1]}` : ""}]` : "";
114
- if (context.includes("slippage")) {
115
- return `Slippage too high \u2014 price moved during execution${suffix}`;
116
- }
117
- if (context.includes("balance::split") || context.includes("balance::ENotEnough")) {
118
- return `Insufficient on-chain balance${suffix}`;
119
- }
120
- const mapped = mapMoveAbortCode(code);
121
- return `${mapped}${suffix}`;
122
- }
123
- return msg;
124
- }
125
103
 
126
104
  // src/constants.ts
127
105
  var MIST_PER_SUI = 1000000000n;
128
106
  var SUI_DECIMALS = 9;
129
107
  var USDC_DECIMALS = 6;
130
108
  var BPS_DENOMINATOR = 10000n;
131
- var AUTO_TOPUP_THRESHOLD = 50000000n;
132
- var GAS_RESERVE_TARGET = 150000000n;
133
- var AUTO_TOPUP_MIN_USDC = 2000000n;
134
109
  var SAVE_FEE_BPS = 10n;
135
110
  var BORROW_FEE_BPS = 5n;
136
111
  var CLOCK_ID = "0x6";
@@ -196,313 +171,8 @@ var T2000_PACKAGE_ID = process.env.T2000_PACKAGE_ID ?? "0xd775fcc66eae26797654d4
196
171
  var T2000_CONFIG_ID = process.env.T2000_CONFIG_ID ?? "0x08ba26f0d260b5edf6a19c71492b3eb914906a7419baf2df1426765157e5862a";
197
172
  var T2000_TREASURY_ID = process.env.T2000_TREASURY_ID ?? "0xf420ec0dcad44433042fb56e1413fb88d3ff65be94fcf425ef9ff750164590e8";
198
173
  var DEFAULT_NETWORK = "mainnet";
199
- var API_BASE_URL = process.env.T2000_API_URL ?? "https://api.t2000.ai";
174
+ process.env.T2000_API_URL ?? "https://api.t2000.ai";
200
175
  var GAS_RESERVE_MIN = 0.05;
201
-
202
- // src/gas/autoTopUp.ts
203
- async function shouldAutoTopUp(client, address) {
204
- const [suiBalance, usdcBalance] = await Promise.all([
205
- client.getBalance({ owner: address, coinType: SUPPORTED_ASSETS.SUI.type }),
206
- client.getBalance({ owner: address, coinType: SUPPORTED_ASSETS.USDC.type })
207
- ]);
208
- const suiRaw = BigInt(suiBalance.totalBalance);
209
- const usdcRaw = BigInt(usdcBalance.totalBalance);
210
- if (suiRaw < GAS_RESERVE_TARGET && usdcRaw >= AUTO_TOPUP_MIN_USDC) {
211
- return false;
212
- }
213
- return false;
214
- }
215
- async function executeAutoTopUp(_client, _signer) {
216
- return { success: false, tx: "", usdcSpent: 0, suiReceived: 0 };
217
- }
218
-
219
- // src/utils/base64.ts
220
- function toBase64(bytes) {
221
- let binary = "";
222
- for (const byte of bytes) binary += String.fromCharCode(byte);
223
- return btoa(binary);
224
- }
225
- function fromBase64(b64) {
226
- const binary = atob(b64);
227
- const bytes = new Uint8Array(binary.length);
228
- for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
229
- return bytes;
230
- }
231
-
232
- // src/gas/gasStation.ts
233
- async function requestGasSponsorship(txJson, sender, type, txBcsBytes) {
234
- const payload = { sender, type };
235
- if (txBcsBytes) {
236
- payload.txBcsBytes = txBcsBytes;
237
- } else {
238
- payload.txJson = txJson;
239
- payload.txBytes = toBase64(new TextEncoder().encode(txJson));
240
- }
241
- const res = await fetch(`${API_BASE_URL}/api/gas`, {
242
- method: "POST",
243
- headers: { "Content-Type": "application/json" },
244
- body: JSON.stringify(payload)
245
- });
246
- const data = await res.json();
247
- if (!res.ok) {
248
- const errorCode = data.error;
249
- if (errorCode === "CIRCUIT_BREAKER" || errorCode === "POOL_DEPLETED" || errorCode === "PRICE_STALE") {
250
- throw new T2000Error(
251
- "GAS_STATION_UNAVAILABLE",
252
- data.message ?? "Gas station temporarily unavailable",
253
- { retryAfter: data.retryAfter, reason: errorCode },
254
- true
255
- );
256
- }
257
- if (errorCode === "GAS_FEE_EXCEEDED") {
258
- throw new T2000Error(
259
- "GAS_FEE_EXCEEDED",
260
- data.message ?? "Gas fee exceeds ceiling",
261
- { retryAfter: data.retryAfter },
262
- true
263
- );
264
- }
265
- throw new T2000Error(
266
- "GAS_STATION_UNAVAILABLE",
267
- data.message ?? "Gas sponsorship request failed",
268
- { reason: errorCode },
269
- true
270
- );
271
- }
272
- return data;
273
- }
274
- async function reportGasUsage(sender, txDigest, gasCostSui, usdcCharged, type) {
275
- try {
276
- await fetch(`${API_BASE_URL}/api/gas/report`, {
277
- method: "POST",
278
- headers: { "Content-Type": "application/json" },
279
- body: JSON.stringify({ sender, txDigest, gasCostSui, usdcCharged, type })
280
- });
281
- } catch {
282
- }
283
- }
284
- async function getGasStatus(address) {
285
- const url = new URL(`${API_BASE_URL}/api/gas/status`);
286
- if (address) url.searchParams.set("address", address);
287
- const res = await fetch(url.toString());
288
- if (!res.ok) {
289
- throw new T2000Error("GAS_STATION_UNAVAILABLE", "Failed to fetch gas status", void 0, true);
290
- }
291
- return await res.json();
292
- }
293
-
294
- // src/gas/manager.ts
295
- function extractGasCost(effects) {
296
- if (!effects?.gasUsed) return 0;
297
- return (Number(effects.gasUsed.computationCost) + Number(effects.gasUsed.storageCost) - Number(effects.gasUsed.storageRebate)) / 1e9;
298
- }
299
- async function getSuiBalance(client, address) {
300
- const bal = await client.getBalance({ owner: address, coinType: SUPPORTED_ASSETS.SUI.type });
301
- return BigInt(bal.totalBalance);
302
- }
303
- async function assertTxSuccess(effects, digest) {
304
- const eff = effects;
305
- if (eff?.status?.status === "failure") {
306
- const errMsg = eff.status.error ?? "unknown on-chain error";
307
- if (isMoveAbort(errMsg)) {
308
- throw new T2000Error("TRANSACTION_FAILED", parseMoveAbortMessage(errMsg));
309
- }
310
- throw new T2000Error("TRANSACTION_FAILED", `Transaction ${digest} failed on-chain: ${errMsg}`);
311
- }
312
- }
313
- async function trySelfFunded(client, signer, tx) {
314
- const address = signer.getAddress();
315
- const suiBalance = await getSuiBalance(client, address);
316
- if (suiBalance < AUTO_TOPUP_THRESHOLD) return null;
317
- tx.setSender(address);
318
- const builtBytes = await tx.build({ client });
319
- const { signature } = await signer.signTransaction(builtBytes);
320
- const result = await client.executeTransactionBlock({
321
- transactionBlock: toBase64(builtBytes),
322
- signature: [signature],
323
- options: { showEffects: true, showBalanceChanges: true }
324
- });
325
- await client.waitForTransaction({ digest: result.digest });
326
- await assertTxSuccess(result.effects, result.digest);
327
- return {
328
- digest: result.digest,
329
- effects: result.effects,
330
- balanceChanges: result.balanceChanges,
331
- gasMethod: "self-funded",
332
- gasCostSui: extractGasCost(result.effects),
333
- preTxSuiMist: suiBalance
334
- };
335
- }
336
- async function tryAutoTopUpThenSelfFund(client, signer, buildTx) {
337
- const address = signer.getAddress();
338
- const canTopUp = await shouldAutoTopUp(client, address);
339
- if (!canTopUp) return null;
340
- await executeAutoTopUp();
341
- const tx = await buildTx();
342
- tx.setSender(address);
343
- const suiAfterTopUp = await getSuiBalance(client, address);
344
- const builtBytes = await tx.build({ client });
345
- const { signature } = await signer.signTransaction(builtBytes);
346
- const result = await client.executeTransactionBlock({
347
- transactionBlock: toBase64(builtBytes),
348
- signature: [signature],
349
- options: { showEffects: true, showBalanceChanges: true }
350
- });
351
- await client.waitForTransaction({ digest: result.digest });
352
- await assertTxSuccess(result.effects, result.digest);
353
- return {
354
- digest: result.digest,
355
- effects: result.effects,
356
- balanceChanges: result.balanceChanges,
357
- gasMethod: "auto-topup",
358
- gasCostSui: extractGasCost(result.effects),
359
- preTxSuiMist: suiAfterTopUp
360
- };
361
- }
362
- async function trySponsored(client, signer, tx) {
363
- const address = signer.getAddress();
364
- const suiBalance = await getSuiBalance(client, address);
365
- tx.setSender(address);
366
- let txJson;
367
- let txBcsBase64;
368
- try {
369
- txJson = tx.serialize();
370
- } catch {
371
- const bcsBytes = await tx.build({ client });
372
- txBcsBase64 = toBase64(bcsBytes);
373
- }
374
- const sponsoredResult = await requestGasSponsorship(txJson ?? "", address, void 0, txBcsBase64);
375
- const sponsoredTxBytes = fromBase64(sponsoredResult.txBytes);
376
- const { signature: agentSig } = await signer.signTransaction(sponsoredTxBytes);
377
- const result = await client.executeTransactionBlock({
378
- transactionBlock: sponsoredResult.txBytes,
379
- signature: [agentSig, sponsoredResult.sponsorSignature],
380
- options: { showEffects: true, showBalanceChanges: true }
381
- });
382
- await client.waitForTransaction({ digest: result.digest });
383
- await assertTxSuccess(result.effects, result.digest);
384
- const gasCost = extractGasCost(result.effects);
385
- reportGasUsage(address, result.digest, gasCost, 0, sponsoredResult.type);
386
- return {
387
- digest: result.digest,
388
- effects: result.effects,
389
- balanceChanges: result.balanceChanges,
390
- gasMethod: "sponsored",
391
- gasCostSui: gasCost,
392
- preTxSuiMist: suiBalance
393
- };
394
- }
395
- async function waitForIndexer(client, digest) {
396
- for (let i = 0; i < 3; i++) {
397
- try {
398
- await client.getTransactionBlock({ digest, options: { showObjectChanges: true } });
399
- return;
400
- } catch {
401
- await new Promise((r) => setTimeout(r, 500));
402
- }
403
- }
404
- }
405
- async function executeWithGas(client, signer, buildTx, options) {
406
- if (options?.enforcer && options?.metadata) {
407
- options.enforcer.check(options.metadata);
408
- }
409
- const result = await resolveGas(client, signer, buildTx);
410
- try {
411
- if (result.preTxSuiMist !== void 0) {
412
- const gasCostMist = result.gasMethod === "sponsored" ? 0n : BigInt(Math.round(result.gasCostSui * 1e9));
413
- const estimatedRemaining = result.preTxSuiMist - gasCostMist;
414
- if (estimatedRemaining < GAS_RESERVE_TARGET) {
415
- const address = signer.getAddress();
416
- const usdcBal = await client.getBalance({
417
- owner: address,
418
- coinType: SUPPORTED_ASSETS.USDC.type
419
- });
420
- if (BigInt(usdcBal.totalBalance) >= AUTO_TOPUP_MIN_USDC) {
421
- await executeAutoTopUp(client, signer);
422
- }
423
- }
424
- }
425
- } catch {
426
- }
427
- return result;
428
- }
429
- var GAS_RESOLUTION_CODES = /* @__PURE__ */ new Set([
430
- "INSUFFICIENT_GAS",
431
- "GAS_STATION_UNAVAILABLE",
432
- "GAS_FEE_EXCEEDED",
433
- "AUTO_TOPUP_FAILED",
434
- "SPONSOR_UNAVAILABLE"
435
- ]);
436
- function isBuildError(err) {
437
- return err instanceof T2000Error && !GAS_RESOLUTION_CODES.has(err.code);
438
- }
439
- async function resolveGas(client, signer, buildTx) {
440
- const errors = [];
441
- let lastBuildError;
442
- try {
443
- const tx = await buildTx();
444
- const result = await trySelfFunded(client, signer, tx);
445
- if (result) {
446
- await waitForIndexer(client, result.digest);
447
- return result;
448
- }
449
- errors.push("self-funded: SUI below threshold");
450
- } catch (err) {
451
- if (err instanceof T2000Error && err.code === "TRANSACTION_FAILED") throw err;
452
- const msg = err instanceof Error ? err.message : String(err);
453
- if (isMoveAbort(msg)) {
454
- throw new T2000Error("TRANSACTION_FAILED", parseMoveAbortMessage(msg));
455
- }
456
- if (isBuildError(err)) lastBuildError = err;
457
- errors.push(`self-funded: ${msg}`);
458
- }
459
- try {
460
- const result = await tryAutoTopUpThenSelfFund(client, signer, buildTx);
461
- if (result) {
462
- await waitForIndexer(client, result.digest);
463
- return result;
464
- }
465
- errors.push("auto-topup: not eligible (low USDC or sufficient SUI)");
466
- } catch (err) {
467
- if (err instanceof T2000Error && err.code === "TRANSACTION_FAILED") throw err;
468
- errors.push(`auto-topup: ${err instanceof Error ? err.message : String(err)}`);
469
- }
470
- try {
471
- const tx = await buildTx();
472
- const result = await trySelfFunded(client, signer, tx);
473
- if (result) {
474
- await waitForIndexer(client, result.digest);
475
- return result;
476
- }
477
- } catch (err) {
478
- if (err instanceof T2000Error && err.code === "TRANSACTION_FAILED") throw err;
479
- const msg = err instanceof Error ? err.message : String(err);
480
- if (isMoveAbort(msg)) {
481
- throw new T2000Error("TRANSACTION_FAILED", parseMoveAbortMessage(msg));
482
- }
483
- if (isBuildError(err)) lastBuildError = err;
484
- errors.push(`self-funded-retry: ${msg}`);
485
- }
486
- try {
487
- const tx = await buildTx();
488
- const result = await trySponsored(client, signer, tx);
489
- if (result) {
490
- await waitForIndexer(client, result.digest);
491
- return result;
492
- }
493
- errors.push("sponsored: returned null");
494
- } catch (err) {
495
- if (err instanceof T2000Error && err.code === "TRANSACTION_FAILED") throw err;
496
- if (isBuildError(err)) lastBuildError = err;
497
- errors.push(`sponsored: ${err instanceof Error ? err.message : String(err)}`);
498
- }
499
- if (lastBuildError) throw lastBuildError;
500
- throw new T2000Error(
501
- "INSUFFICIENT_GAS",
502
- `No SUI for gas and sponsorship unavailable. Fund your wallet with SUI or USDC. [${errors.join(" | ")}]`,
503
- { reason: "all_gas_methods_exhausted", errors }
504
- );
505
- }
506
176
  function validateAddress(address) {
507
177
  const normalized = utils.normalizeSuiAddress(address);
508
178
  if (!utils.isValidSuiAddress(normalized)) {
@@ -839,6 +509,19 @@ for (const [key, info] of Object.entries(SUPPORTED_ASSETS)) {
839
509
  }
840
510
  }
841
511
 
512
+ // src/utils/base64.ts
513
+ function toBase64(bytes) {
514
+ let binary = "";
515
+ for (const byte of bytes) binary += String.fromCharCode(byte);
516
+ return btoa(binary);
517
+ }
518
+ function fromBase64(b64) {
519
+ const binary = atob(b64);
520
+ const bytes = new Uint8Array(binary.length);
521
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
522
+ return bytes;
523
+ }
524
+
842
525
  // src/protocols/protocolFee.ts
843
526
  var FEE_RATES = {
844
527
  save: SAVE_FEE_BPS,
@@ -955,8 +638,6 @@ exports.calculateFee = calculateFee;
955
638
  exports.classifyAction = classifyAction;
956
639
  exports.classifyLabel = classifyLabel;
957
640
  exports.classifyTransaction = classifyTransaction;
958
- exports.executeAutoTopUp = executeAutoTopUp;
959
- exports.executeWithGas = executeWithGas;
960
641
  exports.extractTransferDetails = extractTransferDetails;
961
642
  exports.extractTxCommands = extractTxCommands;
962
643
  exports.extractTxSender = extractTxSender;
@@ -967,7 +648,6 @@ exports.formatUsd = formatUsd;
967
648
  exports.fromBase64 = fromBase64;
968
649
  exports.getDecimals = getDecimals;
969
650
  exports.getDecimalsForCoinType = getDecimalsForCoinType;
970
- exports.getGasStatus = getGasStatus;
971
651
  exports.getTier = getTier;
972
652
  exports.isSupported = isSupported;
973
653
  exports.isTier1 = isTier1;
@@ -981,7 +661,6 @@ exports.rawToUsdc = rawToUsdc;
981
661
  exports.refineLendingLabel = refineLendingLabel;
982
662
  exports.resolveSymbol = resolveSymbol;
983
663
  exports.resolveTokenType = resolveTokenType;
984
- exports.shouldAutoTopUp = shouldAutoTopUp;
985
664
  exports.stableToRaw = stableToRaw;
986
665
  exports.suiToMist = suiToMist;
987
666
  exports.toBase64 = toBase64;