@clonegod/ttd-core 3.1.55 → 3.1.57

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 @@
1
+ export declare function bootstrapAlertSystem(moduleName: string): Promise<void>;
@@ -0,0 +1,29 @@
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.bootstrapAlertSystem = bootstrapAlertSystem;
13
+ const runtime_state_1 = require("./runtime_state");
14
+ const transport_analyze_1 = require("./transport_analyze");
15
+ const index_1 = require("../index");
16
+ const index_2 = require("../index");
17
+ function bootstrapAlertSystem(moduleName) {
18
+ return __awaiter(this, void 0, void 0, function* () {
19
+ try {
20
+ (0, runtime_state_1.setAlertModule)(moduleName);
21
+ (0, runtime_state_1.initAlertEnvDisable)();
22
+ (0, transport_analyze_1.registerAnalyzeTransport)();
23
+ yield (0, runtime_state_1.initAlertRedisSync)((0, index_1.getRedisClient)('alert'));
24
+ }
25
+ catch (e) {
26
+ (0, index_2.log_warn)(`[alert] bootstrap failed: ${e.message}`);
27
+ }
28
+ });
29
+ }
@@ -0,0 +1,9 @@
1
+ import { EventEmitter } from 'events';
2
+ import { AlertEvent } from './types';
3
+ declare class AlertBus extends EventEmitter {
4
+ constructor();
5
+ emitAlert(event: AlertEvent): void;
6
+ onAlert(listener: (event: AlertEvent) => void): void;
7
+ }
8
+ export declare const alertBus: AlertBus;
9
+ export {};
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.alertBus = void 0;
4
+ const events_1 = require("events");
5
+ class AlertBus extends events_1.EventEmitter {
6
+ constructor() {
7
+ super();
8
+ this.setMaxListeners(20);
9
+ }
10
+ emitAlert(event) { this.emit('alert', event); }
11
+ onAlert(listener) { this.on('alert', listener); }
12
+ }
13
+ exports.alertBus = new AlertBus();
@@ -1,4 +1,6 @@
1
1
  export * from './types';
2
- export * from './codes';
3
- export * from './reporter';
4
- export * from './log_rules';
2
+ export * from './bus';
3
+ export * from './registry';
4
+ export { setAlertModule, getAlertModule, initAlertEnvDisable, initAlertRedisSync, stopAlertRedisSync, setAlertEnabled, clearAlertEnabledOverride, isTypeEnabled, onAlertEnabledChange, } from './runtime_state';
5
+ export { registerAnalyzeTransport } from './transport_analyze';
6
+ export { bootstrapAlertSystem } from './bootstrap';
@@ -14,7 +14,21 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.bootstrapAlertSystem = exports.registerAnalyzeTransport = exports.onAlertEnabledChange = exports.isTypeEnabled = exports.clearAlertEnabledOverride = exports.setAlertEnabled = exports.stopAlertRedisSync = exports.initAlertRedisSync = exports.initAlertEnvDisable = exports.getAlertModule = exports.setAlertModule = void 0;
17
18
  __exportStar(require("./types"), exports);
