@trustwallet/cli 0.6.10 → 0.7.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
@@ -3138,7 +3138,7 @@ function getBackend() {
3138
3138
  if (macSecurity.isAvailable()) return macSecurity;
3139
3139
  return null;
3140
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;
3141
+ var fs, path, os, crypto2, import_child_process, import_os, crypto22, crypto3, __require, FileStore, ALGORITHM, KEY_LENGTH, IV_LENGTH, SALT_LENGTH, ITERATIONS, SERVICE, WALLET_ACCOUNT, API_SECRET_ACCOUNT, macSecurity, KeychainManager, CREDENTIALS_FILE, CredentialManager, WALLET_FILE, WalletConfigStore, ALERTS_FILE, AlertStore, AUTOMATIONS_FILE, AutomationStore, SEEN_HASHES_FILE, SeenHashStore;
3142
3142
  var init_dist = __esm({
3143
3143
  "../storage/dist/index.mjs"() {
3144
3144
  "use strict";
@@ -3149,6 +3149,7 @@ var init_dist = __esm({
3149
3149
  import_child_process = require("child_process");
3150
3150
  import_os = require("os");
3151
3151
  crypto22 = __toESM(require("crypto"), 1);
3152
+ crypto3 = __toESM(require("crypto"), 1);
3152
3153
  __require = /* @__PURE__ */ ((x6) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x6, {
3153
3154
  get: (a3, b3) => (typeof require !== "undefined" ? require : a3)[b3]
3154
3155
  }) : x6)(function(x6) {
@@ -3410,6 +3411,51 @@ var init_dist = __esm({
3410
3411
  await this.store.remove(ALERTS_FILE);
3411
3412
  }
3412
3413
  };
3414
+ AUTOMATIONS_FILE = "automations.json";
3415
+ AutomationStore = class {
3416
+ store;
3417
+ constructor(config2) {
3418
+ this.store = new FileStore(config2);
3419
+ }
3420
+ async create(record2) {
3421
+ const automations = await this.list();
3422
+ const entry = {
3423
+ ...record2,
3424
+ id: crypto3.randomUUID(),
3425
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3426
+ active: true
3427
+ };
3428
+ automations.push(entry);
3429
+ await this.store.write(AUTOMATIONS_FILE, automations);
3430
+ return entry;
3431
+ }
3432
+ async list() {
3433
+ return await this.store.read(AUTOMATIONS_FILE) || [];
3434
+ }
3435
+ async getById(id) {
3436
+ const automations = await this.list();
3437
+ return automations.find((a3) => a3.id === id);
3438
+ }
3439
+ async update(id, updates) {
3440
+ const automations = await this.list();
3441
+ const index = automations.findIndex((a3) => a3.id === id);
3442
+ if (index === -1) return void 0;
3443
+ automations[index] = { ...automations[index], ...updates };
3444
+ await this.store.write(AUTOMATIONS_FILE, automations);
3445
+ return automations[index];
3446
+ }
3447
+ async delete(id) {
3448
+ const automations = await this.list();
3449
+ const filtered = automations.filter((a3) => a3.id !== id);
3450
+ if (filtered.length === automations.length) return false;
3451
+ await this.store.write(AUTOMATIONS_FILE, filtered);
3452
+ return true;
3453
+ }
3454
+ async getActive() {
3455
+ const automations = await this.list();
3456
+ return automations.filter((a3) => a3.active);
3457
+ }
3458
+ };
3413
3459
  SEEN_HASHES_FILE = "seen-hashes.json";
3414
3460
  SeenHashStore = class {
3415
3461
  store;
@@ -6167,6 +6213,9 @@ function patchNodeStubs() {
6167
6213
  parsed.pathname = `/naas/session/${_naasSessionId}${parsed.pathname}`;
6168
6214
  resolvedUrl = parsed.toString();
6169
6215
  }
6216
+ if (resolvedUrl.includes("aag") || resolvedUrl.includes("account-abstraction")) {
6217
+ console.error("[AAG]", init3?.method || "GET", resolvedUrl.slice(0, 200));
6218
+ }
6170
6219
  if (resolvedUrl.includes("gateway.us.trustwallet.com")) {
6171
6220
  resolvedUrl = resolvedUrl.replace("gateway.us.trustwallet.com", "tws.trustwallet.com");
6172
6221
  }
@@ -6406,7 +6455,7 @@ function registerShutdownHandlers(providers, onBeforeExit) {
6406
6455
  process.removeListener("SIGTERM", handler);
6407
6456
  };
6408
6457
  }
6409
- 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, BlockchainClient, WalletCoreSigner, domainsByCoinId, GATEWAY_URL, WALLET_ID_SUFFIX, WALLET_ID_LENGTH, EVM_COIN_IDS, MAINTAINED_COIN_IDS, WalletProvider, AgentWalletProvider, SHUTDOWN_TIMEOUT_MS;
6458
+ var fs2, path2, import_trust_sdk, import_trust_sdk2, import_trust_sdk3, import_trust_sdk4, import_crypto2, __require2, polyfillsApplied, _credentials, _naasSessionId, nodeStubsPatched, NAAS_SESSION_RE, naasSessionPatched, coinTypeByValue, walletCore, initialized, SPONSORED_RPC, BlockchainClient, WalletCoreSigner, domainsByCoinId, GATEWAY_URL, WALLET_ID_SUFFIX, WALLET_ID_LENGTH, EVM_COIN_IDS, MAINTAINED_COIN_IDS, WalletProvider, AgentWalletProvider, SHUTDOWN_TIMEOUT_MS;
6410
6459
  var init_dist3 = __esm({
6411
6460
  "../wallet-core/dist/index.mjs"() {
6412
6461
  "use strict";
@@ -6436,7 +6485,11 @@ var init_dist3 = __esm({
6436
6485
  coinTypeByValue = null;
6437
6486
  walletCore = null;
6438
6487
  initialized = false;
6439
- BlockchainClient = class {
6488
+ SPONSORED_RPC = {
6489
+ 20000714: "https://bsc-sponsored.twnodes.com",
6490
+ 60: "https://ethereum-sponsored.twnodes.com"
6491
+ };
6492
+ BlockchainClient = class _BlockchainClient {
6440
6493
  signer;
6441
6494
  constructor(signer) {
6442
6495
  this.signer = signer;
@@ -6553,14 +6606,15 @@ var init_dist3 = __esm({
6553
6606
  const asset = contractAddress ? new import_trust_sdk2.Asset.Token(account, contractAddress, tokenTypeForCoinId(coinId)) : new import_trust_sdk2.Asset.Coin(account);
6554
6607
  const decimals = params.decimals !== void 0 ? params.decimals : contractAddress ? resolveToken(params.assetId, chainKey).decimals : getCoinDecimals(coinId);
6555
6608
  const amountWei = humanAmountToWei(params.amount, decimals);
6556
- const transaction = new import_trust_sdk2.TransactionTransfer(
6609
+ let transaction = new import_trust_sdk2.TransactionTransfer(
6557
6610
  params.to,
6558
6611
  asset,
6559
6612
  amountWei.toString(),
6560
6613
  false,
6561
6614
  params.memo || null
6562
6615
  );
6563
- const fee = await import_trust_sdk2.BlockchainService.calculateFee(transaction);
6616
+ const { fee, transaction7702 } = await this.calculateFeeWithSponsorship(transaction, account, coinId);
6617
+ if (transaction7702) transaction = transaction7702;
6564
6618
  const nonce = await import_trust_sdk2.BlockchainService.fetchNonce(account);
6565
6619
  let encodedTx;
6566
6620
  try {
@@ -6584,7 +6638,7 @@ var init_dist3 = __esm({
6584
6638
  }
6585
6639
  }
6586
6640
  }
6587
- const hash = await import_trust_sdk2.BlockchainService.sendTransactionV2(chain2, encodedTx);
6641
+ const hash = await this.sendTransactionWithSponsorship(chain2, encodedTx, coinId);
6588
6642
  return { hash, chainKey };
6589
6643
  }
6590
6644
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -6602,13 +6656,14 @@ var init_dist3 = __esm({
6602
6656
  const account = this.getAccountFromSigner(chainKey);
6603
6657
  const asset = new import_trust_sdk2.Asset.Token(account, contractAddress, tokenTypeForCoinId(coinId));
6604
6658
  const approveData = this.encodeApproveCalldata(params.spender, params.amount);
6605
- const transaction = new import_trust_sdk2.TransactionSmartContract(
6659
+ let transaction = new import_trust_sdk2.TransactionSmartContract(
6606
6660
  asset,
6607
6661
  contractAddress,
6608
6662
  "0",
6609
6663
  approveData
6610
6664
  );
6611
- const fee = await import_trust_sdk2.BlockchainService.calculateFee(transaction);
6665
+ const { fee, transaction7702 } = await this.calculateFeeWithSponsorship(transaction, account, coinId);
6666
+ if (transaction7702) transaction = transaction7702;
6612
6667
  const nonce = await import_trust_sdk2.BlockchainService.fetchNonce(account);
6613
6668
  let encodedTx;
6614
6669
  try {
@@ -6632,7 +6687,7 @@ var init_dist3 = __esm({
6632
6687
  }
6633
6688
  }
6634
6689
  }
6635
- const hash = await import_trust_sdk2.BlockchainService.sendTransactionV2(chain2, encodedTx);
6690
+ const hash = await this.sendTransactionWithSponsorship(chain2, encodedTx, coinId);
6636
6691
  return { hash, chainKey };
6637
6692
  }
6638
6693
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -6647,8 +6702,9 @@ var init_dist3 = __esm({
6647
6702
  const account = this.getAccountFromSigner(chainKey);
6648
6703
  const asset = new import_trust_sdk2.Asset.Coin(account);
6649
6704
  const valueWei = params.value && params.value !== "0x0" && params.value !== "0x" ? BigInt(params.value).toString() : "0";
6650
- const transaction = new import_trust_sdk2.TransactionSmartContract(asset, params.to, valueWei, params.data);
6651
- const fee = await import_trust_sdk2.BlockchainService.calculateFee(transaction);
6705
+ let transaction = new import_trust_sdk2.TransactionSmartContract(asset, params.to, valueWei, params.data);
6706
+ const { fee, transaction7702 } = await this.calculateFeeWithSponsorship(transaction, account, coinId);
6707
+ if (transaction7702) transaction = transaction7702;
6652
6708
  const nonce = await import_trust_sdk2.BlockchainService.fetchNonce(account);
6653
6709
  let encodedTx;
6654
6710
  try {
@@ -6667,7 +6723,7 @@ var init_dist3 = __esm({
6667
6723
  }
6668
6724
  }
6669
6725
  }
6670
- const hash = await import_trust_sdk2.BlockchainService.sendTransactionV2(chain2, encodedTx);
6726
+ const hash = await this.sendTransactionWithSponsorship(chain2, encodedTx, coinId);
6671
6727
  return { hash, chainKey };
6672
6728
  }
6673
6729
  async getAllowance(params) {
@@ -6828,6 +6884,193 @@ var init_dist3 = __esm({
6828
6884
  const chain2 = new import_trust_sdk2.DefinedChain(ct5);
6829
6885
  await import_trust_sdk2.BlockchainService.setNode(chain2, new import_trust_sdk2.Node(rpcUrl));
6830
6886
  }
6887
+ // EIP-7702 sponsorship-eligible chain coin IDs
6888
+ static SPONSORABLE_COINS = [60, 20000714];
6889
+ /**
6890
+ * Build the sponsored RPC URL with NaaS session if available.
6891
+ */
6892
+ getSponsoredRpcUrl(coinId) {
6893
+ const base3 = SPONSORED_RPC[coinId];
6894
+ if (!base3) return void 0;
6895
+ const sessionId = process.env.TWAK_NAAS_SESSION_ID;
6896
+ return sessionId ? `${base3}/naas/session/${sessionId}/` : base3;
6897
+ }
6898
+ /**
6899
+ * Try sending via sponsored RPC first (checks pm_isSponsorable, then
6900
+ * eth_sendRawTransaction). Falls back to regular BlockchainService.
6901
+ */
6902
+ async sendTransactionWithSponsorship(chain2, encodedTx, coinId) {
6903
+ const sponsoredUrl = this.getSponsoredRpcUrl(coinId);
6904
+ if (sponsoredUrl) {
6905
+ try {
6906
+ let rlpStart = 0;
6907
+ if (encodedTx[0] === 10) {
6908
+ let offset = 1;
6909
+ while (offset < encodedTx.length && (encodedTx[offset] & 128) !== 0) offset++;
6910
+ offset++;
6911
+ rlpStart = offset;
6912
+ }
6913
+ const rawRlp = "0x" + Array.from(encodedTx.slice(rlpStart)).map((b3) => b3.toString(16).padStart(2, "0")).join("");
6914
+ const resp = await fetch(sponsoredUrl, {
6915
+ method: "POST",
6916
+ headers: { "Content-Type": "application/json" },
6917
+ body: JSON.stringify({ id: 0, jsonrpc: "2.0", method: "eth_sendRawTransaction", params: [rawRlp] }),
6918
+ signal: AbortSignal.timeout(3e4)
6919
+ });
6920
+ const json = await resp.json();
6921
+ if (json.result) return json.result;
6922
+ } catch {
6923
+ }
6924
+ }
6925
+ return import_trust_sdk2.BlockchainService.sendTransactionV2(chain2, encodedTx);
6926
+ }
6927
+ // Known stablecoins to try as gas payment tokens when getSupportedTokens returns empty
6928
+ static FALLBACK_GAS_TOKENS = {
6929
+ 20000714: [
6930
+ // BSC
6931
+ "0x55d398326f99059fF775485246999027B3197955",
6932
+ // USDT
6933
+ "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d"
6934
+ // USDC
6935
+ ],
6936
+ 60: [
6937
+ // ETH
6938
+ "0xdAC17F958D2ee523a2206206994597C13D831ec7",
6939
+ // USDT
6940
+ "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
6941
+ // USDC
6942
+ ]
6943
+ };
6944
+ /**
6945
+ * Find a token the wallet holds that can pay for gas on this chain.
6946
+ * Tries SmartWalletConfig.getSupportedTokens() first, then falls back
6947
+ * to known stablecoins (USDT/USDC).
6948
+ */
6949
+ async findGasToken(account, coinId) {
6950
+ const ct5 = requireCoinType(coinId);
6951
+ const chain2 = new import_trust_sdk2.DefinedChain(ct5);
6952
+ const candidates = [];
6953
+ try {
6954
+ const supportedTokens = await import_trust_sdk2.SmartWalletConfig.getSupportedTokens(chain2);
6955
+ if (supportedTokens?.length) {
6956
+ for (const t of supportedTokens) {
6957
+ if (t.address) candidates.push(t.address);
6958
+ }
6959
+ }
6960
+ } catch {
6961
+ }
6962
+ const seen = new Set(candidates.map((a3) => a3.toLowerCase()));
6963
+ for (const addr of _BlockchainClient.FALLBACK_GAS_TOKENS[coinId] || []) {
6964
+ if (!seen.has(addr.toLowerCase())) candidates.push(addr);
6965
+ }
6966
+ if (candidates.length === 0) return void 0;
6967
+ const tokenAssets = candidates.map((addr) => new import_trust_sdk2.Asset.Token(account, addr, import_trust_sdk2.TokenType.Erc20));
6968
+ try {
6969
+ const balances = await import_trust_sdk2.BlockchainService.loadBalances(ct5, tokenAssets);
6970
+ for (let i2 = 0; i2 < balances.length; i2++) {
6971
+ const avail = balances[i2]?.balances?.find(
6972
+ (b3) => b3.type === import_trust_sdk2.BalanceType.Available
6973
+ );
6974
+ if (avail && BigInt(avail.amount || "0") > 0n) {
6975
+ return tokenAssets[i2];
6976
+ }
6977
+ }
6978
+ } catch {
6979
+ }
6980
+ return void 0;
6981
+ }
6982
+ /**
6983
+ * Calculate fee, falling back to EIP-7702 smart wallet sponsorship
6984
+ * when the wallet can't afford native gas (BSC/ETH only).
6985
+ *
6986
+ * Sets is7702=true and energy=tokenAsset on the transaction so the
6987
+ * Trust SDK routes through the Smart Wallet fee service internally.
6988
+ */
6989
+ async calculateFeeWithSponsorship(transaction, account, coinId) {
6990
+ if (!_BlockchainClient.SPONSORABLE_COINS.includes(coinId)) {
6991
+ return { fee: await import_trust_sdk2.BlockchainService.calculateFee(transaction) };
6992
+ }
6993
+ let supports7702 = false;
6994
+ try {
6995
+ const ct5 = requireCoinType(coinId);
6996
+ supports7702 = await import_trust_sdk2.SmartWalletConfig.supports7702(ct5);
6997
+ } catch {
6998
+ }
6999
+ if (!supports7702) {
7000
+ return { fee: await import_trust_sdk2.BlockchainService.calculateFee(transaction) };
7001
+ }
7002
+ const fee = await import_trust_sdk2.BlockchainService.calculateFee(transaction);
7003
+ const feeAmount = BigInt(fee.amount || "0");
7004
+ if (feeAmount <= 0n) return { fee };
7005
+ let canAfford = true;
7006
+ try {
7007
+ const ct5 = requireCoinType(coinId);
7008
+ const energy = new import_trust_sdk2.Asset.Coin(account);
7009
+ const balanceResults = await import_trust_sdk2.BlockchainService.loadBalances(ct5, [energy]);
7010
+ const available = balanceResults[0]?.balances?.find(
7011
+ (b3) => b3.type === import_trust_sdk2.BalanceType.Available
7012
+ );
7013
+ const nativeBalance = BigInt(available?.amount || "0");
7014
+ canAfford = nativeBalance >= feeAmount;
7015
+ } catch {
7016
+ canAfford = false;
7017
+ }
7018
+ if (canAfford) return { fee };
7019
+ const nativeEnergy = new import_trust_sdk2.Asset.Coin(account);
7020
+ let tx7702 = transaction;
7021
+ if (transaction instanceof import_trust_sdk2.TransactionSmartContract) {
7022
+ tx7702 = transaction.copy(void 0, void 0, void 0, void 0, nativeEnergy, true);
7023
+ } else if (transaction instanceof import_trust_sdk2.TransactionTransfer) {
7024
+ tx7702 = transaction.copy(void 0, void 0, void 0, void 0, void 0, void 0, nativeEnergy, true);
7025
+ } else if (transaction instanceof import_trust_sdk2.TransactionTrade) {
7026
+ tx7702 = transaction.copy(void 0, void 0, void 0, void 0, void 0, void 0, void 0, void 0, void 0, void 0, void 0, nativeEnergy, true);
7027
+ }
7028
+ let baseFee;
7029
+ try {
7030
+ baseFee = await import_trust_sdk2.BlockchainService.calculateFee(tx7702);
7031
+ process.stderr.write(`[7702] baseFee ok, amount:${baseFee.amount} type:${baseFee.constructor?.name}
7032
+ `);
7033
+ } catch (e2) {
7034
+ process.stderr.write(`[7702] baseFee FAILED: ${e2 instanceof Error ? e2.message : String(e2)}
7035
+ `);
7036
+ return { fee };
7037
+ }
7038
+ const userOpData = baseFee.data;
7039
+ if (!userOpData) {
7040
+ return { fee: baseFee, transaction7702: tx7702 };
7041
+ }
7042
+ const gasToken = await this.findGasToken(account, coinId);
7043
+ if (!gasToken) {
7044
+ return { fee: baseFee, transaction7702: tx7702 };
7045
+ }
7046
+ try {
7047
+ const int8Data = typeof userOpData === "string" ? new Int8Array(Buffer.from(userOpData.replace("0x", ""), "hex")) : new Int8Array(userOpData);
7048
+ const { EthereumAaService } = await import("@trustwallet-trust-sdk/trust-sdk");
7049
+ const paymasters = await EthereumAaService.getFeeQuotes(
7050
+ tx7702,
7051
+ nativeEnergy,
7052
+ int8Data,
7053
+ [gasToken]
7054
+ );
7055
+ if (paymasters && paymasters.length > 0) {
7056
+ const finalFee = await EthereumAaService.simulatePaymasterTransaction(
7057
+ tx7702,
7058
+ paymasters[0]
7059
+ );
7060
+ let txWithToken = transaction;
7061
+ if (transaction instanceof import_trust_sdk2.TransactionSmartContract) {
7062
+ txWithToken = transaction.copy(void 0, void 0, void 0, void 0, gasToken, true);
7063
+ } else if (transaction instanceof import_trust_sdk2.TransactionTransfer) {
7064
+ txWithToken = transaction.copy(void 0, void 0, void 0, void 0, void 0, void 0, gasToken, true);
7065
+ } else if (transaction instanceof import_trust_sdk2.TransactionTrade) {
7066
+ txWithToken = transaction.copy(void 0, void 0, void 0, void 0, void 0, void 0, void 0, void 0, void 0, void 0, void 0, gasToken, true);
7067
+ }
7068
+ return { fee: finalFee, transaction7702: txWithToken };
7069
+ }
7070
+ } catch {
7071
+ }
7072
+ return { fee: baseFee, transaction7702: tx7702 };
7073
+ }
6831
7074
  getAccountFromSigner(chainKey) {
6832
7075
  if (!this.signer) {
6833
7076
  throw new Error("BlockchainClient requires a signer for this operation");
@@ -14385,8 +14628,8 @@ var require_dataType = __commonJS({
14385
14628
  return types;
14386
14629
  }
14387
14630
  exports2.getSchemaTypes = getSchemaTypes;
14388
- function getJSONTypes(ts3) {
14389
- const types = Array.isArray(ts3) ? ts3 : ts3 ? [ts3] : [];
14631
+ function getJSONTypes(ts4) {
14632
+ const types = Array.isArray(ts4) ? ts4 : ts4 ? [ts4] : [];
14390
14633
  if (types.every(rules_1.isJSONType))
14391
14634
  return types;
14392
14635
  throw new Error("type must be JSONType or JSONType[]: " + types.join(","));
@@ -15428,18 +15671,18 @@ var require_validate = __commonJS({
15428
15671
  });
15429
15672
  narrowSchemaTypes(it4, types);
15430
15673
  }
15431
- function checkMultipleTypes(it4, ts3) {
15432
- if (ts3.length > 1 && !(ts3.length === 2 && ts3.includes("null"))) {
15674
+ function checkMultipleTypes(it4, ts4) {
15675
+ if (ts4.length > 1 && !(ts4.length === 2 && ts4.includes("null"))) {
15433
15676
  strictTypesError(it4, "use allowUnionTypes to allow union type keyword");
15434
15677
  }
15435
15678
  }
15436
- function checkKeywordTypes(it4, ts3) {
15679
+ function checkKeywordTypes(it4, ts4) {
15437
15680
  const rules = it4.self.RULES.all;
15438
15681
  for (const keyword in rules) {
15439
15682
  const rule = rules[keyword];
15440
15683
  if (typeof rule == "object" && (0, applicability_1.shouldUseRule)(it4.schema, rule)) {
15441
15684
  const { type } = rule.definition;
15442
- if (type.length && !type.some((t) => hasApplicableType(ts3, t))) {
15685
+ if (type.length && !type.some((t) => hasApplicableType(ts4, t))) {
15443
15686
  strictTypesError(it4, `missing type "${type.join(",")}" for keyword "${keyword}"`);
15444
15687
  }
15445
15688
  }
@@ -15448,18 +15691,18 @@ var require_validate = __commonJS({
15448
15691
  function hasApplicableType(schTs, kwdT) {
15449
15692
  return schTs.includes(kwdT) || kwdT === "number" && schTs.includes("integer");
15450
15693
  }
15451
- function includesType(ts3, t) {
15452
- return ts3.includes(t) || t === "integer" && ts3.includes("number");
15694
+ function includesType(ts4, t) {
15695
+ return ts4.includes(t) || t === "integer" && ts4.includes("number");
15453
15696
  }
15454
15697
  function narrowSchemaTypes(it4, withTypes) {
15455
- const ts3 = [];
15698
+ const ts4 = [];
15456
15699
  for (const t of it4.dataTypes) {
15457
15700
  if (includesType(withTypes, t))
15458
- ts3.push(t);
15701
+ ts4.push(t);
15459
15702
  else if (withTypes.includes("integer") && t === "number")
15460
- ts3.push("integer");
15703
+ ts4.push("integer");
15461
15704
  }
15462
- it4.dataTypes = ts3;
15705
+ it4.dataTypes = ts4;
15463
15706
  }
15464
15707
  function strictTypesError(it4, msg) {
15465
15708
  const schemaPath = it4.schemaEnv.baseId + it4.errSchemaPath;
@@ -22171,12 +22414,21 @@ function warn(message) {
22171
22414
  function info(message) {
22172
22415
  console.log(import_chalk.default.cyan(message));
22173
22416
  }
22417
+ function dim(message) {
22418
+ console.log(import_chalk.default.dim(message));
22419
+ }
22174
22420
 
22175
22421
  // src/config-loader.ts
22176
22422
  var import_chalk2 = __toESM(require_source());
22177
22423
  init_dist2();
22178
22424
  init_dist4();
22179
22425
  init_dist();
22426
+ var EMBEDDED_NAAS_SESSION_ID = "NDVjMWRjY2MtYTYwNy00NGNhLWIxZGUtMmJiZDIyNmEyODQ4";
22427
+ function resolveNaasSessionId() {
22428
+ if (process.env.TWAK_NAAS_SESSION_ID) return process.env.TWAK_NAAS_SESSION_ID;
22429
+ if (EMBEDDED_NAAS_SESSION_ID && !EMBEDDED_NAAS_SESSION_ID.startsWith("__")) return EMBEDDED_NAAS_SESSION_ID;
22430
+ return void 0;
22431
+ }
22180
22432
  async function getCredentials() {
22181
22433
  const envAccessId = process.env.TWAK_ACCESS_ID;
22182
22434
  const envSecret = process.env.TWAK_HMAC_SECRET;
@@ -22201,7 +22453,7 @@ async function ensureTrustSDK() {
22201
22453
  await initTrustSDK({
22202
22454
  accessId: creds.accessId,
22203
22455
  hmacSecret: creds.hmacSecret,
22204
- naasSessionId: process.env.TWAK_NAAS_SESSION_ID
22456
+ naasSessionId: resolveNaasSessionId()
22205
22457
  });
22206
22458
  return creds;
22207
22459
  }
@@ -25065,7 +25317,7 @@ function walk(err, fn2) {
25065
25317
 
25066
25318
  // ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/cryptoNode.js
25067
25319
  var nc = __toESM(require("crypto"), 1);
25068
- var crypto3 = nc && typeof nc === "object" && "webcrypto" in nc ? nc.webcrypto : nc && typeof nc === "object" && "randomBytes" in nc ? nc : void 0;
25320
+ var crypto4 = nc && typeof nc === "object" && "webcrypto" in nc ? nc.webcrypto : nc && typeof nc === "object" && "randomBytes" in nc ? nc : void 0;
25069
25321
 
25070
25322
  // ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/utils.js
25071
25323
  function isBytes(a3) {
@@ -25162,11 +25414,11 @@ function createHasher(hashCons) {
25162
25414
  return hashC;
25163
25415
  }
25164
25416
  function randomBytes2(bytesLength = 32) {
25165
- if (crypto3 && typeof crypto3.getRandomValues === "function") {
25166
- return crypto3.getRandomValues(new Uint8Array(bytesLength));
25417
+ if (crypto4 && typeof crypto4.getRandomValues === "function") {
25418
+ return crypto4.getRandomValues(new Uint8Array(bytesLength));
25167
25419
  }
25168
- if (crypto3 && typeof crypto3.randomBytes === "function") {
25169
- return Uint8Array.from(crypto3.randomBytes(bytesLength));
25420
+ if (crypto4 && typeof crypto4.randomBytes === "function") {
25421
+ return Uint8Array.from(crypto4.randomBytes(bytesLength));
25170
25422
  }
25171
25423
  throw new Error("crypto.getRandomValues must be defined");
25172
25424
  }
@@ -38951,6 +39203,18 @@ ${qr3}`;
38951
39203
  return [{ chainKey, chain: chain2, address: addrEntry.address }];
38952
39204
  });
38953
39205
  const suppress = (fn2) => fn2();
39206
+ const pMap = async (items, fn2, concurrency) => {
39207
+ const results = new Array(items.length);
39208
+ let idx = 0;
39209
+ const run = async () => {
39210
+ while (idx < items.length) {
39211
+ const i2 = idx++;
39212
+ results[i2] = await fn2(items[i2]);
39213
+ }
39214
+ };
39215
+ await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => run()));
39216
+ return results;
39217
+ };
38954
39218
  const toHuman = (raw, decimals) => {
38955
39219
  const big = BigInt(raw);
38956
39220
  const div = 10n ** BigInt(decimals);
@@ -38961,14 +39225,14 @@ ${qr3}`;
38961
39225
  };
38962
39226
  const holdingsAssets = chainEntries.map(({ chain: chain2, address }) => ({ coin: chain2.coinId, address }));
38963
39227
  const [nativeResults, holdings] = await Promise.all([
38964
- Promise.all(chainEntries.map(async ({ chainKey, chain: chain2, address }) => {
39228
+ pMap(chainEntries, async ({ chainKey, chain: chain2, address }) => {
38965
39229
  try {
38966
39230
  const balance = await suppress(() => blockchainClient.getBalance(`c${chain2.coinId}`, address));
38967
39231
  return { chainKey, chain: chain2, address, balance };
38968
39232
  } catch {
38969
39233
  return { chainKey, chain: chain2, address, balance: null };
38970
39234
  }
38971
- })),
39235
+ }, 5),
38972
39236
  (async () => {
38973
39237
  try {
38974
39238
  return await new SecurityService2(httpClient).getTokenHoldings(holdingsAssets);
@@ -38999,7 +39263,7 @@ ${qr3}`;
38999
39263
  if (t.contract) tokenJobs.push({ chainKey, coinId: chain2.coinId, address, ...t });
39000
39264
  }
39001
39265
  }
39002
- const tokenBalances = await Promise.all(tokenJobs.map(async (job) => {
39266
+ const tokenBalances = await pMap(tokenJobs, async (job) => {
39003
39267
  const fallback = job.balance ?? "0";
39004
39268
  try {
39005
39269
  const assetId = `c${job.coinId}_t${job.contract}`;
@@ -39008,7 +39272,7 @@ ${qr3}`;
39008
39272
  } catch {
39009
39273
  return { ...job, rawBalance: fallback };
39010
39274
  }
39011
- }));
39275
+ }, 5);
39012
39276
  const nativeAssetIds = nativeResults.map(({ chain: chain2 }) => `c${chain2.coinId}`);
39013
39277
  const nonZeroTokens = tokenBalances.filter((t) => {
39014
39278
  const dec = t.decimals ?? 18;
@@ -39256,6 +39520,108 @@ var SwapClient = class {
39256
39520
  }
39257
39521
  }
39258
39522
  };
39523
+ var SwapExecutor = class {
39524
+ constructor(swapClient, walletProvider, options = {}) {
39525
+ this.swapClient = swapClient;
39526
+ this.walletProvider = walletProvider;
39527
+ this.options = options;
39528
+ }
39529
+ log(msg) {
39530
+ this.options.onStatus?.(msg);
39531
+ }
39532
+ async execute(params) {
39533
+ const address = await this.walletProvider.getAddress(params.fromChain);
39534
+ const fromTokenInfo = resolveToken(params.fromToken, params.fromChain);
39535
+ const toTokenInfo = resolveToken(params.toToken, params.toChain);
39536
+ const parsedAmount = parseAmount(params.amount, fromTokenInfo.decimals);
39537
+ const fromCoinId = getChainById(params.fromChain)?.coinId;
39538
+ const toCoinId = getChainById(params.toChain)?.coinId;
39539
+ const domains = this.options.amberDomains || /* @__PURE__ */ new Map();
39540
+ let destinationAddress;
39541
+ if (params.toChain !== params.fromChain) {
39542
+ destinationAddress = await this.walletProvider.getAddress(params.toChain);
39543
+ }
39544
+ const quote = await this.swapClient.getQuote({
39545
+ fromChain: params.fromChain,
39546
+ fromToken: fromTokenInfo.address,
39547
+ toChain: params.toChain,
39548
+ toToken: toTokenInfo.address,
39549
+ amount: parsedAmount,
39550
+ walletAddress: address,
39551
+ destinationAddress,
39552
+ slippage: params.slippage,
39553
+ fromDomainId: fromCoinId ? domains.get(fromCoinId) : void 0,
39554
+ toDomainId: toCoinId ? domains.get(toCoinId) : void 0
39555
+ });
39556
+ if (quote.routes.length === 0) {
39557
+ return { success: false, route: { id: "", steps: [], warnings: ["No routes found"], expirationDate: "", totalOutput: "0" }, error: "No swap routes found" };
39558
+ }
39559
+ const route = quote.routes[0];
39560
+ const firstStep = route.steps[0];
39561
+ const lastStep = route.steps[route.steps.length - 1];
39562
+ const inputAmt = formatAmount(firstStep.from.amount, firstStep.from.asset.metadata.decimals);
39563
+ const outputAmt = formatAmount(lastStep.to.amount, lastStep.to.asset.metadata.decimals);
39564
+ this.log(`Swapping ${inputAmt} ${firstStep.from.asset.metadata.symbol} -> ${outputAmt} ${lastStep.to.asset.metadata.symbol} via ${route.steps.map((s2) => s2.provider.name).join(" -> ")}`);
39565
+ const chainConfig = getChainById(params.fromChain);
39566
+ const transactions = await this.swapClient.getSwapTransactions(route);
39567
+ try {
39568
+ let txHash = "";
39569
+ for (const txData of transactions) {
39570
+ const txType = txData.transaction.type;
39571
+ const isApproval = txType === "approval" || txType === "revokeApproval";
39572
+ if (isApproval) {
39573
+ this.log(txType === "revokeApproval" ? "Revoking prior approval..." : "Sending token approval...");
39574
+ }
39575
+ txHash = await this.sendTransaction(txData, chainConfig, params.fromChain);
39576
+ this.log(`${isApproval ? "Approval" : "Swap"} tx: ${getExplorerTxUrl(params.fromChain, txHash)}`);
39577
+ await this.pollForConfirmation(params.fromChain, address, txHash, isApproval ? 6e4 : 12e4);
39578
+ }
39579
+ return {
39580
+ success: true,
39581
+ txHash,
39582
+ route,
39583
+ explorerUrl: getExplorerTxUrl(params.fromChain, txHash)
39584
+ };
39585
+ } catch (err) {
39586
+ const msg = err instanceof Error ? err.message : "Unknown error";
39587
+ return { success: false, route, error: msg };
39588
+ }
39589
+ }
39590
+ async sendTransaction(txData, chainConfig, fromChain) {
39591
+ if (chainConfig?.namespace === "solana" && txData.transaction.solanaTx) {
39592
+ return this.walletProvider.sendTransaction({
39593
+ chainId: `solana:${chainConfig.chainId}`,
39594
+ to: "",
39595
+ data: txData.transaction.solanaTx.serializedMessage
39596
+ });
39597
+ } else if (txData.transaction.evmTx) {
39598
+ return this.walletProvider.sendTransaction({
39599
+ chainId: `eip155:${chainConfig?.chainId || "1"}`,
39600
+ to: txData.transaction.evmTx.to,
39601
+ data: txData.transaction.evmTx.data,
39602
+ value: txData.transaction.evmTx.value || "0x0"
39603
+ });
39604
+ }
39605
+ throw new Error(`No transaction data returned for swap step on ${fromChain}`);
39606
+ }
39607
+ async pollForConfirmation(chainKey, address, hash, timeoutMs) {
39608
+ const blockchainClient = this.walletProvider.getBlockchainClient?.();
39609
+ if (!blockchainClient) return;
39610
+ const deadline = Date.now() + timeoutMs;
39611
+ while (Date.now() < deadline) {
39612
+ await new Promise((r3) => setTimeout(r3, 2e3));
39613
+ try {
39614
+ const status = await blockchainClient.findTransactionStatus(chainKey, address, hash);
39615
+ if (status.confirmed) return;
39616
+ if (status.failed) throw new Error(`Transaction failed: ${status.error || "unknown error"}`);
39617
+ } catch (e2) {
39618
+ const err = e2 instanceof Error ? e2 : new Error(String(e2));
39619
+ if (err.message.startsWith("Transaction failed")) throw err;
39620
+ }
39621
+ }
39622
+ throw new Error(`Transaction ${hash} was not confirmed within ${Math.round(timeoutMs / 1e3)}s`);
39623
+ }
39624
+ };
39259
39625
 
39260
39626
  // src/commands/transfer.ts
39261
39627
  var DEFAULT_MAX_TRANSFER_USD = 1e4;
@@ -39513,6 +39879,55 @@ function registerSwapCommand(program3) {
39513
39879
  const transactions = await swapClient.getSwapTransactions(route);
39514
39880
  let txHash = "";
39515
39881
  const blockchainClient = provider.getBlockchainClient();
39882
+ const hasApprovalTx = transactions.some((t) => t.transaction.type === "approval");
39883
+ if (!hasApprovalTx && chainConfig?.isEVM) {
39884
+ const stepApproval = firstStep.approval;
39885
+ if (stepApproval && stepApproval.operatorAddress) {
39886
+ const fromAssetAddr = firstStep.from.asset.address;
39887
+ const isNative = !fromAssetAddr || fromAssetAddr === NATIVE_TOKEN_ADDRESS;
39888
+ if (!isNative && chainConfig.coinId) {
39889
+ const assetId = formatTokenId(chainConfig.coinId, fromAssetAddr);
39890
+ try {
39891
+ const allowance = await blockchainClient.getAllowance({
39892
+ assetId,
39893
+ owner: walletAddress,
39894
+ spender: stepApproval.operatorAddress
39895
+ });
39896
+ const requiredAmount = BigInt(stepApproval.minAmount || parsedAmount);
39897
+ if (allowance < requiredAmount) {
39898
+ info("Sending token approval...");
39899
+ const hdWallet = provider.getSigner().getHDWallet();
39900
+ const approveResult = await blockchainClient.approve({
39901
+ assetId,
39902
+ spender: stepApproval.operatorAddress,
39903
+ amount: requiredAmount.toString(),
39904
+ hdWallet
39905
+ });
39906
+ info(`Approval tx: ${getExplorerTxUrl(fromChain, approveResult.hash)}`);
39907
+ const approvalDeadline = Date.now() + 6e4;
39908
+ while (Date.now() < approvalDeadline) {
39909
+ await new Promise((r3) => setTimeout(r3, 2e3));
39910
+ try {
39911
+ const status = await blockchainClient.findTransactionStatus(fromChain, walletAddress, approveResult.hash);
39912
+ if (status.confirmed) break;
39913
+ if (status.failed) throw new Error("Approval transaction failed");
39914
+ } catch (e2) {
39915
+ const err = e2 instanceof Error ? e2 : new Error(String(e2));
39916
+ if (err.message.startsWith("Approval transaction failed")) throw err;
39917
+ }
39918
+ }
39919
+ }
39920
+ } catch (e2) {
39921
+ const msg = e2 instanceof Error ? e2.message : String(e2);
39922
+ if (msg.includes("insufficient funds")) {
39923
+ throw new Error(`Insufficient ${chainConfig?.symbol || "native token"} for approval gas. Fund your wallet with ${chainConfig?.symbol || "native tokens"} first.`);
39924
+ }
39925
+ if (msg.includes("failed")) throw e2;
39926
+ info(`Warning: could not check/send approval: ${msg}`);
39927
+ }
39928
+ }
39929
+ }
39930
+ }
39516
39931
  const pollForConfirmation = async (hash, timeoutMs) => {
39517
39932
  const deadline = Date.now() + timeoutMs;
39518
39933
  while (Date.now() < deadline) {
@@ -39585,10 +40000,11 @@ init_dist2();
39585
40000
  init_dist4();
39586
40001
  function registerErc20Command(program3) {
39587
40002
  const erc20 = program3.command("erc20").description("ERC-20 token operations");
39588
- erc20.command("approve").description("Approve a spender for token spending").requiredOption("--token <assetId>", "Token asset ID (e.g., c60_t0xA0b8...)").requiredOption("--spender <address>", "Spender address").requiredOption("--amount <n>", 'Amount to approve (in smallest unit, or "unlimited")').requiredOption("--password <password>", "Wallet password").option("--confirm-unlimited", "Required when --amount unlimited to acknowledge the security risk").option("--json", "JSON output").action(async (opts) => {
40003
+ erc20.command("approve").description("Approve a spender for token spending").requiredOption("--token <assetId>", "Token asset ID (e.g., c60_t0xA0b8...)").requiredOption("--spender <address>", "Spender address").requiredOption("--amount <n>", 'Amount to approve (in smallest unit, or "unlimited")').option("--password <password>", "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)").option("--confirm-unlimited", "Required when --amount unlimited to acknowledge the security risk").option("--json", "JSON output").action(async (opts) => {
39589
40004
  try {
39590
40005
  await ensureTrustSDK();
39591
- const provider = await AgentWalletProvider.fromStorage(opts.password);
40006
+ const password = await resolvePassword(opts);
40007
+ const provider = await AgentWalletProvider.fromStorage(password);
39592
40008
  const { coinId, contractAddress } = parseTokenId(opts.token);
39593
40009
  if (!contractAddress) {
39594
40010
  const msg = "Cannot approve native tokens. Provide an ERC-20 asset ID (e.g., c60_t0xA0b8...)";
@@ -39648,10 +40064,11 @@ function registerErc20Command(program3) {
39648
40064
  }
39649
40065
  process.exit();
39650
40066
  });
39651
- erc20.command("revoke").description("Revoke token approval (set allowance to 0)").requiredOption("--token <assetId>", "Token asset ID (e.g., c60_t0xA0b8...)").requiredOption("--spender <address>", "Spender address to revoke").requiredOption("--password <password>", "Wallet password").option("--json", "JSON output").action(async (opts) => {
40067
+ erc20.command("revoke").description("Revoke token approval (set allowance to 0)").requiredOption("--token <assetId>", "Token asset ID (e.g., c60_t0xA0b8...)").requiredOption("--spender <address>", "Spender address to revoke").option("--password <password>", "Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)").option("--json", "JSON output").action(async (opts) => {
39652
40068
  try {
39653
40069
  await ensureTrustSDK();
39654
- const provider = await AgentWalletProvider.fromStorage(opts.password);
40070
+ const password = await resolvePassword(opts);
40071
+ const provider = await AgentWalletProvider.fromStorage(password);
39655
40072
  const { coinId, contractAddress } = parseTokenId(opts.token);
39656
40073
  if (!contractAddress) {
39657
40074
  const msg = "Cannot revoke approval for native tokens. Provide an ERC-20 asset ID.";
@@ -39739,7 +40156,7 @@ function resolveTokenLabel(tokenId) {
39739
40156
  function registerAlertCommand(program3) {
39740
40157
  const alert = program3.command("alert").description("Manage price alerts");
39741
40158
  const alertStore = new AlertStore();
39742
- alert.command("create").description("Create a price alert").requiredOption("--token <tokenId>", "Token ID (e.g., c60, c20000714)").requiredOption("--chain <key>", "Chain key").option("--above <price>", "Alert when price goes above").option("--below <price>", "Alert when price goes below").option("--json", "JSON output").action(async (opts) => {
40159
+ alert.command("create").description("Create a price alert").requiredOption("--token <tokenId>", "Token symbol (e.g., ETH, BTC) or asset ID (e.g., c60)").option("--chain <key>", "Chain key (auto-detected for native tokens like ETH, BNB, SOL)").option("--above <price>", "Alert when price goes above").option("--below <price>", "Alert when price goes below").option("--json", "JSON output").action(async (opts) => {
39743
40160
  const aboveProvided = typeof opts.above !== "undefined";
39744
40161
  const belowProvided = typeof opts.below !== "undefined";
39745
40162
  if (aboveProvided && belowProvided || !aboveProvided && !belowProvided) {
@@ -39756,23 +40173,73 @@ function registerAlertCommand(program3) {
39756
40173
  process.exitCode = 1;
39757
40174
  return;
39758
40175
  }
39759
- if (!isChainSupported(opts.chain)) {
39760
- error(`Unknown chain: ${opts.chain}. Run \`twak chains\` to see supported chains.`);
39761
- if (opts.json) output({ error: `Unknown chain: ${opts.chain}` }, true);
39762
- process.exitCode = 1;
39763
- return;
39764
- }
40176
+ let resolvedTokenId = opts.token;
40177
+ let resolvedChain = opts.chain;
39765
40178
  try {
39766
40179
  parseTokenId(opts.token);
40180
+ if (!resolvedChain) {
40181
+ resolvedChain = tokenIdToChain(opts.token);
40182
+ if (!resolvedChain) {
40183
+ error("Could not detect chain from asset ID. Provide --chain.");
40184
+ if (opts.json) output({ error: "Chain required for this asset ID" }, true);
40185
+ process.exitCode = 1;
40186
+ return;
40187
+ }
40188
+ }
39767
40189
  } catch {
39768
- error(`Invalid token ID format: ${opts.token}. Expected format: c60 or c60_t0xABC...`);
39769
- if (opts.json) output({ error: `Invalid token ID: ${opts.token}` }, true);
40190
+ const sym = opts.token.toUpperCase();
40191
+ const NATIVE_SYMBOLS2 = {
40192
+ ETH: "ethereum",
40193
+ BNB: "bsc",
40194
+ SOL: "solana",
40195
+ BTC: "bitcoin",
40196
+ MATIC: "polygon",
40197
+ AVAX: "avalanche",
40198
+ FTM: "fantom",
40199
+ TRX: "tron",
40200
+ DOT: "polkadot",
40201
+ ATOM: "cosmos",
40202
+ NEAR: "near",
40203
+ APT: "aptos",
40204
+ TON: "ton",
40205
+ SUI: "sui",
40206
+ LTC: "litecoin",
40207
+ DOGE: "dogecoin",
40208
+ CELO: "celo",
40209
+ S: "sonic"
40210
+ };
40211
+ if (!resolvedChain && NATIVE_SYMBOLS2[sym]) {
40212
+ resolvedChain = NATIVE_SYMBOLS2[sym];
40213
+ }
40214
+ if (!resolvedChain) {
40215
+ error(`Cannot resolve "${opts.token}" without --chain. Provide --chain or use asset ID format (c60).`);
40216
+ if (opts.json) output({ error: "Chain required" }, true);
40217
+ process.exitCode = 1;
40218
+ return;
40219
+ }
40220
+ const chain2 = getChainById(resolvedChain);
40221
+ if (!chain2) {
40222
+ error(`Unknown chain: ${resolvedChain}. Run \`twak chains\` to see supported chains.`);
40223
+ if (opts.json) output({ error: `Unknown chain: ${resolvedChain}` }, true);
40224
+ process.exitCode = 1;
40225
+ return;
40226
+ }
40227
+ const resolved = resolveToken(sym, resolvedChain);
40228
+ if (resolved.address === NATIVE_TOKEN_ADDRESS || resolved.address === sym) {
40229
+ resolvedTokenId = formatTokenId(chain2.coinId);
40230
+ } else {
40231
+ resolvedTokenId = formatTokenId(chain2.coinId, resolved.address);
40232
+ }
40233
+ }
40234
+ if (!isChainSupported(resolvedChain)) {
40235
+ error(`Unknown chain: ${resolvedChain}. Run \`twak chains\` to see supported chains.`);
40236
+ if (opts.json) output({ error: `Unknown chain: ${resolvedChain}` }, true);
39770
40237
  process.exitCode = 1;
39771
40238
  return;
39772
40239
  }
39773
40240
  const record2 = await alertStore.create({
39774
- tokenId: opts.token,
39775
- chainKey: opts.chain,
40241
+ tokenId: resolvedTokenId,
40242
+ chainKey: resolvedChain,
39776
40243
  condition,
39777
40244
  targetPrice
39778
40245
  });
@@ -45312,6 +45779,171 @@ var MarketActionProvider = class extends ActionProvider {
45312
45779
  };
45313
45780
  }
45314
45781
  };
45782
+ var AutomateActionProvider = class extends ActionProvider {
45783
+ constructor(automationStore, swapClient, walletProvider, blockchainClient) {
45784
+ super();
45785
+ this.automationStore = automationStore;
45786
+ this.swapClient = swapClient;
45787
+ this.walletProvider = walletProvider;
45788
+ this.blockchainClient = blockchainClient;
45789
+ }
45790
+ name = "automate";
45791
+ description = "DCA and limit-order automation for recurring and conditional swaps";
45792
+ getActions() {
45793
+ return [
45794
+ this.createAutomation(),
45795
+ this.listAutomations(),
45796
+ this.deleteAutomation(),
45797
+ this.runAutomationNow()
45798
+ ];
45799
+ }
45800
+ createAutomation() {
45801
+ return {
45802
+ name: "create_automation",
45803
+ description: "Create a DCA (recurring buy) or limit order (buy/sell when price condition is met)",
45804
+ schema: external_exports.object({
45805
+ type: external_exports.enum(["dca", "limit"]).describe("Automation type: dca (recurring) or limit (price trigger)"),
45806
+ fromToken: external_exports.string().describe("Source token symbol or address"),
45807
+ toToken: external_exports.string().describe("Destination token symbol or address"),
45808
+ chain: external_exports.string().default("ethereum").describe("Chain name"),
45809
+ amount: external_exports.string().describe("Amount of fromToken per execution"),
45810
+ intervalMs: external_exports.coerce.number().optional().describe("Interval in milliseconds for DCA (e.g., 86400000 for daily)"),
45811
+ targetPrice: external_exports.coerce.number().optional().describe("Target price in USD for limit orders"),
45812
+ condition: external_exports.enum(["above", "below"]).optional().describe("Price condition for limit orders")
45813
+ }),
45814
+ invoke: async (_ctx, input) => {
45815
+ if (input.type === "dca" && !input.intervalMs) {
45816
+ return { success: false, error: "DCA automations require intervalMs" };
45817
+ }
45818
+ if (input.type === "limit" && (input.targetPrice == null || !input.condition)) {
45819
+ return { success: false, error: "Limit orders require targetPrice and condition" };
45820
+ }
45821
+ const record2 = await this.automationStore.create({
45822
+ type: input.type,
45823
+ fromToken: input.fromToken,
45824
+ toToken: input.toToken,
45825
+ chain: input.chain,
45826
+ amount: input.amount,
45827
+ intervalMs: input.intervalMs,
45828
+ targetPrice: input.targetPrice,
45829
+ condition: input.condition
45830
+ });
45831
+ 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}`;
45832
+ return { success: true, id: record2.id, summary, record: record2 };
45833
+ }
45834
+ };
45835
+ }
45836
+ listAutomations() {
45837
+ return {
45838
+ name: "list_automations",
45839
+ description: "List all active automation rules (DCA and limit orders)",
45840
+ schema: external_exports.object({
45841
+ activeOnly: external_exports.boolean().optional().default(true).describe("Only show active automations")
45842
+ }),
45843
+ invoke: async (_ctx, input) => {
45844
+ const all = await this.automationStore.list();
45845
+ const automations = input.activeOnly ? all.filter((a3) => a3.active) : all;
45846
+ return { success: true, count: automations.length, automations };
45847
+ }
45848
+ };
45849
+ }
45850
+ deleteAutomation() {
45851
+ return {
45852
+ name: "delete_automation",
45853
+ description: "Delete (deactivate) an automation rule",
45854
+ schema: external_exports.object({
45855
+ id: external_exports.string().describe("Automation ID to delete")
45856
+ }),
45857
+ invoke: async (_ctx, input) => {
45858
+ const deleted = await this.automationStore.delete(input.id);
45859
+ return deleted ? { success: true, message: `Automation ${input.id} deleted` } : { success: false, error: `Automation ${input.id} not found` };
45860
+ }
45861
+ };
45862
+ }
45863
+ async sendSwapTx(txData, chainConfig, signer) {
45864
+ if (chainConfig?.namespace === "solana" && txData.transaction.solanaTx) {
45865
+ return signer.sendTransaction({
45866
+ chainId: `solana:${chainConfig.chainId}`,
45867
+ to: "",
45868
+ data: txData.transaction.solanaTx.serializedMessage
45869
+ });
45870
+ } else if (txData.transaction.evmTx) {
45871
+ return signer.sendTransaction({
45872
+ chainId: `eip155:${chainConfig?.chainId || "1"}`,
45873
+ to: txData.transaction.evmTx.to,
45874
+ data: txData.transaction.evmTx.data,
45875
+ value: txData.transaction.evmTx.value || "0x0"
45876
+ });
45877
+ }
45878
+ throw new Error("No transaction data returned for this swap step");
45879
+ }
45880
+ async pollConfirmation(chainKey, address, hash, timeoutMs) {
45881
+ if (!this.blockchainClient) return;
45882
+ const deadline = Date.now() + timeoutMs;
45883
+ while (Date.now() < deadline) {
45884
+ await new Promise((r3) => setTimeout(r3, 2e3));
45885
+ try {
45886
+ const status = await this.blockchainClient.findTransactionStatus(chainKey, address, hash);
45887
+ if (status.confirmed) return;
45888
+ if (status.failed) throw new Error(`Transaction failed: ${status.error || "unknown error"}`);
45889
+ } catch (e2) {
45890
+ const err = e2 instanceof Error ? e2 : new Error(String(e2));
45891
+ if (err.message.startsWith("Transaction failed")) throw err;
45892
+ }
45893
+ }
45894
+ throw new Error(`Transaction ${hash} was not confirmed within ${Math.round(timeoutMs / 1e3)}s`);
45895
+ }
45896
+ runAutomationNow() {
45897
+ return {
45898
+ name: "run_automation_now",
45899
+ description: "Manually execute an automation immediately (runs the swap now regardless of schedule)",
45900
+ schema: external_exports.object({
45901
+ id: external_exports.string().describe("Automation ID to run")
45902
+ }),
45903
+ invoke: async (_ctx, input) => {
45904
+ if (!this.walletProvider || !this.swapClient) {
45905
+ return { success: false, error: "Agent wallet and swap client required to execute automation" };
45906
+ }
45907
+ const record2 = await this.automationStore.getById(input.id);
45908
+ if (!record2) return { success: false, error: `Automation ${input.id} not found` };
45909
+ if (!record2.active) return { success: false, error: `Automation ${input.id} is inactive` };
45910
+ const fromTokenInfo = resolveToken(record2.fromToken, record2.chain);
45911
+ const toTokenInfo = resolveToken(record2.toToken, record2.chain);
45912
+ const parsedAmount = parseAmount(record2.amount, fromTokenInfo.decimals);
45913
+ const address = await this.walletProvider.getAddress(record2.chain);
45914
+ const quote = await this.swapClient.getQuote({
45915
+ fromChain: record2.chain,
45916
+ fromToken: fromTokenInfo.address,
45917
+ toChain: record2.chain,
45918
+ toToken: toTokenInfo.address,
45919
+ amount: parsedAmount,
45920
+ walletAddress: address
45921
+ });
45922
+ if (quote.routes.length === 0) {
45923
+ return { success: false, error: "No swap routes found" };
45924
+ }
45925
+ const route = quote.routes[0];
45926
+ const chainConfig = getChainById(record2.chain);
45927
+ const transactions = await this.swapClient.getSwapTransactions(route);
45928
+ let txHash = "";
45929
+ for (const txData of transactions) {
45930
+ txHash = await this.sendSwapTx(txData, chainConfig, this.walletProvider);
45931
+ const isApproval = txData.transaction.type === "approval" || txData.transaction.type === "revokeApproval";
45932
+ await this.pollConfirmation(record2.chain, address, txHash, isApproval ? 6e4 : 12e4);
45933
+ }
45934
+ await this.automationStore.update(record2.id, { lastRunAt: (/* @__PURE__ */ new Date()).toISOString() });
45935
+ const explorerUrl = getExplorerTxUrl(record2.chain, txHash);
45936
+ return {
45937
+ success: true,
45938
+ txHash,
45939
+ automationId: record2.id,
45940
+ swapped: `${record2.amount} ${record2.fromToken} \u2192 ${record2.toToken}`,
45941
+ explorer: explorerUrl || void 0
45942
+ };
45943
+ }
45944
+ };
45945
+ }
45946
+ };
45315
45947
 
45316
45948
  // src/commands/serve.ts
45317
45949
  init_dist();
@@ -54110,6 +54742,7 @@ function registerServeCommand(program3) {
54110
54742
  const client = await getClient();
54111
54743
  const swapClient = new SwapClient(client);
54112
54744
  const alertStore = new AlertStore();
54745
+ const automationStore = new AutomationStore();
54113
54746
  const blockchainClient = new BlockchainClient();
54114
54747
  let agentWallet;
54115
54748
  try {
@@ -54149,6 +54782,7 @@ function registerServeCommand(program3) {
54149
54782
  registry2.register(new AgentWalletActionProvider(blockchainClient));
54150
54783
  registry2.register(new TokenActionProvider(blockchainClient, agentWallet));
54151
54784
  registry2.register(new MarketActionProvider());
54785
+ registry2.register(new AutomateActionProvider(automationStore, swapClient, walletProvider, blockchainClient));
54152
54786
  const context = {
54153
54787
  httpClient: client
54154
54788
  };
@@ -54180,6 +54814,221 @@ function registerServeCommand(program3) {
54180
54814
  });
54181
54815
  }
54182
54816
 
54817
+ // src/commands/automate.ts
54818
+ init_dist();
54819
+ init_dist2();
54820
+ function parseInterval(value) {
54821
+ const match = value.match(/^(\d+(?:\.\d+)?)\s*(s|m|h)?$/i);
54822
+ if (!match) return null;
54823
+ const num = parseFloat(match[1]);
54824
+ if (isNaN(num)) return null;
54825
+ const unit = (match[2] || "s").toLowerCase();
54826
+ if (unit === "s") return num * 1e3;
54827
+ if (unit === "m") return num * 6e4;
54828
+ if (unit === "h") return num * 36e5;
54829
+ return num * 1e3;
54830
+ }
54831
+ function formatInterval(ms3) {
54832
+ if (ms3 >= 36e5) return `${ms3 / 36e5}h`;
54833
+ if (ms3 >= 6e4) return `${ms3 / 6e4}m`;
54834
+ return `${ms3 / 1e3}s`;
54835
+ }
54836
+ function formatRelativeTime(diffMs) {
54837
+ const absDiff = Math.abs(diffMs);
54838
+ let value;
54839
+ if (absDiff < 6e4) {
54840
+ value = `${Math.round(absDiff / 1e3)}s`;
54841
+ } else if (absDiff < 36e5) {
54842
+ value = `${Math.round(absDiff / 6e4)}m`;
54843
+ } else {
54844
+ value = `${Math.round(absDiff / 36e5)}h`;
54845
+ }
54846
+ if (diffMs <= 0) return "overdue";
54847
+ return `in ${value}`;
54848
+ }
54849
+ function formatTimeAgo(isoDate) {
54850
+ const diffMs = Date.now() - new Date(isoDate).getTime();
54851
+ const absDiff = Math.abs(diffMs);
54852
+ if (absDiff < 6e4) return `${Math.round(absDiff / 1e3)}s ago`;
54853
+ if (absDiff < 36e5) return `${Math.round(absDiff / 6e4)}m ago`;
54854
+ return `${Math.round(absDiff / 36e5)}h ago`;
54855
+ }
54856
+ function registerAutomateCommand(program3) {
54857
+ const automate = program3.command("automate").description("Manage DCA and limit order automations");
54858
+ automate.command("add").description("Create a DCA or limit order automation").requiredOption("--from <token>", "Source token (e.g., USDC)").requiredOption("--to <token>", "Destination token (e.g., ETH)").option("--chain <chain>", "Chain", "ethereum").requiredOption("--amount <n>", "Amount of source token per run").option("--interval <duration>", "DCA interval \u2014 e.g. 30s, 5m, 1h (creates DCA automation)").option("--price <usd>", "Target price in USD (creates limit order)").option("--condition <cond>", "Price condition: above or below", "below").option("--max-runs <n>", "Stop after N executions").option("--expires <date>", "Expiry date (ISO 8601, e.g. 2026-04-01)").option("--json", "JSON output").action(async (opts) => {
54859
+ try {
54860
+ if (!opts.interval && !opts.price) {
54861
+ error("Provide --interval (DCA) or --price (limit order)");
54862
+ process.exitCode = 1;
54863
+ return;
54864
+ }
54865
+ const amount = parseFloat(opts.amount);
54866
+ if (isNaN(amount) || amount <= 0) {
54867
+ error("Amount must be a positive number");
54868
+ process.exitCode = 1;
54869
+ return;
54870
+ }
54871
+ if (!isChainSupported(opts.chain)) {
54872
+ error(`Unsupported chain: ${opts.chain}. Run 'twak chains' to list supported chains.`);
54873
+ process.exitCode = 1;
54874
+ return;
54875
+ }
54876
+ let intervalMs;
54877
+ if (opts.interval) {
54878
+ const parsed = parseInterval(opts.interval);
54879
+ if (parsed === null || parsed < 5e3) {
54880
+ error("Interval must be at least 5s (e.g. 30s, 5m, 1h)");
54881
+ process.exitCode = 1;
54882
+ return;
54883
+ }
54884
+ intervalMs = parsed;
54885
+ }
54886
+ const maxRuns = opts.maxRuns ? parseInt(opts.maxRuns) : void 0;
54887
+ if (maxRuns !== void 0 && (isNaN(maxRuns) || maxRuns < 1)) {
54888
+ error("--max-runs must be a positive integer");
54889
+ process.exitCode = 1;
54890
+ return;
54891
+ }
54892
+ let expiresAt;
54893
+ if (opts.expires) {
54894
+ const d3 = new Date(opts.expires);
54895
+ if (isNaN(d3.getTime())) {
54896
+ error("--expires must be a valid date (e.g. 2026-04-01)");
54897
+ process.exitCode = 1;
54898
+ return;
54899
+ }
54900
+ expiresAt = d3.toISOString();
54901
+ }
54902
+ const store = new AutomationStore();
54903
+ const record2 = await store.create({
54904
+ type: opts.interval ? "dca" : "limit",
54905
+ fromToken: opts.from,
54906
+ toToken: opts.to,
54907
+ chain: opts.chain,
54908
+ amount: opts.amount,
54909
+ intervalMs,
54910
+ targetPrice: opts.price ? parseFloat(opts.price) : void 0,
54911
+ condition: opts.price ? opts.condition : void 0,
54912
+ maxRuns,
54913
+ runCount: 0,
54914
+ expiresAt
54915
+ });
54916
+ success("Automation created!");
54917
+ if (opts.interval && intervalMs) {
54918
+ info(`DCA: ${opts.amount} ${opts.from} \u2192 ${opts.to} every ${formatInterval(intervalMs)} on ${opts.chain}`);
54919
+ } else {
54920
+ info(`Limit: swap ${opts.amount} ${opts.from} \u2192 ${opts.to} when price ${opts.condition} $${opts.price} on ${opts.chain}`);
54921
+ }
54922
+ output(record2, opts.json);
54923
+ } catch (err) {
54924
+ const msg = err instanceof Error ? err.message : String(err);
54925
+ error(msg);
54926
+ if (opts.json) output({ error: msg }, true);
54927
+ process.exitCode = 1;
54928
+ }
54929
+ });
54930
+ automate.command("list").description("List active automations").option("--all", "Include inactive automations").option("--json", "JSON output").action(async (opts) => {
54931
+ try {
54932
+ const store = new AutomationStore();
54933
+ const all = await store.list();
54934
+ const records = opts.all ? all : all.filter((a3) => a3.active);
54935
+ if (opts.json) {
54936
+ const enriched = records.map((r3) => {
54937
+ const nextRunAt = r3.type === "dca" && r3.intervalMs ? r3.lastRunAt ? new Date(new Date(r3.lastRunAt).getTime() + r3.intervalMs).toISOString() : (/* @__PURE__ */ new Date()).toISOString() : null;
54938
+ const nextRunIn = nextRunAt ? formatRelativeTime(new Date(nextRunAt).getTime() - Date.now()) : null;
54939
+ return { ...r3, nextRunAt, nextRunIn };
54940
+ });
54941
+ output(enriched, true);
54942
+ return;
54943
+ }
54944
+ if (records.length === 0) {
54945
+ info("No automations found.");
54946
+ return;
54947
+ }
54948
+ for (const r3 of records) {
54949
+ const tag = r3.id.slice(0, 8);
54950
+ const label = r3.type.toUpperCase();
54951
+ const pair = `${r3.amount} ${r3.fromToken}\u2192${r3.toToken} (${r3.chain})`;
54952
+ if (r3.type === "dca") {
54953
+ const interval = formatInterval(r3.intervalMs || 0);
54954
+ const lastPart = r3.lastRunAt ? `last: ${formatTimeAgo(r3.lastRunAt)}` : "never run";
54955
+ let nextPart;
54956
+ if (!r3.intervalMs) {
54957
+ nextPart = "next: unknown";
54958
+ } else if (!r3.lastRunAt) {
54959
+ nextPart = "next: now";
54960
+ } else {
54961
+ const nextAt = new Date(r3.lastRunAt).getTime() + r3.intervalMs;
54962
+ nextPart = `next: ${formatRelativeTime(nextAt - Date.now())}`;
54963
+ }
54964
+ console.log(`[${tag}] ${label} ${pair} every ${interval} \u2014 ${lastPart}, ${nextPart}`);
54965
+ } else {
54966
+ const cond = `${r3.fromToken} ${r3.condition} $${r3.targetPrice}`;
54967
+ console.log(`[${tag}] ${label} ${pair} when ${cond} \u2014 waiting`);
54968
+ }
54969
+ }
54970
+ } catch (err) {
54971
+ const msg = err instanceof Error ? err.message : String(err);
54972
+ error(msg);
54973
+ if (opts.json) output({ error: msg }, true);
54974
+ process.exitCode = 1;
54975
+ }
54976
+ });
54977
+ automate.command("delete <id>").description("Delete an automation by ID").option("--json", "JSON output").action(async (id, opts) => {
54978
+ try {
54979
+ const store = new AutomationStore();
54980
+ const deleted = await store.delete(id);
54981
+ if (deleted) {
54982
+ success(`Automation ${id} deleted`);
54983
+ } else {
54984
+ error(`Automation ${id} not found`);
54985
+ process.exitCode = 1;
54986
+ }
54987
+ } catch (err) {
54988
+ const msg = err instanceof Error ? err.message : String(err);
54989
+ error(msg);
54990
+ if (opts.json) output({ error: msg }, true);
54991
+ process.exitCode = 1;
54992
+ }
54993
+ });
54994
+ automate.command("pause <id>").description("Pause an automation").option("--json", "JSON output").action(async (id, opts) => {
54995
+ try {
54996
+ const store = new AutomationStore();
54997
+ const updated = await store.update(id, { active: false });
54998
+ if (updated) {
54999
+ success(`Automation ${id} paused`);
55000
+ output(updated, opts.json);
55001
+ } else {
55002
+ error(`Automation ${id} not found`);
55003
+ process.exitCode = 1;
55004
+ }
55005
+ } catch (err) {
55006
+ const msg = err instanceof Error ? err.message : String(err);
55007
+ error(msg);
55008
+ if (opts.json) output({ error: msg }, true);
55009
+ process.exitCode = 1;
55010
+ }
55011
+ });
55012
+ automate.command("resume <id>").description("Resume a paused automation").option("--json", "JSON output").action(async (id, opts) => {
55013
+ try {
55014
+ const store = new AutomationStore();
55015
+ const updated = await store.update(id, { active: true });
55016
+ if (updated) {
55017
+ success(`Automation ${id} resumed`);
55018
+ output(updated, opts.json);
55019
+ } else {
55020
+ error(`Automation ${id} not found`);
55021
+ process.exitCode = 1;
55022
+ }
55023
+ } catch (err) {
55024
+ const msg = err instanceof Error ? err.message : String(err);
55025
+ error(msg);
55026
+ if (opts.json) output({ error: msg }, true);
55027
+ process.exitCode = 1;
55028
+ }
55029
+ });
55030
+ }
55031
+
54183
55032
  // src/commands/skill.ts
54184
55033
  var import_fs = require("fs");
54185
55034
  var import_path = require("path");
@@ -54397,12 +55246,12 @@ Confirm? [y/N] `,
54397
55246
  var fs6 = __toESM(require("fs"));
54398
55247
  var os4 = __toESM(require("os"));
54399
55248
  var path4 = __toESM(require("path"));
54400
- var crypto5 = __toESM(require("crypto"));
55249
+ var crypto6 = __toESM(require("crypto"));
54401
55250
 
54402
55251
  // package.json
54403
55252
  var package_default = {
54404
55253
  name: "@trustwallet/cli",
54405
- version: "0.6.10",
55254
+ version: "0.7.0",
54406
55255
  description: "TWAK CLI \u2014 command-line interface for Trust Wallet Agent Kit",
54407
55256
  main: "dist/index.js",
54408
55257
  types: "dist/index.d.ts",
@@ -54502,7 +55351,7 @@ function getUserId() {
54502
55351
  if (fs6.existsSync(ID_FILE)) return fs6.readFileSync(ID_FILE, "utf-8").trim();
54503
55352
  } catch (_e6) {
54504
55353
  }
54505
- const id = crypto5.randomUUID();
55354
+ const id = crypto6.randomUUID();
54506
55355
  try {
54507
55356
  fs6.mkdirSync(CONFIG_DIR, { recursive: true });
54508
55357
  fs6.writeFileSync(ID_FILE, id);
@@ -54559,6 +55408,200 @@ function registerTelemetryCommand(program3) {
54559
55408
  });
54560
55409
  }
54561
55410
 
55411
+ // src/commands/watch.ts
55412
+ init_dist2();
55413
+ init_dist();
55414
+ init_dist4();
55415
+ function ts3() {
55416
+ return (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
55417
+ }
55418
+ function parseInterval2(value) {
55419
+ const match = value.match(/^(\d+(?:\.\d+)?)\s*(s|m|h)?$/i);
55420
+ if (!match) return null;
55421
+ const num = parseFloat(match[1]);
55422
+ if (isNaN(num)) return null;
55423
+ const unit = (match[2] || "s").toLowerCase();
55424
+ if (unit === "m") return num * 60;
55425
+ if (unit === "h") return num * 3600;
55426
+ return num;
55427
+ }
55428
+ function registerWatchCommand(program3) {
55429
+ program3.command("watch").description("Background watcher \u2014 polls prices to trigger alerts and execute DCA/limit automations").option("--interval <duration>", "Poll interval \u2014 e.g. 30s, 5m, 1h, or plain number (seconds)", "60").option("--password <password>", "Wallet password for executing swaps").option("--auto-lock <minutes>", "Auto-lock wallet after N minutes of inactivity").option("--dry-run", "Check conditions without executing swaps").option("--json", "JSON output per tick").action(async (opts) => {
55430
+ const intervalSec = parseInterval2(opts.interval);
55431
+ if (intervalSec === null || intervalSec < 5) {
55432
+ error("Interval must be at least 5 seconds (e.g. 30s, 5m, 1h)");
55433
+ process.exitCode = 1;
55434
+ return;
55435
+ }
55436
+ let client;
55437
+ let swapClient;
55438
+ let provider = null;
55439
+ let executor = null;
55440
+ let amberDomains = /* @__PURE__ */ new Map();
55441
+ try {
55442
+ client = await getClient();
55443
+ swapClient = new SwapClient(client);
55444
+ if (!opts.dryRun) {
55445
+ await ensureTrustSDK();
55446
+ const password = await resolvePassword(opts);
55447
+ provider = await AgentWalletProvider.fromStorage(password);
55448
+ if (opts.autoLock) {
55449
+ const minutes = parseInt(opts.autoLock, 10);
55450
+ if (isNaN(minutes) || minutes <= 0) {
55451
+ error("--auto-lock must be a positive number of minutes");
55452
+ process.exitCode = 1;
55453
+ return;
55454
+ }
55455
+ provider.setAutoLock({
55456
+ autoLockMinutes: minutes,
55457
+ passwordResolver: () => resolvePassword(opts)
55458
+ });
55459
+ }
55460
+ amberDomains = await getAmberDomainsByCoinId();
55461
+ executor = new SwapExecutor(swapClient, provider, {
55462
+ amberDomains,
55463
+ onStatus: (msg) => {
55464
+ info(`[${ts3()}] ${msg}`);
55465
+ }
55466
+ });
55467
+ }
55468
+ } catch (err) {
55469
+ error(`Init failed: ${err instanceof Error ? err.message : String(err)}`);
55470
+ process.exitCode = 1;
55471
+ return;
55472
+ }
55473
+ const alertStore = new AlertStore();
55474
+ const automationStore = new AutomationStore();
55475
+ info(`[${ts3()}] Watcher started \u2014 polling every ${intervalSec}s${opts.dryRun ? " (dry run)" : ""}`);
55476
+ const tick = async () => {
55477
+ const tickResults = {
55478
+ alerts: { checked: 0, triggered: [] },
55479
+ automations: { executed: [], skipped: 0 }
55480
+ };
55481
+ try {
55482
+ const activeAlerts = await alertStore.getActive();
55483
+ tickResults.alerts.checked = activeAlerts.length;
55484
+ for (const alert of activeAlerts) {
55485
+ try {
55486
+ const { contractAddress } = parseTokenId(alert.tokenId);
55487
+ const price = await swapClient.getTokenPrice(alert.chainKey, contractAddress || "");
55488
+ if (price === null) continue;
55489
+ const triggered = alert.condition === "above" && price >= alert.targetPrice || alert.condition === "below" && price <= alert.targetPrice;
55490
+ if (triggered) {
55491
+ await alertStore.update(alert.id, { active: false, triggeredAt: (/* @__PURE__ */ new Date()).toISOString() });
55492
+ const detail = { id: alert.id, tokenId: alert.tokenId, condition: alert.condition, targetPrice: alert.targetPrice, currentPrice: price };
55493
+ tickResults.alerts.triggered.push(detail);
55494
+ success(`[${ts3()}] ALERT TRIGGERED: ${alert.tokenId} price $${price} is ${alert.condition} $${alert.targetPrice}`);
55495
+ }
55496
+ } catch (err) {
55497
+ warn(`[${ts3()}] Alert ${alert.id} check failed: ${err instanceof Error ? err.message : String(err)}`);
55498
+ }
55499
+ }
55500
+ const automations = await automationStore.getActive();
55501
+ const now = Date.now();
55502
+ for (const auto of automations) {
55503
+ try {
55504
+ if (auto.expiresAt && new Date(auto.expiresAt).getTime() <= now) {
55505
+ await automationStore.update(auto.id, { active: false });
55506
+ dim(`[${ts3()}] Automation ${auto.id.slice(0, 8)} expired`);
55507
+ tickResults.automations.skipped++;
55508
+ continue;
55509
+ }
55510
+ if (auto.maxRuns && (auto.runCount ?? 0) >= auto.maxRuns) {
55511
+ await automationStore.update(auto.id, { active: false });
55512
+ dim(`[${ts3()}] Automation ${auto.id.slice(0, 8)} reached max runs (${auto.maxRuns})`);
55513
+ tickResults.automations.skipped++;
55514
+ continue;
55515
+ }
55516
+ if (auto.type === "dca") {
55517
+ const lastRun = auto.lastRunAt ? new Date(auto.lastRunAt).getTime() : 0;
55518
+ const intervalMs = auto.intervalMs || 0;
55519
+ if (lastRun + intervalMs > now) {
55520
+ tickResults.automations.skipped++;
55521
+ continue;
55522
+ }
55523
+ if (opts.dryRun) {
55524
+ dim(`[${ts3()}] DRY RUN: would swap ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`);
55525
+ tickResults.automations.executed.push({ id: auto.id, type: "dca", dryRun: true });
55526
+ continue;
55527
+ }
55528
+ info(`[${ts3()}] Executing DCA: ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`);
55529
+ const result = await executor.execute({
55530
+ fromChain: auto.chain,
55531
+ fromToken: auto.fromToken,
55532
+ toChain: auto.chain,
55533
+ toToken: auto.toToken,
55534
+ amount: auto.amount
55535
+ });
55536
+ if (result.success) {
55537
+ await automationStore.update(auto.id, { lastRunAt: (/* @__PURE__ */ new Date()).toISOString(), runCount: (auto.runCount ?? 0) + 1 });
55538
+ success(`[${ts3()}] DCA executed: ${result.explorerUrl}`);
55539
+ tickResults.automations.executed.push({ id: auto.id, type: "dca", txHash: result.txHash, explorer: result.explorerUrl });
55540
+ } else {
55541
+ warn(`[${ts3()}] DCA ${auto.id} failed: ${result.error}`);
55542
+ }
55543
+ } else if (auto.type === "limit") {
55544
+ const toTokenInfo = resolveToken(auto.toToken, auto.chain);
55545
+ const price = await swapClient.getTokenPrice(auto.chain, toTokenInfo.address);
55546
+ if (price === null) continue;
55547
+ const triggered = auto.condition === "below" && price <= (auto.targetPrice || 0) || auto.condition === "above" && price >= (auto.targetPrice || 0);
55548
+ if (!triggered) {
55549
+ tickResults.automations.skipped++;
55550
+ continue;
55551
+ }
55552
+ if (opts.dryRun) {
55553
+ dim(`[${ts3()}] DRY RUN: limit order triggered \u2014 would swap ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`);
55554
+ tickResults.automations.executed.push({ id: auto.id, type: "limit", dryRun: true });
55555
+ continue;
55556
+ }
55557
+ info(`[${ts3()}] Limit order triggered: swapping ${auto.amount} ${auto.fromToken} -> ${auto.toToken} on ${auto.chain}`);
55558
+ const result = await executor.execute({
55559
+ fromChain: auto.chain,
55560
+ fromToken: auto.fromToken,
55561
+ toChain: auto.chain,
55562
+ toToken: auto.toToken,
55563
+ amount: auto.amount
55564
+ });
55565
+ if (result.success) {
55566
+ await automationStore.update(auto.id, { active: false, lastRunAt: (/* @__PURE__ */ new Date()).toISOString() });
55567
+ success(`[${ts3()}] Limit order executed: ${result.explorerUrl}`);
55568
+ tickResults.automations.executed.push({ id: auto.id, type: "limit", txHash: result.txHash, explorer: result.explorerUrl });
55569
+ } else {
55570
+ warn(`[${ts3()}] Limit order ${auto.id} failed: ${result.error}`);
55571
+ }
55572
+ }
55573
+ } catch (err) {
55574
+ warn(`[${ts3()}] Automation ${auto.id} failed: ${err instanceof Error ? err.message : String(err)}`);
55575
+ }
55576
+ }
55577
+ } catch (err) {
55578
+ error(`[${ts3()}] Tick error: ${err instanceof Error ? err.message : String(err)}`);
55579
+ }
55580
+ if (opts.json) {
55581
+ output({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), ...tickResults }, true);
55582
+ } else if (tickResults.alerts.triggered.length === 0 && tickResults.automations.executed.length === 0) {
55583
+ dim(`[${ts3()}] Checked ${tickResults.alerts.checked} alerts, ${tickResults.automations.skipped} automations \u2014 nothing to do`);
55584
+ }
55585
+ };
55586
+ await tick();
55587
+ const timer = setInterval(tick, intervalSec * 1e3);
55588
+ const shutdown = () => {
55589
+ clearInterval(timer);
55590
+ if (provider) {
55591
+ provider.disconnect().catch(() => {
55592
+ });
55593
+ }
55594
+ info(`
55595
+ [${ts3()}] Watcher stopped`);
55596
+ process.exit(0);
55597
+ };
55598
+ process.on("SIGINT", shutdown);
55599
+ process.on("SIGTERM", shutdown);
55600
+ await new Promise(() => {
55601
+ });
55602
+ });
55603
+ }
55604
+
54562
55605
  // src/index.ts
54563
55606
  var VERSION2 = package_default.version;
54564
55607
  var program2 = new Command();
@@ -54575,9 +55618,11 @@ registerErc20Command(program2);
54575
55618
  registerAlertCommand(program2);
54576
55619
  registerQueryCommand(program2);
54577
55620
  registerServeCommand(program2);
55621
+ registerAutomateCommand(program2);
54578
55622
  registerSkillCommand(program2);
54579
55623
  registerX402Command(program2);
54580
55624
  registerTelemetryCommand(program2);
55625
+ registerWatchCommand(program2);
54581
55626
  initAnalytics();
54582
55627
  program2.hook("preAction", (_thisCommand, actionCommand) => {
54583
55628
  trackEvent("twak_command", { command: actionCommand.name() });