@t2000/sdk 3.3.0 → 4.0.1
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 +19 -322
- package/dist/adapters/index.cjs.map +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 +2 -1
- package/dist/browser.d.ts +2 -1
- package/dist/browser.js +5 -0
- package/dist/browser.js.map +1 -1
- package/dist/index.cjs +245 -112
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +117 -16
- package/dist/index.d.ts +117 -16
- package/dist/index.js +238 -113
- package/dist/index.js.map +1 -1
- package/dist/{types-C1DH4kPA.d.cts → types-CqpN8a8R.d.cts} +55 -4
- package/dist/{types-DjeENMuV.d.ts → types-_U9XXGFR.d.ts} +55 -4
- package/package.json +7 -4
package/dist/index.js
CHANGED
|
@@ -4,10 +4,10 @@ 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';
|
|
@@ -1122,7 +1122,7 @@ var OPERATION_ASSETS = {
|
|
|
1122
1122
|
borrow: ["USDC", "USDsui"],
|
|
1123
1123
|
withdraw: "*",
|
|
1124
1124
|
repay: "*",
|
|
1125
|
-
send: "
|
|
1125
|
+
send: ["USDC", "USDsui", "SUI"],
|
|
1126
1126
|
swap: "*"
|
|
1127
1127
|
};
|
|
1128
1128
|
function isAllowedAsset(op, asset) {
|
|
@@ -1136,28 +1136,62 @@ function assertAllowedAsset(op, asset) {
|
|
|
1136
1136
|
if (!isAllowedAsset(op, asset)) {
|
|
1137
1137
|
const allowed = OPERATION_ASSETS[op];
|
|
1138
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." : "";
|
|
1139
1140
|
throw new T2000Error(
|
|
1140
1141
|
"INVALID_ASSET",
|
|
1141
|
-
`${op} only supports ${list}. Cannot use ${asset}.${
|
|
1142
|
+
`${op} only supports ${list}. Cannot use ${asset}.${swapHint}`
|
|
1142
1143
|
);
|
|
1143
1144
|
}
|
|
1144
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
|
+
};
|
|
1145
1151
|
var T2000_OVERLAY_FEE_WALLET = process.env.T2000_OVERLAY_FEE_WALLET ?? "0x5366efbf2b4fe5767fe2e78eb197aa5f5d138d88ac3333fbf3f80a1927da473a";
|
|
1146
1152
|
var DEFAULT_NETWORK = "mainnet";
|
|
1147
1153
|
var DEFAULT_RPC_URL = "https://fullnode.mainnet.sui.io:443";
|
|
1154
|
+
var DEFAULT_GRPC_URL = "https://fullnode.mainnet.sui.io:443";
|
|
1148
1155
|
var DEFAULT_KEY_PATH = "~/.t2000/wallet.key";
|
|
1156
|
+
var GASLESS_MIN_STABLE_AMOUNT = 0.01;
|
|
1149
1157
|
process.env.T2000_API_URL ?? "https://api.t2000.ai";
|
|
1150
1158
|
var CETUS_USDC_SUI_POOL = "0x51e883ba7c0b566a26cbc8a94cd33eb0abd418a77cc1e60ad22fd9b1f29cd2ab";
|
|
1151
1159
|
var GAS_RESERVE_MIN = 0.05;
|
|
1152
1160
|
|
|
1153
1161
|
// src/utils/sui.ts
|
|
1154
1162
|
init_errors();
|
|
1155
|
-
|
|
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();
|
|
1156
1176
|
function getSuiClient(rpcUrl) {
|
|
1157
|
-
const url = rpcUrl
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
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;
|
|
1161
1195
|
}
|
|
1162
1196
|
function validateAddress(address) {
|
|
1163
1197
|
const normalized = normalizeSuiAddress(address);
|
|
@@ -1180,49 +1214,10 @@ function normalizeCoinType(coinType) {
|
|
|
1180
1214
|
|
|
1181
1215
|
// src/wallet/keyManager.ts
|
|
1182
1216
|
init_errors();
|
|
1183
|
-
var ALGORITHM = "aes-256-gcm";
|
|
1184
|
-
var SCRYPT_N = 2 ** 14;
|
|
1185
|
-
var SCRYPT_R = 8;
|
|
1186
|
-
var SCRYPT_P = 1;
|
|
1187
|
-
var SALT_LENGTH = 32;
|
|
1188
|
-
var IV_LENGTH = 16;
|
|
1189
1217
|
function expandPath(p) {
|
|
1190
1218
|
if (p.startsWith("~")) return resolve(homedir(), p.slice(2));
|
|
1191
1219
|
return resolve(p);
|
|
1192
1220
|
}
|
|
1193
|
-
function deriveKey(passphrase, salt) {
|
|
1194
|
-
return scryptSync(passphrase, salt, 32, { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P });
|
|
1195
|
-
}
|
|
1196
|
-
function encrypt(data, passphrase) {
|
|
1197
|
-
const salt = randomBytes(SALT_LENGTH);
|
|
1198
|
-
const key = deriveKey(passphrase, salt);
|
|
1199
|
-
const iv = randomBytes(IV_LENGTH);
|
|
1200
|
-
const cipher = createCipheriv(ALGORITHM, key, iv);
|
|
1201
|
-
const ciphertext = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
1202
|
-
const tag = cipher.getAuthTag();
|
|
1203
|
-
return {
|
|
1204
|
-
version: 1,
|
|
1205
|
-
algorithm: ALGORITHM,
|
|
1206
|
-
salt: salt.toString("hex"),
|
|
1207
|
-
iv: iv.toString("hex"),
|
|
1208
|
-
tag: tag.toString("hex"),
|
|
1209
|
-
ciphertext: ciphertext.toString("hex")
|
|
1210
|
-
};
|
|
1211
|
-
}
|
|
1212
|
-
function decrypt(encrypted, passphrase) {
|
|
1213
|
-
const salt = Buffer.from(encrypted.salt, "hex");
|
|
1214
|
-
const key = deriveKey(passphrase, salt);
|
|
1215
|
-
const iv = Buffer.from(encrypted.iv, "hex");
|
|
1216
|
-
const tag = Buffer.from(encrypted.tag, "hex");
|
|
1217
|
-
const ciphertext = Buffer.from(encrypted.ciphertext, "hex");
|
|
1218
|
-
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
|
1219
|
-
decipher.setAuthTag(tag);
|
|
1220
|
-
try {
|
|
1221
|
-
return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
1222
|
-
} catch {
|
|
1223
|
-
throw new T2000Error("WALLET_LOCKED", "Invalid PIN");
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
1221
|
function generateKeypair() {
|
|
1227
1222
|
return Ed25519Keypair.generate();
|
|
1228
1223
|
}
|
|
@@ -1234,7 +1229,30 @@ function keypairFromPrivateKey(privateKey) {
|
|
|
1234
1229
|
const bytes = Buffer.from(privateKey.replace(/^0x/, ""), "hex");
|
|
1235
1230
|
return Ed25519Keypair.fromSecretKey(bytes);
|
|
1236
1231
|
}
|
|
1237
|
-
async function saveKey(keypair,
|
|
1232
|
+
async function saveKey(keypair, _passphrase, keyPath) {
|
|
1233
|
+
const filePath = expandPath(keyPath ?? DEFAULT_KEY_PATH);
|
|
1234
|
+
try {
|
|
1235
|
+
await access(filePath);
|
|
1236
|
+
throw new T2000Error("WALLET_EXISTS", `Wallet already exists at ${filePath}`);
|
|
1237
|
+
} catch (error) {
|
|
1238
|
+
if (error instanceof T2000Error) throw error;
|
|
1239
|
+
}
|
|
1240
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
1241
|
+
const payload = {
|
|
1242
|
+
version: 2,
|
|
1243
|
+
secret: keypair.getSecretKey()
|
|
1244
|
+
};
|
|
1245
|
+
await writeFile(filePath, JSON.stringify(payload, null, 2), { mode: 384 });
|
|
1246
|
+
return filePath;
|
|
1247
|
+
}
|
|
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);
|
|
1238
1256
|
const filePath = expandPath(keyPath ?? DEFAULT_KEY_PATH);
|
|
1239
1257
|
try {
|
|
1240
1258
|
await access(filePath);
|
|
@@ -1243,12 +1261,11 @@ async function saveKey(keypair, passphrase, keyPath) {
|
|
|
1243
1261
|
if (error instanceof T2000Error) throw error;
|
|
1244
1262
|
}
|
|
1245
1263
|
await mkdir(dirname(filePath), { recursive: true });
|
|
1246
|
-
const
|
|
1247
|
-
|
|
1248
|
-
await writeFile(filePath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
1264
|
+
const payload = { version: 2, secret };
|
|
1265
|
+
await writeFile(filePath, JSON.stringify(payload, null, 2), { mode: 384 });
|
|
1249
1266
|
return filePath;
|
|
1250
1267
|
}
|
|
1251
|
-
async function loadKey(
|
|
1268
|
+
async function loadKey(_passphrase, keyPath) {
|
|
1252
1269
|
const filePath = expandPath(keyPath ?? DEFAULT_KEY_PATH);
|
|
1253
1270
|
let content;
|
|
1254
1271
|
try {
|
|
@@ -1256,10 +1273,22 @@ async function loadKey(passphrase, keyPath) {
|
|
|
1256
1273
|
} catch {
|
|
1257
1274
|
throw new T2000Error("WALLET_NOT_FOUND", `No wallet found at ${filePath}`);
|
|
1258
1275
|
}
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
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);
|
|
1263
1292
|
return Ed25519Keypair.fromSecretKey(decoded.secretKey);
|
|
1264
1293
|
}
|
|
1265
1294
|
async function walletExists(keyPath) {
|
|
@@ -1277,6 +1306,9 @@ function exportPrivateKey(keypair) {
|
|
|
1277
1306
|
function getAddress(keypair) {
|
|
1278
1307
|
return keypair.getPublicKey().toSuiAddress();
|
|
1279
1308
|
}
|
|
1309
|
+
function isPlainKey(value) {
|
|
1310
|
+
return typeof value === "object" && value !== null && value.version === 2 && typeof value.secret === "string";
|
|
1311
|
+
}
|
|
1280
1312
|
|
|
1281
1313
|
// src/wallet/keypairSigner.ts
|
|
1282
1314
|
var KeypairSigner = class {
|
|
@@ -1384,12 +1416,19 @@ async function buildSendTx({
|
|
|
1384
1416
|
address,
|
|
1385
1417
|
to,
|
|
1386
1418
|
amount,
|
|
1387
|
-
asset
|
|
1419
|
+
asset
|
|
1388
1420
|
}) {
|
|
1421
|
+
assertAllowedAsset("send", asset);
|
|
1389
1422
|
const recipient = validateAddress(to);
|
|
1390
1423
|
const assetInfo = SUPPORTED_ASSETS[asset];
|
|
1391
1424
|
if (!assetInfo) throw new T2000Error("ASSET_NOT_SUPPORTED", `Asset ${asset} is not supported`);
|
|
1392
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
|
+
}
|
|
1393
1432
|
const rawAmount = displayToRaw(amount, assetInfo.decimals);
|
|
1394
1433
|
const tx = new Transaction();
|
|
1395
1434
|
tx.setSender(address);
|
|
@@ -1401,8 +1440,20 @@ async function buildSendTx({
|
|
|
1401
1440
|
required: amount
|
|
1402
1441
|
});
|
|
1403
1442
|
}
|
|
1404
|
-
|
|
1405
|
-
|
|
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
|
+
});
|
|
1406
1457
|
return tx;
|
|
1407
1458
|
}
|
|
1408
1459
|
function addSendToTx(tx, coin, recipient) {
|
|
@@ -6372,8 +6423,7 @@ var ContactManager = class {
|
|
|
6372
6423
|
"CONTACT_NOT_FOUND",
|
|
6373
6424
|
`"${nameOrAddress}" is not a valid Sui address or saved contact.
|
|
6374
6425
|
Use a SuiNS name (e.g. alex.sui \u2014 register at https://suins.io)
|
|
6375
|
-
or paste the full Sui address (0x... 64 hex characters)
|
|
6376
|
-
Legacy contact aliases: \`t2000 contacts add ${nameOrAddress} 0x...\` (deprecated).`
|
|
6426
|
+
or paste the full Sui address (0x... 64 hex characters).`
|
|
6377
6427
|
);
|
|
6378
6428
|
}
|
|
6379
6429
|
validateName(name) {
|
|
@@ -6445,20 +6495,16 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
6445
6495
|
return registry;
|
|
6446
6496
|
}
|
|
6447
6497
|
static async create(options = {}) {
|
|
6448
|
-
const { keyPath,
|
|
6449
|
-
const secret = pin ?? passphrase;
|
|
6498
|
+
const { keyPath, rpcUrl } = options;
|
|
6450
6499
|
const client = getSuiClient(rpcUrl);
|
|
6451
6500
|
const exists = await walletExists(keyPath);
|
|
6452
6501
|
if (!exists) {
|
|
6453
6502
|
throw new T2000Error(
|
|
6454
6503
|
"WALLET_NOT_FOUND",
|
|
6455
|
-
"No wallet found. Run `
|
|
6504
|
+
"No wallet found. Run `t2 init` to create one."
|
|
6456
6505
|
);
|
|
6457
6506
|
}
|
|
6458
|
-
|
|
6459
|
-
throw new T2000Error("WALLET_LOCKED", "PIN required to unlock wallet");
|
|
6460
|
-
}
|
|
6461
|
-
const keypair = await loadKey(secret, keyPath);
|
|
6507
|
+
const keypair = await loadKey(void 0, keyPath);
|
|
6462
6508
|
return new _T2000(keypair, client, void 0, DEFAULT_CONFIG_DIR);
|
|
6463
6509
|
}
|
|
6464
6510
|
static fromPrivateKey(privateKey, options = {}) {
|
|
@@ -6466,10 +6512,9 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
6466
6512
|
const client = getSuiClient(options.rpcUrl);
|
|
6467
6513
|
return new _T2000(keypair, client);
|
|
6468
6514
|
}
|
|
6469
|
-
static async init(options) {
|
|
6470
|
-
const secret = options.pin ?? options.passphrase ?? "";
|
|
6515
|
+
static async init(options = {}) {
|
|
6471
6516
|
const keypair = generateKeypair();
|
|
6472
|
-
await saveKey(keypair,
|
|
6517
|
+
await saveKey(keypair, void 0, options.keyPath);
|
|
6473
6518
|
const client = getSuiClient();
|
|
6474
6519
|
const agent = new _T2000(keypair, client, void 0, DEFAULT_CONFIG_DIR);
|
|
6475
6520
|
const address = agent.address();
|
|
@@ -6497,7 +6542,7 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
6497
6542
|
this.enforcer.check({ operation: "pay", amount: options.maxPrice ?? 1 });
|
|
6498
6543
|
const { Mppx } = await import('mppx/client');
|
|
6499
6544
|
const { sui, USDC } = await import('@suimpp/mpp/client');
|
|
6500
|
-
const { SuiGrpcClient } = await import('@mysten/sui/grpc');
|
|
6545
|
+
const { SuiGrpcClient: SuiGrpcClient2 } = await import('@mysten/sui/grpc');
|
|
6501
6546
|
const client = this.client;
|
|
6502
6547
|
const signer = this._signer;
|
|
6503
6548
|
const signerAddress = signer.getAddress();
|
|
@@ -6505,7 +6550,7 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
6505
6550
|
let gasCostSui = 0;
|
|
6506
6551
|
const network = client.network === "testnet" ? "testnet" : "mainnet";
|
|
6507
6552
|
const grpcBaseUrl = network === "testnet" ? "https://fullnode.testnet.sui.io" : "https://fullnode.mainnet.sui.io";
|
|
6508
|
-
const grpcClient = new
|
|
6553
|
+
const grpcClient = new SuiGrpcClient2({ baseUrl: grpcBaseUrl, network });
|
|
6509
6554
|
const mppx = Mppx.create({
|
|
6510
6555
|
polyfill: false,
|
|
6511
6556
|
methods: [sui({
|
|
@@ -6688,23 +6733,52 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
6688
6733
|
address() {
|
|
6689
6734
|
return this._address;
|
|
6690
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
|
+
*/
|
|
6691
6757
|
async send(params) {
|
|
6692
6758
|
this.enforcer.assertNotLocked();
|
|
6693
|
-
const asset = params.asset
|
|
6694
|
-
if (!
|
|
6695
|
-
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
|
+
);
|
|
6696
6765
|
}
|
|
6766
|
+
assertAllowedAsset("send", asset);
|
|
6767
|
+
const sendableAsset = asset;
|
|
6697
6768
|
const resolved = await this.resolveRecipient(params.to);
|
|
6698
6769
|
const sendAmount = params.amount;
|
|
6699
6770
|
const sendTo = resolved.address;
|
|
6771
|
+
const useGrpc = sendableAsset === "USDC" || sendableAsset === "USDsui";
|
|
6772
|
+
const buildClient = useGrpc ? getSuiGrpcClient() : void 0;
|
|
6700
6773
|
const gasResult = await executeTx(
|
|
6701
6774
|
this.client,
|
|
6702
6775
|
this._signer,
|
|
6703
|
-
() => 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 }
|
|
6704
6778
|
);
|
|
6705
6779
|
this.enforcer.recordUsage(sendAmount);
|
|
6706
6780
|
const balance = await this.balance();
|
|
6707
|
-
this.emitBalanceChange(
|
|
6781
|
+
this.emitBalanceChange(sendableAsset, sendAmount, "send", gasResult.digest);
|
|
6708
6782
|
return {
|
|
6709
6783
|
success: true,
|
|
6710
6784
|
tx: gasResult.digest,
|
|
@@ -6814,11 +6888,12 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
6814
6888
|
};
|
|
6815
6889
|
}
|
|
6816
6890
|
/**
|
|
6817
|
-
* [SPEC_AGENTIC_STACK P1 / SDK F2
|
|
6818
|
-
* Preferred alias of `deposit()`.
|
|
6819
|
-
*
|
|
6820
|
-
* `deposit()` stays as the canonical method name for
|
|
6821
|
-
*
|
|
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.
|
|
6822
6897
|
*/
|
|
6823
6898
|
async fund() {
|
|
6824
6899
|
return this.deposit();
|
|
@@ -7153,7 +7228,7 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
7153
7228
|
const adapter = await this.resolveLending(params.protocol, asset, "borrow");
|
|
7154
7229
|
const maxResult = await adapter.maxBorrow(this._address, asset);
|
|
7155
7230
|
if (maxResult.maxAmount <= 0) {
|
|
7156
|
-
throw new T2000Error("NO_COLLATERAL", "No collateral deposited.
|
|
7231
|
+
throw new T2000Error("NO_COLLATERAL", "No collateral deposited. Make a save deposit first.");
|
|
7157
7232
|
}
|
|
7158
7233
|
if (params.amount > maxResult.maxAmount) {
|
|
7159
7234
|
throw new T2000Error("HEALTH_FACTOR_TOO_LOW", `Max safe borrow: $${maxResult.maxAmount.toFixed(2)}. Only savings deposits count as borrowable collateral.`, {
|
|
@@ -7911,34 +7986,68 @@ var WRITE_APPENDER_REGISTRY = {
|
|
|
7911
7986
|
},
|
|
7912
7987
|
send_transfer: async (tx, input, ctx) => {
|
|
7913
7988
|
const recipient = validateAddress(input.to);
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
|
|
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
|
+
);
|
|
7918
7994
|
}
|
|
7995
|
+
assertAllowedAsset("send", input.asset);
|
|
7996
|
+
const asset = input.asset;
|
|
7997
|
+
const assetInfo = SUPPORTED_ASSETS[asset];
|
|
7919
7998
|
if (input.amount <= 0) {
|
|
7920
7999
|
throw new T2000Error("INVALID_AMOUNT", "Send amount must be greater than zero");
|
|
7921
8000
|
}
|
|
7922
8001
|
const rawAmount = BigInt(Math.floor(input.amount * 10 ** assetInfo.decimals));
|
|
7923
|
-
let coin;
|
|
7924
|
-
let effectiveRaw;
|
|
7925
8002
|
if (ctx.chainedCoin) {
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
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") {
|
|
7929
8014
|
const result = await selectSuiCoin(tx, ctx.client, ctx.sender, rawAmount, ctx.sponsoredContext);
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
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
|
+
};
|
|
7936
8024
|
}
|
|
7937
|
-
|
|
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
|
+
});
|
|
8037
|
+
}
|
|
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
|
+
});
|
|
7938
8047
|
return {
|
|
7939
8048
|
preview: {
|
|
7940
8049
|
toolName: "send_transfer",
|
|
7941
|
-
effectiveAmount: Number(
|
|
8050
|
+
effectiveAmount: Number(rawAmount) / 10 ** assetInfo.decimals,
|
|
7942
8051
|
recipient,
|
|
7943
8052
|
asset
|
|
7944
8053
|
}
|
|
@@ -8013,24 +8122,40 @@ var WRITE_APPENDER_REGISTRY = {
|
|
|
8013
8122
|
function deriveAllowedAddressesFromPtb(tx) {
|
|
8014
8123
|
const addresses = /* @__PURE__ */ new Set();
|
|
8015
8124
|
const data = tx.getData();
|
|
8016
|
-
|
|
8017
|
-
|
|
8018
|
-
|
|
8019
|
-
|
|
8020
|
-
if (!addressArg) continue;
|
|
8021
|
-
const addressInputIndex = addressArg.Input;
|
|
8022
|
-
if (addressInputIndex === void 0) continue;
|
|
8023
|
-
const input = data.inputs[addressInputIndex];
|
|
8024
|
-
if (!input) continue;
|
|
8125
|
+
const addAddressFromInput = (inputIndex) => {
|
|
8126
|
+
if (inputIndex === void 0) return;
|
|
8127
|
+
const input = data.inputs[inputIndex];
|
|
8128
|
+
if (!input) return;
|
|
8025
8129
|
const pureBytes = input.Pure?.bytes;
|
|
8026
|
-
if (!pureBytes)
|
|
8130
|
+
if (!pureBytes) return;
|
|
8027
8131
|
try {
|
|
8028
8132
|
const bytes = base64ToBytes(pureBytes);
|
|
8029
|
-
if (bytes.length !== 32)
|
|
8133
|
+
if (bytes.length !== 32) return;
|
|
8030
8134
|
const hex = "0x" + Array.from(bytes).map((b2) => b2.toString(16).padStart(2, "0")).join("");
|
|
8031
8135
|
addresses.add(hex);
|
|
8032
8136
|
} catch {
|
|
8033
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
|
+
}
|
|
8034
8159
|
}
|
|
8035
8160
|
return Array.from(addresses);
|
|
8036
8161
|
}
|
|
@@ -8356,6 +8481,6 @@ function displayHandle(label) {
|
|
|
8356
8481
|
(*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
|
|
8357
8482
|
*/
|
|
8358
8483
|
|
|
8359
|
-
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, 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, 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, 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, saveKey, selectAndSplitCoin, selectSuiCoin, serializeCetusRoute, simulateTransaction, stableToRaw, suiToMist, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, validateLabel, verifyCetusRouteCoinMatch, walletExists };
|
|
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 };
|
|
8360
8485
|
//# sourceMappingURL=index.js.map
|
|
8361
8486
|
//# sourceMappingURL=index.js.map
|