four-flap-meme-sdk 4.5.1 → 4.5.2
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/bundle/four-meme/core/create-token-quote.js +2 -1
- package/dist/bundle/four-meme/core/sell-flow.js +2 -2
- package/dist/bundle/four-meme/private/buy-quote.js +2 -1
- package/dist/bundle/four-meme/swap/bundle.js +3 -2
- package/dist/chains/robinhood/sign.js +5 -2
- package/dist/core/gas/bundle-gas-headroom.test.d.ts +1 -0
- package/dist/core/gas/bundle-gas-headroom.test.js +12 -0
- package/dist/core/gas/bundle-gas.d.ts +7 -0
- package/dist/core/gas/bundle-gas.js +13 -2
- package/dist/core/tx/wallet-sign-patch.js +5 -2
- package/dist/domains/flap/portal-bundle/helpers.js +1 -1
- package/dist/domains/four/contracts/tm-bundle-helpers.js +1 -1
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@ import { CHAINS, getMulticall3Address } from '../../../core/constants/index.js';
|
|
|
7
7
|
import { buildCreateTokenReq } from '../../../core/clients/four.js';
|
|
8
8
|
import { computeFourCreateValueWei, isNativeRaisedToken, } from '../../../domains/four/raised-token.js';
|
|
9
9
|
import { PLATFORM_CREATE_FEE } from './constants.js';
|
|
10
|
+
import { withGasHeadroom } from '../../../core/gas/bundle-gas.js';
|
|
10
11
|
export async function resolveCreateRaisedToken(fourClient, tokenInfo) {
|
|
11
12
|
if (tokenInfo.raisedToken)
|
|
12
13
|
return tokenInfo.raisedToken;
|
|
@@ -67,7 +68,7 @@ export async function signQuoteApproveIfNeeded(params) {
|
|
|
67
68
|
try {
|
|
68
69
|
const estimated = await provider.estimateGas({ ...unsigned, from: params.wallet.address });
|
|
69
70
|
if (estimated > 0n)
|
|
70
|
-
gasLimit = estimated;
|
|
71
|
+
gasLimit = withGasHeadroom(estimated);
|
|
71
72
|
}
|
|
72
73
|
catch {
|
|
73
74
|
/* 估算失败用 15 万,避免代理报价币 8 万不够 */
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Four.meme Bundle 核心方法 — 批量卖出流程
|
|
3
3
|
*/
|
|
4
4
|
import { ethers, Contract, Interface } from 'ethers';
|
|
5
|
-
import { getGasLimit } from '../../../core/gas/bundle-gas.js';
|
|
5
|
+
import { getGasLimit, withGasHeadroom } from '../../../core/gas/bundle-gas.js';
|
|
6
6
|
import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_HOP_COUNT, } from '../../../utils/bundle-helpers.js';
|
|
7
7
|
import { ADDRESSES, CHAINS, applySlippageBps, reverseSlippageBps } from '../../../core/constants/index.js';
|
|
8
8
|
import { MULTICALL3_ABI } from '../../../abis/common.js';
|
|
@@ -98,7 +98,7 @@ async function executeSellFlow(options) {
|
|
|
98
98
|
from: wallets[0].address,
|
|
99
99
|
});
|
|
100
100
|
if (estimated > 0n)
|
|
101
|
-
sellGasLimit = estimated;
|
|
101
|
+
sellGasLimit = withGasHeadroom(estimated);
|
|
102
102
|
}
|
|
103
103
|
catch {
|
|
104
104
|
/* 估算失败继续用配置上限 */
|
|
@@ -5,6 +5,7 @@ import { Contract, parseEther, parseUnits } from 'ethers';
|
|
|
5
5
|
import { ADDRESSES, ZERO_ADDRESS } from '../../../core/constants/index.js';
|
|
6
6
|
import { ERC20_ABI } from '../../../abis/common.js';
|
|
7
7
|
import { batchCheckAllowances } from '../../../core/erc20/index.js';
|
|
8
|
+
import { withGasHeadroom } from '../../../core/gas/bundle-gas.js';
|
|
8
9
|
const HELPER_TRY_BUY_ABI = [
|
|
9
10
|
'function tryBuy(address token, uint256 amount, uint256 funds) view returns (address tokenManager, address quote, uint256 estimatedAmount, uint256 estimatedCost, uint256 estimatedFee, uint256 amountMsgValue, uint256 amountApproval, uint256 amountFunds)',
|
|
10
11
|
];
|
|
@@ -60,7 +61,7 @@ export async function signQuoteApproveTxs(params) {
|
|
|
60
61
|
try {
|
|
61
62
|
const estimated = await provider.estimateGas({ ...unsigned, from: wallets[i].address });
|
|
62
63
|
if (estimated > 0n)
|
|
63
|
-
gasLimit = estimated;
|
|
64
|
+
gasLimit = withGasHeadroom(estimated);
|
|
64
65
|
}
|
|
65
66
|
catch {
|
|
66
67
|
/* 估算失败用 15 万 */
|
|
@@ -9,6 +9,7 @@ import { NonceManager, getOptimizedGasPrice, getGasPriceConfig, buildProfitHopTr
|
|
|
9
9
|
import { getBribeAmount, getProfitRecipient } from '../../config-helpers.js';
|
|
10
10
|
import { calculateBundleSwapRoundTripProfit } from '../../../core/constants/index.js';
|
|
11
11
|
import { TM_ABI, TM_ADDRESS } from '../swap-internal.js';
|
|
12
|
+
import { withGasHeadroom } from '../../../core/gas/bundle-gas.js';
|
|
12
13
|
/**
|
|
13
14
|
* Four内盘捆绑换手
|
|
14
15
|
*/
|
|
@@ -82,9 +83,9 @@ export async function fourBundleSwapMerkle(params) {
|
|
|
82
83
|
provider.estimateGas({ ...buyUnsigned, from: buyer.address, value: buyerFunds }),
|
|
83
84
|
]);
|
|
84
85
|
if (sellGas > 0n)
|
|
85
|
-
sellGasLimit = sellGas;
|
|
86
|
+
sellGasLimit = withGasHeadroom(sellGas);
|
|
86
87
|
if (buyGas > 0n)
|
|
87
|
-
buyGasLimit = buyGas;
|
|
88
|
+
buyGasLimit = withGasHeadroom(buyGas);
|
|
88
89
|
}
|
|
89
90
|
catch {
|
|
90
91
|
/* 估算失败继续用配置上限 */
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { JsonRpcProvider, Transaction, Wallet } from 'ethers';
|
|
5
5
|
import { ROBINHOOD_CHAIN_ID, ROBINHOOD_RPC_URL } from './constants.js';
|
|
6
|
+
import { withGasHeadroom } from '../../core/gas/bundle-gas.js';
|
|
6
7
|
export async function signRobinhoodTx(params) {
|
|
7
8
|
const rpcUrl = params.rpcUrl ?? ROBINHOOD_RPC_URL;
|
|
8
9
|
const provider = new JsonRpcProvider(rpcUrl, ROBINHOOD_CHAIN_ID, { staticNetwork: true });
|
|
@@ -25,8 +26,10 @@ export async function signRobinhoodTx(params) {
|
|
|
25
26
|
data: params.data,
|
|
26
27
|
value: params.value ?? 0n,
|
|
27
28
|
});
|
|
28
|
-
if (estimated > 0n)
|
|
29
|
-
|
|
29
|
+
if (estimated > 0n) {
|
|
30
|
+
const isCall = params.data != null && params.data !== '0x';
|
|
31
|
+
gasLimit = isCall ? withGasHeadroom(estimated) : estimated;
|
|
32
|
+
}
|
|
30
33
|
}
|
|
31
34
|
catch {
|
|
32
35
|
/* 节点没给出上限时用服务端配置 */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { estimateApproveGasLimit, estimateGasWithSafety, withGasHeadroom } from './bundle-gas.js';
|
|
3
|
+
describe('gas headroom', () => {
|
|
4
|
+
it('估算结果默认留 20% 余量(捆绑后面的交易状态会变,刚好够会 OutOfGas)', async () => {
|
|
5
|
+
expect(withGasHeadroom(273241n)).toBe(327889n);
|
|
6
|
+
expect(withGasHeadroom(0n)).toBe(0n);
|
|
7
|
+
const provider = { estimateGas: async () => 100000n };
|
|
8
|
+
expect(await estimateApproveGasLimit(provider, '0x1', '0x2', '0x')).toBe(120000n);
|
|
9
|
+
expect(await estimateGasWithSafety(provider, {})).toBe(120000n);
|
|
10
|
+
expect(await estimateGasWithSafety(provider, {}, { multiplier: 1 })).toBe(100000n);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -11,6 +11,13 @@ export interface GasEstimateConfig {
|
|
|
11
11
|
fallbackGasLimit?: bigint;
|
|
12
12
|
allowFailure?: boolean;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* eth_estimateGas 按 latest 状态给「刚好够」的值。捆绑里排在后面的交易执行时,前面的买卖已经改了
|
|
16
|
+
* 池子 / 税币状态,实际用量会更高;刚好够的上限会 OutOfGas,BlockRazor 整包判成
|
|
17
|
+
* non-reverting tx in bundle failed。所有估算都加 20% 余量(4.4.3 去掉后大量捆绑失败)。
|
|
18
|
+
*/
|
|
19
|
+
export declare const GAS_HEADROOM_PERCENT = 120n;
|
|
20
|
+
export declare function withGasHeadroom(gas: bigint): bigint;
|
|
14
21
|
export declare function estimateGasWithSafety(provider: JsonRpcProvider, txRequest: TransactionRequest, config?: GasEstimateConfig): Promise<bigint>;
|
|
15
22
|
export declare function estimateGasBatch(provider: JsonRpcProvider, txRequests: TransactionRequest[], config?: GasEstimateConfig): Promise<bigint[]>;
|
|
16
23
|
export interface BuildTransactionOptions {
|
|
@@ -22,8 +22,19 @@ export async function getOptimizedGasPrice(provider, config) {
|
|
|
22
22
|
}
|
|
23
23
|
return gasPrice;
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* eth_estimateGas 按 latest 状态给「刚好够」的值。捆绑里排在后面的交易执行时,前面的买卖已经改了
|
|
27
|
+
* 池子 / 税币状态,实际用量会更高;刚好够的上限会 OutOfGas,BlockRazor 整包判成
|
|
28
|
+
* non-reverting tx in bundle failed。所有估算都加 20% 余量(4.4.3 去掉后大量捆绑失败)。
|
|
29
|
+
*/
|
|
30
|
+
export const GAS_HEADROOM_PERCENT = 120n;
|
|
31
|
+
export function withGasHeadroom(gas) {
|
|
32
|
+
if (gas <= 0n)
|
|
33
|
+
return gas;
|
|
34
|
+
return (gas * GAS_HEADROOM_PERCENT) / 100n;
|
|
35
|
+
}
|
|
25
36
|
export async function estimateGasWithSafety(provider, txRequest, config) {
|
|
26
|
-
const multiplier = config?.multiplier ?? 1;
|
|
37
|
+
const multiplier = config?.multiplier ?? 1.2;
|
|
27
38
|
const fallback = config?.fallbackGasLimit ?? 600000n;
|
|
28
39
|
const allowFailure = config?.allowFailure ?? true;
|
|
29
40
|
try {
|
|
@@ -96,7 +107,7 @@ export async function estimateApproveGasLimit(provider, from, to, data, fallback
|
|
|
96
107
|
try {
|
|
97
108
|
const estimated = await provider.estimateGas({ from, to, data });
|
|
98
109
|
if (estimated > 0n)
|
|
99
|
-
return estimated;
|
|
110
|
+
return withGasHeadroom(estimated);
|
|
100
111
|
}
|
|
101
112
|
catch {
|
|
102
113
|
/* 节点没给出上限 */
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* 模块加载时 patch Wallet.prototype.signTransaction(全局一次)
|
|
4
4
|
*/
|
|
5
5
|
import { Wallet } from 'ethers';
|
|
6
|
+
import { withGasHeadroom } from '../gas/bundle-gas.js';
|
|
6
7
|
function chainGasPrice(fee) {
|
|
7
8
|
const candidates = [fee.maxFeePerGas, fee.gasPrice].filter((v) => v != null && v > 0n);
|
|
8
9
|
if (candidates.length === 0)
|
|
@@ -63,8 +64,10 @@ Wallet.prototype.signTransaction = async function (tx) {
|
|
|
63
64
|
data: signable.data,
|
|
64
65
|
value: signable.value,
|
|
65
66
|
});
|
|
66
|
-
if (estimated > 0n)
|
|
67
|
-
|
|
67
|
+
if (estimated > 0n) {
|
|
68
|
+
const isCall = signable.data != null && signable.data !== '0x';
|
|
69
|
+
signable = { ...signable, gasLimit: isCall ? withGasHeadroom(estimated) : estimated };
|
|
70
|
+
}
|
|
68
71
|
}
|
|
69
72
|
catch {
|
|
70
73
|
/* 节点没给出 gas 上限时保留原来的上限,避免发不出去 */
|
|
@@ -38,7 +38,7 @@ export function getErrorMessage(key, ...args) {
|
|
|
38
38
|
*/
|
|
39
39
|
export async function estimateGasWithMultiplier(provider, txRequest, multiplier) {
|
|
40
40
|
const estimated = await provider.estimateGas(txRequest);
|
|
41
|
-
const mult = multiplier ?? 1;
|
|
41
|
+
const mult = multiplier ?? 1.2; // 默认 20% 安全余量(捆绑后面的交易状态会变)
|
|
42
42
|
return BigInt(Math.ceil(Number(estimated) * mult));
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
@@ -41,7 +41,7 @@ export async function waitForBundleWithProvider(provider, firstTxSigned, _bundle
|
|
|
41
41
|
*/
|
|
42
42
|
export async function estimateGasWithMultiplier(provider, txRequest, multiplier) {
|
|
43
43
|
const estimated = await provider.estimateGas(txRequest);
|
|
44
|
-
const mult = multiplier ?? 1;
|
|
44
|
+
const mult = multiplier ?? 1.2; // 默认 20% 安全余量(捆绑后面的交易状态会变)
|
|
45
45
|
return BigInt(Math.ceil(Number(estimated) * mult));
|
|
46
46
|
}
|
|
47
47
|
/**
|