@trustwallet/cli 0.6.11 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -49,10 +49,15 @@
49
49
  head: { appendChild: function(){} }
50
50
  };
51
51
  }
52
- // --- Suppress Node.js --localstorage-file warning from Trust SDK WASM ---
52
+ // --- Suppress noisy warnings fired by transitive deps at module load time ---
53
53
  var _origEmit = process.emitWarning;
54
54
  process.emitWarning = function(warning) {
55
55
  if (typeof warning === 'string' && warning.indexOf('--localstorage-file') !== -1) return;
56
+ // DEP0040: punycode module deprecation from ws/url used by Trust SDK
57
+ var opts = arguments[1];
58
+ var code = (opts && typeof opts === 'object' ? opts.code : arguments[2])
59
+ || (warning && typeof warning === 'object' ? warning.code : null);
60
+ if (code === 'DEP0040') return;
56
61
  return _origEmit.apply(process, arguments);
57
62
  };
58
63
 
@@ -71,7 +76,7 @@ var __hasOwnProp = Object.prototype.hasOwnProperty;
71
76
  var __esm = (fn2, res) => function __init() {
72
77
  return fn2 && (res = (0, fn2[__getOwnPropNames(fn2)[0]])(fn2 = 0)), res;
73
78
  };
74
- var __commonJS = (cb, mod2) => function __require3() {
79
+ var __commonJS = (cb, mod2) => function __require2() {
75
80
  return mod2 || (0, cb[__getOwnPropNames(cb)[0]])((mod2 = { exports: {} }).exports, mod2), mod2.exports;
76
81
  };
77
82
  var __export = (target, all) => {
@@ -3125,20 +3130,21 @@ function decrypt(data, password) {
3125
3130
  decrypted += decipher.final("utf-8");
3126
3131
  return decrypted;
3127
3132
  }
3128
- function loadKeytar() {
3133
+ function isNoEntryError(err) {
3134
+ return err instanceof Error && /no entry|no such entry/i.test(err.message);
3135
+ }
3136
+ function isKeychainUnavailableError(err) {
3137
+ return err instanceof Error && (/no keychain service/i.test(err.message) || /org\.freedesktop/i.test(err.message) || /failed to unlock/i.test(err.message) || /secret service/i.test(err.message));
3138
+ }
3139
+ function getPassword(service, account) {
3129
3140
  try {
3130
- return __require("keytar");
3131
- } catch {
3132
- return null;
3141
+ return new import_keyring.Entry(service, account).getPassword();
3142
+ } catch (err) {
3143
+ if (isNoEntryError(err) || isKeychainUnavailableError(err)) return null;
3144
+ throw err;
3133
3145
  }
3134
3146
  }
3135
- function getBackend() {
3136
- const keytar = loadKeytar();
3137
- if (keytar) return keytar;
3138
- if (macSecurity.isAvailable()) return macSecurity;
3139
- return null;
3140
- }
3141
- var fs, path, os, crypto2, import_child_process, import_os, crypto22, __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, 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,15 +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
- __require = /* @__PURE__ */ ((x6) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x6, {
3153
- get: (a3, b3) => (typeof require !== "undefined" ? require : a3)[b3]
3154
- }) : x6)(function(x6) {
3155
- if (typeof require !== "undefined") return require.apply(this, arguments);
3156
- throw Error('Dynamic require of "' + x6 + '" is not supported');
3157
- });
3157
+ crypto3 = __toESM(require("crypto"), 1);
3158
3158
  FileStore = class {
3159
3159
  dataDir;
3160
3160
  constructor(config2) {
@@ -3205,84 +3205,36 @@ var init_dist = __esm({
3205
3205
  SERVICE = "twak";
3206
3206
  WALLET_ACCOUNT = "wallet";
3207
3207
  API_SECRET_ACCOUNT = "api-secret";
3208
- macSecurity = {
3209
- isAvailable() {
3210
- return (0, import_os.platform)() === "darwin";
3211
- },
3212
- getPassword(service, account) {
3213
- try {
3214
- const output2 = (0, import_child_process.execFileSync)(
3215
- "security",
3216
- ["find-generic-password", "-s", service, "-a", account, "-w"],
3217
- { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
3218
- );
3219
- return output2.replace(/\r?\n$/, "");
3220
- } catch {
3221
- return null;
3222
- }
3223
- },
3224
- setPassword(service, account, password) {
3225
- (0, import_child_process.execFileSync)(
3226
- "security",
3227
- ["add-generic-password", "-U", "-s", service, "-a", account, "-w"],
3228
- { input: password + "\n", stdio: ["pipe", "pipe", "pipe"] }
3229
- );
3230
- },
3231
- deletePassword(service, account) {
3232
- try {
3233
- (0, import_child_process.execFileSync)(
3234
- "security",
3235
- ["delete-generic-password", "-s", service, "-a", account],
3236
- { stdio: ["pipe", "pipe", "pipe"] }
3237
- );
3238
- } catch {
3239
- }
3240
- }
3241
- };
3242
3208
  KeychainManager = class {
3243
3209
  async getWalletPassword() {
3244
- const backend = getBackend();
3245
- if (!backend) return null;
3246
- try {
3247
- return await backend.getPassword(SERVICE, WALLET_ACCOUNT);
3248
- } catch {
3249
- return null;
3250
- }
3210
+ return getPassword(SERVICE, WALLET_ACCOUNT);
3251
3211
  }
3252
3212
  async saveWalletPassword(password) {
3253
- const backend = getBackend();
3254
- if (!backend) throw new Error("No keychain backend available (keytar not installed and not on macOS)");
3255
- await backend.setPassword(SERVICE, WALLET_ACCOUNT, password);
3213
+ new import_keyring.Entry(SERVICE, WALLET_ACCOUNT).setPassword(password);
3256
3214
  }
3257
3215
  async deleteWalletPassword() {
3258
- const backend = getBackend();
3259
- if (!backend) throw new Error("No keychain backend available (keytar not installed and not on macOS)");
3260
- await backend.deletePassword(SERVICE, WALLET_ACCOUNT);
3216
+ new import_keyring.Entry(SERVICE, WALLET_ACCOUNT).deletePassword();
3261
3217
  }
3262
3218
  async getApiSecret() {
3263
- const backend = getBackend();
3264
- if (!backend) return null;
3265
- try {
3266
- return await backend.getPassword(SERVICE, API_SECRET_ACCOUNT);
3267
- } catch {
3268
- return null;
3269
- }
3219
+ return getPassword(SERVICE, API_SECRET_ACCOUNT);
3270
3220
  }
3271
3221
  async saveApiSecret(secret) {
3272
- const backend = getBackend();
3273
- if (!backend) throw new Error("No keychain backend available (keytar not installed and not on macOS)");
3274
- await backend.setPassword(SERVICE, API_SECRET_ACCOUNT, secret);
3222
+ new import_keyring.Entry(SERVICE, API_SECRET_ACCOUNT).setPassword(secret);
3275
3223
  }
3276
3224
  async deleteApiSecret() {
3277
- const backend = getBackend();
3278
- if (!backend) return;
3279
3225
  try {
3280
- await backend.deletePassword(SERVICE, API_SECRET_ACCOUNT);
3226
+ new import_keyring.Entry(SERVICE, API_SECRET_ACCOUNT).deletePassword();
3281
3227
  } catch {
3282
3228
  }
3283
3229
  }
3284
3230
  isAvailable() {
3285
- 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
+ }
3286
3238
  }
3287
3239
  };
3288
3240
  CREDENTIALS_FILE = "credentials.json";
@@ -3310,6 +3262,10 @@ var init_dist = __esm({
3310
3262
  try {
3311
3263
  await this.kc.saveApiSecret(credentials.hmacSecret);
3312
3264
  } catch {
3265
+ try {
3266
+ await this.kc.deleteApiSecret();
3267
+ } catch {
3268
+ }
3313
3269
  }
3314
3270
  }
3315
3271
  }
@@ -3410,6 +3366,51 @@ var init_dist = __esm({
3410
3366
  await this.store.remove(ALERTS_FILE);
3411
3367
  }
3412
3368
  };
3369
+ AUTOMATIONS_FILE = "automations.json";
3370
+ AutomationStore = class {
3371
+ store;
3372
+ constructor(config2) {
3373
+ this.store = new FileStore(config2);
3374
+ }
3375
+ async create(record2) {
3376
+ const automations = await this.list();
3377
+ const entry = {
3378
+ ...record2,
3379
+ id: crypto3.randomUUID(),
3380
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3381
+ active: true
3382
+ };
3383
+ automations.push(entry);
3384
+ await this.store.write(AUTOMATIONS_FILE, automations);
3385
+ return entry;
3386
+ }
3387
+ async list() {
3388
+ return await this.store.read(AUTOMATIONS_FILE) || [];
3389
+ }
3390
+ async getById(id) {
3391
+ const automations = await this.list();
3392
+ return automations.find((a3) => a3.id === id);
3393
+ }
3394
+ async update(id, updates) {
3395
+ const automations = await this.list();
3396
+ const index = automations.findIndex((a3) => a3.id === id);
3397
+ if (index === -1) return void 0;
3398
+ automations[index] = { ...automations[index], ...updates };
3399
+ await this.store.write(AUTOMATIONS_FILE, automations);
3400
+ return automations[index];
3401
+ }
3402
+ async delete(id) {
3403
+ const automations = await this.list();
3404
+ const filtered = automations.filter((a3) => a3.id !== id);
3405
+ if (filtered.length === automations.length) return false;
3406
+ await this.store.write(AUTOMATIONS_FILE, filtered);
3407
+ return true;
3408
+ }
3409
+ async getActive() {
3410
+ const automations = await this.list();
3411
+ return automations.filter((a3) => a3.active);
3412
+ }
3413
+ };
3413
3414
  SEEN_HASHES_FILE = "seen-hashes.json";
3414
3415
  SeenHashStore = class {
3415
3416
  store;
@@ -3438,6 +3439,45 @@ var init_dist = __esm({
3438
3439
  await this.store.remove(SEEN_HASHES_FILE);
3439
3440
  }
3440
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
+ };
3441
3481
  }
3442
3482
  });
3443
3483
 
@@ -5120,11 +5160,13 @@ function resolveToken(token, chainKey) {
5120
5160
  };
5121
5161
  }
5122
5162
  if (upperToken.startsWith("USDC")) {
5123
- const addr = chainTokens?.["USDC.e"] || chainTokens?.["USDC"] || token;
5163
+ const addr = chainTokens?.["USDC.e"] || chainTokens?.["USDC"];
5164
+ if (!addr) return null;
5124
5165
  return { address: addr, decimals: isBsc ? 18 : 6 };
5125
5166
  }
5126
5167
  if (upperToken.startsWith("USDT")) {
5127
- const addr = chainTokens?.["USDT"] || token;
5168
+ const addr = chainTokens?.["USDT"];
5169
+ if (!addr) return null;
5128
5170
  return { address: addr, decimals: isBsc ? 18 : 6 };
5129
5171
  }
5130
5172
  if (token.startsWith("0x") || token.length > 20) {
@@ -5140,7 +5182,7 @@ function resolveToken(token, chainKey) {
5140
5182
  }
5141
5183
  return { address: token, decimals: 18 };
5142
5184
  }
5143
- return { address: token, decimals: 18 };
5185
+ return null;
5144
5186
  }
5145
5187
  function chainKeyToCoinType(chainKey) {
5146
5188
  const key = chainKey.toLowerCase();
@@ -5895,7 +5937,7 @@ function setPolyfillNaasSessionId(sessionId) {
5895
5937
  }
5896
5938
  function resolveSdkDir() {
5897
5939
  try {
5898
- return path2.dirname(__require2.resolve("@trustwallet-trust-sdk/trust-sdk"));
5940
+ return path2.dirname(__require.resolve("@trustwallet-trust-sdk/trust-sdk"));
5899
5941
  } catch {
5900
5942
  let dir = __dirname;
5901
5943
  for (let i2 = 0; i2 < 10; i2++) {
@@ -6060,7 +6102,7 @@ function applyPolyfills() {
6060
6102
  };
6061
6103
  }
6062
6104
  if (typeof globalThis.localStorage === "undefined") {
6063
- const os5 = __require2("os");
6105
+ const os5 = __require("os");
6064
6106
  const storePath = path2.join(os5.homedir(), ".twak", "local-storage.json");
6065
6107
  const load = () => {
6066
6108
  try {
@@ -6102,7 +6144,7 @@ function applyPolyfills() {
6102
6144
  };
6103
6145
  }
6104
6146
  if (typeof globalThis.crypto === "undefined") {
6105
- const nodeCrypto = __require2("crypto");
6147
+ const nodeCrypto = __require("crypto");
6106
6148
  globalThis.crypto = nodeCrypto.webcrypto ?? nodeCrypto;
6107
6149
  }
6108
6150
  for (const method of ["log", "error"]) {
@@ -6409,7 +6451,7 @@ function registerShutdownHandlers(providers, onBeforeExit) {
6409
6451
  process.removeListener("SIGTERM", handler);
6410
6452
  };
6411
6453
  }
6412
- 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;
6413
6455
  var init_dist3 = __esm({
6414
6456
  "../wallet-core/dist/index.mjs"() {
6415
6457
  "use strict";
@@ -6425,7 +6467,7 @@ var init_dist3 = __esm({
6425
6467
  import_crypto2 = require("crypto");
6426
6468
  init_dist2();
6427
6469
  init_dist();
6428
- __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, {
6429
6471
  get: (a3, b3) => (typeof require !== "undefined" ? require : a3)[b3]
6430
6472
  }) : x6)(function(x6) {
6431
6473
  if (typeof require !== "undefined") return require.apply(this, arguments);
@@ -6558,7 +6600,16 @@ var init_dist3 = __esm({
6558
6600
  const chain2 = new import_trust_sdk2.DefinedChain(ct5);
6559
6601
  const account = this.getAccountFromSigner(chainKey);
6560
6602
  const asset = contractAddress ? new import_trust_sdk2.Asset.Token(account, contractAddress, tokenTypeForCoinId(coinId)) : new import_trust_sdk2.Asset.Coin(account);
6561
- 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
+ }
6562
6613
  const amountWei = humanAmountToWei(params.amount, decimals);
6563
6614
  let transaction = new import_trust_sdk2.TransactionTransfer(
6564
6615
  params.to,
@@ -7472,11 +7523,11 @@ function __metadata(metadataKey, metadataValue) {
7472
7523
  }
7473
7524
  function __awaiter(thisArg, _arguments, P5, generator) {
7474
7525
  function adopt(value) {
7475
- return value instanceof P5 ? value : new P5(function(resolve3) {
7476
- resolve3(value);
7526
+ return value instanceof P5 ? value : new P5(function(resolve2) {
7527
+ resolve2(value);
7477
7528
  });
7478
7529
  }
7479
- return new (P5 || (P5 = Promise))(function(resolve3, reject) {
7530
+ return new (P5 || (P5 = Promise))(function(resolve2, reject) {
7480
7531
  function fulfilled(value) {
7481
7532
  try {
7482
7533
  step(generator.next(value));
@@ -7492,7 +7543,7 @@ function __awaiter(thisArg, _arguments, P5, generator) {
7492
7543
  }
7493
7544
  }
7494
7545
  function step(result) {
7495
- result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
7546
+ result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
7496
7547
  }
7497
7548
  step((generator = generator.apply(thisArg, _arguments || [])).next());
7498
7549
  });
@@ -7670,14 +7721,14 @@ function __asyncValues(o4) {
7670
7721
  }, i2);
7671
7722
  function verb(n4) {
7672
7723
  i2[n4] = o4[n4] && function(v5) {
7673
- return new Promise(function(resolve3, reject) {
7674
- 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);
7675
7726
  });
7676
7727
  };
7677
7728
  }
7678
- function settle(resolve3, reject, d3, v5) {
7729
+ function settle(resolve2, reject, d3, v5) {
7679
7730
  Promise.resolve(v5).then(function(v6) {
7680
- resolve3({ value: v6, done: d3 });
7731
+ resolve2({ value: v6, done: d3 });
7681
7732
  }, reject);
7682
7733
  }
7683
7734
  }
@@ -7746,9 +7797,9 @@ var require_delay = __commonJS({
7746
7797
  Object.defineProperty(exports2, "__esModule", { value: true });
7747
7798
  exports2.delay = void 0;
7748
7799
  function delay(timeout) {
7749
- return new Promise((resolve3) => {
7800
+ return new Promise((resolve2) => {
7750
7801
  setTimeout(() => {
7751
- resolve3(true);
7802
+ resolve2(true);
7752
7803
  }, timeout);
7753
7804
  });
7754
7805
  }
@@ -12831,13 +12882,12 @@ var init_dist5 = __esm({
12831
12882
  }
12832
12883
  }
12833
12884
  async getAssetInfo(assetId) {
12834
- 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
+ );
12835
12889
  try {
12836
- const priceResp = await this.client.market(
12837
- "POST",
12838
- "/v2/market/tickers",
12839
- { currency: "USD", assets: [assetId] }
12840
- );
12890
+ const priceResp = await this.client.market("POST", "/v2/market/tickers", { currency: "USD", assets: [assetId] });
12841
12891
  const ticker = priceResp.tickers?.[0];
12842
12892
  if (ticker) {
12843
12893
  if (ticker.price != null) info2.priceUsd = ticker.price;
@@ -12873,17 +12923,22 @@ var init_dist5 = __esm({
12873
12923
  return this.transformCoinStatus(assetId, raw);
12874
12924
  }
12875
12925
  async getAssetListings(params) {
12926
+ const limit = params.limit || 20;
12876
12927
  const query = new URLSearchParams({
12877
12928
  version: "27",
12878
12929
  currency: params.currency || "USD",
12879
- limit: String(params.limit || 20)
12930
+ limit: String(params.sort ? Math.max(limit, 50) : limit)
12880
12931
  });
12881
12932
  if (params.categoryId) query.set("category_id", params.categoryId);
12882
- if (params.sort) query.set("sort", params.sort);
12883
12933
  if (params.networks) query.set("networks", params.networks);
12884
- 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
+ );
12885
12940
  const docs = response.docs || response.results || [];
12886
- return docs.map((doc) => {
12941
+ const tokens = docs.map((doc) => {
12887
12942
  const asset = doc.asset || doc;
12888
12943
  const price = doc.price || {};
12889
12944
  const market = doc.market || {};
@@ -12902,6 +12957,16 @@ var init_dist5 = __esm({
12902
12957
  volume24hUsd: market.volume_24h ?? price.volume_24h ?? doc.volume_24h_usd
12903
12958
  };
12904
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);
12905
12970
  }
12906
12971
  async getTrendingTokens(limit = 20, sort = "percent_change_24h") {
12907
12972
  const knownAssets = [
@@ -12926,7 +12991,7 @@ var init_dist5 = __esm({
12926
12991
  "c10000324",
12927
12992
  "c81457"
12928
12993
  ];
12929
- const response = await this.client.market("POST", "/v1/tickers", {
12994
+ const response = await this.client.market("POST", "/v2/market/tickers", {
12930
12995
  currency: "USD",
12931
12996
  assets: knownAssets
12932
12997
  });
@@ -14582,8 +14647,8 @@ var require_dataType = __commonJS({
14582
14647
  return types;
14583
14648
  }
14584
14649
  exports2.getSchemaTypes = getSchemaTypes;
14585
- function getJSONTypes(ts3) {
14586
- const types = Array.isArray(ts3) ? ts3 : ts3 ? [ts3] : [];
14650
+ function getJSONTypes(ts4) {
14651
+ const types = Array.isArray(ts4) ? ts4 : ts4 ? [ts4] : [];
14587
14652
  if (types.every(rules_1.isJSONType))
14588
14653
  return types;
14589
14654
  throw new Error("type must be JSONType or JSONType[]: " + types.join(","));
@@ -15625,18 +15690,18 @@ var require_validate = __commonJS({
15625
15690
  });
15626
15691
  narrowSchemaTypes(it4, types);
15627
15692
  }
15628
- function checkMultipleTypes(it4, ts3) {
15629
- if (ts3.length > 1 && !(ts3.length === 2 && ts3.includes("null"))) {
15693
+ function checkMultipleTypes(it4, ts4) {
15694
+ if (ts4.length > 1 && !(ts4.length === 2 && ts4.includes("null"))) {
15630
15695
  strictTypesError(it4, "use allowUnionTypes to allow union type keyword");
15631
15696
  }
15632
15697
  }
15633
- function checkKeywordTypes(it4, ts3) {
15698
+ function checkKeywordTypes(it4, ts4) {
15634
15699
  const rules = it4.self.RULES.all;
15635
15700
  for (const keyword in rules) {
15636
15701
  const rule = rules[keyword];
15637
15702
  if (typeof rule == "object" && (0, applicability_1.shouldUseRule)(it4.schema, rule)) {
15638
15703
  const { type } = rule.definition;
15639
- if (type.length && !type.some((t) => hasApplicableType(ts3, t))) {
15704
+ if (type.length && !type.some((t) => hasApplicableType(ts4, t))) {
15640
15705
  strictTypesError(it4, `missing type "${type.join(",")}" for keyword "${keyword}"`);
15641
15706
  }
15642
15707
  }
@@ -15645,18 +15710,18 @@ var require_validate = __commonJS({
15645
15710
  function hasApplicableType(schTs, kwdT) {
15646
15711
  return schTs.includes(kwdT) || kwdT === "number" && schTs.includes("integer");
15647
15712
  }
15648
- function includesType(ts3, t) {
15649
- return ts3.includes(t) || t === "integer" && ts3.includes("number");
15713
+ function includesType(ts4, t) {
15714
+ return ts4.includes(t) || t === "integer" && ts4.includes("number");
15650
15715
  }
15651
15716
  function narrowSchemaTypes(it4, withTypes) {
15652
- const ts3 = [];
15717
+ const ts4 = [];
15653
15718
  for (const t of it4.dataTypes) {
15654
15719
  if (includesType(withTypes, t))
15655
- ts3.push(t);
15720
+ ts4.push(t);
15656
15721
  else if (withTypes.includes("integer") && t === "number")
15657
- ts3.push("integer");
15722
+ ts4.push("integer");
15658
15723
  }
15659
- it4.dataTypes = ts3;
15724
+ it4.dataTypes = ts4;
15660
15725
  }
15661
15726
  function strictTypesError(it4, msg) {
15662
15727
  const schemaPath = it4.schemaEnv.baseId + it4.errSchemaPath;
@@ -16049,7 +16114,7 @@ var require_compile = __commonJS({
16049
16114
  const schOrFunc = root.refs[ref];
16050
16115
  if (schOrFunc)
16051
16116
  return schOrFunc;
16052
- let _sch = resolve3.call(this, root, ref);
16117
+ let _sch = resolve2.call(this, root, ref);
16053
16118
  if (_sch === void 0) {
16054
16119
  const schema = (_a2 = root.localRefs) === null || _a2 === void 0 ? void 0 : _a2[ref];
16055
16120
  const { schemaId } = this.opts;
@@ -16076,7 +16141,7 @@ var require_compile = __commonJS({
16076
16141
  function sameSchemaEnv(s1, s2) {
16077
16142
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
16078
16143
  }
16079
- function resolve3(root, ref) {
16144
+ function resolve2(root, ref) {
16080
16145
  let sch;
16081
16146
  while (typeof (sch = this.refs[ref]) == "string")
16082
16147
  ref = sch;
@@ -16651,7 +16716,7 @@ var require_fast_uri = __commonJS({
16651
16716
  }
16652
16717
  return uri;
16653
16718
  }
16654
- function resolve3(baseURI, relativeURI, options) {
16719
+ function resolve2(baseURI, relativeURI, options) {
16655
16720
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
16656
16721
  const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
16657
16722
  schemelessOptions.skipEscape = true;
@@ -16878,7 +16943,7 @@ var require_fast_uri = __commonJS({
16878
16943
  var fastUri = {
16879
16944
  SCHEMES,
16880
16945
  normalize,
16881
- resolve: resolve3,
16946
+ resolve: resolve2,
16882
16947
  resolveComponent,
16883
16948
  equal,
16884
16949
  serialize,
@@ -19890,11 +19955,11 @@ function __rest2(s2, e2) {
19890
19955
  }
19891
19956
  function __awaiter2(thisArg, _arguments, P5, generator) {
19892
19957
  function adopt(value) {
19893
- return value instanceof P5 ? value : new P5(function(resolve3) {
19894
- resolve3(value);
19958
+ return value instanceof P5 ? value : new P5(function(resolve2) {
19959
+ resolve2(value);
19895
19960
  });
19896
19961
  }
19897
- return new (P5 || (P5 = Promise))(function(resolve3, reject) {
19962
+ return new (P5 || (P5 = Promise))(function(resolve2, reject) {
19898
19963
  function fulfilled(value) {
19899
19964
  try {
19900
19965
  step(generator.next(value));
@@ -19910,7 +19975,7 @@ function __awaiter2(thisArg, _arguments, P5, generator) {
19910
19975
  }
19911
19976
  }
19912
19977
  function step(result) {
19913
- result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
19978
+ result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
19914
19979
  }
19915
19980
  step((generator = generator.apply(thisArg, _arguments || [])).next());
19916
19981
  });
@@ -20480,8 +20545,8 @@ var init_timeline = __esm({
20480
20545
  };
20481
20546
  Timeline2.prototype.push = function(event) {
20482
20547
  var _this = this;
20483
- return new Promise(function(resolve3) {
20484
- _this.queue.push([event, resolve3]);
20548
+ return new Promise(function(resolve2) {
20549
+ _this.queue.push([event, resolve2]);
20485
20550
  _this.scheduleApply(0);
20486
20551
  });
20487
20552
  };
@@ -20501,7 +20566,7 @@ var init_timeline = __esm({
20501
20566
  };
20502
20567
  Timeline2.prototype.apply = function(item) {
20503
20568
  return __awaiter2(this, void 0, void 0, function() {
20504
- 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;
20505
20570
  var e_1, _c2, e_2, _d;
20506
20571
  return __generator2(this, function(_e6) {
20507
20572
  switch (_e6.label) {
@@ -20513,7 +20578,7 @@ var init_timeline = __esm({
20513
20578
  ];
20514
20579
  }
20515
20580
  _a2 = __read2(item, 1), event = _a2[0];
20516
- _b = __read2(item, 2), resolve3 = _b[1];
20581
+ _b = __read2(item, 2), resolve2 = _b[1];
20517
20582
  this.loggerProvider.log("Timeline.apply: Initial event", event);
20518
20583
  before = this.plugins.filter(function(plugin2) {
20519
20584
  return plugin2.type === "before";
@@ -20534,7 +20599,7 @@ var init_timeline = __esm({
20534
20599
  e2 = _e6.sent();
20535
20600
  if (e2 === null) {
20536
20601
  this.loggerProvider.log("Timeline.apply: Event filtered out by before plugin '".concat(String(plugin.name), "', event: ").concat(JSON.stringify(event)));
20537
- resolve3({ event, code: 0, message: "" });
20602
+ resolve2({ event, code: 0, message: "" });
20538
20603
  return [
20539
20604
  2
20540
20605
  /*return*/
@@ -20583,7 +20648,7 @@ var init_timeline = __esm({
20583
20648
  e2 = _e6.sent();
20584
20649
  if (e2 === null) {
20585
20650
  this.loggerProvider.log("Timeline.apply: Event filtered out by enrichment plugin '".concat(String(plugin.name), "', event: ").concat(JSON.stringify(event)));
20586
- resolve3({ event, code: 0, message: "" });
20651
+ resolve2({ event, code: 0, message: "" });
20587
20652
  return [
20588
20653
  2
20589
20654
  /*return*/
@@ -20626,7 +20691,7 @@ var init_timeline = __esm({
20626
20691
  void Promise.all(executeDestinations).then(function(_a3) {
20627
20692
  var _b2 = __read2(_a3, 1), result = _b2[0];
20628
20693
  var resolveResult = result || buildResult(event, 100, "Event not tracked, no destination plugins on the instance");
20629
- resolve3(resolveResult);
20694
+ resolve2(resolveResult);
20630
20695
  });
20631
20696
  return [
20632
20697
  2
@@ -20965,8 +21030,8 @@ var init_core_client = __esm({
20965
21030
  var _this = this;
20966
21031
  return __generator2(this, function(_a2) {
20967
21032
  if (!this.isReady) {
20968
- return [2, new Promise(function(resolve3) {
20969
- _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));
20970
21035
  })];
20971
21036
  }
20972
21037
  return [2, this.process(event)];
@@ -21568,12 +21633,12 @@ var init_destination = __esm({
21568
21633
  if (!event.insert_id) {
21569
21634
  event.insert_id = UUID();
21570
21635
  }
21571
- return new Promise(function(resolve3) {
21636
+ return new Promise(function(resolve2) {
21572
21637
  var context = {
21573
21638
  event,
21574
21639
  attempts: 0,
21575
21640
  callback: function(result) {
21576
- return resolve3(result);
21641
+ return resolve2(result);
21577
21642
  },
21578
21643
  timeout: 0
21579
21644
  };
@@ -22085,7 +22150,7 @@ var init_context = __esm({
22085
22150
  };
22086
22151
  Context2.prototype.execute = function(context) {
22087
22152
  var _this = this;
22088
- return new Promise(function(resolve3) {
22153
+ return new Promise(function(resolve2) {
22089
22154
  var time3 = (/* @__PURE__ */ new Date()).getTime();
22090
22155
  var contextEvent = __assign2(__assign2(__assign2({ time: time3, insert_id: UUID(), plan: _this.config.plan }, _this.config.ingestionMetadata && {
22091
22156
  ingestion_metadata: {
@@ -22093,7 +22158,7 @@ var init_context = __esm({
22093
22158
  source_version: _this.config.ingestionMetadata.sourceVersion
22094
22159
  }
22095
22160
  }), context), { event_id: _this.eventId++, library: _this.library });
22096
- return resolve3(contextEvent);
22161
+ return resolve2(contextEvent);
22097
22162
  });
22098
22163
  };
22099
22164
  return Context2;
@@ -22139,7 +22204,7 @@ var init_http = __esm({
22139
22204
  port: url.port,
22140
22205
  protocol: url.protocol
22141
22206
  };
22142
- return new Promise(function(resolve3) {
22207
+ return new Promise(function(resolve2) {
22143
22208
  var req = protocol.request(options, function(res) {
22144
22209
  res.setEncoding("utf8");
22145
22210
  var responsePayload = "";
@@ -22151,15 +22216,15 @@ var init_http = __esm({
22151
22216
  try {
22152
22217
  var parsedResponsePayload = JSON.parse(responsePayload);
22153
22218
  var result = _this.buildResponse(parsedResponsePayload);
22154
- resolve3(result);
22219
+ resolve2(result);
22155
22220
  } catch (_a2) {
22156
- resolve3(_this.buildResponse({ code: res.statusCode }));
22221
+ resolve2(_this.buildResponse({ code: res.statusCode }));
22157
22222
  }
22158
22223
  }
22159
22224
  });
22160
22225
  });
22161
22226
  req.on("error", function() {
22162
- return resolve3(null);
22227
+ return resolve2(null);
22163
22228
  });
22164
22229
  req.end(requestPayload);
22165
22230
  });
@@ -22368,6 +22433,22 @@ function warn(message) {
22368
22433
  function info(message) {
22369
22434
  console.log(import_chalk.default.cyan(message));
22370
22435
  }
22436
+ function dim(message) {
22437
+ console.log(import_chalk.default.dim(message));
22438
+ }
22439
+ function safeExit() {
22440
+ const code2 = process.exitCode ?? 0;
22441
+ const streams = [process.stdout, process.stderr].filter((s2) => s2.writableNeedDrain);
22442
+ if (streams.length === 0) {
22443
+ process.exit(code2);
22444
+ return;
22445
+ }
22446
+ let pending = streams.length;
22447
+ const onDrain = () => {
22448
+ if (--pending === 0) process.exit(code2);
22449
+ };
22450
+ for (const s2 of streams) s2.once("drain", onDrain);
22451
+ }
22371
22452
 
22372
22453
  // src/config-loader.ts
22373
22454
  var import_chalk2 = __toESM(require_source());
@@ -22377,7 +22458,8 @@ init_dist();
22377
22458
  var EMBEDDED_NAAS_SESSION_ID = "NDVjMWRjY2MtYTYwNy00NGNhLWIxZGUtMmJiZDIyNmEyODQ4";
22378
22459
  function resolveNaasSessionId() {
22379
22460
  if (process.env.TWAK_NAAS_SESSION_ID) return process.env.TWAK_NAAS_SESSION_ID;
22380
- if (EMBEDDED_NAAS_SESSION_ID && !EMBEDDED_NAAS_SESSION_ID.startsWith("__")) return EMBEDDED_NAAS_SESSION_ID;
22461
+ if (EMBEDDED_NAAS_SESSION_ID && !EMBEDDED_NAAS_SESSION_ID.startsWith("__"))
22462
+ return EMBEDDED_NAAS_SESSION_ID;
22381
22463
  return void 0;
22382
22464
  }
22383
22465
  async function getCredentials() {
@@ -22392,13 +22474,29 @@ async function getCredentials() {
22392
22474
  return { accessId: credentials.accessId, hmacSecret: credentials.hmacSecret };
22393
22475
  }
22394
22476
  console.error(import_chalk2.default.red("No API credentials found."));
22395
- console.error(import_chalk2.default.dim("Set TWAK_ACCESS_ID and TWAK_HMAC_SECRET environment variables, then run `twak init`"));
22477
+ console.error(
22478
+ import_chalk2.default.dim(
22479
+ "Set TWAK_ACCESS_ID and TWAK_HMAC_SECRET environment variables, then run `twak init`"
22480
+ )
22481
+ );
22396
22482
  process.exit(1);
22397
22483
  }
22398
22484
  async function getClient() {
22399
22485
  const creds = await getCredentials();
22400
22486
  return createTwakClient({ ...creds });
22401
22487
  }
22488
+ async function tryGetClient() {
22489
+ const envAccessId = process.env.TWAK_ACCESS_ID;
22490
+ const envSecret = process.env.TWAK_HMAC_SECRET;
22491
+ if (envAccessId && envSecret) {
22492
+ return createTwakClient({ accessId: envAccessId, hmacSecret: envSecret });
22493
+ }
22494
+ const credentials = await new CredentialManager().load();
22495
+ if (credentials) {
22496
+ return createTwakClient({ accessId: credentials.accessId, hmacSecret: credentials.hmacSecret });
22497
+ }
22498
+ return void 0;
22499
+ }
22402
22500
  async function ensureTrustSDK() {
22403
22501
  const creds = await getCredentials();
22404
22502
  await initTrustSDK({
@@ -22426,7 +22524,9 @@ async function registerWalletWithBackend(creds, provider) {
22426
22524
  async function resolvePassword(opts) {
22427
22525
  if (opts?.password) {
22428
22526
  process.stderr.write(
22429
- import_chalk2.default.yellow("Note: --password is visible in shell history. Use `twak wallet keychain save` or TWAK_WALLET_PASSWORD instead.\n")
22527
+ import_chalk2.default.yellow(
22528
+ "Note: --password is visible in shell history. Use `twak wallet keychain save` or TWAK_WALLET_PASSWORD instead.\n"
22529
+ )
22430
22530
  );
22431
22531
  return opts.password;
22432
22532
  }
@@ -23106,8 +23206,8 @@ async function dispose(driver) {
23106
23206
 
23107
23207
  // ../../node_modules/.pnpm/idb-keyval@6.2.2/node_modules/idb-keyval/dist/index.js
23108
23208
  function promisifyRequest(request) {
23109
- return new Promise((resolve3, reject) => {
23110
- request.oncomplete = request.onsuccess = () => resolve3(request.result);
23209
+ return new Promise((resolve2, reject) => {
23210
+ request.oncomplete = request.onsuccess = () => resolve2(request.result);
23111
23211
  request.onabort = request.onerror = () => reject(request.error);
23112
23212
  });
23113
23213
  }
@@ -25268,7 +25368,7 @@ function walk(err, fn2) {
25268
25368
 
25269
25369
  // ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/cryptoNode.js
25270
25370
  var nc = __toESM(require("crypto"), 1);
25271
- var crypto3 = nc && typeof nc === "object" && "webcrypto" in nc ? nc.webcrypto : nc && typeof nc === "object" && "randomBytes" in nc ? nc : void 0;
25371
+ var crypto4 = nc && typeof nc === "object" && "webcrypto" in nc ? nc.webcrypto : nc && typeof nc === "object" && "randomBytes" in nc ? nc : void 0;
25272
25372
 
25273
25373
  // ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/utils.js
25274
25374
  function isBytes(a3) {
@@ -25365,11 +25465,11 @@ function createHasher(hashCons) {
25365
25465
  return hashC;
25366
25466
  }
25367
25467
  function randomBytes2(bytesLength = 32) {
25368
- if (crypto3 && typeof crypto3.getRandomValues === "function") {
25369
- return crypto3.getRandomValues(new Uint8Array(bytesLength));
25468
+ if (crypto4 && typeof crypto4.getRandomValues === "function") {
25469
+ return crypto4.getRandomValues(new Uint8Array(bytesLength));
25370
25470
  }
25371
- if (crypto3 && typeof crypto3.randomBytes === "function") {
25372
- return Uint8Array.from(crypto3.randomBytes(bytesLength));
25471
+ if (crypto4 && typeof crypto4.randomBytes === "function") {
25472
+ return Uint8Array.from(crypto4.randomBytes(bytesLength));
25373
25473
  }
25374
25474
  throw new Error("crypto.getRandomValues must be defined");
25375
25475
  }
@@ -38749,7 +38849,10 @@ function registerWalletCommand(program3) {
38749
38849
  }
38750
38850
  process.exit();
38751
38851
  });
38752
- wallet.command("address").description("Get wallet address for a chain").requiredOption("--chain <key>", "Chain key (e.g., ethereum, solana)").option("--password <password>", "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)").option("--json", "JSON output").action(async (opts) => {
38852
+ wallet.command("address").description("Get wallet address for a chain").requiredOption("--chain <key>", "Chain key (e.g., ethereum, solana)").option(
38853
+ "--password <password>",
38854
+ "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
38855
+ ).option("--json", "JSON output").action(async (opts) => {
38753
38856
  try {
38754
38857
  await ensureTrustSDK();
38755
38858
  const password = await resolvePassword(opts);
@@ -38773,7 +38876,10 @@ ${qr3}`;
38773
38876
  }
38774
38877
  process.exit();
38775
38878
  });
38776
- wallet.command("addresses").description("List all wallet addresses").option("--password <password>", "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)").option("--json", "JSON output").action(async (opts) => {
38879
+ wallet.command("addresses").description("List all wallet addresses").option(
38880
+ "--password <password>",
38881
+ "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
38882
+ ).option("--json", "JSON output").action(async (opts) => {
38777
38883
  try {
38778
38884
  await ensureTrustSDK();
38779
38885
  const password = await resolvePassword(opts);
@@ -38821,7 +38927,10 @@ ${qr3}`;
38821
38927
  }
38822
38928
  process.exit();
38823
38929
  });
38824
- wallet.command("balance").description("Get wallet balance on a chain (native + token holdings)").option("--chain <key>", "Chain key").option("--all", "Show balances across all chains with funds").option("--password <password>", "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)").option("--no-tokens", "Skip token balance lookup").option("--json", "JSON output").action(async (opts) => {
38930
+ wallet.command("balance").description("Get wallet balance on a chain (native + token holdings)").option("--chain <key>", "Chain key").option("--all", "Show balances across all chains with funds").option(
38931
+ "--password <password>",
38932
+ "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
38933
+ ).option("--no-tokens", "Skip token balance lookup").option("--json", "JSON output").action(async (opts) => {
38825
38934
  try {
38826
38935
  if (opts.all && opts.chain) {
38827
38936
  const msg = "--all and --chain are mutually exclusive";
@@ -38863,11 +38972,14 @@ ${qr3}`;
38863
38972
  const holdings = await securityService.getTokenHoldings(assets);
38864
38973
  for (const h4 of holdings) {
38865
38974
  if (h4.tokens?.length) {
38866
- holdingsMap.set(h4.coin, h4.tokens.map((t) => ({
38867
- symbol: t.symbol ?? t.contract,
38868
- contract: t.contract,
38869
- decimals: t.decimals ?? 18
38870
- })));
38975
+ holdingsMap.set(
38976
+ h4.coin,
38977
+ h4.tokens.map((t) => ({
38978
+ symbol: t.symbol ?? t.contract,
38979
+ contract: t.contract,
38980
+ decimals: t.decimals ?? 18
38981
+ }))
38982
+ );
38871
38983
  }
38872
38984
  }
38873
38985
  } catch {
@@ -38886,11 +38998,14 @@ ${qr3}`;
38886
38998
  const retryHoldings = await retrySec.getTokenHoldings(retryAssets);
38887
38999
  for (const h4 of retryHoldings) {
38888
39000
  if (h4.tokens?.length) {
38889
- holdingsMap.set(h4.coin, h4.tokens.map((t) => ({
38890
- symbol: t.symbol ?? t.contract,
38891
- contract: t.contract,
38892
- decimals: t.decimals ?? 18
38893
- })));
39001
+ holdingsMap.set(
39002
+ h4.coin,
39003
+ h4.tokens.map((t) => ({
39004
+ symbol: t.symbol ?? t.contract,
39005
+ contract: t.contract,
39006
+ decimals: t.decimals ?? 18
39007
+ }))
39008
+ );
38894
39009
  }
38895
39010
  }
38896
39011
  } catch {
@@ -38911,6 +39026,7 @@ ${qr3}`;
38911
39026
  if (knownTokens) {
38912
39027
  for (const [sym, addr] of Object.entries(knownTokens)) {
38913
39028
  const resolved = resolveToken(sym, chainKey);
39029
+ if (!resolved) continue;
38914
39030
  tokenDefs.push({ symbol: sym, contract: addr, decimals: resolved.decimals });
38915
39031
  }
38916
39032
  }
@@ -38918,11 +39034,15 @@ ${qr3}`;
38918
39034
  const tokenResults = await Promise.all(
38919
39035
  tokenDefs.map(async (t) => {
38920
39036
  try {
38921
- const bal = await blockchainClient.getBalance(`c${chain2.coinId}_t${t.contract}`, address);
39037
+ const bal = await blockchainClient.getBalance(
39038
+ `c${chain2.coinId}_t${t.contract}`,
39039
+ address
39040
+ );
38922
39041
  const rawBal = bal.total ?? bal.available ?? "0";
38923
39042
  if (BigInt(rawBal) <= 0n) return null;
38924
39043
  const human = fmtRaw(rawBal, t.decimals);
38925
- if (human && human !== "0.000000") return { symbol: t.symbol, contract: t.contract, balance: human };
39044
+ if (human && human !== "0.000000")
39045
+ return { symbol: t.symbol, contract: t.contract, balance: human };
38926
39046
  } catch {
38927
39047
  }
38928
39048
  return null;
@@ -38951,7 +39071,9 @@ ${qr3}`;
38951
39071
  addressMap.set(a3.chainId, a3.address);
38952
39072
  }
38953
39073
  const results = [];
38954
- const chainEntries = chainKeys.map((k4) => ({ chainKey: k4, chain: getChainById2(k4), address: addressMap.get(k4) })).filter((e2) => !!e2.chain && !!e2.address);
39074
+ const chainEntries = chainKeys.map((k4) => ({ chainKey: k4, chain: getChainById2(k4), address: addressMap.get(k4) })).filter(
39075
+ (e2) => !!e2.chain && !!e2.address
39076
+ );
38955
39077
  const settled = await Promise.allSettled(
38956
39078
  chainEntries.map(
38957
39079
  async ({ chainKey, chain: chain2, address }) => fetchChainBalance(chainKey, chain2, address, opts.tokens === false)
@@ -38961,7 +39083,10 @@ ${qr3}`;
38961
39083
  const result = settled[i2];
38962
39084
  if (result.status === "rejected") {
38963
39085
  const chainKey = chainEntries[i2].chainKey;
38964
- results.push({ chain: chainKey, error: result.reason instanceof Error ? result.reason.message : String(result.reason) });
39086
+ results.push({
39087
+ chain: chainKey,
39088
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
39089
+ });
38965
39090
  continue;
38966
39091
  }
38967
39092
  const r3 = result.value;
@@ -39008,12 +39133,7 @@ ${qr3}`;
39008
39133
  process.exit();
39009
39134
  return;
39010
39135
  }
39011
- const result = await fetchChainBalance(
39012
- opts.chain,
39013
- chain2,
39014
- address,
39015
- opts.tokens === false
39016
- );
39136
+ const result = await fetchChainBalance(opts.chain, chain2, address, opts.tokens === false);
39017
39137
  if (opts.json) {
39018
39138
  output(result, true);
39019
39139
  } else {
@@ -39076,10 +39196,16 @@ ${qr3}`;
39076
39196
  }
39077
39197
  process.exit();
39078
39198
  });
39079
- wallet.command("connect").description("Connect external wallet via WalletConnect").option("--project-id <id>", "WalletConnect project ID (or set WALLETCONNECT_PROJECT_ID)").option("--chains <list>", "Comma-separated CAIP-2 chain IDs", "eip155:1,eip155:56,eip155:137,eip155:8453,eip155:42161").option("--timeout <seconds>", "Connection timeout in seconds", "300").option("--json", "JSON output").action(async (opts) => {
39199
+ wallet.command("connect").description("Connect external wallet via WalletConnect").option("--project-id <id>", "WalletConnect project ID (or set WALLETCONNECT_PROJECT_ID)").option(
39200
+ "--chains <list>",
39201
+ "Comma-separated CAIP-2 chain IDs",
39202
+ "eip155:1,eip155:56,eip155:137,eip155:8453,eip155:42161"
39203
+ ).option("--timeout <seconds>", "Connection timeout in seconds", "300").option("--json", "JSON output").action(async (opts) => {
39080
39204
  const projectId = opts.projectId || process.env.WALLETCONNECT_PROJECT_ID;
39081
39205
  if (!projectId) {
39082
- error("WalletConnect project ID required. Use --project-id or set WALLETCONNECT_PROJECT_ID");
39206
+ error(
39207
+ "WalletConnect project ID required. Use --project-id or set WALLETCONNECT_PROJECT_ID"
39208
+ );
39083
39209
  process.exit(1);
39084
39210
  }
39085
39211
  const provider = new WalletConnectProvider({ projectId });
@@ -39096,7 +39222,15 @@ ${qr3}`;
39096
39222
  const timeoutMs = parseInt(opts.timeout, 10) * 1e3;
39097
39223
  const session = await provider.waitForSession(timeoutMs);
39098
39224
  if (opts.json) {
39099
- output({ connected: true, address: session.address, chainId: session.chainId, peer: session.peerName }, true);
39225
+ output(
39226
+ {
39227
+ connected: true,
39228
+ address: session.address,
39229
+ chainId: session.chainId,
39230
+ peer: session.peerName
39231
+ },
39232
+ true
39233
+ );
39100
39234
  } else {
39101
39235
  success(`Connected to ${session.peerName || "wallet"}`);
39102
39236
  info(` Address: ${session.address}`);
@@ -39128,17 +39262,28 @@ ${qr3}`;
39128
39262
  if (opts.json) {
39129
39263
  output(result, true);
39130
39264
  } else {
39131
- console.log(` ${import_chalk5.default.dim("Wallet")} ${exists ? import_chalk5.default.green("configured") : import_chalk5.default.yellow("not configured")}`);
39265
+ console.log(
39266
+ ` ${import_chalk5.default.dim("Wallet")} ${exists ? import_chalk5.default.green("configured") : import_chalk5.default.yellow("not configured")}`
39267
+ );
39132
39268
  if (createdAt) console.log(` ${import_chalk5.default.dim("Created")} ${createdAt}`);
39133
- console.log(` ${import_chalk5.default.dim("Keychain")} ${keychainHasPassword ? import_chalk5.default.green("password stored") : import_chalk5.default.yellow("no password")}`);
39269
+ console.log(
39270
+ ` ${import_chalk5.default.dim("Keychain")} ${keychainHasPassword ? import_chalk5.default.green("password stored") : import_chalk5.default.yellow("no password")}`
39271
+ );
39134
39272
  console.log(` ${import_chalk5.default.dim("Chains")} ${totalChains} supported`);
39135
39273
  if (addressCount !== void 0) console.log(` ${import_chalk5.default.dim("Addresses")} ${addressCount}`);
39136
39274
  if (!exists) info("\nRun `twak wallet create --password <pw>` to create a wallet.");
39137
39275
  }
39138
39276
  });
39139
- wallet.command("portfolio").description("Show portfolio balances and USD values across chains, including token holdings").option("--chains <list>", "Comma-separated chain keys (default: all EVM + major L1s)", "ethereum,base,bsc,polygon,arbitrum,optimism,avalanche,fantom,linea,scroll,zksync,blast,sonic,celo,aurora,solana,tron").option("--password <password>", "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)").option("--json", "JSON output").action(async (opts) => {
39277
+ wallet.command("portfolio").description("Show portfolio balances and USD values across chains, including token holdings").option(
39278
+ "--chains <list>",
39279
+ "Comma-separated chain keys (default: all supported chains)",
39280
+ Object.keys(CHAINS).join(",")
39281
+ ).option(
39282
+ "--password <password>",
39283
+ "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
39284
+ ).option("--json", "JSON output").action(async (opts) => {
39140
39285
  try {
39141
- await ensureTrustSDK();
39286
+ const creds = await ensureTrustSDK();
39142
39287
  const password = await resolvePassword(opts);
39143
39288
  const provider = await AgentWalletProvider.fromStorage(password);
39144
39289
  const { getChainById: getChainById2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
@@ -39154,6 +39299,15 @@ ${qr3}`;
39154
39299
  return [{ chainKey, chain: chain2, address: addrEntry.address }];
39155
39300
  });
39156
39301
  const suppress = (fn2) => fn2();
39302
+ const withTimeout = (promise, ms3) => {
39303
+ let timerId;
39304
+ const timeout = new Promise((resolve2) => {
39305
+ timerId = setTimeout(() => resolve2(null), ms3);
39306
+ });
39307
+ return Promise.race([promise, timeout]).finally(() => {
39308
+ if (timerId !== void 0) clearTimeout(timerId);
39309
+ });
39310
+ };
39157
39311
  const pMap = async (items, fn2, concurrency) => {
39158
39312
  const results = new Array(items.length);
39159
39313
  let idx = 0;
@@ -39163,7 +39317,9 @@ ${qr3}`;
39163
39317
  results[i2] = await fn2(items[i2]);
39164
39318
  }
39165
39319
  };
39166
- await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => run()));
39320
+ await Promise.all(
39321
+ Array.from({ length: Math.min(concurrency, items.length) }, () => run())
39322
+ );
39167
39323
  return results;
39168
39324
  };
39169
39325
  const toHuman = (raw, decimals) => {
@@ -39174,16 +39330,32 @@ ${qr3}`;
39174
39330
  const fracStr = fracPart.toString().padStart(decimals, "0").replace(/0+$/, "");
39175
39331
  return fracStr ? `${intPart}.${fracStr}` : intPart.toString();
39176
39332
  };
39177
- const holdingsAssets = chainEntries.map(({ chain: chain2, address }) => ({ coin: chain2.coinId, address }));
39333
+ const holdingsAssets = chainEntries.map(
39334
+ ({ chain: chain2, address }) => ({
39335
+ coin: chain2.coinId,
39336
+ address
39337
+ })
39338
+ );
39178
39339
  const [nativeResults, holdings] = await Promise.all([
39179
- pMap(chainEntries, async ({ chainKey, chain: chain2, address }) => {
39180
- try {
39181
- const balance = await suppress(() => blockchainClient.getBalance(`c${chain2.coinId}`, address));
39182
- return { chainKey, chain: chain2, address, balance };
39183
- } catch {
39184
- return { chainKey, chain: chain2, address, balance: null };
39185
- }
39186
- }, 5),
39340
+ pMap(
39341
+ chainEntries,
39342
+ async ({
39343
+ chainKey,
39344
+ chain: chain2,
39345
+ address
39346
+ }) => {
39347
+ try {
39348
+ const balance = await withTimeout(
39349
+ suppress(() => blockchainClient.getBalance(`c${chain2.coinId}`, address)),
39350
+ 15e3
39351
+ );
39352
+ return { chainKey, chain: chain2, address, balance };
39353
+ } catch {
39354
+ return { chainKey, chain: chain2, address, balance: null };
39355
+ }
39356
+ },
39357
+ 5
39358
+ ),
39187
39359
  (async () => {
39188
39360
  try {
39189
39361
  return await new SecurityService2(httpClient).getTokenHoldings(holdingsAssets);
@@ -39196,14 +39368,25 @@ ${qr3}`;
39196
39368
  for (const h4 of holdings) {
39197
39369
  if (h4.tokens?.length) holdingsMap.set(h4.coin, h4.tokens);
39198
39370
  }
39371
+ if (holdingsMap.size === 0) {
39372
+ try {
39373
+ await registerWalletWithBackend(creds, provider);
39374
+ const retryHoldings = await new SecurityService2(httpClient).getTokenHoldings(holdingsAssets);
39375
+ for (const h4 of retryHoldings) {
39376
+ if (h4.tokens?.length) holdingsMap.set(h4.coin, h4.tokens);
39377
+ }
39378
+ } catch {
39379
+ }
39380
+ }
39199
39381
  for (const { chainKey, chain: chain2 } of chainEntries) {
39200
39382
  const coinId = chain2.coinId;
39201
39383
  if (holdingsMap.has(coinId)) continue;
39202
39384
  const knownTokens = COMMON_TOKENS[chainKey];
39203
39385
  if (!knownTokens) continue;
39204
- const fallbackTokens = Object.entries(knownTokens).map(([sym, addr]) => {
39386
+ const fallbackTokens = Object.entries(knownTokens).flatMap(([sym, addr]) => {
39205
39387
  const resolved = resolveToken(sym, chainKey);
39206
- return { contract: addr, decimals: resolved.decimals, symbol: sym };
39388
+ if (!resolved) return [];
39389
+ return [{ contract: addr, decimals: resolved.decimals, symbol: sym }];
39207
39390
  });
39208
39391
  holdingsMap.set(coinId, fallbackTokens);
39209
39392
  }
@@ -39211,28 +39394,45 @@ ${qr3}`;
39211
39394
  for (const { chainKey, chain: chain2, address } of chainEntries) {
39212
39395
  const tokens = holdingsMap.get(chain2.coinId) ?? [];
39213
39396
  for (const t of tokens) {
39214
- if (t.contract) tokenJobs.push({ chainKey, coinId: chain2.coinId, address, ...t });
39397
+ if (t.contract)
39398
+ tokenJobs.push({
39399
+ chainKey,
39400
+ coinId: chain2.coinId,
39401
+ address,
39402
+ ...t
39403
+ });
39215
39404
  }
39216
39405
  }
39217
- const tokenBalances = await pMap(tokenJobs, async (job) => {
39218
- const fallback = job.balance ?? "0";
39219
- try {
39220
- const assetId = `c${job.coinId}_t${job.contract}`;
39221
- const bal = await suppress(() => blockchainClient.getBalance(assetId, job.address));
39222
- return { ...job, rawBalance: bal.total ?? bal.available ?? "0" };
39223
- } catch {
39224
- return { ...job, rawBalance: fallback };
39225
- }
39226
- }, 5);
39227
- const nativeAssetIds = nativeResults.map(({ chain: chain2 }) => `c${chain2.coinId}`);
39406
+ const tokenBalances = await pMap(
39407
+ tokenJobs,
39408
+ async (job) => {
39409
+ const fallback = job.balance ?? "0";
39410
+ try {
39411
+ const assetId = `c${job.coinId}_t${job.contract}`;
39412
+ const bal = await withTimeout(
39413
+ suppress(() => blockchainClient.getBalance(assetId, job.address)),
39414
+ 15e3
39415
+ );
39416
+ return { ...job, rawBalance: bal?.total ?? bal?.available ?? fallback };
39417
+ } catch {
39418
+ return { ...job, rawBalance: fallback };
39419
+ }
39420
+ },
39421
+ 5
39422
+ );
39423
+ const nativeAssetIds = nativeResults.map(
39424
+ ({ chain: chain2 }) => `c${chain2.coinId}`
39425
+ );
39228
39426
  const nonZeroTokens = tokenBalances.filter((t) => {
39229
39427
  const dec = t.decimals ?? 18;
39230
39428
  return toHuman(t.rawBalance, dec) !== "0";
39231
39429
  });
39232
- const tokenAssetIds = nonZeroTokens.map((t) => `c${t.coinId}_t${t.contract}`);
39430
+ const tokenAssetIds = nonZeroTokens.map(
39431
+ (t) => `c${t.coinId}_t${t.contract}`
39432
+ );
39233
39433
  const priceMap = /* @__PURE__ */ new Map();
39234
39434
  try {
39235
- const priceResp = await httpClient.market("POST", "/v1/tickers", {
39435
+ const priceResp = await httpClient.market("POST", "/v2/market/tickers", {
39236
39436
  currency: "USD",
39237
39437
  assets: [.../* @__PURE__ */ new Set([...nativeAssetIds, ...tokenAssetIds])]
39238
39438
  });
@@ -39248,7 +39448,14 @@ ${qr3}`;
39248
39448
  const rawTotal = balance?.total ?? balance?.available ?? "0";
39249
39449
  const humanAmount = toHuman(rawTotal, c3.decimals);
39250
39450
  const usdValue = price !== null ? parseFloat((parseFloat(humanAmount) * price).toFixed(2)) : null;
39251
- rows.push({ chain: chainKey, type: "native", symbol: c3.symbol, address, balance: humanAmount, usdValue });
39451
+ rows.push({
39452
+ chain: chainKey,
39453
+ type: "native",
39454
+ symbol: c3.symbol,
39455
+ address,
39456
+ balance: humanAmount,
39457
+ usdValue
39458
+ });
39252
39459
  }
39253
39460
  for (const t of tokenBalances) {
39254
39461
  const dec = t.decimals ?? 18;
@@ -39257,12 +39464,23 @@ ${qr3}`;
39257
39464
  const tokenAssetId = `c${t.coinId}_t${t.contract}`;
39258
39465
  const tokenPrice = priceMap.get(tokenAssetId) ?? null;
39259
39466
  const usdValue = tokenPrice !== null ? parseFloat((parseFloat(humanAmount) * tokenPrice).toFixed(2)) : null;
39260
- rows.push({ chain: t.chainKey, type: "token", symbol: t.symbol ?? t.contract, address: t.address, contract: t.contract, balance: humanAmount, usdValue });
39467
+ rows.push({
39468
+ chain: t.chainKey,
39469
+ type: "token",
39470
+ symbol: t.symbol ?? t.contract,
39471
+ address: t.address,
39472
+ contract: t.contract,
39473
+ balance: humanAmount,
39474
+ usdValue
39475
+ });
39261
39476
  }
39262
39477
  if (opts.json) {
39263
39478
  output(rows, true);
39264
39479
  } else {
39265
- const totalUsd = rows.reduce((sum, r3) => sum + (typeof r3.usdValue === "number" ? r3.usdValue : 0), 0);
39480
+ const totalUsd = rows.reduce(
39481
+ (sum, r3) => sum + (typeof r3.usdValue === "number" ? r3.usdValue : 0),
39482
+ 0
39483
+ );
39266
39484
  console.log("Chain Type Symbol Balance USD");
39267
39485
  console.log("\u2500".repeat(72));
39268
39486
  for (const r3 of rows) {
@@ -39282,7 +39500,10 @@ ${qr3}`;
39282
39500
  }
39283
39501
  process.exit();
39284
39502
  });
39285
- wallet.command("sign-message").description("Sign a message with the agent wallet").requiredOption("--chain <key>", "Chain key (e.g., ethereum, solana)").requiredOption("--message <text>", "Message to sign").option("--password <password>", "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)").option("--json", "JSON output").action(async (opts) => {
39503
+ wallet.command("sign-message").description("Sign a message with the agent wallet").requiredOption("--chain <key>", "Chain key (e.g., ethereum, solana)").requiredOption("--message <text>", "Message to sign").option(
39504
+ "--password <password>",
39505
+ "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
39506
+ ).option("--json", "JSON output").action(async (opts) => {
39286
39507
  try {
39287
39508
  await ensureTrustSDK();
39288
39509
  const password = await resolvePassword(opts);
@@ -39471,6 +39692,139 @@ var SwapClient = class {
39471
39692
  }
39472
39693
  }
39473
39694
  };
39695
+ var SwapExecutor = class {
39696
+ constructor(swapClient, walletProvider, options = {}) {
39697
+ this.swapClient = swapClient;
39698
+ this.walletProvider = walletProvider;
39699
+ this.options = options;
39700
+ }
39701
+ log(msg) {
39702
+ this.options.onStatus?.(msg);
39703
+ }
39704
+ async execute(params) {
39705
+ const address = await this.walletProvider.getAddress(params.fromChain);
39706
+ const fromTokenInfo = resolveToken(params.fromToken, params.fromChain);
39707
+ if (!fromTokenInfo) throw new Error(`Unknown token: ${params.fromToken} on ${params.fromChain}`);
39708
+ const toTokenInfo = resolveToken(params.toToken, params.toChain);
39709
+ if (!toTokenInfo) throw new Error(`Unknown token: ${params.toToken} on ${params.toChain}`);
39710
+ const parsedAmount = parseAmount(params.amount, fromTokenInfo.decimals);
39711
+ const fromCoinId = getChainById(params.fromChain)?.coinId;
39712
+ const toCoinId = getChainById(params.toChain)?.coinId;
39713
+ const domains = this.options.amberDomains || /* @__PURE__ */ new Map();
39714
+ let destinationAddress;
39715
+ if (params.toChain !== params.fromChain) {
39716
+ destinationAddress = await this.walletProvider.getAddress(params.toChain);
39717
+ }
39718
+ const quote = await this.swapClient.getQuote({
39719
+ fromChain: params.fromChain,
39720
+ fromToken: fromTokenInfo.address,
39721
+ toChain: params.toChain,
39722
+ toToken: toTokenInfo.address,
39723
+ amount: parsedAmount,
39724
+ walletAddress: address,
39725
+ destinationAddress,
39726
+ slippage: params.slippage,
39727
+ fromDomainId: fromCoinId ? domains.get(fromCoinId) : void 0,
39728
+ toDomainId: toCoinId ? domains.get(toCoinId) : void 0
39729
+ });
39730
+ if (quote.routes.length === 0) {
39731
+ return {
39732
+ success: false,
39733
+ route: {
39734
+ id: "",
39735
+ steps: [],
39736
+ warnings: ["No routes found"],
39737
+ expirationDate: "",
39738
+ totalOutput: "0"
39739
+ },
39740
+ error: "No swap routes found"
39741
+ };
39742
+ }
39743
+ const route = quote.routes[0];
39744
+ const firstStep = route.steps[0];
39745
+ const lastStep = route.steps[route.steps.length - 1];
39746
+ const inputAmt = formatAmount(firstStep.from.amount, firstStep.from.asset.metadata.decimals);
39747
+ const outputAmt = formatAmount(lastStep.to.amount, lastStep.to.asset.metadata.decimals);
39748
+ this.log(
39749
+ `Swapping ${inputAmt} ${firstStep.from.asset.metadata.symbol} -> ${outputAmt} ${lastStep.to.asset.metadata.symbol} via ${route.steps.map((s2) => s2.provider.name).join(" -> ")}`
39750
+ );
39751
+ const chainConfig = getChainById(params.fromChain);
39752
+ const transactions = await this.swapClient.getSwapTransactions(route);
39753
+ try {
39754
+ let txHash = "";
39755
+ for (const txData of transactions) {
39756
+ const txType = txData.transaction.type;
39757
+ const isApproval = txType === "approval" || txType === "revokeApproval";
39758
+ if (isApproval) {
39759
+ this.log(
39760
+ txType === "revokeApproval" ? "Revoking prior approval..." : "Sending token approval..."
39761
+ );
39762
+ }
39763
+ txHash = await this.sendTransaction(txData, chainConfig, params.fromChain);
39764
+ this.log(
39765
+ `${isApproval ? "Approval" : "Swap"} tx: ${getExplorerTxUrl(params.fromChain, txHash)}`
39766
+ );
39767
+ await this.pollForConfirmation(
39768
+ params.fromChain,
39769
+ address,
39770
+ txHash,
39771
+ isApproval ? 6e4 : 12e4
39772
+ );
39773
+ }
39774
+ return {
39775
+ success: true,
39776
+ txHash,
39777
+ route,
39778
+ explorerUrl: getExplorerTxUrl(params.fromChain, txHash)
39779
+ };
39780
+ } catch (err) {
39781
+ const msg = err instanceof Error ? err.message : "Unknown error";
39782
+ return { success: false, route, error: msg };
39783
+ }
39784
+ }
39785
+ async sendTransaction(txData, chainConfig, fromChain) {
39786
+ if (chainConfig?.namespace === "solana" && txData.transaction.solanaTx) {
39787
+ return this.walletProvider.sendTransaction({
39788
+ chainId: `solana:${chainConfig.chainId}`,
39789
+ to: "",
39790
+ data: txData.transaction.solanaTx.serializedMessage
39791
+ });
39792
+ } else if (txData.transaction.evmTx) {
39793
+ return this.walletProvider.sendTransaction({
39794
+ chainId: `eip155:${chainConfig?.chainId || "1"}`,
39795
+ to: txData.transaction.evmTx.to,
39796
+ data: txData.transaction.evmTx.data,
39797
+ value: txData.transaction.evmTx.value || "0x0"
39798
+ });
39799
+ }
39800
+ throw new Error(`No transaction data returned for swap step on ${fromChain}`);
39801
+ }
39802
+ async pollForConfirmation(chainKey, address, hash, timeoutMs) {
39803
+ const blockchainClient = this.walletProvider.getBlockchainClient?.();
39804
+ if (!blockchainClient)
39805
+ throw new Error(
39806
+ "Wallet provider does not support transaction status lookup; cannot confirm transaction"
39807
+ );
39808
+ const deadline = Date.now() + timeoutMs;
39809
+ while (Date.now() < deadline) {
39810
+ await new Promise((r3) => setTimeout(r3, 2e3));
39811
+ try {
39812
+ const status = await blockchainClient.findTransactionStatus(chainKey, address, hash);
39813
+ if (status.confirmed) return;
39814
+ if (status.failed)
39815
+ throw new Error(`Transaction failed: ${status.error || "unknown error"}`);
39816
+ } catch (e2) {
39817
+ const err = e2 instanceof Error ? e2 : new Error(String(e2));
39818
+ if (err.message.startsWith("Transaction failed")) throw err;
39819
+ if (!err.message.includes("not found") && !err.message.includes("Transaction not found"))
39820
+ throw err;
39821
+ }
39822
+ }
39823
+ throw new Error(
39824
+ `Transaction ${hash} was not confirmed within ${Math.round(timeoutMs / 1e3)}s`
39825
+ );
39826
+ }
39827
+ };
39474
39828
 
39475
39829
  // src/commands/transfer.ts
39476
39830
  var DEFAULT_MAX_TRANSFER_USD = 1e4;
@@ -39540,6 +39894,11 @@ function registerTransferCommand(program3) {
39540
39894
  }
39541
39895
  try {
39542
39896
  const tokenInfo = resolveToken(opts.token, chainKey);
39897
+ if (!tokenInfo) {
39898
+ error(`Unknown token: ${opts.token} on ${chainKey}. Use a contract address (0x...) or a supported symbol.`);
39899
+ process.exitCode = 1;
39900
+ return;
39901
+ }
39543
39902
  const swapClient = new SwapClient(httpClient);
39544
39903
  const price = await swapClient.getTokenPrice(chainKey, tokenInfo.address);
39545
39904
  if (price && price > 0) {
@@ -39598,250 +39957,299 @@ init_dist4();
39598
39957
  var MAX_SLIPPAGE_PCT = 50;
39599
39958
  var WARN_SLIPPAGE_PCT = 5;
39600
39959
  function registerSwapCommand(program3) {
39601
- 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) => {
39602
- try {
39603
- let amount;
39604
- let from8;
39605
- let to3;
39606
- if (toOrUndefined) {
39607
- amount = amountOrFrom;
39608
- from8 = fromOrTo;
39609
- to3 = toOrUndefined;
39610
- } else {
39611
- from8 = amountOrFrom;
39612
- to3 = fromOrTo;
39613
- }
39614
- if (!amount && !opts.usd) {
39615
- error("Either <amount> or --usd <amount> is required");
39616
- return;
39617
- }
39618
- if (amount && opts.usd) {
39619
- error("Provide either <amount> or --usd, not both");
39620
- return;
39621
- }
39622
- const slippage = parseFloat(opts.slippage);
39623
- if (isNaN(slippage) || slippage <= 0) {
39624
- error("--slippage must be a positive number");
39625
- process.exitCode = 1;
39626
- return;
39627
- }
39628
- if (slippage > MAX_SLIPPAGE_PCT) {
39629
- error(`Slippage ${slippage}% exceeds maximum allowed (${MAX_SLIPPAGE_PCT}%). Reduce --slippage to proceed.`);
39630
- process.exitCode = 1;
39631
- return;
39632
- }
39633
- if (slippage > WARN_SLIPPAGE_PCT) {
39634
- warn(`High slippage tolerance: ${slippage}%. You may receive significantly fewer tokens than quoted.`);
39635
- }
39636
- const client = await getClient();
39637
- const swapClient = new SwapClient(client);
39638
- const detectChain = (token, fallback) => {
39639
- const m2 = token.match(/^c(\d+)/i);
39640
- if (m2) {
39641
- const key = coinTypeToChainKey(parseInt(m2[1]));
39642
- if (!key) throw new Error(`Unknown coin ID ${m2[1]} in token "${token}"`);
39643
- return key;
39644
- }
39645
- return fallback;
39646
- };
39647
- const fromChain = detectChain(from8, opts.chain);
39648
- const toChain = opts.toChain ? detectChain(to3, opts.toChain) : fromChain;
39649
- const fromInfo = resolveToken(from8, fromChain);
39650
- const toInfo = resolveToken(to3, toChain);
39651
- let resolvedAmount = amount;
39652
- if (opts.usd) {
39653
- const usdAmount = parseFloat(opts.usd);
39654
- if (isNaN(usdAmount) || usdAmount <= 0) {
39655
- error("--usd amount must be a positive number");
39960
+ program3.command("swap").description("Quote or execute a token swap").argument("<amountOrFrom>", "Amount to swap, or source token when using --usd").argument("<fromOrTo>", "Source token, or destination token when using --usd").argument("[to]", "Destination token (omit when using --usd with 2 args)").option("--chain <id>", "Chain", "ethereum").option("--to-chain <id>", "Destination chain (for cross-chain swaps)").option("--slippage <pct>", "Slippage tolerance %", "1").option("--usd <amount>", "Swap a USD-equivalent amount of the source token").option("--quote-only", "Get quote without executing").option("--password <password>", "Wallet password (required to execute)").option("--json", "JSON output").action(
39961
+ async (amountOrFrom, fromOrTo, toOrUndefined, opts) => {
39962
+ try {
39963
+ let amount;
39964
+ let from8;
39965
+ let to3;
39966
+ if (toOrUndefined) {
39967
+ amount = amountOrFrom;
39968
+ from8 = fromOrTo;
39969
+ to3 = toOrUndefined;
39970
+ } else {
39971
+ from8 = amountOrFrom;
39972
+ to3 = fromOrTo;
39973
+ }
39974
+ if (!amount && !opts.usd) {
39975
+ error("Either <amount> or --usd <amount> is required");
39656
39976
  return;
39657
39977
  }
39658
- const price = await swapClient.getTokenPrice(fromChain, fromInfo.address);
39659
- if (!price) {
39660
- error(`Unable to fetch price for ${from8} on ${fromChain}`);
39978
+ if (amount && opts.usd) {
39979
+ error("Provide either <amount> or --usd, not both");
39661
39980
  return;
39662
39981
  }
39663
- resolvedAmount = (usdAmount / price).toFixed(fromInfo.decimals);
39664
- info(`$${usdAmount} USD \u2248 ${resolvedAmount} ${from8.toUpperCase()} (@ $${price})`);
39665
- }
39666
- const parsedAmount = parseAmount(resolvedAmount, fromInfo.decimals);
39667
- const needsWallet = !opts.quoteOnly;
39668
- let walletAddress = getChainById(fromChain)?.sampleAccount || "0x0000000000000000000000000000000000000000";
39669
- let provider = null;
39670
- let destinationAddress;
39671
- let amberDomains = /* @__PURE__ */ new Map();
39672
- if (needsWallet) {
39673
- await ensureTrustSDK();
39674
- const password = await resolvePassword(opts);
39675
- provider = await AgentWalletProvider.fromStorage(password);
39676
- walletAddress = await provider.getAddress(fromChain);
39677
- if (toChain !== fromChain) {
39678
- destinationAddress = await provider.getAddress(toChain);
39982
+ if (amount) {
39983
+ const n4 = parseFloat(amount);
39984
+ if (isNaN(n4) || n4 <= 0) {
39985
+ error("Amount must be greater than 0");
39986
+ process.exitCode = 1;
39987
+ return;
39988
+ }
39679
39989
  }
39680
- amberDomains = await getAmberDomainsByCoinId();
39681
- }
39682
- const fromCoinId = getChainById(fromChain)?.coinId;
39683
- const toCoinId = getChainById(toChain)?.coinId;
39684
- const quote = await swapClient.getQuote({
39685
- fromChain,
39686
- fromToken: fromInfo.address,
39687
- toChain,
39688
- toToken: toInfo.address,
39689
- amount: parsedAmount,
39690
- walletAddress,
39691
- destinationAddress,
39692
- slippage: opts.slippage,
39693
- fromDomainId: fromCoinId ? amberDomains.get(fromCoinId) : void 0,
39694
- toDomainId: toCoinId ? amberDomains.get(toCoinId) : void 0
39695
- });
39696
- if (quote.routes.length === 0) {
39697
- error("No swap routes found");
39698
- if (opts.json) output({ error: "No routes found" }, true);
39699
- return;
39700
- }
39701
- const route = quote.routes[0];
39702
- const firstStep = route.steps[0];
39703
- const lastStep = route.steps[route.steps.length - 1];
39704
- const inputAmt = formatAmount(firstStep.from.amount, firstStep.from.asset.metadata.decimals);
39705
- const outputAmt = formatAmount(lastStep.to.amount, lastStep.to.asset.metadata.decimals);
39706
- const minAmountOut = lastStep.to.minAmountOut;
39707
- const minReceivedAmt = minAmountOut ? formatAmount(minAmountOut, lastStep.to.asset.metadata.decimals) : null;
39708
- const networkFees = route.steps.filter((s2) => s2.networkFee).map((s2) => {
39709
- const fee = s2.networkFee;
39710
- const feeAmt = formatAmount(fee.amount, fee.asset.metadata.decimals);
39711
- return `${feeAmt} ${fee.asset.metadata.symbol}`;
39712
- });
39713
- const quoteData = {
39714
- input: `${inputAmt} ${firstStep.from.asset.metadata.symbol}`,
39715
- output: `${outputAmt} ${lastStep.to.asset.metadata.symbol}`,
39716
- ...minReceivedAmt ? { minReceived: `${minReceivedAmt} ${lastStep.to.asset.metadata.symbol}` } : {},
39717
- provider: route.steps.map((s2) => s2.provider.name).join(" \u2192 "),
39718
- priceImpact: firstStep.priceImpact,
39719
- ...networkFees.length > 0 ? { networkFee: networkFees.join(" + ") } : {},
39720
- ...route.steps.length > 1 ? { steps: route.steps.length } : {}
39721
- };
39722
- if (opts.quoteOnly) {
39723
- output(quoteData, opts.json);
39724
- return;
39725
- }
39726
- info(`Swapping ${quoteData.input} \u2192 ${quoteData.output} via ${quoteData.provider}...`);
39727
- const chainConfig = getChainById(fromChain);
39728
- const transactions = await swapClient.getSwapTransactions(route);
39729
- let txHash = "";
39730
- const blockchainClient = provider.getBlockchainClient();
39731
- const hasApprovalTx = transactions.some((t) => t.transaction.type === "approval");
39732
- if (!hasApprovalTx && chainConfig?.isEVM) {
39733
- const stepApproval = firstStep.approval;
39734
- if (stepApproval && stepApproval.operatorAddress) {
39735
- const fromAssetAddr = firstStep.from.asset.address;
39736
- const isNative = !fromAssetAddr || fromAssetAddr === NATIVE_TOKEN_ADDRESS;
39737
- if (!isNative && chainConfig.coinId) {
39738
- const assetId = formatTokenId(chainConfig.coinId, fromAssetAddr);
39739
- try {
39740
- const allowance = await blockchainClient.getAllowance({
39741
- assetId,
39742
- owner: walletAddress,
39743
- spender: stepApproval.operatorAddress
39744
- });
39745
- const requiredAmount = BigInt(stepApproval.minAmount || parsedAmount);
39746
- if (allowance < requiredAmount) {
39747
- info("Sending token approval...");
39748
- const hdWallet = provider.getSigner().getHDWallet();
39749
- const approveResult = await blockchainClient.approve({
39990
+ const slippage = parseFloat(opts.slippage);
39991
+ if (isNaN(slippage) || slippage <= 0) {
39992
+ error("--slippage must be a positive number");
39993
+ process.exitCode = 1;
39994
+ return;
39995
+ }
39996
+ if (slippage > MAX_SLIPPAGE_PCT) {
39997
+ error(
39998
+ `Slippage ${slippage}% exceeds maximum allowed (${MAX_SLIPPAGE_PCT}%). Reduce --slippage to proceed.`
39999
+ );
40000
+ process.exitCode = 1;
40001
+ return;
40002
+ }
40003
+ if (slippage > WARN_SLIPPAGE_PCT) {
40004
+ warn(
40005
+ `High slippage tolerance: ${slippage}%. You may receive significantly fewer tokens than quoted.`
40006
+ );
40007
+ }
40008
+ const client = await getClient();
40009
+ const swapClient = new SwapClient(client);
40010
+ const detectChain = (token, fallback) => {
40011
+ const m2 = token.match(/^c(\d+)/i);
40012
+ if (m2) {
40013
+ const key = coinTypeToChainKey(parseInt(m2[1]));
40014
+ if (!key) throw new Error(`Unknown coin ID ${m2[1]} in token "${token}"`);
40015
+ return key;
40016
+ }
40017
+ return fallback;
40018
+ };
40019
+ const fromChain = detectChain(from8, opts.chain);
40020
+ const toChain = opts.toChain ? detectChain(to3, opts.toChain) : fromChain;
40021
+ const fromInfo = resolveToken(from8, fromChain);
40022
+ if (!fromInfo) {
40023
+ error(
40024
+ `Unknown token: ${from8} on ${fromChain}. Use a contract address (0x...) or a supported symbol.`
40025
+ );
40026
+ process.exitCode = 1;
40027
+ return;
40028
+ }
40029
+ const toInfo = resolveToken(to3, toChain);
40030
+ if (!toInfo) {
40031
+ error(
40032
+ `Unknown token: ${to3} on ${toChain}. Use a contract address (0x...) or a supported symbol.`
40033
+ );
40034
+ process.exitCode = 1;
40035
+ return;
40036
+ }
40037
+ let resolvedAmount = amount;
40038
+ if (opts.usd) {
40039
+ const usdAmount = parseFloat(opts.usd);
40040
+ if (isNaN(usdAmount) || usdAmount <= 0) {
40041
+ error("--usd amount must be a positive number");
40042
+ return;
40043
+ }
40044
+ const price = await swapClient.getTokenPrice(fromChain, fromInfo.address);
40045
+ if (!price) {
40046
+ error(`Unable to fetch price for ${from8} on ${fromChain}`);
40047
+ return;
40048
+ }
40049
+ resolvedAmount = (usdAmount / price).toFixed(fromInfo.decimals);
40050
+ info(`$${usdAmount} USD \u2248 ${resolvedAmount} ${from8.toUpperCase()} (@ $${price})`);
40051
+ }
40052
+ const parsedAmount = parseAmount(resolvedAmount, fromInfo.decimals);
40053
+ const needsWallet = !opts.quoteOnly;
40054
+ let walletAddress = getChainById(fromChain)?.sampleAccount || "0x0000000000000000000000000000000000000000";
40055
+ let provider = null;
40056
+ let destinationAddress;
40057
+ let amberDomains = /* @__PURE__ */ new Map();
40058
+ if (needsWallet) {
40059
+ await ensureTrustSDK();
40060
+ const password = await resolvePassword(opts);
40061
+ provider = await AgentWalletProvider.fromStorage(password);
40062
+ walletAddress = await provider.getAddress(fromChain);
40063
+ if (toChain !== fromChain) {
40064
+ destinationAddress = await provider.getAddress(toChain);
40065
+ }
40066
+ amberDomains = await getAmberDomainsByCoinId();
40067
+ }
40068
+ const fromCoinId = getChainById(fromChain)?.coinId;
40069
+ const toCoinId = getChainById(toChain)?.coinId;
40070
+ const quote = await swapClient.getQuote({
40071
+ fromChain,
40072
+ fromToken: fromInfo.address,
40073
+ toChain,
40074
+ toToken: toInfo.address,
40075
+ amount: parsedAmount,
40076
+ walletAddress,
40077
+ destinationAddress,
40078
+ slippage: opts.slippage,
40079
+ fromDomainId: fromCoinId ? amberDomains.get(fromCoinId) : void 0,
40080
+ toDomainId: toCoinId ? amberDomains.get(toCoinId) : void 0
40081
+ });
40082
+ if (quote.routes.length === 0) {
40083
+ error("No swap routes found");
40084
+ if (opts.json) output({ error: "No routes found" }, true);
40085
+ return;
40086
+ }
40087
+ const route = quote.routes[0];
40088
+ const firstStep = route.steps[0];
40089
+ const lastStep = route.steps[route.steps.length - 1];
40090
+ const inputAmt = formatAmount(
40091
+ firstStep.from.amount,
40092
+ firstStep.from.asset.metadata.decimals
40093
+ );
40094
+ const outputAmt = formatAmount(lastStep.to.amount, lastStep.to.asset.metadata.decimals);
40095
+ const minAmountOut = lastStep.to.minAmountOut;
40096
+ const minReceivedAmt = minAmountOut ? formatAmount(minAmountOut, lastStep.to.asset.metadata.decimals) : null;
40097
+ const networkFees = route.steps.filter((s2) => s2.networkFee).map((s2) => {
40098
+ const fee = s2.networkFee;
40099
+ const feeAmt = formatAmount(fee.amount, fee.asset.metadata.decimals);
40100
+ return `${feeAmt} ${fee.asset.metadata.symbol}`;
40101
+ });
40102
+ const quoteData = {
40103
+ input: `${inputAmt} ${firstStep.from.asset.metadata.symbol}`,
40104
+ output: `${outputAmt} ${lastStep.to.asset.metadata.symbol}`,
40105
+ ...minReceivedAmt ? { minReceived: `${minReceivedAmt} ${lastStep.to.asset.metadata.symbol}` } : {},
40106
+ provider: route.steps.map((s2) => s2.provider.name).join(" \u2192 "),
40107
+ priceImpact: firstStep.priceImpact,
40108
+ ...networkFees.length > 0 ? { networkFee: networkFees.join(" + ") } : {},
40109
+ ...route.steps.length > 1 ? { steps: route.steps.length } : {}
40110
+ };
40111
+ if (opts.quoteOnly) {
40112
+ output(quoteData, opts.json);
40113
+ return;
40114
+ }
40115
+ info(`Swapping ${quoteData.input} \u2192 ${quoteData.output} via ${quoteData.provider}...`);
40116
+ const chainConfig = getChainById(fromChain);
40117
+ const transactions = await swapClient.getSwapTransactions(route);
40118
+ let txHash = "";
40119
+ const blockchainClient = provider.getBlockchainClient();
40120
+ const hasApprovalTx = transactions.some((t) => t.transaction.type === "approval");
40121
+ if (!hasApprovalTx && chainConfig?.isEVM) {
40122
+ const stepApproval = firstStep.approval;
40123
+ if (stepApproval && stepApproval.operatorAddress) {
40124
+ const fromAssetAddr = firstStep.from.asset.address;
40125
+ const isNative = !fromAssetAddr || fromAssetAddr === NATIVE_TOKEN_ADDRESS;
40126
+ if (!isNative && chainConfig.coinId) {
40127
+ const assetId = formatTokenId(chainConfig.coinId, fromAssetAddr);
40128
+ try {
40129
+ const allowance = await blockchainClient.getAllowance({
39750
40130
  assetId,
39751
- spender: stepApproval.operatorAddress,
39752
- amount: requiredAmount.toString(),
39753
- hdWallet
40131
+ owner: walletAddress,
40132
+ spender: stepApproval.operatorAddress
39754
40133
  });
39755
- info(`Approval tx: ${getExplorerTxUrl(fromChain, approveResult.hash)}`);
39756
- const approvalDeadline = Date.now() + 6e4;
39757
- while (Date.now() < approvalDeadline) {
39758
- await new Promise((r3) => setTimeout(r3, 2e3));
39759
- try {
39760
- const status = await blockchainClient.findTransactionStatus(fromChain, walletAddress, approveResult.hash);
39761
- if (status.confirmed) break;
39762
- if (status.failed) throw new Error("Approval transaction failed");
39763
- } catch (e2) {
39764
- const err = e2 instanceof Error ? e2 : new Error(String(e2));
39765
- if (err.message.startsWith("Approval transaction failed")) throw err;
40134
+ const requiredAmount = BigInt(stepApproval.minAmount || parsedAmount);
40135
+ if (allowance < requiredAmount) {
40136
+ info("Sending token approval...");
40137
+ const hdWallet = provider.getSigner().getHDWallet();
40138
+ const approveResult = await blockchainClient.approve({
40139
+ assetId,
40140
+ spender: stepApproval.operatorAddress,
40141
+ amount: requiredAmount.toString(),
40142
+ hdWallet
40143
+ });
40144
+ info(`Approval tx: ${getExplorerTxUrl(fromChain, approveResult.hash)}`);
40145
+ const approvalDeadline = Date.now() + 6e4;
40146
+ while (Date.now() < approvalDeadline) {
40147
+ await new Promise((r3) => setTimeout(r3, 2e3));
40148
+ try {
40149
+ const status = await blockchainClient.findTransactionStatus(
40150
+ fromChain,
40151
+ walletAddress,
40152
+ approveResult.hash
40153
+ );
40154
+ if (status.confirmed) break;
40155
+ if (status.failed) throw new Error("Approval transaction failed");
40156
+ } catch (e2) {
40157
+ const err = e2 instanceof Error ? e2 : new Error(String(e2));
40158
+ if (err.message.startsWith("Approval transaction failed")) throw err;
40159
+ }
39766
40160
  }
39767
40161
  }
40162
+ } catch (e2) {
40163
+ const msg = e2 instanceof Error ? e2.message : String(e2);
40164
+ if (msg.includes("insufficient funds")) {
40165
+ throw new Error(
40166
+ `Insufficient ${chainConfig?.symbol || "native token"} for approval gas. Fund your wallet with ${chainConfig?.symbol || "native tokens"} first.`
40167
+ );
40168
+ }
40169
+ if (msg.includes("failed")) throw e2;
40170
+ info(`Warning: could not check/send approval: ${msg}`);
39768
40171
  }
39769
- } catch (e2) {
39770
- const msg = e2 instanceof Error ? e2.message : String(e2);
39771
- if (msg.includes("insufficient funds")) {
39772
- throw new Error(`Insufficient ${chainConfig?.symbol || "native token"} for approval gas. Fund your wallet with ${chainConfig?.symbol || "native tokens"} first.`);
39773
- }
39774
- if (msg.includes("failed")) throw e2;
39775
- info(`Warning: could not check/send approval: ${msg}`);
39776
40172
  }
39777
40173
  }
39778
40174
  }
39779
- }
39780
- const pollForConfirmation = async (hash, timeoutMs) => {
39781
- const deadline = Date.now() + timeoutMs;
39782
- while (Date.now() < deadline) {
39783
- await new Promise((r3) => setTimeout(r3, 2e3));
39784
- try {
39785
- const status = await blockchainClient.findTransactionStatus(fromChain, walletAddress, hash);
39786
- if (status.confirmed) return;
39787
- if (status.failed) throw new Error(`Transaction failed: ${status.error || "unknown error"}`);
39788
- } catch (e2) {
39789
- const err = e2 instanceof Error ? e2 : new Error(String(e2));
39790
- if (err.message.startsWith("Transaction failed")) throw err;
39791
- if (!err.message.includes("not found") && !err.message.includes("Transaction not found")) {
39792
- info(`Warning: tx status check error (will retry): ${err.message}`);
40175
+ const pollForConfirmation = async (hash, timeoutMs) => {
40176
+ const deadline = Date.now() + timeoutMs;
40177
+ while (Date.now() < deadline) {
40178
+ await new Promise((r3) => setTimeout(r3, 2e3));
40179
+ try {
40180
+ const status = await blockchainClient.findTransactionStatus(
40181
+ fromChain,
40182
+ walletAddress,
40183
+ hash
40184
+ );
40185
+ if (status.confirmed) return;
40186
+ if (status.failed)
40187
+ throw new Error(`Transaction failed: ${status.error || "unknown error"}`);
40188
+ } catch (e2) {
40189
+ const err = e2 instanceof Error ? e2 : new Error(String(e2));
40190
+ if (err.message.startsWith("Transaction failed")) throw err;
40191
+ if (!err.message.includes("not found") && !err.message.includes("Transaction not found")) {
40192
+ info(`Warning: tx status check error (will retry): ${err.message}`);
40193
+ }
39793
40194
  }
39794
40195
  }
40196
+ throw new Error(
40197
+ `Transaction ${hash} was not confirmed within ${Math.round(timeoutMs / 1e3)}s`
40198
+ );
40199
+ };
40200
+ for (const txData of transactions) {
40201
+ const txType = txData.transaction.type;
40202
+ const isApproval = txType === "approval" || txType === "revokeApproval";
40203
+ if (isApproval) {
40204
+ info(
40205
+ txType === "revokeApproval" ? "Revoking prior approval..." : "Sending token approval..."
40206
+ );
40207
+ }
40208
+ if (chainConfig?.namespace === "solana" && txData.transaction.solanaTx) {
40209
+ const solanaChainId = `solana:${chainConfig.chainId}`;
40210
+ txHash = await provider.sendTransaction({
40211
+ chainId: solanaChainId,
40212
+ to: "",
40213
+ data: txData.transaction.solanaTx.serializedMessage
40214
+ });
40215
+ } else if (txData.transaction.evmTx) {
40216
+ const wcChainId = `eip155:${chainConfig?.chainId || "1"}`;
40217
+ txHash = await provider.sendTransaction({
40218
+ chainId: wcChainId,
40219
+ to: txData.transaction.evmTx.to,
40220
+ data: txData.transaction.evmTx.data,
40221
+ value: txData.transaction.evmTx.value || "0x0"
40222
+ });
40223
+ } else {
40224
+ error("No transaction data returned for this swap step");
40225
+ if (opts.json) output({ error: "No transaction data" }, true);
40226
+ return;
40227
+ }
40228
+ info(`${isApproval ? "Approval" : "Swap"} tx: ${getExplorerTxUrl(fromChain, txHash)}`);
40229
+ await pollForConfirmation(txHash, isApproval ? 6e4 : 12e4);
39795
40230
  }
39796
- throw new Error(`Transaction ${hash} was not confirmed within ${Math.round(timeoutMs / 1e3)}s`);
39797
- };
39798
- for (const txData of transactions) {
39799
- const txType = txData.transaction.type;
39800
- const isApproval = txType === "approval" || txType === "revokeApproval";
39801
- if (isApproval) {
39802
- info(txType === "revokeApproval" ? "Revoking prior approval..." : "Sending token approval...");
39803
- }
39804
- if (chainConfig?.namespace === "solana" && txData.transaction.solanaTx) {
39805
- const solanaChainId = `solana:${chainConfig.chainId}`;
39806
- txHash = await provider.sendTransaction({
39807
- chainId: solanaChainId,
39808
- to: "",
39809
- data: txData.transaction.solanaTx.serializedMessage
39810
- });
39811
- } else if (txData.transaction.evmTx) {
39812
- const wcChainId = `eip155:${chainConfig?.chainId || "1"}`;
39813
- txHash = await provider.sendTransaction({
39814
- chainId: wcChainId,
39815
- to: txData.transaction.evmTx.to,
39816
- data: txData.transaction.evmTx.data,
39817
- value: txData.transaction.evmTx.value || "0x0"
39818
- });
39819
- } else {
39820
- error("No transaction data returned for this swap step");
39821
- if (opts.json) output({ error: "No transaction data" }, true);
39822
- return;
39823
- }
39824
- info(`${isApproval ? "Approval" : "Swap"} tx: ${getExplorerTxUrl(fromChain, txHash)}`);
39825
- await pollForConfirmation(txHash, isApproval ? 6e4 : 12e4);
40231
+ const explorerUrl = getExplorerTxUrl(fromChain, txHash);
40232
+ success("Swap executed!");
40233
+ output(
40234
+ {
40235
+ ...quoteData,
40236
+ hash: txHash,
40237
+ fromChain,
40238
+ toChain,
40239
+ explorer: explorerUrl
40240
+ },
40241
+ opts.json
40242
+ );
40243
+ } catch (err) {
40244
+ const msg = err instanceof Error ? err.message : String(err);
40245
+ error(`Swap failed: ${msg}`);
40246
+ if (opts.json) output({ error: msg }, true);
40247
+ }
40248
+ if (!opts.quoteOnly) {
40249
+ setTimeout(() => process.exit(0), 100);
39826
40250
  }
39827
- const explorerUrl = getExplorerTxUrl(fromChain, txHash);
39828
- success("Swap executed!");
39829
- output({
39830
- ...quoteData,
39831
- hash: txHash,
39832
- fromChain,
39833
- toChain,
39834
- explorer: explorerUrl
39835
- }, opts.json);
39836
- } catch (err) {
39837
- const msg = err instanceof Error ? err.message : String(err);
39838
- error(`Swap failed: ${msg}`);
39839
- if (opts.json) output({ error: msg }, true);
39840
- }
39841
- if (!opts.quoteOnly) {
39842
- setTimeout(() => process.exit(0), 100);
39843
40251
  }
39844
- });
40252
+ );
39845
40253
  }
39846
40254
 
39847
40255
  // src/commands/erc20.ts
@@ -39992,12 +40400,43 @@ function registerErc20Command(program3) {
39992
40400
  // src/commands/alert.ts
39993
40401
  init_dist();
39994
40402
  init_dist2();
39995
- function resolveTokenLabel(tokenId) {
40403
+ init_dist5();
40404
+ var assetCache = new AssetCache();
40405
+ async function resolveTokenLabel(tokenId, service) {
40406
+ const cached2 = await assetCache.get(tokenId);
40407
+ if (cached2 !== void 0) return cached2;
39996
40408
  try {
39997
40409
  const { coinId, contractAddress } = parseTokenId(tokenId);
39998
40410
  const chain2 = getChainByCoinId(coinId);
39999
- if (chain2 && !contractAddress) return chain2.symbol;
40000
- return tokenId;
40411
+ if (!chain2) return tokenId;
40412
+ if (!contractAddress) {
40413
+ return chain2.symbol;
40414
+ }
40415
+ const chainKey = tokenIdToChain(tokenId);
40416
+ if (chainKey) {
40417
+ const chainTokens = COMMON_TOKENS[chainKey];
40418
+ if (chainTokens) {
40419
+ const normalizedAddr = contractAddress.toLowerCase();
40420
+ for (const [sym, addr] of Object.entries(chainTokens)) {
40421
+ if (addr.toLowerCase() === normalizedAddr) {
40422
+ await assetCache.set(tokenId, sym);
40423
+ return sym;
40424
+ }
40425
+ }
40426
+ }
40427
+ }
40428
+ if (service) {
40429
+ try {
40430
+ const assetInfo = await service.getAssetInfo(tokenId);
40431
+ if (assetInfo.symbol) {
40432
+ await assetCache.set(tokenId, assetInfo.symbol);
40433
+ return assetInfo.symbol;
40434
+ }
40435
+ } catch {
40436
+ }
40437
+ }
40438
+ const short = contractAddress.length > 10 ? `${contractAddress.slice(0, 6)}\u2026${contractAddress.slice(-4)}` : contractAddress;
40439
+ return `${chain2.symbol}/${short}`;
40001
40440
  } catch {
40002
40441
  return tokenId;
40003
40442
  }
@@ -40061,7 +40500,9 @@ function registerAlertCommand(program3) {
40061
40500
  resolvedChain = NATIVE_SYMBOLS2[sym];
40062
40501
  }
40063
40502
  if (!resolvedChain) {
40064
- error(`Cannot resolve "${opts.token}" without --chain. Provide --chain or use asset ID format (c60).`);
40503
+ error(
40504
+ `Cannot resolve "${opts.token}" without --chain. Provide --chain or use asset ID format (c60).`
40505
+ );
40065
40506
  if (opts.json) output({ error: "Chain required" }, true);
40066
40507
  process.exitCode = 1;
40067
40508
  return;
@@ -40074,6 +40515,12 @@ function registerAlertCommand(program3) {
40074
40515
  return;
40075
40516
  }
40076
40517
  const resolved = resolveToken(sym, resolvedChain);
40518
+ if (!resolved) {
40519
+ error(`Unknown token: ${sym} on ${resolvedChain}. Use a contract address or asset ID (e.g. c60_t0xABC...).`);
40520
+ if (opts.json) output({ error: `Unknown token: ${sym}` }, true);
40521
+ process.exitCode = 1;
40522
+ return;
40523
+ }
40077
40524
  if (resolved.address === NATIVE_TOKEN_ADDRESS || resolved.address === sym) {
40078
40525
  resolvedTokenId = formatTokenId(chain2.coinId);
40079
40526
  } else {
@@ -40097,15 +40544,19 @@ function registerAlertCommand(program3) {
40097
40544
  });
40098
40545
  alert.command("list").description("List all alerts").option("--active", "Show only active alerts").option("--json", "JSON output").action(async (opts) => {
40099
40546
  const alerts = opts.active ? await alertStore.getActive() : await alertStore.list();
40547
+ const client = await tryGetClient();
40548
+ const service = client ? new SecurityService(client) : void 0;
40100
40549
  if (opts.json) {
40101
- const enriched = alerts.map((a3) => {
40102
- let chainLabel = a3.chainKey;
40103
- try {
40104
- chainLabel = tokenIdToChain(a3.tokenId) ?? a3.chainKey;
40105
- } catch {
40106
- }
40107
- return { ...a3, tokenLabel: resolveTokenLabel(a3.tokenId), chainLabel };
40108
- });
40550
+ const enriched = await Promise.all(
40551
+ alerts.map(async (a3) => {
40552
+ let chainLabel = a3.chainKey;
40553
+ try {
40554
+ chainLabel = tokenIdToChain(a3.tokenId) ?? a3.chainKey;
40555
+ } catch {
40556
+ }
40557
+ return { ...a3, tokenLabel: await resolveTokenLabel(a3.tokenId, service), chainLabel };
40558
+ })
40559
+ );
40109
40560
  output(enriched, true);
40110
40561
  } else {
40111
40562
  if (alerts.length === 0) {
@@ -40113,7 +40564,7 @@ function registerAlertCommand(program3) {
40113
40564
  return;
40114
40565
  }
40115
40566
  for (const a3 of alerts) {
40116
- const label = resolveTokenLabel(a3.tokenId);
40567
+ const label = await resolveTokenLabel(a3.tokenId, service);
40117
40568
  let chainHint = "";
40118
40569
  try {
40119
40570
  const chain2 = tokenIdToChain(a3.tokenId);
@@ -40121,7 +40572,9 @@ function registerAlertCommand(program3) {
40121
40572
  } catch {
40122
40573
  chainHint = "";
40123
40574
  }
40124
- info(`${a3.id} | ${label}${chainHint} | ${a3.condition} $${a3.targetPrice} | ${a3.active ? "active" : "triggered"}`);
40575
+ info(
40576
+ `${a3.id} | ${label}${chainHint} | ${a3.condition} $${a3.targetPrice} | ${a3.active ? "active" : "triggered"}`
40577
+ );
40125
40578
  }
40126
40579
  }
40127
40580
  });
@@ -40141,7 +40594,10 @@ function registerAlertCommand(program3) {
40141
40594
  triggered.push({ ...a3, currentPrice: price });
40142
40595
  }
40143
40596
  }
40144
- output({ checked: activeAlerts.length, triggered: triggered.length, alerts: triggered }, opts.json);
40597
+ output(
40598
+ { checked: activeAlerts.length, triggered: triggered.length, alerts: triggered },
40599
+ opts.json
40600
+ );
40145
40601
  } catch (err) {
40146
40602
  const msg = err instanceof Error ? err.message : String(err);
40147
40603
  error(msg);
@@ -40149,8 +40605,22 @@ function registerAlertCommand(program3) {
40149
40605
  }
40150
40606
  });
40151
40607
  alert.command("delete <id>").description("Delete an alert").option("--json", "JSON output").action(async (id, opts) => {
40152
- const deleted = await alertStore.delete(id);
40153
- output({ success: deleted }, opts.json);
40608
+ try {
40609
+ const deleted = await alertStore.delete(id);
40610
+ if (!deleted) {
40611
+ error(`Alert ${id} not found`);
40612
+ if (opts.json) output({ error: `Alert ${id} not found` }, true);
40613
+ process.exitCode = 1;
40614
+ return;
40615
+ }
40616
+ if (!opts.json) success(`Alert ${id} deleted`);
40617
+ output({ success: true }, opts.json);
40618
+ } catch (err) {
40619
+ const msg = err instanceof Error ? err.message : String(err);
40620
+ error(msg);
40621
+ if (opts.json) output({ error: msg }, true);
40622
+ process.exitCode = 1;
40623
+ }
40154
40624
  });
40155
40625
  }
40156
40626
 
@@ -40229,14 +40699,20 @@ function handleError(err, json) {
40229
40699
  process.exitCode = 1;
40230
40700
  }
40231
40701
  function registerQueryCommand(program3) {
40232
- program3.command("price <token>").description("Get token price").option("--chain <id>", "Chain").option("--history [period]", "Price history period: hour, day, week, month, year, all (default: week)").option("--json", "JSON output").action(async (token, opts) => {
40702
+ program3.command("price <token>").description("Get token price").option("--chain <id>", "Chain").option(
40703
+ "--history [period]",
40704
+ "Price history period: hour, day, week, month, year, all (default: week)"
40705
+ ).option("--json", "JSON output").action(async (token, opts) => {
40233
40706
  try {
40234
40707
  const client = await getClient();
40235
40708
  const swapClient = new SwapClient(client);
40236
40709
  const tokens = token.split(",").map((t) => t.trim()).filter(Boolean);
40237
40710
  const resolveChain = (t) => {
40238
40711
  if (opts.chain) return opts.chain;
40239
- const bySymbol = Object.entries(CHAINS).find(([, c3]) => c3.symbol.toUpperCase() === t.toUpperCase())?.[0];
40712
+ if (Object.prototype.hasOwnProperty.call(CHAINS, t.toLowerCase())) return t.toLowerCase();
40713
+ const bySymbol = Object.entries(CHAINS).find(
40714
+ ([, c3]) => c3.symbol.toUpperCase() === t.toUpperCase()
40715
+ )?.[0];
40240
40716
  if (bySymbol) return bySymbol;
40241
40717
  const upper = t.toUpperCase();
40242
40718
  for (const [chainKey, chainTokens] of Object.entries(COMMON_TOKENS)) {
@@ -40244,12 +40720,22 @@ function registerQueryCommand(program3) {
40244
40720
  }
40245
40721
  return "ethereum";
40246
40722
  };
40723
+ if (opts.chain && !getChainById(opts.chain)) {
40724
+ error(`Unknown chain: ${opts.chain}. Run 'twak chains' to list supported chains.`);
40725
+ if (opts.json) output({ error: `Unknown chain: ${opts.chain}` }, true);
40726
+ process.exitCode = 1;
40727
+ return;
40728
+ }
40247
40729
  if (tokens.length > 1) {
40248
40730
  const assetIds = [];
40249
40731
  const tokenMeta = [];
40250
40732
  for (const t of tokens) {
40251
40733
  const chain3 = resolveChain(t);
40252
40734
  const info2 = resolveToken(t, chain3);
40735
+ if (!info2) {
40736
+ warn(`Unknown token: ${t.toUpperCase()}`);
40737
+ continue;
40738
+ }
40253
40739
  const chainInfo = getChainById(chain3);
40254
40740
  if (!chainInfo) throw new Error(`Unknown chain: ${chain3}`);
40255
40741
  const isNative = !info2.address || info2.address === NATIVE_TOKEN_ADDRESS || info2.address === SOLANA_NATIVE_ADDRESS;
@@ -40257,10 +40743,19 @@ function registerQueryCommand(program3) {
40257
40743
  assetIds.push(assetId);
40258
40744
  tokenMeta.push({ token: t.toUpperCase(), chain: chain3 });
40259
40745
  }
40260
- const resp = await client.market("POST", "/v2/market/tickers", {
40261
- currency: "USD",
40262
- assets: assetIds
40263
- });
40746
+ if (assetIds.length === 0) {
40747
+ error("No valid tokens to look up");
40748
+ process.exitCode = 1;
40749
+ return;
40750
+ }
40751
+ const resp = await client.market(
40752
+ "POST",
40753
+ "/v2/market/tickers",
40754
+ {
40755
+ currency: "USD",
40756
+ assets: assetIds
40757
+ }
40758
+ );
40264
40759
  const priceMap = /* @__PURE__ */ new Map();
40265
40760
  for (const t of resp.tickers || []) {
40266
40761
  if (t.id && t.price != null) priceMap.set(t.id, t.price);
@@ -40275,39 +40770,58 @@ function registerQueryCommand(program3) {
40275
40770
  } else {
40276
40771
  for (const r3 of results) {
40277
40772
  const priceStr = r3.priceUsd !== null ? `$${r3.priceUsd.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 6 })}` : "N/A";
40278
- console.log(`${import_chalk6.default.bold(r3.token.padEnd(10))} ${priceStr.padStart(14)} ${import_chalk6.default.dim(r3.chain)}`);
40773
+ console.log(
40774
+ `${import_chalk6.default.bold(r3.token.padEnd(10))} ${priceStr.padStart(14)} ${import_chalk6.default.dim(r3.chain)}`
40775
+ );
40279
40776
  }
40280
40777
  }
40281
40778
  return;
40282
40779
  }
40283
40780
  const chain2 = resolveChain(token);
40284
40781
  const tokenInfo = resolveToken(token, chain2);
40285
- let price = await swapClient.getTokenPrice(chain2, tokenInfo.address);
40782
+ let price = null;
40783
+ if (tokenInfo) {
40784
+ price = await swapClient.getTokenPrice(chain2, tokenInfo.address);
40785
+ }
40286
40786
  if (price === null) {
40287
- const results = await swapClient.searchTokens({ query: token, chains: [chain2], limit: 1 });
40288
- if (results.length > 0 && results[0].priceUsd != null) {
40289
- price = results[0].priceUsd;
40787
+ const results = await swapClient.searchTokens({
40788
+ query: token,
40789
+ chains: [chain2],
40790
+ limit: 5
40791
+ });
40792
+ const exact = results.find(
40793
+ (r3) => r3.symbol?.toUpperCase() === token.toUpperCase() && r3.priceUsd != null
40794
+ );
40795
+ if (exact) {
40796
+ price = exact.priceUsd;
40290
40797
  }
40291
40798
  }
40292
40799
  if (price === null) {
40293
40800
  error(`Token not found: ${token}`);
40294
- if (opts.json) output({ token: token.toUpperCase(), chain: chain2, priceUsd: null, error: "Token not found" }, true);
40801
+ if (opts.json)
40802
+ output(
40803
+ { token: token.toUpperCase(), chain: chain2, priceUsd: null, error: "Token not found" },
40804
+ true
40805
+ );
40295
40806
  process.exitCode = 1;
40296
40807
  return;
40297
40808
  }
40298
40809
  if (opts.history !== void 0) {
40810
+ if (!tokenInfo) {
40811
+ error(`Price history not available for "${token}" \u2014 token not locally resolved. Use an asset ID (e.g. c60) or contract address.`);
40812
+ process.exitCode = 1;
40813
+ return;
40814
+ }
40299
40815
  const period = typeof opts.history === "string" ? opts.history : "week";
40300
40816
  const chainInfo = getChainById(chain2);
40301
40817
  if (!chainInfo) throw new Error(`Unknown chain: ${chain2}`);
40302
40818
  const isNative = !tokenInfo.address || tokenInfo.address === NATIVE_TOKEN_ADDRESS || tokenInfo.address === SOLANA_NATIVE_ADDRESS;
40303
40819
  const assetId = isNative ? `c${chainInfo.coinId}` : `c${chainInfo.coinId}_t${tokenInfo.address}`;
40304
- const chartData = await client.gateway(
40305
- "GET",
40306
- `/v1/market/charts/${assetId}`,
40307
- void 0,
40308
- `currency=USD&period=${period}`
40820
+ const chartData = await client.gateway("GET", `/v1/market/charts/${assetId}`, void 0, `currency=USD&period=${period}`);
40821
+ output(
40822
+ { token: token.toUpperCase(), chain: chain2, priceUsd: price, history: chartData.prices },
40823
+ opts.json
40309
40824
  );
40310
- output({ token: token.toUpperCase(), chain: chain2, priceUsd: price, history: chartData.prices }, opts.json);
40311
40825
  return;
40312
40826
  }
40313
40827
  if (opts.json) {
@@ -40331,7 +40845,9 @@ function registerQueryCommand(program3) {
40331
40845
  if (/^\d+$/.test(trimmed)) return trimmed;
40332
40846
  const chain2 = getChainById(trimmed);
40333
40847
  if (chain2) return String(chain2.coinId);
40334
- throw new Error(`Unknown network "${trimmed}". Use a supported chain name or numeric coin ID.`);
40848
+ throw new Error(
40849
+ `Unknown network "${trimmed}". Use a supported chain name or numeric coin ID.`
40850
+ );
40335
40851
  }).join(",");
40336
40852
  }
40337
40853
  const result = await swapClient.searchTokens({
@@ -40348,6 +40864,18 @@ function registerQueryCommand(program3) {
40348
40864
  try {
40349
40865
  const client = await getClient();
40350
40866
  const service = new TransactionService(client);
40867
+ if (opts.chain && !getChainById(opts.chain)) {
40868
+ error(`Unknown chain: ${opts.chain}. Run 'twak chains' to list supported chains.`);
40869
+ if (opts.json) output({ error: `Unknown chain: ${opts.chain}` }, true);
40870
+ process.exitCode = 1;
40871
+ return;
40872
+ }
40873
+ if (opts.address && opts.chain && !/^(0x[0-9a-fA-F]{40}|[1-9A-HJ-NP-Za-km-z]{20,}|[A-Za-z0-9]{20,})$/.test(opts.address)) {
40874
+ error(`Invalid address: "${opts.address}"`);
40875
+ if (opts.json) output({ error: `Invalid address: "${opts.address}"` }, true);
40876
+ process.exitCode = 1;
40877
+ return;
40878
+ }
40351
40879
  if (!opts.chain) {
40352
40880
  await ensureTrustSDK();
40353
40881
  const password = await resolvePassword(opts);
@@ -40399,7 +40927,9 @@ function registerQueryCommand(program3) {
40399
40927
  const intPart = big / divisor;
40400
40928
  const scale = 10000n;
40401
40929
  const fracScaled = big % divisor * scale / divisor;
40402
- const usdValue = price !== null && (intPart > 0n || fracScaled > 0n) && intPart <= BigInt(Number.MAX_SAFE_INTEGER) ? parseFloat(((Number(intPart) + Number(fracScaled) / 1e4) * price).toFixed(4)) : void 0;
40930
+ const usdValue = price !== null && (intPart > 0n || fracScaled > 0n) && intPart <= BigInt(Number.MAX_SAFE_INTEGER) ? parseFloat(
40931
+ ((Number(intPart) + Number(fracScaled) / 1e4) * price).toFixed(4)
40932
+ ) : void 0;
40403
40933
  return { ...tx, usdValue };
40404
40934
  } catch {
40405
40935
  return tx;
@@ -40424,6 +40954,12 @@ function registerQueryCommand(program3) {
40424
40954
  });
40425
40955
  program3.command("tx <hash>").description("Get transaction details by hash (on-chain RPC lookup)").requiredOption("--chain <name>", "Chain name").option("--address <address>", "Wallet address (auto-detected from wallet if omitted)").option("--password <pw>", "Wallet password (for auto-detecting address)").option("--json", "JSON output").action(async (hash, opts) => {
40426
40956
  try {
40957
+ if (!getChainById(opts.chain)) {
40958
+ error(`Unknown chain: ${opts.chain}. Run 'twak chains' to list supported chains.`);
40959
+ if (opts.json) output({ error: `Unknown chain: ${opts.chain}` }, true);
40960
+ process.exitCode = 1;
40961
+ return;
40962
+ }
40427
40963
  let address = opts.address;
40428
40964
  if (!address) {
40429
40965
  try {
@@ -40438,11 +40974,14 @@ function registerQueryCommand(program3) {
40438
40974
  await ensureTrustSDK();
40439
40975
  const blockchainClient = new BlockchainClient();
40440
40976
  const status = await blockchainClient.findTransactionStatus(opts.chain, address, hash);
40441
- output({
40442
- hash,
40443
- chain: opts.chain,
40444
- ...status
40445
- }, opts.json);
40977
+ output(
40978
+ {
40979
+ hash,
40980
+ chain: opts.chain,
40981
+ ...status
40982
+ },
40983
+ opts.json
40984
+ );
40446
40985
  } else {
40447
40986
  const client = await getClient();
40448
40987
  const service = new TransactionService(client);
@@ -40456,11 +40995,37 @@ function registerQueryCommand(program3) {
40456
40995
  program3.command("chains").description("List supported chains").option("--json", "JSON output").action(async (opts) => {
40457
40996
  const chainEntries = Object.entries(CHAINS);
40458
40997
  if (opts.json) {
40459
- output(chainEntries.map(([key, chain2]) => ({ key, ...chain2 })), true);
40998
+ output(
40999
+ chainEntries.map(([key, chain2]) => ({
41000
+ key,
41001
+ name: chain2.name,
41002
+ symbol: chain2.symbol,
41003
+ namespace: chain2.namespace,
41004
+ chainId: chain2.id,
41005
+ coinId: chain2.coinId
41006
+ })),
41007
+ true
41008
+ );
40460
41009
  } else {
40461
- for (const [key, chain2] of chainEntries) {
40462
- const idHint = key !== chain2.id ? import_chalk6.default.dim(` (${chain2.id})`) : "";
40463
- console.log(`${key.padEnd(15)} ${chain2.name.padEnd(25)} coinId:${chain2.coinId}${idHint}`);
41010
+ const rows = chainEntries.map(([key, chain2]) => ({
41011
+ key,
41012
+ name: chain2.name,
41013
+ symbol: chain2.symbol,
41014
+ namespace: chain2.namespace
41015
+ }));
41016
+ const keyW = Math.max(...rows.map((r3) => r3.key.length), 3);
41017
+ const nameW = Math.max(...rows.map((r3) => r3.name.length), 4);
41018
+ const symW = Math.max(...rows.map((r3) => r3.symbol.length), 6);
41019
+ const nsW = Math.max(...rows.map((r3) => r3.namespace.length), 9);
41020
+ console.log(
41021
+ import_chalk6.default.bold(
41022
+ `${"KEY".padEnd(keyW)} ${"NAME".padEnd(nameW)} ${"SYMBOL".padEnd(symW)} NAMESPACE`
41023
+ )
41024
+ );
41025
+ for (const r3 of rows) {
41026
+ console.log(
41027
+ `${r3.key.padEnd(keyW)} ${r3.name.padEnd(nameW)} ${r3.symbol.padEnd(symW)} ${r3.namespace.padEnd(nsW)}`
41028
+ );
40464
41029
  }
40465
41030
  }
40466
41031
  });
@@ -40499,7 +41064,11 @@ function registerQueryCommand(program3) {
40499
41064
  const apiClient = await getClient();
40500
41065
  const swapClient = new SwapClient(apiClient);
40501
41066
  const chainKey = opts.chain || Object.entries(CHAINS).find(([, c3]) => c3.coinId === coinId)?.[0] || "";
40502
- const tokens = await swapClient.searchTokens({ query: opts.token, chains: [chainKey], limit: 1 });
41067
+ const tokens = await swapClient.searchTokens({
41068
+ query: opts.token,
41069
+ chains: [chainKey],
41070
+ limit: 1
41071
+ });
40503
41072
  if (tokens.length > 0) {
40504
41073
  decimals = tokens[0].decimals ?? 18;
40505
41074
  symbol = tokens[0].symbol ?? opts.token;
@@ -40514,20 +41083,24 @@ function registerQueryCommand(program3) {
40514
41083
  const div = 10n ** BigInt(decimals);
40515
41084
  return `${big / div}.${(big % div).toString().padStart(decimals, "0").slice(0, 6)}`;
40516
41085
  };
40517
- output({
40518
- address: opts.address,
40519
- chain: chain2.id,
40520
- symbol,
40521
- ...opts.token ? { token: opts.token } : {},
40522
- available: fmt(balance.available),
40523
- ...balance.staked ? { staked: fmt(balance.staked) } : {},
40524
- ...balance.pending ? { pending: fmt(balance.pending) } : {},
40525
- total: fmt(balance.total),
40526
- raw: balance
40527
- }, opts.json);
41086
+ output(
41087
+ {
41088
+ address: opts.address,
41089
+ chain: chain2.id,
41090
+ symbol,
41091
+ ...opts.token ? { token: opts.token } : {},
41092
+ available: fmt(balance.available),
41093
+ ...balance.staked ? { staked: fmt(balance.staked) } : {},
41094
+ ...balance.pending ? { pending: fmt(balance.pending) } : {},
41095
+ total: fmt(balance.total),
41096
+ raw: balance
41097
+ },
41098
+ opts.json
41099
+ );
40528
41100
  } catch (err) {
40529
41101
  handleError(err, opts.json);
40530
41102
  }
41103
+ safeExit();
40531
41104
  });
40532
41105
  program3.command("asset <assetId>").description("Get asset info").option("--json", "JSON output").action(async (assetId, opts) => {
40533
41106
  try {
@@ -40541,6 +41114,12 @@ function registerQueryCommand(program3) {
40541
41114
  });
40542
41115
  program3.command("validate").description("Validate an address").requiredOption("--address <address>", "Address to validate").option("--chain <key>", "Chain key (e.g., ethereum, solana)").option("--asset-id <id>", "Asset ID (alternative to --chain)").option("--json", "JSON output").action(async (opts) => {
40543
41116
  try {
41117
+ if (!opts.chain && !opts.assetId) {
41118
+ error("Provide --chain <key> or --asset-id <id> to validate an address");
41119
+ if (opts.json) output({ error: "--chain or --asset-id required" }, true);
41120
+ process.exitCode = 1;
41121
+ return;
41122
+ }
40544
41123
  let assetId = opts.assetId;
40545
41124
  if (!assetId && opts.chain) {
40546
41125
  const chain2 = getChainById(opts.chain);
@@ -40560,7 +41139,12 @@ function registerQueryCommand(program3) {
40560
41139
  if (!isValid2) {
40561
41140
  const chainInfo = getChainByCoinId(coinId);
40562
41141
  const label = chainInfo?.name ?? `coin ${coinId}`;
40563
- const result = { status: "error", valid: false, chain: chainInfo?.id, message: `Invalid address format for ${label}` };
41142
+ const result = {
41143
+ status: "error",
41144
+ valid: false,
41145
+ chain: chainInfo?.id,
41146
+ message: `Invalid address format for ${label}`
41147
+ };
40564
41148
  output(result, opts.json);
40565
41149
  return;
40566
41150
  }
@@ -40582,7 +41166,10 @@ function registerQueryCommand(program3) {
40582
41166
  handleError(err, opts.json);
40583
41167
  }
40584
41168
  });
40585
- program3.command("trending").description("Get trending tokens by market activity or category").option("--category <id>", "Category: ai, rwa, memes, defi, dex, bnb, eth, sol, pumpfun, bonk, launchpad, launchpool, layer1").option("--sort <by>", "Sort: price_change, market_cap, volume (default: price_change)").option("--limit <n>", "Max results", "20").option("--json", "JSON output").action(async (opts) => {
41169
+ program3.command("trending").description("Get trending tokens by market activity or category").option(
41170
+ "--category <id>",
41171
+ "Category: ai, rwa, memes, defi, dex, bnb, eth, sol, pumpfun, bonk, launchpad, launchpool, layer1"
41172
+ ).option("--sort <by>", "Sort: price_change, market_cap, volume (default: price_change)").option("--limit <n>", "Max results", "20").option("--json", "JSON output").action(async (opts) => {
40586
41173
  try {
40587
41174
  const client = await getClient();
40588
41175
  const service = new SecurityService(client);
@@ -40606,15 +41193,13 @@ function registerQueryCommand(program3) {
40606
41193
  launchpool: "binance-launchpool",
40607
41194
  layer1: "layer_1"
40608
41195
  };
40609
- let tokens;
40610
- if (opts.category) {
40611
- const categoryId = categoryMap[opts.category] ?? opts.category;
40612
- const sort = opts.sort ? sortMap[opts.sort] ?? opts.sort : "percent_change_24h";
40613
- tokens = await service.getAssetListings({ categoryId, sort, limit: parseInt(opts.limit, 10) });
40614
- } else {
40615
- const sort = opts.sort ? sortMap[opts.sort] ?? opts.sort : "percent_change_24h";
40616
- tokens = await service.getTrendingTokens(parseInt(opts.limit, 10), sort);
40617
- }
41196
+ const categoryId = opts.category ? categoryMap[opts.category] ?? opts.category : void 0;
41197
+ const sort = opts.sort ? sortMap[opts.sort] ?? opts.sort : "percent_change_24h";
41198
+ const tokens = await service.getAssetListings({
41199
+ categoryId,
41200
+ sort,
41201
+ limit: parseInt(opts.limit, 10)
41202
+ });
40618
41203
  const seen = /* @__PURE__ */ new Map();
40619
41204
  for (const t of tokens) {
40620
41205
  const key = t.symbol.toUpperCase();
@@ -40630,14 +41215,19 @@ function registerQueryCommand(program3) {
40630
41215
  for (const t of deduped) {
40631
41216
  const price = t.priceUsd != null ? `$${t.priceUsd.toFixed(4)}` : "N/A";
40632
41217
  const change = t.priceChange24h != null ? `${t.priceChange24h > 0 ? "+" : ""}${t.priceChange24h.toFixed(2)}%` : "";
40633
- console.log(`${t.symbol.padEnd(10)} ${t.name.padEnd(25)} ${price.padStart(12)} ${change.padStart(8)}`);
41218
+ console.log(
41219
+ `${t.symbol.padEnd(10)} ${t.name.padEnd(25)} ${price.padStart(12)} ${change.padStart(8)}`
41220
+ );
40634
41221
  }
40635
41222
  }
40636
41223
  } catch (err) {
40637
41224
  handleError(err, opts.json);
40638
41225
  }
40639
41226
  });
40640
- program3.command("dapps").description("Browse DeFi protocols and DApps (data from DeFi Llama)").option("--category <name>", "Filter by category (e.g., Lending, Dexs, Liquid Staking, Bridge, Yield)").option("--search <query>", "Search by name, symbol, or category").option("--categories", "List available categories").option("--limit <n>", "Max results", "20").option("--json", "JSON output").action(async (opts) => {
41227
+ program3.command("dapps").description("Browse DeFi protocols and DApps (data from DeFi Llama)").option(
41228
+ "--category <name>",
41229
+ "Filter by category (e.g., Lending, Dexs, Liquid Staking, Bridge, Yield)"
41230
+ ).option("--search <query>", "Search by name, symbol, or category").option("--categories", "List available categories").option("--limit <n>", "Max results", "20").option("--json", "JSON output").action(async (opts) => {
40641
41231
  try {
40642
41232
  const { DappsService: DappsService2 } = await Promise.resolve().then(() => (init_dapps_service(), dapps_service_exports));
40643
41233
  const service = new DappsService2();
@@ -40657,12 +41247,16 @@ function registerQueryCommand(program3) {
40657
41247
  if (dapps.length === 0) {
40658
41248
  console.log("No DApps found.");
40659
41249
  } else {
40660
- console.log("Name".padEnd(25) + "Category".padEnd(20) + "TVL".padStart(15) + " Chains");
41250
+ console.log(
41251
+ "Name".padEnd(25) + "Category".padEnd(20) + "TVL".padStart(15) + " Chains"
41252
+ );
40661
41253
  console.log("\u2500".repeat(80));
40662
41254
  for (const d3 of dapps) {
40663
41255
  const tvl = d3.tvl >= 1e9 ? `$${(d3.tvl / 1e9).toFixed(2)}B` : d3.tvl >= 1e6 ? `$${(d3.tvl / 1e6).toFixed(0)}M` : `$${(d3.tvl / 1e3).toFixed(0)}K`;
40664
41256
  const chains = d3.chains.slice(0, 5).join(", ") + (d3.chains.length > 5 ? ` +${d3.chains.length - 5}` : "");
40665
- console.log(`${d3.name.slice(0, 24).padEnd(25)}${d3.category.slice(0, 19).padEnd(20)}${tvl.padStart(15)} ${chains}`);
41257
+ console.log(
41258
+ `${d3.name.slice(0, 24).padEnd(25)}${d3.category.slice(0, 19).padEnd(20)}${tvl.padStart(15)} ${chains}`
41259
+ );
40666
41260
  }
40667
41261
  }
40668
41262
  }
@@ -45069,7 +45663,9 @@ var SwapActionProvider = class extends ActionProvider {
45069
45663
  return { success: false, error: `Slippage must be greater than 0% and at most 50%. Got: ${input.slippage}` };
45070
45664
  }
45071
45665
  const fromTokenInfo = resolveToken(input.fromToken, input.fromChain);
45666
+ if (!fromTokenInfo) return { success: false, error: `Unknown token: ${input.fromToken} on ${input.fromChain}` };
45072
45667
  const toTokenInfo = resolveToken(input.toToken, input.toChain);
45668
+ if (!toTokenInfo) return { success: false, error: `Unknown token: ${input.toToken} on ${input.toChain}` };
45073
45669
  const parsedAmount = parseAmount(input.amount, fromTokenInfo.decimals);
45074
45670
  const address = await signer.getAddress(input.fromChain);
45075
45671
  const quote = await this.swapClient.getQuote({
@@ -45122,7 +45718,9 @@ var SwapActionProvider = class extends ActionProvider {
45122
45718
  invoke: async (_ctx, input) => {
45123
45719
  if (!this.swapClient) return { success: false, error: "Swap client required" };
45124
45720
  const fromTokenInfo = resolveToken(input.fromToken, input.fromChain);
45721
+ if (!fromTokenInfo) return { success: false, error: `Unknown token: ${input.fromToken} on ${input.fromChain}` };
45125
45722
  const toTokenInfo = resolveToken(input.toToken, input.toChain);
45723
+ if (!toTokenInfo) return { success: false, error: `Unknown token: ${input.toToken} on ${input.toChain}` };
45126
45724
  const parsedAmount = parseAmount(input.amount, fromTokenInfo.decimals);
45127
45725
  const quote = await this.swapClient.getQuote({
45128
45726
  fromChain: input.fromChain,
@@ -45159,6 +45757,7 @@ var SwapActionProvider = class extends ActionProvider {
45159
45757
  invoke: async (_ctx, input) => {
45160
45758
  if (!this.swapClient) return { success: false, error: "Swap client required" };
45161
45759
  const tokenInfo = resolveToken(input.token, input.chain);
45760
+ if (!tokenInfo) return { success: false, error: `Unknown token: ${input.token} on ${input.chain}` };
45162
45761
  const price = await this.swapClient.getTokenPrice(input.chain, tokenInfo.address);
45163
45762
  if (price === null) return { success: false, error: `Price not found for ${input.token}` };
45164
45763
  return { success: true, token: input.token.toUpperCase(), chain: input.chain, priceUsd: price };
@@ -45594,7 +46193,7 @@ var MarketActionProvider = class extends ActionProvider {
45594
46193
  limit: external_exports.coerce.number().optional().default(20).describe("Number of tokens to return (default 20)")
45595
46194
  }),
45596
46195
  invoke: async (ctx, input) => {
45597
- const tokens = await this.getService(ctx).getTrendingTokens(input.limit);
46196
+ const tokens = await this.getService(ctx).getAssetListings({ limit: input.limit });
45598
46197
  return { success: true, count: tokens.length, tokens };
45599
46198
  }
45600
46199
  };
@@ -45628,6 +46227,173 @@ var MarketActionProvider = class extends ActionProvider {
45628
46227
  };
45629
46228
  }
45630
46229
  };
46230
+ var AutomateActionProvider = class extends ActionProvider {
46231
+ constructor(automationStore, swapClient, walletProvider, blockchainClient) {
46232
+ super();
46233
+ this.automationStore = automationStore;
46234
+ this.swapClient = swapClient;
46235
+ this.walletProvider = walletProvider;
46236
+ this.blockchainClient = blockchainClient;
46237
+ }
46238
+ name = "automate";
46239
+ description = "DCA and limit-order automation for recurring and conditional swaps";
46240
+ getActions() {
46241
+ return [
46242
+ this.createAutomation(),
46243
+ this.listAutomations(),
46244
+ this.deleteAutomation(),
46245
+ this.runAutomationNow()
46246
+ ];
46247
+ }
46248
+ createAutomation() {
46249
+ return {
46250
+ name: "create_automation",
46251
+ description: "Create a DCA (recurring buy) or limit order (buy/sell when price condition is met)",
46252
+ schema: external_exports.object({
46253
+ type: external_exports.enum(["dca", "limit"]).describe("Automation type: dca (recurring) or limit (price trigger)"),
46254
+ fromToken: external_exports.string().describe("Source token symbol or address"),
46255
+ toToken: external_exports.string().describe("Destination token symbol or address"),
46256
+ chain: external_exports.string().default("ethereum").describe("Chain name"),
46257
+ amount: external_exports.string().describe("Amount of fromToken per execution"),
46258
+ intervalMs: external_exports.coerce.number().optional().describe("Interval in milliseconds for DCA (e.g., 86400000 for daily)"),
46259
+ targetPrice: external_exports.coerce.number().optional().describe("Target price in USD for limit orders"),
46260
+ condition: external_exports.enum(["above", "below"]).optional().describe("Price condition for limit orders")
46261
+ }),
46262
+ invoke: async (_ctx, input) => {
46263
+ if (input.type === "dca" && !input.intervalMs) {
46264
+ return { success: false, error: "DCA automations require intervalMs" };
46265
+ }
46266
+ if (input.type === "limit" && (input.targetPrice == null || !input.condition)) {
46267
+ return { success: false, error: "Limit orders require targetPrice and condition" };
46268
+ }
46269
+ const record2 = await this.automationStore.create({
46270
+ type: input.type,
46271
+ fromToken: input.fromToken,
46272
+ toToken: input.toToken,
46273
+ chain: input.chain,
46274
+ amount: input.amount,
46275
+ intervalMs: input.intervalMs,
46276
+ targetPrice: input.targetPrice,
46277
+ condition: input.condition
46278
+ });
46279
+ const summary = input.type === "dca" ? `DCA: ${input.amount} ${input.fromToken} \u2192 ${input.toToken} every ${Math.round((input.intervalMs || 0) / 36e5)}h` : `Limit: swap ${input.amount} ${input.fromToken} \u2192 ${input.toToken} when price ${input.condition} $${input.targetPrice}`;
46280
+ return { success: true, id: record2.id, summary, record: record2 };
46281
+ }
46282
+ };
46283
+ }
46284
+ listAutomations() {
46285
+ return {
46286
+ name: "list_automations",
46287
+ description: "List all active automation rules (DCA and limit orders)",
46288
+ schema: external_exports.object({
46289
+ activeOnly: external_exports.boolean().optional().default(true).describe("Only show active automations")
46290
+ }),
46291
+ invoke: async (_ctx, input) => {
46292
+ const all = await this.automationStore.list();
46293
+ const automations = input.activeOnly ? all.filter((a3) => a3.active) : all;
46294
+ return { success: true, count: automations.length, automations };
46295
+ }
46296
+ };
46297
+ }
46298
+ deleteAutomation() {
46299
+ return {
46300
+ name: "delete_automation",
46301
+ description: "Delete (deactivate) an automation rule",
46302
+ schema: external_exports.object({
46303
+ id: external_exports.string().describe("Automation ID to delete")
46304
+ }),
46305
+ invoke: async (_ctx, input) => {
46306
+ const deleted = await this.automationStore.delete(input.id);
46307
+ return deleted ? { success: true, message: `Automation ${input.id} deleted` } : { success: false, error: `Automation ${input.id} not found` };
46308
+ }
46309
+ };
46310
+ }
46311
+ async sendSwapTx(txData, chainConfig, signer) {
46312
+ if (chainConfig?.namespace === "solana" && txData.transaction.solanaTx) {
46313
+ return signer.sendTransaction({
46314
+ chainId: `solana:${chainConfig.chainId}`,
46315
+ to: "",
46316
+ data: txData.transaction.solanaTx.serializedMessage
46317
+ });
46318
+ } else if (txData.transaction.evmTx) {
46319
+ return signer.sendTransaction({
46320
+ chainId: `eip155:${chainConfig?.chainId || "1"}`,
46321
+ to: txData.transaction.evmTx.to,
46322
+ data: txData.transaction.evmTx.data,
46323
+ value: txData.transaction.evmTx.value || "0x0"
46324
+ });
46325
+ }
46326
+ throw new Error("No transaction data returned for this swap step");
46327
+ }
46328
+ async pollConfirmation(chainKey, address, hash, timeoutMs) {
46329
+ if (!this.blockchainClient) return;
46330
+ const deadline = Date.now() + timeoutMs;
46331
+ while (Date.now() < deadline) {
46332
+ await new Promise((r3) => setTimeout(r3, 2e3));
46333
+ try {
46334
+ const status = await this.blockchainClient.findTransactionStatus(chainKey, address, hash);
46335
+ if (status.confirmed) return;
46336
+ if (status.failed) throw new Error(`Transaction failed: ${status.error || "unknown error"}`);
46337
+ } catch (e2) {
46338
+ const err = e2 instanceof Error ? e2 : new Error(String(e2));
46339
+ if (err.message.startsWith("Transaction failed")) throw err;
46340
+ }
46341
+ }
46342
+ throw new Error(`Transaction ${hash} was not confirmed within ${Math.round(timeoutMs / 1e3)}s`);
46343
+ }
46344
+ runAutomationNow() {
46345
+ return {
46346
+ name: "run_automation_now",
46347
+ description: "Manually execute an automation immediately (runs the swap now regardless of schedule)",
46348
+ schema: external_exports.object({
46349
+ id: external_exports.string().describe("Automation ID to run")
46350
+ }),
46351
+ invoke: async (_ctx, input) => {
46352
+ if (!this.walletProvider || !this.swapClient) {
46353
+ return { success: false, error: "Agent wallet and swap client required to execute automation" };
46354
+ }
46355
+ const record2 = await this.automationStore.getById(input.id);
46356
+ if (!record2) return { success: false, error: `Automation ${input.id} not found` };
46357
+ if (!record2.active) return { success: false, error: `Automation ${input.id} is inactive` };
46358
+ const fromTokenInfo = resolveToken(record2.fromToken, record2.chain);
46359
+ if (!fromTokenInfo) return { success: false, error: `Unknown token: ${record2.fromToken} on ${record2.chain}` };
46360
+ const toTokenInfo = resolveToken(record2.toToken, record2.chain);
46361
+ if (!toTokenInfo) return { success: false, error: `Unknown token: ${record2.toToken} on ${record2.chain}` };
46362
+ const parsedAmount = parseAmount(record2.amount, fromTokenInfo.decimals);
46363
+ const address = await this.walletProvider.getAddress(record2.chain);
46364
+ const quote = await this.swapClient.getQuote({
46365
+ fromChain: record2.chain,
46366
+ fromToken: fromTokenInfo.address,
46367
+ toChain: record2.chain,
46368
+ toToken: toTokenInfo.address,
46369
+ amount: parsedAmount,
46370
+ walletAddress: address
46371
+ });
46372
+ if (quote.routes.length === 0) {
46373
+ return { success: false, error: "No swap routes found" };
46374
+ }
46375
+ const route = quote.routes[0];
46376
+ const chainConfig = getChainById(record2.chain);
46377
+ const transactions = await this.swapClient.getSwapTransactions(route);
46378
+ let txHash = "";
46379
+ for (const txData of transactions) {
46380
+ txHash = await this.sendSwapTx(txData, chainConfig, this.walletProvider);
46381
+ const isApproval = txData.transaction.type === "approval" || txData.transaction.type === "revokeApproval";
46382
+ await this.pollConfirmation(record2.chain, address, txHash, isApproval ? 6e4 : 12e4);
46383
+ }
46384
+ await this.automationStore.update(record2.id, { lastRunAt: (/* @__PURE__ */ new Date()).toISOString() });
46385
+ const explorerUrl = getExplorerTxUrl(record2.chain, txHash);
46386
+ return {
46387
+ success: true,
46388
+ txHash,
46389
+ automationId: record2.id,
46390
+ swapped: `${record2.amount} ${record2.fromToken} \u2192 ${record2.toToken}`,
46391
+ explorer: explorerUrl || void 0
46392
+ };
46393
+ }
46394
+ };
46395
+ }
46396
+ };
45631
46397
 
45632
46398
  // src/commands/serve.ts
45633
46399
  init_dist();
@@ -52735,7 +53501,7 @@ var Protocol = class {
52735
53501
  return;
52736
53502
  }
52737
53503
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
52738
- await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
53504
+ await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
52739
53505
  options?.signal?.throwIfAborted();
52740
53506
  }
52741
53507
  } catch (error3) {
@@ -52752,7 +53518,7 @@ var Protocol = class {
52752
53518
  */
52753
53519
  request(request, resultSchema, options) {
52754
53520
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
52755
- return new Promise((resolve3, reject) => {
53521
+ return new Promise((resolve2, reject) => {
52756
53522
  const earlyReject = (error3) => {
52757
53523
  reject(error3);
52758
53524
  };
@@ -52830,7 +53596,7 @@ var Protocol = class {
52830
53596
  if (!parseResult.success) {
52831
53597
  reject(parseResult.error);
52832
53598
  } else {
52833
- resolve3(parseResult.data);
53599
+ resolve2(parseResult.data);
52834
53600
  }
52835
53601
  } catch (error3) {
52836
53602
  reject(error3);
@@ -53091,12 +53857,12 @@ var Protocol = class {
53091
53857
  }
53092
53858
  } catch {
53093
53859
  }
53094
- return new Promise((resolve3, reject) => {
53860
+ return new Promise((resolve2, reject) => {
53095
53861
  if (signal.aborted) {
53096
53862
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
53097
53863
  return;
53098
53864
  }
53099
- const timeoutId = setTimeout(resolve3, interval);
53865
+ const timeoutId = setTimeout(resolve2, interval);
53100
53866
  signal.addEventListener("abort", () => {
53101
53867
  clearTimeout(timeoutId);
53102
53868
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -53966,12 +54732,12 @@ var StdioServerTransport = class {
53966
54732
  this.onclose?.();
53967
54733
  }
53968
54734
  send(message) {
53969
- return new Promise((resolve3) => {
54735
+ return new Promise((resolve2) => {
53970
54736
  const json = serializeMessage(message);
53971
54737
  if (this._stdout.write(json)) {
53972
- resolve3();
54738
+ resolve2();
53973
54739
  } else {
53974
- this._stdout.once("drain", resolve3);
54740
+ this._stdout.once("drain", resolve2);
53975
54741
  }
53976
54742
  });
53977
54743
  }
@@ -54282,7 +55048,7 @@ var TwakRestServer = class {
54282
55048
  };
54283
55049
  }
54284
55050
  start() {
54285
- return new Promise((resolve3, reject) => {
55051
+ return new Promise((resolve2, reject) => {
54286
55052
  const server = (0, import_http.createServer)((req, res) => this.handleRequest(req, res));
54287
55053
  this.httpServer = server;
54288
55054
  server.on("error", (err) => {
@@ -54293,7 +55059,9 @@ var TwakRestServer = class {
54293
55059
  }
54294
55060
  });
54295
55061
  server.listen(this.config.port, this.config.host, () => {
54296
- console.error(`TWAK REST server listening on http://${this.config.host}:${this.config.port}`);
55062
+ console.error(
55063
+ `TWAK REST server listening on http://${this.config.host}:${this.config.port}`
55064
+ );
54297
55065
  console.error("Endpoints: POST /actions/:name");
54298
55066
  if (this.config.apiKey) {
54299
55067
  console.error("API key authentication enabled (use Authorization: Bearer <HMAC_SECRET>)");
@@ -54301,28 +55069,38 @@ var TwakRestServer = class {
54301
55069
  console.error("WARNING: No API key configured \u2014 REST server is unauthenticated");
54302
55070
  }
54303
55071
  if (this.config.paymentRequired) {
54304
- console.error(`x402 payment required: ${this.config.paymentAmount} ${this.config.paymentAsset} on ${this.config.paymentChainId}`);
55072
+ console.error(
55073
+ `x402 payment required: ${this.config.paymentAmount} ${this.config.paymentAsset} on ${this.config.paymentChainId}`
55074
+ );
54305
55075
  }
54306
- resolve3();
55076
+ resolve2();
54307
55077
  });
54308
55078
  });
54309
55079
  }
54310
55080
  stop() {
54311
- return new Promise((resolve3) => {
55081
+ return new Promise((resolve2) => {
54312
55082
  if (this.httpServer?.listening) {
54313
55083
  this.httpServer.close(() => {
54314
55084
  this.httpServer = null;
54315
- resolve3();
55085
+ resolve2();
54316
55086
  });
54317
55087
  } else {
54318
55088
  this.httpServer = null;
54319
- resolve3();
55089
+ resolve2();
54320
55090
  }
54321
55091
  });
54322
55092
  }
54323
55093
  async handleRequest(req, res) {
54324
55094
  res.setHeader("Content-Type", "application/json");
54325
- res.setHeader("Access-Control-Allow-Origin", "*");
55095
+ const origin = req.headers.origin || "";
55096
+ try {
55097
+ const parsed = new URL(origin);
55098
+ if (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "::1") {
55099
+ res.setHeader("Access-Control-Allow-Origin", origin);
55100
+ res.setHeader("Vary", "Origin");
55101
+ }
55102
+ } catch {
55103
+ }
54326
55104
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
54327
55105
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Payment, Authorization");
54328
55106
  if (req.method === "OPTIONS") {
@@ -54340,16 +55118,20 @@ var TwakRestServer = class {
54340
55118
  const tokenValid = token && token.length === expected.length && (0, import_crypto5.timingSafeEqual)(Buffer.from(token), Buffer.from(expected));
54341
55119
  if (!tokenValid) {
54342
55120
  res.writeHead(401);
54343
- res.end(JSON.stringify({ error: "Unauthorized \u2014 provide Authorization: Bearer <HMAC_SECRET>" }));
55121
+ res.end(
55122
+ JSON.stringify({ error: "Unauthorized \u2014 provide Authorization: Bearer <HMAC_SECRET>" })
55123
+ );
54344
55124
  return;
54345
55125
  }
54346
55126
  }
54347
55127
  if (req.method === "GET" && pathname === "/actions") {
54348
55128
  const actions = this.registry.getActions();
54349
55129
  res.writeHead(200);
54350
- res.end(JSON.stringify({
54351
- actions: actions.map((a3) => ({ name: a3.name, description: a3.description }))
54352
- }));
55130
+ res.end(
55131
+ JSON.stringify({
55132
+ actions: actions.map((a3) => ({ name: a3.name, description: a3.description }))
55133
+ })
55134
+ );
54353
55135
  return;
54354
55136
  }
54355
55137
  const match = pathname.match(/^\/actions\/([^/]+)$/);
@@ -54362,31 +55144,44 @@ var TwakRestServer = class {
54362
55144
  if (this.config.paymentRequired) {
54363
55145
  const paymentHeader = req.headers["x-payment"];
54364
55146
  if (!paymentHeader) {
54365
- res.setHeader("WWW-Authenticate", `x402 realm="twak", asset="${this.config.paymentAsset}", amount="${this.config.paymentAmount}", chain="${this.config.paymentChainId}"`);
55147
+ res.setHeader(
55148
+ "WWW-Authenticate",
55149
+ `x402 realm="twak", asset="${this.config.paymentAsset}", amount="${this.config.paymentAmount}", chain="${this.config.paymentChainId}"`
55150
+ );
54366
55151
  res.writeHead(402);
54367
- res.end(JSON.stringify({
54368
- error: "payment_required",
54369
- message: `This endpoint requires payment of ${this.config.paymentAmount} ${this.config.paymentAsset}`,
54370
- asset: this.config.paymentAsset,
54371
- amount: this.config.paymentAmount,
54372
- chainId: this.config.paymentChainId,
54373
- recipient: this.config.paymentRecipient || void 0
54374
- }));
55152
+ res.end(
55153
+ JSON.stringify({
55154
+ error: "payment_required",
55155
+ message: `This endpoint requires payment of ${this.config.paymentAmount} ${this.config.paymentAsset}`,
55156
+ asset: this.config.paymentAsset,
55157
+ amount: this.config.paymentAmount,
55158
+ chainId: this.config.paymentChainId,
55159
+ recipient: this.config.paymentRecipient || void 0
55160
+ })
55161
+ );
54375
55162
  return;
54376
55163
  }
54377
55164
  if (this.config.paymentRecipient) {
54378
- const result = await verifyPayment(paymentHeader, {
54379
- asset: this.config.paymentAsset,
54380
- amount: this.config.paymentAmount,
54381
- chainId: this.config.paymentChainId,
54382
- recipient: this.config.paymentRecipient
54383
- }, this.context.httpClient, this.seenHashes, this.config.blockchainClient);
55165
+ const result = await verifyPayment(
55166
+ paymentHeader,
55167
+ {
55168
+ asset: this.config.paymentAsset,
55169
+ amount: this.config.paymentAmount,
55170
+ chainId: this.config.paymentChainId,
55171
+ recipient: this.config.paymentRecipient
55172
+ },
55173
+ this.context.httpClient,
55174
+ this.seenHashes,
55175
+ this.config.blockchainClient
55176
+ );
54384
55177
  if (!result.valid) {
54385
55178
  res.writeHead(402);
54386
- res.end(JSON.stringify({
54387
- error: "payment_verification_failed",
54388
- message: result.error ?? "Payment could not be verified"
54389
- }));
55179
+ res.end(
55180
+ JSON.stringify({
55181
+ error: "payment_verification_failed",
55182
+ message: result.error ?? "Payment could not be verified"
55183
+ })
55184
+ );
54390
55185
  return;
54391
55186
  }
54392
55187
  }
@@ -54426,6 +55221,7 @@ function registerServeCommand(program3) {
54426
55221
  const client = await getClient();
54427
55222
  const swapClient = new SwapClient(client);
54428
55223
  const alertStore = new AlertStore();
55224
+ const automationStore = new AutomationStore();
54429
55225
  const blockchainClient = new BlockchainClient();
54430
55226
  let agentWallet;
54431
55227
  try {
@@ -54465,6 +55261,7 @@ function registerServeCommand(program3) {
54465
55261
  registry2.register(new AgentWalletActionProvider(blockchainClient));
54466
55262
  registry2.register(new TokenActionProvider(blockchainClient, agentWallet));
54467
55263
  registry2.register(new MarketActionProvider());
55264
+ registry2.register(new AutomateActionProvider(automationStore, swapClient, walletProvider, blockchainClient));
54468
55265
  const context = {
54469
55266
  httpClient: client
54470
55267
  };
@@ -54496,63 +55293,237 @@ function registerServeCommand(program3) {
54496
55293
  });
54497
55294
  }
54498
55295
 
54499
- // src/commands/skill.ts
54500
- var import_fs = require("fs");
54501
- var import_path = require("path");
54502
- var import_os2 = require("os");
54503
- var SKILL_ID = "twak";
54504
- var SKILL_NAME = "TWAK \u2014 Trust Wallet Agent Kit";
54505
- function findSkillMd() {
54506
- const candidates = [
54507
- (0, import_path.resolve)(__dirname, "../../../../skill.md"),
54508
- (0, import_path.resolve)(__dirname, "../../../skill.md"),
54509
- (0, import_path.resolve)(__dirname, "../../skill.md"),
54510
- (0, import_path.resolve)(process.cwd(), "skill.md")
54511
- ];
54512
- for (const p3 of candidates) {
54513
- if ((0, import_fs.existsSync)(p3)) return p3;
55296
+ // src/commands/automate.ts
55297
+ init_dist();
55298
+ init_dist2();
55299
+ function parseInterval(value) {
55300
+ const match = value.match(/^(\d+(?:\.\d+)?)\s*(s|m|h)?$/i);
55301
+ if (!match) return null;
55302
+ const num = parseFloat(match[1]);
55303
+ if (isNaN(num)) return null;
55304
+ const unit = (match[2] || "s").toLowerCase();
55305
+ if (unit === "s") return num * 1e3;
55306
+ if (unit === "m") return num * 6e4;
55307
+ if (unit === "h") return num * 36e5;
55308
+ return num * 1e3;
55309
+ }
55310
+ function formatInterval(ms3) {
55311
+ if (ms3 >= 36e5) return `${ms3 / 36e5}h`;
55312
+ if (ms3 >= 6e4) return `${ms3 / 6e4}m`;
55313
+ return `${ms3 / 1e3}s`;
55314
+ }
55315
+ function formatRelativeTime(diffMs) {
55316
+ const absDiff = Math.abs(diffMs);
55317
+ let value;
55318
+ if (absDiff < 6e4) {
55319
+ value = `${Math.round(absDiff / 1e3)}s`;
55320
+ } else if (absDiff < 36e5) {
55321
+ value = `${Math.round(absDiff / 6e4)}m`;
55322
+ } else {
55323
+ value = `${Math.round(absDiff / 36e5)}h`;
54514
55324
  }
54515
- return null;
55325
+ if (diffMs <= 0) return "overdue";
55326
+ return `in ${value}`;
55327
+ }
55328
+ function formatTimeAgo(isoDate) {
55329
+ const diffMs = Date.now() - new Date(isoDate).getTime();
55330
+ const absDiff = Math.abs(diffMs);
55331
+ if (absDiff < 6e4) return `${Math.round(absDiff / 1e3)}s ago`;
55332
+ if (absDiff < 36e5) return `${Math.round(absDiff / 6e4)}m ago`;
55333
+ return `${Math.round(absDiff / 36e5)}h ago`;
54516
55334
  }
54517
- function registerSkillCommand(program3) {
54518
- const skill = program3.command("skill").description("Manage AI skills for Claude Code and other agents");
54519
- 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) => {
54520
- const skillSrc = findSkillMd();
54521
- if (!skillSrc) {
54522
- error("skill.md not found. Run from the TWAK monorepo root or a published install.");
55335
+ function registerAutomateCommand(program3) {
55336
+ const automate = program3.command("automate").description("Manage DCA and limit order automations");
55337
+ automate.command("add").description("Create a DCA or limit order automation").requiredOption("--from <token>", "Source token (e.g., USDC)").requiredOption("--to <token>", "Destination token (e.g., ETH)").option("--chain <chain>", "Chain", "ethereum").requiredOption("--amount <n>", "Amount of source token per run").option("--interval <duration>", "DCA interval \u2014 e.g. 30s, 5m, 1h (creates DCA automation)").option("--price <usd>", "Target price in USD (creates limit order)").option("--condition <cond>", "Price condition: above or below", "below").option("--max-runs <n>", "Stop after N executions").option("--expires <date>", "Expiry date (ISO 8601, e.g. 2026-04-01)").option("--json", "JSON output").action(async (opts) => {
55338
+ try {
55339
+ if (opts.interval && opts.price) {
55340
+ error("Provide --interval (DCA) or --price (limit order), not both");
55341
+ process.exitCode = 1;
55342
+ return;
55343
+ }
55344
+ if (!opts.interval && !opts.price) {
55345
+ error("Provide --interval (DCA) or --price (limit order)");
55346
+ process.exitCode = 1;
55347
+ return;
55348
+ }
55349
+ const amount = parseFloat(opts.amount);
55350
+ if (isNaN(amount) || amount <= 0) {
55351
+ error("Amount must be a positive number");
55352
+ process.exitCode = 1;
55353
+ return;
55354
+ }
55355
+ if (!isChainSupported(opts.chain)) {
55356
+ error(`Unsupported chain: ${opts.chain}. Run 'twak chains' to list supported chains.`);
55357
+ process.exitCode = 1;
55358
+ return;
55359
+ }
55360
+ const SWAP_SUPPORTED_NAMESPACES = ["eip155", "solana"];
55361
+ const chainInfo = getChainById(opts.chain);
55362
+ if (chainInfo && !SWAP_SUPPORTED_NAMESPACES.includes(chainInfo.namespace)) {
55363
+ error(`${chainInfo.name} automations are not supported. Use an EVM chain or Solana.`);
55364
+ process.exitCode = 1;
55365
+ return;
55366
+ }
55367
+ let intervalMs;
55368
+ if (opts.interval) {
55369
+ const parsed = parseInterval(opts.interval);
55370
+ if (parsed === null || parsed < 5e3) {
55371
+ error("Interval must be at least 5s (e.g. 30s, 5m, 1h)");
55372
+ process.exitCode = 1;
55373
+ return;
55374
+ }
55375
+ intervalMs = parsed;
55376
+ }
55377
+ const maxRuns = opts.maxRuns ? parseInt(opts.maxRuns) : void 0;
55378
+ if (maxRuns !== void 0 && (isNaN(maxRuns) || maxRuns < 1)) {
55379
+ error("--max-runs must be a positive integer");
55380
+ process.exitCode = 1;
55381
+ return;
55382
+ }
55383
+ let expiresAt;
55384
+ if (opts.expires) {
55385
+ const d3 = new Date(opts.expires);
55386
+ if (isNaN(d3.getTime())) {
55387
+ error("--expires must be a valid date (e.g. 2026-04-01)");
55388
+ process.exitCode = 1;
55389
+ return;
55390
+ }
55391
+ expiresAt = d3.toISOString();
55392
+ }
55393
+ const store = new AutomationStore();
55394
+ const record2 = await store.create({
55395
+ type: opts.interval ? "dca" : "limit",
55396
+ fromToken: opts.from,
55397
+ toToken: opts.to,
55398
+ chain: opts.chain,
55399
+ amount: opts.amount,
55400
+ intervalMs,
55401
+ targetPrice: opts.price ? parseFloat(opts.price) : void 0,
55402
+ condition: opts.price ? opts.condition : void 0,
55403
+ maxRuns,
55404
+ runCount: 0,
55405
+ expiresAt
55406
+ });
55407
+ success("Automation created!");
55408
+ if (opts.interval && intervalMs) {
55409
+ info(
55410
+ `DCA: ${opts.amount} ${opts.from} \u2192 ${opts.to} every ${formatInterval(intervalMs)} on ${opts.chain}`
55411
+ );
55412
+ } else {
55413
+ info(
55414
+ `Limit: swap ${opts.amount} ${opts.from} \u2192 ${opts.to} when price ${opts.condition} $${opts.price} on ${opts.chain}`
55415
+ );
55416
+ }
55417
+ output(record2, opts.json);
55418
+ } catch (err) {
55419
+ const msg = err instanceof Error ? err.message : String(err);
55420
+ error(msg);
55421
+ if (opts.json) output({ error: msg }, true);
54523
55422
  process.exitCode = 1;
54524
- return;
54525
55423
  }
54526
- const skillsDir = opts.dir ? (0, import_path.resolve)(opts.dir) : (0, import_path.join)((0, import_os2.homedir)(), ".claude", "skills");
54527
- if (opts.dir) {
54528
- const { relative, sep: sep2 } = await import("path");
54529
- const safeBase = (0, import_path.join)((0, import_os2.homedir)(), ".claude");
54530
- const rel = relative(safeBase, skillsDir);
54531
- const isOutside = rel.startsWith("..") || rel.startsWith(sep2) || (0, import_path.resolve)(opts.dir) === (0, import_path.resolve)("/");
54532
- if (isOutside) {
54533
- error(`Refusing to install outside ~/.claude/ (got ${skillsDir}). Use --dir ~/.claude/skills or omit --dir.`);
54534
- process.exitCode = 1;
55424
+ });
55425
+ automate.command("list").description("List automations").option("--json", "JSON output").action(async (opts) => {
55426
+ try {
55427
+ const store = new AutomationStore();
55428
+ const records = await store.list();
55429
+ if (opts.json) {
55430
+ const enriched = records.map((r3) => {
55431
+ const nextRunAt = r3.type === "dca" && r3.intervalMs ? r3.lastRunAt ? new Date(new Date(r3.lastRunAt).getTime() + r3.intervalMs).toISOString() : (/* @__PURE__ */ new Date()).toISOString() : null;
55432
+ const nextRunIn = nextRunAt ? formatRelativeTime(new Date(nextRunAt).getTime() - Date.now()) : null;
55433
+ return { ...r3, nextRunAt, nextRunIn };
55434
+ });
55435
+ output(enriched, true);
54535
55436
  return;
54536
55437
  }
55438
+ if (records.length === 0) {
55439
+ info("No automations found.");
55440
+ return;
55441
+ }
55442
+ for (const r3 of records) {
55443
+ const tag = r3.id.slice(0, 8);
55444
+ const label = r3.type.toUpperCase();
55445
+ const pair = `${r3.amount} ${r3.fromToken}\u2192${r3.toToken} (${r3.chain})`;
55446
+ if (r3.type === "dca") {
55447
+ const interval = formatInterval(r3.intervalMs || 0);
55448
+ const lastPart = r3.lastRunAt ? `last: ${formatTimeAgo(r3.lastRunAt)}` : "never run";
55449
+ let nextPart;
55450
+ if (!r3.intervalMs) {
55451
+ nextPart = "next: unknown";
55452
+ } else if (!r3.lastRunAt) {
55453
+ nextPart = "next: now";
55454
+ } else {
55455
+ const nextAt = new Date(r3.lastRunAt).getTime() + r3.intervalMs;
55456
+ nextPart = `next: ${formatRelativeTime(nextAt - Date.now())}`;
55457
+ }
55458
+ const statusTag = r3.active ? "" : " [paused]";
55459
+ console.log(
55460
+ `[${tag}] ${label} ${pair} every ${interval} \u2014 ${lastPart}, ${nextPart}${statusTag}`
55461
+ );
55462
+ } else {
55463
+ const cond = `${r3.toToken} ${r3.condition} $${r3.targetPrice}`;
55464
+ const statusTag = r3.active ? "" : " [paused]";
55465
+ console.log(`[${tag}] ${label} ${pair} when ${cond} \u2014 waiting${statusTag}`);
55466
+ }
55467
+ }
55468
+ } catch (err) {
55469
+ const msg = err instanceof Error ? err.message : String(err);
55470
+ error(msg);
55471
+ if (opts.json) output({ error: msg }, true);
55472
+ process.exitCode = 1;
54537
55473
  }
54538
- (0, import_fs.mkdirSync)(skillsDir, { recursive: true });
54539
- const dest = (0, import_path.join)(skillsDir, `${SKILL_ID}.md`);
54540
- (0, import_fs.copyFileSync)(skillSrc, dest);
54541
- success(`Skill installed to ${dest}`);
54542
- info("Claude Code will load it automatically on next startup.");
54543
55474
  });
54544
- skill.command("list").description("List available TWAK skills").option("--json", "JSON output").action((opts) => {
54545
- const skills = [{ id: SKILL_ID, name: SKILL_NAME, description: "Wallets, swaps, transfers, market data, alerts, DCA, on/off-ramp, and MCP server." }];
54546
- output(skills, opts.json);
55475
+ automate.command("delete <id>").description("Delete an automation by ID").option("--json", "JSON output").action(async (id, opts) => {
55476
+ try {
55477
+ const store = new AutomationStore();
55478
+ const deleted = await store.delete(id);
55479
+ if (deleted) {
55480
+ success(`Automation ${id} deleted`);
55481
+ } else {
55482
+ error(`Automation ${id} not found`);
55483
+ process.exitCode = 1;
55484
+ }
55485
+ } catch (err) {
55486
+ const msg = err instanceof Error ? err.message : String(err);
55487
+ error(msg);
55488
+ if (opts.json) output({ error: msg }, true);
55489
+ process.exitCode = 1;
55490
+ }
55491
+ });
55492
+ automate.command("pause <id>").description("Pause an automation").option("--json", "JSON output").action(async (id, opts) => {
55493
+ try {
55494
+ const store = new AutomationStore();
55495
+ const updated = await store.update(id, { active: false });
55496
+ if (updated) {
55497
+ success(`Automation ${id} paused`);
55498
+ output(updated, opts.json);
55499
+ } else {
55500
+ error(`Automation ${id} not found`);
55501
+ process.exitCode = 1;
55502
+ }
55503
+ } catch (err) {
55504
+ const msg = err instanceof Error ? err.message : String(err);
55505
+ error(msg);
55506
+ if (opts.json) output({ error: msg }, true);
55507
+ process.exitCode = 1;
55508
+ }
54547
55509
  });
54548
- skill.command("retrieve").description("Print the full TWAK skill instructions").action(() => {
54549
- const skillSrc = findSkillMd();
54550
- if (!skillSrc) {
54551
- error("skill.md not found.");
55510
+ automate.command("resume <id>").description("Resume a paused automation").option("--json", "JSON output").action(async (id, opts) => {
55511
+ try {
55512
+ const store = new AutomationStore();
55513
+ const updated = await store.update(id, { active: true });
55514
+ if (updated) {
55515
+ success(`Automation ${id} resumed`);
55516
+ output(updated, opts.json);
55517
+ } else {
55518
+ error(`Automation ${id} not found`);
55519
+ process.exitCode = 1;
55520
+ }
55521
+ } catch (err) {
55522
+ const msg = err instanceof Error ? err.message : String(err);
55523
+ error(msg);
55524
+ if (opts.json) output({ error: msg }, true);
54552
55525
  process.exitCode = 1;
54553
- return;
54554
55526
  }
54555
- console.log((0, import_fs.readFileSync)(skillSrc, "utf8"));
54556
55527
  });
54557
55528
  }
54558
55529
 
@@ -54588,7 +55559,7 @@ async function validateX402Url(urlStr) {
54588
55559
  if (parsed.protocol !== "https:") {
54589
55560
  throw new Error(`x402: only https:// URLs are allowed (got ${parsed.protocol}//)`);
54590
55561
  }
54591
- const h4 = parsed.hostname;
55562
+ const h4 = parsed.hostname.replace(/^\[|\]$/g, "");
54592
55563
  if (h4 === "localhost" || isPrivateIp(h4)) {
54593
55564
  throw new Error(`x402: private/loopback addresses are not allowed (${h4})`);
54594
55565
  }
@@ -54597,7 +55568,9 @@ async function validateX402Url(urlStr) {
54597
55568
  const records = await dns.lookup(h4, { all: true });
54598
55569
  for (const record2 of records) {
54599
55570
  if (isPrivateIp(record2.address)) {
54600
- throw new Error(`x402: "${h4}" resolves to a private/loopback address (${record2.address}) \u2014 not allowed`);
55571
+ throw new Error(
55572
+ `x402: "${h4}" resolves to a private/loopback address (${record2.address}) \u2014 not allowed`
55573
+ );
54601
55574
  }
54602
55575
  }
54603
55576
  } catch (err) {
@@ -54606,7 +55579,14 @@ async function validateX402Url(urlStr) {
54606
55579
  }
54607
55580
  function registerX402Command(program3) {
54608
55581
  const x402 = program3.command("x402").description("x402 micropayment protocol \u2014 pay HTTP endpoints that require on-chain payment");
54609
- x402.command("request <url>").description("Make an HTTP request to an x402-gated endpoint, paying automatically if required").option("--method <method>", "HTTP method", "GET").option("--body <json>", "Request body (JSON string)").option("--max-payment <amount>", "Maximum payment amount to auto-approve (default: 0.01)", "0.01").option("--yes", "Skip confirmation prompt and auto-approve payment up to --max-payment").option("--password <password>", "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)").option("--json", "JSON output").action(async (url, opts) => {
55582
+ x402.command("request <url>").description("Make an HTTP request to an x402-gated endpoint, paying automatically if required").option("--method <method>", "HTTP method", "GET").option("--body <json>", "Request body (JSON string)").option(
55583
+ "--max-payment <amount>",
55584
+ "Maximum payment amount to auto-approve (default: 0.01)",
55585
+ "0.01"
55586
+ ).option("--yes", "Skip confirmation prompt and auto-approve payment up to --max-payment").option(
55587
+ "--password <password>",
55588
+ "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
55589
+ ).option("--json", "JSON output").action(async (url, opts) => {
54610
55590
  try {
54611
55591
  await validateX402Url(url);
54612
55592
  await ensureTrustSDK();
@@ -54614,7 +55594,7 @@ function registerX402Command(program3) {
54614
55594
  const provider = await AgentWalletProvider.fromStorage(password);
54615
55595
  const init3 = {
54616
55596
  method: opts.method.toUpperCase(),
54617
- headers: { "Content-Type": "application/json", "Accept": "application/json" },
55597
+ headers: { "Content-Type": "application/json", Accept: "application/json" },
54618
55598
  body: opts.body || void 0
54619
55599
  };
54620
55600
  let resp = await fetch(url, init3);
@@ -54627,7 +55607,9 @@ function registerX402Command(program3) {
54627
55607
  if (!recipient) throw new Error("x402: no recipient address in 402 response");
54628
55608
  const maxPayment = parseFloat(opts.maxPayment);
54629
55609
  if (!Number.isFinite(maxPayment) || maxPayment <= 0) {
54630
- throw new Error(`x402: invalid --max-payment value "${opts.maxPayment}" \u2014 must be a positive number`);
55610
+ throw new Error(
55611
+ `x402: invalid --max-payment value "${opts.maxPayment}" \u2014 must be a positive number`
55612
+ );
54631
55613
  }
54632
55614
  const requestedAmount = parseFloat(amount);
54633
55615
  if (isNaN(requestedAmount) || requestedAmount <= 0) {
@@ -54641,7 +55623,7 @@ function registerX402Command(program3) {
54641
55623
  if (!opts.yes) {
54642
55624
  const readline = await import("readline");
54643
55625
  const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
54644
- const confirmed = await new Promise((resolve3) => {
55626
+ const confirmed = await new Promise((resolve2) => {
54645
55627
  rl.question(
54646
55628
  `
54647
55629
  Pay ${amount} ${asset.toUpperCase()} on ${chainId}
@@ -54649,7 +55631,7 @@ Recipient: ${recipient}
54649
55631
  Confirm? [y/N] `,
54650
55632
  (answer) => {
54651
55633
  rl.close();
54652
- resolve3(answer.toLowerCase() === "y");
55634
+ resolve2(answer.toLowerCase() === "y");
54653
55635
  }
54654
55636
  );
54655
55637
  });
@@ -54677,7 +55659,9 @@ Confirm? [y/N] `,
54677
55659
  }
54678
55660
  const txResult = await bc2.transfer({ assetId, to: recipient, amount, hdWallet });
54679
55661
  info(`Payment sent: ${txResult.hash}`);
54680
- const paymentProof = Buffer.from(JSON.stringify({ txHash: txResult.hash, chainId })).toString("base64");
55662
+ const paymentProof = Buffer.from(
55663
+ JSON.stringify({ txHash: txResult.hash, chainId })
55664
+ ).toString("base64");
54681
55665
  resp = await fetch(url, {
54682
55666
  ...init3,
54683
55667
  headers: { ...init3.headers, "X-Payment": paymentProof }
@@ -54713,12 +55697,12 @@ Confirm? [y/N] `,
54713
55697
  var fs6 = __toESM(require("fs"));
54714
55698
  var os4 = __toESM(require("os"));
54715
55699
  var path4 = __toESM(require("path"));
54716
- var crypto5 = __toESM(require("crypto"));
55700
+ var crypto6 = __toESM(require("crypto"));
54717
55701
 
54718
55702
  // package.json
54719
55703
  var package_default = {
54720
55704
  name: "@trustwallet/cli",
54721
- version: "0.6.11",
55705
+ version: "0.8.0",
54722
55706
  description: "TWAK CLI \u2014 command-line interface for Trust Wallet Agent Kit",
54723
55707
  main: "dist/index.js",
54724
55708
  types: "dist/index.d.ts",
@@ -54734,16 +55718,17 @@ var package_default = {
54734
55718
  prebuild: "rm -rf trust-sdk && cp -r ../../vendor/trust-sdk trust-sdk",
54735
55719
  build: "tsup && tsc --emitDeclarationOnly --outDir dist",
54736
55720
  typecheck: "tsc --noEmit",
55721
+ test: "vitest run",
54737
55722
  lint: "eslint src/",
54738
55723
  clean: "rm -rf dist"
54739
55724
  },
54740
55725
  dependencies: {
54741
55726
  "node-fetch": "2.7.0",
54742
- "qrcode-terminal": "0.12.0"
55727
+ "qrcode-terminal": "0.12.0",
55728
+ "@napi-rs/keyring": "1.2.0"
54743
55729
  },
54744
55730
  optionalDependencies: {
54745
- "@amplitude/analytics-node": "1.5.39",
54746
- keytar: "7.9.0"
55731
+ "@amplitude/analytics-node": "1.5.39"
54747
55732
  },
54748
55733
  devDependencies: {
54749
55734
  boxen: "5.1.2",
@@ -54762,7 +55747,8 @@ var package_default = {
54762
55747
  "@twak/wallet-core": "workspace:*",
54763
55748
  "@twak/walletconnect": "workspace:*",
54764
55749
  tsup: "8.5.1",
54765
- typescript: "5.7.3"
55750
+ typescript: "5.7.3",
55751
+ vitest: "4.1.2"
54766
55752
  },
54767
55753
  publishConfig: {
54768
55754
  access: "public"
@@ -54818,7 +55804,7 @@ function getUserId() {
54818
55804
  if (fs6.existsSync(ID_FILE)) return fs6.readFileSync(ID_FILE, "utf-8").trim();
54819
55805
  } catch (_e6) {
54820
55806
  }
54821
- const id = crypto5.randomUUID();
55807
+ const id = crypto6.randomUUID();
54822
55808
  try {
54823
55809
  fs6.mkdirSync(CONFIG_DIR, { recursive: true });
54824
55810
  fs6.writeFileSync(ID_FILE, id);
@@ -54875,6 +55861,299 @@ function registerTelemetryCommand(program3) {
54875
55861
  });
54876
55862
  }
54877
55863
 
55864
+ // src/commands/watch.ts
55865
+ init_dist2();
55866
+ init_dist();
55867
+ init_dist4();
55868
+ function ts3() {
55869
+ return (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
55870
+ }
55871
+ function parseInterval2(value) {
55872
+ const match = value.match(/^(\d+(?:\.\d+)?)\s*(s|m|h)?$/i);
55873
+ if (!match) return null;
55874
+ const num = parseFloat(match[1]);
55875
+ if (isNaN(num)) return null;
55876
+ const unit = (match[2] || "s").toLowerCase();
55877
+ if (unit === "m") return num * 60;
55878
+ if (unit === "h") return num * 3600;
55879
+ return num;
55880
+ }
55881
+ function registerWatchCommand(program3) {
55882
+ program3.command("watch").description(
55883
+ "Background watcher \u2014 polls prices to trigger alerts and execute DCA/limit automations"
55884
+ ).option(
55885
+ "--interval <duration>",
55886
+ "Poll interval \u2014 e.g. 30s, 5m, 1h, or plain number (seconds)",
55887
+ "60"
55888
+ ).option("--password <password>", "Wallet password for executing swaps").option("--auto-lock <minutes>", "Auto-lock wallet after N minutes of inactivity").option("--dry-run", "Check conditions without executing swaps").option("--json", "JSON output per tick").action(async (opts) => {
55889
+ const intervalSec = parseInterval2(opts.interval);
55890
+ if (intervalSec === null || intervalSec < 5) {
55891
+ error("Interval must be at least 5 seconds (e.g. 30s, 5m, 1h)");
55892
+ process.exitCode = 1;
55893
+ return;
55894
+ }
55895
+ let client;
55896
+ let swapClient;
55897
+ let provider = null;
55898
+ let executor = null;
55899
+ let amberDomains = /* @__PURE__ */ new Map();
55900
+ try {
55901
+ client = await getClient();
55902
+ swapClient = new SwapClient(client);
55903
+ if (!opts.dryRun) {
55904
+ await ensureTrustSDK();
55905
+ const password = await resolvePassword(opts);
55906
+ provider = await AgentWalletProvider.fromStorage(password);
55907
+ if (opts.autoLock) {
55908
+ const minutes = parseInt(opts.autoLock, 10);
55909
+ if (isNaN(minutes) || minutes <= 0) {
55910
+ error("--auto-lock must be a positive number of minutes");
55911
+ process.exitCode = 1;
55912
+ return;
55913
+ }
55914
+ provider.setAutoLock({
55915
+ autoLockMinutes: minutes,
55916
+ passwordResolver: () => resolvePassword(opts)
55917
+ });
55918
+ }
55919
+ amberDomains = await getAmberDomainsByCoinId();
55920
+ executor = new SwapExecutor(swapClient, provider, {
55921
+ amberDomains,
55922
+ onStatus: (msg) => {
55923
+ info(`[${ts3()}] ${msg}`);
55924
+ }
55925
+ });
55926
+ }
55927
+ } catch (err) {
55928
+ error(`Init failed: ${err instanceof Error ? err.message : String(err)}`);
55929
+ process.exitCode = 1;
55930
+ return;
55931
+ }
55932
+ const alertStore = new AlertStore();
55933
+ const automationStore = new AutomationStore();
55934
+ const dryRunState = /* @__PURE__ */ new Map();
55935
+ const getMemState = (id, stored) => {
55936
+ if (!dryRunState.has(id)) {
55937
+ dryRunState.set(id, {
55938
+ lastRunAt: stored.lastRunAt ?? null,
55939
+ runCount: stored.runCount ?? 0,
55940
+ active: true
55941
+ });
55942
+ }
55943
+ return dryRunState.get(id);
55944
+ };
55945
+ info(
55946
+ `[${ts3()}] Watcher started \u2014 polling every ${intervalSec}s${opts.dryRun ? " (dry run)" : ""}`
55947
+ );
55948
+ const tick = async () => {
55949
+ const tickResults = {
55950
+ alerts: { checked: 0, triggered: [] },
55951
+ automations: { executed: [], skipped: 0 }
55952
+ };
55953
+ try {
55954
+ const activeAlerts = await alertStore.getActive();
55955
+ tickResults.alerts.checked = activeAlerts.length;
55956
+ for (const alert of activeAlerts) {
55957
+ try {
55958
+ const { contractAddress } = parseTokenId(alert.tokenId);
55959
+ const price = await swapClient.getTokenPrice(alert.chainKey, contractAddress || "");
55960
+ if (price === null) continue;
55961
+ const triggered = alert.condition === "above" && price >= alert.targetPrice || alert.condition === "below" && price <= alert.targetPrice;
55962
+ if (triggered) {
55963
+ if (!opts.dryRun)
55964
+ await alertStore.update(alert.id, {
55965
+ active: false,
55966
+ triggeredAt: (/* @__PURE__ */ new Date()).toISOString()
55967
+ });
55968
+ const detail = {
55969
+ id: alert.id,
55970
+ tokenId: alert.tokenId,
55971
+ condition: alert.condition,
55972
+ targetPrice: alert.targetPrice,
55973
+ currentPrice: price
55974
+ };
55975
+ tickResults.alerts.triggered.push(detail);
55976
+ success(
55977
+ `[${ts3()}] ${opts.dryRun ? "DRY RUN \u2014 would trigger alert: " : "ALERT TRIGGERED: "}${alert.tokenId} price $${price} is ${alert.condition} $${alert.targetPrice}`
55978
+ );
55979
+ }
55980
+ } catch (err) {
55981
+ warn(
55982
+ `[${ts3()}] Alert ${alert.id} check failed: ${err instanceof Error ? err.message : String(err)}`
55983
+ );
55984
+ }
55985
+ }
55986
+ const automations = await automationStore.getActive();
55987
+ const now = Date.now();
55988
+ for (const auto of automations) {
55989
+ try {
55990
+ const memState = opts.dryRun ? getMemState(auto.id, auto) : null;
55991
+ if (memState && !memState.active) {
55992
+ tickResults.automations.skipped++;
55993
+ continue;
55994
+ }
55995
+ if (auto.expiresAt && new Date(auto.expiresAt).getTime() <= now) {
55996
+ if (!opts.dryRun) await automationStore.update(auto.id, { active: false });
55997
+ else if (memState) memState.active = false;
55998
+ dim(`[${ts3()}] Automation ${auto.id.slice(0, 8)} expired`);
55999
+ tickResults.automations.skipped++;
56000
+ continue;
56001
+ }
56002
+ const effectiveRunCount = memState ? memState.runCount : auto.runCount ?? 0;
56003
+ if (auto.maxRuns && effectiveRunCount >= auto.maxRuns) {
56004
+ if (!opts.dryRun) await automationStore.update(auto.id, { active: false });
56005
+ dim(
56006
+ `[${ts3()}] Automation ${auto.id.slice(0, 8)} reached max runs (${auto.maxRuns})`
56007
+ );
56008
+ tickResults.automations.skipped++;
56009
+ continue;
56010
+ }
56011
+ if (!getChainById(auto.chain)) {
56012
+ warn(
56013
+ `[${ts3()}] Automation ${auto.id.slice(0, 8)} skipped \u2014 unknown chain "${auto.chain}"`
56014
+ );
56015
+ tickResults.automations.skipped++;
56016
+ continue;
56017
+ }
56018
+ if (auto.type === "dca") {
56019
+ const effectiveLastRunAt = memState ? memState.lastRunAt : auto.lastRunAt;
56020
+ const lastRun = effectiveLastRunAt ? new Date(effectiveLastRunAt).getTime() : 0;
56021
+ const intervalMs = auto.intervalMs || 0;
56022
+ if (lastRun + intervalMs > now) {
56023
+ tickResults.automations.skipped++;
56024
+ continue;
56025
+ }
56026
+ if (opts.dryRun && memState) {
56027
+ memState.lastRunAt = (/* @__PURE__ */ new Date()).toISOString();
56028
+ memState.runCount++;
56029
+ dim(
56030
+ `[${ts3()}] DRY RUN: would swap ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`
56031
+ );
56032
+ tickResults.automations.executed.push({ id: auto.id, type: "dca", dryRun: true });
56033
+ continue;
56034
+ }
56035
+ info(
56036
+ `[${ts3()}] Executing DCA: ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`
56037
+ );
56038
+ const result = await executor.execute({
56039
+ fromChain: auto.chain,
56040
+ fromToken: auto.fromToken,
56041
+ toChain: auto.chain,
56042
+ toToken: auto.toToken,
56043
+ amount: auto.amount
56044
+ });
56045
+ if (result.success) {
56046
+ await automationStore.update(auto.id, {
56047
+ lastRunAt: (/* @__PURE__ */ new Date()).toISOString(),
56048
+ runCount: (auto.runCount ?? 0) + 1
56049
+ });
56050
+ success(`[${ts3()}] DCA executed: ${result.explorerUrl}`);
56051
+ tickResults.automations.executed.push({
56052
+ id: auto.id,
56053
+ type: "dca",
56054
+ txHash: result.txHash,
56055
+ explorer: result.explorerUrl
56056
+ });
56057
+ } else {
56058
+ warn(`[${ts3()}] DCA ${auto.id} failed: ${result.error}`);
56059
+ }
56060
+ } else if (auto.type === "limit") {
56061
+ const toTokenInfo = resolveToken(auto.toToken, auto.chain);
56062
+ if (!toTokenInfo) {
56063
+ warn(`[${ts3()}] Automation ${auto.id.slice(0, 8)} skipped \u2014 unknown token "${auto.toToken}" on ${auto.chain}`);
56064
+ continue;
56065
+ }
56066
+ const price = await swapClient.getTokenPrice(auto.chain, toTokenInfo.address);
56067
+ if (price === null) {
56068
+ if (opts.dryRun)
56069
+ dim(
56070
+ `[${ts3()}] DRY RUN: skipping limit order ${auto.id.slice(0, 8)} \u2014 price unavailable for ${auto.toToken}`
56071
+ );
56072
+ continue;
56073
+ }
56074
+ const triggered = auto.condition === "below" && price <= (auto.targetPrice || 0) || auto.condition === "above" && price >= (auto.targetPrice || 0);
56075
+ if (!triggered) {
56076
+ if (opts.dryRun)
56077
+ dim(
56078
+ `[${ts3()}] DRY RUN: limit order ${auto.id.slice(0, 8)} waiting \u2014 ${auto.toToken} $${price} vs target $${auto.targetPrice} (${auto.condition})`
56079
+ );
56080
+ tickResults.automations.skipped++;
56081
+ continue;
56082
+ }
56083
+ if (opts.dryRun && memState) {
56084
+ memState.active = false;
56085
+ dim(
56086
+ `[${ts3()}] DRY RUN: limit order triggered \u2014 would swap ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`
56087
+ );
56088
+ tickResults.automations.executed.push({
56089
+ id: auto.id,
56090
+ type: "limit",
56091
+ dryRun: true
56092
+ });
56093
+ continue;
56094
+ }
56095
+ info(
56096
+ `[${ts3()}] Limit order triggered: swapping ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`
56097
+ );
56098
+ const result = await executor.execute({
56099
+ fromChain: auto.chain,
56100
+ fromToken: auto.fromToken,
56101
+ toChain: auto.chain,
56102
+ toToken: auto.toToken,
56103
+ amount: auto.amount
56104
+ });
56105
+ if (result.success) {
56106
+ await automationStore.update(auto.id, {
56107
+ active: false,
56108
+ lastRunAt: (/* @__PURE__ */ new Date()).toISOString()
56109
+ });
56110
+ success(`[${ts3()}] Limit order executed: ${result.explorerUrl}`);
56111
+ tickResults.automations.executed.push({
56112
+ id: auto.id,
56113
+ type: "limit",
56114
+ txHash: result.txHash,
56115
+ explorer: result.explorerUrl
56116
+ });
56117
+ } else {
56118
+ warn(`[${ts3()}] Limit order ${auto.id} failed: ${result.error}`);
56119
+ }
56120
+ }
56121
+ } catch (err) {
56122
+ warn(
56123
+ `[${ts3()}] Automation ${auto.id} failed: ${err instanceof Error ? err.message : String(err)}`
56124
+ );
56125
+ }
56126
+ }
56127
+ } catch (err) {
56128
+ error(`[${ts3()}] Tick error: ${err instanceof Error ? err.message : String(err)}`);
56129
+ }
56130
+ if (opts.json) {
56131
+ output({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), ...tickResults }, true);
56132
+ } else if (tickResults.alerts.triggered.length === 0 && tickResults.automations.executed.length === 0) {
56133
+ dim(
56134
+ `[${ts3()}] Checked ${tickResults.alerts.checked} alerts, ${tickResults.automations.skipped} automations \u2014 nothing to do`
56135
+ );
56136
+ }
56137
+ };
56138
+ await tick();
56139
+ const timer = setInterval(tick, intervalSec * 1e3);
56140
+ const shutdown = () => {
56141
+ clearInterval(timer);
56142
+ if (provider) {
56143
+ provider.disconnect().catch(() => {
56144
+ });
56145
+ }
56146
+ info(`
56147
+ [${ts3()}] Watcher stopped`);
56148
+ process.exit(0);
56149
+ };
56150
+ process.on("SIGINT", shutdown);
56151
+ process.on("SIGTERM", shutdown);
56152
+ await new Promise(() => {
56153
+ });
56154
+ });
56155
+ }
56156
+
54878
56157
  // src/index.ts
54879
56158
  var VERSION2 = package_default.version;
54880
56159
  var program2 = new Command();
@@ -54891,9 +56170,10 @@ registerErc20Command(program2);
54891
56170
  registerAlertCommand(program2);
54892
56171
  registerQueryCommand(program2);
54893
56172
  registerServeCommand(program2);
54894
- registerSkillCommand(program2);
56173
+ registerAutomateCommand(program2);
54895
56174
  registerX402Command(program2);
54896
56175
  registerTelemetryCommand(program2);
56176
+ registerWatchCommand(program2);
54897
56177
  initAnalytics();
54898
56178
  program2.hook("preAction", (_thisCommand, actionCommand) => {
54899
56179
  trackEvent("twak_command", { command: actionCommand.name() });