four-flap-meme-sdk 4.3.2 → 4.3.4
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/chains/eni/platforms/daoaas/create.js +4 -2
- package/dist/chains/eni/platforms/daoaas/portal-direct-submit.js +8 -6
- package/dist/chains/eni/platforms/dswap/liquidity.js +5 -3
- package/dist/chains/eni/platforms/fair-launch/launcher.js +5 -3
- package/dist/chains/eni/platforms/fair-launch/pool.js +5 -3
- package/dist/chains/eni/platforms/iro/token.js +2 -2
- package/dist/chains/xlayer/eip7702/profit.d.ts +2 -2
- package/dist/chains/xlayer/eip7702/profit.js +2 -2
- package/dist/core/constants/index.d.ts +1 -1
- package/dist/core/constants/index.js +1 -3
- package/dist/core/constants/profit.d.ts +30 -52
- package/dist/core/constants/profit.js +188 -99
- package/dist/core/constants/profit.test-setup.d.ts +1 -0
- package/dist/core/constants/profit.test-setup.js +26 -0
- package/dist/core/constants/profit.test.js +53 -2
- package/dist/core/nonce/nonce-manager.js +17 -1
- package/dist/domains/lp-inspect/inspect-tax.js +4 -0
- package/dist/domains/lp-inspect/inspect-tax.test.js +3 -0
- package/dist/domains/lp-inspect/inspect.js +8 -5
- package/dist/domains/private-sale/private-sale.d.ts +1 -1
- package/dist/domains/private-sale/private-sale.js +1 -1
- package/dist/exports/root-foundations.d.ts +1 -1
- package/dist/exports/root-foundations.js +1 -1
- package/dist/utils/constants.d.ts +1 -1
- package/dist/utils/constants.js +1 -1
- package/package.json +1 -1
|
@@ -8,7 +8,9 @@ import { ENI_CHAIN_ID, ENI_RPC_URL, DAOAAS_PORTAL, NATIVE_TRANSFER_GAS_LIMIT } f
|
|
|
8
8
|
import { encodeCreateCall } from './portal.js';
|
|
9
9
|
import { buildProfitTransfer } from './portal-direct.js';
|
|
10
10
|
import { calculateProfitAmount, getProfitRateBps, getProfitRecipient } from '../../../../core/constants/profit.js';
|
|
11
|
-
|
|
11
|
+
function normalProfitBps() {
|
|
12
|
+
return getProfitRateBps('normal');
|
|
13
|
+
}
|
|
12
14
|
/**
|
|
13
15
|
* 生成确定性 salt (uint256)
|
|
14
16
|
* Portal 合约 createAndInitPurchase 的 _salt 参数类型是 uint256
|
|
@@ -63,7 +65,7 @@ export async function createTokenForSubmit(params) {
|
|
|
63
65
|
gasPrice: maxFeePerGas,
|
|
64
66
|
});
|
|
65
67
|
const profitTxGasCost = NATIVE_TRANSFER_GAS_LIMIT * maxFeePerGas;
|
|
66
|
-
const profitAmount = calculateProfitAmount(initialBuyAmount,
|
|
68
|
+
const profitAmount = calculateProfitAmount(initialBuyAmount, normalProfitBps()) + profitTxGasCost;
|
|
67
69
|
const signedTransactions = [createTx.signedTx];
|
|
68
70
|
let totalGasCost = 2000000n * maxFeePerGas;
|
|
69
71
|
if (profitAmount > 0n) {
|
|
@@ -10,7 +10,9 @@ import { encodeBuyCall, encodeSellCall, applySlippage, DaoaasPortalQuery } from
|
|
|
10
10
|
import { getProfitRecipient, getProfitRateBps, calculateProfitAmount, calculateBundleSwapRoundTripProfit, } from '../../../../core/constants/profit.js';
|
|
11
11
|
import { directBuy, directSell, directBatchBuy, directBatchSell } from './portal-direct-tx.js';
|
|
12
12
|
import { buildProfitTransfer } from './portal-direct-profit.js';
|
|
13
|
-
|
|
13
|
+
function normalProfitBps() {
|
|
14
|
+
return getProfitRateBps('normal');
|
|
15
|
+
}
|
|
14
16
|
function formatSubmitMetadata(totalFlow, profitAmount) {
|
|
15
17
|
return {
|
|
16
18
|
profitAmount: profitAmount > 0n ? profitAmount.toString() : '0',
|
|
@@ -47,7 +49,7 @@ export async function directBuyForSubmit(params) {
|
|
|
47
49
|
const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? 0n;
|
|
48
50
|
const tradeTx = await directBuy({ ...params, rpcUrl, gasPrice: maxFeePerGas });
|
|
49
51
|
const profitTxGasCost = NATIVE_TRANSFER_GAS_LIMIT * maxFeePerGas;
|
|
50
|
-
const profitAmount = calculateProfitAmount(params.amount,
|
|
52
|
+
const profitAmount = calculateProfitAmount(params.amount, normalProfitBps()) + profitTxGasCost;
|
|
51
53
|
const signedTransactions = [tradeTx.signedTx];
|
|
52
54
|
let totalGasCost = 300000n * maxFeePerGas;
|
|
53
55
|
if (profitAmount > 0n) {
|
|
@@ -78,7 +80,7 @@ export async function directSellForSubmit(params) {
|
|
|
78
80
|
const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? 0n;
|
|
79
81
|
const estimatedOut = await query.previewSell(params.token, params.tokenAmount);
|
|
80
82
|
const profitTxGasCost = NATIVE_TRANSFER_GAS_LIMIT * maxFeePerGas;
|
|
81
|
-
const profitAmount = calculateProfitAmount(estimatedOut,
|
|
83
|
+
const profitAmount = calculateProfitAmount(estimatedOut, normalProfitBps()) + profitTxGasCost;
|
|
82
84
|
const tradeTx = await directSell({
|
|
83
85
|
...params,
|
|
84
86
|
rpcUrl,
|
|
@@ -121,7 +123,7 @@ export async function directBatchBuyForSubmit(params) {
|
|
|
121
123
|
const swapGasLimit = 300000n;
|
|
122
124
|
const profitTxGasCost = NATIVE_TRANSFER_GAS_LIMIT * maxFeePerGas;
|
|
123
125
|
const totalFlowRaw = rawAmounts.reduce((sum, amount) => sum + amount, 0n);
|
|
124
|
-
const profitAmountFull = calculateProfitAmount(totalFlowRaw,
|
|
126
|
+
const profitAmountFull = calculateProfitAmount(totalFlowRaw, normalProfitBps()) + profitTxGasCost;
|
|
125
127
|
const maxPayerIdx = pickMaxAmountIndex(rawAmounts);
|
|
126
128
|
const wallets = rawKeys.map((pk) => new Wallet(pk, provider));
|
|
127
129
|
const balances = await Promise.all(wallets.map((w) => provider.getBalance(w.address)));
|
|
@@ -165,7 +167,7 @@ export async function directBatchBuyForSubmit(params) {
|
|
|
165
167
|
});
|
|
166
168
|
transactions.push({ hash: '', signedTx, from: wallet.address, nonce });
|
|
167
169
|
}
|
|
168
|
-
const profitAmount = calculateProfitAmount(totalFlow,
|
|
170
|
+
const profitAmount = calculateProfitAmount(totalFlow, normalProfitBps()) + profitTxGasCost;
|
|
169
171
|
const payerIndex = pickMaxAmountIndex(amounts);
|
|
170
172
|
const profitTx = await buildRevenueTail({
|
|
171
173
|
rpcUrl,
|
|
@@ -220,7 +222,7 @@ export async function directBatchSellForSubmit(params) {
|
|
|
220
222
|
}
|
|
221
223
|
const totalFlow = estimatedOuts.reduce((sum, amount) => sum + amount, 0n);
|
|
222
224
|
const profitTxGasCost = NATIVE_TRANSFER_GAS_LIMIT * maxFeePerGas;
|
|
223
|
-
const profitAmount = calculateProfitAmount(totalFlow,
|
|
225
|
+
const profitAmount = calculateProfitAmount(totalFlow, normalProfitBps()) + profitTxGasCost;
|
|
224
226
|
const payerIndex = pickMaxAmountIndex(estimatedOuts);
|
|
225
227
|
const profitTx = await buildRevenueTail({
|
|
226
228
|
rpcUrl,
|
|
@@ -13,7 +13,9 @@ import { ERC20_ABI } from '../../../../abis/common.js';
|
|
|
13
13
|
import { getProfitRecipient, getProfitRateBps, calculateProfitAmount } from '../../../../core/constants/profit.js';
|
|
14
14
|
const DEFAULT_DEADLINE_MINUTES = 20;
|
|
15
15
|
const DEFAULT_SLIPPAGE_PCT = 5;
|
|
16
|
-
|
|
16
|
+
function normalProfitBps() {
|
|
17
|
+
return getProfitRateBps('normal');
|
|
18
|
+
}
|
|
17
19
|
const routerIface = new Interface(DSWAP_V2_ROUTER_ABI);
|
|
18
20
|
const erc20Iface = new Interface(ERC20_ABI);
|
|
19
21
|
const pairIface = new Interface(DSWAP_V2_PAIR_ABI);
|
|
@@ -137,7 +139,7 @@ export async function addLiquidityETHForSubmit(params) {
|
|
|
137
139
|
});
|
|
138
140
|
signedTxs.push(addLiqTx);
|
|
139
141
|
const profitTxGasCost = NATIVE_TRANSFER_GAS_LIMIT * maxFeePerGas;
|
|
140
|
-
const profitAmount = calculateProfitAmount(amountETHDesired,
|
|
142
|
+
const profitAmount = calculateProfitAmount(amountETHDesired, normalProfitBps()) + profitTxGasCost;
|
|
141
143
|
if (profitAmount > 0n) {
|
|
142
144
|
const profitTx = await signProfitTx({
|
|
143
145
|
wallet,
|
|
@@ -223,7 +225,7 @@ export async function removeLiquidityETHForSubmit(params) {
|
|
|
223
225
|
});
|
|
224
226
|
signedTxs.push(removeLiqTx);
|
|
225
227
|
const profitTxGasCost = NATIVE_TRANSFER_GAS_LIMIT * maxFeePerGas;
|
|
226
|
-
const profitAmount = calculateProfitAmount(amountETHExpected,
|
|
228
|
+
const profitAmount = calculateProfitAmount(amountETHExpected, normalProfitBps()) + profitTxGasCost;
|
|
227
229
|
if (profitAmount > 0n) {
|
|
228
230
|
const profitTx = await signProfitTx({
|
|
229
231
|
wallet,
|
|
@@ -13,6 +13,9 @@ import { buildProfitTransfer } from '../daoaas/portal-direct.js';
|
|
|
13
13
|
import { calculateProfitAmount, getProfitRateBps, getProfitRecipient } from '../../../../core/constants/profit.js';
|
|
14
14
|
import { validateLaunchParams } from './presets.js';
|
|
15
15
|
const launcherIface = new Interface(LP_FAIR_LAUNCHER_ABI);
|
|
16
|
+
function normalProfitBps() {
|
|
17
|
+
return getProfitRateBps('normal');
|
|
18
|
+
}
|
|
16
19
|
// ============================================================================
|
|
17
20
|
// 只读查询
|
|
18
21
|
// ============================================================================
|
|
@@ -158,7 +161,7 @@ export async function setTokenTradeStartTimeForSubmit(params) {
|
|
|
158
161
|
const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? 0n;
|
|
159
162
|
const setResult = await setTokenTradeStartTime({ ...params, rpcUrl, gasPrice: maxFeePerGas });
|
|
160
163
|
const profitTxGasCost = NATIVE_TRANSFER_GAS_LIMIT * maxFeePerGas;
|
|
161
|
-
const profitAmount = calculateProfitAmount(maxFeePerGas * 200000n,
|
|
164
|
+
const profitAmount = calculateProfitAmount(maxFeePerGas * 200000n, normalProfitBps()) + profitTxGasCost;
|
|
162
165
|
const profitTx = await buildProfitTransfer({
|
|
163
166
|
rpcUrl,
|
|
164
167
|
payerKey: privateKey,
|
|
@@ -214,7 +217,6 @@ export async function launchToken(params) {
|
|
|
214
217
|
});
|
|
215
218
|
return { signedTx, from: wallet.address, nonce };
|
|
216
219
|
}
|
|
217
|
-
const NORMAL_PROFIT_BPS = getProfitRateBps('normal');
|
|
218
220
|
/**
|
|
219
221
|
* 创建公平发射代币 — ForSubmit 版本
|
|
220
222
|
* 自动追加利润转账交易,兼容 emitservice submit_blockrazor 接口
|
|
@@ -234,7 +236,7 @@ export async function launchTokenForSubmit(params) {
|
|
|
234
236
|
// WEGAS: value = initialQuote, ERC20: value = 0 但 initialQuote 仍为实际投资额
|
|
235
237
|
const profitFlow = launchParams.initialQuote > 0n ? launchParams.initialQuote : value;
|
|
236
238
|
const profitTxGasCost = NATIVE_TRANSFER_GAS_LIMIT * maxFeePerGas;
|
|
237
|
-
const profitAmount = calculateProfitAmount(profitFlow,
|
|
239
|
+
const profitAmount = calculateProfitAmount(profitFlow, normalProfitBps()) + profitTxGasCost;
|
|
238
240
|
const profitTx = await buildProfitTransfer({
|
|
239
241
|
rpcUrl,
|
|
240
242
|
payerKey: privateKey,
|
|
@@ -124,7 +124,9 @@ export async function claimFromPool(params) {
|
|
|
124
124
|
});
|
|
125
125
|
return { signedTx, from: wallet.address, nonce };
|
|
126
126
|
}
|
|
127
|
-
|
|
127
|
+
function normalProfitBps() {
|
|
128
|
+
return getProfitRateBps('normal');
|
|
129
|
+
}
|
|
128
130
|
/**
|
|
129
131
|
* 构建 Pool claim 交易 — ForSubmit 版本
|
|
130
132
|
* 自动追加利润转账,兼容 emitservice submit_blockrazor 接口
|
|
@@ -139,7 +141,7 @@ export async function claimFromPoolForSubmit(params) {
|
|
|
139
141
|
// 与 BSC 一致:利润基于实际投资金额计算(含利润转账 Gas)
|
|
140
142
|
const profitFlow = quoteAmount ?? value;
|
|
141
143
|
const profitTxGasCost = NATIVE_TRANSFER_GAS_LIMIT * maxFeePerGas;
|
|
142
|
-
const profitAmount = calculateProfitAmount(profitFlow,
|
|
144
|
+
const profitAmount = calculateProfitAmount(profitFlow, normalProfitBps()) + profitTxGasCost;
|
|
143
145
|
const profitTx = await buildProfitTransfer({
|
|
144
146
|
rpcUrl,
|
|
145
147
|
payerKey: privateKey,
|
|
@@ -200,7 +202,7 @@ export async function batchClaimForSubmit(params) {
|
|
|
200
202
|
// WEGAS: quoteAmount = value, ERC20: quoteAmount = 实际 ERC20 投资额
|
|
201
203
|
const profitFlow = totalQuoteAmount > 0n ? totalQuoteAmount : totalValue;
|
|
202
204
|
const profitTxGasCost = NATIVE_TRANSFER_GAS_LIMIT * maxFeePerGas;
|
|
203
|
-
const profitAmount = calculateProfitAmount(profitFlow,
|
|
205
|
+
const profitAmount = calculateProfitAmount(profitFlow, normalProfitBps()) + profitTxGasCost;
|
|
204
206
|
if (profitAmount > 0n) {
|
|
205
207
|
let maxIdx = 0;
|
|
206
208
|
for (let i = 1; i < claimResults.length; i++) {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { Contract, Interface, Wallet, JsonRpcProvider } from 'ethers';
|
|
8
8
|
import { ENI_CHAIN_ID, ENI_RPC_URL, NATIVE_TRANSFER_GAS_LIMIT } from '../../constants.js';
|
|
9
9
|
import { IRO_TOKEN_ABI, SUBSCRIBE_GAS_LIMIT } from './constants.js';
|
|
10
|
-
import { getProfitRecipient } from '../../../../core/constants/profit.js';
|
|
10
|
+
import { getProfitRecipient, getIroSubscribeFeePerAddress } from '../../../../core/constants/profit.js';
|
|
11
11
|
const tokenIface = new Interface(IRO_TOKEN_ABI);
|
|
12
12
|
// ============================================================================
|
|
13
13
|
// 查询
|
|
@@ -207,7 +207,7 @@ export async function batchSubscribeForSubmit(params) {
|
|
|
207
207
|
subscribeTxs.push(signedTx);
|
|
208
208
|
subscribeResults.push({ signedTx, from: wallets[i].address, nonce: nonces[i] });
|
|
209
209
|
}
|
|
210
|
-
const profitPerAddress =
|
|
210
|
+
const profitPerAddress = getIroSubscribeFeePerAddress(chainId);
|
|
211
211
|
const profitTxGasCost = NATIVE_TRANSFER_GAS_LIMIT * maxFeePerGas;
|
|
212
212
|
const totalProfit = profitPerAddress * BigInt(subscribers.length) + profitTxGasCost;
|
|
213
213
|
let maxIndex = 0;
|
|
@@ -2,8 +2,8 @@ import type { EIP7702Config } from './types.js';
|
|
|
2
2
|
export type UserType = 'v0' | 'v1' | 'default';
|
|
3
3
|
/**
|
|
4
4
|
* 根据用户类型获取利润率(单边:发币/买卖/捆绑购买)
|
|
5
|
-
* - v0 / default:
|
|
6
|
-
* - v1:
|
|
5
|
+
* - v0 / default: v0Single(库表)
|
|
6
|
+
* - v1: v1Single(库表)
|
|
7
7
|
* 刷量/换手不要走这里,用 calculateBundleSwapRoundTripProfit
|
|
8
8
|
*/
|
|
9
9
|
export declare function getProfitRateBps(userType?: UserType): number;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { getProfitRateBps as getSharedProfitRateBps, getProfitRecipient as pickRandomProfitRecipient, calculateBundleSwapRoundTripProfit, } from '../../../core/constants/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* 根据用户类型获取利润率(单边:发币/买卖/捆绑购买)
|
|
4
|
-
* - v0 / default:
|
|
5
|
-
* - v1:
|
|
4
|
+
* - v0 / default: v0Single(库表)
|
|
5
|
+
* - v1: v1Single(库表)
|
|
6
6
|
* 刷量/换手不要走这里,用 calculateBundleSwapRoundTripProfit
|
|
7
7
|
*/
|
|
8
8
|
export function getProfitRateBps(userType) {
|
|
@@ -15,4 +15,4 @@ export { applySlippageBps, reverseSlippageBps } from './slippage.js';
|
|
|
15
15
|
/** Flap 链标识(与 `FlapPortal` 一致,供子路径 `./constants` 使用) */
|
|
16
16
|
export type { FlapChain } from '../../domains/flap/portal.js';
|
|
17
17
|
export { ZERO_ADDRESS, ZERO_BYTES32, BLOCKRAZOR_BUILDER_EOA, ADDRESSES, getAddress, getWrappedNativeAddress, getMulticall3Address, getFlapPortalAddress, isZeroAddress, isNativeToken, ENI_SPENDERS, getEniSpenders, type ChainAddresses, type AddressKey, } from './addresses.js';
|
|
18
|
-
export { PROFIT_CONFIG, getProfitRecipient, getAllProfitRecipients, getProfitRateBps, calculateProfitAmount, calculateBundleSwapRoundTripProfit, calculateProfitHopGasCost, type ProfitRateKey, TRANSFER_FEE_PER_ADDRESS, getTransferFeePerAddress, calculateTransferFee, } from './profit.js';
|
|
18
|
+
export { PROFIT_CONFIG, setProfitRecipients, getProfitRecipient, getAllProfitRecipients, setProfitRates, getProfitRateBps, calculateProfitAmount, calculateBundleSwapRoundTripProfit, calculateProfitHopGasCost, type ProfitRateKey, type ProfitRateMode, type ProfitRates, TRANSFER_FEE_PER_ADDRESS, setTransferFees, setTransferFeePerAddress, getTransferFeePerAddress, calculateTransferFee, setIroSubscribeFees, setIroSubscribeFeePerAddress, getIroSubscribeFeePerAddress, } from './profit.js';
|
|
@@ -25,6 +25,4 @@ export { ZERO_ADDRESS, ZERO_BYTES32, BLOCKRAZOR_BUILDER_EOA, ADDRESSES, getAddre
|
|
|
25
25
|
// ============================================================================
|
|
26
26
|
// 利润配置
|
|
27
27
|
// ============================================================================
|
|
28
|
-
export { PROFIT_CONFIG, getProfitRecipient, getAllProfitRecipients, getProfitRateBps, calculateProfitAmount, calculateBundleSwapRoundTripProfit, calculateProfitHopGasCost,
|
|
29
|
-
// ✅ 钱包转账按地址收费
|
|
30
|
-
TRANSFER_FEE_PER_ADDRESS, getTransferFeePerAddress, calculateTransferFee, } from './profit.js';
|
|
28
|
+
export { PROFIT_CONFIG, setProfitRecipients, getProfitRecipient, getAllProfitRecipients, setProfitRates, getProfitRateBps, calculateProfitAmount, calculateBundleSwapRoundTripProfit, calculateProfitHopGasCost, TRANSFER_FEE_PER_ADDRESS, setTransferFees, setTransferFeePerAddress, getTransferFeePerAddress, calculateTransferFee, setIroSubscribeFees, setIroSubscribeFeePerAddress, getIroSubscribeFeePerAddress, } from './profit.js';
|
|
@@ -1,83 +1,61 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 利润配置
|
|
3
|
-
*
|
|
3
|
+
* 手续费地址、费率、每地址转账费都只来自服务端,由前端 set* 灌进来。
|
|
4
4
|
*/
|
|
5
|
+
export type ProfitRateMode = 'normal' | 'bundleSwap' | 'v0Single' | 'v0Double' | 'v1Single' | 'v1Double';
|
|
6
|
+
export type ProfitRates = Record<ProfitRateMode, number>;
|
|
5
7
|
export declare const PROFIT_CONFIG: {
|
|
6
|
-
/**
|
|
8
|
+
/** 手续费地址只来自 setProfitRecipients(库表),这里不放地址 */
|
|
7
9
|
RECIPIENTS: readonly string[];
|
|
8
|
-
/** @deprecated 向后兼容,使用
|
|
10
|
+
/** @deprecated 向后兼容,使用 getProfitRecipient() 替代 */
|
|
9
11
|
readonly RECIPIENT: string;
|
|
10
|
-
/**
|
|
12
|
+
/** 利润比例;真源是 setProfitRates,未配置时读会抛错 */
|
|
11
13
|
RATES: {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
/** V0 用户双边:4 bps = 0.04% */
|
|
19
|
-
V0_DOUBLE: number;
|
|
20
|
-
/** V1 用户单边:10 bps = 0.10% */
|
|
21
|
-
V1_SINGLE: number;
|
|
22
|
-
/** V1 用户双边:5 bps = 0.05% */
|
|
23
|
-
V1_DOUBLE: number;
|
|
14
|
+
readonly NORMAL: number;
|
|
15
|
+
readonly BUNDLE_SWAP: number;
|
|
16
|
+
readonly V0_SINGLE: number;
|
|
17
|
+
readonly V0_DOUBLE: number;
|
|
18
|
+
readonly V1_SINGLE: number;
|
|
19
|
+
readonly V1_DOUBLE: number;
|
|
24
20
|
};
|
|
25
|
-
/** 利润多跳配置 */
|
|
26
21
|
HOP: {
|
|
27
|
-
/** 利润多跳数量(通过多个中转地址转账以增加隐私性) */
|
|
28
22
|
COUNT: number;
|
|
29
|
-
/** 利润多跳 Gas Limit */
|
|
30
23
|
GAS_LIMIT: 21055n;
|
|
31
24
|
};
|
|
32
25
|
};
|
|
33
26
|
export type ProfitRateKey = keyof typeof PROFIT_CONFIG.RATES;
|
|
34
27
|
/**
|
|
35
|
-
*
|
|
28
|
+
* 前端把库表收益地址灌进来。空数组清掉配置。
|
|
36
29
|
*/
|
|
37
|
-
export declare function
|
|
30
|
+
export declare function setProfitRecipients(addresses: readonly string[]): void;
|
|
38
31
|
/**
|
|
39
|
-
*
|
|
32
|
+
* 从已配置地址里取一个。没配置就抛错,不使用 SDK 内置地址。
|
|
40
33
|
*/
|
|
34
|
+
export declare function getProfitRecipient(): string;
|
|
41
35
|
export declare function getAllProfitRecipients(): readonly string[];
|
|
42
36
|
/**
|
|
43
|
-
*
|
|
44
|
-
* @param mode 模式:normal, bundleSwap, v0Single, v0Double, v1Single, v1Double
|
|
37
|
+
* 前端把库表功能费率灌进来。null / 缺字段清掉配置。
|
|
45
38
|
*/
|
|
46
|
-
export declare function
|
|
39
|
+
export declare function setProfitRates(rates: ProfitRates | null | undefined): void;
|
|
47
40
|
/**
|
|
48
|
-
*
|
|
49
|
-
* @param amount 原始金额
|
|
50
|
-
* @param rateBps 利润比例(基点)
|
|
41
|
+
* 获取利润比例(基点)。没配置就抛错。
|
|
51
42
|
*/
|
|
43
|
+
export declare function getProfitRateBps(mode?: ProfitRateMode): number;
|
|
52
44
|
export declare function calculateProfitAmount(amount: bigint, rateBps: number): bigint;
|
|
53
45
|
/**
|
|
54
|
-
* 刷量 /
|
|
55
|
-
* 与钱包数量无关;两腿金额接近时合计万六。
|
|
46
|
+
* 刷量 / 换手:买的合计收一档,卖的合计(同计价)再收一档。
|
|
56
47
|
*/
|
|
57
48
|
export declare function calculateBundleSwapRoundTripProfit(buyQuoteWei: bigint, sellQuoteWei: bigint): bigint;
|
|
58
|
-
/**
|
|
59
|
-
* 计算利润多跳所需的 Gas 费用
|
|
60
|
-
* @param gasPrice Gas 价格
|
|
61
|
-
* @returns 总 Gas 费用
|
|
62
|
-
*/
|
|
63
49
|
export declare function calculateProfitHopGasCost(gasPrice: bigint): bigint;
|
|
64
|
-
/**
|
|
65
|
-
* 钱包转账固定费用配置
|
|
66
|
-
* - 按地址数量收取,不受转账金额和中转次数影响
|
|
67
|
-
* - 适用于:归集(sweep)、分散(disperse)、多对多(pairwise)
|
|
68
|
-
* - 所有代币(原生币/ERC20)统一按地址执行次数收取原生币
|
|
69
|
-
*/
|
|
50
|
+
/** 与 getTransferFeePerAddress 同步的活对象;未 set 时为空 */
|
|
70
51
|
export declare const TRANSFER_FEE_PER_ADDRESS: Record<number, bigint>;
|
|
71
|
-
/**
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
* @returns 每地址费用(wei)
|
|
75
|
-
*/
|
|
52
|
+
/** 整表覆盖各链每地址转账费。空对象清掉。 */
|
|
53
|
+
export declare function setTransferFees(fees: Record<number, bigint | string> | null | undefined): void;
|
|
54
|
+
export declare function setTransferFeePerAddress(chainId: number, wei: bigint | string): void;
|
|
76
55
|
export declare function getTransferFeePerAddress(chainId: number): bigint;
|
|
77
|
-
/**
|
|
78
|
-
* 计算钱包转账总费用
|
|
79
|
-
* @param chainId 链 ID
|
|
80
|
-
* @param addressCount 地址数量
|
|
81
|
-
* @returns 总费用(wei)
|
|
82
|
-
*/
|
|
83
56
|
export declare function calculateTransferFee(chainId: number, addressCount: number): bigint;
|
|
57
|
+
/** 整表覆盖各链 IRO 认购每地址费。空对象清掉。 */
|
|
58
|
+
export declare function setIroSubscribeFees(fees: Record<number, bigint | string> | null | undefined): void;
|
|
59
|
+
export declare function setIroSubscribeFeePerAddress(chainId: number, wei: bigint | string): void;
|
|
60
|
+
/** ENI IRO 认购每地址费。没配置就抛错,不回落到转账档。 */
|
|
61
|
+
export declare function getIroSubscribeFeePerAddress(chainId: number): bigint;
|
|
@@ -1,96 +1,136 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 利润配置
|
|
3
|
-
*
|
|
3
|
+
* 手续费地址、费率、每地址转账费都只来自服务端,由前端 set* 灌进来。
|
|
4
4
|
*/
|
|
5
5
|
import { GAS_LIMITS } from './gas.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const EVM_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
7
|
+
const RATE_MODES = [
|
|
8
|
+
'normal',
|
|
9
|
+
'bundleSwap',
|
|
10
|
+
'v0Single',
|
|
11
|
+
'v0Double',
|
|
12
|
+
'v1Single',
|
|
13
|
+
'v1Double',
|
|
14
|
+
];
|
|
15
|
+
let configuredRecipients = null;
|
|
16
|
+
let configuredRates = null;
|
|
17
|
+
const configuredTransferFees = new Map();
|
|
18
|
+
const configuredIroSubscribeFees = new Map();
|
|
19
|
+
function requireRecipients() {
|
|
20
|
+
if (!configuredRecipients || configuredRecipients.length === 0) {
|
|
21
|
+
throw new Error('profit recipient is not configured; call setProfitRecipients from the fee address in DB');
|
|
22
|
+
}
|
|
23
|
+
return configuredRecipients;
|
|
24
|
+
}
|
|
25
|
+
function requireRates() {
|
|
26
|
+
if (!configuredRates) {
|
|
27
|
+
throw new Error('profit rates are not configured; call setProfitRates from the fee config in DB');
|
|
28
|
+
}
|
|
29
|
+
return configuredRates;
|
|
30
|
+
}
|
|
31
|
+
function parseRateBps(value) {
|
|
32
|
+
if (typeof value !== 'number' || !Number.isFinite(value))
|
|
33
|
+
return null;
|
|
34
|
+
const n = Math.floor(value);
|
|
35
|
+
if (n < 0 || n > 10000)
|
|
36
|
+
return null;
|
|
37
|
+
return n;
|
|
38
|
+
}
|
|
9
39
|
export const PROFIT_CONFIG = {
|
|
10
|
-
/**
|
|
11
|
-
RECIPIENTS: [
|
|
12
|
-
|
|
13
|
-
'0xa63135eeb26F2d9D995a8f449b633cd51ac9782b',
|
|
14
|
-
'0x7c37c7eeF07dFDB7d39af907295C177DEc64dae9',
|
|
15
|
-
'0xCB0DC994B383E207D9aF53B8beBF412450C4b612',
|
|
16
|
-
'0x71C23140c75c598e2126Ce1B7d70f59d2cDcBddf',
|
|
17
|
-
],
|
|
18
|
-
/** @deprecated 向后兼容,使用 RECIPIENTS + getProfitRecipient() 替代 */
|
|
40
|
+
/** 手续费地址只来自 setProfitRecipients(库表),这里不放地址 */
|
|
41
|
+
RECIPIENTS: [],
|
|
42
|
+
/** @deprecated 向后兼容,使用 getProfitRecipient() 替代 */
|
|
19
43
|
get RECIPIENT() {
|
|
20
|
-
return
|
|
44
|
+
return getProfitRecipient();
|
|
21
45
|
},
|
|
22
|
-
/**
|
|
46
|
+
/** 利润比例;真源是 setProfitRates,未配置时读会抛错 */
|
|
23
47
|
RATES: {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
BUNDLE_SWAP
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
48
|
+
get NORMAL() {
|
|
49
|
+
return getProfitRateBps('normal');
|
|
50
|
+
},
|
|
51
|
+
get BUNDLE_SWAP() {
|
|
52
|
+
return getProfitRateBps('bundleSwap');
|
|
53
|
+
},
|
|
54
|
+
get V0_SINGLE() {
|
|
55
|
+
return getProfitRateBps('v0Single');
|
|
56
|
+
},
|
|
57
|
+
get V0_DOUBLE() {
|
|
58
|
+
return getProfitRateBps('v0Double');
|
|
59
|
+
},
|
|
60
|
+
get V1_SINGLE() {
|
|
61
|
+
return getProfitRateBps('v1Single');
|
|
62
|
+
},
|
|
63
|
+
get V1_DOUBLE() {
|
|
64
|
+
return getProfitRateBps('v1Double');
|
|
65
|
+
},
|
|
36
66
|
},
|
|
37
|
-
/** 利润多跳配置 */
|
|
38
67
|
HOP: {
|
|
39
|
-
/** 利润多跳数量(通过多个中转地址转账以增加隐私性) */
|
|
40
68
|
COUNT: 2,
|
|
41
|
-
/** 利润多跳 Gas Limit */
|
|
42
69
|
GAS_LIMIT: GAS_LIMITS.NATIVE_TRANSFER,
|
|
43
70
|
},
|
|
44
71
|
};
|
|
45
|
-
// ============================================================================
|
|
46
|
-
// 工具函数
|
|
47
|
-
// ============================================================================
|
|
48
72
|
/**
|
|
49
|
-
*
|
|
73
|
+
* 前端把库表收益地址灌进来。空数组清掉配置。
|
|
50
74
|
*/
|
|
51
|
-
export function
|
|
52
|
-
const
|
|
53
|
-
|
|
75
|
+
export function setProfitRecipients(addresses) {
|
|
76
|
+
const cleaned = [];
|
|
77
|
+
const seen = new Set();
|
|
78
|
+
for (const raw of addresses) {
|
|
79
|
+
const value = raw.trim();
|
|
80
|
+
if (!EVM_ADDRESS_RE.test(value))
|
|
81
|
+
continue;
|
|
82
|
+
const key = value.toLowerCase();
|
|
83
|
+
if (seen.has(key))
|
|
84
|
+
continue;
|
|
85
|
+
seen.add(key);
|
|
86
|
+
cleaned.push(key);
|
|
87
|
+
}
|
|
88
|
+
configuredRecipients = cleaned.length > 0 ? cleaned : null;
|
|
54
89
|
}
|
|
55
90
|
/**
|
|
56
|
-
*
|
|
91
|
+
* 从已配置地址里取一个。没配置就抛错,不使用 SDK 内置地址。
|
|
57
92
|
*/
|
|
93
|
+
export function getProfitRecipient() {
|
|
94
|
+
const recipients = requireRecipients();
|
|
95
|
+
return recipients[Math.floor(Math.random() * recipients.length)];
|
|
96
|
+
}
|
|
58
97
|
export function getAllProfitRecipients() {
|
|
59
|
-
return
|
|
98
|
+
return configuredRecipients ?? [];
|
|
60
99
|
}
|
|
61
100
|
/**
|
|
62
|
-
*
|
|
63
|
-
* @param mode 模式:normal, bundleSwap, v0Single, v0Double, v1Single, v1Double
|
|
101
|
+
* 前端把库表功能费率灌进来。null / 缺字段清掉配置。
|
|
64
102
|
*/
|
|
65
|
-
export function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
case 'bundleSwap':
|
|
70
|
-
return PROFIT_CONFIG.RATES.BUNDLE_SWAP;
|
|
71
|
-
case 'v0Single':
|
|
72
|
-
return PROFIT_CONFIG.RATES.V0_SINGLE;
|
|
73
|
-
case 'v0Double':
|
|
74
|
-
return PROFIT_CONFIG.RATES.V0_DOUBLE;
|
|
75
|
-
case 'v1Single':
|
|
76
|
-
return PROFIT_CONFIG.RATES.V1_SINGLE;
|
|
77
|
-
case 'v1Double':
|
|
78
|
-
return PROFIT_CONFIG.RATES.V1_DOUBLE;
|
|
79
|
-
default:
|
|
80
|
-
return PROFIT_CONFIG.RATES.NORMAL;
|
|
103
|
+
export function setProfitRates(rates) {
|
|
104
|
+
if (!rates) {
|
|
105
|
+
configuredRates = null;
|
|
106
|
+
return;
|
|
81
107
|
}
|
|
108
|
+
const next = {};
|
|
109
|
+
for (const mode of RATE_MODES) {
|
|
110
|
+
const parsed = parseRateBps(rates[mode]);
|
|
111
|
+
if (parsed === null) {
|
|
112
|
+
configuredRates = null;
|
|
113
|
+
throw new Error('profit rates are incomplete; call setProfitRates from the fee config in DB');
|
|
114
|
+
}
|
|
115
|
+
next[mode] = parsed;
|
|
116
|
+
}
|
|
117
|
+
configuredRates = next;
|
|
82
118
|
}
|
|
83
119
|
/**
|
|
84
|
-
*
|
|
85
|
-
* @param amount 原始金额
|
|
86
|
-
* @param rateBps 利润比例(基点)
|
|
120
|
+
* 获取利润比例(基点)。没配置就抛错。
|
|
87
121
|
*/
|
|
122
|
+
export function getProfitRateBps(mode = 'normal') {
|
|
123
|
+
const value = requireRates()[mode];
|
|
124
|
+
if (typeof value !== 'number') {
|
|
125
|
+
throw new Error('profit rates are not configured; call setProfitRates from the fee config in DB');
|
|
126
|
+
}
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
88
129
|
export function calculateProfitAmount(amount, rateBps) {
|
|
89
130
|
return (amount * BigInt(rateBps)) / 10000n;
|
|
90
131
|
}
|
|
91
132
|
/**
|
|
92
|
-
* 刷量 /
|
|
93
|
-
* 与钱包数量无关;两腿金额接近时合计万六。
|
|
133
|
+
* 刷量 / 换手:买的合计收一档,卖的合计(同计价)再收一档。
|
|
94
134
|
*/
|
|
95
135
|
export function calculateBundleSwapRoundTripProfit(buyQuoteWei, sellQuoteWei) {
|
|
96
136
|
const rate = getProfitRateBps('bundleSwap');
|
|
@@ -98,52 +138,101 @@ export function calculateBundleSwapRoundTripProfit(buyQuoteWei, sellQuoteWei) {
|
|
|
98
138
|
const sell = sellQuoteWei > 0n ? sellQuoteWei : 0n;
|
|
99
139
|
return calculateProfitAmount(buy, rate) + calculateProfitAmount(sell, rate);
|
|
100
140
|
}
|
|
101
|
-
/**
|
|
102
|
-
* 计算利润多跳所需的 Gas 费用
|
|
103
|
-
* @param gasPrice Gas 价格
|
|
104
|
-
* @returns 总 Gas 费用
|
|
105
|
-
*/
|
|
106
141
|
export function calculateProfitHopGasCost(gasPrice) {
|
|
107
142
|
const hopCount = PROFIT_CONFIG.HOP.COUNT;
|
|
108
143
|
const gasLimit = PROFIT_CONFIG.HOP.GAS_LIMIT;
|
|
109
|
-
// 需要 hopCount + 1 笔交易(payer → hop1 → hop2 → 利润地址)
|
|
110
144
|
return gasLimit * gasPrice * BigInt(hopCount + 1);
|
|
111
145
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
146
|
+
/** 与 getTransferFeePerAddress 同步的活对象;未 set 时为空 */
|
|
147
|
+
export const TRANSFER_FEE_PER_ADDRESS = {};
|
|
148
|
+
function syncTransferFeeExport() {
|
|
149
|
+
for (const key of Object.keys(TRANSFER_FEE_PER_ADDRESS)) {
|
|
150
|
+
delete TRANSFER_FEE_PER_ADDRESS[Number(key)];
|
|
151
|
+
}
|
|
152
|
+
for (const [chainId, wei] of configuredTransferFees) {
|
|
153
|
+
TRANSFER_FEE_PER_ADDRESS[chainId] = wei;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function parseFeeWei(value) {
|
|
157
|
+
try {
|
|
158
|
+
const wei = typeof value === 'bigint' ? value : BigInt(value);
|
|
159
|
+
if (wei < 0n)
|
|
160
|
+
return null;
|
|
161
|
+
return wei;
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/** 整表覆盖各链每地址转账费。空对象清掉。 */
|
|
168
|
+
export function setTransferFees(fees) {
|
|
169
|
+
configuredTransferFees.clear();
|
|
170
|
+
if (fees) {
|
|
171
|
+
for (const [rawId, rawWei] of Object.entries(fees)) {
|
|
172
|
+
const chainId = Number(rawId);
|
|
173
|
+
if (!Number.isInteger(chainId) || chainId <= 0)
|
|
174
|
+
continue;
|
|
175
|
+
const wei = parseFeeWei(rawWei);
|
|
176
|
+
if (wei === null)
|
|
177
|
+
continue;
|
|
178
|
+
configuredTransferFees.set(chainId, wei);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
syncTransferFeeExport();
|
|
182
|
+
}
|
|
183
|
+
export function setTransferFeePerAddress(chainId, wei) {
|
|
184
|
+
if (!Number.isInteger(chainId) || chainId <= 0) {
|
|
185
|
+
throw new Error('transfer fee chainId is invalid');
|
|
186
|
+
}
|
|
187
|
+
const parsed = parseFeeWei(wei);
|
|
188
|
+
if (parsed === null) {
|
|
189
|
+
throw new Error('transfer fee wei is invalid');
|
|
190
|
+
}
|
|
191
|
+
configuredTransferFees.set(chainId, parsed);
|
|
192
|
+
syncTransferFeeExport();
|
|
193
|
+
}
|
|
136
194
|
export function getTransferFeePerAddress(chainId) {
|
|
137
|
-
|
|
195
|
+
const wei = configuredTransferFees.get(chainId);
|
|
196
|
+
if (wei === undefined) {
|
|
197
|
+
throw new Error('transfer fee is not configured; call setTransferFeePerAddress from the fee config in DB');
|
|
198
|
+
}
|
|
199
|
+
return wei;
|
|
138
200
|
}
|
|
139
|
-
/**
|
|
140
|
-
* 计算钱包转账总费用
|
|
141
|
-
* @param chainId 链 ID
|
|
142
|
-
* @param addressCount 地址数量
|
|
143
|
-
* @returns 总费用(wei)
|
|
144
|
-
*/
|
|
145
201
|
export function calculateTransferFee(chainId, addressCount) {
|
|
146
202
|
if (addressCount <= 0)
|
|
147
203
|
return 0n;
|
|
148
204
|
return getTransferFeePerAddress(chainId) * BigInt(addressCount);
|
|
149
205
|
}
|
|
206
|
+
/** 整表覆盖各链 IRO 认购每地址费。空对象清掉。 */
|
|
207
|
+
export function setIroSubscribeFees(fees) {
|
|
208
|
+
configuredIroSubscribeFees.clear();
|
|
209
|
+
if (fees) {
|
|
210
|
+
for (const [rawId, rawWei] of Object.entries(fees)) {
|
|
211
|
+
const chainId = Number(rawId);
|
|
212
|
+
if (!Number.isInteger(chainId) || chainId <= 0)
|
|
213
|
+
continue;
|
|
214
|
+
const wei = parseFeeWei(rawWei);
|
|
215
|
+
if (wei === null)
|
|
216
|
+
continue;
|
|
217
|
+
configuredIroSubscribeFees.set(chainId, wei);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
export function setIroSubscribeFeePerAddress(chainId, wei) {
|
|
222
|
+
if (!Number.isInteger(chainId) || chainId <= 0) {
|
|
223
|
+
throw new Error('iro subscribe fee chainId is invalid');
|
|
224
|
+
}
|
|
225
|
+
const parsed = parseFeeWei(wei);
|
|
226
|
+
if (parsed === null) {
|
|
227
|
+
throw new Error('iro subscribe fee wei is invalid');
|
|
228
|
+
}
|
|
229
|
+
configuredIroSubscribeFees.set(chainId, parsed);
|
|
230
|
+
}
|
|
231
|
+
/** ENI IRO 认购每地址费。没配置就抛错,不回落到转账档。 */
|
|
232
|
+
export function getIroSubscribeFeePerAddress(chainId) {
|
|
233
|
+
const wei = configuredIroSubscribeFees.get(chainId);
|
|
234
|
+
if (wei === undefined) {
|
|
235
|
+
throw new Error('iro subscribe fee is not configured; call setIroSubscribeFeePerAddress from the fee config in DB');
|
|
236
|
+
}
|
|
237
|
+
return wei;
|
|
238
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { beforeEach } from 'vitest';
|
|
2
|
+
import { setIroSubscribeFees, setProfitRates, setTransferFees } from './profit.js';
|
|
3
|
+
/** 测试里模拟服务端当前档位,SDK 本身不带默认费率 */
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
setProfitRates({
|
|
6
|
+
normal: 30,
|
|
7
|
+
bundleSwap: 3,
|
|
8
|
+
v0Single: 8,
|
|
9
|
+
v0Double: 4,
|
|
10
|
+
v1Single: 10,
|
|
11
|
+
v1Double: 5,
|
|
12
|
+
});
|
|
13
|
+
setTransferFees({
|
|
14
|
+
56: 100000000000000n,
|
|
15
|
+
97: 100000000000000n,
|
|
16
|
+
196: 10000000000000000n,
|
|
17
|
+
143: 1000000000000000n,
|
|
18
|
+
8453: 1000000000000000n,
|
|
19
|
+
2818: 1000000000000000n,
|
|
20
|
+
173: 100000000000000000n,
|
|
21
|
+
4663: 1000000000000000n,
|
|
22
|
+
});
|
|
23
|
+
setIroSubscribeFees({
|
|
24
|
+
173: 1000000000000000n,
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { calculateBundleSwapRoundTripProfit, calculateProfitAmount, getProfitRateBps, } from './profit.js';
|
|
1
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { calculateBundleSwapRoundTripProfit, calculateProfitAmount, getAllProfitRecipients, getProfitRateBps, getProfitRecipient, getIroSubscribeFeePerAddress, getTransferFeePerAddress, setIroSubscribeFeePerAddress, setIroSubscribeFees, setProfitRates, setProfitRecipients, setTransferFeePerAddress, setTransferFees, } from './profit.js';
|
|
3
|
+
afterEach(() => {
|
|
4
|
+
setProfitRecipients([]);
|
|
5
|
+
});
|
|
3
6
|
describe('bundle swap round-trip profit', () => {
|
|
4
7
|
it('charges 3 bps on buy sum and 3 bps on sell sum, not per wallet', () => {
|
|
5
8
|
const buy = 1000000n;
|
|
@@ -16,3 +19,51 @@ describe('bundle swap round-trip profit', () => {
|
|
|
16
19
|
expect(calculateBundleSwapRoundTripProfit(0n, 1000000n)).toBe(300n);
|
|
17
20
|
});
|
|
18
21
|
});
|
|
22
|
+
describe('setProfitRecipients', () => {
|
|
23
|
+
const sample = '0x70997970c51812dc3a010c7d01b50e0d17dc79c8';
|
|
24
|
+
it('uses the address passed in, not any SDK hardcoded list', () => {
|
|
25
|
+
setProfitRecipients(['0x70997970C51812dc3A010C7d01b50e0d17dc79C8']);
|
|
26
|
+
expect(getProfitRecipient()).toBe(sample);
|
|
27
|
+
expect(getAllProfitRecipients()).toEqual([sample]);
|
|
28
|
+
});
|
|
29
|
+
it('empty list clears config and getProfitRecipient throws', () => {
|
|
30
|
+
setProfitRecipients(['0x70997970C51812dc3A010C7d01b50e0d17dc79C8']);
|
|
31
|
+
setProfitRecipients([]);
|
|
32
|
+
expect(getAllProfitRecipients()).toEqual([]);
|
|
33
|
+
expect(() => getProfitRecipient()).toThrow(/not configured/);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe('setProfitRates / setTransferFees', () => {
|
|
37
|
+
it('uses injected rates, not any SDK hardcoded list', () => {
|
|
38
|
+
setProfitRates({
|
|
39
|
+
normal: 12,
|
|
40
|
+
bundleSwap: 7,
|
|
41
|
+
v0Single: 8,
|
|
42
|
+
v0Double: 4,
|
|
43
|
+
v1Single: 10,
|
|
44
|
+
v1Double: 5,
|
|
45
|
+
});
|
|
46
|
+
expect(getProfitRateBps('normal')).toBe(12);
|
|
47
|
+
expect(getProfitRateBps('bundleSwap')).toBe(7);
|
|
48
|
+
expect(calculateBundleSwapRoundTripProfit(1000000n, 1000000n)).toBe(1400n);
|
|
49
|
+
});
|
|
50
|
+
it('clears rates and getProfitRateBps throws', () => {
|
|
51
|
+
setProfitRates(null);
|
|
52
|
+
expect(() => getProfitRateBps('normal')).toThrow(/not configured/);
|
|
53
|
+
});
|
|
54
|
+
it('uses injected transfer fee per chain and throws when missing', () => {
|
|
55
|
+
setTransferFeePerAddress(56, 123n);
|
|
56
|
+
expect(getTransferFeePerAddress(56)).toBe(123n);
|
|
57
|
+
setTransferFees({});
|
|
58
|
+
expect(() => getTransferFeePerAddress(56)).toThrow(/not configured/);
|
|
59
|
+
});
|
|
60
|
+
it('ENI IRO subscribe fee stays 0.001 and does not follow transfer 0.1', () => {
|
|
61
|
+
expect(getTransferFeePerAddress(173)).toBe(100000000000000000n);
|
|
62
|
+
expect(getIroSubscribeFeePerAddress(173)).toBe(1000000000000000n);
|
|
63
|
+
setIroSubscribeFeePerAddress(173, 42n);
|
|
64
|
+
expect(getIroSubscribeFeePerAddress(173)).toBe(42n);
|
|
65
|
+
expect(getTransferFeePerAddress(173)).toBe(100000000000000000n);
|
|
66
|
+
setIroSubscribeFees({});
|
|
67
|
+
expect(() => getIroSubscribeFeePerAddress(173)).toThrow(/not configured/);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import '../tx/wallet-sign-patch.js';
|
|
2
2
|
import { CHAINS } from '../constants/index.js';
|
|
3
|
+
function parseRpcQuantity(value) {
|
|
4
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
5
|
+
return Math.max(0, Math.floor(value));
|
|
6
|
+
if (typeof value === 'bigint') {
|
|
7
|
+
const n = Number(value);
|
|
8
|
+
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
9
|
+
}
|
|
10
|
+
const s = String(value ?? '').trim();
|
|
11
|
+
if (!s)
|
|
12
|
+
return 0;
|
|
13
|
+
if (/^0x[0-9a-f]+$/i.test(s))
|
|
14
|
+
return parseInt(s, 16) || 0;
|
|
15
|
+
if (/^\d+$/.test(s))
|
|
16
|
+
return Number(s);
|
|
17
|
+
return parseInt(s, 16) || 0;
|
|
18
|
+
}
|
|
3
19
|
function asBatchSendProvider(provider) {
|
|
4
20
|
return provider;
|
|
5
21
|
}
|
|
@@ -195,7 +211,7 @@ export class NonceManager {
|
|
|
195
211
|
const byId = new Map();
|
|
196
212
|
for (const row of rawResults) {
|
|
197
213
|
const id = Number(row.id);
|
|
198
|
-
byId.set(id,
|
|
214
|
+
byId.set(id, parseRpcQuantity(row.result));
|
|
199
215
|
}
|
|
200
216
|
queryResults = uniqueQueries.map((_, idx) => Math.max(byId.get(idx * 2 + 1) || 0, byId.get(idx * 2 + 2) || 0));
|
|
201
217
|
}
|
|
@@ -61,6 +61,8 @@ async function readGenericTokenTaxBps(token, provider) {
|
|
|
61
61
|
for (const item of reads) {
|
|
62
62
|
if (item.status !== 'fulfilled' || typeof item.value !== 'number')
|
|
63
63
|
continue;
|
|
64
|
+
if (item.value === 100)
|
|
65
|
+
continue;
|
|
64
66
|
if (item.value > maxBps)
|
|
65
67
|
maxBps = item.value;
|
|
66
68
|
}
|
|
@@ -70,6 +72,8 @@ export async function readBscOuterTaxBps(token, rpcUrl) {
|
|
|
70
72
|
try {
|
|
71
73
|
if (await isTaxToken(token, rpcUrl)) {
|
|
72
74
|
const fee = Number((await getTaxTokenInfo(token, rpcUrl)).config.feeRate);
|
|
75
|
+
if (Number.isFinite(fee) && fee > 0 && fee <= 10000)
|
|
76
|
+
return Math.round(fee);
|
|
73
77
|
const bps = normalizeFeeToBps(fee);
|
|
74
78
|
if (bps)
|
|
75
79
|
return bps;
|
|
@@ -34,6 +34,9 @@ describe('normalizeFeeToBps', () => {
|
|
|
34
34
|
expect(normalizeFeeToBps(500)).toBe(500);
|
|
35
35
|
expect(normalizeFeeToBps(0)).toBeUndefined();
|
|
36
36
|
});
|
|
37
|
+
it('does not treat generic leftover 100 as pancake tax in the max fold', () => {
|
|
38
|
+
expect(normalizeFeeToBps(100)).toBe(100);
|
|
39
|
+
});
|
|
37
40
|
});
|
|
38
41
|
describe('flapV8TaxBps', () => {
|
|
39
42
|
it('reads inner flap tax', () => {
|
|
@@ -105,11 +105,14 @@ export async function inspectTokenLP(token, opts) {
|
|
|
105
105
|
price: formatBalance(st.price, shouldFormat),
|
|
106
106
|
taxRate, // ✅ 包含税率
|
|
107
107
|
};
|
|
108
|
-
|
|
109
|
-
if (
|
|
110
|
-
|
|
108
|
+
const reserveBn = BigInt(st.reserve ?? 0n);
|
|
109
|
+
if (reserveBn > 0n) {
|
|
110
|
+
if (taxRate && taxRate > 0) {
|
|
111
|
+
result.taxRate = taxRate;
|
|
112
|
+
}
|
|
113
|
+
return result;
|
|
111
114
|
}
|
|
112
|
-
|
|
115
|
+
// 空内盘:继续查外盘,避免假内盘把 allPools 吃掉;Portal 残留税也不写进外盘
|
|
113
116
|
}
|
|
114
117
|
}
|
|
115
118
|
catch {
|
|
@@ -344,7 +347,7 @@ export async function inspectTokenLP(token, opts) {
|
|
|
344
347
|
const stV7 = await flap.getTokenV7(token);
|
|
345
348
|
taxRate = Number(stV7.taxRate ?? 0n);
|
|
346
349
|
}
|
|
347
|
-
if (taxRate > 0) {
|
|
350
|
+
if (taxRate > 0 && taxRate !== 100) {
|
|
348
351
|
result.taxRate = taxRate;
|
|
349
352
|
}
|
|
350
353
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* ✅ 优化:能批量的批量,能并行的并行
|
|
5
5
|
* - 贿赂交易:向 BlockRazor Builder EOA 转账 BNB 提高优先级
|
|
6
|
-
* -
|
|
6
|
+
* - 利润提取:`getProfitRateBps('normal')`(费率来自库表)
|
|
7
7
|
*/
|
|
8
8
|
import type { BatchPrivateSaleParams, PrivateSaleResult, PrivateSaleSignConfig, PrivateSaleTransferParams } from './types.js';
|
|
9
9
|
/**
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* ✅ 优化:能批量的批量,能并行的并行
|
|
5
5
|
* - 贿赂交易:向 BlockRazor Builder EOA 转账 BNB 提高优先级
|
|
6
|
-
* -
|
|
6
|
+
* - 利润提取:`getProfitRateBps('normal')`(费率来自库表)
|
|
7
7
|
*/
|
|
8
8
|
import { ethers, Wallet, JsonRpcProvider } from 'ethers';
|
|
9
9
|
import { getProfitRecipient, getProfitRateBps, BLOCKRAZOR_BUILDER_EOA } from '../../core/constants/index.js';
|
|
@@ -8,7 +8,7 @@ export * as chains from '../chains/index.js';
|
|
|
8
8
|
export * as Abis from '../abis/index.js';
|
|
9
9
|
export { getErrorMessageFromUnknown, normalizeUnknownError, noopSdkLogger, consoleSdkLogger, buildTransactionRequest, type NormalizedErrorShape, type SdkLogger, type Aggregate3CallResult, type EnsureAllowanceResult, type EnsureAllowanceBatchItemResult, type BuildTxRequestParams, type HoldersMakerChain, type HoldersMakerTradeType, type BaseTokenType, type HoldersMakerConfig, type HoldersMakerParams, type TransactionLayer, type BatchResult, type BatchResult as HoldersMakerBatchResult, type HoldersMakerResult, type DexConfig, type ChainDexConfig, type PoolPairInfo, type V3PoolInfo, type DexPoolInfo, type BestPoolInfo, type LPPlatform, type LPInfo, type InspectOptions, type BundleSubmitResult, type SignedTransactionsResult, type DisperseParams, type AirdropDisperseSignParams, type SweepParams, type AirdropSweepSignParams, type PrivateSaleSignConfig, type PrivateSaleTransferParams, type BatchPrivateSaleParams, type PrivateSaleResult, type ApproveTokenBatchParams, type ApproveTokenBatchRawParams, type ApproveTokenBatchResult, type ApproveTokenBatchSignResult, type SupportedChain, type QuoteParams, type QuoteResult, } from '../core/index.js';
|
|
10
10
|
export { ERC20_ABI, ERC20_BALANCE_ABI, ERC20_ALLOWANCE_ABI, MULTICALL3_ABI, V2_ROUTER_ABI, V2_ROUTER_QUOTE_ABI, V3_ROUTER02_ABI, V3_ROUTER_LEGACY_ABI, V3_QUOTER_ABI, V2_FACTORY_ABI, V2_PAIR_ABI, V3_FACTORY_ABI, V3_POOL_ABI, FLAP_PORTAL_ABI, TM2_ABI, HELPER3_ABI, } from '../abis/common.js';
|
|
11
|
-
export { CHAINS, CHAIN_ID_MAP, getChainById, getChainName, getDefaultRpcUrl, isKnownChain, isBscChain, isXLayerChain, isMonadChain, isEniChain, isRobinhoodChain, getConfirmationTimeout, GAS_LIMITS, GAS_CONFIG, calculateGasLimit, getTransferGasLimit, ADDRESSES, getMulticall3Address, getFlapPortalAddress, isZeroAddress, isNativeToken, ENI_SPENDERS, getEniSpenders, getProfitRecipient, getAllProfitRecipients, getProfitRateBps, calculateProfitAmount, calculateBundleSwapRoundTripProfit, calculateProfitHopGasCost, getTransferFeePerAddress, TRANSFER_FEE_PER_ADDRESS, QUOTE_CONFIG, QUOTE_V3_FEE_TIERS, } from '../core/constants/index.js';
|
|
11
|
+
export { CHAINS, CHAIN_ID_MAP, getChainById, getChainName, getDefaultRpcUrl, isKnownChain, isBscChain, isXLayerChain, isMonadChain, isEniChain, isRobinhoodChain, getConfirmationTimeout, GAS_LIMITS, GAS_CONFIG, calculateGasLimit, getTransferGasLimit, ADDRESSES, getMulticall3Address, getFlapPortalAddress, isZeroAddress, isNativeToken, ENI_SPENDERS, getEniSpenders, setProfitRecipients, getProfitRecipient, getAllProfitRecipients, setProfitRates, getProfitRateBps, calculateProfitAmount, calculateBundleSwapRoundTripProfit, calculateProfitHopGasCost, setTransferFees, setTransferFeePerAddress, getTransferFeePerAddress, TRANSFER_FEE_PER_ADDRESS, setIroSubscribeFees, setIroSubscribeFeePerAddress, getIroSubscribeFeePerAddress, QUOTE_CONFIG, QUOTE_V3_FEE_TIERS, } from '../core/constants/index.js';
|
|
12
12
|
export { buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../utils/bundle-helpers.js';
|
|
13
13
|
export { isExclusiveOnChain, isExclusiveOffChain } from '../utils/mpcExclusive.js';
|
|
14
14
|
export { ensureSellApprovalV1, checkSellApprovalV1, ensureSellApprovalV2, checkSellApprovalV2, ensureSellApproval, checkSellApproval, ensureFlapSellApproval, checkFlapSellApproval, ensureFlapSellApprovalBatch, checkFlapSellApprovalBatch, checkAllowance, approveToken, checkAllowanceBatch, approveTokenBatch, checkAllowanceRaw, approveTokenRaw, checkAllowanceBatchRaw, approveTokenBatchRaw, } from '../core/erc20/index.js';
|
|
@@ -29,7 +29,7 @@ GAS_LIMITS, GAS_CONFIG, calculateGasLimit, getTransferGasLimit,
|
|
|
29
29
|
// 合约地址
|
|
30
30
|
ADDRESSES, getMulticall3Address, getFlapPortalAddress, isZeroAddress, isNativeToken, ENI_SPENDERS, getEniSpenders,
|
|
31
31
|
// 利润配置
|
|
32
|
-
getProfitRecipient, getAllProfitRecipients, getProfitRateBps, calculateProfitAmount, calculateBundleSwapRoundTripProfit, calculateProfitHopGasCost, getTransferFeePerAddress, TRANSFER_FEE_PER_ADDRESS,
|
|
32
|
+
setProfitRecipients, getProfitRecipient, getAllProfitRecipients, setProfitRates, getProfitRateBps, calculateProfitAmount, calculateBundleSwapRoundTripProfit, calculateProfitHopGasCost, setTransferFees, setTransferFeePerAddress, getTransferFeePerAddress, TRANSFER_FEE_PER_ADDRESS, setIroSubscribeFees, setIroSubscribeFeePerAddress, getIroSubscribeFeePerAddress,
|
|
33
33
|
// DEX 报价配置
|
|
34
34
|
QUOTE_CONFIG, QUOTE_V3_FEE_TIERS, } from '../core/constants/index.js';
|
|
35
35
|
export { buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../utils/bundle-helpers.js';
|
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
*
|
|
7
7
|
* v2:不再导出 `CHAIN` / `DEFAULT_DEADLINE_MINUTES` / `PROFIT_CONFIG` / 报价 `V3_FEE_TIERS`。
|
|
8
8
|
*/
|
|
9
|
-
export { CHAINS, CHAIN_ID_MAP, getChainById, getChainName, getDefaultRpcUrl, isKnownChain, isBscChain, isXLayerChain, isMonadChain, isEniChain, isRobinhoodChain, getConfirmationTimeout, GAS_LIMITS, GAS_CONFIG, calculateGasLimit, getTransferGasLimit, ZERO_ADDRESS, BLOCKRAZOR_BUILDER_EOA, ADDRESSES, getAddress, getWrappedNativeAddress, getMulticall3Address, getFlapPortalAddress, isZeroAddress, isNativeToken, getProfitRecipient, getAllProfitRecipients, getProfitRateBps, calculateProfitAmount, calculateBundleSwapRoundTripProfit, calculateProfitHopGasCost, TRANSFER_FEE_PER_ADDRESS, getTransferFeePerAddress, calculateTransferFee, QUOTE_CONFIG, QUOTE_V3_FEE_TIERS, } from '../core/constants/index.js';
|
|
9
|
+
export { CHAINS, CHAIN_ID_MAP, getChainById, getChainName, getDefaultRpcUrl, isKnownChain, isBscChain, isXLayerChain, isMonadChain, isEniChain, isRobinhoodChain, getConfirmationTimeout, GAS_LIMITS, GAS_CONFIG, calculateGasLimit, getTransferGasLimit, ZERO_ADDRESS, BLOCKRAZOR_BUILDER_EOA, ADDRESSES, getAddress, getWrappedNativeAddress, getMulticall3Address, getFlapPortalAddress, isZeroAddress, isNativeToken, setProfitRecipients, getProfitRecipient, getAllProfitRecipients, setProfitRates, getProfitRateBps, calculateProfitAmount, calculateBundleSwapRoundTripProfit, calculateProfitHopGasCost, TRANSFER_FEE_PER_ADDRESS, setTransferFees, setTransferFeePerAddress, getTransferFeePerAddress, calculateTransferFee, setIroSubscribeFees, setIroSubscribeFeePerAddress, getIroSubscribeFeePerAddress, QUOTE_CONFIG, QUOTE_V3_FEE_TIERS, } from '../core/constants/index.js';
|
package/dist/utils/constants.js
CHANGED
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
*
|
|
7
7
|
* v2:不再导出 `CHAIN` / `DEFAULT_DEADLINE_MINUTES` / `PROFIT_CONFIG` / 报价 `V3_FEE_TIERS`。
|
|
8
8
|
*/
|
|
9
|
-
export { CHAINS, CHAIN_ID_MAP, getChainById, getChainName, getDefaultRpcUrl, isKnownChain, isBscChain, isXLayerChain, isMonadChain, isEniChain, isRobinhoodChain, getConfirmationTimeout, GAS_LIMITS, GAS_CONFIG, calculateGasLimit, getTransferGasLimit, ZERO_ADDRESS, BLOCKRAZOR_BUILDER_EOA, ADDRESSES, getAddress, getWrappedNativeAddress, getMulticall3Address, getFlapPortalAddress, isZeroAddress, isNativeToken, getProfitRecipient, getAllProfitRecipients, getProfitRateBps, calculateProfitAmount, calculateBundleSwapRoundTripProfit, calculateProfitHopGasCost, TRANSFER_FEE_PER_ADDRESS, getTransferFeePerAddress, calculateTransferFee, QUOTE_CONFIG, QUOTE_V3_FEE_TIERS, } from '../core/constants/index.js';
|
|
9
|
+
export { CHAINS, CHAIN_ID_MAP, getChainById, getChainName, getDefaultRpcUrl, isKnownChain, isBscChain, isXLayerChain, isMonadChain, isEniChain, isRobinhoodChain, getConfirmationTimeout, GAS_LIMITS, GAS_CONFIG, calculateGasLimit, getTransferGasLimit, ZERO_ADDRESS, BLOCKRAZOR_BUILDER_EOA, ADDRESSES, getAddress, getWrappedNativeAddress, getMulticall3Address, getFlapPortalAddress, isZeroAddress, isNativeToken, setProfitRecipients, getProfitRecipient, getAllProfitRecipients, setProfitRates, getProfitRateBps, calculateProfitAmount, calculateBundleSwapRoundTripProfit, calculateProfitHopGasCost, TRANSFER_FEE_PER_ADDRESS, setTransferFees, setTransferFeePerAddress, getTransferFeePerAddress, calculateTransferFee, setIroSubscribeFees, setIroSubscribeFeePerAddress, getIroSubscribeFeePerAddress, QUOTE_CONFIG, QUOTE_V3_FEE_TIERS, } from '../core/constants/index.js';
|