@clonegod/ttd-core 3.1.11 → 3.1.13

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.
@@ -0,0 +1,5 @@
1
+ import { AlertCodeDef, AlertSeverity } from './types';
2
+ export declare const ALERT_CODES: Record<string, AlertCodeDef>;
3
+ export declare function getDefaultSeverity(code: string): AlertSeverity;
4
+ export declare function getCategory(code: string): string;
5
+ export declare function isKnownCode(code: string): boolean;
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ALERT_CODES = void 0;
4
+ exports.getDefaultSeverity = getDefaultSeverity;
5
+ exports.getCategory = getCategory;
6
+ exports.isKnownCode = isKnownCode;
7
+ const types_1 = require("./types");
8
+ const S = types_1.AlertSeverity;
9
+ exports.ALERT_CODES = {
10
+ QUOTE_RPC_TIMEOUT: { code: 'QUOTE_RPC_TIMEOUT', severity: S.WARN, category: 'quote', desc: '单次 RPC 请求超时', suggested_action: '框架自动重试' },
11
+ QUOTE_RPC_DISCONNECT: { code: 'QUOTE_RPC_DISCONNECT', severity: S.WARN, category: 'quote', desc: '单个 RPC ws 断开', suggested_action: '自动重连' },
12
+ QUOTE_RPC_DISCONNECT_ALL: { code: 'QUOTE_RPC_DISCONNECT_ALL', severity: S.CRITICAL, category: 'quote', desc: '所有 RPC 全断', suggested_action: '立即人工介入' },
13
+ QUOTE_BLOCK_LAG_HIGH: { code: 'QUOTE_BLOCK_LAG_HIGH', severity: S.WARN, category: 'quote', desc: 'block 接收延迟过高' },
14
+ QUOTE_BLOCK_LAG_PERSIST: { code: 'QUOTE_BLOCK_LAG_PERSIST', severity: S.ERROR, category: 'quote', desc: 'block 延迟持续 > 2 分钟', suggested_action: '考虑切 RPC' },
15
+ QUOTE_TICK_FETCH_FAIL: { code: 'QUOTE_TICK_FETCH_FAIL', severity: S.WARN, category: 'quote', desc: 'tick cache 刷新失败' },
16
+ QUOTE_TICK_STALE: { code: 'QUOTE_TICK_STALE', severity: S.ERROR, category: 'quote', desc: 'tick 数据过期但仍在报价', suggested_action: '风险提示' },
17
+ QUOTE_PRICE_ANOMALY: { code: 'QUOTE_PRICE_ANOMALY', severity: S.ERROR, category: 'quote', desc: '报价偏离参考价 > 阈值', suggested_action: '检查数据源' },
18
+ QUOTE_INTERVAL_BREACH: { code: 'QUOTE_INTERVAL_BREACH', severity: S.WARN, category: 'quote', desc: '询价间隔超出预期' },
19
+ TRADE_ENCODE_FAIL: { code: 'TRADE_ENCODE_FAIL', severity: S.ERROR, category: 'trade', desc: 'calldata 编码失败', suggested_action: '检查订单参数' },
20
+ TRADE_SIGN_FAIL: { code: 'TRADE_SIGN_FAIL', severity: S.ERROR, category: 'trade', desc: '交易签名失败', suggested_action: '检查 caller 钱包' },
21
+ TRADE_NONCE_CONFLICT: {
22
+ code: 'TRADE_NONCE_CONFLICT', severity: S.WARN, category: 'trade',
23
+ desc: '单次 nonce 冲突', suggested_action: '自动重试',
24
+ escalate: { burst_threshold: 5, escalated_severity: S.CRITICAL },
25
+ },
26
+ TRADE_NONCE_CONFLICT_BURST: { code: 'TRADE_NONCE_CONFLICT_BURST', severity: S.CRITICAL, category: 'trade', desc: '短时间大量 nonce 冲突', suggested_action: 'nonce 管理异常,排查 stream-trade' },
27
+ TRADE_CALLER_LOW_BALANCE: { code: 'TRADE_CALLER_LOW_BALANCE', severity: S.ERROR, category: 'trade', desc: 'caller 原生币低于警戒线', suggested_action: '尽快充值' },
28
+ TRADE_CALLER_EMPTY: { code: 'TRADE_CALLER_EMPTY', severity: S.CRITICAL, category: 'trade', desc: 'caller 原生币为 0', suggested_action: '立即停用或充值' },
29
+ TRADE_BUILDER_REJECT: { code: 'TRADE_BUILDER_REJECT', severity: S.WARN, category: 'trade', desc: '单个 Builder 拒绝' },
30
+ TRADE_BUILDER_REJECT_ALL: { code: 'TRADE_BUILDER_REJECT_ALL', severity: S.CRITICAL, category: 'trade', desc: '所有 Builder 都拒', suggested_action: '检查私钥白名单 / builder 配置' },
31
+ TRADE_SUBMIT_TIMEOUT: { code: 'TRADE_SUBMIT_TIMEOUT', severity: S.ERROR, category: 'trade', desc: '提交后 N 秒未 receipt' },
32
+ TRADE_RECEIPT_MISS: { code: 'TRADE_RECEIPT_MISS', severity: S.ERROR, category: 'trade', desc: '最终查不到 receipt', suggested_action: '交易可能丢' },
33
+ TRADE_REVERT_SLIPPAGE: {
34
+ code: 'TRADE_REVERT_SLIPPAGE', severity: S.WARN, category: 'trade',
35
+ desc: '链上 revert(滑点)', suggested_action: '市场合理',
36
+ escalate: { burst_threshold: 5, escalated_severity: S.ERROR },
37
+ },
38
+ TRADE_REVERT_SLIPPAGE_BURST: { code: 'TRADE_REVERT_SLIPPAGE_BURST', severity: S.ERROR, category: 'trade', desc: '短时间内大量 slippage revert', suggested_action: '滑点参数需调' },
39
+ TRADE_REVERT_UNKNOWN: { code: 'TRADE_REVERT_UNKNOWN', severity: S.ERROR, category: 'trade', desc: '链上 revert(非滑点原因)', suggested_action: '必须人工看 revert reason' },
40
+ TRADE_GAS_UNDERPRICED: { code: 'TRADE_GAS_UNDERPRICED', severity: S.WARN, category: 'trade', desc: 'gas 过低被挤出' },
41
+ CHAIN_RPC_TIMEOUT: { code: 'CHAIN_RPC_TIMEOUT', severity: S.WARN, category: 'chain', desc: '单次 RPC 请求超时' },
42
+ CHAIN_RPC_DISCONNECT: { code: 'CHAIN_RPC_DISCONNECT', severity: S.WARN, category: 'chain', desc: '单个 RPC ws 断' },
43
+ CHAIN_RPC_DISCONNECT_ALL: { code: 'CHAIN_RPC_DISCONNECT_ALL', severity: S.CRITICAL, category: 'chain', desc: '所有 RPC 全断', suggested_action: '立即检查网络' },
44
+ CHAIN_RPC_LAG_HIGH: { code: 'CHAIN_RPC_LAG_HIGH', severity: S.WARN, category: 'chain', desc: 'RPC 响应延迟异常' },
45
+ CHAIN_BLOCK_SUB_LAG: { code: 'CHAIN_BLOCK_SUB_LAG', severity: S.WARN, category: 'chain', desc: 'stream-block 订阅延迟' },
46
+ CHAIN_POOL_EVENT_GAP: { code: 'CHAIN_POOL_EVENT_GAP', severity: S.WARN, category: 'chain', desc: '池子事件 block 跳跃' },
47
+ CHAIN_POOL_EVENT_LOSS: { code: 'CHAIN_POOL_EVENT_LOSS', severity: S.ERROR, category: 'chain', desc: '确认池子事件丢失' },
48
+ THIRDPARTY_RATE_LIMITED: {
49
+ code: 'THIRDPARTY_RATE_LIMITED', severity: S.WARN, category: 'thirdparty',
50
+ desc: '第三方服务限流(429)',
51
+ escalate: { burst_threshold: 5, escalated_severity: S.ERROR },
52
+ },
53
+ THIRDPARTY_RATE_LIMITED_BURST: { code: 'THIRDPARTY_RATE_LIMITED_BURST', severity: S.ERROR, category: 'thirdparty', desc: '短时连续限流', suggested_action: '降频 / 换 key' },
54
+ THIRDPARTY_API_FAIL: { code: 'THIRDPARTY_API_FAIL', severity: S.WARN, category: 'thirdparty', desc: '单次失败(5xx / 网络 / 解析)' },
55
+ THIRDPARTY_API_DOWN: { code: 'THIRDPARTY_API_DOWN', severity: S.ERROR, category: 'thirdparty', desc: '持续失败 > N 分钟' },
56
+ THIRDPARTY_DATA_INVALID: { code: 'THIRDPARTY_DATA_INVALID', severity: S.WARN, category: 'thirdparty', desc: '返回数据格式错' },
57
+ THIRDPARTY_AUTH_FAIL: { code: 'THIRDPARTY_AUTH_FAIL', severity: S.ERROR, category: 'thirdparty', desc: '认证失败(API key 过期)' },
58
+ INFRA_REDIS_DISCONNECT: { code: 'INFRA_REDIS_DISCONNECT', severity: S.CRITICAL, category: 'infra', desc: 'Redis 连接断', suggested_action: '立即查 Redis' },
59
+ INFRA_REDIS_LATENCY_HIGH: { code: 'INFRA_REDIS_LATENCY_HIGH', severity: S.WARN, category: 'infra', desc: 'Redis P95 延迟异常' },
60
+ INFRA_CONFIG_CENTER_DOWN: { code: 'INFRA_CONFIG_CENTER_DOWN', severity: S.ERROR, category: 'infra', desc: 'config-center 不可达' },
61
+ INFRA_MARKET_DATA_DOWN: { code: 'INFRA_MARKET_DATA_DOWN', severity: S.ERROR, category: 'infra', desc: 'market-data 不可达' },
62
+ INFRA_TRADE_MGT_DOWN: { code: 'INFRA_TRADE_MGT_DOWN', severity: S.ERROR, category: 'infra', desc: 'trade-mgt 不可达' },
63
+ INFRA_PROCESS_CRASH: {
64
+ code: 'INFRA_PROCESS_CRASH', severity: S.ERROR, category: 'infra',
65
+ desc: 'PM2 restart_count 增加',
66
+ escalate: { burst_threshold: 3, escalated_severity: S.CRITICAL },
67
+ },
68
+ INFRA_PROCESS_CRASH_LOOP: { code: 'INFRA_PROCESS_CRASH_LOOP', severity: S.CRITICAL, category: 'infra', desc: '进程 5 分钟内反复重启', suggested_action: '停用该进程' },
69
+ INFRA_ENV_MISSING: { code: 'INFRA_ENV_MISSING', severity: S.ERROR, category: 'infra', desc: '关键 env 未配置' },
70
+ ANALYZE_UPSTREAM_MISSING: { code: 'ANALYZE_UPSTREAM_MISSING', severity: S.WARN, category: 'analyze', desc: '上游消息缺环节(PreTrade/OnTrade/PostTrade)' },
71
+ ANALYZE_SQLITE_FULL: { code: 'ANALYZE_SQLITE_FULL', severity: S.WARN, category: 'analyze', desc: 'SQLite 接近容量上限' },
72
+ ANALYZE_SQLITE_WRITE_FAIL: { code: 'ANALYZE_SQLITE_WRITE_FAIL', severity: S.ERROR, category: 'analyze', desc: 'SQLite 写失败' },
73
+ ANALYZE_ALERT_FLOOD: { code: 'ANALYZE_ALERT_FLOOD', severity: S.ERROR, category: 'analyze', desc: '告警上报速率异常(防噪音自保)' },
74
+ };
75
+ function getDefaultSeverity(code) {
76
+ var _a;
77
+ return ((_a = exports.ALERT_CODES[code]) === null || _a === void 0 ? void 0 : _a.severity) || types_1.AlertSeverity.ERROR;
78
+ }
79
+ function getCategory(code) {
80
+ var _a;
81
+ return ((_a = exports.ALERT_CODES[code]) === null || _a === void 0 ? void 0 : _a.category) || 'unknown';
82
+ }
83
+ function isKnownCode(code) {
84
+ return code in exports.ALERT_CODES;
85
+ }
@@ -0,0 +1,4 @@
1
+ export * from './types';
2
+ export * from './codes';
3
+ export * from './reporter';
4
+ export * from './log_rules';
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./types"), exports);
18
+ __exportStar(require("./codes"), exports);
19
+ __exportStar(require("./reporter"), exports);
20
+ __exportStar(require("./log_rules"), exports);
@@ -0,0 +1,8 @@
1
+ export interface LogAlertRule {
2
+ pattern: RegExp;
3
+ level?: 'warn' | 'error';
4
+ code: string;
5
+ context?: Record<string, any> | ((match: RegExpMatchArray, msg: string) => Record<string, any>);
6
+ }
7
+ export declare function registerLogRules(rules: LogAlertRule[]): void;
8
+ export declare function getRegisteredRuleCount(): number;
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerLogRules = registerLogRules;
4
+ exports.getRegisteredRuleCount = getRegisteredRuleCount;
5
+ const reporter_1 = require("./reporter");
6
+ const log_bus_1 = require("../log_bus");
7
+ const _rules = [];
8
+ function registerLogRules(rules) {
9
+ _rules.push(...rules);
10
+ }
11
+ function getRegisteredRuleCount() {
12
+ return _rules.length;
13
+ }
14
+ function detectProvider(msg) {
15
+ const m = msg.toLowerCase();
16
+ if (m.includes('geckoterminal') || m.includes('gecko-terminal'))
17
+ return 'geckoterminal';
18
+ if (m.includes('dexscreener'))
19
+ return 'dexscreener';
20
+ if (m.includes('birdeye'))
21
+ return 'birdeye';
22
+ if (m.includes('coingecko'))
23
+ return 'coingecko';
24
+ if (m.includes('defillama'))
25
+ return 'defillama';
26
+ return 'unknown';
27
+ }
28
+ registerLogRules([
29
+ { pattern: /redis.*(disconnect|connection.*lost|econnrefused|pubsub.*error|subscribe.*failed|exceeded max reconnect|连接失败)/i,
30
+ code: 'INFRA_REDIS_DISCONNECT' },
31
+ { pattern: /econnrefused.*4000|config-center.*(down|unavailable)|(get|update|delete|post|check)\s+(pools|tokens|group|fixed-symbol|zh-pinyin).*error|init_.*client.*error/i,
32
+ level: 'error', code: 'INFRA_CONFIG_CENTER_DOWN' },
33
+ { pattern: /uncaught exception|unhandled rejection|startup failed|main error|create_trade_runtime error|arb_cache.*init.*error/i,
34
+ level: 'error', code: 'INFRA_PROCESS_CRASH' },
35
+ { pattern: /未设置.*api.*key|missing.*api.*key|env.*not.*configured/i,
36
+ code: 'INFRA_ENV_MISSING' },
37
+ { pattern: /\[collector\].*(run error|fatal)|market.?data.*(down|unavailable)/i,
38
+ level: 'error', code: 'INFRA_MARKET_DATA_DOWN' },
39
+ { pattern: /\b429\b|too many requests/i, code: 'THIRDPARTY_RATE_LIMITED',
40
+ context: (_m, msg) => ({ provider: detectProvider(msg) }) },
41
+ { pattern: /geckoterminal.*(error|fail)|defillama.*(error|fail)|failed to get prices.*after trying all channels/i,
42
+ code: 'THIRDPARTY_API_FAIL',
43
+ context: (_m, msg) => ({ provider: detectProvider(msg) }) },
44
+ { pattern: /api.*(unauthorized|invalid api key|auth.*failed)/i,
45
+ level: 'error', code: 'THIRDPARTY_AUTH_FAIL' },
46
+ { pattern: /pricefeed.*(parse failed|not an array|empty)|empty response at page/i,
47
+ level: 'warn', code: 'THIRDPARTY_DATA_INVALID' },
48
+ { pattern: /sqlite.*(disk|full|i\/o error|cleanup error|write.*fail)/i,
49
+ level: 'error', code: 'ANALYZE_SQLITE_WRITE_FAIL' },
50
+ { pattern: /db size.*\d+mb.*limit/i, level: 'warn', code: 'ANALYZE_SQLITE_FULL' },
51
+ { pattern: /poolsync.*(failed|skip)|blocktimecache.*(failed|skip)|\[preload\].*failed/i,
52
+ level: 'warn', code: 'ANALYZE_UPSTREAM_MISSING' },
53
+ ]);
54
+ function matchAndReport(level, msg, err) {
55
+ setImmediate(() => {
56
+ try {
57
+ if (!msg)
58
+ return;
59
+ for (const rule of _rules) {
60
+ if (rule.level && rule.level !== level)
61
+ continue;
62
+ const match = msg.match(rule.pattern);
63
+ if (!match)
64
+ continue;
65
+ const context = typeof rule.context === 'function'
66
+ ? rule.context(match, msg)
67
+ : (rule.context || {});
68
+ (0, reporter_1.reportAlert)({
69
+ code: rule.code,
70
+ source: '',
71
+ message: msg.length > 500 ? msg.slice(0, 500) + '...' : msg,
72
+ context,
73
+ err: err instanceof Error ? err : undefined,
74
+ });
75
+ break;
76
+ }
77
+ }
78
+ catch (_a) {
79
+ }
80
+ });
81
+ }
82
+ log_bus_1.logBus.onLog((event) => {
83
+ matchAndReport(event.level, event.msg, event.err || event.data);
84
+ });
@@ -0,0 +1,3 @@
1
+ import { AlertPayload } from './types';
2
+ export declare function reportAlert(payload: AlertPayload): void;
3
+ export declare function flushAlerts(): Promise<void>;
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.reportAlert = reportAlert;
13
+ exports.flushAlerts = flushAlerts;
14
+ const types_1 = require("./types");
15
+ const codes_1 = require("./codes");
16
+ let _axios = null;
17
+ function getAxios() {
18
+ if (!_axios)
19
+ _axios = require('axios');
20
+ return _axios;
21
+ }
22
+ function getConfig() {
23
+ return {
24
+ analyze_host: process.env.ALERT_ANALYZE_HOST || '',
25
+ analyze_port: parseInt(process.env.ALERT_ANALYZE_PORT || '8004'),
26
+ flush_size: parseInt(process.env.ALERT_FLUSH_SIZE || '10'),
27
+ flush_ms: parseInt(process.env.ALERT_FLUSH_MS || '5000'),
28
+ dedup_window_ms: parseInt(process.env.ALERT_DEDUP_WINDOW_MS || '60000'),
29
+ source_prefix: process.env.APP_NAME || process.env.NAMESPACE || 'unknown',
30
+ };
31
+ }
32
+ const buffer = new Map();
33
+ let flushTimer = null;
34
+ function dedupKey(code, source) {
35
+ return `${code}|${source}`;
36
+ }
37
+ function reportAlert(payload) {
38
+ try {
39
+ const cfg = getConfig();
40
+ if (!cfg.analyze_host)
41
+ return;
42
+ const severity = payload.severity || (0, codes_1.getDefaultSeverity)(payload.code);
43
+ if (severity === types_1.AlertSeverity.DEBUG)
44
+ return;
45
+ const normalized = Object.assign(Object.assign({}, payload), { severity, source: payload.source || cfg.source_prefix, ts: payload.ts || Date.now(), err: payload.err instanceof Error ? serializeError(payload.err) : payload.err });
46
+ const key = dedupKey(normalized.code, normalized.source);
47
+ const now = Date.now();
48
+ const existing = buffer.get(key);
49
+ if (existing && now - existing.first_ts < cfg.dedup_window_ms) {
50
+ existing.count += 1;
51
+ existing.last_ts = now;
52
+ existing.payload.context = Object.assign(Object.assign({}, (existing.payload.context || {})), (normalized.context || {}));
53
+ const def = codes_1.ALERT_CODES[normalized.code];
54
+ if ((def === null || def === void 0 ? void 0 : def.escalate) && !existing.escalated && existing.count >= def.escalate.burst_threshold) {
55
+ existing.payload.severity = def.escalate.escalated_severity;
56
+ existing.escalated = true;
57
+ }
58
+ }
59
+ else {
60
+ buffer.set(key, {
61
+ payload: normalized,
62
+ first_ts: now,
63
+ last_ts: now,
64
+ count: 1,
65
+ });
66
+ }
67
+ if (buffer.size >= cfg.flush_size) {
68
+ flush();
69
+ }
70
+ else if (!flushTimer) {
71
+ flushTimer = setTimeout(flush, cfg.flush_ms);
72
+ }
73
+ }
74
+ catch (e) {
75
+ safeLogWarn('reportAlert internal error', e);
76
+ }
77
+ }
78
+ function flush() {
79
+ return __awaiter(this, void 0, void 0, function* () {
80
+ if (flushTimer) {
81
+ clearTimeout(flushTimer);
82
+ flushTimer = null;
83
+ }
84
+ if (buffer.size === 0)
85
+ return;
86
+ const cfg = getConfig();
87
+ const items = Array.from(buffer.values());
88
+ buffer.clear();
89
+ const url = `http://${cfg.analyze_host}:${cfg.analyze_port}/trade/analyze/alert/ingest`;
90
+ const body = {
91
+ alerts: items.map(b => ({
92
+ code: b.payload.code,
93
+ severity: b.payload.severity,
94
+ source: b.payload.source,
95
+ message: b.payload.message,
96
+ context: b.payload.context,
97
+ err: b.payload.err,
98
+ count: b.count,
99
+ first_ts: b.first_ts,
100
+ last_ts: b.last_ts,
101
+ escalated: b.escalated || false,
102
+ })),
103
+ };
104
+ try {
105
+ yield getAxios().post(url, body, {
106
+ timeout: 3000,
107
+ headers: { 'Content-Type': 'application/json' },
108
+ });
109
+ }
110
+ catch (e) {
111
+ safeLogWarn(`[alert] flush failed (${items.length} items): ${e.message}`);
112
+ }
113
+ });
114
+ }
115
+ function flushAlerts() {
116
+ return __awaiter(this, void 0, void 0, function* () {
117
+ yield flush();
118
+ });
119
+ }
120
+ process.on('SIGTERM', () => { flushAlerts(); });
121
+ process.on('SIGINT', () => { flushAlerts(); });
122
+ function serializeError(err) {
123
+ return { name: err.name, message: err.message, stack: err.stack };
124
+ }
125
+ function safeLogWarn(msg, err) {
126
+ try {
127
+ console.warn(`[alert] ${msg}`, err ? (err.message || err) : '');
128
+ }
129
+ catch (_a) { }
130
+ }
@@ -0,0 +1,40 @@
1
+ export declare enum AlertSeverity {
2
+ DEBUG = "debug",
3
+ INFO = "info",
4
+ WARN = "warn",
5
+ ERROR = "error",
6
+ CRITICAL = "critical"
7
+ }
8
+ export interface AlertCodeDef {
9
+ code: string;
10
+ severity: AlertSeverity;
11
+ category: 'chain' | 'quote' | 'trade' | 'thirdparty' | 'infra' | 'analyze';
12
+ desc: string;
13
+ suggested_action?: string;
14
+ escalate?: {
15
+ burst_threshold: number;
16
+ escalated_severity: AlertSeverity;
17
+ };
18
+ }
19
+ export interface AlertPayload {
20
+ code: string;
21
+ severity?: AlertSeverity;
22
+ source: string;
23
+ message: string;
24
+ context?: Record<string, any>;
25
+ err?: {
26
+ name?: string;
27
+ message?: string;
28
+ stack?: string;
29
+ } | Error;
30
+ ts?: number;
31
+ }
32
+ export interface AlertRecord extends AlertPayload {
33
+ id: string;
34
+ ts: number;
35
+ count: number;
36
+ first_ts: number;
37
+ last_ts: number;
38
+ escalated?: boolean;
39
+ read?: boolean;
40
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AlertSeverity = void 0;
4
+ var AlertSeverity;
5
+ (function (AlertSeverity) {
6
+ AlertSeverity["DEBUG"] = "debug";
7
+ AlertSeverity["INFO"] = "info";
8
+ AlertSeverity["WARN"] = "warn";
9
+ AlertSeverity["ERROR"] = "error";
10
+ AlertSeverity["CRITICAL"] = "critical";
11
+ })(AlertSeverity || (exports.AlertSeverity = AlertSeverity = {}));
@@ -19,11 +19,25 @@ const ANALYZE_HOST = process.env.TRADE_ANALYZE_HOST || '';
19
19
  const ANALYZE_PORT = process.env.TRADE_ANALYZE_PORT || '8004';
20
20
  const ANALYZE_BASE = ANALYZE_HOST ? `http://${ANALYZE_HOST}:${ANALYZE_PORT}/trade/analyze` : '';
21
21
  const headers = { 'Content-Type': 'application/json' };
22
+ const INGEST_PATH = {
23
+ PriceMessageType: '/ingest/price',
24
+ OrderMessageType: '/ingest/order',
25
+ DepthLevels: '/ingest/depth',
26
+ TickLiquidity: '/ingest/tick',
27
+ TradeResultType: '/ingest/trade-result',
28
+ SendTxLogType: '/ingest/send-tx',
29
+ QuoteVerify: '/ingest/quote-verify',
30
+ };
22
31
  const report_trade_analyze_data = (type, message) => {
23
32
  if (!ANALYZE_BASE)
24
33
  return;
34
+ const path = INGEST_PATH[type];
35
+ if (!path) {
36
+ (0, index_1.log_warn)(`[analyze] unknown report type: ${type}`);
37
+ return;
38
+ }
25
39
  try {
26
- axios_1.default.post(ANALYZE_BASE, { type, message }, { headers, timeout: 3000 })
40
+ axios_1.default.post(ANALYZE_BASE + path, message, { headers, timeout: 3000 })
27
41
  .then(() => (0, index_1.log_trace)(`[analyze] report ${type} ok`))
28
42
  .catch(err => (0, index_1.log_warn)(`[analyze] report ${type} fail: ${err.message}`));
29
43
  }
@@ -36,7 +50,7 @@ const get_pool_latest_quote_price = (unique_orderbook_id) => __awaiter(void 0, v
36
50
  if (!ANALYZE_BASE || (0, index_1.isEmpty)(unique_orderbook_id))
37
51
  return null;
38
52
  try {
39
- const url = `${ANALYZE_BASE}/price_msg?unique_orderbook_id=${unique_orderbook_id}`;
53
+ const url = `${ANALYZE_BASE}/quote/price-msg?unique_orderbook_id=${unique_orderbook_id}`;
40
54
  const res = (yield axios_1.default.get(url, { headers, timeout: 3000 })).data;
41
55
  return res.data;
42
56
  }
@@ -16,8 +16,22 @@ registerEnvVars({
16
16
  dex_id: { env: 'DEX_ID', type: 'string', desc: 'DEX 标识' },
17
17
  redis_host: { env: 'REDIS_HOST', type: 'string', default: '127.0.0.1', desc: 'Redis 地址' },
18
18
  redis_port: { env: 'REDIS_PORT', type: 'number', default: 6379, desc: 'Redis 端口' },
19
- config_center_host: { env: 'CONFIG_CENTER_HOST', type: 'string', default: '', desc: 'config-center 地址' },
20
- trade_analyze_host: { env: 'TRADE_ANALYZE_HOST', type: 'string', default: '', desc: 'analyze 地址' },
19
+ config_center_host: { env: 'CONFIG_CENTER_HOST', type: 'string', default: '127.0.0.1', desc: 'config-center 地址(IP),端口固定 SERVICE_PORT.CONFIG_CENTER_HTTP' },
20
+ trade_analyze_host: { env: 'TRADE_ANALYZE_HOST', type: 'string', default: '127.0.0.1', desc: 'analyze 地址(IP)' },
21
+ trade_mgt_host: { env: 'TRADE_MGT_HOST', type: 'string', default: '127.0.0.1', desc: 'trade-mgt 默认地址(IP),端口固定 SERVICE_PORT.TRADE_MGT_HTTP' },
22
+ trade_mgt_hosts: { env: 'TRADE_MGT_HOSTS', type: 'string', default: '', desc: 'trade-mgt 可选地址列表(IP逗号分隔),UI 可切换;未配置则使用 TRADE_MGT_HOST' },
23
+ alert_analyze_host: { env: 'ALERT_ANALYZE_HOST', type: 'string', default: '', desc: 'analyze 告警端点地址(IP),为空则不上报;默认 127.0.0.1' },
24
+ alert_analyze_port: { env: 'ALERT_ANALYZE_PORT', type: 'number', default: 8004, desc: 'analyze 告警端点端口' },
25
+ alert_flush_size: { env: 'ALERT_FLUSH_SIZE', type: 'number', default: 10, desc: 'alert 批处理阈值(条数)' },
26
+ alert_flush_ms: { env: 'ALERT_FLUSH_MS', type: 'number', default: 5000, desc: 'alert 批处理阈值(ms)' },
27
+ alert_dedup_window_ms: { env: 'ALERT_DEDUP_WINDOW_MS', type: 'number', default: 60000, desc: 'alert 去重窗口(ms)' },
28
+ analyze_sqlite_retain: { env: 'ANALYZE_SQLITE_RETAIN', type: 'string', default: '8h', desc: 'analyze SQLite 数据保留期,支持 8h/2d/30m 格式' },
29
+ alert_webhook_url: { env: 'ALERT_WEBHOOK_URL', type: 'string', default: '', sensitive: true, desc: '外推 webhook URL;为空则禁用外推' },
30
+ alert_webhook_type: { env: 'ALERT_WEBHOOK_TYPE', type: 'string', default: '', desc: 'webhook 类型(lark/slack/dingtalk/generic);省略则按 URL 自动识别' },
31
+ alert_webhook_min_severity: { env: 'ALERT_WEBHOOK_MIN_SEVERITY', type: 'string', default: 'critical', desc: '只外推 >= 此级别的告警(warn/error/critical)' },
32
+ alert_webhook_rate_limit: { env: 'ALERT_WEBHOOK_RATE_LIMIT', type: 'number', default: 10, desc: 'webhook 速率上限(条/分钟),防刷屏' },
33
+ alert_webhook_dedup_ms: { env: 'ALERT_WEBHOOK_DEDUP_MS', type: 'string', default: '5m', desc: 'webhook 去重窗口(同 code+source 不重复推),支持 5m/30s/1h' },
34
+ alert_webhook_system_label: { env: 'ALERT_WEBHOOK_SYSTEM_LABEL', type: 'string', default: '', desc: 'webhook 消息中显示的系统标识,省略时使用 CHAIN_ID' },
21
35
  trade_analyze_port: { env: 'TRADE_ANALYZE_PORT', type: 'number', default: 8004, desc: 'analyze 端口' },
22
36
  trade_analyze_url: { env: 'TRADE_ANALYZE_URL', type: 'string', default: '', desc: 'analyze URL(旧)' },
23
37
  rpc_endpoint: { env: 'RPC_ENDPOINT', type: 'string', default: '', desc: 'RPC HTTP 端点' },
@@ -0,0 +1,9 @@
1
+ export interface EvmGasOptions {
2
+ gasLimit: number;
3
+ defaultGasPriceGwei: number;
4
+ maxGasPriceGwei: number;
5
+ maxFeePerGasGwei: number;
6
+ maxPriorityFeePerGasGwei: number;
7
+ defaultTipAmountGwei: number;
8
+ maxTipAmountGwei: number;
9
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/index.d.ts CHANGED
@@ -15,6 +15,8 @@ export * from './quote';
15
15
  export * from './token';
16
16
  export * from './trade';
17
17
  export * from './chains';
18
+ export * from './alert';
19
+ export * from './log_bus';
18
20
  export * from './ws';
19
21
  export * from './util';
20
22
  export type { CommonServiceType, ConfigCenterServiceType, QuoteServiceType, OrderbookServiceType, TradeProxyServiceType, StandardTokenConfigType, StandardTokenInfoType, StandardPoolConfigType, StandardDexPoolConfigType, StandardPoolInfoType, StandardPairType, TradeServiceConfigType, TradeGroupType, TradePairType, TradePairDexPoolsType, TradeSettingsType, RpcInfoType, TradeStrategyType, TradeCommandLineArgs, TradeRuntimeType, TradeProcessInstanceType, TokenPriceWithAmountType, QuoteResultType, PriceMessageType, QuoteSourceType, UniqueOrderbookIdType, PriceType, QuoteTimeInfoType, OrderbookPriceType, Ladder, OrderMessageType, OrderSubmitResultType, OrderSubmitResponseType, StandardSwapDetailType, TokenBalChangeType, TokenBalanceChangeType, TradeResponseType, TradeResultType, TradeResultBalanceChangeType, TradeExecutionInfoType, TradeTimeFlowType, TradeBroadcastType, TradeGasFeeType, ServerInfoType, SendTxLogType, WalletInfoType, WalletTokenBalanceInfoType, RedisConfigChangeEventMessageType, RedisOrderEventMessageType, RedisQuoteEventMessageType, PreTradeEventType, ProviderComparisonType, ProviderArrivalType, OnTradeType, OnTradePriceType, OnTradeDepthType, OnTradeExecutionType, OnTradeResultType, AnalyzeDepthLevelType, AnalyzeTokenAmountType, PostTradeType, AnalyzeQuoteSnapshotType, AnalyzePoolEventType, AnalyzeEventSummaryType, AnalyzeSameBlockType, } from '../types';
package/dist/index.js CHANGED
@@ -76,6 +76,7 @@ exports.load_wallet = load_wallet;
76
76
  exports.load_wallet_multi = load_wallet_multi;
77
77
  exports.chunkArray = chunkArray;
78
78
  require('dotenv').config();
79
+ const log_bus_1 = require("./log_bus");
79
80
  const bn_js_1 = __importDefault(require("bn.js"));
80
81
  const decimal_js_1 = __importDefault(require("decimal.js"));
81
82
  const fs_1 = __importDefault(require("fs"));
@@ -97,6 +98,8 @@ __exportStar(require("./quote"), exports);
97
98
  __exportStar(require("./token"), exports);
98
99
  __exportStar(require("./trade"), exports);
99
100
  __exportStar(require("./chains"), exports);
101
+ __exportStar(require("./alert"), exports);
102
+ __exportStar(require("./log_bus"), exports);
100
103
  __exportStar(require("./ws"), exports);
101
104
  __exportStar(require("./util"), exports);
102
105
  const short = require('short-uuid');
@@ -395,7 +398,16 @@ function log_debug(msg, data = {}, traceId = '') {
395
398
  log_info(msg, data, traceId, _caller(), LOG_LEVEL.DEBUG);
396
399
  }
397
400
  function log_warn(msg, data = {}, traceId = '') {
398
- log_info(`!!! ${msg}`, data, traceId, _caller(), LOG_LEVEL.WARN);
401
+ const caller = _caller();
402
+ log_info(`!!! ${msg}`, data, traceId, caller, LOG_LEVEL.WARN);
403
+ log_bus_1.logBus.emitLog({
404
+ level: 'warn',
405
+ msg,
406
+ data,
407
+ caller: caller === null || caller === void 0 ? void 0 : caller.module,
408
+ trace_id: traceId,
409
+ ts: Date.now(),
410
+ });
399
411
  }
400
412
  function log_info(msg, data = {}, traceId = '', caller = undefined, level = LOG_LEVEL.INFO) {
401
413
  if (level < _log_level) {
@@ -424,6 +436,14 @@ function log_error(msg, err, traceId = '', caller = undefined) {
424
436
  else {
425
437
  console.error(`${getCurDateTime()} - [5] - [${caller.module}] - ${traceId} - ${msg}`, err);
426
438
  }
439
+ log_bus_1.logBus.emitLog({
440
+ level: 'error',
441
+ msg,
442
+ err,
443
+ caller: caller === null || caller === void 0 ? void 0 : caller.module,
444
+ trace_id: traceId,
445
+ ts: Date.now(),
446
+ });
427
447
  }
428
448
  function createLogger(filename) {
429
449
  const module = path_1.default.basename(filename).replace(/\.(js|ts)$/, '');
@@ -0,0 +1,17 @@
1
+ import { EventEmitter } from 'events';
2
+ export interface LogEvent {
3
+ level: 'warn' | 'error';
4
+ msg: string;
5
+ err?: any;
6
+ data?: any;
7
+ caller?: string;
8
+ trace_id?: string;
9
+ ts: number;
10
+ }
11
+ declare class LogBus extends EventEmitter {
12
+ constructor();
13
+ emitLog(event: LogEvent): void;
14
+ onLog(handler: (event: LogEvent) => void): this;
15
+ }
16
+ export declare const logBus: LogBus;
17
+ export {};
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logBus = void 0;
4
+ const events_1 = require("events");
5
+ class LogBus extends events_1.EventEmitter {
6
+ constructor() {
7
+ super();
8
+ this.setMaxListeners(50);
9
+ }
10
+ emitLog(event) {
11
+ try {
12
+ this.emit('log', event);
13
+ }
14
+ catch (_a) {
15
+ }
16
+ }
17
+ onLog(handler) {
18
+ return this.on('log', handler);
19
+ }
20
+ }
21
+ exports.logBus = new LogBus();
@@ -31,7 +31,7 @@ function get_pair_name(native_token, pool_name) {
31
31
  function check_pool_token_address(pool) {
32
32
  let { pool_name, pool_address, tokenA, tokenB } = pool;
33
33
  for (let token of [tokenA, tokenB]) {
34
- let is_fake_token = token_1.fixed_symbol_address.some(e => e.symbol === token.symbol && e.address.toLowerCase() !== token.address.toLowerCase());
34
+ let is_fake_token = (0, token_1.get_fixed_symbol_address)().some(e => e.symbol === token.symbol && e.address.toLowerCase() !== token.address.toLowerCase());
35
35
  if (is_fake_token) {
36
36
  throw new Error(`Invalid token address of ${token.symbol}: ${token.address}, ${pool_name}, ${pool_address}`);
37
37
  }
@@ -1,2 +1,2 @@
1
1
  import { TokenFixedSymbolAddress } from './types';
2
- export declare const fixed_symbol_address: TokenFixedSymbolAddress[];
2
+ export declare const get_fixed_symbol_address: () => TokenFixedSymbolAddress[];
@@ -3,25 +3,31 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.fixed_symbol_address = void 0;
6
+ exports.get_fixed_symbol_address = void 0;
7
7
  require('dotenv').config();
8
8
  const os_1 = __importDefault(require("os"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const index_1 = require("../index");
11
+ let _first_load_logged = false;
11
12
  const get_fixed_symbol_address = () => {
12
- let chain_id = process.env.CHAIN_ID;
13
+ const chain_id = process.env.CHAIN_ID;
13
14
  if (!chain_id) {
14
15
  return [];
15
16
  }
16
- let filepath = process.env.BSC_FIXED_SYMBOL_ADDRESS_FILE || path_1.default.join(os_1.default.homedir(), 'data', 'onchain_data', chain_id.toLowerCase(), 'fixed_symbol_address.json');
17
- console.log(`get_fixed_symbol_address: ${filepath}`);
18
- let fixed_symbol_address_list = [];
17
+ const filepath = process.env.BSC_FIXED_SYMBOL_ADDRESS_FILE ||
18
+ path_1.default.join(os_1.default.homedir(), 'data', 'onchain_data', chain_id.toLowerCase(), 'fixed_symbol_address.json');
19
+ const print_log = process.env.PRINT_FIXED_SYMBOL_ADDRESS === 'true';
20
+ let list = [];
19
21
  try {
20
- fixed_symbol_address_list = (0, index_1.readFile)(filepath, true, true);
22
+ list = (0, index_1.readFile)(filepath, true, true);
23
+ if (print_log && !_first_load_logged) {
24
+ (0, index_1.log_info)(`[fixed_symbol_address] loaded ${list.length} entries from ${filepath}`);
25
+ _first_load_logged = true;
26
+ }
21
27
  }
22
28
  catch (error) {
23
- console.error('get_fixed_symbol_address erro!', error);
29
+ (0, index_1.log_error)(`[fixed_symbol_address] load failed from ${filepath}`, error);
24
30
  }
25
- return fixed_symbol_address_list;
31
+ return list;
26
32
  };
27
- exports.fixed_symbol_address = get_fixed_symbol_address();
33
+ exports.get_fixed_symbol_address = get_fixed_symbol_address;
@@ -4,3 +4,4 @@ export * from './is_not_arb_token';
4
4
  export * from './price';
5
5
  export * from './token_util';
6
6
  export * from './types';
7
+ export * from './zh_pinyin_map';
@@ -20,3 +20,4 @@ __exportStar(require("./is_not_arb_token"), exports);
20
20
  __exportStar(require("./price"), exports);
21
21
  __exportStar(require("./token_util"), exports);
22
22
  __exportStar(require("./types"), exports);
23
+ __exportStar(require("./zh_pinyin_map"), exports);
@@ -21,6 +21,6 @@ const is_valid_symbol = (symbol) => {
21
21
  };
22
22
  exports.is_valid_symbol = is_valid_symbol;
23
23
  function get_token_symbol_alias(token_address) {
24
- let token = __1.fixed_symbol_address.find(e => e.address === token_address);
24
+ let token = (0, __1.get_fixed_symbol_address)().find(e => e.address === token_address);
25
25
  return (token === null || token === void 0 ? void 0 : token.alias) || '';
26
26
  }
@@ -11,6 +11,10 @@ export interface TokenFixedSymbolAddress {
11
11
  address: string;
12
12
  is_token2022?: boolean;
13
13
  }
14
+ export interface ZhPinyinRow {
15
+ zh: string;
16
+ pinyin: string;
17
+ }
14
18
  export interface FormattedTokenInfo {
15
19
  chainId: number;
16
20
  address: string;
@@ -0,0 +1,3 @@
1
+ import { ZhPinyinRow } from './types';
2
+ export declare const get_zh_pinyin_map: () => ZhPinyinRow[];
3
+ export declare function replace_zh_symbols(text: string): string;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.get_zh_pinyin_map = void 0;
7
+ exports.replace_zh_symbols = replace_zh_symbols;
8
+ require('dotenv').config();
9
+ const os_1 = __importDefault(require("os"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const index_1 = require("../index");
12
+ let _first_load_logged = false;
13
+ const get_zh_pinyin_map = () => {
14
+ const chain_id = process.env.CHAIN_ID;
15
+ if (!chain_id) {
16
+ return [];
17
+ }
18
+ const filepath = process.env.ZH_PINYIN_MAP_FILE ||
19
+ path_1.default.join(os_1.default.homedir(), 'data', 'onchain_data', chain_id.toLowerCase(), 'zh_pinyin_map.json');
20
+ const print_log = process.env.PRINT_ZH_PINYIN_MAP === 'true';
21
+ let list = [];
22
+ try {
23
+ list = (0, index_1.readFile)(filepath, true, true);
24
+ if (print_log && !_first_load_logged) {
25
+ (0, index_1.log_info)(`[zh_pinyin_map] loaded ${list.length} entries from ${filepath}`);
26
+ _first_load_logged = true;
27
+ }
28
+ }
29
+ catch (error) {
30
+ (0, index_1.log_error)(`[zh_pinyin_map] load failed from ${filepath}`, error);
31
+ }
32
+ return list;
33
+ };
34
+ exports.get_zh_pinyin_map = get_zh_pinyin_map;
35
+ function replace_zh_symbols(text) {
36
+ if (!text)
37
+ return text;
38
+ const list = (0, exports.get_zh_pinyin_map)();
39
+ let s = text;
40
+ for (const row of list) {
41
+ if (row.zh && s.includes(row.zh)) {
42
+ s = s.replace(row.zh, row.pinyin || '');
43
+ }
44
+ }
45
+ return s;
46
+ }
@@ -0,0 +1,2 @@
1
+ export declare function parseDuration(value: string | number | undefined | null): number;
2
+ export declare function formatDuration(ms: number): string;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseDuration = parseDuration;
4
+ exports.formatDuration = formatDuration;
5
+ function parseDuration(value) {
6
+ if (value === undefined || value === null || value === '')
7
+ return 0;
8
+ if (typeof value === 'number')
9
+ return value;
10
+ const s = String(value).trim();
11
+ if (!s)
12
+ return 0;
13
+ const match = s.match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/i);
14
+ if (!match) {
15
+ const n = parseInt(s, 10);
16
+ return isNaN(n) ? 0 : n;
17
+ }
18
+ const n = parseFloat(match[1]);
19
+ const unit = (match[2] || 'ms').toLowerCase();
20
+ const multipliers = {
21
+ ms: 1,
22
+ s: 1000,
23
+ m: 60000,
24
+ h: 3600000,
25
+ d: 86400000,
26
+ };
27
+ return Math.round(n * (multipliers[unit] || 1));
28
+ }
29
+ function formatDuration(ms) {
30
+ if (!ms || ms <= 0)
31
+ return '0ms';
32
+ const days = Math.floor(ms / 86400000);
33
+ const hours = Math.floor((ms % 86400000) / 3600000);
34
+ const mins = Math.floor((ms % 3600000) / 60000);
35
+ const secs = Math.floor((ms % 60000) / 1000);
36
+ const remMs = ms % 1000;
37
+ const parts = [];
38
+ if (days > 0)
39
+ parts.push(`${days}d`);
40
+ if (hours > 0)
41
+ parts.push(`${hours}h`);
42
+ if (mins > 0)
43
+ parts.push(`${mins}m`);
44
+ if (secs > 0)
45
+ parts.push(`${secs}s`);
46
+ if (remMs > 0 && parts.length === 0)
47
+ parts.push(`${remMs}ms`);
48
+ return parts.join(' ') || '0ms';
49
+ }
@@ -1,3 +1,4 @@
1
1
  export * from './fileutil';
2
2
  export * from './httputil';
3
3
  export * from './ip_util';
4
+ export * from './duration';
@@ -17,3 +17,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./fileutil"), exports);
18
18
  __exportStar(require("./httputil"), exports);
19
19
  __exportStar(require("./ip_util"), exports);
20
+ __exportStar(require("./duration"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clonegod/ttd-core",
3
- "version": "3.1.11",
3
+ "version": "3.1.13",
4
4
  "description": "Common types and utilities for trading systems - use `npm run push` to publish",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/types/index.d.ts CHANGED
@@ -177,13 +177,14 @@ export interface TradeStrategyType {
177
177
  max_block_offset: number // Solana特定:限制交易在多少个区块内有效(约等于秒数,1 block ≈ 0.4秒)。使用最新blockhash,设置lastValidBlockHeight = currentBlockHeight + max_block_offset
178
178
 
179
179
  // evm chain
180
+ evm_tx_type: 0 | 2 // 交易类型:0=Legacy, 2=EIP-1559
180
181
  evm_gas_limit: number // 300000
181
- evm_gas_price_gwei: number // 1.1 Gwei
182
- evm_tip_amount_gwei: number // 48000 Gwei
182
+ evm_gas_price_gwei: number // Legacy: gasPrice(Gwei
183
+ evm_tip_amount_gwei: number // Builder tip(Gwei),独立原生代币转账
183
184
 
184
- // evm EIP-1559 交易类型
185
- evm_max_gas_price_gwei: number // 0.01 Gwei 允许的gas price上限
186
- evm_max_priority_fee_per_gas_gwei: number // 0.002 Gwei 允许的priority fee上限,需小于 max_gas_price_gwei
185
+ // evm EIP-1559 (Type 2)
186
+ evm_max_fee_per_gas_gwei: number // Type2: maxFeePerGas(Gwei
187
+ evm_max_priority_fee_per_gas_gwei: number // Type2: maxPriorityFeePerGas(Gwei),允许为 0
187
188
 
188
189
  // sui
189
190
  gas_price: number // default
@@ -921,9 +922,8 @@ export interface OnTradeResultType {
921
922
  error_code?: string
922
923
  token_in: AnalyzeTokenAmountType
923
924
  token_out: AnalyzeTokenAmountType
924
- gas_used: number
925
- gas_price_gwei: number
926
- gas_cost_usd: number
925
+ /** gas 成本 — 单位为链的原生币(BNB/ETH/SOL/...),USD 计价由消费者用原生币市价换算 */
926
+ gas_cost_native: number
927
927
  }
928
928
 
929
929
  export interface AnalyzeTokenAmountType {