@clonegod/ttd-sol-common 2.0.48 → 2.0.50

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.
@@ -1,7 +1,7 @@
1
1
  import { Transaction, VersionedTransaction } from '@solana/web3.js';
2
+ import { TradeContext } from '@clonegod/ttd-core/dist';
2
3
  export declare class TransactionSender {
3
4
  constructor();
4
- sendTransaction(singedTransaction: Transaction | VersionedTransaction, swqos_only?: boolean): Promise<string>;
5
- sendBundle(mainTx: Transaction | VersionedTransaction, tipTx: Transaction | VersionedTransaction, use_multi_ips?: boolean): Promise<string>;
5
+ sendTransaction(context: TradeContext, mainTx: Transaction | VersionedTransaction, swqos_only?: boolean): Promise<string>;
6
+ sendBundle(context: TradeContext, mainTx: Transaction | VersionedTransaction, tipTx: Transaction | VersionedTransaction, use_multi_ips?: boolean): Promise<string>;
6
7
  }
7
- export declare function serializeTransactionBase64(tx: Transaction | VersionedTransaction): string;
@@ -1,23 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TransactionSender = void 0;
4
- exports.serializeTransactionBase64 = serializeTransactionBase64;
5
4
  const web3_js_1 = require("@solana/web3.js");
6
5
  const helius_1 = require("./helius");
7
6
  const jito_1 = require("./jito");
8
7
  const common_1 = require("../../common");
