@clonegod/ttd-bsc-common 3.1.63 → 3.1.65

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.
@@ -5,3 +5,4 @@ export * from './depth';
5
5
  export * from './verify';
6
6
  export * from './price_feed_handler';
7
7
  export * from './quote_amount';
8
+ export * from './preload_token_prices';
@@ -21,3 +21,4 @@ __exportStar(require("./depth"), exports);
21
21
  __exportStar(require("./verify"), exports);
22
22
  __exportStar(require("./price_feed_handler"), exports);
23
23
  __exportStar(require("./quote_amount"), exports);
24
+ __exportStar(require("./preload_token_prices"), exports);
@@ -0,0 +1,2 @@
1
+ import { StandardPoolInfoType } from '@clonegod/ttd-core';
2
+ export declare function preloadTokenPrices(pool_list: StandardPoolInfoType[], label?: string): Promise<void>;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.preloadTokenPrices = preloadTokenPrices;
4
+ const ttd_core_1 = require("@clonegod/ttd-core");
5
+ async function preloadTokenPrices(pool_list, label = 'Quote') {
6
+ if (!pool_list || pool_list.length === 0)
7
+ return;
8
+ const addrSet = new Set();
9
+ for (const pool of pool_list) {
10
+ if (pool.tokenA?.address)
11
+ addrSet.add(pool.tokenA.address.toLowerCase());
12
+ if (pool.tokenB?.address)
13
+ addrSet.add(pool.tokenB.address.toLowerCase());
14
+ }
15
+ const addresses = Array.from(addrSet);
16
+ if (addresses.length === 0)
17
+ return;
18
+ try {
19
+ const priceMap = await (0, ttd_core_1.get_bsc_token_price_info)(addresses, { source: 'force_fetch' });
20
+ const got = [];
21
+ const miss = [];
22
+ for (const a of addresses) {
23
+ if (priceMap.get(a)?.price)
24
+ got.push(a);
25
+ else
26
+ miss.push(a);
27
+ }
28
+ (0, ttd_core_1.log_info)(`[${label}] preloadTokenPrices done, total=${addresses.length}, got=${got.length}, miss=${miss.length}`);
29
+ if (miss.length > 0) {
30
+ (0, ttd_core_1.log_warn)(`[${label}] preloadTokenPrices missing: ${miss.join(', ')}`);
31
+ }
32
+ }
33
+ catch (err) {
34
+ (0, ttd_core_1.log_warn)(`[${label}] preloadTokenPrices error (non-fatal): ${err.message}`);
35
+ }
36
+ }
@@ -11,45 +11,33 @@ function buildQuoteFromPriceFeed(poolInfo, data) {
11
11
  return null;
12
12
  }
13
13
  const normSymbol = (s) => {
14
- const u = s.toUpperCase();
14
+ const u = (s || '').toUpperCase();
15
15
  return u === 'WBNB' ? 'BNB' : u;
16
16
  };
17
- let askPrice;
18
- let bidPrice;
19
- let baseToken;
20
- let quoteToken;
21
17
  const tokenAIsQuote = normSymbol(tokenA.symbol) === normSymbol(quoteTokenSymbol);
22
18
  const tokenBIsQuote = normSymbol(tokenB.symbol) === normSymbol(quoteTokenSymbol);
23
19
  if (!tokenAIsQuote && !tokenBIsQuote) {
24
- (0, ttd_core_1.log_warn)(`[PriceFeed] quote_token ${quoteTokenSymbol} not found in pool ${poolInfo.pool_name} (tokenA=${tokenA.symbol}, tokenB=${tokenB.symbol})`);
20
+ (0, ttd_core_1.log_warn)(`[PriceFeed] quote_token "${quoteTokenSymbol}" 在池子配置里找不到 (tokenA=${tokenA.symbol}, tokenB=${tokenB.symbol}, pool=${poolInfo.pool_name})`);
25
21
  return null;
26
22
  }
23
+ let askPrice;
24
+ let bidPrice;
25
+ let baseToken;
26
+ let quoteToken;
27
27
  if (tokenAIsQuote) {
28
28
  quoteToken = tokenA;
29
29
  baseToken = tokenB;
30
+ askPrice = data.askToken1InToken0;
31
+ bidPrice = data.bidToken1InToken0;
30
32
  }
