@clonegod/ttd-bsc-common 3.1.75 → 3.1.76
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.js +37 -4
- package/dist/quote/pricing/pool_state_initializer.d.ts +3 -0
- package/dist/quote/pricing/pool_state_initializer.js +35 -3
- package/dist/quote/tick/cached_tick_data_provider.d.ts +1 -1
- package/dist/quote/tick/cached_tick_data_provider.js +6 -1
- package/package.json +2 -2
|
@@ -69,6 +69,31 @@ function grossUpFee(amountInNet, feeRateBps) {
|
|
|
69
69
|
return { amountInGross, feeAmount };
|
|
70
70
|
}
|
|
71
71
|
const _tickLiquidityLastBuild = new Map();
|
|
72
|
+
const _tierInvariantLastWarn = new Map();
|
|
73
|
+
function assertTierInvariant(poolLabel, side, tiers) {
|
|
74
|
+
if (!tiers || tiers.length === 0)
|
|
75
|
+
return;
|
|
76
|
+
const TOL_REL = 0.0001;
|
|
77
|
+
for (const t of tiers) {
|
|
78
|
+
if (!(t.price > 0) || !(t.amount > 0) || !(t.amount_in > 0))
|
|
79
|
+
continue;
|
|
80
|
+
const expected = side === 'ASK' ? t.amount_in / t.amount : t.amount / t.amount_in;
|
|
81
|
+
if (!(expected > 0))
|
|
82
|
+
continue;
|
|
83
|
+
const relDiff = Math.abs(t.price - expected) / expected;
|
|
84
|
+
if (relDiff > TOL_REL) {
|
|
85
|
+
const key = `${poolLabel}_${side}_${t.pct}`;
|
|
86
|
+
const now = Date.now();
|
|
87
|
+
if ((now - (_tierInvariantLastWarn.get(key) || 0)) < 60000)
|
|
88
|
+
return;
|
|
89
|
+
_tierInvariantLastWarn.set(key, now);
|
|
90
|
+
(0, ttd_core_1.log_warn)(`[tier-invariant] ${poolLabel} ${side} pct=${t.pct} price=${t.price.toFixed(10)} ` +
|
|
91
|
+
`≠ expected ${expected.toFixed(10)} (relDiff=${(relDiff * 10000).toFixed(2)}bps) — ` +
|
|
92
|
+
`tier.price MUST be effective avg price including fee`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
72
97
|
function assembleEntry(tiers) {
|
|
73
98
|
const defaultTier = tiers.find(t => t.pct === ttd_core_2.DEFAULT_TIER_PCT) ?? tiers[0];
|
|
74
99
|
return {
|
|
@@ -131,9 +156,11 @@ function buildClmmDepth(input) {
|
|
|
131
156
|
const bidTickMove = bidResult.targetTick > bidResult.currentTick
|
|
132
157
|
? `${bidResult.currentTick} -> ${bidResult.targetTick}`
|
|
133
158
|
: `${bidResult.targetTick} <- ${bidResult.currentTick}`;
|
|
159
|
+
const askEffPrice = askResult.amountOut > 0 ? askGross.amountInGross / askResult.amountOut : askTargetPrice;
|
|
160
|
+
const bidEffPrice = bidGross.amountInGross > 0 ? bidResult.amountOut / bidGross.amountInGross : bidTargetPrice;
|
|
134
161
|
askTiers.push({
|
|
135
162
|
pct,
|
|
136
|
-
price:
|
|
163
|
+
price: askEffPrice,
|
|
137
164
|
amount: askResult.amountOut,
|
|
138
165
|
amount_in: askGross.amountInGross,
|
|
139
166
|
amount_in_usd: askGross.amountInGross * quotePriceUsd,
|
|
@@ -143,7 +170,7 @@ function buildClmmDepth(input) {
|
|
|
143
170
|
});
|
|
144
171
|
bidTiers.push({
|
|
145
172
|
pct,
|
|
146
|
-
price:
|
|
173
|
+
price: bidEffPrice,
|
|
147
174
|
amount: bidResult.amountOut,
|
|
148
175
|
amount_in: bidGross.amountInGross,
|
|
149
176
|
amount_in_usd: bidGross.amountInGross * basePriceUsd,
|
|
@@ -152,6 +179,8 @@ function buildClmmDepth(input) {
|
|
|
152
179
|
tick_move: bidTickMove,
|
|
153
180
|
});
|
|
154
181
|
}
|
|
182
|
+
assertTierInvariant(poolInfo.pool_name || poolAddress, 'ASK', askTiers);
|
|
183
|
+
assertTierInvariant(poolInfo.pool_name || poolAddress, 'BID', bidTiers);
|
|
155
184
|
const depth = {
|
|
156
185
|
mid_price: midPrice,
|
|
157
186
|
fee_rate_bps: feeRateBps,
|
|
@@ -231,9 +260,11 @@ function buildAmmDepth(input) {
|
|
|
231
260
|
});
|
|
232
261
|
const askGross = grossUpFee(askResult.amountIn, feeRateBps);
|
|
233
262
|
const bidGross = grossUpFee(bidResult.amountIn, feeRateBps);
|
|
263
|
+
const askEffPrice = askResult.amountOut > 0 ? askGross.amountInGross / askResult.amountOut : askResult.targetPrice;
|
|
264
|
+
const bidEffPrice = bidGross.amountInGross > 0 ? bidResult.amountOut / bidGross.amountInGross : bidResult.targetPrice;
|
|
234
265
|
askTiers.push({
|
|
235
266
|
pct,
|
|
236
|
-
price:
|
|
267
|
+
price: askEffPrice,
|
|
237
268
|
amount: askResult.amountOut,
|
|
238
269
|
amount_in: askGross.amountInGross,
|
|
239
270
|
amount_in_usd: askGross.amountInGross * quotePriceUsd,
|
|
@@ -242,7 +273,7 @@ function buildAmmDepth(input) {
|
|
|
242
273
|
});
|
|
243
274
|
bidTiers.push({
|
|
244
275
|
pct,
|
|
245
|
-
price:
|
|
276
|
+
price: bidEffPrice,
|
|
246
277
|
amount: bidResult.amountOut,
|
|
247
278
|
amount_in: bidGross.amountInGross,
|
|
248
279
|
amount_in_usd: bidGross.amountInGross * basePriceUsd,
|
|
@@ -250,6 +281,8 @@ function buildAmmDepth(input) {
|
|
|
250
281
|
fee_usd: bidGross.feeAmount * basePriceUsd,
|
|
251
282
|
});
|
|
252
283
|
}
|
|
284
|
+
assertTierInvariant(poolInfo.pool_name || 'amm-pool', 'ASK', askTiers);
|
|
285
|
+
assertTierInvariant(poolInfo.pool_name || 'amm-pool', 'BID', bidTiers);
|
|
253
286
|
return {
|
|
254
287
|
mid_price: midPrice,
|
|
255
288
|
fee_rate_bps: feeRateBps,
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { AmmPoolState, ClmmPoolState, InfinityPoolState } from "../../types/pool_state";
|
|
2
|
+
import { StandardPoolInfoType } from '@clonegod/ttd-core';
|
|
2
3
|
declare function withInitRetry<T>(fn: () => Promise<T>, label: string): Promise<T>;
|
|
3
4
|
export { withInitRetry };
|
|
5
|
+
declare function assertPoolTokensMatch(poolInfo: StandardPoolInfoType, onchainToken0: string, onchainToken1: string, nativeAddress?: string): void;
|
|
6
|
+
export { assertPoolTokensMatch };
|
|
4
7
|
export declare class PoolStateInitializer {
|
|
5
8
|
static initAmmPool(provider: any, poolAddress: string, dexId: string, ethersLib: any): Promise<AmmPoolState>;
|
|
6
9
|
static initClmmPool(provider: any, poolAddress: string, ethersLib: any, poolAbi?: any[]): Promise<ClmmPoolState>;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PoolStateInitializer = void 0;
|
|
4
4
|
exports.withInitRetry = withInitRetry;
|
|
5
|
+
exports.assertPoolTokensMatch = assertPoolTokensMatch;
|
|
5
6
|
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
6
7
|
async function withInitRetry(fn, label) {
|
|
7
8
|
try {
|
|
@@ -27,6 +28,37 @@ async function withInitRetry(fn, label) {
|
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
}
|
|
31
|
+
function assertPoolTokensMatch(poolInfo, onchainToken0, onchainToken1, nativeAddress) {
|
|
32
|
+
const configured = new Set([
|
|
33
|
+
poolInfo.tokenA.address.toLowerCase(),
|
|
34
|
+
poolInfo.tokenB.address.toLowerCase(),
|
|
35
|
+
]);
|
|
36
|
+
const native = nativeAddress?.toLowerCase();
|
|
37
|
+
const onchain = [onchainToken0.toLowerCase(), onchainToken1.toLowerCase()];
|
|
38
|
+
const missing = onchain.filter(addr => addr !== native && !configured.has(addr));
|
|
39
|
+
if (missing.length === 0)
|
|
40
|
+
return;
|
|
41
|
+
const identity = poolInfo.pool_address.toLowerCase();
|
|
42
|
+
const title = `${poolInfo.pool_name} 配置 token 与链上不一致(疑似同名 symbol 选错地址): ` +
|
|
43
|
+
`config=[${[...configured].join(', ')}] chain=[${onchain.join(', ')}] missing=[${missing.join(', ')}]`;
|
|
44
|
+
ttd_core_1.ALERT_TYPES.QUOTE_POOL_INIT_FAILED.report({
|
|
45
|
+
severity: 'critical',
|
|
46
|
+
identity,
|
|
47
|
+
scope: { dex_id: poolInfo.dex_id, pool_address: identity, pair: poolInfo.pair },
|
|
48
|
+
title,
|
|
49
|
+
detail: { pool_name: poolInfo.pool_name, configured: [...configured], onchain, missing },
|
|
50
|
+
});
|
|
51
|
+
throw new Error(`[pool-token-mismatch] ${title}`);
|
|
52
|
+
}
|
|
53
|
+
function attributeInitError(error, poolId, expectedType) {
|
|
54
|
+
const msg = String(error?.message || error);
|
|
55
|
+
const isRevert = /CALL_EXCEPTION|execution reverted|missing revert data/i.test(msg);
|
|
56
|
+
if (!isRevert)
|
|
57
|
+
return error instanceof Error ? error : new Error(msg);
|
|
58
|
+
return new Error(`[pool-type-mismatch?] ${poolId} 期望是 ${expectedType} 池,但链上特征函数 revert ` +
|
|
59
|
+
`—— 疑似池子类型/DEX_ID 配错(核对该地址链上真实协议:CLMM 有 slot0/tickSpacing,` +
|
|
60
|
+
`AMM 有 getReserves/metadata)。原始错误: ${msg}`);
|
|
61
|
+
}
|
|
30
62
|
const V2_PAIR_ABI = [
|
|
31
63
|
'function token0() view returns (address)',
|
|
32
64
|
'function token1() view returns (address)',
|
|
@@ -71,7 +103,7 @@ class PoolStateInitializer {
|
|
|
71
103
|
}
|
|
72
104
|
catch (error) {
|
|
73
105
|
(0, ttd_core_1.log_error)(`[PoolStateInitializer] Failed to init AMM pool ${poolAddress}:`, error);
|
|
74
|
-
throw error;
|
|
106
|
+
throw attributeInitError(error, poolAddress, 'AMM');
|
|
75
107
|
}
|
|
76
108
|
}
|
|
77
109
|
static async initClmmPool(provider, poolAddress, ethersLib, poolAbi) {
|
|
@@ -106,7 +138,7 @@ class PoolStateInitializer {
|
|
|
106
138
|
}
|
|
107
139
|
catch (error) {
|
|
108
140
|
(0, ttd_core_1.log_error)(`[PoolStateInitializer] Failed to init CLMM pool ${poolAddress}:`, error);
|
|
109
|
-
throw error;
|
|
141
|
+
throw attributeInitError(error, poolAddress, 'CLMM');
|
|
110
142
|
}
|
|
111
143
|
}
|
|
112
144
|
static async initInfinityPool(provider, poolKey, poolManagerAddress, poolManagerAbi, ethersLib, decodeParameters) {
|
|
@@ -147,7 +179,7 @@ class PoolStateInitializer {
|
|
|
147
179
|
}
|
|
148
180
|
catch (error) {
|
|
149
181
|
(0, ttd_core_1.log_error)(`[PoolStateInitializer] Failed to init Infinity pool ${poolKey}:`, error);
|
|
150
|
-
throw error;
|
|
182
|
+
throw attributeInitError(error, poolKey, 'Infinity');
|
|
151
183
|
}
|
|
152
184
|
}
|
|
153
185
|
}
|
|
@@ -8,5 +8,5 @@ export declare class CachedTickDataProvider {
|
|
|
8
8
|
liquidityNet: string;
|
|
9
9
|
liquidityGross: string;
|
|
10
10
|
}>;
|
|
11
|
-
nextInitializedTickWithinOneWord(tick: number, lte: boolean,
|
|
11
|
+
nextInitializedTickWithinOneWord(tick: number, lte: boolean, _sdkTickSpacing: number): Promise<[number, boolean]>;
|
|
12
12
|
}
|
|
@@ -24,7 +24,12 @@ class CachedTickDataProvider {
|
|
|
24
24
|
liquidityGross: '0',
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
|
-
async nextInitializedTickWithinOneWord(tick, lte,
|
|
27
|
+
async nextInitializedTickWithinOneWord(tick, lte, _sdkTickSpacing) {
|
|
28
|
+
const tickSpacing = this.tickSpacing;
|
|
29
|
+
if (!Number.isInteger(tickSpacing) || tickSpacing <= 0) {
|
|
30
|
+
throw new Error(`[CachedTickDataProvider] invalid tickSpacing=${tickSpacing} for ${this.poolAddress} ` +
|
|
31
|
+
`— 必须来自链上 pool.tickSpacing(),不能从 fee 反推`);
|
|
32
|
+
}
|
|
28
33
|
const zeroForOne = !lte;
|
|
29
34
|
const result = this.tickCache.getNextInitializedTickWithinOneWord(this.poolAddress, tick, tickSpacing, zeroForOne);
|
|
30
35
|
if (result)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clonegod/ttd-bsc-common",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.76",
|
|
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.1.
|
|
17
|
+
"@clonegod/ttd-core": "3.1.79",
|
|
18
18
|
"axios": "1.15.0",
|
|
19
19
|
"dotenv": "^16.4.7",
|
|
20
20
|
"ethers": "^5.8.0",
|