@clonegod/ttd-bsc-common 3.0.55 → 3.1.2
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/quote/depth/index.d.ts +8 -1
- package/dist/quote/depth/index.js +47 -4
- package/dist/quote/depth/tick_liquidity_snapshot.d.ts +58 -0
- package/dist/quote/depth/tick_liquidity_snapshot.js +143 -0
- package/dist/quote/tick/clmm_tick_cache.d.ts +1 -0
- package/dist/quote/tick/clmm_tick_cache.js +3 -2
- package/dist/trade/abstract_dex_trade.js +2 -0
- package/dist/trade/caller_manager.js +44 -21
- package/dist/trade/trade_trace.d.ts +2 -0
- package/dist/trade/trade_trace.js +10 -0
- package/package.json +2 -2
|
@@ -3,6 +3,9 @@ export { calculateClmmDepth, getSqrtRatioAtTick, computeTargetSqrtPrice, bigIntS
|
|
|
3
3
|
export type { ClmmDepthInput, DepthResult } from './clmm_depth_calculator';
|
|
4
4
|
export { calculateAmmDepth } from './amm_depth_calculator';
|
|
5
5
|
export type { AmmDepthInput, AmmDepthResult } from './amm_depth_calculator';
|
|
6
|
+
export { buildTickLiquiditySnapshot } from './tick_liquidity_snapshot';
|
|
7
|
+
export type { TickLiquiditySnapshot, TickLiquiditySnapshotInput, TickDataPoint } from './tick_liquidity_snapshot';
|
|
8
|
+
import { TickLiquiditySnapshot } from './tick_liquidity_snapshot';
|
|
6
9
|
import { PoolDepthData } from '@clonegod/ttd-core';
|
|
7
10
|
export type { PoolDepthData } from '@clonegod/ttd-core';
|
|
8
11
|
export declare function getDepthBps(): number[];
|
|
@@ -25,7 +28,11 @@ export interface BuildClmmDepthInput {
|
|
|
25
28
|
basePriceUsd: number;
|
|
26
29
|
quotePriceUsd: number;
|
|
27
30
|
}
|
|
28
|
-
export
|
|
31
|
+
export interface ClmmDepthResult {
|
|
32
|
+
depth: PoolDepthData;
|
|
33
|
+
tickLiquidity?: TickLiquiditySnapshot;
|
|
34
|
+
}
|
|
35
|
+
export declare function buildClmmDepth(input: BuildClmmDepthInput): ClmmDepthResult | undefined;
|
|
29
36
|
export interface BuildAmmDepthInput {
|
|
30
37
|
poolInfo: {
|
|
31
38
|
pool_name: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.calculateAmmDepth = exports.bigIntSqrt = exports.computeTargetSqrtPrice = exports.getSqrtRatioAtTick = exports.calculateClmmDepth = void 0;
|
|
3
|
+
exports.buildTickLiquiditySnapshot = exports.calculateAmmDepth = exports.bigIntSqrt = exports.computeTargetSqrtPrice = exports.getSqrtRatioAtTick = exports.calculateClmmDepth = void 0;
|
|
4
4
|
exports.getDepthBps = getDepthBps;
|
|
5
5
|
exports.buildClmmDepth = buildClmmDepth;
|
|
6
6
|
exports.buildAmmDepth = buildAmmDepth;
|
|
@@ -15,18 +15,30 @@ Object.defineProperty(exports, "computeTargetSqrtPrice", { enumerable: true, get
|
|
|
15
15
|
Object.defineProperty(exports, "bigIntSqrt", { enumerable: true, get: function () { return clmm_depth_calculator_2.bigIntSqrt; } });
|
|
16
16
|
var amm_depth_calculator_2 = require("./amm_depth_calculator");
|
|
17
17
|
Object.defineProperty(exports, "calculateAmmDepth", { enumerable: true, get: function () { return amm_depth_calculator_2.calculateAmmDepth; } });
|
|
18
|
+
var tick_liquidity_snapshot_1 = require("./tick_liquidity_snapshot");
|
|
19
|
+
Object.defineProperty(exports, "buildTickLiquiditySnapshot", { enumerable: true, get: function () { return tick_liquidity_snapshot_1.buildTickLiquiditySnapshot; } });
|
|
20
|
+
const tick_liquidity_snapshot_2 = require("./tick_liquidity_snapshot");
|
|
21
|
+
const clmm_tick_cache_1 = require("../tick/clmm_tick_cache");
|
|
18
22
|
let _depthBpsLogged = false;
|
|
23
|
+
const DEFAULT_BPS = 20;
|
|
19
24
|
function getDepthBps() {
|
|
20
25
|
const raw = process.env.DEPTH_BPS_LEVELS;
|
|
21
26
|
const levels = raw
|
|
22
27
|
? raw.split(',').map(s => parseInt(s.trim(), 10)).filter(n => !isNaN(n) && n > 0)
|
|
23
|
-
: [];
|
|
28
|
+
: [DEFAULT_BPS];
|
|
29
|
+
const cexBps = parseInt(process.env.DEPTH_CEX_BPS || String(DEFAULT_BPS));
|
|
30
|
+
if (!levels.includes(cexBps))
|
|
31
|
+
levels.push(cexBps);
|
|
32
|
+
if (!levels.includes(DEFAULT_BPS))
|
|
33
|
+
levels.push(DEFAULT_BPS);
|
|
34
|
+
levels.sort((a, b) => a - b);
|
|
24
35
|
if (!_depthBpsLogged) {
|
|
25
36
|
_depthBpsLogged = true;
|
|
26
|
-
(0, ttd_core_1.log_info)(`[Depth]
|
|
37
|
+
(0, ttd_core_1.log_info)(`[Depth] bpsLevels=${JSON.stringify(levels)}, cexBps=${cexBps}`);
|
|
27
38
|
}
|
|
28
39
|
return levels;
|
|
29
40
|
}
|
|
41
|
+
const _tickLiquidityLastBuild = new Map();
|
|
30
42
|
function buildClmmDepth(input) {
|
|
31
43
|
const bpsLevels = getDepthBps();
|
|
32
44
|
if (bpsLevels.length === 0)
|
|
@@ -66,7 +78,38 @@ function buildClmmDepth(input) {
|
|
|
66
78
|
depth.ask[bps] = { amount: askResult.amountOut, amountUsd: askResult.amountOut * basePriceUsd, amountIn: askResult.amountIn, amountInUsd: askResult.amountIn * quotePriceUsd, tickMove: askTickMove };
|
|
67
79
|
depth.bid[bps] = { amount: bidResult.amountOut, amountUsd: bidResult.amountOut * quotePriceUsd, amountIn: bidResult.amountIn, amountInUsd: bidResult.amountIn * basePriceUsd, tickMove: bidTickMove };
|
|
68
80
|
}
|
|
69
|
-
|
|
81
|
+
let tickLiquidity;
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
const lastBuild = _tickLiquidityLastBuild.get(poolAddress) || 0;
|
|
84
|
+
if (now - lastBuild < clmm_tick_cache_1.TICK_REFRESH_INTERVAL_MS) {
|
|
85
|
+
return { depth, tickLiquidity: undefined };
|
|
86
|
+
}
|
|
87
|
+
_tickLiquidityLastBuild.set(poolAddress, now);
|
|
88
|
+
try {
|
|
89
|
+
const baseIsToken0 = baseToken.address.toLowerCase() === poolState.token0.toLowerCase();
|
|
90
|
+
const tokenAIsToken0 = poolInfo.tokenA?.address?.toLowerCase() === poolState.token0.toLowerCase();
|
|
91
|
+
const token0Info = tokenAIsToken0 ? poolInfo.tokenA : poolInfo.tokenB;
|
|
92
|
+
const token1Info = tokenAIsToken0 ? poolInfo.tokenB : poolInfo.tokenA;
|
|
93
|
+
tickLiquidity = (0, tick_liquidity_snapshot_2.buildTickLiquiditySnapshot)({
|
|
94
|
+
poolAddress,
|
|
95
|
+
pair: '',
|
|
96
|
+
protocolId: '',
|
|
97
|
+
tickCache,
|
|
98
|
+
currentTick: poolState.tick,
|
|
99
|
+
sqrtPriceX96: poolState.sqrtPriceX96,
|
|
100
|
+
liquidity: poolState.liquidity,
|
|
101
|
+
tickSpacing: poolState.tickSpacing,
|
|
102
|
+
token0: { address: poolState.token0, symbol: token0Info?.symbol || '', decimals: token0Info?.decimals || 18 },
|
|
103
|
+
token1: { address: token1Info?.address || '', symbol: token1Info?.symbol || '', decimals: token1Info?.decimals || 18 },
|
|
104
|
+
baseIsToken0,
|
|
105
|
+
token0PriceUsd: baseIsToken0 ? basePriceUsd : quotePriceUsd,
|
|
106
|
+
token1PriceUsd: baseIsToken0 ? quotePriceUsd : basePriceUsd,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
(0, ttd_core_1.log_debug)(`[TickLiquidity] ${poolInfo.pool_name} failed: ${e.message}`, '');
|
|
111
|
+
}
|
|
112
|
+
return { depth, tickLiquidity };
|
|
70
113
|
}
|
|
71
114
|
catch (error) {
|
|
72
115
|
(0, ttd_core_1.log_debug)(`[Depth] ${poolInfo.pool_name} CLMM depth failed: ${error.message}`, '');
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ClmmTickCache } from '../tick/clmm_tick_cache';
|
|
2
|
+
export interface TickLiquiditySnapshotInput {
|
|
3
|
+
poolAddress: string;
|
|
4
|
+
pair: string;
|
|
5
|
+
protocolId: string;
|
|
6
|
+
tickCache: ClmmTickCache;
|
|
7
|
+
currentTick: number;
|
|
8
|
+
sqrtPriceX96: string;
|
|
9
|
+
liquidity: string;
|
|
10
|
+
tickSpacing: number;
|
|
11
|
+
token0: {
|
|
12
|
+
address: string;
|
|
13
|
+
symbol: string;
|
|
14
|
+
decimals: number;
|
|
15
|
+
};
|
|
16
|
+
token1: {
|
|
17
|
+
address: string;
|
|
18
|
+
symbol: string;
|
|
19
|
+
decimals: number;
|
|
20
|
+
};
|
|
21
|
+
baseIsToken0: boolean;
|
|
22
|
+
token0PriceUsd: number;
|
|
23
|
+
token1PriceUsd: number;
|
|
24
|
+
rangeBps?: number;
|
|
25
|
+
}
|
|
26
|
+
export interface TickDataPoint {
|
|
27
|
+
tick: number;
|
|
28
|
+
price0: number;
|
|
29
|
+
price1: number;
|
|
30
|
+
liquidity: string;
|
|
31
|
+
amount0: number;
|
|
32
|
+
amount1: number;
|
|
33
|
+
tvlUsd: number;
|
|
34
|
+
buyable: number;
|
|
35
|
+
sellable: number;
|
|
36
|
+
}
|
|
37
|
+
export interface TickLiquiditySnapshot {
|
|
38
|
+
poolAddress: string;
|
|
39
|
+
pair: string;
|
|
40
|
+
protocolId: string;
|
|
41
|
+
currentTick: number;
|
|
42
|
+
tickSpacing: number;
|
|
43
|
+
currentPrice: number;
|
|
44
|
+
token0: {
|
|
45
|
+
address: string;
|
|
46
|
+
symbol: string;
|
|
47
|
+
decimals: number;
|
|
48
|
+
};
|
|
49
|
+
token1: {
|
|
50
|
+
address: string;
|
|
51
|
+
symbol: string;
|
|
52
|
+
decimals: number;
|
|
53
|
+
};
|
|
54
|
+
ticks: TickDataPoint[];
|
|
55
|
+
totalTvlUsd: number;
|
|
56
|
+
timestamp: number;
|
|
57
|
+
}
|
|
58
|
+
export declare function buildTickLiquiditySnapshot(input: TickLiquiditySnapshotInput): TickLiquiditySnapshot | undefined;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildTickLiquiditySnapshot = buildTickLiquiditySnapshot;
|
|
4
|
+
const clmm_depth_calculator_1 = require("./clmm_depth_calculator");
|
|
5
|
+
const Q96 = 1n << 96n;
|
|
6
|
+
const DEFAULT_RANGE_BPS = 200;
|
|
7
|
+
function buildTickLiquiditySnapshot(input) {
|
|
8
|
+
const { poolAddress, pair, protocolId, tickCache, currentTick, tickSpacing, token0, token1, baseIsToken0, token0PriceUsd, token1PriceUsd, } = input;
|
|
9
|
+
const currentSqrtPriceX96 = BigInt(input.sqrtPriceX96);
|
|
10
|
+
const currentLiquidity = BigInt(input.liquidity);
|
|
11
|
+
const rangeBps = input.rangeBps ?? DEFAULT_RANGE_BPS;
|
|
12
|
+
if (currentLiquidity === 0n)
|
|
13
|
+
return undefined;
|
|
14
|
+
const allTickIndices = tickCache.getCachedTickIndices(poolAddress);
|
|
15
|
+
if (allTickIndices.length === 0)
|
|
16
|
+
return undefined;
|
|
17
|
+
const upperSqrtPrice = (0, clmm_depth_calculator_1.computeTargetSqrtPrice)(currentSqrtPriceX96, rangeBps, false);
|
|
18
|
+
const lowerSqrtPrice = (0, clmm_depth_calculator_1.computeTargetSqrtPrice)(currentSqrtPriceX96, rangeBps, true);
|
|
19
|
+
const upperTick = sqrtPriceToTick(upperSqrtPrice);
|
|
20
|
+
const lowerTick = sqrtPriceToTick(lowerSqrtPrice);
|
|
21
|
+
const ticksInRange = allTickIndices.filter(t => t >= lowerTick && t <= upperTick);
|
|
22
|
+
if (ticksInRange.length === 0)
|
|
23
|
+
return undefined;
|
|
24
|
+
const liquidityMap = computeLiquidityAtTicks(ticksInRange, currentTick, currentLiquidity, tickCache, poolAddress);
|
|
25
|
+
const tickDataPoints = [];
|
|
26
|
+
let totalTvlUsd = 0;
|
|
27
|
+
for (let i = 0; i < ticksInRange.length - 1; i++) {
|
|
28
|
+
const tickLower = ticksInRange[i];
|
|
29
|
+
const tickUpper = ticksInRange[i + 1];
|
|
30
|
+
const liq = liquidityMap.get(tickLower) ?? 0n;
|
|
31
|
+
if (liq <= 0n) {
|
|
32
|
+
const price0 = tickToPrice(tickLower, token0.decimals, token1.decimals);
|
|
33
|
+
const price1 = price0 > 0 ? 1 / price0 : 0;
|
|
34
|
+
tickDataPoints.push({
|
|
35
|
+
tick: tickLower,
|
|
36
|
+
price0, price1,
|
|
37
|
+
liquidity: '0',
|
|
38
|
+
amount0: 0, amount1: 0, tvlUsd: 0,
|
|
39
|
+
buyable: 0, sellable: 0,
|
|
40
|
+
});
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const sqrtPriceLower = (0, clmm_depth_calculator_1.getSqrtRatioAtTick)(tickLower);
|
|
44
|
+
const sqrtPriceUpper = (0, clmm_depth_calculator_1.getSqrtRatioAtTick)(tickUpper);
|
|
45
|
+
const { amount0, amount1 } = computeTokenAmounts(sqrtPriceLower, sqrtPriceUpper, currentSqrtPriceX96, liq, token0.decimals, token1.decimals);
|
|
46
|
+
const tvlUsd = amount0 * token0PriceUsd + amount1 * token1PriceUsd;
|
|
47
|
+
totalTvlUsd += tvlUsd;
|
|
48
|
+
const { buyable, sellable } = computeBuySell(sqrtPriceLower, sqrtPriceUpper, liq, token0.decimals, token1.decimals, baseIsToken0);
|
|
49
|
+
const price0 = tickToPrice(tickLower, token0.decimals, token1.decimals);
|
|
50
|
+
const price1 = price0 > 0 ? 1 / price0 : 0;
|
|
51
|
+
tickDataPoints.push({
|
|
52
|
+
tick: tickLower,
|
|
53
|
+
price0, price1,
|
|
54
|
+
liquidity: liq.toString(),
|
|
55
|
+
amount0, amount1, tvlUsd,
|
|
56
|
+
buyable, sellable,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const currentPriceFloat = Number(currentSqrtPriceX96) / Number(Q96);
|
|
60
|
+
const currentPrice = (currentPriceFloat * currentPriceFloat) *
|
|
61
|
+
Math.pow(10, token0.decimals - token1.decimals);
|
|
62
|
+
return {
|
|
63
|
+
poolAddress,
|
|
64
|
+
pair,
|
|
65
|
+
protocolId,
|
|
66
|
+
currentTick,
|
|
67
|
+
tickSpacing,
|
|
68
|
+
currentPrice,
|
|
69
|
+
token0,
|
|
70
|
+
token1,
|
|
71
|
+
ticks: tickDataPoints,
|
|
72
|
+
totalTvlUsd,
|
|
73
|
+
timestamp: Date.now(),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function computeLiquidityAtTicks(sortedTicks, currentTick, currentLiquidity, tickCache, poolAddress) {
|
|
77
|
+
const result = new Map();
|
|
78
|
+
let splitIdx = sortedTicks.findIndex(t => t > currentTick);
|
|
79
|
+
if (splitIdx === -1)
|
|
80
|
+
splitIdx = sortedTicks.length;
|
|
81
|
+
let liq = currentLiquidity;
|
|
82
|
+
for (let i = splitIdx; i < sortedTicks.length; i++) {
|
|
83
|
+
const tick = sortedTicks[i];
|
|
84
|
+
const netLiq = tickCache.getTickLiquidityNet(poolAddress, tick) ?? 0n;
|
|
85
|
+
liq = liq + netLiq;
|
|
86
|
+
result.set(tick, liq > 0n ? liq : 0n);
|
|
87
|
+
}
|
|
88
|
+
liq = currentLiquidity;
|
|
89
|
+
for (let i = splitIdx - 1; i >= 0; i--) {
|
|
90
|
+
const tick = sortedTicks[i];
|
|
91
|
+
result.set(tick, liq > 0n ? liq : 0n);
|
|
92
|
+
const netLiq = tickCache.getTickLiquidityNet(poolAddress, tick) ?? 0n;
|
|
93
|
+
liq = liq - netLiq;
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
function computeTokenAmounts(sqrtPriceLower, sqrtPriceUpper, currentSqrtPrice, liquidity, decimals0, decimals1) {
|
|
98
|
+
let amount0Wei = 0n;
|
|
99
|
+
let amount1Wei = 0n;
|
|
100
|
+
if (currentSqrtPrice <= sqrtPriceLower) {
|
|
101
|
+
amount0Wei = calcToken0Amount(sqrtPriceLower, sqrtPriceUpper, liquidity);
|
|
102
|
+
}
|
|
103
|
+
else if (currentSqrtPrice >= sqrtPriceUpper) {
|
|
104
|
+
amount1Wei = calcToken1Amount(sqrtPriceLower, sqrtPriceUpper, liquidity);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
amount0Wei = calcToken0Amount(currentSqrtPrice, sqrtPriceUpper, liquidity);
|
|
108
|
+
amount1Wei = calcToken1Amount(sqrtPriceLower, currentSqrtPrice, liquidity);
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
amount0: Number(amount0Wei) / Math.pow(10, decimals0),
|
|
112
|
+
amount1: Number(amount1Wei) / Math.pow(10, decimals1),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function computeBuySell(sqrtPriceLower, sqrtPriceUpper, liquidity, decimals0, decimals1, baseIsToken0) {
|
|
116
|
+
const token0Out = Number(calcToken0Amount(sqrtPriceLower, sqrtPriceUpper, liquidity)) / Math.pow(10, decimals0);
|
|
117
|
+
const token1Out = Number(calcToken1Amount(sqrtPriceLower, sqrtPriceUpper, liquidity)) / Math.pow(10, decimals1);
|
|
118
|
+
if (baseIsToken0) {
|
|
119
|
+
return { buyable: token0Out, sellable: token1Out };
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
return { buyable: token1Out, sellable: token0Out };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function calcToken0Amount(sqrtPriceLower, sqrtPriceUpper, liquidity) {
|
|
126
|
+
if (sqrtPriceLower >= sqrtPriceUpper || liquidity === 0n)
|
|
127
|
+
return 0n;
|
|
128
|
+
const diff = sqrtPriceUpper - sqrtPriceLower;
|
|
129
|
+
return liquidity * diff * Q96 / (sqrtPriceLower * sqrtPriceUpper);
|
|
130
|
+
}
|
|
131
|
+
function calcToken1Amount(sqrtPriceLower, sqrtPriceUpper, liquidity) {
|
|
132
|
+
if (sqrtPriceLower >= sqrtPriceUpper || liquidity === 0n)
|
|
133
|
+
return 0n;
|
|
134
|
+
const diff = sqrtPriceUpper - sqrtPriceLower;
|
|
135
|
+
return liquidity * diff / Q96;
|
|
136
|
+
}
|
|
137
|
+
function sqrtPriceToTick(sqrtPriceX96) {
|
|
138
|
+
const sqrtPriceFloat = Number(sqrtPriceX96) / Number(Q96);
|
|
139
|
+
return Math.floor(Math.log(sqrtPriceFloat * sqrtPriceFloat) / Math.log(1.0001));
|
|
140
|
+
}
|
|
141
|
+
function tickToPrice(tick, decimals0, decimals1) {
|
|
142
|
+
return Math.pow(1.0001, tick) * Math.pow(10, decimals0 - decimals1);
|
|
143
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ClmmTickCache = void 0;
|
|
3
|
+
exports.ClmmTickCache = exports.TICK_REFRESH_INTERVAL_MS = void 0;
|
|
4
4
|
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
5
|
+
exports.TICK_REFRESH_INTERVAL_MS = Number(process.env.TICK_CACHE_FORCE_FULL_REFRESH_INTERVAL) || 60000;
|
|
5
6
|
class ClmmTickCache {
|
|
6
7
|
constructor(loader, config = {}) {
|
|
7
8
|
this.pools = new Map();
|
|
@@ -10,7 +11,7 @@ class ClmmTickCache {
|
|
|
10
11
|
this.loader = loader;
|
|
11
12
|
this.config = {
|
|
12
13
|
neighboringWords: config.neighboringWords ?? (Number(process.env.TICK_CACHE_NEIGHBORING_WORDS) || 2),
|
|
13
|
-
refreshInterval: config.refreshInterval ??
|
|
14
|
+
refreshInterval: config.refreshInterval ?? exports.TICK_REFRESH_INTERVAL_MS,
|
|
14
15
|
minUpdateInterval: config.minUpdateInterval ?? (Number(process.env.TICK_CACHE_MIN_UPDATE_INTERVAL) || 3000),
|
|
15
16
|
};
|
|
16
17
|
}
|
|
@@ -180,6 +180,8 @@ class AbstractDexTrade extends ttd_core_1.AbastrcatTrade {
|
|
|
180
180
|
await this.transactionSender.sendTransaction(signedMainTx, tipTxMap, order_trace_id, pair, only_bundle, trace);
|
|
181
181
|
trace.mark('sent');
|
|
182
182
|
trace.flush();
|
|
183
|
+
context._execution_marks = trace.getAbsoluteMarks();
|
|
184
|
+
context._execution_start_time = trace.getStartTime();
|
|
183
185
|
try {
|
|
184
186
|
base_tx_result_checker_1.TradeResultSubscriber.getInstance().sendNonceWatch(caller.address, txid);
|
|
185
187
|
}
|
|
@@ -62,32 +62,55 @@ class CallerManager {
|
|
|
62
62
|
logger.info(`CallerManager initialized for ${this.config.groupId}: loaded=${allWallets.length}, active=${this.callers.length}, skipped=${skipped.length}`, callerSummary);
|
|
63
63
|
}
|
|
64
64
|
async acquireCaller() {
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
let
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
65
|
+
const lockKey = `${this.config.chainName}:caller:lock:select`;
|
|
66
|
+
const lockValue = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
67
|
+
const lockExpireSeconds = 1;
|
|
68
|
+
const maxRetries = 20;
|
|
69
|
+
const retryDelayMs = 10;
|
|
70
|
+
const startTime = Date.now();
|
|
71
|
+
let acquired = false;
|
|
72
|
+
let retries = 0;
|
|
73
|
+
for (; retries < maxRetries; retries++) {
|
|
74
|
+
acquired = await this.redis.acquireLock(lockKey, lockValue, lockExpireSeconds);
|
|
75
|
+
if (acquired)
|
|
76
|
+
break;
|
|
77
|
+
await new Promise(r => setTimeout(r, retryDelayMs));
|
|
78
|
+
}
|
|
79
|
+
if (!acquired) {
|
|
80
|
+
throw new Error(`acquireCaller: failed to acquire lock after ${maxRetries} retries (${Date.now() - startTime}ms)`);
|
|
81
|
+
}
|
|
82
|
+
const lockAcquiredTime = Date.now();
|
|
83
|
+
let callerAddr;
|
|
84
|
+
let selectedIdx;
|
|
85
|
+
try {
|
|
86
|
+
const addresses = this.callers.map(w => w.address.toLowerCase());
|
|
87
|
+
const lastUsedKey = this.getLastUsedRedisKey();
|
|
88
|
+
const lastUsedData = await this.redis.hgetall(lastUsedKey);
|
|
89
|
+
selectedIdx = 0;
|
|
90
|
+
let minLastUsed = Number.MAX_SAFE_INTEGER;
|
|
91
|
+
for (let i = 0; i < this.callers.length; i++) {
|
|
92
|
+
const redisTs = parseInt(lastUsedData?.[addresses[i]] || '0', 10);
|
|
93
|
+
if (redisTs < minLastUsed) {
|
|
94
|
+
minLastUsed = redisTs;
|
|
95
|
+
selectedIdx = i;
|
|
96
|
+
}
|
|
79
97
|
}
|
|
98
|
+
callerAddr = addresses[selectedIdx];
|
|
99
|
+
await this.redis.hsetValue(lastUsedKey, callerAddr, String(Date.now()), 24 * 60 * 60);
|
|
80
100
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
101
|
+
finally {
|
|
102
|
+
this.redis.releaseLock(lockKey, lockValue).catch(() => { });
|
|
103
|
+
}
|
|
104
|
+
const nonceKey = this.getNonceRedisKey();
|
|
105
|
+
const nonceStr = await this.redis.hgetvalue(nonceKey, callerAddr);
|
|
84
106
|
if (nonceStr === null || nonceStr === undefined) {
|
|
85
|
-
throw new Error(`Caller ${
|
|
107
|
+
throw new Error(`Caller ${callerAddr} nonce not found in Redis, stream-trade may not be running`);
|
|
86
108
|
}
|
|
87
109
|
const nonce = parseInt(nonceStr, 10);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
110
|
+
const totalMs = Date.now() - startTime;
|
|
111
|
+
const lockWaitMs = lockAcquiredTime - startTime;
|
|
112
|
+
logger.info(`acquireCaller: ${callerAddr} nonce=${nonce}, lock_wait=${lockWaitMs}ms, total=${totalMs}ms${retries > 0 ? `, retries=${retries}` : ''}`);
|
|
113
|
+
return { wallet: this.callers[selectedIdx], nonce };
|
|
91
114
|
}
|
|
92
115
|
async confirmNonce(address, confirmedNonce) {
|
|
93
116
|
const current = await this.getNonce(address);
|
|
@@ -34,6 +34,16 @@ class TradeTrace {
|
|
|
34
34
|
const total = this.marks[this.marks.length - 1].elapsed;
|
|
35
35
|
return `recv -> ${parts.join(' -> ')} = ${total}ms`;
|
|
36
36
|
}
|
|
37
|
+
getAbsoluteMarks() {
|
|
38
|
+
const result = {};
|
|
39
|
+
for (const m of this.marks) {
|
|
40
|
+
result[m.name] = this.startTime + m.elapsed;
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
getStartTime() {
|
|
45
|
+
return this.startTime;
|
|
46
|
+
}
|
|
37
47
|
flush() {
|
|
38
48
|
const timeline = this.buildTimeline();
|
|
39
49
|
if (this.error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clonegod/ttd-bsc-common",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.2",
|
|
4
4
|
"description": "BSC common library",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"push": "npm run build && npm publish"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@clonegod/ttd-core": "3.
|
|
17
|
+
"@clonegod/ttd-core": "3.1.2",
|
|
18
18
|
"axios": "^1.12.0",
|
|
19
19
|
"dotenv": "^16.4.7",
|
|
20
20
|
"ethers": "^5.8.0",
|