@clonegod/ttd-sol-common 2.0.78 → 2.0.79

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.
@@ -50,7 +50,8 @@ export declare abstract class AbstractDexQuote<C extends SolanaChainOps = Solana
50
50
  protected calculateDepth(_poolInfo: StandardPoolInfoType, _poolAddress: string, _priceMap: Map<string, {
51
51
  price: string;
52
52
  } | undefined>): Promise<QuoteDepthOutput | undefined>;
53
- protected readonly feeInVault: boolean;
53
+ protected getEffectiveFeeBps(poolInfo: StandardPoolInfoType): number;
54
+ protected getOutOfVaultFeeBps(_poolInfo: StandardPoolInfoType): number;
54
55
  protected isStateConsistentForQuote(_poolInfo: StandardPoolInfoType): boolean;
55
56
  protected accountSlots: Map<string, number>;
56
57
  protected recordAccountSlot(accountAddr: string, slot: number): void;
@@ -15,13 +15,14 @@ class AbstractDexQuote {
15
15
  this.latestBlockSlot = 0;
16
16
  this.MIN_QUOTE_INTERVAL_MS = Math.max(3000, parseInt(process.env.MIN_QUOTE_INTERVAL_MS || '10000', 10) || 10000);
17
17
  this.consistencyPolicy = 'snapshot';
18
- this.feeInVault = true;
19
18
  this.accountSlots = new Map();
20
19
  this.appConfig = appConfig;
21
20
  this.chain = chain;
22
21
  }
23
22
  async refreshStateFromEvent(_poolInfo, _evt) { }
24
23
  async calculateDepth(_poolInfo, _poolAddress, _priceMap) { return undefined; }
24
+ getEffectiveFeeBps(poolInfo) { return Number(poolInfo.fee_rate) || 0; }
25
+ getOutOfVaultFeeBps(_poolInfo) { return 0; }
25
26
  isStateConsistentForQuote(_poolInfo) { return true; }
26
27
  recordAccountSlot(accountAddr, slot) {
27
28
  this.accountSlots.set(accountAddr, slot);
@@ -72,8 +73,7 @@ class AbstractDexQuote {
72
73
  token1Decimals: Number(poolInfo.tokenB?.decimals ?? 0),
73
74
  poolInfo: { tokenA: poolInfo.tokenA, tokenB: poolInfo.tokenB, quote_token: poolInfo.quote_token },
74
75
  swapperDeltaConvention: false,
75
- feeExclusiveExec: !this.feeInVault,
76
- feeRateBps: poolInfo.fee_rate,
76
+ outOfVaultFeeBps: this.getOutOfVaultFeeBps(poolInfo),
77
77
  };
78
78
  const verifyLog = this.quotePriceVerify.checkSwap(params);
79
79
  if (verifyLog)
@@ -16,8 +16,7 @@ export interface CheckSwapParams {
16
16
  quote_token: string;
17
17
  };
18
18
  swapperDeltaConvention?: boolean;
19
- feeExclusiveExec?: boolean;
20
- feeRateBps?: number;
19
+ outOfVaultFeeBps?: number;
21
20
  }
