four-flap-meme-sdk 4.0.2 → 4.0.4
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/abis/openfour.d.ts +94 -0
- package/dist/abis/openfour.js +63 -0
- package/dist/bundle/four-meme/core/constants.js +1 -0
- package/dist/bundle/four-meme/core/create-openfour.d.ts +23 -0
- package/dist/bundle/four-meme/core/create-openfour.js +118 -0
- package/dist/bundle/four-meme/core/create-token-quote.d.ts +24 -0
- package/dist/bundle/four-meme/core/create-token-quote.js +53 -0
- package/dist/bundle/four-meme/core/create-token.js +28 -39
- package/dist/bundle/four-meme/core/index.d.ts +2 -0
- package/dist/bundle/four-meme/core/index.js +1 -0
- package/dist/bundle/four-meme/private/buy-quote.d.ts +35 -0
- package/dist/bundle/four-meme/private/buy-quote.js +70 -0
- package/dist/bundle/four-meme/private/buy.js +82 -87
- package/dist/bundle/four-meme/private/index.d.ts +2 -0
- package/dist/bundle/four-meme/private/index.js +1 -0
- package/dist/bundle/four-meme/private/openfour-buy.d.ts +10 -0
- package/dist/bundle/four-meme/private/openfour-buy.js +65 -0
- package/dist/bundle/four-meme/types/index.d.ts +13 -0
- package/dist/chains/bsc/constants.d.ts +1 -0
- package/dist/core/clients/four.d.ts +25 -17
- package/dist/core/clients/four.js +128 -3
- package/dist/core/clients/index.d.ts +1 -1
- package/dist/core/clients/index.js +1 -1
- package/dist/core/constants/addresses.d.ts +2 -0
- package/dist/core/constants/addresses.js +2 -0
- package/dist/domains/flows/create.d.ts +1 -0
- package/dist/domains/flows/create.js +13 -12
- package/dist/domains/four/contracts/tm-bundle-create.js +42 -11
- package/dist/domains/four/contracts/tm-bundle-helpers.d.ts +6 -0
- package/dist/domains/four/index.d.ts +1 -0
- package/dist/domains/four/index.js +1 -0
- package/dist/domains/four/raised-token.d.ts +38 -0
- package/dist/domains/four/raised-token.js +75 -0
- package/dist/domains/four/raised-token.test.d.ts +1 -0
- package/dist/domains/four/raised-token.test.js +43 -0
- package/dist/domains/four/tax-token.d.ts +2 -0
- package/dist/domains/four/tax-token.js +13 -4
- package/dist/domains/four/tax-token.test.d.ts +1 -0
- package/dist/domains/four/tax-token.test.js +89 -0
- package/dist/domains/openfour/create-arg.d.ts +13 -0
- package/dist/domains/openfour/create-arg.js +78 -0
- package/dist/domains/openfour/index.d.ts +4 -0
- package/dist/domains/openfour/index.js +3 -0
- package/dist/domains/openfour/registry.d.ts +4 -0
- package/dist/domains/openfour/registry.js +44 -0
- package/dist/domains/openfour/schema.d.ts +20 -0
- package/dist/domains/openfour/schema.js +88 -0
- package/dist/domains/openfour/types.d.ts +76 -0
- package/dist/domains/openfour/types.js +1 -0
- package/dist/exports/root-bundle-and-tooling.d.ts +4 -2
- package/dist/exports/root-bundle-and-tooling.js +2 -2
- package/dist/exports/root-foundations.d.ts +3 -1
- package/dist/exports/root-foundations.js +3 -1
- package/package.json +1 -1
|
@@ -1,4 +1,34 @@
|
|
|
1
1
|
import { getErrorMessageFromUnknown } from '../index.js';
|
|
2
|
+
import { FOUR_BNB_RAISED_TOKEN_FALLBACK, parseRaisedTokens, pickRaisedToken, publishedRaisedTokens, } from '../../domains/four/raised-token.js';
|
|
3
|
+
export function buildCreateTokenReq(custom, raisedToken) {
|
|
4
|
+
const totalSupply = Number(raisedToken.totalAmount);
|
|
5
|
+
const raisedAmount = Number(raisedToken.totalBAmount);
|
|
6
|
+
const saleRate = Number(raisedToken.saleRate);
|
|
7
|
+
return {
|
|
8
|
+
name: custom.name,
|
|
9
|
+
shortName: custom.shortName,
|
|
10
|
+
desc: custom.desc,
|
|
11
|
+
imgUrl: custom.imgUrl,
|
|
12
|
+
launchTime: custom.launchTime,
|
|
13
|
+
label: custom.label,
|
|
14
|
+
webUrl: custom.webUrl,
|
|
15
|
+
twitterUrl: custom.twitterUrl,
|
|
16
|
+
telegramUrl: custom.telegramUrl,
|
|
17
|
+
preSale: custom.preSale,
|
|
18
|
+
onlyMPC: custom.onlyMPC ?? false,
|
|
19
|
+
...(custom.feePlan ? { feePlan: true } : {}),
|
|
20
|
+
...(custom.tokenTaxInfo ? { tokenTaxInfo: custom.tokenTaxInfo } : {}),
|
|
21
|
+
lpTradingFee: 0.0025,
|
|
22
|
+
symbol: raisedToken.symbol,
|
|
23
|
+
totalSupply: Number.isFinite(totalSupply) && totalSupply > 0 ? totalSupply : 1_000_000_000,
|
|
24
|
+
raisedAmount: Number.isFinite(raisedAmount) && raisedAmount > 0 ? raisedAmount : 18,
|
|
25
|
+
saleRate: Number.isFinite(saleRate) && saleRate > 0 ? saleRate : 0.8,
|
|
26
|
+
reserveRate: 0,
|
|
27
|
+
funGroup: false,
|
|
28
|
+
clickFun: false,
|
|
29
|
+
raisedToken,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
2
32
|
// ============================================================================
|
|
3
33
|
// 图片上传缓存(按内容哈希去重,同一张图片只上传一次)
|
|
4
34
|
// ✅ 使用 localStorage 持久化,刷新页面 / 重启浏览器后缓存仍然有效
|
|
@@ -239,10 +269,12 @@ export class FourClient {
|
|
|
239
269
|
return `image-${Date.now()}.${ext}`;
|
|
240
270
|
}
|
|
241
271
|
async createToken(accessToken, req) {
|
|
272
|
+
const raisedToken = req.raisedToken ?? (await this.resolveRaisedToken(req.symbol));
|
|
273
|
+
const body = buildCreateTokenReq(req, raisedToken);
|
|
242
274
|
const r = await fetch(`${this.baseUrl}/v1/private/token/create`, {
|
|
243
275
|
method: 'POST',
|
|
244
276
|
headers: { 'Content-Type': 'application/json', 'meme-web-access': accessToken },
|
|
245
|
-
body: JSON.stringify(
|
|
277
|
+
body: JSON.stringify(body),
|
|
246
278
|
});
|
|
247
279
|
const j = await r.json();
|
|
248
280
|
if (j.code !== '0' && j.code !== 0) {
|
|
@@ -251,8 +283,35 @@ export class FourClient {
|
|
|
251
283
|
return j.data;
|
|
252
284
|
}
|
|
253
285
|
async getPublicConfig() {
|
|
254
|
-
const
|
|
255
|
-
|
|
286
|
+
const urls = [...new Set([this.baseUrl, FOUR_MEME_OFFICIAL_API])];
|
|
287
|
+
let last;
|
|
288
|
+
for (const url of urls) {
|
|
289
|
+
try {
|
|
290
|
+
const r = await fetch(`${url}/v1/public/config`);
|
|
291
|
+
if (!r.ok)
|
|
292
|
+
continue;
|
|
293
|
+
const j = (await r.json());
|
|
294
|
+
if (parseRaisedTokens(j).length > 0)
|
|
295
|
+
return j;
|
|
296
|
+
last = j;
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return last ?? {};
|
|
303
|
+
}
|
|
304
|
+
async listPublishedRaisedTokens() {
|
|
305
|
+
return publishedRaisedTokens(parseRaisedTokens(await this.getPublicConfig()));
|
|
306
|
+
}
|
|
307
|
+
async resolveRaisedToken(symbol) {
|
|
308
|
+
try {
|
|
309
|
+
const tokens = await this.listPublishedRaisedTokens();
|
|
310
|
+
return pickRaisedToken(tokens, symbol) ?? pickRaisedToken(tokens, 'BNB') ?? FOUR_BNB_RAISED_TOKEN_FALLBACK;
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
return pickRaisedToken([FOUR_BNB_RAISED_TOKEN_FALLBACK], symbol) ?? FOUR_BNB_RAISED_TOKEN_FALLBACK;
|
|
314
|
+
}
|
|
256
315
|
}
|
|
257
316
|
async getTokenByAddress(address, accessToken) {
|
|
258
317
|
const r = await fetch(`${this.baseUrl}/v1/private/token/get?address=${address}`, {
|
|
@@ -280,6 +339,72 @@ export class FourClient {
|
|
|
280
339
|
}
|
|
281
340
|
return (j.data ?? j);
|
|
282
341
|
}
|
|
342
|
+
async searchTokenTemplates() {
|
|
343
|
+
const urls = [...new Set([this.baseUrl, FOUR_MEME_OFFICIAL_API])];
|
|
344
|
+
let last = [];
|
|
345
|
+
for (const url of urls) {
|
|
346
|
+
try {
|
|
347
|
+
const r = await fetch(`${url}/v1/public/token_template/search`, {
|
|
348
|
+
method: 'POST',
|
|
349
|
+
headers: { 'Content-Type': 'application/json' },
|
|
350
|
+
body: JSON.stringify({ sort: 'LAST' }),
|
|
351
|
+
});
|
|
352
|
+
if (!r.ok)
|
|
353
|
+
continue;
|
|
354
|
+
const j = (await r.json());
|
|
355
|
+
if (j.code !== '0' && j.code !== 0)
|
|
356
|
+
continue;
|
|
357
|
+
const list = Array.isArray(j.data) ? j.data : [];
|
|
358
|
+
if (list.length > 0)
|
|
359
|
+
return list;
|
|
360
|
+
last = list;
|
|
361
|
+
}
|
|
362
|
+
catch {
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return last;
|
|
367
|
+
}
|
|
368
|
+
async listPublishedTokenTemplates() {
|
|
369
|
+
const list = await this.searchTokenTemplates();
|
|
370
|
+
return list.filter((t) => String(t.status || '').toUpperCase() === 'PUBLISHED');
|
|
371
|
+
}
|
|
372
|
+
async getTokenTemplateConfig(templateId) {
|
|
373
|
+
const urls = [...new Set([this.baseUrl, FOUR_MEME_OFFICIAL_API])];
|
|
374
|
+
let last = [];
|
|
375
|
+
for (const url of urls) {
|
|
376
|
+
try {
|
|
377
|
+
const r = await fetch(`${url}/v1/public/token_template/config?templateId=${templateId}`);
|
|
378
|
+
if (!r.ok)
|
|
379
|
+
continue;
|
|
380
|
+
const j = (await r.json());
|
|
381
|
+
if (j.code !== '0' && j.code !== 0)
|
|
382
|
+
continue;
|
|
383
|
+
const list = Array.isArray(j.data) ? j.data : [];
|
|
384
|
+
if (list.length > 0)
|
|
385
|
+
return list;
|
|
386
|
+
last = list;
|
|
387
|
+
}
|
|
388
|
+
catch {
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return last;
|
|
393
|
+
}
|
|
394
|
+
async createOpenFourToken(accessToken, req) {
|
|
395
|
+
const r = await fetch(`${this.baseUrl}/v1/private/token_template/token/create`, {
|
|
396
|
+
method: 'POST',
|
|
397
|
+
headers: { 'Content-Type': 'application/json', 'meme-web-access': accessToken },
|
|
398
|
+
body: JSON.stringify(req),
|
|
399
|
+
});
|
|
400
|
+
const j = await r.json();
|
|
401
|
+
if (j.code !== '0' && j.code !== 0) {
|
|
402
|
+
throw new Error(`createOpenFourToken failed: ${JSON.stringify(j)}`);
|
|
403
|
+
}
|
|
404
|
+
const data = j.data;
|
|
405
|
+
const first = Array.isArray(data) ? data[0] : data;
|
|
406
|
+
return first;
|
|
407
|
+
}
|
|
283
408
|
}
|
|
284
409
|
export function buildLoginMessage(nonce) {
|
|
285
410
|
return `You are sign in Meme ${nonce}`;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* HTTP / Bundle 客户端统一出口(不替代根包的扁平别名,仅供子路径导入)
|
|
3
3
|
*/
|
|
4
|
-
export { FourClient, buildLoginMessage, type FourConfig, type GenerateNonceReq, type LoginReq, type CreateTokenReq, type CreateTokenResp, type FourTokenTaxInfo, } from './four.js';
|
|
4
|
+
export { FourClient, buildLoginMessage, buildCreateTokenReq, type FourConfig, type GenerateNonceReq, type LoginReq, type CreateTokenReq, type CreateTokenResp, type FourTokenTaxInfo, type FourRaisedToken, type FourTokenLabel, } from './four.js';
|
|
5
5
|
export { Club48Client, sendBatchPrivateTransactions, sendBackrunBundle, type BundleParams, type BundleStatus, type Club48Config, } from './club48.js';
|
|
6
6
|
export { MerkleClient, createMerkleClient, type MerkleConfig, type BundleParams as MerkleBundleParams, type SendBundleOptions, type BundleResult, type TransactionResult, } from './merkle.js';
|
|
7
7
|
export { BlockRazorClient, createBlockRazorClient, BLOCKRAZOR_BUILDER_EOA, type BlockRazorConfig, type BlockRazorBundleParams, type SendBundleOptions as BlockRazorSendBundleOptions, type BundleResult as BlockRazorBundleResult, type TransactionResult as BlockRazorTransactionResult, type IncentiveTransactionParams, } from './blockrazor.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* HTTP / Bundle 客户端统一出口(不替代根包的扁平别名,仅供子路径导入)
|
|
3
3
|
*/
|
|
4
|
-
export { FourClient, buildLoginMessage, } from './four.js';
|
|
4
|
+
export { FourClient, buildLoginMessage, buildCreateTokenReq, } from './four.js';
|
|
5
5
|
export { Club48Client, sendBatchPrivateTransactions, sendBackrunBundle, } from './club48.js';
|
|
6
6
|
export { MerkleClient, createMerkleClient, } from './merkle.js';
|
|
7
7
|
export { BlockRazorClient, createBlockRazorClient, BLOCKRAZOR_BUILDER_EOA, } from './blockrazor.js';
|
|
@@ -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 官方合约 ----------
|
|
@@ -3,6 +3,7 @@ import { createTokenOnChain } from '../four/contracts/tm.js';
|
|
|
3
3
|
import { Wallet, parseEther } from 'ethers';
|
|
4
4
|
import { readFileSync } from 'fs';
|
|
5
5
|
import { basename } from 'path';
|
|
6
|
+
import { computeFourCreateValueWei } from '../four/raised-token.js';
|
|
6
7
|
/**
|
|
7
8
|
* 根据文件扩展名获取 MIME 类型
|
|
8
9
|
*/
|
|
@@ -59,8 +60,8 @@ export async function createTokenFlow(input) {
|
|
|
59
60
|
imgUrl = await four.uploadImage(accessToken, imageBlob, filename);
|
|
60
61
|
}
|
|
61
62
|
// 4) 创建参数(REST)
|
|
63
|
+
const raisedToken = await four.resolveRaisedToken(input.payload.quoteSymbol);
|
|
62
64
|
const createResp = await four.createToken(accessToken, {
|
|
63
|
-
// 可自定义参数
|
|
64
65
|
name: input.payload.name,
|
|
65
66
|
shortName: input.payload.shortName,
|
|
66
67
|
desc: input.payload.desc,
|
|
@@ -71,23 +72,23 @@ export async function createTokenFlow(input) {
|
|
|
71
72
|
twitterUrl: input.payload.twitterUrl,
|
|
72
73
|
telegramUrl: input.payload.telegramUrl,
|
|
73
74
|
preSale: input.payload.preSale,
|
|
74
|
-
onlyMPC: input.payload.onlyMPC ?? false,
|
|
75
|
-
// 固定参数
|
|
75
|
+
onlyMPC: input.payload.onlyMPC ?? false,
|
|
76
76
|
lpTradingFee: 0.0025,
|
|
77
|
-
symbol:
|
|
77
|
+
symbol: raisedToken.symbol,
|
|
78
78
|
totalSupply: 1000000000,
|
|
79
|
-
raisedAmount:
|
|
80
|
-
saleRate: 0.8,
|
|
79
|
+
raisedAmount: Number(raisedToken.totalBAmount) || 18,
|
|
80
|
+
saleRate: Number(raisedToken.saleRate) || 0.8,
|
|
81
81
|
reserveRate: 0,
|
|
82
82
|
funGroup: false,
|
|
83
83
|
clickFun: false,
|
|
84
|
+
raisedToken,
|
|
85
|
+
});
|
|
86
|
+
const launchFeeWei = parseEther('0.01');
|
|
87
|
+
const valueWei = computeFourCreateValueWei({
|
|
88
|
+
raisedToken,
|
|
89
|
+
preSale: input.payload.preSale,
|
|
90
|
+
launchFeeWei,
|
|
84
91
|
});
|
|
85
|
-
// 5) 链上创建
|
|
86
|
-
// 根据文档:b0Amount(初始金额,默认 8 BNB)+ preSale(预购金额)
|
|
87
|
-
const b0AmountStr = input.b0Amount ?? '8'; // 用户可自定义,默认为 8 BNB
|
|
88
|
-
const b0Amount = parseEther(b0AmountStr);
|
|
89
|
-
const preSaleAmount = input.payload.preSale !== '0' ? parseEther(input.payload.preSale) : 0n;
|
|
90
|
-
const valueWei = b0Amount + preSaleAmount;
|
|
91
92
|
const { receipt, tokenAddress } = await createTokenOnChain({
|
|
92
93
|
chain: input.networkCode,
|
|
93
94
|
rpcUrl: input.rpcUrl,
|
|
@@ -8,6 +8,8 @@ import { GAS_LIMITS, getProfitRateBps, getProfitRecipient } from '../../../core/
|
|
|
8
8
|
import { buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../../../utils/bundle-helpers.js';
|
|
9
9
|
import { FourClient, buildLoginMessage } from '../../../core/clients/four.js';
|
|
10
10
|
import { consoleSdkLogger, getErrorMessageFromUnknown } from '../../../core/index.js';
|
|
11
|
+
import { ERC20_ABI } from '../../../abis/common.js';
|
|
12
|
+
import { computeFourCreateValueWei, isNativeRaisedToken } from '../raised-token.js';
|
|
11
13
|
import { TM2_ABI, getErrorMessage, waitForBundleWithProvider, } from './tm-bundle-helpers.js';
|
|
12
14
|
const sdkLogger = consoleSdkLogger;
|
|
13
15
|
export async function createTokenWithBundleBuy(params) {
|
|
@@ -56,8 +58,9 @@ export async function createTokenWithBundleBuy(params) {
|
|
|
56
58
|
throw new Error(getErrorMessage('IMAGE_REQUIRED'));
|
|
57
59
|
}
|
|
58
60
|
// 3. 获取创建参数
|
|
61
|
+
const raisedToken = await fourClient.resolveRaisedToken(tokenInfo.quoteSymbol);
|
|
62
|
+
const createRaised = tokenInfo.raisedToken ?? raisedToken;
|
|
59
63
|
const createResp = await fourClient.createToken(accessToken, {
|
|
60
|
-
// 可自定义参数
|
|
61
64
|
name: tokenInfo.name,
|
|
62
65
|
shortName: tokenInfo.symbol,
|
|
63
66
|
desc: tokenInfo.description,
|
|
@@ -69,15 +72,15 @@ export async function createTokenWithBundleBuy(params) {
|
|
|
69
72
|
telegramUrl: tokenInfo.telegramUrl,
|
|
70
73
|
preSale: tokenInfo.preSale || '0',
|
|
71
74
|
onlyMPC: false,
|
|
72
|
-
// 固定参数
|
|
73
75
|
lpTradingFee: 0.0025,
|
|
74
|
-
symbol:
|
|
76
|
+
symbol: createRaised.symbol,
|
|
75
77
|
totalSupply: 1000000000,
|
|
76
|
-
raisedAmount:
|
|
77
|
-
saleRate: 0.8,
|
|
78
|
+
raisedAmount: Number(createRaised.totalBAmount) || 18,
|
|
79
|
+
saleRate: Number(createRaised.saleRate) || 0.8,
|
|
78
80
|
reserveRate: 0,
|
|
79
81
|
funGroup: false,
|
|
80
82
|
clickFun: false,
|
|
83
|
+
raisedToken: createRaised,
|
|
81
84
|
});
|
|
82
85
|
// 4. 构建交易
|
|
83
86
|
const tmAddr = ADDRESSES.BSC.TokenManagerOriginal;
|
|
@@ -107,13 +110,41 @@ export async function createTokenWithBundleBuy(params) {
|
|
|
107
110
|
};
|
|
108
111
|
// four.meme 只支持 BNB,固定使用 0 地址
|
|
109
112
|
const tokenAddress = ZERO_ADDRESS;
|
|
110
|
-
// 4.1 创建交易
|
|
111
|
-
// 根据文档:b0Amount(初始金额,默认 8 BNB)+ preSale(预购金额)
|
|
112
|
-
const b0AmountStr = params.b0Amount ?? '8';
|
|
113
|
-
const b0AmountWei = ethers.parseEther(b0AmountStr);
|
|
114
|
-
const preSaleWei = tokenInfo.preSale ? ethers.parseEther(tokenInfo.preSale) : 0n;
|
|
115
|
-
const valueWei = b0AmountWei + preSaleWei;
|
|
116
113
|
const tm2 = new ethers.Contract(tmAddr, TM2_ABI, devWallet);
|
|
114
|
+
let launchFeeWei = ethers.parseEther('0.01');
|
|
115
|
+
try {
|
|
116
|
+
const fee = await tm2._launchFee();
|
|
117
|
+
if (typeof fee === 'bigint' && fee > 0n)
|
|
118
|
+
launchFeeWei = fee;
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// TokenManager2 JSON ABI 可能没有 _launchFee
|
|
122
|
+
}
|
|
123
|
+
const valueWei = computeFourCreateValueWei({
|
|
124
|
+
raisedToken: createRaised,
|
|
125
|
+
preSale: tokenInfo.preSale || '0',
|
|
126
|
+
launchFeeWei,
|
|
127
|
+
});
|
|
128
|
+
const preSaleStr = tokenInfo.preSale || '0';
|
|
129
|
+
if (!isNativeRaisedToken(createRaised) && preSaleStr !== '0') {
|
|
130
|
+
const quote = new ethers.Contract(createRaised.symbolAddress, ERC20_ABI, devWallet);
|
|
131
|
+
const decimals = Number(await quote.decimals());
|
|
132
|
+
const amount = ethers.parseUnits(preSaleStr, decimals);
|
|
133
|
+
const allowance = await quote.allowance(devWallet.address, tmAddr);
|
|
134
|
+
if (allowance < amount) {
|
|
135
|
+
const approveUnsigned = await quote.approve.populateTransaction(tmAddr, amount);
|
|
136
|
+
const signedApprove = await devWallet.signTransaction({
|
|
137
|
+
...approveUnsigned,
|
|
138
|
+
from: devWallet.address,
|
|
139
|
+
nonce: await getNextNonce(devWallet),
|
|
140
|
+
gasLimit: 80000n,
|
|
141
|
+
gasPrice,
|
|
142
|
+
chainId: CHAINS.BSC.chainId,
|
|
143
|
+
type: 0,
|
|
144
|
+
});
|
|
145
|
+
signedTxs.push(signedApprove);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
117
148
|
const createTxUnsigned = await tm2.createToken.populateTransaction(createResp.createArg, createResp.signature, {
|
|
118
149
|
value: valueWei,
|
|
119
150
|
});
|
|
@@ -2,6 +2,8 @@ import { JsonRpcProvider } from 'ethers';
|
|
|
2
2
|
import type { InterfaceAbi, TransactionRequest } from 'ethers';
|
|
3
3
|
import type { SoulPointSignatureMode, VNormalizationMode } from '../../../core/clients/club48.js';
|
|
4
4
|
import { BundleStatus } from '../../../core/clients/club48.js';
|
|
5
|
+
import type { FourTokenTaxInfo } from '../../../core/clients/four.js';
|
|
6
|
+
import type { FourRaisedToken } from '../raised-token.js';
|
|
5
7
|
import type { GeneratedWallet } from '../../../utils/wallet.js';
|
|
6
8
|
export declare const TM2_ABI: InterfaceAbi;
|
|
7
9
|
/**
|
|
@@ -67,6 +69,10 @@ export type FourCreateWithBundleBuyParams = {
|
|
|
67
69
|
webUrl?: string;
|
|
68
70
|
twitterUrl?: string;
|
|
69
71
|
telegramUrl?: string;
|
|
72
|
+
tokenTaxInfo?: FourTokenTaxInfo;
|
|
73
|
+
feePlan?: boolean;
|
|
74
|
+
quoteSymbol?: string;
|
|
75
|
+
raisedToken?: FourRaisedToken;
|
|
70
76
|
};
|
|
71
77
|
config: FourBundleConfig;
|
|
72
78
|
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export declare const FOUR_TOKEN_LABELS: readonly ["Meme", "AI", "Defi", "Games", "Infra", "De-Sci", "Social", "Depin", "Charity", "Others"];
|
|
2
|
+
export type FourTokenLabel = (typeof FOUR_TOKEN_LABELS)[number];
|
|
3
|
+
export type FourRaisedToken = {
|
|
4
|
+
symbol: string;
|
|
5
|
+
nativeSymbol: string;
|
|
6
|
+
symbolAddress: string;
|
|
7
|
+
deployCost: string;
|
|
8
|
+
buyFee: string;
|
|
9
|
+
sellFee: string;
|
|
10
|
+
minTradeFee: string;
|
|
11
|
+
b0Amount: string;
|
|
12
|
+
totalBAmount: string;
|
|
13
|
+
totalAmount: string;
|
|
14
|
+
logoUrl: string;
|
|
15
|
+
tradeLevel: string[];
|
|
16
|
+
status: string;
|
|
17
|
+
buyTokenLink: string;
|
|
18
|
+
reservedNumber: number;
|
|
19
|
+
saleRate: string;
|
|
20
|
+
networkCode: string;
|
|
21
|
+
platform: string;
|
|
22
|
+
isRwa?: boolean;
|
|
23
|
+
};
|
|
24
|
+
/** 官网当前 PUBLISH 的 BNB 档,仅在拉配置失败时兜底(字段与 /public/config 一致) */
|
|
25
|
+
export declare const FOUR_BNB_RAISED_TOKEN_FALLBACK: FourRaisedToken;
|
|
26
|
+
export declare function parseRaisedTokens(raw: unknown): FourRaisedToken[];
|
|
27
|
+
export declare function publishedRaisedTokens(tokens: FourRaisedToken[]): FourRaisedToken[];
|
|
28
|
+
export declare function pickRaisedToken(tokens: FourRaisedToken[], symbol?: string): FourRaisedToken | undefined;
|
|
29
|
+
export declare function isNativeRaisedToken(token: FourRaisedToken): boolean;
|
|
30
|
+
/** buyFee 如 "0.01" 表示 1%,换算成 TokenManager 的 万分比 */
|
|
31
|
+
export declare function raisedTokenTradingFeeBps(token: FourRaisedToken): bigint;
|
|
32
|
+
/** TokenManager2.createToken 的 msg.value:ERC20 计价只付 launch fee;BNB 预购再加 preSale + 交易费 */
|
|
33
|
+
export declare function computeFourCreateValueWei(params: {
|
|
34
|
+
raisedToken: FourRaisedToken;
|
|
35
|
+
preSale: string;
|
|
36
|
+
launchFeeWei: bigint;
|
|
37
|
+
tradingFeeRateBps?: bigint;
|
|
38
|
+
}): bigint;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Four.meme 发币计价币(raisedToken)
|
|
3
|
+
* 来源:GET /v1/public/config,官网 create-token 只展示 status=PUBLISH
|
|
4
|
+
*/
|
|
5
|
+
import { parseEther } from 'ethers';
|
|
6
|
+
export const FOUR_TOKEN_LABELS = [
|
|
7
|
+
'Meme',
|
|
8
|
+
'AI',
|
|
9
|
+
'Defi',
|
|
10
|
+
'Games',
|
|
11
|
+
'Infra',
|
|
12
|
+
'De-Sci',
|
|
13
|
+
'Social',
|
|
14
|
+
'Depin',
|
|
15
|
+
'Charity',
|
|
16
|
+
'Others',
|
|
17
|
+
];
|
|
18
|
+
/** 官网当前 PUBLISH 的 BNB 档,仅在拉配置失败时兜底(字段与 /public/config 一致) */
|
|
19
|
+
export const FOUR_BNB_RAISED_TOKEN_FALLBACK = {
|
|
20
|
+
symbol: 'BNB',
|
|
21
|
+
nativeSymbol: 'BNB',
|
|
22
|
+
symbolAddress: '0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c',
|
|
23
|
+
deployCost: '0',
|
|
24
|
+
buyFee: '0.01',
|
|
25
|
+
sellFee: '0.01',
|
|
26
|
+
minTradeFee: '0',
|
|
27
|
+
b0Amount: '8',
|
|
28
|
+
totalBAmount: '18',
|
|
29
|
+
totalAmount: '1000000000',
|
|
30
|
+
logoUrl: '',
|
|
31
|
+
tradeLevel: ['0.1', '0.5', '1'],
|
|
32
|
+
status: 'PUBLISH',
|
|
33
|
+
buyTokenLink: 'https://pancakeswap.finance/swap',
|
|
34
|
+
reservedNumber: 10,
|
|
35
|
+
saleRate: '0.8',
|
|
36
|
+
networkCode: 'BSC',
|
|
37
|
+
platform: 'MEME',
|
|
38
|
+
isRwa: false,
|
|
39
|
+
};
|
|
40
|
+
export function parseRaisedTokens(raw) {
|
|
41
|
+
if (!raw || typeof raw !== 'object')
|
|
42
|
+
return [];
|
|
43
|
+
const data = 'data' in raw ? raw.data : raw;
|
|
44
|
+
if (!Array.isArray(data))
|
|
45
|
+
return [];
|
|
46
|
+
return data.filter((item) => !!item && typeof item === 'object' && typeof item.symbol === 'string');
|
|
47
|
+
}
|
|
48
|
+
export function publishedRaisedTokens(tokens) {
|
|
49
|
+
return tokens.filter((t) => String(t.status || '').toUpperCase() === 'PUBLISH');
|
|
50
|
+
}
|
|
51
|
+
export function pickRaisedToken(tokens, symbol) {
|
|
52
|
+
const key = String(symbol || 'BNB').toUpperCase();
|
|
53
|
+
return tokens.find((t) => t.symbol.toUpperCase() === key);
|
|
54
|
+
}
|
|
55
|
+
export function isNativeRaisedToken(token) {
|
|
56
|
+
return String(token.symbol || '').toUpperCase() === 'BNB';
|
|
57
|
+
}
|
|
58
|
+
/** buyFee 如 "0.01" 表示 1%,换算成 TokenManager 的 万分比 */
|
|
59
|
+
export function raisedTokenTradingFeeBps(token) {
|
|
60
|
+
const fee = Number(token.buyFee);
|
|
61
|
+
if (!Number.isFinite(fee) || fee <= 0)
|
|
62
|
+
return 100n;
|
|
63
|
+
return BigInt(Math.round(fee * 10000));
|
|
64
|
+
}
|
|
65
|
+
/** TokenManager2.createToken 的 msg.value:ERC20 计价只付 launch fee;BNB 预购再加 preSale + 交易费 */
|
|
66
|
+
export function computeFourCreateValueWei(params) {
|
|
67
|
+
const preSale = String(params.preSale || '').trim() || '0';
|
|
68
|
+
if (!isNativeRaisedToken(params.raisedToken) || preSale === '0') {
|
|
69
|
+
return params.launchFeeWei;
|
|
70
|
+
}
|
|
71
|
+
const preSaleWei = parseEther(preSale);
|
|
72
|
+
const rate = params.tradingFeeRateBps ?? raisedTokenTradingFeeBps(params.raisedToken);
|
|
73
|
+
const tradingFee = (preSaleWei * rate) / 10000n;
|
|
74
|
+
return params.launchFeeWei + preSaleWei + tradingFee;
|
|
75
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { parseEther } from 'ethers';
|
|
3
|
+
import { FOUR_BNB_RAISED_TOKEN_FALLBACK, computeFourCreateValueWei, isNativeRaisedToken, parseRaisedTokens, pickRaisedToken, publishedRaisedTokens, raisedTokenTradingFeeBps, } from './raised-token.js';
|
|
4
|
+
const usdt = {
|
|
5
|
+
...FOUR_BNB_RAISED_TOKEN_FALLBACK,
|
|
6
|
+
symbol: 'USDT',
|
|
7
|
+
nativeSymbol: 'USDT',
|
|
8
|
+
symbolAddress: '0x55d398326f99059ff775485246999027b3197955',
|
|
9
|
+
b0Amount: '4000',
|
|
10
|
+
totalBAmount: '12000',
|
|
11
|
+
buyFee: '0.01',
|
|
12
|
+
};
|
|
13
|
+
describe('four raised-token', () => {
|
|
14
|
+
it('parses public config array and keeps PUBLISH only', () => {
|
|
15
|
+
const tokens = parseRaisedTokens({
|
|
16
|
+
code: 0,
|
|
17
|
+
data: [FOUR_BNB_RAISED_TOKEN_FALLBACK, { ...usdt, status: 'INIT' }, usdt],
|
|
18
|
+
});
|
|
19
|
+
expect(tokens.map((t) => t.symbol)).toEqual(['BNB', 'USDT', 'USDT']);
|
|
20
|
+
expect(publishedRaisedTokens(tokens).map((t) => t.symbol)).toEqual(['BNB', 'USDT']);
|
|
21
|
+
expect(pickRaisedToken(tokens, 'usdt')?.symbol).toBe('USDT');
|
|
22
|
+
});
|
|
23
|
+
it('BNB presale value is launch fee + presale + trading fee, not b0Amount', () => {
|
|
24
|
+
const launch = parseEther('0.01');
|
|
25
|
+
const preSale = '1';
|
|
26
|
+
const value = computeFourCreateValueWei({
|
|
27
|
+
raisedToken: FOUR_BNB_RAISED_TOKEN_FALLBACK,
|
|
28
|
+
preSale,
|
|
29
|
+
launchFeeWei: launch,
|
|
30
|
+
});
|
|
31
|
+
const trading = (parseEther('1') * raisedTokenTradingFeeBps(FOUR_BNB_RAISED_TOKEN_FALLBACK)) / 10000n;
|
|
32
|
+
expect(value).toBe(launch + parseEther('1') + trading);
|
|
33
|
+
expect(isNativeRaisedToken(FOUR_BNB_RAISED_TOKEN_FALLBACK)).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
it('ERC20 quote create value is launch fee only', () => {
|
|
36
|
+
expect(isNativeRaisedToken(usdt)).toBe(false);
|
|
37
|
+
expect(computeFourCreateValueWei({
|
|
38
|
+
raisedToken: usdt,
|
|
39
|
+
preSale: '100',
|
|
40
|
+
launchFeeWei: parseEther('0.01'),
|
|
41
|
+
})).toBe(parseEther('0.01'));
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -296,16 +296,25 @@ export function validateFourTokenTaxInfo(info) {
|
|
|
296
296
|
if (![1, 3, 5, 10].includes(info.feeRate)) {
|
|
297
297
|
return { valid: false, error: `feeRate 必须为 1, 3, 5 或 10,当前: ${info.feeRate}` };
|
|
298
298
|
}
|
|
299
|
-
|
|
300
|
-
const
|
|
299
|
+
const giggle = info.giggleCharityRate ?? 0;
|
|
300
|
+
const binance = info.binanceCharityRate ?? 0;
|
|
301
|
+
const isTaxToken9 = info.giggleCharityRate != null || info.binanceCharityRate != null;
|
|
302
|
+
const rateSum = info.burnRate + info.divideRate + info.liquidityRate + info.recipientRate + giggle + binance;
|
|
301
303
|
if (rateSum !== 100) {
|
|
302
304
|
return {
|
|
303
305
|
valid: false,
|
|
304
|
-
error:
|
|
306
|
+
error: isTaxToken9
|
|
307
|
+
? `burnRate + divideRate + liquidityRate + recipientRate + giggleCharityRate + binanceCharityRate 必须等于 100,当前: ${rateSum}`
|
|
308
|
+
: `burnRate + divideRate + liquidityRate + recipientRate 必须等于 100,当前: ${rateSum}`,
|
|
305
309
|
};
|
|
306
310
|
}
|
|
307
311
|
// 每项比例 >= 0
|
|
308
|
-
if (info.burnRate < 0 ||
|
|
312
|
+
if (info.burnRate < 0 ||
|
|
313
|
+
info.divideRate < 0 ||
|
|
314
|
+
info.liquidityRate < 0 ||
|
|
315
|
+
info.recipientRate < 0 ||
|
|
316
|
+
giggle < 0 ||
|
|
317
|
+
binance < 0) {
|
|
309
318
|
return { valid: false, error: '所有比例值不能为负数' };
|
|
310
319
|
}
|
|
311
320
|
// minSharing 验证: d × 10^n, n >= 5, 1 <= d <= 9
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|