8
+ const sol_gas_cache_1 = require("../sol_gas_cache");
9
9
  class TransactionSender {
10
10
  constructor() {
11
11
  }
12
- async sendTransaction(singedTransaction, swqos_only = true) {
13
- const singedTxBase64 = serializeTransactionBase64(singedTransaction);
12
+ async sendTransaction(context, mainTx, swqos_only = true) {
13
+ const mainTxHash = (0, common_1.getSignature)(mainTx);
14
+ set_transaciton_gas_cost(mainTxHash, context);
15
+ const singedTxBase64 = serializeTransactionBase64(mainTx);
14
16
  return await (0, helius_1.sendTxWithHelius)(singedTxBase64, swqos_only);
15
17
  }
16
- async sendBundle(mainTx, tipTx, use_multi_ips = false) {
18
+ async sendBundle(context, mainTx, tipTx, use_multi_ips = false) {
19
+ const mainTxHash = (0, common_1.getSignature)(mainTx);
20
+ set_transaciton_gas_cost(mainTxHash, context);
17
21
  const mainTxBase64 = serializeTransactionBase64(mainTx);
18
22
  const tipTxBase64 = serializeTransactionBase64(tipTx);
19
23
  if (use_multi_ips) {
20
- const mainTxHash = (0, common_1.getSignature)(mainTx);
21
24
  return await (0, jito_1.sendBundleWithJitoMultiIps)(mainTxHash, mainTxBase64, tipTxBase64);
22
25
  }
23
26
  else {
@@ -36,3 +39,7 @@ function serializeTransactionBase64(tx) {
36
39
  }
37
40
  return buffer.toString('base64');
38
41
  }
42
+ function set_transaciton_gas_cost(txid, context) {
43
+ const { sol_priority_fee, sol_tip_fee } = context.trade_runtime.settings.strategy;
44
+ (0, sol_gas_cache_1.set_sol_gas_fee)(txid, { base_fee: 5000, priority_fee: sol_priority_fee + sol_tip_fee, total_fee: 5000 + sol_priority_fee + sol_tip_fee });
45
+ }
@@ -13,6 +13,7 @@ export declare class SolTransactionBuilder {
13
13
  init(): Promise<void>;
14
14
  private handleBlockUpdateEvent;
15
15
  private getPreInstructions;
16
+ private createComputeUnitLimitWithJitoDontFront;
16
17
  private getPostInstructions;
17
18
  buildTransactionForSwap(context: TradeContext, swapInstructions: TransactionInstruction[]): Transaction;
18
19
  buildTransactionForTipJito(context: TradeContext): Transaction;
@@ -7,6 +7,46 @@ const common_1 = require("../common");
7
7
  const helius_1 = require("./send/helius");
8
8
  const jito_1 = require("./send/jito");
9
9
  const jito_tip_wallets_1 = require("./jito_tip_wallets");
10
+ const jitodontfrontAccounts = [
11
+ 'jitodontfront111111111111111111111111111111',
12
+ 'jitodontfront111111111111111111111111111123',
13
+ 'jitodontfront111111111111234565432123456782',
14
+ 'jitodontfront111111111111111111111111111118',
15
+ 'jitodontfront111111111111111111111111111183',
16
+ 'jitodontfront11111111111111111111111111111o',
17
+ 'jitodontfront1111111111111111111111111111of',
18
+ 'jitodontfront111111111111111111111111111116',
19
+ 'jitodontfront11111111111111111111111111116b',
20
+ 'jitodontfront11111111111111111111111111111a',
21
+ 'jitodontfront1111111111111111111111111111aJ',
22
+ 'jitodontfront11111111111111111111111111111J',
23
+ 'jitodontfront1111111111111111111111111111JR',
24
+ 'jitodontfront11111111111111111111111111111U',
25
+ 'jitodontfront1111111111111111111111111111UU',
26
+ 'jitodontfront11111111111111111111111111111g',
27
+ 'jitodontfront1111111111111111111111111111gt',
28
+ 'jitodontfront11111111111111111111111111111F',
29
+ 'jitodontfront1111111111111111111111111111Ff',
30
+ 'jitodontfront11111111111111111111111111111v',
31
+ 'jitodontfront1111111111111111111111111111vw',
32
+ 'jitodontfront1111111111111111111111111111UA',
33
+ 'jitodontfront111111111111111111111111111bcv',
34
+ 'jitodontfront111111111111111111111111111Svm',
35
+ 'jitodontfront111111111111111111111111111Zj2',
36
+ 'jitodontfront111111111111111111111111111wja',
37
+ 'jitodontfront111111111111111111111111111r3j',
38
+ 'jitodontfront111111111111111111111111111LAG',
39
+ 'jitodontfront111111111111111111111111111iej',
40
+ 'jitodontfront111111111111111111111111111mxb',
41
+ 'jitodontfront111111111111111111111111111m9D',
42
+ 'jitodontfront111111111111111111111111111rjQ',
43
+ 'jitodontfront1111111111111111111111111111wP',
44
+ 'jitodontfront111111111111234565432123456RX5',
45
+ ];
46
+ function getJitoDontFrontAccount() {
47
+ const randomIndex = Math.floor(Math.random() * jitodontfrontAccounts.length);
48
+ return new web3_js_1.PublicKey(jitodontfrontAccounts[randomIndex]);
49
+ }
10
50
  class SolTransactionBuilder {
11
51
  constructor(appConfig) {
12
52
  this.appConfig = appConfig;
@@ -28,14 +68,25 @@ class SolTransactionBuilder {
28
68
  const cu_limit = context.pool_info.cu_limit || 600000;
29
69
  const sol_priority_fee_lamports = context.trade_runtime.settings.strategy.sol_priority_fee;
30
70
  const priorityFeeMicroLamports = Math.floor((sol_priority_fee_lamports * common_1.LAMPORTS_PER_MICRO_LAMPORTS) / cu_limit);
31
- const computeBudgetInstruction = web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({
32
- units: cu_limit,
33
- });
71
+ const computeBudgetInstruction = this.createComputeUnitLimitWithJitoDontFront(cu_limit);
34
72
  const priorityFeeInstruction = web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({
35
73
  microLamports: priorityFeeMicroLamports,
36
74
  });
37
75
  return [computeBudgetInstruction, priorityFeeInstruction];
38
76
  }
77
+ createComputeUnitLimitWithJitoDontFront(units) {
78
+ const jitoDontFrontAccount = getJitoDontFrontAccount();
79
+ const data = Buffer.alloc(5);
80
+ data.writeUInt8(2, 0);
81
+ data.writeUInt32LE(units, 1);
82
+ return new web3_js_1.TransactionInstruction({
83
+ keys: [
84
+ { pubkey: jitoDontFrontAccount, isSigner: false, isWritable: false },
85
+ ],
86
+ programId: web3_js_1.ComputeBudgetProgram.programId,
87
+ data: data,
88
+ });
89
+ }
39
90
  getPostInstructions(context) {
40
91
  return [];
41
92
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clonegod/ttd-sol-common",
3
- "version": "2.0.48",
3
+ "version": "2.0.50",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "types/index.d.ts",
@@ -2,6 +2,8 @@ import { Transaction, VersionedTransaction } from '@solana/web3.js';
2
2
  import { sendTxWithHelius } from './helius';
3
3
  import { sendBundleWithJito, sendBundleWithJitoMultiIps } from './jito';
4
4
  import { getSignature } from '../../common';
5
+ import { TradeContext } from '@clonegod/ttd-core/dist';
6
+ import { set_sol_gas_fee } from '../sol_gas_cache';
5
7
 
6
8
 
7
9
  /**
@@ -17,12 +19,15 @@ export class TransactionSender {
17
19
  * - swqos_only=true, 需给 helius 发送至少5000lamports的通道使用费
18
20
  * - swqos_only=false, 需给helius 发送至少0.0002SOL的小费,会同时向jito发送交易
19
21
  *
20
- * @param singedTransaction 签名后的交易
22
+ * @param mainTx 签名后的交易
21
23
  * @param swqos_only true:swqos ;false: swqos + jito
22
24
  * @returns txhash
23
25
  */
24
- async sendTransaction(singedTransaction: Transaction | VersionedTransaction, swqos_only:boolean=true): Promise<string> {
25
- const singedTxBase64 = serializeTransactionBase64(singedTransaction)
26
+ async sendTransaction(context:TradeContext, mainTx: Transaction | VersionedTransaction, swqos_only:boolean=true): Promise<string> {
27
+ const mainTxHash = getSignature(mainTx)
28
+ set_transaciton_gas_cost(mainTxHash, context)
29
+
30
+ const singedTxBase64 = serializeTransactionBase64(mainTx)
26
31
  return await sendTxWithHelius(singedTxBase64, swqos_only)
27
32
  }
28
33
 
@@ -36,11 +41,14 @@ export class TransactionSender {
36
41
  * @param use_multi_ips true:多节点;false: 单节点
37
42
  * @returns bundleId
38
43
  */
39
- async sendBundle(mainTx: Transaction | VersionedTransaction, tipTx: Transaction | VersionedTransaction, use_multi_ips: boolean=false): Promise<string> {
44
+ async sendBundle(context:TradeContext, mainTx: Transaction | VersionedTransaction, tipTx: Transaction | VersionedTransaction, use_multi_ips: boolean=false): Promise<string> {
45
+ const mainTxHash = getSignature(mainTx)
46
+ set_transaciton_gas_cost(mainTxHash, context)
47
+
40
48
  const mainTxBase64 = serializeTransactionBase64(mainTx)
41
49
  const tipTxBase64 = serializeTransactionBase64(tipTx)
50
+
42
51
  if(use_multi_ips) {
43
- const mainTxHash = getSignature(mainTx)
44
52
  return await sendBundleWithJitoMultiIps(mainTxHash, mainTxBase64, tipTxBase64)
45
53
  } else {
46
54
  return await sendBundleWithJito(mainTxBase64, tipTxBase64)
@@ -53,7 +61,7 @@ export class TransactionSender {
53
61
  /**
54
62
  * 序列化交易为 base64 字符串
55
63
  */
56
- export function serializeTransactionBase64(tx: Transaction | VersionedTransaction): string {
64
+ function serializeTransactionBase64(tx: Transaction | VersionedTransaction): string {
57
65
  let buffer:Buffer
58
66
  if (tx instanceof VersionedTransaction) {
59
67
  buffer = Buffer.from(tx.serialize())
@@ -64,3 +72,11 @@ export function serializeTransactionBase64(tx: Transaction | VersionedTransactio
64
72
  }
65
73
  return buffer.toString('base64')
66
74
  }
75
+
76
+
77
+ // 缓存交易的gas支出
78
+ function set_transaciton_gas_cost(txid:string, context:TradeContext) {
79
+ const {sol_priority_fee, sol_tip_fee} = context.trade_runtime.settings.strategy
80
+ set_sol_gas_fee(txid, {base_fee: 5000, priority_fee: sol_priority_fee + sol_tip_fee, total_fee: 5000 + sol_priority_fee + sol_tip_fee})
81
+ }
82
+
@@ -7,6 +7,61 @@ import { HELIUS_TIP_ACCOUNTS } from "./send/helius";
7
7
  import { getJitoTipAccount } from "./send/jito";
8
8
  import { JitoTipWalletManager } from "./jito_tip_wallets";
9
9
 
10
+ /**
11
+ * Jito Sandwich Mitigation: jitodontfront 账户
12
+ * 用于防止三明治攻击,包含此账户的交易必须在 bundle 的第一个位置(索引 0)
13
+ * 参考: https://docs.jito.wtf/lowlatencytxnsend/#sandwich-mitigation
14
+ *
15
+ * 重要说明:
16
+ * 1. 账户不需要在链上存在,但必须是有效的 pubkey
17
+ * 2. 账户应标记为只读(isWritable: false)以优化落地速度
18
+ * 3. (可选)可以使用唯一的 jitodontfront 变体用于应用程序
19
+ *
20
+ * 可以通过环境变量 JITO_DONT_FRONT_ACCOUNT 自定义账户,否则从默认列表中随机选择
21
+ */
22
+
23
+ const jitodontfrontAccounts = [
24
+ 'jitodontfront111111111111111111111111111111',
25
+ 'jitodontfront111111111111111111111111111123',
26
+ 'jitodontfront111111111111234565432123456782',
27
+ 'jitodontfront111111111111111111111111111118',
28
+ 'jitodontfront111111111111111111111111111183',
29
+ 'jitodontfront11111111111111111111111111111o',
30
+ 'jitodontfront1111111111111111111111111111of',
31
+ 'jitodontfront111111111111111111111111111116',
32
+ 'jitodontfront11111111111111111111111111116b',
33
+ 'jitodontfront11111111111111111111111111111a',
34
+ 'jitodontfront1111111111111111111111111111aJ',
35
+ 'jitodontfront11111111111111111111111111111J',
36
+ 'jitodontfront1111111111111111111111111111JR',
37
+ 'jitodontfront11111111111111111111111111111U',
38
+ 'jitodontfront1111111111111111111111111111UU',
39
+ 'jitodontfront11111111111111111111111111111g',
40
+ 'jitodontfront1111111111111111111111111111gt',
41
+ 'jitodontfront11111111111111111111111111111F',
42
+ 'jitodontfront1111111111111111111111111111Ff',
43
+ 'jitodontfront11111111111111111111111111111v',
44
+ 'jitodontfront1111111111111111111111111111vw',
45
+ 'jitodontfront1111111111111111111111111111UA',
46
+ 'jitodontfront111111111111111111111111111bcv',
47
+ 'jitodontfront111111111111111111111111111Svm',
48
+ 'jitodontfront111111111111111111111111111Zj2',
49
+ 'jitodontfront111111111111111111111111111wja',
50
+ 'jitodontfront111111111111111111111111111r3j',
51
+ 'jitodontfront111111111111111111111111111LAG',
52
+ 'jitodontfront111111111111111111111111111iej',
53
+ 'jitodontfront111111111111111111111111111mxb',
54
+ 'jitodontfront111111111111111111111111111m9D',
55
+ 'jitodontfront111111111111111111111111111rjQ',
56
+ 'jitodontfront1111111111111111111111111111wP',
57
+ 'jitodontfront111111111111234565432123456RX5',
58
+ ]
59
+
60
+ function getJitoDontFrontAccount(): PublicKey {
61
+ const randomIndex = Math.floor(Math.random() * jitodontfrontAccounts.length);
62
+ return new PublicKey(jitodontfrontAccounts[randomIndex]);
63
+ }
64
+
10
65
 
11
66
  export class SolTransactionBuilder {
12
67
  appConfig: SolanaTradeAppConfig
@@ -47,10 +102,8 @@ export class SolTransactionBuilder {
47
102
  const priorityFeeMicroLamports = Math.floor((sol_priority_fee_lamports * LAMPORTS_PER_MICRO_LAMPORTS) / cu_limit)
48
103
  // console.log('getPreInstructions', { cu_limit, sol_priority_fee_lamports, priorityFeeMicroLamports, })
49
104
 
50
- // set compute budget instruction
51
- const computeBudgetInstruction = ComputeBudgetProgram.setComputeUnitLimit({
52
- units: cu_limit,
53
- })
105
+ // set compute budget instruction with jitodontfront
106
+ const computeBudgetInstruction = this.createComputeUnitLimitWithJitoDontFront(cu_limit)
54
107
 
55
108
  // set priority fee instruction
56
109
  const priorityFeeInstruction = ComputeBudgetProgram.setComputeUnitPrice({
@@ -60,7 +113,37 @@ export class SolTransactionBuilder {
60
113
  return [computeBudgetInstruction, priorityFeeInstruction]
61
114
  }
62
115
 
63
-
116
+ /**
117
+ * 创建带有 jitodontfront 账户的 SetComputeUnitLimit 指令
118
+ * 将 jitodontfront 账户直接添加到 Compute Budget 指令中,用于防止三明治攻击
119
+ * 参考:
120
+ * - https://docs.jito.wtf/lowlatencytxnsend/#sandwich-mitigation
121
+ *
122
+ * 重要说明:
123
+ * 1. 包含 jitodontfront 账户的交易必须在 bundle 的第一个位置(索引 0)
124
+ * 2. 账户标记为只读(isWritable: false)以优化落地速度
125
+ * 3. 账户不需要在链上存在,但必须是有效的 pubkey
126
+ * 4. (可选)可以通过环境变量 JITO_DONT_FRONT_ACCOUNT 使用唯一的变体
127
+ */
128
+ private createComputeUnitLimitWithJitoDontFront(units: number): TransactionInstruction {
129
+ const jitoDontFrontAccount = getJitoDontFrontAccount();
130
+
131
+ // 构造 Compute Budget SetComputeUnitLimit 指令数据
132
+ // 1 字节鉴别符(2 表示 SetComputeUnitLimit)+ 4 字节 units(小端序)
133
+ const data = Buffer.alloc(5);
134
+ data.writeUInt8(2, 0); // 鉴别符:2 表示 SetComputeUnitLimit
135
+ data.writeUInt32LE(units, 1); // 设置计算单元(小端序)
136
+
137
+ return new TransactionInstruction({
138
+ keys: [
139
+ { pubkey: jitoDontFrontAccount, isSigner: false, isWritable: false }, // jitodontfront 账户,标记为只读
140
+ ],
141
+ programId: ComputeBudgetProgram.programId, // Compute Budget 程序 ID
142
+ data: data,
143
+ });
144
+ }
145
+
146
+
64
147
  // post-instructions
65
148
  private getPostInstructions(context: TradeContext): TransactionInstruction[] {
66
149
  return []
@@ -87,21 +170,21 @@ export class SolTransactionBuilder {
87
170
  swapTx.lastValidBlockHeight = this.recentBlockheight + max_block_offset
88
171
  swapTx.feePayer = this.keypair.publicKey
89
172
 
90
- const {sol_bundle_only, sol_tip_fee} = context.trade_runtime.settings.strategy
173
+ const { sol_bundle_only, sol_tip_fee } = context.trade_runtime.settings.strategy
91
174
 
92
175
  // sol_bundle_only = false, 使用 Helius Sender
93
176
  // sol_bundle_only = true, 使用 Jito Bundle - 不需要给Helius小费
94
- if(!sol_bundle_only) {
177
+ if (!sol_bundle_only) {
95
178
  // 使用 Helius Sender:
96
179
  // 1、swqos_only: 至少 5000 lamports 小费
97
180
  // -> transaction must send a tip of at least 5000 lamports to one of the following Helius wallets: [xxx, ...]
98
181
  // 2、swqos + jito: 至少 0.0002 SOL 小费 -> lamports = 0.0002 * LAMPORTS_PER_SOL
99
182
  // -> Requires minimum 0.0002 SOL tip.
100
183
  let tip_lamports = sol_tip_fee
101
- if(tip_lamports < 5000) {
184
+ if (tip_lamports < 5000) {
102
185
  tip_lamports = 5000
103
186
  }
104
- if(tip_lamports > 300000) {
187
+ if (tip_lamports > 300000) {
105
188
  tip_lamports = 300000
106
189
  }
107
190
  let tip_instruction = SystemProgram.transfer({
@@ -127,19 +210,19 @@ export class SolTransactionBuilder {
127
210
  const max_block_offset = context.trade_runtime.settings.strategy.max_block_offset
128
211
 
129
212
  let tipPayer = this.jitoTipWalletManager.getNextWallet(groupId)
130
- if(!tipPayer) {
213
+ if (!tipPayer) {
131
214
  tipPayer = this.keypair
132
215
  }
133
216
 
134
217
  let tipAccount = getJitoTipAccount()
135
218
  let tip_lamports = context.trade_runtime.settings.strategy.sol_tip_fee
136
- if(tip_lamports < 5000) {
219
+ if (tip_lamports < 5000) {
137
220
  tip_lamports = 5000
138
221
  }
139
- if(tip_lamports > 300000) {
222
+ if (tip_lamports > 300000) {
140
223
  tip_lamports = 300000
141
224
  }
142
-
225
+
143
226
  const jitoTipTx = new Transaction();
144
227
  jitoTipTx.add(
145
228
  SystemProgram.transfer({