31
33
  else {
32
34
  quoteToken = tokenB;
33
35
  baseToken = tokenA;
34
- }
35
- const feedToken0 = normSymbol(data.token0Symbol || '');
36
- const feedToken1 = normSymbol(data.token1Symbol || '');
37
- const baseSymbolNorm = normSymbol(baseToken.symbol);
38
- const quoteSymbolNorm = normSymbol(quoteToken.symbol);
39
- if (normSymbol(feedToken0) === quoteSymbolNorm) {
40
- askPrice = data.askToken1InToken0;
41
- bidPrice = data.bidToken1InToken0;
42
- }
43
- else if (normSymbol(feedToken1) === quoteSymbolNorm) {
44
36
  askPrice = data.askToken0InToken1;
45
37
  bidPrice = data.bidToken0InToken1;
46
38
  }
47
- else {
48
- (0, ttd_core_1.log_warn)(`[PriceFeed] Cannot match quote_token symbol: quote=${quoteSymbolNorm}, feed=[${feedToken0}/${feedToken1}], pool=${poolInfo.pool_name}`);
49
- return null;
50
- }
51
39
  if (!askPrice || !bidPrice) {
52
- (0, ttd_core_1.log_warn)(`[PriceFeed] Missing price data for ${poolInfo.pool_name}`);
40
+ (0, ttd_core_1.log_warn)(`[PriceFeed] Missing price for pool=${poolInfo.pool_name}, direction=${tokenAIsQuote ? 'token1InToken0' : 'token0InToken1'}, feedSymbols=[${data.token0Symbol}/${data.token1Symbol}]`);
53
41
  return null;
54
42
  }