22
21
  export declare class QuotePriceVerify {
23
22
  private quoteCache;
@@ -114,9 +114,6 @@ class QuotePriceVerify {
114
114
  if (!match)
115
115
  continue;
116
116
  refPrice = match.tier.price;
117
- if (params.feeExclusiveExec && match.tier.amount_in > 0) {
118
- refPrice = stripFeeExclusive(refPrice, isBuy, match.tier.fee / match.tier.amount_in);
119
- }
120
117
  tierInfo = {
121
118
  matched_pct: parseFloat(match.tier.pct.toFixed(4)),
122
119
  mode: match.mode,
@@ -127,9 +124,9 @@ class QuotePriceVerify {
127
124
  }
128
125
  else {
129
126
  refPrice = isBuy ? cached.askPrice : cached.bidPrice;
130
- if (params.feeExclusiveExec && params.feeRateBps && params.feeRateBps > 0) {
131
- refPrice = stripFeeExclusive(refPrice, isBuy, params.feeRateBps / 10000);
132
- }
127
+ }
128
+ if (params.outOfVaultFeeBps && params.outOfVaultFeeBps > 0) {
129
+ refPrice = stripFeeExclusive(refPrice, isBuy, params.outOfVaultFeeBps / 10000);
133
130
  }
134
131
  if (refPrice <= 0)
135
132
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clonegod/ttd-sol-common",
3
- "version": "2.0.78",
3
+ "version": "2.0.79",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -17,7 +17,7 @@ import { QuoteTrace } from './quote_trace'
17
17
  // · 无 v3 PriceFeed(同 SUI,省 do_quote_v3)。
18
18
  // · verify(报价偏差)走**独立的 swap-verify 通道**(非账户快照):stream-quote 从真实交易的
19
19
  // pre/postTokenBalances 解码订阅池子 vault Δ(DEX 无关)→ WS push → handleSwapVerify → checkSwap。
20
- // exec_price = |Δquote/Δbase|(pool-side vault Δ),fee-exclusive 对账(费路由出池的 DEX 由 feeInVault=false 触发剥费)。
20
+ // exec_price = |Δquote/Δbase|(pool-side vault Δ),fee-exclusive 对账(费路由出池的部分由 getOutOfVaultFeeBps 提供,方向感知剥掉)。
21
21
 
22
22
  /**
23
23
  * 流动性一致性策略(设计文档 §2.2 定稿:3 订阅原型)。各 DEX 子类按协议特性声明:
@@ -114,12 +114,17 @@ export abstract class AbstractDexQuote<C extends SolanaChainOps = SolanaChainOps
114
114
  /** 深度(tier 化);fee 来自链上 load。priceMap 由基类预取。默认 undefined(step 3 各 DEX 逐档实现)。 */
115
115
  protected async calculateDepth(_poolInfo: StandardPoolInfoType, _poolAddress: string, _priceMap: Map<string, { price: string } | undefined>): Promise<QuoteDepthOutput | undefined> { return undefined }
116
116
  /**
117
- * verify 口径:池子手续费是否留在 vault 内。
118
- * - true(默认;Raydium/Orca/Meteora 等):fee 累进储备vault Δ 含费 → 与含费报价同口径,不剥费。
119
- * - false(pumpswap:protocol/creator fee 路由出池到独立账户):vault Δ 不含费 → checkSwap 把参考价剥成 fee-exclusive。
120
- * 详见 quote_price_verify CheckSwapParams.feeExclusiveExec。
117
+ * depth gross-up 用的**总费率**(bps)。默认配置 fee_rate;动态费 DEX(pumpswap 市值档位 / DLMM 波动费)覆盖,
118
+ * 每事件按真实链上 feeConfig 重算报价自适应费率变化。
121
119
  */
122
- protected readonly feeInVault: boolean = true
120
+ protected getEffectiveFeeBps(poolInfo: StandardPoolInfoType): number { return Number(poolInfo.fee_rate) || 0 }
121
+
122
+ /**
123
+ * verify 剥费用的**出池费率**(bps)= 路由出 vault 的费(vault Δ exec 不含的部分)。
124
+ * - 0(默认):费全留池(Raydium/Orca/Meteora 等)→ vault Δ 与含费报价同口径,不剥。
125
+ * - >0:pumpswap(protocol+creator,动态按市值档位)等覆盖。详见 CheckSwapParams.outOfVaultFeeBps。
126
+ */
127
+ protected getOutOfVaultFeeBps(_poolInfo: StandardPoolInfoType): number { return 0 }
123
128
 
124
129
  /**
125
130
  * slot 门控钩子(仅 consistencyPolicy='slot-gate' 生效):判断本池"走单触碰的多账户"状态是否 slot 一致。
@@ -181,7 +186,7 @@ export abstract class AbstractDexQuote<C extends SolanaChainOps = SolanaChainOps
181
186
 
182
187
  /**
183
188
  * 消费 stream-quote 推来的 swap-verify(pool-side vault Δ)→ checkSwap 对账缓存报价 → 打 [Verify]。
184
- * exec_price 由 |Δquote/Δbase| 算;fee-exclusive 口径由 feeInVault 决定(详见 CheckSwapParams.feeExclusiveExec)。
189
+ * exec_price 由 |Δquote/Δbase| 算;剥费用 getOutOfVaultFeeBps(详见 CheckSwapParams.outOfVaultFeeBps)。
185
190
  */
186
191
  private handleSwapVerify(sv: SolanaSwapVerifyEventData): void {
187
192
  const poolInfo = this.poolInfoMap.get(sv.pool_address)
@@ -198,9 +203,8 @@ export abstract class AbstractDexQuote<C extends SolanaChainOps = SolanaChainOps
198
203
  token0Decimals: Number(poolInfo.tokenA?.decimals ?? 0),
199
204
  token1Decimals: Number(poolInfo.tokenB?.decimals ?? 0),
200
205
  poolInfo: { tokenA: poolInfo.tokenA, tokenB: poolInfo.tokenB, quote_token: poolInfo.quote_token },
201
- swapperDeltaConvention: false, // amount0/1 是 pool-side vault Δ
202
- feeExclusiveExec: !this.feeInVault, // 费路由出池(pumpswap)→ 剥参考价的费
203
- feeRateBps: poolInfo.fee_rate, // market-data 强制整数 bps
206
+ swapperDeltaConvention: false, // amount0/1 是 pool-side vault Δ
207
+ outOfVaultFeeBps: this.getOutOfVaultFeeBps(poolInfo), // 出池费(默认 0=全留池不剥;pumpswap 动态)
204
208
  }
205
209
  const verifyLog = this.quotePriceVerify.checkSwap(params)
206
210
  if (verifyLog) log_info(verifyLog)
@@ -120,12 +120,13 @@ function pickMatchingTier(tiers: QuoteTier[], actualAmountIn: number): TierMatch
120
120
  }
121
121
 
122
122
  /**
123
- * 把含费报价价剥成 fee-exclusive(曲线价),与"费路由出池"DEX(pumpswap)的 vault Δ exec 同口径。
123
+ * 把含费报价剥成 fee-exclusive(与 vault Δ exec 同口径),r = **出池费率**(路由出 vault 的部分)。
124
124
  *
125
- * 关键:费总是把 ask/bid **向两侧各撑开** r(曲线价 M 在中间),方向**不对称**:
126
- * - ask(买基础币) = M / (1 − r) → ask_excl = ask × (1 − r)
127
- * - bid(卖基础币) = M × (1 − r) → bid_excl = bid / (1 − r) ← 是除,不是乘!
128
- * 不论费记输入侧还是输出侧(pumpswap 实测两种都有),上式都成立(逐项核对见 session memory)。
125
+ * vault Δ exec 不含"出池费"(如 pumpswap 的 protocol+creator,转给独立账户),但**含**留池的 lp 费。
126
+ * 报价(ask/bid)是 swapper 含全费的成交价。两者差一个"出池费",且方向**不对称**:
127
+ * - ask(买基础币) = exec / (1 − r) → ask_excl = ask × (1 − r)
128
+ * - bid(卖基础币) = exec × (1 − r) → bid_excl = bid / (1 − r) ← 是除,不是乘!
129
+ * 不论出池费记输入侧还是输出侧,上式一阶成立(lp 留池的二阶项可忽略)。
129
130
  * ask/bid 同为 quote/base 口径(calculateStandardPrice),仅方向决定撑开符号。
130
131
  */
131
132
  function stripFeeExclusive(refPrice: number, isBuy: boolean, r: number): number {
@@ -157,18 +158,14 @@ export interface CheckSwapParams {
157
158
  */
158
159
  swapperDeltaConvention?: boolean;
159
160
  /**
160
- * fee-exclusive 对账口径(Solana pool-side vault Δ 用)。
161
+ * **出池费率**(bps)= 路由出 vault 的费(pumpswap=protocol+creator,**动态**按市值档位)。
161
162
  *
162
- * 背景:exec_price 由 **pool vault Δ** 算(Δquote/Δbase)。vault Δ 是否含费**因 DEX 而异**:
163
- * - 费留池内(Raydium/Orca/Meteora 等):vault Δ 含费 与"含费 tier 价"同口径 → 不剥(false,默认)。
164
- * - 费路由出池(pumpswap protocol/creator fee 进独立账户):vault Δ 不含费须把参考价剥成 fee-exclusive
165
- * 才与 vault Δ 同口径(否则系统性偏差≈费率)。
166
- * 为 true 时:refPrice ×= (amount_in − fee)/amount_in(tier 模式,用该档自身的含费/费额);
167
- * scalar 兜底用 (1 − feeRateBps/1e4) 一阶近似。
163
+ * exec_price 由 pool vault Δ 算,**不含出池费、含留池 lp 费**。报价含全费 两者差出池费。
164
+ * verify 把报价按出池费剥成 vault 口径(方向感知,见 stripFeeExclusive)。
165
+ * - 0 / 缺省:费全留池(Raydium/Orca/Meteora 等),vault Δ 与含费报价同口径不剥。
166
+ * - >0:pumpswap 等,DEX 子类经 getOutOfVaultFeeBps 动态提供(每事件按真实 feeConfig 算)。
168
167
  */
169
- feeExclusiveExec?: boolean;
170
- /** 池子费率(bps);feeExclusiveExec 且 scalar 兜底(无 tier)时用作一阶剥费因子。 */
171
- feeRateBps?: number;
168
+ outOfVaultFeeBps?: number;
172
169
  }
173
170
 
174
171
  // ========== 主类 ==========
@@ -303,11 +300,6 @@ export class QuotePriceVerify {
303
300
  const match = pickMatchingTier(tiers, swapData.inputAmountUi);
304
301
  if (!match) continue;
305
302
  refPrice = match.tier.price;
306
- // fee-exclusive 对账:把含费 tier 价剥成 fee-exclusive,与 fee-out-of-vault DEX(pumpswap)的 vault Δ 同口径。
307
- // r = 该档费率(fee/amount_in,amount_in 含费)。
308
- if (params.feeExclusiveExec && match.tier.amount_in > 0) {
309
- refPrice = stripFeeExclusive(refPrice, isBuy, match.tier.fee / match.tier.amount_in);
310
- }
311
303
  tierInfo = {
312
304
  matched_pct: parseFloat(match.tier.pct.toFixed(4)),
313
305
  mode: match.mode,
@@ -318,10 +310,12 @@ export class QuotePriceVerify {
318
310
  } else {
319
311
  // scalar fallback:旧 DEX 包未传 tiers 时使用
320
312
  refPrice = isBuy ? cached.askPrice : cached.bidPrice;
321
- // scalar 无 tier 费额 → 用池子费率一阶剥费(feeExclusiveExec 时)
322
- if (params.feeExclusiveExec && params.feeRateBps && params.feeRateBps > 0) {
323
- refPrice = stripFeeExclusive(refPrice, isBuy, params.feeRateBps / 10000);
324
- }
313
+ }
314
+
315
+ // fee-exclusive 对账:剥掉"出池费"(vault Δ exec 不含的部分)→ 报价与 vault Δ 同口径。
316
+ // tier / scalar 统一处理:出池费是定比(与档位/大小无关),方向感知(买×卖÷)。
317
+ if (params.outOfVaultFeeBps && params.outOfVaultFeeBps > 0) {
318
+ refPrice = stripFeeExclusive(refPrice, isBuy, params.outOfVaultFeeBps / 10000);
325
319
  }
326
320
 
327
321
  if (refPrice <= 0) continue;