@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/index.js CHANGED
@@ -49,10 +49,15 @@
49
49
  head: { appendChild: function(){} }
50
50
  };
51
51
  }
52
- // --- Suppress Node.js --localstorage-file warning from Trust SDK WASM ---
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 __require3() {
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 loadKeytar() {
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 __require("keytar");
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
- function getBackend() {
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
- import_child_process = require("child_process");
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
- const backend = getBackend();
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
- const backend = getBackend();
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
- const backend = getBackend();
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
- const backend = getBackend();
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
- const backend = getBackend();
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
- await backend.deletePassword(SERVICE, API_SECRET_ACCOUNT);
3226
+ new import_keyring.Entry(SERVICE, API_SECRET_ACCOUNT).deletePassword();
3282
3227
  } catch {
3283
3228
  }
3284
3229
  }
3285
3230
  isAvailable() {
3286
- return getBackend() !== null;
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"] || token;
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"] || token;
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 { address: token, decimals: 18 };
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(__require2.resolve("@trustwallet-trust-sdk/trust-sdk"));
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 = __require2("os");
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 = __require2("crypto");
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, __require2, 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;
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
- __require2 = /* @__PURE__ */ ((x6) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x6, {
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
- const decimals = params.decimals !== void 0 ? params.decimals : contractAddress ? resolveToken(params.assetId, chainKey).decimals : getCoinDecimals(coinId);
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(resolve3) {
7522
- resolve3(value);
7526
+ return value instanceof P5 ? value : new P5(function(resolve2) {
7527
+ resolve2(value);
7523
7528
  });
7524
7529
  }
7525
- return new (P5 || (P5 = Promise))(function(resolve3, reject) {
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 ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
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(resolve3, reject) {
7720
- v5 = o4[n4](v5), settle(resolve3, reject, v5.done, v5.value);
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(resolve3, reject, d3, v5) {
7729
+ function settle(resolve2, reject, d3, v5) {
7725
7730
  Promise.resolve(v5).then(function(v6) {
7726
- resolve3({ value: v6, done: d3 });
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((resolve3) => {
7800
+ return new Promise((resolve2) => {
7796
7801
  setTimeout(() => {
7797
- resolve3(true);
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("GET", `/v1/assets/${assetId}`);
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 || 20)
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("GET", "/v1/assets/listings", void 0, query.toString());
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
- return docs.map((doc) => {
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", "/v1/tickers", {
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 = resolve3.call(this, root, ref);
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 resolve3(root, ref) {
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 resolve3(baseURI, relativeURI, options) {
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: resolve3,
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(resolve3) {
19940
- resolve3(value);
19958
+ return value instanceof P5 ? value : new P5(function(resolve2) {
19959
+ resolve2(value);
19941
19960
  });
19942
19961
  }
19943
- return new (P5 || (P5 = Promise))(function(resolve3, reject) {
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 ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
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(resolve3) {
20530
- _this.queue.push([event, resolve3]);
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, resolve3, before, before_1, before_1_1, plugin, e2, e_1_1, enrichment, enrichment_1, enrichment_1_1, plugin, e2, e_2_1, destination, executeDestinations;
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), resolve3 = _b[1];
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
- resolve3({ event, code: 0, message: "" });
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
- resolve3({ event, code: 0, message: "" });
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
- resolve3(resolveResult);
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(resolve3) {
21015
- _this.dispatchQ.push(_this.dispatchWithCallback.bind(_this, event, resolve3));
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(resolve3) {
21636
+ return new Promise(function(resolve2) {
21618
21637
  var context = {
21619
21638
  event,
21620
21639
  attempts: 0,
21621
21640
  callback: function(result) {
21622
- return resolve3(result);
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(resolve3) {
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 resolve3(contextEvent);
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(resolve3) {
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
- resolve3(result);
22219
+ resolve2(result);
22201
22220
  } catch (_a2) {
22202
- resolve3(_this.buildResponse({ code: res.statusCode }));
22221
+ resolve2(_this.buildResponse({ code: res.statusCode }));
22203
22222
  }
22204
22223
  }
22205
22224
  });
22206
22225
  });
22207
22226
  req.on("error", function() {
22208
- return resolve3(null);
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("__")) return EMBEDDED_NAAS_SESSION_ID;
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(import_chalk2.default.dim("Set TWAK_ACCESS_ID and TWAK_HMAC_SECRET environment variables, then run `twak init`"));
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("Note: --password is visible in shell history. Use `twak wallet keychain save` or TWAK_WALLET_PASSWORD instead.\n")
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((resolve3, reject) => {
23159
- request.oncomplete = request.onsuccess = () => resolve3(request.result);
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("--password <password>", "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)").option("--json", "JSON output").action(async (opts) => {
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("--password <password>", "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)").option("--json", "JSON output").action(async (opts) => {
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("--password <password>", "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)").option("--no-tokens", "Skip token balance lookup").option("--json", "JSON output").action(async (opts) => {
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(h4.coin, h4.tokens.map((t) => ({
38916
- symbol: t.symbol ?? t.contract,
38917
- contract: t.contract,
38918
- decimals: t.decimals ?? 18
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(h4.coin, h4.tokens.map((t) => ({
38939
- symbol: t.symbol ?? t.contract,
38940
- contract: t.contract,
38941
- decimals: t.decimals ?? 18
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(`c${chain2.coinId}_t${t.contract}`, address);
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") return { symbol: t.symbol, contract: t.contract, balance: human };
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((e2) => !!e2.chain && !!e2.address);
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({ chain: chainKey, error: result.reason instanceof Error ? result.reason.message : String(result.reason) });
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("--chains <list>", "Comma-separated CAIP-2 chain IDs", "eip155:1,eip155:56,eip155:137,eip155:8453,eip155:42161").option("--timeout <seconds>", "Connection timeout in seconds", "300").option("--json", "JSON output").action(async (opts) => {
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("WalletConnect project ID required. Use --project-id or set WALLETCONNECT_PROJECT_ID");
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({ connected: true, address: session.address, chainId: session.chainId, peer: session.peerName }, true);
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(` ${import_chalk5.default.dim("Wallet")} ${exists ? import_chalk5.default.green("configured") : import_chalk5.default.yellow("not configured")}`);
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(` ${import_chalk5.default.dim("Keychain")} ${keychainHasPassword ? import_chalk5.default.green("password stored") : import_chalk5.default.yellow("no password")}`);
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("--chains <list>", "Comma-separated chain keys (default: all EVM + major L1s)", "ethereum,base,bsc,polygon,arbitrum,optimism,avalanche,fantom,linea,scroll,zksync,blast,sonic,celo,aurora,solana,tron").option("--password <password>", "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)").option("--json", "JSON output").action(async (opts) => {
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(Array.from({ length: Math.min(concurrency, items.length) }, () => run()));
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(({ chain: chain2, address }) => ({ coin: chain2.coinId, address }));
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(chainEntries, async ({ chainKey, chain: chain2, address }) => {
39229
- try {
39230
- const balance = await suppress(() => blockchainClient.getBalance(`c${chain2.coinId}`, address));
39231
- return { chainKey, chain: chain2, address, balance };
39232
- } catch {
39233
- return { chainKey, chain: chain2, address, balance: null };
39234
- }
39235
- }, 5),
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).map(([sym, addr]) => {
39386
+ const fallbackTokens = Object.entries(knownTokens).flatMap(([sym, addr]) => {
39254
39387
  const resolved = resolveToken(sym, chainKey);
39255
- return { contract: addr, decimals: resolved.decimals, symbol: sym };
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) tokenJobs.push({ chainKey, coinId: chain2.coinId, address, ...t });
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(tokenJobs, async (job) => {
39267
- const fallback = job.balance ?? "0";
39268
- try {
39269
- const assetId = `c${job.coinId}_t${job.contract}`;
39270
- const bal = await suppress(() => blockchainClient.getBalance(assetId, job.address));
39271
- return { ...job, rawBalance: bal.total ?? bal.available ?? "0" };
39272
- } catch {
39273
- return { ...job, rawBalance: fallback };
39274
- }
39275
- }, 5);
39276
- const nativeAssetIds = nativeResults.map(({ chain: chain2 }) => `c${chain2.coinId}`);
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((t) => `c${t.coinId}_t${t.contract}`);
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", "/v1/tickers", {
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({ chain: chainKey, type: "native", symbol: c3.symbol, address, balance: humanAmount, usdValue });
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({ chain: t.chainKey, type: "token", symbol: t.symbol ?? t.contract, address: t.address, contract: t.contract, balance: humanAmount, usdValue });
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((sum, r3) => sum + (typeof r3.usdValue === "number" ? r3.usdValue : 0), 0);
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("--password <password>", "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)").option("--json", "JSON output").action(async (opts) => {
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 { success: false, route: { id: "", steps: [], warnings: ["No routes found"], expirationDate: "", totalOutput: "0" }, error: "No swap routes found" };
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(`Swapping ${inputAmt} ${firstStep.from.asset.metadata.symbol} -> ${outputAmt} ${lastStep.to.asset.metadata.symbol} via ${route.steps.map((s2) => s2.provider.name).join(" -> ")}`);
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(txType === "revokeApproval" ? "Revoking prior approval..." : "Sending token approval...");
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(`${isApproval ? "Approval" : "Swap"} tx: ${getExplorerTxUrl(params.fromChain, txHash)}`);
39577
- await this.pollForConfirmation(params.fromChain, address, txHash, isApproval ? 6e4 : 12e4);
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) return;
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) throw new Error(`Transaction failed: ${status.error || "unknown error"}`);
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(`Transaction ${hash} was not confirmed within ${Math.round(timeoutMs / 1e3)}s`);
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(async (amountOrFrom, fromOrTo, toOrUndefined, opts) => {
39753
- try {
39754
- let amount;
39755
- let from8;
39756
- let to3;
39757
- if (toOrUndefined) {
39758
- amount = amountOrFrom;
39759
- from8 = fromOrTo;
39760
- to3 = toOrUndefined;
39761
- } else {
39762
- from8 = amountOrFrom;
39763
- to3 = fromOrTo;
39764
- }
39765
- if (!amount && !opts.usd) {
39766
- error("Either <amount> or --usd <amount> is required");
39767
- return;
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
- const price = await swapClient.getTokenPrice(fromChain, fromInfo.address);
39810
- if (!price) {
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
- resolvedAmount = (usdAmount / price).toFixed(fromInfo.decimals);
39815
- info(`$${usdAmount} USD \u2248 ${resolvedAmount} ${from8.toUpperCase()} (@ $${price})`);
39816
- }
39817
- const parsedAmount = parseAmount(resolvedAmount, fromInfo.decimals);
39818
- const needsWallet = !opts.quoteOnly;
39819
- let walletAddress = getChainById(fromChain)?.sampleAccount || "0x0000000000000000000000000000000000000000";
39820
- let provider = null;
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
- amberDomains = await getAmberDomainsByCoinId();
39832
- }
39833
- const fromCoinId = getChainById(fromChain)?.coinId;
39834
- const toCoinId = getChainById(toChain)?.coinId;
39835
- const quote = await swapClient.getQuote({
39836
- fromChain,
39837
- fromToken: fromInfo.address,
39838
- toChain,
39839
- toToken: toInfo.address,
39840
- amount: parsedAmount,
39841
- walletAddress,
39842
- destinationAddress,
39843
- slippage: opts.slippage,
39844
- fromDomainId: fromCoinId ? amberDomains.get(fromCoinId) : void 0,
39845
- toDomainId: toCoinId ? amberDomains.get(toCoinId) : void 0
39846
- });
39847
- if (quote.routes.length === 0) {
39848
- error("No swap routes found");
39849
- if (opts.json) output({ error: "No routes found" }, true);
39850
- return;
39851
- }
39852
- const route = quote.routes[0];
39853
- const firstStep = route.steps[0];
39854
- const lastStep = route.steps[route.steps.length - 1];
39855
- const inputAmt = formatAmount(firstStep.from.amount, firstStep.from.asset.metadata.decimals);
39856
- const outputAmt = formatAmount(lastStep.to.amount, lastStep.to.asset.metadata.decimals);
39857
- const minAmountOut = lastStep.to.minAmountOut;
39858
- const minReceivedAmt = minAmountOut ? formatAmount(minAmountOut, lastStep.to.asset.metadata.decimals) : null;
39859
- const networkFees = route.steps.filter((s2) => s2.networkFee).map((s2) => {
39860
- const fee = s2.networkFee;
39861
- const feeAmt = formatAmount(fee.amount, fee.asset.metadata.decimals);
39862
- return `${feeAmt} ${fee.asset.metadata.symbol}`;
39863
- });
39864
- const quoteData = {
39865
- input: `${inputAmt} ${firstStep.from.asset.metadata.symbol}`,
39866
- output: `${outputAmt} ${lastStep.to.asset.metadata.symbol}`,
39867
- ...minReceivedAmt ? { minReceived: `${minReceivedAmt} ${lastStep.to.asset.metadata.symbol}` } : {},
39868
- provider: route.steps.map((s2) => s2.provider.name).join(" \u2192 "),
39869
- priceImpact: firstStep.priceImpact,
39870
- ...networkFees.length > 0 ? { networkFee: networkFees.join(" + ") } : {},
39871
- ...route.steps.length > 1 ? { steps: route.steps.length } : {}
39872
- };
39873
- if (opts.quoteOnly) {
39874
- output(quoteData, opts.json);
39875
- return;
39876
- }
39877
- info(`Swapping ${quoteData.input} \u2192 ${quoteData.output} via ${quoteData.provider}...`);
39878
- const chainConfig = getChainById(fromChain);
39879
- const transactions = await swapClient.getSwapTransactions(route);
39880
- let txHash = "";
39881
- const blockchainClient = provider.getBlockchainClient();
39882
- const hasApprovalTx = transactions.some((t) => t.transaction.type === "approval");
39883
- if (!hasApprovalTx && chainConfig?.isEVM) {
39884
- const stepApproval = firstStep.approval;
39885
- if (stepApproval && stepApproval.operatorAddress) {
39886
- const fromAssetAddr = firstStep.from.asset.address;
39887
- const isNative = !fromAssetAddr || fromAssetAddr === NATIVE_TOKEN_ADDRESS;
39888
- if (!isNative && chainConfig.coinId) {
39889
- const assetId = formatTokenId(chainConfig.coinId, fromAssetAddr);
39890
- try {
39891
- const allowance = await blockchainClient.getAllowance({
39892
- assetId,
39893
- owner: walletAddress,
39894
- spender: stepApproval.operatorAddress
39895
- });
39896
- const requiredAmount = BigInt(stepApproval.minAmount || parsedAmount);
39897
- if (allowance < requiredAmount) {
39898
- info("Sending token approval...");
39899
- const hdWallet = provider.getSigner().getHDWallet();
39900
- const approveResult = await blockchainClient.approve({
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
- spender: stepApproval.operatorAddress,
39903
- amount: requiredAmount.toString(),
39904
- hdWallet
40131
+ owner: walletAddress,
40132
+ spender: stepApproval.operatorAddress
39905
40133
  });
39906
- info(`Approval tx: ${getExplorerTxUrl(fromChain, approveResult.hash)}`);
39907
- const approvalDeadline = Date.now() + 6e4;
39908
- while (Date.now() < approvalDeadline) {
39909
- await new Promise((r3) => setTimeout(r3, 2e3));
39910
- try {
39911
- const status = await blockchainClient.findTransactionStatus(fromChain, walletAddress, approveResult.hash);
39912
- if (status.confirmed) break;
39913
- if (status.failed) throw new Error("Approval transaction failed");
39914
- } catch (e2) {
39915
- const err = e2 instanceof Error ? e2 : new Error(String(e2));
39916
- if (err.message.startsWith("Approval transaction failed")) throw err;
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
- const pollForConfirmation = async (hash, timeoutMs) => {
39932
- const deadline = Date.now() + timeoutMs;
39933
- while (Date.now() < deadline) {
39934
- await new Promise((r3) => setTimeout(r3, 2e3));
39935
- try {
39936
- const status = await blockchainClient.findTransactionStatus(fromChain, walletAddress, hash);
39937
- if (status.confirmed) return;
39938
- if (status.failed) throw new Error(`Transaction failed: ${status.error || "unknown error"}`);
39939
- } catch (e2) {
39940
- const err = e2 instanceof Error ? e2 : new Error(String(e2));
39941
- if (err.message.startsWith("Transaction failed")) throw err;
39942
- if (!err.message.includes("not found") && !err.message.includes("Transaction not found")) {
39943
- info(`Warning: tx status check error (will retry): ${err.message}`);
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
- throw new Error(`Transaction ${hash} was not confirmed within ${Math.round(timeoutMs / 1e3)}s`);
39948
- };
39949
- for (const txData of transactions) {
39950
- const txType = txData.transaction.type;
39951
- const isApproval = txType === "approval" || txType === "revokeApproval";
39952
- if (isApproval) {
39953
- info(txType === "revokeApproval" ? "Revoking prior approval..." : "Sending token approval...");
39954
- }
39955
- if (chainConfig?.namespace === "solana" && txData.transaction.solanaTx) {
39956
- const solanaChainId = `solana:${chainConfig.chainId}`;
39957
- txHash = await provider.sendTransaction({
39958
- chainId: solanaChainId,
39959
- to: "",
39960
- data: txData.transaction.solanaTx.serializedMessage
39961
- });
39962
- } else if (txData.transaction.evmTx) {
39963
- const wcChainId = `eip155:${chainConfig?.chainId || "1"}`;
39964
- txHash = await provider.sendTransaction({
39965
- chainId: wcChainId,
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
- function resolveTokenLabel(tokenId) {
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 && !contractAddress) return chain2.symbol;
40151
- return tokenId;
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(`Cannot resolve "${opts.token}" without --chain. Provide --chain or use asset ID format (c60).`);
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 = alerts.map((a3) => {
40253
- let chainLabel = a3.chainKey;
40254
- try {
40255
- chainLabel = tokenIdToChain(a3.tokenId) ?? a3.chainKey;
40256
- } catch {
40257
- }
40258
- return { ...a3, tokenLabel: resolveTokenLabel(a3.tokenId), chainLabel };
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(`${a3.id} | ${label}${chainHint} | ${a3.condition} $${a3.targetPrice} | ${a3.active ? "active" : "triggered"}`);
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({ checked: activeAlerts.length, triggered: triggered.length, alerts: triggered }, opts.json);
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
- const deleted = await alertStore.delete(id);
40304
- output({ success: deleted }, opts.json);
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("--history [period]", "Price history period: hour, day, week, month, year, all (default: week)").option("--json", "JSON output").action(async (token, opts) => {
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
- const bySymbol = Object.entries(CHAINS).find(([, c3]) => c3.symbol.toUpperCase() === t.toUpperCase())?.[0];
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
- const resp = await client.market("POST", "/v2/market/tickers", {
40412
- currency: "USD",
40413
- assets: assetIds
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(`${import_chalk6.default.bold(r3.token.padEnd(10))} ${priceStr.padStart(14)} ${import_chalk6.default.dim(r3.chain)}`);
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 = await swapClient.getTokenPrice(chain2, tokenInfo.address);
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({ query: token, chains: [chain2], limit: 1 });
40439
- if (results.length > 0 && results[0].priceUsd != null) {
40440
- price = results[0].priceUsd;
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) output({ token: token.toUpperCase(), chain: chain2, priceUsd: null, error: "Token not found" }, true);
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
- "GET",
40457
- `/v1/market/charts/${assetId}`,
40458
- void 0,
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(`Unknown network "${trimmed}". Use a supported chain name or numeric coin ID.`);
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(((Number(intPart) + Number(fracScaled) / 1e4) * price).toFixed(4)) : void 0;
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
- hash,
40594
- chain: opts.chain,
40595
- ...status
40596
- }, opts.json);
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(chainEntries.map(([key, chain2]) => ({ key, ...chain2 })), true);
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
- for (const [key, chain2] of chainEntries) {
40613
- const idHint = key !== chain2.id ? import_chalk6.default.dim(` (${chain2.id})`) : "";
40614
- console.log(`${key.padEnd(15)} ${chain2.name.padEnd(25)} coinId:${chain2.coinId}${idHint}`);
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({ query: opts.token, chains: [chainKey], limit: 1 });
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
- address: opts.address,
40670
- chain: chain2.id,
40671
- symbol,
40672
- ...opts.token ? { token: opts.token } : {},
40673
- available: fmt(balance.available),
40674
- ...balance.staked ? { staked: fmt(balance.staked) } : {},
40675
- ...balance.pending ? { pending: fmt(balance.pending) } : {},
40676
- total: fmt(balance.total),
40677
- raw: balance
40678
- }, opts.json);
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 = { status: "error", valid: false, chain: chainInfo?.id, message: `Invalid address format for ${label}` };
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("--category <id>", "Category: ai, rwa, memes, defi, dex, bnb, eth, sol, pumpfun, bonk, launchpad, launchpool, layer1").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) => {
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
- let tokens;
40761
- if (opts.category) {
40762
- const categoryId = categoryMap[opts.category] ?? opts.category;
40763
- const sort = opts.sort ? sortMap[opts.sort] ?? opts.sort : "percent_change_24h";
40764
- tokens = await service.getAssetListings({ categoryId, sort, limit: parseInt(opts.limit, 10) });
40765
- } else {
40766
- const sort = opts.sort ? sortMap[opts.sort] ?? opts.sort : "percent_change_24h";
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(`${t.symbol.padEnd(10)} ${t.name.padEnd(25)} ${price.padStart(12)} ${change.padStart(8)}`);
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("--category <name>", "Filter by category (e.g., Lending, Dexs, Liquid Staking, Bridge, Yield)").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) => {
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("Name".padEnd(25) + "Category".padEnd(20) + "TVL".padStart(15) + " Chains");
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(`${d3.name.slice(0, 24).padEnd(25)}${d3.category.slice(0, 19).padEnd(20)}${tvl.padStart(15)} ${chains}`);
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).getTrendingTokens(input.limit);
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((resolve3) => setTimeout(resolve3, pollInterval));
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((resolve3, reject) => {
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
- resolve3(parseResult.data);
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((resolve3, reject) => {
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(resolve3, interval);
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((resolve3) => {
54735
+ return new Promise((resolve2) => {
54286
54736
  const json = serializeMessage(message);
54287
54737
  if (this._stdout.write(json)) {
54288
- resolve3();
54738
+ resolve2();
54289
54739
  } else {
54290
- this._stdout.once("drain", resolve3);
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((resolve3, reject) => {
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(`TWAK REST server listening on http://${this.config.host}:${this.config.port}`);
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(`x402 payment required: ${this.config.paymentAmount} ${this.config.paymentAsset} on ${this.config.paymentChainId}`);
55072
+ console.error(
55073
+ `x402 payment required: ${this.config.paymentAmount} ${this.config.paymentAsset} on ${this.config.paymentChainId}`
55074
+ );
54621
55075
  }
54622
- resolve3();
55076
+ resolve2();
54623
55077
  });
54624
55078
  });
54625
55079
  }
54626
55080
  stop() {
54627
- return new Promise((resolve3) => {
55081
+ return new Promise((resolve2) => {
54628
55082
  if (this.httpServer?.listening) {
54629
55083
  this.httpServer.close(() => {
54630
55084
  this.httpServer = null;
54631
- resolve3();
55085
+ resolve2();
54632
55086
  });
54633
55087
  } else {
54634
55088
  this.httpServer = null;
54635
- resolve3();
55089
+ resolve2();
54636
55090
  }
54637
55091
  });
54638
55092
  }
54639
55093
  async handleRequest(req, res) {
54640
55094
  res.setHeader("Content-Type", "application/json");
54641
- res.setHeader("Access-Control-Allow-Origin", "*");
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(JSON.stringify({ error: "Unauthorized \u2014 provide Authorization: Bearer <HMAC_SECRET>" }));
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(JSON.stringify({
54667
- actions: actions.map((a3) => ({ name: a3.name, description: a3.description }))
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("WWW-Authenticate", `x402 realm="twak", asset="${this.config.paymentAsset}", amount="${this.config.paymentAmount}", chain="${this.config.paymentChainId}"`);
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(JSON.stringify({
54684
- error: "payment_required",
54685
- message: `This endpoint requires payment of ${this.config.paymentAmount} ${this.config.paymentAsset}`,
54686
- asset: this.config.paymentAsset,
54687
- amount: this.config.paymentAmount,
54688
- chainId: this.config.paymentChainId,
54689
- recipient: this.config.paymentRecipient || void 0
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(paymentHeader, {
54695
- asset: this.config.paymentAsset,
54696
- amount: this.config.paymentAmount,
54697
- chainId: this.config.paymentChainId,
54698
- recipient: this.config.paymentRecipient
54699
- }, this.context.httpClient, this.seenHashes, this.config.blockchainClient);
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(JSON.stringify({
54703
- error: "payment_verification_failed",
54704
- message: result.error ?? "Payment could not be verified"
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(`DCA: ${opts.amount} ${opts.from} \u2192 ${opts.to} every ${formatInterval(intervalMs)} on ${opts.chain}`);
55409
+ info(
55410
+ `DCA: ${opts.amount} ${opts.from} \u2192 ${opts.to} every ${formatInterval(intervalMs)} on ${opts.chain}`
55411
+ );
54919
55412
  } else {
54920
- info(`Limit: swap ${opts.amount} ${opts.from} \u2192 ${opts.to} when price ${opts.condition} $${opts.price} on ${opts.chain}`);
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 active automations").option("--all", "Include inactive automations").option("--json", "JSON output").action(async (opts) => {
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 all = await store.list();
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
- console.log(`[${tag}] ${label} ${pair} every ${interval} \u2014 ${lastPart}, ${nextPart}`);
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.fromToken} ${r3.condition} $${r3.targetPrice}`;
54967
- console.log(`[${tag}] ${label} ${pair} when ${cond} \u2014 waiting`);
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(`x402: "${h4}" resolves to a private/loopback address (${record2.address}) \u2014 not allowed`);
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("--max-payment <amount>", "Maximum payment amount to auto-approve (default: 0.01)", "0.01").option("--yes", "Skip confirmation prompt and auto-approve payment up to --max-payment").option("--password <password>", "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)").option("--json", "JSON output").action(async (url, opts) => {
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", "Accept": "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(`x402: invalid --max-payment value "${opts.maxPayment}" \u2014 must be a positive number`);
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((resolve3) => {
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
- resolve3(answer.toLowerCase() === "y");
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(JSON.stringify({ txHash: txResult.hash, chainId })).toString("base64");
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.7.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("Background watcher \u2014 polls prices to trigger alerts and execute DCA/limit automations").option("--interval <duration>", "Poll interval \u2014 e.g. 30s, 5m, 1h, or plain number (seconds)", "60").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) => {
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
- info(`[${ts3()}] Watcher started \u2014 polling every ${intervalSec}s${opts.dryRun ? " (dry run)" : ""}`);
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
- await alertStore.update(alert.id, { active: false, triggeredAt: (/* @__PURE__ */ new Date()).toISOString() });
55492
- const detail = { id: alert.id, tokenId: alert.tokenId, condition: alert.condition, targetPrice: alert.targetPrice, currentPrice: price };
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(`[${ts3()}] ALERT TRIGGERED: ${alert.tokenId} price $${price} is ${alert.condition} $${alert.targetPrice}`);
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(`[${ts3()}] Alert ${alert.id} check failed: ${err instanceof Error ? err.message : String(err)}`);
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
- if (auto.maxRuns && (auto.runCount ?? 0) >= auto.maxRuns) {
55511
- await automationStore.update(auto.id, { active: false });
55512
- dim(`[${ts3()}] Automation ${auto.id.slice(0, 8)} reached max runs (${auto.maxRuns})`);
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 lastRun = auto.lastRunAt ? new Date(auto.lastRunAt).getTime() : 0;
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
- dim(`[${ts3()}] DRY RUN: would swap ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`);
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(`[${ts3()}] Executing DCA: ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`);
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, { lastRunAt: (/* @__PURE__ */ new Date()).toISOString(), runCount: (auto.runCount ?? 0) + 1 });
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({ id: auto.id, type: "dca", txHash: result.txHash, explorer: result.explorerUrl });
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) continue;
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
- dim(`[${ts3()}] DRY RUN: limit order triggered \u2014 would swap ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`);
55554
- tickResults.automations.executed.push({ id: auto.id, type: "limit", dryRun: true });
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(`[${ts3()}] Limit order triggered: swapping ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`);
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, { active: false, lastRunAt: (/* @__PURE__ */ new Date()).toISOString() });
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({ id: auto.id, type: "limit", txHash: result.txHash, explorer: result.explorerUrl });
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(`[${ts3()}] Automation ${auto.id} failed: ${err instanceof Error ? err.message : String(err)}`);
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(`[${ts3()}] Checked ${tickResults.alerts.checked} alerts, ${tickResults.automations.skipped} automations \u2014 nothing to do`);
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);