55
43
  const makeQuoteResult = (price, input, output) => ({
@@ -1,2 +1,2 @@
1
1
  export { QuotePriceVerify } from './quote_price_verify';
2
- export type { CheckSwapParams, CheckSyncParams } from './quote_price_verify';
2
+ export type { CheckSwapParams } from './quote_price_verify';
@@ -16,30 +16,11 @@ export interface CheckSwapParams {
16
16
  };
17
17
  swapperDeltaConvention?: boolean;
18
18
  }
19
- export interface CheckSyncParams {
20
- poolAddress: string;
21
- poolName: string;
22
- blockNumber: number;
23
- reserve0: string;
24
- reserve1: string;
25
- token0Address: string;
26
- token1Address: string;
27
- token0Decimals: number;
28
- token1Decimals: number;
29
- poolInfo: {
30
- tokenA: any;
31
- tokenB: any;
32
- quote_token: string;
33
- };
34
- txHash?: string;
35
- }
36
19
  export declare class QuotePriceVerify {
37
20
  private quoteCache;
38
- private reserveCache;
39
21
  private get enabled();
40
- seedReserves(poolAddress: string, reserve0: string, reserve1: string): void;
41
22
  cacheQuote(poolAddress: string, priceId: string, source: string, askPrice: number, bidPrice: number, blockNumber: number, quoteAmountUsd: number, token0PriceUsd?: number, token1PriceUsd?: number): void;
42
23
  checkSwap(params: CheckSwapParams): void;
43
- checkSync(params: CheckSyncParams): void;
24
+ private deriveSwapDirection;
44
25
  private compareAndLog;
45
26
  }
@@ -6,29 +6,28 @@ const ttd_core_1 = require("@clonegod/ttd-core");
6
6
  class QuotePriceVerify {
7
7
  constructor() {
8
8
  this.quoteCache = new Map();
9
- this.reserveCache = new Map();
10
9
  }
11
10
  get enabled() {
12
11
  return process.env.VERIFY_QUOTE_PRICE === 'true';
13
12
  }
14
- seedReserves(poolAddress, reserve0, reserve1) {
15
- if (!this.enabled)
16
- return;
17
- try {
18
- this.reserveCache.set(poolAddress, {
19
- reserve0: BigInt(reserve0),
20
- reserve1: BigInt(reserve1),
21
- });
22
- }
23
- catch { }
24
- }
25
13
  cacheQuote(poolAddress, priceId, source, askPrice, bidPrice, blockNumber, quoteAmountUsd, token0PriceUsd = 0, token1PriceUsd = 0) {
26
14
  if (!this.enabled)
27
15
  return;
28
- this.quoteCache.set(poolAddress, {
16
+ if (!source)
17
+ return;
18
+ let sourceMap = this.quoteCache.get(poolAddress);
19
+ if (!sourceMap) {
20
+ sourceMap = new Map();
21
+ this.quoteCache.set(poolAddress, sourceMap);
22
+ }
23
+ const existing = sourceMap.get(source);
24
+ const verifiedReferenceBlock = (existing && existing.referenceBlock === blockNumber)
25
+ ? existing.verifiedReferenceBlock
26
+ : 0;
27
+ sourceMap.set(source, {
29
28
  priceId, source, askPrice, bidPrice,
30
29
  referenceBlock: blockNumber,
31
- verifiedReferenceBlock: 0,
30
+ verifiedReferenceBlock,
32
31
  quoteAmountUsd,
33
32
  token0PriceUsd, token1PriceUsd,
34
33
  });
@@ -36,85 +35,74 @@ class QuotePriceVerify {
36
35
  checkSwap(params) {
37
36
  if (!this.enabled)
38
37
  return;
39
- const { poolAddress, poolName, blockNumber, txHash, amount0, amount1, poolInfo } = params;
40
- const { token0Address, token1Address, token0Decimals, token1Decimals } = params;
41
- const cached = this.quoteCache.get(poolAddress);
42
- if (!cached)
43
- return;
44
- if (blockNumber !== cached.referenceBlock + 1)
38
+ const { poolAddress, blockNumber, txHash, poolInfo, token0Address } = params;
39
+ const sourceMap = this.quoteCache.get(poolAddress);
40
+ if (!sourceMap || sourceMap.size === 0)
45
41
  return;
46
- if (cached.verifiedReferenceBlock === cached.referenceBlock)
42
+ const swapData = this.deriveSwapDirection(params);
43
+ if (!swapData)
47
44
  return;
48
- cached.verifiedReferenceBlock = cached.referenceBlock;
45
+ const direction = (0, trade_direction_1.resolveTradeDirection)(poolInfo, true);
46
+ const inputIsQuoteToken = swapData.inputTokenAddress.toLowerCase() === direction.quoteToken.address.toLowerCase();
47
+ const isBuy = inputIsQuoteToken;
48
+ const execPriceStr = (0, trade_direction_1.calculateStandardPrice)(swapData.inputAmountUi, swapData.outputAmountUi, isBuy);
49
+ const execPrice = Number(execPriceStr);
50
+ if (execPrice <= 0)
51
+ return;
52
+ const verifiable = [];
53
+ for (const [source, cached] of sourceMap) {
54
+ if (blockNumber <= cached.referenceBlock)
55
+ continue;
56
+ if (cached.verifiedReferenceBlock === cached.referenceBlock)
57
+ continue;
58
+ const refPrice = isBuy ? cached.askPrice : cached.bidPrice;
59
+ if (refPrice <= 0)
60
+ continue;
61
+ cached.verifiedReferenceBlock = cached.referenceBlock;
62
+ const diff_bps = (refPrice - execPrice) / refPrice * 10000;
63
+ verifiable.push({ source, cached, refPrice, diff_bps });
64
+ }
65
+ if (verifiable.length === 0)
66
+ return;
67
+ verifiable.sort((a, b) => b.cached.referenceBlock - a.cached.referenceBlock);
68
+ const primary = verifiable[0];
69
+ const sources = {};
70
+ for (const r of verifiable) {
71
+ sources[r.source] = {
72
+ ask: r.cached.askPrice,
73
+ bid: r.cached.bidPrice,
74
+ diff_bps: parseFloat(r.diff_bps.toFixed(1)),
75
+ quote_block: r.cached.referenceBlock,
76
+ };
77
+ }
78
+ this.compareAndLog(poolAddress, params.poolName, poolInfo, primary.cached, swapData.inputTokenAddress, swapData.outputTokenAddress, swapData.inputAmountUi, swapData.outputAmountUi, primary.cached.token0PriceUsd, primary.cached.token1PriceUsd, token0Address, blockNumber, txHash, sources);
79
+ }
80
+ deriveSwapDirection(params) {
81
+ const { amount0, amount1, token0Address, token1Address, token0Decimals, token1Decimals } = params;
49
82
  const sign = params.swapperDeltaConvention ? -1n : 1n;
50
83
  const amt0 = BigInt(amount0) * sign;
51
84
  const amt1 = BigInt(amount1) * sign;
52
85
  if (amt0 === 0n && amt1 === 0n)
53
- return;
54
- let inputTokenAddress;
55
- let outputTokenAddress;
56
- let inputAmountUi;
57
- let outputAmountUi;
86
+ return null;
58
87
  if (amt0 > 0n && amt1 < 0n) {
59
- inputTokenAddress = token0Address;
60
- outputTokenAddress = token1Address;
61
- inputAmountUi = Number(amt0) / Math.pow(10, token0Decimals);
62
- outputAmountUi = Number(-amt1) / Math.pow(10, token1Decimals);
88
+ return {
89
+ inputTokenAddress: token0Address,
90
+ outputTokenAddress: token1Address,
91
+ inputAmountUi: Number(amt0) / Math.pow(10, token0Decimals),
92
+ outputAmountUi: Number(-amt1) / Math.pow(10, token1Decimals),
93
+ };
63
94
  }
64
95
  else if (amt0 < 0n && amt1 > 0n) {
65
- inputTokenAddress = token1Address;
66
- outputTokenAddress = token0Address;
67
- inputAmountUi = Number(amt1) / Math.pow(10, token1Decimals);
68
- outputAmountUi = Number(-amt0) / Math.pow(10, token0Decimals);
69
- }
70
- else {
71
- return;
72
- }
73
- this.compareAndLog(poolAddress, poolName, poolInfo, cached, inputTokenAddress, outputTokenAddress, inputAmountUi, outputAmountUi, cached.token0PriceUsd, cached.token1PriceUsd, token0Address, blockNumber, txHash);
74
- }
75
- checkSync(params) {
76
- if (!this.enabled)
77
- return;
78
- const { poolAddress, poolName, blockNumber, reserve0, reserve1, poolInfo, txHash } = params;
79
- const { token0Address, token1Address, token0Decimals, token1Decimals } = params;
80
- const cached = this.quoteCache.get(poolAddress);
81
- if (!cached)
82
- return;
83
- if (blockNumber !== cached.referenceBlock + 1)
84
- return;
85
- if (cached.verifiedReferenceBlock === cached.referenceBlock)
86
- return;
87
- const newR0 = BigInt(reserve0);
88
- const newR1 = BigInt(reserve1);
89
- const oldReserves = this.reserveCache.get(poolAddress);
90
- this.reserveCache.set(poolAddress, { reserve0: newR0, reserve1: newR1 });
91
- if (!oldReserves)
92
- return;
93
- const delta0 = newR0 - oldReserves.reserve0;
94
- const delta1 = newR1 - oldReserves.reserve1;
95
- if ((delta0 > 0n && delta1 > 0n) || (delta0 < 0n && delta1 < 0n) || (delta0 === 0n && delta1 === 0n)) {
96
- return;
97
- }
98
- cached.verifiedReferenceBlock = cached.referenceBlock;
99
- let inputTokenAddress;
100
- let outputTokenAddress;
101
- let inputAmountUi;
102
- let outputAmountUi;
103
- if (delta0 > 0n && delta1 < 0n) {
104
- inputTokenAddress = token0Address;
105
- outputTokenAddress = token1Address;
106
- inputAmountUi = Number(delta0) / Math.pow(10, token0Decimals);
107
- outputAmountUi = Number(-delta1) / Math.pow(10, token1Decimals);
108
- }
109
- else {
110
- inputTokenAddress = token1Address;
111
- outputTokenAddress = token0Address;
112
- inputAmountUi = Number(delta1) / Math.pow(10, token1Decimals);
113
- outputAmountUi = Number(-delta0) / Math.pow(10, token0Decimals);
96
+ return {
97
+ inputTokenAddress: token1Address,
98
+ outputTokenAddress: token0Address,
99
+ inputAmountUi: Number(amt1) / Math.pow(10, token1Decimals),
100
+ outputAmountUi: Number(-amt0) / Math.pow(10, token0Decimals),
101
+ };
114
102
  }
115
- this.compareAndLog(poolAddress, poolName, poolInfo, cached, inputTokenAddress, outputTokenAddress, inputAmountUi, outputAmountUi, cached.token0PriceUsd, cached.token1PriceUsd, token0Address, blockNumber, txHash);
103
+ return null;
116
104
  }
117
- compareAndLog(poolAddress, poolName, poolInfo, cached, inputTokenAddress, outputTokenAddress, inputAmountUi, outputAmountUi, token0PriceUsd, token1PriceUsd, token0Address, swapBlockNumber, txHash) {
105
+ compareAndLog(poolAddress, poolName, poolInfo, cached, inputTokenAddress, outputTokenAddress, inputAmountUi, outputAmountUi, token0PriceUsd, token1PriceUsd, token0Address, swapBlockNumber, txHash, sources) {
118
106
  if (inputAmountUi <= 0 || outputAmountUi <= 0)
119
107
  return;
120
108
  let swapUsd = 0;
@@ -175,6 +163,7 @@ class QuotePriceVerify {
175
163
  output_symbol: isBuy ? baseToken.symbol : quoteToken.symbol,
176
164
  status,
177
165
  time: Date.now(),
166
+ sources: sources || undefined,
178
167
  });
179
168
  }
180
169
  catch (_) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clonegod/ttd-bsc-common",
3
- "version": "3.1.63",
3
+ "version": "3.1.65",
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.54",
17
+ "@clonegod/ttd-core": "3.1.55",
18
18
  "axios": "1.15.0",
19
19
  "dotenv": "^16.4.7",
20
20
  "ethers": "^5.8.0",