four-flap-meme-sdk 4.3.22 → 4.4.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/bundle/four-meme/approve-tokenmanager.js +46 -16
- package/dist/bundle/four-meme/core/create-token-quote.d.ts +5 -1
- package/dist/bundle/four-meme/core/create-token-quote.js +29 -4
- package/dist/bundle/four-meme/core/sell-flow.js +3 -3
- package/dist/bundle/four-meme/pancake-proxy/approve.js +47 -10
- package/dist/bundle/four-meme/private/sell.js +2 -10
- package/dist/bundle/four-meme/swap/bundle.js +4 -4
- package/dist/bundle/four-meme/swap-buy-first/multi-wallet.js +25 -8
- package/dist/bundle/four-meme/swap-buy-first/single.js +30 -9
- package/dist/bundle/four-meme/swap-sign-helpers.js +56 -3
- package/dist/bundle/four-meme/utils-sweep/erc20-no-hop.js +18 -7
- package/dist/bundle/four-meme/utils-sweep/with-hops.js +10 -4
- package/dist/bundle/internal.d.ts +5 -0
- package/dist/bundle/internal.js +36 -0
- package/dist/chains/bsc/pancake/bundle-buy-first-quote.js +24 -8
- package/dist/chains/bsc/pancake/bundle-buy-first-types.d.ts +2 -0
- package/dist/chains/bsc/pancake/bundle-buy-first-validate.d.ts +1 -1
- package/dist/chains/bsc/pancake/bundle-buy-first-validate.js +2 -2
- package/dist/chains/bsc/pancake/bundle-buy-first.js +1 -0
- package/dist/chains/bsc/pancake/bundle-swap-types.d.ts +2 -0
- package/dist/chains/bsc/pancake/quote.d.ts +7 -0
- package/dist/chains/bsc/pancake/quote.js +9 -1
- package/dist/chains/bsc/pancake/swap-batch.js +15 -16
- package/dist/chains/bsc/pancake/swap-build.d.ts +1 -1
- package/dist/chains/bsc/pancake/swap-build.js +15 -9
- package/dist/chains/bsc/pancake/swap-quick.js +4 -4
- package/dist/chains/bsc/pancake/swap-single.js +1 -0
- package/dist/chains/bsc/platforms/iro/token.d.ts +1 -0
- package/dist/chains/bsc/platforms/iro/token.js +36 -1
- package/dist/chains/eni/batch-router/transfer-sweep.js +10 -14
- package/dist/chains/eni/platforms/daoaas/graduation.js +60 -14
- package/dist/chains/eni/platforms/daoaas/meta.d.ts +0 -4
- package/dist/chains/eni/platforms/daoaas/meta.js +112 -27
- package/dist/chains/eni/platforms/daoaas/portal-direct-submit.js +11 -6
- package/dist/chains/eni/platforms/daoaas/portal-direct-tx.js +4 -2
- package/dist/chains/eni/platforms/daoaas/portal.d.ts +5 -0
- package/dist/chains/eni/platforms/daoaas/portal.js +63 -9
- package/dist/chains/eni/platforms/dswap/liquidity.js +144 -49
- package/dist/chains/eni/platforms/fair-launch/launcher.js +2 -10
- package/dist/chains/eni/platforms/fair-launch/pool.d.ts +1 -0
- package/dist/chains/eni/platforms/fair-launch/pool.js +16 -2
- package/dist/chains/eni/platforms/iro/token.d.ts +1 -0
- package/dist/chains/eni/platforms/iro/token.js +36 -1
- package/dist/chains/robinhood/pons-v2/launch.js +49 -18
- package/dist/chains/xlayer/eip7702/bundle-approve.js +8 -8
- package/dist/chains/xlayer/eip7702/bundle-sell.js +3 -4
- package/dist/chains/xlayer/eip7702/bundle-swap-batch.js +4 -2
- package/dist/chains/xlayer/eip7702/bundle-swap-multi.js +3 -3
- package/dist/chains/xlayer/eip7702/multi-hop-transfer-sweep.js +4 -10
- package/dist/chains/xlayer/eip7702/volume-bundle.js +45 -24
- package/dist/chains/xlayer/eoa/eoa-wash-volume.js +4 -14
- package/dist/core/quote/quote-helpers.d.ts +11 -0
- package/dist/core/quote/quote-helpers.js +121 -20
- package/dist/core/quote/quote-helpers.test.js +11 -1
- package/dist/domains/dex/direct-router-v2-buy.js +4 -2
- package/dist/domains/dex/direct-router-v2-sell.js +63 -12
- package/dist/domains/dex/direct-router-v3.js +3 -4
- package/dist/domains/dex/quote.js +15 -5
- package/dist/domains/flap/portal-bundle-merkle/pancake-proxy/approve.js +48 -8
- package/dist/domains/flap/portal-bundle-merkle/swap-batch.js +8 -10
- package/dist/domains/flap/portal-bundle-merkle/swap-buy-first-quote.js +24 -9
- package/dist/domains/flap/portal-bundle-merkle/swap-planning.js +6 -2
- package/dist/domains/flap/portal-bundle-merkle/utils/sweep.js +14 -7
- package/dist/domains/flap/reader.d.ts +6 -0
- package/dist/domains/flap/reader.js +77 -1
- package/dist/domains/four/contracts/tm-bundle-batch.js +2 -10
- package/dist/domains/four/contracts/tm-bundle-create.js +5 -2
- package/dist/domains/four/contracts/tm.d.ts +2 -0
- package/dist/domains/four/contracts/tm.js +59 -2
- package/dist/domains/four/tax-token.js +60 -28
- package/dist/domains/lp-inspect/inspect-tax.js +98 -12
- package/dist/domains/lp-inspect/inspect.js +57 -44
- package/dist/domains/lp-inspect/registry.js +33 -7
- package/dist/domains/openfour/registry.js +15 -5
- package/dist/utils/airdrop-sweep.js +37 -17
- package/dist/utils/swap-helpers.js +35 -7
- package/package.json +1 -1
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* - 构建跟买交易 (claimForSubmit / batchClaimForSubmit)
|
|
8
8
|
*/
|
|
9
9
|
import { Contract, Interface, Wallet, JsonRpcProvider } from 'ethers';
|
|
10
|
+
import { MULTICALL3_ABI } from '../../../../abis/common.js';
|
|
11
|
+
import { getMulticall3Address } from '../../../../core/constants/index.js';
|
|
10
12
|
import { ENI_CHAIN_ID, ENI_RPC_URL, LP_FAIR_POOL_ABI, NATIVE_TRANSFER_GAS_LIMIT } from '../../constants.js';
|
|
11
13
|
import { buildProfitTransfer } from '../daoaas/portal-direct.js';
|
|
12
14
|
import { calculateProfitAmount, getProfitRateBps, getProfitRecipient } from '../../../../core/constants/profit.js';
|
|
@@ -68,14 +70,26 @@ export class FairPoolQuery {
|
|
|
68
70
|
return this.pool.userCount();
|
|
69
71
|
}
|
|
70
72
|
/** 检查当前是否在 claim 窗口内 */
|
|
73
|
+
async readPoolUints(names) {
|
|
74
|
+
const multicall = new Contract(getMulticall3Address(ENI_CHAIN_ID), MULTICALL3_ABI, this.provider);
|
|
75
|
+
const aggregate3 = multicall.aggregate3;
|
|
76
|
+
if (!aggregate3)
|
|
77
|
+
throw new Error('aggregate3 missing');
|
|
78
|
+
const rows = (await aggregate3.staticCall(names.map((name) => ({
|
|
79
|
+
target: this.poolAddress,
|
|
80
|
+
allowFailure: false,
|
|
81
|
+
callData: poolIface.encodeFunctionData(name, []),
|
|
82
|
+
}))));
|
|
83
|
+
return names.map((name, index) => poolIface.decodeFunctionResult(name, rows[index].returnData)[0]);
|
|
84
|
+
}
|
|
71
85
|
async isClaimOpen() {
|
|
72
|
-
const [start, end] = await
|
|
86
|
+
const [start, end] = await this.readPoolUints(['claimStartTime', 'claimEndTime']);
|
|
73
87
|
const now = BigInt(Math.floor(Date.now() / 1000));
|
|
74
88
|
return now >= start && now <= end;
|
|
75
89
|
}
|
|
76
90
|
/** 检查是否还有 claim 名额 */
|
|
77
91
|
async hasClaimSlots() {
|
|
78
|
-
const [count, max] = await
|
|
92
|
+
const [count, max] = await this.readPoolUints(['userCount', 'maxCount']);
|
|
79
93
|
return count < max;
|
|
80
94
|
}
|
|
81
95
|
get providerInstance() {
|
|
@@ -9,6 +9,7 @@ import type { IroSubscribeParams, IroPledgeParams, IroTxResult, IroTokenInfo, Ir
|
|
|
9
9
|
export declare class IroTokenQuery {
|
|
10
10
|
private provider;
|
|
11
11
|
private token;
|
|
12
|
+
private chainId;
|
|
12
13
|
readonly tokenAddress: string;
|
|
13
14
|
constructor(tokenAddress: string, rpcUrl?: string, chainId?: number);
|
|
14
15
|
name(): Promise<string>;
|
|
@@ -5,20 +5,37 @@
|
|
|
5
5
|
* 利润使用直接转账(无多跳),与 ENI 其他模块一致
|
|
6
6
|
*/
|
|
7
7
|
import { Contract, Interface, Wallet, JsonRpcProvider } from 'ethers';
|
|
8
|
+
import { MULTICALL3_ABI } from '../../../../abis/common.js';
|
|
9
|
+
import { getMulticall3Address } from '../../../../core/constants/index.js';
|
|
8
10
|
import { ENI_CHAIN_ID, ENI_RPC_URL, NATIVE_TRANSFER_GAS_LIMIT } from '../../constants.js';
|
|
9
11
|
import { IRO_TOKEN_ABI, SUBSCRIBE_GAS_LIMIT } from './constants.js';
|
|
10
12
|
import { getProfitRecipient, getIroSubscribeFeePerAddress } from '../../../../core/constants/profit.js';
|
|
11
13
|
const tokenIface = new Interface(IRO_TOKEN_ABI);
|
|
14
|
+
const IRO_INFO_VIEWS = ['name', 'symbol', 'decimals', 'totalSupply', 'POOL', 'isOpen', 'price', 'creator', 'TKA'];
|
|
15
|
+
async function readIroInfoViews(provider, tokenAddress, chainId) {
|
|
16
|
+
const multicall = new Contract(getMulticall3Address(chainId), MULTICALL3_ABI, provider);
|
|
17
|
+
const aggregate3 = multicall.aggregate3;
|
|
18
|
+
if (!aggregate3)
|
|
19
|
+
throw new Error('aggregate3 missing');
|
|
20
|
+
const rows = (await aggregate3.staticCall(IRO_INFO_VIEWS.map((name) => ({
|
|
21
|
+
target: tokenAddress,
|
|
22
|
+
allowFailure: false,
|
|
23
|
+
callData: tokenIface.encodeFunctionData(name, []),
|
|
24
|
+
}))));
|
|
25
|
+
return IRO_INFO_VIEWS.map((name, index) => tokenIface.decodeFunctionResult(name, rows[index].returnData)[0]);
|
|
26
|
+
}
|
|
12
27
|
// ============================================================================
|
|
13
28
|
// 查询
|
|
14
29
|
// ============================================================================
|
|
15
30
|
export class IroTokenQuery {
|
|
16
31
|
provider;
|
|
17
32
|
token;
|
|
33
|
+
chainId;
|
|
18
34
|
tokenAddress;
|
|
19
35
|
constructor(tokenAddress, rpcUrl, chainId) {
|
|
20
36
|
this.tokenAddress = tokenAddress;
|
|
21
|
-
this.
|
|
37
|
+
this.chainId = chainId ?? ENI_CHAIN_ID;
|
|
38
|
+
this.provider = new JsonRpcProvider(rpcUrl ?? ENI_RPC_URL, this.chainId, { staticNetwork: true });
|
|
22
39
|
this.token = new Contract(tokenAddress, IRO_TOKEN_ABI, this.provider);
|
|
23
40
|
}
|
|
24
41
|
async name() {
|
|
@@ -78,6 +95,24 @@ export class IroTokenQuery {
|
|
|
78
95
|
return this.token.dayProduce(day);
|
|
79
96
|
}
|
|
80
97
|
async getTokenInfo() {
|
|
98
|
+
try {
|
|
99
|
+
const row = await readIroInfoViews(this.provider, this.tokenAddress, this.chainId);
|
|
100
|
+
return {
|
|
101
|
+
name: String(row[0]),
|
|
102
|
+
symbol: String(row[1]),
|
|
103
|
+
decimals: Number(row[2]),
|
|
104
|
+
totalSupply: row[3],
|
|
105
|
+
tokenAddress: this.tokenAddress,
|
|
106
|
+
poolAddress: String(row[4]),
|
|
107
|
+
isOpen: Boolean(row[5]),
|
|
108
|
+
price: row[6],
|
|
109
|
+
creator: String(row[7]),
|
|
110
|
+
tka: String(row[8]),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
/* 回退逐个 view */
|
|
115
|
+
}
|
|
81
116
|
const [name, symbol, decimals, totalSupply, poolAddress, isOpen, priceVal, creatorAddr, tkaAddr] = await Promise.all([
|
|
82
117
|
this.name(),
|
|
83
118
|
this.symbol(),
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* pons v2 发币:读 launchFee + expectedEconomics,再签 launchToken 或 launchAndBuy
|
|
3
3
|
*/
|
|
4
|
-
import { Contract, JsonRpcProvider, Wallet, ZeroAddress } from 'ethers';
|
|
4
|
+
import { Contract, Interface, JsonRpcProvider, Wallet, ZeroAddress } from 'ethers';
|
|
5
|
+
import { MULTICALL3_ABI } from '../../../abis/common.js';
|
|
6
|
+
import { getMulticall3Address } from '../../../core/constants/index.js';
|
|
5
7
|
import { PONS_V2_FACTORY, PONS_V2_FACTORY_ABI, robinhoodConfiguredGas, ROBINHOOD_CHAIN_ID, ROBINHOOD_RPC_URL, } from '../constants.js';
|
|
6
8
|
import { signRobinhoodTx } from '../sign.js';
|
|
7
9
|
import { buildPonsV2LaunchCall, encodePonsV2PreviewLaunchEconomics, randomPonsV2Salt, } from './encode.js';
|
|
@@ -24,23 +26,36 @@ export async function listOpenPonsV2LaunchConfigs(rpcUrl = ROBINHOOD_RPC_URL) {
|
|
|
24
26
|
const provider = new JsonRpcProvider(rpcUrl, ROBINHOOD_CHAIN_ID, { staticNetwork: true });
|
|
25
27
|
const factory = new Contract(PONS_V2_FACTORY, PONS_V2_FACTORY_ABI, provider);
|
|
26
28
|
const count = Number(await factory.launchConfigCount());
|
|
29
|
+
if (count <= 0)
|
|
30
|
+
return [];
|
|
31
|
+
const configIface = new Interface(PONS_V2_FACTORY_ABI);
|
|
32
|
+
const multicall = new Contract(getMulticall3Address(ROBINHOOD_CHAIN_ID), MULTICALL3_ABI, provider);
|
|
33
|
+
const aggregate3 = multicall.aggregate3;
|
|
34
|
+
if (!aggregate3)
|
|
35
|
+
return [];
|
|
36
|
+
const rows = (await aggregate3.staticCall(Array.from({ length: count }, (_, id) => ({
|
|
37
|
+
target: PONS_V2_FACTORY,
|
|
38
|
+
allowFailure: true,
|
|
39
|
+
callData: configIface.encodeFunctionData('getLaunchConfig', [BigInt(id)]),
|
|
40
|
+
}))));
|
|
27
41
|
const configs = [];
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
42
|
+
rows.forEach((row, id) => {
|
|
43
|
+
if (!row.success || row.returnData === '0x')
|
|
44
|
+
return;
|
|
45
|
+
const decoded = configIface.decodeFunctionResult('getLaunchConfig', row.returnData)[0];
|
|
46
|
+
if (!decoded.enabled)
|
|
47
|
+
return;
|
|
33
48
|
configs.push({
|
|
34
49
|
id: BigInt(id),
|
|
35
|
-
supply:
|
|
36
|
-
curveFeeBps:
|
|
37
|
-
phantomQuote:
|
|
38
|
-
graduationThreshold:
|
|
39
|
-
poolFee: Number(
|
|
40
|
-
tickSpacing: Number(
|
|
41
|
-
enabled,
|
|
50
|
+
supply: decoded.supply,
|
|
51
|
+
curveFeeBps: decoded.curveFeeBps,
|
|
52
|
+
phantomQuote: decoded.phantomQuote,
|
|
53
|
+
graduationThreshold: decoded.graduationThreshold,
|
|
54
|
+
poolFee: Number(decoded.poolFee),
|
|
55
|
+
tickSpacing: Number(decoded.tickSpacing),
|
|
56
|
+
enabled: true,
|
|
42
57
|
});
|
|
43
|
-
}
|
|
58
|
+
});
|
|
44
59
|
return configs;
|
|
45
60
|
}
|
|
46
61
|
export async function signPonsV2Launch(params) {
|
|
@@ -56,10 +71,26 @@ export async function signPonsV2Launch(params) {
|
|
|
56
71
|
if (!allowed) {
|
|
57
72
|
throw new Error('pons factory is not allowing this wallet to launch');
|
|
58
73
|
}
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
74
|
+
const provider = new JsonRpcProvider(rpcUrl, ROBINHOOD_CHAIN_ID, { staticNetwork: true });
|
|
75
|
+
const feeIface = new Interface(['function launchFee() view returns (uint256)']);
|
|
76
|
+
const multicall = new Contract(getMulticall3Address(ROBINHOOD_CHAIN_ID), MULTICALL3_ABI, provider);
|
|
77
|
+
const aggregate3 = multicall.aggregate3;
|
|
78
|
+
if (!aggregate3)
|
|
79
|
+
throw new Error('Robinhood Multicall3 不可用');
|
|
80
|
+
const feeRows = (await aggregate3.staticCall([
|
|
81
|
+
{
|
|
82
|
+
target: PONS_V2_FACTORY,
|
|
83
|
+
allowFailure: false,
|
|
84
|
+
callData: feeIface.encodeFunctionData('launchFee', []),
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
target: PONS_V2_FACTORY,
|
|
88
|
+
allowFailure: false,
|
|
89
|
+
callData: encodePonsV2PreviewLaunchEconomics(launchConfigId, pairToken),
|
|
90
|
+
},
|
|
91
|
+
]));
|
|
92
|
+
const launchFee = feeIface.decodeFunctionResult('launchFee', feeRows[0].returnData)[0];
|
|
93
|
+
const expectedEconomics = feeRows[1].returnData;
|
|
63
94
|
const token = {
|
|
64
95
|
name: params.name,
|
|
65
96
|
symbol: params.symbol,
|
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
* 在 EIP-7702 中,通过 delegate 调用时 msg.sender 是 EOA 本身
|
|
8
8
|
* 需要使用 executeApprove 函数
|
|
9
9
|
*/
|
|
10
|
-
import { ethers
|
|
10
|
+
import { ethers } from 'ethers';
|
|
11
11
|
import { getCachedProvider, createWallet, signAuthorizationsBatch, buildEIP7702Transaction } from './utils.js';
|
|
12
|
-
import { UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI
|
|
12
|
+
import { UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI } from './constants.js';
|
|
13
|
+
import { batchCheckAllowances } from '../../../core/erc20/allowances.js';
|
|
13
14
|
// 扩展 ABI 包含 executeApprove
|
|
14
15
|
const EXTENDED_DELEGATE_ABI = [
|
|
15
16
|
...UNIFIED_DELEGATE_ABI,
|
|
@@ -133,16 +134,15 @@ export async function bundleApproveMultiSpenders(params) {
|
|
|
133
134
|
*/
|
|
134
135
|
export async function checkApprovalStatus(walletAddresses, tokenAddress, spenderAddress, config, decimals = 18) {
|
|
135
136
|
const provider = getCachedProvider(config?.rpcUrl);
|
|
136
|
-
const
|
|
137
|
-
const results =
|
|
138
|
-
const
|
|
139
|
-
const allowanceBigInt = BigInt(allowance.toString());
|
|
137
|
+
const allowances = await batchCheckAllowances(provider, tokenAddress, walletAddresses, spenderAddress);
|
|
138
|
+
const results = walletAddresses.map((address, index) => {
|
|
139
|
+
const allowanceBigInt = allowances[index] ?? 0n;
|
|
140
140
|
return {
|
|
141
141
|
address,
|
|
142
|
-
allowance: ethers.formatUnits(
|
|
142
|
+
allowance: ethers.formatUnits(allowanceBigInt, decimals),
|
|
143
143
|
allowanceRaw: allowanceBigInt.toString(),
|
|
144
144
|
isApproved: allowanceBigInt > 0n,
|
|
145
145
|
};
|
|
146
|
-
})
|
|
146
|
+
});
|
|
147
147
|
return results;
|
|
148
148
|
}
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
* 只生成签名,由调用方决定如何提交
|
|
8
8
|
*/
|
|
9
9
|
import { ethers, Contract } from 'ethers';
|
|
10
|
+
import { batchGetBalances } from '../../../bundle/internal.js';
|
|
10
11
|
import { getCachedProvider, createWallet, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync, calculateProfitAmountByUserType, getProfitRecipient, } from './utils.js';
|
|
11
|
-
import { UNIFIED_DELEGATE_ADDRESS, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, FLAP_PORTAL_ADDRESS, WOKB_ADDRESS, V3_FEE_TIERS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI,
|
|
12
|
+
import { UNIFIED_DELEGATE_ADDRESS, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, FLAP_PORTAL_ADDRESS, WOKB_ADDRESS, V3_FEE_TIERS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, XLAYER_CHAIN_ID, XLAYER_RPC_URL, } from './constants.js';
|
|
12
13
|
import { quoteV2, quoteV3 } from '../../../core/quote/quote-helpers.js';
|
|
13
14
|
// ========================================
|
|
14
15
|
// 路由地址获取
|
|
@@ -105,9 +106,7 @@ async function getTokenBalances(wallets, tokenAddress) {
|
|
|
105
106
|
const provider = wallets[0].provider;
|
|
106
107
|
if (!provider)
|
|
107
108
|
throw new Error('Wallet has no provider');
|
|
108
|
-
|
|
109
|
-
const balances = await Promise.all(wallets.map((w) => tokenContract.balanceOf(w.address)));
|
|
110
|
-
return balances.map((b) => BigInt(b.toString()));
|
|
109
|
+
return batchGetBalances(provider, wallets.map((w) => w.address), tokenAddress, XLAYER_CHAIN_ID);
|
|
111
110
|
}
|
|
112
111
|
// ========================================
|
|
113
112
|
// FLAP 内盘卖出
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* 只生成签名,由调用方决定如何提交
|
|
12
12
|
*/
|
|
13
13
|
import { ethers } from 'ethers';
|
|
14
|
+
import { batchGetBalances } from '../../../bundle/internal.js';
|
|
15
|
+
import { XLAYER_CHAIN_ID } from './constants.js';
|
|
14
16
|
import { getCachedProvider, createWallet, signAuthorization, batchGetNonces, buildEIP7702TransactionSync, calculateProfitAmountByUserType, getProfitRecipient, generateRandomWallets, } from './utils.js';
|
|
15
17
|
import { UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, V3_FEE_TIERS, } from './constants.js';
|
|
16
18
|
import { getDefaultRouter, truncateDecimals, quoteSellOutput, buildBuyCallInternal, buildSellCallWithAmountInternal, } from './bundle-swap-helpers.js';
|
|
@@ -46,10 +48,10 @@ export async function bundleBatchSwap(params) {
|
|
|
46
48
|
// 并行获取数据
|
|
47
49
|
// ========================================
|
|
48
50
|
const addressesToQuery = [sellerWallet.address, ...buyerWallets.map((w) => w.address)];
|
|
49
|
-
const [nonces, feeData,
|
|
51
|
+
const [nonces, feeData, buyerBalances] = await Promise.all([
|
|
50
52
|
batchGetNonces(addressesToQuery, provider),
|
|
51
53
|
provider.getFeeData(),
|
|
52
|
-
|
|
54
|
+
batchGetBalances(provider, buyerWallets.map((w) => w.address), undefined, XLAYER_CHAIN_ID),
|
|
53
55
|
]);
|
|
54
56
|
const sellerNonce = nonces[0];
|
|
55
57
|
const buyerNonces = nonces.slice(1);
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
import { ethers } from 'ethers';
|
|
10
10
|
import { getCachedProvider, createWallet, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync, getProfitRecipient, } from './utils.js';
|
|
11
11
|
import { calculateBundleSwapRoundTripProfit } from '../../../core/constants/index.js';
|
|
12
|
-
import {
|
|
12
|
+
import { batchGetBalances } from '../../../bundle/internal.js';
|
|
13
|
+
import { UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, V3_FEE_TIERS, XLAYER_CHAIN_ID, } from './constants.js';
|
|
13
14
|
import { getDefaultRouter, truncateDecimals, buildBuyCallInternal, buildSellCallWithAmountInternal, } from './bundle-swap-helpers.js';
|
|
14
15
|
/**
|
|
15
16
|
* 多对多换手 - 多个卖家 + 多个买家
|
|
@@ -83,11 +84,10 @@ export async function bundleMultiSwap(params) {
|
|
|
83
84
|
// ========================================
|
|
84
85
|
const allAddresses = allWallets.map((w) => w.address);
|
|
85
86
|
// 获取卖出钱包的代币余额
|
|
86
|
-
const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
|
|
87
87
|
const [nonces, feeData, sellerBalances] = await Promise.all([
|
|
88
88
|
batchGetNonces(allAddresses, provider),
|
|
89
89
|
provider.getFeeData(),
|
|
90
|
-
|
|
90
|
+
batchGetBalances(provider, sellerWallets.map((w) => w.address), tokenAddress, XLAYER_CHAIN_ID),
|
|
91
91
|
]);
|
|
92
92
|
// 计算每个卖家的卖出金额(按百分比)
|
|
93
93
|
const percentBigInt = BigInt(Math.min(100, Math.max(1, sellPercent)));
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* multi-hop-transfer - 归集 (Sweep) 多对一
|
|
3
3
|
*/
|
|
4
|
-
import { ethers
|
|
4
|
+
import { ethers } from 'ethers';
|
|
5
5
|
import { createWallet, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync } from './utils.js';
|
|
6
|
-
import {
|
|
6
|
+
import { XLAYER_CHAIN_ID } from './constants.js';
|
|
7
|
+
import { batchGetBalances } from '../../../bundle/internal.js';
|
|
7
8
|
import { calculateTransferFee } from '../../../core/constants/index.js';
|
|
8
9
|
import { createEip7702DelegateContext, generateHopWalletMatrix, isNativeTransferToken, resolveListPayerWallet, } from './transfer-context-helpers.js';
|
|
9
10
|
export async function sweep(params) {
|
|
@@ -22,14 +23,7 @@ export async function sweep(params) {
|
|
|
22
23
|
const [allNoncesResult, feeData, balancesRaw] = await Promise.all([
|
|
23
24
|
batchGetNonces(addressesToGetNonces, provider),
|
|
24
25
|
provider.getFeeData(),
|
|
25
|
-
isNative
|
|
26
|
-
? Promise.all(sourceWallets.map((w) => provider.getBalance(w.address)))
|
|
27
|
-
: Promise.all(sourceWallets.map((w) => {
|
|
28
|
-
if (!tokenAddress)
|
|
29
|
-
throw new Error('tokenAddress is required for ERC20 sweep');
|
|
30
|
-
const tokenContract = new Contract(tokenAddress, ERC20_ABI, provider);
|
|
31
|
-
return tokenContract.balanceOf(w.address);
|
|
32
|
-
})),
|
|
26
|
+
batchGetBalances(provider, sourceAddresses, isNative ? undefined : tokenAddress, XLAYER_CHAIN_ID),
|
|
33
27
|
]);
|
|
34
28
|
const payerNonce = payerWallet ? allNoncesResult[0] : allNoncesResult[0];
|
|
35
29
|
const nonces = payerWallet ? allNoncesResult.slice(1) : allNoncesResult;
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
import { ethers } from 'ethers';
|
|
8
8
|
import { truncateDecimals } from './volume-helpers.js';
|
|
9
9
|
import { getCachedProvider, createWallet, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync, getProfitRecipient, } from './utils.js';
|
|
10
|
-
import { calculateBundleSwapRoundTripProfit } from '../../../core/constants/index.js';
|
|
11
|
-
import {
|
|
10
|
+
import { calculateBundleSwapRoundTripProfit, getMulticall3Address } from '../../../core/constants/index.js';
|
|
11
|
+
import { MULTICALL3_ABI } from '../../../abis/common.js';
|
|
12
|
+
import { batchGetBalances } from '../../../bundle/internal.js';
|
|
13
|
+
import { UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, FLAP_PORTAL_ADDRESS, POTATOSWAP_V2_ROUTER, WOKB_ADDRESS, V3_FEE_TIERS, V2_ROUTER_ABI, XLAYER_CHAIN_ID, } from './constants.js';
|
|
12
14
|
import { getDefaultRouter, buildBuyCall, buildSellCall, buildSellCallWithAmount } from './volume-internal.js';
|
|
13
15
|
/**
|
|
14
16
|
* 捆绑刷量 - 多钱包批量买入 + 批量卖出
|
|
@@ -76,12 +78,11 @@ export async function bundleVolume(params) {
|
|
|
76
78
|
const allAddresses = allWallets.map((w) => w.address);
|
|
77
79
|
// 获取卖出钱包的代币余额(如果需要)
|
|
78
80
|
const needBalanceQuery = !sellAmounts || sellAmounts.length !== sellerWallets.length;
|
|
79
|
-
const tokenContract = needBalanceQuery && sellerWallets.length > 0 ? new ethers.Contract(tokenAddress, ERC20_ABI, provider) : null;
|
|
80
81
|
const [nonces, feeData, sellerBalancesRaw] = await Promise.all([
|
|
81
82
|
batchGetNonces(allAddresses, provider),
|
|
82
83
|
provider.getFeeData(),
|
|
83
|
-
needBalanceQuery && sellerWallets.length > 0
|
|
84
|
-
?
|
|
84
|
+
needBalanceQuery && sellerWallets.length > 0
|
|
85
|
+
? batchGetBalances(provider, sellerWallets.map((w) => w.address), tokenAddress, XLAYER_CHAIN_ID)
|
|
85
86
|
: Promise.resolve(null),
|
|
86
87
|
]);
|
|
87
88
|
// 计算卖出金额
|
|
@@ -96,32 +97,52 @@ export async function bundleVolume(params) {
|
|
|
96
97
|
try {
|
|
97
98
|
if (poolType === 'FLAP') {
|
|
98
99
|
// FLAP 内盘:使用 previewBuy,降级到 quoteExactInput
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
const portalIface = new ethers.Interface(FLAP_PORTAL_ABI);
|
|
101
|
+
const multicall = new ethers.Contract(getMulticall3Address(XLAYER_CHAIN_ID), MULTICALL3_ABI, provider);
|
|
102
|
+
const aggregate3 = multicall.aggregate3;
|
|
103
|
+
if (aggregate3) {
|
|
104
|
+
try {
|
|
105
|
+
const quoteRows = (await aggregate3.staticCall([
|
|
106
|
+
{
|
|
107
|
+
target: FLAP_PORTAL_ADDRESS,
|
|
108
|
+
allowFailure: true,
|
|
109
|
+
callData: portalIface.encodeFunctionData('previewBuy', [tokenAddress, totalBuyAmount]),
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
target: FLAP_PORTAL_ADDRESS,
|
|
113
|
+
allowFailure: true,
|
|
114
|
+
callData: portalIface.encodeFunctionData('quoteBuy', [tokenAddress, totalBuyAmount]),
|
|
115
|
+
},
|
|
116
|
+
]));
|
|
117
|
+
if (quoteRows[0]?.success && quoteRows[0].returnData !== '0x') {
|
|
118
|
+
estimatedTokenAmount = portalIface.decodeFunctionResult('previewBuy', quoteRows[0].returnData)[0];
|
|
119
|
+
}
|
|
120
|
+
else if (quoteRows[1]?.success && quoteRows[1].returnData !== '0x') {
|
|
121
|
+
estimatedTokenAmount = portalIface.decodeFunctionResult('quoteBuy', quoteRows[1].returnData)[0];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
estimatedTokenAmount = 0n;
|
|
126
|
+
}
|
|
102
127
|
}
|
|
103
|
-
|
|
128
|
+
if (estimatedTokenAmount <= 0n) {
|
|
104
129
|
try {
|
|
105
|
-
|
|
130
|
+
const portalContract = new ethers.Contract(FLAP_PORTAL_ADDRESS, FLAP_PORTAL_ABI, provider);
|
|
131
|
+
estimatedTokenAmount = await portalContract.quoteExactInput.staticCall({
|
|
132
|
+
inputToken: ethers.ZeroAddress,
|
|
133
|
+
outputToken: tokenAddress,
|
|
134
|
+
inputAmount: totalBuyAmount,
|
|
135
|
+
});
|
|
106
136
|
}
|
|
107
137
|
catch {
|
|
108
138
|
try {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
});
|
|
139
|
+
const v2Router = new ethers.Contract(POTATOSWAP_V2_ROUTER, V2_ROUTER_ABI, provider);
|
|
140
|
+
const path = [WOKB_ADDRESS, tokenAddress];
|
|
141
|
+
const amounts = await v2Router.getAmountsOut(totalBuyAmount, path);
|
|
142
|
+
estimatedTokenAmount = amounts[amounts.length - 1];
|
|
114
143
|
}
|
|
115
144
|
catch {
|
|
116
|
-
|
|
117
|
-
const v2Router = new ethers.Contract(POTATOSWAP_V2_ROUTER, V2_ROUTER_ABI, provider);
|
|
118
|
-
const path = [WOKB_ADDRESS, tokenAddress];
|
|
119
|
-
const amounts = await v2Router.getAmountsOut(totalBuyAmount, path);
|
|
120
|
-
estimatedTokenAmount = amounts[amounts.length - 1];
|
|
121
|
-
}
|
|
122
|
-
catch {
|
|
123
|
-
// all quote paths failed
|
|
124
|
-
}
|
|
145
|
+
// all quote paths failed
|
|
125
146
|
}
|
|
126
147
|
}
|
|
127
148
|
}
|
|
@@ -18,26 +18,16 @@
|
|
|
18
18
|
* 利润刮取全部复用 DEX 层 direct-router 的实现,属于真实业务依赖(非纯函数/常量),
|
|
19
19
|
* 不宜抽到 core 或 chains 内部,否则会复制一份 DEX 路由逻辑导致双实现漂移。
|
|
20
20
|
*/
|
|
21
|
-
import { ethers, Wallet
|
|
21
|
+
import { ethers, Wallet } from 'ethers';
|
|
22
22
|
import { createXLayerProvider } from '../../../utils/provider-factory.js';
|
|
23
|
+
import { batchGetBalances } from '../../../bundle/internal.js';
|
|
24
|
+
import { XLAYER_CHAIN_ID } from './constants.js';
|
|
23
25
|
import { getTokenDecimals as fetchTokenDecimals } from '../../../utils/swap-helpers.js';
|
|
24
26
|
import { directV2BatchBuy, directV2BatchSell, directV3BatchBuy, directV3BatchSell, } from '../../../domains/dex/direct-router.js';
|
|
25
27
|
import { getProfitRecipient } from '../../../core/constants/index.js';
|
|
26
28
|
// ============================================================================
|
|
27
29
|
// 辅助函数
|
|
28
30
|
// ============================================================================
|
|
29
|
-
/**
|
|
30
|
-
* 获取代币余额
|
|
31
|
-
*/
|
|
32
|
-
async function getTokenBalance(provider, tokenAddress, walletAddress) {
|
|
33
|
-
try {
|
|
34
|
-
const contract = new Contract(tokenAddress, ['function balanceOf(address) view returns (uint256)'], provider);
|
|
35
|
-
return await contract.balanceOf(walletAddress);
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
return 0n;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
31
|
// ============================================================================
|
|
42
32
|
// 核心实现
|
|
43
33
|
// ============================================================================
|
|
@@ -108,7 +98,7 @@ export async function signEoaWashSellTransactions(params, wallets, tokenDecimals
|
|
|
108
98
|
// 获取代币精度
|
|
109
99
|
const decimals = tokenDecimals ?? (await fetchTokenDecimals(provider, tokenAddress, { fallback: 18 }));
|
|
110
100
|
// 查询所有钱包的代币余额
|
|
111
|
-
const balancesWei = await
|
|
101
|
+
const balancesWei = await batchGetBalances(provider, wallets.map((w) => w.address), tokenAddress, XLAYER_CHAIN_ID);
|
|
112
102
|
// 转换为格式化金额
|
|
113
103
|
const sellAmounts = balancesWei.map((balance) => {
|
|
114
104
|
if (balance <= 0n)
|
|
@@ -11,6 +11,15 @@ import { QUOTE_CONFIG, QUOTE_V3_FEE_TIERS } from '../constants/quote.js';
|
|
|
11
11
|
import type { QuoteParams, QuoteResult } from './types.js';
|
|
12
12
|
export { QUOTE_CONFIG, QUOTE_V3_FEE_TIERS };
|
|
13
13
|
export declare function buildV2QuotePaths(tokenIn: string, tokenOut: string, hops: readonly string[]): string[][];
|
|
14
|
+
export declare function selectBestV2Quote(paths: readonly string[][], rows: readonly {
|
|
15
|
+
success: boolean;
|
|
16
|
+
amountOut: bigint;
|
|
17
|
+
}[]): {
|
|
18
|
+
amountOut: bigint;
|
|
19
|
+
path: string[];
|
|
20
|
+
} | undefined;
|
|
21
|
+
/** 同一条 V2 path、多笔金额,一次 multicall。0 金额直接回 0,不打节点。 */
|
|
22
|
+
export declare function quoteV2SamePathAmounts(provider: JsonRpcProvider, chain: string, routerAddress: string, path: string[], amountsIn: readonly bigint[]): Promise<bigint[]>;
|
|
14
23
|
/**
|
|
15
24
|
* V2 报价(直接路径 + 多跳路径)
|
|
16
25
|
* ✅ 优化:并行尝试直接路径和多跳路径
|
|
@@ -23,6 +32,8 @@ export declare function buildV2QuotePaths(tokenIn: string, tokenOut: string, hop
|
|
|
23
32
|
* @returns 报价结果
|
|
24
33
|
*/
|
|
25
34
|
export declare function quoteV2(provider: JsonRpcProvider, tokenIn: string, tokenOut: string, amountIn: bigint, chain: string, routerOverride?: string): Promise<QuoteResult>;
|
|
35
|
+
/** 多笔金额走同一套 V2 路径搜索,稳定币路径一次 multicall,不够再补一跳。 */
|
|
36
|
+
export declare function quoteV2ManyAmounts(provider: JsonRpcProvider, tokenIn: string, tokenOut: string, amountsIn: readonly bigint[], chain: string, routerOverride?: string): Promise<bigint[] | null>;
|
|
26
37
|
export declare function resolveQuotedV2Path(provider: JsonRpcProvider, tokenIn: string, tokenOut: string, amountIn: bigint, chain: string, routerOverride?: string): Promise<string[]>;
|
|
27
38
|
/**
|
|
28
39
|
* V3 报价(多费率尝试 + 多跳路径)
|