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