18
- __exportStar(require("./codes"), exports);
19
- __exportStar(require("./reporter"), exports);
20
- __exportStar(require("./log_rules"), exports);
19
+ __exportStar(require("./bus"), exports);
20
+ __exportStar(require("./registry"), exports);
21
+ var runtime_state_1 = require("./runtime_state");
22
+ Object.defineProperty(exports, "setAlertModule", { enumerable: true, get: function () { return runtime_state_1.setAlertModule; } });
23
+ Object.defineProperty(exports, "getAlertModule", { enumerable: true, get: function () { return runtime_state_1.getAlertModule; } });
24
+ Object.defineProperty(exports, "initAlertEnvDisable", { enumerable: true, get: function () { return runtime_state_1.initAlertEnvDisable; } });
25
+ Object.defineProperty(exports, "initAlertRedisSync", { enumerable: true, get: function () { return runtime_state_1.initAlertRedisSync; } });
26
+ Object.defineProperty(exports, "stopAlertRedisSync", { enumerable: true, get: function () { return runtime_state_1.stopAlertRedisSync; } });
27
+ Object.defineProperty(exports, "setAlertEnabled", { enumerable: true, get: function () { return runtime_state_1.setAlertEnabled; } });
28
+ Object.defineProperty(exports, "clearAlertEnabledOverride", { enumerable: true, get: function () { return runtime_state_1.clearAlertEnabledOverride; } });
29
+ Object.defineProperty(exports, "isTypeEnabled", { enumerable: true, get: function () { return runtime_state_1.isTypeEnabled; } });
30
+ Object.defineProperty(exports, "onAlertEnabledChange", { enumerable: true, get: function () { return runtime_state_1.onAlertEnabledChange; } });
31
+ var transport_analyze_1 = require("./transport_analyze");
32
+ Object.defineProperty(exports, "registerAnalyzeTransport", { enumerable: true, get: function () { return transport_analyze_1.registerAnalyzeTransport; } });
33
+ var bootstrap_1 = require("./bootstrap");
34
+ Object.defineProperty(exports, "bootstrapAlertSystem", { enumerable: true, get: function () { return bootstrap_1.bootstrapAlertSystem; } });
@@ -0,0 +1,46 @@
1
+ import { AlertEvent, AlertSeverity, AlertTypeMeta, ThresholdSpec, WrapContext } from './types';
2
+ type BuildPartial = () => Partial<Omit<AlertEvent, 'type' | 'severity' | 'module'>>;
3
+ export declare class AlertTypeDef {
4
+ readonly name: string;
5
+ readonly defaultSeverity: AlertSeverity;
6
+ readonly meta: AlertTypeMeta;
7
+ private lastSignature;
8
+ private firedOnce;
9
+ private thresholdState;
10
+ constructor(name: string, defaultSeverity: AlertSeverity, meta: AlertTypeMeta);
11
+ get enabled(): boolean;
12
+ private emit;
13
+ report(partial: Partial<Omit<AlertEvent, 'type' | 'module'>>): void;
14
+ reportIfChanged(fingerprint: string, changed: boolean, build: BuildPartial): void;
15
+ reportThreshold(value: number, opts: ThresholdSpec): void;
16
+ assert(cond: boolean, build: BuildPartial): asserts cond;
17
+ wrap<T>(ctx: WrapContext, fn: () => Promise<T>): Promise<T>;
18
+ reportOnce(fingerprint: string, build: BuildPartial): void;
19
+ }
20
+ export declare const ALERT_TYPES: {
21
+ readonly QUOTE_FEE_CHAIN_DRIFT: AlertTypeDef;
22
+ readonly QUOTE_TIER_AMOUNT_ZERO: AlertTypeDef;
23
+ readonly QUOTE_ASK_LT_BID: AlertTypeDef;
24
+ readonly QUOTE_POOL_INIT_FAILED: AlertTypeDef;
25
+ readonly QUOTE_V2_STALL: AlertTypeDef;
26
+ readonly QUOTE_VERIFY_HIGH_DRIFT: AlertTypeDef;
27
+ readonly QUOTE_HOOK_FEE_CHANGE: AlertTypeDef;
28
+ readonly TRADE_NONCE_DESYNC: AlertTypeDef;
29
+ readonly TRADE_CALLER_LOW_BALANCE: AlertTypeDef;
30
+ readonly TRADE_TX_PENDING_TOO_LONG: AlertTypeDef;
31
+ readonly TRADE_BUILDER_REJECTED: AlertTypeDef;
32
+ readonly TRADE_RESULT_MISSING: AlertTypeDef;
33
+ readonly TRADE_VAULT_REVERT: AlertTypeDef;
34
+ readonly INFRA_RPC_UNHEALTHY: AlertTypeDef;
35
+ readonly INFRA_REDIS_DISCONNECTED: AlertTypeDef;
36
+ readonly INFRA_WS_RECONNECT_STORM: AlertTypeDef;
37
+ readonly INFRA_BLOCK_STREAM_LAG: AlertTypeDef;
38
+ };
39
+ export declare const ALERT_TYPES_BY_NAME: Map<string, AlertTypeDef>;
40
+ export declare function listAllAlertTypes(): Array<{
41
+ name: string;
42
+ defaultSeverity: AlertSeverity;
43
+ meta: AlertTypeMeta;
44
+ enabled: boolean;
45
+ }>;
46
+ export {};
@@ -0,0 +1,164 @@
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.ALERT_TYPES_BY_NAME = exports.ALERT_TYPES = exports.AlertTypeDef = void 0;
13
+ exports.listAllAlertTypes = listAllAlertTypes;
14
+ const bus_1 = require("./bus");
15
+ const runtime_state_1 = require("./runtime_state");
16
+ class AlertTypeDef {
17
+ constructor(name, defaultSeverity, meta) {
18
+ this.name = name;
19
+ this.defaultSeverity = defaultSeverity;
20
+ this.meta = meta;
21
+ this.lastSignature = new Map();
22
+ this.firedOnce = new Set();
23
+ this.thresholdState = new Map();
24
+ }
25
+ get enabled() {
26
+ return (0, runtime_state_1.isTypeEnabled)(this.name, this.meta.enable);
27
+ }
28
+ emit(partial) {
29
+ var _a;
30
+ if (!this.enabled)
31
+ return;
32
+ if (!partial.identity || !partial.title) {
33
+ return;
34
+ }
35
+ const event = Object.assign({ type: this.name, severity: partial.severity || this.defaultSeverity, module: (0, runtime_state_1.getAlertModule)(), ts: partial.ts || Date.now(), ttl_ms: (_a = partial.ttl_ms) !== null && _a !== void 0 ? _a : this.meta.ttl_ms }, partial);
36
+ try {
37
+ bus_1.alertBus.emitAlert(event);
38
+ }
39
+ catch (_) { }
40
+ }
41
+ report(partial) {
42
+ this.emit(partial);
43
+ }
44
+ reportIfChanged(fingerprint, changed, build) {
45
+ if (!this.enabled)
46
+ return;
47
+ if (!changed) {
48
+ if (this.lastSignature.has(fingerprint) && this.meta.auto_resolve_on_recheck) {
49
+ const partial = Object.assign(Object.assign({}, build()), { resolved: true });
50
+ this.lastSignature.delete(fingerprint);
51
+ this.emit(partial);
52
+ }
53
+ return;
54
+ }
55
+ const partial = build();
56
+ const sig = JSON.stringify(partial.detail || {});
57
+ if (this.lastSignature.get(fingerprint) === sig)
58
+ return;
59
+ this.lastSignature.set(fingerprint, sig);
60
+ this.emit(Object.assign(Object.assign({}, partial), { fingerprint }));
61
+ }
62
+ reportThreshold(value, opts) {
63
+ var _a;
64
+ if (!this.enabled)
65
+ return;
66
+ const fp = `${this.name}:${opts.identity}`;
67
+ const prev = this.thresholdState.get(fp) || 'ok';
68
+ const worse = opts.higher_is_worse
69
+ ? (a, b) => a > b
70
+ : (a, b) => a < b;
71
+ let nextSev = 'ok';
72
+ if (worse(value, opts.thresholds.critical))
73
+ nextSev = 'critical';
74
+ else if (worse(value, opts.thresholds.warn))
75
+ nextSev = 'warn';
76
+ if (nextSev === prev)
77
+ return;
78
+ this.thresholdState.set(fp, nextSev);
79
+ const title = ((_a = opts.title) === null || _a === void 0 ? void 0 : _a.call(opts, value)) || `${this.meta.display}: ${value}`;
80
+ const baseDetail = Object.assign({ value, thresholds: opts.thresholds }, (opts.detail || {}));
81
+ if (nextSev === 'ok') {
82
+ this.emit({
83
+ identity: opts.identity, scope: opts.scope, chain_id: opts.chain_id,
84
+ title, detail: baseDetail, resolved: true,
85
+ });
86
+ }
87
+ else {
88
+ this.emit({
89
+ identity: opts.identity, scope: opts.scope, chain_id: opts.chain_id,
90
+ severity: nextSev, title, detail: baseDetail,
91
+ });
92
+ }
93
+ }
94
+ assert(cond, build) {
95
+ if (cond)
96
+ return;
97
+ const partial = build();
98
+ this.emit(partial);
99
+ throw new Error(`[${this.name}] invariant violated: ${partial.title || ''}`);
100
+ }
101
+ wrap(ctx, fn) {
102
+ return __awaiter(this, void 0, void 0, function* () {
103
+ var _a;
104
+ try {
105
+ return yield fn();
106
+ }
107
+ catch (err) {
108
+ const e = err;
109
+ this.emit({
110
+ severity: 'critical',
111
+ identity: ctx.identity,
112
+ scope: ctx.scope,
113
+ chain_id: ctx.chain_id,
114
+ title: ((_a = ctx.title) === null || _a === void 0 ? void 0 : _a.call(ctx, e)) || `${this.meta.display}: ${e.message}`,
115
+ detail: Object.assign({ error: e.message, stack: e.stack }, (ctx.detail || {})),
116
+ });
117
+ throw err;
118
+ }
119
+ });
120
+ }
121
+ reportOnce(fingerprint, build) {
122
+ if (!this.enabled)
123
+ return;
124
+ if (this.firedOnce.has(fingerprint))
125
+ return;
126
+ this.firedOnce.add(fingerprint);
127
+ this.emit(Object.assign(Object.assign({}, build()), { fingerprint }));
128
+ }
129
+ }
130
+ exports.AlertTypeDef = AlertTypeDef;
131
+ function def(name, severity, meta) {
132
+ return new AlertTypeDef(name, severity, meta);
133
+ }
134
+ exports.ALERT_TYPES = {
135
+ QUOTE_FEE_CHAIN_DRIFT: def('quote.fee_chain_drift', 'warn', { display: 'Fee 配置与链上不一致', ttl_ms: 0, enable: true, auto_resolve_on_recheck: true }),
136
+ QUOTE_TIER_AMOUNT_ZERO: def('quote.tier_amount_zero', 'warn', { display: 'Tier 金额计算为 0', ttl_ms: 60000, enable: true }),
137
+ QUOTE_ASK_LT_BID: def('quote.ask_lt_bid', 'critical', { display: 'ask<bid invariant 违反', ttl_ms: 30000, enable: true }),
138
+ QUOTE_POOL_INIT_FAILED: def('quote.pool_init_failed', 'critical', { display: '池子初始化失败', ttl_ms: 0, enable: true }),
139
+ QUOTE_V2_STALL: def('quote.v2_stall', 'warn', { display: 'V2 事件流停摆(降级 V1)', ttl_ms: 60000, enable: true, auto_resolve_on_recheck: true }),
140
+ QUOTE_VERIFY_HIGH_DRIFT: def('quote.verify_high_drift', 'warn', { display: '报价反校验偏差超阈值', ttl_ms: 300000, enable: true }),
141
+ QUOTE_HOOK_FEE_CHANGE: def('quote.hook_fee_change', 'info', { display: 'Hook 动态 fee 变化', ttl_ms: 0, enable: true, kind: 'event' }),
142
+ TRADE_NONCE_DESYNC: def('trade.nonce_desync', 'critical', { display: 'Nonce 不同步', ttl_ms: 30000, enable: true, auto_resolve_on_recheck: true }),
143
+ TRADE_CALLER_LOW_BALANCE: def('trade.caller_low_balance', 'warn', { display: 'Caller 余额不足', ttl_ms: 300000, enable: true }),
144
+ TRADE_TX_PENDING_TOO_LONG: def('trade.tx_pending_too_long', 'warn', { display: '交易长时间未上链', ttl_ms: 60000, enable: true }),
145
+ TRADE_BUILDER_REJECTED: def('trade.builder_rejected', 'warn', { display: 'Bundle builder 拒绝', ttl_ms: 60000, enable: true }),
146
+ TRADE_RESULT_MISSING: def('trade.result_missing', 'critical', { display: 'TradeResult 事件缺失', ttl_ms: 30000, enable: true }),
147
+ TRADE_VAULT_REVERT: def('trade.vault_revert', 'critical', { display: 'Vault 执行 revert', ttl_ms: 30000, enable: true }),
148
+ INFRA_RPC_UNHEALTHY: def('infra.rpc_unhealthy', 'warn', { display: 'RPC provider 不健康', ttl_ms: 120000, enable: true, auto_resolve_on_recheck: true }),
149
+ INFRA_REDIS_DISCONNECTED: def('infra.redis_disconnected', 'critical', { display: 'Redis 连接断开', ttl_ms: 30000, enable: true, auto_resolve_on_recheck: true }),
150
+ INFRA_WS_RECONNECT_STORM: def('infra.ws_reconnect_storm', 'warn', { display: 'WS 频繁重连', ttl_ms: 60000, enable: true }),
151
+ INFRA_BLOCK_STREAM_LAG: def('infra.block_stream_lag', 'warn', { display: '区块流落后', ttl_ms: 60000, enable: true, auto_resolve_on_recheck: true }),
152
+ };
153
+ exports.ALERT_TYPES_BY_NAME = new Map();
154
+ for (const def of Object.values(exports.ALERT_TYPES)) {
155
+ exports.ALERT_TYPES_BY_NAME.set(def.name, def);
156
+ }
157
+ function listAllAlertTypes() {
158
+ return Object.values(exports.ALERT_TYPES).map(t => ({
159
+ name: t.name,
160
+ defaultSeverity: t.defaultSeverity,
161
+ meta: t.meta,
162
+ enabled: t.enabled,
163
+ }));
164
+ }
@@ -0,0 +1,10 @@
1
+ import { RedisClient } from '../redis';
2
+ export declare function setAlertModule(name: string): void;
3
+ export declare function getAlertModule(): string;
4
+ export declare function initAlertEnvDisable(): void;
5
+ export declare function initAlertRedisSync(redis: RedisClient): Promise<void>;
6
+ export declare function stopAlertRedisSync(): void;
7
+ export declare function setAlertEnabled(redis: RedisClient, type: string, enabled: boolean): Promise<void>;
8
+ export declare function clearAlertEnabledOverride(redis: RedisClient, type: string): Promise<void>;
9
+ export declare function isTypeEnabled(typeName: string, defaultEnable: boolean): boolean;
10
+ export declare function onAlertEnabledChange(cb: (type: string, enabled: boolean) => void): () => void;
@@ -0,0 +1,126 @@
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.setAlertModule = setAlertModule;
13
+ exports.getAlertModule = getAlertModule;
14
+ exports.initAlertEnvDisable = initAlertEnvDisable;
15
+ exports.initAlertRedisSync = initAlertRedisSync;
16
+ exports.stopAlertRedisSync = stopAlertRedisSync;
17
+ exports.setAlertEnabled = setAlertEnabled;
18
+ exports.clearAlertEnabledOverride = clearAlertEnabledOverride;
19
+ exports.isTypeEnabled = isTypeEnabled;
20
+ exports.onAlertEnabledChange = onAlertEnabledChange;
21
+ const index_1 = require("../index");
22
+ const REDIS_HASH = 'alert:type_enabled';
23
+ const POLL_INTERVAL_MS = 60000;
24
+ let moduleName = process.env.MODULE_NAME || 'unknown';
25
+ const disabledByEnv = new Set();
26
+ const overrides = new Map();
27
+ const changeListeners = new Set();
28
+ let pollTimer = null;
29
+ let pollRedis = null;
30
+ function setAlertModule(name) {
31
+ moduleName = name;
32
+ }
33
+ function getAlertModule() {
34
+ return moduleName;
35
+ }
36
+ function initAlertEnvDisable() {
37
+ const raw = process.env.ALERT_DISABLED_TYPES || '';
38
+ raw.split(',').map(s => s.trim()).filter(Boolean).forEach(t => disabledByEnv.add(t));
39
+ if (disabledByEnv.size > 0) {
40
+ (0, index_1.log_info)(`[alert] disabled via env: ${[...disabledByEnv].join(',')}`);
41
+ }
42
+ }
43
+ function pollOnce(redis) {
44
+ return __awaiter(this, void 0, void 0, function* () {
45
+ try {
46
+ const map = yield redis.hgetall(REDIS_HASH);
47
+ const nextKeys = new Set(map ? Object.keys(map) : []);
48
+ if (map) {
49
+ for (const [type, val] of Object.entries(map)) {
50
+ const next = (val === '1' || val === 'true');
51
+ const prev = overrides.get(type);
52
+ if (prev !== next) {
53
+ overrides.set(type, next);
54
+ changeListeners.forEach(cb => { try {
55
+ cb(type, next);
56
+ }
57
+ catch (_) { } });
58
+ }
59
+ }
60
+ }
61
+ for (const type of [...overrides.keys()]) {
62
+ if (!nextKeys.has(type)) {
63
+ overrides.delete(type);
64
+ changeListeners.forEach(cb => { try {
65
+ cb(type, null);
66
+ }
67
+ catch (_) { } });
68
+ }
69
+ }
70
+ }
71
+ catch (e) {
72
+ (0, index_1.log_warn)(`[alert] redis poll failed: ${e.message}`);
73
+ }
74
+ });
75
+ }
76
+ function initAlertRedisSync(redis) {
77
+ return __awaiter(this, void 0, void 0, function* () {
78
+ var _a;
79
+ pollRedis = redis;
80
+ yield pollOnce(redis);
81
+ (0, index_1.log_info)(`[alert] redis sync started (interval ${POLL_INTERVAL_MS}ms, ${overrides.size} overrides loaded)`);
82
+ if (pollTimer)
83
+ clearInterval(pollTimer);
84
+ pollTimer = setInterval(() => { if (pollRedis)
85
+ pollOnce(pollRedis); }, POLL_INTERVAL_MS);
86
+ (_a = pollTimer.unref) === null || _a === void 0 ? void 0 : _a.call(pollTimer);
87
+ });
88
+ }
89
+ function stopAlertRedisSync() {
90
+ if (pollTimer) {
91
+ clearInterval(pollTimer);
92
+ pollTimer = null;
93
+ }
94
+ pollRedis = null;
95
+ }
96
+ function setAlertEnabled(redis, type, enabled) {
97
+ return __awaiter(this, void 0, void 0, function* () {
98
+ overrides.set(type, enabled);
99
+ yield redis.hset(REDIS_HASH, type, enabled ? '1' : '0');
100
+ changeListeners.forEach(cb => { try {
101
+ cb(type, enabled);
102
+ }
103
+ catch (_) { } });
104
+ });
105
+ }
106
+ function clearAlertEnabledOverride(redis, type) {
107
+ return __awaiter(this, void 0, void 0, function* () {
108
+ overrides.delete(type);
109
+ yield redis.hdel(REDIS_HASH, type);
110
+ changeListeners.forEach(cb => { try {
111
+ cb(type, null);
112
+ }
113
+ catch (_) { } });
114
+ });
115
+ }
116
+ function isTypeEnabled(typeName, defaultEnable) {
117
+ if (overrides.has(typeName))
118
+ return overrides.get(typeName);
119
+ if (disabledByEnv.has(typeName))
120
+ return false;
121
+ return defaultEnable;
122
+ }
123
+ function onAlertEnabledChange(cb) {
124
+ changeListeners.add(cb);
125
+ return () => changeListeners.delete(cb);
126
+ }
@@ -0,0 +1 @@
1
+ export declare function registerAnalyzeTransport(): void;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerAnalyzeTransport = registerAnalyzeTransport;
4
+ const bus_1 = require("./bus");
5
+ const analyze_1 = require("../analyze");
6
+ let registered = false;
7
+ function registerAnalyzeTransport() {
8
+ if (registered)
9
+ return;
10
+ registered = true;
11
+ bus_1.alertBus.onAlert((event) => {
12
+ (0, analyze_1.report_data_to_analyze)('Alert', event);
13
+ });
14
+ }
@@ -1,40 +1,69 @@
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;
1
+ export type AlertSeverity = 'info' | 'warn' | 'critical';
2
+ export type AlertStatus = 'active' | 'resolved' | 'acked' | 'expired';
3
+ export interface AlertEvent {
4
+ type: string;
10
5
  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;
6
+ module: string;
7
+ chain_id?: string;
8
+ identity: string;
9
+ scope?: {
10
+ dex_id?: string;
11
+ pair?: string;
12
+ group_id?: string;
13
+ pool_address?: string;
14
+ caller_address?: string;
15
+ rpc_id?: string;
16
+ [k: string]: string | undefined;
17
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;
18
+ title: string;
19
+ detail?: Record<string, any>;
20
+ fingerprint?: string;
21
+ resolved?: boolean;
22
+ ttl_ms?: number;
30
23
  ts?: number;
31
24
  }
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;
25
+ export interface AlertRecord {
26
+ fingerprint: string;
27
+ type: string;
28
+ severity: AlertSeverity;
29
+ module: string;
30
+ chain_id?: string;
31
+ identity: string;
32
+ scope?: AlertEvent['scope'];
33
+ title: string;
34
+ latest_detail?: Record<string, any>;
35
+ status: AlertStatus;
36
+ first_seen_ms: number;
37
+ last_seen_ms: number;
38
+ occurrences: number;
39
+ resolved_ms?: number;
40
+ acked_ms?: number;
41
+ acked_by?: string;
42
+ }
43
+ export interface AlertTypeMeta {
44
+ display: string;
45
+ kind?: 'state' | 'event';
46
+ ttl_ms: number;
47
+ enable: boolean;
48
+ auto_resolve_on_recheck?: boolean;
49
+ doc_url?: string;
50
+ }
51
+ export interface ThresholdSpec {
52
+ identity: string;
53
+ thresholds: {
54
+ warn: number;
55
+ critical: number;
56
+ };
57
+ higher_is_worse?: boolean;
58
+ scope?: AlertEvent['scope'];
59
+ chain_id?: string;
60
+ title?: (value: number) => string;
61
+ detail?: Record<string, any>;
62
+ }
63
+ export interface WrapContext {
64
+ identity: string;
65
+ scope?: AlertEvent['scope'];
66
+ chain_id?: string;
67
+ detail?: Record<string, any>;
68
+ title?: (err: Error) => string;
40
69
  }
@@ -1,11 +1,2 @@
1
1
  "use strict";
2
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 = {}));
@@ -105,6 +105,7 @@ const WS_TYPES = new Set([
105
105
  const HTTP_PATH = {
106
106
  OrderMessageType: '/ingest/order',
107
107
  TradeResultType: '/ingest/trade-result',
108
+ Alert: '/ingest/alert',
108
109
  };
109
110
  const report_data_to_analyze = (type, data) => {
110
111
  const host = getAnalyzeHost();
@@ -27,7 +27,6 @@ export declare class EnvArgs {
27
27
  quote_pool_name: string;
28
28
  quote_pool_fee_rate: number;
29
29
  tick_cache_force_full_refresh_interval: number;
30
- depth_cex_bps: number;
31
30
  trade_log_report_cex_url: string;
32
31
  trade_log_report_cex_data_type: string;
33
32
  token_price_refresh_interval_seconds: number;
@@ -36,7 +36,6 @@ registerEnvVars({
36
36
  quote_pool_name: { env: 'QUOTE_POOL_NAME', type: 'string', default: '', desc: '指定池子名称' },
37
37
  quote_pool_fee_rate: { env: 'QUOTE_POOL_FEE_RATE', type: 'number', default: 0, desc: '指定池子费率' },
38
38
  tick_cache_force_full_refresh_interval: { env: 'TICK_CACHE_FORCE_FULL_REFRESH_INTERVAL', type: 'number', default: 60000, desc: 'tick 流动性强制全量刷新间隔(ms)' },
39
- depth_cex_bps: { env: 'DEPTH_CEX_BPS', type: 'number', default: 20, desc: 'CEX 深度 bps(用于报价转换)' },
40
39
  trade_log_report_cex_url: { env: 'TRADE_LOG_REPORT_CEX_URL', type: 'string', default: '', desc: '【使用方: ttd-core/src/index.ts save_trade_execution_logs】DEX 交易执行日志上报 cex 侧接收端 URL;为空则跳过上报' },
41
40
  trade_log_report_cex_data_type: { env: 'TRADE_LOG_REPORT_CEX_DATA_TYPE', type: 'string', default: '', desc: '【使用方: ttd-core/src/index.ts save_trade_execution_logs】上报 payload.type,cex 侧用于路由/区分;为空则跳过上报' },
42
41
  wallet_dir: { env: 'WALLET_DIR', type: 'string', default: '', desc: '钱包目录' },
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ import fs from "fs";
4
4
  import { StandardPoolInfoType, StandardTokenInfoType, TokenPriceWithAmountType, TradeResponseType, UniqueOrderbookIdType, WalletInfoType } from '../types';
5
5
  import * as redis from './redis';
6
6
  import * as cache from './cache';
7
+ export * from './alert';
7
8
  export * from './analyze';
8
9
  export * from './appconfig';
9
10
  export * from './redis';
@@ -17,14 +18,15 @@ export * from './chains';
17
18
  export * from './log_bus';
18
19
  export * from './ws';
19
20
  export * from './util';
20
- export type { 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';
21
+ export type { 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, AnalyzeTierLevelType, AnalyzeTokenAmountType, PostTradeType, AnalyzeQuoteSnapshotType, AnalyzePoolEventType, AnalyzeEventSummaryType, AnalyzeSameBlockType, } from '../types';
21
22
  export declare const FAILED = "FAILED";
22
23
  export declare const SUCCESS = "SUCCESS";
23
24
  export declare const NOT_FOUND = "NOT_FOUND";
24
25
  export declare const PROCESSING = "PROCESSING";
25
26
  export declare const REGEX_HEX_PREFIX: RegExp;
26
- export declare const QUOTE_AMOUNT_USD_MIN = 50;
27
+ export declare const QUOTE_AMOUNT_USD_MIN = 20;
27
28
  export declare const QUOTE_AMOUNT_USD_MAX = 500;
29
+ export declare const QUOTE_AMOUNT_USD_DEFAULT = 100;
28
30
  export declare const TRANSACTION_STATE_PROCESSING = "TRANSACTION_STATE_PROCESSING";
29
31
  export declare const TRANSACTION_STATE_SUCCESS = "TRANSACTION_STATE_SUCCESS";
30
32
  export declare const TRANSACTION_STATE_FAILED = "TRANSACTION_STATE_FAILED";
package/dist/index.js CHANGED
@@ -48,8 +48,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
48
48
  return (mod && mod.__esModule) ? mod : { "default": mod };
49
49
  };
50
50
  Object.defineProperty(exports, "__esModule", { value: true });
51
- exports.DATE_TIME_FORMAT_DEFAULT = exports.to_json_str = exports.DecimalUtil = exports.generateRandomNumber = exports.LOG = exports._active_log_level = exports.LOG_LEVEL = exports.REDIS_EVENT_TYPE_TRADE_INSTANCE_CHANGE = exports.REDIS_EVENT_TYPE_ORDER = exports.REDIS_EVENT_TYPE_QUOTE = exports.REDIS_EVENT_TYPE_CONFIG_CHANGE = exports.REDIS_EVENT_CHANNEL = exports.get_new_block_channel_name = exports.get_tx_result_channel_name = exports.get_wallet_raw_tx_channel_name = exports.get_order_channel_name = exports.get_quote_channel_name = exports.get_config_channel_name = exports.get_cache_key = exports.getArbEventSubscriber = exports.getArbEventPublisher = exports.getArbCache = exports.getLoadingCache = exports.getRedisClient = exports.format_order_lock_key = exports.format_unique_order_msg_id = exports.parse_unique_orderbook_id = exports.format_unique_orderbook_id = exports.format_pair_symbol = exports.format_token_symbol = exports.LOCAL_EVENT_NAME = exports.CACHE_KEY_TYPE = exports.OnChainDataSubscribeType = exports.TradeErrorCodeType = exports.SystemErrorCodeType = exports.TRADE_TYPE = exports.GROUP_ID = exports.DEX_ID = exports.CHAIN_ID = exports.STREAMING_TRANSACTION_CONFIRMED = exports.TRANSACTION_STATE_FAILED = exports.TRANSACTION_STATE_SUCCESS = exports.TRANSACTION_STATE_PROCESSING = exports.QUOTE_AMOUNT_USD_MAX = exports.QUOTE_AMOUNT_USD_MIN = exports.REGEX_HEX_PREFIX = exports.PROCESSING = exports.NOT_FOUND = exports.SUCCESS = exports.FAILED = void 0;
52
- exports.inspect_arb_local_cache = exports.save_trade_execution_logs = exports.get_input_out_token_fix = exports.get_input_out_token = exports.calc_amount_in_token = exports.failed = exports.success = exports.home_dir = exports.postJSON = exports.deep_merge_object = exports.isArray = exports.isTrue = exports.deep_clone = exports.uuid = exports.DATE_TIME_FORMAT_NO_WHITESPACE = void 0;
51
+ exports.to_json_str = exports.DecimalUtil = exports.generateRandomNumber = exports.LOG = exports._active_log_level = exports.LOG_LEVEL = exports.REDIS_EVENT_TYPE_TRADE_INSTANCE_CHANGE = exports.REDIS_EVENT_TYPE_ORDER = exports.REDIS_EVENT_TYPE_QUOTE = exports.REDIS_EVENT_TYPE_CONFIG_CHANGE = exports.REDIS_EVENT_CHANNEL = exports.get_new_block_channel_name = exports.get_tx_result_channel_name = exports.get_wallet_raw_tx_channel_name = exports.get_order_channel_name = exports.get_quote_channel_name = exports.get_config_channel_name = exports.get_cache_key = exports.getArbEventSubscriber = exports.getArbEventPublisher = exports.getArbCache = exports.getLoadingCache = exports.getRedisClient = exports.format_order_lock_key = exports.format_unique_order_msg_id = exports.parse_unique_orderbook_id = exports.format_unique_orderbook_id = exports.format_pair_symbol = exports.format_token_symbol = exports.LOCAL_EVENT_NAME = exports.CACHE_KEY_TYPE = exports.OnChainDataSubscribeType = exports.TradeErrorCodeType = exports.SystemErrorCodeType = exports.TRADE_TYPE = exports.GROUP_ID = exports.DEX_ID = exports.CHAIN_ID = exports.STREAMING_TRANSACTION_CONFIRMED = exports.TRANSACTION_STATE_FAILED = exports.TRANSACTION_STATE_SUCCESS = exports.TRANSACTION_STATE_PROCESSING = exports.QUOTE_AMOUNT_USD_DEFAULT = exports.QUOTE_AMOUNT_USD_MAX = exports.QUOTE_AMOUNT_USD_MIN = exports.REGEX_HEX_PREFIX = exports.PROCESSING = exports.NOT_FOUND = exports.SUCCESS = exports.FAILED = void 0;
52
+ exports.inspect_arb_local_cache = exports.save_trade_execution_logs = exports.get_input_out_token_fix = exports.get_input_out_token = exports.calc_amount_in_token = exports.failed = exports.success = exports.home_dir = exports.postJSON = exports.deep_merge_object = exports.isArray = exports.isTrue = exports.deep_clone = exports.uuid = exports.DATE_TIME_FORMAT_NO_WHITESPACE = exports.DATE_TIME_FORMAT_DEFAULT = void 0;
53
53
  exports._caller = _caller;
54
54
  exports.log_trace = log_trace;
55
55
  exports.log_debug = log_debug;
@@ -87,6 +87,7 @@ const path_1 = __importDefault(require("path"));
87
87
  const redis = __importStar(require("./redis"));
88
88
  const cache = __importStar(require("./cache"));
89
89
  const crypto_1 = require("./util/crypto");
90
+ __exportStar(require("./alert"), exports);
90
91
  __exportStar(require("./analyze"), exports);
91
92
  __exportStar(require("./appconfig"), exports);
92
93
  __exportStar(require("./redis"), exports);
@@ -106,8 +107,9 @@ exports.SUCCESS = 'SUCCESS';
106
107
  exports.NOT_FOUND = 'NOT_FOUND';
107
108
  exports.PROCESSING = 'PROCESSING';
108
109
  exports.REGEX_HEX_PREFIX = /^(0x)/;
109
- exports.QUOTE_AMOUNT_USD_MIN = 50;
110
+ exports.QUOTE_AMOUNT_USD_MIN = 20;
110
111
  exports.QUOTE_AMOUNT_USD_MAX = 500;
112
+ exports.QUOTE_AMOUNT_USD_DEFAULT = 100;
111
113
  exports.TRANSACTION_STATE_PROCESSING = 'TRANSACTION_STATE_PROCESSING';
112
114
  exports.TRANSACTION_STATE_SUCCESS = 'TRANSACTION_STATE_SUCCESS';
113
115
  exports.TRANSACTION_STATE_FAILED = 'TRANSACTION_STATE_FAILED';
@@ -0,0 +1,39 @@
1
+ export interface QuoteTier {
2
+ pct: number;
3
+ price: number;
4
+ amount: number;
5
+ amount_in: number;
6
+ amount_in_usd: number;
7
+ fee: number;
8
+ fee_usd: number;
9
+ tick_move?: string;
10
+ }
11
+ export interface QuoteEntry {
12
+ price: number;
13
+ amount: number;
14
+ amount_in: number;
15
+ amount_in_usd: number;
16
+ fee: number;
17
+ fee_usd: number;
18
+ tick_move?: string;
19
+ tiers: QuoteTier[];
20
+ }
21
+ export interface QuoteDepthOutput {
22
+ mid_price: number;
23
+ fee_rate_bps: number;
24
+ ask: QuoteEntry;
25
+ bid: QuoteEntry;
26
+ }
27
+ export interface OrderbookTier {
28
+ pct: number;
29
+ price: number;
30
+ amount: number;
31
+ }
32
+ export interface OrderbookEntry {
33
+ price: number;
34
+ amount: number;
35
+ tiers: OrderbookTier[];
36
+ }
37
+ export declare function toOrderbookEntry(q: QuoteEntry): OrderbookEntry;
38
+ export declare const DEFAULT_TIER_PCT = 0.2;
39
+ export declare function getDepthPricePctLevels(): number[];
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_TIER_PCT = void 0;
4
+ exports.toOrderbookEntry = toOrderbookEntry;
5
+ exports.getDepthPricePctLevels = getDepthPricePctLevels;
6
+ function toOrderbookEntry(q) {
7
+ return {
8
+ price: q.price,
9
+ amount: q.amount,
10
+ tiers: q.tiers.map(t => ({ pct: t.pct, price: t.price, amount: t.amount })),
11
+ };
12
+ }
13
+ const PCT_LEVEL_DEFAULT = [0.1, 0.2, 0.3, 0.5, 1.0];
14
+ const PCT_LEVEL_CAP = 1.0;
15
+ exports.DEFAULT_TIER_PCT = 0.2;
16
+ let _pctLevelsCache;
17
+ function getDepthPricePctLevels() {
18
+ if (_pctLevelsCache)
19
+ return _pctLevelsCache;
20
+ const raw = process.env.DEPTH_PRICE_PCT;
21
+ let levels;
22
+ if (raw) {
23
+ levels = raw.split(',').map(s => parseFloat(s.trim())).filter(n => !isNaN(n) && n > 0 && n <= PCT_LEVEL_CAP);
24
+ if (levels.length === 0)
25
+ levels = [...PCT_LEVEL_DEFAULT];
26
+ }
27
+ else {
28
+ levels = [...PCT_LEVEL_DEFAULT];
29
+ }
30
+ if (!levels.includes(exports.DEFAULT_TIER_PCT))
31
+ levels.push(exports.DEFAULT_TIER_PCT);
32
+ levels = Array.from(new Set(levels)).sort((a, b) => a - b);
33
+ _pctLevelsCache = levels;
34
+ return levels;
35
+ }
@@ -1 +1,2 @@
1
1
  export * from './on_quote_response';
2
+ export * from './depth_tier';
@@ -15,3 +15,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./on_quote_response"), exports);
18
+ __exportStar(require("./depth_tier"), exports);
@@ -1,5 +1,6 @@
1
1
  import { StandardPoolInfoType, QuoteResultType } from "../../types";
2
2
  import { AppConfig } from "../appconfig";
3
+ import { QuoteDepthOutput } from "./depth_tier";
3
4
  export interface PoolDepthData {
4
5
  ask: Record<number, {
5
6
  amount: number;
@@ -27,7 +28,7 @@ export interface QuoteResponseOptions {
27
28
  txid: string;
28
29
  pushPriceMessage?: boolean;
29
30
  source?: string;
30
- depth?: PoolDepthData;
31
+ depth?: QuoteDepthOutput;
31
32
  }
32
33
  export declare function on_quote_response(opts: QuoteResponseOptions): void;
33
34
  export declare function report_quote_candidate(opts: {
@@ -38,5 +39,16 @@ export declare function report_quote_candidate(opts: {
38
39
  block_number: number;
39
40
  ask_price: number | string;
40
41
  bid_price: number | string;
42
+ ask_tiers?: Array<{
43
+ pct: number;
44
+ price: number;
45
+ amount: number;
46
+ amount_in: number;
47
+ }>;
48
+ bid_tiers?: Array<{
49
+ pct: number;
50
+ price: number;
51
+ amount: number;
52
+ amount_in: number;
53
+ }>;
41
54
  }): void;
42
- export declare function on_quote_respose(appConfig: AppConfig, pool_info: StandardPoolInfoType, quote_amount_usd: number, _execution_price: number, stream_time: number, quote_start_time: number, slot_info: string, result: [QuoteResultType, QuoteResultType, number], txid: string, push_price_message?: boolean, source?: string, depth?: PoolDepthData): void;
@@ -2,17 +2,17 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.on_quote_response = on_quote_response;
4
4
  exports.report_quote_candidate = report_quote_candidate;
5
- exports.on_quote_respose = on_quote_respose;
6
5
  const analyze_1 = require("../analyze");
7
6
  const log_quote_price_1 = require("./log_quote_price");
8
7
  const publish_quote_price_1 = require("./publish_quote_price");
9
8
  const to_price_message_1 = require("./to_price_message");
10
9
  function on_quote_response(opts) {
10
+ var _a, _b;
11
11
  const { appConfig, poolInfo, quoteAmountUsd, streamTime, quoteStartTime, blockNumber, quotes, txid, pushPriceMessage = true, source = '', depth, } = opts;
12
12
  const quoteEndTime = Date.now();
13
- const quoteAskPrice = Number(quotes[0].price);
14
- const quoteBidPrice = Number(quotes[1].price);
15
13
  const priceTime = Date.now();
14
+ const quoteAskPrice = Number((_a = quotes[0]) === null || _a === void 0 ? void 0 : _a.price) || 0;
15
+ const quoteBidPrice = Number((_b = quotes[1]) === null || _b === void 0 ? void 0 : _b.price) || 0;
16
16
  const time = {
17
17
  block_time: 0,
18
18
  stream_time: streamTime,
@@ -33,7 +33,10 @@ function on_quote_response(opts) {
33
33
  price_id: price_msg.price_id,
34
34
  unique_orderbook_id: price_msg.unique_orderbook_id,
35
35
  pair: price_msg.pair,
36
- depth,
36
+ mid_price: depth.mid_price,
37
+ fee_rate_bps: depth.fee_rate_bps,
38
+ ask: depth.ask,
39
+ bid: depth.bid,
37
40
  });
38
41
  }
39
42
  })
@@ -52,21 +55,8 @@ function report_quote_candidate(opts) {
52
55
  block_number: opts.block_number,
53
56
  ask_price: Number(opts.ask_price) || 0,
54
57
  bid_price: Number(opts.bid_price) || 0,
58
+ ask_tiers: opts.ask_tiers,
59
+ bid_tiers: opts.bid_tiers,
55
60
  computed_at: Date.now(),
56
61
  });
57
62
  }
58
- function on_quote_respose(appConfig, pool_info, quote_amount_usd, _execution_price, stream_time, quote_start_time, slot_info, result, txid, push_price_message = true, source = "", depth) {
59
- on_quote_response({
60
- appConfig,
61
- poolInfo: pool_info,
62
- quoteAmountUsd: quote_amount_usd,
63
- streamTime: stream_time,
64
- quoteStartTime: quote_start_time,
65
- blockNumber: parseInt(slot_info, 10) || 0,
66
- quotes: [result[0], result[1]],
67
- txid,
68
- pushPriceMessage: push_price_message,
69
- source,
70
- depth,
71
- });
72
- }
@@ -1,15 +1,30 @@
1
1
  "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
2
13
  Object.defineProperty(exports, "__esModule", { value: true });
3
14
  exports.publish_quote_price = void 0;
4
15
  const __1 = require("..");
16
+ const depth_tier_1 = require("./depth_tier");
5
17
  const publish_quote_price = (appConfig, price_msg) => {
6
18
  const chain_id = appConfig.env_args.chain_id;
7
19
  let price_id = price_msg.price_id;
20
+ const { fee_rate_bps, mid_price } = price_msg, rest = __rest(price_msg, ["fee_rate_bps", "mid_price"]);
21
+ const isTierEntry = (e) => e && 'tiers' in e && Array.isArray(e.tiers);
22
+ const orderbook_msg = Object.assign(Object.assign({}, rest), { ask: isTierEntry(price_msg.ask) ? (0, depth_tier_1.toOrderbookEntry)(price_msg.ask) : price_msg.ask, bid: isTierEntry(price_msg.bid) ? (0, depth_tier_1.toOrderbookEntry)(price_msg.bid) : price_msg.bid });
8
23
  let event = {
9
24
  event_type: __1.REDIS_EVENT_TYPE_QUOTE.NEW_QUOTE_PRICE,
10
25
  event_time: Date.now(),
11
26
  chain_id,
12
- data: price_msg
27
+ data: orderbook_msg,
13
28
  };
14
29
  appConfig.arb_cache.redis_event_publisher.publish_quote_price_event(chain_id, event);
15
30
  appConfig.arb_cache.cache_price_message(price_msg)
@@ -1,6 +1,6 @@
1
1
  import { StandardPoolInfoType, QuoteTimeInfoType, PriceMessageType } from '../../types';
2
2
  import { AppConfig } from '../appconfig';
3
- import { PoolDepthData } from './on_quote_response';
3
+ import { QuoteDepthOutput } from './depth_tier';
4
4
  export declare const get_quote_token_decimals: (pool_info: StandardPoolInfoType) => number;
5
- export declare function to_price_message(appConfig: AppConfig, quote_amount_usd: number, tx_price: number, quote_ask_price: number, quote_bid_price: number, pool_info: StandardPoolInfoType, time: QuoteTimeInfoType, slot?: string, source?: string, depth?: PoolDepthData): Promise<PriceMessageType>;
5
+ export declare function to_price_message(appConfig: AppConfig, quote_amount_usd: number, tx_price: number, quote_ask_price: number, quote_bid_price: number, pool_info: StandardPoolInfoType, time: QuoteTimeInfoType, slot: string | undefined, source: string, depth?: QuoteDepthOutput): Promise<PriceMessageType>;
6
6
  export declare function normalize_pair_name(priceMessage: PriceMessageType): void;
@@ -8,15 +8,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
11
  Object.defineProperty(exports, "__esModule", { value: true });
15
12
  exports.get_quote_token_decimals = void 0;
16
13
  exports.to_price_message = to_price_message;
17
14
  exports.normalize_pair_name = normalize_pair_name;
18
- const decimal_js_1 = __importDefault(require("decimal.js"));
19
- const core_env_1 = require("../appconfig/core_env");
20
15
  const uuid_1 = require("uuid");
21
16
  const __1 = require("..");
22
17
  const get_quote_token_decimals = (pool_info) => {
@@ -31,43 +26,40 @@ const get_quote_token_decimals = (pool_info) => {
31
26
  return decimals;
32
27
  };
33
28
  exports.get_quote_token_decimals = get_quote_token_decimals;
34
- function to_price_message(appConfig_1, quote_amount_usd_1, tx_price_1, quote_ask_price_1, quote_bid_price_1, pool_info_1, time_1, slot_1) {
35
- return __awaiter(this, arguments, void 0, function* (appConfig, quote_amount_usd, tx_price, quote_ask_price, quote_bid_price, pool_info, time, slot, source = "", depth) {
36
- var _a, _b;
29
+ function to_price_message(appConfig, quote_amount_usd, tx_price, quote_ask_price, quote_bid_price, pool_info, time, slot, source, depth) {
30
+ return __awaiter(this, void 0, void 0, function* () {
37
31
  let { pool_address, tokenA, tokenB } = pool_info;
38
32
  let { dex_id, pool_name, fee_rate, is_reverse_token } = yield appConfig.arb_cache.get_one_pool_info(pool_address);
39
33
  let _dex_id = dex_id;
40
34
  let price_id = (0, uuid_1.v4)().replace(/-/gi, '');
41
35
  let chain_id = appConfig.env_args.chain_id;
42
36
  let unique_orderbook_id = (0, __1.format_unique_orderbook_id)(chain_id, _dex_id, pool_address, fee_rate);
43
- time.price_time = new Date().getTime();
44
- let fee_rate_bps = fee_rate / 100100;
45
- let ask_price = getAskPrice(new decimal_js_1.default(quote_ask_price), fee_rate_bps * 0);
46
- let bid_price = getBidPrice(new decimal_js_1.default(quote_bid_price), fee_rate_bps * 0);
47
- const askData = {
48
- price: ask_price,
49
- quantity: quote_amount_usd
50
- };
51
- const bidData = {
52
- price: bid_price,
53
- quantity: quote_amount_usd
54
- };
55
- const cexBps = (0, core_env_1.getCoreEnv)().depth_cex_bps;
56
- const askDepth = (_a = depth === null || depth === void 0 ? void 0 : depth.ask) === null || _a === void 0 ? void 0 : _a[cexBps];
57
- const bidDepth = (_b = depth === null || depth === void 0 ? void 0 : depth.bid) === null || _b === void 0 ? void 0 : _b[cexBps];
58
- askData.depth_bps = cexBps;
59
- askData.depth_amount = askDepth ? askDepth.amount.toFixed(6) : '0';
60
- askData.depth_usd = askDepth ? askDepth.amountUsd.toFixed(2) : '0';
61
- bidData.depth_bps = cexBps;
62
- bidData.depth_amount = bidDepth ? bidDepth.amount.toFixed(6) : '0';
63
- bidData.depth_usd = bidDepth ? bidDepth.amountUsd.toFixed(2) : '0';
64
- let price_message = {
37
+ time.price_time = Date.now();
38
+ let ask;
39
+ let bid;
40
+ let mid_price;
41
+ let fee_rate_bps;
42
+ if (depth) {
43
+ ask = depth.ask;
44
+ bid = depth.bid;
45
+ mid_price = depth.mid_price;
46
+ fee_rate_bps = depth.fee_rate_bps;
47
+ }
48
+ else {
49
+ ask = { price: quote_ask_price.toFixed(12), quantity: quote_amount_usd };
50
+ bid = { price: quote_bid_price.toFixed(12), quantity: quote_amount_usd };
51
+ mid_price = (quote_ask_price + quote_bid_price) / 2;
52
+ fee_rate_bps = fee_rate;
53
+ }
54
+ const price_message = {
65
55
  chain_id,
66
56
  dex_id,
67
57
  price_id,
68
58
  pool_id: pool_address,
69
59
  pool_name,
70
60
  fee_rate,
61
+ fee_rate_bps,
62
+ mid_price,
71
63
  pair: pool_name,
72
64
  unique_orderbook_id,
73
65
  is_reverse_token,
@@ -87,25 +79,19 @@ function to_price_message(appConfig_1, quote_amount_usd_1, tx_price_1, quote_ask
87
79
  name: tokenB.name,
88
80
  enable: true,
89
81
  },
90
- tx_price: tx_price === null || tx_price === void 0 ? void 0 : tx_price.toFixed(12),
82
+ tx_price: tx_price ? tx_price.toFixed(12) : undefined,
91
83
  quote_amount_usd,
92
- ask: askData,
93
- bid: bidData,
84
+ ask,
85
+ bid,
94
86
  time,
95
- slot,
87
+ slot: slot !== null && slot !== void 0 ? slot : '',
96
88
  blockNumber: slot ? parseInt(slot, 10) || undefined : undefined,
97
- source: source !== null && source !== void 0 ? source : ""
89
+ source: source !== null && source !== void 0 ? source : '',
98
90
  };
99
91
  normalize_pair_name(price_message);
100
92
  return price_message;
101
93
  });
102
94
  }
103
- const getAskPrice = (price, feeRate) => {
104
- return price.mul(1 + feeRate).toFixed(12);
105
- };
106
- const getBidPrice = (price, feeRate) => {
107
- return price.mul(1 - feeRate).toFixed(12);
108
- };
109
95
  function normalize_pair_name(priceMessage) {
110
96
  let { pool_name, is_reverse_token } = priceMessage;
111
97
  if (is_reverse_token) {
@@ -59,7 +59,7 @@ class AbstractTransactionResultCheck {
59
59
  let { pool_name, is_reverse_token } = this.pool_info;
60
60
  let { chain_id, dex_id, unique_orderbook_id, pair, price_id, time: quote_time, ask, bid } = this.price_msg;
61
61
  let { group_id, unique_order_msg_id, order_send_time, order_recv_time, order_submit_time, aToB } = this.order_msg;
62
- let order_price = aToB ? bid.price : ask.price;
62
+ let order_price = String(aToB ? bid.price : ask.price);
63
63
  let server_info = (0, __1.getServerInfo)();
64
64
  let order_end_time = Date.now();
65
65
  let start_time = order_recv_time;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clonegod/ttd-core",
3
- "version": "3.1.55",
3
+ "version": "3.1.57",
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
@@ -1,3 +1,5 @@
1
+ import type { QuoteEntry } from '../src/quote/depth_tier'
2
+
1
3
  /*********************************************************************
2
4
  * CONFIG - TOKEN CONFIG TYPE *
3
5
  * *******************************************************************/
@@ -259,7 +261,12 @@ export interface PriceMessageType {
259
261
  dex_id: string
260
262
  pool_id: string
261
263
  pool_name: string
264
+ /** @deprecated 使用 fee_rate_bps;该字段在 W5 清理时移除 */
262
265
  fee_rate: number
266
+ /** 池子 effective fee(bps,例如 25 表示 0.25%);动态协议为最新观测值 */
267
+ fee_rate_bps: number
268
+ /** 池子当前 mid price(无冲击、无费的中间价;CLMM 从 sqrtPriceX96 反算,AMM 从 reserves 比值) */
269
+ mid_price: number
263
270
  pair: string // 标准化的币对名称
264
271
  unique_orderbook_id: string // ${chain_id}_${dex_id}_${pool_id}_${fee_rate}
265
272
  is_reverse_token: boolean // 是否反转币对进行交易(按USDT,SOL,TRX等作为计价币)
@@ -267,9 +274,16 @@ export interface PriceMessageType {
267
274
  tokenB?: StandardTokenInfoType
268
275
  price_id: string // 价格消息的唯一id
269
276
  tx_price?: string // 链上交易的执行价格
270
- quote_amount_usd: number // quote size,按多少U价值的Token数量进行询价
271
- ask: PriceType
272
- bid: PriceType
277
+ /** @deprecated W5 清理时移除;新模型不再用 USD 名义额驱动询价 */
278
+ quote_amount_usd: number
279
+ /**
280
+ * 卖方报价(双形态:V2 走 QuoteEntry tier 化、V1/V3 走 PriceType scalar)
281
+ *
282
+ * 下游用 `'tiers' in ask` 运行时判别 + `source` 后缀(`:v2` vs `:v1`/`:v3`)双保险
283
+ */
284
+ ask: QuoteEntry | PriceType
285
+ /** 买方报价:同上,两种形态共存 */
286
+ bid: QuoteEntry | PriceType
273
287
  time: QuoteTimeInfoType // quote 耗时数据
274
288
  slot: string // 询价生成的价格,所对应的slot (account, vaultA, vaultB)
275
289
  blockNumber?: number // 报价基于的区块号
@@ -290,10 +304,13 @@ export interface UniqueOrderbookIdType {
290
304
  fee_rate: string
291
305
  }
292
306
 
307
+ /**
308
+ * @deprecated 使用 QuoteEntry (from src/quote/depth_tier)。W5 清理时移除。
309
+ * 旧的"单价 + 动态 depth_{bps}_*"扁平结构。
310
+ */
293
311
  export interface PriceType {
294
312
  price: string
295
- quantity: number // ask: baseToken的挂单数量 bid: quoteToken的挂单数量
296
- /** 动态深度字段:depth_{bps} = token 数量, depth_{bps}_usd = USD 估值 */
313
+ quantity: number
297
314
  [key: string]: any
298
315
  }
299
316
 
@@ -312,15 +329,29 @@ export interface QuoteTimeInfoType {
312
329
  export interface OrderbookPriceType {
313
330
  chain_id: string
314
331
  pair: string
315
- quote_amount_usd: number
332
+ /** @deprecated W5 删除:新模型不再用 USD 名义额驱动询价。保留只为兼容旧消费者 */
333
+ quote_amount_usd?: number
316
334
  asks: Ladder[]
317
335
  bids: Ladder[]
318
336
  }
319
337
 
338
+ /**
339
+ * Orderbook 对外 HTTP 响应里的单档报价行(向 CEX 暴露)。
340
+ *
341
+ * 与内部 PriceMessage.ask 的对应关系:
342
+ * - 顶层 price/amount 镜像 tiers[pct=0.2](默认主档)
343
+ * - tiers 数组按 pct 升序,每档含 {pct, price, amount}
344
+ * - 不含 amount_in/fee/tick_move 等内部字段(已在 publish 时投影掉)
345
+ */
320
346
  export interface Ladder {
321
347
  unique_orderbook_id: string
348
+
349
+ /** 默认主档(0.2%)目标价;字符串以保持 wire 兼容性 */
322
350
  price: string
323
- quantity: string
351
+ /** 默认主档(0.2%)可成交 base token 数量 */
352
+ amount: string
353
+ /** 多档完整数据(按 pct 升序:0.1, 0.2, 0.3, 0.5, 1.0) */
354
+ tiers: { pct: number; price: string; amount: string }[]
324
355
 
325
356
  // 询价生成的价格,所对应的slot (account, vaultA, vaultB)
326
357
  slot: string
@@ -852,14 +883,36 @@ export interface OnTradePriceType {
852
883
  quote_bid: string
853
884
  cex_price: string
854
885
  exec_price: string
855
- /** 报价 vs 成交偏差 (bps), 正=成交比报价差 */
886
+ /**
887
+ * 报价 vs 成交偏差 (bps), 正=成交比报价差
888
+ * - tier 模式:用按 actualAmount 匹配的 tier price 作为参考价(更准)
889
+ * - scalar 模式:用 ask/bid 标量价
890
+ */
856
891
  quote_exec_deviation_bps: number
857
892
  /** CEX vs DEX 价差 (bps) */
858
893
  cex_dex_spread_bps: number
894
+
895
+ // ===== W3 新增:tier 模式偏差细分(旧消费者忽略可继续工作) =====
896
+ /** 实际成交量命中的 tier 偏移百分比(如 0.2 表示 0.2%) */
897
+ matched_tier_pct?: number
898
+ /**
899
+ * tier 匹配模式:
900
+ * - 'exact': 落在某档 amount_in 上
901
+ * - 'interpolated': 在两档之间线性插值
902
+ * - 'extrapolated_low': 小于最低档(用最低档兜底)
903
+ * - 'extrapolated_high': 大于最高档(用最高档兜底;提示成交量超出深度)
904
+ * - 'single_tier': V1 兜底仅 1 档可用
905
+ */
906
+ matched_tier_mode?: string
907
+ /** 纯价格漂移 (bps) = tier 匹配价 vs 成交价 —— 隔离了"大小不匹配"带来的伪偏差 */
908
+ price_drift_bps?: number
859
909
  }
860
910
 
861
911
  export interface OnTradeDepthType {
912
+ /** @deprecated 旧 bps-keyed 结构;新代码用 tiers 数组 */
862
913
  levels: Record<number, AnalyzeDepthLevelType>
914
+ /** W3 新增:按 pct 升序的多档数据 */
915
+ tiers?: AnalyzeTierLevelType[]
863
916
  trade_size_usd: number
864
917
  /** 交易量消耗了多少可用深度 (%) */
865
918
  depth_utilization_pct: number
@@ -876,6 +929,21 @@ export interface AnalyzeDepthLevelType {
876
929
  amount_in_usd: number
877
930
  }
878
931
 
932
+ /**
933
+ * W3 新增:tier 化深度档位
934
+ */
935
+ export interface AnalyzeTierLevelType {
936
+ pct: number
937
+ target_price: number
938
+ amount: number
939
+ amount_in: number
940
+ amount_in_usd: number
941
+ fee: number
942
+ fee_usd: number
943
+ cumulative_usd: number
944
+ tick_move?: string
945
+ }
946
+
879
947
  export interface OnTradeExecutionType {
880
948
  // Quote 阶段(来自 PriceMessage.time)
881
949
  block_time: number // 链上出块时间