four-flap-meme-sdk 4.0.3 → 4.0.5

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.
Files changed (47) hide show
  1. package/README.en.md +17 -2
  2. package/README.zh-CN.md +17 -2
  3. package/dist/abis/openfour.d.ts +94 -0
  4. package/dist/abis/openfour.js +63 -0
  5. package/dist/bundle/four-meme/core/create-openfour.d.ts +26 -0
  6. package/dist/bundle/four-meme/core/create-openfour.js +121 -0
  7. package/dist/bundle/four-meme/core/create-token.js +3 -0
  8. package/dist/bundle/four-meme/core/index.d.ts +2 -0
  9. package/dist/bundle/four-meme/core/index.js +1 -0
  10. package/dist/bundle/four-meme/private/buy-quote.d.ts +35 -0
  11. package/dist/bundle/four-meme/private/buy-quote.js +70 -0
  12. package/dist/bundle/four-meme/private/buy.js +82 -87
  13. package/dist/bundle/four-meme/private/index.d.ts +2 -0
  14. package/dist/bundle/four-meme/private/index.js +1 -0
  15. package/dist/bundle/four-meme/private/openfour-buy.d.ts +10 -0
  16. package/dist/bundle/four-meme/private/openfour-buy.js +65 -0
  17. package/dist/bundle/four-meme/types/index.d.ts +13 -0
  18. package/dist/chains/bsc/constants.d.ts +1 -0
  19. package/dist/core/clients/four.d.ts +13 -1
  20. package/dist/core/clients/four.js +69 -0
  21. package/dist/core/constants/addresses.d.ts +2 -0
  22. package/dist/core/constants/addresses.js +2 -0
  23. package/dist/domains/flap/ipfs.d.ts +3 -0
  24. package/dist/domains/flap/ipfs.js +20 -1
  25. package/dist/domains/flows/create.d.ts +3 -0
  26. package/dist/domains/flows/create.js +3 -0
  27. package/dist/domains/four/contracts/tm-bundle-create.js +3 -0
  28. package/dist/domains/four/contracts/tm-bundle-helpers.d.ts +3 -0
  29. package/dist/domains/four/tax-token.d.ts +2 -0
  30. package/dist/domains/four/tax-token.js +13 -4
  31. package/dist/domains/four/tax-token.test.d.ts +1 -0
  32. package/dist/domains/four/tax-token.test.js +89 -0
  33. package/dist/domains/openfour/create-arg.d.ts +13 -0
  34. package/dist/domains/openfour/create-arg.js +78 -0
  35. package/dist/domains/openfour/index.d.ts +4 -0
  36. package/dist/domains/openfour/index.js +3 -0
  37. package/dist/domains/openfour/registry.d.ts +4 -0
  38. package/dist/domains/openfour/registry.js +44 -0
  39. package/dist/domains/openfour/schema.d.ts +20 -0
  40. package/dist/domains/openfour/schema.js +88 -0
  41. package/dist/domains/openfour/types.d.ts +79 -0
  42. package/dist/domains/openfour/types.js +1 -0
  43. package/dist/exports/root-bundle-and-tooling.d.ts +4 -2
  44. package/dist/exports/root-bundle-and-tooling.js +2 -2
  45. package/dist/exports/root-foundations.d.ts +1 -0
  46. package/dist/exports/root-foundations.js +1 -0
  47. package/package.json +1 -1
@@ -8,6 +8,7 @@ import { ADDRESSES, BLOCKRAZOR_BUILDER_EOA, CHAINS } from '../../../core/constan
8
8
  import { TM2_ABI } from '../../../abis/common.js';
9
9
  import { getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, calculateBatchProfit, getProfitRecipient, getBribeAmount, } from '../../config-helpers.js';
10
10
  import { createSignProvider } from '../../../core/sign-context-helpers.js';
11
+ import { isFourErc20BuyQuote, resolveFourBuyPayment, signQuoteApproveTxs } from './buy-quote.js';
11
12
  const CHAIN_ID = CHAINS.BSC.chainId;
12
13
  /**
13
14
  * 私有购买(单笔)(仅签名版本 - 不依赖 Merkle)
@@ -15,46 +16,45 @@ const CHAIN_ID = CHAINS.BSC.chainId;
15
16
  * ✅ 优化:并行获取 gasPrice 和 nonce
16
17
  */
