@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/dist/index.cjs CHANGED
@@ -6,10 +6,10 @@ var BN = require('bn.js');
6
6
  var eventemitter3 = require('eventemitter3');
7
7
  var paymentKit = require('@mysten/payment-kit');
8
8
  var jsonRpc = require('@mysten/sui/jsonRpc');
9
+ var grpc = require('@mysten/sui/grpc');
9
10
  var utils = require('@mysten/sui/utils');
10
11
  var ed25519 = require('@mysten/sui/keypairs/ed25519');
11
12
  var cryptography = require('@mysten/sui/cryptography');
12
- var crypto$1 = require('crypto');
13
13
  var promises = require('fs/promises');
14
14
  var path = require('path');
15
15
  var os = require('os');
@@ -651,155 +651,6 @@ var require_lodash = __commonJS({
651
651
  }
652
652
  });
653
653
 
654
- // src/protocols/volo.ts
655
- var volo_exports = {};
656
- __export(volo_exports, {
657
- MIN_STAKE_MIST: () => MIN_STAKE_MIST,
658
- SUI_SYSTEM_STATE: () => SUI_SYSTEM_STATE,
659
- VOLO_METADATA: () => exports.VOLO_METADATA,
660
- VOLO_PKG: () => exports.VOLO_PKG,
661
- VOLO_POOL: () => exports.VOLO_POOL,
662
- VSUI_TYPE: () => exports.VSUI_TYPE,
663
- addStakeVSuiToTx: () => addStakeVSuiToTx,
664
- addUnstakeVSuiToTx: () => addUnstakeVSuiToTx,
665
- buildStakeVSuiTx: () => buildStakeVSuiTx,
666
- buildUnstakeVSuiTx: () => buildUnstakeVSuiTx,
667
- getVoloStats: () => getVoloStats
668
- });
669
- async function getVoloStats() {
670
- const res = await fetch(VOLO_STATS_URL, {
671
- signal: AbortSignal.timeout(8e3)
672
- });
673
- if (!res.ok) {
674
- throw new Error(`VOLO stats API error: HTTP ${res.status}`);
675
- }
676
- const data = await res.json();
677
- const d = data.data ?? data;
678
- return {
679
- apy: d.apy ?? 0,
680
- exchangeRate: d.exchange_rate ?? d.exchangeRate ?? 1,
681
- tvl: d.tvl ?? 0
682
- };
683
- }
684
- async function buildStakeVSuiTx(_client, address, amountMist) {
685
- if (amountMist < MIN_STAKE_MIST) {
686
- throw new Error(`Minimum stake is 1 SUI (${MIN_STAKE_MIST} MIST). Got: ${amountMist}`);
687
- }
688
- const tx = new transactions.Transaction();
689
- tx.setSender(address);
690
- const [suiCoin] = tx.splitCoins(tx.gas, [amountMist]);
691
- const [vSuiCoin] = tx.moveCall({
692
- target: `${exports.VOLO_PKG}::stake_pool::stake`,
693
- arguments: [
694
- tx.object(exports.VOLO_POOL),
695
- tx.object(exports.VOLO_METADATA),
696
- tx.object(SUI_SYSTEM_STATE),
697
- suiCoin
698
- ]
699
- });
700
- tx.transferObjects([vSuiCoin], address);
701
- return tx;
702
- }
703
- async function buildUnstakeVSuiTx(client, address, amountMist) {
704
- const balResp = await client.getBalance({ owner: address, coinType: exports.VSUI_TYPE });
705
- const totalBalance = BigInt(balResp.totalBalance);
706
- if (totalBalance === 0n) {
707
- throw new Error("No vSUI found in wallet.");
708
- }
709
- const tx = new transactions.Transaction();
710
- tx.setSender(address);
711
- const requested = amountMist === "all" ? totalBalance : amountMist;
712
- if (requested > totalBalance) {
713
- throw new Error(`Insufficient vSUI: need ${requested} MIST, have ${totalBalance}`);
714
- }
715
- const vSuiCoin = transactions.coinWithBalance({ type: exports.VSUI_TYPE, balance: requested })(tx);
716
- const [suiCoin] = tx.moveCall({
717
- target: `${exports.VOLO_PKG}::stake_pool::unstake`,
718
- arguments: [
719
- tx.object(exports.VOLO_POOL),
720
- tx.object(exports.VOLO_METADATA),
721
- tx.object(SUI_SYSTEM_STATE),
722
- vSuiCoin
723
- ]
724
- });
725
- tx.transferObjects([suiCoin], address);
726
- return tx;
727
- }
728
- async function addStakeVSuiToTx(tx, client, address, input) {
729
- if (input.amountMist < MIN_STAKE_MIST) {
730
- throw new Error(`Minimum stake is 1 SUI (${MIN_STAKE_MIST} MIST). Got: ${input.amountMist}`);
731
- }
732
- let suiCoin;
733
- if (input.inputCoin) {
734
- suiCoin = input.inputCoin;
735
- } else {
736
- const balResp = await client.getBalance({ owner: address, coinType: exports.SUI_TYPE });
737
- const totalBalance = BigInt(balResp.totalBalance);
738
- if (totalBalance < input.amountMist) {
739
- throw new Error(`Insufficient SUI: need ${input.amountMist} MIST, have ${totalBalance}`);
740
- }
741
- suiCoin = transactions.coinWithBalance({
742
- type: exports.SUI_TYPE,
743
- balance: input.amountMist,
744
- useGasCoin: false
745
- })(tx);
746
- }
747
- const [vSuiCoin] = tx.moveCall({
748
- target: `${exports.VOLO_PKG}::stake_pool::stake`,
749
- arguments: [
750
- tx.object(exports.VOLO_POOL),
751
- tx.object(exports.VOLO_METADATA),
752
- tx.object(SUI_SYSTEM_STATE),
753
- suiCoin
754
- ]
755
- });
756
- return { coin: vSuiCoin, effectiveAmountMist: input.amountMist };
757
- }
758
- async function addUnstakeVSuiToTx(tx, client, address, input) {
759
- let vSuiCoin;
760
- if (input.inputCoin) {
761
- if (input.amountMist === "all") {
762
- vSuiCoin = input.inputCoin;
763
- } else {
764
- [vSuiCoin] = tx.splitCoins(input.inputCoin, [input.amountMist]);
765
- }
766
- } else {
767
- const balResp = await client.getBalance({ owner: address, coinType: exports.VSUI_TYPE });
768
- const totalBalance = BigInt(balResp.totalBalance);
769
- if (totalBalance === 0n) {
770
- throw new Error("No vSUI found in wallet.");
771
- }
772
- const requested = input.amountMist === "all" ? totalBalance : input.amountMist;
773
- if (requested > totalBalance) {
774
- throw new Error(`Insufficient vSUI: need ${requested} MIST, have ${totalBalance}`);
775
- }
776
- vSuiCoin = transactions.coinWithBalance({ type: exports.VSUI_TYPE, balance: requested })(tx);
777
- }
778
- const [suiCoin] = tx.moveCall({
779
- target: `${exports.VOLO_PKG}::stake_pool::unstake`,
780
- arguments: [
781
- tx.object(exports.VOLO_POOL),
782
- tx.object(exports.VOLO_METADATA),
783
- tx.object(SUI_SYSTEM_STATE),
784
- vSuiCoin
785
- ]
786
- });
787
- return { coin: suiCoin, effectiveAmountMist: input.amountMist };
788
- }
789
- exports.VOLO_PKG = void 0; exports.VOLO_POOL = void 0; exports.VOLO_METADATA = void 0; exports.VSUI_TYPE = void 0; var SUI_SYSTEM_STATE, MIN_STAKE_MIST, VOLO_STATS_URL;
790
- var init_volo = __esm({
791
- "src/protocols/volo.ts"() {
792
- init_token_registry();
793
- exports.VOLO_PKG = "0x68d22cf8bdbcd11ecba1e094922873e4080d4d11133e2443fddda0bfd11dae20";
794
- exports.VOLO_POOL = "0x2d914e23d82fedef1b5f56a32d5c64bdcc3087ccfea2b4d6ea51a71f587840e5";
795
- exports.VOLO_METADATA = "0x680cd26af32b2bde8d3361e804c53ec1d1cfe24c7f039eb7f549e8dfde389a60";
796
- exports.VSUI_TYPE = "0x549e8b69270defbfafd4f94e17ec44cdbdd99820b33bda2278dea3b9a32d3f55::cert::CERT";
797
- SUI_SYSTEM_STATE = "0x05";
798
- MIN_STAKE_MIST = 1000000000n;
799
- VOLO_STATS_URL = "https://open-api.naviprotocol.io/api/volo/stats";
800
- }
801
- });
802
-
803
654
  // src/wallet/coinSelection.ts
