@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/README.md +142 -253
- package/dist/adapters/index.cjs.map +1 -1
- package/dist/adapters/index.d.cts +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/browser.cjs +5 -0
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +3 -2
- package/dist/browser.d.ts +3 -2
- package/dist/browser.js +5 -0
- package/dist/browser.js.map +1 -1
- package/dist/index.cjs +255 -348
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +131 -148
- package/dist/index.d.ts +131 -148
- package/dist/index.js +249 -345
- package/dist/index.js.map +1 -1
- package/dist/{types-BmgPRlnq.d.ts → types-CqpN8a8R.d.cts} +56 -5
- package/dist/{types-Bk7NncQ_.d.cts → types-HJGbXJ37.d.cts} +1 -16
- package/dist/{types-Bk7NncQ_.d.ts → types-HJGbXJ37.d.ts} +1 -16
- package/dist/{types-Bh_0d-te.d.cts → types-_U9XXGFR.d.ts} +56 -5
- package/package.json +7 -4
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}.${
|
|
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
|
-
|
|
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
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
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,
|
|
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
|
|
1402
|
-
|
|
1403
|
-
|
|
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
|
|
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
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
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
|
|
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
|
-
|
|
1560
|
-
|
|
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,
|
|
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 `
|
|
6510
|
+
"No wallet found. Run `t2 init` to create one."
|
|
6611
6511
|
);
|
|
6612
6512
|
}
|
|
6613
|
-
|
|
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,
|
|
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
|
|
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
|
-
//
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
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
|
|
6886
|
-
if (!
|
|
6887
|
-
throw new exports.T2000Error(
|
|
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(
|
|
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
|
|
7010
|
-
* Preferred alias of `deposit()`.
|
|
7011
|
-
*
|
|
7012
|
-
* `deposit()` stays as the canonical method name for
|
|
7013
|
-
*
|
|
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.
|
|
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
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8110
|
-
|
|
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
|
-
|
|
8120
|
-
|
|
8121
|
-
|
|
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
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
|
|
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)
|
|
8136
|
+
if (!pureBytes) return;
|
|
8253
8137
|
try {
|
|
8254
8138
|
const bytes = base64ToBytes(pureBytes);
|
|
8255
|
-
if (bytes.length !== 32)
|
|
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
|
|
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;
|