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,400 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Merkle.io Bundle Service 客户端
|
|
3
|
-
*
|
|
4
|
-
* 提供基于 Merkle 的 MEV 保护和捆绑交易服务
|
|
5
|
-
*
|
|
6
|
-
* 官方文档: https://docs.merkle.io/
|
|
7
|
-
*
|
|
8
|
-
* 使用示例:
|
|
9
|
-
* ```typescript
|
|
10
|
-
* import { MerkleClient } from 'four-flap-meme-sdk';
|
|
11
|
-
*
|
|
12
|
-
* const merkle = new MerkleClient({
|
|
13
|
-
* apiKey: 'pk_mbs_...',
|
|
14
|
-
* chainId: 56
|
|
15
|
-
* });
|
|
16
|
-
*
|
|
17
|
-
* const bundleHash = await merkle.sendBundle({
|
|
18
|
-
* transactions: [tx1, tx2, tx3],
|
|
19
|
-
* targetBlock: currentBlock + 2
|
|
20
|
-
* });
|
|
21
|
-
* ```
|
|
22
|
-
*/
|
|
23
|
-
import { JsonRpcProvider, Transaction } from 'ethers';
|
|
24
|
-
import { CHAINS } from '../constants/index.js';
|
|
25
|
-
/**
|
|
26
|
-
* Merkle 客户端
|
|
27
|
-
*/
|
|
28
|
-
export class MerkleClient {
|
|
29
|
-
constructor(config) {
|
|
30
|
-
// ✅ 区块号缓存(减少 RPC 调用)
|
|
31
|
-
// 注意:TTL 设为 1 秒,确保区块号尽可能新鲜,减少 "Missed inclusion" 风险
|
|
32
|
-
this.blockNumberCache = null;
|
|
33
|
-
this.chainId = config.chainId;
|
|
34
|
-
this.chainName = config.chainId === CHAINS.BSC.chainId ? 'bsc' : 'ethereum';
|
|
35
|
-
// ✅ 修复:移除弃用的 mempool.merkle.io RPC,使用公共 RPC 作为默认值
|
|
36
|
-
// 优先使用用户自定义 RPC,否则使用官方公共 RPC
|
|
37
|
-
const defaultRpc = config.chainId === CHAINS.BSC.chainId
|
|
38
|
-
? CHAINS.BSC.rpcUrls.backup // bsc-dataseed1
|
|
39
|
-
: CHAINS.ETHEREUM.rpcUrls.default;
|
|
40
|
-
const rpcUrl = config.customRpcUrl || defaultRpc;
|
|
41
|
-
this.provider = new JsonRpcProvider(rpcUrl, {
|
|
42
|
-
chainId: this.chainId,
|
|
43
|
-
name: this.chainName,
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* 获取 Provider
|
|
48
|
-
*/
|
|
49
|
-
getProvider() {
|
|
50
|
-
return this.provider;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* 获取当前区块号(带缓存,减少 RPC 调用)
|
|
54
|
-
* @param forceRefresh 是否强制刷新缓存
|
|
55
|
-
*/
|
|
56
|
-
async getBlockNumber(forceRefresh = false) {
|
|
57
|
-
const now = Date.now();
|
|
58
|
-
// ✅ 检查缓存
|
|
59
|
-
if (!forceRefresh && this.blockNumberCache) {
|
|
60
|
-
const age = now - this.blockNumberCache.timestamp;
|
|
61
|
-
if (age < MerkleClient.BLOCK_CACHE_TTL_MS) {
|
|
62
|
-
// 缓存有效,直接返回(加 1 作为安全偏移)
|
|
63
|
-
return this.blockNumberCache.value;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
// ✅ 缓存过期或强制刷新,查询 RPC
|
|
67
|
-
const blockNumber = await this.provider.getBlockNumber();
|
|
68
|
-
this.blockNumberCache = { value: blockNumber, timestamp: now };
|
|
69
|
-
return blockNumber;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* 获取 Gas 价格信息
|
|
73
|
-
*/
|
|
74
|
-
async getFeeData() {
|
|
75
|
-
return await this.provider.getFeeData();
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* 获取网络状态(用于 Bundle 参数优化)
|
|
79
|
-
*/
|
|
80
|
-
async getNetworkStatus() {
|
|
81
|
-
const [currentBlock, feeData] = await Promise.all([
|
|
82
|
-
this.getBlockNumber(),
|
|
83
|
-
this.getFeeData()
|
|
84
|
-
]);
|
|
85
|
-
// 动态获取 gas price,并计算建议的最小值
|
|
86
|
-
const baseGasPrice = feeData.gasPrice || feeData.maxFeePerGas || 3000000000n;
|
|
87
|
-
// 建议至少提高 80% 以确保交易被打包
|
|
88
|
-
const suggestedMinGasPrice = (baseGasPrice * 180n) / 100n;
|
|
89
|
-
// BSC 平均 3 秒,Ethereum 平均 12 秒
|
|
90
|
-
const avgBlockTime = this.chainId === CHAINS.BSC.chainId
|
|
91
|
-
? CHAINS.BSC.blockTime / 1000
|
|
92
|
-
: CHAINS.ETHEREUM.blockTime / 1000;
|
|
93
|
-
return {
|
|
94
|
-
currentBlock,
|
|
95
|
-
gasPrice: baseGasPrice,
|
|
96
|
-
suggestedMinGasPrice,
|
|
97
|
-
avgBlockTime
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* 发送捆绑交易(底层方法)
|
|
102
|
-
*
|
|
103
|
-
* @param params Bundle 参数
|
|
104
|
-
* @returns Bundle Hash
|
|
105
|
-
*/
|
|
106
|
-
async sendBundleRaw(params) {
|
|
107
|
-
// 验证输入
|
|
108
|
-
if (!params.txs || params.txs.length === 0) {
|
|
109
|
-
throw new Error('Bundle transactions cannot be empty');
|
|
110
|
-
}
|
|
111
|
-
if (!params.blockNumber) {
|
|
112
|
-
throw new Error('Target block number is required');
|
|
113
|
-
}
|
|
114
|
-
const bundleParams = {
|
|
115
|
-
txs: params.txs,
|
|
116
|
-
blockNumber: params.blockNumber,
|
|
117
|
-
minTimestamp: params.minTimestamp || 0,
|
|
118
|
-
maxTimestamp: params.maxTimestamp || 0,
|
|
119
|
-
};
|
|
120
|
-
try {
|
|
121
|
-
const result = await this.provider.send('eth_sendBundle', [bundleParams]);
|
|
122
|
-
// 规范化返回值
|
|
123
|
-
if (typeof result === 'string') {
|
|
124
|
-
return result;
|
|
125
|
-
}
|
|
126
|
-
// 如果是对象,尝试提取 bundleHash
|
|
127
|
-
if (result && typeof result === 'object') {
|
|
128
|
-
// Merkle 可能返回 { bundleHash: "0x..." }
|
|
129
|
-
if ('bundleHash' in result) {
|
|
130
|
-
return String(result.bundleHash);
|
|
131
|
-
}
|
|
132
|
-
// 或者直接是哈希
|
|
133
|
-
if ('hash' in result) {
|
|
134
|
-
return String(result.hash);
|
|
135
|
-
}
|
|
136
|
-
// 否则转为 JSON 字符串(但这不是理想情况)
|
|
137
|
-
return JSON.stringify(result);
|
|
138
|
-
}
|
|
139
|
-
throw new Error('Invalid bundle result format');
|
|
140
|
-
}
|
|
141
|
-
catch (error) {
|
|
142
|
-
// 增强错误信息
|
|
143
|
-
const errorMsg = error.message || String(error);
|
|
144
|
-
throw new Error(`Send bundle failed: ${errorMsg}`);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* 发送捆绑交易(高级方法,带智能优化和重试)
|
|
149
|
-
*
|
|
150
|
-
* @param options 发送选项
|
|
151
|
-
* @returns Bundle 结果
|
|
152
|
-
*/
|
|
153
|
-
async sendBundle(options) {
|
|
154
|
-
// 验证输入
|
|
155
|
-
if (!options.transactions || options.transactions.length === 0) {
|
|
156
|
-
throw new Error('Transactions array cannot be empty');
|
|
157
|
-
}
|
|
158
|
-
const minBlockOffset = options.minBlockOffset ?? 3; // 默认至少3个区块的窗口
|
|
159
|
-
const autoRetry = options.autoRetry ?? false;
|
|
160
|
-
const maxRetries = options.maxRetries ?? 2;
|
|
161
|
-
let attempt = 0;
|
|
162
|
-
let lastError = null;
|
|
163
|
-
while (attempt <= (autoRetry ? maxRetries : 0)) {
|
|
164
|
-
try {
|
|
165
|
-
// 实时获取当前区块(每次尝试都重新获取)
|
|
166
|
-
const currentBlock = await this.getBlockNumber();
|
|
167
|
-
// 确定目标区块
|
|
168
|
-
let targetBlock;
|
|
169
|
-
if (options.targetBlock) {
|
|
170
|
-
targetBlock = options.targetBlock;
|
|
171
|
-
// 检查目标区块是否已经过期
|
|
172
|
-
if (targetBlock <= currentBlock) {
|
|
173
|
-
if (autoRetry && attempt < maxRetries) {
|
|
174
|
-
attempt++;
|
|
175
|
-
await new Promise(resolve => setTimeout(resolve, 300)); // 短暂延迟
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
|
-
throw new Error(`Target block ${targetBlock} has already passed (current block: ${currentBlock})`);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
// 动态计算:确保至少有 minBlockOffset 个区块的窗口
|
|
183
|
-
const requestedOffset = options.blockOffset ?? 3;
|
|
184
|
-
const actualOffset = Math.max(requestedOffset, minBlockOffset);
|
|
185
|
-
targetBlock = currentBlock + actualOffset;
|
|
186
|
-
}
|
|
187
|
-
// 转换为十六进制
|
|
188
|
-
const targetBlockHex = '0x' + targetBlock.toString(16);
|
|
189
|
-
// 发送 Bundle
|
|
190
|
-
const bundleHash = await this.sendBundleRaw({
|
|
191
|
-
txs: options.transactions,
|
|
192
|
-
blockNumber: targetBlockHex,
|
|
193
|
-
minTimestamp: options.minTimestamp,
|
|
194
|
-
maxTimestamp: options.maxTimestamp,
|
|
195
|
-
});
|
|
196
|
-
// ✅ 优化:并行提取交易哈希(map 比 for 循环更高效)
|
|
197
|
-
const txHashes = options.transactions.map(rawTx => {
|
|
198
|
-
try {
|
|
199
|
-
return Transaction.from(rawTx).hash || '';
|
|
200
|
-
}
|
|
201
|
-
catch {
|
|
202
|
-
return '';
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
return {
|
|
206
|
-
bundleHash,
|
|
207
|
-
txHashes,
|
|
208
|
-
targetBlock,
|
|
209
|
-
txCount: options.transactions.length,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
catch (error) {
|
|
213
|
-
lastError = error;
|
|
214
|
-
// 如果启用了重试且还有重试次数
|
|
215
|
-
if (autoRetry && attempt < maxRetries) {
|
|
216
|
-
attempt++;
|
|
217
|
-
// 等待一个区块时间再重试
|
|
218
|
-
const blockTime = this.chainId === CHAINS.BSC.chainId
|
|
219
|
-
? CHAINS.BSC.blockTime
|
|
220
|
-
: CHAINS.ETHEREUM.blockTime;
|
|
221
|
-
await new Promise(resolve => setTimeout(resolve, blockTime));
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
// 没有重试或已达最大重试次数
|
|
225
|
-
throw error;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
// 如果执行到这里,说明所有重试都失败了
|
|
229
|
-
throw lastError || new Error('Bundle submission failed after all retries');
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* 等待 Bundle 中的交易确认
|
|
233
|
-
*
|
|
234
|
-
* @param txHashes 交易哈希列表
|
|
235
|
-
* @param confirmations 确认数(默认 1)
|
|
236
|
-
* @param timeout 超时时间(毫秒,默认 120000)
|
|
237
|
-
* @returns 交易结果列表
|
|
238
|
-
*/
|
|
239
|
-
async waitForBundleConfirmation(txHashes, confirmations = 1, timeout = 120000) {
|
|
240
|
-
const promises = txHashes.map(async (hash, index) => {
|
|
241
|
-
try {
|
|
242
|
-
const receipt = await this.provider.waitForTransaction(hash, confirmations, timeout);
|
|
243
|
-
if (!receipt) {
|
|
244
|
-
return {
|
|
245
|
-
index,
|
|
246
|
-
hash,
|
|
247
|
-
success: false,
|
|
248
|
-
error: 'Transaction not found',
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
let gasCost;
|
|
252
|
-
if (receipt.gasUsed && receipt.gasPrice) {
|
|
253
|
-
// 使用 BigInt 避免精度丢失
|
|
254
|
-
try {
|
|
255
|
-
const costWei = receipt.gasUsed * receipt.gasPrice;
|
|
256
|
-
// 转换为 ETH/BNB(保持精度)
|
|
257
|
-
const costEth = Number(costWei) / 1e18;
|
|
258
|
-
gasCost = costEth.toFixed(8); // 增加精度到 8 位小数
|
|
259
|
-
}
|
|
260
|
-
catch (error) {
|
|
261
|
-
// 如果计算溢出,使用字符串处理
|
|
262
|
-
const costWei = receipt.gasUsed * receipt.gasPrice;
|
|
263
|
-
gasCost = (Number(costWei / 1000000000n) / 1e9).toFixed(8);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
return {
|
|
267
|
-
index,
|
|
268
|
-
hash,
|
|
269
|
-
success: receipt.status === 1,
|
|
270
|
-
blockNumber: receipt.blockNumber,
|
|
271
|
-
gasUsed: receipt.gasUsed.toString(),
|
|
272
|
-
effectiveGasPrice: receipt.gasPrice?.toString(),
|
|
273
|
-
gasCost,
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
catch (error) {
|
|
277
|
-
return {
|
|
278
|
-
index,
|
|
279
|
-
hash,
|
|
280
|
-
success: false,
|
|
281
|
-
error: error.message,
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
});
|
|
285
|
-
return await Promise.all(promises);
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* 准备并签名交易(辅助方法)
|
|
289
|
-
*
|
|
290
|
-
* @param wallet 钱包
|
|
291
|
-
* @param transactions 交易列表
|
|
292
|
-
* @param options 可选参数
|
|
293
|
-
* @returns 已签名的交易数组
|
|
294
|
-
*/
|
|
295
|
-
async signTransactions(wallet, transactions, options) {
|
|
296
|
-
// 验证输入
|
|
297
|
-
if (!transactions || transactions.length === 0) {
|
|
298
|
-
throw new Error('Transactions array cannot be empty');
|
|
299
|
-
}
|
|
300
|
-
// 获取起始 Nonce(使用 pending 确保包含待处理交易)
|
|
301
|
-
let nonce = options?.startNonce;
|
|
302
|
-
if (nonce === undefined) {
|
|
303
|
-
nonce = await this.provider.getTransactionCount(wallet.address, 'pending');
|
|
304
|
-
}
|
|
305
|
-
// 获取 Gas Price
|
|
306
|
-
let gasPrice = options?.gasPrice;
|
|
307
|
-
if (!gasPrice) {
|
|
308
|
-
const feeData = await this.provider.getFeeData();
|
|
309
|
-
// 动态获取基础 gas price,增加容错
|
|
310
|
-
const baseGasPrice = feeData.gasPrice || feeData.maxFeePerGas || 3000000000n;
|
|
311
|
-
const multiplier = options?.gasPriceMultiplier || 50;
|
|
312
|
-
gasPrice = (baseGasPrice * BigInt(100 + multiplier)) / 100n;
|
|
313
|
-
}
|
|
314
|
-
// 签名所有交易(避免直接修改 nonce)
|
|
315
|
-
const signedTxs = [];
|
|
316
|
-
for (let i = 0; i < transactions.length; i++) {
|
|
317
|
-
const currentNonce = nonce + i;
|
|
318
|
-
const fullTx = {
|
|
319
|
-
...transactions[i],
|
|
320
|
-
nonce: currentNonce,
|
|
321
|
-
gasPrice,
|
|
322
|
-
chainId: this.chainId,
|
|
323
|
-
};
|
|
324
|
-
const signedTx = await wallet.signTransaction(fullTx);
|
|
325
|
-
signedTxs.push(signedTx);
|
|
326
|
-
}
|
|
327
|
-
return signedTxs;
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* 检查交易是否已包含在区块中
|
|
331
|
-
*
|
|
332
|
-
* @param txHash 交易哈希
|
|
333
|
-
* @returns 是否已包含
|
|
334
|
-
*/
|
|
335
|
-
async isTransactionIncluded(txHash) {
|
|
336
|
-
try {
|
|
337
|
-
const receipt = await this.provider.getTransactionReceipt(txHash);
|
|
338
|
-
return receipt !== null && receipt.blockNumber !== null;
|
|
339
|
-
}
|
|
340
|
-
catch {
|
|
341
|
-
return false;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* 等待达到目标区块
|
|
346
|
-
*
|
|
347
|
-
* @param targetBlock 目标区块号
|
|
348
|
-
* @param maxAttempts 最大尝试次数(默认 10)
|
|
349
|
-
* @param intervalMs 检查间隔(毫秒,默认 1000)
|
|
350
|
-
* @returns 是否达到目标区块
|
|
351
|
-
*/
|
|
352
|
-
async waitForBlock(targetBlock, maxAttempts = 10, intervalMs = 1000) {
|
|
353
|
-
let attempts = 0;
|
|
354
|
-
let lastError = null;
|
|
355
|
-
while (attempts < maxAttempts) {
|
|
356
|
-
try {
|
|
357
|
-
const currentBlock = await this.getBlockNumber();
|
|
358
|
-
// 已达到或超过目标区块
|
|
359
|
-
if (currentBlock >= targetBlock) {
|
|
360
|
-
return true;
|
|
361
|
-
}
|
|
362
|
-
// 检查是否已经错过目标区块(超过太多)
|
|
363
|
-
// 修复:应该是 currentBlock > targetBlock,而不是 targetBlock + 5
|
|
364
|
-
if (currentBlock > targetBlock) {
|
|
365
|
-
// 已经错过目标区块
|
|
366
|
-
return false;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
catch (error) {
|
|
370
|
-
lastError = error;
|
|
371
|
-
// 继续重试
|
|
372
|
-
}
|
|
373
|
-
// 等待下一次检查
|
|
374
|
-
await new Promise(resolve => setTimeout(resolve, intervalMs));
|
|
375
|
-
attempts++;
|
|
376
|
-
}
|
|
377
|
-
// 达到最大尝试次数
|
|
378
|
-
if (lastError) {
|
|
379
|
-
}
|
|
380
|
-
return false;
|
|
381
|
-
}
|
|
382
|
-
/**
|
|
383
|
-
* 获取当前 Provider 的统计信息(用于调试)
|
|
384
|
-
*/
|
|
385
|
-
getProviderInfo() {
|
|
386
|
-
return {
|
|
387
|
-
chainId: this.chainId,
|
|
388
|
-
chainName: this.chainName,
|
|
389
|
-
// @ts-ignore - 访问私有属性用于调试
|
|
390
|
-
rpcUrl: this.provider._getConnection?.().url || 'unknown',
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
MerkleClient.BLOCK_CACHE_TTL_MS = 1000; // 1秒缓存(BSC 约 3 秒出块,ETH 约 12 秒)
|
|
395
|
-
/**
|
|
396
|
-
* 创建 Merkle 客户端的便捷函数
|
|
397
|
-
*/
|
|
398
|
-
export function createMerkleClient(apiKey, chainId = 56) {
|
|
399
|
-
return new MerkleClient({ apiKey, chainId });
|
|
400
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { CDPV2 } from '../curve.js';
|
|
3
|
-
describe('CDPV2', () => {
|
|
4
|
-
describe('getCurve', () => {
|
|
5
|
-
it('creates curve with default h and k when only r is provided', () => {
|
|
6
|
-
const curve = CDPV2.getCurve(0.1);
|
|
7
|
-
expect(curve.r).toBe(0.1);
|
|
8
|
-
expect(curve.h).toBe(0);
|
|
9
|
-
expect(curve.k).toBe(1e9 * 0.1);
|
|
10
|
-
});
|
|
11
|
-
it('creates curve with explicit h and k', () => {
|
|
12
|
-
const curve = CDPV2.getCurve(0.1, 1000, 500000);
|
|
13
|
-
expect(curve.r).toBe(0.1);
|
|
14
|
-
expect(curve.h).toBe(1000);
|
|
15
|
-
expect(curve.k).toBe(500000);
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
describe('estimateSupply', () => {
|
|
19
|
-
it('returns 0 for empty reserve', () => {
|
|
20
|
-
const curve = CDPV2.getCurve(0.1);
|
|
21
|
-
expect(curve.estimateSupply('0').toString()).toBe('0.0');
|
|
22
|
-
});
|
|
23
|
-
it('returns 0 for falsy reserve', () => {
|
|
24
|
-
const curve = CDPV2.getCurve(0.1);
|
|
25
|
-
expect(curve.estimateSupply('').toString()).toBe('0.0');
|
|
26
|
-
});
|
|
27
|
-
it('calculates supply correctly for given reserve', () => {
|
|
28
|
-
const curve = CDPV2.getCurve(0.1);
|
|
29
|
-
const supply = curve.estimateSupply('1000000');
|
|
30
|
-
expect(supply.toUnsafeFloat()).toBeGreaterThan(0);
|
|
31
|
-
expect(supply.toUnsafeFloat()).toBeLessThan(1_000_000_000);
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
describe('estimateReserve', () => {
|
|
35
|
-
it('returns 0 for empty amount', () => {
|
|
36
|
-
const curve = CDPV2.getCurve(0.1);
|
|
37
|
-
expect(curve.estimateReserve('0').toString()).toBe('0.0');
|
|
38
|
-
});
|
|
39
|
-
it('returns 0 for falsy amount', () => {
|
|
40
|
-
const curve = CDPV2.getCurve(0.1);
|
|
41
|
-
expect(curve.estimateReserve('').toString()).toBe('0.0');
|
|
42
|
-
});
|
|
43
|
-
it('calculates reserve inversely proportional to supply', () => {
|
|
44
|
-
const curve = CDPV2.getCurve(0.1);
|
|
45
|
-
const reserve = curve.estimateReserve('500000000');
|
|
46
|
-
expect(reserve.toUnsafeFloat()).toBeGreaterThan(0);
|
|
47
|
-
});
|
|
48
|
-
it('supply and reserve are mutually consistent', () => {
|
|
49
|
-
const curve = CDPV2.getCurve(0.1);
|
|
50
|
-
const initialReserve = '1000000';
|
|
51
|
-
const supply = curve.estimateSupply(initialReserve);
|
|
52
|
-
const reserveBack = curve.estimateReserve(supply.toString());
|
|
53
|
-
// Allow small rounding differences
|
|
54
|
-
const diff = Math.abs(reserveBack.toUnsafeFloat() - 1_000_000);
|
|
55
|
-
expect(diff).toBeLessThan(1);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
describe('price', () => {
|
|
59
|
-
it('returns positive price for valid supply', () => {
|
|
60
|
-
const curve = CDPV2.getCurve(0.1);
|
|
61
|
-
const p = curve.price('500000000');
|
|
62
|
-
expect(p.toUnsafeFloat()).toBeGreaterThan(0);
|
|
63
|
-
});
|
|
64
|
-
it('price increases as supply increases (bonding curve)', () => {
|
|
65
|
-
const curve = CDPV2.getCurve(0.1);
|
|
66
|
-
const priceLow = curve.price('100000000');
|
|
67
|
-
const priceHigh = curve.price('500000000');
|
|
68
|
-
expect(priceHigh.toUnsafeFloat()).toBeGreaterThan(priceLow.toUnsafeFloat());
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
describe('fdv', () => {
|
|
72
|
-
it('calculates FDV as price * 1B', () => {
|
|
73
|
-
const curve = CDPV2.getCurve(0.1);
|
|
74
|
-
const supply = '500000000';
|
|
75
|
-
const p = curve.price(supply);
|
|
76
|
-
const f = curve.fdv(supply);
|
|
77
|
-
expect(f.toUnsafeFloat()).toBeCloseTo(p.toUnsafeFloat() * 1_000_000_000, -3);
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
describe('defaultDexSupplyThreshold', () => {
|
|
81
|
-
it('returns 800M', () => {
|
|
82
|
-
expect(CDPV2.defaultDexSupplyThreshold().toString()).toBe('800000000.0');
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
});
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Flap Portal 模块
|
|
3
|
-
*
|
|
4
|
-
* 拆分自 portal.ts。包括:
|
|
5
|
-
* - types.ts — 枚举、类型、工具函数
|
|
6
|
-
* - portal.ts — FlapPortal(只读操作)
|
|
7
|
-
* - writer.ts — FlapPortalWriter(读写操作)
|
|
8
|
-
*/
|
|
9
|
-
export type { FlapChain, PortalConfig, TokenStateV2, TokenStateV3, TokenStateV4, TokenStateV5, TokenStateV7, QuoteExactInputParams, ExactInputParams, ExactInputV3Params, NewTokenV3Params, NewTokenV4Params, NewTokenV5Params, TaxDistributionConfig, TokenStatus, TokenVersion, DexThreshType, MigratorType, V3LPFeeProfile, DEXId, } from './types.js';
|
|
10
|
-
export { FlapPortal } from './portal.js';
|
|
11
|
-
export { FlapPortalWriter } from './writer.js';
|
|
12
|
-
export { lpFeeProfileToV3Fee, validateTaxDistribution } from './types.js';
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Flap Portal 模块
|
|
3
|
-
*
|
|
4
|
-
* 拆分自 portal.ts。包括:
|
|
5
|
-
* - types.ts — 枚举、类型、工具函数
|
|
6
|
-
* - portal.ts — FlapPortal(只读操作)
|
|
7
|
-
* - writer.ts — FlapPortalWriter(读写操作)
|
|
8
|
-
*/
|
|
9
|
-
export { FlapPortal } from './portal.js';
|
|
10
|
-
export { FlapPortalWriter } from './writer.js';
|
|
11
|
-
export { lpFeeProfileToV3Fee, validateTaxDistribution } from './types.js';
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import type { PortalConfig, TokenStateV2, TokenStateV3, TokenStateV4, TokenStateV5, TokenStateV7, QuoteExactInputParams } from './types.js';
|
|
2
|
-
import { Contract } from 'ethers';
|
|
3
|
-
export declare class FlapPortal {
|
|
4
|
-
private rpcUrl;
|
|
5
|
-
protected portal: string;
|
|
6
|
-
protected contract: Contract;
|
|
7
|
-
private buyFeeRate;
|
|
8
|
-
private sellFeeRate;
|
|
9
|
-
constructor(cfg: PortalConfig);
|
|
10
|
-
getTokenV2(token: string): Promise<TokenStateV2>;
|
|
11
|
-
getTokenV3(token: string): Promise<TokenStateV3>;
|
|
12
|
-
getTokenV4(token: string): Promise<TokenStateV4>;
|
|
13
|
-
getTokenV5(token: string): Promise<TokenStateV5>;
|
|
14
|
-
/**
|
|
15
|
-
* 获取代币状态 V7(包含毕业相关信息)
|
|
16
|
-
* 注意:仅部分链支持 V7(如 XLayer v5.7+),不支持的链会 revert
|
|
17
|
-
*/
|
|
18
|
-
getTokenV7(token: string): Promise<TokenStateV7>;
|
|
19
|
-
/**
|
|
20
|
-
* 计算距离毕业还需多少 OKB/ETH(含手续费)
|
|
21
|
-
* @param state TokenStateV5 或 TokenStateV7
|
|
22
|
-
* @returns 毕业相关信息
|
|
23
|
-
*/
|
|
24
|
-
computeRemainingToGraduation(state: TokenStateV5 | TokenStateV7): {
|
|
25
|
-
remainingOkb: string;
|
|
26
|
-
graduationReserve: string;
|
|
27
|
-
currentReserve: string;
|
|
28
|
-
isGraduated: boolean;
|
|
29
|
-
progressPercent: string;
|
|
30
|
-
};
|
|
31
|
-
computePriceAndProgress(state: TokenStateV5): {
|
|
32
|
-
price: string;
|
|
33
|
-
progress: string;
|
|
34
|
-
};
|
|
35
|
-
quoteBuy(state: TokenStateV5, inputEth: string): string;
|
|
36
|
-
quoteSell(state: TokenStateV5, inputToken: string): string;
|
|
37
|
-
previewBuy(token: string, ethAmount: bigint): Promise<bigint>;
|
|
38
|
-
previewSell(token: string, tokenAmount: bigint): Promise<bigint>;
|
|
39
|
-
quoteExactInput(params: QuoteExactInputParams): Promise<bigint>;
|
|
40
|
-
getFeeRate(): Promise<{
|
|
41
|
-
buyFeeRate: bigint;
|
|
42
|
-
sellFeeRate: bigint;
|
|
43
|
-
}>;
|
|
44
|
-
nonce(): Promise<bigint>;
|
|
45
|
-
version(): Promise<string>;
|
|
46
|
-
getLocks(token: string): Promise<bigint[]>;
|
|
47
|
-
}
|