@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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
6651
|
-
const fee = await
|
|
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
|
|
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(
|
|
14389
|
-
const types = Array.isArray(
|
|
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,
|
|
15432
|
-
if (
|
|
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,
|
|
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(
|
|
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(
|
|
15452
|
-
return
|
|
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
|
|
15698
|
+
const ts4 = [];
|
|
15456
15699
|
for (const t of it4.dataTypes) {
|
|
15457
15700
|
if (includesType(withTypes, t))
|
|
15458
|
-
|
|
15701
|
+
ts4.push(t);
|
|
15459
15702
|
else if (withTypes.includes("integer") && t === "number")
|
|
15460
|
-
|
|
15703
|
+
ts4.push("integer");
|
|
15461
15704
|
}
|
|
15462
|
-
it4.dataTypes =
|
|
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:
|
|
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
|
|
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 (
|
|
25166
|
-
return
|
|
25417
|
+
if (crypto4 && typeof crypto4.getRandomValues === "function") {
|
|
25418
|
+
return crypto4.getRandomValues(new Uint8Array(bytesLength));
|
|
25167
25419
|
}
|
|
25168
|
-
if (
|
|
25169
|
-
return Uint8Array.from(
|
|
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
|
-
|
|
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
|
|
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")').
|
|
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
|
|
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").
|
|
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
|
|
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
|
|
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
|
-
|
|
39760
|
-
|
|
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
|
-
|
|
39769
|
-
|
|
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:
|
|
39775
|
-
chainKey:
|
|
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
|
|
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.
|
|
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 =
|
|
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() });
|