17
18
  export async function fourPrivateBuyMerkle(params) {
18
- const { privateKey, tokenAddress, funds, to, config } = params;
19
- // ✅ 直接使用传入的 RPC URL 创建 provider(不依赖 Merkle)
19
+ const { privateKey, tokenAddress, funds, to, config, quoteTokenAddress, quoteDecimals } = params;
20
20
  const provider = createSignProvider(config.rpcUrl, CHAIN_ID, 'BSC');
21
21
  const wallet = new Wallet(privateKey, provider);
22
22
  const tmAddr = ADDRESSES.BSC.TokenManagerOriginal;
23
23
  const txType = getTxType(config);
24
24
  const gasLimit = getGasLimit(config, 800000);
25
- const originalFundsWei = ethers.parseEther(funds);
26
- // ✅ 利润提取配置(同步计算)
27
- const extractProfit = shouldExtractProfit();
25
+ const payment = await resolveFourBuyPayment({
26
+ provider,
27
+ tokenAddress,
28
+ funds,
29
+ quote: { quoteTokenAddress, quoteDecimals },
30
+ });
31
+ const originalFundsWei = payment.fundsWei;
32
+ const erc20Quote = isFourErc20BuyQuote(quoteTokenAddress);
33
+ const extractProfit = shouldExtractProfit() && !erc20Quote;
28
34
  let profitWei = 0n;
29
35
  let actualFundsWei = originalFundsWei;
36
+ let actualValueWei = payment.valueWei;
30
37
  if (extractProfit) {
31
38
  const { profit, remaining } = calculateProfit(originalFundsWei);
32
39
  profitWei = profit;
33
40
  actualFundsWei = remaining;
41
+ actualValueWei = remaining;
34
42
  }
35
43
  const tm2 = new ethers.Contract(tmAddr, TM2_ABI, wallet);
36
- // ✅ 获取贿赂金额
37
44
  const bribeAmount = getBribeAmount(config);
38
45
  const needBribeTx = bribeAmount > 0n;
39
46
  const needProfitTx = extractProfit && profitWei > 0n;
40
- // ✅ 优化:并行获取 gasPrice、nonce 和构建未签名交易
41
- const [gasPrice, baseNonce, unsigned] = await Promise.all([
47
+ const nonceManager = new NonceManager(provider);
48
+ const [gasPrice, unsigned] = await Promise.all([
42
49
  getOptimizedGasPrice(provider, getGasPriceConfig(config)),
43
- wallet.getNonce(),
44
50
  tm2.buyTokenAMAP.populateTransaction(0n, tokenAddress, to ?? wallet.address, actualFundsWei, 0n, {
45
- value: actualFundsWei,
51
+ value: actualValueWei,
46
52
  }),
47
53
  ]);
48
- // ✅ 分配 nonces:贿赂 → 买入 → 利润
49
- let nonceIdx = 0;
50
- const bribeNonce = needBribeTx ? baseNonce + nonceIdx++ : undefined;
51
- const buyNonce = baseNonce + nonceIdx++;
52
- const profitNonce = needProfitTx ? baseNonce + nonceIdx : undefined;
53
- // ✅ 并行签名所有交易(贿赂、买入、利润)
54
- const signPromises = [];
55
- // 贿赂交易
56
- if (needBribeTx && bribeNonce !== undefined) {
57
- signPromises.push(wallet.signTransaction({
54
+ const signedTxs = [];
55
+ if (needBribeTx) {
56
+ const bribeNonce = await nonceManager.getNextNonce(wallet);
57
+ signedTxs.push(await wallet.signTransaction({
58
58
  to: BLOCKRAZOR_BUILDER_EOA,
59
59
  value: bribeAmount,
60
60
  nonce: bribeNonce,
@@ -64,22 +64,32 @@ export async function fourPrivateBuyMerkle(params) {
64
64
  type: txType,
65
65
  }));
66
66
  }
67
- // 买入交易
68
- signPromises.push(wallet.signTransaction({
67
+ if (erc20Quote && payment.quoteAddress && payment.approvalWei > 0n) {
68
+ const approves = await signQuoteApproveTxs({
69
+ wallets: [wallet],
70
+ quoteAddress: payment.quoteAddress,
71
+ spender: tmAddr,
72
+ amounts: [actualFundsWei],
73
+ nonceManager,
74
+ gasPrice,
75
+ chainId: CHAIN_ID,
76
+ txType,
77
+ provider,
78
+ });
79
+ signedTxs.push(...approves);
80
+ }
81
+ signedTxs.push(await wallet.signTransaction({
69
82
  ...unsigned,
70
83
  from: wallet.address,
71
- nonce: buyNonce,
84
+ nonce: await nonceManager.getNextNonce(wallet),
72
85
  gasLimit,
73
86
  gasPrice,
74
87
  chainId: CHAIN_ID,
75
88
  type: txType,
76
- value: actualFundsWei,
89
+ value: actualValueWei,
77
90
  }));
78
- // ✅ 并行签名完成后按顺序返回
79
- const signedTxs = await Promise.all(signPromises);
80
- // ✅ 利润多跳转账(强制 2 跳中转)
81
91
  let profitHopWallets;
82
- if (needProfitTx && profitNonce !== undefined) {
92
+ if (needProfitTx) {
83
93
  const profitHopResult = await buildProfitHopTransactions({
84
94
  provider,
85
95
  payerWallet: wallet,
@@ -89,14 +99,15 @@ export async function fourPrivateBuyMerkle(params) {
89
99
  gasPrice,
90
100
  chainId: CHAIN_ID,
91
101
  txType,
92
- startNonce: profitNonce,
102
+ startNonce: await nonceManager.getNextNonce(wallet),
93
103
  });
94
104
  signedTxs.push(...profitHopResult.signedTransactions);
95
- profitHopWallets = profitHopResult.hopWallets; // ✅ 收集利润多跳钱包
105
+ profitHopWallets = profitHopResult.hopWallets;
96
106
  }
107
+ nonceManager.clearTemp();
97
108
  return {
98
109
  signedTransactions: signedTxs,
99
- profitHopWallets, // ✅ 返回利润多跳钱包
110
+ profitHopWallets,
100
111
  };
101
112
  }
102
113
  /**
@@ -105,25 +116,30 @@ export async function fourPrivateBuyMerkle(params) {
105
116
  * ✅ 优化:并行获取 gasPrice 和 nonces,批量构建交易
106
117
  */
107
118
  export async function fourBatchPrivateBuyMerkle(params) {
108
- const { privateKeys, fundsList, tokenAddress, config } = params;
119
+ const { privateKeys, fundsList, tokenAddress, config, quoteTokenAddress, quoteDecimals } = params;
109
120
  if (privateKeys.length !== fundsList.length) {
110
121
  throw new Error('privateKeys and fundsList length mismatch');
111
122
  }
112
- // ✅ 直接使用传入的 RPC URL 创建 provider(不依赖 Merkle)
113
123
  const provider = createSignProvider(config.rpcUrl, CHAIN_ID, 'BSC');
114
124
  const tmAddr = ADDRESSES.BSC.TokenManagerOriginal;
115
125
  const txType = getTxType(config);
116
126
  const finalGasLimit = getGasLimit(config, 800000);
127
+ const erc20Quote = isFourErc20BuyQuote(quoteTokenAddress);
117
128
  const wallets = privateKeys.map((k) => new Wallet(k, provider));
118
- const originalAmountsWei = fundsList.map((a) => ethers.parseEther(a));
119
- // ✅ 利润提取配置(同步计算)
120
- const extractProfit = shouldExtractProfit();
129
+ const payments = await Promise.all(fundsList.map((funds) => resolveFourBuyPayment({
130
+ provider,
131
+ tokenAddress,
132
+ funds,
133
+ quote: { quoteTokenAddress, quoteDecimals },
134
+ })));
135
+ const originalAmountsWei = payments.map((p) => p.fundsWei);
136
+ const valueWeiList = payments.map((p) => p.valueWei);
137
+ const extractProfit = shouldExtractProfit() && !erc20Quote;
121
138
  const { totalProfit, remainingAmounts } = calculateBatchProfit(originalAmountsWei);
122
- const actualAmountsWei = remainingAmounts;
123
- // ✅ 获取贿赂金额
139
+ const actualAmountsWei = extractProfit ? remainingAmounts : originalAmountsWei;
140
+ const actualValueWei = extractProfit ? remainingAmounts : valueWeiList;
124
141
  const bribeAmount = getBribeAmount(config);
125
142
  const needBribeTx = bribeAmount > 0n;
126
- // ✅ 找出投入金额最多的钱包(作为贿赂和利润支付者)- 同步计算
127
143
  let maxFundsIndex = 0;
128
144
  let maxFunds = originalAmountsWei[0];
129
145
  for (let i = 1; i < originalAmountsWei.length; i++) {
@@ -135,74 +151,53 @@ export async function fourBatchPrivateBuyMerkle(params) {
135
151
  const tm2Contracts = wallets.map((w) => new ethers.Contract(tmAddr, TM2_ABI, w));
136
152
  const nonceManager = new NonceManager(provider);
137
153
  const needProfitTx = extractProfit && totalProfit > 0n;
138
- // ✅ 计算 payer 需要的 nonce 数量:贿赂(可选) + 买入 + 利润(可选)
139
- const payerNonceCount = 1 + (needBribeTx ? 1 : 0) + (needProfitTx ? 1 : 0);
140
- // ✅ 优化:并行获取 gasPrice、nonces 和构建未签名交易
141
- const [gasPrice, unsignedList, noncesResult] = await Promise.all([
154
+ const [gasPrice, unsignedList] = await Promise.all([
142
155
  getOptimizedGasPrice(provider, getGasPriceConfig(config)),
143
156
  Promise.all(tm2Contracts.map((c, i) => c.buyTokenAMAP.populateTransaction(0n, tokenAddress, wallets[i].address, actualAmountsWei[i], 0n, {
144
- value: actualAmountsWei[i],
157
+ value: actualValueWei[i],
145
158
  }))),
146
- (async () => {
147
- if (payerNonceCount > 1) {
148
- // payer 需要多个连续 nonce
149
- const payerNonces = await nonceManager.getNextNonceBatch(wallets[maxFundsIndex], payerNonceCount);
150
- // 其他钱包各需要 1 个 nonce
151
- const otherWallets = wallets.filter((_, i) => i !== maxFundsIndex);
152
- const otherNonces = otherWallets.length > 0 ? await nonceManager.getNextNoncesForWallets(otherWallets) : [];
153
- // 组装最终的 nonces 数组(保持原顺序)
154
- const nonces = [];
155
- let otherIdx = 0;
156
- let nonceIdx = 0;
157
- const bribeNonce = needBribeTx ? payerNonces[nonceIdx++] : undefined;
158
- for (let i = 0; i < wallets.length; i++) {
159
- if (i === maxFundsIndex) {
160
- nonces.push(payerNonces[nonceIdx++]); // 买入交易
161
- }
162
- else {
163
- nonces.push(otherNonces[otherIdx++]);
164
- }
165
- }
166
- const profitNonce = needProfitTx ? payerNonces[nonceIdx] : undefined;
167
- return { nonces, bribeNonce, profitNonce };
168
- }
169
- else {
170
- // 所有钱包各 1 个 nonce
171
- const nonces = await nonceManager.getNextNoncesForWallets(wallets);
172
- return { nonces, bribeNonce: undefined, profitNonce: undefined };
173
- }
174
- })(),
175
159
  ]);
176
- const { nonces, bribeNonce, profitNonce } = noncesResult;
177
- // ✅ 贿赂交易放在首位(提高 BlockRazor 打包优先级)
178
160
  const signedTxs = [];
179
- if (needBribeTx && bribeNonce !== undefined) {
180
- const bribeTx = await wallets[maxFundsIndex].signTransaction({
161
+ if (needBribeTx) {
162
+ signedTxs.push(await wallets[maxFundsIndex].signTransaction({
181
163
  to: BLOCKRAZOR_BUILDER_EOA,
182
164
  value: bribeAmount,
183
- nonce: bribeNonce,
165
+ nonce: await nonceManager.getNextNonce(wallets[maxFundsIndex]),
184
166
  gasPrice,
185
167
  gasLimit: 21000n,
186
168
  chainId: CHAIN_ID,
187
169
  type: txType,
170
+ }));
171
+ }
172
+ const quoteAddress = payments[0]?.quoteAddress;
173
+ if (erc20Quote && quoteAddress) {
174
+ const approves = await signQuoteApproveTxs({
175
+ wallets,
176
+ quoteAddress,
177
+ spender: tmAddr,
178
+ amounts: actualAmountsWei,
179
+ nonceManager,
180
+ gasPrice,
181
+ chainId: CHAIN_ID,
182
+ txType,
183
+ provider,
188
184
  });
189
- signedTxs.push(bribeTx);
185
+ signedTxs.push(...approves);
190
186
  }
191
- // ✅ 并行签名所有买入交易
187
+ const buyNonces = await nonceManager.getNextNoncesForWallets(wallets);
192
188
  const signedList = await Promise.all(unsignedList.map((unsigned, i) => wallets[i].signTransaction({
193
189
  ...unsigned,
194
190
  from: wallets[i].address,
195
- nonce: nonces[i],
191
+ nonce: buyNonces[i],
196
192
  gasLimit: finalGasLimit,
197
193
  gasPrice,
198
194
  chainId: CHAIN_ID,
199
195
  type: txType,
200
- value: actualAmountsWei[i],
196
+ value: actualValueWei[i],
201
197
  })));
202
198
  signedTxs.push(...signedList);
203
- // ✅ 利润多跳转账(强制 2 跳中转)
204
199
  let profitHopWallets;
205
- if (needProfitTx && profitNonce !== undefined) {
200
+ if (needProfitTx) {
206
201
  const profitHopResult = await buildProfitHopTransactions({
207
202
  provider,
208
203
  payerWallet: wallets[maxFundsIndex],
@@ -212,14 +207,14 @@ export async function fourBatchPrivateBuyMerkle(params) {
212
207
  gasPrice,
213
208
  chainId: CHAIN_ID,
214
209
  txType,
215
- startNonce: profitNonce,
210
+ startNonce: await nonceManager.getNextNonce(wallets[maxFundsIndex]),
216
211
  });
217
212
  signedTxs.push(...profitHopResult.signedTransactions);
218
- profitHopWallets = profitHopResult.hopWallets; // ✅ 收集利润多跳钱包
213
+ profitHopWallets = profitHopResult.hopWallets;
219
214
  }
220
215
  nonceManager.clearTemp();
221
216
  return {
222
217
  signedTransactions: signedTxs,
223
- profitHopWallets, // ✅ 返回利润多跳钱包
218
+ profitHopWallets,
224
219
  };
225
220
  }
@@ -8,4 +8,6 @@
8
8
  * 对外 export 符号与路径保持不变。
9
9
  */
10
10
  export { fourPrivateBuyMerkle, fourBatchPrivateBuyMerkle } from './buy.js';
11
+ export { openFourBatchBuyByBudgetMerkle } from './openfour-buy.js';
12
+ export type { OpenFourBatchBuySignParams } from './openfour-buy.js';
11
13
  export { fourPrivateSellMerkle, fourBatchPrivateSellMerkle } from './sell.js';
@@ -8,4 +8,5 @@
8
8
  * 对外 export 符号与路径保持不变。
9
9
  */
10
10
  export { fourPrivateBuyMerkle, fourBatchPrivateBuyMerkle } from './buy.js';
11
+ export { openFourBatchBuyByBudgetMerkle } from './openfour-buy.js';
11
12
  export { fourPrivateSellMerkle, fourBatchPrivateSellMerkle } from './sell.js';
@@ -0,0 +1,10 @@
1
+ import type { FourBatchPrivateMerkleResult, FourSignConfig } from '../types/index.js';
2
+ export type OpenFourBatchBuySignParams = {
3
+ privateKeys: string[];
4
+ fundsList: string[];
5
+ tokenAddress: string;
6
+ quoteTokenAddress?: string;
7
+ quoteDecimals?: number;
8
+ config: FourSignConfig;
9
+ };
10
+ export declare function openFourBatchBuyByBudgetMerkle(params: OpenFourBatchBuySignParams): Promise<FourBatchPrivateMerkleResult>;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * OpenFour 发币后跟买:buyByBudget
3
+ * - quote = WBNB:msg.value 付 BNB
4
+ * - 其它 ERC20:approve Core + value=0
5
+ */
6
+ import { Contract, Wallet, parseEther, parseUnits } from 'ethers';
7
+ import { getGasLimit } from '../../../core/gas/bundle-gas.js';
8
+ import { NonceManager, getOptimizedGasPrice } from '../../../utils/bundle-helpers.js';
9
+ import { CHAINS } from '../../../core/constants/index.js';
10
+ import { getTxType, getGasPriceConfig } from '../../config-helpers.js';
11
+ import { createSignProvider } from '../../../core/sign-context-helpers.js';
12
+ import { OPENFOUR_CORE_ABI } from '../../../abis/openfour.js';
13
+ import { loadOpenFourAddresses } from '../../../domains/openfour/index.js';
14
+ import { isFourErc20BuyQuote, resolveQuoteDecimals, signQuoteApproveTxs } from './buy-quote.js';
15
+ const CHAIN_ID = CHAINS.BSC.chainId;
16
+ export async function openFourBatchBuyByBudgetMerkle(params) {
17
+ const { privateKeys, fundsList, tokenAddress, config, quoteTokenAddress, quoteDecimals } = params;
18
+ if (privateKeys.length !== fundsList.length) {
19
+ throw new Error('privateKeys and fundsList length mismatch');
20
+ }
21
+ const provider = createSignProvider(config.rpcUrl, CHAIN_ID, 'BSC');
22
+ const wallets = privateKeys.map((k) => new Wallet(k, provider));
23
+ const txType = getTxType(config);
24
+ const gasLimit = getGasLimit(config, 900000);
25
+ const addrs = await loadOpenFourAddresses(provider);
26
+ const erc20Quote = isFourErc20BuyQuote(quoteTokenAddress);
27
+ const decimals = erc20Quote
28
+ ? await resolveQuoteDecimals(provider, { quoteTokenAddress, quoteDecimals })
29
+ : 18;
30
+ const amountsWei = fundsList.map((a) => (erc20Quote ? parseUnits(a, decimals) : parseEther(a)));
31
+ const cores = wallets.map((w) => new Contract(addrs.core, OPENFOUR_CORE_ABI, w));
32
+ const nonceManager = new NonceManager(provider);
33
+ const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
34
+ const signedTxs = [];
35
+ if (erc20Quote && quoteTokenAddress) {
36
+ signedTxs.push(...(await signQuoteApproveTxs({
37
+ wallets,
38
+ quoteAddress: quoteTokenAddress,
39
+ spender: addrs.core,
40
+ amounts: amountsWei,
41
+ nonceManager,
42
+ gasPrice,
43
+ chainId: CHAIN_ID,
44
+ txType,
45
+ provider,
46
+ })));
47
+ }
48
+ const unsignedList = await Promise.all(cores.map((c, i) => c.buyByBudget.populateTransaction(tokenAddress, amountsWei[i], 0n, 0n, '0x', {
49
+ value: erc20Quote ? 0n : amountsWei[i],
50
+ })));
51
+ const nonces = await nonceManager.getNextNoncesForWallets(wallets);
52
+ const buys = await Promise.all(unsignedList.map((unsigned, i) => wallets[i].signTransaction({
53
+ ...unsigned,
54
+ from: wallets[i].address,
55
+ nonce: nonces[i],
56
+ gasLimit,
57
+ gasPrice,
58
+ chainId: CHAIN_ID,
59
+ type: txType,
60
+ value: erc20Quote ? 0n : amountsWei[i],
61
+ })));
62
+ signedTxs.push(...buys);
63
+ nonceManager.clearTemp();
64
+ return { signedTransactions: signedTxs };
65
+ }
@@ -102,6 +102,9 @@ export type FourCreateWithBundleBuyMerkleParams = {
102
102
  webUrl?: string;
103
103
  twitterUrl?: string;
104
104
  telegramUrl?: string;
105
+ githubUrl?: string;
106
+ youtubeUrl?: string;
107
+ deboxUrl?: string;
105
108
  /** ✅ AntiSniperFeeMode(开盘高税模式) */
106
109
  feePlan?: boolean;
107
110
  /** ✅ 税币配置(Tax Token, creatorType=5) */
@@ -128,6 +131,9 @@ export type FourCreateWithBundleBuySignParams = {
128
131
  webUrl?: string;
129
132
  twitterUrl?: string;
130
133
  telegramUrl?: string;
134
+ githubUrl?: string;
135
+ youtubeUrl?: string;
136
+ deboxUrl?: string;
131
137
  /** ✅ AntiSniperFeeMode(开盘高税模式) */
132
138
  feePlan?: boolean;
133
139
  /** ✅ 税币配置(Tax Token, creatorType=5) */
@@ -197,6 +203,9 @@ export type FourPrivateBuySignParams = FourPrivateSignBase & {
197
203
  tokenAddress: string;
198
204
  funds: string;
199
205
  to?: string;
206
+ /** ERC20 计价跟买;不传则按 BNB `msg.value` */
207
+ quoteTokenAddress?: string;
208
+ quoteDecimals?: number;
200
209
  };
201
210
  /** ✅ 私有卖出参数(Merkle 版本 - 完整) */
202
211
  export type FourPrivateSellMerkleParams = FourPrivateMerkleBase & {
@@ -219,12 +228,16 @@ export type FourBatchPrivateBuyMerkleParams = FourPrivateMerkleBase & {
219
228
  privateKeys: string[];
220
229
  fundsList: string[];
221
230
  tokenAddress: string;
231
+ quoteTokenAddress?: string;
232
+ quoteDecimals?: number;
222
233
  };
223
234
  /** ✅ 批量私有购买参数(仅签名版本 - 精简) */
224
235
  export type FourBatchPrivateBuySignParams = FourPrivateSignBase & {
225
236
  privateKeys: string[];
226
237
  fundsList: string[];
227
238
  tokenAddress: string;
239
+ quoteTokenAddress?: string;
240
+ quoteDecimals?: number;
228
241
  };
229
242
  /** ✅ 批量私有卖出参数(Merkle 版本 - 完整) */
230
243
  export type FourBatchPrivateSellMerkleParams = FourPrivateMerkleBase & {
@@ -16,6 +16,7 @@ export declare const BSC_ADDRESSES: {
16
16
  readonly TokenManagerV1: "0xf7F823d0E790219dBf727bDb971837574655fCB0";
17
17
  readonly TokenManagerV2: "0x342399a59943B5815849657Aa0e06D7058D9d5C6";
18
18
  readonly TokenManagerHelper3: "0xF251F83e40a78868FcfA3FA4599Dad6494E46034";
19
+ readonly OpenFourRegistry: "0x912CEf0C3aE9Ab6eB3Ec87cab69371cFb317Ab94";
19
20
  readonly FlapPortal: "0xe2cE6ab80874Fa9Fa2aAE65D277Dd6B8e65C9De0";
20
21
  readonly PancakeV2Router: "0x10ED43C718714eb63d5aA57B78B54704E256024E";
21
22
  readonly PancakeV2Factory: "0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73";
@@ -1,4 +1,5 @@
1
1
  import { type FourRaisedToken, type FourTokenLabel } from '../../domains/four/raised-token.js';
2
+ import type { OpenFourCreateReq, OpenFourTemplate, OpenFourTemplateConfig } from '../../domains/openfour/types.js';
2
3
  export type { FourRaisedToken, FourTokenLabel };
3
4
  export type NetworkCode = 'BSC';
4
5
  export type FourConfig = {
@@ -45,7 +46,9 @@ export type LoginReq = {
45
46
  * - recipientAddress: 接收者地址(不使用时传空字符串 "")
46
47
  * - minSharing: 最低持仓分红门槛(ether 单位,须满足 d × 10^n, n≥5, 1≤d≤9)
47
48
  *
48
- * 约束:burnRate + divideRate + liquidityRate + recipientRate = 100
49
+ * 约束:burnRate + divideRate + liquidityRate + recipientRate
50
+ * + giggleCharityRate + binanceCharityRate = 100
51
+ * 带 giggleCharityRate / binanceCharityRate 即为 TaxToken9(即使都是 0)
49
52
  */
50
53
  export type FourTokenTaxInfo = {
51
54
  feeRate: 1 | 3 | 5 | 10;
@@ -55,6 +58,8 @@ export type FourTokenTaxInfo = {
55
58
  recipientRate: number;
56
59
  recipientAddress: string;
57
60
  minSharing: number;
61
+ giggleCharityRate?: number;
62
+ binanceCharityRate?: number;
58
63
  };
59
64
  export type CreateTokenCustom = {
60
65
  name: string;
@@ -66,6 +71,9 @@ export type CreateTokenCustom = {
66
71
  webUrl?: string;
67
72
  twitterUrl?: string;
68
73
  telegramUrl?: string;
74
+ githubUrl?: string;
75
+ youtubeUrl?: string;
76
+ deboxUrl?: string;
69
77
  preSale: string;
70
78
  onlyMPC?: boolean;
71
79
  feePlan?: boolean;
@@ -142,5 +150,9 @@ export declare class FourClient {
142
150
  error: string;
143
151
  })[]>;
144
152
  getTokenById(id: string | number, accessToken?: string): Promise<FourTokenDetail>;
153
+ searchTokenTemplates(): Promise<OpenFourTemplate[]>;
154
+ listPublishedTokenTemplates(): Promise<OpenFourTemplate[]>;
155
+ getTokenTemplateConfig(templateId: number | string): Promise<OpenFourTemplateConfig[]>;
156
+ createOpenFourToken(accessToken: string, req: OpenFourCreateReq): Promise<CreateTokenResp>;
145
157
  }
146
158
  export declare function buildLoginMessage(nonce: string): string;
@@ -14,6 +14,9 @@ export function buildCreateTokenReq(custom, raisedToken) {
14
14
  webUrl: custom.webUrl,
15
15
  twitterUrl: custom.twitterUrl,
16
16
  telegramUrl: custom.telegramUrl,
17
+ githubUrl: custom.githubUrl,
18
+ youtubeUrl: custom.youtubeUrl,
19
+ deboxUrl: custom.deboxUrl,
17
20
  preSale: custom.preSale,
18
21
  onlyMPC: custom.onlyMPC ?? false,
19
22
  ...(custom.feePlan ? { feePlan: true } : {}),
@@ -339,6 +342,72 @@ export class FourClient {
339
342
  }
340
343
  return (j.data ?? j);
341
344
  }
345
+ async searchTokenTemplates() {
346
+ const urls = [...new Set([this.baseUrl, FOUR_MEME_OFFICIAL_API])];
347
+ let last = [];
348
+ for (const url of urls) {
349
+ try {
350
+ const r = await fetch(`${url}/v1/public/token_template/search`, {
351
+ method: 'POST',
352
+ headers: { 'Content-Type': 'application/json' },
353
+ body: JSON.stringify({ sort: 'LAST' }),
354
+ });
355
+ if (!r.ok)
356
+ continue;
357
+ const j = (await r.json());
358
+ if (j.code !== '0' && j.code !== 0)
359
+ continue;
360
+ const list = Array.isArray(j.data) ? j.data : [];
361
+ if (list.length > 0)
362
+ return list;
363
+ last = list;
364
+ }
365
+ catch {
366
+ continue;
367
+ }
368
+ }
369
+ return last;
370
+ }
371
+ async listPublishedTokenTemplates() {
372
+ const list = await this.searchTokenTemplates();
373
+ return list.filter((t) => String(t.status || '').toUpperCase() === 'PUBLISHED');
374
+ }
375
+ async getTokenTemplateConfig(templateId) {
376
+ const urls = [...new Set([this.baseUrl, FOUR_MEME_OFFICIAL_API])];
377
+ let last = [];
378
+ for (const url of urls) {
379
+ try {
380
+ const r = await fetch(`${url}/v1/public/token_template/config?templateId=${templateId}`);
381
+ if (!r.ok)
382
+ continue;
383
+ const j = (await r.json());
384
+ if (j.code !== '0' && j.code !== 0)
385
+ continue;
386
+ const list = Array.isArray(j.data) ? j.data : [];
387
+ if (list.length > 0)
388
+ return list;
389
+ last = list;
390
+ }
391
+ catch {
392
+ continue;
393
+ }
394
+ }
395
+ return last;
396
+ }
397
+ async createOpenFourToken(accessToken, req) {
398
+ const r = await fetch(`${this.baseUrl}/v1/private/token_template/token/create`, {
399
+ method: 'POST',
400
+ headers: { 'Content-Type': 'application/json', 'meme-web-access': accessToken },
401
+ body: JSON.stringify(req),
402
+ });
403
+ const j = await r.json();
404
+ if (j.code !== '0' && j.code !== 0) {
405
+ throw new Error(`createOpenFourToken failed: ${JSON.stringify(j)}`);
406
+ }
407
+ const data = j.data;
408
+ const first = Array.isArray(data) ? data[0] : data;
409
+ return first;
410
+ }
342
411
  }
343
412
  export function buildLoginMessage(nonce) {
344
413
  return `You are sign in Meme ${nonce}`;
@@ -22,6 +22,8 @@ export declare const ADDRESSES: {
22
22
  readonly TokenManagerV2: "0x342399a59943B5815849657Aa0e06D7058D9d5C6";
23
23
  /** 查询辅助合约(Helper3) */
24
24
  readonly TokenManagerHelper3: "0xF251F83e40a78868FcfA3FA4599Dad6494E46034";
25
+ /** OpenFour Registry(链上发现 Core / Tools) */
26
+ readonly OpenFourRegistry: "0x912CEf0C3aE9Ab6eB3Ec87cab69371cFb317Ab94";
25
27
  readonly FlapPortal: "0xe2cE6ab80874Fa9Fa2aAE65D277Dd6B8e65C9De0";
26
28
  readonly PancakeV2Router: "0x10ED43C718714eb63d5aA57B78B54704E256024E";
27
29
  readonly PancakeV2Factory: "0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73";
@@ -34,6 +34,8 @@ export const ADDRESSES = {
34
34
  TokenManagerV2: '0x342399a59943B5815849657Aa0e06D7058D9d5C6',
35
35
  /** 查询辅助合约(Helper3) */
36
36
  TokenManagerHelper3: '0xF251F83e40a78868FcfA3FA4599Dad6494E46034',
37
+ /** OpenFour Registry(链上发现 Core / Tools) */
38
+ OpenFourRegistry: '0x912CEf0C3aE9Ab6eB3Ec87cab69371cFb317Ab94',
37
39
  // ---------- Flap Portal ----------
38
40
  FlapPortal: '0xe2cE6ab80874Fa9Fa2aAE65D277Dd6B8e65C9De0',
39
41
  // ---------- PancakeSwap 官方合约 ----------
@@ -2,6 +2,9 @@ export type TokenMetaInput = {
2
2
  website?: string | null;
3
3
  twitter?: string | null;
4
4
  telegram?: string | null;
5
+ github?: string | null;
6
+ youtube?: string | null;
7
+ debox?: string | null;
5
8
  description: string;
6
9
  creator: string;
7
10
  };
@@ -1,4 +1,23 @@
1
1
  import { FLAP_IPFS_API_URL } from './constants.js';
2
+ function emptyToNull(value) {
3
+ if (value == null)
4
+ return null;
5
+ const trimmed = String(value).trim();
6
+ return trimmed === '' ? null : trimmed;
7
+ }
8
+ function toOfficialIpfsMeta(meta) {
9
+ return {
10
+ creator: meta.creator,
11
+ description: meta.description,
12
+ website: emptyToNull(meta.website),
13
+ telegram: emptyToNull(meta.telegram),
14
+ twitter: emptyToNull(meta.twitter),
15
+ github: emptyToNull(meta.github),
16
+ youtube: emptyToNull(meta.youtube),
17
+ debox: emptyToNull(meta.debox),
18
+ buy: null,
19
+ };
20
+ }
2
21
  /**
3
22
  * 上传代币元数据到 IPFS
4
23
  * @param file 图片文件(File 对象)
@@ -24,7 +43,7 @@ export async function uploadTokenMeta(file, meta, apiUrl) {
24
43
  }
25
44
  const form = new FormData();
26
45
  const MUTATION_CREATE = `mutation Create($file: Upload!, $meta: MetadataInput!) { create(file: $file, meta: $meta) }`;
27
- form.append('operations', JSON.stringify({ query: MUTATION_CREATE, variables: { file: null, meta } }));
46
+ form.append('operations', JSON.stringify({ query: MUTATION_CREATE, variables: { file: null, meta: toOfficialIpfsMeta(meta) } }));
28
47
  form.append('map', JSON.stringify({ '0': ['variables.file'] }));
29
48
  form.append('0', file);
30
49
  const res = await fetch(endpoint, { method: 'POST', body: form });
@@ -19,6 +19,9 @@ export type CreateTokenFlowInput = {
19
19
  webUrl?: string;
20
20
  twitterUrl?: string;
21
21
  telegramUrl?: string;
22
+ githubUrl?: string;
23
+ youtubeUrl?: string;
24
+ deboxUrl?: string;
22
25
  quoteSymbol?: string;
23
26
  };
24
27
  };
@@ -71,6 +71,9 @@ export async function createTokenFlow(input) {
71
71
  webUrl: input.payload.webUrl,
72
72
  twitterUrl: input.payload.twitterUrl,
73
73
  telegramUrl: input.payload.telegramUrl,
74
+ githubUrl: input.payload.githubUrl,
75
+ youtubeUrl: input.payload.youtubeUrl,
76
+ deboxUrl: input.payload.deboxUrl,
74
77
  preSale: input.payload.preSale,
75
78
  onlyMPC: input.payload.onlyMPC ?? false,
76
79
  lpTradingFee: 0.0025,
@@ -70,6 +70,9 @@ export async function createTokenWithBundleBuy(params) {
70
70
  webUrl: tokenInfo.webUrl,
71
71
  twitterUrl: tokenInfo.twitterUrl,
72
72
  telegramUrl: tokenInfo.telegramUrl,
73
+ githubUrl: tokenInfo.githubUrl,
74
+ youtubeUrl: tokenInfo.youtubeUrl,
75
+ deboxUrl: tokenInfo.deboxUrl,
73
76
  preSale: tokenInfo.preSale || '0',
74
77
  onlyMPC: false,
75
78
  lpTradingFee: 0.0025,