four-flap-meme-sdk 2.0.0 → 2.1.0
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/__tests__/subpath-exports.test.js +34 -0
- package/dist/chains/index.d.ts +13 -0
- package/dist/chains/index.js +13 -0
- package/dist/chains/xlayer/eip7702/index.d.ts +2 -0
- package/dist/flap/index.d.ts +10 -0
- package/dist/flap/index.js +8 -0
- package/dist/shared/constants/index.d.ts +2 -0
- package/dist/shared/index.d.ts +2 -0
- package/package.json +66 -1
- package/dist/chains/bsc/four/disperse.d.ts +0 -12
- package/dist/chains/bsc/four/disperse.js +0 -470
- package/dist/chains/bsc/four/pairwise.d.ts +0 -7
- package/dist/chains/bsc/four/pairwise.js +0 -308
- package/dist/chains/bsc/four/submit/blockrazor.d.ts +0 -18
- package/dist/chains/bsc/four/submit/blockrazor.js +0 -86
- package/dist/chains/bsc/four/submit/direct.d.ts +0 -66
- package/dist/chains/bsc/four/submit/direct.js +0 -452
- package/dist/chains/bsc/four/submit/helpers.d.ts +0 -18
- package/dist/chains/bsc/four/submit/helpers.js +0 -57
- package/dist/chains/bsc/four/submit/index.d.ts +0 -12
- package/dist/chains/bsc/four/submit/index.js +0 -11
- package/dist/chains/bsc/four/submit/merkle.d.ts +0 -18
- package/dist/chains/bsc/four/submit/merkle.js +0 -74
- package/dist/chains/bsc/four/submit/types.d.ts +0 -143
- package/dist/chains/bsc/four/swap/index.d.ts +0 -32
- package/dist/chains/bsc/four/swap/index.js +0 -829
- package/dist/chains/bsc/four/swap/types.d.ts +0 -70
- package/dist/chains/bsc/four/swap/types.js +0 -1
- package/dist/chains/bsc/four/sweep.d.ts +0 -13
- package/dist/chains/bsc/four/sweep.js +0 -788
- package/dist/chains/bsc/four/utils/index.d.ts +0 -20
- package/dist/chains/bsc/four/utils/index.js +0 -1558
- package/dist/chains/bsc/four/utils/types.d.ts +0 -1
- package/dist/chains/bsc/four/utils/types.js +0 -1
- package/dist/chains/bsc/pancake/bundle-buy-first/index.d.ts +0 -8
- package/dist/chains/bsc/pancake/bundle-buy-first/index.js +0 -907
- package/dist/chains/bsc/pancake/bundle-buy-first/types.d.ts +0 -73
- package/dist/chains/bsc/pancake/bundle-buy-first/types.js +0 -1
- package/dist/chains/bsc/pancake/bundle-swap/helpers.d.ts +0 -102
- package/dist/chains/bsc/pancake/bundle-swap/helpers.js +0 -572
- package/dist/chains/bsc/pancake/bundle-swap/index.d.ts +0 -50
- package/dist/chains/bsc/pancake/bundle-swap/index.js +0 -1066
- package/dist/chains/bsc/pancake/bundle-swap/types.d.ts +0 -202
- package/dist/chains/bsc/pancake/bundle-swap/types.js +0 -3
- package/dist/chains/xlayer/eip7702/bundle-swap/index.d.ts +0 -72
- package/dist/chains/xlayer/eip7702/bundle-swap/index.js +0 -921
- package/dist/chains/xlayer/eip7702/bundle-swap/types.d.ts +0 -65
- package/dist/chains/xlayer/eip7702/bundle-swap/types.js +0 -1
- package/dist/chains/xlayer/eip7702/multi-hop-transfer/index.d.ts +0 -128
- package/dist/chains/xlayer/eip7702/multi-hop-transfer/index.js +0 -857
- package/dist/chains/xlayer/eip7702/multi-hop-transfer/types.d.ts +0 -85
- package/dist/chains/xlayer/eip7702/multi-hop-transfer/types.js +0 -1
- package/dist/chains/xlayer/eip7702/volume/index.d.ts +0 -96
- package/dist/chains/xlayer/eip7702/volume/index.js +0 -793
- package/dist/chains/xlayer/eip7702/volume/types.d.ts +0 -124
- package/dist/chains/xlayer/eip7702/volume/types.js +0 -1
- package/dist/chains/xlayer/eoa/types-core.d.ts +0 -363
- package/dist/chains/xlayer/eoa/types-core.js +0 -53
- package/dist/chains/xlayer/eoa/types-create.d.ts +0 -413
- package/dist/chains/xlayer/eoa/types-create.js +0 -9
- package/dist/chains/xlayer/eoa/types-volume.d.ts +0 -209
- package/dist/chains/xlayer/eoa/types-volume.js +0 -13
- package/dist/dex/direct-router/index.d.ts +0 -70
- package/dist/dex/direct-router/index.js +0 -1410
- package/dist/dex/direct-router/types.d.ts +0 -81
- package/dist/dex/direct-router/types.js +0 -1
- package/dist/shared/abis/TaxToken.json +0 -969
- package/dist/shared/abis/TokenManager.json +0 -836
- package/dist/shared/abis/TokenManager2.json +0 -136
- package/dist/shared/abis/TokenManagerHelper3.json +0 -993
- package/dist/shared/abis 2/TaxToken.json +0 -105
- package/dist/shared/abis 2/TokenManager.json +0 -836
- package/dist/shared/abis 2/TokenManager2.json +0 -60
- package/dist/shared/abis 2/TokenManagerHelper3.json +0 -993
- package/dist/shared/abis 2/common.d.ts +0 -85
- package/dist/shared/abis 2/common.js +0 -254
- package/dist/shared/abis 2/index.d.ts +0 -8
- package/dist/shared/abis 2/index.js +0 -8
- package/dist/shared/clients 2/blockrazor.d.ts +0 -314
- package/dist/shared/clients 2/blockrazor.js +0 -596
- package/dist/shared/clients 2/club48.d.ts +0 -154
- package/dist/shared/clients 2/club48.js +0 -331
- package/dist/shared/clients 2/emitservice.d.ts +0 -47
- package/dist/shared/clients 2/emitservice.js +0 -44
- package/dist/shared/clients 2/four.d.ts +0 -132
- package/dist/shared/clients 2/four.js +0 -281
- package/dist/shared/clients 2/merkle.d.ts +0 -210
- package/dist/shared/clients 2/merkle.js +0 -400
- package/dist/shared/flap/__tests__/curve.test.d.ts +0 -1
- package/dist/shared/flap/__tests__/curve.test.js +0 -85
- package/dist/shared/flap/portal/index.d.ts +0 -12
- package/dist/shared/flap/portal/index.js +0 -11
- package/dist/shared/flap/portal/portal.d.ts +0 -47
- package/dist/shared/flap/portal/portal.js +0 -218
- package/dist/shared/flap/portal/types.d.ts +0 -227
- package/dist/shared/flap/portal/types.js +0 -80
- package/dist/shared/flap/portal/writer.d.ts +0 -121
- package/dist/shared/flap/portal/writer.js +0 -265
- package/dist/shared/flap/portal-bundle-merkle/core/index.d.ts +0 -18
- package/dist/shared/flap/portal-bundle-merkle/core/index.js +0 -938
- package/dist/shared/flap/portal-bundle-merkle/core/types.d.ts +0 -1
- package/dist/shared/flap/portal-bundle-merkle/core/types.js +0 -1
- package/dist/shared/flap/portal-bundle-merkle/swap/index.d.ts +0 -42
- package/dist/shared/flap/portal-bundle-merkle/swap/index.js +0 -1448
- package/dist/shared/flap/portal-bundle-merkle/swap/types.d.ts +0 -84
- package/dist/shared/flap/portal-bundle-merkle/swap/types.js +0 -1
- package/dist/shared/flap/portal-bundle-merkle/utils/index.d.ts +0 -17
- package/dist/shared/flap/portal-bundle-merkle/utils/index.js +0 -1024
- package/dist/shared/flap/portal-bundle-merkle/utils/types.d.ts +0 -16
- package/dist/shared/flap/portal-bundle-merkle/utils/types.js +0 -1
- package/dist/shared/flap/portal-bundle-merkle/utils-helpers.d.ts +0 -100
- package/dist/shared/flap/portal-bundle-merkle/utils-helpers.js +0 -133
- package/dist/shared/flap 2/__tests__/curve.test.d.ts +0 -1
- package/dist/shared/flap 2/__tests__/curve.test.js +0 -85
- package/dist/shared/flap 2/abi.d.ts +0 -4
- package/dist/shared/flap 2/abi.js +0 -4
- package/dist/shared/flap 2/constants.d.ts +0 -128
- package/dist/shared/flap 2/constants.js +0 -143
- package/dist/shared/flap 2/curve.d.ts +0 -33
- package/dist/shared/flap 2/curve.js +0 -84
- package/dist/shared/flap 2/errors.d.ts +0 -37
- package/dist/shared/flap 2/errors.js +0 -114
- package/dist/shared/flap 2/index.d.ts +0 -22
- package/dist/shared/flap 2/index.js +0 -33
- package/dist/shared/flap 2/ipfs.d.ts +0 -21
- package/dist/shared/flap 2/ipfs.js +0 -38
- package/dist/shared/flap 2/meta.d.ts +0 -30
- package/dist/shared/flap 2/meta.js +0 -195
- package/dist/shared/flap 2/permit.d.ts +0 -16
- package/dist/shared/flap 2/permit.js +0 -67
- package/dist/shared/flap 2/pinata.d.ts +0 -40
- package/dist/shared/flap 2/pinata.js +0 -106
- package/dist/shared/flap 2/portal-bundle-merkle/config.d.ts +0 -79
- package/dist/shared/flap 2/portal-bundle-merkle/config.js +0 -133
- package/dist/shared/flap 2/portal-bundle-merkle/core.d.ts +0 -18
- package/dist/shared/flap 2/portal-bundle-merkle/core.js +0 -938
- package/dist/shared/flap 2/portal-bundle-merkle/create-to-dex.d.ts +0 -125
- package/dist/shared/flap 2/portal-bundle-merkle/create-to-dex.js +0 -665
- package/dist/shared/flap 2/portal-bundle-merkle/curve-to-dex.d.ts +0 -88
- package/dist/shared/flap 2/portal-bundle-merkle/curve-to-dex.js +0 -446
- package/dist/shared/flap 2/portal-bundle-merkle/index.d.ts +0 -14
- package/dist/shared/flap 2/portal-bundle-merkle/index.js +0 -26
- package/dist/shared/flap 2/portal-bundle-merkle/pancake-proxy.d.ts +0 -28
- package/dist/shared/flap 2/portal-bundle-merkle/pancake-proxy.js +0 -701
- package/dist/shared/flap 2/portal-bundle-merkle/private.d.ts +0 -17
- package/dist/shared/flap 2/portal-bundle-merkle/private.js +0 -549
- package/dist/shared/flap 2/portal-bundle-merkle/swap-buy-first.d.ts +0 -65
- package/dist/shared/flap 2/portal-bundle-merkle/swap-buy-first.js +0 -831
- package/dist/shared/flap 2/portal-bundle-merkle/swap.d.ts +0 -201
- package/dist/shared/flap 2/portal-bundle-merkle/swap.js +0 -1359
- package/dist/shared/flap 2/portal-bundle-merkle/types.d.ts +0 -358
- package/dist/shared/flap 2/portal-bundle-merkle/types.js +0 -1
- package/dist/shared/flap 2/portal-bundle-merkle/utils.d.ts +0 -89
- package/dist/shared/flap 2/portal-bundle-merkle/utils.js +0 -963
- package/dist/shared/flap 2/portal-bundle.d.ts +0 -119
- package/dist/shared/flap 2/portal-bundle.js +0 -584
- package/dist/shared/flap 2/portal.d.ts +0 -392
- package/dist/shared/flap 2/portal.js +0 -559
- package/dist/shared/flap 2/vanity.d.ts +0 -48
- package/dist/shared/flap 2/vanity.js +0 -110
- package/dist/shared/flap 2/vault.d.ts +0 -240
- package/dist/shared/flap 2/vault.js +0 -366
- package/dist/shared/four 2/index.d.ts +0 -7
- package/dist/shared/four 2/index.js +0 -22
- package/dist/shared/four 2/tax-token.d.ts +0 -176
- package/dist/shared/four 2/tax-token.js +0 -302
- package/dist/shared/index 2.js +0 -10
- package/dist/shared/index.d 2.ts +0 -10
- package/dist/types/distribute.d.ts +0 -72
- package/dist/types/distribute.js +0 -1
- package/dist/types 2/errors.d.ts +0 -27
- package/dist/types 2/errors.js +0 -34
- package/dist/utils/__tests__/errors.test.d.ts +0 -1
- package/dist/utils/__tests__/errors.test.js +0 -76
- package/dist/utils/airdrop-sweep-types.d.ts +0 -1
- package/dist/utils/airdrop-sweep-types.js +0 -1
- package/dist/utils/erc20/index.d.ts +0 -242
- package/dist/utils/erc20/index.js +0 -645
- package/dist/utils/erc20/types.d.ts +0 -77
- package/dist/utils/erc20/types.js +0 -1
- package/dist/utils/erc20-types.d.ts +0 -1
- package/dist/utils/erc20-types.js +0 -1
- package/dist/utils/holders-maker/helpers.d.ts +0 -43
- package/dist/utils/holders-maker/helpers.js +0 -371
- package/dist/utils/holders-maker/index.d.ts +0 -26
- package/dist/utils/holders-maker/index.js +0 -218
- package/dist/utils/holders-maker/types.d.ts +0 -72
- package/dist/utils/holders-maker/types.js +0 -4
- package/dist/utils/holders-maker-types.d.ts +0 -1
- package/dist/utils/holders-maker-types.js +0 -1
- package/dist/utils/lp-inspect/index.d.ts +0 -44
- package/dist/utils/lp-inspect/index.js +0 -937
- package/dist/utils/lp-inspect/types.d.ts +0 -100
- package/dist/utils/lp-inspect/types.js +0 -1
- package/dist/utils/lp-inspect-types.d.ts +0 -1
- package/dist/utils/lp-inspect-types.js +0 -1
- package/dist/utils/private-sale-types.d.ts +0 -1
- package/dist/utils/private-sale-types.js +0 -1
- package/dist/utils/quote-helpers/index.d.ts +0 -107
- package/dist/utils/quote-helpers/index.js +0 -346
- package/dist/utils/quote-helpers/types.d.ts +0 -16
- package/dist/utils/quote-helpers/types.js +0 -1
- package/dist/utils/quote-helpers-types.d.ts +0 -1
- package/dist/utils/quote-helpers-types.js +0 -1
- /package/dist/{chains/bsc/four/submit/types.js → __tests__/subpath-exports.test.d.ts} +0 -0
|
@@ -1,596 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BlockRazor Bundle Service 客户端
|
|
3
|
-
*
|
|
4
|
-
* 提供基于 BlockRazor 的 MEV 保护和捆绑交易服务
|
|
5
|
-
*
|
|
6
|
-
* 官方文档: https://blockrazor.gitbook.io/blockrazor/bsc/block-builder/send-bundle
|
|
7
|
-
*
|
|
8
|
-
* 特点:
|
|
9
|
-
* - 支持 BSC 链的 Bundle 提交
|
|
10
|
-
* - 激励机制:向 Builder EOA 转账 BNB 可提高优先级
|
|
11
|
-
* - 支持 Bundle 合并提高打包率
|
|
12
|
-
*
|
|
13
|
-
* 使用示例:
|
|
14
|
-
* ```typescript
|
|
15
|
-
* import { BlockRazorClient } from 'four-flap-meme-sdk';
|
|
16
|
-
*
|
|
17
|
-
* const client = new BlockRazorClient({
|
|
18
|
-
* apiKey: 'your-api-key',
|
|
19
|
-
* chainId: 56
|
|
20
|
-
* });
|
|
21
|
-
*
|
|
22
|
-
* const result = await client.sendBundle({
|
|
23
|
-
* transactions: [tx1, tx2, tx3],
|
|
24
|
-
* blockOffset: 10
|
|
25
|
-
* });
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
import { JsonRpcProvider, Transaction, ethers } from 'ethers';
|
|
29
|
-
import { GAS_LIMITS } from '../constants/index.js';
|
|
30
|
-
// ============================================================================
|
|
31
|
-
// 常量
|
|
32
|
-
// ============================================================================
|
|
33
|
-
/**
|
|
34
|
-
* BlockRazor Builder EOA 地址
|
|
35
|
-
* 向此地址转账 BNB 可提高 Bundle 优先级
|
|
36
|
-
* 来源: https://blockrazor.gitbook.io/blockrazor/bsc/block-builder/send-bundle
|
|
37
|
-
*/
|
|
38
|
-
export const BLOCKRAZOR_BUILDER_EOA = '0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20';
|
|
39
|
-
/**
|
|
40
|
-
* BlockRazor BSC RPC 端点
|
|
41
|
-
* 来源: 测试文件 008_blockrazor_smoke_test.ts
|
|
42
|
-
*/
|
|
43
|
-
const BLOCKRAZOR_RPC_ENDPOINTS = {
|
|
44
|
-
BSC: 'https://rpc.blockrazor.builders',
|
|
45
|
-
// 可以添加其他链的端点
|
|
46
|
-
};
|
|
47
|
-
/**
|
|
48
|
-
* 最低 Gas Price 要求 (0.05 Gwei)
|
|
49
|
-
*/
|
|
50
|
-
const MIN_GAS_PRICE_GWEI = 0.05;
|
|
51
|
-
// ============================================================================
|
|
52
|
-
// BlockRazor 客户端
|
|
53
|
-
// ============================================================================
|
|
54
|
-
/**
|
|
55
|
-
* BlockRazor 客户端
|
|
56
|
-
*/
|
|
57
|
-
export class BlockRazorClient {
|
|
58
|
-
constructor(config) {
|
|
59
|
-
// 区块号缓存
|
|
60
|
-
this.blockNumberCache = null;
|
|
61
|
-
this.chainId = config.chainId;
|
|
62
|
-
this.apiKey = config.apiKey;
|
|
63
|
-
// 普通 RPC(用于查询)
|
|
64
|
-
const rpcUrl = config.customRpcUrl || 'https://bsc-dataseed.binance.org';
|
|
65
|
-
this.provider = new JsonRpcProvider(rpcUrl, {
|
|
66
|
-
chainId: this.chainId,
|
|
67
|
-
name: 'bsc',
|
|
68
|
-
});
|
|
69
|
-
// Builder RPC URL(用于发送 Bundle)
|
|
70
|
-
// ✅ 正确的端点: https://rpc.blockrazor.builders
|
|
71
|
-
this.builderUrl = config.builderRpcUrl || BLOCKRAZOR_RPC_ENDPOINTS.BSC;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* 获取 Provider
|
|
75
|
-
*/
|
|
76
|
-
getProvider() {
|
|
77
|
-
return this.provider;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* 获取 Builder URL
|
|
81
|
-
*/
|
|
82
|
-
getBuilderUrl() {
|
|
83
|
-
return this.builderUrl;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* 获取当前区块号(带缓存)
|
|
87
|
-
*/
|
|
88
|
-
async getBlockNumber(forceRefresh = false) {
|
|
89
|
-
const now = Date.now();
|
|
90
|
-
if (!forceRefresh && this.blockNumberCache) {
|
|
91
|
-
const age = now - this.blockNumberCache.timestamp;
|
|
92
|
-
if (age < BlockRazorClient.BLOCK_CACHE_TTL_MS) {
|
|
93
|
-
return this.blockNumberCache.value;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
const blockNumber = await this.provider.getBlockNumber();
|
|
97
|
-
this.blockNumberCache = { value: blockNumber, timestamp: now };
|
|
98
|
-
return blockNumber;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* 获取 Gas 价格信息
|
|
102
|
-
*/
|
|
103
|
-
async getFeeData() {
|
|
104
|
-
return await this.provider.getFeeData();
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* 构建激励交易(向 BlockRazor Builder EOA 转账 BNB)
|
|
108
|
-
*
|
|
109
|
-
* 根据文档:向 Builder EOA 转账更多 BNB 可提高 Bundle 优先级
|
|
110
|
-
*
|
|
111
|
-
* @param params 激励交易参数
|
|
112
|
-
* @returns 已签名的交易
|
|
113
|
-
*/
|
|
114
|
-
async buildIncentiveTransaction(params) {
|
|
115
|
-
const { wallet, amount, nonce, gasPrice } = params;
|
|
116
|
-
// 获取 nonce(使用 pending 确保包含待处理交易)
|
|
117
|
-
const txNonce = nonce ?? await this.provider.getTransactionCount(wallet.address, 'pending');
|
|
118
|
-
// 获取 gas price
|
|
119
|
-
let txGasPrice = gasPrice;
|
|
120
|
-
if (!txGasPrice) {
|
|
121
|
-
const feeData = await this.getFeeData();
|
|
122
|
-
txGasPrice = feeData.gasPrice || ethers.parseUnits(String(MIN_GAS_PRICE_GWEI), 'gwei');
|
|
123
|
-
}
|
|
124
|
-
// 构建交易
|
|
125
|
-
const tx = {
|
|
126
|
-
to: BLOCKRAZOR_BUILDER_EOA,
|
|
127
|
-
value: ethers.parseEther(amount),
|
|
128
|
-
nonce: txNonce,
|
|
129
|
-
gasLimit: GAS_LIMITS.BRIBE,
|
|
130
|
-
gasPrice: txGasPrice,
|
|
131
|
-
chainId: this.chainId,
|
|
132
|
-
type: 0,
|
|
133
|
-
};
|
|
134
|
-
return await wallet.signTransaction(tx);
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* 发送捆绑交易(底层方法)
|
|
138
|
-
*
|
|
139
|
-
* ✅ 使用 fetch 直接发送请求,支持 Authorization 头
|
|
140
|
-
*
|
|
141
|
-
* @param params Bundle 参数
|
|
142
|
-
* @returns Bundle Hash
|
|
143
|
-
*/
|
|
144
|
-
async sendBundleRaw(params) {
|
|
145
|
-
if (!params.txs || params.txs.length === 0) {
|
|
146
|
-
throw new Error('Bundle transactions cannot be empty');
|
|
147
|
-
}
|
|
148
|
-
// 验证 Gas Price(最低 0.05 Gwei)
|
|
149
|
-
for (const rawTx of params.txs) {
|
|
150
|
-
try {
|
|
151
|
-
const tx = Transaction.from(rawTx);
|
|
152
|
-
const gasPrice = tx.gasPrice || 0n;
|
|
153
|
-
const minGasPrice = ethers.parseUnits(String(MIN_GAS_PRICE_GWEI), 'gwei');
|
|
154
|
-
if (gasPrice < minGasPrice) {
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
catch {
|
|
158
|
-
// 解析失败,跳过检查
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
// 构建请求参数
|
|
162
|
-
const bundleParams = {
|
|
163
|
-
txs: params.txs,
|
|
164
|
-
};
|
|
165
|
-
if (params.maxBlockNumber !== undefined) {
|
|
166
|
-
bundleParams.maxBlockNumber = params.maxBlockNumber;
|
|
167
|
-
}
|
|
168
|
-
if (params.minTimestamp !== undefined) {
|
|
169
|
-
bundleParams.minTimestamp = params.minTimestamp;
|
|
170
|
-
}
|
|
171
|
-
if (params.maxTimestamp !== undefined) {
|
|
172
|
-
bundleParams.maxTimestamp = params.maxTimestamp;
|
|
173
|
-
}
|
|
174
|
-
if (params.revertingTxHashes && params.revertingTxHashes.length > 0) {
|
|
175
|
-
bundleParams.revertingTxHashes = params.revertingTxHashes;
|
|
176
|
-
}
|
|
177
|
-
if (params.noMerge !== undefined) {
|
|
178
|
-
bundleParams.noMerge = params.noMerge;
|
|
179
|
-
}
|
|
180
|
-
try {
|
|
181
|
-
// ✅ 使用 fetch 发送请求,支持 Authorization 头
|
|
182
|
-
const requestBody = {
|
|
183
|
-
jsonrpc: '2.0',
|
|
184
|
-
id: '1',
|
|
185
|
-
method: 'eth_sendBundle',
|
|
186
|
-
params: [bundleParams]
|
|
187
|
-
};
|
|
188
|
-
const headers = {
|
|
189
|
-
'Content-Type': 'application/json',
|
|
190
|
-
};
|
|
191
|
-
// 如果有 API Key,添加 Authorization 头
|
|
192
|
-
if (this.apiKey) {
|
|
193
|
-
headers['Authorization'] = this.apiKey;
|
|
194
|
-
}
|
|
195
|
-
const response = await fetch(this.builderUrl, {
|
|
196
|
-
method: 'POST',
|
|
197
|
-
headers,
|
|
198
|
-
body: JSON.stringify(requestBody),
|
|
199
|
-
});
|
|
200
|
-
if (!response.ok) {
|
|
201
|
-
const text = await response.text();
|
|
202
|
-
throw new Error(`HTTP ${response.status}: ${text}`);
|
|
203
|
-
}
|
|
204
|
-
const result = await response.json();
|
|
205
|
-
// 检查 JSON-RPC 错误
|
|
206
|
-
if (result.error) {
|
|
207
|
-
throw new Error(`RPC Error: ${result.error.message || JSON.stringify(result.error)}`);
|
|
208
|
-
}
|
|
209
|
-
// 提取 bundle hash
|
|
210
|
-
const bundleHash = result.result;
|
|
211
|
-
if (typeof bundleHash === 'string') {
|
|
212
|
-
return bundleHash;
|
|
213
|
-
}
|
|
214
|
-
if (bundleHash && typeof bundleHash === 'object') {
|
|
215
|
-
if ('bundleHash' in bundleHash) {
|
|
216
|
-
return String(bundleHash.bundleHash);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
return JSON.stringify(result.result || result);
|
|
220
|
-
}
|
|
221
|
-
catch (error) {
|
|
222
|
-
const errorMsg = error.message || String(error);
|
|
223
|
-
console.error(`❌ [BlockRazor] Bundle 发送失败: ${errorMsg}`);
|
|
224
|
-
throw new Error(`BlockRazor send bundle failed: ${errorMsg}`);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* 发送捆绑交易(高级方法)
|
|
229
|
-
*
|
|
230
|
-
* @param options 发送选项
|
|
231
|
-
* @returns Bundle 结果
|
|
232
|
-
*/
|
|
233
|
-
async sendBundle(options) {
|
|
234
|
-
if (!options.transactions || options.transactions.length === 0) {
|
|
235
|
-
throw new Error('Transactions array cannot be empty');
|
|
236
|
-
}
|
|
237
|
-
const blockOffset = options.blockOffset ?? 100;
|
|
238
|
-
const maxBlockOffset = options.maxBlockOffset ?? 100;
|
|
239
|
-
const autoRetry = options.autoRetry ?? false;
|
|
240
|
-
const maxRetries = options.maxRetries ?? 3;
|
|
241
|
-
let attempt = 0;
|
|
242
|
-
let lastError = null;
|
|
243
|
-
while (attempt <= (autoRetry ? maxRetries : 0)) {
|
|
244
|
-
try {
|
|
245
|
-
// 获取当前区块
|
|
246
|
-
const currentBlock = await this.getBlockNumber(true);
|
|
247
|
-
// 计算最大有效区块号
|
|
248
|
-
const actualOffset = Math.min(blockOffset, maxBlockOffset);
|
|
249
|
-
const maxBlockNumber = currentBlock + actualOffset;
|
|
250
|
-
// 发送 Bundle
|
|
251
|
-
const bundleHash = await this.sendBundleRaw({
|
|
252
|
-
txs: options.transactions,
|
|
253
|
-
maxBlockNumber,
|
|
254
|
-
minTimestamp: options.minTimestamp,
|
|
255
|
-
maxTimestamp: options.maxTimestamp,
|
|
256
|
-
revertingTxHashes: options.revertingTxHashes,
|
|
257
|
-
noMerge: options.noMerge,
|
|
258
|
-
});
|
|
259
|
-
// 提取交易哈希
|
|
260
|
-
const txHashes = options.transactions.map(rawTx => {
|
|
261
|
-
try {
|
|
262
|
-
return Transaction.from(rawTx).hash || '';
|
|
263
|
-
}
|
|
264
|
-
catch {
|
|
265
|
-
return '';
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
return {
|
|
269
|
-
bundleHash,
|
|
270
|
-
txHashes,
|
|
271
|
-
maxBlockNumber,
|
|
272
|
-
txCount: options.transactions.length,
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
catch (error) {
|
|
276
|
-
lastError = error;
|
|
277
|
-
if (autoRetry && attempt < maxRetries) {
|
|
278
|
-
attempt++;
|
|
279
|
-
await new Promise(resolve => setTimeout(resolve, 3000)); // 等待一个区块
|
|
280
|
-
continue;
|
|
281
|
-
}
|
|
282
|
-
throw error;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
throw lastError || new Error('Bundle submission failed after all retries');
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* 发送带激励的 Bundle(自动添加激励交易)
|
|
289
|
-
*
|
|
290
|
-
* @param options 发送选项
|
|
291
|
-
* @param incentive 激励参数
|
|
292
|
-
* @returns Bundle 结果
|
|
293
|
-
*/
|
|
294
|
-
async sendBundleWithIncentive(options, incentive) {
|
|
295
|
-
// 获取激励钱包的 nonce(使用 pending 确保包含待处理交易)
|
|
296
|
-
const nonce = await this.provider.getTransactionCount(incentive.wallet.address, 'pending');
|
|
297
|
-
// 构建激励交易(放在 Bundle 最后)
|
|
298
|
-
const incentiveTx = await this.buildIncentiveTransaction({
|
|
299
|
-
wallet: incentive.wallet,
|
|
300
|
-
amount: incentive.amount,
|
|
301
|
-
nonce: nonce,
|
|
302
|
-
});
|
|
303
|
-
// 将激励交易添加到 Bundle 末尾
|
|
304
|
-
const allTransactions = [...options.transactions, incentiveTx];
|
|
305
|
-
return await this.sendBundle({
|
|
306
|
-
...options,
|
|
307
|
-
transactions: allTransactions,
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* 等待 Bundle 中的交易确认
|
|
312
|
-
*
|
|
313
|
-
* @param txHashes 交易哈希列表
|
|
314
|
-
* @param confirmations 确认数(默认 1)
|
|
315
|
-
* @param timeout 超时时间(毫秒,默认 120000)
|
|
316
|
-
* @returns 交易结果列表
|
|
317
|
-
*/
|
|
318
|
-
async waitForBundleConfirmation(txHashes, confirmations = 1, timeout = 120000) {
|
|
319
|
-
const promises = txHashes.map(async (hash, index) => {
|
|
320
|
-
try {
|
|
321
|
-
const receipt = await this.provider.waitForTransaction(hash, confirmations, timeout);
|
|
322
|
-
if (!receipt) {
|
|
323
|
-
return {
|
|
324
|
-
index,
|
|
325
|
-
hash,
|
|
326
|
-
success: false,
|
|
327
|
-
error: 'Transaction not found',
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
let gasCost;
|
|
331
|
-
if (receipt.gasUsed && receipt.gasPrice) {
|
|
332
|
-
const costWei = receipt.gasUsed * receipt.gasPrice;
|
|
333
|
-
const costBnb = Number(costWei) / 1e18;
|
|
334
|
-
gasCost = costBnb.toFixed(8);
|
|
335
|
-
}
|
|
336
|
-
return {
|
|
337
|
-
index,
|
|
338
|
-
hash,
|
|
339
|
-
success: receipt.status === 1,
|
|
340
|
-
blockNumber: receipt.blockNumber,
|
|
341
|
-
gasUsed: receipt.gasUsed.toString(),
|
|
342
|
-
effectiveGasPrice: receipt.gasPrice?.toString(),
|
|
343
|
-
gasCost,
|
|
344
|
-
};
|
|
345
|
-
}
|
|
346
|
-
catch (error) {
|
|
347
|
-
return {
|
|
348
|
-
index,
|
|
349
|
-
hash,
|
|
350
|
-
success: false,
|
|
351
|
-
error: error.message,
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
});
|
|
355
|
-
return await Promise.all(promises);
|
|
356
|
-
}
|
|
357
|
-
/**
|
|
358
|
-
* 签名交易批次
|
|
359
|
-
*
|
|
360
|
-
* @param wallet 钱包
|
|
361
|
-
* @param transactions 交易列表
|
|
362
|
-
* @param options 可选参数
|
|
363
|
-
* @returns 已签名的交易数组
|
|
364
|
-
*/
|
|
365
|
-
async signTransactions(wallet, transactions, options) {
|
|
366
|
-
if (!transactions || transactions.length === 0) {
|
|
367
|
-
throw new Error('Transactions array cannot be empty');
|
|
368
|
-
}
|
|
369
|
-
// 获取 nonce(使用 pending 确保包含待处理交易)
|
|
370
|
-
let nonce = options?.startNonce;
|
|
371
|
-
if (nonce === undefined) {
|
|
372
|
-
nonce = await this.provider.getTransactionCount(wallet.address, 'pending');
|
|
373
|
-
}
|
|
374
|
-
// 获取 Gas Price(确保不低于最低要求)
|
|
375
|
-
let gasPrice = options?.gasPrice;
|
|
376
|
-
if (!gasPrice) {
|
|
377
|
-
const feeData = await this.provider.getFeeData();
|
|
378
|
-
const baseGasPrice = feeData.gasPrice || ethers.parseUnits(String(MIN_GAS_PRICE_GWEI), 'gwei');
|
|
379
|
-
const multiplier = options?.gasPriceMultiplier || 50;
|
|
380
|
-
gasPrice = (baseGasPrice * BigInt(100 + multiplier)) / 100n;
|
|
381
|
-
// 确保不低于最低要求
|
|
382
|
-
const minGasPrice = ethers.parseUnits(String(MIN_GAS_PRICE_GWEI), 'gwei');
|
|
383
|
-
if (gasPrice < minGasPrice) {
|
|
384
|
-
gasPrice = minGasPrice;
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
// 并行签名所有交易
|
|
388
|
-
const signedTxs = await Promise.all(transactions.map(async (tx, i) => {
|
|
389
|
-
const fullTx = {
|
|
390
|
-
...tx,
|
|
391
|
-
nonce: nonce + i,
|
|
392
|
-
gasPrice,
|
|
393
|
-
chainId: this.chainId,
|
|
394
|
-
};
|
|
395
|
-
return await wallet.signTransaction(fullTx);
|
|
396
|
-
}));
|
|
397
|
-
return signedTxs;
|
|
398
|
-
}
|
|
399
|
-
/**
|
|
400
|
-
* 检查交易是否已包含在区块中
|
|
401
|
-
*/
|
|
402
|
-
async isTransactionIncluded(txHash) {
|
|
403
|
-
try {
|
|
404
|
-
const receipt = await this.provider.getTransactionReceipt(txHash);
|
|
405
|
-
return receipt !== null && receipt.blockNumber !== null;
|
|
406
|
-
}
|
|
407
|
-
catch {
|
|
408
|
-
return false;
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
// ============================================================================
|
|
412
|
-
// 非捆绑广播方法(适用于 XLayer/Monad 等不支持 Bundle 的链)
|
|
413
|
-
// ============================================================================
|
|
414
|
-
/**
|
|
415
|
-
* 并行广播交易(非捆绑)
|
|
416
|
-
*
|
|
417
|
-
* 所有交易同时广播,适合不同钱包的独立交易
|
|
418
|
-
* ⚠️ 注意:同一钱包的多笔交易(连续 nonce)不建议并行广播,可能导致后续交易延迟确认
|
|
419
|
-
*
|
|
420
|
-
* @param signedTransactions 已签名的交易数组
|
|
421
|
-
* @param options 可选配置
|
|
422
|
-
* @returns 广播结果
|
|
423
|
-
*/
|
|
424
|
-
async broadcastTransactionsParallel(signedTransactions, options) {
|
|
425
|
-
if (!signedTransactions || signedTransactions.length === 0) {
|
|
426
|
-
return { success: false, txHashes: [], errors: [{ index: 0, error: '没有交易数据' }] };
|
|
427
|
-
}
|
|
428
|
-
const provider = options?.provider || this.provider;
|
|
429
|
-
const concurrencyLimit = options?.concurrencyLimit || 10;
|
|
430
|
-
const txHashes = [];
|
|
431
|
-
const errors = [];
|
|
432
|
-
// 分批并行广播
|
|
433
|
-
for (let i = 0; i < signedTransactions.length; i += concurrencyLimit) {
|
|
434
|
-
const batch = signedTransactions.slice(i, i + concurrencyLimit);
|
|
435
|
-
const batchPromises = batch.map(async (rawTx, batchIndex) => {
|
|
436
|
-
const globalIndex = i + batchIndex;
|
|
437
|
-
try {
|
|
438
|
-
const txResponse = await provider.broadcastTransaction(rawTx);
|
|
439
|
-
return { index: globalIndex, hash: txResponse.hash, error: null };
|
|
440
|
-
}
|
|
441
|
-
catch (err) {
|
|
442
|
-
const errorMsg = err?.message || String(err);
|
|
443
|
-
console.warn(`[broadcastParallel] 交易 ${globalIndex} 广播失败:`, errorMsg);
|
|
444
|
-
return { index: globalIndex, hash: '', error: errorMsg };
|
|
445
|
-
}
|
|
446
|
-
});
|
|
447
|
-
const results = await Promise.all(batchPromises);
|
|
448
|
-
for (const result of results) {
|
|
449
|
-
if (result.hash) {
|
|
450
|
-
txHashes.push(result.hash);
|
|
451
|
-
}
|
|
452
|
-
if (result.error) {
|
|
453
|
-
errors.push({ index: result.index, error: result.error });
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
return {
|
|
458
|
-
success: errors.length === 0,
|
|
459
|
-
txHashes,
|
|
460
|
-
errors,
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
/**
|
|
464
|
-
* 顺序广播交易(非捆绑)
|
|
465
|
-
*
|
|
466
|
-
* 按顺序逐笔广播,可选等待确认后再广播下一笔
|
|
467
|
-
* ✅ 适合同一钱包的多笔交易(连续 nonce),确保交易按顺序上链
|
|
468
|
-
*
|
|
469
|
-
* @param signedTransactions 已签名的交易数组
|
|
470
|
-
* @param options 可选配置
|
|
471
|
-
* @returns 广播结果
|
|
472
|
-
*/
|
|
473
|
-
async broadcastTransactionsSequential(signedTransactions, options) {
|
|
474
|
-
if (!signedTransactions || signedTransactions.length === 0) {
|
|
475
|
-
return { success: false, txHashes: [], errors: [{ index: 0, error: '没有交易数据' }] };
|
|
476
|
-
}
|
|
477
|
-
const provider = options?.provider || this.provider;
|
|
478
|
-
const waitConfirmation = options?.waitConfirmation ?? false;
|
|
479
|
-
const confirmationTimeout = options?.confirmationTimeout ?? 60000;
|
|
480
|
-
const broadcastDelay = options?.broadcastDelay ?? 100;
|
|
481
|
-
const txHashes = [];
|
|
482
|
-
const errors = [];
|
|
483
|
-
for (let i = 0; i < signedTransactions.length; i++) {
|
|
484
|
-
const rawTx = signedTransactions[i];
|
|
485
|
-
if (!rawTx)
|
|
486
|
-
continue;
|
|
487
|
-
try {
|
|
488
|
-
// 广播交易
|
|
489
|
-
const txResponse = await provider.broadcastTransaction(rawTx);
|
|
490
|
-
txHashes.push(txResponse.hash);
|
|
491
|
-
console.log(`[broadcastSequential] 交易 ${i}/${signedTransactions.length} 广播成功: ${txResponse.hash.slice(0, 10)}...`);
|
|
492
|
-
// 如果需要等待确认
|
|
493
|
-
if (waitConfirmation) {
|
|
494
|
-
try {
|
|
495
|
-
const receipt = await provider.waitForTransaction(txResponse.hash, 1, confirmationTimeout);
|
|
496
|
-
if (receipt && receipt.status === 1) {
|
|
497
|
-
console.log(`[broadcastSequential] 交易 ${i} 确认成功,区块: ${receipt.blockNumber}`);
|
|
498
|
-
}
|
|
499
|
-
else if (receipt && receipt.status === 0) {
|
|
500
|
-
console.warn(`[broadcastSequential] 交易 ${i} 执行失败(revert)`);
|
|
501
|
-
errors.push({ index: i, error: 'Transaction reverted' });
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
catch (waitErr) {
|
|
505
|
-
console.warn(`[broadcastSequential] 交易 ${i} 等待确认超时:`, waitErr?.message);
|
|
506
|
-
// 超时不算失败,交易可能还在 pending
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
else {
|
|
510
|
-
// 不等待确认,只加一个小延迟确保 RPC 处理完成
|
|
511
|
-
if (broadcastDelay > 0 && i < signedTransactions.length - 1) {
|
|
512
|
-
await new Promise(resolve => setTimeout(resolve, broadcastDelay));
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
catch (err) {
|
|
517
|
-
const errorMsg = err?.message || String(err);
|
|
518
|
-
console.warn(`[broadcastSequential] 交易 ${i} 广播失败:`, errorMsg);
|
|
519
|
-
errors.push({ index: i, error: errorMsg });
|
|
520
|
-
// 如果是 nonce 问题,后续交易也会失败,提前终止
|
|
521
|
-
if (errorMsg.includes('nonce') || errorMsg.includes('replacement')) {
|
|
522
|
-
console.error(`[broadcastSequential] Nonce 问题,终止后续广播`);
|
|
523
|
-
break;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
return {
|
|
528
|
-
success: errors.length === 0,
|
|
529
|
-
txHashes,
|
|
530
|
-
errors,
|
|
531
|
-
};
|
|
532
|
-
}
|
|
533
|
-
/**
|
|
534
|
-
* 智能广播交易(自动选择并行或顺序)
|
|
535
|
-
*
|
|
536
|
-
* 根据交易的发送者地址自动决定广播策略:
|
|
537
|
-
* - 如果所有交易来自不同钱包:并行广播
|
|
538
|
-
* - 如果存在同一钱包的多笔交易:顺序广播
|
|
539
|
-
*
|
|
540
|
-
* @param signedTransactions 已签名的交易数组
|
|
541
|
-
* @param options 可选配置
|
|
542
|
-
* @returns 广播结果
|
|
543
|
-
*/
|
|
544
|
-
async broadcastTransactionsSmart(signedTransactions, options) {
|
|
545
|
-
// 检测是否有同一钱包的多笔交易
|
|
546
|
-
const hasMultipleFromSameWallet = (() => {
|
|
547
|
-
if (!Array.isArray(signedTransactions) || signedTransactions.length < 2)
|
|
548
|
-
return false;
|
|
549
|
-
const seen = new Set();
|
|
550
|
-
for (const raw of signedTransactions) {
|
|
551
|
-
if (!raw)
|
|
552
|
-
continue;
|
|
553
|
-
try {
|
|
554
|
-
const tx = Transaction.from(raw);
|
|
555
|
-
const from = (tx.from || '').toLowerCase();
|
|
556
|
-
if (!from)
|
|
557
|
-
continue;
|
|
558
|
-
if (seen.has(from))
|
|
559
|
-
return true;
|
|
560
|
-
seen.add(from);
|
|
561
|
-
}
|
|
562
|
-
catch {
|
|
563
|
-
// ignore parse errors
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
return false;
|
|
567
|
-
})();
|
|
568
|
-
if (hasMultipleFromSameWallet) {
|
|
569
|
-
console.log(`[broadcastSmart] 检测到同一钱包多笔交易,使用顺序广播`);
|
|
570
|
-
const result = await this.broadcastTransactionsSequential(signedTransactions, options);
|
|
571
|
-
return { ...result, mode: 'sequential' };
|
|
572
|
-
}
|
|
573
|
-
else {
|
|
574
|
-
console.log(`[broadcastSmart] 所有交易来自不同钱包,使用并行广播`);
|
|
575
|
-
const result = await this.broadcastTransactionsParallel(signedTransactions, { provider: options?.provider });
|
|
576
|
-
return { ...result, mode: 'parallel' };
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
/**
|
|
580
|
-
* 获取客户端信息
|
|
581
|
-
*/
|
|
582
|
-
getClientInfo() {
|
|
583
|
-
return {
|
|
584
|
-
chainId: this.chainId,
|
|
585
|
-
builderEOA: BLOCKRAZOR_BUILDER_EOA,
|
|
586
|
-
minGasPriceGwei: MIN_GAS_PRICE_GWEI,
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
BlockRazorClient.BLOCK_CACHE_TTL_MS = 1000; // 1秒缓存
|
|
591
|
-
/**
|
|
592
|
-
* 创建 BlockRazor 客户端的便捷函数
|
|
593
|
-
*/
|
|
594
|
-
export function createBlockRazorClient(apiKey) {
|
|
595
|
-
return new BlockRazorClient({ apiKey, chainId: 56 });
|
|
596
|
-
}
|