@trustwallet/cli 0.6.11 → 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 +750 -21
- package/package.json +12 -12
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;
|
|
@@ -14582,8 +14628,8 @@ var require_dataType = __commonJS({
|
|
|
14582
14628
|
return types;
|
|
14583
14629
|
}
|
|
14584
14630
|
exports2.getSchemaTypes = getSchemaTypes;
|
|
14585
|
-
function getJSONTypes(
|
|
14586
|
-
const types = Array.isArray(
|
|
14631
|
+
function getJSONTypes(ts4) {
|
|
14632
|
+
const types = Array.isArray(ts4) ? ts4 : ts4 ? [ts4] : [];
|
|
14587
14633
|
if (types.every(rules_1.isJSONType))
|
|
14588
14634
|
return types;
|
|
14589
14635
|
throw new Error("type must be JSONType or JSONType[]: " + types.join(","));
|
|
@@ -15625,18 +15671,18 @@ var require_validate = __commonJS({
|
|
|
15625
15671
|
});
|
|
15626
15672
|
narrowSchemaTypes(it4, types);
|
|
15627
15673
|
}
|
|
15628
|
-
function checkMultipleTypes(it4,
|
|
15629
|
-
if (
|
|
15674
|
+
function checkMultipleTypes(it4, ts4) {
|
|
15675
|
+
if (ts4.length > 1 && !(ts4.length === 2 && ts4.includes("null"))) {
|
|
15630
15676
|
strictTypesError(it4, "use allowUnionTypes to allow union type keyword");
|
|
15631
15677
|
}
|
|
15632
15678
|
}
|
|
15633
|
-
function checkKeywordTypes(it4,
|
|
15679
|
+
function checkKeywordTypes(it4, ts4) {
|
|
15634
15680
|
const rules = it4.self.RULES.all;
|
|
15635
15681
|
for (const keyword in rules) {
|
|
15636
15682
|
const rule = rules[keyword];
|
|
15637
15683
|
if (typeof rule == "object" && (0, applicability_1.shouldUseRule)(it4.schema, rule)) {
|
|
15638
15684
|
const { type } = rule.definition;
|
|
15639
|
-
if (type.length && !type.some((t) => hasApplicableType(
|
|
15685
|
+
if (type.length && !type.some((t) => hasApplicableType(ts4, t))) {
|
|
15640
15686
|
strictTypesError(it4, `missing type "${type.join(",")}" for keyword "${keyword}"`);
|
|
15641
15687
|
}
|
|
15642
15688
|
}
|
|
@@ -15645,18 +15691,18 @@ var require_validate = __commonJS({
|
|
|
15645
15691
|
function hasApplicableType(schTs, kwdT) {
|
|
15646
15692
|
return schTs.includes(kwdT) || kwdT === "number" && schTs.includes("integer");
|
|
15647
15693
|
}
|
|
15648
|
-
function includesType(
|
|
15649
|
-
return
|
|
15694
|
+
function includesType(ts4, t) {
|
|
15695
|
+
return ts4.includes(t) || t === "integer" && ts4.includes("number");
|
|
15650
15696
|
}
|
|
15651
15697
|
function narrowSchemaTypes(it4, withTypes) {
|
|
15652
|
-
const
|
|
15698
|
+
const ts4 = [];
|
|
15653
15699
|
for (const t of it4.dataTypes) {
|
|
15654
15700
|
if (includesType(withTypes, t))
|
|
15655
|
-
|
|
15701
|
+
ts4.push(t);
|
|
15656
15702
|
else if (withTypes.includes("integer") && t === "number")
|
|
15657
|
-
|
|
15703
|
+
ts4.push("integer");
|
|
15658
15704
|
}
|
|
15659
|
-
it4.dataTypes =
|
|
15705
|
+
it4.dataTypes = ts4;
|
|
15660
15706
|
}
|
|
15661
15707
|
function strictTypesError(it4, msg) {
|
|
15662
15708
|
const schemaPath = it4.schemaEnv.baseId + it4.errSchemaPath;
|
|
@@ -22368,6 +22414,9 @@ function warn(message) {
|
|
|
22368
22414
|
function info(message) {
|
|
22369
22415
|
console.log(import_chalk.default.cyan(message));
|
|
22370
22416
|
}
|
|
22417
|
+
function dim(message) {
|
|
22418
|
+
console.log(import_chalk.default.dim(message));
|
|
22419
|
+
}
|
|
22371
22420
|
|
|
22372
22421
|
// src/config-loader.ts
|
|
22373
22422
|
var import_chalk2 = __toESM(require_source());
|
|
@@ -25268,7 +25317,7 @@ function walk(err, fn2) {
|
|
|
25268
25317
|
|
|
25269
25318
|
// ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/cryptoNode.js
|
|
25270
25319
|
var nc = __toESM(require("crypto"), 1);
|
|
25271
|
-
var
|
|
25320
|
+
var crypto4 = nc && typeof nc === "object" && "webcrypto" in nc ? nc.webcrypto : nc && typeof nc === "object" && "randomBytes" in nc ? nc : void 0;
|
|
25272
25321
|
|
|
25273
25322
|
// ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/utils.js
|
|
25274
25323
|
function isBytes(a3) {
|
|
@@ -25365,11 +25414,11 @@ function createHasher(hashCons) {
|
|
|
25365
25414
|
return hashC;
|
|
25366
25415
|
}
|
|
25367
25416
|
function randomBytes2(bytesLength = 32) {
|
|
25368
|
-
if (
|
|
25369
|
-
return
|
|
25417
|
+
if (crypto4 && typeof crypto4.getRandomValues === "function") {
|
|
25418
|
+
return crypto4.getRandomValues(new Uint8Array(bytesLength));
|
|
25370
25419
|
}
|
|
25371
|
-
if (
|
|
25372
|
-
return Uint8Array.from(
|
|
25420
|
+
if (crypto4 && typeof crypto4.randomBytes === "function") {
|
|
25421
|
+
return Uint8Array.from(crypto4.randomBytes(bytesLength));
|
|
25373
25422
|
}
|
|
25374
25423
|
throw new Error("crypto.getRandomValues must be defined");
|
|
25375
25424
|
}
|
|
@@ -39471,6 +39520,108 @@ var SwapClient = class {
|
|
|
39471
39520
|
}
|
|
39472
39521
|
}
|
|
39473
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
|
+
};
|
|
39474
39625
|
|
|
39475
39626
|
// src/commands/transfer.ts
|
|
39476
39627
|
var DEFAULT_MAX_TRANSFER_USD = 1e4;
|
|
@@ -45628,6 +45779,171 @@ var MarketActionProvider = class extends ActionProvider {
|
|
|
45628
45779
|
};
|
|
45629
45780
|
}
|
|
45630
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
|
+
};
|
|
45631
45947
|
|
|
45632
45948
|
// src/commands/serve.ts
|
|
45633
45949
|
init_dist();
|
|
@@ -54426,6 +54742,7 @@ function registerServeCommand(program3) {
|
|
|
54426
54742
|
const client = await getClient();
|
|
54427
54743
|
const swapClient = new SwapClient(client);
|
|
54428
54744
|
const alertStore = new AlertStore();
|
|
54745
|
+
const automationStore = new AutomationStore();
|
|
54429
54746
|
const blockchainClient = new BlockchainClient();
|
|
54430
54747
|
let agentWallet;
|
|
54431
54748
|
try {
|
|
@@ -54465,6 +54782,7 @@ function registerServeCommand(program3) {
|
|
|
54465
54782
|
registry2.register(new AgentWalletActionProvider(blockchainClient));
|
|
54466
54783
|
registry2.register(new TokenActionProvider(blockchainClient, agentWallet));
|
|
54467
54784
|
registry2.register(new MarketActionProvider());
|
|
54785
|
+
registry2.register(new AutomateActionProvider(automationStore, swapClient, walletProvider, blockchainClient));
|
|
54468
54786
|
const context = {
|
|
54469
54787
|
httpClient: client
|
|
54470
54788
|
};
|
|
@@ -54496,6 +54814,221 @@ function registerServeCommand(program3) {
|
|
|
54496
54814
|
});
|
|
54497
54815
|
}
|
|
54498
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
|
+
|
|
54499
55032
|
// src/commands/skill.ts
|
|
54500
55033
|
var import_fs = require("fs");
|
|
54501
55034
|
var import_path = require("path");
|
|
@@ -54713,12 +55246,12 @@ Confirm? [y/N] `,
|
|
|
54713
55246
|
var fs6 = __toESM(require("fs"));
|
|
54714
55247
|
var os4 = __toESM(require("os"));
|
|
54715
55248
|
var path4 = __toESM(require("path"));
|
|
54716
|
-
var
|
|
55249
|
+
var crypto6 = __toESM(require("crypto"));
|
|
54717
55250
|
|
|
54718
55251
|
// package.json
|
|
54719
55252
|
var package_default = {
|
|
54720
55253
|
name: "@trustwallet/cli",
|
|
54721
|
-
version: "0.
|
|
55254
|
+
version: "0.7.0",
|
|
54722
55255
|
description: "TWAK CLI \u2014 command-line interface for Trust Wallet Agent Kit",
|
|
54723
55256
|
main: "dist/index.js",
|
|
54724
55257
|
types: "dist/index.d.ts",
|
|
@@ -54818,7 +55351,7 @@ function getUserId() {
|
|
|
54818
55351
|
if (fs6.existsSync(ID_FILE)) return fs6.readFileSync(ID_FILE, "utf-8").trim();
|
|
54819
55352
|
} catch (_e6) {
|
|
54820
55353
|
}
|
|
54821
|
-
const id =
|
|
55354
|
+
const id = crypto6.randomUUID();
|
|
54822
55355
|
try {
|
|
54823
55356
|
fs6.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
54824
55357
|
fs6.writeFileSync(ID_FILE, id);
|
|
@@ -54875,6 +55408,200 @@ function registerTelemetryCommand(program3) {
|
|
|
54875
55408
|
});
|
|
54876
55409
|
}
|
|
54877
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
|
+
|
|
54878
55605
|
// src/index.ts
|
|
54879
55606
|
var VERSION2 = package_default.version;
|
|
54880
55607
|
var program2 = new Command();
|
|
@@ -54891,9 +55618,11 @@ registerErc20Command(program2);
|
|
|
54891
55618
|
registerAlertCommand(program2);
|
|
54892
55619
|
registerQueryCommand(program2);
|
|
54893
55620
|
registerServeCommand(program2);
|
|
55621
|
+
registerAutomateCommand(program2);
|
|
54894
55622
|
registerSkillCommand(program2);
|
|
54895
55623
|
registerX402Command(program2);
|
|
54896
55624
|
registerTelemetryCommand(program2);
|
|
55625
|
+
registerWatchCommand(program2);
|
|
54897
55626
|
initAnalytics();
|
|
54898
55627
|
program2.hook("preAction", (_thisCommand, actionCommand) => {
|
|
54899
55628
|
trackEvent("twak_command", { command: actionCommand.name() });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trustwallet/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "TWAK CLI — command-line interface for Trust Wallet Agent Kit",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -27,17 +27,17 @@
|
|
|
27
27
|
"ora": "5.4.1",
|
|
28
28
|
"tsup": "8.5.1",
|
|
29
29
|
"typescript": "5.7.3",
|
|
30
|
-
"@twak/
|
|
31
|
-
"@twak/
|
|
32
|
-
"@twak/
|
|
33
|
-
"@twak/
|
|
34
|
-
"@twak/
|
|
35
|
-
"@twak/swap": "0.
|
|
36
|
-
"@twak/transactions": "0.
|
|
37
|
-
"@twak/wallet": "0.
|
|
38
|
-
"@twak/wallet-core": "0.
|
|
39
|
-
"@twak/
|
|
40
|
-
"@twak/walletconnect": "0.
|
|
30
|
+
"@twak/blockchain": "0.7.0",
|
|
31
|
+
"@twak/core": "0.7.0",
|
|
32
|
+
"@twak/mcp": "0.7.0",
|
|
33
|
+
"@twak/security": "0.7.0",
|
|
34
|
+
"@twak/storage": "0.7.0",
|
|
35
|
+
"@twak/swap": "0.7.0",
|
|
36
|
+
"@twak/transactions": "0.7.0",
|
|
37
|
+
"@twak/wallet": "0.7.0",
|
|
38
|
+
"@twak/wallet-core": "0.7.0",
|
|
39
|
+
"@twak/actions": "0.7.0",
|
|
40
|
+
"@twak/walletconnect": "0.7.0"
|
|
41
41
|
},
|
|
42
42
|
"publishConfig": {
|
|
43
43
|
"access": "public"
|