@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,508 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 询价价格验证
|
|
3
|
+
*
|
|
4
|
+
* 验证逻辑:
|
|
5
|
+
* 1. 每次询价完成后,缓存 ask/bid 价格 + blockNumber
|
|
6
|
+
* 2. 收到同池子新 block 的第一笔 swap 时,用 swap 的 amount0/amount1 算出实际执行价格
|
|
7
|
+
* 3. 对比缓存的 ask 或 bid,打印偏差 bps
|
|
8
|
+
*
|
|
9
|
+
* 支持两种事件类型:
|
|
10
|
+
* - CLMM swap 事件:带符号的 amount0/amount1
|
|
11
|
+
* - AMM sync 事件:通过 reserve 变化量推算
|
|
12
|
+
*
|
|
13
|
+
* 通过 VERIFY_QUOTE_PRICE=true 启用
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { resolveTradeDirection, calculateStandardPrice } from '../../utils/trade_direction';
|
|
17
|
+
import { report_data_to_analyze, QuoteTier } from '@clonegod/ttd-core';
|
|
18
|
+
|
|
19
|
+
// ========== 类型定义 ==========
|
|
20
|
+
|
|
21
|
+
interface CachedQuote {
|
|
22
|
+
priceId: string;
|
|
23
|
+
source: string;
|
|
24
|
+
askPrice: number;
|
|
25
|
+
bidPrice: number;
|
|
26
|
+
/**
|
|
27
|
+
* tier 模式:缓存完整 ask/bid 档位数组(W3 新增)
|
|
28
|
+
* - V2 路径会传完整 5 档
|
|
29
|
+
* - V1 兜底路径会传单档(仅 pct=0.2)
|
|
30
|
+
* - 未传时 checkSwap 走 scalar fallback(向后兼容旧 DEX 包)
|
|
31
|
+
*/
|
|
32
|
+
askTiers?: QuoteTier[];
|
|
33
|
+
bidTiers?: QuoteTier[];
|
|
34
|
+
/**
|
|
35
|
+
* 该报价基于哪个 block 末态算的(cacheQuote 时收到事件的 blockNumber)。
|
|
36
|
+
* 验证规则:只接受 swap.block === referenceBlock + 1 的"下一 block 第一笔 swap"。
|
|
37
|
+
* - swap.block === referenceBlock:同 block,旧报价没意义,跳
|
|
38
|
+
* - swap.block > referenceBlock + 1:报价滞后或漏更新,比对会污染统计,跳
|
|
39
|
+
* - swap.block < referenceBlock:不可能,但 defensive
|
|
40
|
+
*/
|
|
41
|
+
referenceBlock: number;
|
|
42
|
+
quoteAmountUsd: number;
|
|
43
|
+
/**
|
|
44
|
+
* 该 referenceBlock 是否已经被对应的 next-block 验证过。
|
|
45
|
+
* == referenceBlock 表示已验过;防同 block 内多笔 swap 重复验证。
|
|
46
|
+
*/
|
|
47
|
+
verifiedReferenceBlock: number;
|
|
48
|
+
/** 缓存的 token0 USD 价格(cacheQuote 时传入,供 checkSwap 过滤用) */
|
|
49
|
+
token0PriceUsd: number;
|
|
50
|
+
/** 缓存的 token1 USD 价格 */
|
|
51
|
+
token1PriceUsd: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Tier 选择/插值结果
|
|
56
|
+
*/
|
|
57
|
+
interface TierMatch {
|
|
58
|
+
/** 选中(或插值得到)的档位 */
|
|
59
|
+
tier: QuoteTier;
|
|
60
|
+
/** 实际成交量落点 vs tier 档位的关系 */
|
|
61
|
+
mode: 'exact' | 'interpolated' | 'extrapolated_low' | 'extrapolated_high' | 'single_tier';
|
|
62
|
+
/** 命中区间的下界标准档 pct(extrapolated_low 时 null = 小于最小档)*/
|
|
63
|
+
lo_pct: number | null;
|
|
64
|
+
/** 命中区间的上界标准档 pct(extrapolated_high 时 null = 超出最大档)*/
|
|
65
|
+
hi_pct: number | null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 按实际 amount_in 在 tiers 数组里挑出/插值出参考档位
|
|
70
|
+
*
|
|
71
|
+
* 算法:
|
|
72
|
+
* - tiers 数组按 amount_in 升序排
|
|
73
|
+
* - actualAmountIn <= tiers[0].amount_in → extrapolated_low(用最低档兜底)
|
|
74
|
+
* - actualAmountIn >= tiers[last].amount_in → extrapolated_high(用最高档兜底,标记"超出深度")
|
|
75
|
+
* - 落在两档之间 → 线性插值
|
|
76
|
+
* - tiers.length === 1 → single_tier(V1 兜底场景)
|
|
77
|
+
*/
|
|
78
|
+
function pickMatchingTier(tiers: QuoteTier[], actualAmountIn: number): TierMatch | null {
|
|
79
|
+
if (!tiers || tiers.length === 0) return null;
|
|
80
|
+
if (tiers.length === 1) return { tier: tiers[0], mode: 'single_tier', lo_pct: tiers[0].pct, hi_pct: tiers[0].pct };
|
|
81
|
+
|
|
82
|
+
const sorted = [...tiers].sort((a, b) => a.amount_in - b.amount_in);
|
|
83
|
+
const top = sorted[sorted.length - 1];
|
|
84
|
+
|
|
85
|
+
// 严格"小于"才算外推;等于最低/最高档算 exact(保持统计 mode 标签准确)
|
|
86
|
+
if (actualAmountIn < sorted[0].amount_in) {
|
|
87
|
+
return { tier: sorted[0], mode: 'extrapolated_low', lo_pct: null, hi_pct: sorted[0].pct };
|
|
88
|
+
}
|
|
89
|
+
if (actualAmountIn > top.amount_in) {
|
|
90
|
+
return { tier: top, mode: 'extrapolated_high', lo_pct: top.pct, hi_pct: null };
|
|
91
|
+
}
|
|
92
|
+
for (let i = 0; i < sorted.length - 1; i++) {
|
|
93
|
+
const lower = sorted[i];
|
|
94
|
+
const upper = sorted[i + 1];
|
|
95
|
+
if (actualAmountIn >= lower.amount_in && actualAmountIn <= upper.amount_in) {
|
|
96
|
+
// 边界相等 → exact
|
|
97
|
+
if (actualAmountIn === lower.amount_in) return { tier: lower, mode: 'exact', lo_pct: lower.pct, hi_pct: lower.pct };
|
|
98
|
+
if (actualAmountIn === upper.amount_in) return { tier: upper, mode: 'exact', lo_pct: upper.pct, hi_pct: upper.pct };
|
|
99
|
+
const span = upper.amount_in - lower.amount_in;
|
|
100
|
+
if (span <= 0) return { tier: lower, mode: 'exact', lo_pct: lower.pct, hi_pct: lower.pct };
|
|
101
|
+
const ratio = (actualAmountIn - lower.amount_in) / span;
|
|
102
|
+
const lerp = (a: number, b: number) => a + ratio * (b - a);
|
|
103
|
+
return {
|
|
104
|
+
tier: {
|
|
105
|
+
pct: lerp(lower.pct, upper.pct),
|
|
106
|
+
price: lerp(lower.price, upper.price),
|
|
107
|
+
amount: lerp(lower.amount, upper.amount),
|
|
108
|
+
amount_in: actualAmountIn,
|
|
109
|
+
amount_in_usd: lerp(lower.amount_in_usd, upper.amount_in_usd),
|
|
110
|
+
fee: lerp(lower.fee, upper.fee),
|
|
111
|
+
fee_usd: lerp(lower.fee_usd, upper.fee_usd),
|
|
112
|
+
},
|
|
113
|
+
mode: 'interpolated',
|
|
114
|
+
lo_pct: lower.pct,
|
|
115
|
+
hi_pct: upper.pct,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return { tier: top, mode: 'extrapolated_high', lo_pct: top.pct, hi_pct: null };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface CheckSwapParams {
|
|
123
|
+
poolAddress: string;
|
|
124
|
+
poolName: string;
|
|
125
|
+
blockNumber: number;
|
|
126
|
+
/** 验证用的 swap 交易哈希 */
|
|
127
|
+
txHash: string;
|
|
128
|
+
/** swap 事件的 amount0;含义由 swapperDeltaConvention 决定 */
|
|
129
|
+
amount0: string;
|
|
130
|
+
/** swap 事件的 amount1;含义同上 */
|
|
131
|
+
amount1: string;
|
|
132
|
+
token0Address: string;
|
|
133
|
+
token1Address: string;
|
|
134
|
+
token0Decimals: number;
|
|
135
|
+
token1Decimals: number;
|
|
136
|
+
/** 用于 resolveTradeDirection 判断 ask/bid */
|
|
137
|
+
poolInfo: { tokenA: any; tokenB: any; quote_token: string };
|
|
138
|
+
/**
|
|
139
|
+
* amount 符号约定(**实测**结论,BscScan 验证):
|
|
140
|
+
* - false(默认 / V3 CLMM):amount0/1 是**池子的 delta**(正=池子收到=swapper 输入)
|
|
141
|
+
* - true(V4 / Pancake Infinity / Uniswap V4):amount0/1 是**swapper 的 delta**(正=swapper 收到=swapper 输出)
|
|
142
|
+
* V4 caller 必须传 true,否则 side 会被算反(参见 tx 0xea45784b... / 0xdae0faa7... 验证)。
|
|
143
|
+
*/
|
|
144
|
+
swapperDeltaConvention?: boolean;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ========== 主类 ==========
|
|
148
|
+
|
|
149
|
+
export class QuotePriceVerify {
|
|
150
|
+
/**
|
|
151
|
+
* per-source cache: pool → source → CachedQuote
|
|
152
|
+
*
|
|
153
|
+
* 设计意图:v1/v2/v3 每个 source 独立 cache 槽,互不覆盖。
|
|
154
|
+
* 每笔 chain Swap 触发时遍历该池的所有 source,每个独立做一次 verify 上报,
|
|
155
|
+
* 跨源精度量化各自独立。yynode 抢跑不影响 v1/v2 的 verify 数据。
|
|
156
|
+
*/
|
|
157
|
+
private quoteCache: Map<string, Map<string, CachedQuote>> = new Map();
|
|
158
|
+
private get enabled(): boolean {
|
|
159
|
+
return process.env.VERIFY_QUOTE_PRICE === 'true';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* 询价完成后调用,缓存 ask/bid 价格(per-source 写入对应槽)。
|
|
164
|
+
*
|
|
165
|
+
* 同 (pool, source, refBlock) 重复 cache 的语义(典型场景:USD 异步补全后第二次 cacheQuote):
|
|
166
|
+
* - askPrice / bidPrice / quoteAmountUsd / token{0,1}PriceUsd 用新传入值覆盖(USD 补全有意义)
|
|
167
|
+
* - **verifiedReferenceBlock 保留**(防止"已验过的 quote 被重置后又验一次",违反单 quote 单验约定)
|
|
168
|
+
*
|
|
169
|
+
* 不同 refBlock:完全重置(含 verifiedReferenceBlock=0)→ 新 quote 周期,下一笔 swap 可正常验
|
|
170
|
+
*/
|
|
171
|
+
cacheQuote(
|
|
172
|
+
poolAddress: string,
|
|
173
|
+
priceId: string,
|
|
174
|
+
source: string,
|
|
175
|
+
askPrice: number,
|
|
176
|
+
bidPrice: number,
|
|
177
|
+
blockNumber: number,
|
|
178
|
+
quoteAmountUsd: number,
|
|
179
|
+
token0PriceUsd: number = 0,
|
|
180
|
+
token1PriceUsd: number = 0,
|
|
181
|
+
tiers?: { askTiers?: QuoteTier[]; bidTiers?: QuoteTier[] },
|
|
182
|
+
): void {
|
|
183
|
+
if (!this.enabled) return;
|
|
184
|
+
if (!source) return; // source 是 key,必须有
|
|
185
|
+
|
|
186
|
+
let sourceMap = this.quoteCache.get(poolAddress);
|
|
187
|
+
if (!sourceMap) {
|
|
188
|
+
sourceMap = new Map();
|
|
189
|
+
this.quoteCache.set(poolAddress, sourceMap);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// 同 source 同 refBlock 重复 cache:保留 verifiedReferenceBlock 状态(避免 USD 二次补全导致重验)
|
|
193
|
+
const existing = sourceMap.get(source);
|
|
194
|
+
const verifiedReferenceBlock = (existing && existing.referenceBlock === blockNumber)
|
|
195
|
+
? existing.verifiedReferenceBlock
|
|
196
|
+
: 0;
|
|
197
|
+
|
|
198
|
+
sourceMap.set(source, {
|
|
199
|
+
priceId, source, askPrice, bidPrice,
|
|
200
|
+
askTiers: tiers?.askTiers,
|
|
201
|
+
bidTiers: tiers?.bidTiers,
|
|
202
|
+
referenceBlock: blockNumber,
|
|
203
|
+
verifiedReferenceBlock,
|
|
204
|
+
quoteAmountUsd,
|
|
205
|
+
token0PriceUsd, token1PriceUsd,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* 收到 swap 事件时调用:遍历该池子下所有 source 的 cache,**收集** verifiable,
|
|
211
|
+
* **一次性 upload 1 条 verify 记录**(含 sources field,前端表 1 行 N 列展示)。
|
|
212
|
+
*
|
|
213
|
+
* 设计选择(合并上报 vs 上报后合并):
|
|
214
|
+
* - 合并上报(当前):1 条原子 upload,含 sources。优点:网络/存储省 30-40%,UI 零聚合开销,
|
|
215
|
+
* 原子性强(不会 partial state),跟"per-source cache 是单一权威源"哲学一致。
|
|
216
|
+
* - 上报后合并:N 条独立 upload,后端 group by tx_hash 聚合。
|
|
217
|
+
* 优点:按 source SQL 查询友好;缺点:网络/存储多 N 倍,partial state 风险,
|
|
218
|
+
* 后端需聚合逻辑。
|
|
219
|
+
* 选择"合并上报"因为:UI 表是核心查询模式(聚合视角),按 source 长期分析非主用法
|
|
220
|
+
* (真要做用 mem_quote_source_diff 更合适)。
|
|
221
|
+
*
|
|
222
|
+
* - primary source = referenceBlock 最大的(最新 quote)→ 主字段 (source / ref_price / diff_bps)
|
|
223
|
+
* - sources = 全 verifiable source 的 {ask, bid, diff_bps, quote_block}(前端 v1/v2/v3 各列)
|
|
224
|
+
* - 同 block 后续 swap 被 verifiedReferenceBlock 拦掉(每 source 每 quote 只验 1 次)
|
|
225
|
+
*/
|
|
226
|
+
/** 返回 [Verify] 日志行(调用方择时打印——基类在 [QUOTE OK] 之后输出,保证日志顺序);无可验证则返回 void。 */
|
|
227
|
+
checkSwap(params: CheckSwapParams): string | void {
|
|
228
|
+
if (!this.enabled) return;
|
|
229
|
+
|
|
230
|
+
const { poolAddress, blockNumber, txHash, poolInfo, token0Address } = params;
|
|
231
|
+
const sourceMap = this.quoteCache.get(poolAddress);
|
|
232
|
+
if (!sourceMap || sourceMap.size === 0) return;
|
|
233
|
+
|
|
234
|
+
// 1. swap 方向 / amount(跟 source 无关)
|
|
235
|
+
const swapData = this.deriveSwapDirection(params);
|
|
236
|
+
if (!swapData) return;
|
|
237
|
+
|
|
238
|
+
// 2. 算 exec_price(跟 source 无关)
|
|
239
|
+
const direction = resolveTradeDirection(poolInfo, true);
|
|
240
|
+
const inputIsQuoteToken = swapData.inputTokenAddress.toLowerCase() === direction.quoteToken.address.toLowerCase();
|
|
241
|
+
const isBuy = inputIsQuoteToken;
|
|
242
|
+
const execPriceStr = calculateStandardPrice(swapData.inputAmountUi, swapData.outputAmountUi, isBuy);
|
|
243
|
+
const execPrice = Number(execPriceStr);
|
|
244
|
+
if (execPrice <= 0) return;
|
|
245
|
+
|
|
246
|
+
// 3. 遍历每个 source 收集 verifiable
|
|
247
|
+
// tier 模式(W3):用实际 swap 的 inputAmount 在 cached.askTiers/bidTiers 里匹配 + 插值
|
|
248
|
+
// scalar 模式(向后兼容):直接用 cached.askPrice/bidPrice
|
|
249
|
+
const verifiable: Array<{
|
|
250
|
+
source: string;
|
|
251
|
+
cached: CachedQuote;
|
|
252
|
+
refPrice: number;
|
|
253
|
+
diff_bps: number;
|
|
254
|
+
/** 实际成交量命中的 tier 信息(tier 模式才有) */
|
|
255
|
+
tierInfo?: {
|
|
256
|
+
matched_pct: number;
|
|
257
|
+
mode: TierMatch['mode'];
|
|
258
|
+
tier_amount_in: number;
|
|
259
|
+
lo_pct: number | null;
|
|
260
|
+
hi_pct: number | null;
|
|
261
|
+
};
|
|
262
|
+
}> = [];
|
|
263
|
+
|
|
264
|
+
for (const [source, cached] of sourceMap) {
|
|
265
|
+
if (blockNumber <= cached.referenceBlock) continue;
|
|
266
|
+
if (cached.verifiedReferenceBlock === cached.referenceBlock) continue;
|
|
267
|
+
|
|
268
|
+
const tiers = isBuy ? cached.askTiers : cached.bidTiers;
|
|
269
|
+
let refPrice: number;
|
|
270
|
+
let tierInfo: typeof verifiable[0]['tierInfo'] | undefined;
|
|
271
|
+
|
|
272
|
+
// 所有 source(V1/V2/V3)的 tiers 都按统一契约输出真实非零 amount_in,
|
|
273
|
+
// tier 匹配可无分支启用(V3 单档时退化为 single_tier 模式)
|
|
274
|
+
if (tiers && tiers.length > 0) {
|
|
275
|
+
// tier 模式:按 actualAmountIn 匹配/插值
|
|
276
|
+
const match = pickMatchingTier(tiers, swapData.inputAmountUi);
|
|
277
|
+
if (!match) continue;
|
|
278
|
+
refPrice = match.tier.price;
|
|
279
|
+
tierInfo = {
|
|
280
|
+
matched_pct: parseFloat(match.tier.pct.toFixed(4)),
|
|
281
|
+
mode: match.mode,
|
|
282
|
+
tier_amount_in: match.tier.amount_in,
|
|
283
|
+
lo_pct: match.lo_pct,
|
|
284
|
+
hi_pct: match.hi_pct,
|
|
285
|
+
};
|
|
286
|
+
} else {
|
|
287
|
+
// scalar fallback:旧 DEX 包未传 tiers 时使用
|
|
288
|
+
refPrice = isBuy ? cached.askPrice : cached.bidPrice;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (refPrice <= 0) continue;
|
|
292
|
+
|
|
293
|
+
cached.verifiedReferenceBlock = cached.referenceBlock;
|
|
294
|
+
const diff_bps = (refPrice - execPrice) / refPrice * 10000;
|
|
295
|
+
verifiable.push({ source, cached, refPrice, diff_bps, tierInfo });
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (verifiable.length === 0) return;
|
|
299
|
+
|
|
300
|
+
// 4. 选 primary(referenceBlock 最大 = 最新 quote)
|
|
301
|
+
verifiable.sort((a, b) => b.cached.referenceBlock - a.cached.referenceBlock);
|
|
302
|
+
const primary = verifiable[0];
|
|
303
|
+
|
|
304
|
+
// 5. 构造 sources field(前端"1 行多列"用,每 source 独立 diff_bps)
|
|
305
|
+
// tier 模式:额外携带 matched_pct / mode / tier_amount_in(V1 单档 vs V2 多档可区分)
|
|
306
|
+
const sources: Record<string, {
|
|
307
|
+
ask: number;
|
|
308
|
+
bid: number;
|
|
309
|
+
/** diff_bps 真正用来比对的 refPrice(tier 模式 = 匹配/插值的 tier price;scalar 模式 = ask/bid 对应那个)
|
|
310
|
+
* UI 应该显示这个数而不是 ask/bid,否则会出现"显示价 vs diff_bps 不自洽"的视觉 bug */
|
|
311
|
+
matched_ref_price: number;
|
|
312
|
+
diff_bps: number;
|
|
313
|
+
quote_block: number;
|
|
314
|
+
matched_pct?: number;
|
|
315
|
+
tier_mode?: TierMatch['mode'];
|
|
316
|
+
tier_amount_in?: number;
|
|
317
|
+
}> = {};
|
|
318
|
+
for (const r of verifiable) {
|
|
319
|
+
sources[r.source] = {
|
|
320
|
+
ask: r.cached.askPrice,
|
|
321
|
+
bid: r.cached.bidPrice,
|
|
322
|
+
matched_ref_price: r.refPrice,
|
|
323
|
+
diff_bps: parseFloat(r.diff_bps.toFixed(1)),
|
|
324
|
+
quote_block: r.cached.referenceBlock,
|
|
325
|
+
...(r.tierInfo && {
|
|
326
|
+
matched_pct: r.tierInfo.matched_pct,
|
|
327
|
+
tier_mode: r.tierInfo.mode,
|
|
328
|
+
tier_amount_in: r.tierInfo.tier_amount_in,
|
|
329
|
+
}),
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 6. 用 primary source 走 compareAndLog(构造主字段 + analyze 上报,返回日志行),透传 sources + primary tierInfo
|
|
334
|
+
// refPrice 直接传 primary.refPrice(已含 tier 匹配后的值),避免重新算
|
|
335
|
+
return this.compareAndLog(
|
|
336
|
+
poolAddress, params.poolName, poolInfo, primary.cached,
|
|
337
|
+
swapData.inputTokenAddress, swapData.outputTokenAddress,
|
|
338
|
+
swapData.inputAmountUi, swapData.outputAmountUi,
|
|
339
|
+
primary.refPrice, execPrice,
|
|
340
|
+
primary.cached.token0PriceUsd, primary.cached.token1PriceUsd,
|
|
341
|
+
token0Address, blockNumber, txHash,
|
|
342
|
+
sources,
|
|
343
|
+
primary.tierInfo,
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* 从 swap event 的 amount0/amount1 反推 swapper 的方向 + 数量。
|
|
349
|
+
* 跟 source 无关(同一笔 swap,方向 / 数量是客观事实),所以从 checkSwap 主流程提取出来复用。
|
|
350
|
+
*/
|
|
351
|
+
private deriveSwapDirection(params: CheckSwapParams): {
|
|
352
|
+
inputTokenAddress: string;
|
|
353
|
+
outputTokenAddress: string;
|
|
354
|
+
inputAmountUi: number;
|
|
355
|
+
outputAmountUi: number;
|
|
356
|
+
} | null {
|
|
357
|
+
const { amount0, amount1, token0Address, token1Address, token0Decimals, token1Decimals } = params;
|
|
358
|
+
|
|
359
|
+
// V4 (Pancake Infinity / Uniswap V4) amount = swapper's delta(正=swapper 收到);
|
|
360
|
+
// V3 amount = 池子的 delta(正=池子收到)。统一翻转到"池子 delta"语义。
|
|
361
|
+
const sign = params.swapperDeltaConvention ? -1n : 1n;
|
|
362
|
+
const amt0 = BigInt(amount0) * sign;
|
|
363
|
+
const amt1 = BigInt(amount1) * sign;
|
|
364
|
+
|
|
365
|
+
if (amt0 === 0n && amt1 === 0n) return null;
|
|
366
|
+
|
|
367
|
+
if (amt0 > 0n && amt1 < 0n) {
|
|
368
|
+
// swapper 输入 token0,获得 token1
|
|
369
|
+
return {
|
|
370
|
+
inputTokenAddress: token0Address,
|
|
371
|
+
outputTokenAddress: token1Address,
|
|
372
|
+
inputAmountUi: Number(amt0) / Math.pow(10, token0Decimals),
|
|
373
|
+
outputAmountUi: Number(-amt1) / Math.pow(10, token1Decimals),
|
|
374
|
+
};
|
|
375
|
+
} else if (amt0 < 0n && amt1 > 0n) {
|
|
376
|
+
// swapper 输入 token1,获得 token0
|
|
377
|
+
return {
|
|
378
|
+
inputTokenAddress: token1Address,
|
|
379
|
+
outputTokenAddress: token0Address,
|
|
380
|
+
inputAmountUi: Number(amt1) / Math.pow(10, token1Decimals),
|
|
381
|
+
outputAmountUi: Number(-amt0) / Math.pow(10, token0Decimals),
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* 通用对比逻辑:计算执行价格,跟缓存的 ask/bid 对比
|
|
389
|
+
*/
|
|
390
|
+
private compareAndLog(
|
|
391
|
+
poolAddress: string,
|
|
392
|
+
poolName: string,
|
|
393
|
+
poolInfo: { tokenA: any; tokenB: any; quote_token: string },
|
|
394
|
+
cached: CachedQuote,
|
|
395
|
+
inputTokenAddress: string,
|
|
396
|
+
outputTokenAddress: string,
|
|
397
|
+
inputAmountUi: number,
|
|
398
|
+
outputAmountUi: number,
|
|
399
|
+
refPrice: number,
|
|
400
|
+
execPriceNum: number,
|
|
401
|
+
token0PriceUsd?: number,
|
|
402
|
+
token1PriceUsd?: number,
|
|
403
|
+
token0Address?: string,
|
|
404
|
+
swapBlockNumber?: number,
|
|
405
|
+
txHash?: string,
|
|
406
|
+
sources?: Record<string, {
|
|
407
|
+
ask: number; bid: number; diff_bps: number; quote_block: number;
|
|
408
|
+
matched_pct?: number; tier_mode?: TierMatch['mode']; tier_amount_in?: number;
|
|
409
|
+
}>,
|
|
410
|
+
primaryTierInfo?: { matched_pct: number; mode: TierMatch['mode']; tier_amount_in: number; lo_pct: number | null; hi_pct: number | null },
|
|
411
|
+
): string | void {
|
|
412
|
+
if (inputAmountUi <= 0 || outputAmountUi <= 0) return;
|
|
413
|
+
|
|
414
|
+
// 估算 USD 金额
|
|
415
|
+
let swapUsd = 0;
|
|
416
|
+
if (token0PriceUsd && token1PriceUsd && token0Address) {
|
|
417
|
+
const inputIsToken0 = inputTokenAddress.toLowerCase() === token0Address.toLowerCase();
|
|
418
|
+
swapUsd = inputIsToken0
|
|
419
|
+
? inputAmountUi * token0PriceUsd
|
|
420
|
+
: inputAmountUi * token1PriceUsd;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// 判断是 买入 base 还是 卖出 base
|
|
424
|
+
const direction = resolveTradeDirection(poolInfo, true);
|
|
425
|
+
const baseToken = direction.baseToken;
|
|
426
|
+
const quoteToken = direction.quoteToken;
|
|
427
|
+
|
|
428
|
+
const inputIsQuoteToken = inputTokenAddress.toLowerCase() === quoteToken.address.toLowerCase();
|
|
429
|
+
const isBuy = inputIsQuoteToken;
|
|
430
|
+
|
|
431
|
+
const side = isBuy ? 'BUY' : 'SELL';
|
|
432
|
+
|
|
433
|
+
if (refPrice <= 0 || execPriceNum <= 0) return;
|
|
434
|
+
|
|
435
|
+
// sign 约定:(ref - exec) / ref —— "我们报价 vs 实际成交"
|
|
436
|
+
// "+" = 我们报价 > 实际成交(quote 偏高)
|
|
437
|
+
// "-" = 我们报价 < 实际成交(quote 偏低)
|
|
438
|
+
// 不论 BUY (ref=ask) / SELL (ref=bid),含义一致;business 决策看 |diff_bps|,sign 仅指方向
|
|
439
|
+
const diffBps = (refPrice - execPriceNum) / refPrice * 10000;
|
|
440
|
+
const absDiffBps = Math.abs(diffBps);
|
|
441
|
+
// 阈值统一:<5 ✅ / 5-10 ⚠️ / ≥10 ❌(前端颜色阈值同步)
|
|
442
|
+
const status = absDiffBps < 5 ? '✅' : absDiffBps < 10 ? '⚠️' : '❌';
|
|
443
|
+
const usdStr = swapUsd > 0 ? `$${swapUsd.toFixed(0)}` : '$?';
|
|
444
|
+
|
|
445
|
+
// USD 范围标注(不过滤,全部打印)
|
|
446
|
+
// 规则:仅排除"过大的 swap"(超过 2× 询价金额),偏差大可能是滑点而非报价不准;
|
|
447
|
+
// 小于询价金额的 swap 仍纳入 in_range(小单滑点小,价格偏差对询价精度评估有效)。
|
|
448
|
+
const maxUsd = cached.quoteAmountUsd * 2;
|
|
449
|
+
const inRange = swapUsd <= 0 || swapUsd <= maxUsd;
|
|
450
|
+
const rangeTag = inRange ? '' : ' [out]';
|
|
451
|
+
|
|
452
|
+
const tradeFlow = isBuy
|
|
453
|
+
? `${inputAmountUi.toFixed(4)} ${quoteToken.symbol} -> ${outputAmountUi.toFixed(4)} ${baseToken.symbol}`
|
|
454
|
+
: `${inputAmountUi.toFixed(4)} ${baseToken.symbol} -> ${outputAmountUi.toFixed(4)} ${quoteToken.symbol}`;
|
|
455
|
+
|
|
456
|
+
// 格式:quote[source blk:N] price vs swap[blk:M tx:hash] exec_price → diff
|
|
457
|
+
const quoteSrc = cached.source || '?';
|
|
458
|
+
const quoteTag = `quote[${quoteSrc} blk:${cached.referenceBlock}]`;
|
|
459
|
+
const swapBlk = swapBlockNumber || '?';
|
|
460
|
+
const swapTx = txHash ? ` ${txHash.slice(0, 10)}` : '';
|
|
461
|
+
const swapTag = `swap[blk:${swapBlk}${swapTx}]`;
|
|
462
|
+
const priceLabel = isBuy ? 'ask' : 'bid';
|
|
463
|
+
|
|
464
|
+
const msg = ` ↳ [Verify] ${side} ${usdStr} (${tradeFlow}) ${quoteTag} ${priceLabel}=${refPrice.toFixed(12)} vs ${swapTag} exec=${execPriceNum.toFixed(12)} diff=${diffBps > 0 ? '+' : ''}${diffBps.toFixed(1)}bps ${status}${rangeTag}`;
|
|
465
|
+
// 注:日志行不在此打印,返回给调用方(基类在 [QUOTE OK] 之后打,保证"先报价后验证"顺序);analyze 上报仍即时发。
|
|
466
|
+
|
|
467
|
+
// 上报 verify 结果到 analyze
|
|
468
|
+
try {
|
|
469
|
+
report_data_to_analyze('QuoteVerify', {
|
|
470
|
+
pool_address: poolAddress,
|
|
471
|
+
pool_name: poolName,
|
|
472
|
+
price_id: cached.priceId,
|
|
473
|
+
source: cached.source, // 当时实际推送的源(用于 analyze 标识 pushed 列)
|
|
474
|
+
quote_amount_usd: cached.quoteAmountUsd, // 询价金额(前端展示 in_range 阈值)
|
|
475
|
+
quote_block: cached.referenceBlock,
|
|
476
|
+
swap_block: swapBlockNumber,
|
|
477
|
+
tx_hash: txHash || '',
|
|
478
|
+
side,
|
|
479
|
+
ref_price: refPrice,
|
|
480
|
+
exec_price: execPriceNum,
|
|
481
|
+
diff_bps: parseFloat(diffBps.toFixed(1)),
|
|
482
|
+
swap_usd: parseFloat(swapUsd.toFixed(0)),
|
|
483
|
+
in_range: inRange,
|
|
484
|
+
input_amount: inputAmountUi,
|
|
485
|
+
output_amount: outputAmountUi,
|
|
486
|
+
input_symbol: isBuy ? quoteToken.symbol : baseToken.symbol,
|
|
487
|
+
output_symbol: isBuy ? baseToken.symbol : quoteToken.symbol,
|
|
488
|
+
status,
|
|
489
|
+
time: Date.now(),
|
|
490
|
+
// per-source 全量数据:前端"1 行 N 列"展示用
|
|
491
|
+
// 直接来自 per-source cache,无 TTL 限制(candidate 表 5s TTL,作为兜底)
|
|
492
|
+
sources: sources || undefined,
|
|
493
|
+
// tier 模式(W3):primary source 命中的档位信息(兼容 V1 单档/V2 多档)
|
|
494
|
+
...(primaryTierInfo && {
|
|
495
|
+
matched_tier_pct: primaryTierInfo.matched_pct,
|
|
496
|
+
matched_tier_mode: primaryTierInfo.mode,
|
|
497
|
+
matched_tier_amount_in: primaryTierInfo.tier_amount_in,
|
|
498
|
+
matched_tier_lo_pct: primaryTierInfo.lo_pct, // 命中区间下界标准档 pct(前端 size档位列显示区间)
|
|
499
|
+
matched_tier_hi_pct: primaryTierInfo.hi_pct, // 命中区间上界标准档 pct
|
|
500
|
+
}),
|
|
501
|
+
})
|
|
502
|
+
} catch (_) {
|
|
503
|
+
// 上报失败不影响主流程
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
return msg;
|
|
507
|
+
}
|
|
508
|
+
}
|
package/src/trade/index.ts
CHANGED
package/src/trade/tx_builder.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { CHAIN_ID, TradeContext } from "@clonegod/ttd-core/dist";
|
|
|
2
2
|
import { ComputeBudgetProgram, Connection, Keypair, LAMPORTS_PER_SOL, PublicKey, SystemProgram, Transaction, TransactionInstruction } from "@solana/web3.js";
|
|
3
3
|
import { LAMPORTS_PER_MICRO_LAMPORTS } from "../common";
|
|
4
4
|
import { SolanaBlockMetaUpdateEvent } from "../types";
|
|
5
|
-
import { SolanaTradeAppConfig } from "../
|
|
5
|
+
import { SolanaTradeAppConfig } from "../appconfig/SolanaTradeAppConfig";
|
|
6
6
|
import { HELIUS_TIP_ACCOUNTS } from "./send/helius";
|
|
7
7
|
import { getJitoTipAccount } from "./send/jito";
|
|
8
8
|
import { JitoTipWalletManager } from "./jito_tip_wallets";
|
package/src/types/index.ts
CHANGED
|
@@ -5,6 +5,9 @@ export enum SolanaPoolAccountType {
|
|
|
5
5
|
POOL='pool',
|
|
6
6
|
VAULT_A='vaultA',
|
|
7
7
|
VAULT_B='vaultB',
|
|
8
|
+
// 动态子账户(CLMM tickArray / DLMM binArray,方案 A):独立 push,不参与 AMM 三账户聚合
|
|
9
|
+
TICK_ARRAY='tickArray',
|
|
10
|
+
BIN_ARRAY='binArray',
|
|
8
11
|
}
|
|
9
12
|
|
|
10
13
|
export interface SolanaAccountUpdateEvent {
|
|
@@ -58,16 +61,31 @@ export interface SolanaPoolAccountUpdateEventData {
|
|
|
58
61
|
pool_name: string
|
|
59
62
|
data: {
|
|
60
63
|
slot: number
|
|
61
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Solana 账户写版本(Geyser write_version,全局单调递增,高版本覆盖低版本)。
|
|
66
|
+
* 询价基类用 (slot, write_version) 做单调水位线去重(AbstractDexQuote / SolPoolEvent)。
|
|
67
|
+
* AMM 聚合快照取 pool+vault 三账户里的 max(最近一次写)。
|
|
68
|
+
*/
|
|
69
|
+
write_version?: number
|
|
62
70
|
tx_hash: string
|
|
63
71
|
pool_account: string
|
|
64
72
|
pool_account_data: string
|
|
65
73
|
|
|
66
|
-
// AMM 池子的Vault
|
|
74
|
+
// AMM 池子的Vault
|
|
67
75
|
vaultA_account?: string
|
|
68
76
|
vaultA_account_data?: string
|
|
69
77
|
vaultB_account?: string
|
|
70
78
|
vaultB_account_data?: string
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 动态子账户更新(CLMM tickArray / DLMM binArray,方案 A)。独立 push:
|
|
82
|
+
* 该笔只带 sub_account(pool_account_data 为空),询价侧 refreshStateFromEvent 按 role 解码 → 更新本地分布。
|
|
83
|
+
*/
|
|
84
|
+
sub_account?: {
|
|
85
|
+
role: string // 'tickArray' | 'binArray'
|
|
86
|
+
account: string
|
|
87
|
+
account_data: string
|
|
88
|
+
}
|
|
71
89
|
}
|
|
72
90
|
}
|
|
73
91
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './trade_direction'
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 交易方向工具(与 BSC/SUI 同名文件对齐 —— 链无关,直接照搬 [[feedback_cross_chain_consistency]])
|
|
3
|
+
*
|
|
4
|
+
* 统一处理 isBuy + quote_token 的语义映射,与池子的 tokenA/tokenB 顺序无关。
|
|
5
|
+
*
|
|
6
|
+
* 概念:
|
|
7
|
+
* pair: "TOKEN/SOL" → 左边是基础币(base),右边是报价币(quote)
|
|
8
|
+
* quote_token: "SOL" → 用 SOL 报价
|
|
9
|
+
* isBuy:
|
|
10
|
+
* true = 买入基础币(花费报价币,获得基础币)→ ask 价格
|
|
11
|
+
* false = 卖出基础币(花费基础币,获得报价币)→ bid 价格
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export interface TradeDirectionResult<T = any> {
|
|
15
|
+
/** 是否是买入(报价币→基础币) */
|
|
16
|
+
isBuy: boolean;
|
|
17
|
+
/** 输入代币(保持传入的原始类型) */
|
|
18
|
+
inputToken: T;
|
|
19
|
+
/** 输出代币 */
|
|
20
|
+
outputToken: T;
|
|
21
|
+
/** 基础币(非报价币) */
|
|
22
|
+
baseToken: T;
|
|
23
|
+
/** 报价币 */
|
|
24
|
+
quoteToken: T;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 从 pool_info + isBuy 推导输入输出代币
|
|
29
|
+
* 泛型 T 保持 pool_info.tokenA/tokenB 的原始类型(如 StandardTokenInfoType)
|
|
30
|
+
*
|
|
31
|
+
* @param poolInfo 池信息(必须包含 tokenA, tokenB, quote_token)
|
|
32
|
+
* @param isBuy true=买入基础币, false=卖出基础币
|
|
33
|
+
*/
|
|
34
|
+
export function resolveTradeDirection<T = any>(poolInfo: { tokenA: T; tokenB: T; quote_token: string; [key: string]: any }, isBuy: boolean): TradeDirectionResult<T> {
|
|
35
|
+
const { tokenA, tokenB, quote_token } = poolInfo;
|
|
36
|
+
|
|
37
|
+
const quoteToken = [tokenA, tokenB].find(t => (t as any).symbol === quote_token);
|
|
38
|
+
const baseToken = [tokenA, tokenB].find(t => (t as any).symbol !== quote_token);
|
|
39
|
+
|
|
40
|
+
if (!quoteToken || !baseToken) {
|
|
41
|
+
throw new Error(`Cannot resolve tokens: tokenA=${(tokenA as any)?.symbol}, tokenB=${(tokenB as any)?.symbol}, quote_token=${quote_token}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// isBuy=true → 输入报价币,输出基础币
|
|
45
|
+
// isBuy=false → 输入基础币,输出报价币
|
|
46
|
+
const inputToken = isBuy ? quoteToken : baseToken;
|
|
47
|
+
const outputToken = isBuy ? baseToken : quoteToken;
|
|
48
|
+
|
|
49
|
+
return { isBuy, inputToken, outputToken, baseToken, quoteToken };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 计算标准化价格:1 基础币 = X 报价币
|
|
54
|
+
*
|
|
55
|
+
* @param inputAmount 输入数量(UI 单位)
|
|
56
|
+
* @param outputAmount 输出数量(UI 单位)
|
|
57
|
+
* @param isBuy 是否是买入
|
|
58
|
+
* @returns 价格字符串(1 基础币 = X 报价币)
|
|
59
|
+
*/
|
|
60
|
+
export function calculateStandardPrice(inputAmount: number, outputAmount: number, isBuy: boolean): string {
|
|
61
|
+
if (isBuy) {
|
|
62
|
+
// 买入:输入报价币,输出基础币 → 价格 = inputAmount / outputAmount
|
|
63
|
+
return (inputAmount / outputAmount).toFixed(18);
|
|
64
|
+
} else {
|
|
65
|
+
// 卖出:输入基础币,输出报价币 → 价格 = outputAmount / inputAmount
|
|
66
|
+
return (outputAmount / inputAmount).toFixed(18);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { AbstractTradeAppConfig } from "@clonegod/ttd-core/dist";
|
|
2
|
-
import { Connection, Keypair } from "@solana/web3.js";
|
|
3
|
-
export declare class SolanaTradeAppConfig extends AbstractTradeAppConfig {
|
|
4
|
-
connection: Connection;
|
|
5
|
-
keypair: Keypair;
|
|
6
|
-
private ws_client;
|
|
7
|
-
constructor();
|
|
8
|
-
init(): Promise<void>;
|
|
9
|
-
subscribe_wallet_raw_txn_event(): void;
|
|
10
|
-
}
|
package/dist/config/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './SolanaTradeAppConfig';
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { AbstractTradeAppConfig } from "@clonegod/ttd-core/dist";
|
|
2
|
-
import { Connection, Keypair } from "@solana/web3.js";
|
|
3
|
-
export declare class SolanaTradeAppConfig extends AbstractTradeAppConfig {
|
|
4
|
-
connection: Connection;
|
|
5
|
-
keypair: Keypair;
|
|
6
|
-
constructor();
|
|
7
|
-
subscribe_wallet_raw_txn_event(): void;
|
|
8
|
-
}
|