804
655
  var coinSelection_exports = {};
805
656
  __export(coinSelection_exports, {
@@ -1277,7 +1128,7 @@ var OPERATION_ASSETS = {
1277
1128
  borrow: ["USDC", "USDsui"],
1278
1129
  withdraw: "*",
1279
1130
  repay: "*",
1280
- send: "*",
1131
+ send: ["USDC", "USDsui", "SUI"],
1281
1132
  swap: "*"
1282
1133
  };
1283
1134
  function isAllowedAsset(op, asset) {
@@ -1291,28 +1142,62 @@ function assertAllowedAsset(op, asset) {
1291
1142
  if (!isAllowedAsset(op, asset)) {
1292
1143
  const allowed = OPERATION_ASSETS[op];
1293
1144
  const list = Array.isArray(allowed) ? allowed.join(", ") : "any";
1145
+ const swapHint = op === "save" ? " Swap to USDC or USDsui first." : op === "send" ? " Swap to USDC or USDsui first, or send SUI." : "";
1294
1146
  throw new exports.T2000Error(
1295
1147
  "INVALID_ASSET",
1296
- `${op} only supports ${list}. Cannot use ${asset}.${op === "save" ? " Swap to USDC or USDsui first." : ""}`
1148
+ `${op} only supports ${list}. Cannot use ${asset}.${swapHint}`
1297
1149
  );
1298
1150
  }
1299
1151
  }
1152
+ var SENDABLE_ASSETS = ["USDC", "USDsui", "SUI"];
1153
+ var GASLESS_STABLE_TYPES = {
1154
+ USDC: SUPPORTED_ASSETS.USDC.type,
1155
+ USDsui: SUPPORTED_ASSETS.USDsui.type
1156
+ };
1300
1157
  var T2000_OVERLAY_FEE_WALLET = process.env.T2000_OVERLAY_FEE_WALLET ?? "0x5366efbf2b4fe5767fe2e78eb197aa5f5d138d88ac3333fbf3f80a1927da473a";
1301
1158
  var DEFAULT_NETWORK = "mainnet";
1302
1159
  var DEFAULT_RPC_URL = "https://fullnode.mainnet.sui.io:443";
1160
+ var DEFAULT_GRPC_URL = "https://fullnode.mainnet.sui.io:443";
1303
1161
  var DEFAULT_KEY_PATH = "~/.t2000/wallet.key";
1162
+ var GASLESS_MIN_STABLE_AMOUNT = 0.01;
1304
1163
  process.env.T2000_API_URL ?? "https://api.t2000.ai";
1305
1164
  var CETUS_USDC_SUI_POOL = "0x51e883ba7c0b566a26cbc8a94cd33eb0abd418a77cc1e60ad22fd9b1f29cd2ab";
1306
1165
  var GAS_RESERVE_MIN = 0.05;
1307
1166
 
1308
1167
  // src/utils/sui.ts
1309
1168
  init_errors();
1310
- var cachedClient = null;
1169
+ function resolveRpcUrl(rpcUrl) {
1170
+ if (rpcUrl) return rpcUrl;
1171
+ const envUrl = process.env.T2000_RPC_URL?.trim();
1172
+ if (envUrl) return envUrl;
1173
+ return DEFAULT_RPC_URL;
1174
+ }
1175
+ function resolveGrpcUrl(grpcUrl) {
1176
+ if (grpcUrl) return grpcUrl;
1177
+ const envUrl = process.env.T2000_GRPC_URL?.trim();
1178
+ if (envUrl) return envUrl;
1179
+ return DEFAULT_GRPC_URL;
1180
+ }
1181
+ var rpcClientCache = /* @__PURE__ */ new Map();
1311
1182
  function getSuiClient(rpcUrl) {
1312
- const url = rpcUrl ?? DEFAULT_RPC_URL;
1313
- if (cachedClient) return cachedClient;
1314
- cachedClient = new jsonRpc.SuiJsonRpcClient({ url, network: "mainnet" });
1315
- return cachedClient;
1183
+ const url = resolveRpcUrl(rpcUrl);
1184
+ const cached = rpcClientCache.get(url);
1185
+ if (cached) return cached;
1186
+ const client = new jsonRpc.SuiJsonRpcClient({ url, network: "mainnet" });
1187
+ rpcClientCache.set(url, client);
1188
+ return client;
1189
+ }
1190
+ function createSuiClient(network = "mainnet") {
1191
+ return new jsonRpc.SuiJsonRpcClient({ url: jsonRpc.getJsonRpcFullnodeUrl(network), network });
1192
+ }
1193
+ var grpcClientCache = /* @__PURE__ */ new Map();
1194
+ function getSuiGrpcClient(grpcUrl) {
1195
+ const baseUrl = resolveGrpcUrl(grpcUrl);
1196
+ const cached = grpcClientCache.get(baseUrl);
1197
+ if (cached) return cached;
1198
+ const client = new grpc.SuiGrpcClient({ baseUrl, network: "mainnet" });
1199
+ grpcClientCache.set(baseUrl, client);
1200
+ return client;
1316
1201
  }
1317
1202
  function validateAddress(address) {
1318
1203
  const normalized = utils.normalizeSuiAddress(address);
@@ -1335,49 +1220,10 @@ function normalizeCoinType(coinType) {
1335
1220
 
1336
1221
  // src/wallet/keyManager.ts
1337
1222
  init_errors();
1338
- var ALGORITHM = "aes-256-gcm";
1339
- var SCRYPT_N = 2 ** 14;
1340
- var SCRYPT_R = 8;
1341
- var SCRYPT_P = 1;
1342
- var SALT_LENGTH = 32;
1343
- var IV_LENGTH = 16;
1344
1223
  function expandPath(p) {
1345
1224
  if (p.startsWith("~")) return path.resolve(os.homedir(), p.slice(2));
1346
1225
  return path.resolve(p);
1347
1226
  }
1348
- function deriveKey(passphrase, salt) {
1349
- return crypto$1.scryptSync(passphrase, salt, 32, { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P });
1350
- }
1351
- function encrypt(data, passphrase) {
1352
- const salt = crypto$1.randomBytes(SALT_LENGTH);
1353
- const key = deriveKey(passphrase, salt);
1354
- const iv = crypto$1.randomBytes(IV_LENGTH);
1355
- const cipher = crypto$1.createCipheriv(ALGORITHM, key, iv);
1356
- const ciphertext = Buffer.concat([cipher.update(data), cipher.final()]);
1357
- const tag = cipher.getAuthTag();
1358
- return {
1359
- version: 1,
1360
- algorithm: ALGORITHM,
1361
- salt: salt.toString("hex"),
1362
- iv: iv.toString("hex"),
1363
- tag: tag.toString("hex"),
1364
- ciphertext: ciphertext.toString("hex")
1365
- };
1366
- }
1367
- function decrypt(encrypted, passphrase) {
1368
- const salt = Buffer.from(encrypted.salt, "hex");
1369
- const key = deriveKey(passphrase, salt);
1370
- const iv = Buffer.from(encrypted.iv, "hex");
1371
- const tag = Buffer.from(encrypted.tag, "hex");
1372
- const ciphertext = Buffer.from(encrypted.ciphertext, "hex");
1373
- const decipher = crypto$1.createDecipheriv(ALGORITHM, key, iv);
1374
- decipher.setAuthTag(tag);
1375
- try {
1376
- return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
1377
- } catch {
1378
- throw new exports.T2000Error("WALLET_LOCKED", "Invalid PIN");
1379
- }
1380
- }
1381
1227
  function generateKeypair() {
1382
1228
  return ed25519.Ed25519Keypair.generate();
1383
1229
  }
@@ -1389,7 +1235,7 @@ function keypairFromPrivateKey(privateKey) {
1389
1235
  const bytes = Buffer.from(privateKey.replace(/^0x/, ""), "hex");
1390
1236
  return ed25519.Ed25519Keypair.fromSecretKey(bytes);
1391
1237
  }
1392
- async function saveKey(keypair, passphrase, keyPath) {
1238
+ async function saveKey(keypair, _passphrase, keyPath) {
1393
1239
  const filePath = expandPath(keyPath ?? DEFAULT_KEY_PATH);
1394
1240
  try {
1395
1241
  await promises.access(filePath);
@@ -1398,12 +1244,34 @@ async function saveKey(keypair, passphrase, keyPath) {
1398
1244
  if (error instanceof exports.T2000Error) throw error;
1399
1245
  }
1400
1246
  await promises.mkdir(path.dirname(filePath), { recursive: true });
1401
- const bech32Key = keypair.getSecretKey();
1402
- const encrypted = encrypt(Buffer.from(bech32Key, "utf-8"), passphrase);
1403
- await promises.writeFile(filePath, JSON.stringify(encrypted, null, 2), { mode: 384 });
1247
+ const payload = {
1248
+ version: 2,
1249
+ secret: keypair.getSecretKey()
1250
+ };
1251
+ await promises.writeFile(filePath, JSON.stringify(payload, null, 2), { mode: 384 });
1404
1252
  return filePath;
1405
1253
  }
1406
- async function loadKey(passphrase, keyPath) {
1254
+ async function saveBech32(secret, keyPath) {
1255
+ if (!secret.startsWith("suiprivkey")) {
1256
+ throw new exports.T2000Error(
1257
+ "INVALID_KEY",
1258
+ `Secret must be a Bech32 suiprivkey1... string. Got: ${secret.slice(0, 12)}...`
1259
+ );
1260
+ }
1261
+ cryptography.decodeSuiPrivateKey(secret);
1262
+ const filePath = expandPath(keyPath ?? DEFAULT_KEY_PATH);
1263
+ try {
1264
+ await promises.access(filePath);
1265
+ throw new exports.T2000Error("WALLET_EXISTS", `Wallet already exists at ${filePath}`);
1266
+ } catch (error) {
1267
+ if (error instanceof exports.T2000Error) throw error;
1268
+ }
1269
+ await promises.mkdir(path.dirname(filePath), { recursive: true });
1270
+ const payload = { version: 2, secret };
1271
+ await promises.writeFile(filePath, JSON.stringify(payload, null, 2), { mode: 384 });
1272
+ return filePath;
1273
+ }
1274
+ async function loadKey(_passphrase, keyPath) {
1407
1275
  const filePath = expandPath(keyPath ?? DEFAULT_KEY_PATH);
1408
1276
  let content;
1409
1277
  try {
@@ -1411,10 +1279,22 @@ async function loadKey(passphrase, keyPath) {
1411
1279
  } catch {
1412
1280
  throw new exports.T2000Error("WALLET_NOT_FOUND", `No wallet found at ${filePath}`);
1413
1281
  }
1414
- const encrypted = JSON.parse(content);
1415
- const decrypted = decrypt(encrypted, passphrase);
1416
- const bech32Key = decrypted.toString("utf-8");
1417
- const decoded = cryptography.decodeSuiPrivateKey(bech32Key);
1282
+ let parsed;
1283
+ try {
1284
+ parsed = JSON.parse(content);
1285
+ } catch {
1286
+ throw new exports.T2000Error(
1287
+ "WALLET_CORRUPT",
1288
+ `Wallet file at ${filePath} is not a valid v4 wallet. Move or delete the file, then run \`t2 init\`.`
1289
+ );
1290
+ }
1291
+ if (!isPlainKey(parsed)) {
1292
+ throw new exports.T2000Error(
1293
+ "WALLET_CORRUPT",
1294
+ `Wallet file at ${filePath} is not a valid v4 wallet. Expected { version: 2, secret: "suiprivkey1..." }. Move or delete the file, then run \`t2 init\`.`
1295
+ );
1296
+ }
1297
+ const decoded = cryptography.decodeSuiPrivateKey(parsed.secret);
1418
1298
  return ed25519.Ed25519Keypair.fromSecretKey(decoded.secretKey);
1419
1299
  }
1420
1300
  async function walletExists(keyPath) {
@@ -1432,6 +1312,9 @@ function exportPrivateKey(keypair) {
1432
1312
  function getAddress(keypair) {
1433
1313
  return keypair.getPublicKey().toSuiAddress();
1434
1314
  }
1315
+ function isPlainKey(value) {
1316
+ return typeof value === "object" && value !== null && value.version === 2 && typeof value.secret === "string";
1317
+ }
1435
1318
 
1436
1319
  // src/wallet/keypairSigner.ts
1437
1320
  var KeypairSigner = class {
@@ -1539,12 +1422,19 @@ async function buildSendTx({
1539
1422
  address,
1540
1423
  to,
1541
1424
  amount,
1542
- asset = "USDC"
1425
+ asset
1543
1426
  }) {
1427
+ assertAllowedAsset("send", asset);
1544
1428
  const recipient = validateAddress(to);
1545
1429
  const assetInfo = SUPPORTED_ASSETS[asset];
1546
1430
  if (!assetInfo) throw new exports.T2000Error("ASSET_NOT_SUPPORTED", `Asset ${asset} is not supported`);
1547
1431
  if (amount <= 0) throw new exports.T2000Error("INVALID_AMOUNT", "Amount must be greater than zero");
1432
+ if ((asset === "USDC" || asset === "USDsui") && amount < GASLESS_MIN_STABLE_AMOUNT) {
1433
+ throw new exports.T2000Error(
1434
+ "INVALID_AMOUNT",
1435
+ `Minimum gasless transfer is ${GASLESS_MIN_STABLE_AMOUNT} ${asset}. Got ${amount}.`
1436
+ );
1437
+ }
1548
1438
  const rawAmount = displayToRaw(amount, assetInfo.decimals);
1549
1439
  const tx = new transactions.Transaction();
1550
1440
  tx.setSender(address);
@@ -1556,8 +1446,20 @@ async function buildSendTx({
1556
1446
  required: amount
1557
1447
  });
1558
1448
  }
1559
- const sendCoin = asset === "SUI" ? tx.splitCoins(tx.gas, [rawAmount])[0] : transactions.coinWithBalance({ type: assetInfo.type, balance: rawAmount })(tx);
1560
- tx.transferObjects([sendCoin], recipient);
1449
+ if (asset === "SUI") {
1450
+ const [sendCoin] = tx.splitCoins(tx.gas, [rawAmount]);
1451
+ tx.transferObjects([sendCoin], recipient);
1452
+ return tx;
1453
+ }
1454
+ const coinType = GASLESS_STABLE_TYPES[asset];
1455
+ tx.moveCall({
1456
+ target: "0x2::balance::send_funds",
1457
+ typeArguments: [coinType],
1458
+ arguments: [
1459
+ tx.balance({ type: coinType, balance: rawAmount }),
1460
+ tx.pure.address(recipient)
1461
+ ]
1462
+ });
1561
1463
  return tx;
1562
1464
  }
1563
1465
  function addSendToTx(tx, coin, recipient) {
@@ -6527,8 +6429,7 @@ var ContactManager = class {
6527
6429
  "CONTACT_NOT_FOUND",
6528
6430
  `"${nameOrAddress}" is not a valid Sui address or saved contact.
6529
6431
  Use a SuiNS name (e.g. alex.sui \u2014 register at https://suins.io)
6530
- or paste the full Sui address (0x... 64 hex characters).
6531
- Legacy contact aliases: \`t2000 contacts add ${nameOrAddress} 0x...\` (deprecated).`
6432
+ or paste the full Sui address (0x... 64 hex characters).`
6532
6433
  );
6533
6434
  }
6534
6435
  validateName(name) {
@@ -6600,20 +6501,16 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
6600
6501
  return registry;
6601
6502
  }
6602
6503
  static async create(options = {}) {
6603
- const { keyPath, pin, passphrase, rpcUrl } = options;
6604
- const secret = pin ?? passphrase;
6504
+ const { keyPath, rpcUrl } = options;
6605
6505
  const client = getSuiClient(rpcUrl);
6606
6506
  const exists = await walletExists(keyPath);
6607
6507
  if (!exists) {
6608
6508
  throw new exports.T2000Error(
6609
6509
  "WALLET_NOT_FOUND",
6610
- "No wallet found. Run `t2000 init` to create one."
6510
+ "No wallet found. Run `t2 init` to create one."
6611
6511
  );
6612
6512
  }
6613
- if (!secret) {
6614
- throw new exports.T2000Error("WALLET_LOCKED", "PIN required to unlock wallet");
6615
- }
6616
- const keypair = await loadKey(secret, keyPath);
6513
+ const keypair = await loadKey(void 0, keyPath);
6617
6514
  return new _T2000(keypair, client, void 0, DEFAULT_CONFIG_DIR);
6618
6515
  }
6619
6516
  static fromPrivateKey(privateKey, options = {}) {
@@ -6621,10 +6518,9 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
6621
6518
  const client = getSuiClient(options.rpcUrl);
6622
6519
  return new _T2000(keypair, client);
6623
6520
  }
6624
- static async init(options) {
6625
- const secret = options.pin ?? options.passphrase ?? "";
6521
+ static async init(options = {}) {
6626
6522
  const keypair = generateKeypair();
6627
- await saveKey(keypair, secret, options.keyPath);
6523
+ await saveKey(keypair, void 0, options.keyPath);
6628
6524
  const client = getSuiClient();
6629
6525
  const agent = new _T2000(keypair, client, void 0, DEFAULT_CONFIG_DIR);
6630
6526
  const address = agent.address();
@@ -6652,7 +6548,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
6652
6548
  this.enforcer.check({ operation: "pay", amount: options.maxPrice ?? 1 });
6653
6549
  const { Mppx } = await import('mppx/client');
6654
6550
  const { sui, USDC } = await import('@suimpp/mpp/client');
6655
- const { SuiGrpcClient } = await import('@mysten/sui/grpc');
6551
+ const { SuiGrpcClient: SuiGrpcClient2 } = await import('@mysten/sui/grpc');
6656
6552
  const client = this.client;
6657
6553
  const signer = this._signer;
6658
6554
  const signerAddress = signer.getAddress();
@@ -6660,7 +6556,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
6660
6556
  let gasCostSui = 0;
6661
6557
  const network = client.network === "testnet" ? "testnet" : "mainnet";
6662
6558
  const grpcBaseUrl = network === "testnet" ? "https://fullnode.testnet.sui.io" : "https://fullnode.mainnet.sui.io";
6663
- const grpcClient = new SuiGrpcClient({ baseUrl: grpcBaseUrl, network });
6559
+ const grpcClient = new SuiGrpcClient2({ baseUrl: grpcBaseUrl, network });
6664
6560
  const mppx = Mppx.create({
6665
6561
  polyfill: false,
6666
6562
  methods: [sui({
@@ -6705,51 +6601,14 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
6705
6601
  receipt: paymentDigest ? { reference: paymentDigest, timestamp: (/* @__PURE__ */ new Date()).toISOString() } : void 0
6706
6602
  };
6707
6603
  }
6708
- // -- VOLO vSUI Staking --
6709
- async stakeVSui(params) {
6710
- this.enforcer.assertNotLocked();
6711
- const { buildStakeVSuiTx: buildStakeVSuiTx2, getVoloStats: getVoloStats2 } = await Promise.resolve().then(() => (init_volo(), volo_exports));
6712
- const amountMist = BigInt(Math.floor(params.amount * Number(MIST_PER_SUI)));
6713
- const stats = await getVoloStats2();
6714
- const gasResult = await executeTx(this.client, this._signer, async () => {
6715
- return buildStakeVSuiTx2(this.client, this._address, amountMist);
6716
- });
6717
- const vSuiReceived = params.amount / stats.exchangeRate;
6718
- return {
6719
- success: true,
6720
- tx: gasResult.digest,
6721
- amountSui: params.amount,
6722
- vSuiReceived,
6723
- apy: stats.apy,
6724
- gasCost: gasResult.gasCostSui
6725
- };
6726
- }
6727
- async unstakeVSui(params) {
6728
- this.enforcer.assertNotLocked();
6729
- const { buildUnstakeVSuiTx: buildUnstakeVSuiTx2, getVoloStats: getVoloStats2, VSUI_TYPE: VSUI_TYPE2 } = await Promise.resolve().then(() => (init_volo(), volo_exports));
6730
- let amountMist;
6731
- let vSuiAmount;
6732
- if (params.amount === "all") {
6733
- amountMist = "all";
6734
- const bal = await this.client.getBalance({ owner: this._address, coinType: VSUI_TYPE2 });
6735
- vSuiAmount = Number(bal.totalBalance) / 1e9;
6736
- } else {
6737
- amountMist = BigInt(Math.floor(params.amount * 1e9));
6738
- vSuiAmount = params.amount;
6739
- }
6740
- const stats = await getVoloStats2();
6741
- const gasResult = await executeTx(this.client, this._signer, async () => {
6742
- return buildUnstakeVSuiTx2(this.client, this._address, amountMist);
6743
- });
6744
- const suiReceived = vSuiAmount * stats.exchangeRate;
6745
- return {
6746
- success: true,
6747
- tx: gasResult.digest,
6748
- vSuiAmount,
6749
- suiReceived,
6750
- gasCost: gasResult.gasCostSui
6751
- };
6752
- }
6604
+ // [S.323 / 2026-05-25] VOLO vSUI staking surfaces removed (full cut).
6605
+ // Engine cut Volo in S.277; SDK + CLI + MCP followed in S.323 because the
6606
+ // product surface (five products: Passport / Intelligence / Finance / Pay
6607
+ // / Store) doesn't include a staking primitive. vSUI still appears in the
6608
+ // codebase as a passive token (NAVI reward rewards, Cetus swap routing),
6609
+ // but there is no longer any way to MINT or REDEEM vSUI through t2000.
6610
+ // History: see spec/archive/v07e/AUDIT_V07E_EARNS_ITS_KEEP_2026-05-23.md
6611
+ // and the S.323 build-tracker entry.
6753
6612
  // -- Swap --
6754
6613
  async swap(params) {
6755
6614
  this.enforcer.assertNotLocked();
@@ -6880,23 +6739,52 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
6880
6739
  address() {
6881
6740
  return this._address;
6882
6741
  }
6742
+ /**
6743
+ * Send `amount` of `asset` to `to` (hex address, SuiNS name, or
6744
+ * `@audric` handle / saved contact).
6745
+ *
6746
+ * [v4.0 Phase A Day 2 — SPEC_AGENT_WALLET_GREENFIELD §A]
6747
+ *
6748
+ * **Breaking changes from v3.x:**
6749
+ * - `asset` is now REQUIRED (no implicit `?? 'USDC'` default). Callers
6750
+ * must specify `'USDC' | 'USDsui' | 'SUI'`. Sending `'USDT'` /
6751
+ * `'USDe'` / `'WAL'` / `'ETH'` / `'NAVX'` / `'GOLD'` now errors
6752
+ * with `INVALID_ASSET` — swap to a stable first.
6753
+ * - USDC + USDsui builds go through `SuiGrpcClient` so the gRPC build
6754
+ * resolver auto-detects `0x2::balance::send_funds` eligibility and
6755
+ * zeros gas at simulate time. Result: **gasless USDC / USDsui sends
6756
+ * from a zero-SUI wallet.** SUI sends stay on the standard gas-paid
6757
+ * path.
6758
+ *
6759
+ * Submission stays on the JSON-RPC client (the rest of the SDK
6760
+ * expects JSON-RPC for read paths, and Sui's docs explicitly support
6761
+ * the "build via gRPC, execute via JSON-RPC" hybrid).
6762
+ */
6883
6763
  async send(params) {
6884
6764
  this.enforcer.assertNotLocked();
6885
- const asset = params.asset ?? "USDC";
6886
- if (!(asset in SUPPORTED_ASSETS)) {
6887
- throw new exports.T2000Error("ASSET_NOT_SUPPORTED", `Asset ${asset} is not supported`);
6765
+ const asset = params.asset;
6766
+ if (!asset) {
6767
+ throw new exports.T2000Error(
6768
+ "INVALID_ASSET",
6769
+ "send() requires an explicit asset. Use one of: USDC, USDsui, SUI."
6770
+ );
6888
6771
  }
6772
+ assertAllowedAsset("send", asset);
6773
+ const sendableAsset = asset;
6889
6774
  const resolved = await this.resolveRecipient(params.to);
6890
6775
  const sendAmount = params.amount;
6891
6776
  const sendTo = resolved.address;
6777
+ const useGrpc = sendableAsset === "USDC" || sendableAsset === "USDsui";
6778
+ const buildClient = useGrpc ? getSuiGrpcClient() : void 0;
6892
6779
  const gasResult = await executeTx(
6893
6780
  this.client,
6894
6781
  this._signer,
6895
- () => buildSendTx({ client: this.client, address: this._address, to: sendTo, amount: sendAmount, asset })
6782
+ () => buildSendTx({ client: this.client, address: this._address, to: sendTo, amount: sendAmount, asset: sendableAsset }),
6783
+ { buildClient }
6896
6784
  );
6897
6785
  this.enforcer.recordUsage(sendAmount);
6898
6786
  const balance = await this.balance();
6899
- this.emitBalanceChange(asset, sendAmount, "send", gasResult.digest);
6787
+ this.emitBalanceChange(sendableAsset, sendAmount, "send", gasResult.digest);
6900
6788
  return {
6901
6789
  success: true,
6902
6790
  tx: gasResult.digest,
@@ -7006,11 +6894,12 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
7006
6894
  };
7007
6895
  }
7008
6896
  /**
7009
- * [SPEC_AGENTIC_STACK P1 / SDK F2 (CLI rename support) 2026-05-25]
7010
- * Preferred alias of `deposit()`. The CLI surface is `t2000 fund` post-Phase 1
7011
- * (more intuitive than "deposit" which sounds like a NAVI lending action).
7012
- * `deposit()` stays as the canonical method name for back-compat; it is NOT
7013
- * deprecated. This wrapper exists so SDK consumers can mirror CLI naming.
6897
+ * [SPEC_AGENTIC_STACK P1 / SDK F2 2026-05-25; refreshed S.342 / 2026-05-26]
6898
+ * Preferred alias of `deposit()`. Was introduced to mirror the v3 `t2000 fund`
6899
+ * CLI command; the v4 CLI surface is `t2 receive` (deleted `fund` in the
6900
+ * S.332 bulk cut). `deposit()` stays as the canonical method name for
6901
+ * back-compat; `fund()` stays as a programmatic alias for audric + other
6902
+ * SDK consumers that prefer the verb.
7014
6903
  */
7015
6904
  async fund() {
7016
6905
  return this.deposit();
@@ -7345,7 +7234,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
7345
7234
  const adapter = await this.resolveLending(params.protocol, asset, "borrow");
7346
7235
  const maxResult = await adapter.maxBorrow(this._address, asset);
7347
7236
  if (maxResult.maxAmount <= 0) {
7348
- throw new exports.T2000Error("NO_COLLATERAL", "No collateral deposited. Save first with `t2000 save <amount>`.");
7237
+ throw new exports.T2000Error("NO_COLLATERAL", "No collateral deposited. Make a save deposit first.");
7349
7238
  }
7350
7239
  if (params.amount > maxResult.maxAmount) {
7351
7240
  throw new exports.T2000Error("HEALTH_FACTOR_TOO_LOW", `Max safe borrow: $${maxResult.maxAmount.toFixed(2)}. Only savings deposits count as borrowable collateral.`, {
@@ -7976,7 +7865,6 @@ async function buildHarvestRewardsTx(client, address, options = {}) {
7976
7865
 
7977
7866
  // src/composeTx.ts
7978
7867
  init_cetus_swap();
7979
- init_volo();
7980
7868
  init_coinSelection();
7981
7869
  init_token_registry();
7982
7870
  init_errors();
@@ -8104,34 +7992,68 @@ var WRITE_APPENDER_REGISTRY = {
8104
7992
  },
8105
7993
  send_transfer: async (tx, input, ctx) => {
8106
7994
  const recipient = validateAddress(input.to);
8107
- const asset = input.asset ?? "USDC";
8108
- const assetInfo = SUPPORTED_ASSETS[asset];
8109
- if (!assetInfo) {
8110
- throw new exports.T2000Error("ASSET_NOT_SUPPORTED", `Asset ${asset} is not supported`);
7995
+ if (!input.asset) {
7996
+ throw new exports.T2000Error(
7997
+ "INVALID_ASSET",
7998
+ "send_transfer requires an explicit asset. Use one of: USDC, USDsui, SUI."
7999
+ );
8111
8000
  }
8001
+ assertAllowedAsset("send", input.asset);
8002
+ const asset = input.asset;
8003
+ const assetInfo = SUPPORTED_ASSETS[asset];
8112
8004
  if (input.amount <= 0) {
8113
8005
  throw new exports.T2000Error("INVALID_AMOUNT", "Send amount must be greater than zero");
8114
8006
  }
8115
8007
  const rawAmount = BigInt(Math.floor(input.amount * 10 ** assetInfo.decimals));
8116
- let coin;
8117
- let effectiveRaw;
8118
8008
  if (ctx.chainedCoin) {
8119
- coin = ctx.chainedCoin;
8120
- effectiveRaw = rawAmount;
8121
- } else if (asset === "SUI") {
8009
+ addSendToTx(tx, ctx.chainedCoin, recipient);
8010
+ return {
8011
+ preview: {
8012
+ toolName: "send_transfer",
8013
+ effectiveAmount: Number(rawAmount) / 10 ** assetInfo.decimals,
8014
+ recipient,
8015
+ asset
8016
+ }
8017
+ };
8018
+ }
8019
+ if (asset === "SUI") {
8122
8020
  const result = await selectSuiCoin(tx, ctx.client, ctx.sender, rawAmount, ctx.sponsoredContext);
8123
- coin = result.coin;
8124
- effectiveRaw = result.effectiveAmount;
8125
- } else {
8126
- const result = await selectAndSplitCoin(tx, ctx.client, ctx.sender, assetInfo.type, rawAmount);
8127
- coin = result.coin;
8128
- effectiveRaw = result.effectiveAmount;
8021
+ addSendToTx(tx, result.coin, recipient);
8022
+ return {
8023
+ preview: {
8024
+ toolName: "send_transfer",
8025
+ effectiveAmount: Number(result.effectiveAmount) / 10 ** assetInfo.decimals,
8026
+ recipient,
8027
+ asset
8028
+ }
8029
+ };
8030
+ }
8031
+ if (input.amount < GASLESS_MIN_STABLE_AMOUNT) {
8032
+ throw new exports.T2000Error(
8033
+ "INVALID_AMOUNT",
8034
+ `Minimum gasless transfer is ${GASLESS_MIN_STABLE_AMOUNT} ${asset}. Got ${input.amount}.`
8035
+ );
8036
+ }
8037
+ const balanceResp = await ctx.client.getBalance({ owner: ctx.sender, coinType: assetInfo.type });
8038
+ if (BigInt(balanceResp.totalBalance) < rawAmount) {
8039
+ throw new exports.T2000Error("INSUFFICIENT_BALANCE", `Insufficient ${asset} balance`, {
8040
+ available: Number(balanceResp.totalBalance) / 10 ** assetInfo.decimals,
8041
+ required: input.amount
8042
+ });
8129
8043
  }
8130
- addSendToTx(tx, coin, recipient);
8044
+ const coinType = GASLESS_STABLE_TYPES[asset];
8045
+ tx.moveCall({
8046
+ target: "0x2::balance::send_funds",
8047
+ typeArguments: [coinType],
8048
+ arguments: [
8049
+ tx.balance({ type: coinType, balance: rawAmount }),
8050
+ tx.pure.address(recipient)
8051
+ ]
8052
+ });
8131
8053
  return {
8132
8054
  preview: {
8133
8055
  toolName: "send_transfer",
8134
- effectiveAmount: Number(effectiveRaw) / 10 ** assetInfo.decimals,
8056
+ effectiveAmount: Number(rawAmount) / 10 ** assetInfo.decimals,
8135
8057
  recipient,
8136
8058
  asset
8137
8059
  }
@@ -8200,63 +8122,46 @@ var WRITE_APPENDER_REGISTRY = {
8200
8122
  expectedUsdcDeposited: plan.expectedUsdcDeposited
8201
8123
  }
8202
8124
  };
8203
- },
8204
- volo_stake: async (tx, input, ctx) => {
8205
- if (input.amountSui <= 0) {
8206
- throw new exports.T2000Error("INVALID_AMOUNT", "Stake amount must be greater than zero");
8207
- }
8208
- const amountMist = BigInt(Math.floor(input.amountSui * 1e9));
8209
- const result = await addStakeVSuiToTx(tx, ctx.client, ctx.sender, {
8210
- amountMist,
8211
- inputCoin: ctx.chainedCoin
8212
- });
8213
- if (!ctx.isOutputConsumed) {
8214
- tx.transferObjects([result.coin], ctx.sender);
8215
- }
8216
- return {
8217
- preview: { toolName: "volo_stake", effectiveAmountMist: result.effectiveAmountMist },
8218
- outputCoin: result.coin
8219
- };
8220
- },
8221
- volo_unstake: async (tx, input, ctx) => {
8222
- const amountMist = input.amountVSui === "all" ? "all" : BigInt(Math.floor(input.amountVSui * 1e9));
8223
- if (amountMist !== "all" && amountMist <= 0n) {
8224
- throw new exports.T2000Error("INVALID_AMOUNT", "Unstake amount must be greater than zero");
8225
- }
8226
- const result = await addUnstakeVSuiToTx(tx, ctx.client, ctx.sender, {
8227
- amountMist,
8228
- inputCoin: ctx.chainedCoin
8229
- });
8230
- if (!ctx.isOutputConsumed) {
8231
- tx.transferObjects([result.coin], ctx.sender);
8232
- }
8233
- return {
8234
- preview: { toolName: "volo_unstake", effectiveAmountMist: result.effectiveAmountMist },
8235
- outputCoin: result.coin
8236
- };
8237
8125
  }
8126
+ // [S.323 / 2026-05-25] volo_stake / volo_unstake appenders removed.
8238
8127
  };
8239
8128
  function deriveAllowedAddressesFromPtb(tx) {
8240
8129
  const addresses = /* @__PURE__ */ new Set();
8241
8130
  const data = tx.getData();
8242
- for (const cmd of data.commands) {
8243
- const transferCmd = cmd.TransferObjects;
8244
- if (!transferCmd) continue;
8245
- const addressArg = transferCmd.address;
8246
- if (!addressArg) continue;
8247
- const addressInputIndex = addressArg.Input;
8248
- if (addressInputIndex === void 0) continue;
8249
- const input = data.inputs[addressInputIndex];
8250
- if (!input) continue;
8131
+ const addAddressFromInput = (inputIndex) => {
8132
+ if (inputIndex === void 0) return;
8133
+ const input = data.inputs[inputIndex];
8134
+ if (!input) return;
8251
8135
  const pureBytes = input.Pure?.bytes;
8252
- if (!pureBytes) continue;
8136
+ if (!pureBytes) return;
8253
8137
  try {
8254
8138
  const bytes = base64ToBytes(pureBytes);
8255
- if (bytes.length !== 32) continue;
8139
+ if (bytes.length !== 32) return;
8256
8140
  const hex = "0x" + Array.from(bytes).map((b2) => b2.toString(16).padStart(2, "0")).join("");
8257
8141
  addresses.add(hex);
8258
8142
  } catch {
8259
8143
  }
8144
+ };
8145
+ for (const cmd of data.commands) {
8146
+ const transferCmd = cmd.TransferObjects;
8147
+ if (transferCmd) {
8148
+ const addressArg = transferCmd.address;
8149
+ const addressInputIndex = addressArg?.Input;
8150
+ addAddressFromInput(addressInputIndex);
8151
+ continue;
8152
+ }
8153
+ const moveCall = cmd.MoveCall;
8154
+ if (moveCall) {
8155
+ const mc = moveCall;
8156
+ const isBalanceSendFunds = mc.module === "balance" && mc.function === "send_funds";
8157
+ const isCoinSendFunds = mc.module === "coin" && mc.function === "send_funds";
8158
+ if (isBalanceSendFunds || isCoinSendFunds) {
8159
+ const args = mc.arguments ?? [];
8160
+ const recipientArg = args[1];
8161
+ addAddressFromInput(recipientArg?.Input);
8162
+ }
8163
+ continue;
8164
+ }
8260
8165
  }
8261
8166
  return Array.from(addresses);
8262
8167
  }
@@ -8295,7 +8200,7 @@ async function composeTx(opts) {
8295
8200
  if (producer.toolName === "save_deposit" || producer.toolName === "repay_debt" || producer.toolName === "send_transfer" || producer.toolName === "claim_rewards") {
8296
8201
  throw new exports.T2000Error(
8297
8202
  "CHAIN_MODE_INVALID",
8298
- `Step ${i} (${step.toolName}) references step ${idx} (${producer.toolName}) as producer, but '${producer.toolName}' is a terminal consumer that does not produce a chainable coin handle. Allowed producers: withdraw, borrow, swap_execute, volo_stake, volo_unstake.`
8203
+ `Step ${i} (${step.toolName}) references step ${idx} (${producer.toolName}) as producer, but '${producer.toolName}' is a terminal consumer that does not produce a chainable coin handle. Allowed producers: withdraw, borrow, swap_execute.`
8299
8204
  );
8300
8205
  }
8301
8206
  consumedSteps.add(idx);
@@ -8515,7 +8420,6 @@ function parseMoveAbort(errorStr) {
8515
8420
  init_swap_quote();
8516
8421
  init_cetus_swap();
8517
8422
  init_token_registry();
8518
- init_volo();
8519
8423
  var AUDRIC_PARENT_NAME = "audric.sui";
8520
8424
  var AUDRIC_PARENT_NFT_ID = "0x070456e283ec988b6302bdd6cc5172bbdcb709998cf116586fb98d19b0870198";
8521
8425
  var LABEL_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
@@ -8591,8 +8495,11 @@ exports.BPS_DENOMINATOR = BPS_DENOMINATOR;
8591
8495
  exports.CETUS_USDC_SUI_POOL = CETUS_USDC_SUI_POOL;
8592
8496
  exports.CLOCK_ID = CLOCK_ID;
8593
8497
  exports.ContactManager = ContactManager;
8498
+ exports.DEFAULT_GRPC_URL = DEFAULT_GRPC_URL;
8594
8499
  exports.DEFAULT_NETWORK = DEFAULT_NETWORK;
8595
8500
  exports.DEFAULT_SAFEGUARD_CONFIG = DEFAULT_SAFEGUARD_CONFIG;
8501
+ exports.GASLESS_MIN_STABLE_AMOUNT = GASLESS_MIN_STABLE_AMOUNT;
8502
+ exports.GASLESS_STABLE_TYPES = GASLESS_STABLE_TYPES;
8596
8503
  exports.GAS_RESERVE_MIN = GAS_RESERVE_MIN;
8597
8504
  exports.HF_CRITICAL_THRESHOLD = HF_CRITICAL_THRESHOLD;
8598
8505
  exports.HF_WARN_THRESHOLD = HF_WARN_THRESHOLD;
@@ -8607,6 +8514,7 @@ exports.OUTBOUND_OPS = OUTBOUND_OPS;
8607
8514
  exports.ProtocolRegistry = ProtocolRegistry;
8608
8515
  exports.SAVEABLE_ASSETS = SAVEABLE_ASSETS;
8609
8516
  exports.SAVE_FEE_BPS = SAVE_FEE_BPS;
8517
+ exports.SENDABLE_ASSETS = SENDABLE_ASSETS;
8610
8518
  exports.SPONSORED_PYTH_DEPENDENT_PROVIDERS = SPONSORED_PYTH_DEPENDENT_PROVIDERS;
8611
8519
  exports.STABLE_ASSETS = STABLE_ASSETS;
8612
8520
  exports.SUINS_NAME_REGEX = SUINS_NAME_REGEX;
@@ -8626,9 +8534,7 @@ exports.ZkLoginSigner = ZkLoginSigner;
8626
8534
  exports.addClaimRewardsToTx = addClaimRewardsToTx;
8627
8535
  exports.addFeeTransfer = addFeeTransfer;
8628
8536
  exports.addSendToTx = addSendToTx;
8629
- exports.addStakeVSuiToTx = addStakeVSuiToTx;
8630
8537
  exports.addSwapToTx = addSwapToTx;
8631
- exports.addUnstakeVSuiToTx = addUnstakeVSuiToTx;
8632
8538
  exports.aggregateClaimableRewards = aggregateClaimableRewards;
8633
8539
  exports.allDescriptors = allDescriptors;
8634
8540
  exports.assertAllowedAsset = assertAllowedAsset;
@@ -8637,14 +8543,13 @@ exports.buildClaimRewardsTx = buildClaimRewardsTx;
8637
8543
  exports.buildHarvestRewardsTx = buildHarvestRewardsTx;
8638
8544
  exports.buildRevokeLeafTx = buildRevokeLeafTx;
8639
8545
  exports.buildSendTx = buildSendTx;
8640
- exports.buildStakeVSuiTx = buildStakeVSuiTx;
8641
8546
  exports.buildSwapTx = buildSwapTx;
8642
- exports.buildUnstakeVSuiTx = buildUnstakeVSuiTx;
8643
8547
  exports.calculateFee = calculateFee;
8644
8548
  exports.classifyAction = classifyAction;
8645
8549
  exports.classifyLabel = classifyLabel;
8646
8550
  exports.classifyTransaction = classifyTransaction;
8647
8551
  exports.composeTx = composeTx;
8552
+ exports.createSuiClient = createSuiClient;
8648
8553
  exports.deriveAllowedAddressesFromPtb = deriveAllowedAddressesFromPtb;
8649
8554
  exports.deserializeCetusRoute = deserializeCetusRoute;
8650
8555
  exports.displayHandle = displayHandle;
@@ -8670,9 +8575,10 @@ exports.getPendingRewards = getPendingRewards;
8670
8575
  exports.getPendingRewardsByAddress = getPendingRewardsByAddress;
8671
8576
  exports.getRates = getRates;
8672
8577
  exports.getSponsoredSwapProviders = getSponsoredSwapProviders;
8578
+ exports.getSuiClient = getSuiClient;
8579
+ exports.getSuiGrpcClient = getSuiGrpcClient;
8673
8580
  exports.getSwapQuote = getSwapQuote;
8674
8581
  exports.getTier = getTier;
8675
- exports.getVoloStats = getVoloStats;
8676
8582
  exports.isAllowedAsset = isAllowedAsset;
8677
8583
  exports.isCetusRouteFresh = isCetusRouteFresh;
8678
8584
  exports.isInRegistry = isInRegistry;
@@ -8699,6 +8605,7 @@ exports.resolveAddressToSuinsViaRpc = resolveAddressToSuinsViaRpc;
8699
8605
  exports.resolveSuinsViaRpc = resolveSuinsViaRpc;
8700
8606
  exports.resolveSymbol = resolveSymbol;
8701
8607
  exports.resolveTokenType = resolveTokenType;
8608
+ exports.saveBech32 = saveBech32;
8702
8609
  exports.saveKey = saveKey;
8703
8610
  exports.selectAndSplitCoin = selectAndSplitCoin;
8704
8611
  exports.selectSuiCoin = selectSuiCoin;