@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.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { coinWithBalance, Transaction } from '@mysten/sui/transactions';
|
|
2
2
|
import { AggregatorClient, Env } from '@cetusprotocol/aggregator-sdk';
|
|
3
3
|
import BN from 'bn.js';
|
|
4
4
|
import { EventEmitter } from 'eventemitter3';
|
|
5
5
|
import { createPaymentTransactionUri } from '@mysten/payment-kit';
|
|
6
6
|
import { SuiJsonRpcClient, getJsonRpcFullnodeUrl } from '@mysten/sui/jsonRpc';
|
|
7
|
+
import { SuiGrpcClient } from '@mysten/sui/grpc';
|
|
7
8
|
import { normalizeSuiAddress, isValidSuiAddress, normalizeStructTag } from '@mysten/sui/utils';
|
|
8
9
|
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
|
|
9
10
|
import { decodeSuiPrivateKey } from '@mysten/sui/cryptography';
|
|
10
|
-
import { randomBytes, createCipheriv, createDecipheriv, scryptSync } from 'crypto';
|
|
11
11
|
import { access, mkdir, writeFile, readFile } from 'fs/promises';
|
|
12
12
|
import { join as join$1, dirname, resolve } from 'path';
|
|
13
13
|
import { homedir } from 'os';
|
|
@@ -645,155 +645,6 @@ var require_lodash = __commonJS({
|
|
|
645
645
|
}
|
|
646
646
|
});
|
|
647
647
|
|
|
648
|
-
// src/protocols/volo.ts
|
|
649
|
-
var volo_exports = {};
|
|
650
|
-
__export(volo_exports, {
|
|
651
|
-
MIN_STAKE_MIST: () => MIN_STAKE_MIST,
|
|
652
|
-
SUI_SYSTEM_STATE: () => SUI_SYSTEM_STATE,
|
|
653
|
-
VOLO_METADATA: () => VOLO_METADATA,
|
|
654
|
-
VOLO_PKG: () => VOLO_PKG,
|
|
655
|
-
VOLO_POOL: () => VOLO_POOL,
|
|
656
|
-
VSUI_TYPE: () => VSUI_TYPE,
|
|
657
|
-
addStakeVSuiToTx: () => addStakeVSuiToTx,
|
|
658
|
-
addUnstakeVSuiToTx: () => addUnstakeVSuiToTx,
|
|
659
|
-
buildStakeVSuiTx: () => buildStakeVSuiTx,
|
|
660
|
-
buildUnstakeVSuiTx: () => buildUnstakeVSuiTx,
|
|
661
|
-
getVoloStats: () => getVoloStats
|
|
662
|
-
});
|
|
663
|
-
async function getVoloStats() {
|
|
664
|
-
const res = await fetch(VOLO_STATS_URL, {
|
|
665
|
-
signal: AbortSignal.timeout(8e3)
|
|
666
|
-
});
|
|
667
|
-
if (!res.ok) {
|
|
668
|
-
throw new Error(`VOLO stats API error: HTTP ${res.status}`);
|
|
669
|
-
}
|
|
670
|
-
const data = await res.json();
|
|
671
|
-
const d = data.data ?? data;
|
|
672
|
-
return {
|
|
673
|
-
apy: d.apy ?? 0,
|
|
674
|
-
exchangeRate: d.exchange_rate ?? d.exchangeRate ?? 1,
|
|
675
|
-
tvl: d.tvl ?? 0
|
|
676
|
-
};
|
|
677
|
-
}
|
|
678
|
-
async function buildStakeVSuiTx(_client, address, amountMist) {
|
|
679
|
-
if (amountMist < MIN_STAKE_MIST) {
|
|
680
|
-
throw new Error(`Minimum stake is 1 SUI (${MIN_STAKE_MIST} MIST). Got: ${amountMist}`);
|
|
681
|
-
}
|
|
682
|
-
const tx = new Transaction();
|
|
683
|
-
tx.setSender(address);
|
|
684
|
-
const [suiCoin] = tx.splitCoins(tx.gas, [amountMist]);
|
|
685
|
-
const [vSuiCoin] = tx.moveCall({
|
|
686
|
-
target: `${VOLO_PKG}::stake_pool::stake`,
|
|
687
|
-
arguments: [
|
|
688
|
-
tx.object(VOLO_POOL),
|
|
689
|
-
tx.object(VOLO_METADATA),
|
|
690
|
-
tx.object(SUI_SYSTEM_STATE),
|
|
691
|
-
suiCoin
|
|
692
|
-
]
|
|
693
|
-
});
|
|
694
|
-
tx.transferObjects([vSuiCoin], address);
|
|
695
|
-
return tx;
|
|
696
|
-
}
|
|
697
|
-
async function buildUnstakeVSuiTx(client, address, amountMist) {
|
|
698
|
-
const balResp = await client.getBalance({ owner: address, coinType: VSUI_TYPE });
|
|
699
|
-
const totalBalance = BigInt(balResp.totalBalance);
|
|
700
|
-
if (totalBalance === 0n) {
|
|
701
|
-
throw new Error("No vSUI found in wallet.");
|
|
702
|
-
}
|
|
703
|
-
const tx = new Transaction();
|
|
704
|
-
tx.setSender(address);
|
|
705
|
-
const requested = amountMist === "all" ? totalBalance : amountMist;
|
|
706
|
-
if (requested > totalBalance) {
|
|
707
|
-
throw new Error(`Insufficient vSUI: need ${requested} MIST, have ${totalBalance}`);
|
|
708
|
-
}
|
|
709
|
-
const vSuiCoin = coinWithBalance({ type: VSUI_TYPE, balance: requested })(tx);
|
|
710
|
-
const [suiCoin] = tx.moveCall({
|
|
711
|
-
target: `${VOLO_PKG}::stake_pool::unstake`,
|
|
712
|
-
arguments: [
|
|
713
|
-
tx.object(VOLO_POOL),
|
|
714
|
-
tx.object(VOLO_METADATA),
|
|
715
|
-
tx.object(SUI_SYSTEM_STATE),
|
|
716
|
-
vSuiCoin
|
|
717
|
-
]
|
|
718
|
-
});
|
|
719
|
-
tx.transferObjects([suiCoin], address);
|
|
720
|
-
return tx;
|
|
721
|
-
}
|
|
722
|
-
async function addStakeVSuiToTx(tx, client, address, input) {
|
|
723
|
-
if (input.amountMist < MIN_STAKE_MIST) {
|
|
724
|
-
throw new Error(`Minimum stake is 1 SUI (${MIN_STAKE_MIST} MIST). Got: ${input.amountMist}`);
|
|
725
|
-
}
|
|
726
|
-
let suiCoin;
|
|
727
|
-
if (input.inputCoin) {
|
|
728
|
-
suiCoin = input.inputCoin;
|
|
729
|
-
} else {
|
|
730
|
-
const balResp = await client.getBalance({ owner: address, coinType: SUI_TYPE });
|
|
731
|
-
const totalBalance = BigInt(balResp.totalBalance);
|
|
732
|
-
if (totalBalance < input.amountMist) {
|
|
733
|
-
throw new Error(`Insufficient SUI: need ${input.amountMist} MIST, have ${totalBalance}`);
|
|
734
|
-
}
|
|
735
|
-
suiCoin = coinWithBalance({
|
|
736
|
-
type: SUI_TYPE,
|
|
737
|
-
balance: input.amountMist,
|
|
738
|
-
useGasCoin: false
|
|
739
|
-
})(tx);
|
|
740
|
-
}
|
|
741
|
-
const [vSuiCoin] = tx.moveCall({
|
|
742
|
-
target: `${VOLO_PKG}::stake_pool::stake`,
|
|
743
|
-
arguments: [
|
|
744
|
-
tx.object(VOLO_POOL),
|
|
745
|
-
tx.object(VOLO_METADATA),
|
|
746
|
-
tx.object(SUI_SYSTEM_STATE),
|
|
747
|
-
suiCoin
|
|
748
|
-
]
|
|
749
|
-
});
|
|
750
|
-
return { coin: vSuiCoin, effectiveAmountMist: input.amountMist };
|
|
751
|
-
}
|
|
752
|
-
async function addUnstakeVSuiToTx(tx, client, address, input) {
|
|
753
|
-
let vSuiCoin;
|
|
754
|
-
if (input.inputCoin) {
|
|
755
|
-
if (input.amountMist === "all") {
|
|
756
|
-
vSuiCoin = input.inputCoin;
|
|
757
|
-
} else {
|
|
758
|
-
[vSuiCoin] = tx.splitCoins(input.inputCoin, [input.amountMist]);
|
|
759
|
-
}
|
|
760
|
-
} else {
|
|
761
|
-
const balResp = await client.getBalance({ owner: address, coinType: VSUI_TYPE });
|
|
762
|
-
const totalBalance = BigInt(balResp.totalBalance);
|
|
763
|
-
if (totalBalance === 0n) {
|
|
764
|
-
throw new Error("No vSUI found in wallet.");
|
|
765
|
-
}
|
|
766
|
-
const requested = input.amountMist === "all" ? totalBalance : input.amountMist;
|
|
767
|
-
if (requested > totalBalance) {
|
|
768
|
-
throw new Error(`Insufficient vSUI: need ${requested} MIST, have ${totalBalance}`);
|
|
769
|
-
}
|
|
770
|
-
vSuiCoin = coinWithBalance({ type: VSUI_TYPE, balance: requested })(tx);
|
|
771
|
-
}
|
|
772
|
-
const [suiCoin] = tx.moveCall({
|
|
773
|
-
target: `${VOLO_PKG}::stake_pool::unstake`,
|
|
774
|
-
arguments: [
|
|
775
|
-
tx.object(VOLO_POOL),
|
|
776
|
-
tx.object(VOLO_METADATA),
|
|
777
|
-
tx.object(SUI_SYSTEM_STATE),
|
|
778
|
-
vSuiCoin
|
|
779
|
-
]
|
|
780
|
-
});
|
|
781
|
-
return { coin: suiCoin, effectiveAmountMist: input.amountMist };
|
|
782
|
-
}
|
|
783
|
-
var VOLO_PKG, VOLO_POOL, VOLO_METADATA, VSUI_TYPE, SUI_SYSTEM_STATE, MIN_STAKE_MIST, VOLO_STATS_URL;
|
|
784
|
-
var init_volo = __esm({
|
|
785
|
-
"src/protocols/volo.ts"() {
|
|
786
|
-
init_token_registry();
|
|
787
|
-
VOLO_PKG = "0x68d22cf8bdbcd11ecba1e094922873e4080d4d11133e2443fddda0bfd11dae20";
|
|
788
|
-
VOLO_POOL = "0x2d914e23d82fedef1b5f56a32d5c64bdcc3087ccfea2b4d6ea51a71f587840e5";
|
|
789
|
-
VOLO_METADATA = "0x680cd26af32b2bde8d3361e804c53ec1d1cfe24c7f039eb7f549e8dfde389a60";
|
|
790
|
-
VSUI_TYPE = "0x549e8b69270defbfafd4f94e17ec44cdbdd99820b33bda2278dea3b9a32d3f55::cert::CERT";
|
|
791
|
-
SUI_SYSTEM_STATE = "0x05";
|
|
792
|
-
MIN_STAKE_MIST = 1000000000n;
|
|
793
|
-
VOLO_STATS_URL = "https://open-api.naviprotocol.io/api/volo/stats";
|
|
794
|
-
}
|
|
795
|
-
});
|
|
796
|
-
|
|
797
648
|
// src/wallet/coinSelection.ts
|
|
798
649
|
var coinSelection_exports = {};
|
|
799
650
|
__export(coinSelection_exports, {
|
|
@@ -1271,7 +1122,7 @@ var OPERATION_ASSETS = {
|
|
|
1271
1122
|
borrow: ["USDC", "USDsui"],
|
|
1272
1123
|
withdraw: "*",
|
|
1273
1124
|
repay: "*",
|
|
1274
|
-
send: "
|
|
1125
|
+
send: ["USDC", "USDsui", "SUI"],
|
|
1275
1126
|
swap: "*"
|
|
1276
1127
|
};
|
|
1277
1128
|
function isAllowedAsset(op, asset) {
|
|
@@ -1285,28 +1136,62 @@ function assertAllowedAsset(op, asset) {
|
|
|
1285
1136
|
if (!isAllowedAsset(op, asset)) {
|
|
1286
1137
|
const allowed = OPERATION_ASSETS[op];
|
|
1287
1138
|
const list = Array.isArray(allowed) ? allowed.join(", ") : "any";
|
|
1139
|
+
const swapHint = op === "save" ? " Swap to USDC or USDsui first." : op === "send" ? " Swap to USDC or USDsui first, or send SUI." : "";
|
|
1288
1140
|
throw new T2000Error(
|
|
1289
1141
|
"INVALID_ASSET",
|
|
1290
|
-
`${op} only supports ${list}. Cannot use ${asset}.${
|
|
1142
|
+
`${op} only supports ${list}. Cannot use ${asset}.${swapHint}`
|
|
1291
1143
|
);
|
|
1292
1144
|
}
|
|
1293
1145
|
}
|
|
1146
|
+
var SENDABLE_ASSETS = ["USDC", "USDsui", "SUI"];
|
|
1147
|
+
var GASLESS_STABLE_TYPES = {
|
|
1148
|
+
USDC: SUPPORTED_ASSETS.USDC.type,
|
|
1149
|
+
USDsui: SUPPORTED_ASSETS.USDsui.type
|
|
1150
|
+
};
|
|
1294
1151
|
var T2000_OVERLAY_FEE_WALLET = process.env.T2000_OVERLAY_FEE_WALLET ?? "0x5366efbf2b4fe5767fe2e78eb197aa5f5d138d88ac3333fbf3f80a1927da473a";
|
|
1295
1152
|
var DEFAULT_NETWORK = "mainnet";
|
|
1296
1153
|
var DEFAULT_RPC_URL = "https://fullnode.mainnet.sui.io:443";
|
|
1154
|
+
var DEFAULT_GRPC_URL = "https://fullnode.mainnet.sui.io:443";
|
|
1297
1155
|
var DEFAULT_KEY_PATH = "~/.t2000/wallet.key";
|
|
1156
|
+
var GASLESS_MIN_STABLE_AMOUNT = 0.01;
|
|
1298
1157
|
process.env.T2000_API_URL ?? "https://api.t2000.ai";
|
|
1299
1158
|
var CETUS_USDC_SUI_POOL = "0x51e883ba7c0b566a26cbc8a94cd33eb0abd418a77cc1e60ad22fd9b1f29cd2ab";
|
|
1300
1159
|
var GAS_RESERVE_MIN = 0.05;
|
|
1301
1160
|
|
|
1302
1161
|
// src/utils/sui.ts
|
|
1303
1162
|
init_errors();
|
|
1304
|
-
|
|
1163
|
+
function resolveRpcUrl(rpcUrl) {
|
|
1164
|
+
if (rpcUrl) return rpcUrl;
|
|
1165
|
+
const envUrl = process.env.T2000_RPC_URL?.trim();
|
|
1166
|
+
if (envUrl) return envUrl;
|
|
1167
|
+
return DEFAULT_RPC_URL;
|
|
1168
|
+
}
|
|
1169
|
+
function resolveGrpcUrl(grpcUrl) {
|
|
1170
|
+
if (grpcUrl) return grpcUrl;
|
|
1171
|
+
const envUrl = process.env.T2000_GRPC_URL?.trim();
|
|
1172
|
+
if (envUrl) return envUrl;
|
|
1173
|
+
return DEFAULT_GRPC_URL;
|
|
1174
|
+
}
|
|
1175
|
+
var rpcClientCache = /* @__PURE__ */ new Map();
|
|
1305
1176
|
function getSuiClient(rpcUrl) {
|
|
1306
|
-
const url = rpcUrl
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1177
|
+
const url = resolveRpcUrl(rpcUrl);
|
|
1178
|
+
const cached = rpcClientCache.get(url);
|
|
1179
|
+
if (cached) return cached;
|
|
1180
|
+
const client = new SuiJsonRpcClient({ url, network: "mainnet" });
|
|
1181
|
+
rpcClientCache.set(url, client);
|
|
1182
|
+
return client;
|
|
1183
|
+
}
|
|
1184
|
+
function createSuiClient(network = "mainnet") {
|
|
1185
|
+
return new SuiJsonRpcClient({ url: getJsonRpcFullnodeUrl(network), network });
|
|
1186
|
+
}
|
|
1187
|
+
var grpcClientCache = /* @__PURE__ */ new Map();
|
|
1188
|
+
function getSuiGrpcClient(grpcUrl) {
|
|
1189
|
+
const baseUrl = resolveGrpcUrl(grpcUrl);
|
|
1190
|
+
const cached = grpcClientCache.get(baseUrl);
|
|
1191
|
+
if (cached) return cached;
|
|
1192
|
+
const client = new SuiGrpcClient({ baseUrl, network: "mainnet" });
|
|
1193
|
+
grpcClientCache.set(baseUrl, client);
|
|
1194
|
+
return client;
|
|
1310
1195
|
}
|
|
1311
1196
|
function validateAddress(address) {
|
|
1312
1197
|
const normalized = normalizeSuiAddress(address);
|
|
@@ -1329,49 +1214,10 @@ function normalizeCoinType(coinType) {
|
|
|
1329
1214
|
|
|
1330
1215
|
// src/wallet/keyManager.ts
|
|
1331
1216
|
init_errors();
|
|
1332
|
-
var ALGORITHM = "aes-256-gcm";
|
|
1333
|
-
var SCRYPT_N = 2 ** 14;
|
|
1334
|
-
var SCRYPT_R = 8;
|
|
1335
|
-
var SCRYPT_P = 1;
|
|
1336
|
-
var SALT_LENGTH = 32;
|
|
1337
|
-
var IV_LENGTH = 16;
|
|
1338
1217
|
function expandPath(p) {
|
|
1339
1218
|
if (p.startsWith("~")) return resolve(homedir(), p.slice(2));
|
|
1340
1219
|
return resolve(p);
|
|
1341
1220
|
}
|
|
1342
|
-
function deriveKey(passphrase, salt) {
|
|
1343
|
-
return scryptSync(passphrase, salt, 32, { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P });
|
|
1344
|
-
}
|
|
1345
|
-
function encrypt(data, passphrase) {
|
|
1346
|
-
const salt = randomBytes(SALT_LENGTH);
|
|
1347
|
-
const key = deriveKey(passphrase, salt);
|
|
1348
|
-
const iv = randomBytes(IV_LENGTH);
|
|
1349
|
-
const cipher = createCipheriv(ALGORITHM, key, iv);
|
|
1350
|
-
const ciphertext = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
1351
|
-
const tag = cipher.getAuthTag();
|
|
1352
|
-
return {
|
|
1353
|
-
version: 1,
|
|
1354
|
-
algorithm: ALGORITHM,
|
|
1355
|
-
salt: salt.toString("hex"),
|
|
1356
|
-
iv: iv.toString("hex"),
|
|
1357
|
-
tag: tag.toString("hex"),
|
|
1358
|
-
ciphertext: ciphertext.toString("hex")
|
|
1359
|
-
};
|
|
1360
|
-
}
|
|
1361
|
-
function decrypt(encrypted, passphrase) {
|
|
1362
|
-
const salt = Buffer.from(encrypted.salt, "hex");
|
|
1363
|
-
const key = deriveKey(passphrase, salt);
|
|
1364
|
-
const iv = Buffer.from(encrypted.iv, "hex");
|
|
1365
|
-
const tag = Buffer.from(encrypted.tag, "hex");
|
|
1366
|
-
const ciphertext = Buffer.from(encrypted.ciphertext, "hex");
|
|
1367
|
-
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
|
1368
|
-
decipher.setAuthTag(tag);
|
|
1369
|
-
try {
|
|
1370
|
-
return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
1371
|
-
} catch {
|
|
1372
|
-
throw new T2000Error("WALLET_LOCKED", "Invalid PIN");
|
|
1373
|
-
}
|
|
1374
|
-
}
|
|
1375
1221
|
function generateKeypair() {
|
|
1376
1222
|
return Ed25519Keypair.generate();
|
|
1377
1223
|
}
|
|
@@ -1383,7 +1229,7 @@ function keypairFromPrivateKey(privateKey) {
|
|
|
1383
1229
|
const bytes = Buffer.from(privateKey.replace(/^0x/, ""), "hex");
|
|
1384
1230
|
return Ed25519Keypair.fromSecretKey(bytes);
|
|
1385
1231
|
}
|
|
1386
|
-
async function saveKey(keypair,
|
|
1232
|
+
async function saveKey(keypair, _passphrase, keyPath) {
|
|
1387
1233
|
const filePath = expandPath(keyPath ?? DEFAULT_KEY_PATH);
|
|
1388
1234
|
try {
|
|
1389
1235
|
await access(filePath);
|
|
@@ -1392,12 +1238,34 @@ async function saveKey(keypair, passphrase, keyPath) {
|
|
|
1392
1238
|
if (error instanceof T2000Error) throw error;
|
|
1393
1239
|
}
|
|
1394
1240
|
await mkdir(dirname(filePath), { recursive: true });
|
|
1395
|
-
const
|
|
1396
|
-
|
|
1397
|
-
|
|
1241
|
+
const payload = {
|
|
1242
|
+
version: 2,
|
|
1243
|
+
secret: keypair.getSecretKey()
|
|
1244
|
+
};
|
|
1245
|
+
await writeFile(filePath, JSON.stringify(payload, null, 2), { mode: 384 });
|
|
1398
1246
|
return filePath;
|
|
1399
1247
|
}
|
|
1400
|
-
async function
|
|
1248
|
+
async function saveBech32(secret, keyPath) {
|
|
1249
|
+
if (!secret.startsWith("suiprivkey")) {
|
|
1250
|
+
throw new T2000Error(
|
|
1251
|
+
"INVALID_KEY",
|
|
1252
|
+
`Secret must be a Bech32 suiprivkey1... string. Got: ${secret.slice(0, 12)}...`
|
|
1253
|
+
);
|
|
1254
|
+
}
|
|
1255
|
+
decodeSuiPrivateKey(secret);
|
|
1256
|
+
const filePath = expandPath(keyPath ?? DEFAULT_KEY_PATH);
|
|
1257
|
+
try {
|
|
1258
|
+
await access(filePath);
|
|
1259
|
+
throw new T2000Error("WALLET_EXISTS", `Wallet already exists at ${filePath}`);
|
|
1260
|
+
} catch (error) {
|
|
1261
|
+
if (error instanceof T2000Error) throw error;
|
|
1262
|
+
}
|
|
1263
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
1264
|
+
const payload = { version: 2, secret };
|
|
1265
|
+
await writeFile(filePath, JSON.stringify(payload, null, 2), { mode: 384 });
|
|
1266
|
+
return filePath;
|
|
1267
|
+
}
|
|
1268
|
+
async function loadKey(_passphrase, keyPath) {
|
|
1401
1269
|
const filePath = expandPath(keyPath ?? DEFAULT_KEY_PATH);
|
|
1402
1270
|
let content;
|
|
1403
1271
|
try {
|
|
@@ -1405,10 +1273,22 @@ async function loadKey(passphrase, keyPath) {
|
|
|
1405
1273
|
} catch {
|
|
1406
1274
|
throw new T2000Error("WALLET_NOT_FOUND", `No wallet found at ${filePath}`);
|
|
1407
1275
|
}
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1276
|
+
let parsed;
|
|
1277
|
+
try {
|
|
1278
|
+
parsed = JSON.parse(content);
|
|
1279
|
+
} catch {
|
|
1280
|
+
throw new T2000Error(
|
|
1281
|
+
"WALLET_CORRUPT",
|
|
1282
|
+
`Wallet file at ${filePath} is not a valid v4 wallet. Move or delete the file, then run \`t2 init\`.`
|
|
1283
|
+
);
|
|
1284
|
+
}
|
|
1285
|
+
if (!isPlainKey(parsed)) {
|
|
1286
|
+
throw new T2000Error(
|
|
1287
|
+
"WALLET_CORRUPT",
|
|
1288
|
+
`Wallet file at ${filePath} is not a valid v4 wallet. Expected { version: 2, secret: "suiprivkey1..." }. Move or delete the file, then run \`t2 init\`.`
|
|
1289
|
+
);
|
|
1290
|
+
}
|
|
1291
|
+
const decoded = decodeSuiPrivateKey(parsed.secret);
|
|
1412
1292
|
return Ed25519Keypair.fromSecretKey(decoded.secretKey);
|
|
1413
1293
|
}
|
|
1414
1294
|
async function walletExists(keyPath) {
|
|
@@ -1426,6 +1306,9 @@ function exportPrivateKey(keypair) {
|
|
|
1426
1306
|
function getAddress(keypair) {
|
|
1427
1307
|
return keypair.getPublicKey().toSuiAddress();
|
|
1428
1308
|
}
|
|
1309
|
+
function isPlainKey(value) {
|
|
1310
|
+
return typeof value === "object" && value !== null && value.version === 2 && typeof value.secret === "string";
|
|
1311
|
+
}
|
|
1429
1312
|
|
|
1430
1313
|
// src/wallet/keypairSigner.ts
|
|
1431
1314
|
var KeypairSigner = class {
|
|
@@ -1533,12 +1416,19 @@ async function buildSendTx({
|
|
|
1533
1416
|
address,
|
|
1534
1417
|
to,
|
|
1535
1418
|
amount,
|
|
1536
|
-
asset
|
|
1419
|
+
asset
|
|
1537
1420
|
}) {
|
|
1421
|
+
assertAllowedAsset("send", asset);
|
|
1538
1422
|
const recipient = validateAddress(to);
|
|
1539
1423
|
const assetInfo = SUPPORTED_ASSETS[asset];
|
|
1540
1424
|
if (!assetInfo) throw new T2000Error("ASSET_NOT_SUPPORTED", `Asset ${asset} is not supported`);
|
|
1541
1425
|
if (amount <= 0) throw new T2000Error("INVALID_AMOUNT", "Amount must be greater than zero");
|
|
1426
|
+
if ((asset === "USDC" || asset === "USDsui") && amount < GASLESS_MIN_STABLE_AMOUNT) {
|
|
1427
|
+
throw new T2000Error(
|
|
1428
|
+
"INVALID_AMOUNT",
|
|
1429
|
+
`Minimum gasless transfer is ${GASLESS_MIN_STABLE_AMOUNT} ${asset}. Got ${amount}.`
|
|
1430
|
+
);
|
|
1431
|
+
}
|
|
1542
1432
|
const rawAmount = displayToRaw(amount, assetInfo.decimals);
|
|
1543
1433
|
const tx = new Transaction();
|
|
1544
1434
|
tx.setSender(address);
|
|
@@ -1550,8 +1440,20 @@ async function buildSendTx({
|
|
|
1550
1440
|
required: amount
|
|
1551
1441
|
});
|
|
1552
1442
|
}
|
|
1553
|
-
|
|
1554
|
-
|
|
1443
|
+
if (asset === "SUI") {
|
|
1444
|
+
const [sendCoin] = tx.splitCoins(tx.gas, [rawAmount]);
|
|
1445
|
+
tx.transferObjects([sendCoin], recipient);
|
|
1446
|
+
return tx;
|
|
1447
|
+
}
|
|
1448
|
+
const coinType = GASLESS_STABLE_TYPES[asset];
|
|
1449
|
+
tx.moveCall({
|
|
1450
|
+
target: "0x2::balance::send_funds",
|
|
1451
|
+
typeArguments: [coinType],
|
|
1452
|
+
arguments: [
|
|
1453
|
+
tx.balance({ type: coinType, balance: rawAmount }),
|
|
1454
|
+
tx.pure.address(recipient)
|
|
1455
|
+
]
|
|
1456
|
+
});
|
|
1555
1457
|
return tx;
|
|
1556
1458
|
}
|
|
1557
1459
|
function addSendToTx(tx, coin, recipient) {
|
|
@@ -6521,8 +6423,7 @@ var ContactManager = class {
|
|
|
6521
6423
|
"CONTACT_NOT_FOUND",
|
|
6522
6424
|
`"${nameOrAddress}" is not a valid Sui address or saved contact.
|
|
6523
6425
|
Use a SuiNS name (e.g. alex.sui \u2014 register at https://suins.io)
|
|
6524
|
-
or paste the full Sui address (0x... 64 hex characters)
|
|
6525
|
-
Legacy contact aliases: \`t2000 contacts add ${nameOrAddress} 0x...\` (deprecated).`
|
|
6426
|
+
or paste the full Sui address (0x... 64 hex characters).`
|
|
6526
6427
|
);
|
|
6527
6428
|
}
|
|
6528
6429
|
validateName(name) {
|
|
@@ -6594,20 +6495,16 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
6594
6495
|
return registry;
|
|
6595
6496
|
}
|
|
6596
6497
|
static async create(options = {}) {
|
|
6597
|
-
const { keyPath,
|
|
6598
|
-
const secret = pin ?? passphrase;
|
|
6498
|
+
const { keyPath, rpcUrl } = options;
|
|
6599
6499
|
const client = getSuiClient(rpcUrl);
|
|
6600
6500
|
const exists = await walletExists(keyPath);
|
|
6601
6501
|
if (!exists) {
|
|
6602
6502
|
throw new T2000Error(
|
|
6603
6503
|
"WALLET_NOT_FOUND",
|
|
6604
|
-
"No wallet found. Run `
|
|
6504
|
+
"No wallet found. Run `t2 init` to create one."
|
|
6605
6505
|
);
|
|
6606
6506
|
}
|
|
6607
|
-
|
|
6608
|
-
throw new T2000Error("WALLET_LOCKED", "PIN required to unlock wallet");
|
|
6609
|
-
}
|
|
6610
|
-
const keypair = await loadKey(secret, keyPath);
|
|
6507
|
+
const keypair = await loadKey(void 0, keyPath);
|
|
6611
6508
|
return new _T2000(keypair, client, void 0, DEFAULT_CONFIG_DIR);
|
|
6612
6509
|
}
|
|
6613
6510
|
static fromPrivateKey(privateKey, options = {}) {
|
|
@@ -6615,10 +6512,9 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
6615
6512
|
const client = getSuiClient(options.rpcUrl);
|
|
6616
6513
|
return new _T2000(keypair, client);
|
|
6617
6514
|
}
|
|
6618
|
-
static async init(options) {
|
|
6619
|
-
const secret = options.pin ?? options.passphrase ?? "";
|
|
6515
|
+
static async init(options = {}) {
|
|
6620
6516
|
const keypair = generateKeypair();
|
|
6621
|
-
await saveKey(keypair,
|
|
6517
|
+
await saveKey(keypair, void 0, options.keyPath);
|
|
6622
6518
|
const client = getSuiClient();
|
|
6623
6519
|
const agent = new _T2000(keypair, client, void 0, DEFAULT_CONFIG_DIR);
|
|
6624
6520
|
const address = agent.address();
|
|
@@ -6646,7 +6542,7 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
6646
6542
|
this.enforcer.check({ operation: "pay", amount: options.maxPrice ?? 1 });
|
|
6647
6543
|
const { Mppx } = await import('mppx/client');
|
|
6648
6544
|
const { sui, USDC } = await import('@suimpp/mpp/client');
|
|
6649
|
-
const { SuiGrpcClient } = await import('@mysten/sui/grpc');
|
|
6545
|
+
const { SuiGrpcClient: SuiGrpcClient2 } = await import('@mysten/sui/grpc');
|
|
6650
6546
|
const client = this.client;
|
|
6651
6547
|
const signer = this._signer;
|
|
6652
6548
|
const signerAddress = signer.getAddress();
|
|
@@ -6654,7 +6550,7 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
6654
6550
|
let gasCostSui = 0;
|
|
6655
6551
|
const network = client.network === "testnet" ? "testnet" : "mainnet";
|
|
6656
6552
|
const grpcBaseUrl = network === "testnet" ? "https://fullnode.testnet.sui.io" : "https://fullnode.mainnet.sui.io";
|
|
6657
|
-
const grpcClient = new
|
|
6553
|
+
const grpcClient = new SuiGrpcClient2({ baseUrl: grpcBaseUrl, network });
|
|
6658
6554
|
const mppx = Mppx.create({
|
|
6659
6555
|
polyfill: false,
|
|
6660
6556
|
methods: [sui({
|
|
@@ -6699,51 +6595,14 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
6699
6595
|
receipt: paymentDigest ? { reference: paymentDigest, timestamp: (/* @__PURE__ */ new Date()).toISOString() } : void 0
|
|
6700
6596
|
};
|
|
6701
6597
|
}
|
|
6702
|
-
//
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
});
|
|
6711
|
-
const vSuiReceived = params.amount / stats.exchangeRate;
|
|
6712
|
-
return {
|
|
6713
|
-
success: true,
|
|
6714
|
-
tx: gasResult.digest,
|
|
6715
|
-
amountSui: params.amount,
|
|
6716
|
-
vSuiReceived,
|
|
6717
|
-
apy: stats.apy,
|
|
6718
|
-
gasCost: gasResult.gasCostSui
|
|
6719
|
-
};
|
|
6720
|
-
}
|
|
6721
|
-
async unstakeVSui(params) {
|
|
6722
|
-
this.enforcer.assertNotLocked();
|
|
6723
|
-
const { buildUnstakeVSuiTx: buildUnstakeVSuiTx2, getVoloStats: getVoloStats2, VSUI_TYPE: VSUI_TYPE2 } = await Promise.resolve().then(() => (init_volo(), volo_exports));
|
|
6724
|
-
let amountMist;
|
|
6725
|
-
let vSuiAmount;
|
|
6726
|
-
if (params.amount === "all") {
|
|
6727
|
-
amountMist = "all";
|
|
6728
|
-
const bal = await this.client.getBalance({ owner: this._address, coinType: VSUI_TYPE2 });
|
|
6729
|
-
vSuiAmount = Number(bal.totalBalance) / 1e9;
|
|
6730
|
-
} else {
|
|
6731
|
-
amountMist = BigInt(Math.floor(params.amount * 1e9));
|
|
6732
|
-
vSuiAmount = params.amount;
|
|
6733
|
-
}
|
|
6734
|
-
const stats = await getVoloStats2();
|
|
6735
|
-
const gasResult = await executeTx(this.client, this._signer, async () => {
|
|
6736
|
-
return buildUnstakeVSuiTx2(this.client, this._address, amountMist);
|
|
6737
|
-
});
|
|
6738
|
-
const suiReceived = vSuiAmount * stats.exchangeRate;
|
|
6739
|
-
return {
|
|
6740
|
-
success: true,
|
|
6741
|
-
tx: gasResult.digest,
|
|
6742
|
-
vSuiAmount,
|
|
6743
|
-
suiReceived,
|
|
6744
|
-
gasCost: gasResult.gasCostSui
|
|
6745
|
-
};
|
|
6746
|
-
}
|
|
6598
|
+
// [S.323 / 2026-05-25] VOLO vSUI staking surfaces removed (full cut).
|
|
6599
|
+
// Engine cut Volo in S.277; SDK + CLI + MCP followed in S.323 because the
|
|
6600
|
+
// product surface (five products: Passport / Intelligence / Finance / Pay
|
|
6601
|
+
// / Store) doesn't include a staking primitive. vSUI still appears in the
|
|
6602
|
+
// codebase as a passive token (NAVI reward rewards, Cetus swap routing),
|
|
6603
|
+
// but there is no longer any way to MINT or REDEEM vSUI through t2000.
|
|
6604
|
+
// History: see spec/archive/v07e/AUDIT_V07E_EARNS_ITS_KEEP_2026-05-23.md
|
|
6605
|
+
// and the S.323 build-tracker entry.
|
|
6747
6606
|
// -- Swap --
|
|
6748
6607
|
async swap(params) {
|
|
6749
6608
|
this.enforcer.assertNotLocked();
|
|
@@ -6874,23 +6733,52 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
6874
6733
|
address() {
|
|
6875
6734
|
return this._address;
|
|
6876
6735
|
}
|
|
6736
|
+
/**
|
|
6737
|
+
* Send `amount` of `asset` to `to` (hex address, SuiNS name, or
|
|
6738
|
+
* `@audric` handle / saved contact).
|
|
6739
|
+
*
|
|
6740
|
+
* [v4.0 Phase A Day 2 — SPEC_AGENT_WALLET_GREENFIELD §A]
|
|
6741
|
+
*
|
|
6742
|
+
* **Breaking changes from v3.x:**
|
|
6743
|
+
* - `asset` is now REQUIRED (no implicit `?? 'USDC'` default). Callers
|
|
6744
|
+
* must specify `'USDC' | 'USDsui' | 'SUI'`. Sending `'USDT'` /
|
|
6745
|
+
* `'USDe'` / `'WAL'` / `'ETH'` / `'NAVX'` / `'GOLD'` now errors
|
|
6746
|
+
* with `INVALID_ASSET` — swap to a stable first.
|
|
6747
|
+
* - USDC + USDsui builds go through `SuiGrpcClient` so the gRPC build
|
|
6748
|
+
* resolver auto-detects `0x2::balance::send_funds` eligibility and
|
|
6749
|
+
* zeros gas at simulate time. Result: **gasless USDC / USDsui sends
|
|
6750
|
+
* from a zero-SUI wallet.** SUI sends stay on the standard gas-paid
|
|
6751
|
+
* path.
|
|
6752
|
+
*
|
|
6753
|
+
* Submission stays on the JSON-RPC client (the rest of the SDK
|
|
6754
|
+
* expects JSON-RPC for read paths, and Sui's docs explicitly support
|
|
6755
|
+
* the "build via gRPC, execute via JSON-RPC" hybrid).
|
|
6756
|
+
*/
|
|
6877
6757
|
async send(params) {
|
|
6878
6758
|
this.enforcer.assertNotLocked();
|
|
6879
|
-
const asset = params.asset
|
|
6880
|
-
if (!
|
|
6881
|
-
throw new T2000Error(
|
|
6759
|
+
const asset = params.asset;
|
|
6760
|
+
if (!asset) {
|
|
6761
|
+
throw new T2000Error(
|
|
6762
|
+
"INVALID_ASSET",
|
|
6763
|
+
"send() requires an explicit asset. Use one of: USDC, USDsui, SUI."
|
|
6764
|
+
);
|
|
6882
6765
|
}
|
|
6766
|
+
assertAllowedAsset("send", asset);
|
|
6767
|
+
const sendableAsset = asset;
|
|
6883
6768
|
const resolved = await this.resolveRecipient(params.to);
|
|
6884
6769
|
const sendAmount = params.amount;
|
|
6885
6770
|
const sendTo = resolved.address;
|
|
6771
|
+
const useGrpc = sendableAsset === "USDC" || sendableAsset === "USDsui";
|
|
6772
|
+
const buildClient = useGrpc ? getSuiGrpcClient() : void 0;
|
|
6886
6773
|
const gasResult = await executeTx(
|
|
6887
6774
|
this.client,
|
|
6888
6775
|
this._signer,
|
|
6889
|
-
() => buildSendTx({ client: this.client, address: this._address, to: sendTo, amount: sendAmount, asset })
|
|
6776
|
+
() => buildSendTx({ client: this.client, address: this._address, to: sendTo, amount: sendAmount, asset: sendableAsset }),
|
|
6777
|
+
{ buildClient }
|
|
6890
6778
|
);
|
|
6891
6779
|
this.enforcer.recordUsage(sendAmount);
|
|
6892
6780
|
const balance = await this.balance();
|
|
6893
|
-
this.emitBalanceChange(
|
|
6781
|
+
this.emitBalanceChange(sendableAsset, sendAmount, "send", gasResult.digest);
|
|
6894
6782
|
return {
|
|
6895
6783
|
success: true,
|
|
6896
6784
|
tx: gasResult.digest,
|
|
@@ -7000,11 +6888,12 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
7000
6888
|
};
|
|
7001
6889
|
}
|
|
7002
6890
|
/**
|
|
7003
|
-
* [SPEC_AGENTIC_STACK P1 / SDK F2
|
|
7004
|
-
* Preferred alias of `deposit()`.
|
|
7005
|
-
*
|
|
7006
|
-
* `deposit()` stays as the canonical method name for
|
|
7007
|
-
*
|
|
6891
|
+
* [SPEC_AGENTIC_STACK P1 / SDK F2 — 2026-05-25; refreshed S.342 / 2026-05-26]
|
|
6892
|
+
* Preferred alias of `deposit()`. Was introduced to mirror the v3 `t2000 fund`
|
|
6893
|
+
* CLI command; the v4 CLI surface is `t2 receive` (deleted `fund` in the
|
|
6894
|
+
* S.332 bulk cut). `deposit()` stays as the canonical method name for
|
|
6895
|
+
* back-compat; `fund()` stays as a programmatic alias for audric + other
|
|
6896
|
+
* SDK consumers that prefer the verb.
|
|
7008
6897
|
*/
|
|
7009
6898
|
async fund() {
|
|
7010
6899
|
return this.deposit();
|
|
@@ -7339,7 +7228,7 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
7339
7228
|
const adapter = await this.resolveLending(params.protocol, asset, "borrow");
|
|
7340
7229
|
const maxResult = await adapter.maxBorrow(this._address, asset);
|
|
7341
7230
|
if (maxResult.maxAmount <= 0) {
|
|
7342
|
-
throw new T2000Error("NO_COLLATERAL", "No collateral deposited.
|
|
7231
|
+
throw new T2000Error("NO_COLLATERAL", "No collateral deposited. Make a save deposit first.");
|
|
7343
7232
|
}
|
|
7344
7233
|
if (params.amount > maxResult.maxAmount) {
|
|
7345
7234
|
throw new T2000Error("HEALTH_FACTOR_TOO_LOW", `Max safe borrow: $${maxResult.maxAmount.toFixed(2)}. Only savings deposits count as borrowable collateral.`, {
|
|
@@ -7970,7 +7859,6 @@ async function buildHarvestRewardsTx(client, address, options = {}) {
|
|
|
7970
7859
|
|
|
7971
7860
|
// src/composeTx.ts
|
|
7972
7861
|
init_cetus_swap();
|
|
7973
|
-
init_volo();
|
|
7974
7862
|
init_coinSelection();
|
|
7975
7863
|
init_token_registry();
|
|
7976
7864
|
init_errors();
|
|
@@ -8098,34 +7986,68 @@ var WRITE_APPENDER_REGISTRY = {
|
|
|
8098
7986
|
},
|
|
8099
7987
|
send_transfer: async (tx, input, ctx) => {
|
|
8100
7988
|
const recipient = validateAddress(input.to);
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
|
|
7989
|
+
if (!input.asset) {
|
|
7990
|
+
throw new T2000Error(
|
|
7991
|
+
"INVALID_ASSET",
|
|
7992
|
+
"send_transfer requires an explicit asset. Use one of: USDC, USDsui, SUI."
|
|
7993
|
+
);
|
|
8105
7994
|
}
|
|
7995
|
+
assertAllowedAsset("send", input.asset);
|
|
7996
|
+
const asset = input.asset;
|
|
7997
|
+
const assetInfo = SUPPORTED_ASSETS[asset];
|
|
8106
7998
|
if (input.amount <= 0) {
|
|
8107
7999
|
throw new T2000Error("INVALID_AMOUNT", "Send amount must be greater than zero");
|
|
8108
8000
|
}
|
|
8109
8001
|
const rawAmount = BigInt(Math.floor(input.amount * 10 ** assetInfo.decimals));
|
|
8110
|
-
let coin;
|
|
8111
|
-
let effectiveRaw;
|
|
8112
8002
|
if (ctx.chainedCoin) {
|
|
8113
|
-
|
|
8114
|
-
|
|
8115
|
-
|
|
8003
|
+
addSendToTx(tx, ctx.chainedCoin, recipient);
|
|
8004
|
+
return {
|
|
8005
|
+
preview: {
|
|
8006
|
+
toolName: "send_transfer",
|
|
8007
|
+
effectiveAmount: Number(rawAmount) / 10 ** assetInfo.decimals,
|
|
8008
|
+
recipient,
|
|
8009
|
+
asset
|
|
8010
|
+
}
|
|
8011
|
+
};
|
|
8012
|
+
}
|
|
8013
|
+
if (asset === "SUI") {
|
|
8116
8014
|
const result = await selectSuiCoin(tx, ctx.client, ctx.sender, rawAmount, ctx.sponsoredContext);
|
|
8117
|
-
|
|
8118
|
-
|
|
8119
|
-
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
|
|
8015
|
+
addSendToTx(tx, result.coin, recipient);
|
|
8016
|
+
return {
|
|
8017
|
+
preview: {
|
|
8018
|
+
toolName: "send_transfer",
|
|
8019
|
+
effectiveAmount: Number(result.effectiveAmount) / 10 ** assetInfo.decimals,
|
|
8020
|
+
recipient,
|
|
8021
|
+
asset
|
|
8022
|
+
}
|
|
8023
|
+
};
|
|
8024
|
+
}
|
|
8025
|
+
if (input.amount < GASLESS_MIN_STABLE_AMOUNT) {
|
|
8026
|
+
throw new T2000Error(
|
|
8027
|
+
"INVALID_AMOUNT",
|
|
8028
|
+
`Minimum gasless transfer is ${GASLESS_MIN_STABLE_AMOUNT} ${asset}. Got ${input.amount}.`
|
|
8029
|
+
);
|
|
8030
|
+
}
|
|
8031
|
+
const balanceResp = await ctx.client.getBalance({ owner: ctx.sender, coinType: assetInfo.type });
|
|
8032
|
+
if (BigInt(balanceResp.totalBalance) < rawAmount) {
|
|
8033
|
+
throw new T2000Error("INSUFFICIENT_BALANCE", `Insufficient ${asset} balance`, {
|
|
8034
|
+
available: Number(balanceResp.totalBalance) / 10 ** assetInfo.decimals,
|
|
8035
|
+
required: input.amount
|
|
8036
|
+
});
|
|
8123
8037
|
}
|
|
8124
|
-
|
|
8038
|
+
const coinType = GASLESS_STABLE_TYPES[asset];
|
|
8039
|
+
tx.moveCall({
|
|
8040
|
+
target: "0x2::balance::send_funds",
|
|
8041
|
+
typeArguments: [coinType],
|
|
8042
|
+
arguments: [
|
|
8043
|
+
tx.balance({ type: coinType, balance: rawAmount }),
|
|
8044
|
+
tx.pure.address(recipient)
|
|
8045
|
+
]
|
|
8046
|
+
});
|
|
8125
8047
|
return {
|
|
8126
8048
|
preview: {
|
|
8127
8049
|
toolName: "send_transfer",
|
|
8128
|
-
effectiveAmount: Number(
|
|
8050
|
+
effectiveAmount: Number(rawAmount) / 10 ** assetInfo.decimals,
|
|
8129
8051
|
recipient,
|
|
8130
8052
|
asset
|
|
8131
8053
|
}
|
|
@@ -8194,63 +8116,46 @@ var WRITE_APPENDER_REGISTRY = {
|
|
|
8194
8116
|
expectedUsdcDeposited: plan.expectedUsdcDeposited
|
|
8195
8117
|
}
|
|
8196
8118
|
};
|
|
8197
|
-
},
|
|
8198
|
-
volo_stake: async (tx, input, ctx) => {
|
|
8199
|
-
if (input.amountSui <= 0) {
|
|
8200
|
-
throw new T2000Error("INVALID_AMOUNT", "Stake amount must be greater than zero");
|
|
8201
|
-
}
|
|
8202
|
-
const amountMist = BigInt(Math.floor(input.amountSui * 1e9));
|
|
8203
|
-
const result = await addStakeVSuiToTx(tx, ctx.client, ctx.sender, {
|
|
8204
|
-
amountMist,
|
|
8205
|
-
inputCoin: ctx.chainedCoin
|
|
8206
|
-
});
|
|
8207
|
-
if (!ctx.isOutputConsumed) {
|
|
8208
|
-
tx.transferObjects([result.coin], ctx.sender);
|
|
8209
|
-
}
|
|
8210
|
-
return {
|
|
8211
|
-
preview: { toolName: "volo_stake", effectiveAmountMist: result.effectiveAmountMist },
|
|
8212
|
-
outputCoin: result.coin
|
|
8213
|
-
};
|
|
8214
|
-
},
|
|
8215
|
-
volo_unstake: async (tx, input, ctx) => {
|
|
8216
|
-
const amountMist = input.amountVSui === "all" ? "all" : BigInt(Math.floor(input.amountVSui * 1e9));
|
|
8217
|
-
if (amountMist !== "all" && amountMist <= 0n) {
|
|
8218
|
-
throw new T2000Error("INVALID_AMOUNT", "Unstake amount must be greater than zero");
|
|
8219
|
-
}
|
|
8220
|
-
const result = await addUnstakeVSuiToTx(tx, ctx.client, ctx.sender, {
|
|
8221
|
-
amountMist,
|
|
8222
|
-
inputCoin: ctx.chainedCoin
|
|
8223
|
-
});
|
|
8224
|
-
if (!ctx.isOutputConsumed) {
|
|
8225
|
-
tx.transferObjects([result.coin], ctx.sender);
|
|
8226
|
-
}
|
|
8227
|
-
return {
|
|
8228
|
-
preview: { toolName: "volo_unstake", effectiveAmountMist: result.effectiveAmountMist },
|
|
8229
|
-
outputCoin: result.coin
|
|
8230
|
-
};
|
|
8231
8119
|
}
|
|
8120
|
+
// [S.323 / 2026-05-25] volo_stake / volo_unstake appenders removed.
|
|
8232
8121
|
};
|
|
8233
8122
|
function deriveAllowedAddressesFromPtb(tx) {
|
|
8234
8123
|
const addresses = /* @__PURE__ */ new Set();
|
|
8235
8124
|
const data = tx.getData();
|
|
8236
|
-
|
|
8237
|
-
|
|
8238
|
-
|
|
8239
|
-
|
|
8240
|
-
if (!addressArg) continue;
|
|
8241
|
-
const addressInputIndex = addressArg.Input;
|
|
8242
|
-
if (addressInputIndex === void 0) continue;
|
|
8243
|
-
const input = data.inputs[addressInputIndex];
|
|
8244
|
-
if (!input) continue;
|
|
8125
|
+
const addAddressFromInput = (inputIndex) => {
|
|
8126
|
+
if (inputIndex === void 0) return;
|
|
8127
|
+
const input = data.inputs[inputIndex];
|
|
8128
|
+
if (!input) return;
|
|
8245
8129
|
const pureBytes = input.Pure?.bytes;
|
|
8246
|
-
if (!pureBytes)
|
|
8130
|
+
if (!pureBytes) return;
|
|
8247
8131
|
try {
|
|
8248
8132
|
const bytes = base64ToBytes(pureBytes);
|
|
8249
|
-
if (bytes.length !== 32)
|
|
8133
|
+
if (bytes.length !== 32) return;
|
|
8250
8134
|
const hex = "0x" + Array.from(bytes).map((b2) => b2.toString(16).padStart(2, "0")).join("");
|
|
8251
8135
|
addresses.add(hex);
|
|
8252
8136
|
} catch {
|
|
8253
8137
|
}
|
|
8138
|
+
};
|
|
8139
|
+
for (const cmd of data.commands) {
|
|
8140
|
+
const transferCmd = cmd.TransferObjects;
|
|
8141
|
+
if (transferCmd) {
|
|
8142
|
+
const addressArg = transferCmd.address;
|
|
8143
|
+
const addressInputIndex = addressArg?.Input;
|
|
8144
|
+
addAddressFromInput(addressInputIndex);
|
|
8145
|
+
continue;
|
|
8146
|
+
}
|
|
8147
|
+
const moveCall = cmd.MoveCall;
|
|
8148
|
+
if (moveCall) {
|
|
8149
|
+
const mc = moveCall;
|
|
8150
|
+
const isBalanceSendFunds = mc.module === "balance" && mc.function === "send_funds";
|
|
8151
|
+
const isCoinSendFunds = mc.module === "coin" && mc.function === "send_funds";
|
|
8152
|
+
if (isBalanceSendFunds || isCoinSendFunds) {
|
|
8153
|
+
const args = mc.arguments ?? [];
|
|
8154
|
+
const recipientArg = args[1];
|
|
8155
|
+
addAddressFromInput(recipientArg?.Input);
|
|
8156
|
+
}
|
|
8157
|
+
continue;
|
|
8158
|
+
}
|
|
8254
8159
|
}
|
|
8255
8160
|
return Array.from(addresses);
|
|
8256
8161
|
}
|
|
@@ -8289,7 +8194,7 @@ async function composeTx(opts) {
|
|
|
8289
8194
|
if (producer.toolName === "save_deposit" || producer.toolName === "repay_debt" || producer.toolName === "send_transfer" || producer.toolName === "claim_rewards") {
|
|
8290
8195
|
throw new T2000Error(
|
|
8291
8196
|
"CHAIN_MODE_INVALID",
|
|
8292
|
-
`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
|
|
8197
|
+
`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.`
|
|
8293
8198
|
);
|
|
8294
8199
|
}
|
|
8295
8200
|
consumedSteps.add(idx);
|
|
@@ -8509,7 +8414,6 @@ function parseMoveAbort(errorStr) {
|
|
|
8509
8414
|
init_swap_quote();
|
|
8510
8415
|
init_cetus_swap();
|
|
8511
8416
|
init_token_registry();
|
|
8512
|
-
init_volo();
|
|
8513
8417
|
var AUDRIC_PARENT_NAME = "audric.sui";
|
|
8514
8418
|
var AUDRIC_PARENT_NFT_ID = "0x070456e283ec988b6302bdd6cc5172bbdcb709998cf116586fb98d19b0870198";
|
|
8515
8419
|
var LABEL_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
@@ -8577,6 +8481,6 @@ function displayHandle(label) {
|
|
|
8577
8481
|
(*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
|
|
8578
8482
|
*/
|
|
8579
8483
|
|
|
8580
|
-
export { ALL_NAVI_ASSETS, AUDRIC_PARENT_NAME, AUDRIC_PARENT_NFT_ID, BORROW_FEE_BPS, BPS_DENOMINATOR, CETUS_USDC_SUI_POOL, CLOCK_ID, COIN_REGISTRY, ContactManager, DEFAULT_NETWORK, DEFAULT_SAFEGUARD_CONFIG, ETH_TYPE, GAS_RESERVE_MIN, HF_CRITICAL_THRESHOLD, HF_WARN_THRESHOLD, IKA_TYPE, InvalidAddressError, KNOWN_TARGETS, KeypairSigner, LABEL_PATTERNS, LOFI_TYPE, MANIFEST_TYPE, MIST_PER_SUI, NAVX_TYPE, NaviAdapter, OPERATION_ASSETS, OUTBOUND_OPS, OVERLAY_FEE_RATE, ProtocolRegistry, SAVEABLE_ASSETS, SAVE_FEE_BPS, SPONSORED_PYTH_DEPENDENT_PROVIDERS, STABLE_ASSETS, SUINS_NAME_REGEX, SUI_ADDRESS_REGEX, SUI_ADDRESS_STRICT_REGEX, SUI_DECIMALS, SUI_TYPE, SUPPORTED_ASSETS, SafeguardEnforcer, SafeguardError, SuinsNotRegisteredError, SuinsRpcError, T2000, T2000Error, T2000_OVERLAY_FEE_WALLET, TOKEN_MAP, USDC_DECIMALS, USDC_TYPE, USDE_TYPE, USDSUI_TYPE, USDT_TYPE,
|
|
8484
|
+
export { ALL_NAVI_ASSETS, AUDRIC_PARENT_NAME, AUDRIC_PARENT_NFT_ID, BORROW_FEE_BPS, BPS_DENOMINATOR, CETUS_USDC_SUI_POOL, CLOCK_ID, COIN_REGISTRY, ContactManager, DEFAULT_GRPC_URL, DEFAULT_NETWORK, DEFAULT_SAFEGUARD_CONFIG, ETH_TYPE, GASLESS_MIN_STABLE_AMOUNT, GASLESS_STABLE_TYPES, GAS_RESERVE_MIN, HF_CRITICAL_THRESHOLD, HF_WARN_THRESHOLD, IKA_TYPE, InvalidAddressError, KNOWN_TARGETS, KeypairSigner, LABEL_PATTERNS, LOFI_TYPE, MANIFEST_TYPE, MIST_PER_SUI, NAVX_TYPE, NaviAdapter, OPERATION_ASSETS, OUTBOUND_OPS, OVERLAY_FEE_RATE, ProtocolRegistry, SAVEABLE_ASSETS, SAVE_FEE_BPS, SENDABLE_ASSETS, SPONSORED_PYTH_DEPENDENT_PROVIDERS, STABLE_ASSETS, SUINS_NAME_REGEX, SUI_ADDRESS_REGEX, SUI_ADDRESS_STRICT_REGEX, SUI_DECIMALS, SUI_TYPE, SUPPORTED_ASSETS, SafeguardEnforcer, SafeguardError, SuinsNotRegisteredError, SuinsRpcError, T2000, T2000Error, T2000_OVERLAY_FEE_WALLET, TOKEN_MAP, USDC_DECIMALS, USDC_TYPE, USDE_TYPE, USDSUI_TYPE, USDT_TYPE, WAL_TYPE, WBTC_TYPE, WRITE_APPENDER_REGISTRY, ZkLoginSigner, addClaimRewardsToTx, addFeeTransfer, addSendToTx, addSwapToTx, aggregateClaimableRewards, allDescriptors, assertAllowedAsset, buildAddLeafTx, buildClaimRewardsTx, buildHarvestRewardsTx, buildRevokeLeafTx, buildSendTx, buildSwapTx, calculateFee, classifyAction, classifyLabel, classifyTransaction, composeTx, createSuiClient, deriveAllowedAddressesFromPtb, deserializeCetusRoute, displayHandle, exportPrivateKey, extractAllUserLegs, extractTransferDetails, extractTxCommands, extractTxSender, fallbackLabel, fetchAllCoins, findSwapRoute, formatAssetAmount, formatSui, formatUsd, fullHandle, generateKeypair, getAddress, getCoinMeta, getDecimals, getDecimalsForCoinType, getFinancialSummary, getPendingRewards, getPendingRewardsByAddress, getRates, getSponsoredSwapProviders, getSuiClient, getSuiGrpcClient, getSwapQuote, getTier, isAllowedAsset, isCetusRouteFresh, isInRegistry, isSupported, isTier1, isTier2, keypairFromPrivateKey, loadKey, looksLikeSuiNs, mapMoveAbortCode, mapWalletError, mistToSui, naviDescriptor, normalizeAddressInput, normalizeAsset, normalizeCoinType, parseSuiRpcTx, queryHistory, queryTransaction, rawToStable, rawToUsdc, refineLendingLabel, resolveAddressToSuinsViaRpc, resolveSuinsViaRpc, resolveSymbol, resolveTokenType, saveBech32, saveKey, selectAndSplitCoin, selectSuiCoin, serializeCetusRoute, simulateTransaction, stableToRaw, suiToMist, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, validateLabel, verifyCetusRouteCoinMatch, walletExists };
|
|
8581
8485
|
//# sourceMappingURL=index.js.map
|
|
8582
8486
|
//# sourceMappingURL=index.js.map
|