four-flap-meme-sdk 4.1.0 → 4.2.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/core/sell-flow.js +6 -6
- package/dist/bundle/four-meme/pancake-proxy/helpers.d.ts +5 -38
- package/dist/bundle/four-meme/pancake-proxy/helpers.js +5 -228
- package/dist/bundle/four-meme/private/sell.js +3 -3
- package/dist/bundle/four-meme/swap-hop-helpers.d.ts +1 -5
- package/dist/bundle/four-meme/swap-hop-helpers.js +1 -63
- package/dist/bundle/four-meme/types/bundle-ops.d.ts +166 -0
- package/dist/bundle/four-meme/types/bundle-ops.js +4 -0
- package/dist/bundle/four-meme/types/buy-first.d.ts +1 -1
- package/dist/bundle/four-meme/types/config.d.ts +95 -0
- package/dist/bundle/four-meme/types/config.js +1 -0
- package/dist/bundle/four-meme/types/core-flow.d.ts +2 -1
- package/dist/bundle/four-meme/types/index.d.ts +5 -611
- package/dist/bundle/four-meme/types/pancake-proxy-types.d.ts +118 -0
- package/dist/bundle/four-meme/types/pancake-proxy-types.js +3 -0
- package/dist/bundle/four-meme/types/swap-types.d.ts +103 -0
- package/dist/bundle/four-meme/types/swap-types.js +1 -0
- package/dist/bundle/four-meme/types/transfer-ops.d.ts +134 -0
- package/dist/bundle/four-meme/types/transfer-ops.js +1 -0
- package/dist/chains/bsc/pancake/bundle-buy-first-multi-wallet.js +2 -2
- package/dist/chains/bsc/pancake/bundle-buy-first.js +2 -2
- package/dist/chains/bsc/pancake/hop-chain.d.ts +1 -18
- package/dist/chains/bsc/pancake/hop-chain.js +1 -229
- package/dist/chains/bsc/pancake/quote.d.ts +10 -2
- package/dist/chains/bsc/pancake/quote.js +14 -6
- package/dist/chains/bsc/pancake/quote.test.js +13 -1
- package/dist/chains/index.d.ts +3 -0
- package/dist/chains/index.js +4 -0
- package/dist/chains/robinhood/constants.d.ts +48 -0
- package/dist/chains/robinhood/constants.js +55 -0
- package/dist/chains/robinhood/index.d.ts +11 -0
- package/dist/chains/robinhood/index.js +9 -0
- package/dist/chains/robinhood/longetf/assets.d.ts +11 -0
- package/dist/chains/robinhood/longetf/assets.js +4 -0
- package/dist/chains/robinhood/longetf/encode.d.ts +41 -0
- package/dist/chains/robinhood/longetf/encode.js +116 -0
- package/dist/chains/robinhood/longetf/index.d.ts +3 -0
- package/dist/chains/robinhood/longetf/index.js +3 -0
- package/dist/chains/robinhood/longetf/launch.d.ts +49 -0
- package/dist/chains/robinhood/longetf/launch.js +64 -0
- package/dist/chains/robinhood/pons-v2/encode.d.ts +62 -0
- package/dist/chains/robinhood/pons-v2/encode.js +115 -0
- package/dist/chains/robinhood/pons-v2/index.d.ts +2 -0
- package/dist/chains/robinhood/pons-v2/index.js +2 -0
- package/dist/chains/robinhood/pons-v2/launch.d.ts +48 -0
- package/dist/chains/robinhood/pons-v2/launch.js +107 -0
- package/dist/chains/robinhood/robinhood.test.d.ts +1 -0
- package/dist/chains/robinhood/robinhood.test.js +103 -0
- package/dist/chains/robinhood/sign.d.ts +20 -0
- package/dist/chains/robinhood/sign.js +31 -0
- package/dist/chains/xlayer/eip7702/types-base.d.ts +44 -0
- package/dist/chains/xlayer/eip7702/types-base.js +4 -0
- package/dist/chains/xlayer/eip7702/types-bundle.d.ts +191 -0
- package/dist/chains/xlayer/eip7702/types-bundle.js +1 -0
- package/dist/chains/xlayer/eip7702/types-create.d.ts +222 -0
- package/dist/chains/xlayer/eip7702/types-create.js +1 -0
- package/dist/chains/xlayer/eip7702/types-transfer.d.ts +124 -0
- package/dist/chains/xlayer/eip7702/types-transfer.js +1 -0
- package/dist/chains/xlayer/eip7702/types.d.ts +4 -578
- package/dist/chains/xlayer/eip7702/types.js +0 -3
- package/dist/chains/xlayer/eoa/eoa-dex-types.d.ts +196 -0
- package/dist/chains/xlayer/eoa/eoa-dex-types.js +1 -0
- package/dist/chains/xlayer/eoa/eoa-swap-types.d.ts +283 -0
- package/dist/chains/xlayer/eoa/eoa-swap-types.js +1 -0
- package/dist/chains/xlayer/eoa/eoa-trade-types.d.ts +300 -0
- package/dist/chains/xlayer/eoa/eoa-trade-types.js +6 -0
- package/dist/chains/xlayer/eoa/eoa-types.d.ts +4 -774
- package/dist/chains/xlayer/eoa/eoa-types.js +0 -5
- package/dist/chains/xlayer/eoa/eoa-wash-volume.js +2 -13
- package/dist/chains/xlayer/eoa/hop-chain.d.ts +1 -1
- package/dist/chains/xlayer/eoa/hop-chain.js +4 -83
- package/dist/core/constants/addresses.d.ts +18 -0
- package/dist/core/constants/addresses.js +23 -0
- package/dist/core/constants/chains.d.ts +16 -0
- package/dist/core/constants/chains.js +20 -0
- package/dist/core/constants/gas.d.ts +8 -0
- package/dist/core/constants/gas.js +9 -0
- package/dist/core/constants/index.d.ts +2 -1
- package/dist/core/constants/index.js +2 -1
- package/dist/core/constants/slippage.d.ts +6 -0
- package/dist/core/constants/slippage.js +15 -0
- package/dist/domains/flap/portal-bundle-merkle/core-helpers.js +2 -2
- package/dist/domains/flap/portal-bundle-merkle/pancake-proxy/helpers.d.ts +2 -43
- package/dist/domains/flap/portal-bundle-merkle/pancake-proxy/helpers.js +8 -208
- package/dist/domains/flap/portal-bundle-merkle/private/helpers.js +2 -2
- package/dist/domains/flap/portal-bundle-merkle/private/sell.js +2 -2
- package/dist/domains/flap/portal-bundle-merkle/swap-helpers.d.ts +4 -24
- package/dist/domains/flap/portal-bundle-merkle/swap-helpers.js +12 -166
- package/dist/domains/flap/portal-bundle-merkle/types-bundle.d.ts +137 -0
- package/dist/domains/flap/portal-bundle-merkle/types-bundle.js +7 -0
- package/dist/domains/flap/portal-bundle-merkle/types-config.d.ts +118 -0
- package/dist/domains/flap/portal-bundle-merkle/types-config.js +4 -0
- package/dist/domains/flap/portal-bundle-merkle/types-pancake.d.ts +118 -0
- package/dist/domains/flap/portal-bundle-merkle/types-pancake.js +4 -0
- package/dist/domains/flap/portal-bundle-merkle/types-transfer.d.ts +70 -0
- package/dist/domains/flap/portal-bundle-merkle/types-transfer.js +1 -0
- package/dist/domains/flap/portal-bundle-merkle/types.d.ts +4 -441
- package/dist/domains/holders/internal/disperse.js +2 -18
- package/dist/domains/lp-inspect/inspect-helpers.d.ts +1 -0
- package/dist/domains/lp-inspect/inspect-helpers.js +3 -16
- package/dist/exports/root-foundations.d.ts +1 -1
- package/dist/exports/root-foundations.js +1 -1
- package/dist/exports/root-robinhood.d.ts +9 -0
- package/dist/exports/root-robinhood.js +7 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/utils/constants.d.ts +1 -1
- package/dist/utils/constants.js +1 -1
- package/dist/utils/hop-chains.d.ts +0 -3
- package/dist/utils/hop-chains.js +4 -5
- package/dist/utils/pancake-router-tx.d.ts +41 -0
- package/dist/utils/pancake-router-tx.js +174 -0
- package/dist/utils/swap-helpers.d.ts +6 -2
- package/dist/utils/swap-helpers.js +24 -4
- package/dist/utils/wallet.js +12 -12
- package/package.json +1 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PancakeSwap Router 交易构建 / 报价 / 授权目标
|
|
3
|
+
*
|
|
4
|
+
* flap 与 four-meme pancake-proxy helpers 共用,避免两份实现分叉。
|
|
5
|
+
* 不负责 minOut / 滑点(同钱包刷量仍走 quote.ts 的 0n)。
|
|
6
|
+
*/
|
|
7
|
+
import { Wallet, Contract, type JsonRpcProvider } from 'ethers';
|
|
8
|
+
import type { ContractTransaction } from 'ethers/contract';
|
|
9
|
+
export declare const MULTICALL3_ADDRESS: "0xcA11bde05977b3631167028862bE2a173976CA11";
|
|
10
|
+
export declare const WBNB_ADDRESS: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c";
|
|
11
|
+
export declare const PANCAKE_V2_ROUTER_ADDRESS: "0x10ED43C718714eb63d5aA57B78B54704E256024E";
|
|
12
|
+
export declare const PANCAKE_V3_ROUTER_ADDRESS: "0x13f4EA83D0bd40E75C8222255bc855a974568Dd4";
|
|
13
|
+
export declare const PANCAKE_V3_QUOTER_ADDRESS: "0xB048Bbc1Ee6b733FFfCFb9e9CeF7375518e25997";
|
|
14
|
+
export declare const V3_ROUTER_ABI: (string | {
|
|
15
|
+
inputs: {
|
|
16
|
+
components: {
|
|
17
|
+
name: string;
|
|
18
|
+
type: string;
|
|
19
|
+
}[];
|
|
20
|
+
name: string;
|
|
21
|
+
type: string;
|
|
22
|
+
}[];
|
|
23
|
+
name: string;
|
|
24
|
+
outputs: {
|
|
25
|
+
name: string;
|
|
26
|
+
type: string;
|
|
27
|
+
}[];
|
|
28
|
+
stateMutability: string;
|
|
29
|
+
type: string;
|
|
30
|
+
})[];
|
|
31
|
+
export type PancakeRouteNeedBnb = {
|
|
32
|
+
v2Path?: string[];
|
|
33
|
+
v3TokenIn?: string;
|
|
34
|
+
v3ExactTokenIn?: string;
|
|
35
|
+
};
|
|
36
|
+
export declare function needSendBNBFromRoute(routeType: string, params: PancakeRouteNeedBnb, wbnbAddress?: string): boolean;
|
|
37
|
+
export declare function getApprovalTarget(routeType: string): string;
|
|
38
|
+
export declare function buildV2Transactions(routers: Contract[], wallets: Wallet[], amountsWei: bigint[], minOuts: bigint[], path: string[], isBuy: boolean, needBNB: boolean): Promise<ContractTransaction[]>;
|
|
39
|
+
export declare function buildV3SingleTransactions(routers: Contract[], wallets: Wallet[], tokenIn: string, tokenOut: string, fee: number, amountsWei: bigint[], minOuts: bigint[], isBuy: boolean, needBNB: boolean): Promise<ContractTransaction[]>;
|
|
40
|
+
export declare function buildV3MultiHopTransactions(routers: Contract[], wallets: Wallet[], tokens: string[], fees: number[], amountsWei: bigint[], minOuts: bigint[], isBuy: boolean, needBNB: boolean): Promise<ContractTransaction[]>;
|
|
41
|
+
export declare function batchGetV2Quotes(provider: JsonRpcProvider, amountsWei: bigint[], v2Path: string[]): Promise<bigint[]>;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PancakeSwap Router 交易构建 / 报价 / 授权目标
|
|
3
|
+
*
|
|
4
|
+
* flap 与 four-meme pancake-proxy helpers 共用,避免两份实现分叉。
|
|
5
|
+
* 不负责 minOut / 滑点(同钱包刷量仍走 quote.ts 的 0n)。
|
|
6
|
+
*/
|
|
7
|
+
import { Contract, Interface } from 'ethers';
|
|
8
|
+
import { getDeadline, encodeV3Path } from './bundle-helpers.js';
|
|
9
|
+
import { ADDRESSES } from '../core/constants/index.js';
|
|
10
|
+
import { V2_ROUTER_ABI, MULTICALL3_ABI, V3_ROUTER02_ABI } from '../abis/common.js';
|
|
11
|
+
export const MULTICALL3_ADDRESS = ADDRESSES.BSC.Multicall3;
|
|
12
|
+
export const WBNB_ADDRESS = ADDRESSES.BSC.WBNB;
|
|
13
|
+
export const PANCAKE_V2_ROUTER_ADDRESS = ADDRESSES.BSC.PancakeV2Router;
|
|
14
|
+
export const PANCAKE_V3_ROUTER_ADDRESS = ADDRESSES.BSC.PancakeV3Router;
|
|
15
|
+
export const PANCAKE_V3_QUOTER_ADDRESS = ADDRESSES.BSC.PancakeV3Quoter;
|
|
16
|
+
export const V3_ROUTER_ABI = V3_ROUTER02_ABI;
|
|
17
|
+
export function needSendBNBFromRoute(routeType, params, wbnbAddress = WBNB_ADDRESS) {
|
|
18
|
+
const wbnb = wbnbAddress.toLowerCase();
|
|
19
|
+
if (routeType === 'v2' && params.v2Path && params.v2Path[0].toLowerCase() === wbnb) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
if (routeType === 'v3-single' && params.v3TokenIn && params.v3TokenIn.toLowerCase() === wbnb) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
if (routeType === 'v3-multi' &&
|
|
26
|
+
params.v3ExactTokenIn &&
|
|
27
|
+
params.v3ExactTokenIn.toLowerCase() === wbnb) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
export function getApprovalTarget(routeType) {
|
|
33
|
+
if (routeType === 'v2') {
|
|
34
|
+
return PANCAKE_V2_ROUTER_ADDRESS;
|
|
35
|
+
}
|
|
36
|
+
return PANCAKE_V3_ROUTER_ADDRESS;
|
|
37
|
+
}
|
|
38
|
+
export async function buildV2Transactions(routers, wallets, amountsWei, minOuts, path, isBuy, needBNB) {
|
|
39
|
+
const deadline = getDeadline();
|
|
40
|
+
return Promise.all(routers.map(async (router, i) => {
|
|
41
|
+
if (isBuy && needBNB) {
|
|
42
|
+
return router.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(minOuts[i], path, wallets[i].address, deadline, { value: amountsWei[i] });
|
|
43
|
+
}
|
|
44
|
+
return router.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(amountsWei[i], minOuts[i], path, wallets[i].address, deadline);
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
export async function buildV3SingleTransactions(routers, wallets, tokenIn, tokenOut, fee, amountsWei, minOuts, isBuy, needBNB) {
|
|
48
|
+
const deadline = getDeadline();
|
|
49
|
+
const v3RouterIface = new Interface(V3_ROUTER_ABI);
|
|
50
|
+
return Promise.all(routers.map(async (router, i) => {
|
|
51
|
+
const isTokenOutWBNB = tokenOut.toLowerCase() === WBNB_ADDRESS.toLowerCase();
|
|
52
|
+
const exactInputSingleData = v3RouterIface.encodeFunctionData('exactInputSingle', [
|
|
53
|
+
{
|
|
54
|
+
tokenIn: tokenIn,
|
|
55
|
+
tokenOut: tokenOut,
|
|
56
|
+
fee: fee,
|
|
57
|
+
recipient: isTokenOutWBNB ? PANCAKE_V3_ROUTER_ADDRESS : wallets[i].address,
|
|
58
|
+
amountIn: amountsWei[i],
|
|
59
|
+
amountOutMinimum: minOuts[i],
|
|
60
|
+
sqrtPriceLimitX96: 0n,
|
|
61
|
+
},
|
|
62
|
+
]);
|
|
63
|
+
if (isBuy && needBNB) {
|
|
64
|
+
return router.multicall.populateTransaction(deadline, [exactInputSingleData], { value: amountsWei[i] });
|
|
65
|
+
}
|
|
66
|
+
if (!isBuy && isTokenOutWBNB) {
|
|
67
|
+
const unwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [minOuts[i], wallets[i].address]);
|
|
68
|
+
return router.multicall.populateTransaction(deadline, [exactInputSingleData, unwrapData]);
|
|
69
|
+
}
|
|
70
|
+
return router.multicall.populateTransaction(deadline, [exactInputSingleData]);
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
export async function buildV3MultiHopTransactions(routers, wallets, tokens, fees, amountsWei, minOuts, isBuy, needBNB) {
|
|
74
|
+
if (!tokens || tokens.length < 2) {
|
|
75
|
+
throw new Error('V3 多跳需要至少 2 个代币(v3Tokens)');
|
|
76
|
+
}
|
|
77
|
+
if (!fees || fees.length !== tokens.length - 1) {
|
|
78
|
+
throw new Error(`V3 多跳费率数量 (${fees?.length || 0}) 必须等于代币数量 - 1 (${tokens.length - 1})(v3Fees)`);
|
|
79
|
+
}
|
|
80
|
+
const deadline = getDeadline();
|
|
81
|
+
const v3RouterIface = new Interface(V3_ROUTER_ABI);
|
|
82
|
+
const forwardPath = encodeV3Path(tokens, fees);
|
|
83
|
+
const reversedTokens = [...tokens].reverse();
|
|
84
|
+
const reversedFees = [...fees].reverse();
|
|
85
|
+
const reversePath = encodeV3Path(reversedTokens, reversedFees);
|
|
86
|
+
const tokenOut = tokens[tokens.length - 1];
|
|
87
|
+
const isTokenOutWBNB = tokenOut.toLowerCase() === WBNB_ADDRESS.toLowerCase();
|
|
88
|
+
return Promise.all(routers.map(async (router, i) => {
|
|
89
|
+
if (isBuy && needBNB) {
|
|
90
|
+
const swapData = v3RouterIface.encodeFunctionData('exactInput', [
|
|
91
|
+
{
|
|
92
|
+
path: forwardPath,
|
|
93
|
+
recipient: wallets[i].address,
|
|
94
|
+
amountIn: amountsWei[i],
|
|
95
|
+
amountOutMinimum: minOuts[i],
|
|
96
|
+
},
|
|
97
|
+
]);
|
|
98
|
+
return router.multicall.populateTransaction(deadline, [swapData], { value: amountsWei[i] });
|
|
99
|
+
}
|
|
100
|
+
if (!isBuy) {
|
|
101
|
+
const swapData = v3RouterIface.encodeFunctionData('exactInput', [
|
|
102
|
+
{
|
|
103
|
+
path: reversePath,
|
|
104
|
+
recipient: isTokenOutWBNB ? PANCAKE_V3_ROUTER_ADDRESS : wallets[i].address,
|
|
105
|
+
amountIn: amountsWei[i],
|
|
106
|
+
amountOutMinimum: minOuts[i],
|
|
107
|
+
},
|
|
108
|
+
]);
|
|
109
|
+
if (isTokenOutWBNB) {
|
|
110
|
+
const unwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [minOuts[i], wallets[i].address]);
|
|
111
|
+
return router.multicall.populateTransaction(deadline, [swapData, unwrapData]);
|
|
112
|
+
}
|
|
113
|
+
return router.multicall.populateTransaction(deadline, [swapData]);
|
|
114
|
+
}
|
|
115
|
+
const swapData = v3RouterIface.encodeFunctionData('exactInput', [
|
|
116
|
+
{
|
|
117
|
+
path: forwardPath,
|
|
118
|
+
recipient: wallets[i].address,
|
|
119
|
+
amountIn: amountsWei[i],
|
|
120
|
+
amountOutMinimum: minOuts[i],
|
|
121
|
+
},
|
|
122
|
+
]);
|
|
123
|
+
return router.multicall.populateTransaction(deadline, [swapData]);
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
export async function batchGetV2Quotes(provider, amountsWei, v2Path) {
|
|
127
|
+
if (amountsWei.length === 0)
|
|
128
|
+
return [];
|
|
129
|
+
if (amountsWei.length === 1) {
|
|
130
|
+
try {
|
|
131
|
+
const v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, V2_ROUTER_ABI, provider);
|
|
132
|
+
const amounts = await v2Router.getAmountsOut(amountsWei[0], v2Path);
|
|
133
|
+
return [amounts[amounts.length - 1]];
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return [0n];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
const v2RouterIface = new Interface(V2_ROUTER_ABI);
|
|
141
|
+
const multicall = new Contract(MULTICALL3_ADDRESS, MULTICALL3_ABI, provider);
|
|
142
|
+
const calls = amountsWei.map((amount) => ({
|
|
143
|
+
target: PANCAKE_V2_ROUTER_ADDRESS,
|
|
144
|
+
allowFailure: true,
|
|
145
|
+
callData: v2RouterIface.encodeFunctionData('getAmountsOut', [amount, v2Path]),
|
|
146
|
+
}));
|
|
147
|
+
const results = await multicall.aggregate3.staticCall(calls);
|
|
148
|
+
return results.map((r) => {
|
|
149
|
+
if (r.success && r.returnData && r.returnData !== '0x') {
|
|
150
|
+
try {
|
|
151
|
+
const decoded = v2RouterIface.decodeFunctionResult('getAmountsOut', r.returnData);
|
|
152
|
+
const amounts = decoded[0];
|
|
153
|
+
return amounts[amounts.length - 1];
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
return 0n;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return 0n;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return await Promise.all(amountsWei.map(async (amount) => {
|
|
164
|
+
try {
|
|
165
|
+
const v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, V2_ROUTER_ABI, provider);
|
|
166
|
+
const amounts = await v2Router.getAmountsOut(amount, v2Path);
|
|
167
|
+
return amounts[amounts.length - 1];
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return 0n;
|
|
171
|
+
}
|
|
172
|
+
}));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -20,10 +20,14 @@ export declare function ensureTokenApproval(provider: JsonRpcProvider, merkle: M
|
|
|
20
20
|
* 获取代币余额
|
|
21
21
|
*/
|
|
22
22
|
export declare function getTokenBalance(provider: JsonRpcProvider, tokenAddress: string, walletAddress: string): Promise<bigint>;
|
|
23
|
+
export type GetTokenDecimalsOptions = {
|
|
24
|
+
fallback?: number;
|
|
25
|
+
cache?: boolean;
|
|
26
|
+
};
|
|
23
27
|
/**
|
|
24
|
-
*
|
|
28
|
+
* 获取代币精度。cache 按地址缓存;fallback 在 RPC 失败时使用(不抛)。
|
|
25
29
|
*/
|
|
26
|
-
export declare function getTokenDecimals(provider: JsonRpcProvider, tokenAddress: string): Promise<number>;
|
|
30
|
+
export declare function getTokenDecimals(provider: JsonRpcProvider, tokenAddress: string, options?: GetTokenDecimalsOptions): Promise<number>;
|
|
27
31
|
/**
|
|
28
32
|
* 计算卖出数量(支持百分比)
|
|
29
33
|
*/
|
|
@@ -90,12 +90,32 @@ export async function getTokenBalance(provider, tokenAddress, walletAddress) {
|
|
|
90
90
|
const tokenContract = new Contract(tokenAddress, ERC20_ABI, provider);
|
|
91
91
|
return await tokenContract.balanceOf(walletAddress);
|
|
92
92
|
}
|
|
93
|
+
const tokenDecimalsCache = new Map();
|
|
93
94
|
/**
|
|
94
|
-
*
|
|
95
|
+
* 获取代币精度。cache 按地址缓存;fallback 在 RPC 失败时使用(不抛)。
|
|
95
96
|
*/
|
|
96
|
-
export async function getTokenDecimals(provider, tokenAddress) {
|
|
97
|
-
const
|
|
98
|
-
|
|
97
|
+
export async function getTokenDecimals(provider, tokenAddress, options) {
|
|
98
|
+
const key = tokenAddress.toLowerCase();
|
|
99
|
+
if (options?.cache) {
|
|
100
|
+
const cached = tokenDecimalsCache.get(key);
|
|
101
|
+
if (cached !== undefined)
|
|
102
|
+
return cached;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const tokenContract = new Contract(tokenAddress, ERC20_ABI, provider);
|
|
106
|
+
const value = Number(await tokenContract.decimals());
|
|
107
|
+
if (options?.cache)
|
|
108
|
+
tokenDecimalsCache.set(key, value);
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (options?.fallback !== undefined) {
|
|
113
|
+
if (options.cache)
|
|
114
|
+
tokenDecimalsCache.set(key, options.fallback);
|
|
115
|
+
return options.fallback;
|
|
116
|
+
}
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
99
119
|
}
|
|
100
120
|
/**
|
|
101
121
|
* 计算卖出数量(支持百分比)
|
package/dist/utils/wallet.js
CHANGED
|
@@ -116,8 +116,8 @@ async function _queryMultiTokenBalancesSingle(provider, multicallAddress, tokenA
|
|
|
116
116
|
// ✅ 单次 multicall 获取所有数据
|
|
117
117
|
const callData = multiIface.encodeFunctionData('aggregate', [calls]);
|
|
118
118
|
const result = await provider.call({ to: multicallAddress, data: callData });
|
|
119
|
-
const
|
|
120
|
-
const returnData =
|
|
119
|
+
const aggregateDecoded = multiIface.decodeFunctionResult('aggregate', result);
|
|
120
|
+
const returnData = aggregateDecoded[1];
|
|
121
121
|
// ✅ 并行解码结果
|
|
122
122
|
const outputs = holders.map((addr) => ({
|
|
123
123
|
address: addr,
|
|
@@ -129,8 +129,8 @@ async function _queryMultiTokenBalancesSingle(provider, multicallAddress, tokenA
|
|
|
129
129
|
let idx = 0;
|
|
130
130
|
for (let i = 0; i < holders.length; i++, idx++) {
|
|
131
131
|
try {
|
|
132
|
-
const
|
|
133
|
-
outputs[i].native = formatEther(
|
|
132
|
+
const nativeDecoded = multiIface.decodeFunctionResult('getEthBalance', returnData[idx]);
|
|
133
|
+
outputs[i].native = formatEther(nativeDecoded[0]);
|
|
134
134
|
}
|
|
135
135
|
catch {
|
|
136
136
|
outputs[i].native = '0';
|
|
@@ -142,13 +142,13 @@ async function _queryMultiTokenBalancesSingle(provider, multicallAddress, tokenA
|
|
|
142
142
|
for (const token of tokenAddresses) {
|
|
143
143
|
for (let i = 0; i < holders.length; i++, idx++) {
|
|
144
144
|
try {
|
|
145
|
-
const
|
|
145
|
+
const tokenBalanceDecoded = erc20Iface.decodeFunctionResult('balanceOf', returnData[idx]);
|
|
146
146
|
let balanceMap = rawBalances.get(i);
|
|
147
147
|
if (!balanceMap) {
|
|
148
148
|
balanceMap = new Map();
|
|
149
149
|
rawBalances.set(i, balanceMap);
|
|
150
150
|
}
|
|
151
|
-
balanceMap.set(token,
|
|
151
|
+
balanceMap.set(token, tokenBalanceDecoded[0]);
|
|
152
152
|
}
|
|
153
153
|
catch {
|
|
154
154
|
let balanceMap = rawBalances.get(i);
|
|
@@ -165,8 +165,8 @@ async function _queryMultiTokenBalancesSingle(provider, multicallAddress, tokenA
|
|
|
165
165
|
const tokenDecimals = new Map();
|
|
166
166
|
for (const token of tokenAddresses) {
|
|
167
167
|
try {
|
|
168
|
-
const
|
|
169
|
-
tokenDecimals.set(token, Number(
|
|
168
|
+
const decimalsDecoded = erc20Iface.decodeFunctionResult('decimals', returnData[idx]);
|
|
169
|
+
tokenDecimals.set(token, Number(decimalsDecoded[0]));
|
|
170
170
|
}
|
|
171
171
|
catch {
|
|
172
172
|
tokenDecimals.set(token, 18);
|
|
@@ -216,12 +216,12 @@ async function _querySingleTokenBalancesSingle(provider, multicallAddress, token
|
|
|
216
216
|
}));
|
|
217
217
|
const callData = multiIface.encodeFunctionData('aggregate', [calls]);
|
|
218
218
|
const result = await provider.call({ to: multicallAddress, data: callData });
|
|
219
|
-
const
|
|
220
|
-
const returnData =
|
|
219
|
+
const aggregateDecoded = multiIface.decodeFunctionResult('aggregate', result);
|
|
220
|
+
const returnData = aggregateDecoded[1];
|
|
221
221
|
return holders.map((addr, i) => {
|
|
222
222
|
try {
|
|
223
|
-
const
|
|
224
|
-
return { address: addr, balance:
|
|
223
|
+
const tokenBalanceDecoded = erc20Iface.decodeFunctionResult('balanceOf', returnData[i]);
|
|
224
|
+
return { address: addr, balance: tokenBalanceDecoded[0], success: true };
|
|
225
225
|
}
|
|
226
226
|
catch {
|
|
227
227
|
return { address: addr, balance: 0n, success: false };
|