four-flap-meme-sdk 2.2.0 → 2.2.1

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.
@@ -22,14 +22,14 @@ export declare function getBundleOptions(config: BundleMerkleConfigInput, blockO
22
22
  maxRetries: number;
23
23
  };
24
24
  export declare function getGasPriceConfig(config: BundleGasConfigInput): GasPriceConfig;
25
- export declare function shouldExtractProfit(_config?: unknown): boolean;
26
- export declare function getProfitRateBps(_config?: unknown): number;
27
- export declare function getProfitRecipient(_config?: unknown): string;
28
- export declare function calculateProfit(amount: bigint, _config?: unknown): {
25
+ export declare function shouldExtractProfit(_config?: BundleMerkleConfigInput): boolean;
26
+ export declare function getProfitRateBps(_config?: BundleMerkleConfigInput): number;
27
+ export declare function getProfitRecipient(_config?: BundleMerkleConfigInput): string;
28
+ export declare function calculateProfit(amount: bigint, _config?: BundleMerkleConfigInput): {
29
29
  profit: bigint;
30
30
  remaining: bigint;
31
31
  };
32
- export declare function calculateBatchProfit(amounts: bigint[], _config?: unknown): {
32
+ export declare function calculateBatchProfit(amounts: bigint[], _config?: BundleMerkleConfigInput): {
33
33
  totalProfit: bigint;
34
34
  remainingAmounts: bigint[];
35
35
  };
@@ -26,7 +26,8 @@ export function getBundleErrorMessage(key, ...args) {
26
26
  const lang = (process.env.LANG || process.env.LANGUAGE || 'en').toLowerCase().includes('zh') ? 'zh' : 'en';
27
27
  const message = BUNDLE_ERRORS[key][lang];
28
28
  if (typeof message === 'function') {
29
- return message(...args);
29
+ const [a, b] = args;
30
+ return message(a, b);
30
31
  }
31
32
  return message;
32
33
  }
@@ -101,7 +101,7 @@ export type BalanceValidationParams = {
101
101
  buyerAddress?: string;
102
102
  };
103
103
  export declare function validateFinalBalances({ sameAddress, buyerBalance, buyAmountBNB, reserveGas, gasLimit, gasPrice, useNativeToken, quoteTokenDecimals, provider, buyerAddress, }: BalanceValidationParams): Promise<void>;
104
- export declare function countTruthy(values: unknown[]): number;
104
+ export declare function countTruthy(values: Array<string | number | bigint | boolean | null | undefined>): number;
105
105
  /**
106
106
  * PancakeSwap V2/V3 通用捆绑换手(Merkle Bundle)
107
107
  *
@@ -6,7 +6,7 @@
6
6
  * - 查询: 通过 projectId / token 地址获取信息
7
7
  */
8
8
  import { JsonRpcProvider } from 'ethers';
9
- import type { IroCreateParams, IroCreateProjectResult, IroQueryConfig, IroFactoryTokenInfo } from './types.js';
9
+ import type { IroCreateParams, IroCreateProjectResult, IroQueryConfig, IroFactoryTokenInfo, IroCreateProjectParams } from './types.js';
10
10
  export declare class IroFactoryQuery {
11
11
  private provider;
12
12
  private factory;
@@ -31,7 +31,7 @@ export declare class IroFactoryQuery {
31
31
  /**
32
32
  * 编码 createProject calldata
33
33
  */
34
- export declare function encodeCreateProjectCall(params: import('./types.js').IroCreateProjectParams): string;
34
+ export declare function encodeCreateProjectCall(params: IroCreateProjectParams): string;
35
35
  /**
36
36
  * 创建 IRO 项目 — 签名交易
37
37
  *
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * 各钱包离线签署操作意图,主钱包收集后统一提交。
5
5
  */
6
- import { Wallet, JsonRpcProvider } from 'ethers';
6
+ import { Wallet, JsonRpcProvider, Interface } from 'ethers';
7
7
  import { ENI_CHAIN_ID, ENI_RPC_URL } from '../constants.js';
8
8
  import { ENI_BUNDLER_ABI, EIP712_DOMAIN, SIGNED_OP_TYPE, TOKEN_PULL_TYPE } from './constants.js';
9
9
  const DEFAULT_DEADLINE_SEC = 300;
@@ -19,7 +19,6 @@ function buildDomain(bundlerAddress, chainId = Number(ENI_CHAIN_ID)) {
19
19
  */
20
20
  export async function getBundlerNonce(params) {
21
21
  const provider = new JsonRpcProvider(params.rpcUrl ?? ENI_RPC_URL, ENI_CHAIN_ID, { staticNetwork: true });
22
- const { Interface } = await import('ethers');
23
22
  const iface = new Interface(ENI_BUNDLER_ABI);
24
23
  const data = iface.encodeFunctionData('nonces', [params.signer]);
25
24
  const result = await provider.call({ to: params.bundlerAddress, data });
@@ -5,7 +5,8 @@
5
5
  * 提供代币 Logo、社交链接、描述等元信息查询
6
6
  */
7
7
  import { Contract, JsonRpcProvider } from 'ethers';
8
- import { ENI_CHAIN_ID, ENI_RPC_URL } from '../../constants.js';
8
+ import { ENI_CHAIN_ID, ENI_RPC_URL, LP_FAIR_LAUNCHER_ABI } from '../../constants.js';
9
+ import { LP_FAIR_LAUNCHER_ADDRESS } from '../fair-launch/constants.js';
9
10
  import { ERC20_ABI } from '../../../../abis/common.js';
10
11
  /**
11
12
  * 从 ERC20 合约获取 DAOaaS Portal 代币基本元数据
@@ -41,8 +42,6 @@ export async function getTokenMeta(token, rpcUrl = ENI_RPC_URL) {
41
42
  * FairLaunch 代币的 logo 和 metadata 存储在 LaunchParams 中
42
43
  */
43
44
  export async function getFairLaunchTokenMeta(token, rpcUrl = ENI_RPC_URL) {
44
- const { LP_FAIR_LAUNCHER_ABI } = await import('../../constants.js');
45
- const { LP_FAIR_LAUNCHER_ADDRESS } = await import('../fair-launch/constants.js');
46
45
  const provider = new JsonRpcProvider(rpcUrl, ENI_CHAIN_ID, { staticNetwork: true });
47
46
  const launcher = new Contract(LP_FAIR_LAUNCHER_ADDRESS, LP_FAIR_LAUNCHER_ABI, provider);
48
47
  const info = await launcher.getTokenInfoByToken(token);
@@ -4,7 +4,7 @@
4
4
  * ENI 使用 EIP-1559 (type 2) 交易,与 BSC (type 0) 不同
5
5
  */
6
6
  import { JsonRpcProvider } from 'ethers';
7
- import type { IroCreateParams, IroCreateProjectResult, IroQueryConfig, IroFactoryTokenInfo } from './types.js';
7
+ import type { IroCreateParams, IroCreateProjectResult, IroQueryConfig, IroFactoryTokenInfo, IroCreateProjectParams } from './types.js';
8
8
  export declare class IroFactoryQuery {
9
9
  private provider;
10
10
  private factory;
@@ -20,7 +20,7 @@ export declare class IroFactoryQuery {
20
20
  }>;
21
21
  get providerInstance(): JsonRpcProvider;
22
22
  }
23
- export declare function encodeCreateProjectCall(params: import('./types.js').IroCreateProjectParams): string;
23
+ export declare function encodeCreateProjectCall(params: IroCreateProjectParams): string;
24
24
  export declare function createProject(params: IroCreateParams): Promise<IroCreateProjectResult>;
25
25
  export declare function parseCreateProjectEvent(receipt: {
26
26
  logs: Array<{
@@ -237,23 +237,12 @@ export async function bundleSell(params) {
237
237
  // 判断是否需要查询代币余额
238
238
  const needBalanceQuery = !sellAmounts || sellAmounts.length !== wallets.length;
239
239
  // 构建并行请求
240
- const parallelPromises = [
241
- // 1. 批量获取授权钱包的 nonce
240
+ const [walletNonces, mainNonce, feeData, tokenBalances] = await Promise.all([
242
241
  batchGetNonces(allAddresses, provider),
243
- // 2. 获取主钱包的交易 nonce
244
242
  provider.getTransactionCount(mainWallet.address, 'pending'),
245
- // 3. 获取 gas 费用数据
246
243
  provider.getFeeData(),
247
- ];
248
- // 4. 如果需要查询余额,并行获取
249
- if (needBalanceQuery) {
250
- parallelPromises.push(getTokenBalances(wallets, tokenAddress));
251
- }
252
- const results = await Promise.all(parallelPromises);
253
- const walletNonces = results[0];
254
- const mainNonce = results[1];
255
- const feeData = results[2];
256
- const tokenBalances = needBalanceQuery ? results[3] : null;
244
+ needBalanceQuery ? getTokenBalances(wallets, tokenAddress) : Promise.resolve(null),
245
+ ]);
257
246
  // ========================================
258
247
  // 同步计算卖出数量
259
248
  // ========================================
@@ -18,7 +18,7 @@ import { UserType } from './utils.js';
18
18
  export interface TransferResult {
19
19
  signedTransaction: string;
20
20
  hopWallets?: GeneratedWallet[];
21
- metadata: Record<string, unknown>;
21
+ metadata: Record<string, string | number | boolean | bigint | null | undefined>;
22
22
  }
23
23
  export interface DisperseParams {
24
24
  /** 资金来源钱包私钥(Token/OKB 从这个钱包转出) */
@@ -210,22 +210,21 @@ export async function sweep(params) {
210
210
  // ========================================
211
211
  const sourceAddresses = sourceWallets.map((w) => w.address);
212
212
  const addressesToGetNonces = payerWallet ? [payerWallet.address, ...sourceAddresses] : sourceAddresses;
213
- const parallelPromises = [batchGetNonces(addressesToGetNonces, provider), provider.getFeeData()];
214
- if (isNative) {
215
- parallelPromises.push(Promise.all(sourceWallets.map((w) => provider.getBalance(w.address))));
216
- }
217
- else {
218
- if (!tokenAddress)
219
- throw new Error('tokenAddress is required for ERC20 sweep');
220
- const tokenContract = new Contract(tokenAddress, ERC20_ABI, provider);
221
- parallelPromises.push(Promise.all(sourceWallets.map((w) => tokenContract.balanceOf(w.address))));
222
- }
223
- const results = await Promise.all(parallelPromises);
224
- const allNoncesResult = results[0];
213
+ const [allNoncesResult, feeData, balancesRaw] = await Promise.all([
214
+ batchGetNonces(addressesToGetNonces, provider),
215
+ provider.getFeeData(),
216
+ isNative
217
+ ? Promise.all(sourceWallets.map((w) => provider.getBalance(w.address)))
218
+ : Promise.all(sourceWallets.map((w) => {
219
+ if (!tokenAddress)
220
+ throw new Error('tokenAddress is required for ERC20 sweep');
221
+ const tokenContract = new Contract(tokenAddress, ERC20_ABI, provider);
222
+ return tokenContract.balanceOf(w.address);
223
+ })),
224
+ ]);
225
225
  const payerNonce = payerWallet ? allNoncesResult[0] : allNoncesResult[0];
226
226
  const nonces = payerWallet ? allNoncesResult.slice(1) : allNoncesResult;
227
- const feeData = results[1];
228
- const balances = results[2].map((b) => typeof b === 'bigint' ? b : BigInt(b.toString()));
227
+ const balances = balancesRaw;
229
228
  // 计算转账金额
230
229
  // ✅ 修复:当 percent=100 时,不预留 gas,归集全部余额
231
230
  const isFullSweep = percent >= 100;
@@ -7,6 +7,7 @@ import { UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, FLAP_P
7
7
  import { bundleBuy } from './bundle-buy.js';
8
8
  import { bundleSell } from './bundle-sell.js';
9
9
  import { bundleSwap } from './bundle-swap.js';
10
+ import { quoteV2, quoteV3 } from '../../../utils/quote-helpers.js';
10
11
  import { truncateDecimals, } from './volume-helpers.js';
11
12
  export async function washVolume(params) {
12
13
  const { mainPrivateKey, privateKeys, tokenAddress, tokenDecimals = 18, buyAmountPerWallet, sellPercent = 100, poolType = 'V3', routerAddress, fee = V3_FEE_TIERS.MEDIUM, userType = 'v0', // ✅ 用户类型(影响利润率)
@@ -108,7 +109,6 @@ export async function washVolume(params) {
108
109
  else if (poolType === 'V2' || poolType === 'V3') {
109
110
  // ✅ V2/V3 外盘模式:通过报价获取预估代币数量,确保买卖对等
110
111
  try {
111
- const { quoteV2, quoteV3 } = await import('../../../utils/quote-helpers.js');
112
112
  let totalTokenAmount = 0n;
113
113
  if (poolType === 'V3') {
114
114
  const result = await quoteV3(provider, WOKB_ADDRESS, tokenAddress, totalBuyWei, 'XLAYER', fee);
@@ -258,21 +258,21 @@ export async function bundleVolume(params) {
258
258
  const allAddresses = allWallets.map((w) => w.address);
259
259
  // 获取卖出钱包的代币余额(如果需要)
260
260
  const needBalanceQuery = !sellAmounts || sellAmounts.length !== sellerWallets.length;
261
- const parallelPromises = [batchGetNonces(allAddresses, provider), provider.getFeeData()];
262
- if (needBalanceQuery && sellerWallets.length > 0) {
263
- const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
264
- parallelPromises.push(Promise.all(sellerWallets.map((w) => tokenContract.balanceOf(w.address))));
265
- }
266
- const results = await Promise.all(parallelPromises);
267
- const nonces = results[0];
268
- const feeData = results[1];
261
+ const tokenContract = needBalanceQuery && sellerWallets.length > 0 ? new ethers.Contract(tokenAddress, ERC20_ABI, provider) : null;
262
+ const [nonces, feeData, sellerBalancesRaw] = await Promise.all([
263
+ batchGetNonces(allAddresses, provider),
264
+ provider.getFeeData(),
265
+ needBalanceQuery && sellerWallets.length > 0 && tokenContract
266
+ ? Promise.all(sellerWallets.map((w) => tokenContract.balanceOf(w.address)))
267
+ : Promise.resolve(null),
268
+ ]);
269
269
  // 计算卖出金额
270
270
  let sellAmountsWei;
271
271
  if (sellAmounts && sellAmounts.length === sellerWallets.length) {
272
272
  // ✅ 如果有指定卖出金额,直接使用
273
273
  sellAmountsWei = sellAmounts.map((amt) => ethers.parseUnits(truncateDecimals(amt, tokenDecimals), tokenDecimals));
274
274
  }
275
- else if (needBalanceQuery && results[2]) {
275
+ else if (needBalanceQuery && sellerBalancesRaw) {
276
276
  // ✅ 根据 poolType 预估买入能获得的代币数量
277
277
  let estimatedTokenAmount = 0n;
278
278
  try {
@@ -6,7 +6,7 @@
6
6
  export { disperseWithBundleMerkle } from '../bundle-core/four-meme/utils-disperse.js';
7
7
  export { sweepWithBundleMerkle } from '../bundle-core/four-meme/utils-sweep.js';
8
8
  export { pairwiseTransferWithBundleMerkle } from '../bundle-core/four-meme/utils-pairwise.js';
9
- export { fourBundleSwapMerkle, fourBatchSwapMerkle, fourQuickBatchSwapMerkle, } from '../bundle-core/four-meme/swap.js';
9
+ export { fourBundleSwapMerkle, fourBatchSwapMerkle, fourQuickBatchSwapMerkle } from '../bundle-core/four-meme/swap.js';
10
10
  export { fourBundleBuyFirstMerkle } from '../bundle-core/four-meme/swap-buy-first.js';
11
11
  export { submitBundleToMerkle, submitBundleToBlockRazor, submitDirectToRpc, submitDirectToRpcSmart, type MerkleSubmitConfig, type SubmitBundleResult, type BlockRazorSubmitConfig, type DirectSubmitConfig, } from '../bundle-core/submit.js';
12
12
  export type { DisperseSignParams, DisperseMerkleResult, SweepSignParams, SweepMerkleResult, FourBundleSwapSignParams, FourSwapResult, FourBatchSwapSignParams, FourBatchSwapResult, FourQuickBatchSwapSignParams, FourQuickBatchSwapResult, PairwiseTransferSignParams, PairwiseTransferMerkleResult, } from '../bundle-core/four-meme/types.js';
@@ -6,6 +6,6 @@
6
6
  export { disperseWithBundleMerkle } from '../bundle-core/four-meme/utils-disperse.js';
7
7
  export { sweepWithBundleMerkle } from '../bundle-core/four-meme/utils-sweep.js';
8
8
  export { pairwiseTransferWithBundleMerkle } from '../bundle-core/four-meme/utils-pairwise.js';
9
- export { fourBundleSwapMerkle, fourBatchSwapMerkle, fourQuickBatchSwapMerkle, } from '../bundle-core/four-meme/swap.js';
9
+ export { fourBundleSwapMerkle, fourBatchSwapMerkle, fourQuickBatchSwapMerkle } from '../bundle-core/four-meme/swap.js';
10
10
  export { fourBundleBuyFirstMerkle } from '../bundle-core/four-meme/swap-buy-first.js';
11
11
  export { submitBundleToMerkle, submitBundleToBlockRazor, submitDirectToRpc, submitDirectToRpcSmart, } from '../bundle-core/submit.js';
@@ -87,14 +87,26 @@ export type CreateTokenReq = {
87
87
  funGroup: false;
88
88
  clickFun: false;
89
89
  };
90
+ export type FourJsonValue = string | number | boolean | null | FourJsonValue[] | {
91
+ [key: string]: FourJsonValue;
92
+ };
93
+ export type FourPublicConfig = {
94
+ [key: string]: FourJsonValue;
95
+ };
96
+ export type FourTokenDetail = {
97
+ [key: string]: FourJsonValue;
98
+ } & {
99
+ address?: string;
100
+ name?: string;
101
+ symbol?: string;
102
+ };
90
103
  export type CreateTokenResp = {
91
104
  createArg: string;
92
105
  signature: string;
93
106
  tokenAddr?: string;
94
107
  address?: string;
95
108
  token?: string;
96
- [key: string]: unknown;
97
- };
109
+ } & Record<string, string | undefined>;
98
110
  export declare class FourClient {
99
111
  private baseUrl;
100
112
  private uploadUrls;
@@ -124,9 +136,12 @@ export declare class FourClient {
124
136
  private isCorsOrNetworkError;
125
137
  private getFilenameFromBlob;
126
138
  createToken(accessToken: string, req: CreateTokenReq): Promise<CreateTokenResp>;
127
- getPublicConfig(): Promise<unknown>;
128
- getTokenByAddress(address: string, accessToken?: string): Promise<unknown>;
129
- getTokensByAddresses(addresses: string[], accessToken?: string): Promise<unknown[]>;
130
- getTokenById(id: string | number, accessToken?: string): Promise<unknown>;
139
+ getPublicConfig(): Promise<FourPublicConfig>;
140
+ getTokenByAddress(address: string, accessToken?: string): Promise<FourTokenDetail>;
141
+ getTokensByAddresses(addresses: string[], accessToken?: string): Promise<(FourTokenDetail | {
142
+ address: string;
143
+ error: string;
144
+ })[]>;
145
+ getTokenById(id: string | number, accessToken?: string): Promise<FourTokenDetail>;
131
146
  }
132
147
  export declare function buildLoginMessage(nonce: string): string;
@@ -155,16 +155,17 @@ export class FourClient {
155
155
  return imgUrl;
156
156
  }
157
157
  catch (e) {
158
- lastError = e;
158
+ const err = e instanceof Error ? e : new Error(getErrorMessageFromUnknown(e));
159
+ lastError = err;
159
160
  const canRetry = i < this.uploadUrls.length - 1;
160
- if (this.isRateLimitError(e) && canRetry) {
161
+ if (this.isRateLimitError(err) && canRetry) {
161
162
  // IP 限流 → 切下一个端点
162
163
  const nextUrl = this.uploadUrls[i + 1];
163
164
  const nextLabel = nextUrl === FOUR_MEME_OFFICIAL_API ? '官方直连' : `备用端点 #${i + 2}`;
164
165
  console.warn(`[FourClient] 端点 ${baseUrl} 触发 IP 限流(100次/天),切换到${nextLabel}...`);
165
166
  continue;
166
167
  }
167
- if (this.isCorsOrNetworkError(e) && canRetry) {
168
+ if (this.isCorsOrNetworkError(err) && canRetry) {
168
169
  // CORS/网络错误(常见于浏览器直连官方 API)→ 跳过,继续尝试下一个
169
170
  console.warn(`[FourClient] 端点 ${baseUrl} 请求失败(${isOfficial ? 'CORS限制' : '网络错误'}),跳过`);
170
171
  continue;
@@ -251,7 +252,7 @@ export class FourClient {
251
252
  }
252
253
  async getPublicConfig() {
253
254
  const r = await fetch(`${this.baseUrl}/v1/public/config`);
254
- return await r.json();
255
+ return (await r.json());
255
256
  }
256
257
  async getTokenByAddress(address, accessToken) {
257
258
  const r = await fetch(`${this.baseUrl}/v1/private/token/get?address=${address}`, {
@@ -261,12 +262,12 @@ export class FourClient {
261
262
  if (j.code && j.code !== '0' && j.code !== 0) {
262
263
  throw new Error(`getTokenByAddress failed: ${JSON.stringify(j)}`);
263
264
  }
264
- return j.data ?? j;
265
+ return (j.data ?? j);
265
266
  }
266
267
  async getTokensByAddresses(addresses, accessToken) {
267
268
  if (!Array.isArray(addresses) || addresses.length === 0)
268
269
  return [];
269
- const tasks = addresses.map((addr) => this.getTokenByAddress(addr, accessToken).catch((e) => ({ address: addr, error: String(e) })));
270
+ const tasks = addresses.map((addr) => this.getTokenByAddress(addr, accessToken).catch((e) => ({ address: addr, error: getErrorMessageFromUnknown(e) })));
270
271
  return await Promise.all(tasks);
271
272
  }
272
273
  async getTokenById(id, accessToken) {
@@ -277,7 +278,7 @@ export class FourClient {
277
278
  if (j.code && j.code !== '0' && j.code !== 0) {
278
279
  throw new Error(`getTokenById failed: ${JSON.stringify(j)}`);
279
280
  }
280
- return j.data ?? j;
281
+ return (j.data ?? j);
281
282
  }
282
283
  }
283
284
  export function buildLoginMessage(nonce) {
@@ -1,7 +1,13 @@
1
1
  import { JsonRpcProvider } from 'ethers';
2
2
  export type FlapChain = 'BSC' | 'BASE' | 'XLAYER' | 'MORPH';
3
+ /** JSON 可序列化值(递归) */
4
+ export type JsonValue = string | number | boolean | null | JsonValue[] | {
5
+ [key: string]: JsonValue;
6
+ };
3
7
  /** IPFS 元数据 JSON(字段因项目而异) */
4
- export type FlapTokenMetaJson = Record<string, unknown>;
8
+ export type FlapTokenMetaJson = {
9
+ [key: string]: JsonValue;
10
+ };
5
11
  export type FlapMetaByAddressResult = {
6
12
  cid: string;
7
13
  data?: FlapTokenMetaJson;
@@ -2,25 +2,34 @@ export type PinataConfig = {
2
2
  jwt: string;
3
3
  gateway?: string;
4
4
  };
5
+ type JsonValue = string | number | boolean | null | JsonValue[] | {
6
+ [key: string]: JsonValue;
7
+ };
8
+ /** Pinata gateway JSON 响应(结构因 CID 内容而异) */
9
+ export type PinataGatewayJson = {
10
+ [key: string]: JsonValue;
11
+ };
5
12
  type PinataPinApiResponse = {
6
13
  IpfsHash?: string;
7
14
  cid?: string;
8
15
  PinSize?: number;
9
16
  Timestamp?: string;
10
17
  };
18
+ export type PinataJsonUpload = {
19
+ [key: string]: JsonValue;
20
+ };
11
21
  export declare class PinataClient {
12
22
  private client;
13
- private ready;
14
23
  constructor(cfg: PinataConfig);
15
24
  uploadFile(file: File): Promise<{
16
25
  cid: string;
17
26
  id: string;
18
27
  }>;
19
- uploadJSON(obj: unknown, name?: string): Promise<{
28
+ uploadJSON(obj: PinataJsonUpload, name?: string): Promise<{
20
29
  cid: string;
21
30
  id: string;
22
31
  }>;
23
- get(cid: string): Promise<unknown>;
32
+ get(cid: string): Promise<PinataGatewayJson>;
24
33
  url(cid: string): Promise<string>;
25
34
  }
26
35
  export type PinataPinResp = {
@@ -1,14 +1,13 @@
1
+ import { createReadStream } from 'node:fs';
2
+ import axios from 'axios';
3
+ import NodeFormData from 'form-data';
4
+ import { PinataSDK } from 'pinata';
1
5
  export class PinataClient {
2
6
  client;
3
- ready;
4
7
  constructor(cfg) {
5
- this.ready = (async () => {
6
- const mod = (await import('pinata'));
7
- this.client = new mod.PinataSDK({ pinataJwt: cfg.jwt, pinataGateway: cfg.gateway });
8
- })();
8
+ this.client = new PinataSDK({ pinataJwt: cfg.jwt, pinataGateway: cfg.gateway });
9
9
  }
10
10
  async uploadFile(file) {
11
- await this.ready;
12
11
  const r = await this.client.upload.file(file);
13
12
  return { cid: r.cid, id: r.id ?? '' };
14
13
  }
@@ -18,11 +17,9 @@ export class PinataClient {
18
17
  return await this.uploadFile(file);
19
18
  }
20
19
  async get(cid) {
21
- await this.ready;
22
20
  return await this.client.gateways.public.get(cid);
23
21
  }
24
22
  async url(cid) {
25
- await this.ready;
26
23
  return await this.client.gateways.convert(cid);
27
24
  }
28
25
  }
@@ -34,16 +31,9 @@ export class PinataClient {
34
31
  * @param fileName 可选:内存数据的文件名
35
32
  */
36
33
  export async function pinFileToIPFSWithJWT(jwt, filePath, data, fileName) {
37
- const axiosMod = await import('axios');
38
- const FormDataMod = (await import('form-data'));
39
- const fsMod = await import('node:fs');
40
- const FormDataCtor = FormDataMod.default;
41
- if (!FormDataCtor) {
42
- throw new Error('pinFileToIPFSWithJWT: form-data module unavailable');
43
- }
44
- const form = new FormDataCtor();
34
+ const form = new NodeFormData();
45
35
  if (filePath && filePath.length > 0) {
46
- form.append('file', fsMod.createReadStream(filePath));
36
+ form.append('file', createReadStream(filePath));
47
37
  }
48
38
  else if (data) {
49
39
  form.append('file', data, { filename: fileName || 'upload.bin' });
@@ -51,8 +41,8 @@ export async function pinFileToIPFSWithJWT(jwt, filePath, data, fileName) {
51
41
  else {
52
42
  throw new Error('pinFileToIPFSWithJWT: either filePath or data must be provided');
53
43
  }
54
- const resp = await axiosMod.default.post('https://api.pinata.cloud/pinning/pinFileToIPFS', form, {
55
- headers: { Authorization: `Bearer ${jwt}`, ...(form.getHeaders ? form.getHeaders() : {}) },
44
+ const resp = await axios.post('https://api.pinata.cloud/pinning/pinFileToIPFS', form, {
45
+ headers: { Authorization: `Bearer ${jwt}`, ...form.getHeaders() },
56
46
  maxContentLength: Infinity,
57
47
  maxBodyLength: Infinity,
58
48
  });
@@ -7,6 +7,7 @@
7
7
  * 3. 买到毕业(代币上 DEX)
8
8
  * 4. 多个钱包在 PancakeSwap(外盘)继续买入
9
9
  */
10
+ import { type TaxVaultConfig } from '../vault.js';
10
11
  import { type FlapSignConfig } from './config.js';
11
12
  import type { MerkleSignedResult } from './types.js';
12
13
  export type CreateToDexChain = 'bsc' | 'xlayer' | 'base';
@@ -85,7 +86,7 @@ export interface FlapCreateToDexParams {
85
86
  lpBps: number;
86
87
  minimumShareBalance?: number;
87
88
  /** ✅ Tax Vault 金库配置(可选) */
88
- vaultConfig?: import('../vault.js').TaxVaultConfig;
89
+ vaultConfig?: TaxVaultConfig;
89
90
  };
90
91
  /** V3 扩展 ID */
91
92
  extensionID?: string;
@@ -36,4 +36,14 @@ export interface CommonBundleConfig {
36
36
  export declare function getGasLimit(config: CommonBundleConfig, defaultGas?: number): bigint;
37
37
  export declare function getGasPriceConfig(config: CommonBundleConfig): GasPriceConfig;
38
38
  export declare function getTxType(config: CommonBundleConfig): 0 | 2;
39
- export declare function buildGasFields(txType: number, gasPrice: bigint): Record<string, unknown>;
39
+ export type LegacyGasFields = {
40
+ type: 0;
41
+ gasPrice: bigint;
42
+ };
43
+ export type Eip1559GasFields = {
44
+ type: 2;
45
+ maxFeePerGas: bigint;
46
+ maxPriorityFeePerGas: bigint;
47
+ };
48
+ export type TxGasFields = LegacyGasFields | Eip1559GasFields;
49
+ export declare function buildGasFields(txType: number, gasPrice: bigint): TxGasFields;
@@ -1,5 +1,8 @@
1
1
  import '../tx/wallet-sign-patch.js';
2
2
  import { CHAINS } from '../../constants/index.js';
3
+ function asBatchSendProvider(provider) {
4
+ return provider;
5
+ }
3
6
  const GLOBAL_NONCE_TTL_MS = 60 * 1000;
4
7
  const globalNonceCursorByChain = new Map();
5
8
  function isFlakyNonceChain(chainId) {
@@ -145,7 +148,7 @@ export class NonceManager {
145
148
  id: idx + 1,
146
149
  jsonrpc: '2.0',
147
150
  }));
148
- const rawResults = await this.provider._send(batchRequests);
151
+ const rawResults = await asBatchSendProvider(this.provider)._send(batchRequests);
149
152
  queryResults = rawResults.map((r) => {
150
153
  if (r.result) {
151
154
  return parseInt(r.result, 16);
@@ -146,7 +146,8 @@ export async function sweepWithBundle(params) {
146
146
  try {
147
147
  const balData = iface.encodeFunctionData('balanceOf', [w.address]);
148
148
  const balRaw = await provider.call({ to: tokenAddress, data: balData });
149
- const [bal] = iface.decodeFunctionResult('balanceOf', balRaw);
149
+ const decoded = iface.decodeFunctionResult('balanceOf', balRaw);
150
+ const bal = decoded[0];
150
151
  return bal;
151
152
  }
152
153
  catch {
@@ -4,6 +4,7 @@
4
4
  import { ethers, Contract } from 'ethers';
5
5
  import { batchCheckAllowances } from './erc20.js';
6
6
  import { ERC20_ABI } from '../abis/common.js';
7
+ import { NonceManager, getOptimizedGasPrice } from './bundle-helpers.js';
7
8
  const APPROVAL_THRESHOLD = ethers.MaxUint256 / 2n;
8
9
  /**
9
10
  * 获取 Gas Limit(授权专用)
@@ -35,7 +36,6 @@ export async function ensureTokenApproval(provider, merkle, wallet, tokenAddress
35
36
  return;
36
37
  }
37
38
  // ✅ 使用 NonceManager(与 pancake-proxy.ts 一致)
38
- const { NonceManager, getOptimizedGasPrice } = await import('./bundle-helpers.js');
39
39
  const nonceManager = new NonceManager(provider);
40
40
  // 构建授权交易
41
41
  const tokenContract = new Contract(tokenAddress, ERC20_ABI, wallet);
@@ -120,7 +120,8 @@ async function _queryMultiTokenBalancesSingle(provider, multicallAddress, tokenA
120
120
  // ✅ 单次 multicall 获取所有数据
121
121
  const callData = multiIface.encodeFunctionData('aggregate', [calls]);
122
122
  const result = await provider.call({ to: multicallAddress, data: callData });
123
- const [, returnData] = multiIface.decodeFunctionResult('aggregate', result);
123
+ const decoded = multiIface.decodeFunctionResult('aggregate', result);
124
+ const returnData = decoded[1];
124
125
  // ✅ 并行解码结果
125
126
  const outputs = holders.map((addr) => ({
126
127
  address: addr,
@@ -213,7 +214,8 @@ async function _querySingleTokenBalancesSingle(provider, multicallAddress, token
213
214
  }));
214
215
  const callData = multiIface.encodeFunctionData('aggregate', [calls]);
215
216
  const result = await provider.call({ to: multicallAddress, data: callData });
216
- const [, returnData] = multiIface.decodeFunctionResult('aggregate', result);
217
+ const decoded = multiIface.decodeFunctionResult('aggregate', result);
218
+ const returnData = decoded[1];
217
219
  return holders.map((addr, i) => {
218
220
  try {
219
221
  const decoded = erc20Iface.decodeFunctionResult('balanceOf', returnData[i]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -182,6 +182,7 @@
182
182
  "@ethereumjs/rlp": "^10.1.0",
183
183
  "axios": "^1.12.2",
184
184
  "ethers": "^6.16.0",
185
+ "form-data": "^4.0.2",
185
186
  "pinata": "^1.10.1"
186
187
  },
187
188
  "devDependencies": {