@trustwallet/cli 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/branding.d.ts.map +1 -1
- package/dist/commands/alert.d.ts.map +1 -1
- package/dist/commands/automate.d.ts.map +1 -1
- package/dist/commands/onramp.d.ts.map +1 -1
- package/dist/commands/query.d.ts.map +1 -1
- package/dist/commands/swap.d.ts.map +1 -1
- package/dist/commands/transfer.d.ts.map +1 -1
- package/dist/commands/wallet.d.ts.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/x402.d.ts.map +1 -1
- package/dist/config-loader.d.ts +1 -0
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/index.js +1351 -704
- package/dist/output.d.ts +1 -0
- package/dist/output.d.ts.map +1 -1
- package/package.json +17 -15
- package/scripts/polyfill-banner.js +6 -1
- package/dist/commands/skill.d.ts +0 -3
- package/dist/commands/skill.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -49,10 +49,15 @@
|
|
|
49
49
|
head: { appendChild: function(){} }
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
|
-
// --- Suppress
|
|
52
|
+
// --- Suppress noisy warnings fired by transitive deps at module load time ---
|
|
53
53
|
var _origEmit = process.emitWarning;
|
|
54
54
|
process.emitWarning = function(warning) {
|
|
55
55
|
if (typeof warning === 'string' && warning.indexOf('--localstorage-file') !== -1) return;
|
|
56
|
+
// DEP0040: punycode module deprecation from ws/url used by Trust SDK
|
|
57
|
+
var opts = arguments[1];
|
|
58
|
+
var code = (opts && typeof opts === 'object' ? opts.code : arguments[2])
|
|
59
|
+
|| (warning && typeof warning === 'object' ? warning.code : null);
|
|
60
|
+
if (code === 'DEP0040') return;
|
|
56
61
|
return _origEmit.apply(process, arguments);
|
|
57
62
|
};
|
|
58
63
|
|
|
@@ -71,7 +76,7 @@ var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
|
71
76
|
var __esm = (fn2, res) => function __init() {
|
|
72
77
|
return fn2 && (res = (0, fn2[__getOwnPropNames(fn2)[0]])(fn2 = 0)), res;
|
|
73
78
|
};
|
|
74
|
-
var __commonJS = (cb, mod2) => function
|
|
79
|
+
var __commonJS = (cb, mod2) => function __require2() {
|
|
75
80
|
return mod2 || (0, cb[__getOwnPropNames(cb)[0]])((mod2 = { exports: {} }).exports, mod2), mod2.exports;
|
|
76
81
|
};
|
|
77
82
|
var __export = (target, all) => {
|
|
@@ -3125,20 +3130,21 @@ function decrypt(data, password) {
|
|
|
3125
3130
|
decrypted += decipher.final("utf-8");
|
|
3126
3131
|
return decrypted;
|
|
3127
3132
|
}
|
|
3128
|
-
function
|
|
3133
|
+
function isNoEntryError(err) {
|
|
3134
|
+
return err instanceof Error && /no entry|no such entry/i.test(err.message);
|
|
3135
|
+
}
|
|
3136
|
+
function isKeychainUnavailableError(err) {
|
|
3137
|
+
return err instanceof Error && (/no keychain service/i.test(err.message) || /org\.freedesktop/i.test(err.message) || /failed to unlock/i.test(err.message) || /secret service/i.test(err.message));
|
|
3138
|
+
}
|
|
3139
|
+
function getPassword(service, account) {
|
|
3129
3140
|
try {
|
|
3130
|
-
return
|
|
3131
|
-
} catch {
|
|
3132
|
-
return null;
|
|
3141
|
+
return new import_keyring.Entry(service, account).getPassword();
|
|
3142
|
+
} catch (err) {
|
|
3143
|
+
if (isNoEntryError(err) || isKeychainUnavailableError(err)) return null;
|
|
3144
|
+
throw err;
|
|
3133
3145
|
}
|
|
3134
3146
|
}
|
|
3135
|
-
|
|
3136
|
-
const keytar = loadKeytar();
|
|
3137
|
-
if (keytar) return keytar;
|
|
3138
|
-
if (macSecurity.isAvailable()) return macSecurity;
|
|
3139
|
-
return null;
|
|
3140
|
-
}
|
|
3141
|
-
var fs, path, os, crypto2, import_child_process, import_os, crypto22, crypto3, __require, FileStore, ALGORITHM, KEY_LENGTH, IV_LENGTH, SALT_LENGTH, ITERATIONS, SERVICE, WALLET_ACCOUNT, API_SECRET_ACCOUNT, macSecurity, KeychainManager, CREDENTIALS_FILE, CredentialManager, WALLET_FILE, WalletConfigStore, ALERTS_FILE, AlertStore, AUTOMATIONS_FILE, AutomationStore, SEEN_HASHES_FILE, SeenHashStore;
|
|
3147
|
+
var fs, path, os, crypto2, import_keyring, crypto22, crypto3, FileStore, ALGORITHM, KEY_LENGTH, IV_LENGTH, SALT_LENGTH, ITERATIONS, SERVICE, WALLET_ACCOUNT, API_SECRET_ACCOUNT, KeychainManager, CREDENTIALS_FILE, CredentialManager, WALLET_FILE, WalletConfigStore, ALERTS_FILE, AlertStore, AUTOMATIONS_FILE, AutomationStore, SEEN_HASHES_FILE, SeenHashStore, ASSET_CACHE_FILE, AssetCache;
|
|
3142
3148
|
var init_dist = __esm({
|
|
3143
3149
|
"../storage/dist/index.mjs"() {
|
|
3144
3150
|
"use strict";
|
|
@@ -3146,16 +3152,9 @@ var init_dist = __esm({
|
|
|
3146
3152
|
path = __toESM(require("path"), 1);
|
|
3147
3153
|
os = __toESM(require("os"), 1);
|
|
3148
3154
|
crypto2 = __toESM(require("crypto"), 1);
|
|
3149
|
-
|
|
3150
|
-
import_os = require("os");
|
|
3155
|
+
import_keyring = require("@napi-rs/keyring");
|
|
3151
3156
|
crypto22 = __toESM(require("crypto"), 1);
|
|
3152
3157
|
crypto3 = __toESM(require("crypto"), 1);
|
|
3153
|
-
__require = /* @__PURE__ */ ((x6) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x6, {
|
|
3154
|
-
get: (a3, b3) => (typeof require !== "undefined" ? require : a3)[b3]
|
|
3155
|
-
}) : x6)(function(x6) {
|
|
3156
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
3157
|
-
throw Error('Dynamic require of "' + x6 + '" is not supported');
|
|
3158
|
-
});
|
|
3159
3158
|
FileStore = class {
|
|
3160
3159
|
dataDir;
|
|
3161
3160
|
constructor(config2) {
|
|
@@ -3206,84 +3205,36 @@ var init_dist = __esm({
|
|
|
3206
3205
|
SERVICE = "twak";
|
|
3207
3206
|
WALLET_ACCOUNT = "wallet";
|
|
3208
3207
|
API_SECRET_ACCOUNT = "api-secret";
|
|
3209
|
-
macSecurity = {
|
|
3210
|
-
isAvailable() {
|
|
3211
|
-
return (0, import_os.platform)() === "darwin";
|
|
3212
|
-
},
|
|
3213
|
-
getPassword(service, account) {
|
|
3214
|
-
try {
|
|
3215
|
-
const output2 = (0, import_child_process.execFileSync)(
|
|
3216
|
-
"security",
|
|
3217
|
-
["find-generic-password", "-s", service, "-a", account, "-w"],
|
|
3218
|
-
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
3219
|
-
);
|
|
3220
|
-
return output2.replace(/\r?\n$/, "");
|
|
3221
|
-
} catch {
|
|
3222
|
-
return null;
|
|
3223
|
-
}
|
|
3224
|
-
},
|
|
3225
|
-
setPassword(service, account, password) {
|
|
3226
|
-
(0, import_child_process.execFileSync)(
|
|
3227
|
-
"security",
|
|
3228
|
-
["add-generic-password", "-U", "-s", service, "-a", account, "-w"],
|
|
3229
|
-
{ input: password + "\n", stdio: ["pipe", "pipe", "pipe"] }
|
|
3230
|
-
);
|
|
3231
|
-
},
|
|
3232
|
-
deletePassword(service, account) {
|
|
3233
|
-
try {
|
|
3234
|
-
(0, import_child_process.execFileSync)(
|
|
3235
|
-
"security",
|
|
3236
|
-
["delete-generic-password", "-s", service, "-a", account],
|
|
3237
|
-
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
3238
|
-
);
|
|
3239
|
-
} catch {
|
|
3240
|
-
}
|
|
3241
|
-
}
|
|
3242
|
-
};
|
|
3243
3208
|
KeychainManager = class {
|
|
3244
3209
|
async getWalletPassword() {
|
|
3245
|
-
|
|
3246
|
-
if (!backend) return null;
|
|
3247
|
-
try {
|
|
3248
|
-
return await backend.getPassword(SERVICE, WALLET_ACCOUNT);
|
|
3249
|
-
} catch {
|
|
3250
|
-
return null;
|
|
3251
|
-
}
|
|
3210
|
+
return getPassword(SERVICE, WALLET_ACCOUNT);
|
|
3252
3211
|
}
|
|
3253
3212
|
async saveWalletPassword(password) {
|
|
3254
|
-
|
|
3255
|
-
if (!backend) throw new Error("No keychain backend available (keytar not installed and not on macOS)");
|
|
3256
|
-
await backend.setPassword(SERVICE, WALLET_ACCOUNT, password);
|
|
3213
|
+
new import_keyring.Entry(SERVICE, WALLET_ACCOUNT).setPassword(password);
|
|
3257
3214
|
}
|
|
3258
3215
|
async deleteWalletPassword() {
|
|
3259
|
-
|
|
3260
|
-
if (!backend) throw new Error("No keychain backend available (keytar not installed and not on macOS)");
|
|
3261
|
-
await backend.deletePassword(SERVICE, WALLET_ACCOUNT);
|
|
3216
|
+
new import_keyring.Entry(SERVICE, WALLET_ACCOUNT).deletePassword();
|
|
3262
3217
|
}
|
|
3263
3218
|
async getApiSecret() {
|
|
3264
|
-
|
|
3265
|
-
if (!backend) return null;
|
|
3266
|
-
try {
|
|
3267
|
-
return await backend.getPassword(SERVICE, API_SECRET_ACCOUNT);
|
|
3268
|
-
} catch {
|
|
3269
|
-
return null;
|
|
3270
|
-
}
|
|
3219
|
+
return getPassword(SERVICE, API_SECRET_ACCOUNT);
|
|
3271
3220
|
}
|
|
3272
3221
|
async saveApiSecret(secret) {
|
|
3273
|
-
|
|
3274
|
-
if (!backend) throw new Error("No keychain backend available (keytar not installed and not on macOS)");
|
|
3275
|
-
await backend.setPassword(SERVICE, API_SECRET_ACCOUNT, secret);
|
|
3222
|
+
new import_keyring.Entry(SERVICE, API_SECRET_ACCOUNT).setPassword(secret);
|
|
3276
3223
|
}
|
|
3277
3224
|
async deleteApiSecret() {
|
|
3278
|
-
const backend = getBackend();
|
|
3279
|
-
if (!backend) return;
|
|
3280
3225
|
try {
|
|
3281
|
-
|
|
3226
|
+
new import_keyring.Entry(SERVICE, API_SECRET_ACCOUNT).deletePassword();
|
|
3282
3227
|
} catch {
|
|
3283
3228
|
}
|
|
3284
3229
|
}
|
|
3285
3230
|
isAvailable() {
|
|
3286
|
-
|
|
3231
|
+
try {
|
|
3232
|
+
new import_keyring.Entry(SERVICE, "__availability_probe__").getPassword();
|
|
3233
|
+
return true;
|
|
3234
|
+
} catch (err) {
|
|
3235
|
+
if (isNoEntryError(err)) return true;
|
|
3236
|
+
return false;
|
|
3237
|
+
}
|
|
3287
3238
|
}
|
|
3288
3239
|
};
|
|
3289
3240
|
CREDENTIALS_FILE = "credentials.json";
|
|
@@ -3311,6 +3262,10 @@ var init_dist = __esm({
|
|
|
3311
3262
|
try {
|
|
3312
3263
|
await this.kc.saveApiSecret(credentials.hmacSecret);
|
|
3313
3264
|
} catch {
|
|
3265
|
+
try {
|
|
3266
|
+
await this.kc.deleteApiSecret();
|
|
3267
|
+
} catch {
|
|
3268
|
+
}
|
|
3314
3269
|
}
|
|
3315
3270
|
}
|
|
3316
3271
|
}
|
|
@@ -3484,6 +3439,45 @@ var init_dist = __esm({
|
|
|
3484
3439
|
await this.store.remove(SEEN_HASHES_FILE);
|
|
3485
3440
|
}
|
|
3486
3441
|
};
|
|
3442
|
+
ASSET_CACHE_FILE = "asset-cache.json";
|
|
3443
|
+
AssetCache = class {
|
|
3444
|
+
store;
|
|
3445
|
+
mem = /* @__PURE__ */ new Map();
|
|
3446
|
+
loadPromise = null;
|
|
3447
|
+
writeQueue = Promise.resolve();
|
|
3448
|
+
constructor(config2) {
|
|
3449
|
+
this.store = new FileStore(config2);
|
|
3450
|
+
}
|
|
3451
|
+
async get(tokenId) {
|
|
3452
|
+
await this.ensureLoaded();
|
|
3453
|
+
return this.mem.get(tokenId);
|
|
3454
|
+
}
|
|
3455
|
+
async set(tokenId, symbol) {
|
|
3456
|
+
await this.ensureLoaded();
|
|
3457
|
+
this.mem.set(tokenId, symbol);
|
|
3458
|
+
this.writeQueue = this.writeQueue.then(
|
|
3459
|
+
() => this.store.write(ASSET_CACHE_FILE, Object.fromEntries(this.mem))
|
|
3460
|
+
);
|
|
3461
|
+
await this.writeQueue;
|
|
3462
|
+
}
|
|
3463
|
+
async clear() {
|
|
3464
|
+
this.mem.clear();
|
|
3465
|
+
this.loadPromise = null;
|
|
3466
|
+
await this.store.remove(ASSET_CACHE_FILE);
|
|
3467
|
+
}
|
|
3468
|
+
ensureLoaded() {
|
|
3469
|
+
if (!this.loadPromise) {
|
|
3470
|
+
this.loadPromise = this.store.read(ASSET_CACHE_FILE).then((data) => {
|
|
3471
|
+
if (data) {
|
|
3472
|
+
for (const [k4, v5] of Object.entries(data)) {
|
|
3473
|
+
this.mem.set(k4, v5);
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
});
|
|
3477
|
+
}
|
|
3478
|
+
return this.loadPromise;
|
|
3479
|
+
}
|
|
3480
|
+
};
|
|
3487
3481
|
}
|
|
3488
3482
|
});
|
|
3489
3483
|
|
|
@@ -5166,11 +5160,13 @@ function resolveToken(token, chainKey) {
|
|
|
5166
5160
|
};
|
|
5167
5161
|
}
|
|
5168
5162
|
if (upperToken.startsWith("USDC")) {
|
|
5169
|
-
const addr = chainTokens?.["USDC.e"] || chainTokens?.["USDC"]
|
|
5163
|
+
const addr = chainTokens?.["USDC.e"] || chainTokens?.["USDC"];
|
|
5164
|
+
if (!addr) return null;
|
|
5170
5165
|
return { address: addr, decimals: isBsc ? 18 : 6 };
|
|
5171
5166
|
}
|
|
5172
5167
|
if (upperToken.startsWith("USDT")) {
|
|
5173
|
-
const addr = chainTokens?.["USDT"]
|
|
5168
|
+
const addr = chainTokens?.["USDT"];
|
|
5169
|
+
if (!addr) return null;
|
|
5174
5170
|
return { address: addr, decimals: isBsc ? 18 : 6 };
|
|
5175
5171
|
}
|
|
5176
5172
|
if (token.startsWith("0x") || token.length > 20) {
|
|
@@ -5186,7 +5182,7 @@ function resolveToken(token, chainKey) {
|
|
|
5186
5182
|
}
|
|
5187
5183
|
return { address: token, decimals: 18 };
|
|
5188
5184
|
}
|
|
5189
|
-
return
|
|
5185
|
+
return null;
|
|
5190
5186
|
}
|
|
5191
5187
|
function chainKeyToCoinType(chainKey) {
|
|
5192
5188
|
const key = chainKey.toLowerCase();
|
|
@@ -5941,7 +5937,7 @@ function setPolyfillNaasSessionId(sessionId) {
|
|
|
5941
5937
|
}
|
|
5942
5938
|
function resolveSdkDir() {
|
|
5943
5939
|
try {
|
|
5944
|
-
return path2.dirname(
|
|
5940
|
+
return path2.dirname(__require.resolve("@trustwallet-trust-sdk/trust-sdk"));
|
|
5945
5941
|
} catch {
|
|
5946
5942
|
let dir = __dirname;
|
|
5947
5943
|
for (let i2 = 0; i2 < 10; i2++) {
|
|
@@ -6106,7 +6102,7 @@ function applyPolyfills() {
|
|
|
6106
6102
|
};
|
|
6107
6103
|
}
|
|
6108
6104
|
if (typeof globalThis.localStorage === "undefined") {
|
|
6109
|
-
const os5 =
|
|
6105
|
+
const os5 = __require("os");
|
|
6110
6106
|
const storePath = path2.join(os5.homedir(), ".twak", "local-storage.json");
|
|
6111
6107
|
const load = () => {
|
|
6112
6108
|
try {
|
|
@@ -6148,7 +6144,7 @@ function applyPolyfills() {
|
|
|
6148
6144
|
};
|
|
6149
6145
|
}
|
|
6150
6146
|
if (typeof globalThis.crypto === "undefined") {
|
|
6151
|
-
const nodeCrypto =
|
|
6147
|
+
const nodeCrypto = __require("crypto");
|
|
6152
6148
|
globalThis.crypto = nodeCrypto.webcrypto ?? nodeCrypto;
|
|
6153
6149
|
}
|
|
6154
6150
|
for (const method of ["log", "error"]) {
|
|
@@ -6455,7 +6451,7 @@ function registerShutdownHandlers(providers, onBeforeExit) {
|
|
|
6455
6451
|
process.removeListener("SIGTERM", handler);
|
|
6456
6452
|
};
|
|
6457
6453
|
}
|
|
6458
|
-
var fs2, path2, import_trust_sdk, import_trust_sdk2, import_trust_sdk3, import_trust_sdk4, import_crypto2,
|
|
6454
|
+
var fs2, path2, import_trust_sdk, import_trust_sdk2, import_trust_sdk3, import_trust_sdk4, import_crypto2, __require, polyfillsApplied, _credentials, _naasSessionId, nodeStubsPatched, NAAS_SESSION_RE, naasSessionPatched, coinTypeByValue, walletCore, initialized, SPONSORED_RPC, BlockchainClient, WalletCoreSigner, domainsByCoinId, GATEWAY_URL, WALLET_ID_SUFFIX, WALLET_ID_LENGTH, EVM_COIN_IDS, MAINTAINED_COIN_IDS, WalletProvider, AgentWalletProvider, SHUTDOWN_TIMEOUT_MS;
|
|
6459
6455
|
var init_dist3 = __esm({
|
|
6460
6456
|
"../wallet-core/dist/index.mjs"() {
|
|
6461
6457
|
"use strict";
|
|
@@ -6471,7 +6467,7 @@ var init_dist3 = __esm({
|
|
|
6471
6467
|
import_crypto2 = require("crypto");
|
|
6472
6468
|
init_dist2();
|
|
6473
6469
|
init_dist();
|
|
6474
|
-
|
|
6470
|
+
__require = /* @__PURE__ */ ((x6) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x6, {
|
|
6475
6471
|
get: (a3, b3) => (typeof require !== "undefined" ? require : a3)[b3]
|
|
6476
6472
|
}) : x6)(function(x6) {
|
|
6477
6473
|
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
@@ -6604,7 +6600,16 @@ var init_dist3 = __esm({
|
|
|
6604
6600
|
const chain2 = new import_trust_sdk2.DefinedChain(ct5);
|
|
6605
6601
|
const account = this.getAccountFromSigner(chainKey);
|
|
6606
6602
|
const asset = contractAddress ? new import_trust_sdk2.Asset.Token(account, contractAddress, tokenTypeForCoinId(coinId)) : new import_trust_sdk2.Asset.Coin(account);
|
|
6607
|
-
|
|
6603
|
+
let decimals;
|
|
6604
|
+
if (params.decimals !== void 0) {
|
|
6605
|
+
decimals = params.decimals;
|
|
6606
|
+
} else if (contractAddress) {
|
|
6607
|
+
const resolved = resolveToken(params.assetId, chainKey);
|
|
6608
|
+
if (!resolved) throw new Error(`Cannot resolve decimals for ${params.assetId}`);
|
|
6609
|
+
decimals = resolved.decimals;
|
|
6610
|
+
} else {
|
|
6611
|
+
decimals = getCoinDecimals(coinId);
|
|
6612
|
+
}
|
|
6608
6613
|
const amountWei = humanAmountToWei(params.amount, decimals);
|
|
6609
6614
|
let transaction = new import_trust_sdk2.TransactionTransfer(
|
|
6610
6615
|
params.to,
|
|
@@ -7518,11 +7523,11 @@ function __metadata(metadataKey, metadataValue) {
|
|
|
7518
7523
|
}
|
|
7519
7524
|
function __awaiter(thisArg, _arguments, P5, generator) {
|
|
7520
7525
|
function adopt(value) {
|
|
7521
|
-
return value instanceof P5 ? value : new P5(function(
|
|
7522
|
-
|
|
7526
|
+
return value instanceof P5 ? value : new P5(function(resolve2) {
|
|
7527
|
+
resolve2(value);
|
|
7523
7528
|
});
|
|
7524
7529
|
}
|
|
7525
|
-
return new (P5 || (P5 = Promise))(function(
|
|
7530
|
+
return new (P5 || (P5 = Promise))(function(resolve2, reject) {
|
|
7526
7531
|
function fulfilled(value) {
|
|
7527
7532
|
try {
|
|
7528
7533
|
step(generator.next(value));
|
|
@@ -7538,7 +7543,7 @@ function __awaiter(thisArg, _arguments, P5, generator) {
|
|
|
7538
7543
|
}
|
|
7539
7544
|
}
|
|
7540
7545
|
function step(result) {
|
|
7541
|
-
result.done ?
|
|
7546
|
+
result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
7542
7547
|
}
|
|
7543
7548
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
7544
7549
|
});
|
|
@@ -7716,14 +7721,14 @@ function __asyncValues(o4) {
|
|
|
7716
7721
|
}, i2);
|
|
7717
7722
|
function verb(n4) {
|
|
7718
7723
|
i2[n4] = o4[n4] && function(v5) {
|
|
7719
|
-
return new Promise(function(
|
|
7720
|
-
v5 = o4[n4](v5), settle(
|
|
7724
|
+
return new Promise(function(resolve2, reject) {
|
|
7725
|
+
v5 = o4[n4](v5), settle(resolve2, reject, v5.done, v5.value);
|
|
7721
7726
|
});
|
|
7722
7727
|
};
|
|
7723
7728
|
}
|
|
7724
|
-
function settle(
|
|
7729
|
+
function settle(resolve2, reject, d3, v5) {
|
|
7725
7730
|
Promise.resolve(v5).then(function(v6) {
|
|
7726
|
-
|
|
7731
|
+
resolve2({ value: v6, done: d3 });
|
|
7727
7732
|
}, reject);
|
|
7728
7733
|
}
|
|
7729
7734
|
}
|
|
@@ -7792,9 +7797,9 @@ var require_delay = __commonJS({
|
|
|
7792
7797
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
7793
7798
|
exports2.delay = void 0;
|
|
7794
7799
|
function delay(timeout) {
|
|
7795
|
-
return new Promise((
|
|
7800
|
+
return new Promise((resolve2) => {
|
|
7796
7801
|
setTimeout(() => {
|
|
7797
|
-
|
|
7802
|
+
resolve2(true);
|
|
7798
7803
|
}, timeout);
|
|
7799
7804
|
});
|
|
7800
7805
|
}
|
|
@@ -12877,13 +12882,12 @@ var init_dist5 = __esm({
|
|
|
12877
12882
|
}
|
|
12878
12883
|
}
|
|
12879
12884
|
async getAssetInfo(assetId) {
|
|
12880
|
-
const info2 = await this.client.gateway(
|
|
12885
|
+
const info2 = await this.client.gateway(
|
|
12886
|
+
"GET",
|
|
12887
|
+
`/v1/assets/${encodeURIComponent(assetId)}`
|
|
12888
|
+
);
|
|
12881
12889
|
try {
|
|
12882
|
-
const priceResp = await this.client.market(
|
|
12883
|
-
"POST",
|
|
12884
|
-
"/v2/market/tickers",
|
|
12885
|
-
{ currency: "USD", assets: [assetId] }
|
|
12886
|
-
);
|
|
12890
|
+
const priceResp = await this.client.market("POST", "/v2/market/tickers", { currency: "USD", assets: [assetId] });
|
|
12887
12891
|
const ticker = priceResp.tickers?.[0];
|
|
12888
12892
|
if (ticker) {
|
|
12889
12893
|
if (ticker.price != null) info2.priceUsd = ticker.price;
|
|
@@ -12919,17 +12923,22 @@ var init_dist5 = __esm({
|
|
|
12919
12923
|
return this.transformCoinStatus(assetId, raw);
|
|
12920
12924
|
}
|
|
12921
12925
|
async getAssetListings(params) {
|
|
12926
|
+
const limit = params.limit || 20;
|
|
12922
12927
|
const query = new URLSearchParams({
|
|
12923
12928
|
version: "27",
|
|
12924
12929
|
currency: params.currency || "USD",
|
|
12925
|
-
limit: String(params.limit
|
|
12930
|
+
limit: String(params.sort ? Math.max(limit, 50) : limit)
|
|
12926
12931
|
});
|
|
12927
12932
|
if (params.categoryId) query.set("category_id", params.categoryId);
|
|
12928
|
-
if (params.sort) query.set("sort", params.sort);
|
|
12929
12933
|
if (params.networks) query.set("networks", params.networks);
|
|
12930
|
-
const response = await this.client.gateway(
|
|
12934
|
+
const response = await this.client.gateway(
|
|
12935
|
+
"GET",
|
|
12936
|
+
"/v1/assets/listings",
|
|
12937
|
+
void 0,
|
|
12938
|
+
query.toString()
|
|
12939
|
+
);
|
|
12931
12940
|
const docs = response.docs || response.results || [];
|
|
12932
|
-
|
|
12941
|
+
const tokens = docs.map((doc) => {
|
|
12933
12942
|
const asset = doc.asset || doc;
|
|
12934
12943
|
const price = doc.price || {};
|
|
12935
12944
|
const market = doc.market || {};
|
|
@@ -12948,6 +12957,16 @@ var init_dist5 = __esm({
|
|
|
12948
12957
|
volume24hUsd: market.volume_24h ?? price.volume_24h ?? doc.volume_24h_usd
|
|
12949
12958
|
};
|
|
12950
12959
|
});
|
|
12960
|
+
if (params.sort) {
|
|
12961
|
+
const sortKeyMap = {
|
|
12962
|
+
percent_change_24h: "priceChange24h",
|
|
12963
|
+
market_cap: "marketCapUsd",
|
|
12964
|
+
volume_24h: "volume24hUsd"
|
|
12965
|
+
};
|
|
12966
|
+
const key = sortKeyMap[params.sort] || "priceChange24h";
|
|
12967
|
+
tokens.sort((a3, b3) => (b3[key] ?? 0) - (a3[key] ?? 0));
|
|
12968
|
+
}
|
|
12969
|
+
return tokens.slice(0, limit);
|
|
12951
12970
|
}
|
|
12952
12971
|
async getTrendingTokens(limit = 20, sort = "percent_change_24h") {
|
|
12953
12972
|
const knownAssets = [
|
|
@@ -12972,7 +12991,7 @@ var init_dist5 = __esm({
|
|
|
12972
12991
|
"c10000324",
|
|
12973
12992
|
"c81457"
|
|
12974
12993
|
];
|
|
12975
|
-
const response = await this.client.market("POST", "/
|
|
12994
|
+
const response = await this.client.market("POST", "/v2/market/tickers", {
|
|
12976
12995
|
currency: "USD",
|
|
12977
12996
|
assets: knownAssets
|
|
12978
12997
|
});
|
|
@@ -16095,7 +16114,7 @@ var require_compile = __commonJS({
|
|
|
16095
16114
|
const schOrFunc = root.refs[ref];
|
|
16096
16115
|
if (schOrFunc)
|
|
16097
16116
|
return schOrFunc;
|
|
16098
|
-
let _sch =
|
|
16117
|
+
let _sch = resolve2.call(this, root, ref);
|
|
16099
16118
|
if (_sch === void 0) {
|
|
16100
16119
|
const schema = (_a2 = root.localRefs) === null || _a2 === void 0 ? void 0 : _a2[ref];
|
|
16101
16120
|
const { schemaId } = this.opts;
|
|
@@ -16122,7 +16141,7 @@ var require_compile = __commonJS({
|
|
|
16122
16141
|
function sameSchemaEnv(s1, s2) {
|
|
16123
16142
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
16124
16143
|
}
|
|
16125
|
-
function
|
|
16144
|
+
function resolve2(root, ref) {
|
|
16126
16145
|
let sch;
|
|
16127
16146
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
16128
16147
|
ref = sch;
|
|
@@ -16697,7 +16716,7 @@ var require_fast_uri = __commonJS({
|
|
|
16697
16716
|
}
|
|
16698
16717
|
return uri;
|
|
16699
16718
|
}
|
|
16700
|
-
function
|
|
16719
|
+
function resolve2(baseURI, relativeURI, options) {
|
|
16701
16720
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
16702
16721
|
const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
16703
16722
|
schemelessOptions.skipEscape = true;
|
|
@@ -16924,7 +16943,7 @@ var require_fast_uri = __commonJS({
|
|
|
16924
16943
|
var fastUri = {
|
|
16925
16944
|
SCHEMES,
|
|
16926
16945
|
normalize,
|
|
16927
|
-
resolve:
|
|
16946
|
+
resolve: resolve2,
|
|
16928
16947
|
resolveComponent,
|
|
16929
16948
|
equal,
|
|
16930
16949
|
serialize,
|
|
@@ -19936,11 +19955,11 @@ function __rest2(s2, e2) {
|
|
|
19936
19955
|
}
|
|
19937
19956
|
function __awaiter2(thisArg, _arguments, P5, generator) {
|
|
19938
19957
|
function adopt(value) {
|
|
19939
|
-
return value instanceof P5 ? value : new P5(function(
|
|
19940
|
-
|
|
19958
|
+
return value instanceof P5 ? value : new P5(function(resolve2) {
|
|
19959
|
+
resolve2(value);
|
|
19941
19960
|
});
|
|
19942
19961
|
}
|
|
19943
|
-
return new (P5 || (P5 = Promise))(function(
|
|
19962
|
+
return new (P5 || (P5 = Promise))(function(resolve2, reject) {
|
|
19944
19963
|
function fulfilled(value) {
|
|
19945
19964
|
try {
|
|
19946
19965
|
step(generator.next(value));
|
|
@@ -19956,7 +19975,7 @@ function __awaiter2(thisArg, _arguments, P5, generator) {
|
|
|
19956
19975
|
}
|
|
19957
19976
|
}
|
|
19958
19977
|
function step(result) {
|
|
19959
|
-
result.done ?
|
|
19978
|
+
result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
19960
19979
|
}
|
|
19961
19980
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19962
19981
|
});
|
|
@@ -20526,8 +20545,8 @@ var init_timeline = __esm({
|
|
|
20526
20545
|
};
|
|
20527
20546
|
Timeline2.prototype.push = function(event) {
|
|
20528
20547
|
var _this = this;
|
|
20529
|
-
return new Promise(function(
|
|
20530
|
-
_this.queue.push([event,
|
|
20548
|
+
return new Promise(function(resolve2) {
|
|
20549
|
+
_this.queue.push([event, resolve2]);
|
|
20531
20550
|
_this.scheduleApply(0);
|
|
20532
20551
|
});
|
|
20533
20552
|
};
|
|
@@ -20547,7 +20566,7 @@ var init_timeline = __esm({
|
|
|
20547
20566
|
};
|
|
20548
20567
|
Timeline2.prototype.apply = function(item) {
|
|
20549
20568
|
return __awaiter2(this, void 0, void 0, function() {
|
|
20550
|
-
var _a2, event, _b,
|
|
20569
|
+
var _a2, event, _b, resolve2, before, before_1, before_1_1, plugin, e2, e_1_1, enrichment, enrichment_1, enrichment_1_1, plugin, e2, e_2_1, destination, executeDestinations;
|
|
20551
20570
|
var e_1, _c2, e_2, _d;
|
|
20552
20571
|
return __generator2(this, function(_e6) {
|
|
20553
20572
|
switch (_e6.label) {
|
|
@@ -20559,7 +20578,7 @@ var init_timeline = __esm({
|
|
|
20559
20578
|
];
|
|
20560
20579
|
}
|
|
20561
20580
|
_a2 = __read2(item, 1), event = _a2[0];
|
|
20562
|
-
_b = __read2(item, 2),
|
|
20581
|
+
_b = __read2(item, 2), resolve2 = _b[1];
|
|
20563
20582
|
this.loggerProvider.log("Timeline.apply: Initial event", event);
|
|
20564
20583
|
before = this.plugins.filter(function(plugin2) {
|
|
20565
20584
|
return plugin2.type === "before";
|
|
@@ -20580,7 +20599,7 @@ var init_timeline = __esm({
|
|
|
20580
20599
|
e2 = _e6.sent();
|
|
20581
20600
|
if (e2 === null) {
|
|
20582
20601
|
this.loggerProvider.log("Timeline.apply: Event filtered out by before plugin '".concat(String(plugin.name), "', event: ").concat(JSON.stringify(event)));
|
|
20583
|
-
|
|
20602
|
+
resolve2({ event, code: 0, message: "" });
|
|
20584
20603
|
return [
|
|
20585
20604
|
2
|
|
20586
20605
|
/*return*/
|
|
@@ -20629,7 +20648,7 @@ var init_timeline = __esm({
|
|
|
20629
20648
|
e2 = _e6.sent();
|
|
20630
20649
|
if (e2 === null) {
|
|
20631
20650
|
this.loggerProvider.log("Timeline.apply: Event filtered out by enrichment plugin '".concat(String(plugin.name), "', event: ").concat(JSON.stringify(event)));
|
|
20632
|
-
|
|
20651
|
+
resolve2({ event, code: 0, message: "" });
|
|
20633
20652
|
return [
|
|
20634
20653
|
2
|
|
20635
20654
|
/*return*/
|
|
@@ -20672,7 +20691,7 @@ var init_timeline = __esm({
|
|
|
20672
20691
|
void Promise.all(executeDestinations).then(function(_a3) {
|
|
20673
20692
|
var _b2 = __read2(_a3, 1), result = _b2[0];
|
|
20674
20693
|
var resolveResult = result || buildResult(event, 100, "Event not tracked, no destination plugins on the instance");
|
|
20675
|
-
|
|
20694
|
+
resolve2(resolveResult);
|
|
20676
20695
|
});
|
|
20677
20696
|
return [
|
|
20678
20697
|
2
|
|
@@ -21011,8 +21030,8 @@ var init_core_client = __esm({
|
|
|
21011
21030
|
var _this = this;
|
|
21012
21031
|
return __generator2(this, function(_a2) {
|
|
21013
21032
|
if (!this.isReady) {
|
|
21014
|
-
return [2, new Promise(function(
|
|
21015
|
-
_this.dispatchQ.push(_this.dispatchWithCallback.bind(_this, event,
|
|
21033
|
+
return [2, new Promise(function(resolve2) {
|
|
21034
|
+
_this.dispatchQ.push(_this.dispatchWithCallback.bind(_this, event, resolve2));
|
|
21016
21035
|
})];
|
|
21017
21036
|
}
|
|
21018
21037
|
return [2, this.process(event)];
|
|
@@ -21614,12 +21633,12 @@ var init_destination = __esm({
|
|
|
21614
21633
|
if (!event.insert_id) {
|
|
21615
21634
|
event.insert_id = UUID();
|
|
21616
21635
|
}
|
|
21617
|
-
return new Promise(function(
|
|
21636
|
+
return new Promise(function(resolve2) {
|
|
21618
21637
|
var context = {
|
|
21619
21638
|
event,
|
|
21620
21639
|
attempts: 0,
|
|
21621
21640
|
callback: function(result) {
|
|
21622
|
-
return
|
|
21641
|
+
return resolve2(result);
|
|
21623
21642
|
},
|
|
21624
21643
|
timeout: 0
|
|
21625
21644
|
};
|
|
@@ -22131,7 +22150,7 @@ var init_context = __esm({
|
|
|
22131
22150
|
};
|
|
22132
22151
|
Context2.prototype.execute = function(context) {
|
|
22133
22152
|
var _this = this;
|
|
22134
|
-
return new Promise(function(
|
|
22153
|
+
return new Promise(function(resolve2) {
|
|
22135
22154
|
var time3 = (/* @__PURE__ */ new Date()).getTime();
|
|
22136
22155
|
var contextEvent = __assign2(__assign2(__assign2({ time: time3, insert_id: UUID(), plan: _this.config.plan }, _this.config.ingestionMetadata && {
|
|
22137
22156
|
ingestion_metadata: {
|
|
@@ -22139,7 +22158,7 @@ var init_context = __esm({
|
|
|
22139
22158
|
source_version: _this.config.ingestionMetadata.sourceVersion
|
|
22140
22159
|
}
|
|
22141
22160
|
}), context), { event_id: _this.eventId++, library: _this.library });
|
|
22142
|
-
return
|
|
22161
|
+
return resolve2(contextEvent);
|
|
22143
22162
|
});
|
|
22144
22163
|
};
|
|
22145
22164
|
return Context2;
|
|
@@ -22185,7 +22204,7 @@ var init_http = __esm({
|
|
|
22185
22204
|
port: url.port,
|
|
22186
22205
|
protocol: url.protocol
|
|
22187
22206
|
};
|
|
22188
|
-
return new Promise(function(
|
|
22207
|
+
return new Promise(function(resolve2) {
|
|
22189
22208
|
var req = protocol.request(options, function(res) {
|
|
22190
22209
|
res.setEncoding("utf8");
|
|
22191
22210
|
var responsePayload = "";
|
|
@@ -22197,15 +22216,15 @@ var init_http = __esm({
|
|
|
22197
22216
|
try {
|
|
22198
22217
|
var parsedResponsePayload = JSON.parse(responsePayload);
|
|
22199
22218
|
var result = _this.buildResponse(parsedResponsePayload);
|
|
22200
|
-
|
|
22219
|
+
resolve2(result);
|
|
22201
22220
|
} catch (_a2) {
|
|
22202
|
-
|
|
22221
|
+
resolve2(_this.buildResponse({ code: res.statusCode }));
|
|
22203
22222
|
}
|
|
22204
22223
|
}
|
|
22205
22224
|
});
|
|
22206
22225
|
});
|
|
22207
22226
|
req.on("error", function() {
|
|
22208
|
-
return
|
|
22227
|
+
return resolve2(null);
|
|
22209
22228
|
});
|
|
22210
22229
|
req.end(requestPayload);
|
|
22211
22230
|
});
|
|
@@ -22417,6 +22436,20 @@ function info(message) {
|
|
|
22417
22436
|
function dim(message) {
|
|
22418
22437
|
console.log(import_chalk.default.dim(message));
|
|
22419
22438
|
}
|
|
22439
|
+
function safeExit() {
|
|
22440
|
+
const code2 = process.exitCode ?? 0;
|
|
22441
|
+
const streams = [process.stdout, process.stderr].filter(
|
|
22442
|
+
(s2) => s2.writableNeedDrain || s2.writableLength > 0
|
|
22443
|
+
);
|
|
22444
|
+
if (streams.length === 0) {
|
|
22445
|
+
process.exit(code2);
|
|
22446
|
+
}
|
|
22447
|
+
let pending = streams.length;
|
|
22448
|
+
const onDrain = () => {
|
|
22449
|
+
if (--pending === 0) process.exit(code2);
|
|
22450
|
+
};
|
|
22451
|
+
for (const s2 of streams) s2.once("drain", onDrain);
|
|
22452
|
+
}
|
|
22420
22453
|
|
|
22421
22454
|
// src/config-loader.ts
|
|
22422
22455
|
var import_chalk2 = __toESM(require_source());
|
|
@@ -22426,7 +22459,8 @@ init_dist();
|
|
|
22426
22459
|
var EMBEDDED_NAAS_SESSION_ID = "NDVjMWRjY2MtYTYwNy00NGNhLWIxZGUtMmJiZDIyNmEyODQ4";
|
|
22427
22460
|
function resolveNaasSessionId() {
|
|
22428
22461
|
if (process.env.TWAK_NAAS_SESSION_ID) return process.env.TWAK_NAAS_SESSION_ID;
|
|
22429
|
-
if (EMBEDDED_NAAS_SESSION_ID && !EMBEDDED_NAAS_SESSION_ID.startsWith("__"))
|
|
22462
|
+
if (EMBEDDED_NAAS_SESSION_ID && !EMBEDDED_NAAS_SESSION_ID.startsWith("__"))
|
|
22463
|
+
return EMBEDDED_NAAS_SESSION_ID;
|
|
22430
22464
|
return void 0;
|
|
22431
22465
|
}
|
|
22432
22466
|
async function getCredentials() {
|
|
@@ -22441,13 +22475,29 @@ async function getCredentials() {
|
|
|
22441
22475
|
return { accessId: credentials.accessId, hmacSecret: credentials.hmacSecret };
|
|
22442
22476
|
}
|
|
22443
22477
|
console.error(import_chalk2.default.red("No API credentials found."));
|
|
22444
|
-
console.error(
|
|
22478
|
+
console.error(
|
|
22479
|
+
import_chalk2.default.dim(
|
|
22480
|
+
"Set TWAK_ACCESS_ID and TWAK_HMAC_SECRET environment variables, then run `twak init`"
|
|
22481
|
+
)
|
|
22482
|
+
);
|
|
22445
22483
|
process.exit(1);
|
|
22446
22484
|
}
|
|
22447
22485
|
async function getClient() {
|
|
22448
22486
|
const creds = await getCredentials();
|
|
22449
22487
|
return createTwakClient({ ...creds });
|
|
22450
22488
|
}
|
|
22489
|
+
async function tryGetClient() {
|
|
22490
|
+
const envAccessId = process.env.TWAK_ACCESS_ID;
|
|
22491
|
+
const envSecret = process.env.TWAK_HMAC_SECRET;
|
|
22492
|
+
if (envAccessId && envSecret) {
|
|
22493
|
+
return createTwakClient({ accessId: envAccessId, hmacSecret: envSecret });
|
|
22494
|
+
}
|
|
22495
|
+
const credentials = await new CredentialManager().load();
|
|
22496
|
+
if (credentials) {
|
|
22497
|
+
return createTwakClient({ accessId: credentials.accessId, hmacSecret: credentials.hmacSecret });
|
|
22498
|
+
}
|
|
22499
|
+
return void 0;
|
|
22500
|
+
}
|
|
22451
22501
|
async function ensureTrustSDK() {
|
|
22452
22502
|
const creds = await getCredentials();
|
|
22453
22503
|
await initTrustSDK({
|
|
@@ -22475,7 +22525,9 @@ async function registerWalletWithBackend(creds, provider) {
|
|
|
22475
22525
|
async function resolvePassword(opts) {
|
|
22476
22526
|
if (opts?.password) {
|
|
22477
22527
|
process.stderr.write(
|
|
22478
|
-
import_chalk2.default.yellow(
|
|
22528
|
+
import_chalk2.default.yellow(
|
|
22529
|
+
"Note: --password is visible in shell history. Use `twak wallet keychain save` or TWAK_WALLET_PASSWORD instead.\n"
|
|
22530
|
+
)
|
|
22479
22531
|
);
|
|
22480
22532
|
return opts.password;
|
|
22481
22533
|
}
|
|
@@ -23155,8 +23207,8 @@ async function dispose(driver) {
|
|
|
23155
23207
|
|
|
23156
23208
|
// ../../node_modules/.pnpm/idb-keyval@6.2.2/node_modules/idb-keyval/dist/index.js
|
|
23157
23209
|
function promisifyRequest(request) {
|
|
23158
|
-
return new Promise((
|
|
23159
|
-
request.oncomplete = request.onsuccess = () =>
|
|
23210
|
+
return new Promise((resolve2, reject) => {
|
|
23211
|
+
request.oncomplete = request.onsuccess = () => resolve2(request.result);
|
|
23160
23212
|
request.onabort = request.onerror = () => reject(request.error);
|
|
23161
23213
|
});
|
|
23162
23214
|
}
|
|
@@ -38756,8 +38808,24 @@ function getQRString(address) {
|
|
|
38756
38808
|
}
|
|
38757
38809
|
function registerWalletCommand(program3) {
|
|
38758
38810
|
const wallet = program3.command("wallet").description("Manage agent wallet");
|
|
38759
|
-
wallet.command("create").description("Create a new agent wallet").requiredOption("--password <password>", "Password to encrypt the wallet").option("--no-keychain", "Do not save password to OS keychain").option("--json", "JSON output").action(async (opts) => {
|
|
38811
|
+
wallet.command("create").description("Create a new agent wallet").requiredOption("--password <password>", "Password to encrypt the wallet").option("--no-keychain", "Do not save password to OS keychain").option("--skip-password-check", "Skip password strength validation").option("--json", "JSON output").action(async (opts) => {
|
|
38760
38812
|
try {
|
|
38813
|
+
if (!opts.skipPasswordCheck) {
|
|
38814
|
+
const pw = opts.password;
|
|
38815
|
+
const issues = [];
|
|
38816
|
+
if (pw.length < 8) issues.push("be at least 8 characters long");
|
|
38817
|
+
if (!/[a-z]/.test(pw)) issues.push("include a lowercase letter");
|
|
38818
|
+
if (!/[A-Z]/.test(pw)) issues.push("include an uppercase letter");
|
|
38819
|
+
if (!/[0-9]/.test(pw)) issues.push("include a number");
|
|
38820
|
+
if (issues.length > 0) {
|
|
38821
|
+
const msg = `Password too weak: must ${issues.join(", ")}.`;
|
|
38822
|
+
error(msg);
|
|
38823
|
+
if (opts.json) output({ error: msg }, true);
|
|
38824
|
+
process.exitCode = 1;
|
|
38825
|
+
process.exit();
|
|
38826
|
+
return;
|
|
38827
|
+
}
|
|
38828
|
+
}
|
|
38761
38829
|
await ensureTrustSDK();
|
|
38762
38830
|
const store = new WalletConfigStore();
|
|
38763
38831
|
if (await store.exists()) {
|
|
@@ -38765,7 +38833,7 @@ function registerWalletCommand(program3) {
|
|
|
38765
38833
|
error(msg);
|
|
38766
38834
|
if (opts.json) output({ error: msg }, true);
|
|
38767
38835
|
process.exitCode = 1;
|
|
38768
|
-
|
|
38836
|
+
safeExit();
|
|
38769
38837
|
return;
|
|
38770
38838
|
}
|
|
38771
38839
|
const provider = await AgentWalletProvider.create(opts.password);
|
|
@@ -38796,9 +38864,12 @@ function registerWalletCommand(program3) {
|
|
|
38796
38864
|
if (opts.json) output({ error: msg }, true);
|
|
38797
38865
|
process.exitCode = 1;
|
|
38798
38866
|
}
|
|
38799
|
-
|
|
38867
|
+
safeExit();
|
|
38800
38868
|
});
|
|
38801
|
-
wallet.command("address").description("Get wallet address for a chain").requiredOption("--chain <key>", "Chain key (e.g., ethereum, solana)").option(
|
|
38869
|
+
wallet.command("address").description("Get wallet address for a chain").requiredOption("--chain <key>", "Chain key (e.g., ethereum, solana)").option(
|
|
38870
|
+
"--password <password>",
|
|
38871
|
+
"Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
|
|
38872
|
+
).option("--json", "JSON output").action(async (opts) => {
|
|
38802
38873
|
try {
|
|
38803
38874
|
await ensureTrustSDK();
|
|
38804
38875
|
const password = await resolvePassword(opts);
|
|
@@ -38820,9 +38891,12 @@ ${qr3}`;
|
|
|
38820
38891
|
if (opts.json) output({ error: msg }, true);
|
|
38821
38892
|
process.exitCode = 1;
|
|
38822
38893
|
}
|
|
38823
|
-
|
|
38894
|
+
safeExit();
|
|
38824
38895
|
});
|
|
38825
|
-
wallet.command("addresses").description("List all wallet addresses").option(
|
|
38896
|
+
wallet.command("addresses").description("List all wallet addresses").option(
|
|
38897
|
+
"--password <password>",
|
|
38898
|
+
"Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
|
|
38899
|
+
).option("--json", "JSON output").action(async (opts) => {
|
|
38826
38900
|
try {
|
|
38827
38901
|
await ensureTrustSDK();
|
|
38828
38902
|
const password = await resolvePassword(opts);
|
|
@@ -38868,16 +38942,19 @@ ${qr3}`;
|
|
|
38868
38942
|
if (opts.json) output({ error: msg }, true);
|
|
38869
38943
|
process.exitCode = 1;
|
|
38870
38944
|
}
|
|
38871
|
-
|
|
38945
|
+
safeExit();
|
|
38872
38946
|
});
|
|
38873
|
-
wallet.command("balance").description("Get wallet balance on a chain (native + token holdings)").option("--chain <key>", "Chain key").option("--all", "Show balances across all chains with funds").option(
|
|
38947
|
+
wallet.command("balance").description("Get wallet balance on a chain (native + token holdings)").option("--chain <key>", "Chain key").option("--all", "Show balances across all chains with funds").option(
|
|
38948
|
+
"--password <password>",
|
|
38949
|
+
"Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
|
|
38950
|
+
).option("--no-tokens", "Skip token balance lookup").option("--json", "JSON output").action(async (opts) => {
|
|
38874
38951
|
try {
|
|
38875
38952
|
if (opts.all && opts.chain) {
|
|
38876
38953
|
const msg = "--all and --chain are mutually exclusive";
|
|
38877
38954
|
error(msg);
|
|
38878
38955
|
if (opts.json) output({ error: msg }, true);
|
|
38879
38956
|
process.exitCode = 1;
|
|
38880
|
-
|
|
38957
|
+
safeExit();
|
|
38881
38958
|
return;
|
|
38882
38959
|
}
|
|
38883
38960
|
if (!opts.all && !opts.chain) {
|
|
@@ -38885,7 +38962,7 @@ ${qr3}`;
|
|
|
38885
38962
|
error(msg);
|
|
38886
38963
|
if (opts.json) output({ error: msg }, true);
|
|
38887
38964
|
process.exitCode = 1;
|
|
38888
|
-
|
|
38965
|
+
safeExit();
|
|
38889
38966
|
return;
|
|
38890
38967
|
}
|
|
38891
38968
|
const creds = await ensureTrustSDK();
|
|
@@ -38912,11 +38989,14 @@ ${qr3}`;
|
|
|
38912
38989
|
const holdings = await securityService.getTokenHoldings(assets);
|
|
38913
38990
|
for (const h4 of holdings) {
|
|
38914
38991
|
if (h4.tokens?.length) {
|
|
38915
|
-
holdingsMap.set(
|
|
38916
|
-
|
|
38917
|
-
|
|
38918
|
-
|
|
38919
|
-
|
|
38992
|
+
holdingsMap.set(
|
|
38993
|
+
h4.coin,
|
|
38994
|
+
h4.tokens.map((t) => ({
|
|
38995
|
+
symbol: t.symbol ?? t.contract,
|
|
38996
|
+
contract: t.contract,
|
|
38997
|
+
decimals: t.decimals ?? 18
|
|
38998
|
+
}))
|
|
38999
|
+
);
|
|
38920
39000
|
}
|
|
38921
39001
|
}
|
|
38922
39002
|
} catch {
|
|
@@ -38935,11 +39015,14 @@ ${qr3}`;
|
|
|
38935
39015
|
const retryHoldings = await retrySec.getTokenHoldings(retryAssets);
|
|
38936
39016
|
for (const h4 of retryHoldings) {
|
|
38937
39017
|
if (h4.tokens?.length) {
|
|
38938
|
-
holdingsMap.set(
|
|
38939
|
-
|
|
38940
|
-
|
|
38941
|
-
|
|
38942
|
-
|
|
39018
|
+
holdingsMap.set(
|
|
39019
|
+
h4.coin,
|
|
39020
|
+
h4.tokens.map((t) => ({
|
|
39021
|
+
symbol: t.symbol ?? t.contract,
|
|
39022
|
+
contract: t.contract,
|
|
39023
|
+
decimals: t.decimals ?? 18
|
|
39024
|
+
}))
|
|
39025
|
+
);
|
|
38943
39026
|
}
|
|
38944
39027
|
}
|
|
38945
39028
|
} catch {
|
|
@@ -38960,6 +39043,7 @@ ${qr3}`;
|
|
|
38960
39043
|
if (knownTokens) {
|
|
38961
39044
|
for (const [sym, addr] of Object.entries(knownTokens)) {
|
|
38962
39045
|
const resolved = resolveToken(sym, chainKey);
|
|
39046
|
+
if (!resolved) continue;
|
|
38963
39047
|
tokenDefs.push({ symbol: sym, contract: addr, decimals: resolved.decimals });
|
|
38964
39048
|
}
|
|
38965
39049
|
}
|
|
@@ -38967,11 +39051,15 @@ ${qr3}`;
|
|
|
38967
39051
|
const tokenResults = await Promise.all(
|
|
38968
39052
|
tokenDefs.map(async (t) => {
|
|
38969
39053
|
try {
|
|
38970
|
-
const bal = await blockchainClient.getBalance(
|
|
39054
|
+
const bal = await blockchainClient.getBalance(
|
|
39055
|
+
`c${chain2.coinId}_t${t.contract}`,
|
|
39056
|
+
address
|
|
39057
|
+
);
|
|
38971
39058
|
const rawBal = bal.total ?? bal.available ?? "0";
|
|
38972
39059
|
if (BigInt(rawBal) <= 0n) return null;
|
|
38973
39060
|
const human = fmtRaw(rawBal, t.decimals);
|
|
38974
|
-
if (human && human !== "0.000000")
|
|
39061
|
+
if (human && human !== "0.000000")
|
|
39062
|
+
return { symbol: t.symbol, contract: t.contract, balance: human };
|
|
38975
39063
|
} catch {
|
|
38976
39064
|
}
|
|
38977
39065
|
return null;
|
|
@@ -39000,7 +39088,9 @@ ${qr3}`;
|
|
|
39000
39088
|
addressMap.set(a3.chainId, a3.address);
|
|
39001
39089
|
}
|
|
39002
39090
|
const results = [];
|
|
39003
|
-
const chainEntries = chainKeys.map((k4) => ({ chainKey: k4, chain: getChainById2(k4), address: addressMap.get(k4) })).filter(
|
|
39091
|
+
const chainEntries = chainKeys.map((k4) => ({ chainKey: k4, chain: getChainById2(k4), address: addressMap.get(k4) })).filter(
|
|
39092
|
+
(e2) => !!e2.chain && !!e2.address
|
|
39093
|
+
);
|
|
39004
39094
|
const settled = await Promise.allSettled(
|
|
39005
39095
|
chainEntries.map(
|
|
39006
39096
|
async ({ chainKey, chain: chain2, address }) => fetchChainBalance(chainKey, chain2, address, opts.tokens === false)
|
|
@@ -39010,7 +39100,10 @@ ${qr3}`;
|
|
|
39010
39100
|
const result = settled[i2];
|
|
39011
39101
|
if (result.status === "rejected") {
|
|
39012
39102
|
const chainKey = chainEntries[i2].chainKey;
|
|
39013
|
-
results.push({
|
|
39103
|
+
results.push({
|
|
39104
|
+
chain: chainKey,
|
|
39105
|
+
error: result.reason instanceof Error ? result.reason.message : String(result.reason)
|
|
39106
|
+
});
|
|
39014
39107
|
continue;
|
|
39015
39108
|
}
|
|
39016
39109
|
const r3 = result.value;
|
|
@@ -39054,15 +39147,10 @@ ${qr3}`;
|
|
|
39054
39147
|
error(msg);
|
|
39055
39148
|
if (opts.json) output({ error: msg }, true);
|
|
39056
39149
|
process.exitCode = 1;
|
|
39057
|
-
|
|
39150
|
+
safeExit();
|
|
39058
39151
|
return;
|
|
39059
39152
|
}
|
|
39060
|
-
const result = await fetchChainBalance(
|
|
39061
|
-
opts.chain,
|
|
39062
|
-
chain2,
|
|
39063
|
-
address,
|
|
39064
|
-
opts.tokens === false
|
|
39065
|
-
);
|
|
39153
|
+
const result = await fetchChainBalance(opts.chain, chain2, address, opts.tokens === false);
|
|
39066
39154
|
if (opts.json) {
|
|
39067
39155
|
output(result, true);
|
|
39068
39156
|
} else {
|
|
@@ -39086,7 +39174,7 @@ ${qr3}`;
|
|
|
39086
39174
|
if (opts.json) output({ error: msg }, true);
|
|
39087
39175
|
process.exitCode = 1;
|
|
39088
39176
|
}
|
|
39089
|
-
|
|
39177
|
+
safeExit();
|
|
39090
39178
|
});
|
|
39091
39179
|
const keychain = wallet.command("keychain").description("Manage wallet password in OS keychain");
|
|
39092
39180
|
keychain.command("save").description("Save wallet password to OS keychain").requiredOption("--password <password>", "Wallet password to store").action(async (opts) => {
|
|
@@ -39098,7 +39186,7 @@ ${qr3}`;
|
|
|
39098
39186
|
error(err instanceof Error ? err.message : String(err));
|
|
39099
39187
|
process.exitCode = 1;
|
|
39100
39188
|
}
|
|
39101
|
-
|
|
39189
|
+
safeExit();
|
|
39102
39190
|
});
|
|
39103
39191
|
keychain.command("delete").description("Remove wallet password from OS keychain").action(async () => {
|
|
39104
39192
|
try {
|
|
@@ -39109,13 +39197,14 @@ ${qr3}`;
|
|
|
39109
39197
|
error(err instanceof Error ? err.message : String(err));
|
|
39110
39198
|
process.exitCode = 1;
|
|
39111
39199
|
}
|
|
39112
|
-
|
|
39200
|
+
safeExit();
|
|
39113
39201
|
});
|
|
39114
39202
|
keychain.command("check").description("Check if wallet password is stored in OS keychain").action(async () => {
|
|
39115
39203
|
const kc2 = new KeychainManager();
|
|
39116
39204
|
if (!kc2.isAvailable()) {
|
|
39117
39205
|
warn("keytar not available on this system");
|
|
39118
|
-
|
|
39206
|
+
safeExit();
|
|
39207
|
+
return;
|
|
39119
39208
|
}
|
|
39120
39209
|
const pw = await kc2.getWalletPassword();
|
|
39121
39210
|
if (pw) {
|
|
@@ -39123,13 +39212,21 @@ ${qr3}`;
|
|
|
39123
39212
|
} else {
|
|
39124
39213
|
info("No wallet password found in OS keychain");
|
|
39125
39214
|
}
|
|
39126
|
-
|
|
39215
|
+
safeExit();
|
|
39127
39216
|
});
|
|
39128
|
-
wallet.command("connect").description("Connect external wallet via WalletConnect").option("--project-id <id>", "WalletConnect project ID (or set WALLETCONNECT_PROJECT_ID)").option(
|
|
39217
|
+
wallet.command("connect").description("Connect external wallet via WalletConnect").option("--project-id <id>", "WalletConnect project ID (or set WALLETCONNECT_PROJECT_ID)").option(
|
|
39218
|
+
"--chains <list>",
|
|
39219
|
+
"Comma-separated CAIP-2 chain IDs",
|
|
39220
|
+
"eip155:1,eip155:56,eip155:137,eip155:8453,eip155:42161"
|
|
39221
|
+
).option("--timeout <seconds>", "Connection timeout in seconds", "300").option("--json", "JSON output").action(async (opts) => {
|
|
39129
39222
|
const projectId = opts.projectId || process.env.WALLETCONNECT_PROJECT_ID;
|
|
39130
39223
|
if (!projectId) {
|
|
39131
|
-
error(
|
|
39132
|
-
|
|
39224
|
+
error(
|
|
39225
|
+
"WalletConnect project ID required. Use --project-id or set WALLETCONNECT_PROJECT_ID"
|
|
39226
|
+
);
|
|
39227
|
+
process.exitCode = 1;
|
|
39228
|
+
safeExit();
|
|
39229
|
+
return;
|
|
39133
39230
|
}
|
|
39134
39231
|
const provider = new WalletConnectProvider({ projectId });
|
|
39135
39232
|
const chains = opts.chains.split(",").map((s2) => s2.trim()).filter(Boolean);
|
|
@@ -39145,13 +39242,21 @@ ${qr3}`;
|
|
|
39145
39242
|
const timeoutMs = parseInt(opts.timeout, 10) * 1e3;
|
|
39146
39243
|
const session = await provider.waitForSession(timeoutMs);
|
|
39147
39244
|
if (opts.json) {
|
|
39148
|
-
output(
|
|
39245
|
+
output(
|
|
39246
|
+
{
|
|
39247
|
+
connected: true,
|
|
39248
|
+
address: session.address,
|
|
39249
|
+
chainId: session.chainId,
|
|
39250
|
+
peer: session.peerName
|
|
39251
|
+
},
|
|
39252
|
+
true
|
|
39253
|
+
);
|
|
39149
39254
|
} else {
|
|
39150
39255
|
success(`Connected to ${session.peerName || "wallet"}`);
|
|
39151
39256
|
info(` Address: ${session.address}`);
|
|
39152
39257
|
info(` Chain: ${session.chainId}`);
|
|
39153
39258
|
}
|
|
39154
|
-
|
|
39259
|
+
safeExit();
|
|
39155
39260
|
});
|
|
39156
39261
|
wallet.command("status").description("Check wallet configuration status").option("--json", "JSON output").action(async (opts) => {
|
|
39157
39262
|
const store = new WalletConfigStore();
|
|
@@ -39177,17 +39282,28 @@ ${qr3}`;
|
|
|
39177
39282
|
if (opts.json) {
|
|
39178
39283
|
output(result, true);
|
|
39179
39284
|
} else {
|
|
39180
|
-
console.log(
|
|
39285
|
+
console.log(
|
|
39286
|
+
` ${import_chalk5.default.dim("Wallet")} ${exists ? import_chalk5.default.green("configured") : import_chalk5.default.yellow("not configured")}`
|
|
39287
|
+
);
|
|
39181
39288
|
if (createdAt) console.log(` ${import_chalk5.default.dim("Created")} ${createdAt}`);
|
|
39182
|
-
console.log(
|
|
39289
|
+
console.log(
|
|
39290
|
+
` ${import_chalk5.default.dim("Keychain")} ${keychainHasPassword ? import_chalk5.default.green("password stored") : import_chalk5.default.yellow("no password")}`
|
|
39291
|
+
);
|
|
39183
39292
|
console.log(` ${import_chalk5.default.dim("Chains")} ${totalChains} supported`);
|
|
39184
39293
|
if (addressCount !== void 0) console.log(` ${import_chalk5.default.dim("Addresses")} ${addressCount}`);
|
|
39185
39294
|
if (!exists) info("\nRun `twak wallet create --password <pw>` to create a wallet.");
|
|
39186
39295
|
}
|
|
39187
39296
|
});
|
|
39188
|
-
wallet.command("portfolio").description("Show portfolio balances and USD values across chains, including token holdings").option(
|
|
39297
|
+
wallet.command("portfolio").description("Show portfolio balances and USD values across chains, including token holdings").option(
|
|
39298
|
+
"--chains <list>",
|
|
39299
|
+
"Comma-separated chain keys (default: all supported chains)",
|
|
39300
|
+
Object.keys(CHAINS).join(",")
|
|
39301
|
+
).option(
|
|
39302
|
+
"--password <password>",
|
|
39303
|
+
"Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
|
|
39304
|
+
).option("--json", "JSON output").action(async (opts) => {
|
|
39189
39305
|
try {
|
|
39190
|
-
await ensureTrustSDK();
|
|
39306
|
+
const creds = await ensureTrustSDK();
|
|
39191
39307
|
const password = await resolvePassword(opts);
|
|
39192
39308
|
const provider = await AgentWalletProvider.fromStorage(password);
|
|
39193
39309
|
const { getChainById: getChainById2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
|
|
@@ -39203,6 +39319,15 @@ ${qr3}`;
|
|
|
39203
39319
|
return [{ chainKey, chain: chain2, address: addrEntry.address }];
|
|
39204
39320
|
});
|
|
39205
39321
|
const suppress = (fn2) => fn2();
|
|
39322
|
+
const withTimeout = (promise, ms3) => {
|
|
39323
|
+
let timerId;
|
|
39324
|
+
const timeout = new Promise((_2, reject) => {
|
|
39325
|
+
timerId = setTimeout(() => reject(new Error(`timed out after ${ms3 / 1e3}s`)), ms3);
|
|
39326
|
+
});
|
|
39327
|
+
return Promise.race([promise, timeout]).finally(() => {
|
|
39328
|
+
if (timerId !== void 0) clearTimeout(timerId);
|
|
39329
|
+
});
|
|
39330
|
+
};
|
|
39206
39331
|
const pMap = async (items, fn2, concurrency) => {
|
|
39207
39332
|
const results = new Array(items.length);
|
|
39208
39333
|
let idx = 0;
|
|
@@ -39212,7 +39337,9 @@ ${qr3}`;
|
|
|
39212
39337
|
results[i2] = await fn2(items[i2]);
|
|
39213
39338
|
}
|
|
39214
39339
|
};
|
|
39215
|
-
await Promise.all(
|
|
39340
|
+
await Promise.all(
|
|
39341
|
+
Array.from({ length: Math.min(concurrency, items.length) }, () => run())
|
|
39342
|
+
);
|
|
39216
39343
|
return results;
|
|
39217
39344
|
};
|
|
39218
39345
|
const toHuman = (raw, decimals) => {
|
|
@@ -39223,16 +39350,52 @@ ${qr3}`;
|
|
|
39223
39350
|
const fracStr = fracPart.toString().padStart(decimals, "0").replace(/0+$/, "");
|
|
39224
39351
|
return fracStr ? `${intPart}.${fracStr}` : intPart.toString();
|
|
39225
39352
|
};
|
|
39226
|
-
const holdingsAssets = chainEntries.map(
|
|
39353
|
+
const holdingsAssets = chainEntries.map(
|
|
39354
|
+
({ chain: chain2, address }) => ({
|
|
39355
|
+
coin: chain2.coinId,
|
|
39356
|
+
address
|
|
39357
|
+
})
|
|
39358
|
+
);
|
|
39227
39359
|
const [nativeResults, holdings] = await Promise.all([
|
|
39228
|
-
pMap(
|
|
39229
|
-
|
|
39230
|
-
|
|
39231
|
-
|
|
39232
|
-
|
|
39233
|
-
|
|
39234
|
-
}
|
|
39235
|
-
|
|
39360
|
+
pMap(
|
|
39361
|
+
chainEntries,
|
|
39362
|
+
async ({
|
|
39363
|
+
chainKey,
|
|
39364
|
+
chain: chain2,
|
|
39365
|
+
address
|
|
39366
|
+
}) => {
|
|
39367
|
+
try {
|
|
39368
|
+
const balance = await withTimeout(
|
|
39369
|
+
suppress(() => blockchainClient.getBalance(`c${chain2.coinId}`, address)),
|
|
39370
|
+
15e3
|
|
39371
|
+
);
|
|
39372
|
+
if (!balance) {
|
|
39373
|
+
return {
|
|
39374
|
+
chainKey,
|
|
39375
|
+
chain: chain2,
|
|
39376
|
+
address,
|
|
39377
|
+
balance: null,
|
|
39378
|
+
error: "empty response from SDK"
|
|
39379
|
+
};
|
|
39380
|
+
}
|
|
39381
|
+
return { chainKey, chain: chain2, address, balance, error: void 0 };
|
|
39382
|
+
} catch (e2) {
|
|
39383
|
+
const code2 = e2 != null ? e2.code : void 0;
|
|
39384
|
+
let msg;
|
|
39385
|
+
if (code2 === 429) {
|
|
39386
|
+
msg = "rate limited (429)";
|
|
39387
|
+
} else if (e2 instanceof Error && e2.message && e2.message !== "null") {
|
|
39388
|
+
msg = e2.message;
|
|
39389
|
+
} else if (code2) {
|
|
39390
|
+
msg = `HTTP ${code2}`;
|
|
39391
|
+
} else {
|
|
39392
|
+
msg = "unsupported or unavailable";
|
|
39393
|
+
}
|
|
39394
|
+
return { chainKey, chain: chain2, address, balance: null, error: msg };
|
|
39395
|
+
}
|
|
39396
|
+
},
|
|
39397
|
+
5
|
|
39398
|
+
),
|
|
39236
39399
|
(async () => {
|
|
39237
39400
|
try {
|
|
39238
39401
|
return await new SecurityService2(httpClient).getTokenHoldings(holdingsAssets);
|
|
@@ -39245,14 +39408,27 @@ ${qr3}`;
|
|
|
39245
39408
|
for (const h4 of holdings) {
|
|
39246
39409
|
if (h4.tokens?.length) holdingsMap.set(h4.coin, h4.tokens);
|
|
39247
39410
|
}
|
|
39411
|
+
if (holdingsMap.size === 0) {
|
|
39412
|
+
try {
|
|
39413
|
+
await registerWalletWithBackend(creds, provider);
|
|
39414
|
+
const retryHoldings = await new SecurityService2(httpClient).getTokenHoldings(
|
|
39415
|
+
holdingsAssets
|
|
39416
|
+
);
|
|
39417
|
+
for (const h4 of retryHoldings) {
|
|
39418
|
+
if (h4.tokens?.length) holdingsMap.set(h4.coin, h4.tokens);
|
|
39419
|
+
}
|
|
39420
|
+
} catch {
|
|
39421
|
+
}
|
|
39422
|
+
}
|
|
39248
39423
|
for (const { chainKey, chain: chain2 } of chainEntries) {
|
|
39249
39424
|
const coinId = chain2.coinId;
|
|
39250
39425
|
if (holdingsMap.has(coinId)) continue;
|
|
39251
39426
|
const knownTokens = COMMON_TOKENS[chainKey];
|
|
39252
39427
|
if (!knownTokens) continue;
|
|
39253
|
-
const fallbackTokens = Object.entries(knownTokens).
|
|
39428
|
+
const fallbackTokens = Object.entries(knownTokens).flatMap(([sym, addr]) => {
|
|
39254
39429
|
const resolved = resolveToken(sym, chainKey);
|
|
39255
|
-
|
|
39430
|
+
if (!resolved) return [];
|
|
39431
|
+
return [{ contract: addr, decimals: resolved.decimals, symbol: sym }];
|
|
39256
39432
|
});
|
|
39257
39433
|
holdingsMap.set(coinId, fallbackTokens);
|
|
39258
39434
|
}
|
|
@@ -39260,28 +39436,45 @@ ${qr3}`;
|
|
|
39260
39436
|
for (const { chainKey, chain: chain2, address } of chainEntries) {
|
|
39261
39437
|
const tokens = holdingsMap.get(chain2.coinId) ?? [];
|
|
39262
39438
|
for (const t of tokens) {
|
|
39263
|
-
if (t.contract)
|
|
39439
|
+
if (t.contract)
|
|
39440
|
+
tokenJobs.push({
|
|
39441
|
+
chainKey,
|
|
39442
|
+
coinId: chain2.coinId,
|
|
39443
|
+
address,
|
|
39444
|
+
...t
|
|
39445
|
+
});
|
|
39264
39446
|
}
|
|
39265
39447
|
}
|
|
39266
|
-
const tokenBalances = await pMap(
|
|
39267
|
-
|
|
39268
|
-
|
|
39269
|
-
const
|
|
39270
|
-
|
|
39271
|
-
|
|
39272
|
-
|
|
39273
|
-
|
|
39274
|
-
|
|
39275
|
-
|
|
39276
|
-
|
|
39448
|
+
const tokenBalances = await pMap(
|
|
39449
|
+
tokenJobs,
|
|
39450
|
+
async (job) => {
|
|
39451
|
+
const fallback = job.balance ?? "0";
|
|
39452
|
+
try {
|
|
39453
|
+
const assetId = `c${job.coinId}_t${job.contract}`;
|
|
39454
|
+
const bal = await withTimeout(
|
|
39455
|
+
suppress(() => blockchainClient.getBalance(assetId, job.address)),
|
|
39456
|
+
15e3
|
|
39457
|
+
);
|
|
39458
|
+
return { ...job, rawBalance: bal.total ?? bal.available ?? fallback };
|
|
39459
|
+
} catch {
|
|
39460
|
+
return { ...job, rawBalance: fallback };
|
|
39461
|
+
}
|
|
39462
|
+
},
|
|
39463
|
+
5
|
|
39464
|
+
);
|
|
39465
|
+
const nativeAssetIds = nativeResults.map(
|
|
39466
|
+
({ chain: chain2 }) => `c${chain2.coinId}`
|
|
39467
|
+
);
|
|
39277
39468
|
const nonZeroTokens = tokenBalances.filter((t) => {
|
|
39278
39469
|
const dec = t.decimals ?? 18;
|
|
39279
39470
|
return toHuman(t.rawBalance, dec) !== "0";
|
|
39280
39471
|
});
|
|
39281
|
-
const tokenAssetIds = nonZeroTokens.map(
|
|
39472
|
+
const tokenAssetIds = nonZeroTokens.map(
|
|
39473
|
+
(t) => `c${t.coinId}_t${t.contract}`
|
|
39474
|
+
);
|
|
39282
39475
|
const priceMap = /* @__PURE__ */ new Map();
|
|
39283
39476
|
try {
|
|
39284
|
-
const priceResp = await httpClient.market("POST", "/
|
|
39477
|
+
const priceResp = await httpClient.market("POST", "/v2/market/tickers", {
|
|
39285
39478
|
currency: "USD",
|
|
39286
39479
|
assets: [.../* @__PURE__ */ new Set([...nativeAssetIds, ...tokenAssetIds])]
|
|
39287
39480
|
});
|
|
@@ -39291,13 +39484,33 @@ ${qr3}`;
|
|
|
39291
39484
|
} catch {
|
|
39292
39485
|
}
|
|
39293
39486
|
const rows = [];
|
|
39294
|
-
|
|
39487
|
+
const failedChains = [];
|
|
39488
|
+
for (const { chainKey, chain: chain2, address, balance, error: fetchError } of nativeResults) {
|
|
39295
39489
|
const c3 = chain2;
|
|
39490
|
+
if (fetchError) {
|
|
39491
|
+
failedChains.push(`${chainKey} (${fetchError})`);
|
|
39492
|
+
rows.push({
|
|
39493
|
+
chain: chainKey,
|
|
39494
|
+
type: "native",
|
|
39495
|
+
symbol: c3.symbol,
|
|
39496
|
+
address,
|
|
39497
|
+
balance: "--",
|
|
39498
|
+
usdValue: null
|
|
39499
|
+
});
|
|
39500
|
+
continue;
|
|
39501
|
+
}
|
|
39296
39502
|
const price = priceMap.get(`c${c3.coinId}`) ?? null;
|
|
39297
39503
|
const rawTotal = balance?.total ?? balance?.available ?? "0";
|
|
39298
39504
|
const humanAmount = toHuman(rawTotal, c3.decimals);
|
|
39299
39505
|
const usdValue = price !== null ? parseFloat((parseFloat(humanAmount) * price).toFixed(2)) : null;
|
|
39300
|
-
rows.push({
|
|
39506
|
+
rows.push({
|
|
39507
|
+
chain: chainKey,
|
|
39508
|
+
type: "native",
|
|
39509
|
+
symbol: c3.symbol,
|
|
39510
|
+
address,
|
|
39511
|
+
balance: humanAmount,
|
|
39512
|
+
usdValue
|
|
39513
|
+
});
|
|
39301
39514
|
}
|
|
39302
39515
|
for (const t of tokenBalances) {
|
|
39303
39516
|
const dec = t.decimals ?? 18;
|
|
@@ -39306,22 +39519,39 @@ ${qr3}`;
|
|
|
39306
39519
|
const tokenAssetId = `c${t.coinId}_t${t.contract}`;
|
|
39307
39520
|
const tokenPrice = priceMap.get(tokenAssetId) ?? null;
|
|
39308
39521
|
const usdValue = tokenPrice !== null ? parseFloat((parseFloat(humanAmount) * tokenPrice).toFixed(2)) : null;
|
|
39309
|
-
rows.push({
|
|
39522
|
+
rows.push({
|
|
39523
|
+
chain: t.chainKey,
|
|
39524
|
+
type: "token",
|
|
39525
|
+
symbol: t.symbol ?? t.contract,
|
|
39526
|
+
address: t.address,
|
|
39527
|
+
contract: t.contract,
|
|
39528
|
+
balance: humanAmount,
|
|
39529
|
+
usdValue
|
|
39530
|
+
});
|
|
39310
39531
|
}
|
|
39311
39532
|
if (opts.json) {
|
|
39533
|
+
if (failedChains.length > 0) {
|
|
39534
|
+
warn(`Balance unavailable for: ${failedChains.join(", ")}`);
|
|
39535
|
+
}
|
|
39312
39536
|
output(rows, true);
|
|
39313
39537
|
} else {
|
|
39314
|
-
const totalUsd = rows.reduce(
|
|
39538
|
+
const totalUsd = rows.reduce(
|
|
39539
|
+
(sum, r3) => sum + (typeof r3.usdValue === "number" ? r3.usdValue : 0),
|
|
39540
|
+
0
|
|
39541
|
+
);
|
|
39315
39542
|
console.log("Chain Type Symbol Balance USD");
|
|
39316
39543
|
console.log("\u2500".repeat(72));
|
|
39317
39544
|
for (const r3 of rows) {
|
|
39318
|
-
const usd = r3.usdValue !== null ? `$${r3.usdValue.toFixed(2)}` : "";
|
|
39545
|
+
const usd = r3.balance === "--" ? "" : r3.usdValue !== null ? `$${r3.usdValue.toFixed(2)}` : "";
|
|
39319
39546
|
console.log(
|
|
39320
39547
|
`${String(r3.chain).padEnd(12)} ${String(r3.type).padEnd(7)} ${String(r3.symbol).padEnd(17)} ${String(r3.balance).padEnd(20)} ${usd}`
|
|
39321
39548
|
);
|
|
39322
39549
|
}
|
|
39323
39550
|
console.log("\u2500".repeat(72));
|
|
39324
39551
|
console.log(`Total USD: $${totalUsd.toFixed(2)}`);
|
|
39552
|
+
if (failedChains.length > 0) {
|
|
39553
|
+
warn(`Balance unavailable for: ${failedChains.join(", ")}`);
|
|
39554
|
+
}
|
|
39325
39555
|
}
|
|
39326
39556
|
} catch (err) {
|
|
39327
39557
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -39329,9 +39559,12 @@ ${qr3}`;
|
|
|
39329
39559
|
if (opts.json) output({ error: msg }, true);
|
|
39330
39560
|
process.exitCode = 1;
|
|
39331
39561
|
}
|
|
39332
|
-
|
|
39562
|
+
safeExit();
|
|
39333
39563
|
});
|
|
39334
|
-
wallet.command("sign-message").description("Sign a message with the agent wallet").requiredOption("--chain <key>", "Chain key (e.g., ethereum, solana)").requiredOption("--message <text>", "Message to sign").option(
|
|
39564
|
+
wallet.command("sign-message").description("Sign a message with the agent wallet").requiredOption("--chain <key>", "Chain key (e.g., ethereum, solana)").requiredOption("--message <text>", "Message to sign").option(
|
|
39565
|
+
"--password <password>",
|
|
39566
|
+
"Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
|
|
39567
|
+
).option("--json", "JSON output").action(async (opts) => {
|
|
39335
39568
|
try {
|
|
39336
39569
|
await ensureTrustSDK();
|
|
39337
39570
|
const password = await resolvePassword(opts);
|
|
@@ -39345,7 +39578,7 @@ ${qr3}`;
|
|
|
39345
39578
|
if (opts.json) output({ error: msg }, true);
|
|
39346
39579
|
process.exitCode = 1;
|
|
39347
39580
|
}
|
|
39348
|
-
|
|
39581
|
+
safeExit();
|
|
39349
39582
|
});
|
|
39350
39583
|
wallet.command("register").description("Register wallet addresses with Trust Wallet backend (enables token holdings)").option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (opts) => {
|
|
39351
39584
|
try {
|
|
@@ -39362,7 +39595,7 @@ ${qr3}`;
|
|
|
39362
39595
|
if (opts.json) output({ error: msg }, true);
|
|
39363
39596
|
process.exitCode = 1;
|
|
39364
39597
|
}
|
|
39365
|
-
|
|
39598
|
+
safeExit();
|
|
39366
39599
|
});
|
|
39367
39600
|
}
|
|
39368
39601
|
|
|
@@ -39374,6 +39607,7 @@ init_dist5();
|
|
|
39374
39607
|
// ../swap/dist/index.mjs
|
|
39375
39608
|
init_dist2();
|
|
39376
39609
|
init_dist2();
|
|
39610
|
+
init_dist2();
|
|
39377
39611
|
var SwapClient = class {
|
|
39378
39612
|
constructor(httpClient) {
|
|
39379
39613
|
this.httpClient = httpClient;
|
|
@@ -39520,6 +39754,26 @@ var SwapClient = class {
|
|
|
39520
39754
|
}
|
|
39521
39755
|
}
|
|
39522
39756
|
};
|
|
39757
|
+
var MAX_RETRIES = 3;
|
|
39758
|
+
var BASE_DELAY_MS = 3e3;
|
|
39759
|
+
function isRateLimited(err) {
|
|
39760
|
+
return err instanceof TwakApiError && err.statusCode === 429;
|
|
39761
|
+
}
|
|
39762
|
+
function sleep(ms3) {
|
|
39763
|
+
return new Promise((r3) => setTimeout(r3, ms3));
|
|
39764
|
+
}
|
|
39765
|
+
async function withRetry(fn2, onRetry) {
|
|
39766
|
+
for (let attempt = 0; ; attempt++) {
|
|
39767
|
+
try {
|
|
39768
|
+
return await fn2();
|
|
39769
|
+
} catch (err) {
|
|
39770
|
+
if (!isRateLimited(err) || attempt >= MAX_RETRIES) throw err;
|
|
39771
|
+
const delay = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
39772
|
+
onRetry?.(attempt + 1, delay);
|
|
39773
|
+
await sleep(delay);
|
|
39774
|
+
}
|
|
39775
|
+
}
|
|
39776
|
+
}
|
|
39523
39777
|
var SwapExecutor = class {
|
|
39524
39778
|
constructor(swapClient, walletProvider, options = {}) {
|
|
39525
39779
|
this.swapClient = swapClient;
|
|
@@ -39532,7 +39786,10 @@ var SwapExecutor = class {
|
|
|
39532
39786
|
async execute(params) {
|
|
39533
39787
|
const address = await this.walletProvider.getAddress(params.fromChain);
|
|
39534
39788
|
const fromTokenInfo = resolveToken(params.fromToken, params.fromChain);
|
|
39789
|
+
if (!fromTokenInfo)
|
|
39790
|
+
throw new Error(`Unknown token: ${params.fromToken} on ${params.fromChain}`);
|
|
39535
39791
|
const toTokenInfo = resolveToken(params.toToken, params.toChain);
|
|
39792
|
+
if (!toTokenInfo) throw new Error(`Unknown token: ${params.toToken} on ${params.toChain}`);
|
|
39536
39793
|
const parsedAmount = parseAmount(params.amount, fromTokenInfo.decimals);
|
|
39537
39794
|
const fromCoinId = getChainById(params.fromChain)?.coinId;
|
|
39538
39795
|
const toCoinId = getChainById(params.toChain)?.coinId;
|
|
@@ -39541,40 +39798,65 @@ var SwapExecutor = class {
|
|
|
39541
39798
|
if (params.toChain !== params.fromChain) {
|
|
39542
39799
|
destinationAddress = await this.walletProvider.getAddress(params.toChain);
|
|
39543
39800
|
}
|
|
39544
|
-
const quote = await
|
|
39545
|
-
|
|
39546
|
-
|
|
39547
|
-
|
|
39548
|
-
|
|
39549
|
-
|
|
39550
|
-
|
|
39551
|
-
|
|
39552
|
-
|
|
39553
|
-
|
|
39554
|
-
|
|
39555
|
-
|
|
39801
|
+
const quote = await withRetry(
|
|
39802
|
+
() => this.swapClient.getQuote({
|
|
39803
|
+
fromChain: params.fromChain,
|
|
39804
|
+
fromToken: fromTokenInfo.address,
|
|
39805
|
+
toChain: params.toChain,
|
|
39806
|
+
toToken: toTokenInfo.address,
|
|
39807
|
+
amount: parsedAmount,
|
|
39808
|
+
walletAddress: address,
|
|
39809
|
+
destinationAddress,
|
|
39810
|
+
slippage: params.slippage,
|
|
39811
|
+
fromDomainId: fromCoinId ? domains.get(fromCoinId) : void 0,
|
|
39812
|
+
toDomainId: toCoinId ? domains.get(toCoinId) : void 0
|
|
39813
|
+
})
|
|
39814
|
+
);
|
|
39556
39815
|
if (quote.routes.length === 0) {
|
|
39557
|
-
return {
|
|
39816
|
+
return {
|
|
39817
|
+
success: false,
|
|
39818
|
+
route: {
|
|
39819
|
+
id: "",
|
|
39820
|
+
steps: [],
|
|
39821
|
+
warnings: ["No routes found"],
|
|
39822
|
+
expirationDate: "",
|
|
39823
|
+
totalOutput: "0"
|
|
39824
|
+
},
|
|
39825
|
+
error: "No swap routes found"
|
|
39826
|
+
};
|
|
39558
39827
|
}
|
|
39559
39828
|
const route = quote.routes[0];
|
|
39560
39829
|
const firstStep = route.steps[0];
|
|
39561
39830
|
const lastStep = route.steps[route.steps.length - 1];
|
|
39562
39831
|
const inputAmt = formatAmount(firstStep.from.amount, firstStep.from.asset.metadata.decimals);
|
|
39563
39832
|
const outputAmt = formatAmount(lastStep.to.amount, lastStep.to.asset.metadata.decimals);
|
|
39564
|
-
this.log(
|
|
39833
|
+
this.log(
|
|
39834
|
+
`Swapping ${inputAmt} ${firstStep.from.asset.metadata.symbol} -> ${outputAmt} ${lastStep.to.asset.metadata.symbol} via ${route.steps.map((s2) => s2.provider.name).join(" -> ")}`
|
|
39835
|
+
);
|
|
39565
39836
|
const chainConfig = getChainById(params.fromChain);
|
|
39566
|
-
const transactions = await this.swapClient.getSwapTransactions(route);
|
|
39837
|
+
const transactions = await withRetry(() => this.swapClient.getSwapTransactions(route));
|
|
39838
|
+
let approvalTxHash;
|
|
39567
39839
|
try {
|
|
39568
39840
|
let txHash = "";
|
|
39569
39841
|
for (const txData of transactions) {
|
|
39570
39842
|
const txType = txData.transaction.type;
|
|
39571
39843
|
const isApproval = txType === "approval" || txType === "revokeApproval";
|
|
39572
39844
|
if (isApproval) {
|
|
39573
|
-
this.log(
|
|
39845
|
+
this.log(
|
|
39846
|
+
txType === "revokeApproval" ? "Revoking prior approval..." : "Sending token approval..."
|
|
39847
|
+
);
|
|
39574
39848
|
}
|
|
39575
39849
|
txHash = await this.sendTransaction(txData, chainConfig, params.fromChain);
|
|
39576
|
-
|
|
39577
|
-
|
|
39850
|
+
if (isApproval) approvalTxHash = txHash;
|
|
39851
|
+
this.log(
|
|
39852
|
+
`${isApproval ? "Approval" : "Swap"} tx: ${getExplorerTxUrl(params.fromChain, txHash)}`
|
|
39853
|
+
);
|
|
39854
|
+
await this.pollForConfirmation(
|
|
39855
|
+
params.fromChain,
|
|
39856
|
+
address,
|
|
39857
|
+
txHash,
|
|
39858
|
+
isApproval ? 6e4 : 12e4
|
|
39859
|
+
);
|
|
39578
39860
|
}
|
|
39579
39861
|
return {
|
|
39580
39862
|
success: true,
|
|
@@ -39583,7 +39865,10 @@ var SwapExecutor = class {
|
|
|
39583
39865
|
explorerUrl: getExplorerTxUrl(params.fromChain, txHash)
|
|
39584
39866
|
};
|
|
39585
39867
|
} catch (err) {
|
|
39586
|
-
|
|
39868
|
+
let msg = err instanceof Error ? err.message : "Unknown error";
|
|
39869
|
+
if (approvalTxHash) {
|
|
39870
|
+
msg += `. Approval was sent (tx: ${approvalTxHash}). Check allowance before retrying.`;
|
|
39871
|
+
}
|
|
39587
39872
|
return { success: false, route, error: msg };
|
|
39588
39873
|
}
|
|
39589
39874
|
}
|
|
@@ -39606,20 +39891,28 @@ var SwapExecutor = class {
|
|
|
39606
39891
|
}
|
|
39607
39892
|
async pollForConfirmation(chainKey, address, hash, timeoutMs) {
|
|
39608
39893
|
const blockchainClient = this.walletProvider.getBlockchainClient?.();
|
|
39609
|
-
if (!blockchainClient)
|
|
39894
|
+
if (!blockchainClient)
|
|
39895
|
+
throw new Error(
|
|
39896
|
+
"Wallet provider does not support transaction status lookup; cannot confirm transaction"
|
|
39897
|
+
);
|
|
39610
39898
|
const deadline = Date.now() + timeoutMs;
|
|
39611
39899
|
while (Date.now() < deadline) {
|
|
39612
39900
|
await new Promise((r3) => setTimeout(r3, 2e3));
|
|
39613
39901
|
try {
|
|
39614
39902
|
const status = await blockchainClient.findTransactionStatus(chainKey, address, hash);
|
|
39615
39903
|
if (status.confirmed) return;
|
|
39616
|
-
if (status.failed)
|
|
39904
|
+
if (status.failed)
|
|
39905
|
+
throw new Error(`Transaction failed: ${status.error || "unknown error"}`);
|
|
39617
39906
|
} catch (e2) {
|
|
39618
39907
|
const err = e2 instanceof Error ? e2 : new Error(String(e2));
|
|
39619
39908
|
if (err.message.startsWith("Transaction failed")) throw err;
|
|
39909
|
+
if (!err.message.includes("not found") && !err.message.includes("Transaction not found"))
|
|
39910
|
+
throw err;
|
|
39620
39911
|
}
|
|
39621
39912
|
}
|
|
39622
|
-
throw new Error(
|
|
39913
|
+
throw new Error(
|
|
39914
|
+
`Transaction ${hash} was not confirmed within ${Math.round(timeoutMs / 1e3)}s`
|
|
39915
|
+
);
|
|
39623
39916
|
}
|
|
39624
39917
|
};
|
|
39625
39918
|
|
|
@@ -39648,7 +39941,7 @@ function registerTransferCommand(program3) {
|
|
|
39648
39941
|
error(msg);
|
|
39649
39942
|
if (opts.json) output({ error: msg }, true);
|
|
39650
39943
|
process.exitCode = 1;
|
|
39651
|
-
|
|
39944
|
+
safeExit();
|
|
39652
39945
|
return;
|
|
39653
39946
|
}
|
|
39654
39947
|
const resolvedTo = await resolveAddressOrName(opts.to);
|
|
@@ -39659,7 +39952,7 @@ function registerTransferCommand(program3) {
|
|
|
39659
39952
|
error(msg);
|
|
39660
39953
|
if (opts.json) output({ error: msg }, true);
|
|
39661
39954
|
process.exitCode = 1;
|
|
39662
|
-
|
|
39955
|
+
safeExit();
|
|
39663
39956
|
return;
|
|
39664
39957
|
}
|
|
39665
39958
|
}
|
|
@@ -39677,7 +39970,7 @@ function registerTransferCommand(program3) {
|
|
|
39677
39970
|
error(msg);
|
|
39678
39971
|
if (opts.json) output({ error: msg }, true);
|
|
39679
39972
|
process.exitCode = 1;
|
|
39680
|
-
|
|
39973
|
+
safeExit();
|
|
39681
39974
|
return;
|
|
39682
39975
|
}
|
|
39683
39976
|
const parsedAmount = parseFloat(opts.amount);
|
|
@@ -39686,11 +39979,16 @@ function registerTransferCommand(program3) {
|
|
|
39686
39979
|
error(msg);
|
|
39687
39980
|
if (opts.json) output({ error: msg }, true);
|
|
39688
39981
|
process.exitCode = 1;
|
|
39689
|
-
|
|
39982
|
+
safeExit();
|
|
39690
39983
|
return;
|
|
39691
39984
|
}
|
|
39692
39985
|
try {
|
|
39693
39986
|
const tokenInfo = resolveToken(opts.token, chainKey);
|
|
39987
|
+
if (!tokenInfo) {
|
|
39988
|
+
error(`Unknown token: ${opts.token} on ${chainKey}. Use a contract address (0x...) or a supported symbol.`);
|
|
39989
|
+
process.exitCode = 1;
|
|
39990
|
+
return;
|
|
39991
|
+
}
|
|
39694
39992
|
const swapClient = new SwapClient(httpClient);
|
|
39695
39993
|
const price = await swapClient.getTokenPrice(chainKey, tokenInfo.address);
|
|
39696
39994
|
if (price && price > 0) {
|
|
@@ -39700,7 +39998,7 @@ function registerTransferCommand(program3) {
|
|
|
39700
39998
|
error(msg);
|
|
39701
39999
|
if (opts.json) output({ error: msg }, true);
|
|
39702
40000
|
process.exitCode = 1;
|
|
39703
|
-
|
|
40001
|
+
safeExit();
|
|
39704
40002
|
return;
|
|
39705
40003
|
}
|
|
39706
40004
|
if (usdValue > 1e3) {
|
|
@@ -39739,7 +40037,7 @@ function registerTransferCommand(program3) {
|
|
|
39739
40037
|
if (opts.json) output({ error: msg }, true);
|
|
39740
40038
|
process.exitCode = 1;
|
|
39741
40039
|
}
|
|
39742
|
-
|
|
40040
|
+
safeExit();
|
|
39743
40041
|
});
|
|
39744
40042
|
}
|
|
39745
40043
|
|
|
@@ -39749,250 +40047,300 @@ init_dist4();
|
|
|
39749
40047
|
var MAX_SLIPPAGE_PCT = 50;
|
|
39750
40048
|
var WARN_SLIPPAGE_PCT = 5;
|
|
39751
40049
|
function registerSwapCommand(program3) {
|
|
39752
|
-
program3.command("swap").description("Quote or execute a token swap").argument("<amountOrFrom>", "Amount to swap, or source token when using --usd").argument("<fromOrTo>", "Source token, or destination token when using --usd").argument("[to]", "Destination token (omit when using --usd with 2 args)").option("--chain <id>", "Chain", "ethereum").option("--to-chain <id>", "Destination chain (for cross-chain swaps)").option("--slippage <pct>", "Slippage tolerance %", "1").option("--usd <amount>", "Swap a USD-equivalent amount of the source token").option("--quote-only", "Get quote without executing").option("--password <password>", "Wallet password (required to execute)").option("--json", "JSON output").action(
|
|
39753
|
-
|
|
39754
|
-
|
|
39755
|
-
|
|
39756
|
-
|
|
39757
|
-
|
|
39758
|
-
|
|
39759
|
-
|
|
39760
|
-
|
|
39761
|
-
|
|
39762
|
-
|
|
39763
|
-
|
|
39764
|
-
|
|
39765
|
-
|
|
39766
|
-
|
|
39767
|
-
|
|
39768
|
-
}
|
|
39769
|
-
if (amount && opts.usd) {
|
|
39770
|
-
error("Provide either <amount> or --usd, not both");
|
|
39771
|
-
return;
|
|
39772
|
-
}
|
|
39773
|
-
const slippage = parseFloat(opts.slippage);
|
|
39774
|
-
if (isNaN(slippage) || slippage <= 0) {
|
|
39775
|
-
error("--slippage must be a positive number");
|
|
39776
|
-
process.exitCode = 1;
|
|
39777
|
-
return;
|
|
39778
|
-
}
|
|
39779
|
-
if (slippage > MAX_SLIPPAGE_PCT) {
|
|
39780
|
-
error(`Slippage ${slippage}% exceeds maximum allowed (${MAX_SLIPPAGE_PCT}%). Reduce --slippage to proceed.`);
|
|
39781
|
-
process.exitCode = 1;
|
|
39782
|
-
return;
|
|
39783
|
-
}
|
|
39784
|
-
if (slippage > WARN_SLIPPAGE_PCT) {
|
|
39785
|
-
warn(`High slippage tolerance: ${slippage}%. You may receive significantly fewer tokens than quoted.`);
|
|
39786
|
-
}
|
|
39787
|
-
const client = await getClient();
|
|
39788
|
-
const swapClient = new SwapClient(client);
|
|
39789
|
-
const detectChain = (token, fallback) => {
|
|
39790
|
-
const m2 = token.match(/^c(\d+)/i);
|
|
39791
|
-
if (m2) {
|
|
39792
|
-
const key = coinTypeToChainKey(parseInt(m2[1]));
|
|
39793
|
-
if (!key) throw new Error(`Unknown coin ID ${m2[1]} in token "${token}"`);
|
|
39794
|
-
return key;
|
|
39795
|
-
}
|
|
39796
|
-
return fallback;
|
|
39797
|
-
};
|
|
39798
|
-
const fromChain = detectChain(from8, opts.chain);
|
|
39799
|
-
const toChain = opts.toChain ? detectChain(to3, opts.toChain) : fromChain;
|
|
39800
|
-
const fromInfo = resolveToken(from8, fromChain);
|
|
39801
|
-
const toInfo = resolveToken(to3, toChain);
|
|
39802
|
-
let resolvedAmount = amount;
|
|
39803
|
-
if (opts.usd) {
|
|
39804
|
-
const usdAmount = parseFloat(opts.usd);
|
|
39805
|
-
if (isNaN(usdAmount) || usdAmount <= 0) {
|
|
39806
|
-
error("--usd amount must be a positive number");
|
|
40050
|
+
program3.command("swap").description("Quote or execute a token swap").argument("<amountOrFrom>", "Amount to swap, or source token when using --usd").argument("<fromOrTo>", "Source token, or destination token when using --usd").argument("[to]", "Destination token (omit when using --usd with 2 args)").option("--chain <id>", "Chain", "ethereum").option("--to-chain <id>", "Destination chain (for cross-chain swaps)").option("--slippage <pct>", "Slippage tolerance %", "1").option("--usd <amount>", "Swap a USD-equivalent amount of the source token").option("--quote-only", "Get quote without executing").option("--password <password>", "Wallet password (required to execute)").option("--json", "JSON output").action(
|
|
40051
|
+
async (amountOrFrom, fromOrTo, toOrUndefined, opts) => {
|
|
40052
|
+
try {
|
|
40053
|
+
let amount;
|
|
40054
|
+
let from8;
|
|
40055
|
+
let to3;
|
|
40056
|
+
if (toOrUndefined) {
|
|
40057
|
+
amount = amountOrFrom;
|
|
40058
|
+
from8 = fromOrTo;
|
|
40059
|
+
to3 = toOrUndefined;
|
|
40060
|
+
} else {
|
|
40061
|
+
from8 = amountOrFrom;
|
|
40062
|
+
to3 = fromOrTo;
|
|
40063
|
+
}
|
|
40064
|
+
if (!amount && !opts.usd) {
|
|
40065
|
+
error("Either <amount> or --usd <amount> is required");
|
|
39807
40066
|
return;
|
|
39808
40067
|
}
|
|
39809
|
-
|
|
39810
|
-
|
|
39811
|
-
error(`Unable to fetch price for ${from8} on ${fromChain}`);
|
|
40068
|
+
if (amount && opts.usd) {
|
|
40069
|
+
error("Provide either <amount> or --usd, not both");
|
|
39812
40070
|
return;
|
|
39813
40071
|
}
|
|
39814
|
-
|
|
39815
|
-
|
|
39816
|
-
|
|
39817
|
-
|
|
39818
|
-
|
|
39819
|
-
|
|
39820
|
-
|
|
39821
|
-
let destinationAddress;
|
|
39822
|
-
let amberDomains = /* @__PURE__ */ new Map();
|
|
39823
|
-
if (needsWallet) {
|
|
39824
|
-
await ensureTrustSDK();
|
|
39825
|
-
const password = await resolvePassword(opts);
|
|
39826
|
-
provider = await AgentWalletProvider.fromStorage(password);
|
|
39827
|
-
walletAddress = await provider.getAddress(fromChain);
|
|
39828
|
-
if (toChain !== fromChain) {
|
|
39829
|
-
destinationAddress = await provider.getAddress(toChain);
|
|
40072
|
+
if (amount) {
|
|
40073
|
+
const n4 = parseFloat(amount);
|
|
40074
|
+
if (isNaN(n4) || n4 <= 0) {
|
|
40075
|
+
error("Amount must be greater than 0");
|
|
40076
|
+
process.exitCode = 1;
|
|
40077
|
+
return;
|
|
40078
|
+
}
|
|
39830
40079
|
}
|
|
39831
|
-
|
|
39832
|
-
|
|
39833
|
-
|
|
39834
|
-
|
|
39835
|
-
|
|
39836
|
-
|
|
39837
|
-
|
|
39838
|
-
|
|
39839
|
-
|
|
39840
|
-
|
|
39841
|
-
|
|
39842
|
-
|
|
39843
|
-
|
|
39844
|
-
|
|
39845
|
-
|
|
39846
|
-
|
|
39847
|
-
|
|
39848
|
-
|
|
39849
|
-
|
|
39850
|
-
|
|
39851
|
-
|
|
39852
|
-
|
|
39853
|
-
|
|
39854
|
-
|
|
39855
|
-
|
|
39856
|
-
|
|
39857
|
-
|
|
39858
|
-
|
|
39859
|
-
|
|
39860
|
-
const
|
|
39861
|
-
const
|
|
39862
|
-
|
|
39863
|
-
|
|
39864
|
-
|
|
39865
|
-
|
|
39866
|
-
|
|
39867
|
-
|
|
39868
|
-
|
|
39869
|
-
|
|
39870
|
-
|
|
39871
|
-
|
|
39872
|
-
|
|
39873
|
-
|
|
39874
|
-
|
|
39875
|
-
|
|
39876
|
-
|
|
39877
|
-
|
|
39878
|
-
|
|
39879
|
-
|
|
39880
|
-
|
|
39881
|
-
|
|
39882
|
-
|
|
39883
|
-
|
|
39884
|
-
|
|
39885
|
-
|
|
39886
|
-
|
|
39887
|
-
|
|
39888
|
-
|
|
39889
|
-
|
|
39890
|
-
|
|
39891
|
-
|
|
39892
|
-
|
|
39893
|
-
|
|
39894
|
-
|
|
39895
|
-
|
|
39896
|
-
|
|
39897
|
-
|
|
39898
|
-
|
|
39899
|
-
|
|
39900
|
-
|
|
40080
|
+
const slippage = parseFloat(opts.slippage);
|
|
40081
|
+
if (isNaN(slippage) || slippage <= 0) {
|
|
40082
|
+
error("--slippage must be a positive number");
|
|
40083
|
+
process.exitCode = 1;
|
|
40084
|
+
return;
|
|
40085
|
+
}
|
|
40086
|
+
if (slippage > MAX_SLIPPAGE_PCT) {
|
|
40087
|
+
error(
|
|
40088
|
+
`Slippage ${slippage}% exceeds maximum allowed (${MAX_SLIPPAGE_PCT}%). Reduce --slippage to proceed.`
|
|
40089
|
+
);
|
|
40090
|
+
process.exitCode = 1;
|
|
40091
|
+
return;
|
|
40092
|
+
}
|
|
40093
|
+
if (slippage > WARN_SLIPPAGE_PCT) {
|
|
40094
|
+
warn(
|
|
40095
|
+
`High slippage tolerance: ${slippage}%. You may receive significantly fewer tokens than quoted.`
|
|
40096
|
+
);
|
|
40097
|
+
}
|
|
40098
|
+
const client = await getClient();
|
|
40099
|
+
const swapClient = new SwapClient(client);
|
|
40100
|
+
const detectChain = (token, fallback) => {
|
|
40101
|
+
const m2 = token.match(/^c(\d+)/i);
|
|
40102
|
+
if (m2) {
|
|
40103
|
+
const key = coinTypeToChainKey(parseInt(m2[1]));
|
|
40104
|
+
if (!key) throw new Error(`Unknown coin ID ${m2[1]} in token "${token}"`);
|
|
40105
|
+
return key;
|
|
40106
|
+
}
|
|
40107
|
+
return fallback;
|
|
40108
|
+
};
|
|
40109
|
+
const fromChain = detectChain(from8, opts.chain);
|
|
40110
|
+
const toChain = opts.toChain ? detectChain(to3, opts.toChain) : fromChain;
|
|
40111
|
+
const fromInfo = resolveToken(from8, fromChain);
|
|
40112
|
+
if (!fromInfo) {
|
|
40113
|
+
error(
|
|
40114
|
+
`Unknown token: ${from8} on ${fromChain}. Use a contract address (0x...) or a supported symbol.`
|
|
40115
|
+
);
|
|
40116
|
+
process.exitCode = 1;
|
|
40117
|
+
return;
|
|
40118
|
+
}
|
|
40119
|
+
const toInfo = resolveToken(to3, toChain);
|
|
40120
|
+
if (!toInfo) {
|
|
40121
|
+
error(
|
|
40122
|
+
`Unknown token: ${to3} on ${toChain}. Use a contract address (0x...) or a supported symbol.`
|
|
40123
|
+
);
|
|
40124
|
+
process.exitCode = 1;
|
|
40125
|
+
return;
|
|
40126
|
+
}
|
|
40127
|
+
let resolvedAmount = amount;
|
|
40128
|
+
if (opts.usd) {
|
|
40129
|
+
const usdAmount = parseFloat(opts.usd);
|
|
40130
|
+
if (isNaN(usdAmount) || usdAmount <= 0) {
|
|
40131
|
+
error("--usd amount must be a positive number");
|
|
40132
|
+
return;
|
|
40133
|
+
}
|
|
40134
|
+
const price = await swapClient.getTokenPrice(fromChain, fromInfo.address);
|
|
40135
|
+
if (!price) {
|
|
40136
|
+
error(`Unable to fetch price for ${from8} on ${fromChain}`);
|
|
40137
|
+
return;
|
|
40138
|
+
}
|
|
40139
|
+
resolvedAmount = (usdAmount / price).toFixed(fromInfo.decimals);
|
|
40140
|
+
info(`$${usdAmount} USD \u2248 ${resolvedAmount} ${from8.toUpperCase()} (@ $${price})`);
|
|
40141
|
+
}
|
|
40142
|
+
const parsedAmount = parseAmount(resolvedAmount, fromInfo.decimals);
|
|
40143
|
+
const needsWallet = !opts.quoteOnly;
|
|
40144
|
+
let walletAddress = getChainById(fromChain)?.sampleAccount || "0x0000000000000000000000000000000000000000";
|
|
40145
|
+
let provider = null;
|
|
40146
|
+
let destinationAddress;
|
|
40147
|
+
let amberDomains = /* @__PURE__ */ new Map();
|
|
40148
|
+
if (needsWallet) {
|
|
40149
|
+
await ensureTrustSDK();
|
|
40150
|
+
const password = await resolvePassword(opts);
|
|
40151
|
+
provider = await AgentWalletProvider.fromStorage(password);
|
|
40152
|
+
walletAddress = await provider.getAddress(fromChain);
|
|
40153
|
+
if (toChain !== fromChain) {
|
|
40154
|
+
destinationAddress = await provider.getAddress(toChain);
|
|
40155
|
+
}
|
|
40156
|
+
amberDomains = await getAmberDomainsByCoinId();
|
|
40157
|
+
}
|
|
40158
|
+
const fromCoinId = getChainById(fromChain)?.coinId;
|
|
40159
|
+
const toCoinId = getChainById(toChain)?.coinId;
|
|
40160
|
+
const quote = await swapClient.getQuote({
|
|
40161
|
+
fromChain,
|
|
40162
|
+
fromToken: fromInfo.address,
|
|
40163
|
+
toChain,
|
|
40164
|
+
toToken: toInfo.address,
|
|
40165
|
+
amount: parsedAmount,
|
|
40166
|
+
walletAddress,
|
|
40167
|
+
destinationAddress,
|
|
40168
|
+
slippage: opts.slippage,
|
|
40169
|
+
fromDomainId: fromCoinId ? amberDomains.get(fromCoinId) : void 0,
|
|
40170
|
+
toDomainId: toCoinId ? amberDomains.get(toCoinId) : void 0
|
|
40171
|
+
});
|
|
40172
|
+
if (quote.routes.length === 0) {
|
|
40173
|
+
error("No swap routes found");
|
|
40174
|
+
if (opts.json) output({ error: "No routes found" }, true);
|
|
40175
|
+
return;
|
|
40176
|
+
}
|
|
40177
|
+
const route = quote.routes[0];
|
|
40178
|
+
const firstStep = route.steps[0];
|
|
40179
|
+
const lastStep = route.steps[route.steps.length - 1];
|
|
40180
|
+
const inputAmt = formatAmount(
|
|
40181
|
+
firstStep.from.amount,
|
|
40182
|
+
firstStep.from.asset.metadata.decimals
|
|
40183
|
+
);
|
|
40184
|
+
const outputAmt = formatAmount(lastStep.to.amount, lastStep.to.asset.metadata.decimals);
|
|
40185
|
+
const minAmountOut = lastStep.to.minAmountOut;
|
|
40186
|
+
const minReceivedAmt = minAmountOut ? formatAmount(minAmountOut, lastStep.to.asset.metadata.decimals) : null;
|
|
40187
|
+
const networkFees = route.steps.filter((s2) => s2.networkFee).map((s2) => {
|
|
40188
|
+
const fee = s2.networkFee;
|
|
40189
|
+
const feeAmt = formatAmount(fee.amount, fee.asset.metadata.decimals);
|
|
40190
|
+
return `${feeAmt} ${fee.asset.metadata.symbol}`;
|
|
40191
|
+
});
|
|
40192
|
+
const quoteData = {
|
|
40193
|
+
input: `${inputAmt} ${firstStep.from.asset.metadata.symbol}`,
|
|
40194
|
+
output: `${outputAmt} ${lastStep.to.asset.metadata.symbol}`,
|
|
40195
|
+
...minReceivedAmt ? { minReceived: `${minReceivedAmt} ${lastStep.to.asset.metadata.symbol}` } : {},
|
|
40196
|
+
provider: route.steps.map((s2) => s2.provider.name).join(" \u2192 "),
|
|
40197
|
+
priceImpact: firstStep.priceImpact,
|
|
40198
|
+
...networkFees.length > 0 ? { networkFee: networkFees.join(" + ") } : {},
|
|
40199
|
+
...route.steps.length > 1 ? { steps: route.steps.length } : {}
|
|
40200
|
+
};
|
|
40201
|
+
if (opts.quoteOnly) {
|
|
40202
|
+
output(quoteData, opts.json);
|
|
40203
|
+
return;
|
|
40204
|
+
}
|
|
40205
|
+
info(`Swapping ${quoteData.input} \u2192 ${quoteData.output} via ${quoteData.provider}...`);
|
|
40206
|
+
const chainConfig = getChainById(fromChain);
|
|
40207
|
+
const transactions = await swapClient.getSwapTransactions(route);
|
|
40208
|
+
let txHash = "";
|
|
40209
|
+
const blockchainClient = provider.getBlockchainClient();
|
|
40210
|
+
const hasApprovalTx = transactions.some((t) => t.transaction.type === "approval");
|
|
40211
|
+
if (!hasApprovalTx && chainConfig?.isEVM) {
|
|
40212
|
+
const stepApproval = firstStep.approval;
|
|
40213
|
+
if (stepApproval && stepApproval.operatorAddress) {
|
|
40214
|
+
const fromAssetAddr = firstStep.from.asset.address;
|
|
40215
|
+
const isNative = !fromAssetAddr || fromAssetAddr === NATIVE_TOKEN_ADDRESS;
|
|
40216
|
+
if (!isNative && chainConfig.coinId) {
|
|
40217
|
+
const assetId = formatTokenId(chainConfig.coinId, fromAssetAddr);
|
|
40218
|
+
try {
|
|
40219
|
+
const allowance = await blockchainClient.getAllowance({
|
|
39901
40220
|
assetId,
|
|
39902
|
-
|
|
39903
|
-
|
|
39904
|
-
hdWallet
|
|
40221
|
+
owner: walletAddress,
|
|
40222
|
+
spender: stepApproval.operatorAddress
|
|
39905
40223
|
});
|
|
39906
|
-
|
|
39907
|
-
|
|
39908
|
-
|
|
39909
|
-
|
|
39910
|
-
|
|
39911
|
-
|
|
39912
|
-
|
|
39913
|
-
|
|
39914
|
-
|
|
39915
|
-
|
|
39916
|
-
|
|
40224
|
+
const requiredAmount = BigInt(stepApproval.minAmount || parsedAmount);
|
|
40225
|
+
if (allowance < requiredAmount) {
|
|
40226
|
+
info("Sending token approval...");
|
|
40227
|
+
const hdWallet = provider.getSigner().getHDWallet();
|
|
40228
|
+
const approveResult = await blockchainClient.approve({
|
|
40229
|
+
assetId,
|
|
40230
|
+
spender: stepApproval.operatorAddress,
|
|
40231
|
+
amount: requiredAmount.toString(),
|
|
40232
|
+
hdWallet
|
|
40233
|
+
});
|
|
40234
|
+
info(`Approval tx: ${getExplorerTxUrl(fromChain, approveResult.hash)}`);
|
|
40235
|
+
const approvalDeadline = Date.now() + 6e4;
|
|
40236
|
+
while (Date.now() < approvalDeadline) {
|
|
40237
|
+
await new Promise((r3) => setTimeout(r3, 2e3));
|
|
40238
|
+
try {
|
|
40239
|
+
const status = await blockchainClient.findTransactionStatus(
|
|
40240
|
+
fromChain,
|
|
40241
|
+
walletAddress,
|
|
40242
|
+
approveResult.hash
|
|
40243
|
+
);
|
|
40244
|
+
if (status.confirmed) break;
|
|
40245
|
+
if (status.failed) throw new Error("Approval transaction failed");
|
|
40246
|
+
} catch (e2) {
|
|
40247
|
+
const err = e2 instanceof Error ? e2 : new Error(String(e2));
|
|
40248
|
+
if (err.message.startsWith("Approval transaction failed")) throw err;
|
|
40249
|
+
}
|
|
39917
40250
|
}
|
|
39918
40251
|
}
|
|
40252
|
+
} catch (e2) {
|
|
40253
|
+
const msg = e2 instanceof Error ? e2.message : String(e2);
|
|
40254
|
+
if (msg.includes("insufficient funds")) {
|
|
40255
|
+
throw new Error(
|
|
40256
|
+
`Insufficient ${chainConfig?.symbol || "native token"} for approval gas. Fund your wallet with ${chainConfig?.symbol || "native tokens"} first.`
|
|
40257
|
+
);
|
|
40258
|
+
}
|
|
40259
|
+
if (msg.includes("failed")) throw e2;
|
|
40260
|
+
info(`Warning: could not check/send approval: ${msg}`);
|
|
39919
40261
|
}
|
|
39920
|
-
} catch (e2) {
|
|
39921
|
-
const msg = e2 instanceof Error ? e2.message : String(e2);
|
|
39922
|
-
if (msg.includes("insufficient funds")) {
|
|
39923
|
-
throw new Error(`Insufficient ${chainConfig?.symbol || "native token"} for approval gas. Fund your wallet with ${chainConfig?.symbol || "native tokens"} first.`);
|
|
39924
|
-
}
|
|
39925
|
-
if (msg.includes("failed")) throw e2;
|
|
39926
|
-
info(`Warning: could not check/send approval: ${msg}`);
|
|
39927
40262
|
}
|
|
39928
40263
|
}
|
|
39929
40264
|
}
|
|
39930
|
-
|
|
39931
|
-
|
|
39932
|
-
|
|
39933
|
-
|
|
39934
|
-
|
|
39935
|
-
|
|
39936
|
-
|
|
39937
|
-
|
|
39938
|
-
|
|
39939
|
-
|
|
39940
|
-
|
|
39941
|
-
|
|
39942
|
-
|
|
39943
|
-
|
|
40265
|
+
const pollForConfirmation = async (hash, timeoutMs) => {
|
|
40266
|
+
const deadline = Date.now() + timeoutMs;
|
|
40267
|
+
while (Date.now() < deadline) {
|
|
40268
|
+
await new Promise((r3) => setTimeout(r3, 2e3));
|
|
40269
|
+
try {
|
|
40270
|
+
const status = await blockchainClient.findTransactionStatus(
|
|
40271
|
+
fromChain,
|
|
40272
|
+
walletAddress,
|
|
40273
|
+
hash
|
|
40274
|
+
);
|
|
40275
|
+
if (status.confirmed) return;
|
|
40276
|
+
if (status.failed)
|
|
40277
|
+
throw new Error(`Transaction failed: ${status.error || "unknown error"}`);
|
|
40278
|
+
} catch (e2) {
|
|
40279
|
+
const err = e2 instanceof Error ? e2 : new Error(String(e2));
|
|
40280
|
+
if (err.message.startsWith("Transaction failed")) throw err;
|
|
40281
|
+
if (!err.message.includes("not found") && !err.message.includes("Transaction not found")) {
|
|
40282
|
+
info(`Warning: tx status check error (will retry): ${err.message}`);
|
|
40283
|
+
}
|
|
39944
40284
|
}
|
|
39945
40285
|
}
|
|
40286
|
+
throw new Error(
|
|
40287
|
+
`Transaction ${hash} was not confirmed within ${Math.round(timeoutMs / 1e3)}s`
|
|
40288
|
+
);
|
|
40289
|
+
};
|
|
40290
|
+
for (const txData of transactions) {
|
|
40291
|
+
const txType = txData.transaction.type;
|
|
40292
|
+
const isApproval = txType === "approval" || txType === "revokeApproval";
|
|
40293
|
+
if (isApproval) {
|
|
40294
|
+
info(
|
|
40295
|
+
txType === "revokeApproval" ? "Revoking prior approval..." : "Sending token approval..."
|
|
40296
|
+
);
|
|
40297
|
+
}
|
|
40298
|
+
if (chainConfig?.namespace === "solana" && txData.transaction.solanaTx) {
|
|
40299
|
+
const solanaChainId = `solana:${chainConfig.chainId}`;
|
|
40300
|
+
txHash = await provider.sendTransaction({
|
|
40301
|
+
chainId: solanaChainId,
|
|
40302
|
+
to: "",
|
|
40303
|
+
data: txData.transaction.solanaTx.serializedMessage
|
|
40304
|
+
});
|
|
40305
|
+
} else if (txData.transaction.evmTx) {
|
|
40306
|
+
const wcChainId = `eip155:${chainConfig?.chainId || "1"}`;
|
|
40307
|
+
txHash = await provider.sendTransaction({
|
|
40308
|
+
chainId: wcChainId,
|
|
40309
|
+
to: txData.transaction.evmTx.to,
|
|
40310
|
+
data: txData.transaction.evmTx.data,
|
|
40311
|
+
value: txData.transaction.evmTx.value || "0x0"
|
|
40312
|
+
});
|
|
40313
|
+
} else {
|
|
40314
|
+
error("No transaction data returned for this swap step");
|
|
40315
|
+
if (opts.json) output({ error: "No transaction data" }, true);
|
|
40316
|
+
return;
|
|
40317
|
+
}
|
|
40318
|
+
info(`${isApproval ? "Approval" : "Swap"} tx: ${getExplorerTxUrl(fromChain, txHash)}`);
|
|
40319
|
+
await pollForConfirmation(txHash, isApproval ? 6e4 : 12e4);
|
|
39946
40320
|
}
|
|
39947
|
-
|
|
39948
|
-
|
|
39949
|
-
|
|
39950
|
-
|
|
39951
|
-
|
|
39952
|
-
|
|
39953
|
-
|
|
39954
|
-
|
|
39955
|
-
|
|
39956
|
-
|
|
39957
|
-
|
|
39958
|
-
|
|
39959
|
-
|
|
39960
|
-
|
|
39961
|
-
|
|
39962
|
-
|
|
39963
|
-
|
|
39964
|
-
|
|
39965
|
-
|
|
39966
|
-
|
|
39967
|
-
data: txData.transaction.evmTx.data,
|
|
39968
|
-
value: txData.transaction.evmTx.value || "0x0"
|
|
39969
|
-
});
|
|
39970
|
-
} else {
|
|
39971
|
-
error("No transaction data returned for this swap step");
|
|
39972
|
-
if (opts.json) output({ error: "No transaction data" }, true);
|
|
39973
|
-
return;
|
|
39974
|
-
}
|
|
39975
|
-
info(`${isApproval ? "Approval" : "Swap"} tx: ${getExplorerTxUrl(fromChain, txHash)}`);
|
|
39976
|
-
await pollForConfirmation(txHash, isApproval ? 6e4 : 12e4);
|
|
40321
|
+
const explorerUrl = getExplorerTxUrl(fromChain, txHash);
|
|
40322
|
+
success("Swap executed!");
|
|
40323
|
+
output(
|
|
40324
|
+
{
|
|
40325
|
+
...quoteData,
|
|
40326
|
+
hash: txHash,
|
|
40327
|
+
fromChain,
|
|
40328
|
+
toChain,
|
|
40329
|
+
explorer: explorerUrl
|
|
40330
|
+
},
|
|
40331
|
+
opts.json
|
|
40332
|
+
);
|
|
40333
|
+
} catch (err) {
|
|
40334
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
40335
|
+
error(`Swap failed: ${msg}`);
|
|
40336
|
+
if (opts.json) output({ error: msg }, true);
|
|
40337
|
+
process.exitCode = 1;
|
|
40338
|
+
}
|
|
40339
|
+
if (!opts.quoteOnly) {
|
|
40340
|
+
safeExit();
|
|
39977
40341
|
}
|
|
39978
|
-
const explorerUrl = getExplorerTxUrl(fromChain, txHash);
|
|
39979
|
-
success("Swap executed!");
|
|
39980
|
-
output({
|
|
39981
|
-
...quoteData,
|
|
39982
|
-
hash: txHash,
|
|
39983
|
-
fromChain,
|
|
39984
|
-
toChain,
|
|
39985
|
-
explorer: explorerUrl
|
|
39986
|
-
}, opts.json);
|
|
39987
|
-
} catch (err) {
|
|
39988
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
39989
|
-
error(`Swap failed: ${msg}`);
|
|
39990
|
-
if (opts.json) output({ error: msg }, true);
|
|
39991
|
-
}
|
|
39992
|
-
if (!opts.quoteOnly) {
|
|
39993
|
-
setTimeout(() => process.exit(0), 100);
|
|
39994
40342
|
}
|
|
39995
|
-
|
|
40343
|
+
);
|
|
39996
40344
|
}
|
|
39997
40345
|
|
|
39998
40346
|
// src/commands/erc20.ts
|
|
@@ -40011,7 +40359,7 @@ function registerErc20Command(program3) {
|
|
|
40011
40359
|
error(msg);
|
|
40012
40360
|
if (opts.json) output({ error: msg }, true);
|
|
40013
40361
|
process.exitCode = 1;
|
|
40014
|
-
|
|
40362
|
+
safeExit();
|
|
40015
40363
|
return;
|
|
40016
40364
|
}
|
|
40017
40365
|
const chainKey = coinTypeToChainKey(coinId);
|
|
@@ -40020,7 +40368,7 @@ function registerErc20Command(program3) {
|
|
|
40020
40368
|
error(msg);
|
|
40021
40369
|
if (opts.json) output({ error: msg }, true);
|
|
40022
40370
|
process.exitCode = 1;
|
|
40023
|
-
|
|
40371
|
+
safeExit();
|
|
40024
40372
|
return;
|
|
40025
40373
|
}
|
|
40026
40374
|
const address = await provider.getAddress(chainKey);
|
|
@@ -40031,7 +40379,7 @@ function registerErc20Command(program3) {
|
|
|
40031
40379
|
error(msg);
|
|
40032
40380
|
if (opts.json) output({ error: msg }, true);
|
|
40033
40381
|
process.exitCode = 1;
|
|
40034
|
-
|
|
40382
|
+
safeExit();
|
|
40035
40383
|
return;
|
|
40036
40384
|
}
|
|
40037
40385
|
warn("WARNING: Unlimited approval grants the spender access to ALL tokens of this type.");
|
|
@@ -40062,7 +40410,7 @@ function registerErc20Command(program3) {
|
|
|
40062
40410
|
if (opts.json) output({ error: msg }, true);
|
|
40063
40411
|
process.exitCode = 1;
|
|
40064
40412
|
}
|
|
40065
|
-
|
|
40413
|
+
safeExit();
|
|
40066
40414
|
});
|
|
40067
40415
|
erc20.command("revoke").description("Revoke token approval (set allowance to 0)").requiredOption("--token <assetId>", "Token asset ID (e.g., c60_t0xA0b8...)").requiredOption("--spender <address>", "Spender address to revoke").option("--password <password>", "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)").option("--json", "JSON output").action(async (opts) => {
|
|
40068
40416
|
try {
|
|
@@ -40075,7 +40423,7 @@ function registerErc20Command(program3) {
|
|
|
40075
40423
|
error(msg);
|
|
40076
40424
|
if (opts.json) output({ error: msg }, true);
|
|
40077
40425
|
process.exitCode = 1;
|
|
40078
|
-
|
|
40426
|
+
safeExit();
|
|
40079
40427
|
return;
|
|
40080
40428
|
}
|
|
40081
40429
|
const chainKey = coinTypeToChainKey(coinId);
|
|
@@ -40084,7 +40432,7 @@ function registerErc20Command(program3) {
|
|
|
40084
40432
|
error(msg);
|
|
40085
40433
|
if (opts.json) output({ error: msg }, true);
|
|
40086
40434
|
process.exitCode = 1;
|
|
40087
|
-
|
|
40435
|
+
safeExit();
|
|
40088
40436
|
return;
|
|
40089
40437
|
}
|
|
40090
40438
|
const address = await provider.getAddress(chainKey);
|
|
@@ -40113,7 +40461,7 @@ function registerErc20Command(program3) {
|
|
|
40113
40461
|
if (opts.json) output({ error: msg }, true);
|
|
40114
40462
|
process.exitCode = 1;
|
|
40115
40463
|
}
|
|
40116
|
-
|
|
40464
|
+
safeExit();
|
|
40117
40465
|
});
|
|
40118
40466
|
erc20.command("allowance").description("Check token allowance").requiredOption("--token <assetId>", "Token asset ID (e.g., c60_t0xA0b8...)").requiredOption("--owner <address>", "Owner address").requiredOption("--spender <address>", "Spender address").option("--json", "JSON output").action(async (opts) => {
|
|
40119
40467
|
try {
|
|
@@ -40136,19 +40484,50 @@ function registerErc20Command(program3) {
|
|
|
40136
40484
|
if (opts.json) output({ error: msg }, true);
|
|
40137
40485
|
process.exitCode = 1;
|
|
40138
40486
|
}
|
|
40139
|
-
|
|
40487
|
+
safeExit();
|
|
40140
40488
|
});
|
|
40141
40489
|
}
|
|
40142
40490
|
|
|
40143
40491
|
// src/commands/alert.ts
|
|
40144
40492
|
init_dist();
|
|
40145
40493
|
init_dist2();
|
|
40146
|
-
|
|
40494
|
+
init_dist5();
|
|
40495
|
+
var assetCache = new AssetCache();
|
|
40496
|
+
async function resolveTokenLabel(tokenId, service) {
|
|
40497
|
+
const cached2 = await assetCache.get(tokenId);
|
|
40498
|
+
if (cached2 !== void 0) return cached2;
|
|
40147
40499
|
try {
|
|
40148
40500
|
const { coinId, contractAddress } = parseTokenId(tokenId);
|
|
40149
40501
|
const chain2 = getChainByCoinId(coinId);
|
|
40150
|
-
if (chain2
|
|
40151
|
-
|
|
40502
|
+
if (!chain2) return tokenId;
|
|
40503
|
+
if (!contractAddress) {
|
|
40504
|
+
return chain2.symbol;
|
|
40505
|
+
}
|
|
40506
|
+
const chainKey = tokenIdToChain(tokenId);
|
|
40507
|
+
if (chainKey) {
|
|
40508
|
+
const chainTokens = COMMON_TOKENS[chainKey];
|
|
40509
|
+
if (chainTokens) {
|
|
40510
|
+
const normalizedAddr = contractAddress.toLowerCase();
|
|
40511
|
+
for (const [sym, addr] of Object.entries(chainTokens)) {
|
|
40512
|
+
if (addr.toLowerCase() === normalizedAddr) {
|
|
40513
|
+
await assetCache.set(tokenId, sym);
|
|
40514
|
+
return sym;
|
|
40515
|
+
}
|
|
40516
|
+
}
|
|
40517
|
+
}
|
|
40518
|
+
}
|
|
40519
|
+
if (service) {
|
|
40520
|
+
try {
|
|
40521
|
+
const assetInfo = await service.getAssetInfo(tokenId);
|
|
40522
|
+
if (assetInfo.symbol) {
|
|
40523
|
+
await assetCache.set(tokenId, assetInfo.symbol);
|
|
40524
|
+
return assetInfo.symbol;
|
|
40525
|
+
}
|
|
40526
|
+
} catch {
|
|
40527
|
+
}
|
|
40528
|
+
}
|
|
40529
|
+
const short = contractAddress.length > 10 ? `${contractAddress.slice(0, 6)}\u2026${contractAddress.slice(-4)}` : contractAddress;
|
|
40530
|
+
return `${chain2.symbol}/${short}`;
|
|
40152
40531
|
} catch {
|
|
40153
40532
|
return tokenId;
|
|
40154
40533
|
}
|
|
@@ -40212,7 +40591,9 @@ function registerAlertCommand(program3) {
|
|
|
40212
40591
|
resolvedChain = NATIVE_SYMBOLS2[sym];
|
|
40213
40592
|
}
|
|
40214
40593
|
if (!resolvedChain) {
|
|
40215
|
-
error(
|
|
40594
|
+
error(
|
|
40595
|
+
`Cannot resolve "${opts.token}" without --chain. Provide --chain or use asset ID format (c60).`
|
|
40596
|
+
);
|
|
40216
40597
|
if (opts.json) output({ error: "Chain required" }, true);
|
|
40217
40598
|
process.exitCode = 1;
|
|
40218
40599
|
return;
|
|
@@ -40225,6 +40606,14 @@ function registerAlertCommand(program3) {
|
|
|
40225
40606
|
return;
|
|
40226
40607
|
}
|
|
40227
40608
|
const resolved = resolveToken(sym, resolvedChain);
|
|
40609
|
+
if (!resolved) {
|
|
40610
|
+
error(
|
|
40611
|
+
`Unknown token: ${sym} on ${resolvedChain}. Use a contract address or asset ID (e.g. c60_t0xABC...).`
|
|
40612
|
+
);
|
|
40613
|
+
if (opts.json) output({ error: `Unknown token: ${sym}` }, true);
|
|
40614
|
+
process.exitCode = 1;
|
|
40615
|
+
return;
|
|
40616
|
+
}
|
|
40228
40617
|
if (resolved.address === NATIVE_TOKEN_ADDRESS || resolved.address === sym) {
|
|
40229
40618
|
resolvedTokenId = formatTokenId(chain2.coinId);
|
|
40230
40619
|
} else {
|
|
@@ -40248,15 +40637,19 @@ function registerAlertCommand(program3) {
|
|
|
40248
40637
|
});
|
|
40249
40638
|
alert.command("list").description("List all alerts").option("--active", "Show only active alerts").option("--json", "JSON output").action(async (opts) => {
|
|
40250
40639
|
const alerts = opts.active ? await alertStore.getActive() : await alertStore.list();
|
|
40640
|
+
const client = await tryGetClient();
|
|
40641
|
+
const service = client ? new SecurityService(client) : void 0;
|
|
40251
40642
|
if (opts.json) {
|
|
40252
|
-
const enriched =
|
|
40253
|
-
|
|
40254
|
-
|
|
40255
|
-
|
|
40256
|
-
|
|
40257
|
-
|
|
40258
|
-
|
|
40259
|
-
|
|
40643
|
+
const enriched = await Promise.all(
|
|
40644
|
+
alerts.map(async (a3) => {
|
|
40645
|
+
let chainLabel = a3.chainKey;
|
|
40646
|
+
try {
|
|
40647
|
+
chainLabel = tokenIdToChain(a3.tokenId) ?? a3.chainKey;
|
|
40648
|
+
} catch {
|
|
40649
|
+
}
|
|
40650
|
+
return { ...a3, tokenLabel: await resolveTokenLabel(a3.tokenId, service), chainLabel };
|
|
40651
|
+
})
|
|
40652
|
+
);
|
|
40260
40653
|
output(enriched, true);
|
|
40261
40654
|
} else {
|
|
40262
40655
|
if (alerts.length === 0) {
|
|
@@ -40264,7 +40657,7 @@ function registerAlertCommand(program3) {
|
|
|
40264
40657
|
return;
|
|
40265
40658
|
}
|
|
40266
40659
|
for (const a3 of alerts) {
|
|
40267
|
-
const label = resolveTokenLabel(a3.tokenId);
|
|
40660
|
+
const label = await resolveTokenLabel(a3.tokenId, service);
|
|
40268
40661
|
let chainHint = "";
|
|
40269
40662
|
try {
|
|
40270
40663
|
const chain2 = tokenIdToChain(a3.tokenId);
|
|
@@ -40272,7 +40665,9 @@ function registerAlertCommand(program3) {
|
|
|
40272
40665
|
} catch {
|
|
40273
40666
|
chainHint = "";
|
|
40274
40667
|
}
|
|
40275
|
-
info(
|
|
40668
|
+
info(
|
|
40669
|
+
`${a3.id} | ${label}${chainHint} | ${a3.condition} $${a3.targetPrice} | ${a3.active ? "active" : "triggered"}`
|
|
40670
|
+
);
|
|
40276
40671
|
}
|
|
40277
40672
|
}
|
|
40278
40673
|
});
|
|
@@ -40292,16 +40687,34 @@ function registerAlertCommand(program3) {
|
|
|
40292
40687
|
triggered.push({ ...a3, currentPrice: price });
|
|
40293
40688
|
}
|
|
40294
40689
|
}
|
|
40295
|
-
output(
|
|
40690
|
+
output(
|
|
40691
|
+
{ checked: activeAlerts.length, triggered: triggered.length, alerts: triggered },
|
|
40692
|
+
opts.json
|
|
40693
|
+
);
|
|
40296
40694
|
} catch (err) {
|
|
40297
40695
|
const msg = err instanceof Error ? err.message : String(err);
|
|
40298
40696
|
error(msg);
|
|
40299
40697
|
if (opts.json) output({ error: msg }, true);
|
|
40698
|
+
process.exitCode = 1;
|
|
40300
40699
|
}
|
|
40301
40700
|
});
|
|
40302
40701
|
alert.command("delete <id>").description("Delete an alert").option("--json", "JSON output").action(async (id, opts) => {
|
|
40303
|
-
|
|
40304
|
-
|
|
40702
|
+
try {
|
|
40703
|
+
const deleted = await alertStore.delete(id);
|
|
40704
|
+
if (!deleted) {
|
|
40705
|
+
error(`Alert ${id} not found`);
|
|
40706
|
+
if (opts.json) output({ error: `Alert ${id} not found` }, true);
|
|
40707
|
+
process.exitCode = 1;
|
|
40708
|
+
return;
|
|
40709
|
+
}
|
|
40710
|
+
if (!opts.json) success(`Alert ${id} deleted`);
|
|
40711
|
+
output({ success: true }, opts.json);
|
|
40712
|
+
} catch (err) {
|
|
40713
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
40714
|
+
error(msg);
|
|
40715
|
+
if (opts.json) output({ error: msg }, true);
|
|
40716
|
+
process.exitCode = 1;
|
|
40717
|
+
}
|
|
40305
40718
|
});
|
|
40306
40719
|
}
|
|
40307
40720
|
|
|
@@ -40380,14 +40793,20 @@ function handleError(err, json) {
|
|
|
40380
40793
|
process.exitCode = 1;
|
|
40381
40794
|
}
|
|
40382
40795
|
function registerQueryCommand(program3) {
|
|
40383
|
-
program3.command("price <token>").description("Get token price").option("--chain <id>", "Chain").option(
|
|
40796
|
+
program3.command("price <token>").description("Get token price").option("--chain <id>", "Chain").option(
|
|
40797
|
+
"--history [period]",
|
|
40798
|
+
"Price history period: hour, day, week, month, year, all (default: week)"
|
|
40799
|
+
).option("--json", "JSON output").action(async (token, opts) => {
|
|
40384
40800
|
try {
|
|
40385
40801
|
const client = await getClient();
|
|
40386
40802
|
const swapClient = new SwapClient(client);
|
|
40387
40803
|
const tokens = token.split(",").map((t) => t.trim()).filter(Boolean);
|
|
40388
40804
|
const resolveChain = (t) => {
|
|
40389
40805
|
if (opts.chain) return opts.chain;
|
|
40390
|
-
|
|
40806
|
+
if (Object.prototype.hasOwnProperty.call(CHAINS, t.toLowerCase())) return t.toLowerCase();
|
|
40807
|
+
const bySymbol = Object.entries(CHAINS).find(
|
|
40808
|
+
([, c3]) => c3.symbol.toUpperCase() === t.toUpperCase()
|
|
40809
|
+
)?.[0];
|
|
40391
40810
|
if (bySymbol) return bySymbol;
|
|
40392
40811
|
const upper = t.toUpperCase();
|
|
40393
40812
|
for (const [chainKey, chainTokens] of Object.entries(COMMON_TOKENS)) {
|
|
@@ -40395,12 +40814,22 @@ function registerQueryCommand(program3) {
|
|
|
40395
40814
|
}
|
|
40396
40815
|
return "ethereum";
|
|
40397
40816
|
};
|
|
40817
|
+
if (opts.chain && !getChainById(opts.chain)) {
|
|
40818
|
+
error(`Unknown chain: ${opts.chain}. Run 'twak chains' to list supported chains.`);
|
|
40819
|
+
if (opts.json) output({ error: `Unknown chain: ${opts.chain}` }, true);
|
|
40820
|
+
process.exitCode = 1;
|
|
40821
|
+
return;
|
|
40822
|
+
}
|
|
40398
40823
|
if (tokens.length > 1) {
|
|
40399
40824
|
const assetIds = [];
|
|
40400
40825
|
const tokenMeta = [];
|
|
40401
40826
|
for (const t of tokens) {
|
|
40402
40827
|
const chain3 = resolveChain(t);
|
|
40403
40828
|
const info2 = resolveToken(t, chain3);
|
|
40829
|
+
if (!info2) {
|
|
40830
|
+
warn(`Unknown token: ${t.toUpperCase()}`);
|
|
40831
|
+
continue;
|
|
40832
|
+
}
|
|
40404
40833
|
const chainInfo = getChainById(chain3);
|
|
40405
40834
|
if (!chainInfo) throw new Error(`Unknown chain: ${chain3}`);
|
|
40406
40835
|
const isNative = !info2.address || info2.address === NATIVE_TOKEN_ADDRESS || info2.address === SOLANA_NATIVE_ADDRESS;
|
|
@@ -40408,10 +40837,19 @@ function registerQueryCommand(program3) {
|
|
|
40408
40837
|
assetIds.push(assetId);
|
|
40409
40838
|
tokenMeta.push({ token: t.toUpperCase(), chain: chain3 });
|
|
40410
40839
|
}
|
|
40411
|
-
|
|
40412
|
-
|
|
40413
|
-
|
|
40414
|
-
|
|
40840
|
+
if (assetIds.length === 0) {
|
|
40841
|
+
error("No valid tokens to look up");
|
|
40842
|
+
process.exitCode = 1;
|
|
40843
|
+
return;
|
|
40844
|
+
}
|
|
40845
|
+
const resp = await client.market(
|
|
40846
|
+
"POST",
|
|
40847
|
+
"/v2/market/tickers",
|
|
40848
|
+
{
|
|
40849
|
+
currency: "USD",
|
|
40850
|
+
assets: assetIds
|
|
40851
|
+
}
|
|
40852
|
+
);
|
|
40415
40853
|
const priceMap = /* @__PURE__ */ new Map();
|
|
40416
40854
|
for (const t of resp.tickers || []) {
|
|
40417
40855
|
if (t.id && t.price != null) priceMap.set(t.id, t.price);
|
|
@@ -40426,39 +40864,58 @@ function registerQueryCommand(program3) {
|
|
|
40426
40864
|
} else {
|
|
40427
40865
|
for (const r3 of results) {
|
|
40428
40866
|
const priceStr = r3.priceUsd !== null ? `$${r3.priceUsd.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 6 })}` : "N/A";
|
|
40429
|
-
console.log(
|
|
40867
|
+
console.log(
|
|
40868
|
+
`${import_chalk6.default.bold(r3.token.padEnd(10))} ${priceStr.padStart(14)} ${import_chalk6.default.dim(r3.chain)}`
|
|
40869
|
+
);
|
|
40430
40870
|
}
|
|
40431
40871
|
}
|
|
40432
40872
|
return;
|
|
40433
40873
|
}
|
|
40434
40874
|
const chain2 = resolveChain(token);
|
|
40435
40875
|
const tokenInfo = resolveToken(token, chain2);
|
|
40436
|
-
let price =
|
|
40876
|
+
let price = null;
|
|
40877
|
+
if (tokenInfo) {
|
|
40878
|
+
price = await swapClient.getTokenPrice(chain2, tokenInfo.address);
|
|
40879
|
+
}
|
|
40437
40880
|
if (price === null) {
|
|
40438
|
-
const results = await swapClient.searchTokens({
|
|
40439
|
-
|
|
40440
|
-
|
|
40881
|
+
const results = await swapClient.searchTokens({
|
|
40882
|
+
query: token,
|
|
40883
|
+
chains: [chain2],
|
|
40884
|
+
limit: 5
|
|
40885
|
+
});
|
|
40886
|
+
const exact = results.find(
|
|
40887
|
+
(r3) => r3.symbol?.toUpperCase() === token.toUpperCase() && r3.priceUsd != null
|
|
40888
|
+
);
|
|
40889
|
+
if (exact) {
|
|
40890
|
+
price = exact.priceUsd;
|
|
40441
40891
|
}
|
|
40442
40892
|
}
|
|
40443
40893
|
if (price === null) {
|
|
40444
40894
|
error(`Token not found: ${token}`);
|
|
40445
|
-
if (opts.json)
|
|
40895
|
+
if (opts.json)
|
|
40896
|
+
output(
|
|
40897
|
+
{ token: token.toUpperCase(), chain: chain2, priceUsd: null, error: "Token not found" },
|
|
40898
|
+
true
|
|
40899
|
+
);
|
|
40446
40900
|
process.exitCode = 1;
|
|
40447
40901
|
return;
|
|
40448
40902
|
}
|
|
40449
40903
|
if (opts.history !== void 0) {
|
|
40904
|
+
if (!tokenInfo) {
|
|
40905
|
+
error(`Price history not available for "${token}" \u2014 token not locally resolved. Use an asset ID (e.g. c60) or contract address.`);
|
|
40906
|
+
process.exitCode = 1;
|
|
40907
|
+
return;
|
|
40908
|
+
}
|
|
40450
40909
|
const period = typeof opts.history === "string" ? opts.history : "week";
|
|
40451
40910
|
const chainInfo = getChainById(chain2);
|
|
40452
40911
|
if (!chainInfo) throw new Error(`Unknown chain: ${chain2}`);
|
|
40453
40912
|
const isNative = !tokenInfo.address || tokenInfo.address === NATIVE_TOKEN_ADDRESS || tokenInfo.address === SOLANA_NATIVE_ADDRESS;
|
|
40454
40913
|
const assetId = isNative ? `c${chainInfo.coinId}` : `c${chainInfo.coinId}_t${tokenInfo.address}`;
|
|
40455
|
-
const chartData = await client.gateway(
|
|
40456
|
-
|
|
40457
|
-
|
|
40458
|
-
|
|
40459
|
-
`currency=USD&period=${period}`
|
|
40914
|
+
const chartData = await client.gateway("GET", `/v1/market/charts/${assetId}`, void 0, `currency=USD&period=${period}`);
|
|
40915
|
+
output(
|
|
40916
|
+
{ token: token.toUpperCase(), chain: chain2, priceUsd: price, history: chartData.prices },
|
|
40917
|
+
opts.json
|
|
40460
40918
|
);
|
|
40461
|
-
output({ token: token.toUpperCase(), chain: chain2, priceUsd: price, history: chartData.prices }, opts.json);
|
|
40462
40919
|
return;
|
|
40463
40920
|
}
|
|
40464
40921
|
if (opts.json) {
|
|
@@ -40482,7 +40939,9 @@ function registerQueryCommand(program3) {
|
|
|
40482
40939
|
if (/^\d+$/.test(trimmed)) return trimmed;
|
|
40483
40940
|
const chain2 = getChainById(trimmed);
|
|
40484
40941
|
if (chain2) return String(chain2.coinId);
|
|
40485
|
-
throw new Error(
|
|
40942
|
+
throw new Error(
|
|
40943
|
+
`Unknown network "${trimmed}". Use a supported chain name or numeric coin ID.`
|
|
40944
|
+
);
|
|
40486
40945
|
}).join(",");
|
|
40487
40946
|
}
|
|
40488
40947
|
const result = await swapClient.searchTokens({
|
|
@@ -40499,6 +40958,18 @@ function registerQueryCommand(program3) {
|
|
|
40499
40958
|
try {
|
|
40500
40959
|
const client = await getClient();
|
|
40501
40960
|
const service = new TransactionService(client);
|
|
40961
|
+
if (opts.chain && !getChainById(opts.chain)) {
|
|
40962
|
+
error(`Unknown chain: ${opts.chain}. Run 'twak chains' to list supported chains.`);
|
|
40963
|
+
if (opts.json) output({ error: `Unknown chain: ${opts.chain}` }, true);
|
|
40964
|
+
process.exitCode = 1;
|
|
40965
|
+
return;
|
|
40966
|
+
}
|
|
40967
|
+
if (opts.address && opts.chain && !/^(0x[0-9a-fA-F]{40}|[1-9A-HJ-NP-Za-km-z]{20,}|[A-Za-z0-9]{20,})$/.test(opts.address)) {
|
|
40968
|
+
error(`Invalid address: "${opts.address}"`);
|
|
40969
|
+
if (opts.json) output({ error: `Invalid address: "${opts.address}"` }, true);
|
|
40970
|
+
process.exitCode = 1;
|
|
40971
|
+
return;
|
|
40972
|
+
}
|
|
40502
40973
|
if (!opts.chain) {
|
|
40503
40974
|
await ensureTrustSDK();
|
|
40504
40975
|
const password = await resolvePassword(opts);
|
|
@@ -40550,7 +41021,9 @@ function registerQueryCommand(program3) {
|
|
|
40550
41021
|
const intPart = big / divisor;
|
|
40551
41022
|
const scale = 10000n;
|
|
40552
41023
|
const fracScaled = big % divisor * scale / divisor;
|
|
40553
|
-
const usdValue = price !== null && (intPart > 0n || fracScaled > 0n) && intPart <= BigInt(Number.MAX_SAFE_INTEGER) ? parseFloat(
|
|
41024
|
+
const usdValue = price !== null && (intPart > 0n || fracScaled > 0n) && intPart <= BigInt(Number.MAX_SAFE_INTEGER) ? parseFloat(
|
|
41025
|
+
((Number(intPart) + Number(fracScaled) / 1e4) * price).toFixed(4)
|
|
41026
|
+
) : void 0;
|
|
40554
41027
|
return { ...tx, usdValue };
|
|
40555
41028
|
} catch {
|
|
40556
41029
|
return tx;
|
|
@@ -40572,9 +41045,16 @@ function registerQueryCommand(program3) {
|
|
|
40572
41045
|
} catch (err) {
|
|
40573
41046
|
handleError(err, opts.json);
|
|
40574
41047
|
}
|
|
41048
|
+
safeExit();
|
|
40575
41049
|
});
|
|
40576
41050
|
program3.command("tx <hash>").description("Get transaction details by hash (on-chain RPC lookup)").requiredOption("--chain <name>", "Chain name").option("--address <address>", "Wallet address (auto-detected from wallet if omitted)").option("--password <pw>", "Wallet password (for auto-detecting address)").option("--json", "JSON output").action(async (hash, opts) => {
|
|
40577
41051
|
try {
|
|
41052
|
+
if (!getChainById(opts.chain)) {
|
|
41053
|
+
error(`Unknown chain: ${opts.chain}. Run 'twak chains' to list supported chains.`);
|
|
41054
|
+
if (opts.json) output({ error: `Unknown chain: ${opts.chain}` }, true);
|
|
41055
|
+
process.exitCode = 1;
|
|
41056
|
+
return;
|
|
41057
|
+
}
|
|
40578
41058
|
let address = opts.address;
|
|
40579
41059
|
if (!address) {
|
|
40580
41060
|
try {
|
|
@@ -40589,11 +41069,14 @@ function registerQueryCommand(program3) {
|
|
|
40589
41069
|
await ensureTrustSDK();
|
|
40590
41070
|
const blockchainClient = new BlockchainClient();
|
|
40591
41071
|
const status = await blockchainClient.findTransactionStatus(opts.chain, address, hash);
|
|
40592
|
-
output(
|
|
40593
|
-
|
|
40594
|
-
|
|
40595
|
-
|
|
40596
|
-
|
|
41072
|
+
output(
|
|
41073
|
+
{
|
|
41074
|
+
hash,
|
|
41075
|
+
chain: opts.chain,
|
|
41076
|
+
...status
|
|
41077
|
+
},
|
|
41078
|
+
opts.json
|
|
41079
|
+
);
|
|
40597
41080
|
} else {
|
|
40598
41081
|
const client = await getClient();
|
|
40599
41082
|
const service = new TransactionService(client);
|
|
@@ -40607,11 +41090,37 @@ function registerQueryCommand(program3) {
|
|
|
40607
41090
|
program3.command("chains").description("List supported chains").option("--json", "JSON output").action(async (opts) => {
|
|
40608
41091
|
const chainEntries = Object.entries(CHAINS);
|
|
40609
41092
|
if (opts.json) {
|
|
40610
|
-
output(
|
|
41093
|
+
output(
|
|
41094
|
+
chainEntries.map(([key, chain2]) => ({
|
|
41095
|
+
key,
|
|
41096
|
+
name: chain2.name,
|
|
41097
|
+
symbol: chain2.symbol,
|
|
41098
|
+
namespace: chain2.namespace,
|
|
41099
|
+
chainId: chain2.id,
|
|
41100
|
+
coinId: chain2.coinId
|
|
41101
|
+
})),
|
|
41102
|
+
true
|
|
41103
|
+
);
|
|
40611
41104
|
} else {
|
|
40612
|
-
|
|
40613
|
-
|
|
40614
|
-
|
|
41105
|
+
const rows = chainEntries.map(([key, chain2]) => ({
|
|
41106
|
+
key,
|
|
41107
|
+
name: chain2.name,
|
|
41108
|
+
symbol: chain2.symbol,
|
|
41109
|
+
namespace: chain2.namespace
|
|
41110
|
+
}));
|
|
41111
|
+
const keyW = Math.max(...rows.map((r3) => r3.key.length), 3);
|
|
41112
|
+
const nameW = Math.max(...rows.map((r3) => r3.name.length), 4);
|
|
41113
|
+
const symW = Math.max(...rows.map((r3) => r3.symbol.length), 6);
|
|
41114
|
+
const nsW = Math.max(...rows.map((r3) => r3.namespace.length), 9);
|
|
41115
|
+
console.log(
|
|
41116
|
+
import_chalk6.default.bold(
|
|
41117
|
+
`${"KEY".padEnd(keyW)} ${"NAME".padEnd(nameW)} ${"SYMBOL".padEnd(symW)} NAMESPACE`
|
|
41118
|
+
)
|
|
41119
|
+
);
|
|
41120
|
+
for (const r3 of rows) {
|
|
41121
|
+
console.log(
|
|
41122
|
+
`${r3.key.padEnd(keyW)} ${r3.name.padEnd(nameW)} ${r3.symbol.padEnd(symW)} ${r3.namespace.padEnd(nsW)}`
|
|
41123
|
+
);
|
|
40615
41124
|
}
|
|
40616
41125
|
}
|
|
40617
41126
|
});
|
|
@@ -40650,7 +41159,11 @@ function registerQueryCommand(program3) {
|
|
|
40650
41159
|
const apiClient = await getClient();
|
|
40651
41160
|
const swapClient = new SwapClient(apiClient);
|
|
40652
41161
|
const chainKey = opts.chain || Object.entries(CHAINS).find(([, c3]) => c3.coinId === coinId)?.[0] || "";
|
|
40653
|
-
const tokens = await swapClient.searchTokens({
|
|
41162
|
+
const tokens = await swapClient.searchTokens({
|
|
41163
|
+
query: opts.token,
|
|
41164
|
+
chains: [chainKey],
|
|
41165
|
+
limit: 1
|
|
41166
|
+
});
|
|
40654
41167
|
if (tokens.length > 0) {
|
|
40655
41168
|
decimals = tokens[0].decimals ?? 18;
|
|
40656
41169
|
symbol = tokens[0].symbol ?? opts.token;
|
|
@@ -40665,20 +41178,24 @@ function registerQueryCommand(program3) {
|
|
|
40665
41178
|
const div = 10n ** BigInt(decimals);
|
|
40666
41179
|
return `${big / div}.${(big % div).toString().padStart(decimals, "0").slice(0, 6)}`;
|
|
40667
41180
|
};
|
|
40668
|
-
output(
|
|
40669
|
-
|
|
40670
|
-
|
|
40671
|
-
|
|
40672
|
-
|
|
40673
|
-
|
|
40674
|
-
|
|
40675
|
-
|
|
40676
|
-
|
|
40677
|
-
|
|
40678
|
-
|
|
41181
|
+
output(
|
|
41182
|
+
{
|
|
41183
|
+
address: opts.address,
|
|
41184
|
+
chain: chain2.id,
|
|
41185
|
+
symbol,
|
|
41186
|
+
...opts.token ? { token: opts.token } : {},
|
|
41187
|
+
available: fmt(balance.available),
|
|
41188
|
+
...balance.staked ? { staked: fmt(balance.staked) } : {},
|
|
41189
|
+
...balance.pending ? { pending: fmt(balance.pending) } : {},
|
|
41190
|
+
total: fmt(balance.total),
|
|
41191
|
+
raw: balance
|
|
41192
|
+
},
|
|
41193
|
+
opts.json
|
|
41194
|
+
);
|
|
40679
41195
|
} catch (err) {
|
|
40680
41196
|
handleError(err, opts.json);
|
|
40681
41197
|
}
|
|
41198
|
+
safeExit();
|
|
40682
41199
|
});
|
|
40683
41200
|
program3.command("asset <assetId>").description("Get asset info").option("--json", "JSON output").action(async (assetId, opts) => {
|
|
40684
41201
|
try {
|
|
@@ -40692,6 +41209,12 @@ function registerQueryCommand(program3) {
|
|
|
40692
41209
|
});
|
|
40693
41210
|
program3.command("validate").description("Validate an address").requiredOption("--address <address>", "Address to validate").option("--chain <key>", "Chain key (e.g., ethereum, solana)").option("--asset-id <id>", "Asset ID (alternative to --chain)").option("--json", "JSON output").action(async (opts) => {
|
|
40694
41211
|
try {
|
|
41212
|
+
if (!opts.chain && !opts.assetId) {
|
|
41213
|
+
error("Provide --chain <key> or --asset-id <id> to validate an address");
|
|
41214
|
+
if (opts.json) output({ error: "--chain or --asset-id required" }, true);
|
|
41215
|
+
process.exitCode = 1;
|
|
41216
|
+
return;
|
|
41217
|
+
}
|
|
40695
41218
|
let assetId = opts.assetId;
|
|
40696
41219
|
if (!assetId && opts.chain) {
|
|
40697
41220
|
const chain2 = getChainById(opts.chain);
|
|
@@ -40711,7 +41234,12 @@ function registerQueryCommand(program3) {
|
|
|
40711
41234
|
if (!isValid2) {
|
|
40712
41235
|
const chainInfo = getChainByCoinId(coinId);
|
|
40713
41236
|
const label = chainInfo?.name ?? `coin ${coinId}`;
|
|
40714
|
-
const result = {
|
|
41237
|
+
const result = {
|
|
41238
|
+
status: "error",
|
|
41239
|
+
valid: false,
|
|
41240
|
+
chain: chainInfo?.id,
|
|
41241
|
+
message: `Invalid address format for ${label}`
|
|
41242
|
+
};
|
|
40715
41243
|
output(result, opts.json);
|
|
40716
41244
|
return;
|
|
40717
41245
|
}
|
|
@@ -40732,8 +41260,12 @@ function registerQueryCommand(program3) {
|
|
|
40732
41260
|
} catch (err) {
|
|
40733
41261
|
handleError(err, opts.json);
|
|
40734
41262
|
}
|
|
41263
|
+
safeExit();
|
|
40735
41264
|
});
|
|
40736
|
-
program3.command("trending").description("Get trending tokens by market activity or category").option(
|
|
41265
|
+
program3.command("trending").description("Get trending tokens by market activity or category").option(
|
|
41266
|
+
"--category <id>",
|
|
41267
|
+
"Category: ai, rwa, memes, defi, dex, bnb, eth, sol, pumpfun, bonk, launchpad, launchpool, layer1"
|
|
41268
|
+
).option("--sort <by>", "Sort: price_change, market_cap, volume (default: price_change)").option("--limit <n>", "Max results", "20").option("--json", "JSON output").action(async (opts) => {
|
|
40737
41269
|
try {
|
|
40738
41270
|
const client = await getClient();
|
|
40739
41271
|
const service = new SecurityService(client);
|
|
@@ -40757,15 +41289,13 @@ function registerQueryCommand(program3) {
|
|
|
40757
41289
|
launchpool: "binance-launchpool",
|
|
40758
41290
|
layer1: "layer_1"
|
|
40759
41291
|
};
|
|
40760
|
-
|
|
40761
|
-
|
|
40762
|
-
|
|
40763
|
-
|
|
40764
|
-
|
|
40765
|
-
|
|
40766
|
-
|
|
40767
|
-
tokens = await service.getTrendingTokens(parseInt(opts.limit, 10), sort);
|
|
40768
|
-
}
|
|
41292
|
+
const categoryId = opts.category ? categoryMap[opts.category] ?? opts.category : void 0;
|
|
41293
|
+
const sort = opts.sort ? sortMap[opts.sort] ?? opts.sort : "percent_change_24h";
|
|
41294
|
+
const tokens = await service.getAssetListings({
|
|
41295
|
+
categoryId,
|
|
41296
|
+
sort,
|
|
41297
|
+
limit: parseInt(opts.limit, 10)
|
|
41298
|
+
});
|
|
40769
41299
|
const seen = /* @__PURE__ */ new Map();
|
|
40770
41300
|
for (const t of tokens) {
|
|
40771
41301
|
const key = t.symbol.toUpperCase();
|
|
@@ -40781,14 +41311,19 @@ function registerQueryCommand(program3) {
|
|
|
40781
41311
|
for (const t of deduped) {
|
|
40782
41312
|
const price = t.priceUsd != null ? `$${t.priceUsd.toFixed(4)}` : "N/A";
|
|
40783
41313
|
const change = t.priceChange24h != null ? `${t.priceChange24h > 0 ? "+" : ""}${t.priceChange24h.toFixed(2)}%` : "";
|
|
40784
|
-
console.log(
|
|
41314
|
+
console.log(
|
|
41315
|
+
`${t.symbol.padEnd(10)} ${t.name.padEnd(25)} ${price.padStart(12)} ${change.padStart(8)}`
|
|
41316
|
+
);
|
|
40785
41317
|
}
|
|
40786
41318
|
}
|
|
40787
41319
|
} catch (err) {
|
|
40788
41320
|
handleError(err, opts.json);
|
|
40789
41321
|
}
|
|
40790
41322
|
});
|
|
40791
|
-
program3.command("dapps").description("Browse DeFi protocols and DApps (data from DeFi Llama)").option(
|
|
41323
|
+
program3.command("dapps").description("Browse DeFi protocols and DApps (data from DeFi Llama)").option(
|
|
41324
|
+
"--category <name>",
|
|
41325
|
+
"Filter by category (e.g., Lending, Dexs, Liquid Staking, Bridge, Yield)"
|
|
41326
|
+
).option("--search <query>", "Search by name, symbol, or category").option("--categories", "List available categories").option("--limit <n>", "Max results", "20").option("--json", "JSON output").action(async (opts) => {
|
|
40792
41327
|
try {
|
|
40793
41328
|
const { DappsService: DappsService2 } = await Promise.resolve().then(() => (init_dapps_service(), dapps_service_exports));
|
|
40794
41329
|
const service = new DappsService2();
|
|
@@ -40808,12 +41343,16 @@ function registerQueryCommand(program3) {
|
|
|
40808
41343
|
if (dapps.length === 0) {
|
|
40809
41344
|
console.log("No DApps found.");
|
|
40810
41345
|
} else {
|
|
40811
|
-
console.log(
|
|
41346
|
+
console.log(
|
|
41347
|
+
"Name".padEnd(25) + "Category".padEnd(20) + "TVL".padStart(15) + " Chains"
|
|
41348
|
+
);
|
|
40812
41349
|
console.log("\u2500".repeat(80));
|
|
40813
41350
|
for (const d3 of dapps) {
|
|
40814
41351
|
const tvl = d3.tvl >= 1e9 ? `$${(d3.tvl / 1e9).toFixed(2)}B` : d3.tvl >= 1e6 ? `$${(d3.tvl / 1e6).toFixed(0)}M` : `$${(d3.tvl / 1e3).toFixed(0)}K`;
|
|
40815
41352
|
const chains = d3.chains.slice(0, 5).join(", ") + (d3.chains.length > 5 ? ` +${d3.chains.length - 5}` : "");
|
|
40816
|
-
console.log(
|
|
41353
|
+
console.log(
|
|
41354
|
+
`${d3.name.slice(0, 24).padEnd(25)}${d3.category.slice(0, 19).padEnd(20)}${tvl.padStart(15)} ${chains}`
|
|
41355
|
+
);
|
|
40817
41356
|
}
|
|
40818
41357
|
}
|
|
40819
41358
|
}
|
|
@@ -45220,7 +45759,9 @@ var SwapActionProvider = class extends ActionProvider {
|
|
|
45220
45759
|
return { success: false, error: `Slippage must be greater than 0% and at most 50%. Got: ${input.slippage}` };
|
|
45221
45760
|
}
|
|
45222
45761
|
const fromTokenInfo = resolveToken(input.fromToken, input.fromChain);
|
|
45762
|
+
if (!fromTokenInfo) return { success: false, error: `Unknown token: ${input.fromToken} on ${input.fromChain}` };
|
|
45223
45763
|
const toTokenInfo = resolveToken(input.toToken, input.toChain);
|
|
45764
|
+
if (!toTokenInfo) return { success: false, error: `Unknown token: ${input.toToken} on ${input.toChain}` };
|
|
45224
45765
|
const parsedAmount = parseAmount(input.amount, fromTokenInfo.decimals);
|
|
45225
45766
|
const address = await signer.getAddress(input.fromChain);
|
|
45226
45767
|
const quote = await this.swapClient.getQuote({
|
|
@@ -45273,7 +45814,9 @@ var SwapActionProvider = class extends ActionProvider {
|
|
|
45273
45814
|
invoke: async (_ctx, input) => {
|
|
45274
45815
|
if (!this.swapClient) return { success: false, error: "Swap client required" };
|
|
45275
45816
|
const fromTokenInfo = resolveToken(input.fromToken, input.fromChain);
|
|
45817
|
+
if (!fromTokenInfo) return { success: false, error: `Unknown token: ${input.fromToken} on ${input.fromChain}` };
|
|
45276
45818
|
const toTokenInfo = resolveToken(input.toToken, input.toChain);
|
|
45819
|
+
if (!toTokenInfo) return { success: false, error: `Unknown token: ${input.toToken} on ${input.toChain}` };
|
|
45277
45820
|
const parsedAmount = parseAmount(input.amount, fromTokenInfo.decimals);
|
|
45278
45821
|
const quote = await this.swapClient.getQuote({
|
|
45279
45822
|
fromChain: input.fromChain,
|
|
@@ -45310,6 +45853,7 @@ var SwapActionProvider = class extends ActionProvider {
|
|
|
45310
45853
|
invoke: async (_ctx, input) => {
|
|
45311
45854
|
if (!this.swapClient) return { success: false, error: "Swap client required" };
|
|
45312
45855
|
const tokenInfo = resolveToken(input.token, input.chain);
|
|
45856
|
+
if (!tokenInfo) return { success: false, error: `Unknown token: ${input.token} on ${input.chain}` };
|
|
45313
45857
|
const price = await this.swapClient.getTokenPrice(input.chain, tokenInfo.address);
|
|
45314
45858
|
if (price === null) return { success: false, error: `Price not found for ${input.token}` };
|
|
45315
45859
|
return { success: true, token: input.token.toUpperCase(), chain: input.chain, priceUsd: price };
|
|
@@ -45745,7 +46289,7 @@ var MarketActionProvider = class extends ActionProvider {
|
|
|
45745
46289
|
limit: external_exports.coerce.number().optional().default(20).describe("Number of tokens to return (default 20)")
|
|
45746
46290
|
}),
|
|
45747
46291
|
invoke: async (ctx, input) => {
|
|
45748
|
-
const tokens = await this.getService(ctx).
|
|
46292
|
+
const tokens = await this.getService(ctx).getAssetListings({ limit: input.limit });
|
|
45749
46293
|
return { success: true, count: tokens.length, tokens };
|
|
45750
46294
|
}
|
|
45751
46295
|
};
|
|
@@ -45908,7 +46452,9 @@ var AutomateActionProvider = class extends ActionProvider {
|
|
|
45908
46452
|
if (!record2) return { success: false, error: `Automation ${input.id} not found` };
|
|
45909
46453
|
if (!record2.active) return { success: false, error: `Automation ${input.id} is inactive` };
|
|
45910
46454
|
const fromTokenInfo = resolveToken(record2.fromToken, record2.chain);
|
|
46455
|
+
if (!fromTokenInfo) return { success: false, error: `Unknown token: ${record2.fromToken} on ${record2.chain}` };
|
|
45911
46456
|
const toTokenInfo = resolveToken(record2.toToken, record2.chain);
|
|
46457
|
+
if (!toTokenInfo) return { success: false, error: `Unknown token: ${record2.toToken} on ${record2.chain}` };
|
|
45912
46458
|
const parsedAmount = parseAmount(record2.amount, fromTokenInfo.decimals);
|
|
45913
46459
|
const address = await this.walletProvider.getAddress(record2.chain);
|
|
45914
46460
|
const quote = await this.swapClient.getQuote({
|
|
@@ -53051,7 +53597,7 @@ var Protocol = class {
|
|
|
53051
53597
|
return;
|
|
53052
53598
|
}
|
|
53053
53599
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
53054
|
-
await new Promise((
|
|
53600
|
+
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
53055
53601
|
options?.signal?.throwIfAborted();
|
|
53056
53602
|
}
|
|
53057
53603
|
} catch (error3) {
|
|
@@ -53068,7 +53614,7 @@ var Protocol = class {
|
|
|
53068
53614
|
*/
|
|
53069
53615
|
request(request, resultSchema, options) {
|
|
53070
53616
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
53071
|
-
return new Promise((
|
|
53617
|
+
return new Promise((resolve2, reject) => {
|
|
53072
53618
|
const earlyReject = (error3) => {
|
|
53073
53619
|
reject(error3);
|
|
53074
53620
|
};
|
|
@@ -53146,7 +53692,7 @@ var Protocol = class {
|
|
|
53146
53692
|
if (!parseResult.success) {
|
|
53147
53693
|
reject(parseResult.error);
|
|
53148
53694
|
} else {
|
|
53149
|
-
|
|
53695
|
+
resolve2(parseResult.data);
|
|
53150
53696
|
}
|
|
53151
53697
|
} catch (error3) {
|
|
53152
53698
|
reject(error3);
|
|
@@ -53407,12 +53953,12 @@ var Protocol = class {
|
|
|
53407
53953
|
}
|
|
53408
53954
|
} catch {
|
|
53409
53955
|
}
|
|
53410
|
-
return new Promise((
|
|
53956
|
+
return new Promise((resolve2, reject) => {
|
|
53411
53957
|
if (signal.aborted) {
|
|
53412
53958
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
53413
53959
|
return;
|
|
53414
53960
|
}
|
|
53415
|
-
const timeoutId = setTimeout(
|
|
53961
|
+
const timeoutId = setTimeout(resolve2, interval);
|
|
53416
53962
|
signal.addEventListener("abort", () => {
|
|
53417
53963
|
clearTimeout(timeoutId);
|
|
53418
53964
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -54282,12 +54828,12 @@ var StdioServerTransport = class {
|
|
|
54282
54828
|
this.onclose?.();
|
|
54283
54829
|
}
|
|
54284
54830
|
send(message) {
|
|
54285
|
-
return new Promise((
|
|
54831
|
+
return new Promise((resolve2) => {
|
|
54286
54832
|
const json = serializeMessage(message);
|
|
54287
54833
|
if (this._stdout.write(json)) {
|
|
54288
|
-
|
|
54834
|
+
resolve2();
|
|
54289
54835
|
} else {
|
|
54290
|
-
this._stdout.once("drain",
|
|
54836
|
+
this._stdout.once("drain", resolve2);
|
|
54291
54837
|
}
|
|
54292
54838
|
});
|
|
54293
54839
|
}
|
|
@@ -54598,7 +55144,7 @@ var TwakRestServer = class {
|
|
|
54598
55144
|
};
|
|
54599
55145
|
}
|
|
54600
55146
|
start() {
|
|
54601
|
-
return new Promise((
|
|
55147
|
+
return new Promise((resolve2, reject) => {
|
|
54602
55148
|
const server = (0, import_http.createServer)((req, res) => this.handleRequest(req, res));
|
|
54603
55149
|
this.httpServer = server;
|
|
54604
55150
|
server.on("error", (err) => {
|
|
@@ -54609,7 +55155,9 @@ var TwakRestServer = class {
|
|
|
54609
55155
|
}
|
|
54610
55156
|
});
|
|
54611
55157
|
server.listen(this.config.port, this.config.host, () => {
|
|
54612
|
-
console.error(
|
|
55158
|
+
console.error(
|
|
55159
|
+
`TWAK REST server listening on http://${this.config.host}:${this.config.port}`
|
|
55160
|
+
);
|
|
54613
55161
|
console.error("Endpoints: POST /actions/:name");
|
|
54614
55162
|
if (this.config.apiKey) {
|
|
54615
55163
|
console.error("API key authentication enabled (use Authorization: Bearer <HMAC_SECRET>)");
|
|
@@ -54617,28 +55165,38 @@ var TwakRestServer = class {
|
|
|
54617
55165
|
console.error("WARNING: No API key configured \u2014 REST server is unauthenticated");
|
|
54618
55166
|
}
|
|
54619
55167
|
if (this.config.paymentRequired) {
|
|
54620
|
-
console.error(
|
|
55168
|
+
console.error(
|
|
55169
|
+
`x402 payment required: ${this.config.paymentAmount} ${this.config.paymentAsset} on ${this.config.paymentChainId}`
|
|
55170
|
+
);
|
|
54621
55171
|
}
|
|
54622
|
-
|
|
55172
|
+
resolve2();
|
|
54623
55173
|
});
|
|
54624
55174
|
});
|
|
54625
55175
|
}
|
|
54626
55176
|
stop() {
|
|
54627
|
-
return new Promise((
|
|
55177
|
+
return new Promise((resolve2) => {
|
|
54628
55178
|
if (this.httpServer?.listening) {
|
|
54629
55179
|
this.httpServer.close(() => {
|
|
54630
55180
|
this.httpServer = null;
|
|
54631
|
-
|
|
55181
|
+
resolve2();
|
|
54632
55182
|
});
|
|
54633
55183
|
} else {
|
|
54634
55184
|
this.httpServer = null;
|
|
54635
|
-
|
|
55185
|
+
resolve2();
|
|
54636
55186
|
}
|
|
54637
55187
|
});
|
|
54638
55188
|
}
|
|
54639
55189
|
async handleRequest(req, res) {
|
|
54640
55190
|
res.setHeader("Content-Type", "application/json");
|
|
54641
|
-
|
|
55191
|
+
const origin = req.headers.origin || "";
|
|
55192
|
+
try {
|
|
55193
|
+
const parsed = new URL(origin);
|
|
55194
|
+
if (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "::1") {
|
|
55195
|
+
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
55196
|
+
res.setHeader("Vary", "Origin");
|
|
55197
|
+
}
|
|
55198
|
+
} catch {
|
|
55199
|
+
}
|
|
54642
55200
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
54643
55201
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Payment, Authorization");
|
|
54644
55202
|
if (req.method === "OPTIONS") {
|
|
@@ -54656,16 +55214,20 @@ var TwakRestServer = class {
|
|
|
54656
55214
|
const tokenValid = token && token.length === expected.length && (0, import_crypto5.timingSafeEqual)(Buffer.from(token), Buffer.from(expected));
|
|
54657
55215
|
if (!tokenValid) {
|
|
54658
55216
|
res.writeHead(401);
|
|
54659
|
-
res.end(
|
|
55217
|
+
res.end(
|
|
55218
|
+
JSON.stringify({ error: "Unauthorized \u2014 provide Authorization: Bearer <HMAC_SECRET>" })
|
|
55219
|
+
);
|
|
54660
55220
|
return;
|
|
54661
55221
|
}
|
|
54662
55222
|
}
|
|
54663
55223
|
if (req.method === "GET" && pathname === "/actions") {
|
|
54664
55224
|
const actions = this.registry.getActions();
|
|
54665
55225
|
res.writeHead(200);
|
|
54666
|
-
res.end(
|
|
54667
|
-
|
|
54668
|
-
|
|
55226
|
+
res.end(
|
|
55227
|
+
JSON.stringify({
|
|
55228
|
+
actions: actions.map((a3) => ({ name: a3.name, description: a3.description }))
|
|
55229
|
+
})
|
|
55230
|
+
);
|
|
54669
55231
|
return;
|
|
54670
55232
|
}
|
|
54671
55233
|
const match = pathname.match(/^\/actions\/([^/]+)$/);
|
|
@@ -54678,31 +55240,44 @@ var TwakRestServer = class {
|
|
|
54678
55240
|
if (this.config.paymentRequired) {
|
|
54679
55241
|
const paymentHeader = req.headers["x-payment"];
|
|
54680
55242
|
if (!paymentHeader) {
|
|
54681
|
-
res.setHeader(
|
|
55243
|
+
res.setHeader(
|
|
55244
|
+
"WWW-Authenticate",
|
|
55245
|
+
`x402 realm="twak", asset="${this.config.paymentAsset}", amount="${this.config.paymentAmount}", chain="${this.config.paymentChainId}"`
|
|
55246
|
+
);
|
|
54682
55247
|
res.writeHead(402);
|
|
54683
|
-
res.end(
|
|
54684
|
-
|
|
54685
|
-
|
|
54686
|
-
|
|
54687
|
-
|
|
54688
|
-
|
|
54689
|
-
|
|
54690
|
-
|
|
55248
|
+
res.end(
|
|
55249
|
+
JSON.stringify({
|
|
55250
|
+
error: "payment_required",
|
|
55251
|
+
message: `This endpoint requires payment of ${this.config.paymentAmount} ${this.config.paymentAsset}`,
|
|
55252
|
+
asset: this.config.paymentAsset,
|
|
55253
|
+
amount: this.config.paymentAmount,
|
|
55254
|
+
chainId: this.config.paymentChainId,
|
|
55255
|
+
recipient: this.config.paymentRecipient || void 0
|
|
55256
|
+
})
|
|
55257
|
+
);
|
|
54691
55258
|
return;
|
|
54692
55259
|
}
|
|
54693
55260
|
if (this.config.paymentRecipient) {
|
|
54694
|
-
const result = await verifyPayment(
|
|
54695
|
-
|
|
54696
|
-
|
|
54697
|
-
|
|
54698
|
-
|
|
54699
|
-
|
|
55261
|
+
const result = await verifyPayment(
|
|
55262
|
+
paymentHeader,
|
|
55263
|
+
{
|
|
55264
|
+
asset: this.config.paymentAsset,
|
|
55265
|
+
amount: this.config.paymentAmount,
|
|
55266
|
+
chainId: this.config.paymentChainId,
|
|
55267
|
+
recipient: this.config.paymentRecipient
|
|
55268
|
+
},
|
|
55269
|
+
this.context.httpClient,
|
|
55270
|
+
this.seenHashes,
|
|
55271
|
+
this.config.blockchainClient
|
|
55272
|
+
);
|
|
54700
55273
|
if (!result.valid) {
|
|
54701
55274
|
res.writeHead(402);
|
|
54702
|
-
res.end(
|
|
54703
|
-
|
|
54704
|
-
|
|
54705
|
-
|
|
55275
|
+
res.end(
|
|
55276
|
+
JSON.stringify({
|
|
55277
|
+
error: "payment_verification_failed",
|
|
55278
|
+
message: result.error ?? "Payment could not be verified"
|
|
55279
|
+
})
|
|
55280
|
+
);
|
|
54706
55281
|
return;
|
|
54707
55282
|
}
|
|
54708
55283
|
}
|
|
@@ -54857,6 +55432,11 @@ function registerAutomateCommand(program3) {
|
|
|
54857
55432
|
const automate = program3.command("automate").description("Manage DCA and limit order automations");
|
|
54858
55433
|
automate.command("add").description("Create a DCA or limit order automation").requiredOption("--from <token>", "Source token (e.g., USDC)").requiredOption("--to <token>", "Destination token (e.g., ETH)").option("--chain <chain>", "Chain", "ethereum").requiredOption("--amount <n>", "Amount of source token per run").option("--interval <duration>", "DCA interval \u2014 e.g. 30s, 5m, 1h (creates DCA automation)").option("--price <usd>", "Target price in USD (creates limit order)").option("--condition <cond>", "Price condition: above or below", "below").option("--max-runs <n>", "Stop after N executions").option("--expires <date>", "Expiry date (ISO 8601, e.g. 2026-04-01)").option("--json", "JSON output").action(async (opts) => {
|
|
54859
55434
|
try {
|
|
55435
|
+
if (opts.interval && opts.price) {
|
|
55436
|
+
error("Provide --interval (DCA) or --price (limit order), not both");
|
|
55437
|
+
process.exitCode = 1;
|
|
55438
|
+
return;
|
|
55439
|
+
}
|
|
54860
55440
|
if (!opts.interval && !opts.price) {
|
|
54861
55441
|
error("Provide --interval (DCA) or --price (limit order)");
|
|
54862
55442
|
process.exitCode = 1;
|
|
@@ -54873,6 +55453,13 @@ function registerAutomateCommand(program3) {
|
|
|
54873
55453
|
process.exitCode = 1;
|
|
54874
55454
|
return;
|
|
54875
55455
|
}
|
|
55456
|
+
const SWAP_SUPPORTED_NAMESPACES = ["eip155", "solana"];
|
|
55457
|
+
const chainInfo = getChainById(opts.chain);
|
|
55458
|
+
if (chainInfo && !SWAP_SUPPORTED_NAMESPACES.includes(chainInfo.namespace)) {
|
|
55459
|
+
error(`${chainInfo.name} automations are not supported. Use an EVM chain or Solana.`);
|
|
55460
|
+
process.exitCode = 1;
|
|
55461
|
+
return;
|
|
55462
|
+
}
|
|
54876
55463
|
let intervalMs;
|
|
54877
55464
|
if (opts.interval) {
|
|
54878
55465
|
const parsed = parseInterval(opts.interval);
|
|
@@ -54915,9 +55502,13 @@ function registerAutomateCommand(program3) {
|
|
|
54915
55502
|
});
|
|
54916
55503
|
success("Automation created!");
|
|
54917
55504
|
if (opts.interval && intervalMs) {
|
|
54918
|
-
info(
|
|
55505
|
+
info(
|
|
55506
|
+
`DCA: ${opts.amount} ${opts.from} \u2192 ${opts.to} every ${formatInterval(intervalMs)} on ${opts.chain}`
|
|
55507
|
+
);
|
|
54919
55508
|
} else {
|
|
54920
|
-
info(
|
|
55509
|
+
info(
|
|
55510
|
+
`Limit: swap ${opts.amount} ${opts.from} \u2192 ${opts.to} when price ${opts.condition} $${opts.price} on ${opts.chain}`
|
|
55511
|
+
);
|
|
54921
55512
|
}
|
|
54922
55513
|
output(record2, opts.json);
|
|
54923
55514
|
} catch (err) {
|
|
@@ -54927,11 +55518,10 @@ function registerAutomateCommand(program3) {
|
|
|
54927
55518
|
process.exitCode = 1;
|
|
54928
55519
|
}
|
|
54929
55520
|
});
|
|
54930
|
-
automate.command("list").description("List
|
|
55521
|
+
automate.command("list").description("List automations").option("--json", "JSON output").action(async (opts) => {
|
|
54931
55522
|
try {
|
|
54932
55523
|
const store = new AutomationStore();
|
|
54933
|
-
const
|
|
54934
|
-
const records = opts.all ? all : all.filter((a3) => a3.active);
|
|
55524
|
+
const records = await store.list();
|
|
54935
55525
|
if (opts.json) {
|
|
54936
55526
|
const enriched = records.map((r3) => {
|
|
54937
55527
|
const nextRunAt = r3.type === "dca" && r3.intervalMs ? r3.lastRunAt ? new Date(new Date(r3.lastRunAt).getTime() + r3.intervalMs).toISOString() : (/* @__PURE__ */ new Date()).toISOString() : null;
|
|
@@ -54961,10 +55551,14 @@ function registerAutomateCommand(program3) {
|
|
|
54961
55551
|
const nextAt = new Date(r3.lastRunAt).getTime() + r3.intervalMs;
|
|
54962
55552
|
nextPart = `next: ${formatRelativeTime(nextAt - Date.now())}`;
|
|
54963
55553
|
}
|
|
54964
|
-
|
|
55554
|
+
const statusTag = r3.active ? "" : " [paused]";
|
|
55555
|
+
console.log(
|
|
55556
|
+
`[${tag}] ${label} ${pair} every ${interval} \u2014 ${lastPart}, ${nextPart}${statusTag}`
|
|
55557
|
+
);
|
|
54965
55558
|
} else {
|
|
54966
|
-
const cond = `${r3.
|
|
54967
|
-
|
|
55559
|
+
const cond = `${r3.toToken} ${r3.condition} $${r3.targetPrice}`;
|
|
55560
|
+
const statusTag = r3.active ? "" : " [paused]";
|
|
55561
|
+
console.log(`[${tag}] ${label} ${pair} when ${cond} \u2014 waiting${statusTag}`);
|
|
54968
55562
|
}
|
|
54969
55563
|
}
|
|
54970
55564
|
} catch (err) {
|
|
@@ -55029,66 +55623,6 @@ function registerAutomateCommand(program3) {
|
|
|
55029
55623
|
});
|
|
55030
55624
|
}
|
|
55031
55625
|
|
|
55032
|
-
// src/commands/skill.ts
|
|
55033
|
-
var import_fs = require("fs");
|
|
55034
|
-
var import_path = require("path");
|
|
55035
|
-
var import_os2 = require("os");
|
|
55036
|
-
var SKILL_ID = "twak";
|
|
55037
|
-
var SKILL_NAME = "TWAK \u2014 Trust Wallet Agent Kit";
|
|
55038
|
-
function findSkillMd() {
|
|
55039
|
-
const candidates = [
|
|
55040
|
-
(0, import_path.resolve)(__dirname, "../../../../skill.md"),
|
|
55041
|
-
(0, import_path.resolve)(__dirname, "../../../skill.md"),
|
|
55042
|
-
(0, import_path.resolve)(__dirname, "../../skill.md"),
|
|
55043
|
-
(0, import_path.resolve)(process.cwd(), "skill.md")
|
|
55044
|
-
];
|
|
55045
|
-
for (const p3 of candidates) {
|
|
55046
|
-
if ((0, import_fs.existsSync)(p3)) return p3;
|
|
55047
|
-
}
|
|
55048
|
-
return null;
|
|
55049
|
-
}
|
|
55050
|
-
function registerSkillCommand(program3) {
|
|
55051
|
-
const skill = program3.command("skill").description("Manage AI skills for Claude Code and other agents");
|
|
55052
|
-
skill.command("install").description("Install the TWAK skill into your Claude Code skills directory").option("--dir <path>", "Custom skills directory (default: ~/.claude/skills/)").action(async (opts) => {
|
|
55053
|
-
const skillSrc = findSkillMd();
|
|
55054
|
-
if (!skillSrc) {
|
|
55055
|
-
error("skill.md not found. Run from the TWAK monorepo root or a published install.");
|
|
55056
|
-
process.exitCode = 1;
|
|
55057
|
-
return;
|
|
55058
|
-
}
|
|
55059
|
-
const skillsDir = opts.dir ? (0, import_path.resolve)(opts.dir) : (0, import_path.join)((0, import_os2.homedir)(), ".claude", "skills");
|
|
55060
|
-
if (opts.dir) {
|
|
55061
|
-
const { relative, sep: sep2 } = await import("path");
|
|
55062
|
-
const safeBase = (0, import_path.join)((0, import_os2.homedir)(), ".claude");
|
|
55063
|
-
const rel = relative(safeBase, skillsDir);
|
|
55064
|
-
const isOutside = rel.startsWith("..") || rel.startsWith(sep2) || (0, import_path.resolve)(opts.dir) === (0, import_path.resolve)("/");
|
|
55065
|
-
if (isOutside) {
|
|
55066
|
-
error(`Refusing to install outside ~/.claude/ (got ${skillsDir}). Use --dir ~/.claude/skills or omit --dir.`);
|
|
55067
|
-
process.exitCode = 1;
|
|
55068
|
-
return;
|
|
55069
|
-
}
|
|
55070
|
-
}
|
|
55071
|
-
(0, import_fs.mkdirSync)(skillsDir, { recursive: true });
|
|
55072
|
-
const dest = (0, import_path.join)(skillsDir, `${SKILL_ID}.md`);
|
|
55073
|
-
(0, import_fs.copyFileSync)(skillSrc, dest);
|
|
55074
|
-
success(`Skill installed to ${dest}`);
|
|
55075
|
-
info("Claude Code will load it automatically on next startup.");
|
|
55076
|
-
});
|
|
55077
|
-
skill.command("list").description("List available TWAK skills").option("--json", "JSON output").action((opts) => {
|
|
55078
|
-
const skills = [{ id: SKILL_ID, name: SKILL_NAME, description: "Wallets, swaps, transfers, market data, alerts, DCA, on/off-ramp, and MCP server." }];
|
|
55079
|
-
output(skills, opts.json);
|
|
55080
|
-
});
|
|
55081
|
-
skill.command("retrieve").description("Print the full TWAK skill instructions").action(() => {
|
|
55082
|
-
const skillSrc = findSkillMd();
|
|
55083
|
-
if (!skillSrc) {
|
|
55084
|
-
error("skill.md not found.");
|
|
55085
|
-
process.exitCode = 1;
|
|
55086
|
-
return;
|
|
55087
|
-
}
|
|
55088
|
-
console.log((0, import_fs.readFileSync)(skillSrc, "utf8"));
|
|
55089
|
-
});
|
|
55090
|
-
}
|
|
55091
|
-
|
|
55092
55626
|
// src/commands/x402.ts
|
|
55093
55627
|
init_dist2();
|
|
55094
55628
|
init_dist4();
|
|
@@ -55121,7 +55655,7 @@ async function validateX402Url(urlStr) {
|
|
|
55121
55655
|
if (parsed.protocol !== "https:") {
|
|
55122
55656
|
throw new Error(`x402: only https:// URLs are allowed (got ${parsed.protocol}//)`);
|
|
55123
55657
|
}
|
|
55124
|
-
const h4 = parsed.hostname;
|
|
55658
|
+
const h4 = parsed.hostname.replace(/^\[|\]$/g, "");
|
|
55125
55659
|
if (h4 === "localhost" || isPrivateIp(h4)) {
|
|
55126
55660
|
throw new Error(`x402: private/loopback addresses are not allowed (${h4})`);
|
|
55127
55661
|
}
|
|
@@ -55130,7 +55664,9 @@ async function validateX402Url(urlStr) {
|
|
|
55130
55664
|
const records = await dns.lookup(h4, { all: true });
|
|
55131
55665
|
for (const record2 of records) {
|
|
55132
55666
|
if (isPrivateIp(record2.address)) {
|
|
55133
|
-
throw new Error(
|
|
55667
|
+
throw new Error(
|
|
55668
|
+
`x402: "${h4}" resolves to a private/loopback address (${record2.address}) \u2014 not allowed`
|
|
55669
|
+
);
|
|
55134
55670
|
}
|
|
55135
55671
|
}
|
|
55136
55672
|
} catch (err) {
|
|
@@ -55139,7 +55675,14 @@ async function validateX402Url(urlStr) {
|
|
|
55139
55675
|
}
|
|
55140
55676
|
function registerX402Command(program3) {
|
|
55141
55677
|
const x402 = program3.command("x402").description("x402 micropayment protocol \u2014 pay HTTP endpoints that require on-chain payment");
|
|
55142
|
-
x402.command("request <url>").description("Make an HTTP request to an x402-gated endpoint, paying automatically if required").option("--method <method>", "HTTP method", "GET").option("--body <json>", "Request body (JSON string)").option(
|
|
55678
|
+
x402.command("request <url>").description("Make an HTTP request to an x402-gated endpoint, paying automatically if required").option("--method <method>", "HTTP method", "GET").option("--body <json>", "Request body (JSON string)").option(
|
|
55679
|
+
"--max-payment <amount>",
|
|
55680
|
+
"Maximum payment amount to auto-approve (default: 0.01)",
|
|
55681
|
+
"0.01"
|
|
55682
|
+
).option("--yes", "Skip confirmation prompt and auto-approve payment up to --max-payment").option(
|
|
55683
|
+
"--password <password>",
|
|
55684
|
+
"Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
|
|
55685
|
+
).option("--json", "JSON output").action(async (url, opts) => {
|
|
55143
55686
|
try {
|
|
55144
55687
|
await validateX402Url(url);
|
|
55145
55688
|
await ensureTrustSDK();
|
|
@@ -55147,7 +55690,7 @@ function registerX402Command(program3) {
|
|
|
55147
55690
|
const provider = await AgentWalletProvider.fromStorage(password);
|
|
55148
55691
|
const init3 = {
|
|
55149
55692
|
method: opts.method.toUpperCase(),
|
|
55150
|
-
headers: { "Content-Type": "application/json",
|
|
55693
|
+
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
55151
55694
|
body: opts.body || void 0
|
|
55152
55695
|
};
|
|
55153
55696
|
let resp = await fetch(url, init3);
|
|
@@ -55160,7 +55703,9 @@ function registerX402Command(program3) {
|
|
|
55160
55703
|
if (!recipient) throw new Error("x402: no recipient address in 402 response");
|
|
55161
55704
|
const maxPayment = parseFloat(opts.maxPayment);
|
|
55162
55705
|
if (!Number.isFinite(maxPayment) || maxPayment <= 0) {
|
|
55163
|
-
throw new Error(
|
|
55706
|
+
throw new Error(
|
|
55707
|
+
`x402: invalid --max-payment value "${opts.maxPayment}" \u2014 must be a positive number`
|
|
55708
|
+
);
|
|
55164
55709
|
}
|
|
55165
55710
|
const requestedAmount = parseFloat(amount);
|
|
55166
55711
|
if (isNaN(requestedAmount) || requestedAmount <= 0) {
|
|
@@ -55174,7 +55719,7 @@ function registerX402Command(program3) {
|
|
|
55174
55719
|
if (!opts.yes) {
|
|
55175
55720
|
const readline = await import("readline");
|
|
55176
55721
|
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
55177
|
-
const confirmed = await new Promise((
|
|
55722
|
+
const confirmed = await new Promise((resolve2) => {
|
|
55178
55723
|
rl.question(
|
|
55179
55724
|
`
|
|
55180
55725
|
Pay ${amount} ${asset.toUpperCase()} on ${chainId}
|
|
@@ -55182,7 +55727,7 @@ Recipient: ${recipient}
|
|
|
55182
55727
|
Confirm? [y/N] `,
|
|
55183
55728
|
(answer) => {
|
|
55184
55729
|
rl.close();
|
|
55185
|
-
|
|
55730
|
+
resolve2(answer.toLowerCase() === "y");
|
|
55186
55731
|
}
|
|
55187
55732
|
);
|
|
55188
55733
|
});
|
|
@@ -55210,7 +55755,9 @@ Confirm? [y/N] `,
|
|
|
55210
55755
|
}
|
|
55211
55756
|
const txResult = await bc2.transfer({ assetId, to: recipient, amount, hdWallet });
|
|
55212
55757
|
info(`Payment sent: ${txResult.hash}`);
|
|
55213
|
-
const paymentProof = Buffer.from(
|
|
55758
|
+
const paymentProof = Buffer.from(
|
|
55759
|
+
JSON.stringify({ txHash: txResult.hash, chainId })
|
|
55760
|
+
).toString("base64");
|
|
55214
55761
|
resp = await fetch(url, {
|
|
55215
55762
|
...init3,
|
|
55216
55763
|
headers: { ...init3.headers, "X-Payment": paymentProof }
|
|
@@ -55251,7 +55798,7 @@ var crypto6 = __toESM(require("crypto"));
|
|
|
55251
55798
|
// package.json
|
|
55252
55799
|
var package_default = {
|
|
55253
55800
|
name: "@trustwallet/cli",
|
|
55254
|
-
version: "0.
|
|
55801
|
+
version: "0.9.0",
|
|
55255
55802
|
description: "TWAK CLI \u2014 command-line interface for Trust Wallet Agent Kit",
|
|
55256
55803
|
main: "dist/index.js",
|
|
55257
55804
|
types: "dist/index.d.ts",
|
|
@@ -55267,16 +55814,17 @@ var package_default = {
|
|
|
55267
55814
|
prebuild: "rm -rf trust-sdk && cp -r ../../vendor/trust-sdk trust-sdk",
|
|
55268
55815
|
build: "tsup && tsc --emitDeclarationOnly --outDir dist",
|
|
55269
55816
|
typecheck: "tsc --noEmit",
|
|
55817
|
+
test: "vitest run",
|
|
55270
55818
|
lint: "eslint src/",
|
|
55271
55819
|
clean: "rm -rf dist"
|
|
55272
55820
|
},
|
|
55273
55821
|
dependencies: {
|
|
55274
55822
|
"node-fetch": "2.7.0",
|
|
55275
|
-
"qrcode-terminal": "0.12.0"
|
|
55823
|
+
"qrcode-terminal": "0.12.0",
|
|
55824
|
+
"@napi-rs/keyring": "1.2.0"
|
|
55276
55825
|
},
|
|
55277
55826
|
optionalDependencies: {
|
|
55278
|
-
"@amplitude/analytics-node": "1.5.39"
|
|
55279
|
-
keytar: "7.9.0"
|
|
55827
|
+
"@amplitude/analytics-node": "1.5.39"
|
|
55280
55828
|
},
|
|
55281
55829
|
devDependencies: {
|
|
55282
55830
|
boxen: "5.1.2",
|
|
@@ -55295,7 +55843,8 @@ var package_default = {
|
|
|
55295
55843
|
"@twak/wallet-core": "workspace:*",
|
|
55296
55844
|
"@twak/walletconnect": "workspace:*",
|
|
55297
55845
|
tsup: "8.5.1",
|
|
55298
|
-
typescript: "5.7.3"
|
|
55846
|
+
typescript: "5.7.3",
|
|
55847
|
+
vitest: "4.1.2"
|
|
55299
55848
|
},
|
|
55300
55849
|
publishConfig: {
|
|
55301
55850
|
access: "public"
|
|
@@ -55426,7 +55975,13 @@ function parseInterval2(value) {
|
|
|
55426
55975
|
return num;
|
|
55427
55976
|
}
|
|
55428
55977
|
function registerWatchCommand(program3) {
|
|
55429
|
-
program3.command("watch").description(
|
|
55978
|
+
program3.command("watch").description(
|
|
55979
|
+
"Background watcher \u2014 polls prices to trigger alerts and execute DCA/limit automations"
|
|
55980
|
+
).option(
|
|
55981
|
+
"--interval <duration>",
|
|
55982
|
+
"Poll interval \u2014 e.g. 30s, 5m, 1h, or plain number (seconds)",
|
|
55983
|
+
"60"
|
|
55984
|
+
).option("--password <password>", "Wallet password for executing swaps").option("--auto-lock <minutes>", "Auto-lock wallet after N minutes of inactivity").option("--dry-run", "Check conditions without executing swaps").option("--json", "JSON output per tick").action(async (opts) => {
|
|
55430
55985
|
const intervalSec = parseInterval2(opts.interval);
|
|
55431
55986
|
if (intervalSec === null || intervalSec < 5) {
|
|
55432
55987
|
error("Interval must be at least 5 seconds (e.g. 30s, 5m, 1h)");
|
|
@@ -55472,7 +56027,20 @@ function registerWatchCommand(program3) {
|
|
|
55472
56027
|
}
|
|
55473
56028
|
const alertStore = new AlertStore();
|
|
55474
56029
|
const automationStore = new AutomationStore();
|
|
55475
|
-
|
|
56030
|
+
const dryRunState = /* @__PURE__ */ new Map();
|
|
56031
|
+
const getMemState = (id, stored) => {
|
|
56032
|
+
if (!dryRunState.has(id)) {
|
|
56033
|
+
dryRunState.set(id, {
|
|
56034
|
+
lastRunAt: stored.lastRunAt ?? null,
|
|
56035
|
+
runCount: stored.runCount ?? 0,
|
|
56036
|
+
active: true
|
|
56037
|
+
});
|
|
56038
|
+
}
|
|
56039
|
+
return dryRunState.get(id);
|
|
56040
|
+
};
|
|
56041
|
+
info(
|
|
56042
|
+
`[${ts3()}] Watcher started \u2014 polling every ${intervalSec}s${opts.dryRun ? " (dry run)" : ""}`
|
|
56043
|
+
);
|
|
55476
56044
|
const tick = async () => {
|
|
55477
56045
|
const tickResults = {
|
|
55478
56046
|
alerts: { checked: 0, triggered: [] },
|
|
@@ -55488,44 +56056,81 @@ function registerWatchCommand(program3) {
|
|
|
55488
56056
|
if (price === null) continue;
|
|
55489
56057
|
const triggered = alert.condition === "above" && price >= alert.targetPrice || alert.condition === "below" && price <= alert.targetPrice;
|
|
55490
56058
|
if (triggered) {
|
|
55491
|
-
|
|
55492
|
-
|
|
56059
|
+
if (!opts.dryRun)
|
|
56060
|
+
await alertStore.update(alert.id, {
|
|
56061
|
+
active: false,
|
|
56062
|
+
triggeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
56063
|
+
});
|
|
56064
|
+
const detail = {
|
|
56065
|
+
id: alert.id,
|
|
56066
|
+
tokenId: alert.tokenId,
|
|
56067
|
+
condition: alert.condition,
|
|
56068
|
+
targetPrice: alert.targetPrice,
|
|
56069
|
+
currentPrice: price
|
|
56070
|
+
};
|
|
55493
56071
|
tickResults.alerts.triggered.push(detail);
|
|
55494
|
-
success(
|
|
56072
|
+
success(
|
|
56073
|
+
`[${ts3()}] ${opts.dryRun ? "DRY RUN \u2014 would trigger alert: " : "ALERT TRIGGERED: "}${alert.tokenId} price $${price} is ${alert.condition} $${alert.targetPrice}`
|
|
56074
|
+
);
|
|
55495
56075
|
}
|
|
55496
56076
|
} catch (err) {
|
|
55497
|
-
warn(
|
|
56077
|
+
warn(
|
|
56078
|
+
`[${ts3()}] Alert ${alert.id} check failed: ${err instanceof Error ? err.message : String(err)}`
|
|
56079
|
+
);
|
|
55498
56080
|
}
|
|
55499
56081
|
}
|
|
55500
56082
|
const automations = await automationStore.getActive();
|
|
55501
56083
|
const now = Date.now();
|
|
55502
56084
|
for (const auto of automations) {
|
|
55503
56085
|
try {
|
|
56086
|
+
const memState = opts.dryRun ? getMemState(auto.id, auto) : null;
|
|
56087
|
+
if (memState && !memState.active) {
|
|
56088
|
+
tickResults.automations.skipped++;
|
|
56089
|
+
continue;
|
|
56090
|
+
}
|
|
55504
56091
|
if (auto.expiresAt && new Date(auto.expiresAt).getTime() <= now) {
|
|
55505
|
-
await automationStore.update(auto.id, { active: false });
|
|
56092
|
+
if (!opts.dryRun) await automationStore.update(auto.id, { active: false });
|
|
56093
|
+
else if (memState) memState.active = false;
|
|
55506
56094
|
dim(`[${ts3()}] Automation ${auto.id.slice(0, 8)} expired`);
|
|
55507
56095
|
tickResults.automations.skipped++;
|
|
55508
56096
|
continue;
|
|
55509
56097
|
}
|
|
55510
|
-
|
|
55511
|
-
|
|
55512
|
-
|
|
56098
|
+
const effectiveRunCount = memState ? memState.runCount : auto.runCount ?? 0;
|
|
56099
|
+
if (auto.maxRuns && effectiveRunCount >= auto.maxRuns) {
|
|
56100
|
+
if (!opts.dryRun) await automationStore.update(auto.id, { active: false });
|
|
56101
|
+
dim(
|
|
56102
|
+
`[${ts3()}] Automation ${auto.id.slice(0, 8)} reached max runs (${auto.maxRuns})`
|
|
56103
|
+
);
|
|
56104
|
+
tickResults.automations.skipped++;
|
|
56105
|
+
continue;
|
|
56106
|
+
}
|
|
56107
|
+
if (!getChainById(auto.chain)) {
|
|
56108
|
+
warn(
|
|
56109
|
+
`[${ts3()}] Automation ${auto.id.slice(0, 8)} skipped \u2014 unknown chain "${auto.chain}"`
|
|
56110
|
+
);
|
|
55513
56111
|
tickResults.automations.skipped++;
|
|
55514
56112
|
continue;
|
|
55515
56113
|
}
|
|
55516
56114
|
if (auto.type === "dca") {
|
|
55517
|
-
const
|
|
56115
|
+
const effectiveLastRunAt = memState ? memState.lastRunAt : auto.lastRunAt;
|
|
56116
|
+
const lastRun = effectiveLastRunAt ? new Date(effectiveLastRunAt).getTime() : 0;
|
|
55518
56117
|
const intervalMs = auto.intervalMs || 0;
|
|
55519
56118
|
if (lastRun + intervalMs > now) {
|
|
55520
56119
|
tickResults.automations.skipped++;
|
|
55521
56120
|
continue;
|
|
55522
56121
|
}
|
|
55523
|
-
if (opts.dryRun) {
|
|
55524
|
-
|
|
56122
|
+
if (opts.dryRun && memState) {
|
|
56123
|
+
memState.lastRunAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
56124
|
+
memState.runCount++;
|
|
56125
|
+
dim(
|
|
56126
|
+
`[${ts3()}] DRY RUN: would swap ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`
|
|
56127
|
+
);
|
|
55525
56128
|
tickResults.automations.executed.push({ id: auto.id, type: "dca", dryRun: true });
|
|
55526
56129
|
continue;
|
|
55527
56130
|
}
|
|
55528
|
-
info(
|
|
56131
|
+
info(
|
|
56132
|
+
`[${ts3()}] Executing DCA: ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`
|
|
56133
|
+
);
|
|
55529
56134
|
const result = await executor.execute({
|
|
55530
56135
|
fromChain: auto.chain,
|
|
55531
56136
|
fromToken: auto.fromToken,
|
|
@@ -55534,27 +56139,58 @@ function registerWatchCommand(program3) {
|
|
|
55534
56139
|
amount: auto.amount
|
|
55535
56140
|
});
|
|
55536
56141
|
if (result.success) {
|
|
55537
|
-
await automationStore.update(auto.id, {
|
|
56142
|
+
await automationStore.update(auto.id, {
|
|
56143
|
+
lastRunAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
56144
|
+
runCount: (auto.runCount ?? 0) + 1
|
|
56145
|
+
});
|
|
55538
56146
|
success(`[${ts3()}] DCA executed: ${result.explorerUrl}`);
|
|
55539
|
-
tickResults.automations.executed.push({
|
|
56147
|
+
tickResults.automations.executed.push({
|
|
56148
|
+
id: auto.id,
|
|
56149
|
+
type: "dca",
|
|
56150
|
+
txHash: result.txHash,
|
|
56151
|
+
explorer: result.explorerUrl
|
|
56152
|
+
});
|
|
55540
56153
|
} else {
|
|
55541
56154
|
warn(`[${ts3()}] DCA ${auto.id} failed: ${result.error}`);
|
|
55542
56155
|
}
|
|
55543
56156
|
} else if (auto.type === "limit") {
|
|
55544
56157
|
const toTokenInfo = resolveToken(auto.toToken, auto.chain);
|
|
56158
|
+
if (!toTokenInfo) {
|
|
56159
|
+
warn(`[${ts3()}] Automation ${auto.id.slice(0, 8)} skipped \u2014 unknown token "${auto.toToken}" on ${auto.chain}`);
|
|
56160
|
+
continue;
|
|
56161
|
+
}
|
|
55545
56162
|
const price = await swapClient.getTokenPrice(auto.chain, toTokenInfo.address);
|
|
55546
|
-
if (price === null)
|
|
56163
|
+
if (price === null) {
|
|
56164
|
+
if (opts.dryRun)
|
|
56165
|
+
dim(
|
|
56166
|
+
`[${ts3()}] DRY RUN: skipping limit order ${auto.id.slice(0, 8)} \u2014 price unavailable for ${auto.toToken}`
|
|
56167
|
+
);
|
|
56168
|
+
continue;
|
|
56169
|
+
}
|
|
55547
56170
|
const triggered = auto.condition === "below" && price <= (auto.targetPrice || 0) || auto.condition === "above" && price >= (auto.targetPrice || 0);
|
|
55548
56171
|
if (!triggered) {
|
|
56172
|
+
if (opts.dryRun)
|
|
56173
|
+
dim(
|
|
56174
|
+
`[${ts3()}] DRY RUN: limit order ${auto.id.slice(0, 8)} waiting \u2014 ${auto.toToken} $${price} vs target $${auto.targetPrice} (${auto.condition})`
|
|
56175
|
+
);
|
|
55549
56176
|
tickResults.automations.skipped++;
|
|
55550
56177
|
continue;
|
|
55551
56178
|
}
|
|
55552
|
-
if (opts.dryRun) {
|
|
55553
|
-
|
|
55554
|
-
|
|
56179
|
+
if (opts.dryRun && memState) {
|
|
56180
|
+
memState.active = false;
|
|
56181
|
+
dim(
|
|
56182
|
+
`[${ts3()}] DRY RUN: limit order triggered \u2014 would swap ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`
|
|
56183
|
+
);
|
|
56184
|
+
tickResults.automations.executed.push({
|
|
56185
|
+
id: auto.id,
|
|
56186
|
+
type: "limit",
|
|
56187
|
+
dryRun: true
|
|
56188
|
+
});
|
|
55555
56189
|
continue;
|
|
55556
56190
|
}
|
|
55557
|
-
info(
|
|
56191
|
+
info(
|
|
56192
|
+
`[${ts3()}] Limit order triggered: swapping ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`
|
|
56193
|
+
);
|
|
55558
56194
|
const result = await executor.execute({
|
|
55559
56195
|
fromChain: auto.chain,
|
|
55560
56196
|
fromToken: auto.fromToken,
|
|
@@ -55563,15 +56199,25 @@ function registerWatchCommand(program3) {
|
|
|
55563
56199
|
amount: auto.amount
|
|
55564
56200
|
});
|
|
55565
56201
|
if (result.success) {
|
|
55566
|
-
await automationStore.update(auto.id, {
|
|
56202
|
+
await automationStore.update(auto.id, {
|
|
56203
|
+
active: false,
|
|
56204
|
+
lastRunAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
56205
|
+
});
|
|
55567
56206
|
success(`[${ts3()}] Limit order executed: ${result.explorerUrl}`);
|
|
55568
|
-
tickResults.automations.executed.push({
|
|
56207
|
+
tickResults.automations.executed.push({
|
|
56208
|
+
id: auto.id,
|
|
56209
|
+
type: "limit",
|
|
56210
|
+
txHash: result.txHash,
|
|
56211
|
+
explorer: result.explorerUrl
|
|
56212
|
+
});
|
|
55569
56213
|
} else {
|
|
55570
56214
|
warn(`[${ts3()}] Limit order ${auto.id} failed: ${result.error}`);
|
|
55571
56215
|
}
|
|
55572
56216
|
}
|
|
55573
56217
|
} catch (err) {
|
|
55574
|
-
warn(
|
|
56218
|
+
warn(
|
|
56219
|
+
`[${ts3()}] Automation ${auto.id} failed: ${err instanceof Error ? err.message : String(err)}`
|
|
56220
|
+
);
|
|
55575
56221
|
}
|
|
55576
56222
|
}
|
|
55577
56223
|
} catch (err) {
|
|
@@ -55580,7 +56226,9 @@ function registerWatchCommand(program3) {
|
|
|
55580
56226
|
if (opts.json) {
|
|
55581
56227
|
output({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), ...tickResults }, true);
|
|
55582
56228
|
} else if (tickResults.alerts.triggered.length === 0 && tickResults.automations.executed.length === 0) {
|
|
55583
|
-
dim(
|
|
56229
|
+
dim(
|
|
56230
|
+
`[${ts3()}] Checked ${tickResults.alerts.checked} alerts, ${tickResults.automations.skipped} automations \u2014 nothing to do`
|
|
56231
|
+
);
|
|
55584
56232
|
}
|
|
55585
56233
|
};
|
|
55586
56234
|
await tick();
|
|
@@ -55619,7 +56267,6 @@ registerAlertCommand(program2);
|
|
|
55619
56267
|
registerQueryCommand(program2);
|
|
55620
56268
|
registerServeCommand(program2);
|
|
55621
56269
|
registerAutomateCommand(program2);
|
|
55622
|
-
registerSkillCommand(program2);
|
|
55623
56270
|
registerX402Command(program2);
|
|
55624
56271
|
registerTelemetryCommand(program2);
|
|
55625
56272
|
registerWatchCommand(program2);
|