@trustwallet/cli 0.7.0 → 0.8.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 +1207 -656
- 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,19 @@ 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((s2) => s2.writableNeedDrain);
|
|
22442
|
+
if (streams.length === 0) {
|
|
22443
|
+
process.exit(code2);
|
|
22444
|
+
return;
|
|
22445
|
+
}
|
|
22446
|
+
let pending = streams.length;
|
|
22447
|
+
const onDrain = () => {
|
|
22448
|
+
if (--pending === 0) process.exit(code2);
|
|
22449
|
+
};
|
|
22450
|
+
for (const s2 of streams) s2.once("drain", onDrain);
|
|
22451
|
+
}
|
|
22420
22452
|
|
|
22421
22453
|
// src/config-loader.ts
|
|
22422
22454
|
var import_chalk2 = __toESM(require_source());
|
|
@@ -22426,7 +22458,8 @@ init_dist();
|
|
|
22426
22458
|
var EMBEDDED_NAAS_SESSION_ID = "NDVjMWRjY2MtYTYwNy00NGNhLWIxZGUtMmJiZDIyNmEyODQ4";
|
|
22427
22459
|
function resolveNaasSessionId() {
|
|
22428
22460
|
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("__"))
|
|
22461
|
+
if (EMBEDDED_NAAS_SESSION_ID && !EMBEDDED_NAAS_SESSION_ID.startsWith("__"))
|
|
22462
|
+
return EMBEDDED_NAAS_SESSION_ID;
|
|
22430
22463
|
return void 0;
|
|
22431
22464
|
}
|
|
22432
22465
|
async function getCredentials() {
|
|
@@ -22441,13 +22474,29 @@ async function getCredentials() {
|
|
|
22441
22474
|
return { accessId: credentials.accessId, hmacSecret: credentials.hmacSecret };
|
|
22442
22475
|
}
|
|
22443
22476
|
console.error(import_chalk2.default.red("No API credentials found."));
|
|
22444
|
-
console.error(
|
|
22477
|
+
console.error(
|
|
22478
|
+
import_chalk2.default.dim(
|
|
22479
|
+
"Set TWAK_ACCESS_ID and TWAK_HMAC_SECRET environment variables, then run `twak init`"
|
|
22480
|
+
)
|
|
22481
|
+
);
|
|
22445
22482
|
process.exit(1);
|
|
22446
22483
|
}
|
|
22447
22484
|
async function getClient() {
|
|
22448
22485
|
const creds = await getCredentials();
|
|
22449
22486
|
return createTwakClient({ ...creds });
|
|
22450
22487
|
}
|
|
22488
|
+
async function tryGetClient() {
|
|
22489
|
+
const envAccessId = process.env.TWAK_ACCESS_ID;
|
|
22490
|
+
const envSecret = process.env.TWAK_HMAC_SECRET;
|
|
22491
|
+
if (envAccessId && envSecret) {
|
|
22492
|
+
return createTwakClient({ accessId: envAccessId, hmacSecret: envSecret });
|
|
22493
|
+
}
|
|
22494
|
+
const credentials = await new CredentialManager().load();
|
|
22495
|
+
if (credentials) {
|
|
22496
|
+
return createTwakClient({ accessId: credentials.accessId, hmacSecret: credentials.hmacSecret });
|
|
22497
|
+
}
|
|
22498
|
+
return void 0;
|
|
22499
|
+
}
|
|
22451
22500
|
async function ensureTrustSDK() {
|
|
22452
22501
|
const creds = await getCredentials();
|
|
22453
22502
|
await initTrustSDK({
|
|
@@ -22475,7 +22524,9 @@ async function registerWalletWithBackend(creds, provider) {
|
|
|
22475
22524
|
async function resolvePassword(opts) {
|
|
22476
22525
|
if (opts?.password) {
|
|
22477
22526
|
process.stderr.write(
|
|
22478
|
-
import_chalk2.default.yellow(
|
|
22527
|
+
import_chalk2.default.yellow(
|
|
22528
|
+
"Note: --password is visible in shell history. Use `twak wallet keychain save` or TWAK_WALLET_PASSWORD instead.\n"
|
|
22529
|
+
)
|
|
22479
22530
|
);
|
|
22480
22531
|
return opts.password;
|
|
22481
22532
|
}
|
|
@@ -23155,8 +23206,8 @@ async function dispose(driver) {
|
|
|
23155
23206
|
|
|
23156
23207
|
// ../../node_modules/.pnpm/idb-keyval@6.2.2/node_modules/idb-keyval/dist/index.js
|
|
23157
23208
|
function promisifyRequest(request) {
|
|
23158
|
-
return new Promise((
|
|
23159
|
-
request.oncomplete = request.onsuccess = () =>
|
|
23209
|
+
return new Promise((resolve2, reject) => {
|
|
23210
|
+
request.oncomplete = request.onsuccess = () => resolve2(request.result);
|
|
23160
23211
|
request.onabort = request.onerror = () => reject(request.error);
|
|
23161
23212
|
});
|
|
23162
23213
|
}
|
|
@@ -38798,7 +38849,10 @@ function registerWalletCommand(program3) {
|
|
|
38798
38849
|
}
|
|
38799
38850
|
process.exit();
|
|
38800
38851
|
});
|
|
38801
|
-
wallet.command("address").description("Get wallet address for a chain").requiredOption("--chain <key>", "Chain key (e.g., ethereum, solana)").option(
|
|
38852
|
+
wallet.command("address").description("Get wallet address for a chain").requiredOption("--chain <key>", "Chain key (e.g., ethereum, solana)").option(
|
|
38853
|
+
"--password <password>",
|
|
38854
|
+
"Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
|
|
38855
|
+
).option("--json", "JSON output").action(async (opts) => {
|
|
38802
38856
|
try {
|
|
38803
38857
|
await ensureTrustSDK();
|
|
38804
38858
|
const password = await resolvePassword(opts);
|
|
@@ -38822,7 +38876,10 @@ ${qr3}`;
|
|
|
38822
38876
|
}
|
|
38823
38877
|
process.exit();
|
|
38824
38878
|
});
|
|
38825
|
-
wallet.command("addresses").description("List all wallet addresses").option(
|
|
38879
|
+
wallet.command("addresses").description("List all wallet addresses").option(
|
|
38880
|
+
"--password <password>",
|
|
38881
|
+
"Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
|
|
38882
|
+
).option("--json", "JSON output").action(async (opts) => {
|
|
38826
38883
|
try {
|
|
38827
38884
|
await ensureTrustSDK();
|
|
38828
38885
|
const password = await resolvePassword(opts);
|
|
@@ -38870,7 +38927,10 @@ ${qr3}`;
|
|
|
38870
38927
|
}
|
|
38871
38928
|
process.exit();
|
|
38872
38929
|
});
|
|
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(
|
|
38930
|
+
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(
|
|
38931
|
+
"--password <password>",
|
|
38932
|
+
"Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
|
|
38933
|
+
).option("--no-tokens", "Skip token balance lookup").option("--json", "JSON output").action(async (opts) => {
|
|
38874
38934
|
try {
|
|
38875
38935
|
if (opts.all && opts.chain) {
|
|
38876
38936
|
const msg = "--all and --chain are mutually exclusive";
|
|
@@ -38912,11 +38972,14 @@ ${qr3}`;
|
|
|
38912
38972
|
const holdings = await securityService.getTokenHoldings(assets);
|
|
38913
38973
|
for (const h4 of holdings) {
|
|
38914
38974
|
if (h4.tokens?.length) {
|
|
38915
|
-
holdingsMap.set(
|
|
38916
|
-
|
|
38917
|
-
|
|
38918
|
-
|
|
38919
|
-
|
|
38975
|
+
holdingsMap.set(
|
|
38976
|
+
h4.coin,
|
|
38977
|
+
h4.tokens.map((t) => ({
|
|
38978
|
+
symbol: t.symbol ?? t.contract,
|
|
38979
|
+
contract: t.contract,
|
|
38980
|
+
decimals: t.decimals ?? 18
|
|
38981
|
+
}))
|
|
38982
|
+
);
|
|
38920
38983
|
}
|
|
38921
38984
|
}
|
|
38922
38985
|
} catch {
|
|
@@ -38935,11 +38998,14 @@ ${qr3}`;
|
|
|
38935
38998
|
const retryHoldings = await retrySec.getTokenHoldings(retryAssets);
|
|
38936
38999
|
for (const h4 of retryHoldings) {
|
|
38937
39000
|
if (h4.tokens?.length) {
|
|
38938
|
-
holdingsMap.set(
|
|
38939
|
-
|
|
38940
|
-
|
|
38941
|
-
|
|
38942
|
-
|
|
39001
|
+
holdingsMap.set(
|
|
39002
|
+
h4.coin,
|
|
39003
|
+
h4.tokens.map((t) => ({
|
|
39004
|
+
symbol: t.symbol ?? t.contract,
|
|
39005
|
+
contract: t.contract,
|
|
39006
|
+
decimals: t.decimals ?? 18
|
|
39007
|
+
}))
|
|
39008
|
+
);
|
|
38943
39009
|
}
|
|
38944
39010
|
}
|
|
38945
39011
|
} catch {
|
|
@@ -38960,6 +39026,7 @@ ${qr3}`;
|
|
|
38960
39026
|
if (knownTokens) {
|
|
38961
39027
|
for (const [sym, addr] of Object.entries(knownTokens)) {
|
|
38962
39028
|
const resolved = resolveToken(sym, chainKey);
|
|
39029
|
+
if (!resolved) continue;
|
|
38963
39030
|
tokenDefs.push({ symbol: sym, contract: addr, decimals: resolved.decimals });
|
|
38964
39031
|
}
|
|
38965
39032
|
}
|
|
@@ -38967,11 +39034,15 @@ ${qr3}`;
|
|
|
38967
39034
|
const tokenResults = await Promise.all(
|
|
38968
39035
|
tokenDefs.map(async (t) => {
|
|
38969
39036
|
try {
|
|
38970
|
-
const bal = await blockchainClient.getBalance(
|
|
39037
|
+
const bal = await blockchainClient.getBalance(
|
|
39038
|
+
`c${chain2.coinId}_t${t.contract}`,
|
|
39039
|
+
address
|
|
39040
|
+
);
|
|
38971
39041
|
const rawBal = bal.total ?? bal.available ?? "0";
|
|
38972
39042
|
if (BigInt(rawBal) <= 0n) return null;
|
|
38973
39043
|
const human = fmtRaw(rawBal, t.decimals);
|
|
38974
|
-
if (human && human !== "0.000000")
|
|
39044
|
+
if (human && human !== "0.000000")
|
|
39045
|
+
return { symbol: t.symbol, contract: t.contract, balance: human };
|
|
38975
39046
|
} catch {
|
|
38976
39047
|
}
|
|
38977
39048
|
return null;
|
|
@@ -39000,7 +39071,9 @@ ${qr3}`;
|
|
|
39000
39071
|
addressMap.set(a3.chainId, a3.address);
|
|
39001
39072
|
}
|
|
39002
39073
|
const results = [];
|
|
39003
|
-
const chainEntries = chainKeys.map((k4) => ({ chainKey: k4, chain: getChainById2(k4), address: addressMap.get(k4) })).filter(
|
|
39074
|
+
const chainEntries = chainKeys.map((k4) => ({ chainKey: k4, chain: getChainById2(k4), address: addressMap.get(k4) })).filter(
|
|
39075
|
+
(e2) => !!e2.chain && !!e2.address
|
|
39076
|
+
);
|
|
39004
39077
|
const settled = await Promise.allSettled(
|
|
39005
39078
|
chainEntries.map(
|
|
39006
39079
|
async ({ chainKey, chain: chain2, address }) => fetchChainBalance(chainKey, chain2, address, opts.tokens === false)
|
|
@@ -39010,7 +39083,10 @@ ${qr3}`;
|
|
|
39010
39083
|
const result = settled[i2];
|
|
39011
39084
|
if (result.status === "rejected") {
|
|
39012
39085
|
const chainKey = chainEntries[i2].chainKey;
|
|
39013
|
-
results.push({
|
|
39086
|
+
results.push({
|
|
39087
|
+
chain: chainKey,
|
|
39088
|
+
error: result.reason instanceof Error ? result.reason.message : String(result.reason)
|
|
39089
|
+
});
|
|
39014
39090
|
continue;
|
|
39015
39091
|
}
|
|
39016
39092
|
const r3 = result.value;
|
|
@@ -39057,12 +39133,7 @@ ${qr3}`;
|
|
|
39057
39133
|
process.exit();
|
|
39058
39134
|
return;
|
|
39059
39135
|
}
|
|
39060
|
-
const result = await fetchChainBalance(
|
|
39061
|
-
opts.chain,
|
|
39062
|
-
chain2,
|
|
39063
|
-
address,
|
|
39064
|
-
opts.tokens === false
|
|
39065
|
-
);
|
|
39136
|
+
const result = await fetchChainBalance(opts.chain, chain2, address, opts.tokens === false);
|
|
39066
39137
|
if (opts.json) {
|
|
39067
39138
|
output(result, true);
|
|
39068
39139
|
} else {
|
|
@@ -39125,10 +39196,16 @@ ${qr3}`;
|
|
|
39125
39196
|
}
|
|
39126
39197
|
process.exit();
|
|
39127
39198
|
});
|
|
39128
|
-
wallet.command("connect").description("Connect external wallet via WalletConnect").option("--project-id <id>", "WalletConnect project ID (or set WALLETCONNECT_PROJECT_ID)").option(
|
|
39199
|
+
wallet.command("connect").description("Connect external wallet via WalletConnect").option("--project-id <id>", "WalletConnect project ID (or set WALLETCONNECT_PROJECT_ID)").option(
|
|
39200
|
+
"--chains <list>",
|
|
39201
|
+
"Comma-separated CAIP-2 chain IDs",
|
|
39202
|
+
"eip155:1,eip155:56,eip155:137,eip155:8453,eip155:42161"
|
|
39203
|
+
).option("--timeout <seconds>", "Connection timeout in seconds", "300").option("--json", "JSON output").action(async (opts) => {
|
|
39129
39204
|
const projectId = opts.projectId || process.env.WALLETCONNECT_PROJECT_ID;
|
|
39130
39205
|
if (!projectId) {
|
|
39131
|
-
error(
|
|
39206
|
+
error(
|
|
39207
|
+
"WalletConnect project ID required. Use --project-id or set WALLETCONNECT_PROJECT_ID"
|
|
39208
|
+
);
|
|
39132
39209
|
process.exit(1);
|
|
39133
39210
|
}
|
|
39134
39211
|
const provider = new WalletConnectProvider({ projectId });
|
|
@@ -39145,7 +39222,15 @@ ${qr3}`;
|
|
|
39145
39222
|
const timeoutMs = parseInt(opts.timeout, 10) * 1e3;
|
|
39146
39223
|
const session = await provider.waitForSession(timeoutMs);
|
|
39147
39224
|
if (opts.json) {
|
|
39148
|
-
output(
|
|
39225
|
+
output(
|
|
39226
|
+
{
|
|
39227
|
+
connected: true,
|
|
39228
|
+
address: session.address,
|
|
39229
|
+
chainId: session.chainId,
|
|
39230
|
+
peer: session.peerName
|
|
39231
|
+
},
|
|
39232
|
+
true
|
|
39233
|
+
);
|
|
39149
39234
|
} else {
|
|
39150
39235
|
success(`Connected to ${session.peerName || "wallet"}`);
|
|
39151
39236
|
info(` Address: ${session.address}`);
|
|
@@ -39177,17 +39262,28 @@ ${qr3}`;
|
|
|
39177
39262
|
if (opts.json) {
|
|
39178
39263
|
output(result, true);
|
|
39179
39264
|
} else {
|
|
39180
|
-
console.log(
|
|
39265
|
+
console.log(
|
|
39266
|
+
` ${import_chalk5.default.dim("Wallet")} ${exists ? import_chalk5.default.green("configured") : import_chalk5.default.yellow("not configured")}`
|
|
39267
|
+
);
|
|
39181
39268
|
if (createdAt) console.log(` ${import_chalk5.default.dim("Created")} ${createdAt}`);
|
|
39182
|
-
console.log(
|
|
39269
|
+
console.log(
|
|
39270
|
+
` ${import_chalk5.default.dim("Keychain")} ${keychainHasPassword ? import_chalk5.default.green("password stored") : import_chalk5.default.yellow("no password")}`
|
|
39271
|
+
);
|
|
39183
39272
|
console.log(` ${import_chalk5.default.dim("Chains")} ${totalChains} supported`);
|
|
39184
39273
|
if (addressCount !== void 0) console.log(` ${import_chalk5.default.dim("Addresses")} ${addressCount}`);
|
|
39185
39274
|
if (!exists) info("\nRun `twak wallet create --password <pw>` to create a wallet.");
|
|
39186
39275
|
}
|
|
39187
39276
|
});
|
|
39188
|
-
wallet.command("portfolio").description("Show portfolio balances and USD values across chains, including token holdings").option(
|
|
39277
|
+
wallet.command("portfolio").description("Show portfolio balances and USD values across chains, including token holdings").option(
|
|
39278
|
+
"--chains <list>",
|
|
39279
|
+
"Comma-separated chain keys (default: all supported chains)",
|
|
39280
|
+
Object.keys(CHAINS).join(",")
|
|
39281
|
+
).option(
|
|
39282
|
+
"--password <password>",
|
|
39283
|
+
"Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
|
|
39284
|
+
).option("--json", "JSON output").action(async (opts) => {
|
|
39189
39285
|
try {
|
|
39190
|
-
await ensureTrustSDK();
|
|
39286
|
+
const creds = await ensureTrustSDK();
|
|
39191
39287
|
const password = await resolvePassword(opts);
|
|
39192
39288
|
const provider = await AgentWalletProvider.fromStorage(password);
|
|
39193
39289
|
const { getChainById: getChainById2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
|
|
@@ -39203,6 +39299,15 @@ ${qr3}`;
|
|
|
39203
39299
|
return [{ chainKey, chain: chain2, address: addrEntry.address }];
|
|
39204
39300
|
});
|
|
39205
39301
|
const suppress = (fn2) => fn2();
|
|
39302
|
+
const withTimeout = (promise, ms3) => {
|
|
39303
|
+
let timerId;
|
|
39304
|
+
const timeout = new Promise((resolve2) => {
|
|
39305
|
+
timerId = setTimeout(() => resolve2(null), ms3);
|
|
39306
|
+
});
|
|
39307
|
+
return Promise.race([promise, timeout]).finally(() => {
|
|
39308
|
+
if (timerId !== void 0) clearTimeout(timerId);
|
|
39309
|
+
});
|
|
39310
|
+
};
|
|
39206
39311
|
const pMap = async (items, fn2, concurrency) => {
|
|
39207
39312
|
const results = new Array(items.length);
|
|
39208
39313
|
let idx = 0;
|
|
@@ -39212,7 +39317,9 @@ ${qr3}`;
|
|
|
39212
39317
|
results[i2] = await fn2(items[i2]);
|
|
39213
39318
|
}
|
|
39214
39319
|
};
|
|
39215
|
-
await Promise.all(
|
|
39320
|
+
await Promise.all(
|
|
39321
|
+
Array.from({ length: Math.min(concurrency, items.length) }, () => run())
|
|
39322
|
+
);
|
|
39216
39323
|
return results;
|
|
39217
39324
|
};
|
|
39218
39325
|
const toHuman = (raw, decimals) => {
|
|
@@ -39223,16 +39330,32 @@ ${qr3}`;
|
|
|
39223
39330
|
const fracStr = fracPart.toString().padStart(decimals, "0").replace(/0+$/, "");
|
|
39224
39331
|
return fracStr ? `${intPart}.${fracStr}` : intPart.toString();
|
|
39225
39332
|
};
|
|
39226
|
-
const holdingsAssets = chainEntries.map(
|
|
39333
|
+
const holdingsAssets = chainEntries.map(
|
|
39334
|
+
({ chain: chain2, address }) => ({
|
|
39335
|
+
coin: chain2.coinId,
|
|
39336
|
+
address
|
|
39337
|
+
})
|
|
39338
|
+
);
|
|
39227
39339
|
const [nativeResults, holdings] = await Promise.all([
|
|
39228
|
-
pMap(
|
|
39229
|
-
|
|
39230
|
-
|
|
39231
|
-
|
|
39232
|
-
|
|
39233
|
-
|
|
39234
|
-
}
|
|
39235
|
-
|
|
39340
|
+
pMap(
|
|
39341
|
+
chainEntries,
|
|
39342
|
+
async ({
|
|
39343
|
+
chainKey,
|
|
39344
|
+
chain: chain2,
|
|
39345
|
+
address
|
|
39346
|
+
}) => {
|
|
39347
|
+
try {
|
|
39348
|
+
const balance = await withTimeout(
|
|
39349
|
+
suppress(() => blockchainClient.getBalance(`c${chain2.coinId}`, address)),
|
|
39350
|
+
15e3
|
|
39351
|
+
);
|
|
39352
|
+
return { chainKey, chain: chain2, address, balance };
|
|
39353
|
+
} catch {
|
|
39354
|
+
return { chainKey, chain: chain2, address, balance: null };
|
|
39355
|
+
}
|
|
39356
|
+
},
|
|
39357
|
+
5
|
|
39358
|
+
),
|
|
39236
39359
|
(async () => {
|
|
39237
39360
|
try {
|
|
39238
39361
|
return await new SecurityService2(httpClient).getTokenHoldings(holdingsAssets);
|
|
@@ -39245,14 +39368,25 @@ ${qr3}`;
|
|
|
39245
39368
|
for (const h4 of holdings) {
|
|
39246
39369
|
if (h4.tokens?.length) holdingsMap.set(h4.coin, h4.tokens);
|
|
39247
39370
|
}
|
|
39371
|
+
if (holdingsMap.size === 0) {
|
|
39372
|
+
try {
|
|
39373
|
+
await registerWalletWithBackend(creds, provider);
|
|
39374
|
+
const retryHoldings = await new SecurityService2(httpClient).getTokenHoldings(holdingsAssets);
|
|
39375
|
+
for (const h4 of retryHoldings) {
|
|
39376
|
+
if (h4.tokens?.length) holdingsMap.set(h4.coin, h4.tokens);
|
|
39377
|
+
}
|
|
39378
|
+
} catch {
|
|
39379
|
+
}
|
|
39380
|
+
}
|
|
39248
39381
|
for (const { chainKey, chain: chain2 } of chainEntries) {
|
|
39249
39382
|
const coinId = chain2.coinId;
|
|
39250
39383
|
if (holdingsMap.has(coinId)) continue;
|
|
39251
39384
|
const knownTokens = COMMON_TOKENS[chainKey];
|
|
39252
39385
|
if (!knownTokens) continue;
|
|
39253
|
-
const fallbackTokens = Object.entries(knownTokens).
|
|
39386
|
+
const fallbackTokens = Object.entries(knownTokens).flatMap(([sym, addr]) => {
|
|
39254
39387
|
const resolved = resolveToken(sym, chainKey);
|
|
39255
|
-
|
|
39388
|
+
if (!resolved) return [];
|
|
39389
|
+
return [{ contract: addr, decimals: resolved.decimals, symbol: sym }];
|
|
39256
39390
|
});
|
|
39257
39391
|
holdingsMap.set(coinId, fallbackTokens);
|
|
39258
39392
|
}
|
|
@@ -39260,28 +39394,45 @@ ${qr3}`;
|
|
|
39260
39394
|
for (const { chainKey, chain: chain2, address } of chainEntries) {
|
|
39261
39395
|
const tokens = holdingsMap.get(chain2.coinId) ?? [];
|
|
39262
39396
|
for (const t of tokens) {
|
|
39263
|
-
if (t.contract)
|
|
39397
|
+
if (t.contract)
|
|
39398
|
+
tokenJobs.push({
|
|
39399
|
+
chainKey,
|
|
39400
|
+
coinId: chain2.coinId,
|
|
39401
|
+
address,
|
|
39402
|
+
...t
|
|
39403
|
+
});
|
|
39264
39404
|
}
|
|
39265
39405
|
}
|
|
39266
|
-
const tokenBalances = await pMap(
|
|
39267
|
-
|
|
39268
|
-
|
|
39269
|
-
const
|
|
39270
|
-
|
|
39271
|
-
|
|
39272
|
-
|
|
39273
|
-
|
|
39274
|
-
|
|
39275
|
-
|
|
39276
|
-
|
|
39406
|
+
const tokenBalances = await pMap(
|
|
39407
|
+
tokenJobs,
|
|
39408
|
+
async (job) => {
|
|
39409
|
+
const fallback = job.balance ?? "0";
|
|
39410
|
+
try {
|
|
39411
|
+
const assetId = `c${job.coinId}_t${job.contract}`;
|
|
39412
|
+
const bal = await withTimeout(
|
|
39413
|
+
suppress(() => blockchainClient.getBalance(assetId, job.address)),
|
|
39414
|
+
15e3
|
|
39415
|
+
);
|
|
39416
|
+
return { ...job, rawBalance: bal?.total ?? bal?.available ?? fallback };
|
|
39417
|
+
} catch {
|
|
39418
|
+
return { ...job, rawBalance: fallback };
|
|
39419
|
+
}
|
|
39420
|
+
},
|
|
39421
|
+
5
|
|
39422
|
+
);
|
|
39423
|
+
const nativeAssetIds = nativeResults.map(
|
|
39424
|
+
({ chain: chain2 }) => `c${chain2.coinId}`
|
|
39425
|
+
);
|
|
39277
39426
|
const nonZeroTokens = tokenBalances.filter((t) => {
|
|
39278
39427
|
const dec = t.decimals ?? 18;
|
|
39279
39428
|
return toHuman(t.rawBalance, dec) !== "0";
|
|
39280
39429
|
});
|
|
39281
|
-
const tokenAssetIds = nonZeroTokens.map(
|
|
39430
|
+
const tokenAssetIds = nonZeroTokens.map(
|
|
39431
|
+
(t) => `c${t.coinId}_t${t.contract}`
|
|
39432
|
+
);
|
|
39282
39433
|
const priceMap = /* @__PURE__ */ new Map();
|
|
39283
39434
|
try {
|
|
39284
|
-
const priceResp = await httpClient.market("POST", "/
|
|
39435
|
+
const priceResp = await httpClient.market("POST", "/v2/market/tickers", {
|
|
39285
39436
|
currency: "USD",
|
|
39286
39437
|
assets: [.../* @__PURE__ */ new Set([...nativeAssetIds, ...tokenAssetIds])]
|
|
39287
39438
|
});
|
|
@@ -39297,7 +39448,14 @@ ${qr3}`;
|
|
|
39297
39448
|
const rawTotal = balance?.total ?? balance?.available ?? "0";
|
|
39298
39449
|
const humanAmount = toHuman(rawTotal, c3.decimals);
|
|
39299
39450
|
const usdValue = price !== null ? parseFloat((parseFloat(humanAmount) * price).toFixed(2)) : null;
|
|
39300
|
-
rows.push({
|
|
39451
|
+
rows.push({
|
|
39452
|
+
chain: chainKey,
|
|
39453
|
+
type: "native",
|
|
39454
|
+
symbol: c3.symbol,
|
|
39455
|
+
address,
|
|
39456
|
+
balance: humanAmount,
|
|
39457
|
+
usdValue
|
|
39458
|
+
});
|
|
39301
39459
|
}
|
|
39302
39460
|
for (const t of tokenBalances) {
|
|
39303
39461
|
const dec = t.decimals ?? 18;
|
|
@@ -39306,12 +39464,23 @@ ${qr3}`;
|
|
|
39306
39464
|
const tokenAssetId = `c${t.coinId}_t${t.contract}`;
|
|
39307
39465
|
const tokenPrice = priceMap.get(tokenAssetId) ?? null;
|
|
39308
39466
|
const usdValue = tokenPrice !== null ? parseFloat((parseFloat(humanAmount) * tokenPrice).toFixed(2)) : null;
|
|
39309
|
-
rows.push({
|
|
39467
|
+
rows.push({
|
|
39468
|
+
chain: t.chainKey,
|
|
39469
|
+
type: "token",
|
|
39470
|
+
symbol: t.symbol ?? t.contract,
|
|
39471
|
+
address: t.address,
|
|
39472
|
+
contract: t.contract,
|
|
39473
|
+
balance: humanAmount,
|
|
39474
|
+
usdValue
|
|
39475
|
+
});
|
|
39310
39476
|
}
|
|
39311
39477
|
if (opts.json) {
|
|
39312
39478
|
output(rows, true);
|
|
39313
39479
|
} else {
|
|
39314
|
-
const totalUsd = rows.reduce(
|
|
39480
|
+
const totalUsd = rows.reduce(
|
|
39481
|
+
(sum, r3) => sum + (typeof r3.usdValue === "number" ? r3.usdValue : 0),
|
|
39482
|
+
0
|
|
39483
|
+
);
|
|
39315
39484
|
console.log("Chain Type Symbol Balance USD");
|
|
39316
39485
|
console.log("\u2500".repeat(72));
|
|
39317
39486
|
for (const r3 of rows) {
|
|
@@ -39331,7 +39500,10 @@ ${qr3}`;
|
|
|
39331
39500
|
}
|
|
39332
39501
|
process.exit();
|
|
39333
39502
|
});
|
|
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(
|
|
39503
|
+
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(
|
|
39504
|
+
"--password <password>",
|
|
39505
|
+
"Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
|
|
39506
|
+
).option("--json", "JSON output").action(async (opts) => {
|
|
39335
39507
|
try {
|
|
39336
39508
|
await ensureTrustSDK();
|
|
39337
39509
|
const password = await resolvePassword(opts);
|
|
@@ -39532,7 +39704,9 @@ var SwapExecutor = class {
|
|
|
39532
39704
|
async execute(params) {
|
|
39533
39705
|
const address = await this.walletProvider.getAddress(params.fromChain);
|
|
39534
39706
|
const fromTokenInfo = resolveToken(params.fromToken, params.fromChain);
|
|
39707
|
+
if (!fromTokenInfo) throw new Error(`Unknown token: ${params.fromToken} on ${params.fromChain}`);
|
|
39535
39708
|
const toTokenInfo = resolveToken(params.toToken, params.toChain);
|
|
39709
|
+
if (!toTokenInfo) throw new Error(`Unknown token: ${params.toToken} on ${params.toChain}`);
|
|
39536
39710
|
const parsedAmount = parseAmount(params.amount, fromTokenInfo.decimals);
|
|
39537
39711
|
const fromCoinId = getChainById(params.fromChain)?.coinId;
|
|
39538
39712
|
const toCoinId = getChainById(params.toChain)?.coinId;
|
|
@@ -39554,14 +39728,26 @@ var SwapExecutor = class {
|
|
|
39554
39728
|
toDomainId: toCoinId ? domains.get(toCoinId) : void 0
|
|
39555
39729
|
});
|
|
39556
39730
|
if (quote.routes.length === 0) {
|
|
39557
|
-
return {
|
|
39731
|
+
return {
|
|
39732
|
+
success: false,
|
|
39733
|
+
route: {
|
|
39734
|
+
id: "",
|
|
39735
|
+
steps: [],
|
|
39736
|
+
warnings: ["No routes found"],
|
|
39737
|
+
expirationDate: "",
|
|
39738
|
+
totalOutput: "0"
|
|
39739
|
+
},
|
|
39740
|
+
error: "No swap routes found"
|
|
39741
|
+
};
|
|
39558
39742
|
}
|
|
39559
39743
|
const route = quote.routes[0];
|
|
39560
39744
|
const firstStep = route.steps[0];
|
|
39561
39745
|
const lastStep = route.steps[route.steps.length - 1];
|
|
39562
39746
|
const inputAmt = formatAmount(firstStep.from.amount, firstStep.from.asset.metadata.decimals);
|
|
39563
39747
|
const outputAmt = formatAmount(lastStep.to.amount, lastStep.to.asset.metadata.decimals);
|
|
39564
|
-
this.log(
|
|
39748
|
+
this.log(
|
|
39749
|
+
`Swapping ${inputAmt} ${firstStep.from.asset.metadata.symbol} -> ${outputAmt} ${lastStep.to.asset.metadata.symbol} via ${route.steps.map((s2) => s2.provider.name).join(" -> ")}`
|
|
39750
|
+
);
|
|
39565
39751
|
const chainConfig = getChainById(params.fromChain);
|
|
39566
39752
|
const transactions = await this.swapClient.getSwapTransactions(route);
|
|
39567
39753
|
try {
|
|
@@ -39570,11 +39756,20 @@ var SwapExecutor = class {
|
|
|
39570
39756
|
const txType = txData.transaction.type;
|
|
39571
39757
|
const isApproval = txType === "approval" || txType === "revokeApproval";
|
|
39572
39758
|
if (isApproval) {
|
|
39573
|
-
this.log(
|
|
39759
|
+
this.log(
|
|
39760
|
+
txType === "revokeApproval" ? "Revoking prior approval..." : "Sending token approval..."
|
|
39761
|
+
);
|
|
39574
39762
|
}
|
|
39575
39763
|
txHash = await this.sendTransaction(txData, chainConfig, params.fromChain);
|
|
39576
|
-
this.log(
|
|
39577
|
-
|
|
39764
|
+
this.log(
|
|
39765
|
+
`${isApproval ? "Approval" : "Swap"} tx: ${getExplorerTxUrl(params.fromChain, txHash)}`
|
|
39766
|
+
);
|
|
39767
|
+
await this.pollForConfirmation(
|
|
39768
|
+
params.fromChain,
|
|
39769
|
+
address,
|
|
39770
|
+
txHash,
|
|
39771
|
+
isApproval ? 6e4 : 12e4
|
|
39772
|
+
);
|
|
39578
39773
|
}
|
|
39579
39774
|
return {
|
|
39580
39775
|
success: true,
|
|
@@ -39606,20 +39801,28 @@ var SwapExecutor = class {
|
|
|
39606
39801
|
}
|
|
39607
39802
|
async pollForConfirmation(chainKey, address, hash, timeoutMs) {
|
|
39608
39803
|
const blockchainClient = this.walletProvider.getBlockchainClient?.();
|
|
39609
|
-
if (!blockchainClient)
|
|
39804
|
+
if (!blockchainClient)
|
|
39805
|
+
throw new Error(
|
|
39806
|
+
"Wallet provider does not support transaction status lookup; cannot confirm transaction"
|
|
39807
|
+
);
|
|
39610
39808
|
const deadline = Date.now() + timeoutMs;
|
|
39611
39809
|
while (Date.now() < deadline) {
|
|
39612
39810
|
await new Promise((r3) => setTimeout(r3, 2e3));
|
|
39613
39811
|
try {
|
|
39614
39812
|
const status = await blockchainClient.findTransactionStatus(chainKey, address, hash);
|
|
39615
39813
|
if (status.confirmed) return;
|
|
39616
|
-
if (status.failed)
|
|
39814
|
+
if (status.failed)
|
|
39815
|
+
throw new Error(`Transaction failed: ${status.error || "unknown error"}`);
|
|
39617
39816
|
} catch (e2) {
|
|
39618
39817
|
const err = e2 instanceof Error ? e2 : new Error(String(e2));
|
|
39619
39818
|
if (err.message.startsWith("Transaction failed")) throw err;
|
|
39819
|
+
if (!err.message.includes("not found") && !err.message.includes("Transaction not found"))
|
|
39820
|
+
throw err;
|
|
39620
39821
|
}
|
|
39621
39822
|
}
|
|
39622
|
-
throw new Error(
|
|
39823
|
+
throw new Error(
|
|
39824
|
+
`Transaction ${hash} was not confirmed within ${Math.round(timeoutMs / 1e3)}s`
|
|
39825
|
+
);
|
|
39623
39826
|
}
|
|
39624
39827
|
};
|
|
39625
39828
|
|
|
@@ -39691,6 +39894,11 @@ function registerTransferCommand(program3) {
|
|
|
39691
39894
|
}
|
|
39692
39895
|
try {
|
|
39693
39896
|
const tokenInfo = resolveToken(opts.token, chainKey);
|
|
39897
|
+
if (!tokenInfo) {
|
|
39898
|
+
error(`Unknown token: ${opts.token} on ${chainKey}. Use a contract address (0x...) or a supported symbol.`);
|
|
39899
|
+
process.exitCode = 1;
|
|
39900
|
+
return;
|
|
39901
|
+
}
|
|
39694
39902
|
const swapClient = new SwapClient(httpClient);
|
|
39695
39903
|
const price = await swapClient.getTokenPrice(chainKey, tokenInfo.address);
|
|
39696
39904
|
if (price && price > 0) {
|
|
@@ -39749,250 +39957,299 @@ init_dist4();
|
|
|
39749
39957
|
var MAX_SLIPPAGE_PCT = 50;
|
|
39750
39958
|
var WARN_SLIPPAGE_PCT = 5;
|
|
39751
39959
|
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");
|
|
39960
|
+
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(
|
|
39961
|
+
async (amountOrFrom, fromOrTo, toOrUndefined, opts) => {
|
|
39962
|
+
try {
|
|
39963
|
+
let amount;
|
|
39964
|
+
let from8;
|
|
39965
|
+
let to3;
|
|
39966
|
+
if (toOrUndefined) {
|
|
39967
|
+
amount = amountOrFrom;
|
|
39968
|
+
from8 = fromOrTo;
|
|
39969
|
+
to3 = toOrUndefined;
|
|
39970
|
+
} else {
|
|
39971
|
+
from8 = amountOrFrom;
|
|
39972
|
+
to3 = fromOrTo;
|
|
39973
|
+
}
|
|
39974
|
+
if (!amount && !opts.usd) {
|
|
39975
|
+
error("Either <amount> or --usd <amount> is required");
|
|
39807
39976
|
return;
|
|
39808
39977
|
}
|
|
39809
|
-
|
|
39810
|
-
|
|
39811
|
-
error(`Unable to fetch price for ${from8} on ${fromChain}`);
|
|
39978
|
+
if (amount && opts.usd) {
|
|
39979
|
+
error("Provide either <amount> or --usd, not both");
|
|
39812
39980
|
return;
|
|
39813
39981
|
}
|
|
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);
|
|
39982
|
+
if (amount) {
|
|
39983
|
+
const n4 = parseFloat(amount);
|
|
39984
|
+
if (isNaN(n4) || n4 <= 0) {
|
|
39985
|
+
error("Amount must be greater than 0");
|
|
39986
|
+
process.exitCode = 1;
|
|
39987
|
+
return;
|
|
39988
|
+
}
|
|
39830
39989
|
}
|
|
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
|
-
|
|
39990
|
+
const slippage = parseFloat(opts.slippage);
|
|
39991
|
+
if (isNaN(slippage) || slippage <= 0) {
|
|
39992
|
+
error("--slippage must be a positive number");
|
|
39993
|
+
process.exitCode = 1;
|
|
39994
|
+
return;
|
|
39995
|
+
}
|
|
39996
|
+
if (slippage > MAX_SLIPPAGE_PCT) {
|
|
39997
|
+
error(
|
|
39998
|
+
`Slippage ${slippage}% exceeds maximum allowed (${MAX_SLIPPAGE_PCT}%). Reduce --slippage to proceed.`
|
|
39999
|
+
);
|
|
40000
|
+
process.exitCode = 1;
|
|
40001
|
+
return;
|
|
40002
|
+
}
|
|
40003
|
+
if (slippage > WARN_SLIPPAGE_PCT) {
|
|
40004
|
+
warn(
|
|
40005
|
+
`High slippage tolerance: ${slippage}%. You may receive significantly fewer tokens than quoted.`
|
|
40006
|
+
);
|
|
40007
|
+
}
|
|
40008
|
+
const client = await getClient();
|
|
40009
|
+
const swapClient = new SwapClient(client);
|
|
40010
|
+
const detectChain = (token, fallback) => {
|
|
40011
|
+
const m2 = token.match(/^c(\d+)/i);
|
|
40012
|
+
if (m2) {
|
|
40013
|
+
const key = coinTypeToChainKey(parseInt(m2[1]));
|
|
40014
|
+
if (!key) throw new Error(`Unknown coin ID ${m2[1]} in token "${token}"`);
|
|
40015
|
+
return key;
|
|
40016
|
+
}
|
|
40017
|
+
return fallback;
|
|
40018
|
+
};
|
|
40019
|
+
const fromChain = detectChain(from8, opts.chain);
|
|
40020
|
+
const toChain = opts.toChain ? detectChain(to3, opts.toChain) : fromChain;
|
|
40021
|
+
const fromInfo = resolveToken(from8, fromChain);
|
|
40022
|
+
if (!fromInfo) {
|
|
40023
|
+
error(
|
|
40024
|
+
`Unknown token: ${from8} on ${fromChain}. Use a contract address (0x...) or a supported symbol.`
|
|
40025
|
+
);
|
|
40026
|
+
process.exitCode = 1;
|
|
40027
|
+
return;
|
|
40028
|
+
}
|
|
40029
|
+
const toInfo = resolveToken(to3, toChain);
|
|
40030
|
+
if (!toInfo) {
|
|
40031
|
+
error(
|
|
40032
|
+
`Unknown token: ${to3} on ${toChain}. Use a contract address (0x...) or a supported symbol.`
|
|
40033
|
+
);
|
|
40034
|
+
process.exitCode = 1;
|
|
40035
|
+
return;
|
|
40036
|
+
}
|
|
40037
|
+
let resolvedAmount = amount;
|
|
40038
|
+
if (opts.usd) {
|
|
40039
|
+
const usdAmount = parseFloat(opts.usd);
|
|
40040
|
+
if (isNaN(usdAmount) || usdAmount <= 0) {
|
|
40041
|
+
error("--usd amount must be a positive number");
|
|
40042
|
+
return;
|
|
40043
|
+
}
|
|
40044
|
+
const price = await swapClient.getTokenPrice(fromChain, fromInfo.address);
|
|
40045
|
+
if (!price) {
|
|
40046
|
+
error(`Unable to fetch price for ${from8} on ${fromChain}`);
|
|
40047
|
+
return;
|
|
40048
|
+
}
|
|
40049
|
+
resolvedAmount = (usdAmount / price).toFixed(fromInfo.decimals);
|
|
40050
|
+
info(`$${usdAmount} USD \u2248 ${resolvedAmount} ${from8.toUpperCase()} (@ $${price})`);
|
|
40051
|
+
}
|
|
40052
|
+
const parsedAmount = parseAmount(resolvedAmount, fromInfo.decimals);
|
|
40053
|
+
const needsWallet = !opts.quoteOnly;
|
|
40054
|
+
let walletAddress = getChainById(fromChain)?.sampleAccount || "0x0000000000000000000000000000000000000000";
|
|
40055
|
+
let provider = null;
|
|
40056
|
+
let destinationAddress;
|
|
40057
|
+
let amberDomains = /* @__PURE__ */ new Map();
|
|
40058
|
+
if (needsWallet) {
|
|
40059
|
+
await ensureTrustSDK();
|
|
40060
|
+
const password = await resolvePassword(opts);
|
|
40061
|
+
provider = await AgentWalletProvider.fromStorage(password);
|
|
40062
|
+
walletAddress = await provider.getAddress(fromChain);
|
|
40063
|
+
if (toChain !== fromChain) {
|
|
40064
|
+
destinationAddress = await provider.getAddress(toChain);
|
|
40065
|
+
}
|
|
40066
|
+
amberDomains = await getAmberDomainsByCoinId();
|
|
40067
|
+
}
|
|
40068
|
+
const fromCoinId = getChainById(fromChain)?.coinId;
|
|
40069
|
+
const toCoinId = getChainById(toChain)?.coinId;
|
|
40070
|
+
const quote = await swapClient.getQuote({
|
|
40071
|
+
fromChain,
|
|
40072
|
+
fromToken: fromInfo.address,
|
|
40073
|
+
toChain,
|
|
40074
|
+
toToken: toInfo.address,
|
|
40075
|
+
amount: parsedAmount,
|
|
40076
|
+
walletAddress,
|
|
40077
|
+
destinationAddress,
|
|
40078
|
+
slippage: opts.slippage,
|
|
40079
|
+
fromDomainId: fromCoinId ? amberDomains.get(fromCoinId) : void 0,
|
|
40080
|
+
toDomainId: toCoinId ? amberDomains.get(toCoinId) : void 0
|
|
40081
|
+
});
|
|
40082
|
+
if (quote.routes.length === 0) {
|
|
40083
|
+
error("No swap routes found");
|
|
40084
|
+
if (opts.json) output({ error: "No routes found" }, true);
|
|
40085
|
+
return;
|
|
40086
|
+
}
|
|
40087
|
+
const route = quote.routes[0];
|
|
40088
|
+
const firstStep = route.steps[0];
|
|
40089
|
+
const lastStep = route.steps[route.steps.length - 1];
|
|
40090
|
+
const inputAmt = formatAmount(
|
|
40091
|
+
firstStep.from.amount,
|
|
40092
|
+
firstStep.from.asset.metadata.decimals
|
|
40093
|
+
);
|
|
40094
|
+
const outputAmt = formatAmount(lastStep.to.amount, lastStep.to.asset.metadata.decimals);
|
|
40095
|
+
const minAmountOut = lastStep.to.minAmountOut;
|
|
40096
|
+
const minReceivedAmt = minAmountOut ? formatAmount(minAmountOut, lastStep.to.asset.metadata.decimals) : null;
|
|
40097
|
+
const networkFees = route.steps.filter((s2) => s2.networkFee).map((s2) => {
|
|
40098
|
+
const fee = s2.networkFee;
|
|
40099
|
+
const feeAmt = formatAmount(fee.amount, fee.asset.metadata.decimals);
|
|
40100
|
+
return `${feeAmt} ${fee.asset.metadata.symbol}`;
|
|
40101
|
+
});
|
|
40102
|
+
const quoteData = {
|
|
40103
|
+
input: `${inputAmt} ${firstStep.from.asset.metadata.symbol}`,
|
|
40104
|
+
output: `${outputAmt} ${lastStep.to.asset.metadata.symbol}`,
|
|
40105
|
+
...minReceivedAmt ? { minReceived: `${minReceivedAmt} ${lastStep.to.asset.metadata.symbol}` } : {},
|
|
40106
|
+
provider: route.steps.map((s2) => s2.provider.name).join(" \u2192 "),
|
|
40107
|
+
priceImpact: firstStep.priceImpact,
|
|
40108
|
+
...networkFees.length > 0 ? { networkFee: networkFees.join(" + ") } : {},
|
|
40109
|
+
...route.steps.length > 1 ? { steps: route.steps.length } : {}
|
|
40110
|
+
};
|
|
40111
|
+
if (opts.quoteOnly) {
|
|
40112
|
+
output(quoteData, opts.json);
|
|
40113
|
+
return;
|
|
40114
|
+
}
|
|
40115
|
+
info(`Swapping ${quoteData.input} \u2192 ${quoteData.output} via ${quoteData.provider}...`);
|
|
40116
|
+
const chainConfig = getChainById(fromChain);
|
|
40117
|
+
const transactions = await swapClient.getSwapTransactions(route);
|
|
40118
|
+
let txHash = "";
|
|
40119
|
+
const blockchainClient = provider.getBlockchainClient();
|
|
40120
|
+
const hasApprovalTx = transactions.some((t) => t.transaction.type === "approval");
|
|
40121
|
+
if (!hasApprovalTx && chainConfig?.isEVM) {
|
|
40122
|
+
const stepApproval = firstStep.approval;
|
|
40123
|
+
if (stepApproval && stepApproval.operatorAddress) {
|
|
40124
|
+
const fromAssetAddr = firstStep.from.asset.address;
|
|
40125
|
+
const isNative = !fromAssetAddr || fromAssetAddr === NATIVE_TOKEN_ADDRESS;
|
|
40126
|
+
if (!isNative && chainConfig.coinId) {
|
|
40127
|
+
const assetId = formatTokenId(chainConfig.coinId, fromAssetAddr);
|
|
40128
|
+
try {
|
|
40129
|
+
const allowance = await blockchainClient.getAllowance({
|
|
39901
40130
|
assetId,
|
|
39902
|
-
|
|
39903
|
-
|
|
39904
|
-
hdWallet
|
|
40131
|
+
owner: walletAddress,
|
|
40132
|
+
spender: stepApproval.operatorAddress
|
|
39905
40133
|
});
|
|
39906
|
-
|
|
39907
|
-
|
|
39908
|
-
|
|
39909
|
-
|
|
39910
|
-
|
|
39911
|
-
|
|
39912
|
-
|
|
39913
|
-
|
|
39914
|
-
|
|
39915
|
-
|
|
39916
|
-
|
|
40134
|
+
const requiredAmount = BigInt(stepApproval.minAmount || parsedAmount);
|
|
40135
|
+
if (allowance < requiredAmount) {
|
|
40136
|
+
info("Sending token approval...");
|
|
40137
|
+
const hdWallet = provider.getSigner().getHDWallet();
|
|
40138
|
+
const approveResult = await blockchainClient.approve({
|
|
40139
|
+
assetId,
|
|
40140
|
+
spender: stepApproval.operatorAddress,
|
|
40141
|
+
amount: requiredAmount.toString(),
|
|
40142
|
+
hdWallet
|
|
40143
|
+
});
|
|
40144
|
+
info(`Approval tx: ${getExplorerTxUrl(fromChain, approveResult.hash)}`);
|
|
40145
|
+
const approvalDeadline = Date.now() + 6e4;
|
|
40146
|
+
while (Date.now() < approvalDeadline) {
|
|
40147
|
+
await new Promise((r3) => setTimeout(r3, 2e3));
|
|
40148
|
+
try {
|
|
40149
|
+
const status = await blockchainClient.findTransactionStatus(
|
|
40150
|
+
fromChain,
|
|
40151
|
+
walletAddress,
|
|
40152
|
+
approveResult.hash
|
|
40153
|
+
);
|
|
40154
|
+
if (status.confirmed) break;
|
|
40155
|
+
if (status.failed) throw new Error("Approval transaction failed");
|
|
40156
|
+
} catch (e2) {
|
|
40157
|
+
const err = e2 instanceof Error ? e2 : new Error(String(e2));
|
|
40158
|
+
if (err.message.startsWith("Approval transaction failed")) throw err;
|
|
40159
|
+
}
|
|
39917
40160
|
}
|
|
39918
40161
|
}
|
|
40162
|
+
} catch (e2) {
|
|
40163
|
+
const msg = e2 instanceof Error ? e2.message : String(e2);
|
|
40164
|
+
if (msg.includes("insufficient funds")) {
|
|
40165
|
+
throw new Error(
|
|
40166
|
+
`Insufficient ${chainConfig?.symbol || "native token"} for approval gas. Fund your wallet with ${chainConfig?.symbol || "native tokens"} first.`
|
|
40167
|
+
);
|
|
40168
|
+
}
|
|
40169
|
+
if (msg.includes("failed")) throw e2;
|
|
40170
|
+
info(`Warning: could not check/send approval: ${msg}`);
|
|
39919
40171
|
}
|
|
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
40172
|
}
|
|
39928
40173
|
}
|
|
39929
40174
|
}
|
|
39930
|
-
|
|
39931
|
-
|
|
39932
|
-
|
|
39933
|
-
|
|
39934
|
-
|
|
39935
|
-
|
|
39936
|
-
|
|
39937
|
-
|
|
39938
|
-
|
|
39939
|
-
|
|
39940
|
-
|
|
39941
|
-
|
|
39942
|
-
|
|
39943
|
-
|
|
40175
|
+
const pollForConfirmation = async (hash, timeoutMs) => {
|
|
40176
|
+
const deadline = Date.now() + timeoutMs;
|
|
40177
|
+
while (Date.now() < deadline) {
|
|
40178
|
+
await new Promise((r3) => setTimeout(r3, 2e3));
|
|
40179
|
+
try {
|
|
40180
|
+
const status = await blockchainClient.findTransactionStatus(
|
|
40181
|
+
fromChain,
|
|
40182
|
+
walletAddress,
|
|
40183
|
+
hash
|
|
40184
|
+
);
|
|
40185
|
+
if (status.confirmed) return;
|
|
40186
|
+
if (status.failed)
|
|
40187
|
+
throw new Error(`Transaction failed: ${status.error || "unknown error"}`);
|
|
40188
|
+
} catch (e2) {
|
|
40189
|
+
const err = e2 instanceof Error ? e2 : new Error(String(e2));
|
|
40190
|
+
if (err.message.startsWith("Transaction failed")) throw err;
|
|
40191
|
+
if (!err.message.includes("not found") && !err.message.includes("Transaction not found")) {
|
|
40192
|
+
info(`Warning: tx status check error (will retry): ${err.message}`);
|
|
40193
|
+
}
|
|
39944
40194
|
}
|
|
39945
40195
|
}
|
|
40196
|
+
throw new Error(
|
|
40197
|
+
`Transaction ${hash} was not confirmed within ${Math.round(timeoutMs / 1e3)}s`
|
|
40198
|
+
);
|
|
40199
|
+
};
|
|
40200
|
+
for (const txData of transactions) {
|
|
40201
|
+
const txType = txData.transaction.type;
|
|
40202
|
+
const isApproval = txType === "approval" || txType === "revokeApproval";
|
|
40203
|
+
if (isApproval) {
|
|
40204
|
+
info(
|
|
40205
|
+
txType === "revokeApproval" ? "Revoking prior approval..." : "Sending token approval..."
|
|
40206
|
+
);
|
|
40207
|
+
}
|
|
40208
|
+
if (chainConfig?.namespace === "solana" && txData.transaction.solanaTx) {
|
|
40209
|
+
const solanaChainId = `solana:${chainConfig.chainId}`;
|
|
40210
|
+
txHash = await provider.sendTransaction({
|
|
40211
|
+
chainId: solanaChainId,
|
|
40212
|
+
to: "",
|
|
40213
|
+
data: txData.transaction.solanaTx.serializedMessage
|
|
40214
|
+
});
|
|
40215
|
+
} else if (txData.transaction.evmTx) {
|
|
40216
|
+
const wcChainId = `eip155:${chainConfig?.chainId || "1"}`;
|
|
40217
|
+
txHash = await provider.sendTransaction({
|
|
40218
|
+
chainId: wcChainId,
|
|
40219
|
+
to: txData.transaction.evmTx.to,
|
|
40220
|
+
data: txData.transaction.evmTx.data,
|
|
40221
|
+
value: txData.transaction.evmTx.value || "0x0"
|
|
40222
|
+
});
|
|
40223
|
+
} else {
|
|
40224
|
+
error("No transaction data returned for this swap step");
|
|
40225
|
+
if (opts.json) output({ error: "No transaction data" }, true);
|
|
40226
|
+
return;
|
|
40227
|
+
}
|
|
40228
|
+
info(`${isApproval ? "Approval" : "Swap"} tx: ${getExplorerTxUrl(fromChain, txHash)}`);
|
|
40229
|
+
await pollForConfirmation(txHash, isApproval ? 6e4 : 12e4);
|
|
39946
40230
|
}
|
|
39947
|
-
|
|
39948
|
-
|
|
39949
|
-
|
|
39950
|
-
|
|
39951
|
-
|
|
39952
|
-
|
|
39953
|
-
|
|
39954
|
-
|
|
39955
|
-
|
|
39956
|
-
|
|
39957
|
-
|
|
39958
|
-
|
|
39959
|
-
|
|
39960
|
-
|
|
39961
|
-
|
|
39962
|
-
|
|
39963
|
-
|
|
39964
|
-
|
|
39965
|
-
|
|
39966
|
-
to: txData.transaction.evmTx.to,
|
|
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);
|
|
40231
|
+
const explorerUrl = getExplorerTxUrl(fromChain, txHash);
|
|
40232
|
+
success("Swap executed!");
|
|
40233
|
+
output(
|
|
40234
|
+
{
|
|
40235
|
+
...quoteData,
|
|
40236
|
+
hash: txHash,
|
|
40237
|
+
fromChain,
|
|
40238
|
+
toChain,
|
|
40239
|
+
explorer: explorerUrl
|
|
40240
|
+
},
|
|
40241
|
+
opts.json
|
|
40242
|
+
);
|
|
40243
|
+
} catch (err) {
|
|
40244
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
40245
|
+
error(`Swap failed: ${msg}`);
|
|
40246
|
+
if (opts.json) output({ error: msg }, true);
|
|
40247
|
+
}
|
|
40248
|
+
if (!opts.quoteOnly) {
|
|
40249
|
+
setTimeout(() => process.exit(0), 100);
|
|
39977
40250
|
}
|
|
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
40251
|
}
|
|
39995
|
-
|
|
40252
|
+
);
|
|
39996
40253
|
}
|
|
39997
40254
|
|
|
39998
40255
|
// src/commands/erc20.ts
|
|
@@ -40143,12 +40400,43 @@ function registerErc20Command(program3) {
|
|
|
40143
40400
|
// src/commands/alert.ts
|
|
40144
40401
|
init_dist();
|
|
40145
40402
|
init_dist2();
|
|
40146
|
-
|
|
40403
|
+
init_dist5();
|
|
40404
|
+
var assetCache = new AssetCache();
|
|
40405
|
+
async function resolveTokenLabel(tokenId, service) {
|
|
40406
|
+
const cached2 = await assetCache.get(tokenId);
|
|
40407
|
+
if (cached2 !== void 0) return cached2;
|
|
40147
40408
|
try {
|
|
40148
40409
|
const { coinId, contractAddress } = parseTokenId(tokenId);
|
|
40149
40410
|
const chain2 = getChainByCoinId(coinId);
|
|
40150
|
-
if (chain2
|
|
40151
|
-
|
|
40411
|
+
if (!chain2) return tokenId;
|
|
40412
|
+
if (!contractAddress) {
|
|
40413
|
+
return chain2.symbol;
|
|
40414
|
+
}
|
|
40415
|
+
const chainKey = tokenIdToChain(tokenId);
|
|
40416
|
+
if (chainKey) {
|
|
40417
|
+
const chainTokens = COMMON_TOKENS[chainKey];
|
|
40418
|
+
if (chainTokens) {
|
|
40419
|
+
const normalizedAddr = contractAddress.toLowerCase();
|
|
40420
|
+
for (const [sym, addr] of Object.entries(chainTokens)) {
|
|
40421
|
+
if (addr.toLowerCase() === normalizedAddr) {
|
|
40422
|
+
await assetCache.set(tokenId, sym);
|
|
40423
|
+
return sym;
|
|
40424
|
+
}
|
|
40425
|
+
}
|
|
40426
|
+
}
|
|
40427
|
+
}
|
|
40428
|
+
if (service) {
|
|
40429
|
+
try {
|
|
40430
|
+
const assetInfo = await service.getAssetInfo(tokenId);
|
|
40431
|
+
if (assetInfo.symbol) {
|
|
40432
|
+
await assetCache.set(tokenId, assetInfo.symbol);
|
|
40433
|
+
return assetInfo.symbol;
|
|
40434
|
+
}
|
|
40435
|
+
} catch {
|
|
40436
|
+
}
|
|
40437
|
+
}
|
|
40438
|
+
const short = contractAddress.length > 10 ? `${contractAddress.slice(0, 6)}\u2026${contractAddress.slice(-4)}` : contractAddress;
|
|
40439
|
+
return `${chain2.symbol}/${short}`;
|
|
40152
40440
|
} catch {
|
|
40153
40441
|
return tokenId;
|
|
40154
40442
|
}
|
|
@@ -40212,7 +40500,9 @@ function registerAlertCommand(program3) {
|
|
|
40212
40500
|
resolvedChain = NATIVE_SYMBOLS2[sym];
|
|
40213
40501
|
}
|
|
40214
40502
|
if (!resolvedChain) {
|
|
40215
|
-
error(
|
|
40503
|
+
error(
|
|
40504
|
+
`Cannot resolve "${opts.token}" without --chain. Provide --chain or use asset ID format (c60).`
|
|
40505
|
+
);
|
|
40216
40506
|
if (opts.json) output({ error: "Chain required" }, true);
|
|
40217
40507
|
process.exitCode = 1;
|
|
40218
40508
|
return;
|
|
@@ -40225,6 +40515,12 @@ function registerAlertCommand(program3) {
|
|
|
40225
40515
|
return;
|
|
40226
40516
|
}
|
|
40227
40517
|
const resolved = resolveToken(sym, resolvedChain);
|
|
40518
|
+
if (!resolved) {
|
|
40519
|
+
error(`Unknown token: ${sym} on ${resolvedChain}. Use a contract address or asset ID (e.g. c60_t0xABC...).`);
|
|
40520
|
+
if (opts.json) output({ error: `Unknown token: ${sym}` }, true);
|
|
40521
|
+
process.exitCode = 1;
|
|
40522
|
+
return;
|
|
40523
|
+
}
|
|
40228
40524
|
if (resolved.address === NATIVE_TOKEN_ADDRESS || resolved.address === sym) {
|
|
40229
40525
|
resolvedTokenId = formatTokenId(chain2.coinId);
|
|
40230
40526
|
} else {
|
|
@@ -40248,15 +40544,19 @@ function registerAlertCommand(program3) {
|
|
|
40248
40544
|
});
|
|
40249
40545
|
alert.command("list").description("List all alerts").option("--active", "Show only active alerts").option("--json", "JSON output").action(async (opts) => {
|
|
40250
40546
|
const alerts = opts.active ? await alertStore.getActive() : await alertStore.list();
|
|
40547
|
+
const client = await tryGetClient();
|
|
40548
|
+
const service = client ? new SecurityService(client) : void 0;
|
|
40251
40549
|
if (opts.json) {
|
|
40252
|
-
const enriched =
|
|
40253
|
-
|
|
40254
|
-
|
|
40255
|
-
|
|
40256
|
-
|
|
40257
|
-
|
|
40258
|
-
|
|
40259
|
-
|
|
40550
|
+
const enriched = await Promise.all(
|
|
40551
|
+
alerts.map(async (a3) => {
|
|
40552
|
+
let chainLabel = a3.chainKey;
|
|
40553
|
+
try {
|
|
40554
|
+
chainLabel = tokenIdToChain(a3.tokenId) ?? a3.chainKey;
|
|
40555
|
+
} catch {
|
|
40556
|
+
}
|
|
40557
|
+
return { ...a3, tokenLabel: await resolveTokenLabel(a3.tokenId, service), chainLabel };
|
|
40558
|
+
})
|
|
40559
|
+
);
|
|
40260
40560
|
output(enriched, true);
|
|
40261
40561
|
} else {
|
|
40262
40562
|
if (alerts.length === 0) {
|
|
@@ -40264,7 +40564,7 @@ function registerAlertCommand(program3) {
|
|
|
40264
40564
|
return;
|
|
40265
40565
|
}
|
|
40266
40566
|
for (const a3 of alerts) {
|
|
40267
|
-
const label = resolveTokenLabel(a3.tokenId);
|
|
40567
|
+
const label = await resolveTokenLabel(a3.tokenId, service);
|
|
40268
40568
|
let chainHint = "";
|
|
40269
40569
|
try {
|
|
40270
40570
|
const chain2 = tokenIdToChain(a3.tokenId);
|
|
@@ -40272,7 +40572,9 @@ function registerAlertCommand(program3) {
|
|
|
40272
40572
|
} catch {
|
|
40273
40573
|
chainHint = "";
|
|
40274
40574
|
}
|
|
40275
|
-
info(
|
|
40575
|
+
info(
|
|
40576
|
+
`${a3.id} | ${label}${chainHint} | ${a3.condition} $${a3.targetPrice} | ${a3.active ? "active" : "triggered"}`
|
|
40577
|
+
);
|
|
40276
40578
|
}
|
|
40277
40579
|
}
|
|
40278
40580
|
});
|
|
@@ -40292,7 +40594,10 @@ function registerAlertCommand(program3) {
|
|
|
40292
40594
|
triggered.push({ ...a3, currentPrice: price });
|
|
40293
40595
|
}
|
|
40294
40596
|
}
|
|
40295
|
-
output(
|
|
40597
|
+
output(
|
|
40598
|
+
{ checked: activeAlerts.length, triggered: triggered.length, alerts: triggered },
|
|
40599
|
+
opts.json
|
|
40600
|
+
);
|
|
40296
40601
|
} catch (err) {
|
|
40297
40602
|
const msg = err instanceof Error ? err.message : String(err);
|
|
40298
40603
|
error(msg);
|
|
@@ -40300,8 +40605,22 @@ function registerAlertCommand(program3) {
|
|
|
40300
40605
|
}
|
|
40301
40606
|
});
|
|
40302
40607
|
alert.command("delete <id>").description("Delete an alert").option("--json", "JSON output").action(async (id, opts) => {
|
|
40303
|
-
|
|
40304
|
-
|
|
40608
|
+
try {
|
|
40609
|
+
const deleted = await alertStore.delete(id);
|
|
40610
|
+
if (!deleted) {
|
|
40611
|
+
error(`Alert ${id} not found`);
|
|
40612
|
+
if (opts.json) output({ error: `Alert ${id} not found` }, true);
|
|
40613
|
+
process.exitCode = 1;
|
|
40614
|
+
return;
|
|
40615
|
+
}
|
|
40616
|
+
if (!opts.json) success(`Alert ${id} deleted`);
|
|
40617
|
+
output({ success: true }, opts.json);
|
|
40618
|
+
} catch (err) {
|
|
40619
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
40620
|
+
error(msg);
|
|
40621
|
+
if (opts.json) output({ error: msg }, true);
|
|
40622
|
+
process.exitCode = 1;
|
|
40623
|
+
}
|
|
40305
40624
|
});
|
|
40306
40625
|
}
|
|
40307
40626
|
|
|
@@ -40380,14 +40699,20 @@ function handleError(err, json) {
|
|
|
40380
40699
|
process.exitCode = 1;
|
|
40381
40700
|
}
|
|
40382
40701
|
function registerQueryCommand(program3) {
|
|
40383
|
-
program3.command("price <token>").description("Get token price").option("--chain <id>", "Chain").option(
|
|
40702
|
+
program3.command("price <token>").description("Get token price").option("--chain <id>", "Chain").option(
|
|
40703
|
+
"--history [period]",
|
|
40704
|
+
"Price history period: hour, day, week, month, year, all (default: week)"
|
|
40705
|
+
).option("--json", "JSON output").action(async (token, opts) => {
|
|
40384
40706
|
try {
|
|
40385
40707
|
const client = await getClient();
|
|
40386
40708
|
const swapClient = new SwapClient(client);
|
|
40387
40709
|
const tokens = token.split(",").map((t) => t.trim()).filter(Boolean);
|
|
40388
40710
|
const resolveChain = (t) => {
|
|
40389
40711
|
if (opts.chain) return opts.chain;
|
|
40390
|
-
|
|
40712
|
+
if (Object.prototype.hasOwnProperty.call(CHAINS, t.toLowerCase())) return t.toLowerCase();
|
|
40713
|
+
const bySymbol = Object.entries(CHAINS).find(
|
|
40714
|
+
([, c3]) => c3.symbol.toUpperCase() === t.toUpperCase()
|
|
40715
|
+
)?.[0];
|
|
40391
40716
|
if (bySymbol) return bySymbol;
|
|
40392
40717
|
const upper = t.toUpperCase();
|
|
40393
40718
|
for (const [chainKey, chainTokens] of Object.entries(COMMON_TOKENS)) {
|
|
@@ -40395,12 +40720,22 @@ function registerQueryCommand(program3) {
|
|
|
40395
40720
|
}
|
|
40396
40721
|
return "ethereum";
|
|
40397
40722
|
};
|
|
40723
|
+
if (opts.chain && !getChainById(opts.chain)) {
|
|
40724
|
+
error(`Unknown chain: ${opts.chain}. Run 'twak chains' to list supported chains.`);
|
|
40725
|
+
if (opts.json) output({ error: `Unknown chain: ${opts.chain}` }, true);
|
|
40726
|
+
process.exitCode = 1;
|
|
40727
|
+
return;
|
|
40728
|
+
}
|
|
40398
40729
|
if (tokens.length > 1) {
|
|
40399
40730
|
const assetIds = [];
|
|
40400
40731
|
const tokenMeta = [];
|
|
40401
40732
|
for (const t of tokens) {
|
|
40402
40733
|
const chain3 = resolveChain(t);
|
|
40403
40734
|
const info2 = resolveToken(t, chain3);
|
|
40735
|
+
if (!info2) {
|
|
40736
|
+
warn(`Unknown token: ${t.toUpperCase()}`);
|
|
40737
|
+
continue;
|
|
40738
|
+
}
|
|
40404
40739
|
const chainInfo = getChainById(chain3);
|
|
40405
40740
|
if (!chainInfo) throw new Error(`Unknown chain: ${chain3}`);
|
|
40406
40741
|
const isNative = !info2.address || info2.address === NATIVE_TOKEN_ADDRESS || info2.address === SOLANA_NATIVE_ADDRESS;
|
|
@@ -40408,10 +40743,19 @@ function registerQueryCommand(program3) {
|
|
|
40408
40743
|
assetIds.push(assetId);
|
|
40409
40744
|
tokenMeta.push({ token: t.toUpperCase(), chain: chain3 });
|
|
40410
40745
|
}
|
|
40411
|
-
|
|
40412
|
-
|
|
40413
|
-
|
|
40414
|
-
|
|
40746
|
+
if (assetIds.length === 0) {
|
|
40747
|
+
error("No valid tokens to look up");
|
|
40748
|
+
process.exitCode = 1;
|
|
40749
|
+
return;
|
|
40750
|
+
}
|
|
40751
|
+
const resp = await client.market(
|
|
40752
|
+
"POST",
|
|
40753
|
+
"/v2/market/tickers",
|
|
40754
|
+
{
|
|
40755
|
+
currency: "USD",
|
|
40756
|
+
assets: assetIds
|
|
40757
|
+
}
|
|
40758
|
+
);
|
|
40415
40759
|
const priceMap = /* @__PURE__ */ new Map();
|
|
40416
40760
|
for (const t of resp.tickers || []) {
|
|
40417
40761
|
if (t.id && t.price != null) priceMap.set(t.id, t.price);
|
|
@@ -40426,39 +40770,58 @@ function registerQueryCommand(program3) {
|
|
|
40426
40770
|
} else {
|
|
40427
40771
|
for (const r3 of results) {
|
|
40428
40772
|
const priceStr = r3.priceUsd !== null ? `$${r3.priceUsd.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 6 })}` : "N/A";
|
|
40429
|
-
console.log(
|
|
40773
|
+
console.log(
|
|
40774
|
+
`${import_chalk6.default.bold(r3.token.padEnd(10))} ${priceStr.padStart(14)} ${import_chalk6.default.dim(r3.chain)}`
|
|
40775
|
+
);
|
|
40430
40776
|
}
|
|
40431
40777
|
}
|
|
40432
40778
|
return;
|
|
40433
40779
|
}
|
|
40434
40780
|
const chain2 = resolveChain(token);
|
|
40435
40781
|
const tokenInfo = resolveToken(token, chain2);
|
|
40436
|
-
let price =
|
|
40782
|
+
let price = null;
|
|
40783
|
+
if (tokenInfo) {
|
|
40784
|
+
price = await swapClient.getTokenPrice(chain2, tokenInfo.address);
|
|
40785
|
+
}
|
|
40437
40786
|
if (price === null) {
|
|
40438
|
-
const results = await swapClient.searchTokens({
|
|
40439
|
-
|
|
40440
|
-
|
|
40787
|
+
const results = await swapClient.searchTokens({
|
|
40788
|
+
query: token,
|
|
40789
|
+
chains: [chain2],
|
|
40790
|
+
limit: 5
|
|
40791
|
+
});
|
|
40792
|
+
const exact = results.find(
|
|
40793
|
+
(r3) => r3.symbol?.toUpperCase() === token.toUpperCase() && r3.priceUsd != null
|
|
40794
|
+
);
|
|
40795
|
+
if (exact) {
|
|
40796
|
+
price = exact.priceUsd;
|
|
40441
40797
|
}
|
|
40442
40798
|
}
|
|
40443
40799
|
if (price === null) {
|
|
40444
40800
|
error(`Token not found: ${token}`);
|
|
40445
|
-
if (opts.json)
|
|
40801
|
+
if (opts.json)
|
|
40802
|
+
output(
|
|
40803
|
+
{ token: token.toUpperCase(), chain: chain2, priceUsd: null, error: "Token not found" },
|
|
40804
|
+
true
|
|
40805
|
+
);
|
|
40446
40806
|
process.exitCode = 1;
|
|
40447
40807
|
return;
|
|
40448
40808
|
}
|
|
40449
40809
|
if (opts.history !== void 0) {
|
|
40810
|
+
if (!tokenInfo) {
|
|
40811
|
+
error(`Price history not available for "${token}" \u2014 token not locally resolved. Use an asset ID (e.g. c60) or contract address.`);
|
|
40812
|
+
process.exitCode = 1;
|
|
40813
|
+
return;
|
|
40814
|
+
}
|
|
40450
40815
|
const period = typeof opts.history === "string" ? opts.history : "week";
|
|
40451
40816
|
const chainInfo = getChainById(chain2);
|
|
40452
40817
|
if (!chainInfo) throw new Error(`Unknown chain: ${chain2}`);
|
|
40453
40818
|
const isNative = !tokenInfo.address || tokenInfo.address === NATIVE_TOKEN_ADDRESS || tokenInfo.address === SOLANA_NATIVE_ADDRESS;
|
|
40454
40819
|
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}`
|
|
40820
|
+
const chartData = await client.gateway("GET", `/v1/market/charts/${assetId}`, void 0, `currency=USD&period=${period}`);
|
|
40821
|
+
output(
|
|
40822
|
+
{ token: token.toUpperCase(), chain: chain2, priceUsd: price, history: chartData.prices },
|
|
40823
|
+
opts.json
|
|
40460
40824
|
);
|
|
40461
|
-
output({ token: token.toUpperCase(), chain: chain2, priceUsd: price, history: chartData.prices }, opts.json);
|
|
40462
40825
|
return;
|
|
40463
40826
|
}
|
|
40464
40827
|
if (opts.json) {
|
|
@@ -40482,7 +40845,9 @@ function registerQueryCommand(program3) {
|
|
|
40482
40845
|
if (/^\d+$/.test(trimmed)) return trimmed;
|
|
40483
40846
|
const chain2 = getChainById(trimmed);
|
|
40484
40847
|
if (chain2) return String(chain2.coinId);
|
|
40485
|
-
throw new Error(
|
|
40848
|
+
throw new Error(
|
|
40849
|
+
`Unknown network "${trimmed}". Use a supported chain name or numeric coin ID.`
|
|
40850
|
+
);
|
|
40486
40851
|
}).join(",");
|
|
40487
40852
|
}
|
|
40488
40853
|
const result = await swapClient.searchTokens({
|
|
@@ -40499,6 +40864,18 @@ function registerQueryCommand(program3) {
|
|
|
40499
40864
|
try {
|
|
40500
40865
|
const client = await getClient();
|
|
40501
40866
|
const service = new TransactionService(client);
|
|
40867
|
+
if (opts.chain && !getChainById(opts.chain)) {
|
|
40868
|
+
error(`Unknown chain: ${opts.chain}. Run 'twak chains' to list supported chains.`);
|
|
40869
|
+
if (opts.json) output({ error: `Unknown chain: ${opts.chain}` }, true);
|
|
40870
|
+
process.exitCode = 1;
|
|
40871
|
+
return;
|
|
40872
|
+
}
|
|
40873
|
+
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)) {
|
|
40874
|
+
error(`Invalid address: "${opts.address}"`);
|
|
40875
|
+
if (opts.json) output({ error: `Invalid address: "${opts.address}"` }, true);
|
|
40876
|
+
process.exitCode = 1;
|
|
40877
|
+
return;
|
|
40878
|
+
}
|
|
40502
40879
|
if (!opts.chain) {
|
|
40503
40880
|
await ensureTrustSDK();
|
|
40504
40881
|
const password = await resolvePassword(opts);
|
|
@@ -40550,7 +40927,9 @@ function registerQueryCommand(program3) {
|
|
|
40550
40927
|
const intPart = big / divisor;
|
|
40551
40928
|
const scale = 10000n;
|
|
40552
40929
|
const fracScaled = big % divisor * scale / divisor;
|
|
40553
|
-
const usdValue = price !== null && (intPart > 0n || fracScaled > 0n) && intPart <= BigInt(Number.MAX_SAFE_INTEGER) ? parseFloat(
|
|
40930
|
+
const usdValue = price !== null && (intPart > 0n || fracScaled > 0n) && intPart <= BigInt(Number.MAX_SAFE_INTEGER) ? parseFloat(
|
|
40931
|
+
((Number(intPart) + Number(fracScaled) / 1e4) * price).toFixed(4)
|
|
40932
|
+
) : void 0;
|
|
40554
40933
|
return { ...tx, usdValue };
|
|
40555
40934
|
} catch {
|
|
40556
40935
|
return tx;
|
|
@@ -40575,6 +40954,12 @@ function registerQueryCommand(program3) {
|
|
|
40575
40954
|
});
|
|
40576
40955
|
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
40956
|
try {
|
|
40957
|
+
if (!getChainById(opts.chain)) {
|
|
40958
|
+
error(`Unknown chain: ${opts.chain}. Run 'twak chains' to list supported chains.`);
|
|
40959
|
+
if (opts.json) output({ error: `Unknown chain: ${opts.chain}` }, true);
|
|
40960
|
+
process.exitCode = 1;
|
|
40961
|
+
return;
|
|
40962
|
+
}
|
|
40578
40963
|
let address = opts.address;
|
|
40579
40964
|
if (!address) {
|
|
40580
40965
|
try {
|
|
@@ -40589,11 +40974,14 @@ function registerQueryCommand(program3) {
|
|
|
40589
40974
|
await ensureTrustSDK();
|
|
40590
40975
|
const blockchainClient = new BlockchainClient();
|
|
40591
40976
|
const status = await blockchainClient.findTransactionStatus(opts.chain, address, hash);
|
|
40592
|
-
output(
|
|
40593
|
-
|
|
40594
|
-
|
|
40595
|
-
|
|
40596
|
-
|
|
40977
|
+
output(
|
|
40978
|
+
{
|
|
40979
|
+
hash,
|
|
40980
|
+
chain: opts.chain,
|
|
40981
|
+
...status
|
|
40982
|
+
},
|
|
40983
|
+
opts.json
|
|
40984
|
+
);
|
|
40597
40985
|
} else {
|
|
40598
40986
|
const client = await getClient();
|
|
40599
40987
|
const service = new TransactionService(client);
|
|
@@ -40607,11 +40995,37 @@ function registerQueryCommand(program3) {
|
|
|
40607
40995
|
program3.command("chains").description("List supported chains").option("--json", "JSON output").action(async (opts) => {
|
|
40608
40996
|
const chainEntries = Object.entries(CHAINS);
|
|
40609
40997
|
if (opts.json) {
|
|
40610
|
-
output(
|
|
40998
|
+
output(
|
|
40999
|
+
chainEntries.map(([key, chain2]) => ({
|
|
41000
|
+
key,
|
|
41001
|
+
name: chain2.name,
|
|
41002
|
+
symbol: chain2.symbol,
|
|
41003
|
+
namespace: chain2.namespace,
|
|
41004
|
+
chainId: chain2.id,
|
|
41005
|
+
coinId: chain2.coinId
|
|
41006
|
+
})),
|
|
41007
|
+
true
|
|
41008
|
+
);
|
|
40611
41009
|
} else {
|
|
40612
|
-
|
|
40613
|
-
|
|
40614
|
-
|
|
41010
|
+
const rows = chainEntries.map(([key, chain2]) => ({
|
|
41011
|
+
key,
|
|
41012
|
+
name: chain2.name,
|
|
41013
|
+
symbol: chain2.symbol,
|
|
41014
|
+
namespace: chain2.namespace
|
|
41015
|
+
}));
|
|
41016
|
+
const keyW = Math.max(...rows.map((r3) => r3.key.length), 3);
|
|
41017
|
+
const nameW = Math.max(...rows.map((r3) => r3.name.length), 4);
|
|
41018
|
+
const symW = Math.max(...rows.map((r3) => r3.symbol.length), 6);
|
|
41019
|
+
const nsW = Math.max(...rows.map((r3) => r3.namespace.length), 9);
|
|
41020
|
+
console.log(
|
|
41021
|
+
import_chalk6.default.bold(
|
|
41022
|
+
`${"KEY".padEnd(keyW)} ${"NAME".padEnd(nameW)} ${"SYMBOL".padEnd(symW)} NAMESPACE`
|
|
41023
|
+
)
|
|
41024
|
+
);
|
|
41025
|
+
for (const r3 of rows) {
|
|
41026
|
+
console.log(
|
|
41027
|
+
`${r3.key.padEnd(keyW)} ${r3.name.padEnd(nameW)} ${r3.symbol.padEnd(symW)} ${r3.namespace.padEnd(nsW)}`
|
|
41028
|
+
);
|
|
40615
41029
|
}
|
|
40616
41030
|
}
|
|
40617
41031
|
});
|
|
@@ -40650,7 +41064,11 @@ function registerQueryCommand(program3) {
|
|
|
40650
41064
|
const apiClient = await getClient();
|
|
40651
41065
|
const swapClient = new SwapClient(apiClient);
|
|
40652
41066
|
const chainKey = opts.chain || Object.entries(CHAINS).find(([, c3]) => c3.coinId === coinId)?.[0] || "";
|
|
40653
|
-
const tokens = await swapClient.searchTokens({
|
|
41067
|
+
const tokens = await swapClient.searchTokens({
|
|
41068
|
+
query: opts.token,
|
|
41069
|
+
chains: [chainKey],
|
|
41070
|
+
limit: 1
|
|
41071
|
+
});
|
|
40654
41072
|
if (tokens.length > 0) {
|
|
40655
41073
|
decimals = tokens[0].decimals ?? 18;
|
|
40656
41074
|
symbol = tokens[0].symbol ?? opts.token;
|
|
@@ -40665,20 +41083,24 @@ function registerQueryCommand(program3) {
|
|
|
40665
41083
|
const div = 10n ** BigInt(decimals);
|
|
40666
41084
|
return `${big / div}.${(big % div).toString().padStart(decimals, "0").slice(0, 6)}`;
|
|
40667
41085
|
};
|
|
40668
|
-
output(
|
|
40669
|
-
|
|
40670
|
-
|
|
40671
|
-
|
|
40672
|
-
|
|
40673
|
-
|
|
40674
|
-
|
|
40675
|
-
|
|
40676
|
-
|
|
40677
|
-
|
|
40678
|
-
|
|
41086
|
+
output(
|
|
41087
|
+
{
|
|
41088
|
+
address: opts.address,
|
|
41089
|
+
chain: chain2.id,
|
|
41090
|
+
symbol,
|
|
41091
|
+
...opts.token ? { token: opts.token } : {},
|
|
41092
|
+
available: fmt(balance.available),
|
|
41093
|
+
...balance.staked ? { staked: fmt(balance.staked) } : {},
|
|
41094
|
+
...balance.pending ? { pending: fmt(balance.pending) } : {},
|
|
41095
|
+
total: fmt(balance.total),
|
|
41096
|
+
raw: balance
|
|
41097
|
+
},
|
|
41098
|
+
opts.json
|
|
41099
|
+
);
|
|
40679
41100
|
} catch (err) {
|
|
40680
41101
|
handleError(err, opts.json);
|
|
40681
41102
|
}
|
|
41103
|
+
safeExit();
|
|
40682
41104
|
});
|
|
40683
41105
|
program3.command("asset <assetId>").description("Get asset info").option("--json", "JSON output").action(async (assetId, opts) => {
|
|
40684
41106
|
try {
|
|
@@ -40692,6 +41114,12 @@ function registerQueryCommand(program3) {
|
|
|
40692
41114
|
});
|
|
40693
41115
|
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
41116
|
try {
|
|
41117
|
+
if (!opts.chain && !opts.assetId) {
|
|
41118
|
+
error("Provide --chain <key> or --asset-id <id> to validate an address");
|
|
41119
|
+
if (opts.json) output({ error: "--chain or --asset-id required" }, true);
|
|
41120
|
+
process.exitCode = 1;
|
|
41121
|
+
return;
|
|
41122
|
+
}
|
|
40695
41123
|
let assetId = opts.assetId;
|
|
40696
41124
|
if (!assetId && opts.chain) {
|
|
40697
41125
|
const chain2 = getChainById(opts.chain);
|
|
@@ -40711,7 +41139,12 @@ function registerQueryCommand(program3) {
|
|
|
40711
41139
|
if (!isValid2) {
|
|
40712
41140
|
const chainInfo = getChainByCoinId(coinId);
|
|
40713
41141
|
const label = chainInfo?.name ?? `coin ${coinId}`;
|
|
40714
|
-
const result = {
|
|
41142
|
+
const result = {
|
|
41143
|
+
status: "error",
|
|
41144
|
+
valid: false,
|
|
41145
|
+
chain: chainInfo?.id,
|
|
41146
|
+
message: `Invalid address format for ${label}`
|
|
41147
|
+
};
|
|
40715
41148
|
output(result, opts.json);
|
|
40716
41149
|
return;
|
|
40717
41150
|
}
|
|
@@ -40733,7 +41166,10 @@ function registerQueryCommand(program3) {
|
|
|
40733
41166
|
handleError(err, opts.json);
|
|
40734
41167
|
}
|
|
40735
41168
|
});
|
|
40736
|
-
program3.command("trending").description("Get trending tokens by market activity or category").option(
|
|
41169
|
+
program3.command("trending").description("Get trending tokens by market activity or category").option(
|
|
41170
|
+
"--category <id>",
|
|
41171
|
+
"Category: ai, rwa, memes, defi, dex, bnb, eth, sol, pumpfun, bonk, launchpad, launchpool, layer1"
|
|
41172
|
+
).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
41173
|
try {
|
|
40738
41174
|
const client = await getClient();
|
|
40739
41175
|
const service = new SecurityService(client);
|
|
@@ -40757,15 +41193,13 @@ function registerQueryCommand(program3) {
|
|
|
40757
41193
|
launchpool: "binance-launchpool",
|
|
40758
41194
|
layer1: "layer_1"
|
|
40759
41195
|
};
|
|
40760
|
-
|
|
40761
|
-
|
|
40762
|
-
|
|
40763
|
-
|
|
40764
|
-
|
|
40765
|
-
|
|
40766
|
-
|
|
40767
|
-
tokens = await service.getTrendingTokens(parseInt(opts.limit, 10), sort);
|
|
40768
|
-
}
|
|
41196
|
+
const categoryId = opts.category ? categoryMap[opts.category] ?? opts.category : void 0;
|
|
41197
|
+
const sort = opts.sort ? sortMap[opts.sort] ?? opts.sort : "percent_change_24h";
|
|
41198
|
+
const tokens = await service.getAssetListings({
|
|
41199
|
+
categoryId,
|
|
41200
|
+
sort,
|
|
41201
|
+
limit: parseInt(opts.limit, 10)
|
|
41202
|
+
});
|
|
40769
41203
|
const seen = /* @__PURE__ */ new Map();
|
|
40770
41204
|
for (const t of tokens) {
|
|
40771
41205
|
const key = t.symbol.toUpperCase();
|
|
@@ -40781,14 +41215,19 @@ function registerQueryCommand(program3) {
|
|
|
40781
41215
|
for (const t of deduped) {
|
|
40782
41216
|
const price = t.priceUsd != null ? `$${t.priceUsd.toFixed(4)}` : "N/A";
|
|
40783
41217
|
const change = t.priceChange24h != null ? `${t.priceChange24h > 0 ? "+" : ""}${t.priceChange24h.toFixed(2)}%` : "";
|
|
40784
|
-
console.log(
|
|
41218
|
+
console.log(
|
|
41219
|
+
`${t.symbol.padEnd(10)} ${t.name.padEnd(25)} ${price.padStart(12)} ${change.padStart(8)}`
|
|
41220
|
+
);
|
|
40785
41221
|
}
|
|
40786
41222
|
}
|
|
40787
41223
|
} catch (err) {
|
|
40788
41224
|
handleError(err, opts.json);
|
|
40789
41225
|
}
|
|
40790
41226
|
});
|
|
40791
|
-
program3.command("dapps").description("Browse DeFi protocols and DApps (data from DeFi Llama)").option(
|
|
41227
|
+
program3.command("dapps").description("Browse DeFi protocols and DApps (data from DeFi Llama)").option(
|
|
41228
|
+
"--category <name>",
|
|
41229
|
+
"Filter by category (e.g., Lending, Dexs, Liquid Staking, Bridge, Yield)"
|
|
41230
|
+
).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
41231
|
try {
|
|
40793
41232
|
const { DappsService: DappsService2 } = await Promise.resolve().then(() => (init_dapps_service(), dapps_service_exports));
|
|
40794
41233
|
const service = new DappsService2();
|
|
@@ -40808,12 +41247,16 @@ function registerQueryCommand(program3) {
|
|
|
40808
41247
|
if (dapps.length === 0) {
|
|
40809
41248
|
console.log("No DApps found.");
|
|
40810
41249
|
} else {
|
|
40811
|
-
console.log(
|
|
41250
|
+
console.log(
|
|
41251
|
+
"Name".padEnd(25) + "Category".padEnd(20) + "TVL".padStart(15) + " Chains"
|
|
41252
|
+
);
|
|
40812
41253
|
console.log("\u2500".repeat(80));
|
|
40813
41254
|
for (const d3 of dapps) {
|
|
40814
41255
|
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
41256
|
const chains = d3.chains.slice(0, 5).join(", ") + (d3.chains.length > 5 ? ` +${d3.chains.length - 5}` : "");
|
|
40816
|
-
console.log(
|
|
41257
|
+
console.log(
|
|
41258
|
+
`${d3.name.slice(0, 24).padEnd(25)}${d3.category.slice(0, 19).padEnd(20)}${tvl.padStart(15)} ${chains}`
|
|
41259
|
+
);
|
|
40817
41260
|
}
|
|
40818
41261
|
}
|
|
40819
41262
|
}
|
|
@@ -45220,7 +45663,9 @@ var SwapActionProvider = class extends ActionProvider {
|
|
|
45220
45663
|
return { success: false, error: `Slippage must be greater than 0% and at most 50%. Got: ${input.slippage}` };
|
|
45221
45664
|
}
|
|
45222
45665
|
const fromTokenInfo = resolveToken(input.fromToken, input.fromChain);
|
|
45666
|
+
if (!fromTokenInfo) return { success: false, error: `Unknown token: ${input.fromToken} on ${input.fromChain}` };
|
|
45223
45667
|
const toTokenInfo = resolveToken(input.toToken, input.toChain);
|
|
45668
|
+
if (!toTokenInfo) return { success: false, error: `Unknown token: ${input.toToken} on ${input.toChain}` };
|
|
45224
45669
|
const parsedAmount = parseAmount(input.amount, fromTokenInfo.decimals);
|
|
45225
45670
|
const address = await signer.getAddress(input.fromChain);
|
|
45226
45671
|
const quote = await this.swapClient.getQuote({
|
|
@@ -45273,7 +45718,9 @@ var SwapActionProvider = class extends ActionProvider {
|
|
|
45273
45718
|
invoke: async (_ctx, input) => {
|
|
45274
45719
|
if (!this.swapClient) return { success: false, error: "Swap client required" };
|
|
45275
45720
|
const fromTokenInfo = resolveToken(input.fromToken, input.fromChain);
|
|
45721
|
+
if (!fromTokenInfo) return { success: false, error: `Unknown token: ${input.fromToken} on ${input.fromChain}` };
|
|
45276
45722
|
const toTokenInfo = resolveToken(input.toToken, input.toChain);
|
|
45723
|
+
if (!toTokenInfo) return { success: false, error: `Unknown token: ${input.toToken} on ${input.toChain}` };
|
|
45277
45724
|
const parsedAmount = parseAmount(input.amount, fromTokenInfo.decimals);
|
|
45278
45725
|
const quote = await this.swapClient.getQuote({
|
|
45279
45726
|
fromChain: input.fromChain,
|
|
@@ -45310,6 +45757,7 @@ var SwapActionProvider = class extends ActionProvider {
|
|
|
45310
45757
|
invoke: async (_ctx, input) => {
|
|
45311
45758
|
if (!this.swapClient) return { success: false, error: "Swap client required" };
|
|
45312
45759
|
const tokenInfo = resolveToken(input.token, input.chain);
|
|
45760
|
+
if (!tokenInfo) return { success: false, error: `Unknown token: ${input.token} on ${input.chain}` };
|
|
45313
45761
|
const price = await this.swapClient.getTokenPrice(input.chain, tokenInfo.address);
|
|
45314
45762
|
if (price === null) return { success: false, error: `Price not found for ${input.token}` };
|
|
45315
45763
|
return { success: true, token: input.token.toUpperCase(), chain: input.chain, priceUsd: price };
|
|
@@ -45745,7 +46193,7 @@ var MarketActionProvider = class extends ActionProvider {
|
|
|
45745
46193
|
limit: external_exports.coerce.number().optional().default(20).describe("Number of tokens to return (default 20)")
|
|
45746
46194
|
}),
|
|
45747
46195
|
invoke: async (ctx, input) => {
|
|
45748
|
-
const tokens = await this.getService(ctx).
|
|
46196
|
+
const tokens = await this.getService(ctx).getAssetListings({ limit: input.limit });
|
|
45749
46197
|
return { success: true, count: tokens.length, tokens };
|
|
45750
46198
|
}
|
|
45751
46199
|
};
|
|
@@ -45908,7 +46356,9 @@ var AutomateActionProvider = class extends ActionProvider {
|
|
|
45908
46356
|
if (!record2) return { success: false, error: `Automation ${input.id} not found` };
|
|
45909
46357
|
if (!record2.active) return { success: false, error: `Automation ${input.id} is inactive` };
|
|
45910
46358
|
const fromTokenInfo = resolveToken(record2.fromToken, record2.chain);
|
|
46359
|
+
if (!fromTokenInfo) return { success: false, error: `Unknown token: ${record2.fromToken} on ${record2.chain}` };
|
|
45911
46360
|
const toTokenInfo = resolveToken(record2.toToken, record2.chain);
|
|
46361
|
+
if (!toTokenInfo) return { success: false, error: `Unknown token: ${record2.toToken} on ${record2.chain}` };
|
|
45912
46362
|
const parsedAmount = parseAmount(record2.amount, fromTokenInfo.decimals);
|
|
45913
46363
|
const address = await this.walletProvider.getAddress(record2.chain);
|
|
45914
46364
|
const quote = await this.swapClient.getQuote({
|
|
@@ -53051,7 +53501,7 @@ var Protocol = class {
|
|
|
53051
53501
|
return;
|
|
53052
53502
|
}
|
|
53053
53503
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
53054
|
-
await new Promise((
|
|
53504
|
+
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
53055
53505
|
options?.signal?.throwIfAborted();
|
|
53056
53506
|
}
|
|
53057
53507
|
} catch (error3) {
|
|
@@ -53068,7 +53518,7 @@ var Protocol = class {
|
|
|
53068
53518
|
*/
|
|
53069
53519
|
request(request, resultSchema, options) {
|
|
53070
53520
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
53071
|
-
return new Promise((
|
|
53521
|
+
return new Promise((resolve2, reject) => {
|
|
53072
53522
|
const earlyReject = (error3) => {
|
|
53073
53523
|
reject(error3);
|
|
53074
53524
|
};
|
|
@@ -53146,7 +53596,7 @@ var Protocol = class {
|
|
|
53146
53596
|
if (!parseResult.success) {
|
|
53147
53597
|
reject(parseResult.error);
|
|
53148
53598
|
} else {
|
|
53149
|
-
|
|
53599
|
+
resolve2(parseResult.data);
|
|
53150
53600
|
}
|
|
53151
53601
|
} catch (error3) {
|
|
53152
53602
|
reject(error3);
|
|
@@ -53407,12 +53857,12 @@ var Protocol = class {
|
|
|
53407
53857
|
}
|
|
53408
53858
|
} catch {
|
|
53409
53859
|
}
|
|
53410
|
-
return new Promise((
|
|
53860
|
+
return new Promise((resolve2, reject) => {
|
|
53411
53861
|
if (signal.aborted) {
|
|
53412
53862
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
53413
53863
|
return;
|
|
53414
53864
|
}
|
|
53415
|
-
const timeoutId = setTimeout(
|
|
53865
|
+
const timeoutId = setTimeout(resolve2, interval);
|
|
53416
53866
|
signal.addEventListener("abort", () => {
|
|
53417
53867
|
clearTimeout(timeoutId);
|
|
53418
53868
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -54282,12 +54732,12 @@ var StdioServerTransport = class {
|
|
|
54282
54732
|
this.onclose?.();
|
|
54283
54733
|
}
|
|
54284
54734
|
send(message) {
|
|
54285
|
-
return new Promise((
|
|
54735
|
+
return new Promise((resolve2) => {
|
|
54286
54736
|
const json = serializeMessage(message);
|
|
54287
54737
|
if (this._stdout.write(json)) {
|
|
54288
|
-
|
|
54738
|
+
resolve2();
|
|
54289
54739
|
} else {
|
|
54290
|
-
this._stdout.once("drain",
|
|
54740
|
+
this._stdout.once("drain", resolve2);
|
|
54291
54741
|
}
|
|
54292
54742
|
});
|
|
54293
54743
|
}
|
|
@@ -54598,7 +55048,7 @@ var TwakRestServer = class {
|
|
|
54598
55048
|
};
|
|
54599
55049
|
}
|
|
54600
55050
|
start() {
|
|
54601
|
-
return new Promise((
|
|
55051
|
+
return new Promise((resolve2, reject) => {
|
|
54602
55052
|
const server = (0, import_http.createServer)((req, res) => this.handleRequest(req, res));
|
|
54603
55053
|
this.httpServer = server;
|
|
54604
55054
|
server.on("error", (err) => {
|
|
@@ -54609,7 +55059,9 @@ var TwakRestServer = class {
|
|
|
54609
55059
|
}
|
|
54610
55060
|
});
|
|
54611
55061
|
server.listen(this.config.port, this.config.host, () => {
|
|
54612
|
-
console.error(
|
|
55062
|
+
console.error(
|
|
55063
|
+
`TWAK REST server listening on http://${this.config.host}:${this.config.port}`
|
|
55064
|
+
);
|
|
54613
55065
|
console.error("Endpoints: POST /actions/:name");
|
|
54614
55066
|
if (this.config.apiKey) {
|
|
54615
55067
|
console.error("API key authentication enabled (use Authorization: Bearer <HMAC_SECRET>)");
|
|
@@ -54617,28 +55069,38 @@ var TwakRestServer = class {
|
|
|
54617
55069
|
console.error("WARNING: No API key configured \u2014 REST server is unauthenticated");
|
|
54618
55070
|
}
|
|
54619
55071
|
if (this.config.paymentRequired) {
|
|
54620
|
-
console.error(
|
|
55072
|
+
console.error(
|
|
55073
|
+
`x402 payment required: ${this.config.paymentAmount} ${this.config.paymentAsset} on ${this.config.paymentChainId}`
|
|
55074
|
+
);
|
|
54621
55075
|
}
|
|
54622
|
-
|
|
55076
|
+
resolve2();
|
|
54623
55077
|
});
|
|
54624
55078
|
});
|
|
54625
55079
|
}
|
|
54626
55080
|
stop() {
|
|
54627
|
-
return new Promise((
|
|
55081
|
+
return new Promise((resolve2) => {
|
|
54628
55082
|
if (this.httpServer?.listening) {
|
|
54629
55083
|
this.httpServer.close(() => {
|
|
54630
55084
|
this.httpServer = null;
|
|
54631
|
-
|
|
55085
|
+
resolve2();
|
|
54632
55086
|
});
|
|
54633
55087
|
} else {
|
|
54634
55088
|
this.httpServer = null;
|
|
54635
|
-
|
|
55089
|
+
resolve2();
|
|
54636
55090
|
}
|
|
54637
55091
|
});
|
|
54638
55092
|
}
|
|
54639
55093
|
async handleRequest(req, res) {
|
|
54640
55094
|
res.setHeader("Content-Type", "application/json");
|
|
54641
|
-
|
|
55095
|
+
const origin = req.headers.origin || "";
|
|
55096
|
+
try {
|
|
55097
|
+
const parsed = new URL(origin);
|
|
55098
|
+
if (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "::1") {
|
|
55099
|
+
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
55100
|
+
res.setHeader("Vary", "Origin");
|
|
55101
|
+
}
|
|
55102
|
+
} catch {
|
|
55103
|
+
}
|
|
54642
55104
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
54643
55105
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Payment, Authorization");
|
|
54644
55106
|
if (req.method === "OPTIONS") {
|
|
@@ -54656,16 +55118,20 @@ var TwakRestServer = class {
|
|
|
54656
55118
|
const tokenValid = token && token.length === expected.length && (0, import_crypto5.timingSafeEqual)(Buffer.from(token), Buffer.from(expected));
|
|
54657
55119
|
if (!tokenValid) {
|
|
54658
55120
|
res.writeHead(401);
|
|
54659
|
-
res.end(
|
|
55121
|
+
res.end(
|
|
55122
|
+
JSON.stringify({ error: "Unauthorized \u2014 provide Authorization: Bearer <HMAC_SECRET>" })
|
|
55123
|
+
);
|
|
54660
55124
|
return;
|
|
54661
55125
|
}
|
|
54662
55126
|
}
|
|
54663
55127
|
if (req.method === "GET" && pathname === "/actions") {
|
|
54664
55128
|
const actions = this.registry.getActions();
|
|
54665
55129
|
res.writeHead(200);
|
|
54666
|
-
res.end(
|
|
54667
|
-
|
|
54668
|
-
|
|
55130
|
+
res.end(
|
|
55131
|
+
JSON.stringify({
|
|
55132
|
+
actions: actions.map((a3) => ({ name: a3.name, description: a3.description }))
|
|
55133
|
+
})
|
|
55134
|
+
);
|
|
54669
55135
|
return;
|
|
54670
55136
|
}
|
|
54671
55137
|
const match = pathname.match(/^\/actions\/([^/]+)$/);
|
|
@@ -54678,31 +55144,44 @@ var TwakRestServer = class {
|
|
|
54678
55144
|
if (this.config.paymentRequired) {
|
|
54679
55145
|
const paymentHeader = req.headers["x-payment"];
|
|
54680
55146
|
if (!paymentHeader) {
|
|
54681
|
-
res.setHeader(
|
|
55147
|
+
res.setHeader(
|
|
55148
|
+
"WWW-Authenticate",
|
|
55149
|
+
`x402 realm="twak", asset="${this.config.paymentAsset}", amount="${this.config.paymentAmount}", chain="${this.config.paymentChainId}"`
|
|
55150
|
+
);
|
|
54682
55151
|
res.writeHead(402);
|
|
54683
|
-
res.end(
|
|
54684
|
-
|
|
54685
|
-
|
|
54686
|
-
|
|
54687
|
-
|
|
54688
|
-
|
|
54689
|
-
|
|
54690
|
-
|
|
55152
|
+
res.end(
|
|
55153
|
+
JSON.stringify({
|
|
55154
|
+
error: "payment_required",
|
|
55155
|
+
message: `This endpoint requires payment of ${this.config.paymentAmount} ${this.config.paymentAsset}`,
|
|
55156
|
+
asset: this.config.paymentAsset,
|
|
55157
|
+
amount: this.config.paymentAmount,
|
|
55158
|
+
chainId: this.config.paymentChainId,
|
|
55159
|
+
recipient: this.config.paymentRecipient || void 0
|
|
55160
|
+
})
|
|
55161
|
+
);
|
|
54691
55162
|
return;
|
|
54692
55163
|
}
|
|
54693
55164
|
if (this.config.paymentRecipient) {
|
|
54694
|
-
const result = await verifyPayment(
|
|
54695
|
-
|
|
54696
|
-
|
|
54697
|
-
|
|
54698
|
-
|
|
54699
|
-
|
|
55165
|
+
const result = await verifyPayment(
|
|
55166
|
+
paymentHeader,
|
|
55167
|
+
{
|
|
55168
|
+
asset: this.config.paymentAsset,
|
|
55169
|
+
amount: this.config.paymentAmount,
|
|
55170
|
+
chainId: this.config.paymentChainId,
|
|
55171
|
+
recipient: this.config.paymentRecipient
|
|
55172
|
+
},
|
|
55173
|
+
this.context.httpClient,
|
|
55174
|
+
this.seenHashes,
|
|
55175
|
+
this.config.blockchainClient
|
|
55176
|
+
);
|
|
54700
55177
|
if (!result.valid) {
|
|
54701
55178
|
res.writeHead(402);
|
|
54702
|
-
res.end(
|
|
54703
|
-
|
|
54704
|
-
|
|
54705
|
-
|
|
55179
|
+
res.end(
|
|
55180
|
+
JSON.stringify({
|
|
55181
|
+
error: "payment_verification_failed",
|
|
55182
|
+
message: result.error ?? "Payment could not be verified"
|
|
55183
|
+
})
|
|
55184
|
+
);
|
|
54706
55185
|
return;
|
|
54707
55186
|
}
|
|
54708
55187
|
}
|
|
@@ -54857,6 +55336,11 @@ function registerAutomateCommand(program3) {
|
|
|
54857
55336
|
const automate = program3.command("automate").description("Manage DCA and limit order automations");
|
|
54858
55337
|
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
55338
|
try {
|
|
55339
|
+
if (opts.interval && opts.price) {
|
|
55340
|
+
error("Provide --interval (DCA) or --price (limit order), not both");
|
|
55341
|
+
process.exitCode = 1;
|
|
55342
|
+
return;
|
|
55343
|
+
}
|
|
54860
55344
|
if (!opts.interval && !opts.price) {
|
|
54861
55345
|
error("Provide --interval (DCA) or --price (limit order)");
|
|
54862
55346
|
process.exitCode = 1;
|
|
@@ -54873,6 +55357,13 @@ function registerAutomateCommand(program3) {
|
|
|
54873
55357
|
process.exitCode = 1;
|
|
54874
55358
|
return;
|
|
54875
55359
|
}
|
|
55360
|
+
const SWAP_SUPPORTED_NAMESPACES = ["eip155", "solana"];
|
|
55361
|
+
const chainInfo = getChainById(opts.chain);
|
|
55362
|
+
if (chainInfo && !SWAP_SUPPORTED_NAMESPACES.includes(chainInfo.namespace)) {
|
|
55363
|
+
error(`${chainInfo.name} automations are not supported. Use an EVM chain or Solana.`);
|
|
55364
|
+
process.exitCode = 1;
|
|
55365
|
+
return;
|
|
55366
|
+
}
|
|
54876
55367
|
let intervalMs;
|
|
54877
55368
|
if (opts.interval) {
|
|
54878
55369
|
const parsed = parseInterval(opts.interval);
|
|
@@ -54915,9 +55406,13 @@ function registerAutomateCommand(program3) {
|
|
|
54915
55406
|
});
|
|
54916
55407
|
success("Automation created!");
|
|
54917
55408
|
if (opts.interval && intervalMs) {
|
|
54918
|
-
info(
|
|
55409
|
+
info(
|
|
55410
|
+
`DCA: ${opts.amount} ${opts.from} \u2192 ${opts.to} every ${formatInterval(intervalMs)} on ${opts.chain}`
|
|
55411
|
+
);
|
|
54919
55412
|
} else {
|
|
54920
|
-
info(
|
|
55413
|
+
info(
|
|
55414
|
+
`Limit: swap ${opts.amount} ${opts.from} \u2192 ${opts.to} when price ${opts.condition} $${opts.price} on ${opts.chain}`
|
|
55415
|
+
);
|
|
54921
55416
|
}
|
|
54922
55417
|
output(record2, opts.json);
|
|
54923
55418
|
} catch (err) {
|
|
@@ -54927,11 +55422,10 @@ function registerAutomateCommand(program3) {
|
|
|
54927
55422
|
process.exitCode = 1;
|
|
54928
55423
|
}
|
|
54929
55424
|
});
|
|
54930
|
-
automate.command("list").description("List
|
|
55425
|
+
automate.command("list").description("List automations").option("--json", "JSON output").action(async (opts) => {
|
|
54931
55426
|
try {
|
|
54932
55427
|
const store = new AutomationStore();
|
|
54933
|
-
const
|
|
54934
|
-
const records = opts.all ? all : all.filter((a3) => a3.active);
|
|
55428
|
+
const records = await store.list();
|
|
54935
55429
|
if (opts.json) {
|
|
54936
55430
|
const enriched = records.map((r3) => {
|
|
54937
55431
|
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 +55455,14 @@ function registerAutomateCommand(program3) {
|
|
|
54961
55455
|
const nextAt = new Date(r3.lastRunAt).getTime() + r3.intervalMs;
|
|
54962
55456
|
nextPart = `next: ${formatRelativeTime(nextAt - Date.now())}`;
|
|
54963
55457
|
}
|
|
54964
|
-
|
|
55458
|
+
const statusTag = r3.active ? "" : " [paused]";
|
|
55459
|
+
console.log(
|
|
55460
|
+
`[${tag}] ${label} ${pair} every ${interval} \u2014 ${lastPart}, ${nextPart}${statusTag}`
|
|
55461
|
+
);
|
|
54965
55462
|
} else {
|
|
54966
|
-
const cond = `${r3.
|
|
54967
|
-
|
|
55463
|
+
const cond = `${r3.toToken} ${r3.condition} $${r3.targetPrice}`;
|
|
55464
|
+
const statusTag = r3.active ? "" : " [paused]";
|
|
55465
|
+
console.log(`[${tag}] ${label} ${pair} when ${cond} \u2014 waiting${statusTag}`);
|
|
54968
55466
|
}
|
|
54969
55467
|
}
|
|
54970
55468
|
} catch (err) {
|
|
@@ -55029,66 +55527,6 @@ function registerAutomateCommand(program3) {
|
|
|
55029
55527
|
});
|
|
55030
55528
|
}
|
|
55031
55529
|
|
|
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
55530
|
// src/commands/x402.ts
|
|
55093
55531
|
init_dist2();
|
|
55094
55532
|
init_dist4();
|
|
@@ -55121,7 +55559,7 @@ async function validateX402Url(urlStr) {
|
|
|
55121
55559
|
if (parsed.protocol !== "https:") {
|
|
55122
55560
|
throw new Error(`x402: only https:// URLs are allowed (got ${parsed.protocol}//)`);
|
|
55123
55561
|
}
|
|
55124
|
-
const h4 = parsed.hostname;
|
|
55562
|
+
const h4 = parsed.hostname.replace(/^\[|\]$/g, "");
|
|
55125
55563
|
if (h4 === "localhost" || isPrivateIp(h4)) {
|
|
55126
55564
|
throw new Error(`x402: private/loopback addresses are not allowed (${h4})`);
|
|
55127
55565
|
}
|
|
@@ -55130,7 +55568,9 @@ async function validateX402Url(urlStr) {
|
|
|
55130
55568
|
const records = await dns.lookup(h4, { all: true });
|
|
55131
55569
|
for (const record2 of records) {
|
|
55132
55570
|
if (isPrivateIp(record2.address)) {
|
|
55133
|
-
throw new Error(
|
|
55571
|
+
throw new Error(
|
|
55572
|
+
`x402: "${h4}" resolves to a private/loopback address (${record2.address}) \u2014 not allowed`
|
|
55573
|
+
);
|
|
55134
55574
|
}
|
|
55135
55575
|
}
|
|
55136
55576
|
} catch (err) {
|
|
@@ -55139,7 +55579,14 @@ async function validateX402Url(urlStr) {
|
|
|
55139
55579
|
}
|
|
55140
55580
|
function registerX402Command(program3) {
|
|
55141
55581
|
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(
|
|
55582
|
+
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(
|
|
55583
|
+
"--max-payment <amount>",
|
|
55584
|
+
"Maximum payment amount to auto-approve (default: 0.01)",
|
|
55585
|
+
"0.01"
|
|
55586
|
+
).option("--yes", "Skip confirmation prompt and auto-approve payment up to --max-payment").option(
|
|
55587
|
+
"--password <password>",
|
|
55588
|
+
"Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
|
|
55589
|
+
).option("--json", "JSON output").action(async (url, opts) => {
|
|
55143
55590
|
try {
|
|
55144
55591
|
await validateX402Url(url);
|
|
55145
55592
|
await ensureTrustSDK();
|
|
@@ -55147,7 +55594,7 @@ function registerX402Command(program3) {
|
|
|
55147
55594
|
const provider = await AgentWalletProvider.fromStorage(password);
|
|
55148
55595
|
const init3 = {
|
|
55149
55596
|
method: opts.method.toUpperCase(),
|
|
55150
|
-
headers: { "Content-Type": "application/json",
|
|
55597
|
+
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
55151
55598
|
body: opts.body || void 0
|
|
55152
55599
|
};
|
|
55153
55600
|
let resp = await fetch(url, init3);
|
|
@@ -55160,7 +55607,9 @@ function registerX402Command(program3) {
|
|
|
55160
55607
|
if (!recipient) throw new Error("x402: no recipient address in 402 response");
|
|
55161
55608
|
const maxPayment = parseFloat(opts.maxPayment);
|
|
55162
55609
|
if (!Number.isFinite(maxPayment) || maxPayment <= 0) {
|
|
55163
|
-
throw new Error(
|
|
55610
|
+
throw new Error(
|
|
55611
|
+
`x402: invalid --max-payment value "${opts.maxPayment}" \u2014 must be a positive number`
|
|
55612
|
+
);
|
|
55164
55613
|
}
|
|
55165
55614
|
const requestedAmount = parseFloat(amount);
|
|
55166
55615
|
if (isNaN(requestedAmount) || requestedAmount <= 0) {
|
|
@@ -55174,7 +55623,7 @@ function registerX402Command(program3) {
|
|
|
55174
55623
|
if (!opts.yes) {
|
|
55175
55624
|
const readline = await import("readline");
|
|
55176
55625
|
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
55177
|
-
const confirmed = await new Promise((
|
|
55626
|
+
const confirmed = await new Promise((resolve2) => {
|
|
55178
55627
|
rl.question(
|
|
55179
55628
|
`
|
|
55180
55629
|
Pay ${amount} ${asset.toUpperCase()} on ${chainId}
|
|
@@ -55182,7 +55631,7 @@ Recipient: ${recipient}
|
|
|
55182
55631
|
Confirm? [y/N] `,
|
|
55183
55632
|
(answer) => {
|
|
55184
55633
|
rl.close();
|
|
55185
|
-
|
|
55634
|
+
resolve2(answer.toLowerCase() === "y");
|
|
55186
55635
|
}
|
|
55187
55636
|
);
|
|
55188
55637
|
});
|
|
@@ -55210,7 +55659,9 @@ Confirm? [y/N] `,
|
|
|
55210
55659
|
}
|
|
55211
55660
|
const txResult = await bc2.transfer({ assetId, to: recipient, amount, hdWallet });
|
|
55212
55661
|
info(`Payment sent: ${txResult.hash}`);
|
|
55213
|
-
const paymentProof = Buffer.from(
|
|
55662
|
+
const paymentProof = Buffer.from(
|
|
55663
|
+
JSON.stringify({ txHash: txResult.hash, chainId })
|
|
55664
|
+
).toString("base64");
|
|
55214
55665
|
resp = await fetch(url, {
|
|
55215
55666
|
...init3,
|
|
55216
55667
|
headers: { ...init3.headers, "X-Payment": paymentProof }
|
|
@@ -55251,7 +55702,7 @@ var crypto6 = __toESM(require("crypto"));
|
|
|
55251
55702
|
// package.json
|
|
55252
55703
|
var package_default = {
|
|
55253
55704
|
name: "@trustwallet/cli",
|
|
55254
|
-
version: "0.
|
|
55705
|
+
version: "0.8.0",
|
|
55255
55706
|
description: "TWAK CLI \u2014 command-line interface for Trust Wallet Agent Kit",
|
|
55256
55707
|
main: "dist/index.js",
|
|
55257
55708
|
types: "dist/index.d.ts",
|
|
@@ -55267,16 +55718,17 @@ var package_default = {
|
|
|
55267
55718
|
prebuild: "rm -rf trust-sdk && cp -r ../../vendor/trust-sdk trust-sdk",
|
|
55268
55719
|
build: "tsup && tsc --emitDeclarationOnly --outDir dist",
|
|
55269
55720
|
typecheck: "tsc --noEmit",
|
|
55721
|
+
test: "vitest run",
|
|
55270
55722
|
lint: "eslint src/",
|
|
55271
55723
|
clean: "rm -rf dist"
|
|
55272
55724
|
},
|
|
55273
55725
|
dependencies: {
|
|
55274
55726
|
"node-fetch": "2.7.0",
|
|
55275
|
-
"qrcode-terminal": "0.12.0"
|
|
55727
|
+
"qrcode-terminal": "0.12.0",
|
|
55728
|
+
"@napi-rs/keyring": "1.2.0"
|
|
55276
55729
|
},
|
|
55277
55730
|
optionalDependencies: {
|
|
55278
|
-
"@amplitude/analytics-node": "1.5.39"
|
|
55279
|
-
keytar: "7.9.0"
|
|
55731
|
+
"@amplitude/analytics-node": "1.5.39"
|
|
55280
55732
|
},
|
|
55281
55733
|
devDependencies: {
|
|
55282
55734
|
boxen: "5.1.2",
|
|
@@ -55295,7 +55747,8 @@ var package_default = {
|
|
|
55295
55747
|
"@twak/wallet-core": "workspace:*",
|
|
55296
55748
|
"@twak/walletconnect": "workspace:*",
|
|
55297
55749
|
tsup: "8.5.1",
|
|
55298
|
-
typescript: "5.7.3"
|
|
55750
|
+
typescript: "5.7.3",
|
|
55751
|
+
vitest: "4.1.2"
|
|
55299
55752
|
},
|
|
55300
55753
|
publishConfig: {
|
|
55301
55754
|
access: "public"
|
|
@@ -55426,7 +55879,13 @@ function parseInterval2(value) {
|
|
|
55426
55879
|
return num;
|
|
55427
55880
|
}
|
|
55428
55881
|
function registerWatchCommand(program3) {
|
|
55429
|
-
program3.command("watch").description(
|
|
55882
|
+
program3.command("watch").description(
|
|
55883
|
+
"Background watcher \u2014 polls prices to trigger alerts and execute DCA/limit automations"
|
|
55884
|
+
).option(
|
|
55885
|
+
"--interval <duration>",
|
|
55886
|
+
"Poll interval \u2014 e.g. 30s, 5m, 1h, or plain number (seconds)",
|
|
55887
|
+
"60"
|
|
55888
|
+
).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
55889
|
const intervalSec = parseInterval2(opts.interval);
|
|
55431
55890
|
if (intervalSec === null || intervalSec < 5) {
|
|
55432
55891
|
error("Interval must be at least 5 seconds (e.g. 30s, 5m, 1h)");
|
|
@@ -55472,7 +55931,20 @@ function registerWatchCommand(program3) {
|
|
|
55472
55931
|
}
|
|
55473
55932
|
const alertStore = new AlertStore();
|
|
55474
55933
|
const automationStore = new AutomationStore();
|
|
55475
|
-
|
|
55934
|
+
const dryRunState = /* @__PURE__ */ new Map();
|
|
55935
|
+
const getMemState = (id, stored) => {
|
|
55936
|
+
if (!dryRunState.has(id)) {
|
|
55937
|
+
dryRunState.set(id, {
|
|
55938
|
+
lastRunAt: stored.lastRunAt ?? null,
|
|
55939
|
+
runCount: stored.runCount ?? 0,
|
|
55940
|
+
active: true
|
|
55941
|
+
});
|
|
55942
|
+
}
|
|
55943
|
+
return dryRunState.get(id);
|
|
55944
|
+
};
|
|
55945
|
+
info(
|
|
55946
|
+
`[${ts3()}] Watcher started \u2014 polling every ${intervalSec}s${opts.dryRun ? " (dry run)" : ""}`
|
|
55947
|
+
);
|
|
55476
55948
|
const tick = async () => {
|
|
55477
55949
|
const tickResults = {
|
|
55478
55950
|
alerts: { checked: 0, triggered: [] },
|
|
@@ -55488,44 +55960,81 @@ function registerWatchCommand(program3) {
|
|
|
55488
55960
|
if (price === null) continue;
|
|
55489
55961
|
const triggered = alert.condition === "above" && price >= alert.targetPrice || alert.condition === "below" && price <= alert.targetPrice;
|
|
55490
55962
|
if (triggered) {
|
|
55491
|
-
|
|
55492
|
-
|
|
55963
|
+
if (!opts.dryRun)
|
|
55964
|
+
await alertStore.update(alert.id, {
|
|
55965
|
+
active: false,
|
|
55966
|
+
triggeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
55967
|
+
});
|
|
55968
|
+
const detail = {
|
|
55969
|
+
id: alert.id,
|
|
55970
|
+
tokenId: alert.tokenId,
|
|
55971
|
+
condition: alert.condition,
|
|
55972
|
+
targetPrice: alert.targetPrice,
|
|
55973
|
+
currentPrice: price
|
|
55974
|
+
};
|
|
55493
55975
|
tickResults.alerts.triggered.push(detail);
|
|
55494
|
-
success(
|
|
55976
|
+
success(
|
|
55977
|
+
`[${ts3()}] ${opts.dryRun ? "DRY RUN \u2014 would trigger alert: " : "ALERT TRIGGERED: "}${alert.tokenId} price $${price} is ${alert.condition} $${alert.targetPrice}`
|
|
55978
|
+
);
|
|
55495
55979
|
}
|
|
55496
55980
|
} catch (err) {
|
|
55497
|
-
warn(
|
|
55981
|
+
warn(
|
|
55982
|
+
`[${ts3()}] Alert ${alert.id} check failed: ${err instanceof Error ? err.message : String(err)}`
|
|
55983
|
+
);
|
|
55498
55984
|
}
|
|
55499
55985
|
}
|
|
55500
55986
|
const automations = await automationStore.getActive();
|
|
55501
55987
|
const now = Date.now();
|
|
55502
55988
|
for (const auto of automations) {
|
|
55503
55989
|
try {
|
|
55990
|
+
const memState = opts.dryRun ? getMemState(auto.id, auto) : null;
|
|
55991
|
+
if (memState && !memState.active) {
|
|
55992
|
+
tickResults.automations.skipped++;
|
|
55993
|
+
continue;
|
|
55994
|
+
}
|
|
55504
55995
|
if (auto.expiresAt && new Date(auto.expiresAt).getTime() <= now) {
|
|
55505
|
-
await automationStore.update(auto.id, { active: false });
|
|
55996
|
+
if (!opts.dryRun) await automationStore.update(auto.id, { active: false });
|
|
55997
|
+
else if (memState) memState.active = false;
|
|
55506
55998
|
dim(`[${ts3()}] Automation ${auto.id.slice(0, 8)} expired`);
|
|
55507
55999
|
tickResults.automations.skipped++;
|
|
55508
56000
|
continue;
|
|
55509
56001
|
}
|
|
55510
|
-
|
|
55511
|
-
|
|
55512
|
-
|
|
56002
|
+
const effectiveRunCount = memState ? memState.runCount : auto.runCount ?? 0;
|
|
56003
|
+
if (auto.maxRuns && effectiveRunCount >= auto.maxRuns) {
|
|
56004
|
+
if (!opts.dryRun) await automationStore.update(auto.id, { active: false });
|
|
56005
|
+
dim(
|
|
56006
|
+
`[${ts3()}] Automation ${auto.id.slice(0, 8)} reached max runs (${auto.maxRuns})`
|
|
56007
|
+
);
|
|
56008
|
+
tickResults.automations.skipped++;
|
|
56009
|
+
continue;
|
|
56010
|
+
}
|
|
56011
|
+
if (!getChainById(auto.chain)) {
|
|
56012
|
+
warn(
|
|
56013
|
+
`[${ts3()}] Automation ${auto.id.slice(0, 8)} skipped \u2014 unknown chain "${auto.chain}"`
|
|
56014
|
+
);
|
|
55513
56015
|
tickResults.automations.skipped++;
|
|
55514
56016
|
continue;
|
|
55515
56017
|
}
|
|
55516
56018
|
if (auto.type === "dca") {
|
|
55517
|
-
const
|
|
56019
|
+
const effectiveLastRunAt = memState ? memState.lastRunAt : auto.lastRunAt;
|
|
56020
|
+
const lastRun = effectiveLastRunAt ? new Date(effectiveLastRunAt).getTime() : 0;
|
|
55518
56021
|
const intervalMs = auto.intervalMs || 0;
|
|
55519
56022
|
if (lastRun + intervalMs > now) {
|
|
55520
56023
|
tickResults.automations.skipped++;
|
|
55521
56024
|
continue;
|
|
55522
56025
|
}
|
|
55523
|
-
if (opts.dryRun) {
|
|
55524
|
-
|
|
56026
|
+
if (opts.dryRun && memState) {
|
|
56027
|
+
memState.lastRunAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
56028
|
+
memState.runCount++;
|
|
56029
|
+
dim(
|
|
56030
|
+
`[${ts3()}] DRY RUN: would swap ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`
|
|
56031
|
+
);
|
|
55525
56032
|
tickResults.automations.executed.push({ id: auto.id, type: "dca", dryRun: true });
|
|
55526
56033
|
continue;
|
|
55527
56034
|
}
|
|
55528
|
-
info(
|
|
56035
|
+
info(
|
|
56036
|
+
`[${ts3()}] Executing DCA: ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`
|
|
56037
|
+
);
|
|
55529
56038
|
const result = await executor.execute({
|
|
55530
56039
|
fromChain: auto.chain,
|
|
55531
56040
|
fromToken: auto.fromToken,
|
|
@@ -55534,27 +56043,58 @@ function registerWatchCommand(program3) {
|
|
|
55534
56043
|
amount: auto.amount
|
|
55535
56044
|
});
|
|
55536
56045
|
if (result.success) {
|
|
55537
|
-
await automationStore.update(auto.id, {
|
|
56046
|
+
await automationStore.update(auto.id, {
|
|
56047
|
+
lastRunAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
56048
|
+
runCount: (auto.runCount ?? 0) + 1
|
|
56049
|
+
});
|
|
55538
56050
|
success(`[${ts3()}] DCA executed: ${result.explorerUrl}`);
|
|
55539
|
-
tickResults.automations.executed.push({
|
|
56051
|
+
tickResults.automations.executed.push({
|
|
56052
|
+
id: auto.id,
|
|
56053
|
+
type: "dca",
|
|
56054
|
+
txHash: result.txHash,
|
|
56055
|
+
explorer: result.explorerUrl
|
|
56056
|
+
});
|
|
55540
56057
|
} else {
|
|
55541
56058
|
warn(`[${ts3()}] DCA ${auto.id} failed: ${result.error}`);
|
|
55542
56059
|
}
|
|
55543
56060
|
} else if (auto.type === "limit") {
|
|
55544
56061
|
const toTokenInfo = resolveToken(auto.toToken, auto.chain);
|
|
56062
|
+
if (!toTokenInfo) {
|
|
56063
|
+
warn(`[${ts3()}] Automation ${auto.id.slice(0, 8)} skipped \u2014 unknown token "${auto.toToken}" on ${auto.chain}`);
|
|
56064
|
+
continue;
|
|
56065
|
+
}
|
|
55545
56066
|
const price = await swapClient.getTokenPrice(auto.chain, toTokenInfo.address);
|
|
55546
|
-
if (price === null)
|
|
56067
|
+
if (price === null) {
|
|
56068
|
+
if (opts.dryRun)
|
|
56069
|
+
dim(
|
|
56070
|
+
`[${ts3()}] DRY RUN: skipping limit order ${auto.id.slice(0, 8)} \u2014 price unavailable for ${auto.toToken}`
|
|
56071
|
+
);
|
|
56072
|
+
continue;
|
|
56073
|
+
}
|
|
55547
56074
|
const triggered = auto.condition === "below" && price <= (auto.targetPrice || 0) || auto.condition === "above" && price >= (auto.targetPrice || 0);
|
|
55548
56075
|
if (!triggered) {
|
|
56076
|
+
if (opts.dryRun)
|
|
56077
|
+
dim(
|
|
56078
|
+
`[${ts3()}] DRY RUN: limit order ${auto.id.slice(0, 8)} waiting \u2014 ${auto.toToken} $${price} vs target $${auto.targetPrice} (${auto.condition})`
|
|
56079
|
+
);
|
|
55549
56080
|
tickResults.automations.skipped++;
|
|
55550
56081
|
continue;
|
|
55551
56082
|
}
|
|
55552
|
-
if (opts.dryRun) {
|
|
55553
|
-
|
|
55554
|
-
|
|
56083
|
+
if (opts.dryRun && memState) {
|
|
56084
|
+
memState.active = false;
|
|
56085
|
+
dim(
|
|
56086
|
+
`[${ts3()}] DRY RUN: limit order triggered \u2014 would swap ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`
|
|
56087
|
+
);
|
|
56088
|
+
tickResults.automations.executed.push({
|
|
56089
|
+
id: auto.id,
|
|
56090
|
+
type: "limit",
|
|
56091
|
+
dryRun: true
|
|
56092
|
+
});
|
|
55555
56093
|
continue;
|
|
55556
56094
|
}
|
|
55557
|
-
info(
|
|
56095
|
+
info(
|
|
56096
|
+
`[${ts3()}] Limit order triggered: swapping ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`
|
|
56097
|
+
);
|
|
55558
56098
|
const result = await executor.execute({
|
|
55559
56099
|
fromChain: auto.chain,
|
|
55560
56100
|
fromToken: auto.fromToken,
|
|
@@ -55563,15 +56103,25 @@ function registerWatchCommand(program3) {
|
|
|
55563
56103
|
amount: auto.amount
|
|
55564
56104
|
});
|
|
55565
56105
|
if (result.success) {
|
|
55566
|
-
await automationStore.update(auto.id, {
|
|
56106
|
+
await automationStore.update(auto.id, {
|
|
56107
|
+
active: false,
|
|
56108
|
+
lastRunAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
56109
|
+
});
|
|
55567
56110
|
success(`[${ts3()}] Limit order executed: ${result.explorerUrl}`);
|
|
55568
|
-
tickResults.automations.executed.push({
|
|
56111
|
+
tickResults.automations.executed.push({
|
|
56112
|
+
id: auto.id,
|
|
56113
|
+
type: "limit",
|
|
56114
|
+
txHash: result.txHash,
|
|
56115
|
+
explorer: result.explorerUrl
|
|
56116
|
+
});
|
|
55569
56117
|
} else {
|
|
55570
56118
|
warn(`[${ts3()}] Limit order ${auto.id} failed: ${result.error}`);
|
|
55571
56119
|
}
|
|
55572
56120
|
}
|
|
55573
56121
|
} catch (err) {
|
|
55574
|
-
warn(
|
|
56122
|
+
warn(
|
|
56123
|
+
`[${ts3()}] Automation ${auto.id} failed: ${err instanceof Error ? err.message : String(err)}`
|
|
56124
|
+
);
|
|
55575
56125
|
}
|
|
55576
56126
|
}
|
|
55577
56127
|
} catch (err) {
|
|
@@ -55580,7 +56130,9 @@ function registerWatchCommand(program3) {
|
|
|
55580
56130
|
if (opts.json) {
|
|
55581
56131
|
output({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), ...tickResults }, true);
|
|
55582
56132
|
} else if (tickResults.alerts.triggered.length === 0 && tickResults.automations.executed.length === 0) {
|
|
55583
|
-
dim(
|
|
56133
|
+
dim(
|
|
56134
|
+
`[${ts3()}] Checked ${tickResults.alerts.checked} alerts, ${tickResults.automations.skipped} automations \u2014 nothing to do`
|
|
56135
|
+
);
|
|
55584
56136
|
}
|
|
55585
56137
|
};
|
|
55586
56138
|
await tick();
|
|
@@ -55619,7 +56171,6 @@ registerAlertCommand(program2);
|
|
|
55619
56171
|
registerQueryCommand(program2);
|
|
55620
56172
|
registerServeCommand(program2);
|
|
55621
56173
|
registerAutomateCommand(program2);
|
|
55622
|
-
registerSkillCommand(program2);
|
|
55623
56174
|
registerX402Command(program2);
|
|
55624
56175
|
registerTelemetryCommand(program2);
|
|
55625
56176
|
registerWatchCommand(program2);
|