@clonegod/ttd-bsc-common 3.1.65 → 3.1.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/appconfig/bsc_env_args.d.ts +1 -0
- package/dist/appconfig/bsc_env_args.js +1 -0
- package/dist/quote/depth/amm_depth_calculator.d.ts +4 -0
- package/dist/quote/depth/amm_depth_calculator.js +21 -4
- package/dist/quote/depth/clmm_depth_calculator.d.ts +2 -0
- package/dist/quote/depth/clmm_depth_calculator.js +13 -2
- package/dist/quote/depth/index.d.ts +8 -6
- package/dist/quote/depth/index.js +149 -35
- package/dist/quote/hook_fee_monitor.d.ts +10 -0
- package/dist/quote/hook_fee_monitor.js +32 -0
- package/dist/quote/index.d.ts +1 -0
- package/dist/quote/index.js +1 -0
- package/dist/quote/pricing/pool_state_initializer.d.ts +2 -0
- package/dist/quote/pricing/pool_state_initializer.js +37 -7
- package/dist/quote/verify/quote_price_verify.d.ts +5 -1
- package/dist/quote/verify/quote_price_verify.js +75 -8
- package/dist/trade/abstract_dex_trade.js +7 -0
- package/dist/trade/check/abstract_tx_result_checker.js +23 -0
- package/dist/types/pool_state.d.ts +4 -3
- package/package.json +2 -2
- package/dist/ws/event_filter.d.ts +0 -8
- package/dist/ws/event_filter.js +0 -36
- package/dist/ws/index.d.ts +0 -2
- package/dist/ws/index.js +0 -18
- package/dist/ws/subscribe_v2_events.d.ts +0 -14
- package/dist/ws/subscribe_v2_events.js +0 -160
- package/dist/ws/subscribe_v3_events.d.ts +0 -16
- package/dist/ws/subscribe_v3_events.js +0 -163
|
@@ -36,6 +36,7 @@ const constants_1 = require("../common/constants");
|
|
|
36
36
|
redis_url: { env: 'REDIS_URL', type: 'string', default: 'redis://127.0.0.1:6379', desc: 'Redis 连接 URL' },
|
|
37
37
|
skip_pool_list: { env: 'SKIP_POOL_LIST', type: 'string', default: '', desc: '跳过的池子名称列表(逗号分隔)' },
|
|
38
38
|
price_feed_skip_dup: { env: 'PRICE_FEED_SKIP_DUP', type: 'boolean', default: false, desc: 'PriceFeed 去重过滤' },
|
|
39
|
+
price_feed_client_id: { env: 'PRICE_FEED_CLIENT_ID', type: 'string', default: 'BSC_DC', desc: 'PriceFeed 订阅消息里的 client 标识(如 yyNode 区分客户端)' },
|
|
39
40
|
trade_analyze_port: { env: 'TRADE_ANALYZE_PORT', type: 'number', default: 8004, desc: '上报 analyze 的 HTTP 端口' },
|
|
40
41
|
log_max_size_mb: { env: 'LOG_MAX_SIZE_MB', type: 'number', default: 20, desc: '日志文件大小上限(MB)' },
|
|
41
42
|
log_check_interval_min: { env: 'LOG_CHECK_INTERVAL_MIN', type: 'number', default: 10, desc: '日志检查间隔(分钟)' },
|
|
@@ -11,5 +11,9 @@ export interface AmmDepthResult {
|
|
|
11
11
|
amountOutWei: bigint;
|
|
12
12
|
amountOut: number;
|
|
13
13
|
outputDecimals: number;
|
|
14
|
+
amountInWei: bigint;
|
|
15
|
+
amountIn: number;
|
|
16
|
+
inputDecimals: number;
|
|
17
|
+
targetPrice: number;
|
|
14
18
|
}
|
|
15
19
|
export declare function calculateAmmDepth(input: AmmDepthInput): AmmDepthResult;
|
|
@@ -7,32 +7,49 @@ function calculateAmmDepth(input) {
|
|
|
7
7
|
const R0 = BigInt(input.reserve0);
|
|
8
8
|
const R1 = BigInt(input.reserve1);
|
|
9
9
|
if (R0 === 0n || R1 === 0n) {
|
|
10
|
-
return { amountOutWei: 0n, amountOut: 0, outputDecimals: 0 };
|
|
10
|
+
return { amountOutWei: 0n, amountOut: 0, outputDecimals: 0, amountInWei: 0n, amountIn: 0, inputDecimals: 0, targetPrice: 0 };
|
|
11
11
|
}
|
|
12
12
|
const k = R0 * R1;
|
|
13
13
|
const bps = BigInt(targetBps);
|
|
14
14
|
const PRECISION = 10n ** 18n;
|
|
15
15
|
let amountOutWei;
|
|
16
|
+
let amountInWei;
|
|
16
17
|
let outputDecimals;
|
|
18
|
+
let inputDecimals;
|
|
17
19
|
if (isBuy) {
|
|
18
20
|
const [baseReserve, quoteReserve] = baseIsToken0 ? [R0, R1] : [R1, R0];
|
|
19
21
|
outputDecimals = baseIsToken0 ? token0Decimals : token1Decimals;
|
|
20
|
-
|
|
21
|
-
const denominator = (10000n + bps) * quoteReserve;
|
|
22
|
+
inputDecimals = baseIsToken0 ? token1Decimals : token0Decimals;
|
|
22
23
|
const baseReserveNew = baseReserve * (0, clmm_depth_calculator_1.bigIntSqrt)(10000n * PRECISION) / (0, clmm_depth_calculator_1.bigIntSqrt)((10000n + bps) * PRECISION);
|
|
24
|
+
const quoteReserveNew = quoteReserve * (0, clmm_depth_calculator_1.bigIntSqrt)((10000n + bps) * PRECISION) / (0, clmm_depth_calculator_1.bigIntSqrt)(10000n * PRECISION);
|
|
23
25
|
amountOutWei = baseReserve - baseReserveNew;
|
|
26
|
+
amountInWei = quoteReserveNew - quoteReserve;
|
|
24
27
|
}
|
|
25
28
|
else {
|
|
26
29
|
const [baseReserve, quoteReserve] = baseIsToken0 ? [R0, R1] : [R1, R0];
|
|
27
30
|
outputDecimals = baseIsToken0 ? token1Decimals : token0Decimals;
|
|
31
|
+
inputDecimals = baseIsToken0 ? token0Decimals : token1Decimals;
|
|
28
32
|
if (bps >= 10000n) {
|
|
29
33
|
amountOutWei = quoteReserve;
|
|
34
|
+
amountInWei = baseReserve;
|
|
30
35
|
}
|
|
31
36
|
else {
|
|
32
37
|
const quoteReserveNew = quoteReserve * (0, clmm_depth_calculator_1.bigIntSqrt)((10000n - bps) * PRECISION) / (0, clmm_depth_calculator_1.bigIntSqrt)(10000n * PRECISION);
|
|
38
|
+
const baseReserveNew = baseReserve * (0, clmm_depth_calculator_1.bigIntSqrt)(10000n * PRECISION) / (0, clmm_depth_calculator_1.bigIntSqrt)((10000n - bps) * PRECISION);
|
|
33
39
|
amountOutWei = quoteReserve - quoteReserveNew;
|
|
40
|
+
amountInWei = baseReserveNew - baseReserve;
|
|
34
41
|
}
|
|
35
42
|
}
|
|
36
43
|
const amountOut = Number(amountOutWei) / Math.pow(10, outputDecimals);
|
|
37
|
-
|
|
44
|
+
const amountIn = Number(amountInWei) / Math.pow(10, inputDecimals);
|
|
45
|
+
const baseDec = baseIsToken0 ? token0Decimals : token1Decimals;
|
|
46
|
+
const quoteDec = baseIsToken0 ? token1Decimals : token0Decimals;
|
|
47
|
+
const baseReserveUi = Number(baseIsToken0 ? R0 : R1) / Math.pow(10, baseDec);
|
|
48
|
+
const quoteReserveUi = Number(baseIsToken0 ? R1 : R0) / Math.pow(10, quoteDec);
|
|
49
|
+
const midPrice = quoteReserveUi / baseReserveUi;
|
|
50
|
+
const bpsNum = Number(targetBps);
|
|
51
|
+
const targetPrice = isBuy
|
|
52
|
+
? midPrice * (10000 + bpsNum) / 10000
|
|
53
|
+
: midPrice * (10000 - bpsNum) / 10000;
|
|
54
|
+
return { amountOutWei, amountOut, outputDecimals, amountInWei, amountIn, inputDecimals, targetPrice };
|
|
38
55
|
}
|
|
@@ -19,8 +19,10 @@ export interface DepthResult {
|
|
|
19
19
|
amountOut: number;
|
|
20
20
|
currentTick: number;
|
|
21
21
|
targetTick: number;
|
|
22
|
+
targetSqrtPriceX96: bigint;
|
|
22
23
|
}
|
|
23
24
|
export declare function calculateClmmDepth(input: ClmmDepthInput): DepthResult;
|
|
25
|
+
export declare function priceFromSqrtX96(sqrtPriceX96: bigint, baseDecimals: number, quoteDecimals: number, baseIsToken0: boolean): number;
|
|
24
26
|
declare function computeTargetSqrtPrice(currentSqrtPriceX96: bigint, bps: number, zeroForOne: boolean): bigint;
|
|
25
27
|
declare function bigIntSqrt(n: bigint): bigint;
|
|
26
28
|
export { getSqrtRatioAtTick, computeTargetSqrtPrice, bigIntSqrt };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.calculateClmmDepth = calculateClmmDepth;
|
|
4
|
+
exports.priceFromSqrtX96 = priceFromSqrtX96;
|
|
4
5
|
exports.getSqrtRatioAtTick = getSqrtRatioAtTick;
|
|
5
6
|
exports.computeTargetSqrtPrice = computeTargetSqrtPrice;
|
|
6
7
|
exports.bigIntSqrt = bigIntSqrt;
|
|
@@ -60,7 +61,7 @@ function calculateClmmDepth(input) {
|
|
|
60
61
|
const currentSqrtPriceX96 = BigInt(input.sqrtPriceX96);
|
|
61
62
|
let liquidity = BigInt(input.liquidity);
|
|
62
63
|
if (liquidity === 0n) {
|
|
63
|
-
return { amountInWei: 0n, amountIn: 0, amountOutWei: 0n, amountOut: 0, currentTick, targetTick: currentTick };
|
|
64
|
+
return { amountInWei: 0n, amountIn: 0, amountOutWei: 0n, amountOut: 0, currentTick, targetTick: currentTick, targetSqrtPriceX96: currentSqrtPriceX96 };
|
|
64
65
|
}
|
|
65
66
|
const targetSqrtPriceX96 = computeTargetSqrtPrice(currentSqrtPriceX96, targetBps, zeroForOne);
|
|
66
67
|
const allTicks = tickCache.getCachedTickIndices(poolAddress);
|
|
@@ -94,7 +95,17 @@ function calculateClmmDepth(input) {
|
|
|
94
95
|
const amountOut = Number(totalOutput) / Math.pow(10, outputDecimals);
|
|
95
96
|
const sqrtPriceFloat = Number(targetSqrtPriceX96) / Number(Q96);
|
|
96
97
|
const targetTick = Math.floor(Math.log(sqrtPriceFloat * sqrtPriceFloat) / Math.log(1.0001));
|
|
97
|
-
return { amountInWei: totalInput, amountIn, amountOutWei: totalOutput, amountOut, currentTick, targetTick };
|
|
98
|
+
return { amountInWei: totalInput, amountIn, amountOutWei: totalOutput, amountOut, currentTick, targetTick, targetSqrtPriceX96 };
|
|
99
|
+
}
|
|
100
|
+
function priceFromSqrtX96(sqrtPriceX96, baseDecimals, quoteDecimals, baseIsToken0) {
|
|
101
|
+
const sp = Number(sqrtPriceX96) / Number(Q96);
|
|
102
|
+
const priceToken1PerToken0 = sp * sp;
|
|
103
|
+
if (baseIsToken0) {
|
|
104
|
+
return priceToken1PerToken0 * Math.pow(10, baseDecimals - quoteDecimals);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
return (1 / priceToken1PerToken0) * Math.pow(10, baseDecimals - quoteDecimals);
|
|
108
|
+
}
|
|
98
109
|
}
|
|
99
110
|
function computeTargetSqrtPrice(currentSqrtPriceX96, bps, zeroForOne) {
|
|
100
111
|
const PRECISION = 10n ** 18n;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
+
import { QuoteDepthOutput } from '@clonegod/ttd-core';
|
|
1
2
|
import { ClmmTickCache } from '../tick/clmm_tick_cache';
|
|
2
|
-
export { calculateClmmDepth, getSqrtRatioAtTick, computeTargetSqrtPrice, bigIntSqrt } from './clmm_depth_calculator';
|
|
3
|
+
export { calculateClmmDepth, getSqrtRatioAtTick, computeTargetSqrtPrice, bigIntSqrt, priceFromSqrtX96 } from './clmm_depth_calculator';
|
|
3
4
|
export type { ClmmDepthInput, DepthResult } from './clmm_depth_calculator';
|
|
4
5
|
export { calculateAmmDepth } from './amm_depth_calculator';
|
|
5
6
|
export type { AmmDepthInput, AmmDepthResult } from './amm_depth_calculator';
|
|
6
7
|
export { buildTickLiquiditySnapshot } from './tick_liquidity_snapshot';
|
|
7
8
|
export type { TickLiquiditySnapshot, TickLiquiditySnapshotInput, TickDataPoint } from './tick_liquidity_snapshot';
|
|
8
9
|
import { TickLiquiditySnapshot } from './tick_liquidity_snapshot';
|
|
9
|
-
|
|
10
|
-
export
|
|
11
|
-
export declare function getDepthBps(): number[];
|
|
10
|
+
export declare function logFeeChainConfigOnce(poolName: string, poolAddress: string, configFeeBps: number, chainFeeBps: number, dexId?: string, pair?: string): void;
|
|
11
|
+
export declare function clearFeeMatchLogged(poolAddress: string): void;
|
|
12
12
|
export interface BuildClmmDepthInput {
|
|
13
13
|
poolInfo: {
|
|
14
14
|
pool_name: string;
|
|
@@ -27,9 +27,10 @@ export interface BuildClmmDepthInput {
|
|
|
27
27
|
};
|
|
28
28
|
basePriceUsd: number;
|
|
29
29
|
quotePriceUsd: number;
|
|
30
|
+
feeRateBps: number;
|
|
30
31
|
}
|
|
31
32
|
export interface ClmmDepthResult {
|
|
32
|
-
depth:
|
|
33
|
+
depth: QuoteDepthOutput;
|
|
33
34
|
tickLiquidity?: TickLiquiditySnapshot;
|
|
34
35
|
}
|
|
35
36
|
export declare function buildClmmDepth(input: BuildClmmDepthInput): ClmmDepthResult | undefined;
|
|
@@ -45,5 +46,6 @@ export interface BuildAmmDepthInput {
|
|
|
45
46
|
token0Address: string;
|
|
46
47
|
basePriceUsd: number;
|
|
47
48
|
quotePriceUsd: number;
|
|
49
|
+
feeRateBps: number;
|
|
48
50
|
}
|
|
49
|
-
export declare function buildAmmDepth(input: BuildAmmDepthInput):
|
|
51
|
+
export declare function buildAmmDepth(input: BuildAmmDepthInput): QuoteDepthOutput | undefined;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.buildTickLiquiditySnapshot = exports.calculateAmmDepth = exports.bigIntSqrt = exports.computeTargetSqrtPrice = exports.getSqrtRatioAtTick = exports.calculateClmmDepth = void 0;
|
|
4
|
-
exports.
|
|
3
|
+
exports.buildTickLiquiditySnapshot = exports.calculateAmmDepth = exports.priceFromSqrtX96 = exports.bigIntSqrt = exports.computeTargetSqrtPrice = exports.getSqrtRatioAtTick = exports.calculateClmmDepth = void 0;
|
|
4
|
+
exports.logFeeChainConfigOnce = logFeeChainConfigOnce;
|
|
5
|
+
exports.clearFeeMatchLogged = clearFeeMatchLogged;
|
|
5
6
|
exports.buildClmmDepth = buildClmmDepth;
|
|
6
7
|
exports.buildAmmDepth = buildAmmDepth;
|
|
7
8
|
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
9
|
+
const ttd_core_2 = require("@clonegod/ttd-core");
|
|
8
10
|
const clmm_depth_calculator_1 = require("./clmm_depth_calculator");
|
|
9
11
|
const amm_depth_calculator_1 = require("./amm_depth_calculator");
|
|
10
12
|
const trade_direction_1 = require("../../utils/trade_direction");
|
|
@@ -13,39 +15,85 @@ Object.defineProperty(exports, "calculateClmmDepth", { enumerable: true, get: fu
|
|
|
13
15
|
Object.defineProperty(exports, "getSqrtRatioAtTick", { enumerable: true, get: function () { return clmm_depth_calculator_2.getSqrtRatioAtTick; } });
|
|
14
16
|
Object.defineProperty(exports, "computeTargetSqrtPrice", { enumerable: true, get: function () { return clmm_depth_calculator_2.computeTargetSqrtPrice; } });
|
|
15
17
|
Object.defineProperty(exports, "bigIntSqrt", { enumerable: true, get: function () { return clmm_depth_calculator_2.bigIntSqrt; } });
|
|
18
|
+
Object.defineProperty(exports, "priceFromSqrtX96", { enumerable: true, get: function () { return clmm_depth_calculator_2.priceFromSqrtX96; } });
|
|
16
19
|
var amm_depth_calculator_2 = require("./amm_depth_calculator");
|
|
17
20
|
Object.defineProperty(exports, "calculateAmmDepth", { enumerable: true, get: function () { return amm_depth_calculator_2.calculateAmmDepth; } });
|
|
18
21
|
var tick_liquidity_snapshot_1 = require("./tick_liquidity_snapshot");
|
|
19
22
|
Object.defineProperty(exports, "buildTickLiquiditySnapshot", { enumerable: true, get: function () { return tick_liquidity_snapshot_1.buildTickLiquiditySnapshot; } });
|
|
20
23
|
const tick_liquidity_snapshot_2 = require("./tick_liquidity_snapshot");
|
|
21
24
|
const clmm_tick_cache_1 = require("../tick/clmm_tick_cache");
|
|
22
|
-
let
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
25
|
+
let _depthPctLogged = false;
|
|
26
|
+
function logDepthLevelsOnce() {
|
|
27
|
+
if (_depthPctLogged)
|
|
28
|
+
return;
|
|
29
|
+
_depthPctLogged = true;
|
|
30
|
+
const pcts = (0, ttd_core_2.getDepthPricePctLevels)();
|
|
31
|
+
(0, ttd_core_1.log_info)(`[Depth] pctLevels=${JSON.stringify(pcts)}, default=${ttd_core_2.DEFAULT_TIER_PCT}`);
|
|
32
|
+
}
|
|
33
|
+
const _feeMatchLoggedPools = new Set();
|
|
34
|
+
function logFeeChainConfigOnce(poolName, poolAddress, configFeeBps, chainFeeBps, dexId, pair) {
|
|
35
|
+
if (_feeMatchLoggedPools.has(poolAddress))
|
|
36
|
+
return;
|
|
37
|
+
_feeMatchLoggedPools.add(poolAddress);
|
|
38
|
+
const diff = Math.abs(configFeeBps - chainFeeBps);
|
|
39
|
+
const drifted = diff >= 0.01;
|
|
40
|
+
if (drifted) {
|
|
41
|
+
(0, ttd_core_1.log_warn)(`[Fee] ${poolName} 配置漂移: config=${configFeeBps}bps ≠ chain=${chainFeeBps}bps (差 ${diff.toFixed(2)}bps) — 已用链上值,请运维检查池子配置`);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
(0, ttd_core_1.log_info)(`[Fee] ${poolName} config=${configFeeBps}bps == chain=${chainFeeBps}bps ✓`);
|
|
38
45
|
}
|
|
39
|
-
|
|
46
|
+
const addrLower = poolAddress.toLowerCase();
|
|
47
|
+
ttd_core_2.ALERT_TYPES.QUOTE_FEE_CHAIN_DRIFT.reportIfChanged(addrLower, drifted, () => ({
|
|
48
|
+
identity: addrLower,
|
|
49
|
+
scope: { dex_id: dexId, pool_address: addrLower, pair },
|
|
50
|
+
title: `${poolName} fee 漂移 cfg=${configFeeBps}bps chain=${chainFeeBps}bps`,
|
|
51
|
+
detail: {
|
|
52
|
+
pool_name: poolName,
|
|
53
|
+
config_fee_bps: configFeeBps,
|
|
54
|
+
chain_fee_bps: chainFeeBps,
|
|
55
|
+
diff_bps: diff,
|
|
56
|
+
},
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
function clearFeeMatchLogged(poolAddress) {
|
|
60
|
+
_feeMatchLoggedPools.delete(poolAddress);
|
|
61
|
+
}
|
|
62
|
+
function grossUpFee(amountInNet, feeRateBps) {
|
|
63
|
+
if (feeRateBps <= 0 || amountInNet <= 0) {
|
|
64
|
+
return { amountInGross: amountInNet, feeAmount: 0 };
|
|
65
|
+
}
|
|
66
|
+
const feeRatio = feeRateBps / 10000;
|
|
67
|
+
const amountInGross = amountInNet / (1 - feeRatio);
|
|
68
|
+
const feeAmount = amountInGross - amountInNet;
|
|
69
|
+
return { amountInGross, feeAmount };
|
|
40
70
|
}
|
|
41
71
|
const _tickLiquidityLastBuild = new Map();
|
|
72
|
+
function assembleEntry(tiers) {
|
|
73
|
+
const defaultTier = tiers.find(t => t.pct === ttd_core_2.DEFAULT_TIER_PCT) ?? tiers[0];
|
|
74
|
+
return {
|
|
75
|
+
price: defaultTier.price,
|
|
76
|
+
amount: defaultTier.amount,
|
|
77
|
+
amount_in: defaultTier.amount_in,
|
|
78
|
+
amount_in_usd: defaultTier.amount_in_usd,
|
|
79
|
+
fee: defaultTier.fee,
|
|
80
|
+
fee_usd: defaultTier.fee_usd,
|
|
81
|
+
tick_move: defaultTier.tick_move,
|
|
82
|
+
tiers,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
42
85
|
function buildClmmDepth(input) {
|
|
43
|
-
|
|
44
|
-
|
|
86
|
+
logDepthLevelsOnce();
|
|
87
|
+
const pctLevels = (0, ttd_core_2.getDepthPricePctLevels)();
|
|
88
|
+
if (pctLevels.length === 0)
|
|
45
89
|
return undefined;
|
|
46
|
-
const { poolInfo, poolAddress, tickCache, poolState, basePriceUsd, quotePriceUsd } = input;
|
|
90
|
+
const { poolInfo, poolAddress, tickCache, poolState, basePriceUsd, quotePriceUsd, feeRateBps } = input;
|
|
47
91
|
if (BigInt(poolState.liquidity) === 0n)
|
|
48
92
|
return undefined;
|
|
93
|
+
if (feeRateBps == null || feeRateBps < 0) {
|
|
94
|
+
(0, ttd_core_1.log_debug)(`[Depth] ${poolInfo.pool_name} CLMM: invalid feeRateBps=${feeRateBps}, skip`, '');
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
49
97
|
try {
|
|
50
98
|
const askDirection = (0, trade_direction_1.resolveTradeDirection)(poolInfo, true);
|
|
51
99
|
const bidDirection = (0, trade_direction_1.resolveTradeDirection)(poolInfo, false);
|
|
@@ -53,8 +101,12 @@ function buildClmmDepth(input) {
|
|
|
53
101
|
const bidZeroForOne = bidDirection.inputToken.address.toLowerCase() === poolState.token0.toLowerCase();
|
|
54
102
|
const baseToken = askDirection.baseToken;
|
|
55
103
|
const quoteToken = askDirection.quoteToken;
|
|
56
|
-
const
|
|
57
|
-
|
|
104
|
+
const baseIsToken0 = baseToken.address.toLowerCase() === poolState.token0.toLowerCase();
|
|
105
|
+
const midPrice = (0, clmm_depth_calculator_1.priceFromSqrtX96)(BigInt(poolState.sqrtPriceX96), baseToken.decimals, quoteToken.decimals, baseIsToken0);
|
|
106
|
+
const askTiers = [];
|
|
107
|
+
const bidTiers = [];
|
|
108
|
+
for (const pct of pctLevels) {
|
|
109
|
+
const bps = Math.round(pct * 100);
|
|
58
110
|
const askResult = (0, clmm_depth_calculator_1.calculateClmmDepth)({
|
|
59
111
|
poolAddress, tickCache,
|
|
60
112
|
currentTick: poolState.tick, sqrtPriceX96: poolState.sqrtPriceX96,
|
|
@@ -69,15 +121,43 @@ function buildClmmDepth(input) {
|
|
|
69
121
|
zeroForOne: bidZeroForOne, targetBps: bps,
|
|
70
122
|
inputDecimals: baseToken.decimals, outputDecimals: quoteToken.decimals,
|
|
71
123
|
});
|
|
124
|
+
const askTargetPrice = (0, clmm_depth_calculator_1.priceFromSqrtX96)(askResult.targetSqrtPriceX96, baseToken.decimals, quoteToken.decimals, baseIsToken0);
|
|
125
|
+
const bidTargetPrice = (0, clmm_depth_calculator_1.priceFromSqrtX96)(bidResult.targetSqrtPriceX96, baseToken.decimals, quoteToken.decimals, baseIsToken0);
|
|
126
|
+
const askGross = grossUpFee(askResult.amountIn, feeRateBps);
|
|
127
|
+
const bidGross = grossUpFee(bidResult.amountIn, feeRateBps);
|
|
72
128
|
const askTickMove = askResult.targetTick < askResult.currentTick
|
|
73
129
|
? `${askResult.targetTick} <- ${askResult.currentTick}`
|
|
74
130
|
: `${askResult.currentTick} -> ${askResult.targetTick}`;
|
|
75
131
|
const bidTickMove = bidResult.targetTick > bidResult.currentTick
|
|
76
132
|
? `${bidResult.currentTick} -> ${bidResult.targetTick}`
|
|
77
133
|
: `${bidResult.targetTick} <- ${bidResult.currentTick}`;
|
|
78
|
-
|
|
79
|
-
|
|
134
|
+
askTiers.push({
|
|
135
|
+
pct,
|
|
136
|
+
price: askTargetPrice,
|
|
137
|
+
amount: askResult.amountOut,
|
|
138
|
+
amount_in: askGross.amountInGross,
|
|
139
|
+
amount_in_usd: askGross.amountInGross * quotePriceUsd,
|
|
140
|
+
fee: askGross.feeAmount,
|
|
141
|
+
fee_usd: askGross.feeAmount * quotePriceUsd,
|
|
142
|
+
tick_move: askTickMove,
|
|
143
|
+
});
|
|
144
|
+
bidTiers.push({
|
|
145
|
+
pct,
|
|
146
|
+
price: bidTargetPrice,
|
|
147
|
+
amount: bidResult.amountOut,
|
|
148
|
+
amount_in: bidGross.amountInGross,
|
|
149
|
+
amount_in_usd: bidGross.amountInGross * basePriceUsd,
|
|
150
|
+
fee: bidGross.feeAmount,
|
|
151
|
+
fee_usd: bidGross.feeAmount * basePriceUsd,
|
|
152
|
+
tick_move: bidTickMove,
|
|
153
|
+
});
|
|
80
154
|
}
|
|
155
|
+
const depth = {
|
|
156
|
+
mid_price: midPrice,
|
|
157
|
+
fee_rate_bps: feeRateBps,
|
|
158
|
+
ask: assembleEntry(askTiers),
|
|
159
|
+
bid: assembleEntry(bidTiers),
|
|
160
|
+
};
|
|
81
161
|
let tickLiquidity;
|
|
82
162
|
const now = Date.now();
|
|
83
163
|
const lastBuild = _tickLiquidityLastBuild.get(poolAddress) || 0;
|
|
@@ -86,7 +166,6 @@ function buildClmmDepth(input) {
|
|
|
86
166
|
}
|
|
87
167
|
_tickLiquidityLastBuild.set(poolAddress, now);
|
|
88
168
|
try {
|
|
89
|
-
const baseIsToken0 = baseToken.address.toLowerCase() === poolState.token0.toLowerCase();
|
|
90
169
|
const tokenAIsToken0 = poolInfo.tokenA?.address?.toLowerCase() === poolState.token0.toLowerCase();
|
|
91
170
|
const token0Info = tokenAIsToken0 ? poolInfo.tokenA : poolInfo.tokenB;
|
|
92
171
|
const token1Info = tokenAIsToken0 ? poolInfo.tokenB : poolInfo.tokenA;
|
|
@@ -117,17 +196,29 @@ function buildClmmDepth(input) {
|
|
|
117
196
|
}
|
|
118
197
|
}
|
|
119
198
|
function buildAmmDepth(input) {
|
|
120
|
-
|
|
121
|
-
|
|
199
|
+
logDepthLevelsOnce();
|
|
200
|
+
const pctLevels = (0, ttd_core_2.getDepthPricePctLevels)();
|
|
201
|
+
if (pctLevels.length === 0)
|
|
202
|
+
return undefined;
|
|
203
|
+
const { poolInfo, reserve0, reserve1, token0Address, basePriceUsd, quotePriceUsd, feeRateBps } = input;
|
|
204
|
+
if (feeRateBps == null || feeRateBps < 0) {
|
|
205
|
+
(0, ttd_core_1.log_debug)(`[Depth] ${poolInfo.pool_name} AMM: invalid feeRateBps=${feeRateBps}, skip`, '');
|
|
122
206
|
return undefined;
|
|
123
|
-
|
|
207
|
+
}
|
|
124
208
|
try {
|
|
125
209
|
const askDirection = (0, trade_direction_1.resolveTradeDirection)(poolInfo, true);
|
|
126
210
|
const baseToken = askDirection.baseToken;
|
|
127
211
|
const quoteToken = askDirection.quoteToken;
|
|
128
212
|
const baseIsToken0 = baseToken.address.toLowerCase() === token0Address.toLowerCase();
|
|
129
|
-
const
|
|
130
|
-
|
|
213
|
+
const baseDec = baseIsToken0 ? baseToken.decimals : quoteToken.decimals;
|
|
214
|
+
const quoteDec = baseIsToken0 ? quoteToken.decimals : baseToken.decimals;
|
|
215
|
+
const baseReserveUi = Number(BigInt(baseIsToken0 ? reserve0 : reserve1)) / Math.pow(10, baseToken.decimals);
|
|
216
|
+
const quoteReserveUi = Number(BigInt(baseIsToken0 ? reserve1 : reserve0)) / Math.pow(10, quoteToken.decimals);
|
|
217
|
+
const midPrice = quoteReserveUi / baseReserveUi;
|
|
218
|
+
const askTiers = [];
|
|
219
|
+
const bidTiers = [];
|
|
220
|
+
for (const pct of pctLevels) {
|
|
221
|
+
const bps = Math.round(pct * 100);
|
|
131
222
|
const askResult = (0, amm_depth_calculator_1.calculateAmmDepth)({
|
|
132
223
|
reserve0, reserve1, targetBps: bps, isBuy: true, baseIsToken0,
|
|
133
224
|
token0Decimals: baseIsToken0 ? baseToken.decimals : quoteToken.decimals,
|
|
@@ -138,10 +229,33 @@ function buildAmmDepth(input) {
|
|
|
138
229
|
token0Decimals: baseIsToken0 ? baseToken.decimals : quoteToken.decimals,
|
|
139
230
|
token1Decimals: baseIsToken0 ? quoteToken.decimals : baseToken.decimals,
|
|
140
231
|
});
|
|
141
|
-
|
|
142
|
-
|
|
232
|
+
const askGross = grossUpFee(askResult.amountIn, feeRateBps);
|
|
233
|
+
const bidGross = grossUpFee(bidResult.amountIn, feeRateBps);
|
|
234
|
+
askTiers.push({
|
|
235
|
+
pct,
|
|
236
|
+
price: askResult.targetPrice,
|
|
237
|
+
amount: askResult.amountOut,
|
|
238
|
+
amount_in: askGross.amountInGross,
|
|
239
|
+
amount_in_usd: askGross.amountInGross * quotePriceUsd,
|
|
240
|
+
fee: askGross.feeAmount,
|
|
241
|
+
fee_usd: askGross.feeAmount * quotePriceUsd,
|
|
242
|
+
});
|
|
243
|
+
bidTiers.push({
|
|
244
|
+
pct,
|
|
245
|
+
price: bidResult.targetPrice,
|
|
246
|
+
amount: bidResult.amountOut,
|
|
247
|
+
amount_in: bidGross.amountInGross,
|
|
248
|
+
amount_in_usd: bidGross.amountInGross * basePriceUsd,
|
|
249
|
+
fee: bidGross.feeAmount,
|
|
250
|
+
fee_usd: bidGross.feeAmount * basePriceUsd,
|
|
251
|
+
});
|
|
143
252
|
}
|
|
144
|
-
return
|
|
253
|
+
return {
|
|
254
|
+
mid_price: midPrice,
|
|
255
|
+
fee_rate_bps: feeRateBps,
|
|
256
|
+
ask: assembleEntry(askTiers),
|
|
257
|
+
bid: assembleEntry(bidTiers),
|
|
258
|
+
};
|
|
145
259
|
}
|
|
146
260
|
catch (error) {
|
|
147
261
|
(0, ttd_core_1.log_debug)(`[Depth] ${poolInfo.pool_name} AMM depth failed: ${error.message}`, '');
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function reportHookFeeChangeIfChanged(opts: {
|
|
2
|
+
pool_address: string;
|
|
3
|
+
dex_id: string;
|
|
4
|
+
pool_name?: string;
|
|
5
|
+
block_number: number;
|
|
6
|
+
prevLpFee: number | undefined;
|
|
7
|
+
prevProtocolFee: number | undefined;
|
|
8
|
+
newLpFee: number;
|
|
9
|
+
newProtocolFee: number;
|
|
10
|
+
}): void;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reportHookFeeChangeIfChanged = reportHookFeeChangeIfChanged;
|
|
4
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
5
|
+
function reportHookFeeChangeIfChanged(opts) {
|
|
6
|
+
const { pool_address, dex_id, pool_name, block_number, prevLpFee, prevProtocolFee, newLpFee, newProtocolFee } = opts;
|
|
7
|
+
if (prevLpFee == null)
|
|
8
|
+
return;
|
|
9
|
+
const prevTotal = prevLpFee + (prevProtocolFee ?? 0);
|
|
10
|
+
const newTotal = newLpFee + newProtocolFee;
|
|
11
|
+
if (prevTotal === newTotal)
|
|
12
|
+
return;
|
|
13
|
+
const addr = pool_address.toLowerCase();
|
|
14
|
+
const prevBps = (prevTotal / 100).toFixed(2);
|
|
15
|
+
const newBps = (newTotal / 100).toFixed(2);
|
|
16
|
+
ttd_core_1.ALERT_TYPES.QUOTE_HOOK_FEE_CHANGE.reportIfChanged(addr, true, () => ({
|
|
17
|
+
identity: addr,
|
|
18
|
+
scope: { dex_id, pool_address: addr },
|
|
19
|
+
title: `${pool_name || addr.slice(0, 10)} hook fee ${prevBps}bps → ${newBps}bps`,
|
|
20
|
+
detail: {
|
|
21
|
+
pool_name: pool_name || '',
|
|
22
|
+
block_number,
|
|
23
|
+
prev_lp_fee_ppm: prevLpFee,
|
|
24
|
+
prev_protocol_fee_ppm: prevProtocolFee ?? 0,
|
|
25
|
+
prev_total_ppm: prevTotal,
|
|
26
|
+
new_lp_fee_ppm: newLpFee,
|
|
27
|
+
new_protocol_fee_ppm: newProtocolFee,
|
|
28
|
+
new_total_ppm: newTotal,
|
|
29
|
+
delta_ppm: newTotal - prevTotal,
|
|
30
|
+
},
|
|
31
|
+
}));
|
|
32
|
+
}
|
package/dist/quote/index.d.ts
CHANGED
package/dist/quote/index.js
CHANGED
|
@@ -22,3 +22,4 @@ __exportStar(require("./verify"), exports);
|
|
|
22
22
|
__exportStar(require("./price_feed_handler"), exports);
|
|
23
23
|
__exportStar(require("./quote_amount"), exports);
|
|
24
24
|
__exportStar(require("./preload_token_prices"), exports);
|
|
25
|
+
__exportStar(require("./hook_fee_monitor"), exports);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { AmmPoolState, ClmmPoolState, InfinityPoolState } from "../../types/pool_state";
|
|
2
|
+
declare function withInitRetry<T>(fn: () => Promise<T>, label: string): Promise<T>;
|
|
3
|
+
export { withInitRetry };
|
|
2
4
|
export declare class PoolStateInitializer {
|
|
3
5
|
static initAmmPool(provider: any, poolAddress: string, dexId: string, ethersLib: any): Promise<AmmPoolState>;
|
|
4
6
|
static initClmmPool(provider: any, poolAddress: string, ethersLib: any, poolAbi?: any[]): Promise<ClmmPoolState>;
|
|
@@ -1,7 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PoolStateInitializer = void 0;
|
|
4
|
+
exports.withInitRetry = withInitRetry;
|
|
4
5
|
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
6
|
+
async function withInitRetry(fn, label) {
|
|
7
|
+
try {
|
|
8
|
+
return await fn();
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
(0, ttd_core_1.log_warn)(`[PoolStateInitializer] ${label} attempt 1 failed: ${err?.message || err}, retrying once...`);
|
|
12
|
+
try {
|
|
13
|
+
return await fn();
|
|
14
|
+
}
|
|
15
|
+
catch (err2) {
|
|
16
|
+
(0, ttd_core_1.log_error)(`[PoolStateInitializer] ${label} retry also failed`, err2 instanceof Error ? err2 : new Error(String(err2)));
|
|
17
|
+
const addrMatch = label.match(/0x[0-9a-fA-F]+/);
|
|
18
|
+
const identity = (addrMatch?.[0] || label).toLowerCase();
|
|
19
|
+
ttd_core_1.ALERT_TYPES.QUOTE_POOL_INIT_FAILED.report({
|
|
20
|
+
severity: 'critical',
|
|
21
|
+
identity,
|
|
22
|
+
scope: { pool_address: addrMatch ? identity : undefined },
|
|
23
|
+
title: `Pool init failed: ${label} — ${err2?.message || err2}`,
|
|
24
|
+
detail: { label, error: String(err2?.message || err2), stack: err2?.stack },
|
|
25
|
+
});
|
|
26
|
+
throw err2;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
5
30
|
const V2_PAIR_ABI = [
|
|
6
31
|
'function token0() view returns (address)',
|
|
7
32
|
'function token1() view returns (address)',
|
|
@@ -30,17 +55,17 @@ class PoolStateInitializer {
|
|
|
30
55
|
poolContract.token0(),
|
|
31
56
|
poolContract.token1(),
|
|
32
57
|
]);
|
|
33
|
-
const
|
|
58
|
+
const feeRateBps = AMM_FEE_MAP[dexId] || AMM_FEE_MAP[dexId.replace('-', '_')] || 30;
|
|
34
59
|
const state = {
|
|
35
60
|
address: poolAddress,
|
|
36
61
|
token0: token0.toLowerCase(),
|
|
37
62
|
token1: token1.toLowerCase(),
|
|
38
|
-
|
|
63
|
+
feeRateBps,
|
|
39
64
|
reserve0: reserves[0].toString(),
|
|
40
65
|
reserve1: reserves[1].toString(),
|
|
41
66
|
};
|
|
42
67
|
(0, ttd_core_1.log_info)(`[PoolStateInitializer] AMM pool initialized: ${poolAddress}`, {
|
|
43
|
-
token0: state.token0, token1: state.token1,
|
|
68
|
+
token0: state.token0, token1: state.token1, feeRateBps: state.feeRateBps,
|
|
44
69
|
});
|
|
45
70
|
return state;
|
|
46
71
|
}
|
|
@@ -62,11 +87,13 @@ class PoolStateInitializer {
|
|
|
62
87
|
poolContract.liquidity(),
|
|
63
88
|
]);
|
|
64
89
|
const [sqrtPriceX96, tick] = slot0;
|
|
90
|
+
const feeRatePpm = Number(fee);
|
|
65
91
|
const state = {
|
|
66
92
|
address: poolAddress,
|
|
67
93
|
token0: token0.toLowerCase(),
|
|
68
94
|
token1: token1.toLowerCase(),
|
|
69
|
-
|
|
95
|
+
feeRateBps: feeRatePpm / 100,
|
|
96
|
+
feeRatePpm,
|
|
70
97
|
tickSpacing: Number(tickSpacing),
|
|
71
98
|
tick: Number(tick),
|
|
72
99
|
sqrtPriceX96: sqrtPriceX96.toString(),
|
|
@@ -74,7 +101,8 @@ class PoolStateInitializer {
|
|
|
74
101
|
};
|
|
75
102
|
(0, ttd_core_1.log_info)(`[PoolStateInitializer] CLMM pool initialized: ${poolAddress}`, {
|
|
76
103
|
token0: state.token0, token1: state.token1,
|
|
77
|
-
|
|
104
|
+
feeRateBps: state.feeRateBps, feeRatePpm: state.feeRatePpm,
|
|
105
|
+
tickSpacing: state.tickSpacing, tick: state.tick,
|
|
78
106
|
});
|
|
79
107
|
return state;
|
|
80
108
|
}
|
|
@@ -98,7 +126,8 @@ class PoolStateInitializer {
|
|
|
98
126
|
address: poolKey,
|
|
99
127
|
token0: currency0.toLowerCase ? currency0.toLowerCase() : currency0,
|
|
100
128
|
token1: currency1.toLowerCase ? currency1.toLowerCase() : currency1,
|
|
101
|
-
|
|
129
|
+
feeRateBps: (Number(lpFee) + Number(protocolFee)) / 100,
|
|
130
|
+
feeRatePpm: Number(fee),
|
|
102
131
|
tickSpacing,
|
|
103
132
|
tick: Number(tick),
|
|
104
133
|
sqrtPriceX96: sqrtPriceX96.toString(),
|
|
@@ -113,7 +142,8 @@ class PoolStateInitializer {
|
|
|
113
142
|
};
|
|
114
143
|
(0, ttd_core_1.log_info)(`[PoolStateInitializer] Infinity pool initialized: ${poolKey}`, {
|
|
115
144
|
currency0: state.currency0, currency1: state.currency1,
|
|
116
|
-
|
|
145
|
+
feeRateBps: state.feeRateBps, feeRatePpm: state.feeRatePpm,
|
|
146
|
+
tickSpacing: state.tickSpacing, tick: state.tick,
|
|
117
147
|
lpFee: state.lpFee, protocolFee: state.protocolFee,
|
|
118
148
|
});
|
|
119
149
|
return state;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { QuoteTier } from '@clonegod/ttd-core';
|
|
1
2
|
export interface CheckSwapParams {
|
|
2
3
|
poolAddress: string;
|
|
3
4
|
poolName: string;
|
|
@@ -19,7 +20,10 @@ export interface CheckSwapParams {
|
|
|
19
20
|
export declare class QuotePriceVerify {
|
|
20
21
|
private quoteCache;
|
|
21
22
|
private get enabled();
|
|
22
|
-
cacheQuote(poolAddress: string, priceId: string, source: string, askPrice: number, bidPrice: number, blockNumber: number, quoteAmountUsd: number, token0PriceUsd?: number, token1PriceUsd?: number
|
|
23
|
+
cacheQuote(poolAddress: string, priceId: string, source: string, askPrice: number, bidPrice: number, blockNumber: number, quoteAmountUsd: number, token0PriceUsd?: number, token1PriceUsd?: number, tiers?: {
|
|
24
|
+
askTiers?: QuoteTier[];
|
|
25
|
+
bidTiers?: QuoteTier[];
|
|
26
|
+
}): void;
|
|
23
27
|
checkSwap(params: CheckSwapParams): void;
|
|
24
28
|
private deriveSwapDirection;
|
|
25
29
|
private compareAndLog;
|