four-flap-meme-sdk 4.0.1 → 4.0.3
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/constants.js +1 -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/types/index.d.ts +6 -0
- package/dist/core/clients/four.d.ts +15 -16
- package/dist/core/clients/four.js +62 -3
- package/dist/core/clients/index.d.ts +1 -1
- package/dist/core/clients/index.js +1 -1
- package/dist/domains/flap/vault/constants.js +10 -2
- 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/exports/root-foundations.d.ts +2 -1
- package/dist/exports/root-foundations.js +2 -1
- package/package.json +1 -1
|
@@ -10,6 +10,7 @@ export const MULTICALL3_ADDRESS = ADDRESSES.BSC.Multicall3;
|
|
|
10
10
|
export const TM2_ABI = [
|
|
11
11
|
..._TM2_ABI,
|
|
12
12
|
'function createToken(bytes args, bytes signature) payable',
|
|
13
|
+
'function _launchFee() view returns (uint256)',
|
|
13
14
|
'event TokenCreate(address indexed creator, address indexed token, uint256 timestamp)',
|
|
14
15
|
];
|
|
15
16
|
export const PLATFORM_CREATE_FEE = ethers.parseEther('0.01');
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Four.meme 发币:计价币、create value、ERC20 预购授权
|
|
3
|
+
*/
|
|
4
|
+
import { ethers, Wallet } from 'ethers';
|
|
5
|
+
import { FourClient, buildCreateTokenReq, type CreateTokenCustom } from '../../../core/clients/four.js';
|
|
6
|
+
import { type FourRaisedToken } from '../../../domains/four/raised-token.js';
|
|
7
|
+
export type FourLaunchQuoteInfo = {
|
|
8
|
+
quoteSymbol?: string;
|
|
9
|
+
raisedToken?: FourRaisedToken;
|
|
10
|
+
};
|
|
11
|
+
export declare function resolveCreateRaisedToken(fourClient: FourClient, tokenInfo: FourLaunchQuoteInfo): Promise<FourRaisedToken>;
|
|
12
|
+
export declare function readLaunchFeeWei(tm: ethers.Contract): Promise<bigint>;
|
|
13
|
+
export declare function fourCreateValueWei(raisedToken: FourRaisedToken, preSale: string, launchFeeWei: bigint): bigint;
|
|
14
|
+
export declare function signQuoteApproveIfNeeded(params: {
|
|
15
|
+
raisedToken: FourRaisedToken;
|
|
16
|
+
preSale: string;
|
|
17
|
+
wallet: Wallet;
|
|
18
|
+
spender: string;
|
|
19
|
+
getNonce: () => Promise<number>;
|
|
20
|
+
gasPrice: bigint;
|
|
21
|
+
chainId: number;
|
|
22
|
+
txType: number;
|
|
23
|
+
}): Promise<string | undefined>;
|
|
24
|
+
export declare function fourCreateApiBody(custom: CreateTokenCustom, raisedToken: FourRaisedToken): ReturnType<typeof buildCreateTokenReq>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Four.meme 发币:计价币、create value、ERC20 预购授权
|
|
3
|
+
*/
|
|
4
|
+
import { ethers } from 'ethers';
|
|
5
|
+
import { ERC20_ABI } from '../../../abis/common.js';
|
|
6
|
+
import { buildCreateTokenReq } from '../../../core/clients/four.js';
|
|
7
|
+
import { computeFourCreateValueWei, isNativeRaisedToken, } from '../../../domains/four/raised-token.js';
|
|
8
|
+
import { PLATFORM_CREATE_FEE } from './constants.js';
|
|
9
|
+
export async function resolveCreateRaisedToken(fourClient, tokenInfo) {
|
|
10
|
+
if (tokenInfo.raisedToken)
|
|
11
|
+
return tokenInfo.raisedToken;
|
|
12
|
+
return fourClient.resolveRaisedToken(tokenInfo.quoteSymbol);
|
|
13
|
+
}
|
|
14
|
+
export async function readLaunchFeeWei(tm) {
|
|
15
|
+
try {
|
|
16
|
+
const fee = await tm._launchFee();
|
|
17
|
+
if (typeof fee === 'bigint' && fee > 0n)
|
|
18
|
+
return fee;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// 合约未暴露或 RPC 失败时用官网当前档 0.01 BNB
|
|
22
|
+
}
|
|
23
|
+
return PLATFORM_CREATE_FEE;
|
|
24
|
+
}
|
|
25
|
+
export function fourCreateValueWei(raisedToken, preSale, launchFeeWei) {
|
|
26
|
+
return computeFourCreateValueWei({ raisedToken, preSale, launchFeeWei });
|
|
27
|
+
}
|
|
28
|
+
export async function signQuoteApproveIfNeeded(params) {
|
|
29
|
+
if (isNativeRaisedToken(params.raisedToken))
|
|
30
|
+
return undefined;
|
|
31
|
+
const preSale = String(params.preSale || '').trim() || '0';
|
|
32
|
+
if (preSale === '0')
|
|
33
|
+
return undefined;
|
|
34
|
+
const token = new ethers.Contract(params.raisedToken.symbolAddress, ERC20_ABI, params.wallet);
|
|
35
|
+
const decimals = Number(await token.decimals());
|
|
36
|
+
const amount = ethers.parseUnits(preSale, decimals);
|
|
37
|
+
const allowance = await token.allowance(params.wallet.address, params.spender);
|
|
38
|
+
if (allowance >= amount)
|
|
39
|
+
return undefined;
|
|
40
|
+
const unsigned = await token.approve.populateTransaction(params.spender, amount);
|
|
41
|
+
return params.wallet.signTransaction({
|
|
42
|
+
...unsigned,
|
|
43
|
+
from: params.wallet.address,
|
|
44
|
+
nonce: await params.getNonce(),
|
|
45
|
+
gasLimit: 80000n,
|
|
46
|
+
gasPrice: params.gasPrice,
|
|
47
|
+
chainId: params.chainId,
|
|
48
|
+
type: params.txType,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
export function fourCreateApiBody(custom, raisedToken) {
|
|
52
|
+
return buildCreateTokenReq(custom, raisedToken);
|
|
53
|
+
}
|
|
@@ -9,10 +9,11 @@ import { FourClient } from '../../../core/clients/four.js';
|
|
|
9
9
|
import { getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, getProfitRecipient, getBribeAmount, } from '../../config-helpers.js';
|
|
10
10
|
import { signBribeTransaction } from '../../../core/sign-context-helpers.js';
|
|
11
11
|
import { createChainContext, loginFourClient, resolveTokenImage } from '../core-helpers.js';
|
|
12
|
-
import { TM2_ABI
|
|
12
|
+
import { TM2_ABI } from './constants.js';
|
|
13
|
+
import { fourCreateApiBody, fourCreateValueWei, readLaunchFeeWei, resolveCreateRaisedToken, signQuoteApproveIfNeeded, } from './create-token-quote.js';
|
|
14
|
+
import { isNativeRaisedToken } from '../../../domains/four/raised-token.js';
|
|
13
15
|
export async function createTokenWithBundleBuyMerkle(params) {
|
|
14
16
|
const { privateKeys, buyAmounts, tokenInfo, config } = params;
|
|
15
|
-
// ⚠️ 限制: 只支持创建者买入,不支持其他钱包
|
|
16
17
|
if (privateKeys.length !== 1) {
|
|
17
18
|
throw new Error('只支持创建者买入,privateKeys 只能有1个元素');
|
|
18
19
|
}
|
|
@@ -20,20 +21,17 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
20
21
|
throw new Error('只支持买入一次,buyAmounts 只能有1个元素');
|
|
21
22
|
}
|
|
22
23
|
const creatorKey = privateKeys[0];
|
|
23
|
-
// ✅ 使用公共 BSC RPC 节点(支持浏览器 CORS)
|
|
24
|
-
// 48.club 的 RPC 不支持浏览器跨域访问,所以使用公共节点进行余额查询和 gas 估算
|
|
25
24
|
const rpcUrl = config.rpcUrl || 'https://bsc-dataseed.binance.org';
|
|
26
25
|
const { provider, chainId } = createChainContext(rpcUrl);
|
|
27
26
|
const devWallet = new Wallet(creatorKey, provider);
|
|
28
27
|
const fourClient = new FourClient({ baseUrl: config.fourApiUrl });
|
|
29
28
|
const accessToken = await loginFourClient(devWallet, fourClient);
|
|
30
29
|
const imgUrl = await resolveTokenImage(fourClient, tokenInfo, accessToken);
|
|
31
|
-
// ✅ 兼容:历史调用可能把 Dev 首买金额塞在 buyAmounts[0],而不是 tokenInfo.preSale
|
|
32
|
-
// Four 官方 API/链上 createArg 使用 preSale 表达“创建者预购金额”
|
|
33
30
|
const effectivePreSaleStr = tokenInfo.preSale && String(tokenInfo.preSale).trim().length > 0
|
|
34
31
|
? String(tokenInfo.preSale)
|
|
35
32
|
: String(buyAmounts[0] ?? '0');
|
|
36
|
-
const
|
|
33
|
+
const raisedToken = await resolveCreateRaisedToken(fourClient, tokenInfo);
|
|
34
|
+
const createResp = await fourClient.createToken(accessToken, fourCreateApiBody({
|
|
37
35
|
name: tokenInfo.name,
|
|
38
36
|
shortName: tokenInfo.symbol,
|
|
39
37
|
desc: tokenInfo.description,
|
|
@@ -45,49 +43,43 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
45
43
|
telegramUrl: tokenInfo.telegramUrl,
|
|
46
44
|
preSale: effectivePreSaleStr || '0',
|
|
47
45
|
onlyMPC: false,
|
|
48
|
-
// ✅ AntiSniperFeeMode(开盘高税模式)
|
|
49
46
|
...(tokenInfo.feePlan ? { feePlan: true } : {}),
|
|
50
|
-
// ✅ 税币配置(Tax Token, creatorType=5)
|
|
51
47
|
...(tokenInfo.tokenTaxInfo ? { tokenTaxInfo: tokenInfo.tokenTaxInfo } : {}),
|
|
52
|
-
|
|
53
|
-
symbol: 'BNB',
|
|
54
|
-
totalSupply: 1000000000,
|
|
55
|
-
raisedAmount: 24,
|
|
56
|
-
saleRate: 0.8,
|
|
57
|
-
reserveRate: 0,
|
|
58
|
-
funGroup: false,
|
|
59
|
-
clickFun: false,
|
|
60
|
-
});
|
|
48
|
+
}, raisedToken));
|
|
61
49
|
const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
|
|
62
50
|
const nonceManager = new NonceManager(provider);
|
|
63
51
|
const txType = getTxType(config);
|
|
64
52
|
const signedTxs = [];
|
|
65
53
|
const tmCreateAddr = ADDRESSES.BSC.TokenManagerOriginal;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const originalBuyAmount = ethers.parseEther(effectivePreSaleStr || '0');
|
|
54
|
+
const extractProfit = shouldExtractProfit() && isNativeRaisedToken(raisedToken);
|
|
55
|
+
const originalBuyAmount = isNativeRaisedToken(raisedToken)
|
|
56
|
+
? ethers.parseEther(effectivePreSaleStr || '0')
|
|
57
|
+
: 0n;
|
|
71
58
|
const profitAmount = extractProfit ? calculateProfit(originalBuyAmount).profit : 0n;
|
|
72
|
-
// ✅ 获取贿赂金额
|
|
73
59
|
const bribeAmount = getBribeAmount(config);
|
|
74
60
|
const needBribeTx = bribeAmount > 0n;
|
|
75
|
-
const b0AmountWei = ethers.parseEther(params.b0Amount ?? '0');
|
|
76
|
-
const preSaleWei = effectivePreSaleStr ? ethers.parseEther(effectivePreSaleStr) : 0n;
|
|
77
|
-
// ✅ 关键修复:
|
|
78
|
-
// createToken 的预购金额由 createArg.preSale 决定;因此这里只需要支付:创建费 + b0Amount + preSale
|
|
79
|
-
// 不要把“其它买入金额”额外塞进 msg.value,否则会被合约退回,造成“看起来没买到”的误解。
|
|
80
|
-
const valueWei = PLATFORM_CREATE_FEE + b0AmountWei + preSaleWei;
|
|
81
61
|
const tmCreate = new ethers.Contract(tmCreateAddr, TM2_ABI, devWallet);
|
|
62
|
+
const launchFeeWei = await readLaunchFeeWei(tmCreate);
|
|
63
|
+
const valueWei = fourCreateValueWei(raisedToken, effectivePreSaleStr || '0', launchFeeWei);
|
|
82
64
|
const createTxUnsigned = await tmCreate.createToken.populateTransaction(createResp.createArg, createResp.signature, {
|
|
83
65
|
value: valueWei,
|
|
84
66
|
});
|
|
85
|
-
// ✅ 贿赂交易放在首位(由 devWallet 发送)
|
|
86
|
-
let bribeNonce;
|
|
87
67
|
if (needBribeTx) {
|
|
88
|
-
bribeNonce = await nonceManager.getNextNonce(devWallet);
|
|
68
|
+
const bribeNonce = await nonceManager.getNextNonce(devWallet);
|
|
89
69
|
signedTxs.push(await signBribeTransaction(devWallet, bribeAmount, bribeNonce, gasPrice, chainId, txType));
|
|
90
70
|
}
|
|
71
|
+
const approveTx = await signQuoteApproveIfNeeded({
|
|
72
|
+
raisedToken,
|
|
73
|
+
preSale: effectivePreSaleStr || '0',
|
|
74
|
+
wallet: devWallet,
|
|
75
|
+
spender: tmCreateAddr,
|
|
76
|
+
getNonce: () => nonceManager.getNextNonce(devWallet),
|
|
77
|
+
gasPrice,
|
|
78
|
+
chainId,
|
|
79
|
+
txType,
|
|
80
|
+
});
|
|
81
|
+
if (approveTx)
|
|
82
|
+
signedTxs.push(approveTx);
|
|
91
83
|
const createTxRequest = {
|
|
92
84
|
...createTxUnsigned,
|
|
93
85
|
from: devWallet.address,
|
|
@@ -99,7 +91,6 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
99
91
|
value: valueWei,
|
|
100
92
|
};
|
|
101
93
|
signedTxs.push(await devWallet.signTransaction(createTxRequest));
|
|
102
|
-
// ✅ 利润多跳转账(强制 2 跳中转)
|
|
103
94
|
let profitHopWallets;
|
|
104
95
|
if (extractProfit && profitAmount > 0n) {
|
|
105
96
|
const profitNonce = await nonceManager.getNextNonce(devWallet);
|
|
@@ -115,11 +106,9 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
115
106
|
startNonce: profitNonce,
|
|
116
107
|
});
|
|
117
108
|
signedTxs.push(...profitHopResult.signedTransactions);
|
|
118
|
-
profitHopWallets = profitHopResult.hopWallets;
|
|
109
|
+
profitHopWallets = profitHopResult.hopWallets;
|
|
119
110
|
}
|
|
120
111
|
nonceManager.clearTemp();
|
|
121
|
-
// ⚠️ 只返回签名交易,不提交
|
|
122
|
-
// 构建元数据
|
|
123
112
|
const metadata = extractProfit && profitAmount > 0n
|
|
124
113
|
? {
|
|
125
114
|
totalBuyAmount: ethers.formatEther(originalBuyAmount),
|
|
@@ -130,8 +119,8 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
130
119
|
: undefined;
|
|
131
120
|
return {
|
|
132
121
|
signedTransactions: signedTxs,
|
|
133
|
-
tokenAddress: ZERO_ADDRESS,
|
|
134
|
-
profitHopWallets,
|
|
122
|
+
tokenAddress: ZERO_ADDRESS,
|
|
123
|
+
profitHopWallets,
|
|
135
124
|
metadata,
|
|
136
125
|
};
|
|
137
126
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { GeneratedWallet } from '../../../utils/wallet.js';
|
|
2
2
|
import type { FourTokenTaxInfo } from '../../../core/clients/four.js';
|
|
3
|
+
import type { FourRaisedToken } from '../../../domains/four/raised-token.js';
|
|
3
4
|
import type { AmountLike } from '../../types/index.js';
|
|
4
5
|
import type { CommonBundleConfig } from '../../../utils/bundle-helpers.js';
|
|
5
6
|
export type { AmountLike };
|
|
@@ -105,6 +106,9 @@ export type FourCreateWithBundleBuyMerkleParams = {
|
|
|
105
106
|
feePlan?: boolean;
|
|
106
107
|
/** ✅ 税币配置(Tax Token, creatorType=5) */
|
|
107
108
|
tokenTaxInfo?: FourTokenTaxInfo;
|
|
109
|
+
/** 官网计价币 symbol,如 BNB / USDT / USD1 */
|
|
110
|
+
quoteSymbol?: string;
|
|
111
|
+
raisedToken?: FourRaisedToken;
|
|
108
112
|
};
|
|
109
113
|
config: FourBundleMerkleConfig;
|
|
110
114
|
};
|
|
@@ -128,6 +132,8 @@ export type FourCreateWithBundleBuySignParams = {
|
|
|
128
132
|
feePlan?: boolean;
|
|
129
133
|
/** ✅ 税币配置(Tax Token, creatorType=5) */
|
|
130
134
|
tokenTaxInfo?: FourTokenTaxInfo;
|
|
135
|
+
quoteSymbol?: string;
|
|
136
|
+
raisedToken?: FourRaisedToken;
|
|
131
137
|
};
|
|
132
138
|
config: FourSignConfig;
|
|
133
139
|
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type FourRaisedToken, type FourTokenLabel } from '../../domains/four/raised-token.js';
|
|
2
|
+
export type { FourRaisedToken, FourTokenLabel };
|
|
1
3
|
export type NetworkCode = 'BSC';
|
|
2
4
|
export type FourConfig = {
|
|
3
5
|
/**
|
|
@@ -54,39 +56,34 @@ export type FourTokenTaxInfo = {
|
|
|
54
56
|
recipientAddress: string;
|
|
55
57
|
minSharing: number;
|
|
56
58
|
};
|
|
57
|
-
export type
|
|
59
|
+
export type CreateTokenCustom = {
|
|
58
60
|
name: string;
|
|
59
61
|
shortName: string;
|
|
60
62
|
desc: string;
|
|
61
63
|
imgUrl: string;
|
|
62
64
|
launchTime: number;
|
|
63
|
-
label:
|
|
65
|
+
label: FourTokenLabel;
|
|
64
66
|
webUrl?: string;
|
|
65
67
|
twitterUrl?: string;
|
|
66
68
|
telegramUrl?: string;
|
|
67
69
|
preSale: string;
|
|
68
70
|
onlyMPC?: boolean;
|
|
69
|
-
/**
|
|
70
|
-
* AntiSniperFeeMode(开盘高税模式)
|
|
71
|
-
* - true: 启用动态手续费系统(开盘时高税,逐区块自动降低)
|
|
72
|
-
* - false: 不启用(默认)
|
|
73
|
-
*/
|
|
74
71
|
feePlan?: boolean;
|
|
75
|
-
/**
|
|
76
|
-
* 税币配置(Tax Token)
|
|
77
|
-
* - 提供此字段时创建税币(creatorType = 5)
|
|
78
|
-
* - 不提供或为 undefined 时创建普通代币
|
|
79
|
-
*/
|
|
80
72
|
tokenTaxInfo?: FourTokenTaxInfo;
|
|
73
|
+
};
|
|
74
|
+
export type CreateTokenReq = CreateTokenCustom & {
|
|
81
75
|
lpTradingFee: 0.0025;
|
|
82
|
-
symbol:
|
|
83
|
-
totalSupply:
|
|
84
|
-
raisedAmount:
|
|
85
|
-
saleRate:
|
|
76
|
+
symbol: string;
|
|
77
|
+
totalSupply: number;
|
|
78
|
+
raisedAmount: number;
|
|
79
|
+
saleRate: number;
|
|
86
80
|
reserveRate: 0;
|
|
87
81
|
funGroup: false;
|
|
88
82
|
clickFun: false;
|
|
83
|
+
/** 官网 /v1/public/config 条目,须原样提交,禁止自造内部字段 */
|
|
84
|
+
raisedToken?: FourRaisedToken;
|
|
89
85
|
};
|
|
86
|
+
export declare function buildCreateTokenReq(custom: CreateTokenCustom, raisedToken: FourRaisedToken): CreateTokenReq;
|
|
90
87
|
export type FourJsonValue = string | number | boolean | null | FourJsonValue[] | {
|
|
91
88
|
[key: string]: FourJsonValue;
|
|
92
89
|
};
|
|
@@ -137,6 +134,8 @@ export declare class FourClient {
|
|
|
137
134
|
private getFilenameFromBlob;
|
|
138
135
|
createToken(accessToken: string, req: CreateTokenReq): Promise<CreateTokenResp>;
|
|
139
136
|
getPublicConfig(): Promise<FourPublicConfig>;
|
|
137
|
+
listPublishedRaisedTokens(): Promise<FourRaisedToken[]>;
|
|
138
|
+
resolveRaisedToken(symbol?: string): Promise<FourRaisedToken>;
|
|
140
139
|
getTokenByAddress(address: string, accessToken?: string): Promise<FourTokenDetail>;
|
|
141
140
|
getTokensByAddresses(addresses: string[], accessToken?: string): Promise<(FourTokenDetail | {
|
|
142
141
|
address: string;
|
|
@@ -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}`, {
|
|
@@ -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';
|
|
@@ -29,6 +29,7 @@ export const VAULT_TYPE_CATEGORIES = {
|
|
|
29
29
|
export const VAULT_PORTAL_ADDRESSES = {
|
|
30
30
|
BSC: '0x90497450f2a706f1951b5bdda52B4E5d16f34C06',
|
|
31
31
|
BASE: '0x027e3704fC5C16522e9393d04C60A3ac5c0d775f',
|
|
32
|
+
MORPH: '0xe9F7AB7DE8FB8756acbB6a1cd13316A43308197B',
|
|
32
33
|
};
|
|
33
34
|
/**
|
|
34
35
|
* VaultFactory 合约地址(每种金库类型 + 每条链)
|
|
@@ -38,18 +39,25 @@ export const VAULT_PORTAL_ADDRESSES = {
|
|
|
38
39
|
*/
|
|
39
40
|
export const VAULT_FACTORY_ADDRESSES = {
|
|
40
41
|
BSC: {
|
|
41
|
-
gift: '
|
|
42
|
+
gift: '0xFb7ccc4Fd09Da5b7016A18d51e227Af4ABE53f44',
|
|
42
43
|
split: '0xfab75Dc774cB9B38b91749B8833360B46a52345F',
|
|
43
44
|
// bnbshare: '0x53AD93F2454cFA532656f80E30571159d7E726A7', // 已禁用 (enabled: false)
|
|
44
45
|
buyback_burn: '0xB64655dab7156c29B63C70fb3ED7f071e2658D19',
|
|
45
46
|
burnToEarn: '0x47A216040Dc9e0AfE2e6fc5dcC44f7bBD1B60d25',
|
|
47
|
+
scheduled_buyback: '0x4ac94f87863012C4F133ef748b7cC5b75CAFE801',
|
|
48
|
+
relay: '0xB8dfFd67204105fB32d51d9cAa7c17A7bf880d9E',
|
|
49
|
+
lp_staking: '0x59F3b82Ea3aA3BCE6156CA61c0A6613C0A632452',
|
|
50
|
+
rank_burn: '0x9F5bF8EB9c4cA7179E60d0B529F4910B9A7eE0Ee',
|
|
46
51
|
buildYourVault: ZeroAddress, // 用户自定义地址
|
|
47
52
|
},
|
|
48
53
|
BASE: {
|
|
49
|
-
gift: '
|
|
54
|
+
gift: '0x2eDD880AB36b07bD030BDa28A13c3E9148C11622',
|
|
50
55
|
split: '0x1ae091F75D593eb7dC6539600a185C8A6076A424',
|
|
51
56
|
buyback_burn: '0x6F97fA674a6FE5EC37e309B63aB54E33c9e64d2E',
|
|
52
57
|
burnToEarn: '0x47A216040Dc9e0AfE2e6fc5dcC44f7bBD1B60d25',
|
|
58
|
+
scheduled_buyback: '0xdc40982a586e4692e1E4D0b05416B1FB5155075a',
|
|
59
|
+
lp_staking: '0xbFfAb600105C83Ea5009A9ccbf7a0f2D007Ad198',
|
|
60
|
+
rank_burn: '0x971ec9df7CEA81d612d48c1f6021ADeF6C6682B4',
|
|
53
61
|
buildYourVault: ZeroAddress,
|
|
54
62
|
},
|
|
55
63
|
};
|
|
@@ -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
|
+
});
|
|
@@ -14,7 +14,8 @@ export { isExclusiveOnChain, isExclusiveOffChain } from '../utils/mpcExclusive.j
|
|
|
14
14
|
export { ensureSellApprovalV1, checkSellApprovalV1, ensureSellApprovalV2, checkSellApprovalV2, ensureSellApproval, checkSellApproval, ensureFlapSellApproval, checkFlapSellApproval, ensureFlapSellApprovalBatch, checkFlapSellApprovalBatch, checkAllowance, approveToken, checkAllowanceBatch, approveTokenBatch, checkAllowanceRaw, approveTokenRaw, checkAllowanceBatchRaw, approveTokenBatchRaw, } from '../core/erc20/index.js';
|
|
15
15
|
export { parseFourError, type FourErrorCode } from '../utils/errors.js';
|
|
16
16
|
export { getTokenManagerV1, getTokenManagerV2, getTokenManagerHelper3, getTokenManagerV1Writer, getTokenManagerV2Writer, getTokenManagerHelper3Writer, getTokenManagerAddress, type ChainName, } from '../utils/contract-factory.js';
|
|
17
|
-
export { FourClient, buildLoginMessage, type FourConfig, type GenerateNonceReq, type LoginReq, type CreateTokenReq, type CreateTokenResp, type FourTokenTaxInfo, } from '../core/clients/four.js';
|
|
17
|
+
export { FourClient, buildLoginMessage, buildCreateTokenReq, type FourConfig, type GenerateNonceReq, type LoginReq, type CreateTokenReq, type CreateTokenCustom, type CreateTokenResp, type FourTokenTaxInfo, type FourRaisedToken, type FourTokenLabel, } from '../core/clients/four.js';
|
|
18
|
+
export { FOUR_TOKEN_LABELS, FOUR_BNB_RAISED_TOKEN_FALLBACK, parseRaisedTokens, publishedRaisedTokens, pickRaisedToken, isNativeRaisedToken, computeFourCreateValueWei, raisedTokenTradingFeeBps, } from '../domains/four/raised-token.js';
|
|
18
19
|
export { createTokenOnChain, tryBuy, trySell, buyTokenWithFunds, sellToken, tradeBuy, tradeSell, type CreateOnChainParams, type TryBuyResult, type TrySellResult, } from '../domains/four/contracts/tm.js';
|
|
19
20
|
export { TM1, type FourChainV1 } from '../domains/four/contracts/tm1.js';
|
|
20
21
|
export { TM2, type FourChainV2 } from '../domains/four/contracts/tm2.js';
|
|
@@ -49,7 +49,8 @@ checkAllowance, approveToken, checkAllowanceBatch, approveTokenBatch,
|
|
|
49
49
|
checkAllowanceRaw, approveTokenRaw, checkAllowanceBatchRaw, approveTokenBatchRaw, } from '../core/erc20/index.js';
|
|
50
50
|
export { parseFourError } from '../utils/errors.js';
|
|
51
51
|
export { getTokenManagerV1, getTokenManagerV2, getTokenManagerHelper3, getTokenManagerV1Writer, getTokenManagerV2Writer, getTokenManagerHelper3Writer, getTokenManagerAddress, } from '../utils/contract-factory.js';
|
|
52
|
-
export { FourClient, buildLoginMessage, } from '../core/clients/four.js';
|
|
52
|
+
export { FourClient, buildLoginMessage, buildCreateTokenReq, } from '../core/clients/four.js';
|
|
53
|
+
export { FOUR_TOKEN_LABELS, FOUR_BNB_RAISED_TOKEN_FALLBACK, parseRaisedTokens, publishedRaisedTokens, pickRaisedToken, isNativeRaisedToken, computeFourCreateValueWei, raisedTokenTradingFeeBps, } from '../domains/four/raised-token.js';
|
|
53
54
|
export { createTokenOnChain, tryBuy, trySell, buyTokenWithFunds, sellToken, tradeBuy, tradeSell, } from '../domains/four/contracts/tm.js';
|
|
54
55
|
export { TM1 } from '../domains/four/contracts/tm1.js';
|
|
55
56
|
export { TM2 } from '../domains/four/contracts/tm2.js';
|