@trustwallet/cli 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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,20 @@ function info(message) {
22417
22436
  function dim(message) {
22418
22437
  console.log(import_chalk.default.dim(message));
22419
22438
  }
22439
+ function safeExit() {
22440
+ const code2 = process.exitCode ?? 0;
22441
+ const streams = [process.stdout, process.stderr].filter(
22442
+ (s2) => s2.writableNeedDrain || s2.writableLength > 0
22443
+ );
22444
+ if (streams.length === 0) {
22445
+ process.exit(code2);
22446
+ }
22447
+ let pending = streams.length;
22448
+ const onDrain = () => {
22449
+ if (--pending === 0) process.exit(code2);
22450
+ };
22451
+ for (const s2 of streams) s2.once("drain", onDrain);
22452
+ }
22420
22453
 
22421
22454
  // src/config-loader.ts
22422
22455
  var import_chalk2 = __toESM(require_source());
@@ -22426,7 +22459,8 @@ init_dist();
22426
22459
  var EMBEDDED_NAAS_SESSION_ID = "NDVjMWRjY2MtYTYwNy00NGNhLWIxZGUtMmJiZDIyNmEyODQ4";
22427
22460
  function resolveNaasSessionId() {
22428
22461
  if (process.env.TWAK_NAAS_SESSION_ID) return process.env.TWAK_NAAS_SESSION_ID;
22429
- if (EMBEDDED_NAAS_SESSION_ID && !EMBEDDED_NAAS_SESSION_ID.startsWith("__")) return EMBEDDED_NAAS_SESSION_ID;
22462
+ if (EMBEDDED_NAAS_SESSION_ID && !EMBEDDED_NAAS_SESSION_ID.startsWith("__"))
22463
+ return EMBEDDED_NAAS_SESSION_ID;
22430
22464
  return void 0;
22431
22465
  }
22432
22466
  async function getCredentials() {
@@ -22441,13 +22475,29 @@ async function getCredentials() {
22441
22475
  return { accessId: credentials.accessId, hmacSecret: credentials.hmacSecret };
22442
22476
  }
22443
22477
  console.error(import_chalk2.default.red("No API credentials found."));
22444
- console.error(import_chalk2.default.dim("Set TWAK_ACCESS_ID and TWAK_HMAC_SECRET environment variables, then run `twak init`"));
22478
+ console.error(
22479
+ import_chalk2.default.dim(
22480
+ "Set TWAK_ACCESS_ID and TWAK_HMAC_SECRET environment variables, then run `twak init`"
22481
+ )
22482
+ );
22445
22483
  process.exit(1);
22446
22484
  }
22447
22485
  async function getClient() {
22448
22486
  const creds = await getCredentials();
22449
22487
  return createTwakClient({ ...creds });
22450
22488
  }
22489
+ async function tryGetClient() {
22490
+ const envAccessId = process.env.TWAK_ACCESS_ID;
22491
+ const envSecret = process.env.TWAK_HMAC_SECRET;
22492
+ if (envAccessId && envSecret) {
22493
+ return createTwakClient({ accessId: envAccessId, hmacSecret: envSecret });
22494
+ }
22495
+ const credentials = await new CredentialManager().load();
22496
+ if (credentials) {
22497
+ return createTwakClient({ accessId: credentials.accessId, hmacSecret: credentials.hmacSecret });
22498
+ }
22499
+ return void 0;
22500
+ }
22451
22501
  async function ensureTrustSDK() {
22452
22502
  const creds = await getCredentials();
22453
22503
  await initTrustSDK({
@@ -22475,7 +22525,9 @@ async function registerWalletWithBackend(creds, provider) {
22475
22525
  async function resolvePassword(opts) {
22476
22526
  if (opts?.password) {
22477
22527
  process.stderr.write(
22478
- import_chalk2.default.yellow("Note: --password is visible in shell history. Use `twak wallet keychain save` or TWAK_WALLET_PASSWORD instead.\n")
22528
+ import_chalk2.default.yellow(
22529
+ "Note: --password is visible in shell history. Use `twak wallet keychain save` or TWAK_WALLET_PASSWORD instead.\n"
22530
+ )
22479
22531
  );
22480
22532
  return opts.password;
22481
22533
  }
@@ -23155,8 +23207,8 @@ async function dispose(driver) {
23155
23207
 
23156
23208
  // ../../node_modules/.pnpm/idb-keyval@6.2.2/node_modules/idb-keyval/dist/index.js
23157
23209
  function promisifyRequest(request) {
23158
- return new Promise((resolve3, reject) => {
23159
- request.oncomplete = request.onsuccess = () => resolve3(request.result);
23210
+ return new Promise((resolve2, reject) => {
23211
+ request.oncomplete = request.onsuccess = () => resolve2(request.result);
23160
23212
  request.onabort = request.onerror = () => reject(request.error);
23161
23213
  });
23162
23214
  }
@@ -38756,8 +38808,24 @@ function getQRString(address) {
38756
38808
  }
38757
38809
  function registerWalletCommand(program3) {
38758
38810
  const wallet = program3.command("wallet").description("Manage agent wallet");
38759
- wallet.command("create").description("Create a new agent wallet").requiredOption("--password <password>", "Password to encrypt the wallet").option("--no-keychain", "Do not save password to OS keychain").option("--json", "JSON output").action(async (opts) => {
38811
+ wallet.command("create").description("Create a new agent wallet").requiredOption("--password <password>", "Password to encrypt the wallet").option("--no-keychain", "Do not save password to OS keychain").option("--skip-password-check", "Skip password strength validation").option("--json", "JSON output").action(async (opts) => {
38760
38812
  try {
38813
+ if (!opts.skipPasswordCheck) {
38814
+ const pw = opts.password;
38815
+ const issues = [];
38816
+ if (pw.length < 8) issues.push("be at least 8 characters long");
38817
+ if (!/[a-z]/.test(pw)) issues.push("include a lowercase letter");
38818
+ if (!/[A-Z]/.test(pw)) issues.push("include an uppercase letter");
38819
+ if (!/[0-9]/.test(pw)) issues.push("include a number");
38820
+ if (issues.length > 0) {
38821
+ const msg = `Password too weak: must ${issues.join(", ")}.`;
38822
+ error(msg);
38823
+ if (opts.json) output({ error: msg }, true);
38824
+ process.exitCode = 1;
38825
+ process.exit();
38826
+ return;
38827
+ }
38828
+ }
38761
38829
  await ensureTrustSDK();
38762
38830
  const store = new WalletConfigStore();
38763
38831
  if (await store.exists()) {
@@ -38765,7 +38833,7 @@ function registerWalletCommand(program3) {
38765
38833
  error(msg);
38766
38834
  if (opts.json) output({ error: msg }, true);
38767
38835
  process.exitCode = 1;
38768
- process.exit();
38836
+ safeExit();
38769
38837
  return;
38770
38838
  }
38771
38839
  const provider = await AgentWalletProvider.create(opts.password);
@@ -38796,9 +38864,12 @@ function registerWalletCommand(program3) {
38796
38864
  if (opts.json) output({ error: msg }, true);
38797
38865
  process.exitCode = 1;
38798
38866
  }
38799
- process.exit();
38867
+ safeExit();
38800
38868
  });
38801
- wallet.command("address").description("Get wallet address for a chain").requiredOption("--chain <key>", "Chain key (e.g., ethereum, solana)").option("--password <password>", "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)").option("--json", "JSON output").action(async (opts) => {
38869
+ wallet.command("address").description("Get wallet address for a chain").requiredOption("--chain <key>", "Chain key (e.g., ethereum, solana)").option(
38870
+ "--password <password>",
38871
+ "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
38872
+ ).option("--json", "JSON output").action(async (opts) => {
38802
38873
  try {
38803
38874
  await ensureTrustSDK();
38804
38875
  const password = await resolvePassword(opts);
@@ -38820,9 +38891,12 @@ ${qr3}`;
38820
38891
  if (opts.json) output({ error: msg }, true);
38821
38892
  process.exitCode = 1;
38822
38893
  }
38823
- process.exit();
38894
+ safeExit();
38824
38895
  });
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) => {
38896
+ wallet.command("addresses").description("List all wallet addresses").option(
38897
+ "--password <password>",
38898
+ "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
38899
+ ).option("--json", "JSON output").action(async (opts) => {
38826
38900
  try {
38827
38901
  await ensureTrustSDK();
38828
38902
  const password = await resolvePassword(opts);
@@ -38868,16 +38942,19 @@ ${qr3}`;
38868
38942
  if (opts.json) output({ error: msg }, true);
38869
38943
  process.exitCode = 1;
38870
38944
  }
38871
- process.exit();
38945
+ safeExit();
38872
38946
  });
38873
- wallet.command("balance").description("Get wallet balance on a chain (native + token holdings)").option("--chain <key>", "Chain key").option("--all", "Show balances across all chains with funds").option("--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) => {
38947
+ wallet.command("balance").description("Get wallet balance on a chain (native + token holdings)").option("--chain <key>", "Chain key").option("--all", "Show balances across all chains with funds").option(
38948
+ "--password <password>",
38949
+ "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
38950
+ ).option("--no-tokens", "Skip token balance lookup").option("--json", "JSON output").action(async (opts) => {
38874
38951
  try {
38875
38952
  if (opts.all && opts.chain) {
38876
38953
  const msg = "--all and --chain are mutually exclusive";
38877
38954
  error(msg);
38878
38955
  if (opts.json) output({ error: msg }, true);
38879
38956
  process.exitCode = 1;
38880
- process.exit();
38957
+ safeExit();
38881
38958
  return;
38882
38959
  }
38883
38960
  if (!opts.all && !opts.chain) {
@@ -38885,7 +38962,7 @@ ${qr3}`;
38885
38962
  error(msg);
38886
38963
  if (opts.json) output({ error: msg }, true);
38887
38964
  process.exitCode = 1;
38888
- process.exit();
38965
+ safeExit();
38889
38966
  return;
38890
38967
  }
38891
38968
  const creds = await ensureTrustSDK();
@@ -38912,11 +38989,14 @@ ${qr3}`;
38912
38989
  const holdings = await securityService.getTokenHoldings(assets);
38913
38990
  for (const h4 of holdings) {
38914
38991
  if (h4.tokens?.length) {
38915
- holdingsMap.set(h4.coin, h4.tokens.map((t) => ({
38916
- symbol: t.symbol ?? t.contract,
38917
- contract: t.contract,
38918
- decimals: t.decimals ?? 18
38919
- })));
38992
+ holdingsMap.set(
38993
+ h4.coin,
38994
+ h4.tokens.map((t) => ({
38995
+ symbol: t.symbol ?? t.contract,
38996
+ contract: t.contract,
38997
+ decimals: t.decimals ?? 18
38998
+ }))
38999
+ );
38920
39000
  }
38921
39001
  }
38922
39002
  } catch {
@@ -38935,11 +39015,14 @@ ${qr3}`;
38935
39015
  const retryHoldings = await retrySec.getTokenHoldings(retryAssets);
38936
39016
  for (const h4 of retryHoldings) {
38937
39017
  if (h4.tokens?.length) {
38938
- holdingsMap.set(h4.coin, h4.tokens.map((t) => ({
38939
- symbol: t.symbol ?? t.contract,
38940
- contract: t.contract,
38941
- decimals: t.decimals ?? 18
38942
- })));
39018
+ holdingsMap.set(
39019
+ h4.coin,
39020
+ h4.tokens.map((t) => ({
39021
+ symbol: t.symbol ?? t.contract,
39022
+ contract: t.contract,
39023
+ decimals: t.decimals ?? 18
39024
+ }))
39025
+ );
38943
39026
  }
38944
39027
  }
38945
39028
  } catch {
@@ -38960,6 +39043,7 @@ ${qr3}`;
38960
39043
  if (knownTokens) {
38961
39044
  for (const [sym, addr] of Object.entries(knownTokens)) {
38962
39045
  const resolved = resolveToken(sym, chainKey);
39046
+ if (!resolved) continue;
38963
39047
  tokenDefs.push({ symbol: sym, contract: addr, decimals: resolved.decimals });
38964
39048
  }
38965
39049
  }
@@ -38967,11 +39051,15 @@ ${qr3}`;
38967
39051
  const tokenResults = await Promise.all(
38968
39052
  tokenDefs.map(async (t) => {
38969
39053
  try {
38970
- const bal = await blockchainClient.getBalance(`c${chain2.coinId}_t${t.contract}`, address);
39054
+ const bal = await blockchainClient.getBalance(
39055
+ `c${chain2.coinId}_t${t.contract}`,
39056
+ address
39057
+ );
38971
39058
  const rawBal = bal.total ?? bal.available ?? "0";
38972
39059
  if (BigInt(rawBal) <= 0n) return null;
38973
39060
  const human = fmtRaw(rawBal, t.decimals);
38974
- if (human && human !== "0.000000") return { symbol: t.symbol, contract: t.contract, balance: human };
39061
+ if (human && human !== "0.000000")
39062
+ return { symbol: t.symbol, contract: t.contract, balance: human };
38975
39063
  } catch {
38976
39064
  }
38977
39065
  return null;
@@ -39000,7 +39088,9 @@ ${qr3}`;
39000
39088
  addressMap.set(a3.chainId, a3.address);
39001
39089
  }
39002
39090
  const results = [];
39003
- const chainEntries = chainKeys.map((k4) => ({ chainKey: k4, chain: getChainById2(k4), address: addressMap.get(k4) })).filter((e2) => !!e2.chain && !!e2.address);
39091
+ const chainEntries = chainKeys.map((k4) => ({ chainKey: k4, chain: getChainById2(k4), address: addressMap.get(k4) })).filter(
39092
+ (e2) => !!e2.chain && !!e2.address
39093
+ );
39004
39094
  const settled = await Promise.allSettled(
39005
39095
  chainEntries.map(
39006
39096
  async ({ chainKey, chain: chain2, address }) => fetchChainBalance(chainKey, chain2, address, opts.tokens === false)
@@ -39010,7 +39100,10 @@ ${qr3}`;
39010
39100
  const result = settled[i2];
39011
39101
  if (result.status === "rejected") {
39012
39102
  const chainKey = chainEntries[i2].chainKey;
39013
- results.push({ chain: chainKey, error: result.reason instanceof Error ? result.reason.message : String(result.reason) });
39103
+ results.push({
39104
+ chain: chainKey,
39105
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
39106
+ });
39014
39107
  continue;
39015
39108
  }
39016
39109
  const r3 = result.value;
@@ -39054,15 +39147,10 @@ ${qr3}`;
39054
39147
  error(msg);
39055
39148
  if (opts.json) output({ error: msg }, true);
39056
39149
  process.exitCode = 1;
39057
- process.exit();
39150
+ safeExit();
39058
39151
  return;
39059
39152
  }
39060
- const result = await fetchChainBalance(
39061
- opts.chain,
39062
- chain2,
39063
- address,
39064
- opts.tokens === false
39065
- );
39153
+ const result = await fetchChainBalance(opts.chain, chain2, address, opts.tokens === false);
39066
39154
  if (opts.json) {
39067
39155
  output(result, true);
39068
39156
  } else {
@@ -39086,7 +39174,7 @@ ${qr3}`;
39086
39174
  if (opts.json) output({ error: msg }, true);
39087
39175
  process.exitCode = 1;
39088
39176
  }
39089
- process.exit();
39177
+ safeExit();
39090
39178
  });
39091
39179
  const keychain = wallet.command("keychain").description("Manage wallet password in OS keychain");
39092
39180
  keychain.command("save").description("Save wallet password to OS keychain").requiredOption("--password <password>", "Wallet password to store").action(async (opts) => {
@@ -39098,7 +39186,7 @@ ${qr3}`;
39098
39186
  error(err instanceof Error ? err.message : String(err));
39099
39187
  process.exitCode = 1;
39100
39188
  }
39101
- process.exit();
39189
+ safeExit();
39102
39190
  });
39103
39191
  keychain.command("delete").description("Remove wallet password from OS keychain").action(async () => {
39104
39192
  try {
@@ -39109,13 +39197,14 @@ ${qr3}`;
39109
39197
  error(err instanceof Error ? err.message : String(err));
39110
39198
  process.exitCode = 1;
39111
39199
  }
39112
- process.exit();
39200
+ safeExit();
39113
39201
  });
39114
39202
  keychain.command("check").description("Check if wallet password is stored in OS keychain").action(async () => {
39115
39203
  const kc2 = new KeychainManager();
39116
39204
  if (!kc2.isAvailable()) {
39117
39205
  warn("keytar not available on this system");
39118
- process.exit(0);
39206
+ safeExit();
39207
+ return;
39119
39208
  }
39120
39209
  const pw = await kc2.getWalletPassword();
39121
39210
  if (pw) {
@@ -39123,13 +39212,21 @@ ${qr3}`;
39123
39212
  } else {
39124
39213
  info("No wallet password found in OS keychain");
39125
39214
  }
39126
- process.exit();
39215
+ safeExit();
39127
39216
  });
39128
- wallet.command("connect").description("Connect external wallet via WalletConnect").option("--project-id <id>", "WalletConnect project ID (or set WALLETCONNECT_PROJECT_ID)").option("--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) => {
39217
+ wallet.command("connect").description("Connect external wallet via WalletConnect").option("--project-id <id>", "WalletConnect project ID (or set WALLETCONNECT_PROJECT_ID)").option(
39218
+ "--chains <list>",
39219
+ "Comma-separated CAIP-2 chain IDs",
39220
+ "eip155:1,eip155:56,eip155:137,eip155:8453,eip155:42161"
39221
+ ).option("--timeout <seconds>", "Connection timeout in seconds", "300").option("--json", "JSON output").action(async (opts) => {
39129
39222
  const projectId = opts.projectId || process.env.WALLETCONNECT_PROJECT_ID;
39130
39223
  if (!projectId) {
39131
- error("WalletConnect project ID required. Use --project-id or set WALLETCONNECT_PROJECT_ID");
39132
- process.exit(1);
39224
+ error(
39225
+ "WalletConnect project ID required. Use --project-id or set WALLETCONNECT_PROJECT_ID"
39226
+ );
39227
+ process.exitCode = 1;
39228
+ safeExit();
39229
+ return;
39133
39230
  }
39134
39231
  const provider = new WalletConnectProvider({ projectId });
39135
39232
  const chains = opts.chains.split(",").map((s2) => s2.trim()).filter(Boolean);
@@ -39145,13 +39242,21 @@ ${qr3}`;
39145
39242
  const timeoutMs = parseInt(opts.timeout, 10) * 1e3;
39146
39243
  const session = await provider.waitForSession(timeoutMs);
39147
39244
  if (opts.json) {
39148
- output({ connected: true, address: session.address, chainId: session.chainId, peer: session.peerName }, true);
39245
+ output(
39246
+ {
39247
+ connected: true,
39248
+ address: session.address,
39249
+ chainId: session.chainId,
39250
+ peer: session.peerName
39251
+ },
39252
+ true
39253
+ );
39149
39254
  } else {
39150
39255
  success(`Connected to ${session.peerName || "wallet"}`);
39151
39256
  info(` Address: ${session.address}`);
39152
39257
  info(` Chain: ${session.chainId}`);
39153
39258
  }
39154
- process.exit();
39259
+ safeExit();
39155
39260
  });
39156
39261
  wallet.command("status").description("Check wallet configuration status").option("--json", "JSON output").action(async (opts) => {
39157
39262
  const store = new WalletConfigStore();
@@ -39177,17 +39282,28 @@ ${qr3}`;
39177
39282
  if (opts.json) {
39178
39283
  output(result, true);
39179
39284
  } else {
39180
- console.log(` ${import_chalk5.default.dim("Wallet")} ${exists ? import_chalk5.default.green("configured") : import_chalk5.default.yellow("not configured")}`);
39285
+ console.log(
39286
+ ` ${import_chalk5.default.dim("Wallet")} ${exists ? import_chalk5.default.green("configured") : import_chalk5.default.yellow("not configured")}`
39287
+ );
39181
39288
  if (createdAt) console.log(` ${import_chalk5.default.dim("Created")} ${createdAt}`);
39182
- console.log(` ${import_chalk5.default.dim("Keychain")} ${keychainHasPassword ? import_chalk5.default.green("password stored") : import_chalk5.default.yellow("no password")}`);
39289
+ console.log(
39290
+ ` ${import_chalk5.default.dim("Keychain")} ${keychainHasPassword ? import_chalk5.default.green("password stored") : import_chalk5.default.yellow("no password")}`
39291
+ );
39183
39292
  console.log(` ${import_chalk5.default.dim("Chains")} ${totalChains} supported`);
39184
39293
  if (addressCount !== void 0) console.log(` ${import_chalk5.default.dim("Addresses")} ${addressCount}`);
39185
39294
  if (!exists) info("\nRun `twak wallet create --password <pw>` to create a wallet.");
39186
39295
  }
39187
39296
  });
39188
- wallet.command("portfolio").description("Show portfolio balances and USD values across chains, including token holdings").option("--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) => {
39297
+ wallet.command("portfolio").description("Show portfolio balances and USD values across chains, including token holdings").option(
39298
+ "--chains <list>",
39299
+ "Comma-separated chain keys (default: all supported chains)",
39300
+ Object.keys(CHAINS).join(",")
39301
+ ).option(
39302
+ "--password <password>",
39303
+ "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
39304
+ ).option("--json", "JSON output").action(async (opts) => {
39189
39305
  try {
39190
- await ensureTrustSDK();
39306
+ const creds = await ensureTrustSDK();
39191
39307
  const password = await resolvePassword(opts);
39192
39308
  const provider = await AgentWalletProvider.fromStorage(password);
39193
39309
  const { getChainById: getChainById2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
@@ -39203,6 +39319,15 @@ ${qr3}`;
39203
39319
  return [{ chainKey, chain: chain2, address: addrEntry.address }];
39204
39320
  });
39205
39321
  const suppress = (fn2) => fn2();
39322
+ const withTimeout = (promise, ms3) => {
39323
+ let timerId;
39324
+ const timeout = new Promise((_2, reject) => {
39325
+ timerId = setTimeout(() => reject(new Error(`timed out after ${ms3 / 1e3}s`)), ms3);
39326
+ });
39327
+ return Promise.race([promise, timeout]).finally(() => {
39328
+ if (timerId !== void 0) clearTimeout(timerId);
39329
+ });
39330
+ };
39206
39331
  const pMap = async (items, fn2, concurrency) => {
39207
39332
  const results = new Array(items.length);
39208
39333
  let idx = 0;
@@ -39212,7 +39337,9 @@ ${qr3}`;
39212
39337
  results[i2] = await fn2(items[i2]);
39213
39338
  }
39214
39339
  };
39215
- await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => run()));
39340
+ await Promise.all(
39341
+ Array.from({ length: Math.min(concurrency, items.length) }, () => run())
39342
+ );
39216
39343
  return results;
39217
39344
  };
39218
39345
  const toHuman = (raw, decimals) => {
@@ -39223,16 +39350,52 @@ ${qr3}`;
39223
39350
  const fracStr = fracPart.toString().padStart(decimals, "0").replace(/0+$/, "");
39224
39351
  return fracStr ? `${intPart}.${fracStr}` : intPart.toString();
39225
39352
  };
39226
- const holdingsAssets = chainEntries.map(({ chain: chain2, address }) => ({ coin: chain2.coinId, address }));
39353
+ const holdingsAssets = chainEntries.map(
39354
+ ({ chain: chain2, address }) => ({
39355
+ coin: chain2.coinId,
39356
+ address
39357
+ })
39358
+ );
39227
39359
  const [nativeResults, holdings] = await Promise.all([
39228
- pMap(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),
39360
+ pMap(
39361
+ chainEntries,
39362
+ async ({
39363
+ chainKey,
39364
+ chain: chain2,
39365
+ address
39366
+ }) => {
39367
+ try {
39368
+ const balance = await withTimeout(
39369
+ suppress(() => blockchainClient.getBalance(`c${chain2.coinId}`, address)),
39370
+ 15e3
39371
+ );
39372
+ if (!balance) {
39373
+ return {
39374
+ chainKey,
39375
+ chain: chain2,
39376
+ address,
39377
+ balance: null,
39378
+ error: "empty response from SDK"
39379
+ };
39380
+ }
39381
+ return { chainKey, chain: chain2, address, balance, error: void 0 };
39382
+ } catch (e2) {
39383
+ const code2 = e2 != null ? e2.code : void 0;
39384
+ let msg;
39385
+ if (code2 === 429) {
39386
+ msg = "rate limited (429)";
39387
+ } else if (e2 instanceof Error && e2.message && e2.message !== "null") {
39388
+ msg = e2.message;
39389
+ } else if (code2) {
39390
+ msg = `HTTP ${code2}`;
39391
+ } else {
39392
+ msg = "unsupported or unavailable";
39393
+ }
39394
+ return { chainKey, chain: chain2, address, balance: null, error: msg };
39395
+ }
39396
+ },
39397
+ 5
39398
+ ),
39236
39399
  (async () => {
39237
39400
  try {
39238
39401
  return await new SecurityService2(httpClient).getTokenHoldings(holdingsAssets);
@@ -39245,14 +39408,27 @@ ${qr3}`;
39245
39408
  for (const h4 of holdings) {
39246
39409
  if (h4.tokens?.length) holdingsMap.set(h4.coin, h4.tokens);
39247
39410
  }
39411
+ if (holdingsMap.size === 0) {
39412
+ try {
39413
+ await registerWalletWithBackend(creds, provider);
39414
+ const retryHoldings = await new SecurityService2(httpClient).getTokenHoldings(
39415
+ holdingsAssets
39416
+ );
39417
+ for (const h4 of retryHoldings) {
39418
+ if (h4.tokens?.length) holdingsMap.set(h4.coin, h4.tokens);
39419
+ }
39420
+ } catch {
39421
+ }
39422
+ }
39248
39423
  for (const { chainKey, chain: chain2 } of chainEntries) {
39249
39424
  const coinId = chain2.coinId;
39250
39425
  if (holdingsMap.has(coinId)) continue;
39251
39426
  const knownTokens = COMMON_TOKENS[chainKey];
39252
39427
  if (!knownTokens) continue;
39253
- const fallbackTokens = Object.entries(knownTokens).map(([sym, addr]) => {
39428
+ const fallbackTokens = Object.entries(knownTokens).flatMap(([sym, addr]) => {
39254
39429
  const resolved = resolveToken(sym, chainKey);
39255
- return { contract: addr, decimals: resolved.decimals, symbol: sym };
39430
+ if (!resolved) return [];
39431
+ return [{ contract: addr, decimals: resolved.decimals, symbol: sym }];
39256
39432
  });
39257
39433
  holdingsMap.set(coinId, fallbackTokens);
39258
39434
  }
@@ -39260,28 +39436,45 @@ ${qr3}`;
39260
39436
  for (const { chainKey, chain: chain2, address } of chainEntries) {
39261
39437
  const tokens = holdingsMap.get(chain2.coinId) ?? [];
39262
39438
  for (const t of tokens) {
39263
- if (t.contract) tokenJobs.push({ chainKey, coinId: chain2.coinId, address, ...t });
39439
+ if (t.contract)
39440
+ tokenJobs.push({
39441
+ chainKey,
39442
+ coinId: chain2.coinId,
39443
+ address,
39444
+ ...t
39445
+ });
39264
39446
  }
39265
39447
  }
39266
- const tokenBalances = await pMap(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}`);
39448
+ const tokenBalances = await pMap(
39449
+ tokenJobs,
39450
+ async (job) => {
39451
+ const fallback = job.balance ?? "0";
39452
+ try {
39453
+ const assetId = `c${job.coinId}_t${job.contract}`;
39454
+ const bal = await withTimeout(
39455
+ suppress(() => blockchainClient.getBalance(assetId, job.address)),
39456
+ 15e3
39457
+ );
39458
+ return { ...job, rawBalance: bal.total ?? bal.available ?? fallback };
39459
+ } catch {
39460
+ return { ...job, rawBalance: fallback };
39461
+ }
39462
+ },
39463
+ 5
39464
+ );
39465
+ const nativeAssetIds = nativeResults.map(
39466
+ ({ chain: chain2 }) => `c${chain2.coinId}`
39467
+ );
39277
39468
  const nonZeroTokens = tokenBalances.filter((t) => {
39278
39469
  const dec = t.decimals ?? 18;
39279
39470
  return toHuman(t.rawBalance, dec) !== "0";
39280
39471
  });
39281
- const tokenAssetIds = nonZeroTokens.map((t) => `c${t.coinId}_t${t.contract}`);
39472
+ const tokenAssetIds = nonZeroTokens.map(
39473
+ (t) => `c${t.coinId}_t${t.contract}`
39474
+ );
39282
39475
  const priceMap = /* @__PURE__ */ new Map();
39283
39476
  try {
39284
- const priceResp = await httpClient.market("POST", "/v1/tickers", {
39477
+ const priceResp = await httpClient.market("POST", "/v2/market/tickers", {
39285
39478
  currency: "USD",
39286
39479
  assets: [.../* @__PURE__ */ new Set([...nativeAssetIds, ...tokenAssetIds])]
39287
39480
  });
@@ -39291,13 +39484,33 @@ ${qr3}`;
39291
39484
  } catch {
39292
39485
  }
39293
39486
  const rows = [];
39294
- for (const { chainKey, chain: chain2, address, balance } of nativeResults) {
39487
+ const failedChains = [];
39488
+ for (const { chainKey, chain: chain2, address, balance, error: fetchError } of nativeResults) {
39295
39489
  const c3 = chain2;
39490
+ if (fetchError) {
39491
+ failedChains.push(`${chainKey} (${fetchError})`);
39492
+ rows.push({
39493
+ chain: chainKey,
39494
+ type: "native",
39495
+ symbol: c3.symbol,
39496
+ address,
39497
+ balance: "--",
39498
+ usdValue: null
39499
+ });
39500
+ continue;
39501
+ }
39296
39502
  const price = priceMap.get(`c${c3.coinId}`) ?? null;
39297
39503
  const rawTotal = balance?.total ?? balance?.available ?? "0";
39298
39504
  const humanAmount = toHuman(rawTotal, c3.decimals);
39299
39505
  const usdValue = price !== null ? parseFloat((parseFloat(humanAmount) * price).toFixed(2)) : null;
39300
- rows.push({ chain: chainKey, type: "native", symbol: c3.symbol, address, balance: humanAmount, usdValue });
39506
+ rows.push({
39507
+ chain: chainKey,
39508
+ type: "native",
39509
+ symbol: c3.symbol,
39510
+ address,
39511
+ balance: humanAmount,
39512
+ usdValue
39513
+ });
39301
39514
  }
39302
39515
  for (const t of tokenBalances) {
39303
39516
  const dec = t.decimals ?? 18;
@@ -39306,22 +39519,39 @@ ${qr3}`;
39306
39519
  const tokenAssetId = `c${t.coinId}_t${t.contract}`;
39307
39520
  const tokenPrice = priceMap.get(tokenAssetId) ?? null;
39308
39521
  const usdValue = tokenPrice !== null ? parseFloat((parseFloat(humanAmount) * tokenPrice).toFixed(2)) : null;
39309
- rows.push({ chain: t.chainKey, type: "token", symbol: t.symbol ?? t.contract, address: t.address, contract: t.contract, balance: humanAmount, usdValue });
39522
+ rows.push({
39523
+ chain: t.chainKey,
39524
+ type: "token",
39525
+ symbol: t.symbol ?? t.contract,
39526
+ address: t.address,
39527
+ contract: t.contract,
39528
+ balance: humanAmount,
39529
+ usdValue
39530
+ });
39310
39531
  }
39311
39532
  if (opts.json) {
39533
+ if (failedChains.length > 0) {
39534
+ warn(`Balance unavailable for: ${failedChains.join(", ")}`);
39535
+ }
39312
39536
  output(rows, true);
39313
39537
  } else {
39314
- const totalUsd = rows.reduce((sum, r3) => sum + (typeof r3.usdValue === "number" ? r3.usdValue : 0), 0);
39538
+ const totalUsd = rows.reduce(
39539
+ (sum, r3) => sum + (typeof r3.usdValue === "number" ? r3.usdValue : 0),
39540
+ 0
39541
+ );
39315
39542
  console.log("Chain Type Symbol Balance USD");
39316
39543
  console.log("\u2500".repeat(72));
39317
39544
  for (const r3 of rows) {
39318
- const usd = r3.usdValue !== null ? `$${r3.usdValue.toFixed(2)}` : "";
39545
+ const usd = r3.balance === "--" ? "" : r3.usdValue !== null ? `$${r3.usdValue.toFixed(2)}` : "";
39319
39546
  console.log(
39320
39547
  `${String(r3.chain).padEnd(12)} ${String(r3.type).padEnd(7)} ${String(r3.symbol).padEnd(17)} ${String(r3.balance).padEnd(20)} ${usd}`
39321
39548
  );
39322
39549
  }
39323
39550
  console.log("\u2500".repeat(72));
39324
39551
  console.log(`Total USD: $${totalUsd.toFixed(2)}`);
39552
+ if (failedChains.length > 0) {
39553
+ warn(`Balance unavailable for: ${failedChains.join(", ")}`);
39554
+ }
39325
39555
  }
39326
39556
  } catch (err) {
39327
39557
  const msg = err instanceof Error ? err.message : String(err);
@@ -39329,9 +39559,12 @@ ${qr3}`;
39329
39559
  if (opts.json) output({ error: msg }, true);
39330
39560
  process.exitCode = 1;
39331
39561
  }
39332
- process.exit();
39562
+ safeExit();
39333
39563
  });
39334
- wallet.command("sign-message").description("Sign a message with the agent wallet").requiredOption("--chain <key>", "Chain key (e.g., ethereum, solana)").requiredOption("--message <text>", "Message to sign").option("--password <password>", "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)").option("--json", "JSON output").action(async (opts) => {
39564
+ wallet.command("sign-message").description("Sign a message with the agent wallet").requiredOption("--chain <key>", "Chain key (e.g., ethereum, solana)").requiredOption("--message <text>", "Message to sign").option(
39565
+ "--password <password>",
39566
+ "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
39567
+ ).option("--json", "JSON output").action(async (opts) => {
39335
39568
  try {
39336
39569
  await ensureTrustSDK();
39337
39570
  const password = await resolvePassword(opts);
@@ -39345,7 +39578,7 @@ ${qr3}`;
39345
39578
  if (opts.json) output({ error: msg }, true);
39346
39579
  process.exitCode = 1;
39347
39580
  }
39348
- process.exit();
39581
+ safeExit();
39349
39582
  });
39350
39583
  wallet.command("register").description("Register wallet addresses with Trust Wallet backend (enables token holdings)").option("--password <password>", "Wallet password").option("--json", "JSON output").action(async (opts) => {
39351
39584
  try {
@@ -39362,7 +39595,7 @@ ${qr3}`;
39362
39595
  if (opts.json) output({ error: msg }, true);
39363
39596
  process.exitCode = 1;
39364
39597
  }
39365
- process.exit();
39598
+ safeExit();
39366
39599
  });
39367
39600
  }
39368
39601
 
@@ -39374,6 +39607,7 @@ init_dist5();
39374
39607
  // ../swap/dist/index.mjs
39375
39608
  init_dist2();
39376
39609
  init_dist2();
39610
+ init_dist2();
39377
39611
  var SwapClient = class {
39378
39612
  constructor(httpClient) {
39379
39613
  this.httpClient = httpClient;
@@ -39520,6 +39754,26 @@ var SwapClient = class {
39520
39754
  }
39521
39755
  }
39522
39756
  };
39757
+ var MAX_RETRIES = 3;
39758
+ var BASE_DELAY_MS = 3e3;
39759
+ function isRateLimited(err) {
39760
+ return err instanceof TwakApiError && err.statusCode === 429;
39761
+ }
39762
+ function sleep(ms3) {
39763
+ return new Promise((r3) => setTimeout(r3, ms3));
39764
+ }
39765
+ async function withRetry(fn2, onRetry) {
39766
+ for (let attempt = 0; ; attempt++) {
39767
+ try {
39768
+ return await fn2();
39769
+ } catch (err) {
39770
+ if (!isRateLimited(err) || attempt >= MAX_RETRIES) throw err;
39771
+ const delay = BASE_DELAY_MS * Math.pow(2, attempt);
39772
+ onRetry?.(attempt + 1, delay);
39773
+ await sleep(delay);
39774
+ }
39775
+ }
39776
+ }
39523
39777
  var SwapExecutor = class {
39524
39778
  constructor(swapClient, walletProvider, options = {}) {
39525
39779
  this.swapClient = swapClient;
@@ -39532,7 +39786,10 @@ var SwapExecutor = class {
39532
39786
  async execute(params) {
39533
39787
  const address = await this.walletProvider.getAddress(params.fromChain);
39534
39788
  const fromTokenInfo = resolveToken(params.fromToken, params.fromChain);
39789
+ if (!fromTokenInfo)
39790
+ throw new Error(`Unknown token: ${params.fromToken} on ${params.fromChain}`);
39535
39791
  const toTokenInfo = resolveToken(params.toToken, params.toChain);
39792
+ if (!toTokenInfo) throw new Error(`Unknown token: ${params.toToken} on ${params.toChain}`);
39536
39793
  const parsedAmount = parseAmount(params.amount, fromTokenInfo.decimals);
39537
39794
  const fromCoinId = getChainById(params.fromChain)?.coinId;
39538
39795
  const toCoinId = getChainById(params.toChain)?.coinId;
@@ -39541,40 +39798,65 @@ var SwapExecutor = class {
39541
39798
  if (params.toChain !== params.fromChain) {
39542
39799
  destinationAddress = await this.walletProvider.getAddress(params.toChain);
39543
39800
  }
39544
- const quote = await this.swapClient.getQuote({
39545
- fromChain: params.fromChain,
39546
- fromToken: fromTokenInfo.address,
39547
- toChain: params.toChain,
39548
- toToken: toTokenInfo.address,
39549
- amount: parsedAmount,
39550
- walletAddress: address,
39551
- destinationAddress,
39552
- slippage: params.slippage,
39553
- fromDomainId: fromCoinId ? domains.get(fromCoinId) : void 0,
39554
- toDomainId: toCoinId ? domains.get(toCoinId) : void 0
39555
- });
39801
+ const quote = await withRetry(
39802
+ () => this.swapClient.getQuote({
39803
+ fromChain: params.fromChain,
39804
+ fromToken: fromTokenInfo.address,
39805
+ toChain: params.toChain,
39806
+ toToken: toTokenInfo.address,
39807
+ amount: parsedAmount,
39808
+ walletAddress: address,
39809
+ destinationAddress,
39810
+ slippage: params.slippage,
39811
+ fromDomainId: fromCoinId ? domains.get(fromCoinId) : void 0,
39812
+ toDomainId: toCoinId ? domains.get(toCoinId) : void 0
39813
+ })
39814
+ );
39556
39815
  if (quote.routes.length === 0) {
39557
- return { success: false, route: { id: "", steps: [], warnings: ["No routes found"], expirationDate: "", totalOutput: "0" }, error: "No swap routes found" };
39816
+ return {
39817
+ success: false,
39818
+ route: {
39819
+ id: "",
39820
+ steps: [],
39821
+ warnings: ["No routes found"],
39822
+ expirationDate: "",
39823
+ totalOutput: "0"
39824
+ },
39825
+ error: "No swap routes found"
39826
+ };
39558
39827
  }
39559
39828
  const route = quote.routes[0];
39560
39829
  const firstStep = route.steps[0];
39561
39830
  const lastStep = route.steps[route.steps.length - 1];
39562
39831
  const inputAmt = formatAmount(firstStep.from.amount, firstStep.from.asset.metadata.decimals);
39563
39832
  const outputAmt = formatAmount(lastStep.to.amount, lastStep.to.asset.metadata.decimals);
39564
- this.log(`Swapping ${inputAmt} ${firstStep.from.asset.metadata.symbol} -> ${outputAmt} ${lastStep.to.asset.metadata.symbol} via ${route.steps.map((s2) => s2.provider.name).join(" -> ")}`);
39833
+ this.log(
39834
+ `Swapping ${inputAmt} ${firstStep.from.asset.metadata.symbol} -> ${outputAmt} ${lastStep.to.asset.metadata.symbol} via ${route.steps.map((s2) => s2.provider.name).join(" -> ")}`
39835
+ );
39565
39836
  const chainConfig = getChainById(params.fromChain);
39566
- const transactions = await this.swapClient.getSwapTransactions(route);
39837
+ const transactions = await withRetry(() => this.swapClient.getSwapTransactions(route));
39838
+ let approvalTxHash;
39567
39839
  try {
39568
39840
  let txHash = "";
39569
39841
  for (const txData of transactions) {
39570
39842
  const txType = txData.transaction.type;
39571
39843
  const isApproval = txType === "approval" || txType === "revokeApproval";
39572
39844
  if (isApproval) {
39573
- this.log(txType === "revokeApproval" ? "Revoking prior approval..." : "Sending token approval...");
39845
+ this.log(
39846
+ txType === "revokeApproval" ? "Revoking prior approval..." : "Sending token approval..."
39847
+ );
39574
39848
  }
39575
39849
  txHash = await this.sendTransaction(txData, chainConfig, params.fromChain);
39576
- this.log(`${isApproval ? "Approval" : "Swap"} tx: ${getExplorerTxUrl(params.fromChain, txHash)}`);
39577
- await this.pollForConfirmation(params.fromChain, address, txHash, isApproval ? 6e4 : 12e4);
39850
+ if (isApproval) approvalTxHash = txHash;
39851
+ this.log(
39852
+ `${isApproval ? "Approval" : "Swap"} tx: ${getExplorerTxUrl(params.fromChain, txHash)}`
39853
+ );
39854
+ await this.pollForConfirmation(
39855
+ params.fromChain,
39856
+ address,
39857
+ txHash,
39858
+ isApproval ? 6e4 : 12e4
39859
+ );
39578
39860
  }
39579
39861
  return {
39580
39862
  success: true,
@@ -39583,7 +39865,10 @@ var SwapExecutor = class {
39583
39865
  explorerUrl: getExplorerTxUrl(params.fromChain, txHash)
39584
39866
  };
39585
39867
  } catch (err) {
39586
- const msg = err instanceof Error ? err.message : "Unknown error";
39868
+ let msg = err instanceof Error ? err.message : "Unknown error";
39869
+ if (approvalTxHash) {
39870
+ msg += `. Approval was sent (tx: ${approvalTxHash}). Check allowance before retrying.`;
39871
+ }
39587
39872
  return { success: false, route, error: msg };
39588
39873
  }
39589
39874
  }
@@ -39606,20 +39891,28 @@ var SwapExecutor = class {
39606
39891
  }
39607
39892
  async pollForConfirmation(chainKey, address, hash, timeoutMs) {
39608
39893
  const blockchainClient = this.walletProvider.getBlockchainClient?.();
39609
- if (!blockchainClient) return;
39894
+ if (!blockchainClient)
39895
+ throw new Error(
39896
+ "Wallet provider does not support transaction status lookup; cannot confirm transaction"
39897
+ );
39610
39898
  const deadline = Date.now() + timeoutMs;
39611
39899
  while (Date.now() < deadline) {
39612
39900
  await new Promise((r3) => setTimeout(r3, 2e3));
39613
39901
  try {
39614
39902
  const status = await blockchainClient.findTransactionStatus(chainKey, address, hash);
39615
39903
  if (status.confirmed) return;
39616
- if (status.failed) throw new Error(`Transaction failed: ${status.error || "unknown error"}`);
39904
+ if (status.failed)
39905
+ throw new Error(`Transaction failed: ${status.error || "unknown error"}`);
39617
39906
  } catch (e2) {
39618
39907
  const err = e2 instanceof Error ? e2 : new Error(String(e2));
39619
39908
  if (err.message.startsWith("Transaction failed")) throw err;
39909
+ if (!err.message.includes("not found") && !err.message.includes("Transaction not found"))
39910
+ throw err;
39620
39911
  }
39621
39912
  }
39622
- throw new Error(`Transaction ${hash} was not confirmed within ${Math.round(timeoutMs / 1e3)}s`);
39913
+ throw new Error(
39914
+ `Transaction ${hash} was not confirmed within ${Math.round(timeoutMs / 1e3)}s`
39915
+ );
39623
39916
  }
39624
39917
  };
39625
39918
 
@@ -39648,7 +39941,7 @@ function registerTransferCommand(program3) {
39648
39941
  error(msg);
39649
39942
  if (opts.json) output({ error: msg }, true);
39650
39943
  process.exitCode = 1;
39651
- process.exit(0);
39944
+ safeExit();
39652
39945
  return;
39653
39946
  }
39654
39947
  const resolvedTo = await resolveAddressOrName(opts.to);
@@ -39659,7 +39952,7 @@ function registerTransferCommand(program3) {
39659
39952
  error(msg);
39660
39953
  if (opts.json) output({ error: msg }, true);
39661
39954
  process.exitCode = 1;
39662
- process.exit();
39955
+ safeExit();
39663
39956
  return;
39664
39957
  }
39665
39958
  }
@@ -39677,7 +39970,7 @@ function registerTransferCommand(program3) {
39677
39970
  error(msg);
39678
39971
  if (opts.json) output({ error: msg }, true);
39679
39972
  process.exitCode = 1;
39680
- process.exit();
39973
+ safeExit();
39681
39974
  return;
39682
39975
  }
39683
39976
  const parsedAmount = parseFloat(opts.amount);
@@ -39686,11 +39979,16 @@ function registerTransferCommand(program3) {
39686
39979
  error(msg);
39687
39980
  if (opts.json) output({ error: msg }, true);
39688
39981
  process.exitCode = 1;
39689
- process.exit();
39982
+ safeExit();
39690
39983
  return;
39691
39984
  }
39692
39985
  try {
39693
39986
  const tokenInfo = resolveToken(opts.token, chainKey);
39987
+ if (!tokenInfo) {
39988
+ error(`Unknown token: ${opts.token} on ${chainKey}. Use a contract address (0x...) or a supported symbol.`);
39989
+ process.exitCode = 1;
39990
+ return;
39991
+ }
39694
39992
  const swapClient = new SwapClient(httpClient);
39695
39993
  const price = await swapClient.getTokenPrice(chainKey, tokenInfo.address);
39696
39994
  if (price && price > 0) {
@@ -39700,7 +39998,7 @@ function registerTransferCommand(program3) {
39700
39998
  error(msg);
39701
39999
  if (opts.json) output({ error: msg }, true);
39702
40000
  process.exitCode = 1;
39703
- process.exit();
40001
+ safeExit();
39704
40002
  return;
39705
40003
  }
39706
40004
  if (usdValue > 1e3) {
@@ -39739,7 +40037,7 @@ function registerTransferCommand(program3) {
39739
40037
  if (opts.json) output({ error: msg }, true);
39740
40038
  process.exitCode = 1;
39741
40039
  }
39742
- process.exit();
40040
+ safeExit();
39743
40041
  });
39744
40042
  }
39745
40043
 
@@ -39749,250 +40047,300 @@ init_dist4();
39749
40047
  var MAX_SLIPPAGE_PCT = 50;
39750
40048
  var WARN_SLIPPAGE_PCT = 5;
39751
40049
  function registerSwapCommand(program3) {
39752
- program3.command("swap").description("Quote or execute a token swap").argument("<amountOrFrom>", "Amount to swap, or source token when using --usd").argument("<fromOrTo>", "Source token, or destination token when using --usd").argument("[to]", "Destination token (omit when using --usd with 2 args)").option("--chain <id>", "Chain", "ethereum").option("--to-chain <id>", "Destination chain (for cross-chain swaps)").option("--slippage <pct>", "Slippage tolerance %", "1").option("--usd <amount>", "Swap a USD-equivalent amount of the source token").option("--quote-only", "Get quote without executing").option("--password <password>", "Wallet password (required to execute)").option("--json", "JSON output").action(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");
40050
+ program3.command("swap").description("Quote or execute a token swap").argument("<amountOrFrom>", "Amount to swap, or source token when using --usd").argument("<fromOrTo>", "Source token, or destination token when using --usd").argument("[to]", "Destination token (omit when using --usd with 2 args)").option("--chain <id>", "Chain", "ethereum").option("--to-chain <id>", "Destination chain (for cross-chain swaps)").option("--slippage <pct>", "Slippage tolerance %", "1").option("--usd <amount>", "Swap a USD-equivalent amount of the source token").option("--quote-only", "Get quote without executing").option("--password <password>", "Wallet password (required to execute)").option("--json", "JSON output").action(
40051
+ async (amountOrFrom, fromOrTo, toOrUndefined, opts) => {
40052
+ try {
40053
+ let amount;
40054
+ let from8;
40055
+ let to3;
40056
+ if (toOrUndefined) {
40057
+ amount = amountOrFrom;
40058
+ from8 = fromOrTo;
40059
+ to3 = toOrUndefined;
40060
+ } else {
40061
+ from8 = amountOrFrom;
40062
+ to3 = fromOrTo;
40063
+ }
40064
+ if (!amount && !opts.usd) {
40065
+ error("Either <amount> or --usd <amount> is required");
39807
40066
  return;
39808
40067
  }
39809
- const price = await swapClient.getTokenPrice(fromChain, fromInfo.address);
39810
- if (!price) {
39811
- error(`Unable to fetch price for ${from8} on ${fromChain}`);
40068
+ if (amount && opts.usd) {
40069
+ error("Provide either <amount> or --usd, not both");
39812
40070
  return;
39813
40071
  }
39814
- 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);
40072
+ if (amount) {
40073
+ const n4 = parseFloat(amount);
40074
+ if (isNaN(n4) || n4 <= 0) {
40075
+ error("Amount must be greater than 0");
40076
+ process.exitCode = 1;
40077
+ return;
40078
+ }
39830
40079
  }
39831
- 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({
40080
+ const slippage = parseFloat(opts.slippage);
40081
+ if (isNaN(slippage) || slippage <= 0) {
40082
+ error("--slippage must be a positive number");
40083
+ process.exitCode = 1;
40084
+ return;
40085
+ }
40086
+ if (slippage > MAX_SLIPPAGE_PCT) {
40087
+ error(
40088
+ `Slippage ${slippage}% exceeds maximum allowed (${MAX_SLIPPAGE_PCT}%). Reduce --slippage to proceed.`
40089
+ );
40090
+ process.exitCode = 1;
40091
+ return;
40092
+ }
40093
+ if (slippage > WARN_SLIPPAGE_PCT) {
40094
+ warn(
40095
+ `High slippage tolerance: ${slippage}%. You may receive significantly fewer tokens than quoted.`
40096
+ );
40097
+ }
40098
+ const client = await getClient();
40099
+ const swapClient = new SwapClient(client);
40100
+ const detectChain = (token, fallback) => {
40101
+ const m2 = token.match(/^c(\d+)/i);
40102
+ if (m2) {
40103
+ const key = coinTypeToChainKey(parseInt(m2[1]));
40104
+ if (!key) throw new Error(`Unknown coin ID ${m2[1]} in token "${token}"`);
40105
+ return key;
40106
+ }
40107
+ return fallback;
40108
+ };
40109
+ const fromChain = detectChain(from8, opts.chain);
40110
+ const toChain = opts.toChain ? detectChain(to3, opts.toChain) : fromChain;
40111
+ const fromInfo = resolveToken(from8, fromChain);
40112
+ if (!fromInfo) {
40113
+ error(
40114
+ `Unknown token: ${from8} on ${fromChain}. Use a contract address (0x...) or a supported symbol.`
40115
+ );
40116
+ process.exitCode = 1;
40117
+ return;
40118
+ }
40119
+ const toInfo = resolveToken(to3, toChain);
40120
+ if (!toInfo) {
40121
+ error(
40122
+ `Unknown token: ${to3} on ${toChain}. Use a contract address (0x...) or a supported symbol.`
40123
+ );
40124
+ process.exitCode = 1;
40125
+ return;
40126
+ }
40127
+ let resolvedAmount = amount;
40128
+ if (opts.usd) {
40129
+ const usdAmount = parseFloat(opts.usd);
40130
+ if (isNaN(usdAmount) || usdAmount <= 0) {
40131
+ error("--usd amount must be a positive number");
40132
+ return;
40133
+ }
40134
+ const price = await swapClient.getTokenPrice(fromChain, fromInfo.address);
40135
+ if (!price) {
40136
+ error(`Unable to fetch price for ${from8} on ${fromChain}`);
40137
+ return;
40138
+ }
40139
+ resolvedAmount = (usdAmount / price).toFixed(fromInfo.decimals);
40140
+ info(`$${usdAmount} USD \u2248 ${resolvedAmount} ${from8.toUpperCase()} (@ $${price})`);
40141
+ }
40142
+ const parsedAmount = parseAmount(resolvedAmount, fromInfo.decimals);
40143
+ const needsWallet = !opts.quoteOnly;
40144
+ let walletAddress = getChainById(fromChain)?.sampleAccount || "0x0000000000000000000000000000000000000000";
40145
+ let provider = null;
40146
+ let destinationAddress;
40147
+ let amberDomains = /* @__PURE__ */ new Map();
40148
+ if (needsWallet) {
40149
+ await ensureTrustSDK();
40150
+ const password = await resolvePassword(opts);
40151
+ provider = await AgentWalletProvider.fromStorage(password);
40152
+ walletAddress = await provider.getAddress(fromChain);
40153
+ if (toChain !== fromChain) {
40154
+ destinationAddress = await provider.getAddress(toChain);
40155
+ }
40156
+ amberDomains = await getAmberDomainsByCoinId();
40157
+ }
40158
+ const fromCoinId = getChainById(fromChain)?.coinId;
40159
+ const toCoinId = getChainById(toChain)?.coinId;
40160
+ const quote = await swapClient.getQuote({
40161
+ fromChain,
40162
+ fromToken: fromInfo.address,
40163
+ toChain,
40164
+ toToken: toInfo.address,
40165
+ amount: parsedAmount,
40166
+ walletAddress,
40167
+ destinationAddress,
40168
+ slippage: opts.slippage,
40169
+ fromDomainId: fromCoinId ? amberDomains.get(fromCoinId) : void 0,
40170
+ toDomainId: toCoinId ? amberDomains.get(toCoinId) : void 0
40171
+ });
40172
+ if (quote.routes.length === 0) {
40173
+ error("No swap routes found");
40174
+ if (opts.json) output({ error: "No routes found" }, true);
40175
+ return;
40176
+ }
40177
+ const route = quote.routes[0];
40178
+ const firstStep = route.steps[0];
40179
+ const lastStep = route.steps[route.steps.length - 1];
40180
+ const inputAmt = formatAmount(
40181
+ firstStep.from.amount,
40182
+ firstStep.from.asset.metadata.decimals
40183
+ );
40184
+ const outputAmt = formatAmount(lastStep.to.amount, lastStep.to.asset.metadata.decimals);
40185
+ const minAmountOut = lastStep.to.minAmountOut;
40186
+ const minReceivedAmt = minAmountOut ? formatAmount(minAmountOut, lastStep.to.asset.metadata.decimals) : null;
40187
+ const networkFees = route.steps.filter((s2) => s2.networkFee).map((s2) => {
40188
+ const fee = s2.networkFee;
40189
+ const feeAmt = formatAmount(fee.amount, fee.asset.metadata.decimals);
40190
+ return `${feeAmt} ${fee.asset.metadata.symbol}`;
40191
+ });
40192
+ const quoteData = {
40193
+ input: `${inputAmt} ${firstStep.from.asset.metadata.symbol}`,
40194
+ output: `${outputAmt} ${lastStep.to.asset.metadata.symbol}`,
40195
+ ...minReceivedAmt ? { minReceived: `${minReceivedAmt} ${lastStep.to.asset.metadata.symbol}` } : {},
40196
+ provider: route.steps.map((s2) => s2.provider.name).join(" \u2192 "),
40197
+ priceImpact: firstStep.priceImpact,
40198
+ ...networkFees.length > 0 ? { networkFee: networkFees.join(" + ") } : {},
40199
+ ...route.steps.length > 1 ? { steps: route.steps.length } : {}
40200
+ };
40201
+ if (opts.quoteOnly) {
40202
+ output(quoteData, opts.json);
40203
+ return;
40204
+ }
40205
+ info(`Swapping ${quoteData.input} \u2192 ${quoteData.output} via ${quoteData.provider}...`);
40206
+ const chainConfig = getChainById(fromChain);
40207
+ const transactions = await swapClient.getSwapTransactions(route);
40208
+ let txHash = "";
40209
+ const blockchainClient = provider.getBlockchainClient();
40210
+ const hasApprovalTx = transactions.some((t) => t.transaction.type === "approval");
40211
+ if (!hasApprovalTx && chainConfig?.isEVM) {
40212
+ const stepApproval = firstStep.approval;
40213
+ if (stepApproval && stepApproval.operatorAddress) {
40214
+ const fromAssetAddr = firstStep.from.asset.address;
40215
+ const isNative = !fromAssetAddr || fromAssetAddr === NATIVE_TOKEN_ADDRESS;
40216
+ if (!isNative && chainConfig.coinId) {
40217
+ const assetId = formatTokenId(chainConfig.coinId, fromAssetAddr);
40218
+ try {
40219
+ const allowance = await blockchainClient.getAllowance({
39901
40220
  assetId,
39902
- spender: stepApproval.operatorAddress,
39903
- amount: requiredAmount.toString(),
39904
- hdWallet
40221
+ owner: walletAddress,
40222
+ spender: stepApproval.operatorAddress
39905
40223
  });
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;
40224
+ const requiredAmount = BigInt(stepApproval.minAmount || parsedAmount);
40225
+ if (allowance < requiredAmount) {
40226
+ info("Sending token approval...");
40227
+ const hdWallet = provider.getSigner().getHDWallet();
40228
+ const approveResult = await blockchainClient.approve({
40229
+ assetId,
40230
+ spender: stepApproval.operatorAddress,
40231
+ amount: requiredAmount.toString(),
40232
+ hdWallet
40233
+ });
40234
+ info(`Approval tx: ${getExplorerTxUrl(fromChain, approveResult.hash)}`);
40235
+ const approvalDeadline = Date.now() + 6e4;
40236
+ while (Date.now() < approvalDeadline) {
40237
+ await new Promise((r3) => setTimeout(r3, 2e3));
40238
+ try {
40239
+ const status = await blockchainClient.findTransactionStatus(
40240
+ fromChain,
40241
+ walletAddress,
40242
+ approveResult.hash
40243
+ );
40244
+ if (status.confirmed) break;
40245
+ if (status.failed) throw new Error("Approval transaction failed");
40246
+ } catch (e2) {
40247
+ const err = e2 instanceof Error ? e2 : new Error(String(e2));
40248
+ if (err.message.startsWith("Approval transaction failed")) throw err;
40249
+ }
39917
40250
  }
39918
40251
  }
40252
+ } catch (e2) {
40253
+ const msg = e2 instanceof Error ? e2.message : String(e2);
40254
+ if (msg.includes("insufficient funds")) {
40255
+ throw new Error(
40256
+ `Insufficient ${chainConfig?.symbol || "native token"} for approval gas. Fund your wallet with ${chainConfig?.symbol || "native tokens"} first.`
40257
+ );
40258
+ }
40259
+ if (msg.includes("failed")) throw e2;
40260
+ info(`Warning: could not check/send approval: ${msg}`);
39919
40261
  }
39920
- } catch (e2) {
39921
- const msg = e2 instanceof Error ? e2.message : String(e2);
39922
- if (msg.includes("insufficient funds")) {
39923
- throw new Error(`Insufficient ${chainConfig?.symbol || "native token"} for approval gas. Fund your wallet with ${chainConfig?.symbol || "native tokens"} first.`);
39924
- }
39925
- if (msg.includes("failed")) throw e2;
39926
- info(`Warning: could not check/send approval: ${msg}`);
39927
40262
  }
39928
40263
  }
39929
40264
  }
39930
- }
39931
- 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}`);
40265
+ const pollForConfirmation = async (hash, timeoutMs) => {
40266
+ const deadline = Date.now() + timeoutMs;
40267
+ while (Date.now() < deadline) {
40268
+ await new Promise((r3) => setTimeout(r3, 2e3));
40269
+ try {
40270
+ const status = await blockchainClient.findTransactionStatus(
40271
+ fromChain,
40272
+ walletAddress,
40273
+ hash
40274
+ );
40275
+ if (status.confirmed) return;
40276
+ if (status.failed)
40277
+ throw new Error(`Transaction failed: ${status.error || "unknown error"}`);
40278
+ } catch (e2) {
40279
+ const err = e2 instanceof Error ? e2 : new Error(String(e2));
40280
+ if (err.message.startsWith("Transaction failed")) throw err;
40281
+ if (!err.message.includes("not found") && !err.message.includes("Transaction not found")) {
40282
+ info(`Warning: tx status check error (will retry): ${err.message}`);
40283
+ }
39944
40284
  }
39945
40285
  }
40286
+ throw new Error(
40287
+ `Transaction ${hash} was not confirmed within ${Math.round(timeoutMs / 1e3)}s`
40288
+ );
40289
+ };
40290
+ for (const txData of transactions) {
40291
+ const txType = txData.transaction.type;
40292
+ const isApproval = txType === "approval" || txType === "revokeApproval";
40293
+ if (isApproval) {
40294
+ info(
40295
+ txType === "revokeApproval" ? "Revoking prior approval..." : "Sending token approval..."
40296
+ );
40297
+ }
40298
+ if (chainConfig?.namespace === "solana" && txData.transaction.solanaTx) {
40299
+ const solanaChainId = `solana:${chainConfig.chainId}`;
40300
+ txHash = await provider.sendTransaction({
40301
+ chainId: solanaChainId,
40302
+ to: "",
40303
+ data: txData.transaction.solanaTx.serializedMessage
40304
+ });
40305
+ } else if (txData.transaction.evmTx) {
40306
+ const wcChainId = `eip155:${chainConfig?.chainId || "1"}`;
40307
+ txHash = await provider.sendTransaction({
40308
+ chainId: wcChainId,
40309
+ to: txData.transaction.evmTx.to,
40310
+ data: txData.transaction.evmTx.data,
40311
+ value: txData.transaction.evmTx.value || "0x0"
40312
+ });
40313
+ } else {
40314
+ error("No transaction data returned for this swap step");
40315
+ if (opts.json) output({ error: "No transaction data" }, true);
40316
+ return;
40317
+ }
40318
+ info(`${isApproval ? "Approval" : "Swap"} tx: ${getExplorerTxUrl(fromChain, txHash)}`);
40319
+ await pollForConfirmation(txHash, isApproval ? 6e4 : 12e4);
39946
40320
  }
39947
- 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);
40321
+ const explorerUrl = getExplorerTxUrl(fromChain, txHash);
40322
+ success("Swap executed!");
40323
+ output(
40324
+ {
40325
+ ...quoteData,
40326
+ hash: txHash,
40327
+ fromChain,
40328
+ toChain,
40329
+ explorer: explorerUrl
40330
+ },
40331
+ opts.json
40332
+ );
40333
+ } catch (err) {
40334
+ const msg = err instanceof Error ? err.message : String(err);
40335
+ error(`Swap failed: ${msg}`);
40336
+ if (opts.json) output({ error: msg }, true);
40337
+ process.exitCode = 1;
40338
+ }
40339
+ if (!opts.quoteOnly) {
40340
+ safeExit();
39977
40341
  }
39978
- const explorerUrl = getExplorerTxUrl(fromChain, txHash);
39979
- success("Swap executed!");
39980
- output({
39981
- ...quoteData,
39982
- hash: txHash,
39983
- fromChain,
39984
- toChain,
39985
- explorer: explorerUrl
39986
- }, opts.json);
39987
- } catch (err) {
39988
- const msg = err instanceof Error ? err.message : String(err);
39989
- error(`Swap failed: ${msg}`);
39990
- if (opts.json) output({ error: msg }, true);
39991
- }
39992
- if (!opts.quoteOnly) {
39993
- setTimeout(() => process.exit(0), 100);
39994
40342
  }
39995
- });
40343
+ );
39996
40344
  }
39997
40345
 
39998
40346
  // src/commands/erc20.ts
@@ -40011,7 +40359,7 @@ function registerErc20Command(program3) {
40011
40359
  error(msg);
40012
40360
  if (opts.json) output({ error: msg }, true);
40013
40361
  process.exitCode = 1;
40014
- process.exit();
40362
+ safeExit();
40015
40363
  return;
40016
40364
  }
40017
40365
  const chainKey = coinTypeToChainKey(coinId);
@@ -40020,7 +40368,7 @@ function registerErc20Command(program3) {
40020
40368
  error(msg);
40021
40369
  if (opts.json) output({ error: msg }, true);
40022
40370
  process.exitCode = 1;
40023
- process.exit();
40371
+ safeExit();
40024
40372
  return;
40025
40373
  }
40026
40374
  const address = await provider.getAddress(chainKey);
@@ -40031,7 +40379,7 @@ function registerErc20Command(program3) {
40031
40379
  error(msg);
40032
40380
  if (opts.json) output({ error: msg }, true);
40033
40381
  process.exitCode = 1;
40034
- process.exit();
40382
+ safeExit();
40035
40383
  return;
40036
40384
  }
40037
40385
  warn("WARNING: Unlimited approval grants the spender access to ALL tokens of this type.");
@@ -40062,7 +40410,7 @@ function registerErc20Command(program3) {
40062
40410
  if (opts.json) output({ error: msg }, true);
40063
40411
  process.exitCode = 1;
40064
40412
  }
40065
- process.exit();
40413
+ safeExit();
40066
40414
  });
40067
40415
  erc20.command("revoke").description("Revoke token approval (set allowance to 0)").requiredOption("--token <assetId>", "Token asset ID (e.g., c60_t0xA0b8...)").requiredOption("--spender <address>", "Spender address to revoke").option("--password <password>", "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)").option("--json", "JSON output").action(async (opts) => {
40068
40416
  try {
@@ -40075,7 +40423,7 @@ function registerErc20Command(program3) {
40075
40423
  error(msg);
40076
40424
  if (opts.json) output({ error: msg }, true);
40077
40425
  process.exitCode = 1;
40078
- process.exit();
40426
+ safeExit();
40079
40427
  return;
40080
40428
  }
40081
40429
  const chainKey = coinTypeToChainKey(coinId);
@@ -40084,7 +40432,7 @@ function registerErc20Command(program3) {
40084
40432
  error(msg);
40085
40433
  if (opts.json) output({ error: msg }, true);
40086
40434
  process.exitCode = 1;
40087
- process.exit();
40435
+ safeExit();
40088
40436
  return;
40089
40437
  }
40090
40438
  const address = await provider.getAddress(chainKey);
@@ -40113,7 +40461,7 @@ function registerErc20Command(program3) {
40113
40461
  if (opts.json) output({ error: msg }, true);
40114
40462
  process.exitCode = 1;
40115
40463
  }
40116
- process.exit();
40464
+ safeExit();
40117
40465
  });
40118
40466
  erc20.command("allowance").description("Check token allowance").requiredOption("--token <assetId>", "Token asset ID (e.g., c60_t0xA0b8...)").requiredOption("--owner <address>", "Owner address").requiredOption("--spender <address>", "Spender address").option("--json", "JSON output").action(async (opts) => {
40119
40467
  try {
@@ -40136,19 +40484,50 @@ function registerErc20Command(program3) {
40136
40484
  if (opts.json) output({ error: msg }, true);
40137
40485
  process.exitCode = 1;
40138
40486
  }
40139
- process.exit();
40487
+ safeExit();
40140
40488
  });
40141
40489
  }
40142
40490
 
40143
40491
  // src/commands/alert.ts
40144
40492
  init_dist();
40145
40493
  init_dist2();
40146
- function resolveTokenLabel(tokenId) {
40494
+ init_dist5();
40495
+ var assetCache = new AssetCache();
40496
+ async function resolveTokenLabel(tokenId, service) {
40497
+ const cached2 = await assetCache.get(tokenId);
40498
+ if (cached2 !== void 0) return cached2;
40147
40499
  try {
40148
40500
  const { coinId, contractAddress } = parseTokenId(tokenId);
40149
40501
  const chain2 = getChainByCoinId(coinId);
40150
- if (chain2 && !contractAddress) return chain2.symbol;
40151
- return tokenId;
40502
+ if (!chain2) return tokenId;
40503
+ if (!contractAddress) {
40504
+ return chain2.symbol;
40505
+ }
40506
+ const chainKey = tokenIdToChain(tokenId);
40507
+ if (chainKey) {
40508
+ const chainTokens = COMMON_TOKENS[chainKey];
40509
+ if (chainTokens) {
40510
+ const normalizedAddr = contractAddress.toLowerCase();
40511
+ for (const [sym, addr] of Object.entries(chainTokens)) {
40512
+ if (addr.toLowerCase() === normalizedAddr) {
40513
+ await assetCache.set(tokenId, sym);
40514
+ return sym;
40515
+ }
40516
+ }
40517
+ }
40518
+ }
40519
+ if (service) {
40520
+ try {
40521
+ const assetInfo = await service.getAssetInfo(tokenId);
40522
+ if (assetInfo.symbol) {
40523
+ await assetCache.set(tokenId, assetInfo.symbol);
40524
+ return assetInfo.symbol;
40525
+ }
40526
+ } catch {
40527
+ }
40528
+ }
40529
+ const short = contractAddress.length > 10 ? `${contractAddress.slice(0, 6)}\u2026${contractAddress.slice(-4)}` : contractAddress;
40530
+ return `${chain2.symbol}/${short}`;
40152
40531
  } catch {
40153
40532
  return tokenId;
40154
40533
  }
@@ -40212,7 +40591,9 @@ function registerAlertCommand(program3) {
40212
40591
  resolvedChain = NATIVE_SYMBOLS2[sym];
40213
40592
  }
40214
40593
  if (!resolvedChain) {
40215
- error(`Cannot resolve "${opts.token}" without --chain. Provide --chain or use asset ID format (c60).`);
40594
+ error(
40595
+ `Cannot resolve "${opts.token}" without --chain. Provide --chain or use asset ID format (c60).`
40596
+ );
40216
40597
  if (opts.json) output({ error: "Chain required" }, true);
40217
40598
  process.exitCode = 1;
40218
40599
  return;
@@ -40225,6 +40606,14 @@ function registerAlertCommand(program3) {
40225
40606
  return;
40226
40607
  }
40227
40608
  const resolved = resolveToken(sym, resolvedChain);
40609
+ if (!resolved) {
40610
+ error(
40611
+ `Unknown token: ${sym} on ${resolvedChain}. Use a contract address or asset ID (e.g. c60_t0xABC...).`
40612
+ );
40613
+ if (opts.json) output({ error: `Unknown token: ${sym}` }, true);
40614
+ process.exitCode = 1;
40615
+ return;
40616
+ }
40228
40617
  if (resolved.address === NATIVE_TOKEN_ADDRESS || resolved.address === sym) {
40229
40618
  resolvedTokenId = formatTokenId(chain2.coinId);
40230
40619
  } else {
@@ -40248,15 +40637,19 @@ function registerAlertCommand(program3) {
40248
40637
  });
40249
40638
  alert.command("list").description("List all alerts").option("--active", "Show only active alerts").option("--json", "JSON output").action(async (opts) => {
40250
40639
  const alerts = opts.active ? await alertStore.getActive() : await alertStore.list();
40640
+ const client = await tryGetClient();
40641
+ const service = client ? new SecurityService(client) : void 0;
40251
40642
  if (opts.json) {
40252
- const enriched = 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
- });
40643
+ const enriched = await Promise.all(
40644
+ alerts.map(async (a3) => {
40645
+ let chainLabel = a3.chainKey;
40646
+ try {
40647
+ chainLabel = tokenIdToChain(a3.tokenId) ?? a3.chainKey;
40648
+ } catch {
40649
+ }
40650
+ return { ...a3, tokenLabel: await resolveTokenLabel(a3.tokenId, service), chainLabel };
40651
+ })
40652
+ );
40260
40653
  output(enriched, true);
40261
40654
  } else {
40262
40655
  if (alerts.length === 0) {
@@ -40264,7 +40657,7 @@ function registerAlertCommand(program3) {
40264
40657
  return;
40265
40658
  }
40266
40659
  for (const a3 of alerts) {
40267
- const label = resolveTokenLabel(a3.tokenId);
40660
+ const label = await resolveTokenLabel(a3.tokenId, service);
40268
40661
  let chainHint = "";
40269
40662
  try {
40270
40663
  const chain2 = tokenIdToChain(a3.tokenId);
@@ -40272,7 +40665,9 @@ function registerAlertCommand(program3) {
40272
40665
  } catch {
40273
40666
  chainHint = "";
40274
40667
  }
40275
- info(`${a3.id} | ${label}${chainHint} | ${a3.condition} $${a3.targetPrice} | ${a3.active ? "active" : "triggered"}`);
40668
+ info(
40669
+ `${a3.id} | ${label}${chainHint} | ${a3.condition} $${a3.targetPrice} | ${a3.active ? "active" : "triggered"}`
40670
+ );
40276
40671
  }
40277
40672
  }
40278
40673
  });
@@ -40292,16 +40687,34 @@ function registerAlertCommand(program3) {
40292
40687
  triggered.push({ ...a3, currentPrice: price });
40293
40688
  }
40294
40689
  }
40295
- output({ checked: activeAlerts.length, triggered: triggered.length, alerts: triggered }, opts.json);
40690
+ output(
40691
+ { checked: activeAlerts.length, triggered: triggered.length, alerts: triggered },
40692
+ opts.json
40693
+ );
40296
40694
  } catch (err) {
40297
40695
  const msg = err instanceof Error ? err.message : String(err);
40298
40696
  error(msg);
40299
40697
  if (opts.json) output({ error: msg }, true);
40698
+ process.exitCode = 1;
40300
40699
  }
40301
40700
  });
40302
40701
  alert.command("delete <id>").description("Delete an alert").option("--json", "JSON output").action(async (id, opts) => {
40303
- const deleted = await alertStore.delete(id);
40304
- output({ success: deleted }, opts.json);
40702
+ try {
40703
+ const deleted = await alertStore.delete(id);
40704
+ if (!deleted) {
40705
+ error(`Alert ${id} not found`);
40706
+ if (opts.json) output({ error: `Alert ${id} not found` }, true);
40707
+ process.exitCode = 1;
40708
+ return;
40709
+ }
40710
+ if (!opts.json) success(`Alert ${id} deleted`);
40711
+ output({ success: true }, opts.json);
40712
+ } catch (err) {
40713
+ const msg = err instanceof Error ? err.message : String(err);
40714
+ error(msg);
40715
+ if (opts.json) output({ error: msg }, true);
40716
+ process.exitCode = 1;
40717
+ }
40305
40718
  });
40306
40719
  }
40307
40720
 
@@ -40380,14 +40793,20 @@ function handleError(err, json) {
40380
40793
  process.exitCode = 1;
40381
40794
  }
40382
40795
  function registerQueryCommand(program3) {
40383
- program3.command("price <token>").description("Get token price").option("--chain <id>", "Chain").option("--history [period]", "Price history period: hour, day, week, month, year, all (default: week)").option("--json", "JSON output").action(async (token, opts) => {
40796
+ program3.command("price <token>").description("Get token price").option("--chain <id>", "Chain").option(
40797
+ "--history [period]",
40798
+ "Price history period: hour, day, week, month, year, all (default: week)"
40799
+ ).option("--json", "JSON output").action(async (token, opts) => {
40384
40800
  try {
40385
40801
  const client = await getClient();
40386
40802
  const swapClient = new SwapClient(client);
40387
40803
  const tokens = token.split(",").map((t) => t.trim()).filter(Boolean);
40388
40804
  const resolveChain = (t) => {
40389
40805
  if (opts.chain) return opts.chain;
40390
- const bySymbol = Object.entries(CHAINS).find(([, c3]) => c3.symbol.toUpperCase() === t.toUpperCase())?.[0];
40806
+ if (Object.prototype.hasOwnProperty.call(CHAINS, t.toLowerCase())) return t.toLowerCase();
40807
+ const bySymbol = Object.entries(CHAINS).find(
40808
+ ([, c3]) => c3.symbol.toUpperCase() === t.toUpperCase()
40809
+ )?.[0];
40391
40810
  if (bySymbol) return bySymbol;
40392
40811
  const upper = t.toUpperCase();
40393
40812
  for (const [chainKey, chainTokens] of Object.entries(COMMON_TOKENS)) {
@@ -40395,12 +40814,22 @@ function registerQueryCommand(program3) {
40395
40814
  }
40396
40815
  return "ethereum";
40397
40816
  };
40817
+ if (opts.chain && !getChainById(opts.chain)) {
40818
+ error(`Unknown chain: ${opts.chain}. Run 'twak chains' to list supported chains.`);
40819
+ if (opts.json) output({ error: `Unknown chain: ${opts.chain}` }, true);
40820
+ process.exitCode = 1;
40821
+ return;
40822
+ }
40398
40823
  if (tokens.length > 1) {
40399
40824
  const assetIds = [];
40400
40825
  const tokenMeta = [];
40401
40826
  for (const t of tokens) {
40402
40827
  const chain3 = resolveChain(t);
40403
40828
  const info2 = resolveToken(t, chain3);
40829
+ if (!info2) {
40830
+ warn(`Unknown token: ${t.toUpperCase()}`);
40831
+ continue;
40832
+ }
40404
40833
  const chainInfo = getChainById(chain3);
40405
40834
  if (!chainInfo) throw new Error(`Unknown chain: ${chain3}`);
40406
40835
  const isNative = !info2.address || info2.address === NATIVE_TOKEN_ADDRESS || info2.address === SOLANA_NATIVE_ADDRESS;
@@ -40408,10 +40837,19 @@ function registerQueryCommand(program3) {
40408
40837
  assetIds.push(assetId);
40409
40838
  tokenMeta.push({ token: t.toUpperCase(), chain: chain3 });
40410
40839
  }
40411
- const resp = await client.market("POST", "/v2/market/tickers", {
40412
- currency: "USD",
40413
- assets: assetIds
40414
- });
40840
+ if (assetIds.length === 0) {
40841
+ error("No valid tokens to look up");
40842
+ process.exitCode = 1;
40843
+ return;
40844
+ }
40845
+ const resp = await client.market(
40846
+ "POST",
40847
+ "/v2/market/tickers",
40848
+ {
40849
+ currency: "USD",
40850
+ assets: assetIds
40851
+ }
40852
+ );
40415
40853
  const priceMap = /* @__PURE__ */ new Map();
40416
40854
  for (const t of resp.tickers || []) {
40417
40855
  if (t.id && t.price != null) priceMap.set(t.id, t.price);
@@ -40426,39 +40864,58 @@ function registerQueryCommand(program3) {
40426
40864
  } else {
40427
40865
  for (const r3 of results) {
40428
40866
  const priceStr = r3.priceUsd !== null ? `$${r3.priceUsd.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 6 })}` : "N/A";
40429
- console.log(`${import_chalk6.default.bold(r3.token.padEnd(10))} ${priceStr.padStart(14)} ${import_chalk6.default.dim(r3.chain)}`);
40867
+ console.log(
40868
+ `${import_chalk6.default.bold(r3.token.padEnd(10))} ${priceStr.padStart(14)} ${import_chalk6.default.dim(r3.chain)}`
40869
+ );
40430
40870
  }
40431
40871
  }
40432
40872
  return;
40433
40873
  }
40434
40874
  const chain2 = resolveChain(token);
40435
40875
  const tokenInfo = resolveToken(token, chain2);
40436
- let price = await swapClient.getTokenPrice(chain2, tokenInfo.address);
40876
+ let price = null;
40877
+ if (tokenInfo) {
40878
+ price = await swapClient.getTokenPrice(chain2, tokenInfo.address);
40879
+ }
40437
40880
  if (price === null) {
40438
- const results = await swapClient.searchTokens({ query: token, chains: [chain2], limit: 1 });
40439
- if (results.length > 0 && results[0].priceUsd != null) {
40440
- price = results[0].priceUsd;
40881
+ const results = await swapClient.searchTokens({
40882
+ query: token,
40883
+ chains: [chain2],
40884
+ limit: 5
40885
+ });
40886
+ const exact = results.find(
40887
+ (r3) => r3.symbol?.toUpperCase() === token.toUpperCase() && r3.priceUsd != null
40888
+ );
40889
+ if (exact) {
40890
+ price = exact.priceUsd;
40441
40891
  }
40442
40892
  }
40443
40893
  if (price === null) {
40444
40894
  error(`Token not found: ${token}`);
40445
- if (opts.json) output({ token: token.toUpperCase(), chain: chain2, priceUsd: null, error: "Token not found" }, true);
40895
+ if (opts.json)
40896
+ output(
40897
+ { token: token.toUpperCase(), chain: chain2, priceUsd: null, error: "Token not found" },
40898
+ true
40899
+ );
40446
40900
  process.exitCode = 1;
40447
40901
  return;
40448
40902
  }
40449
40903
  if (opts.history !== void 0) {
40904
+ if (!tokenInfo) {
40905
+ error(`Price history not available for "${token}" \u2014 token not locally resolved. Use an asset ID (e.g. c60) or contract address.`);
40906
+ process.exitCode = 1;
40907
+ return;
40908
+ }
40450
40909
  const period = typeof opts.history === "string" ? opts.history : "week";
40451
40910
  const chainInfo = getChainById(chain2);
40452
40911
  if (!chainInfo) throw new Error(`Unknown chain: ${chain2}`);
40453
40912
  const isNative = !tokenInfo.address || tokenInfo.address === NATIVE_TOKEN_ADDRESS || tokenInfo.address === SOLANA_NATIVE_ADDRESS;
40454
40913
  const assetId = isNative ? `c${chainInfo.coinId}` : `c${chainInfo.coinId}_t${tokenInfo.address}`;
40455
- const chartData = await client.gateway(
40456
- "GET",
40457
- `/v1/market/charts/${assetId}`,
40458
- void 0,
40459
- `currency=USD&period=${period}`
40914
+ const chartData = await client.gateway("GET", `/v1/market/charts/${assetId}`, void 0, `currency=USD&period=${period}`);
40915
+ output(
40916
+ { token: token.toUpperCase(), chain: chain2, priceUsd: price, history: chartData.prices },
40917
+ opts.json
40460
40918
  );
40461
- output({ token: token.toUpperCase(), chain: chain2, priceUsd: price, history: chartData.prices }, opts.json);
40462
40919
  return;
40463
40920
  }
40464
40921
  if (opts.json) {
@@ -40482,7 +40939,9 @@ function registerQueryCommand(program3) {
40482
40939
  if (/^\d+$/.test(trimmed)) return trimmed;
40483
40940
  const chain2 = getChainById(trimmed);
40484
40941
  if (chain2) return String(chain2.coinId);
40485
- throw new Error(`Unknown network "${trimmed}". Use a supported chain name or numeric coin ID.`);
40942
+ throw new Error(
40943
+ `Unknown network "${trimmed}". Use a supported chain name or numeric coin ID.`
40944
+ );
40486
40945
  }).join(",");
40487
40946
  }
40488
40947
  const result = await swapClient.searchTokens({
@@ -40499,6 +40958,18 @@ function registerQueryCommand(program3) {
40499
40958
  try {
40500
40959
  const client = await getClient();
40501
40960
  const service = new TransactionService(client);
40961
+ if (opts.chain && !getChainById(opts.chain)) {
40962
+ error(`Unknown chain: ${opts.chain}. Run 'twak chains' to list supported chains.`);
40963
+ if (opts.json) output({ error: `Unknown chain: ${opts.chain}` }, true);
40964
+ process.exitCode = 1;
40965
+ return;
40966
+ }
40967
+ if (opts.address && opts.chain && !/^(0x[0-9a-fA-F]{40}|[1-9A-HJ-NP-Za-km-z]{20,}|[A-Za-z0-9]{20,})$/.test(opts.address)) {
40968
+ error(`Invalid address: "${opts.address}"`);
40969
+ if (opts.json) output({ error: `Invalid address: "${opts.address}"` }, true);
40970
+ process.exitCode = 1;
40971
+ return;
40972
+ }
40502
40973
  if (!opts.chain) {
40503
40974
  await ensureTrustSDK();
40504
40975
  const password = await resolvePassword(opts);
@@ -40550,7 +41021,9 @@ function registerQueryCommand(program3) {
40550
41021
  const intPart = big / divisor;
40551
41022
  const scale = 10000n;
40552
41023
  const fracScaled = big % divisor * scale / divisor;
40553
- const usdValue = price !== null && (intPart > 0n || fracScaled > 0n) && intPart <= BigInt(Number.MAX_SAFE_INTEGER) ? parseFloat(((Number(intPart) + Number(fracScaled) / 1e4) * price).toFixed(4)) : void 0;
41024
+ const usdValue = price !== null && (intPart > 0n || fracScaled > 0n) && intPart <= BigInt(Number.MAX_SAFE_INTEGER) ? parseFloat(
41025
+ ((Number(intPart) + Number(fracScaled) / 1e4) * price).toFixed(4)
41026
+ ) : void 0;
40554
41027
  return { ...tx, usdValue };
40555
41028
  } catch {
40556
41029
  return tx;
@@ -40572,9 +41045,16 @@ function registerQueryCommand(program3) {
40572
41045
  } catch (err) {
40573
41046
  handleError(err, opts.json);
40574
41047
  }
41048
+ safeExit();
40575
41049
  });
40576
41050
  program3.command("tx <hash>").description("Get transaction details by hash (on-chain RPC lookup)").requiredOption("--chain <name>", "Chain name").option("--address <address>", "Wallet address (auto-detected from wallet if omitted)").option("--password <pw>", "Wallet password (for auto-detecting address)").option("--json", "JSON output").action(async (hash, opts) => {
40577
41051
  try {
41052
+ if (!getChainById(opts.chain)) {
41053
+ error(`Unknown chain: ${opts.chain}. Run 'twak chains' to list supported chains.`);
41054
+ if (opts.json) output({ error: `Unknown chain: ${opts.chain}` }, true);
41055
+ process.exitCode = 1;
41056
+ return;
41057
+ }
40578
41058
  let address = opts.address;
40579
41059
  if (!address) {
40580
41060
  try {
@@ -40589,11 +41069,14 @@ function registerQueryCommand(program3) {
40589
41069
  await ensureTrustSDK();
40590
41070
  const blockchainClient = new BlockchainClient();
40591
41071
  const status = await blockchainClient.findTransactionStatus(opts.chain, address, hash);
40592
- output({
40593
- hash,
40594
- chain: opts.chain,
40595
- ...status
40596
- }, opts.json);
41072
+ output(
41073
+ {
41074
+ hash,
41075
+ chain: opts.chain,
41076
+ ...status
41077
+ },
41078
+ opts.json
41079
+ );
40597
41080
  } else {
40598
41081
  const client = await getClient();
40599
41082
  const service = new TransactionService(client);
@@ -40607,11 +41090,37 @@ function registerQueryCommand(program3) {
40607
41090
  program3.command("chains").description("List supported chains").option("--json", "JSON output").action(async (opts) => {
40608
41091
  const chainEntries = Object.entries(CHAINS);
40609
41092
  if (opts.json) {
40610
- output(chainEntries.map(([key, chain2]) => ({ key, ...chain2 })), true);
41093
+ output(
41094
+ chainEntries.map(([key, chain2]) => ({
41095
+ key,
41096
+ name: chain2.name,
41097
+ symbol: chain2.symbol,
41098
+ namespace: chain2.namespace,
41099
+ chainId: chain2.id,
41100
+ coinId: chain2.coinId
41101
+ })),
41102
+ true
41103
+ );
40611
41104
  } else {
40612
- 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}`);
41105
+ const rows = chainEntries.map(([key, chain2]) => ({
41106
+ key,
41107
+ name: chain2.name,
41108
+ symbol: chain2.symbol,
41109
+ namespace: chain2.namespace
41110
+ }));
41111
+ const keyW = Math.max(...rows.map((r3) => r3.key.length), 3);
41112
+ const nameW = Math.max(...rows.map((r3) => r3.name.length), 4);
41113
+ const symW = Math.max(...rows.map((r3) => r3.symbol.length), 6);
41114
+ const nsW = Math.max(...rows.map((r3) => r3.namespace.length), 9);
41115
+ console.log(
41116
+ import_chalk6.default.bold(
41117
+ `${"KEY".padEnd(keyW)} ${"NAME".padEnd(nameW)} ${"SYMBOL".padEnd(symW)} NAMESPACE`
41118
+ )
41119
+ );
41120
+ for (const r3 of rows) {
41121
+ console.log(
41122
+ `${r3.key.padEnd(keyW)} ${r3.name.padEnd(nameW)} ${r3.symbol.padEnd(symW)} ${r3.namespace.padEnd(nsW)}`
41123
+ );
40615
41124
  }
40616
41125
  }
40617
41126
  });
@@ -40650,7 +41159,11 @@ function registerQueryCommand(program3) {
40650
41159
  const apiClient = await getClient();
40651
41160
  const swapClient = new SwapClient(apiClient);
40652
41161
  const chainKey = opts.chain || Object.entries(CHAINS).find(([, c3]) => c3.coinId === coinId)?.[0] || "";
40653
- const tokens = await swapClient.searchTokens({ query: opts.token, chains: [chainKey], limit: 1 });
41162
+ const tokens = await swapClient.searchTokens({
41163
+ query: opts.token,
41164
+ chains: [chainKey],
41165
+ limit: 1
41166
+ });
40654
41167
  if (tokens.length > 0) {
40655
41168
  decimals = tokens[0].decimals ?? 18;
40656
41169
  symbol = tokens[0].symbol ?? opts.token;
@@ -40665,20 +41178,24 @@ function registerQueryCommand(program3) {
40665
41178
  const div = 10n ** BigInt(decimals);
40666
41179
  return `${big / div}.${(big % div).toString().padStart(decimals, "0").slice(0, 6)}`;
40667
41180
  };
40668
- output({
40669
- 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);
41181
+ output(
41182
+ {
41183
+ address: opts.address,
41184
+ chain: chain2.id,
41185
+ symbol,
41186
+ ...opts.token ? { token: opts.token } : {},
41187
+ available: fmt(balance.available),
41188
+ ...balance.staked ? { staked: fmt(balance.staked) } : {},
41189
+ ...balance.pending ? { pending: fmt(balance.pending) } : {},
41190
+ total: fmt(balance.total),
41191
+ raw: balance
41192
+ },
41193
+ opts.json
41194
+ );
40679
41195
  } catch (err) {
40680
41196
  handleError(err, opts.json);
40681
41197
  }
41198
+ safeExit();
40682
41199
  });
40683
41200
  program3.command("asset <assetId>").description("Get asset info").option("--json", "JSON output").action(async (assetId, opts) => {
40684
41201
  try {
@@ -40692,6 +41209,12 @@ function registerQueryCommand(program3) {
40692
41209
  });
40693
41210
  program3.command("validate").description("Validate an address").requiredOption("--address <address>", "Address to validate").option("--chain <key>", "Chain key (e.g., ethereum, solana)").option("--asset-id <id>", "Asset ID (alternative to --chain)").option("--json", "JSON output").action(async (opts) => {
40694
41211
  try {
41212
+ if (!opts.chain && !opts.assetId) {
41213
+ error("Provide --chain <key> or --asset-id <id> to validate an address");
41214
+ if (opts.json) output({ error: "--chain or --asset-id required" }, true);
41215
+ process.exitCode = 1;
41216
+ return;
41217
+ }
40695
41218
  let assetId = opts.assetId;
40696
41219
  if (!assetId && opts.chain) {
40697
41220
  const chain2 = getChainById(opts.chain);
@@ -40711,7 +41234,12 @@ function registerQueryCommand(program3) {
40711
41234
  if (!isValid2) {
40712
41235
  const chainInfo = getChainByCoinId(coinId);
40713
41236
  const label = chainInfo?.name ?? `coin ${coinId}`;
40714
- const result = { status: "error", valid: false, chain: chainInfo?.id, message: `Invalid address format for ${label}` };
41237
+ const result = {
41238
+ status: "error",
41239
+ valid: false,
41240
+ chain: chainInfo?.id,
41241
+ message: `Invalid address format for ${label}`
41242
+ };
40715
41243
  output(result, opts.json);
40716
41244
  return;
40717
41245
  }
@@ -40732,8 +41260,12 @@ function registerQueryCommand(program3) {
40732
41260
  } catch (err) {
40733
41261
  handleError(err, opts.json);
40734
41262
  }
41263
+ safeExit();
40735
41264
  });
40736
- program3.command("trending").description("Get trending tokens by market activity or category").option("--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) => {
41265
+ program3.command("trending").description("Get trending tokens by market activity or category").option(
41266
+ "--category <id>",
41267
+ "Category: ai, rwa, memes, defi, dex, bnb, eth, sol, pumpfun, bonk, launchpad, launchpool, layer1"
41268
+ ).option("--sort <by>", "Sort: price_change, market_cap, volume (default: price_change)").option("--limit <n>", "Max results", "20").option("--json", "JSON output").action(async (opts) => {
40737
41269
  try {
40738
41270
  const client = await getClient();
40739
41271
  const service = new SecurityService(client);
@@ -40757,15 +41289,13 @@ function registerQueryCommand(program3) {
40757
41289
  launchpool: "binance-launchpool",
40758
41290
  layer1: "layer_1"
40759
41291
  };
40760
- 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
- }
41292
+ const categoryId = opts.category ? categoryMap[opts.category] ?? opts.category : void 0;
41293
+ const sort = opts.sort ? sortMap[opts.sort] ?? opts.sort : "percent_change_24h";
41294
+ const tokens = await service.getAssetListings({
41295
+ categoryId,
41296
+ sort,
41297
+ limit: parseInt(opts.limit, 10)
41298
+ });
40769
41299
  const seen = /* @__PURE__ */ new Map();
40770
41300
  for (const t of tokens) {
40771
41301
  const key = t.symbol.toUpperCase();
@@ -40781,14 +41311,19 @@ function registerQueryCommand(program3) {
40781
41311
  for (const t of deduped) {
40782
41312
  const price = t.priceUsd != null ? `$${t.priceUsd.toFixed(4)}` : "N/A";
40783
41313
  const change = t.priceChange24h != null ? `${t.priceChange24h > 0 ? "+" : ""}${t.priceChange24h.toFixed(2)}%` : "";
40784
- console.log(`${t.symbol.padEnd(10)} ${t.name.padEnd(25)} ${price.padStart(12)} ${change.padStart(8)}`);
41314
+ console.log(
41315
+ `${t.symbol.padEnd(10)} ${t.name.padEnd(25)} ${price.padStart(12)} ${change.padStart(8)}`
41316
+ );
40785
41317
  }
40786
41318
  }
40787
41319
  } catch (err) {
40788
41320
  handleError(err, opts.json);
40789
41321
  }
40790
41322
  });
40791
- program3.command("dapps").description("Browse DeFi protocols and DApps (data from DeFi Llama)").option("--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) => {
41323
+ program3.command("dapps").description("Browse DeFi protocols and DApps (data from DeFi Llama)").option(
41324
+ "--category <name>",
41325
+ "Filter by category (e.g., Lending, Dexs, Liquid Staking, Bridge, Yield)"
41326
+ ).option("--search <query>", "Search by name, symbol, or category").option("--categories", "List available categories").option("--limit <n>", "Max results", "20").option("--json", "JSON output").action(async (opts) => {
40792
41327
  try {
40793
41328
  const { DappsService: DappsService2 } = await Promise.resolve().then(() => (init_dapps_service(), dapps_service_exports));
40794
41329
  const service = new DappsService2();
@@ -40808,12 +41343,16 @@ function registerQueryCommand(program3) {
40808
41343
  if (dapps.length === 0) {
40809
41344
  console.log("No DApps found.");
40810
41345
  } else {
40811
- console.log("Name".padEnd(25) + "Category".padEnd(20) + "TVL".padStart(15) + " Chains");
41346
+ console.log(
41347
+ "Name".padEnd(25) + "Category".padEnd(20) + "TVL".padStart(15) + " Chains"
41348
+ );
40812
41349
  console.log("\u2500".repeat(80));
40813
41350
  for (const d3 of dapps) {
40814
41351
  const tvl = d3.tvl >= 1e9 ? `$${(d3.tvl / 1e9).toFixed(2)}B` : d3.tvl >= 1e6 ? `$${(d3.tvl / 1e6).toFixed(0)}M` : `$${(d3.tvl / 1e3).toFixed(0)}K`;
40815
41352
  const chains = d3.chains.slice(0, 5).join(", ") + (d3.chains.length > 5 ? ` +${d3.chains.length - 5}` : "");
40816
- console.log(`${d3.name.slice(0, 24).padEnd(25)}${d3.category.slice(0, 19).padEnd(20)}${tvl.padStart(15)} ${chains}`);
41353
+ console.log(
41354
+ `${d3.name.slice(0, 24).padEnd(25)}${d3.category.slice(0, 19).padEnd(20)}${tvl.padStart(15)} ${chains}`
41355
+ );
40817
41356
  }
40818
41357
  }
40819
41358
  }
@@ -45220,7 +45759,9 @@ var SwapActionProvider = class extends ActionProvider {
45220
45759
  return { success: false, error: `Slippage must be greater than 0% and at most 50%. Got: ${input.slippage}` };
45221
45760
  }
45222
45761
  const fromTokenInfo = resolveToken(input.fromToken, input.fromChain);
45762
+ if (!fromTokenInfo) return { success: false, error: `Unknown token: ${input.fromToken} on ${input.fromChain}` };
45223
45763
  const toTokenInfo = resolveToken(input.toToken, input.toChain);
45764
+ if (!toTokenInfo) return { success: false, error: `Unknown token: ${input.toToken} on ${input.toChain}` };
45224
45765
  const parsedAmount = parseAmount(input.amount, fromTokenInfo.decimals);
45225
45766
  const address = await signer.getAddress(input.fromChain);
45226
45767
  const quote = await this.swapClient.getQuote({
@@ -45273,7 +45814,9 @@ var SwapActionProvider = class extends ActionProvider {
45273
45814
  invoke: async (_ctx, input) => {
45274
45815
  if (!this.swapClient) return { success: false, error: "Swap client required" };
45275
45816
  const fromTokenInfo = resolveToken(input.fromToken, input.fromChain);
45817
+ if (!fromTokenInfo) return { success: false, error: `Unknown token: ${input.fromToken} on ${input.fromChain}` };
45276
45818
  const toTokenInfo = resolveToken(input.toToken, input.toChain);
45819
+ if (!toTokenInfo) return { success: false, error: `Unknown token: ${input.toToken} on ${input.toChain}` };
45277
45820
  const parsedAmount = parseAmount(input.amount, fromTokenInfo.decimals);
45278
45821
  const quote = await this.swapClient.getQuote({
45279
45822
  fromChain: input.fromChain,
@@ -45310,6 +45853,7 @@ var SwapActionProvider = class extends ActionProvider {
45310
45853
  invoke: async (_ctx, input) => {
45311
45854
  if (!this.swapClient) return { success: false, error: "Swap client required" };
45312
45855
  const tokenInfo = resolveToken(input.token, input.chain);
45856
+ if (!tokenInfo) return { success: false, error: `Unknown token: ${input.token} on ${input.chain}` };
45313
45857
  const price = await this.swapClient.getTokenPrice(input.chain, tokenInfo.address);
45314
45858
  if (price === null) return { success: false, error: `Price not found for ${input.token}` };
45315
45859
  return { success: true, token: input.token.toUpperCase(), chain: input.chain, priceUsd: price };
@@ -45745,7 +46289,7 @@ var MarketActionProvider = class extends ActionProvider {
45745
46289
  limit: external_exports.coerce.number().optional().default(20).describe("Number of tokens to return (default 20)")
45746
46290
  }),
45747
46291
  invoke: async (ctx, input) => {
45748
- const tokens = await this.getService(ctx).getTrendingTokens(input.limit);
46292
+ const tokens = await this.getService(ctx).getAssetListings({ limit: input.limit });
45749
46293
  return { success: true, count: tokens.length, tokens };
45750
46294
  }
45751
46295
  };
@@ -45908,7 +46452,9 @@ var AutomateActionProvider = class extends ActionProvider {
45908
46452
  if (!record2) return { success: false, error: `Automation ${input.id} not found` };
45909
46453
  if (!record2.active) return { success: false, error: `Automation ${input.id} is inactive` };
45910
46454
  const fromTokenInfo = resolveToken(record2.fromToken, record2.chain);
46455
+ if (!fromTokenInfo) return { success: false, error: `Unknown token: ${record2.fromToken} on ${record2.chain}` };
45911
46456
  const toTokenInfo = resolveToken(record2.toToken, record2.chain);
46457
+ if (!toTokenInfo) return { success: false, error: `Unknown token: ${record2.toToken} on ${record2.chain}` };
45912
46458
  const parsedAmount = parseAmount(record2.amount, fromTokenInfo.decimals);
45913
46459
  const address = await this.walletProvider.getAddress(record2.chain);
45914
46460
  const quote = await this.swapClient.getQuote({
@@ -53051,7 +53597,7 @@ var Protocol = class {
53051
53597
  return;
53052
53598
  }
53053
53599
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
53054
- await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
53600
+ await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
53055
53601
  options?.signal?.throwIfAborted();
53056
53602
  }
53057
53603
  } catch (error3) {
@@ -53068,7 +53614,7 @@ var Protocol = class {
53068
53614
  */
53069
53615
  request(request, resultSchema, options) {
53070
53616
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
53071
- return new Promise((resolve3, reject) => {
53617
+ return new Promise((resolve2, reject) => {
53072
53618
  const earlyReject = (error3) => {
53073
53619
  reject(error3);
53074
53620
  };
@@ -53146,7 +53692,7 @@ var Protocol = class {
53146
53692
  if (!parseResult.success) {
53147
53693
  reject(parseResult.error);
53148
53694
  } else {
53149
- resolve3(parseResult.data);
53695
+ resolve2(parseResult.data);
53150
53696
  }
53151
53697
  } catch (error3) {
53152
53698
  reject(error3);
@@ -53407,12 +53953,12 @@ var Protocol = class {
53407
53953
  }
53408
53954
  } catch {
53409
53955
  }
53410
- return new Promise((resolve3, reject) => {
53956
+ return new Promise((resolve2, reject) => {
53411
53957
  if (signal.aborted) {
53412
53958
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
53413
53959
  return;
53414
53960
  }
53415
- const timeoutId = setTimeout(resolve3, interval);
53961
+ const timeoutId = setTimeout(resolve2, interval);
53416
53962
  signal.addEventListener("abort", () => {
53417
53963
  clearTimeout(timeoutId);
53418
53964
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -54282,12 +54828,12 @@ var StdioServerTransport = class {
54282
54828
  this.onclose?.();
54283
54829
  }
54284
54830
  send(message) {
54285
- return new Promise((resolve3) => {
54831
+ return new Promise((resolve2) => {
54286
54832
  const json = serializeMessage(message);
54287
54833
  if (this._stdout.write(json)) {
54288
- resolve3();
54834
+ resolve2();
54289
54835
  } else {
54290
- this._stdout.once("drain", resolve3);
54836
+ this._stdout.once("drain", resolve2);
54291
54837
  }
54292
54838
  });
54293
54839
  }
@@ -54598,7 +55144,7 @@ var TwakRestServer = class {
54598
55144
  };
54599
55145
  }
54600
55146
  start() {
54601
- return new Promise((resolve3, reject) => {
55147
+ return new Promise((resolve2, reject) => {
54602
55148
  const server = (0, import_http.createServer)((req, res) => this.handleRequest(req, res));
54603
55149
  this.httpServer = server;
54604
55150
  server.on("error", (err) => {
@@ -54609,7 +55155,9 @@ var TwakRestServer = class {
54609
55155
  }
54610
55156
  });
54611
55157
  server.listen(this.config.port, this.config.host, () => {
54612
- console.error(`TWAK REST server listening on http://${this.config.host}:${this.config.port}`);
55158
+ console.error(
55159
+ `TWAK REST server listening on http://${this.config.host}:${this.config.port}`
55160
+ );
54613
55161
  console.error("Endpoints: POST /actions/:name");
54614
55162
  if (this.config.apiKey) {
54615
55163
  console.error("API key authentication enabled (use Authorization: Bearer <HMAC_SECRET>)");
@@ -54617,28 +55165,38 @@ var TwakRestServer = class {
54617
55165
  console.error("WARNING: No API key configured \u2014 REST server is unauthenticated");
54618
55166
  }
54619
55167
  if (this.config.paymentRequired) {
54620
- console.error(`x402 payment required: ${this.config.paymentAmount} ${this.config.paymentAsset} on ${this.config.paymentChainId}`);
55168
+ console.error(
55169
+ `x402 payment required: ${this.config.paymentAmount} ${this.config.paymentAsset} on ${this.config.paymentChainId}`
55170
+ );
54621
55171
  }
54622
- resolve3();
55172
+ resolve2();
54623
55173
  });
54624
55174
  });
54625
55175
  }
54626
55176
  stop() {
54627
- return new Promise((resolve3) => {
55177
+ return new Promise((resolve2) => {
54628
55178
  if (this.httpServer?.listening) {
54629
55179
  this.httpServer.close(() => {
54630
55180
  this.httpServer = null;
54631
- resolve3();
55181
+ resolve2();
54632
55182
  });
54633
55183
  } else {
54634
55184
  this.httpServer = null;
54635
- resolve3();
55185
+ resolve2();
54636
55186
  }
54637
55187
  });
54638
55188
  }
54639
55189
  async handleRequest(req, res) {
54640
55190
  res.setHeader("Content-Type", "application/json");
54641
- res.setHeader("Access-Control-Allow-Origin", "*");
55191
+ const origin = req.headers.origin || "";
55192
+ try {
55193
+ const parsed = new URL(origin);
55194
+ if (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "::1") {
55195
+ res.setHeader("Access-Control-Allow-Origin", origin);
55196
+ res.setHeader("Vary", "Origin");
55197
+ }
55198
+ } catch {
55199
+ }
54642
55200
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
54643
55201
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Payment, Authorization");
54644
55202
  if (req.method === "OPTIONS") {
@@ -54656,16 +55214,20 @@ var TwakRestServer = class {
54656
55214
  const tokenValid = token && token.length === expected.length && (0, import_crypto5.timingSafeEqual)(Buffer.from(token), Buffer.from(expected));
54657
55215
  if (!tokenValid) {
54658
55216
  res.writeHead(401);
54659
- res.end(JSON.stringify({ error: "Unauthorized \u2014 provide Authorization: Bearer <HMAC_SECRET>" }));
55217
+ res.end(
55218
+ JSON.stringify({ error: "Unauthorized \u2014 provide Authorization: Bearer <HMAC_SECRET>" })
55219
+ );
54660
55220
  return;
54661
55221
  }
54662
55222
  }
54663
55223
  if (req.method === "GET" && pathname === "/actions") {
54664
55224
  const actions = this.registry.getActions();
54665
55225
  res.writeHead(200);
54666
- res.end(JSON.stringify({
54667
- actions: actions.map((a3) => ({ name: a3.name, description: a3.description }))
54668
- }));
55226
+ res.end(
55227
+ JSON.stringify({
55228
+ actions: actions.map((a3) => ({ name: a3.name, description: a3.description }))
55229
+ })
55230
+ );
54669
55231
  return;
54670
55232
  }
54671
55233
  const match = pathname.match(/^\/actions\/([^/]+)$/);
@@ -54678,31 +55240,44 @@ var TwakRestServer = class {
54678
55240
  if (this.config.paymentRequired) {
54679
55241
  const paymentHeader = req.headers["x-payment"];
54680
55242
  if (!paymentHeader) {
54681
- res.setHeader("WWW-Authenticate", `x402 realm="twak", asset="${this.config.paymentAsset}", amount="${this.config.paymentAmount}", chain="${this.config.paymentChainId}"`);
55243
+ res.setHeader(
55244
+ "WWW-Authenticate",
55245
+ `x402 realm="twak", asset="${this.config.paymentAsset}", amount="${this.config.paymentAmount}", chain="${this.config.paymentChainId}"`
55246
+ );
54682
55247
  res.writeHead(402);
54683
- res.end(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
- }));
55248
+ res.end(
55249
+ JSON.stringify({
55250
+ error: "payment_required",
55251
+ message: `This endpoint requires payment of ${this.config.paymentAmount} ${this.config.paymentAsset}`,
55252
+ asset: this.config.paymentAsset,
55253
+ amount: this.config.paymentAmount,
55254
+ chainId: this.config.paymentChainId,
55255
+ recipient: this.config.paymentRecipient || void 0
55256
+ })
55257
+ );
54691
55258
  return;
54692
55259
  }
54693
55260
  if (this.config.paymentRecipient) {
54694
- const result = await verifyPayment(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);
55261
+ const result = await verifyPayment(
55262
+ paymentHeader,
55263
+ {
55264
+ asset: this.config.paymentAsset,
55265
+ amount: this.config.paymentAmount,
55266
+ chainId: this.config.paymentChainId,
55267
+ recipient: this.config.paymentRecipient
55268
+ },
55269
+ this.context.httpClient,
55270
+ this.seenHashes,
55271
+ this.config.blockchainClient
55272
+ );
54700
55273
  if (!result.valid) {
54701
55274
  res.writeHead(402);
54702
- res.end(JSON.stringify({
54703
- error: "payment_verification_failed",
54704
- message: result.error ?? "Payment could not be verified"
54705
- }));
55275
+ res.end(
55276
+ JSON.stringify({
55277
+ error: "payment_verification_failed",
55278
+ message: result.error ?? "Payment could not be verified"
55279
+ })
55280
+ );
54706
55281
  return;
54707
55282
  }
54708
55283
  }
@@ -54857,6 +55432,11 @@ function registerAutomateCommand(program3) {
54857
55432
  const automate = program3.command("automate").description("Manage DCA and limit order automations");
54858
55433
  automate.command("add").description("Create a DCA or limit order automation").requiredOption("--from <token>", "Source token (e.g., USDC)").requiredOption("--to <token>", "Destination token (e.g., ETH)").option("--chain <chain>", "Chain", "ethereum").requiredOption("--amount <n>", "Amount of source token per run").option("--interval <duration>", "DCA interval \u2014 e.g. 30s, 5m, 1h (creates DCA automation)").option("--price <usd>", "Target price in USD (creates limit order)").option("--condition <cond>", "Price condition: above or below", "below").option("--max-runs <n>", "Stop after N executions").option("--expires <date>", "Expiry date (ISO 8601, e.g. 2026-04-01)").option("--json", "JSON output").action(async (opts) => {
54859
55434
  try {
55435
+ if (opts.interval && opts.price) {
55436
+ error("Provide --interval (DCA) or --price (limit order), not both");
55437
+ process.exitCode = 1;
55438
+ return;
55439
+ }
54860
55440
  if (!opts.interval && !opts.price) {
54861
55441
  error("Provide --interval (DCA) or --price (limit order)");
54862
55442
  process.exitCode = 1;
@@ -54873,6 +55453,13 @@ function registerAutomateCommand(program3) {
54873
55453
  process.exitCode = 1;
54874
55454
  return;
54875
55455
  }
55456
+ const SWAP_SUPPORTED_NAMESPACES = ["eip155", "solana"];
55457
+ const chainInfo = getChainById(opts.chain);
55458
+ if (chainInfo && !SWAP_SUPPORTED_NAMESPACES.includes(chainInfo.namespace)) {
55459
+ error(`${chainInfo.name} automations are not supported. Use an EVM chain or Solana.`);
55460
+ process.exitCode = 1;
55461
+ return;
55462
+ }
54876
55463
  let intervalMs;
54877
55464
  if (opts.interval) {
54878
55465
  const parsed = parseInterval(opts.interval);
@@ -54915,9 +55502,13 @@ function registerAutomateCommand(program3) {
54915
55502
  });
54916
55503
  success("Automation created!");
54917
55504
  if (opts.interval && intervalMs) {
54918
- info(`DCA: ${opts.amount} ${opts.from} \u2192 ${opts.to} every ${formatInterval(intervalMs)} on ${opts.chain}`);
55505
+ info(
55506
+ `DCA: ${opts.amount} ${opts.from} \u2192 ${opts.to} every ${formatInterval(intervalMs)} on ${opts.chain}`
55507
+ );
54919
55508
  } else {
54920
- info(`Limit: swap ${opts.amount} ${opts.from} \u2192 ${opts.to} when price ${opts.condition} $${opts.price} on ${opts.chain}`);
55509
+ info(
55510
+ `Limit: swap ${opts.amount} ${opts.from} \u2192 ${opts.to} when price ${opts.condition} $${opts.price} on ${opts.chain}`
55511
+ );
54921
55512
  }
54922
55513
  output(record2, opts.json);
54923
55514
  } catch (err) {
@@ -54927,11 +55518,10 @@ function registerAutomateCommand(program3) {
54927
55518
  process.exitCode = 1;
54928
55519
  }
54929
55520
  });
54930
- automate.command("list").description("List active automations").option("--all", "Include inactive automations").option("--json", "JSON output").action(async (opts) => {
55521
+ automate.command("list").description("List automations").option("--json", "JSON output").action(async (opts) => {
54931
55522
  try {
54932
55523
  const store = new AutomationStore();
54933
- const all = await store.list();
54934
- const records = opts.all ? all : all.filter((a3) => a3.active);
55524
+ const records = await store.list();
54935
55525
  if (opts.json) {
54936
55526
  const enriched = records.map((r3) => {
54937
55527
  const nextRunAt = r3.type === "dca" && r3.intervalMs ? r3.lastRunAt ? new Date(new Date(r3.lastRunAt).getTime() + r3.intervalMs).toISOString() : (/* @__PURE__ */ new Date()).toISOString() : null;
@@ -54961,10 +55551,14 @@ function registerAutomateCommand(program3) {
54961
55551
  const nextAt = new Date(r3.lastRunAt).getTime() + r3.intervalMs;
54962
55552
  nextPart = `next: ${formatRelativeTime(nextAt - Date.now())}`;
54963
55553
  }
54964
- console.log(`[${tag}] ${label} ${pair} every ${interval} \u2014 ${lastPart}, ${nextPart}`);
55554
+ const statusTag = r3.active ? "" : " [paused]";
55555
+ console.log(
55556
+ `[${tag}] ${label} ${pair} every ${interval} \u2014 ${lastPart}, ${nextPart}${statusTag}`
55557
+ );
54965
55558
  } else {
54966
- const cond = `${r3.fromToken} ${r3.condition} $${r3.targetPrice}`;
54967
- console.log(`[${tag}] ${label} ${pair} when ${cond} \u2014 waiting`);
55559
+ const cond = `${r3.toToken} ${r3.condition} $${r3.targetPrice}`;
55560
+ const statusTag = r3.active ? "" : " [paused]";
55561
+ console.log(`[${tag}] ${label} ${pair} when ${cond} \u2014 waiting${statusTag}`);
54968
55562
  }
54969
55563
  }
54970
55564
  } catch (err) {
@@ -55029,66 +55623,6 @@ function registerAutomateCommand(program3) {
55029
55623
  });
55030
55624
  }
55031
55625
 
55032
- // src/commands/skill.ts
55033
- var import_fs = require("fs");
55034
- var import_path = require("path");
55035
- var import_os2 = require("os");
55036
- var SKILL_ID = "twak";
55037
- var SKILL_NAME = "TWAK \u2014 Trust Wallet Agent Kit";
55038
- function findSkillMd() {
55039
- const candidates = [
55040
- (0, import_path.resolve)(__dirname, "../../../../skill.md"),
55041
- (0, import_path.resolve)(__dirname, "../../../skill.md"),
55042
- (0, import_path.resolve)(__dirname, "../../skill.md"),
55043
- (0, import_path.resolve)(process.cwd(), "skill.md")
55044
- ];
55045
- for (const p3 of candidates) {
55046
- if ((0, import_fs.existsSync)(p3)) return p3;
55047
- }
55048
- return null;
55049
- }
55050
- function registerSkillCommand(program3) {
55051
- const skill = program3.command("skill").description("Manage AI skills for Claude Code and other agents");
55052
- skill.command("install").description("Install the TWAK skill into your Claude Code skills directory").option("--dir <path>", "Custom skills directory (default: ~/.claude/skills/)").action(async (opts) => {
55053
- const skillSrc = findSkillMd();
55054
- if (!skillSrc) {
55055
- error("skill.md not found. Run from the TWAK monorepo root or a published install.");
55056
- process.exitCode = 1;
55057
- return;
55058
- }
55059
- const skillsDir = opts.dir ? (0, import_path.resolve)(opts.dir) : (0, import_path.join)((0, import_os2.homedir)(), ".claude", "skills");
55060
- if (opts.dir) {
55061
- const { relative, sep: sep2 } = await import("path");
55062
- const safeBase = (0, import_path.join)((0, import_os2.homedir)(), ".claude");
55063
- const rel = relative(safeBase, skillsDir);
55064
- const isOutside = rel.startsWith("..") || rel.startsWith(sep2) || (0, import_path.resolve)(opts.dir) === (0, import_path.resolve)("/");
55065
- if (isOutside) {
55066
- error(`Refusing to install outside ~/.claude/ (got ${skillsDir}). Use --dir ~/.claude/skills or omit --dir.`);
55067
- process.exitCode = 1;
55068
- return;
55069
- }
55070
- }
55071
- (0, import_fs.mkdirSync)(skillsDir, { recursive: true });
55072
- const dest = (0, import_path.join)(skillsDir, `${SKILL_ID}.md`);
55073
- (0, import_fs.copyFileSync)(skillSrc, dest);
55074
- success(`Skill installed to ${dest}`);
55075
- info("Claude Code will load it automatically on next startup.");
55076
- });
55077
- skill.command("list").description("List available TWAK skills").option("--json", "JSON output").action((opts) => {
55078
- const skills = [{ id: SKILL_ID, name: SKILL_NAME, description: "Wallets, swaps, transfers, market data, alerts, DCA, on/off-ramp, and MCP server." }];
55079
- output(skills, opts.json);
55080
- });
55081
- skill.command("retrieve").description("Print the full TWAK skill instructions").action(() => {
55082
- const skillSrc = findSkillMd();
55083
- if (!skillSrc) {
55084
- error("skill.md not found.");
55085
- process.exitCode = 1;
55086
- return;
55087
- }
55088
- console.log((0, import_fs.readFileSync)(skillSrc, "utf8"));
55089
- });
55090
- }
55091
-
55092
55626
  // src/commands/x402.ts
55093
55627
  init_dist2();
55094
55628
  init_dist4();
@@ -55121,7 +55655,7 @@ async function validateX402Url(urlStr) {
55121
55655
  if (parsed.protocol !== "https:") {
55122
55656
  throw new Error(`x402: only https:// URLs are allowed (got ${parsed.protocol}//)`);
55123
55657
  }
55124
- const h4 = parsed.hostname;
55658
+ const h4 = parsed.hostname.replace(/^\[|\]$/g, "");
55125
55659
  if (h4 === "localhost" || isPrivateIp(h4)) {
55126
55660
  throw new Error(`x402: private/loopback addresses are not allowed (${h4})`);
55127
55661
  }
@@ -55130,7 +55664,9 @@ async function validateX402Url(urlStr) {
55130
55664
  const records = await dns.lookup(h4, { all: true });
55131
55665
  for (const record2 of records) {
55132
55666
  if (isPrivateIp(record2.address)) {
55133
- throw new Error(`x402: "${h4}" resolves to a private/loopback address (${record2.address}) \u2014 not allowed`);
55667
+ throw new Error(
55668
+ `x402: "${h4}" resolves to a private/loopback address (${record2.address}) \u2014 not allowed`
55669
+ );
55134
55670
  }
55135
55671
  }
55136
55672
  } catch (err) {
@@ -55139,7 +55675,14 @@ async function validateX402Url(urlStr) {
55139
55675
  }
55140
55676
  function registerX402Command(program3) {
55141
55677
  const x402 = program3.command("x402").description("x402 micropayment protocol \u2014 pay HTTP endpoints that require on-chain payment");
55142
- x402.command("request <url>").description("Make an HTTP request to an x402-gated endpoint, paying automatically if required").option("--method <method>", "HTTP method", "GET").option("--body <json>", "Request body (JSON string)").option("--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) => {
55678
+ x402.command("request <url>").description("Make an HTTP request to an x402-gated endpoint, paying automatically if required").option("--method <method>", "HTTP method", "GET").option("--body <json>", "Request body (JSON string)").option(
55679
+ "--max-payment <amount>",
55680
+ "Maximum payment amount to auto-approve (default: 0.01)",
55681
+ "0.01"
55682
+ ).option("--yes", "Skip confirmation prompt and auto-approve payment up to --max-payment").option(
55683
+ "--password <password>",
55684
+ "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
55685
+ ).option("--json", "JSON output").action(async (url, opts) => {
55143
55686
  try {
55144
55687
  await validateX402Url(url);
55145
55688
  await ensureTrustSDK();
@@ -55147,7 +55690,7 @@ function registerX402Command(program3) {
55147
55690
  const provider = await AgentWalletProvider.fromStorage(password);
55148
55691
  const init3 = {
55149
55692
  method: opts.method.toUpperCase(),
55150
- headers: { "Content-Type": "application/json", "Accept": "application/json" },
55693
+ headers: { "Content-Type": "application/json", Accept: "application/json" },
55151
55694
  body: opts.body || void 0
55152
55695
  };
55153
55696
  let resp = await fetch(url, init3);
@@ -55160,7 +55703,9 @@ function registerX402Command(program3) {
55160
55703
  if (!recipient) throw new Error("x402: no recipient address in 402 response");
55161
55704
  const maxPayment = parseFloat(opts.maxPayment);
55162
55705
  if (!Number.isFinite(maxPayment) || maxPayment <= 0) {
55163
- throw new Error(`x402: invalid --max-payment value "${opts.maxPayment}" \u2014 must be a positive number`);
55706
+ throw new Error(
55707
+ `x402: invalid --max-payment value "${opts.maxPayment}" \u2014 must be a positive number`
55708
+ );
55164
55709
  }
55165
55710
  const requestedAmount = parseFloat(amount);
55166
55711
  if (isNaN(requestedAmount) || requestedAmount <= 0) {
@@ -55174,7 +55719,7 @@ function registerX402Command(program3) {
55174
55719
  if (!opts.yes) {
55175
55720
  const readline = await import("readline");
55176
55721
  const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
55177
- const confirmed = await new Promise((resolve3) => {
55722
+ const confirmed = await new Promise((resolve2) => {
55178
55723
  rl.question(
55179
55724
  `
55180
55725
  Pay ${amount} ${asset.toUpperCase()} on ${chainId}
@@ -55182,7 +55727,7 @@ Recipient: ${recipient}
55182
55727
  Confirm? [y/N] `,
55183
55728
  (answer) => {
55184
55729
  rl.close();
55185
- resolve3(answer.toLowerCase() === "y");
55730
+ resolve2(answer.toLowerCase() === "y");
55186
55731
  }
55187
55732
  );
55188
55733
  });
@@ -55210,7 +55755,9 @@ Confirm? [y/N] `,
55210
55755
  }
55211
55756
  const txResult = await bc2.transfer({ assetId, to: recipient, amount, hdWallet });
55212
55757
  info(`Payment sent: ${txResult.hash}`);
55213
- const paymentProof = Buffer.from(JSON.stringify({ txHash: txResult.hash, chainId })).toString("base64");
55758
+ const paymentProof = Buffer.from(
55759
+ JSON.stringify({ txHash: txResult.hash, chainId })
55760
+ ).toString("base64");
55214
55761
  resp = await fetch(url, {
55215
55762
  ...init3,
55216
55763
  headers: { ...init3.headers, "X-Payment": paymentProof }
@@ -55251,7 +55798,7 @@ var crypto6 = __toESM(require("crypto"));
55251
55798
  // package.json
55252
55799
  var package_default = {
55253
55800
  name: "@trustwallet/cli",
55254
- version: "0.7.0",
55801
+ version: "0.9.0",
55255
55802
  description: "TWAK CLI \u2014 command-line interface for Trust Wallet Agent Kit",
55256
55803
  main: "dist/index.js",
55257
55804
  types: "dist/index.d.ts",
@@ -55267,16 +55814,17 @@ var package_default = {
55267
55814
  prebuild: "rm -rf trust-sdk && cp -r ../../vendor/trust-sdk trust-sdk",
55268
55815
  build: "tsup && tsc --emitDeclarationOnly --outDir dist",
55269
55816
  typecheck: "tsc --noEmit",
55817
+ test: "vitest run",
55270
55818
  lint: "eslint src/",
55271
55819
  clean: "rm -rf dist"
55272
55820
  },
55273
55821
  dependencies: {
55274
55822
  "node-fetch": "2.7.0",
55275
- "qrcode-terminal": "0.12.0"
55823
+ "qrcode-terminal": "0.12.0",
55824
+ "@napi-rs/keyring": "1.2.0"
55276
55825
  },
55277
55826
  optionalDependencies: {
55278
- "@amplitude/analytics-node": "1.5.39",
55279
- keytar: "7.9.0"
55827
+ "@amplitude/analytics-node": "1.5.39"
55280
55828
  },
55281
55829
  devDependencies: {
55282
55830
  boxen: "5.1.2",
@@ -55295,7 +55843,8 @@ var package_default = {
55295
55843
  "@twak/wallet-core": "workspace:*",
55296
55844
  "@twak/walletconnect": "workspace:*",
55297
55845
  tsup: "8.5.1",
55298
- typescript: "5.7.3"
55846
+ typescript: "5.7.3",
55847
+ vitest: "4.1.2"
55299
55848
  },
55300
55849
  publishConfig: {
55301
55850
  access: "public"
@@ -55426,7 +55975,13 @@ function parseInterval2(value) {
55426
55975
  return num;
55427
55976
  }
55428
55977
  function registerWatchCommand(program3) {
55429
- program3.command("watch").description("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) => {
55978
+ program3.command("watch").description(
55979
+ "Background watcher \u2014 polls prices to trigger alerts and execute DCA/limit automations"
55980
+ ).option(
55981
+ "--interval <duration>",
55982
+ "Poll interval \u2014 e.g. 30s, 5m, 1h, or plain number (seconds)",
55983
+ "60"
55984
+ ).option("--password <password>", "Wallet password for executing swaps").option("--auto-lock <minutes>", "Auto-lock wallet after N minutes of inactivity").option("--dry-run", "Check conditions without executing swaps").option("--json", "JSON output per tick").action(async (opts) => {
55430
55985
  const intervalSec = parseInterval2(opts.interval);
55431
55986
  if (intervalSec === null || intervalSec < 5) {
55432
55987
  error("Interval must be at least 5 seconds (e.g. 30s, 5m, 1h)");
@@ -55472,7 +56027,20 @@ function registerWatchCommand(program3) {
55472
56027
  }
55473
56028
  const alertStore = new AlertStore();
55474
56029
  const automationStore = new AutomationStore();
55475
- info(`[${ts3()}] Watcher started \u2014 polling every ${intervalSec}s${opts.dryRun ? " (dry run)" : ""}`);
56030
+ const dryRunState = /* @__PURE__ */ new Map();
56031
+ const getMemState = (id, stored) => {
56032
+ if (!dryRunState.has(id)) {
56033
+ dryRunState.set(id, {
56034
+ lastRunAt: stored.lastRunAt ?? null,
56035
+ runCount: stored.runCount ?? 0,
56036
+ active: true
56037
+ });
56038
+ }
56039
+ return dryRunState.get(id);
56040
+ };
56041
+ info(
56042
+ `[${ts3()}] Watcher started \u2014 polling every ${intervalSec}s${opts.dryRun ? " (dry run)" : ""}`
56043
+ );
55476
56044
  const tick = async () => {
55477
56045
  const tickResults = {
55478
56046
  alerts: { checked: 0, triggered: [] },
@@ -55488,44 +56056,81 @@ function registerWatchCommand(program3) {
55488
56056
  if (price === null) continue;
55489
56057
  const triggered = alert.condition === "above" && price >= alert.targetPrice || alert.condition === "below" && price <= alert.targetPrice;
55490
56058
  if (triggered) {
55491
- 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 };
56059
+ if (!opts.dryRun)
56060
+ await alertStore.update(alert.id, {
56061
+ active: false,
56062
+ triggeredAt: (/* @__PURE__ */ new Date()).toISOString()
56063
+ });
56064
+ const detail = {
56065
+ id: alert.id,
56066
+ tokenId: alert.tokenId,
56067
+ condition: alert.condition,
56068
+ targetPrice: alert.targetPrice,
56069
+ currentPrice: price
56070
+ };
55493
56071
  tickResults.alerts.triggered.push(detail);
55494
- success(`[${ts3()}] ALERT TRIGGERED: ${alert.tokenId} price $${price} is ${alert.condition} $${alert.targetPrice}`);
56072
+ success(
56073
+ `[${ts3()}] ${opts.dryRun ? "DRY RUN \u2014 would trigger alert: " : "ALERT TRIGGERED: "}${alert.tokenId} price $${price} is ${alert.condition} $${alert.targetPrice}`
56074
+ );
55495
56075
  }
55496
56076
  } catch (err) {
55497
- warn(`[${ts3()}] Alert ${alert.id} check failed: ${err instanceof Error ? err.message : String(err)}`);
56077
+ warn(
56078
+ `[${ts3()}] Alert ${alert.id} check failed: ${err instanceof Error ? err.message : String(err)}`
56079
+ );
55498
56080
  }
55499
56081
  }
55500
56082
  const automations = await automationStore.getActive();
55501
56083
  const now = Date.now();
55502
56084
  for (const auto of automations) {
55503
56085
  try {
56086
+ const memState = opts.dryRun ? getMemState(auto.id, auto) : null;
56087
+ if (memState && !memState.active) {
56088
+ tickResults.automations.skipped++;
56089
+ continue;
56090
+ }
55504
56091
  if (auto.expiresAt && new Date(auto.expiresAt).getTime() <= now) {
55505
- await automationStore.update(auto.id, { active: false });
56092
+ if (!opts.dryRun) await automationStore.update(auto.id, { active: false });
56093
+ else if (memState) memState.active = false;
55506
56094
  dim(`[${ts3()}] Automation ${auto.id.slice(0, 8)} expired`);
55507
56095
  tickResults.automations.skipped++;
55508
56096
  continue;
55509
56097
  }
55510
- 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})`);
56098
+ const effectiveRunCount = memState ? memState.runCount : auto.runCount ?? 0;
56099
+ if (auto.maxRuns && effectiveRunCount >= auto.maxRuns) {
56100
+ if (!opts.dryRun) await automationStore.update(auto.id, { active: false });
56101
+ dim(
56102
+ `[${ts3()}] Automation ${auto.id.slice(0, 8)} reached max runs (${auto.maxRuns})`
56103
+ );
56104
+ tickResults.automations.skipped++;
56105
+ continue;
56106
+ }
56107
+ if (!getChainById(auto.chain)) {
56108
+ warn(
56109
+ `[${ts3()}] Automation ${auto.id.slice(0, 8)} skipped \u2014 unknown chain "${auto.chain}"`
56110
+ );
55513
56111
  tickResults.automations.skipped++;
55514
56112
  continue;
55515
56113
  }
55516
56114
  if (auto.type === "dca") {
55517
- const lastRun = auto.lastRunAt ? new Date(auto.lastRunAt).getTime() : 0;
56115
+ const effectiveLastRunAt = memState ? memState.lastRunAt : auto.lastRunAt;
56116
+ const lastRun = effectiveLastRunAt ? new Date(effectiveLastRunAt).getTime() : 0;
55518
56117
  const intervalMs = auto.intervalMs || 0;
55519
56118
  if (lastRun + intervalMs > now) {
55520
56119
  tickResults.automations.skipped++;
55521
56120
  continue;
55522
56121
  }
55523
- if (opts.dryRun) {
55524
- dim(`[${ts3()}] DRY RUN: would swap ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`);
56122
+ if (opts.dryRun && memState) {
56123
+ memState.lastRunAt = (/* @__PURE__ */ new Date()).toISOString();
56124
+ memState.runCount++;
56125
+ dim(
56126
+ `[${ts3()}] DRY RUN: would swap ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`
56127
+ );
55525
56128
  tickResults.automations.executed.push({ id: auto.id, type: "dca", dryRun: true });
55526
56129
  continue;
55527
56130
  }
55528
- info(`[${ts3()}] Executing DCA: ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`);
56131
+ info(
56132
+ `[${ts3()}] Executing DCA: ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`
56133
+ );
55529
56134
  const result = await executor.execute({
55530
56135
  fromChain: auto.chain,
55531
56136
  fromToken: auto.fromToken,
@@ -55534,27 +56139,58 @@ function registerWatchCommand(program3) {
55534
56139
  amount: auto.amount
55535
56140
  });
55536
56141
  if (result.success) {
55537
- await automationStore.update(auto.id, { lastRunAt: (/* @__PURE__ */ new Date()).toISOString(), runCount: (auto.runCount ?? 0) + 1 });
56142
+ await automationStore.update(auto.id, {
56143
+ lastRunAt: (/* @__PURE__ */ new Date()).toISOString(),
56144
+ runCount: (auto.runCount ?? 0) + 1
56145
+ });
55538
56146
  success(`[${ts3()}] DCA executed: ${result.explorerUrl}`);
55539
- tickResults.automations.executed.push({ id: auto.id, type: "dca", txHash: result.txHash, explorer: result.explorerUrl });
56147
+ tickResults.automations.executed.push({
56148
+ id: auto.id,
56149
+ type: "dca",
56150
+ txHash: result.txHash,
56151
+ explorer: result.explorerUrl
56152
+ });
55540
56153
  } else {
55541
56154
  warn(`[${ts3()}] DCA ${auto.id} failed: ${result.error}`);
55542
56155
  }
55543
56156
  } else if (auto.type === "limit") {
55544
56157
  const toTokenInfo = resolveToken(auto.toToken, auto.chain);
56158
+ if (!toTokenInfo) {
56159
+ warn(`[${ts3()}] Automation ${auto.id.slice(0, 8)} skipped \u2014 unknown token "${auto.toToken}" on ${auto.chain}`);
56160
+ continue;
56161
+ }
55545
56162
  const price = await swapClient.getTokenPrice(auto.chain, toTokenInfo.address);
55546
- if (price === null) continue;
56163
+ if (price === null) {
56164
+ if (opts.dryRun)
56165
+ dim(
56166
+ `[${ts3()}] DRY RUN: skipping limit order ${auto.id.slice(0, 8)} \u2014 price unavailable for ${auto.toToken}`
56167
+ );
56168
+ continue;
56169
+ }
55547
56170
  const triggered = auto.condition === "below" && price <= (auto.targetPrice || 0) || auto.condition === "above" && price >= (auto.targetPrice || 0);
55548
56171
  if (!triggered) {
56172
+ if (opts.dryRun)
56173
+ dim(
56174
+ `[${ts3()}] DRY RUN: limit order ${auto.id.slice(0, 8)} waiting \u2014 ${auto.toToken} $${price} vs target $${auto.targetPrice} (${auto.condition})`
56175
+ );
55549
56176
  tickResults.automations.skipped++;
55550
56177
  continue;
55551
56178
  }
55552
- if (opts.dryRun) {
55553
- 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 });
56179
+ if (opts.dryRun && memState) {
56180
+ memState.active = false;
56181
+ dim(
56182
+ `[${ts3()}] DRY RUN: limit order triggered \u2014 would swap ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`
56183
+ );
56184
+ tickResults.automations.executed.push({
56185
+ id: auto.id,
56186
+ type: "limit",
56187
+ dryRun: true
56188
+ });
55555
56189
  continue;
55556
56190
  }
55557
- info(`[${ts3()}] Limit order triggered: swapping ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`);
56191
+ info(
56192
+ `[${ts3()}] Limit order triggered: swapping ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`
56193
+ );
55558
56194
  const result = await executor.execute({
55559
56195
  fromChain: auto.chain,
55560
56196
  fromToken: auto.fromToken,
@@ -55563,15 +56199,25 @@ function registerWatchCommand(program3) {
55563
56199
  amount: auto.amount
55564
56200
  });
55565
56201
  if (result.success) {
55566
- await automationStore.update(auto.id, { active: false, lastRunAt: (/* @__PURE__ */ new Date()).toISOString() });
56202
+ await automationStore.update(auto.id, {
56203
+ active: false,
56204
+ lastRunAt: (/* @__PURE__ */ new Date()).toISOString()
56205
+ });
55567
56206
  success(`[${ts3()}] Limit order executed: ${result.explorerUrl}`);
55568
- tickResults.automations.executed.push({ id: auto.id, type: "limit", txHash: result.txHash, explorer: result.explorerUrl });
56207
+ tickResults.automations.executed.push({
56208
+ id: auto.id,
56209
+ type: "limit",
56210
+ txHash: result.txHash,
56211
+ explorer: result.explorerUrl
56212
+ });
55569
56213
  } else {
55570
56214
  warn(`[${ts3()}] Limit order ${auto.id} failed: ${result.error}`);
55571
56215
  }
55572
56216
  }
55573
56217
  } catch (err) {
55574
- warn(`[${ts3()}] Automation ${auto.id} failed: ${err instanceof Error ? err.message : String(err)}`);
56218
+ warn(
56219
+ `[${ts3()}] Automation ${auto.id} failed: ${err instanceof Error ? err.message : String(err)}`
56220
+ );
55575
56221
  }
55576
56222
  }
55577
56223
  } catch (err) {
@@ -55580,7 +56226,9 @@ function registerWatchCommand(program3) {
55580
56226
  if (opts.json) {
55581
56227
  output({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), ...tickResults }, true);
55582
56228
  } else if (tickResults.alerts.triggered.length === 0 && tickResults.automations.executed.length === 0) {
55583
- dim(`[${ts3()}] Checked ${tickResults.alerts.checked} alerts, ${tickResults.automations.skipped} automations \u2014 nothing to do`);
56229
+ dim(
56230
+ `[${ts3()}] Checked ${tickResults.alerts.checked} alerts, ${tickResults.automations.skipped} automations \u2014 nothing to do`
56231
+ );
55584
56232
  }
55585
56233
  };
55586
56234
  await tick();
@@ -55619,7 +56267,6 @@ registerAlertCommand(program2);
55619
56267
  registerQueryCommand(program2);
55620
56268
  registerServeCommand(program2);
55621
56269
  registerAutomateCommand(program2);
55622
- registerSkillCommand(program2);
55623
56270
  registerX402Command(program2);
55624
56271
  registerTelemetryCommand(program2);
55625
56272
  registerWatchCommand(program2);