@four-meme/four-meme-ai 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/CLAUDE.md +91 -0
  2. package/README.md +79 -0
  3. package/bin/fourmeme.cjs +124 -0
  4. package/openclaw.plugin.json +11 -0
  5. package/package.json +55 -0
  6. package/plugin.ts +7 -0
  7. package/skills/four-meme-integration/SKILL.md +432 -0
  8. package/skills/four-meme-integration/references/api-create-token.md +55 -0
  9. package/skills/four-meme-integration/references/contract-addresses.md +34 -0
  10. package/skills/four-meme-integration/references/create-token-scripts.md +63 -0
  11. package/skills/four-meme-integration/references/errors.md +27 -0
  12. package/skills/four-meme-integration/references/event-listening.md +75 -0
  13. package/skills/four-meme-integration/references/execute-trade.md +31 -0
  14. package/skills/four-meme-integration/references/tax-token-query.md +38 -0
  15. package/skills/four-meme-integration/references/token-query-api.md +40 -0
  16. package/skills/four-meme-integration/references/token-tax-info.md +77 -0
  17. package/skills/four-meme-integration/scripts/8004-balance.ts +52 -0
  18. package/skills/four-meme-integration/scripts/8004-register.ts +108 -0
  19. package/skills/four-meme-integration/scripts/create-token-api.ts +251 -0
  20. package/skills/four-meme-integration/scripts/create-token-chain.ts +85 -0
  21. package/skills/four-meme-integration/scripts/execute-buy.ts +198 -0
  22. package/skills/four-meme-integration/scripts/execute-sell.ts +150 -0
  23. package/skills/four-meme-integration/scripts/get-public-config.ts +25 -0
  24. package/skills/four-meme-integration/scripts/get-recent-events.ts +76 -0
  25. package/skills/four-meme-integration/scripts/get-tax-token-info.ts +69 -0
  26. package/skills/four-meme-integration/scripts/get-token-info.ts +94 -0
  27. package/skills/four-meme-integration/scripts/quote-buy.ts +85 -0
  28. package/skills/four-meme-integration/scripts/quote-sell.ts +66 -0
  29. package/skills/four-meme-integration/scripts/send-token.ts +98 -0
  30. package/skills/four-meme-integration/scripts/token-get.ts +31 -0
  31. package/skills/four-meme-integration/scripts/token-list.ts +52 -0
  32. package/skills/four-meme-integration/scripts/token-rankings.ts +54 -0
  33. package/skills/four-meme-integration/scripts/verify-events.ts +47 -0
  34. package/tsconfig.json +15 -0
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Four.meme - submit createToken tx on BSC (TokenManager2.createToken).
4
+ * Uses createArg and signature from create-token-api.ts output.
5
+ *
6
+ * Usage:
7
+ * npx tsx create-token-chain.ts <createArgHex> <signatureHex>
8
+ * echo '{"createArg":"0x...","signature":"0x..."}' | npx tsx create-token-chain.ts --
9
+ *
10
+ * Env: PRIVATE_KEY or FOURMEME_PRIVATE_KEY. Optional: BSC_RPC_URL.
11
+ */
12
+
13
+ import { createWalletClient, http, parseAbi } from 'viem';
14
+ import { privateKeyToAccount } from 'viem/accounts';
15
+ import { bsc } from 'viem/chains';
16
+
17
+ const TOKEN_MANAGER2_BSC = '0x5c952063c7fc8610FFDB798152D69F0B9550762b' as const;
18
+
19
+ const ABI = parseAbi([
20
+ 'function createToken(bytes args, bytes signature) payable',
21
+ ]);
22
+
23
+ function toHex(s: string): `0x${string}` {
24
+ if (s.startsWith('0x')) return s as `0x${string}`;
25
+ if (/^[0-9a-fA-F]+$/.test(s)) return ('0x' + s) as `0x${string}`;
26
+ const buf = Buffer.from(s, 'base64');
27
+ return ('0x' + buf.toString('hex')) as `0x${string}`;
28
+ }
29
+
30
+ async function main() {
31
+ const privateKey = process.env.PRIVATE_KEY || process.env.FOURMEME_PRIVATE_KEY;
32
+ if (!privateKey) {
33
+ console.error('Set PRIVATE_KEY or FOURMEME_PRIVATE_KEY');
34
+ process.exit(1);
35
+ }
36
+ const pk = privateKey.startsWith('0x') ? (privateKey as `0x${string}`) : (`0x${privateKey}` as `0x${string}`);
37
+ const account = privateKeyToAccount(pk);
38
+
39
+ let createArgHex: `0x${string}`;
40
+ let signatureHex: `0x${string}`;
41
+
42
+ const arg1 = process.argv[2];
43
+ if (arg1 === '--' || !arg1) {
44
+ const chunks: Buffer[] = [];
45
+ for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
46
+ const json = JSON.parse(Buffer.concat(chunks).toString('utf8'));
47
+ createArgHex = toHex(json.createArg);
48
+ signatureHex = toHex(json.signature);
49
+ } else {
50
+ const arg2 = process.argv[3];
51
+ if (!arg2) {
52
+ console.error('Usage: npx tsx create-token-chain.ts <createArgHex> <signatureHex>');
53
+ console.error(' or: echo \'{"createArg":"0x...","signature":"0x..."}\' | npx tsx create-token-chain.ts --');
54
+ process.exit(1);
55
+ }
56
+ createArgHex = toHex(arg1);
57
+ signatureHex = toHex(arg2);
58
+ }
59
+
60
+ const rpcUrl = process.env.BSC_RPC_URL || 'https://bsc-dataseed.binance.org';
61
+ const client = createWalletClient({
62
+ account,
63
+ chain: bsc,
64
+ transport: http(rpcUrl),
65
+ });
66
+
67
+ const creationFeeWei = process.env.CREATION_FEE_WEI
68
+ ? BigInt(process.env.CREATION_FEE_WEI)
69
+ : 0n;
70
+
71
+ const hash = await client.writeContract({
72
+ address: TOKEN_MANAGER2_BSC,
73
+ abi: ABI,
74
+ functionName: 'createToken',
75
+ args: [createArgHex, signatureHex],
76
+ value: creationFeeWei,
77
+ });
78
+
79
+ console.log(JSON.stringify({ txHash: hash }, null, 2));
80
+ }
81
+
82
+ main().catch((e) => {
83
+ console.error(e.message || e);
84
+ process.exit(1);
85
+ });
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Four.meme - execute buy on TokenManager2 (BSC only).
4
+ * Usage:
5
+ * npx tsx execute-buy.ts <tokenAddress> amount <amountWei> <maxFundsWei> # buy fixed token amount
6
+ * npx tsx execute-buy.ts <tokenAddress> funds <fundsWei> <minAmountWei> # spend fixed quote (e.g. BNB)
7
+ * Env: PRIVATE_KEY or FOURMEME_PRIVATE_KEY. Only V2 tokens (version 2 from getTokenInfo).
8
+ */
9
+
10
+ import { createPublicClient, createWalletClient, http } from 'viem';
11
+ import { privateKeyToAccount } from 'viem/accounts';
12
+ import { bsc } from 'viem/chains';
13
+
14
+ const HELPER_ADDRESS = '0xF251F83e40a78868FcfA3FA4599Dad6494E46034' as const;
15
+ const ZERO = '0x0000000000000000000000000000000000000000' as const;
16
+
17
+ const HELPER_ABI = [
18
+ {
19
+ name: 'getTokenInfo',
20
+ type: 'function',
21
+ stateMutability: 'view',
22
+ inputs: [{ name: 'token', type: 'address' }],
23
+ outputs: [
24
+ { name: 'version', type: 'uint256' },
25
+ { name: 'tokenManager', type: 'address' },
26
+ { name: 'quote', type: 'address' },
27
+ { name: 'lastPrice', type: 'uint256' },
28
+ { name: 'tradingFeeRate', type: 'uint256' },
29
+ { name: 'minTradingFee', type: 'uint256' },
30
+ { name: 'launchTime', type: 'uint256' },
31
+ { name: 'offers', type: 'uint256' },
32
+ { name: 'maxOffers', type: 'uint256' },
33
+ { name: 'funds', type: 'uint256' },
34
+ { name: 'maxFunds', type: 'uint256' },
35
+ { name: 'liquidityAdded', type: 'bool' },
36
+ ],
37
+ },
38
+ {
39
+ name: 'tryBuy',
40
+ type: 'function',
41
+ stateMutability: 'view',
42
+ inputs: [
43
+ { name: 'token', type: 'address' },
44
+ { name: 'amount', type: 'uint256' },
45
+ { name: 'funds', type: 'uint256' },
46
+ ],
47
+ outputs: [
48
+ { name: 'tokenManager', type: 'address' },
49
+ { name: 'quote', type: 'address' },
50
+ { name: 'estimatedAmount', type: 'uint256' },
51
+ { name: 'estimatedCost', type: 'uint256' },
52
+ { name: 'estimatedFee', type: 'uint256' },
53
+ { name: 'amountMsgValue', type: 'uint256' },
54
+ { name: 'amountApproval', type: 'uint256' },
55
+ { name: 'amountFunds', type: 'uint256' },
56
+ ],
57
+ },
58
+ ] as const;
59
+
60
+ const TM2_ABI = [
61
+ {
62
+ name: 'buyToken',
63
+ type: 'function',
64
+ stateMutability: 'payable',
65
+ inputs: [
66
+ { name: 'token', type: 'address' },
67
+ { name: 'amount', type: 'uint256' },
68
+ { name: 'maxFunds', type: 'uint256' },
69
+ ],
70
+ outputs: [],
71
+ },
72
+ {
73
+ name: 'buyTokenAMAP',
74
+ type: 'function',
75
+ stateMutability: 'payable',
76
+ inputs: [
77
+ { name: 'token', type: 'address' },
78
+ { name: 'funds', type: 'uint256' },
79
+ { name: 'minAmount', type: 'uint256' },
80
+ ],
81
+ outputs: [],
82
+ },
83
+ ] as const;
84
+
85
+ const ERC20_ABI = [
86
+ {
87
+ name: 'approve',
88
+ type: 'function',
89
+ stateMutability: 'nonpayable',
90
+ inputs: [
91
+ { name: 'spender', type: 'address' },
92
+ { name: 'amount', type: 'uint256' },
93
+ ],
94
+ outputs: [{ type: 'bool' }],
95
+ },
96
+ ] as const;
97
+
98
+ const RPC_URL = process.env.BSC_RPC_URL || 'https://bsc-dataseed.binance.org';
99
+
100
+ async function main() {
101
+ const pk = process.env.PRIVATE_KEY || process.env.FOURMEME_PRIVATE_KEY;
102
+ if (!pk) {
103
+ console.error('Set PRIVATE_KEY or FOURMEME_PRIVATE_KEY');
104
+ process.exit(1);
105
+ }
106
+ const account = privateKeyToAccount(
107
+ (pk.startsWith('0x') ? pk : '0x' + pk) as `0x${string}`
108
+ );
109
+
110
+ const tokenAddress = process.argv[2] as `0x${string}`;
111
+ const mode = process.argv[3]; // 'amount' | 'funds'
112
+ const arg1 = process.argv[4];
113
+ const arg2 = process.argv[5];
114
+
115
+ if (!tokenAddress || !mode || !arg1 || !arg2) {
116
+ console.error('Usage:');
117
+ console.error(' execute-buy.ts <token> amount <amountWei> <maxFundsWei>');
118
+ console.error(' execute-buy.ts <token> funds <fundsWei> <minAmountWei>');
119
+ process.exit(1);
120
+ }
121
+
122
+ const publicClient = createPublicClient({
123
+ chain: bsc,
124
+ transport: http(RPC_URL),
125
+ });
126
+ const walletClient = createWalletClient({
127
+ account,
128
+ chain: bsc,
129
+ transport: http(RPC_URL),
130
+ });
131
+
132
+ const [version, tokenManager, quote] = await publicClient.readContract({
133
+ address: HELPER_ADDRESS,
134
+ abi: HELPER_ABI,
135
+ functionName: 'getTokenInfo',
136
+ args: [tokenAddress],
137
+ }).then((r) => [r[0], r[1], r[2]] as const);
138
+
139
+ if (Number(version) !== 2) {
140
+ console.error('Only TokenManager2 (V2) tokens are supported. This token is version', version);
141
+ process.exit(1);
142
+ }
143
+
144
+ const amountWei = mode === 'amount' ? BigInt(arg1) : 0n;
145
+ const maxFundsWei = mode === 'amount' ? BigInt(arg2) : 0n;
146
+ const fundsWei = mode === 'funds' ? BigInt(arg1) : 0n;
147
+ const minAmountWei = mode === 'funds' ? BigInt(arg2) : 0n;
148
+
149
+ const tryBuyResult = await publicClient.readContract({
150
+ address: HELPER_ADDRESS,
151
+ abi: HELPER_ABI,
152
+ functionName: 'tryBuy',
153
+ args: [tokenAddress, amountWei, fundsWei],
154
+ });
155
+ const amountMsgValue = tryBuyResult[5];
156
+ const amountApproval = tryBuyResult[6];
157
+
158
+ if (quote !== ZERO && amountApproval > 0n) {
159
+ const approveHash = await walletClient.writeContract({
160
+ address: quote,
161
+ abi: ERC20_ABI,
162
+ functionName: 'approve',
163
+ args: [tokenManager, amountApproval],
164
+ });
165
+ console.error('Approved quote token. Tx:', approveHash);
166
+ const receipt = await publicClient.waitForTransactionReceipt({ hash: approveHash });
167
+ if (receipt.status !== 'success') {
168
+ console.error('Approve failed');
169
+ process.exit(1);
170
+ }
171
+ }
172
+
173
+ let hash: `0x${string}`;
174
+ if (mode === 'amount') {
175
+ hash = await walletClient.writeContract({
176
+ address: tokenManager as `0x${string}`,
177
+ abi: TM2_ABI,
178
+ functionName: 'buyToken',
179
+ args: [tokenAddress, amountWei, maxFundsWei],
180
+ value: amountMsgValue,
181
+ });
182
+ } else {
183
+ hash = await walletClient.writeContract({
184
+ address: tokenManager as `0x${string}`,
185
+ abi: TM2_ABI,
186
+ functionName: 'buyTokenAMAP',
187
+ args: [tokenAddress, fundsWei, minAmountWei],
188
+ value: amountMsgValue,
189
+ });
190
+ }
191
+
192
+ console.log(JSON.stringify({ txHash: hash }, null, 2));
193
+ }
194
+
195
+ main().catch((e) => {
196
+ console.error(e.message || e);
197
+ process.exit(1);
198
+ });
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Four.meme - execute sell on TokenManager2 (BSC only).
4
+ * Usage: npx tsx execute-sell.ts <tokenAddress> <amountWei> [minFundsWei]
5
+ * Env: PRIVATE_KEY or FOURMEME_PRIVATE_KEY. Sends approve(tokenManager, amount) then sellToken(token, amount).
6
+ */
7
+
8
+ import { createPublicClient, createWalletClient, http } from 'viem';
9
+ import { privateKeyToAccount } from 'viem/accounts';
10
+ import { bsc } from 'viem/chains';
11
+
12
+ const HELPER_ADDRESS = '0xF251F83e40a78868FcfA3FA4599Dad6494E46034' as const;
13
+
14
+ const HELPER_ABI = [
15
+ {
16
+ name: 'getTokenInfo',
17
+ type: 'function',
18
+ stateMutability: 'view',
19
+ inputs: [{ name: 'token', type: 'address' }],
20
+ outputs: [
21
+ { name: 'version', type: 'uint256' },
22
+ { name: 'tokenManager', type: 'address' },
23
+ { name: 'quote', type: 'address' },
24
+ { name: 'lastPrice', type: 'uint256' },
25
+ { name: 'tradingFeeRate', type: 'uint256' },
26
+ { name: 'minTradingFee', type: 'uint256' },
27
+ { name: 'launchTime', type: 'uint256' },
28
+ { name: 'offers', type: 'uint256' },
29
+ { name: 'maxOffers', type: 'uint256' },
30
+ { name: 'funds', type: 'uint256' },
31
+ { name: 'maxFunds', type: 'uint256' },
32
+ { name: 'liquidityAdded', type: 'bool' },
33
+ ],
34
+ },
35
+ ] as const;
36
+
37
+ const TM2_ABI_SIMPLE = [
38
+ {
39
+ name: 'sellToken',
40
+ type: 'function',
41
+ stateMutability: 'nonpayable',
42
+ inputs: [
43
+ { name: 'token', type: 'address' },
44
+ { name: 'amount', type: 'uint256' },
45
+ ],
46
+ outputs: [],
47
+ },
48
+ ] as const;
49
+
50
+ const TM2_ABI_WITH_MIN_FUNDS = [
51
+ {
52
+ name: 'sellToken',
53
+ type: 'function',
54
+ stateMutability: 'nonpayable',
55
+ inputs: [
56
+ { name: 'origin', type: 'uint256' },
57
+ { name: 'token', type: 'address' },
58
+ { name: 'amount', type: 'uint256' },
59
+ { name: 'minFunds', type: 'uint256' },
60
+ ],
61
+ outputs: [],
62
+ },
63
+ ] as const;
64
+
65
+ const ERC20_ABI = [
66
+ {
67
+ name: 'approve',
68
+ type: 'function',
69
+ stateMutability: 'nonpayable',
70
+ inputs: [
71
+ { name: 'spender', type: 'address' },
72
+ { name: 'amount', type: 'uint256' },
73
+ ],
74
+ outputs: [{ type: 'bool' }],
75
+ },
76
+ ] as const;
77
+
78
+ const RPC_URL = process.env.BSC_RPC_URL || 'https://bsc-dataseed.binance.org';
79
+
80
+ async function main() {
81
+ const pk = process.env.PRIVATE_KEY || process.env.FOURMEME_PRIVATE_KEY;
82
+ if (!pk) {
83
+ console.error('Set PRIVATE_KEY or FOURMEME_PRIVATE_KEY');
84
+ process.exit(1);
85
+ }
86
+ const account = privateKeyToAccount(
87
+ (pk.startsWith('0x') ? pk : '0x' + pk) as `0x${string}`
88
+ );
89
+
90
+ const tokenAddress = process.argv[2] as `0x${string}`;
91
+ const amountWei = BigInt(process.argv[3] ?? '0');
92
+ const minFundsWei = process.argv[4] ? BigInt(process.argv[4]) : null;
93
+
94
+ if (!tokenAddress || amountWei <= 0n) {
95
+ console.error('Usage: npx tsx execute-sell.ts <tokenAddress> <amountWei> [minFundsWei]');
96
+ process.exit(1);
97
+ }
98
+
99
+ const publicClient = createPublicClient({
100
+ chain: bsc,
101
+ transport: http(RPC_URL),
102
+ });
103
+ const walletClient = createWalletClient({
104
+ account,
105
+ chain: bsc,
106
+ transport: http(RPC_URL),
107
+ });
108
+
109
+ const [, tokenManager] = await publicClient.readContract({
110
+ address: HELPER_ADDRESS,
111
+ abi: HELPER_ABI,
112
+ functionName: 'getTokenInfo',
113
+ args: [tokenAddress],
114
+ }).then((r) => [r[0], r[1]] as const);
115
+
116
+ const approveHash = await walletClient.writeContract({
117
+ address: tokenAddress,
118
+ abi: ERC20_ABI,
119
+ functionName: 'approve',
120
+ args: [tokenManager as `0x${string}`, amountWei],
121
+ });
122
+ const receipt = await publicClient.waitForTransactionReceipt({ hash: approveHash });
123
+ if (receipt.status !== 'success') {
124
+ console.error('Approve failed');
125
+ process.exit(1);
126
+ }
127
+
128
+ const tm2 = tokenManager as `0x${string}`;
129
+ const hash: `0x${string}` =
130
+ minFundsWei !== null
131
+ ? await walletClient.writeContract({
132
+ address: tm2,
133
+ abi: TM2_ABI_WITH_MIN_FUNDS,
134
+ functionName: 'sellToken',
135
+ args: [0n, tokenAddress, amountWei, minFundsWei],
136
+ })
137
+ : await walletClient.writeContract({
138
+ address: tm2,
139
+ abi: TM2_ABI_SIMPLE,
140
+ functionName: 'sellToken',
141
+ args: [tokenAddress, amountWei],
142
+ });
143
+
144
+ console.log(JSON.stringify({ txHash: hash }, null, 2));
145
+ }
146
+
147
+ main().catch((e) => {
148
+ console.error(e.message || e);
149
+ process.exit(1);
150
+ });
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Four.meme - fetch public config (raisedToken for create-token API)
4
+ * Usage: npx tsx skills/four-meme-integration/scripts/get-public-config.ts
5
+ * Output: JSON with raisedToken and other config (use raisedToken in create body as-is).
6
+ */
7
+
8
+ const API_BASE = 'https://four.meme/meme-api/v1';
9
+
10
+ async function main() {
11
+ const res = await fetch(`${API_BASE}/public/config`);
12
+ if (!res.ok) {
13
+ throw new Error(`Config request failed: ${res.status} ${await res.text()}`);
14
+ }
15
+ const data = await res.json();
16
+ if (data.code !== '0' && data.code !== 0) {
17
+ throw new Error(`Config error: ${JSON.stringify(data)}`);
18
+ }
19
+ console.log(JSON.stringify(data.data ?? data, null, 2));
20
+ }
21
+
22
+ main().catch((e) => {
23
+ console.error(e.message || e);
24
+ process.exit(1);
25
+ });
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Four.meme - get TokenCreate, TokenPurchase, TokenSale, LiquidityAdded from TokenManager2 (V2 only).
4
+ * Usage: npx tsx get-recent-events.ts <chainId> <fromBlock> [toBlock]
5
+ * If toBlock omitted, uses "latest". chainId 56 = BSC (TokenManager2 on BSC only for create; events on BSC).
6
+ */
7
+
8
+ import { createPublicClient, http, parseAbiItem } from 'viem';
9
+ import { bsc } from 'viem/chains';
10
+
11
+ const TOKEN_MANAGER2_BSC = '0x5c952063c7fc8610FFDB798152D69F0B9550762b' as const;
12
+
13
+ const EVENTS = [
14
+ parseAbiItem(
15
+ 'event TokenCreate(address creator, address token, uint256 requestId, string name, string symbol, uint256 totalSupply, uint256 launchTime, uint256 launchFee)'
16
+ ),
17
+ parseAbiItem(
18
+ 'event TokenPurchase(address token, address account, uint256 price, uint256 amount, uint256 cost, uint256 fee, uint256 offers, uint256 funds)'
19
+ ),
20
+ parseAbiItem(
21
+ 'event TokenSale(address token, address account, uint256 price, uint256 amount, uint256 cost, uint256 fee, uint256 offers, uint256 funds)'
22
+ ),
23
+ parseAbiItem('event LiquidityAdded(address base, uint256 offers, address quote, uint256 funds)'),
24
+ ];
25
+
26
+ function getRpcUrl(chainId: number): string {
27
+ if (chainId !== 56) {
28
+ console.error('Events are only on BSC (chainId 56). TokenManager2 is on BSC.');
29
+ process.exit(1);
30
+ }
31
+ return process.env.BSC_RPC_URL || 'https://bsc-dataseed.binance.org';
32
+ }
33
+
34
+ async function main() {
35
+ const chainId = parseInt(process.argv[2], 10);
36
+ const fromBlock = process.argv[3];
37
+ const toBlock = process.argv[4]; // optional, "latest" if omitted
38
+
39
+ if (!chainId || isNaN(chainId) || chainId !== 56 || !fromBlock) {
40
+ console.error('Usage: npx tsx get-recent-events.ts 56 <fromBlock> [toBlock]');
41
+ console.error('Example: npx tsx get-recent-events.ts 56 45000000 45000100');
42
+ process.exit(1);
43
+ }
44
+
45
+ const client = createPublicClient({
46
+ chain: bsc,
47
+ transport: http(getRpcUrl(chainId)),
48
+ });
49
+
50
+ const fromBlockBigInt = fromBlock === 'latest' ? 'latest' : BigInt(fromBlock);
51
+ const toBlockOpt: 'latest' | bigint | undefined = !toBlock
52
+ ? undefined
53
+ : toBlock === 'latest'
54
+ ? 'latest'
55
+ : BigInt(toBlock);
56
+
57
+ const logs = await client.getLogs({
58
+ address: TOKEN_MANAGER2_BSC,
59
+ events: EVENTS,
60
+ fromBlock: fromBlockBigInt,
61
+ ...(toBlockOpt !== undefined && { toBlock: toBlockOpt }),
62
+ });
63
+
64
+ const out = logs.map((log) => ({
65
+ eventName: log.eventName,
66
+ blockNumber: Number(log.blockNumber),
67
+ transactionHash: log.transactionHash,
68
+ args: log.args as object,
69
+ }));
70
+ console.log(JSON.stringify(out, null, 2));
71
+ }
72
+
73
+ main().catch((e) => {
74
+ console.error(e.message || e);
75
+ process.exit(1);
76
+ });
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Four.meme - query TaxToken contract for fee/tax config (BSC only).
4
+ * Only works for TaxToken type (creatorType 5). Usage: npx tsx get-tax-token-info.ts <tokenAddress>
5
+ */
6
+
7
+ import { createPublicClient, http } from 'viem';
8
+ import { bsc } from 'viem/chains';
9
+
10
+ const TAX_TOKEN_ABI = [
11
+ { name: 'feeRate', type: 'function', stateMutability: 'view', inputs: [], outputs: [{ type: 'uint256' }] },
12
+ { name: 'rateFounder', type: 'function', stateMutability: 'view', inputs: [], outputs: [{ type: 'uint256' }] },
13
+ { name: 'rateHolder', type: 'function', stateMutability: 'view', inputs: [], outputs: [{ type: 'uint256' }] },
14
+ { name: 'rateBurn', type: 'function', stateMutability: 'view', inputs: [], outputs: [{ type: 'uint256' }] },
15
+ { name: 'rateLiquidity', type: 'function', stateMutability: 'view', inputs: [], outputs: [{ type: 'uint256' }] },
16
+ { name: 'minDispatch', type: 'function', stateMutability: 'view', inputs: [], outputs: [{ type: 'uint256' }] },
17
+ { name: 'minShare', type: 'function', stateMutability: 'view', inputs: [], outputs: [{ type: 'uint256' }] },
18
+ { name: 'quote', type: 'function', stateMutability: 'view', inputs: [], outputs: [{ type: 'address' }] },
19
+ { name: 'founder', type: 'function', stateMutability: 'view', inputs: [], outputs: [{ type: 'address' }] },
20
+ ] as const;
21
+
22
+ const RPC_URL = process.env.BSC_RPC_URL || 'https://bsc-dataseed.binance.org';
23
+
24
+ async function main() {
25
+ const tokenAddress = process.argv[2] as `0x${string}`;
26
+
27
+ if (!tokenAddress) {
28
+ console.error('Usage: npx tsx get-tax-token-info.ts <tokenAddress>');
29
+ console.error('BSC only. Token must be TaxToken type.');
30
+ process.exit(1);
31
+ }
32
+
33
+ const client = createPublicClient({
34
+ chain: bsc,
35
+ transport: http(RPC_URL),
36
+ });
37
+
38
+ const [feeRate, rateFounder, rateHolder, rateBurn, rateLiquidity, minDispatch, minShare, quote, founder] =
39
+ await Promise.all([
40
+ client.readContract({ address: tokenAddress, abi: TAX_TOKEN_ABI, functionName: 'feeRate' }),
41
+ client.readContract({ address: tokenAddress, abi: TAX_TOKEN_ABI, functionName: 'rateFounder' }),
42
+ client.readContract({ address: tokenAddress, abi: TAX_TOKEN_ABI, functionName: 'rateHolder' }),
43
+ client.readContract({ address: tokenAddress, abi: TAX_TOKEN_ABI, functionName: 'rateBurn' }),
44
+ client.readContract({ address: tokenAddress, abi: TAX_TOKEN_ABI, functionName: 'rateLiquidity' }),
45
+ client.readContract({ address: tokenAddress, abi: TAX_TOKEN_ABI, functionName: 'minDispatch' }),
46
+ client.readContract({ address: tokenAddress, abi: TAX_TOKEN_ABI, functionName: 'minShare' }),
47
+ client.readContract({ address: tokenAddress, abi: TAX_TOKEN_ABI, functionName: 'quote' }),
48
+ client.readContract({ address: tokenAddress, abi: TAX_TOKEN_ABI, functionName: 'founder' }),
49
+ ]);
50
+
51
+ const out = {
52
+ feeRateBps: Number(feeRate),
53
+ feeRatePercent: Number(feeRate) / 100,
54
+ rateFounder: Number(rateFounder),
55
+ rateHolder: Number(rateHolder),
56
+ rateBurn: Number(rateBurn),
57
+ rateLiquidity: Number(rateLiquidity),
58
+ minDispatch: minDispatch.toString(),
59
+ minShare: minShare.toString(),
60
+ quote: quote === '0x0000000000000000000000000000000000000000' ? null : quote,
61
+ founder: founder === '0x0000000000000000000000000000000000000000' ? null : founder,
62
+ };
63
+ console.log(JSON.stringify(out, null, 2));
64
+ }
65
+
66
+ main().catch((e) => {
67
+ console.error(e.message || e);
68
+ process.exit(1);
69
+ });
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Four.meme - get token info via TokenManagerHelper3 (BSC only).
4
+ * Usage: npx tsx get-token-info.ts <tokenAddress>
5
+ */
6
+
7
+ import { createPublicClient, http } from 'viem';
8
+ import { bsc } from 'viem/chains';
9
+
10
+ const HELPER_ADDRESS = '0xF251F83e40a78868FcfA3FA4599Dad6494E46034' as const;
11
+
12
+ const HELPER_ABI = [
13
+ {
14
+ name: 'getTokenInfo',
15
+ type: 'function',
16
+ stateMutability: 'view',
17
+ inputs: [{ name: 'token', type: 'address' }],
18
+ outputs: [
19
+ { name: 'version', type: 'uint256' },
20
+ { name: 'tokenManager', type: 'address' },
21
+ { name: 'quote', type: 'address' },
22
+ { name: 'lastPrice', type: 'uint256' },
23
+ { name: 'tradingFeeRate', type: 'uint256' },
24
+ { name: 'minTradingFee', type: 'uint256' },
25
+ { name: 'launchTime', type: 'uint256' },
26
+ { name: 'offers', type: 'uint256' },
27
+ { name: 'maxOffers', type: 'uint256' },
28
+ { name: 'funds', type: 'uint256' },
29
+ { name: 'maxFunds', type: 'uint256' },
30
+ { name: 'liquidityAdded', type: 'bool' },
31
+ ],
32
+ },
33
+ ] as const;
34
+
35
+ const RPC_URL = process.env.BSC_RPC_URL || 'https://bsc-dataseed.binance.org';
36
+
37
+ async function main() {
38
+ const tokenAddress = process.argv[2] as `0x${string}`;
39
+
40
+ if (!tokenAddress) {
41
+ console.error('Usage: npx tsx get-token-info.ts <tokenAddress>');
42
+ console.error('BSC only.');
43
+ process.exit(1);
44
+ }
45
+
46
+ const client = createPublicClient({
47
+ chain: bsc,
48
+ transport: http(RPC_URL),
49
+ });
50
+
51
+ const helperAddress = HELPER_ADDRESS;
52
+ const result = await client.readContract({
53
+ address: helperAddress,
54
+ abi: HELPER_ABI,
55
+ functionName: 'getTokenInfo',
56
+ args: [tokenAddress],
57
+ });
58
+
59
+ const [
60
+ version,
61
+ tokenManager,
62
+ quote,
63
+ lastPrice,
64
+ tradingFeeRate,
65
+ minTradingFee,
66
+ launchTime,
67
+ offers,
68
+ maxOffers,
69
+ funds,
70
+ maxFunds,
71
+ liquidityAdded,
72
+ ] = result;
73
+
74
+ const out = {
75
+ version: Number(version),
76
+ tokenManager,
77
+ quote: quote === '0x0000000000000000000000000000000000000000' ? null : quote,
78
+ lastPrice: lastPrice.toString(),
79
+ tradingFeeRate: Number(tradingFeeRate) / 10000,
80
+ minTradingFee: minTradingFee.toString(),
81
+ launchTime: Number(launchTime),
82
+ offers: offers.toString(),
83
+ maxOffers: maxOffers.toString(),
84
+ funds: funds.toString(),
85
+ maxFunds: maxFunds.toString(),
86
+ liquidityAdded,
87
+ };
88
+ console.log(JSON.stringify(out, null, 2));
89
+ }
90
+
91
+ main().catch((e) => {
92
+ console.error(e.message || e);
93
+ process.exit(1);
94
+ });