@clonegod/ttd-sol-common 2.0.67 → 2.0.68
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/SolanaQuoteAppConfig.d.ts +9 -0
- package/dist/appconfig/SolanaQuoteAppConfig.js +29 -0
- package/dist/appconfig/SolanaTradeAppConfig.d.ts +13 -0
- package/dist/{config → appconfig}/SolanaTradeAppConfig.js +25 -0
- package/dist/appconfig/ensure_core_env.d.ts +1 -0
- package/dist/appconfig/ensure_core_env.js +18 -0
- package/dist/appconfig/index.d.ts +5 -0
- package/dist/appconfig/index.js +21 -0
- package/dist/appconfig/sol_dex_env_args.d.ts +5 -0
- package/dist/appconfig/sol_dex_env_args.js +29 -0
- package/dist/appconfig/sol_env_args.d.ts +17 -0
- package/dist/appconfig/sol_env_args.js +37 -0
- package/dist/common/get_wallet_token_account.js +13 -16
- package/dist/grpc/grpc_provider_registry.d.ts +14 -0
- package/dist/grpc/grpc_provider_registry.js +70 -0
- package/dist/grpc/index.d.ts +1 -0
- package/dist/grpc/index.js +17 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/quote/abstract_dex_quote.d.ts +68 -0
- package/dist/quote/abstract_dex_quote.js +208 -0
- package/dist/quote/chain_ops.d.ts +18 -0
- package/dist/quote/chain_ops.js +66 -0
- package/dist/quote/depth/clmm_depth_calculator.d.ts +42 -0
- package/dist/quote/depth/clmm_depth_calculator.js +173 -0
- package/dist/quote/depth/index.d.ts +20 -0
- package/dist/quote/depth/index.js +100 -0
- package/dist/quote/index.d.ts +9 -0
- package/dist/quote/index.js +9 -0
- package/dist/quote/pool_event.d.ts +20 -0
- package/dist/quote/pool_event.js +22 -0
- package/dist/quote/pool_subscription_registry.d.ts +4 -0
- package/dist/quote/pool_subscription_registry.js +62 -0
- package/dist/quote/quote_amount.d.ts +4 -0
- package/dist/quote/quote_amount.js +24 -0
- package/dist/quote/quote_trace.d.ts +16 -0
- package/dist/quote/quote_trace.js +40 -0
- package/dist/quote/tick/clmm_tick_math.d.ts +5 -0
- package/dist/quote/tick/clmm_tick_math.js +61 -0
- package/dist/quote/tick/index.d.ts +1 -0
- package/dist/{config → quote/tick}/index.js +1 -1
- package/dist/quote/verify/index.d.ts +1 -0
- package/dist/quote/verify/index.js +17 -0
- package/dist/quote/verify/quote_price_verify.d.ts +30 -0
- package/dist/quote/verify/quote_price_verify.js +247 -0
- package/dist/trade/index.d.ts +0 -1
- package/dist/trade/index.js +0 -1
- package/dist/trade/tx_builder.d.ts +1 -1
- package/dist/trade/tx_result_parse.js +1 -0
- package/dist/types/index.d.ts +9 -1
- package/dist/types/index.js +2 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +17 -0
- package/dist/utils/trade_direction.d.ts +14 -0
- package/dist/utils/trade_direction.js +23 -0
- package/package.json +4 -4
- package/src/appconfig/SolanaQuoteAppConfig.ts +55 -0
- package/src/appconfig/SolanaTradeAppConfig.ts +117 -0
- package/src/appconfig/ensure_core_env.ts +28 -0
- package/src/appconfig/index.ts +5 -0
- package/src/appconfig/sol_dex_env_args.ts +52 -0
- package/src/appconfig/sol_env_args.ts +79 -0
- package/src/common/get_wallet_token_account.ts +27 -33
- package/src/grpc/grpc_provider_registry.ts +103 -0
- package/src/grpc/index.ts +1 -0
- package/src/index.ts +3 -0
- package/src/quote/abstract_dex_quote.ts +337 -0
- package/src/quote/chain_ops.ts +91 -0
- package/src/quote/depth/clmm_depth_calculator.ts +321 -0
- package/src/quote/depth/index.ts +167 -0
- package/src/quote/index.ts +9 -0
- package/src/quote/pool_event.ts +82 -0
- package/src/quote/pool_subscription_registry.ts +81 -0
- package/src/quote/quote_amount.ts +37 -0
- package/src/quote/quote_trace.ts +56 -0
- package/src/quote/tick/clmm_tick_math.ts +77 -0
- package/src/quote/tick/index.ts +1 -0
- package/src/quote/verify/index.ts +1 -0
- package/src/quote/verify/quote_price_verify.ts +508 -0
- package/src/trade/index.ts +0 -1
- package/src/trade/tx_builder.ts +1 -1
- package/src/trade/tx_result_parse.ts +1 -0
- package/src/types/index.ts +20 -2
- package/src/utils/index.ts +1 -0
- package/src/utils/trade_direction.ts +68 -0
- package/dist/config/SolanaTradeAppConfig.d.ts +0 -10
- package/dist/config/index.d.ts +0 -1
- package/dist/trade/SolanaTradeAppConfig.d.ts +0 -8
- package/dist/trade/SolanaTradeAppConfig.js +0 -26
- package/src/config/SolanaTradeAppConfig.ts +0 -70
- package/src/config/index.ts +0 -2
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AbstractDexQuote = void 0;
|
|
4
|
+
const dist_1 = require("@clonegod/ttd-core/dist");
|
|
5
|
+
const quote_price_verify_1 = require("./verify/quote_price_verify");
|
|
6
|
+
const quote_amount_1 = require("./quote_amount");
|
|
7
|
+
const quote_trace_1 = require("./quote_trace");
|
|
8
|
+
class AbstractDexQuote {
|
|
9
|
+
constructor(appConfig, chain) {
|
|
10
|
+
this.poolInfoMap = new Map();
|
|
11
|
+
this.poolLastQuoteTimeMap = new Map();
|
|
12
|
+
this.lastPublished = new Map();
|
|
13
|
+
this.appliedStateWatermark = new Map();
|
|
14
|
+
this.quotePriceVerify = new quote_price_verify_1.QuotePriceVerify();
|
|
15
|
+
this.latestBlockSlot = 0;
|
|
16
|
+
this.MIN_QUOTE_INTERVAL_MS = Math.max(3000, parseInt(process.env.MIN_QUOTE_INTERVAL_MS || '10000', 10) || 10000);
|
|
17
|
+
this.consistencyPolicy = 'snapshot';
|
|
18
|
+
this.accountSlots = new Map();
|
|
19
|
+
this.appConfig = appConfig;
|
|
20
|
+
this.chain = chain;
|
|
21
|
+
}
|
|
22
|
+
async refreshStateFromEvent(_poolInfo, _evt) { }
|
|
23
|
+
async calculateDepth(_poolInfo, _poolAddress, _priceMap) { return undefined; }
|
|
24
|
+
buildSwapVerify(_poolInfo, _evt) { return null; }
|
|
25
|
+
isStateConsistentForQuote(_poolInfo) { return true; }
|
|
26
|
+
recordAccountSlot(accountAddr, slot) {
|
|
27
|
+
this.accountSlots.set(accountAddr, slot);
|
|
28
|
+
}
|
|
29
|
+
async init(poolList) {
|
|
30
|
+
(0, dist_1.log_info)(`初始化 ${this.dexId} Quote,共 ${poolList.length} 个池...`);
|
|
31
|
+
for (const poolInfo of poolList) {
|
|
32
|
+
if (!this.chain.isValidPoolAddress(poolInfo.pool_address)) {
|
|
33
|
+
(0, dist_1.log_warn)(`跳过无效的池地址: ${poolInfo.pool_address}`, '');
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
this.poolInfoMap.set(poolInfo.pool_address, poolInfo);
|
|
37
|
+
}
|
|
38
|
+
await this.beforeLoadPools();
|
|
39
|
+
await Promise.all(Array.from(this.poolInfoMap.values()).map(async (p) => {
|
|
40
|
+
const summary = await this.loadPool(p);
|
|
41
|
+
if (summary.verifyProgram)
|
|
42
|
+
await this.chain.assertProgramDeployed(summary.verifyProgram);
|
|
43
|
+
(0, dist_1.log_info)(`[QUOTE INIT] ${p.pool_name} (${p.pool_address}) dex=${this.dexId}`, { ...summary });
|
|
44
|
+
}));
|
|
45
|
+
this.registerEventHandlers();
|
|
46
|
+
(0, dist_1.log_info)(`${this.dexId} Quote 初始化完成,${this.poolInfoMap.size} 个有效池`);
|
|
47
|
+
}
|
|
48
|
+
async beforeLoadPools() { }
|
|
49
|
+
getQuoteAmountUsd(poolInfo) { return (0, quote_amount_1.getQuoteAmountUsd)(poolInfo); }
|
|
50
|
+
registerEventHandlers() {
|
|
51
|
+
this.chain.subscribeNewBlock((raw) => this.handleBlockUpdateEvent(raw));
|
|
52
|
+
this.chain.subscribePoolEvents(this.dexId, Array.from(this.poolInfoMap.values()), async (evt) => {
|
|
53
|
+
const { bundle, verifyLog } = await this.calculateQuote(evt);
|
|
54
|
+
if (bundle)
|
|
55
|
+
this.publishQuote(bundle);
|
|
56
|
+
if (verifyLog)
|
|
57
|
+
(0, dist_1.log_info)(verifyLog);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
assertValidBlockNumber(blockNumber, ctx) {
|
|
61
|
+
if (!Number.isInteger(blockNumber) || blockNumber <= 0) {
|
|
62
|
+
throw new Error(`[block-number] ${ctx}: 非法 blockNumber=${blockNumber},每条数据源必须携带真实 slot`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
isStaleUpdate(poolAddr, block, txIndex) {
|
|
66
|
+
const last = this.lastPublished.get(poolAddr);
|
|
67
|
+
return !!last && (block < last.block || (block === last.block && txIndex <= last.txIndex));
|
|
68
|
+
}
|
|
69
|
+
shouldApplyState(poolAddr, block, txIndex) {
|
|
70
|
+
const last = this.appliedStateWatermark.get(poolAddr);
|
|
71
|
+
if (last && (block < last.block || (block === last.block && txIndex <= last.txIndex)))
|
|
72
|
+
return false;
|
|
73
|
+
this.appliedStateWatermark.set(poolAddr, { block, txIndex });
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
async handleBlockUpdateEvent(eventData) {
|
|
77
|
+
const { slot, blockTime } = JSON.parse(eventData);
|
|
78
|
+
this.assertValidBlockNumber(slot, `${this.dexId} block-event`);
|
|
79
|
+
this.latestBlockSlot = slot;
|
|
80
|
+
for (const poolInfo of this.poolInfoMap.values()) {
|
|
81
|
+
const poolAddress = poolInfo.pool_address;
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
const last = this.poolLastQuoteTimeMap.get(poolAddress) || 0;
|
|
84
|
+
if (now - last >= this.MIN_QUOTE_INTERVAL_MS) {
|
|
85
|
+
this.poolLastQuoteTimeMap.set(poolAddress, now);
|
|
86
|
+
const result = await this.calculateQuoteForPool(poolInfo, blockTime, slot);
|
|
87
|
+
if (result)
|
|
88
|
+
this.publishQuote(result);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async calculateQuote(evt) {
|
|
93
|
+
const poolInfo = this.poolInfoMap.get(evt.pool_address);
|
|
94
|
+
if (!poolInfo)
|
|
95
|
+
return { bundle: null };
|
|
96
|
+
this.assertValidBlockNumber(evt.blockNumber, `${this.dexId} ${poolInfo.pool_name} account-update`);
|
|
97
|
+
let verifyLog;
|
|
98
|
+
const v = this.buildSwapVerify(poolInfo, evt);
|
|
99
|
+
if (v)
|
|
100
|
+
verifyLog = this.quotePriceVerify.checkSwap(v) || undefined;
|
|
101
|
+
const hasWv = Number.isInteger(evt.writeVersion);
|
|
102
|
+
const skipApply = hasWv && !this.shouldApplyState(evt.pool_address, evt.blockNumber, evt.writeVersion);
|
|
103
|
+
if (!skipApply)
|
|
104
|
+
await this.refreshStateFromEvent(poolInfo, evt);
|
|
105
|
+
if (hasWv && this.isStaleUpdate(evt.pool_address, evt.blockNumber, evt.writeVersion))
|
|
106
|
+
return { bundle: null, verifyLog };
|
|
107
|
+
if (this.consistencyPolicy === 'slot-gate' && !this.isStateConsistentForQuote(poolInfo)) {
|
|
108
|
+
return { bundle: null, verifyLog };
|
|
109
|
+
}
|
|
110
|
+
const bundle = await this.calculateQuoteForPool(poolInfo, evt.blockTime, evt.blockNumber, evt);
|
|
111
|
+
return { bundle, verifyLog };
|
|
112
|
+
}
|
|
113
|
+
async calculateQuoteForPool(poolInfo, streamTimestamp, blockNumber, evt) {
|
|
114
|
+
const { pool_address } = poolInfo;
|
|
115
|
+
const eventDriven = !!evt;
|
|
116
|
+
const source = eventDriven ? 'local:v2' : 'rpc:v1';
|
|
117
|
+
const trace = new quote_trace_1.QuoteTrace(poolInfo.pool_name, pool_address, blockNumber, source);
|
|
118
|
+
trace.mark('trigger');
|
|
119
|
+
let stage = 'compute';
|
|
120
|
+
try {
|
|
121
|
+
const quote_amount_usd = (0, quote_amount_1.getQuoteAmountUsd)(poolInfo);
|
|
122
|
+
const quoteStartTime = Date.now();
|
|
123
|
+
const txid = evt ? (evt.txHash || 'account') : 'block';
|
|
124
|
+
const txIndex = evt?.writeVersion;
|
|
125
|
+
let askQuote, bidQuote;
|
|
126
|
+
if (eventDriven) {
|
|
127
|
+
;
|
|
128
|
+
[askQuote, bidQuote] = await Promise.all([this.quoteV2(poolInfo, true), this.quoteV2(poolInfo, false)]);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
;
|
|
132
|
+
[askQuote, bidQuote] = await Promise.all([this.quoteV1(poolInfo, true), this.quoteV1(poolInfo, false)]);
|
|
133
|
+
}
|
|
134
|
+
trace.mark('compute');
|
|
135
|
+
const askN = Number(askQuote.price), bidN = Number(bidQuote.price);
|
|
136
|
+
if (askN > 0 && bidN > 0 && askN < bidN) {
|
|
137
|
+
throw new Error(`ask(${askN}) < bid(${bidN}) — 异常价差,丢弃该报价`);
|
|
138
|
+
}
|
|
139
|
+
this.poolLastQuoteTimeMap.set(pool_address, Date.now());
|
|
140
|
+
stage = 'depth';
|
|
141
|
+
const t0a = poolInfo.tokenA?.address, t1a = poolInfo.tokenB?.address;
|
|
142
|
+
const priceMap = (t0a && t1a) ? await this.chain.loadTokenPrices([t0a, t1a]) : new Map();
|
|
143
|
+
const depth = await this.calculateDepth(poolInfo, pool_address, priceMap);
|
|
144
|
+
trace.mark('depth');
|
|
145
|
+
trace.set('ask', askQuote.price);
|
|
146
|
+
trace.set('bid', bidQuote.price);
|
|
147
|
+
trace.set('tiers', depth?.ask?.tiers?.length ?? 0);
|
|
148
|
+
trace.set('quote_amount_usd', quote_amount_usd);
|
|
149
|
+
trace.set('txid', txid);
|
|
150
|
+
return { poolInfo, quote_amount_usd, streamTimestamp, quoteStartTime, blockNumber, txIndex, askQuote, bidQuote, txid, source, depth, priceMap, trace };
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
trace.markError(stage, error instanceof Error ? error.message : String(error));
|
|
154
|
+
trace.flush();
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
publishQuote(result) {
|
|
159
|
+
const poolAddr = result.poolInfo.pool_address;
|
|
160
|
+
const isV2 = result.source?.endsWith(':v2');
|
|
161
|
+
let shouldPush = true;
|
|
162
|
+
if (result.source?.endsWith(':v1')) {
|
|
163
|
+
shouldPush = true;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
const incomingTx = result.txIndex;
|
|
167
|
+
if (!Number.isInteger(incomingTx)) {
|
|
168
|
+
shouldPush = true;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
const tx = incomingTx;
|
|
172
|
+
shouldPush = !this.isStaleUpdate(poolAddr, result.blockNumber, tx);
|
|
173
|
+
if (shouldPush)
|
|
174
|
+
this.lastPublished.set(poolAddr, { block: result.blockNumber, txIndex: tx });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (shouldPush) {
|
|
178
|
+
(0, dist_1.on_quote_response)({
|
|
179
|
+
appConfig: this.appConfig, poolInfo: result.poolInfo, quoteAmountUsd: result.quote_amount_usd,
|
|
180
|
+
streamTime: result.streamTimestamp, quoteStartTime: result.quoteStartTime, blockNumber: result.blockNumber,
|
|
181
|
+
quotes: [result.askQuote, result.bidQuote], txid: result.txid, source: result.source, depth: result.depth,
|
|
182
|
+
});
|
|
183
|
+
if (isV2) {
|
|
184
|
+
(0, dist_1.log_info)(`[QUOTE OK v2] ${result.poolInfo.pool_name} ask=${result.askQuote.price} bid=${result.bidQuote.price} slot=${result.blockNumber} wv=${result.txIndex ?? '-'} ${result.txid}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
result.trace?.set('published', shouldPush);
|
|
188
|
+
result.trace?.mark('publish');
|
|
189
|
+
const candidateTiers = isV2 && result.depth ? {
|
|
190
|
+
ask_tiers: result.depth.ask.tiers.map(t => ({ pct: t.pct, price: t.price, amount: t.amount, amount_in: t.amount_in })),
|
|
191
|
+
bid_tiers: result.depth.bid.tiers.map(t => ({ pct: t.pct, price: t.price, amount: t.amount, amount_in: t.amount_in })),
|
|
192
|
+
} : {};
|
|
193
|
+
(0, dist_1.report_quote_candidate)({
|
|
194
|
+
pool_address: poolAddr, pair: result.poolInfo.pair, dex_id: result.poolInfo.dex_id,
|
|
195
|
+
source: result.source || '', block_number: result.blockNumber,
|
|
196
|
+
ask_price: result.askQuote.price, bid_price: result.bidQuote.price, ...candidateTiers,
|
|
197
|
+
});
|
|
198
|
+
const quoteId = result.txid?.slice(0, 10) || `slot:${result.blockNumber}`;
|
|
199
|
+
const tiersArg = isV2 ? { askTiers: result.depth?.ask?.tiers, bidTiers: result.depth?.bid?.tiers } : undefined;
|
|
200
|
+
const t0a = result.poolInfo.tokenA?.address, t1a = result.poolInfo.tokenB?.address;
|
|
201
|
+
const p0 = t0a ? Number(result.priceMap?.get(t0a)?.price) || 0 : 0;
|
|
202
|
+
const p1 = t1a ? Number(result.priceMap?.get(t1a)?.price) || 0 : 0;
|
|
203
|
+
this.quotePriceVerify.cacheQuote(poolAddr, quoteId, result.source || '', Number(result.askQuote.price), Number(result.bidQuote.price), result.blockNumber, result.quote_amount_usd, p0, p1, tiersArg);
|
|
204
|
+
result.trace?.mark('verify');
|
|
205
|
+
result.trace?.flush();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
exports.AbstractDexQuote = AbstractDexQuote;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AppConfig, CHAIN_ID, StandardPoolInfoType } from '@clonegod/ttd-core/dist';
|
|
2
|
+
import { Connection } from '@solana/web3.js';
|
|
3
|
+
import { SolPoolEvent } from './pool_event';
|
|
4
|
+
import { TokenPriceCache } from './pricing/token_price_cache';
|
|
5
|
+
export declare class SolanaChainOps {
|
|
6
|
+
private readonly appConfig;
|
|
7
|
+
private readonly connection;
|
|
8
|
+
private readonly tokenPriceCache;
|
|
9
|
+
readonly chainId = CHAIN_ID.SOLANA;
|
|
10
|
+
constructor(appConfig: AppConfig, connection: Connection, tokenPriceCache: TokenPriceCache);
|
|
11
|
+
isValidPoolAddress(addr: string): boolean;
|
|
12
|
+
assertProgramDeployed(programAddress: string): Promise<void>;
|
|
13
|
+
loadTokenPrices(addresses: string[]): Promise<Map<string, {
|
|
14
|
+
price: string;
|
|
15
|
+
} | undefined>>;
|
|
16
|
+
subscribeNewBlock(handler: (raw: string) => void): void;
|
|
17
|
+
subscribePoolEvents(dexId: string, pools: StandardPoolInfoType[], handler: (evt: SolPoolEvent) => void): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SolanaChainOps = void 0;
|
|
4
|
+
const dist_1 = require("@clonegod/ttd-core/dist");
|
|
5
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
6
|
+
const subscribe_account_update_1 = require("../common/subscribe_account_update");
|
|
7
|
+
const pool_event_1 = require("./pool_event");
|
|
8
|
+
class SolanaChainOps {
|
|
9
|
+
constructor(appConfig, connection, tokenPriceCache) {
|
|
10
|
+
this.appConfig = appConfig;
|
|
11
|
+
this.connection = connection;
|
|
12
|
+
this.tokenPriceCache = tokenPriceCache;
|
|
13
|
+
this.chainId = dist_1.CHAIN_ID.SOLANA;
|
|
14
|
+
}
|
|
15
|
+
isValidPoolAddress(addr) {
|
|
16
|
+
try {
|
|
17
|
+
new web3_js_1.PublicKey(addr);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async assertProgramDeployed(programAddress) {
|
|
25
|
+
if (!programAddress)
|
|
26
|
+
return;
|
|
27
|
+
const info = await this.connection.getAccountInfo(new web3_js_1.PublicKey(programAddress));
|
|
28
|
+
if (!info) {
|
|
29
|
+
throw new Error(`[chain-ops] program ${programAddress} 在本链不存在(getAccountInfo=null)—— 疑似抄错地址/抄别链`);
|
|
30
|
+
}
|
|
31
|
+
if (!info.executable) {
|
|
32
|
+
throw new Error(`[chain-ops] account ${programAddress} 非 executable program —— 疑似把数据账户当 program`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async loadTokenPrices(addresses) {
|
|
36
|
+
const out = new Map();
|
|
37
|
+
await Promise.all(addresses.map(async (addr) => {
|
|
38
|
+
try {
|
|
39
|
+
const p = await this.tokenPriceCache.getTokenPrice(addr);
|
|
40
|
+
out.set(addr, { price: p.price });
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
out.set(addr, undefined);
|
|
44
|
+
}
|
|
45
|
+
}));
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
subscribeNewBlock(handler) {
|
|
49
|
+
this.appConfig.arb_event_subscriber.subscribe_new_block(this.chainId, handler);
|
|
50
|
+
}
|
|
51
|
+
subscribePoolEvents(dexId, pools, handler) {
|
|
52
|
+
const dex = dexId.toUpperCase();
|
|
53
|
+
const poolSet = new Set(pools.map(p => p.pool_address));
|
|
54
|
+
(0, subscribe_account_update_1.subscribe_pool_account_update)(dex, pools, (data) => {
|
|
55
|
+
try {
|
|
56
|
+
if (!poolSet.has(data.pool_address))
|
|
57
|
+
return;
|
|
58
|
+
handler((0, pool_event_1.toSolPoolEvent)(data));
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
(0, dist_1.log_warn)(`[SolanaChainOps] bad pool account update`, data);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.SolanaChainOps = SolanaChainOps;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface DepthTick {
|
|
2
|
+
index: number;
|
|
3
|
+
liquidityNet: bigint;
|
|
4
|
+
sqrtPriceX64: bigint;
|
|
5
|
+
}
|
|
6
|
+
export interface ClmmDepthInput {
|
|
7
|
+
sqrtPriceX64: bigint;
|
|
8
|
+
currentTick: number;
|
|
9
|
+
liquidity: bigint;
|
|
10
|
+
ticks: DepthTick[];
|
|
11
|
+
zeroForOne: boolean;
|
|
12
|
+
targetBps: number;
|
|
13
|
+
inputDecimals: number;
|
|
14
|
+
outputDecimals: number;
|
|
15
|
+
}
|
|
16
|
+
export interface DepthResult {
|
|
17
|
+
amountInWei: bigint;
|
|
18
|
+
amountIn: number;
|
|
19
|
+
amountOutWei: bigint;
|
|
20
|
+
amountOut: number;
|
|
21
|
+
currentTick: number;
|
|
22
|
+
targetTick: number;
|
|
23
|
+
targetSqrtPriceX64: bigint;
|
|
24
|
+
}
|
|
25
|
+
export declare function calculateClmmDepth(input: ClmmDepthInput): DepthResult;
|
|
26
|
+
export declare function priceFromSqrtX64(sqrtPriceX64: bigint, baseDecimals: number, quoteDecimals: number, baseIsToken0: boolean): number;
|
|
27
|
+
export declare function computeTargetSqrtX64(currentSqrtPriceX64: bigint, bps: number, zeroForOne: boolean): bigint;
|
|
28
|
+
export declare function bigIntSqrt(n: bigint): bigint;
|
|
29
|
+
export interface SwapExactInInput {
|
|
30
|
+
sqrtPriceX64: bigint;
|
|
31
|
+
currentTick: number;
|
|
32
|
+
liquidity: bigint;
|
|
33
|
+
ticks: DepthTick[];
|
|
34
|
+
zeroForOne: boolean;
|
|
35
|
+
amountInNetWei: bigint;
|
|
36
|
+
}
|
|
37
|
+
export interface SwapExactInResult {
|
|
38
|
+
amountInConsumedWei: bigint;
|
|
39
|
+
amountOutWei: bigint;
|
|
40
|
+
endSqrtPriceX64: bigint;
|
|
41
|
+
}
|
|
42
|
+
export declare function swapExactInClmm(input: SwapExactInInput): SwapExactInResult;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.calculateClmmDepth = calculateClmmDepth;
|
|
4
|
+
exports.priceFromSqrtX64 = priceFromSqrtX64;
|
|
5
|
+
exports.computeTargetSqrtX64 = computeTargetSqrtX64;
|
|
6
|
+
exports.bigIntSqrt = bigIntSqrt;
|
|
7
|
+
exports.swapExactInClmm = swapExactInClmm;
|
|
8
|
+
const Q64 = 1n << 64n;
|
|
9
|
+
function calculateClmmDepth(input) {
|
|
10
|
+
const { currentTick, zeroForOne, targetBps, inputDecimals, outputDecimals } = input;
|
|
11
|
+
const currentSqrtPriceX64 = input.sqrtPriceX64;
|
|
12
|
+
let liquidity = input.liquidity;
|
|
13
|
+
if (liquidity <= 0n) {
|
|
14
|
+
return { amountInWei: 0n, amountIn: 0, amountOutWei: 0n, amountOut: 0, currentTick, targetTick: currentTick, targetSqrtPriceX64: currentSqrtPriceX64 };
|
|
15
|
+
}
|
|
16
|
+
const targetSqrtPriceX64 = computeTargetSqrtX64(currentSqrtPriceX64, targetBps, zeroForOne);
|
|
17
|
+
const ticksToTraverse = getTicksBetween(input.ticks, currentTick, zeroForOne);
|
|
18
|
+
let totalInput = 0n;
|
|
19
|
+
let totalOutput = 0n;
|
|
20
|
+
let sqrtPriceCursor = currentSqrtPriceX64;
|
|
21
|
+
for (const tick of ticksToTraverse) {
|
|
22
|
+
const sqrtPriceAtTick = tick.sqrtPriceX64;
|
|
23
|
+
if (zeroForOne && sqrtPriceAtTick <= targetSqrtPriceX64)
|
|
24
|
+
break;
|
|
25
|
+
if (!zeroForOne && sqrtPriceAtTick >= targetSqrtPriceX64)
|
|
26
|
+
break;
|
|
27
|
+
if (liquidity > 0n) {
|
|
28
|
+
totalInput += calcInputForRange(sqrtPriceCursor, sqrtPriceAtTick, liquidity, zeroForOne);
|
|
29
|
+
totalOutput += calcOutputForRange(sqrtPriceCursor, sqrtPriceAtTick, liquidity, zeroForOne);
|
|
30
|
+
}
|
|
31
|
+
let liquidityNet = tick.liquidityNet;
|
|
32
|
+
if (zeroForOne)
|
|
33
|
+
liquidityNet = -liquidityNet;
|
|
34
|
+
liquidity = liquidity + liquidityNet;
|
|
35
|
+
if (liquidity < 0n)
|
|
36
|
+
liquidity = 0n;
|
|
37
|
+
sqrtPriceCursor = sqrtPriceAtTick;
|
|
38
|
+
}
|
|
39
|
+
if (liquidity > 0n && sqrtPriceCursor !== targetSqrtPriceX64) {
|
|
40
|
+
totalInput += calcInputForRange(sqrtPriceCursor, targetSqrtPriceX64, liquidity, zeroForOne);
|
|
41
|
+
totalOutput += calcOutputForRange(sqrtPriceCursor, targetSqrtPriceX64, liquidity, zeroForOne);
|
|
42
|
+
}
|
|
43
|
+
const amountIn = Number(totalInput) / Math.pow(10, inputDecimals);
|
|
44
|
+
const amountOut = Number(totalOutput) / Math.pow(10, outputDecimals);
|
|
45
|
+
const sqrtPriceFloat = Number(targetSqrtPriceX64) / Number(Q64);
|
|
46
|
+
const targetTick = Math.floor(Math.log(sqrtPriceFloat * sqrtPriceFloat) / Math.log(1.0001));
|
|
47
|
+
return { amountInWei: totalInput, amountIn, amountOutWei: totalOutput, amountOut, currentTick, targetTick, targetSqrtPriceX64 };
|
|
48
|
+
}
|
|
49
|
+
function priceFromSqrtX64(sqrtPriceX64, baseDecimals, quoteDecimals, baseIsToken0) {
|
|
50
|
+
const sp = Number(sqrtPriceX64) / Number(Q64);
|
|
51
|
+
const priceToken1PerToken0 = sp * sp;
|
|
52
|
+
if (baseIsToken0) {
|
|
53
|
+
return priceToken1PerToken0 * Math.pow(10, baseDecimals - quoteDecimals);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
return (1 / priceToken1PerToken0) * Math.pow(10, baseDecimals - quoteDecimals);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function computeTargetSqrtX64(currentSqrtPriceX64, bps, zeroForOne) {
|
|
60
|
+
const PRECISION = 10n ** 18n;
|
|
61
|
+
const bpsBigInt = BigInt(bps);
|
|
62
|
+
const factor = zeroForOne ? 10000n - bpsBigInt : 10000n + bpsBigInt;
|
|
63
|
+
const sqrtFactor = bigIntSqrt(factor * PRECISION);
|
|
64
|
+
const sqrtBase = bigIntSqrt(10000n * PRECISION);
|
|
65
|
+
return currentSqrtPriceX64 * sqrtFactor / sqrtBase;
|
|
66
|
+
}
|
|
67
|
+
function bigIntSqrt(n) {
|
|
68
|
+
if (n < 0n)
|
|
69
|
+
throw new Error('sqrt of negative');
|
|
70
|
+
if (n === 0n)
|
|
71
|
+
return 0n;
|
|
72
|
+
if (n <= 3n)
|
|
73
|
+
return 1n;
|
|
74
|
+
let x = n;
|
|
75
|
+
let y = (x + 1n) / 2n;
|
|
76
|
+
while (y < x) {
|
|
77
|
+
x = y;
|
|
78
|
+
y = (x + n / x) / 2n;
|
|
79
|
+
}
|
|
80
|
+
return x;
|
|
81
|
+
}
|
|
82
|
+
function getTicksBetween(ticks, currentTick, zeroForOne) {
|
|
83
|
+
if (zeroForOne) {
|
|
84
|
+
return ticks.filter(t => t.index <= currentTick).sort((a, b) => b.index - a.index);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
return ticks.filter(t => t.index > currentTick).sort((a, b) => a.index - b.index);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function nextSqrtFromAmountIn(sqrtPriceX64, liquidity, amountIn, zeroForOne) {
|
|
91
|
+
if (amountIn <= 0n || liquidity <= 0n)
|
|
92
|
+
return sqrtPriceX64;
|
|
93
|
+
if (zeroForOne) {
|
|
94
|
+
const numerator = liquidity * sqrtPriceX64 * Q64;
|
|
95
|
+
const denominator = liquidity * Q64 + amountIn * sqrtPriceX64;
|
|
96
|
+
return numerator / denominator;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
return sqrtPriceX64 + (amountIn * Q64) / liquidity;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function swapExactInClmm(input) {
|
|
103
|
+
const { currentTick, zeroForOne } = input;
|
|
104
|
+
let liquidity = input.liquidity;
|
|
105
|
+
let sqrtCursor = input.sqrtPriceX64;
|
|
106
|
+
let remaining = input.amountInNetWei;
|
|
107
|
+
let totalOut = 0n;
|
|
108
|
+
let consumed = 0n;
|
|
109
|
+
if (liquidity <= 0n || remaining <= 0n) {
|
|
110
|
+
return { amountInConsumedWei: 0n, amountOutWei: 0n, endSqrtPriceX64: sqrtCursor };
|
|
111
|
+
}
|
|
112
|
+
const ticksToTraverse = getTicksBetween(input.ticks, currentTick, zeroForOne);
|
|
113
|
+
for (const tick of ticksToTraverse) {
|
|
114
|
+
if (remaining <= 0n)
|
|
115
|
+
break;
|
|
116
|
+
const sqrtAtTick = tick.sqrtPriceX64;
|
|
117
|
+
if (liquidity > 0n) {
|
|
118
|
+
const segMaxIn = calcInputForRange(sqrtCursor, sqrtAtTick, liquidity, zeroForOne);
|
|
119
|
+
if (remaining < segMaxIn) {
|
|
120
|
+
const nextSqrt = nextSqrtFromAmountIn(sqrtCursor, liquidity, remaining, zeroForOne);
|
|
121
|
+
totalOut += calcOutputForRange(sqrtCursor, nextSqrt, liquidity, zeroForOne);
|
|
122
|
+
consumed += remaining;
|
|
123
|
+
remaining = 0n;
|
|
124
|
+
sqrtCursor = nextSqrt;
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
totalOut += calcOutputForRange(sqrtCursor, sqrtAtTick, liquidity, zeroForOne);
|
|
128
|
+
consumed += segMaxIn;
|
|
129
|
+
remaining -= segMaxIn;
|
|
130
|
+
}
|
|
131
|
+
sqrtCursor = sqrtAtTick;
|
|
132
|
+
let liquidityNet = tick.liquidityNet;
|
|
133
|
+
if (zeroForOne)
|
|
134
|
+
liquidityNet = -liquidityNet;
|
|
135
|
+
liquidity = liquidity + liquidityNet;
|
|
136
|
+
if (liquidity < 0n)
|
|
137
|
+
liquidity = 0n;
|
|
138
|
+
}
|
|
139
|
+
if (remaining > 0n && liquidity > 0n) {
|
|
140
|
+
const nextSqrt = nextSqrtFromAmountIn(sqrtCursor, liquidity, remaining, zeroForOne);
|
|
141
|
+
totalOut += calcOutputForRange(sqrtCursor, nextSqrt, liquidity, zeroForOne);
|
|
142
|
+
consumed += remaining;
|
|
143
|
+
sqrtCursor = nextSqrt;
|
|
144
|
+
remaining = 0n;
|
|
145
|
+
}
|
|
146
|
+
return { amountInConsumedWei: consumed, amountOutWei: totalOut, endSqrtPriceX64: sqrtCursor };
|
|
147
|
+
}
|
|
148
|
+
function calcInputForRange(sqrtPriceA, sqrtPriceB, liquidity, zeroForOne) {
|
|
149
|
+
const lower = sqrtPriceA < sqrtPriceB ? sqrtPriceA : sqrtPriceB;
|
|
150
|
+
const upper = sqrtPriceA < sqrtPriceB ? sqrtPriceB : sqrtPriceA;
|
|
151
|
+
const diff = upper - lower;
|
|
152
|
+
if (diff === 0n || liquidity === 0n)
|
|
153
|
+
return 0n;
|
|
154
|
+
if (zeroForOne) {
|
|
155
|
+
return liquidity * diff * Q64 / (lower * upper);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
return liquidity * diff / Q64;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function calcOutputForRange(sqrtPriceA, sqrtPriceB, liquidity, zeroForOne) {
|
|
162
|
+
const lower = sqrtPriceA < sqrtPriceB ? sqrtPriceA : sqrtPriceB;
|
|
163
|
+
const upper = sqrtPriceA < sqrtPriceB ? sqrtPriceB : sqrtPriceA;
|
|
164
|
+
const diff = upper - lower;
|
|
165
|
+
if (diff === 0n || liquidity === 0n)
|
|
166
|
+
return 0n;
|
|
167
|
+
if (zeroForOne) {
|
|
168
|
+
return liquidity * diff / Q64;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
return liquidity * diff * Q64 / (lower * upper);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { StandardPoolInfoType } from '@clonegod/ttd-core/dist';
|
|
2
|
+
import { QuoteDepthOutput } from '@clonegod/ttd-core/dist';
|
|
3
|
+
import { DepthTick } from './clmm_depth_calculator';
|
|
4
|
+
export { calculateClmmDepth, priceFromSqrtX64, computeTargetSqrtX64, bigIntSqrt, swapExactInClmm } from './clmm_depth_calculator';
|
|
5
|
+
export type { DepthTick, ClmmDepthInput, DepthResult, SwapExactInInput, SwapExactInResult } from './clmm_depth_calculator';
|
|
6
|
+
export interface BuildClmmDepthInput {
|
|
7
|
+
poolInfo: StandardPoolInfoType;
|
|
8
|
+
poolAddress: string;
|
|
9
|
+
poolState: {
|
|
10
|
+
currentSqrtPriceX64: bigint;
|
|
11
|
+
currentTick: number;
|
|
12
|
+
liquidity: bigint;
|
|
13
|
+
baseIsToken0: boolean;
|
|
14
|
+
};
|
|
15
|
+
ticks: DepthTick[];
|
|
16
|
+
basePriceUsd: number;
|
|
17
|
+
quotePriceUsd: number;
|
|
18
|
+
feeRateBps: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function buildClmmDepth(input: BuildClmmDepthInput): QuoteDepthOutput | undefined;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.swapExactInClmm = exports.bigIntSqrt = exports.computeTargetSqrtX64 = exports.priceFromSqrtX64 = exports.calculateClmmDepth = void 0;
|
|
4
|
+
exports.buildClmmDepth = buildClmmDepth;
|
|
5
|
+
const dist_1 = require("@clonegod/ttd-core/dist");
|
|
6
|
+
const dist_2 = require("@clonegod/ttd-core/dist");
|
|
7
|
+
const trade_direction_1 = require("../../utils/trade_direction");
|
|
8
|
+
const clmm_depth_calculator_1 = require("./clmm_depth_calculator");
|
|
9
|
+
var clmm_depth_calculator_2 = require("./clmm_depth_calculator");
|
|
10
|
+
Object.defineProperty(exports, "calculateClmmDepth", { enumerable: true, get: function () { return clmm_depth_calculator_2.calculateClmmDepth; } });
|
|
11
|
+
Object.defineProperty(exports, "priceFromSqrtX64", { enumerable: true, get: function () { return clmm_depth_calculator_2.priceFromSqrtX64; } });
|
|
12
|
+
Object.defineProperty(exports, "computeTargetSqrtX64", { enumerable: true, get: function () { return clmm_depth_calculator_2.computeTargetSqrtX64; } });
|
|
13
|
+
Object.defineProperty(exports, "bigIntSqrt", { enumerable: true, get: function () { return clmm_depth_calculator_2.bigIntSqrt; } });
|
|
14
|
+
Object.defineProperty(exports, "swapExactInClmm", { enumerable: true, get: function () { return clmm_depth_calculator_2.swapExactInClmm; } });
|
|
15
|
+
let _depthPctLogged = false;
|
|
16
|
+
function logDepthLevelsOnce() {
|
|
17
|
+
if (_depthPctLogged)
|
|
18
|
+
return;
|
|
19
|
+
_depthPctLogged = true;
|
|
20
|
+
(0, dist_1.log_info)(`[Depth] pctLevels=${JSON.stringify((0, dist_2.getDepthPricePctLevels)())}, default=${dist_2.DEFAULT_TIER_PCT}`);
|
|
21
|
+
}
|
|
22
|
+
function grossUpFee(amountInNet, feeRateBps) {
|
|
23
|
+
if (feeRateBps <= 0 || amountInNet <= 0)
|
|
24
|
+
return { amountInGross: amountInNet, feeAmount: 0 };
|
|
25
|
+
const feeRatio = feeRateBps / 10000;
|
|
26
|
+
const amountInGross = amountInNet / (1 - feeRatio);
|
|
27
|
+
return { amountInGross, feeAmount: amountInGross - amountInNet };
|
|
28
|
+
}
|
|
29
|
+
function assembleEntry(tiers) {
|
|
30
|
+
const def = tiers.find(t => t.pct === dist_2.DEFAULT_TIER_PCT) ?? tiers[0];
|
|
31
|
+
return {
|
|
32
|
+
price: def.price, amount: def.amount, amount_in: def.amount_in, amount_in_usd: def.amount_in_usd,
|
|
33
|
+
fee: def.fee, fee_usd: def.fee_usd, tick_move: def.tick_move, tiers,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function buildClmmDepth(input) {
|
|
37
|
+
logDepthLevelsOnce();
|
|
38
|
+
const pctLevels = (0, dist_2.getDepthPricePctLevels)();
|
|
39
|
+
if (pctLevels.length === 0)
|
|
40
|
+
return undefined;
|
|
41
|
+
const { poolInfo, poolState, ticks, basePriceUsd, quotePriceUsd, feeRateBps } = input;
|
|
42
|
+
if (poolState.liquidity <= 0n)
|
|
43
|
+
return undefined;
|
|
44
|
+
if (feeRateBps == null || feeRateBps < 0) {
|
|
45
|
+
(0, dist_1.log_debug)(`[Depth] ${poolInfo.pool_name} CLMM: invalid feeRateBps=${feeRateBps}, skip`, '');
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const dir = (0, trade_direction_1.resolveTradeDirection)(poolInfo, true);
|
|
50
|
+
const baseToken = dir.baseToken;
|
|
51
|
+
const quoteToken = dir.quoteToken;
|
|
52
|
+
const baseIsToken0 = poolState.baseIsToken0;
|
|
53
|
+
const askZeroForOne = !baseIsToken0;
|
|
54
|
+
const bidZeroForOne = baseIsToken0;
|
|
55
|
+
const midPrice = (0, clmm_depth_calculator_1.priceFromSqrtX64)(poolState.currentSqrtPriceX64, baseToken.decimals, quoteToken.decimals, baseIsToken0);
|
|
56
|
+
const askTiers = [];
|
|
57
|
+
const bidTiers = [];
|
|
58
|
+
for (const pct of pctLevels) {
|
|
59
|
+
const bps = Math.round(pct * 100);
|
|
60
|
+
const askResult = (0, clmm_depth_calculator_1.calculateClmmDepth)({
|
|
61
|
+
sqrtPriceX64: poolState.currentSqrtPriceX64, currentTick: poolState.currentTick,
|
|
62
|
+
liquidity: poolState.liquidity, ticks, zeroForOne: askZeroForOne, targetBps: bps,
|
|
63
|
+
inputDecimals: quoteToken.decimals, outputDecimals: baseToken.decimals,
|
|
64
|
+
});
|
|
65
|
+
const bidResult = (0, clmm_depth_calculator_1.calculateClmmDepth)({
|
|
66
|
+
sqrtPriceX64: poolState.currentSqrtPriceX64, currentTick: poolState.currentTick,
|
|
67
|
+
liquidity: poolState.liquidity, ticks, zeroForOne: bidZeroForOne, targetBps: bps,
|
|
68
|
+
inputDecimals: baseToken.decimals, outputDecimals: quoteToken.decimals,
|
|
69
|
+
});
|
|
70
|
+
const askTargetPrice = (0, clmm_depth_calculator_1.priceFromSqrtX64)(askResult.targetSqrtPriceX64, baseToken.decimals, quoteToken.decimals, baseIsToken0);
|
|
71
|
+
const bidTargetPrice = (0, clmm_depth_calculator_1.priceFromSqrtX64)(bidResult.targetSqrtPriceX64, baseToken.decimals, quoteToken.decimals, baseIsToken0);
|
|
72
|
+
const askGross = grossUpFee(askResult.amountIn, feeRateBps);
|
|
73
|
+
const bidGross = grossUpFee(bidResult.amountIn, feeRateBps);
|
|
74
|
+
const askTickMove = `${askResult.targetTick} <- ${askResult.currentTick}`;
|
|
75
|
+
const bidTickMove = `${bidResult.currentTick} -> ${bidResult.targetTick}`;
|
|
76
|
+
const askEffPrice = askResult.amountOut > 0 ? askGross.amountInGross / askResult.amountOut : askTargetPrice;
|
|
77
|
+
const bidEffPrice = bidGross.amountInGross > 0 ? bidResult.amountOut / bidGross.amountInGross : bidTargetPrice;
|
|
78
|
+
askTiers.push({
|
|
79
|
+
pct, price: askEffPrice, amount: askResult.amountOut,
|
|
80
|
+
amount_in: askGross.amountInGross, amount_in_usd: askGross.amountInGross * quotePriceUsd,
|
|
81
|
+
fee: askGross.feeAmount, fee_usd: askGross.feeAmount * quotePriceUsd, tick_move: askTickMove,
|
|
82
|
+
});
|
|
83
|
+
bidTiers.push({
|
|
84
|
+
pct, price: bidEffPrice, amount: bidResult.amountOut,
|
|
85
|
+
amount_in: bidGross.amountInGross, amount_in_usd: bidGross.amountInGross * basePriceUsd,
|
|
86
|
+
fee: bidGross.feeAmount, fee_usd: bidGross.feeAmount * basePriceUsd, tick_move: bidTickMove,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
mid_price: midPrice,
|
|
91
|
+
fee_rate_bps: feeRateBps,
|
|
92
|
+
ask: assembleEntry(askTiers),
|
|
93
|
+
bid: assembleEntry(bidTiers),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
(0, dist_1.log_debug)(`[Depth] ${poolInfo.pool_name} CLMM depth failed: ${error.message}`, '');
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
}
|
package/dist/quote/index.d.ts
CHANGED
|
@@ -1 +1,10 @@
|
|
|
1
1
|
export * from './pricing';
|
|
2
|
+
export * from './pool_subscription_registry';
|
|
3
|
+
export * from './pool_event';
|
|
4
|
+
export * from './chain_ops';
|
|
5
|
+
export * from './abstract_dex_quote';
|
|
6
|
+
export * from './quote_trace';
|
|
7
|
+
export * from './quote_amount';
|
|
8
|
+
export * from './verify';
|
|
9
|
+
export * from './depth';
|
|
10
|
+
export * from './tick';
|
package/dist/quote/index.js
CHANGED
|
@@ -15,3 +15,12 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./pricing"), exports);
|
|
18
|
+
__exportStar(require("./pool_subscription_registry"), exports);
|
|
19
|
+
__exportStar(require("./pool_event"), exports);
|
|
20
|
+
__exportStar(require("./chain_ops"), exports);
|
|
21
|
+
__exportStar(require("./abstract_dex_quote"), exports);
|
|
22
|
+
__exportStar(require("./quote_trace"), exports);
|
|
23
|
+
__exportStar(require("./quote_amount"), exports);
|
|
24
|
+
__exportStar(require("./verify"), exports);
|
|
25
|
+
__exportStar(require("./depth"), exports);
|
|
26
|
+
__exportStar(require("./tick"), exports);
|