four-flap-meme-sdk 4.4.3 → 4.5.1
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/README.md +16 -22
- package/dist/bundle/four-meme/types/buy-first.d.ts +2 -1
- package/dist/bundle/four-meme/types/config.d.ts +2 -1
- package/dist/bundle/four-meme/types/swap-types.d.ts +2 -1
- package/dist/bundle/four-meme/utils-sweep/erc20-no-hop.js +16 -8
- package/dist/bundle/four-meme/utils-sweep/main.js +15 -1
- package/dist/bundle/four-meme/utils-sweep/native-no-hop.js +17 -9
- package/dist/bundle/index.d.ts +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/internal.d.ts +2 -0
- package/dist/bundle/internal.js +7 -3
- package/dist/bundle/submit/index.d.ts +1 -3
- package/dist/bundle/submit/index.js +1 -3
- package/dist/chains/bsc/index.d.ts +1 -1
- package/dist/chains/bsc/index.js +1 -1
- package/dist/chains/bsc/pancake/bundle-buy-first-types.d.ts +2 -1
- package/dist/chains/bsc/pancake/bundle-swap-types.d.ts +2 -1
- package/dist/chains/bsc/platforms/four/index.d.ts +1 -1
- package/dist/chains/bsc/platforms/four/index.js +1 -1
- package/dist/core/clients/blockrazor.js +8 -6
- package/dist/core/clients/blockrazor.test.d.ts +1 -0
- package/dist/core/clients/blockrazor.test.js +23 -0
- package/dist/core/clients/index.d.ts +0 -1
- package/dist/core/clients/index.js +0 -1
- package/dist/core/clients/merkle.js +12 -7
- package/dist/core/clients/merkle.test.d.ts +1 -0
- package/dist/core/clients/merkle.test.js +8 -0
- package/dist/core/nonce/nonce-manager.d.ts +2 -0
- package/dist/core/nonce/nonce-manager.js +21 -1
- package/dist/core/tx/wallet-sign-patch.js +22 -11
- package/dist/core/tx/wallet-sign-patch.test.d.ts +1 -0
- package/dist/core/tx/wallet-sign-patch.test.js +21 -0
- package/dist/domains/dex/direct-router-approvals.d.ts +28 -0
- package/dist/domains/dex/direct-router-approvals.js +45 -0
- package/dist/domains/dex/direct-router-helpers.d.ts +1 -0
- package/dist/domains/dex/direct-router-helpers.js +1 -0
- package/dist/domains/dex/direct-router-v2-buy.js +13 -0
- package/dist/domains/dex/direct-router-v3.js +43 -4
- package/dist/domains/flap/portal-bundle-merkle/private/buy.js +3 -3
- package/dist/domains/flap/portal-bundle-merkle/private/helpers.d.ts +1 -1
- package/dist/domains/flap/portal-bundle-merkle/private/helpers.js +4 -8
- package/dist/domains/flap/portal-bundle-merkle/private/sell.js +3 -3
- package/dist/domains/flap/portal-bundle-merkle/swap-buy-first-types.d.ts +2 -1
- package/dist/domains/flap/portal-bundle-merkle/swap-helpers.d.ts +2 -1
- package/dist/domains/flap/portal-bundle-merkle/swap-planning.js +5 -2
- package/dist/domains/flap/portal-bundle-merkle/swap-planning.test.d.ts +1 -0
- package/dist/domains/flap/portal-bundle-merkle/swap-planning.test.js +16 -0
- package/dist/domains/flap/portal-bundle-merkle/types-config.d.ts +2 -1
- package/dist/domains/merkle/index.d.ts +1 -1
- package/dist/domains/merkle/index.js +1 -1
- package/dist/exports/root-bundle-and-tooling.d.ts +1 -1
- package/dist/exports/root-bundle-and-tooling.js +1 -1
- package/dist/exports/root-foundations.d.ts +0 -1
- package/dist/exports/root-foundations.js +0 -1
- package/dist/utils/swap-helpers.d.ts +1 -15
- package/dist/utils/swap-helpers.js +0 -80
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**four.meme 和 Flap Protocol 统一 SDK - 生产环境 MEV 保护方案**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
提供完整的代币发行和交易功能,签名后通过 BlockRazor / 48.club / Emit 提交,确保 MEV 保护和原子性。
|
|
6
6
|
|
|
7
7
|
## 核心优势
|
|
8
8
|
|
|
@@ -20,9 +20,9 @@ npm i four-flap-meme-sdk ethers
|
|
|
20
20
|
|
|
21
21
|
**要求**:Node.js >= 18, ESM, ethers v6
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## 提交
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
SDK 只负责构建与签名;Merkle 网络客户端和 `submitBundleToMerkle` 已移除,请使用 BlockRazor、48.club 或 Emit 提交。
|
|
26
26
|
|
|
27
27
|
## 浏览器环境 CORS 支持
|
|
28
28
|
|
|
@@ -66,7 +66,6 @@ const result = await createTokenWithBundleBuyMerkle({
|
|
|
66
66
|
label: 'Meme'
|
|
67
67
|
},
|
|
68
68
|
config: {
|
|
69
|
-
apiKey: 'YOUR_MERKLE_API_KEY',
|
|
70
69
|
rpcUrl: 'https://bsc-dataseed.binance.org'
|
|
71
70
|
}
|
|
72
71
|
});
|
|
@@ -84,7 +83,7 @@ await batchBuyWithBundleMerkle({
|
|
|
84
83
|
privateKeys: ['0x...买家1', '0x...买家2', '0x...买家3'],
|
|
85
84
|
buyAmounts: ['0.3', '0.5', '0.2'],
|
|
86
85
|
tokenAddress: '0x...代币地址',
|
|
87
|
-
config: {
|
|
86
|
+
config: {}
|
|
88
87
|
});
|
|
89
88
|
```
|
|
90
89
|
|
|
@@ -98,7 +97,7 @@ await batchSellWithBundleMerkle({
|
|
|
98
97
|
privateKeys: ['0x...卖家1', '0x...卖家2'],
|
|
99
98
|
sellAmounts: ['1000', '2000'],
|
|
100
99
|
tokenAddress: '0x...代币地址',
|
|
101
|
-
config: {
|
|
100
|
+
config: {}
|
|
102
101
|
});
|
|
103
102
|
```
|
|
104
103
|
|
|
@@ -110,7 +109,7 @@ import { approveFourBatchMerkle } from 'four-flap-meme-sdk';
|
|
|
110
109
|
await approveFourBatchMerkle({
|
|
111
110
|
privateKeys: ['0x...地址1', '0x...地址2'],
|
|
112
111
|
tokenAddress: '0x...代币地址',
|
|
113
|
-
config: {
|
|
112
|
+
config: {}
|
|
114
113
|
});
|
|
115
114
|
```
|
|
116
115
|
|
|
@@ -131,7 +130,7 @@ await fourPancakeProxyBatchBuyMerkle({
|
|
|
131
130
|
['0x...WBNB', '0x...TOKEN']
|
|
132
131
|
],
|
|
133
132
|
routeType: 'v2',
|
|
134
|
-
config: {
|
|
133
|
+
config: {}
|
|
135
134
|
});
|
|
136
135
|
|
|
137
136
|
// V2 批量卖出
|
|
@@ -143,7 +142,7 @@ await fourPancakeProxyBatchSellMerkle({
|
|
|
143
142
|
['0x...TOKEN', '0x...WBNB']
|
|
144
143
|
],
|
|
145
144
|
routeType: 'v2',
|
|
146
|
-
config: {
|
|
145
|
+
config: {}
|
|
147
146
|
});
|
|
148
147
|
```
|
|
149
148
|
|
|
@@ -158,7 +157,7 @@ await fourPancakeProxyBatchBuyMerkle({
|
|
|
158
157
|
tokenOut: '0x...TOKEN',
|
|
159
158
|
fee: 2500, // 0.25%
|
|
160
159
|
routeType: 'v3-single',
|
|
161
|
-
config: {
|
|
160
|
+
config: {}
|
|
162
161
|
});
|
|
163
162
|
|
|
164
163
|
// V3 多跳买入
|
|
@@ -168,7 +167,7 @@ await fourPancakeProxyBatchBuyMerkle({
|
|
|
168
167
|
lpAddresses: ['0x...WBNB_USDT_LP', '0x...USDT_TOKEN_LP'],
|
|
169
168
|
exactTokenIn: '0x...WBNB',
|
|
170
169
|
routeType: 'v3-multi',
|
|
171
|
-
config: {
|
|
170
|
+
config: {}
|
|
172
171
|
});
|
|
173
172
|
```
|
|
174
173
|
|
|
@@ -196,7 +195,6 @@ const result = await flapCreateTokenWithBundleBuyMerkle({
|
|
|
196
195
|
meta: 'QmXxx...' // IPFS CID
|
|
197
196
|
},
|
|
198
197
|
config: {
|
|
199
|
-
apiKey: 'YOUR_MERKLE_API_KEY',
|
|
200
198
|
rpcUrl: 'https://bsc-dataseed.binance.org'
|
|
201
199
|
},
|
|
202
200
|
dexThresh: 1,
|
|
@@ -222,7 +220,7 @@ await flapBatchBuyWithBundleMerkle({
|
|
|
222
220
|
privateKeys: ['0x...买家1', '0x...买家2'],
|
|
223
221
|
buyAmounts: ['0.5', '1.0'],
|
|
224
222
|
tokenAddress: '0x...代币地址',
|
|
225
|
-
config: {
|
|
223
|
+
config: {}
|
|
226
224
|
});
|
|
227
225
|
|
|
228
226
|
// 批量卖出
|
|
@@ -231,7 +229,7 @@ await flapBatchSellWithBundleMerkle({
|
|
|
231
229
|
privateKeys: ['0x...卖家1', '0x...卖家2'],
|
|
232
230
|
sellAmounts: ['1000', '2000'],
|
|
233
231
|
tokenAddress: '0x...代币地址',
|
|
234
|
-
config: {
|
|
232
|
+
config: {}
|
|
235
233
|
});
|
|
236
234
|
```
|
|
237
235
|
|
|
@@ -253,7 +251,7 @@ await flapPancakeProxyBatchBuyMerkle({
|
|
|
253
251
|
['0x...WBNB', '0x...TOKEN']
|
|
254
252
|
],
|
|
255
253
|
routeType: 'v2',
|
|
256
|
-
config: {
|
|
254
|
+
config: {}
|
|
257
255
|
});
|
|
258
256
|
|
|
259
257
|
// V3 单跳卖出
|
|
@@ -265,7 +263,7 @@ await flapPancakeProxyBatchSellMerkle({
|
|
|
265
263
|
tokenOut: '0x...WBNB',
|
|
266
264
|
fee: 2500,
|
|
267
265
|
routeType: 'v3-single',
|
|
268
|
-
config: {
|
|
266
|
+
config: {}
|
|
269
267
|
});
|
|
270
268
|
```
|
|
271
269
|
|
|
@@ -275,7 +273,6 @@ await flapPancakeProxyBatchSellMerkle({
|
|
|
275
273
|
|
|
276
274
|
```typescript
|
|
277
275
|
type BundleMerkleConfig = {
|
|
278
|
-
apiKey: string; // Merkle API Key(必填)
|
|
279
276
|
rpcUrl?: string; // RPC 节点(可选)
|
|
280
277
|
bundleBlockOffset?: number; // Bundle 有效期,默认 200 区块
|
|
281
278
|
gasLimitMultiplier?: number; // Gas limit 倍数,默认 1.2
|
|
@@ -331,11 +328,8 @@ type BundleMerkleConfig = {
|
|
|
331
328
|
|
|
332
329
|
## 常见问题
|
|
333
330
|
|
|
334
|
-
**Q:
|
|
335
|
-
A:
|
|
336
|
-
|
|
337
|
-
**Q: Merkle API Key 如何获取?**
|
|
338
|
-
A: 访问 [Merkle.io](https://merkle.io) 注册并获取免费 API Key。
|
|
331
|
+
**Q: 现在如何提交 Bundle?**
|
|
332
|
+
A: 使用 BlockRazor、48.club 或 Emit service;Merkle 网络客户端已移除。
|
|
339
333
|
|
|
340
334
|
**Q: Bundle 交易失败怎么办?**
|
|
341
335
|
A: 检查配置参数(gasLimit、blockOffset、slippage),确保所有地址有足够余额,适当增大超时时间。
|
|
@@ -6,7 +6,8 @@ export type FourBuyFirstSignConfig = FourSignConfig & {
|
|
|
6
6
|
userType?: UserType;
|
|
7
7
|
};
|
|
8
8
|
export type FourBuyFirstConfig = CommonBundleConfig & {
|
|
9
|
-
|
|
9
|
+
/** @deprecated Merkle 提交已移除;字段仅为旧调用兼容,不再读取 */
|
|
10
|
+
apiKey?: string;
|
|
10
11
|
customRpcUrl?: string;
|
|
11
12
|
bundleBlockOffset?: number;
|
|
12
13
|
reserveGasBNB?: number;
|
|
@@ -22,7 +22,8 @@ export type FourSignConfig = {
|
|
|
22
22
|
bribeAmount?: number;
|
|
23
23
|
};
|
|
24
24
|
export type FourBundleMerkleConfig = {
|
|
25
|
-
|
|
25
|
+
/** @deprecated Merkle 提交已移除;字段仅为旧调用兼容,不再读取 */
|
|
26
|
+
apiKey?: string;
|
|
26
27
|
customRpcUrl?: string;
|
|
27
28
|
fourApiUrl?: string;
|
|
28
29
|
bundleBlockOffset?: number;
|
|
@@ -16,7 +16,8 @@ export type FourSwapSignConfig = {
|
|
|
16
16
|
};
|
|
17
17
|
/** Swap Merkle 配置(保留兼容) */
|
|
18
18
|
export type FourSwapConfig = CommonBundleConfig & {
|
|
19
|
-
|
|
19
|
+
/** @deprecated Merkle 提交已移除;字段仅为旧调用兼容,不再读取 */
|
|
20
|
+
apiKey?: string;
|
|
20
21
|
customRpcUrl?: string;
|
|
21
22
|
bundleBlockOffset?: number;
|
|
22
23
|
reserveGasBNB?: number;
|
|
@@ -113,14 +113,23 @@ export async function sweepErc20NoHop(ctx) {
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
// ✅ 第二步:生成归集交易(ERC20 全额归集,费用以原生币单独支付)
|
|
116
|
+
// 支付者可能既要归集又要发利润,必须先按“主交易 + 利润”预留连续 nonce;
|
|
117
|
+
// 且支付者不能再从批量 nonce 里取一次,否则 target 同时在 sources 时会拿到 m+1 做主交易、m 做利润。
|
|
116
118
|
let payerProfitNonce;
|
|
119
|
+
let payerMainNonce;
|
|
120
|
+
const payerAddressErc20 = extractProfit && nativeProfitAmount > 0n && profitPayerWalletErc20
|
|
121
|
+
? profitPayerWalletErc20.address.toLowerCase()
|
|
122
|
+
: undefined;
|
|
117
123
|
if (extractProfit && nativeProfitAmount > 0n && profitPayerWalletErc20) {
|
|
118
|
-
const
|
|
119
|
-
|
|
124
|
+
const payerIsSourceWithAmount = targetIndexInSources >= 0 && useTargetAsPayerErc20 && sweepAmounts[targetIndexInSources] > 0n;
|
|
125
|
+
const payerNeedsMain = !useTargetAsPayerErc20 || payerIsSourceWithAmount;
|
|
126
|
+
const nonces = await nonceManager.getNextNonceBatch(profitPayerWalletErc20, payerNeedsMain ? 2 : 1);
|
|
127
|
+
if (payerNeedsMain)
|
|
128
|
+
payerMainNonce = nonces[0];
|
|
129
|
+
payerProfitNonce = payerNeedsMain ? nonces[1] : nonces[0];
|
|
120
130
|
}
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
const walletsToSweepErc20 = wallets.filter((_, i) => sweepAmounts[i] > 0n && (useTargetAsPayerErc20 || i !== maxSweepIndex));
|
|
131
|
+
// 支付者已单独预留 nonce,不能再进入批量查询。
|
|
132
|
+
const walletsToSweepErc20 = wallets.filter((w, i) => sweepAmounts[i] > 0n && (!payerAddressErc20 || w.address.toLowerCase() !== payerAddressErc20));
|
|
124
133
|
const noncesErc20 = walletsToSweepErc20.length > 0 ? await nonceManager.getNextNoncesForWallets(walletsToSweepErc20) : [];
|
|
125
134
|
let nonceIdxErc20 = 0;
|
|
126
135
|
const txPromises = wallets.map(async (w, i) => {
|
|
@@ -129,10 +138,9 @@ export async function sweepErc20NoHop(ctx) {
|
|
|
129
138
|
return null;
|
|
130
139
|
// ✅ ERC20 归集:全部归集,不扣除代币(利润从 BNB 支付)
|
|
131
140
|
const actualToSend = toSend;
|
|
132
|
-
// ✅ 支付者使用预留的第一个 nonce,其他钱包使用批量获取的 nonce
|
|
133
141
|
let nonce;
|
|
134
|
-
if (
|
|
135
|
-
nonce =
|
|
142
|
+
if (payerMainNonce !== undefined && w.address.toLowerCase() === payerAddressErc20) {
|
|
143
|
+
nonce = payerMainNonce;
|
|
136
144
|
}
|
|
137
145
|
else {
|
|
138
146
|
nonce = noncesErc20[nonceIdxErc20++];
|
|
@@ -16,7 +16,7 @@ import { sweepNativeNoHop } from './native-no-hop.js';
|
|
|
16
16
|
import { sweepErc20NoHop } from './erc20-no-hop.js';
|
|
17
17
|
import { sweepWithHops } from './with-hops.js';
|
|
18
18
|
export async function sweepWithBundleMerkle(params) {
|
|
19
|
-
const { sourcePrivateKeys, target, targetPrivateKey, ratioPct, ratios, amount, amounts, tokenAddress, tokenDecimals, skipIfInsufficient = true, hopCount = 0, hopPrivateKeys, sources, config, } = params;
|
|
19
|
+
const { sourcePrivateKeys, target, targetPrivateKey, ratioPct, ratios, amount, amounts, tokenAddress, tokenDecimals, skipIfInsufficient = true, hopCount = 0, hopPrivateKeys, sources, config, startNonce, } = params;
|
|
20
20
|
// 快速返回空结果
|
|
21
21
|
if (!sourcePrivateKeys || sourcePrivateKeys.length === 0) {
|
|
22
22
|
return {
|
|
@@ -45,6 +45,17 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
45
45
|
const hopCountInput = resolveHopCountInput(actualKeys.length, hopCount, sources);
|
|
46
46
|
const preparedHops = providedHops ?? _generateHopWallets(actualKeys.length, hopCountInput);
|
|
47
47
|
const hasHops = preparedHops !== null;
|
|
48
|
+
if (providedHops) {
|
|
49
|
+
// 中转交易当前按 nonce 0/1 组装;复用已发过交易的 hop key 会直接冲突。
|
|
50
|
+
for (const group of providedHops) {
|
|
51
|
+
for (const key of group) {
|
|
52
|
+
const address = new Wallet(key).address;
|
|
53
|
+
if (await provider.getTransactionCount(address, 'latest') > 0) {
|
|
54
|
+
throw new Error('hopPrivateKeys 中包含已使用的地址;请换用未发过交易的中转私钥');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
48
59
|
const maxHopCount = hasHops ? Math.max(...preparedHops.map((h) => h.length)) : 0;
|
|
49
60
|
// 先按配置算上限,再向节点估算。估算成功就用估算值,归集才不会把没用完的 gas 退回钱包。
|
|
50
61
|
let finalGasLimit = _calculateGasLimit(config, isNative, hasHops, maxHopCount);
|
|
@@ -74,6 +85,9 @@ export async function sweepWithBundleMerkle(params) {
|
|
|
74
85
|
const bribeSignedTx = [];
|
|
75
86
|
// ✅ 共享 NonceManager:统一协调 贿赂/利润提取 等 targetWallet 交易的 nonce,避免同一钱包 nonce 冲突
|
|
76
87
|
const nonceManager = new NonceManager(provider);
|
|
88
|
+
if (startNonce !== undefined && actualKeys[0]) {
|
|
89
|
+
nonceManager.seedNextNonce(new Wallet(actualKeys[0]).address, startNonce);
|
|
90
|
+
}
|
|
77
91
|
// ✅ 贿赂交易放在最前面(由 targetWallet 签名)
|
|
78
92
|
if (needBribeTx && targetWallet) {
|
|
79
93
|
const [bribeGasPrice, bribeNonce] = await Promise.all([
|
|
@@ -15,6 +15,7 @@ export async function sweepNativeNoHop(ctx) {
|
|
|
15
15
|
let profitHopWallets;
|
|
16
16
|
const wallets = actualKeys.map((pk) => new Wallet(pk, provider));
|
|
17
17
|
const addresses = wallets.map((w) => w.address);
|
|
18
|
+
const targetIndexInSources = addresses.findIndex((addr) => addr.toLowerCase() === target.toLowerCase());
|
|
18
19
|
// ✅ 复用主流程共享的 NonceManager(贿赂交易已用它预留 targetWallet 的 nonce,避免利润提取与之冲突)
|
|
19
20
|
const nonceManager = ctx.nonceManager ?? new NonceManager(provider);
|
|
20
21
|
// ✅ 优化:并行获取 gasPrice 和余额
|
|
@@ -91,13 +92,20 @@ export async function sweepNativeNoHop(ctx) {
|
|
|
91
92
|
const profitPayerWallet = useTargetAsPayer ? targetWallet : maxSweepIndex >= 0 ? wallets[maxSweepIndex] : null;
|
|
92
93
|
// ✅ 先为支付者预留 nonce,确保 nonce 连续
|
|
93
94
|
let payerProfitNonce;
|
|
95
|
+
let payerMainNonce;
|
|
96
|
+
const payerAddress = extractProfit && totalProfit > 0n && profitPayerWallet
|
|
97
|
+
? profitPayerWallet.address.toLowerCase()
|
|
98
|
+
: undefined;
|
|
94
99
|
if (extractProfit && totalProfit > 0n && profitPayerWallet) {
|
|
95
|
-
const
|
|
96
|
-
|
|
100
|
+
const payerIsSourceWithAmount = targetIndexInSources >= 0 && useTargetAsPayer && sweepAmounts[targetIndexInSources] > 0n;
|
|
101
|
+
const payerNeedsMain = !useTargetAsPayer || payerIsSourceWithAmount;
|
|
102
|
+
const nonces = await nonceManager.getNextNonceBatch(profitPayerWallet, payerNeedsMain ? 2 : 1);
|
|
103
|
+
if (payerNeedsMain)
|
|
104
|
+
payerMainNonce = nonces[0];
|
|
105
|
+
payerProfitNonce = payerNeedsMain ? nonces[1] : nonces[0];
|
|
97
106
|
}
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
const walletsToSweep = wallets.filter((_, i) => sweepAmounts[i] > 0n && (useTargetAsPayer || i !== maxSweepIndex));
|
|
107
|
+
// 支付者已单独预留 nonce,不能再进入批量查询。
|
|
108
|
+
const walletsToSweep = wallets.filter((w, i) => sweepAmounts[i] > 0n && (!payerAddress || w.address.toLowerCase() !== payerAddress));
|
|
101
109
|
const nonces = walletsToSweep.length > 0 ? await nonceManager.getNextNoncesForWallets(walletsToSweep) : [];
|
|
102
110
|
let nonceIdx = 0;
|
|
103
111
|
const txPromises = wallets.map(async (w, i) => {
|
|
@@ -108,17 +116,17 @@ export async function sweepNativeNoHop(ctx) {
|
|
|
108
116
|
if (extractProfit && !useTargetAsPayer) {
|
|
109
117
|
// ✅ 如果由源钱包支付利润,支付者扣除所有利润总和;其他钱包不扣利润,归集干净
|
|
110
118
|
if (i === maxSweepIndex && totalProfit > 0n) {
|
|
111
|
-
actualToSend = toSend - totalProfit; //
|
|
119
|
+
actualToSend = toSend > totalProfit ? toSend - totalProfit : 0n; // 支付者扣除利润,不足则不发
|
|
112
120
|
}
|
|
113
121
|
else {
|
|
114
122
|
actualToSend = toSend; // 其他钱包不扣利润,归集全部
|
|
115
123
|
}
|
|
116
124
|
}
|
|
117
125
|
// ✅ 如果由 targetWallet 支付利润,所有源钱包都归集全部金额
|
|
118
|
-
//
|
|
126
|
+
// 支付者 actualToSend=0 时仍发 0 值交易,消费预留 nonce,避免利润 nonce 出现空洞。
|
|
119
127
|
let nonce;
|
|
120
|
-
if (
|
|
121
|
-
nonce =
|
|
128
|
+
if (payerMainNonce !== undefined && w.address.toLowerCase() === payerAddress) {
|
|
129
|
+
nonce = payerMainNonce;
|
|
122
130
|
}
|
|
123
131
|
else {
|
|
124
132
|
nonce = nonces[nonceIdx++];
|
package/dist/bundle/index.d.ts
CHANGED
|
@@ -5,4 +5,4 @@ export { getErc20Decimals, getErc20DecimalsMerkle, generateHopWallets, normalize
|
|
|
5
5
|
export { resolveChainName, createSignProvider, createSignWallet, resolveSignContext, normalizeItemsOrRecipientsAmounts, resolveProvidedHops, resolveHopCountInput, signBribeTransaction, type SignConfigInput, type HopItem, } from './sign-context-helpers.js';
|
|
6
6
|
export { BUNDLE_ERRORS, ERRORS, getBundleErrorMessage, getErrorMessage } from './errors.js';
|
|
7
7
|
export { getTxType, getGasPriceMultiplier, getBundleOptions, getGasPriceConfig, shouldExtractProfit, getProfitRateBps, getProfitRecipient, calculateProfit, calculateBatchProfit, getBribeAmount, type BundleGasConfigInput, type BundleMerkleConfigInput, } from './config-helpers.js';
|
|
8
|
-
export {
|
|
8
|
+
export { submitBundleToBlockRazor, submitMultipleBundlesToBlockRazor, submitMultipleBundlesToBlockRazorParallel, submitDirectToRpc, submitDirectToRpcSequential, submitDirectToRpcSequentialNoWait, submitDirectToRpcParallel, submitDirectToRpcSmart, type BlockRazorSubmitConfig, type BlockRazorSubmitResult, type DirectSubmitConfig, type DirectSubmitResult, type DirectTxResult, } from './submit.js';
|
package/dist/bundle/index.js
CHANGED
|
@@ -5,4 +5,4 @@ export { getErc20Decimals, getErc20DecimalsMerkle, generateHopWallets, normalize
|
|
|
5
5
|
export { resolveChainName, createSignProvider, createSignWallet, resolveSignContext, normalizeItemsOrRecipientsAmounts, resolveProvidedHops, resolveHopCountInput, signBribeTransaction, } from './sign-context-helpers.js';
|
|
6
6
|
export { BUNDLE_ERRORS, ERRORS, getBundleErrorMessage, getErrorMessage } from './errors.js';
|
|
7
7
|
export { getTxType, getGasPriceMultiplier, getBundleOptions, getGasPriceConfig, shouldExtractProfit, getProfitRateBps, getProfitRecipient, calculateProfit, calculateBatchProfit, getBribeAmount, } from './config-helpers.js';
|
|
8
|
-
export {
|
|
8
|
+
export { submitBundleToBlockRazor, submitMultipleBundlesToBlockRazor, submitMultipleBundlesToBlockRazorParallel, submitDirectToRpc, submitDirectToRpcSequential, submitDirectToRpcSequentialNoWait, submitDirectToRpcParallel, submitDirectToRpcSmart, } from './submit.js';
|
|
@@ -22,6 +22,8 @@ export declare function estimateSweepLimits(params: {
|
|
|
22
22
|
tokenAddress?: string;
|
|
23
23
|
nativeFallback: bigint;
|
|
24
24
|
tokenFallback: bigint;
|
|
25
|
+
/** 估算值安全垫;节点估值通常是刚好够,批量转账/合约收款方需要余量 */
|
|
26
|
+
bufferPercent?: number;
|
|
25
27
|
}): Promise<{
|
|
26
28
|
nativeGasLimit: bigint;
|
|
27
29
|
tokenGasLimit: bigint;
|
package/dist/bundle/internal.js
CHANGED
|
@@ -131,6 +131,10 @@ export async function batchGetTokenAndNativeBalances(provider, addresses, tokenA
|
|
|
131
131
|
}
|
|
132
132
|
/** 归集/分发:按节点估算这笔转账的 gas。失败返回 null,调用方再用固定上限。 */
|
|
133
133
|
export async function estimateSweepLimits(params) {
|
|
134
|
+
const withBuffer = (gas) => {
|
|
135
|
+
const pct = params.bufferPercent ?? 20;
|
|
136
|
+
return gas + (gas * BigInt(Math.max(0, Math.floor(pct)))) / 100n;
|
|
137
|
+
};
|
|
134
138
|
try {
|
|
135
139
|
if (!params.tokenAddress) {
|
|
136
140
|
const gas = await params.provider.estimateGas({
|
|
@@ -139,7 +143,7 @@ export async function estimateSweepLimits(params) {
|
|
|
139
143
|
value: 0n,
|
|
140
144
|
});
|
|
141
145
|
if (gas > 0n)
|
|
142
|
-
return { nativeGasLimit: gas, tokenGasLimit: gas, estimated: true };
|
|
146
|
+
return { nativeGasLimit: withBuffer(gas), tokenGasLimit: withBuffer(gas), estimated: true };
|
|
143
147
|
}
|
|
144
148
|
else {
|
|
145
149
|
const iface = new ethers.Interface(['function transfer(address,uint256) returns (bool)']);
|
|
@@ -155,8 +159,8 @@ export async function estimateSweepLimits(params) {
|
|
|
155
159
|
]);
|
|
156
160
|
if (tokenGas > 0n) {
|
|
157
161
|
return {
|
|
158
|
-
nativeGasLimit: nativeGas > 0n ? nativeGas : params.nativeFallback,
|
|
159
|
-
tokenGasLimit: tokenGas,
|
|
162
|
+
nativeGasLimit: nativeGas > 0n ? withBuffer(nativeGas) : params.nativeFallback,
|
|
163
|
+
tokenGasLimit: withBuffer(tokenGas),
|
|
160
164
|
estimated: true,
|
|
161
165
|
};
|
|
162
166
|
}
|
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
* Bundle 提交 barrel
|
|
3
3
|
*
|
|
4
4
|
* 原超长文件按提交后端拆分为:
|
|
5
|
-
* - helpers.ts:
|
|
6
|
-
* - merkle.ts:Merkle 提交后端
|
|
5
|
+
* - helpers.ts:BlockRazor 共用批量提交辅助
|
|
7
6
|
* - blockrazor.ts:BlockRazor 提交后端
|
|
8
7
|
* - direct.ts:逐笔 RPC 广播后端
|
|
9
8
|
*
|
|
10
9
|
* 对外 export 符号与路径保持不变。
|
|
11
10
|
*/
|
|
12
|
-
export { submitBundleToMerkle, submitMultipleBundles, submitMultipleBundlesParallel, type MerkleSubmitConfig, type SubmitBundleResult, } from './merkle.js';
|
|
13
11
|
export { submitBundleToBlockRazor, submitMultipleBundlesToBlockRazor, submitMultipleBundlesToBlockRazorParallel, type BlockRazorSubmitConfig, type BlockRazorSubmitResult, } from './blockrazor.js';
|
|
14
12
|
export { submitDirectToRpc, submitDirectToRpcSequential, submitDirectToRpcSequentialNoWait, submitDirectToRpcParallel, submitDirectToRpcSmart, type DirectSubmitConfig, type DirectTxResult, type DirectSubmitResult, } from './direct.js';
|
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
* Bundle 提交 barrel
|
|
3
3
|
*
|
|
4
4
|
* 原超长文件按提交后端拆分为:
|
|
5
|
-
* - helpers.ts:
|
|
6
|
-
* - merkle.ts:Merkle 提交后端
|
|
5
|
+
* - helpers.ts:BlockRazor 共用批量提交辅助
|
|
7
6
|
* - blockrazor.ts:BlockRazor 提交后端
|
|
8
7
|
* - direct.ts:逐笔 RPC 广播后端
|
|
9
8
|
*
|
|
10
9
|
* 对外 export 符号与路径保持不变。
|
|
11
10
|
*/
|
|
12
|
-
export { submitBundleToMerkle, submitMultipleBundles, submitMultipleBundlesParallel, } from './merkle.js';
|
|
13
11
|
export { submitBundleToBlockRazor, submitMultipleBundlesToBlockRazor, submitMultipleBundlesToBlockRazorParallel, } from './blockrazor.js';
|
|
14
12
|
export { submitDirectToRpc, submitDirectToRpcSequential, submitDirectToRpcSequentialNoWait, submitDirectToRpcParallel, submitDirectToRpcSmart, } from './direct.js';
|
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export * from './constants.js';
|
|
7
7
|
export * as platforms from './platforms/index.js';
|
|
8
|
-
export { disperseWithBundleMerkle, sweepWithBundleMerkle, pairwiseTransferWithBundleMerkle, fourBundleSwapMerkle, fourBatchSwapMerkle, fourQuickBatchSwapMerkle, fourBundleBuyFirstMerkle, createTokenWithBundleBuyMerkle as fourCreateTokenWithBundleBuyMerkle, batchSellWithBundleMerkle as fourBatchSellWithBundleMerkle, fourBatchPrivateBuyMerkle,
|
|
8
|
+
export { disperseWithBundleMerkle, sweepWithBundleMerkle, pairwiseTransferWithBundleMerkle, fourBundleSwapMerkle, fourBatchSwapMerkle, fourQuickBatchSwapMerkle, fourBundleBuyFirstMerkle, createTokenWithBundleBuyMerkle as fourCreateTokenWithBundleBuyMerkle, batchSellWithBundleMerkle as fourBatchSellWithBundleMerkle, fourBatchPrivateBuyMerkle, submitBundleToBlockRazor, submitDirectToRpc, submitDirectToRpcSmart, approveFourTokenManagerBatch, } from './platforms/four/index.js';
|
|
9
9
|
export { pancakeBundleSwapMerkle, pancakeBatchSwapMerkle, pancakeQuickBatchSwapMerkle, pancakeCrossSwapMerkle, pancakeBundleBuyFirstMerkle, } from './platforms/pancake/index.js';
|
|
10
10
|
export { IroFactoryQuery, createProject as iroCreateProject, encodeCreateProjectCall as iroEncodeCreateProjectCall, parseCreateProjectEvent as iroParseCreateProjectEvent, IroTokenQuery, subscribe as iroSubscribe, pledge as iroPledge, batchSubscribe as iroBatchSubscribe, batchSubscribeForSubmit as iroBatchSubscribeForSubmit, IroPoolQuery, poolRemovePledge as iroPoolRemovePledge, poolExtract as iroPoolExtract, poolTransferAwards as iroPoolTransferAwards, batchExtract as iroBatchExtract, IRO_FACTORY_ADDRESS, IRO_FACTORY_ABI, IRO_TOKEN_ABI, IRO_POOL_ABI, CREATE_PROJECT_GAS_LIMIT as BSC_CREATE_PROJECT_GAS_LIMIT, IRO_PLATFORM_URL, IRO_TESTNET_PLATFORM_URL, setIroApiBase, setIroTestApiBase, iroApiCreateProject, iroApiUploadImage, } from './platforms/iro/index.js';
|
package/dist/chains/bsc/index.js
CHANGED
|
@@ -20,7 +20,7 @@ fourBundleBuyFirstMerkle,
|
|
|
20
20
|
// 发币 / 批量卖 / 私有买入(消费者白名单别名)
|
|
21
21
|
createTokenWithBundleBuyMerkle as fourCreateTokenWithBundleBuyMerkle, batchSellWithBundleMerkle as fourBatchSellWithBundleMerkle, fourBatchPrivateBuyMerkle,
|
|
22
22
|
// 提交
|
|
23
|
-
|
|
23
|
+
submitBundleToBlockRazor, submitDirectToRpc, submitDirectToRpcSmart,
|
|
24
24
|
// 授权
|
|
25
25
|
approveFourTokenManagerBatch, } from './platforms/four/index.js';
|
|
26
26
|
// ============================================================================
|
|
@@ -48,7 +48,8 @@ export interface V3MultiRouteParams {
|
|
|
48
48
|
}
|
|
49
49
|
export type RouteParams = V2RouteParams | V3SingleRouteParams | V3MultiRouteParams;
|
|
50
50
|
export interface PancakeBuyFirstConfig extends CommonBundleConfig {
|
|
51
|
-
|
|
51
|
+
/** @deprecated Merkle 提交已移除;字段仅为旧调用兼容,不再读取 */
|
|
52
|
+
apiKey?: string;
|
|
52
53
|
customRpcUrl?: string;
|
|
53
54
|
bundleBlockOffset?: number;
|
|
54
55
|
reserveGasBNB?: number;
|
|
@@ -103,7 +103,8 @@ export interface PancakeSwapSignConfig {
|
|
|
103
103
|
v2RouterAddress?: string;
|
|
104
104
|
}
|
|
105
105
|
export interface PancakeSwapConfig extends CommonBundleConfig {
|
|
106
|
-
|
|
106
|
+
/** @deprecated Merkle 提交已移除;字段仅为旧调用兼容,不再读取 */
|
|
107
|
+
apiKey?: string;
|
|
107
108
|
customRpcUrl?: string;
|
|
108
109
|
bundleBlockOffset?: number;
|
|
109
110
|
reserveGasBNB?: number;
|
|
@@ -19,6 +19,6 @@ export { sweepWithBundleMerkle } from '../../../../bundle/four-meme/utils-sweep.
|
|
|
19
19
|
export { pairwiseTransferWithBundleMerkle } from '../../../../bundle/four-meme/utils-pairwise.js';
|
|
20
20
|
export { fourPancakeProxyBatchBuyMerkle, fourPancakeProxyBatchSellMerkle, approveFourPancakeProxy, approveFourPancakeProxyBatch, } from '../../../../bundle/four-meme/pancake-proxy.js';
|
|
21
21
|
export { approveFourTokenManagerBatch } from '../../../../bundle/four-meme/approve-tokenmanager.js';
|
|
22
|
-
export {
|
|
22
|
+
export { submitBundleToBlockRazor, submitMultipleBundlesToBlockRazor, submitMultipleBundlesToBlockRazorParallel, submitDirectToRpc, submitDirectToRpcSequential, submitDirectToRpcSequentialNoWait, submitDirectToRpcParallel, submitDirectToRpcSmart, type BlockRazorSubmitConfig, type BlockRazorSubmitResult, type DirectSubmitConfig, type DirectSubmitResult, type DirectTxResult, } from '../../../../bundle/submit.js';
|
|
23
23
|
export { fourBundleBuyFirstMerkle } from '../../../../bundle/four-meme/swap-buy-first.js';
|
|
24
24
|
export { fourBundleSwapMerkle, fourBatchSwapMerkle, fourQuickBatchSwapMerkle, } from '../../../../bundle/four-meme/swap.js';
|
|
@@ -24,7 +24,7 @@ export { fourPancakeProxyBatchBuyMerkle, fourPancakeProxyBatchSellMerkle, approv
|
|
|
24
24
|
// TokenManager 授权方法
|
|
25
25
|
export { approveFourTokenManagerBatch } from '../../../../bundle/four-meme/approve-tokenmanager.js';
|
|
26
26
|
// Bundle 提交方法(服务器端使用)
|
|
27
|
-
export {
|
|
27
|
+
export { submitBundleToBlockRazor, submitMultipleBundlesToBlockRazor, submitMultipleBundlesToBlockRazorParallel, submitDirectToRpc, submitDirectToRpcSequential, submitDirectToRpcSequentialNoWait, submitDirectToRpcParallel, submitDirectToRpcSmart, } from '../../../../bundle/submit.js';
|
|
28
28
|
// 先买后卖换手方法
|
|
29
29
|
export { fourBundleBuyFirstMerkle } from '../../../../bundle/four-meme/swap-buy-first.js';
|
|
30
30
|
// 换手方法
|
|
@@ -146,16 +146,18 @@ export class BlockRazorClient {
|
|
|
146
146
|
}
|
|
147
147
|
// 验证 Gas Price(最低 0.05 Gwei)
|
|
148
148
|
for (const rawTx of params.txs) {
|
|
149
|
+
let tx;
|
|
149
150
|
try {
|
|
150
|
-
|
|
151
|
-
const gasPrice = tx.gasPrice || 0n;
|
|
152
|
-
const minGasPrice = ethers.parseUnits(String(MIN_GAS_PRICE_GWEI), 'gwei');
|
|
153
|
-
if (gasPrice < minGasPrice) {
|
|
154
|
-
throw new Error(`Transaction gas price (${ethers.formatUnits(gasPrice, 'gwei')} gwei) is below minimum (${MIN_GAS_PRICE_GWEI} gwei)`);
|
|
155
|
-
}
|
|
151
|
+
tx = Transaction.from(rawTx);
|
|
156
152
|
}
|
|
157
153
|
catch {
|
|
158
154
|
// 解析失败,跳过检查
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const gasPrice = tx.gasPrice && tx.gasPrice > 0n ? tx.gasPrice : tx.maxFeePerGas ?? 0n;
|
|
158
|
+
const minGasPrice = ethers.parseUnits(String(MIN_GAS_PRICE_GWEI), 'gwei');
|
|
159
|
+
if (gasPrice < minGasPrice) {
|
|
160
|
+
throw new Error(`Transaction gas price (${ethers.formatUnits(gasPrice, 'gwei')} gwei) is below minimum (${MIN_GAS_PRICE_GWEI} gwei)`);
|
|
159
161
|
}
|
|
160
162
|
}
|
|
161
163
|
// 构建请求参数
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Wallet } from 'ethers';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { BlockRazorClient } from './blockrazor.js';
|
|
4
|
+
describe('BlockRazor gas price validation', () => {
|
|
5
|
+
it('低于 0.05 Gwei 的签名交易被拒绝', async () => {
|
|
6
|
+
const client = new BlockRazorClient({
|
|
7
|
+
apiKey: 'test',
|
|
8
|
+
chainId: 56,
|
|
9
|
+
customRpcUrl: 'http://127.0.0.1:1',
|
|
10
|
+
});
|
|
11
|
+
const wallet = Wallet.createRandom();
|
|
12
|
+
const raw = await wallet.signTransaction({
|
|
13
|
+
to: Wallet.createRandom().address,
|
|
14
|
+
value: 0n,
|
|
15
|
+
nonce: 0,
|
|
16
|
+
gasLimit: 21000n,
|
|
17
|
+
gasPrice: 1n,
|
|
18
|
+
chainId: 56,
|
|
19
|
+
type: 0,
|
|
20
|
+
});
|
|
21
|
+
await expect(client.sendBundleRaw({ txs: [raw] })).rejects.toThrow(/below minimum/);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -3,6 +3,5 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { FourClient, buildLoginMessage, buildCreateTokenReq, type FourConfig, type GenerateNonceReq, type LoginReq, type CreateTokenReq, type CreateTokenResp, type FourTokenTaxInfo, type FourRaisedToken, type FourTokenLabel, } from './four.js';
|
|
5
5
|
export { Club48Client, sendBatchPrivateTransactions, sendBackrunBundle, type BundleParams, type BundleStatus, type Club48Config, } from './club48.js';
|
|
6
|
-
export { MerkleClient, createMerkleClient, type MerkleConfig, type BundleParams as MerkleBundleParams, type SendBundleOptions, type BundleResult, type TransactionResult, } from './merkle.js';
|
|
7
6
|
export { BlockRazorClient, createBlockRazorClient, BLOCKRAZOR_BUILDER_EOA, type BlockRazorConfig, type BlockRazorBundleParams, type SendBundleOptions as BlockRazorSendBundleOptions, type BundleResult as BlockRazorBundleResult, type TransactionResult as BlockRazorTransactionResult, type IncentiveTransactionParams, } from './blockrazor.js';
|
|
8
7
|
export { EmitServiceClient, submitToEmitServiceBlockRazor, type EmitServiceClientConfig, type EmitServiceSubmitRequest, type EmitServiceSubmitResponse, type EmitServiceSubmitResponseData, type EmitServiceRouteConfigResponse, } from './emitservice.js';
|
|
@@ -3,6 +3,5 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { FourClient, buildLoginMessage, buildCreateTokenReq, } from './four.js';
|
|
5
5
|
export { Club48Client, sendBatchPrivateTransactions, sendBackrunBundle, } from './club48.js';
|
|
6
|
-
export { MerkleClient, createMerkleClient, } from './merkle.js';
|
|
7
6
|
export { BlockRazorClient, createBlockRazorClient, BLOCKRAZOR_BUILDER_EOA, } from './blockrazor.js';
|
|
8
7
|
export { EmitServiceClient, submitToEmitServiceBlockRazor, } from './emitservice.js';
|
|
@@ -38,12 +38,15 @@ export class MerkleClient {
|
|
|
38
38
|
constructor(config) {
|
|
39
39
|
this.chainId = config.chainId;
|
|
40
40
|
this.chainName = config.chainId === CHAINS.BSC.chainId ? 'bsc' : 'ethereum';
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
const
|
|
44
|
-
? CHAINS.BSC.
|
|
45
|
-
: CHAINS.ETHEREUM.
|
|
46
|
-
const rpcUrl = config.customRpcUrl ||
|
|
41
|
+
// Blink/Merkle 的 bundle RPC 是带 API key 的节点 endpoint;公共 BSC 节点
|
|
42
|
+
// 不支持 eth_sendBundle。customRpcUrl 可显式覆盖(需自带 key)。
|
|
43
|
+
const bundleBase = config.chainId === CHAINS.BSC.chainId
|
|
44
|
+
? CHAINS.BSC.bundleRpcUrl
|
|
45
|
+
: CHAINS.ETHEREUM.bundleRpcUrl;
|
|
46
|
+
const rpcUrl = config.customRpcUrl || (bundleBase ? `${bundleBase}/v1/${config.apiKey}` : '');
|
|
47
|
+
if (!rpcUrl) {
|
|
48
|
+
throw new Error('Merkle bundle RPC endpoint is not configured');
|
|
49
|
+
}
|
|
47
50
|
this.provider = createProvider(rpcUrl, this.chainId, this.chainName);
|
|
48
51
|
}
|
|
49
52
|
/**
|
|
@@ -366,10 +369,12 @@ export class MerkleClient {
|
|
|
366
369
|
*/
|
|
367
370
|
getProviderInfo() {
|
|
368
371
|
const rpcConnection = this.provider._getConnection?.();
|
|
372
|
+
const rawUrl = rpcConnection?.url ?? 'unknown';
|
|
369
373
|
return {
|
|
370
374
|
chainId: this.chainId,
|
|
371
375
|
chainName: this.chainName,
|
|
372
|
-
|
|
376
|
+
// API key 在路径中,调试输出只保留脱敏后的 endpoint。
|
|
377
|
+
rpcUrl: rawUrl.replace(/\/v1\/[^/?#]+/, '/v1/***'),
|
|
373
378
|
};
|
|
374
379
|
}
|
|
375
380
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { MerkleClient } from './merkle.js';
|
|
3
|
+
describe('MerkleClient endpoint', () => {
|
|
4
|
+
it('默认使用带 apiKey 的 Blink BSC bundle RPC', () => {
|
|
5
|
+
const client = new MerkleClient({ apiKey: 'test-key', chainId: 56 });
|
|
6
|
+
expect(client.getProviderInfo().rpcUrl).toContain('https://bsc.blinklabs.xyz/v1/***');
|
|
7
|
+
});
|
|
8
|
+
});
|
|
@@ -14,6 +14,8 @@ export declare class NonceManager {
|
|
|
14
14
|
private readOnchainNonce;
|
|
15
15
|
getNextNonce(wallet: Wallet | string): Promise<number>;
|
|
16
16
|
getNextNonceBatch(wallet: Wallet | string, count: number): Promise<number[]>;
|
|
17
|
+
/** 预填某个地址的下一个 nonce(用于调用方显式 startNonce 的并行场景)。 */
|
|
18
|
+
seedNextNonce(wallet: Wallet | string, nextNonce: number): void;
|
|
17
19
|
clearTemp(): void;
|
|
18
20
|
getTempCachedNonce(address: string): number | undefined;
|
|
19
21
|
getNextNoncesForWallets(wallets: (Wallet | string)[]): Promise<number[]>;
|