four-flap-meme-sdk 4.1.1 → 4.2.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 (41) hide show
  1. package/dist/chains/index.d.ts +3 -0
  2. package/dist/chains/index.js +4 -0
  3. package/dist/chains/robinhood/constants.d.ts +48 -0
  4. package/dist/chains/robinhood/constants.js +55 -0
  5. package/dist/chains/robinhood/index.d.ts +11 -0
  6. package/dist/chains/robinhood/index.js +9 -0
  7. package/dist/chains/robinhood/longetf/assets.d.ts +11 -0
  8. package/dist/chains/robinhood/longetf/assets.js +4 -0
  9. package/dist/chains/robinhood/longetf/encode.d.ts +41 -0
  10. package/dist/chains/robinhood/longetf/encode.js +116 -0
  11. package/dist/chains/robinhood/longetf/index.d.ts +3 -0
  12. package/dist/chains/robinhood/longetf/index.js +3 -0
  13. package/dist/chains/robinhood/longetf/launch.d.ts +49 -0
  14. package/dist/chains/robinhood/longetf/launch.js +64 -0
  15. package/dist/chains/robinhood/pons-v2/encode.d.ts +62 -0
  16. package/dist/chains/robinhood/pons-v2/encode.js +115 -0
  17. package/dist/chains/robinhood/pons-v2/index.d.ts +2 -0
  18. package/dist/chains/robinhood/pons-v2/index.js +2 -0
  19. package/dist/chains/robinhood/pons-v2/launch.d.ts +48 -0
  20. package/dist/chains/robinhood/pons-v2/launch.js +107 -0
  21. package/dist/chains/robinhood/robinhood.test.d.ts +1 -0
  22. package/dist/chains/robinhood/robinhood.test.js +103 -0
  23. package/dist/chains/robinhood/sign.d.ts +20 -0
  24. package/dist/chains/robinhood/sign.js +31 -0
  25. package/dist/core/constants/addresses.d.ts +18 -0
  26. package/dist/core/constants/addresses.js +23 -0
  27. package/dist/core/constants/chains.d.ts +16 -0
  28. package/dist/core/constants/chains.js +20 -0
  29. package/dist/core/constants/gas.d.ts +8 -0
  30. package/dist/core/constants/gas.js +9 -0
  31. package/dist/core/constants/index.d.ts +1 -1
  32. package/dist/core/constants/index.js +1 -1
  33. package/dist/exports/root-foundations.d.ts +1 -1
  34. package/dist/exports/root-foundations.js +1 -1
  35. package/dist/exports/root-robinhood.d.ts +9 -0
  36. package/dist/exports/root-robinhood.js +7 -0
  37. package/dist/index.d.ts +1 -0
  38. package/dist/index.js +1 -0
  39. package/dist/utils/constants.d.ts +1 -1
  40. package/dist/utils/constants.js +1 -1
  41. package/package.json +1 -1
@@ -0,0 +1,62 @@
1
+ /**
2
+ * pons v2 发币编码
3
+ *
4
+ * 对齐 https://docs.ponsfamily.com/v2:launchToken / launchAndBuy。
5
+ */
6
+ export declare const PONS_V2_TOKEN_LAUNCHED_TOPIC: string;
7
+ export interface PonsV2Socials {
8
+ twitter: string;
9
+ telegram: string;
10
+ discord: string;
11
+ website: string;
12
+ farcaster: string;
13
+ }
14
+ export interface PonsV2TokenParams {
15
+ name: string;
16
+ symbol: string;
17
+ logo?: string;
18
+ description?: string;
19
+ socials?: Partial<PonsV2Socials>;
20
+ creatorFeeRecipient?: string;
21
+ creatorTaxBps?: number;
22
+ buybackEnabled?: boolean;
23
+ expectedEconomics: string;
24
+ salt?: string;
25
+ }
26
+ export interface PonsV2LaunchCall {
27
+ to: string;
28
+ data: string;
29
+ value: bigint;
30
+ }
31
+ export declare function randomPonsV2Salt(): string;
32
+ export declare function toPonsV2TokenTuple(params: PonsV2TokenParams, options?: {
33
+ requireCreatorFeeRecipient?: boolean;
34
+ }): {
35
+ name: string;
36
+ symbol: string;
37
+ logo: string;
38
+ description: string;
39
+ socials: PonsV2Socials;
40
+ creatorFeeRecipient: string;
41
+ creatorTaxBps: number;
42
+ buybackEnabled: boolean;
43
+ expectedEconomics: string;
44
+ salt: string;
45
+ };
46
+ export declare function encodePonsV2LaunchToken(params: PonsV2TokenParams, launchConfigId: bigint, pairToken?: string, snipeTaxExemptions?: string[]): string;
47
+ export declare function encodePonsV2LaunchAndBuy(params: PonsV2TokenParams, launchConfigId: bigint, pairToken: string, quoteIn: bigint, minTokensOut: bigint, recipient: string, snipeTaxExemptions?: string[]): string;
48
+ export declare function encodePonsV2PreviewLaunchEconomics(launchConfigId: bigint, pairToken: string): string;
49
+ export declare function parsePonsV2TokenFromLogs(logs: ReadonlyArray<{
50
+ topics: readonly string[];
51
+ address: string;
52
+ }>, factory?: string): string | undefined;
53
+ export declare function buildPonsV2LaunchCall(params: {
54
+ token: PonsV2TokenParams;
55
+ launchConfigId: bigint;
56
+ pairToken?: string;
57
+ launchFee: bigint;
58
+ quoteIn?: bigint;
59
+ minTokensOut?: bigint;
60
+ recipient?: string;
61
+ snipeTaxExemptions?: string[];
62
+ }): PonsV2LaunchCall;
@@ -0,0 +1,115 @@
1
+ /**
2
+ * pons v2 发币编码
3
+ *
4
+ * 对齐 https://docs.ponsfamily.com/v2:launchToken / launchAndBuy。
5
+ */
6
+ import { getAddress, hexlify, id, Interface, randomBytes, ZeroAddress } from 'ethers';
7
+ import { PONS_V2_FACTORY, PONS_V2_LAUNCH_AND_BUY, PONS_V2_LAUNCH_AND_BUY_ABI } from '../constants.js';
8
+ const SOCIALS_TUPLE = 'tuple(string twitter, string telegram, string discord, string website, string farcaster)';
9
+ const TOKEN_PARAMS_TUPLE = `tuple(string name, string symbol, string logo, string description, ${SOCIALS_TUPLE} socials, address creatorFeeRecipient, uint16 creatorTaxBps, bool buybackEnabled, bytes32 expectedEconomics, bytes32 salt)`;
10
+ const launchTokenIface = new Interface([
11
+ `function launchToken(${TOKEN_PARAMS_TUPLE} params, uint256 launchConfigId, address pairToken) payable returns (address token, address curve)`,
12
+ ]);
13
+ const launchTokenExemptIface = new Interface([
14
+ `function launchToken(${TOKEN_PARAMS_TUPLE} params, uint256 launchConfigId, address pairToken, address[] snipeTaxExemptions) payable returns (address token, address curve)`,
15
+ ]);
16
+ const previewIface = new Interface([
17
+ 'function previewLaunchEconomics(uint256 launchConfigId, address pairToken) view returns (bytes32)',
18
+ ]);
19
+ const launchAndBuyIface = new Interface(PONS_V2_LAUNCH_AND_BUY_ABI);
20
+ export const PONS_V2_TOKEN_LAUNCHED_TOPIC = id('TokenLaunched(address,address,address,address,uint256,uint256)');
21
+ function emptySocials() {
22
+ return { twitter: '', telegram: '', discord: '', website: '', farcaster: '' };
23
+ }
24
+ export function randomPonsV2Salt() {
25
+ return hexlify(randomBytes(32));
26
+ }
27
+ export function toPonsV2TokenTuple(params, options) {
28
+ const recipient = params.creatorFeeRecipient?.trim()
29
+ ? getAddress(params.creatorFeeRecipient)
30
+ : ZeroAddress;
31
+ if (options?.requireCreatorFeeRecipient && recipient === ZeroAddress) {
32
+ throw new Error('pons launchAndBuy requires an explicit creatorFeeRecipient');
33
+ }
34
+ const socials = { ...emptySocials(), ...params.socials };
35
+ return {
36
+ name: params.name,
37
+ symbol: params.symbol,
38
+ logo: params.logo ?? '',
39
+ description: params.description ?? '',
40
+ socials,
41
+ creatorFeeRecipient: recipient,
42
+ creatorTaxBps: params.creatorTaxBps ?? 0,
43
+ buybackEnabled: params.buybackEnabled ?? true,
44
+ expectedEconomics: params.expectedEconomics,
45
+ salt: params.salt ?? randomPonsV2Salt(),
46
+ };
47
+ }
48
+ export function encodePonsV2LaunchToken(params, launchConfigId, pairToken = ZeroAddress, snipeTaxExemptions) {
49
+ const tuple = toPonsV2TokenTuple(params);
50
+ const pair = pairToken === ZeroAddress ? ZeroAddress : getAddress(pairToken);
51
+ if (snipeTaxExemptions && snipeTaxExemptions.length > 0) {
52
+ if (snipeTaxExemptions.length > 32) {
53
+ throw new Error('pons snipe tax exemption list exceeds 32');
54
+ }
55
+ return launchTokenExemptIface.encodeFunctionData('launchToken', [
56
+ tuple,
57
+ launchConfigId,
58
+ pair,
59
+ snipeTaxExemptions.map((a) => getAddress(a)),
60
+ ]);
61
+ }
62
+ return launchTokenIface.encodeFunctionData('launchToken', [tuple, launchConfigId, pair]);
63
+ }
64
+ export function encodePonsV2LaunchAndBuy(params, launchConfigId, pairToken, quoteIn, minTokensOut, recipient, snipeTaxExemptions = []) {
65
+ const tuple = toPonsV2TokenTuple(params, { requireCreatorFeeRecipient: true });
66
+ const pair = pairToken === ZeroAddress ? ZeroAddress : getAddress(pairToken);
67
+ if (snipeTaxExemptions.length > 32) {
68
+ throw new Error('pons snipe tax exemption list exceeds 32');
69
+ }
70
+ return launchAndBuyIface.encodeFunctionData('launchAndBuy', [
71
+ tuple,
72
+ launchConfigId,
73
+ pair,
74
+ quoteIn,
75
+ minTokensOut,
76
+ getAddress(recipient),
77
+ snipeTaxExemptions.map((a) => getAddress(a)),
78
+ ]);
79
+ }
80
+ export function encodePonsV2PreviewLaunchEconomics(launchConfigId, pairToken) {
81
+ const pair = pairToken === ZeroAddress ? ZeroAddress : getAddress(pairToken);
82
+ return previewIface.encodeFunctionData('previewLaunchEconomics', [launchConfigId, pair]);
83
+ }
84
+ export function parsePonsV2TokenFromLogs(logs, factory = PONS_V2_FACTORY) {
85
+ const factoryAddr = getAddress(factory);
86
+ for (const log of logs) {
87
+ if (log.topics[0] !== PONS_V2_TOKEN_LAUNCHED_TOPIC || log.topics.length < 2)
88
+ continue;
89
+ if (getAddress(log.address) !== factoryAddr && getAddress(log.address) !== getAddress(PONS_V2_LAUNCH_AND_BUY)) {
90
+ // TokenLaunched is emitted by the factory; router path still originates there.
91
+ }
92
+ return getAddress(`0x${log.topics[1].slice(26)}`);
93
+ }
94
+ return undefined;
95
+ }
96
+ export function buildPonsV2LaunchCall(params) {
97
+ const pairToken = params.pairToken ?? ZeroAddress;
98
+ const quoteIn = params.quoteIn ?? 0n;
99
+ if (quoteIn > 0n) {
100
+ if (!params.recipient) {
101
+ throw new Error('pons launchAndBuy needs a recipient');
102
+ }
103
+ const nativePair = pairToken === ZeroAddress;
104
+ return {
105
+ to: PONS_V2_LAUNCH_AND_BUY,
106
+ data: encodePonsV2LaunchAndBuy(params.token, params.launchConfigId, pairToken, quoteIn, params.minTokensOut ?? 0n, params.recipient, params.snipeTaxExemptions ?? []),
107
+ value: nativePair ? params.launchFee + quoteIn : params.launchFee,
108
+ };
109
+ }
110
+ return {
111
+ to: PONS_V2_FACTORY,
112
+ data: encodePonsV2LaunchToken(params.token, params.launchConfigId, pairToken, params.snipeTaxExemptions),
113
+ value: params.launchFee,
114
+ };
115
+ }
@@ -0,0 +1,2 @@
1
+ export { PONS_V2_TOKEN_LAUNCHED_TOPIC, randomPonsV2Salt, toPonsV2TokenTuple, encodePonsV2LaunchToken, encodePonsV2LaunchAndBuy, encodePonsV2PreviewLaunchEconomics, parsePonsV2TokenFromLogs, buildPonsV2LaunchCall, type PonsV2Socials, type PonsV2TokenParams, type PonsV2LaunchCall, } from './encode.js';
2
+ export { getPonsV2LaunchFee, previewPonsV2LaunchEconomics, canPonsV2Launch, listOpenPonsV2LaunchConfigs, signPonsV2Launch, type PonsV2LaunchConfig, type PonsV2LaunchParams, type PonsV2LaunchSignResult, } from './launch.js';
@@ -0,0 +1,2 @@
1
+ export { PONS_V2_TOKEN_LAUNCHED_TOPIC, randomPonsV2Salt, toPonsV2TokenTuple, encodePonsV2LaunchToken, encodePonsV2LaunchAndBuy, encodePonsV2PreviewLaunchEconomics, parsePonsV2TokenFromLogs, buildPonsV2LaunchCall, } from './encode.js';
2
+ export { getPonsV2LaunchFee, previewPonsV2LaunchEconomics, canPonsV2Launch, listOpenPonsV2LaunchConfigs, signPonsV2Launch, } from './launch.js';
@@ -0,0 +1,48 @@
1
+ /**
2
+ * pons v2 发币:读 launchFee + expectedEconomics,再签 launchToken 或 launchAndBuy
3
+ */
4
+ import { type RobinhoodSignedTx } from '../sign.js';
5
+ export interface PonsV2LaunchConfig {
6
+ id: bigint;
7
+ supply: bigint;
8
+ curveFeeBps: bigint;
9
+ phantomQuote: bigint;
10
+ graduationThreshold: bigint;
11
+ poolFee: number;
12
+ tickSpacing: number;
13
+ enabled: boolean;
14
+ }
15
+ export interface PonsV2LaunchParams {
16
+ rpcUrl?: string;
17
+ privateKey: string;
18
+ name: string;
19
+ symbol: string;
20
+ logo?: string;
21
+ description?: string;
22
+ twitter?: string;
23
+ telegram?: string;
24
+ website?: string;
25
+ creatorFeeRecipient?: string;
26
+ creatorTaxBps?: number;
27
+ buybackEnabled?: boolean;
28
+ launchConfigId?: bigint;
29
+ pairToken?: string;
30
+ quoteIn?: bigint;
31
+ minTokensOut?: bigint;
32
+ snipeTaxExemptions?: string[];
33
+ salt?: string;
34
+ gasPrice?: bigint;
35
+ nonce?: number;
36
+ }
37
+ export interface PonsV2LaunchSignResult extends RobinhoodSignedTx {
38
+ launchFee: bigint;
39
+ expectedEconomics: string;
40
+ salt: string;
41
+ to: string;
42
+ value: bigint;
43
+ }
44
+ export declare function getPonsV2LaunchFee(rpcUrl?: string): Promise<bigint>;
45
+ export declare function previewPonsV2LaunchEconomics(launchConfigId: bigint, pairToken?: string, rpcUrl?: string): Promise<string>;
46
+ export declare function canPonsV2Launch(account: string, rpcUrl?: string): Promise<boolean>;
47
+ export declare function listOpenPonsV2LaunchConfigs(rpcUrl?: string): Promise<PonsV2LaunchConfig[]>;
48
+ export declare function signPonsV2Launch(params: PonsV2LaunchParams): Promise<PonsV2LaunchSignResult>;
@@ -0,0 +1,107 @@
1
+ /**
2
+ * pons v2 发币:读 launchFee + expectedEconomics,再签 launchToken 或 launchAndBuy
3
+ */
4
+ import { Contract, JsonRpcProvider, Wallet, ZeroAddress } from 'ethers';
5
+ import { PONS_V2_FACTORY, PONS_V2_FACTORY_ABI, PONS_V2_LAUNCH_GAS_LIMIT, ROBINHOOD_CHAIN_ID, ROBINHOOD_RPC_URL, } from '../constants.js';
6
+ import { signRobinhoodTx } from '../sign.js';
7
+ import { buildPonsV2LaunchCall, encodePonsV2PreviewLaunchEconomics, randomPonsV2Salt, } from './encode.js';
8
+ export async function getPonsV2LaunchFee(rpcUrl = ROBINHOOD_RPC_URL) {
9
+ const provider = new JsonRpcProvider(rpcUrl, ROBINHOOD_CHAIN_ID, { staticNetwork: true });
10
+ const factory = new Contract(PONS_V2_FACTORY, PONS_V2_FACTORY_ABI, provider);
11
+ return factory.launchFee();
12
+ }
13
+ export async function previewPonsV2LaunchEconomics(launchConfigId, pairToken = ZeroAddress, rpcUrl = ROBINHOOD_RPC_URL) {
14
+ const provider = new JsonRpcProvider(rpcUrl, ROBINHOOD_CHAIN_ID, { staticNetwork: true });
15
+ const data = encodePonsV2PreviewLaunchEconomics(launchConfigId, pairToken);
16
+ return provider.call({ to: PONS_V2_FACTORY, data });
17
+ }
18
+ export async function canPonsV2Launch(account, rpcUrl = ROBINHOOD_RPC_URL) {
19
+ const provider = new JsonRpcProvider(rpcUrl, ROBINHOOD_CHAIN_ID, { staticNetwork: true });
20
+ const factory = new Contract(PONS_V2_FACTORY, PONS_V2_FACTORY_ABI, provider);
21
+ return factory.canLaunch(account);
22
+ }
23
+ export async function listOpenPonsV2LaunchConfigs(rpcUrl = ROBINHOOD_RPC_URL) {
24
+ const provider = new JsonRpcProvider(rpcUrl, ROBINHOOD_CHAIN_ID, { staticNetwork: true });
25
+ const factory = new Contract(PONS_V2_FACTORY, PONS_V2_FACTORY_ABI, provider);
26
+ const count = Number(await factory.launchConfigCount());
27
+ const configs = [];
28
+ for (let id = 0; id < count; id++) {
29
+ const row = (await factory.getLaunchConfig(BigInt(id)));
30
+ const enabled = Boolean(row.enabled);
31
+ if (!enabled)
32
+ continue;
33
+ configs.push({
34
+ id: BigInt(id),
35
+ supply: row.supply,
36
+ curveFeeBps: row.curveFeeBps,
37
+ phantomQuote: row.phantomQuote,
38
+ graduationThreshold: row.graduationThreshold,
39
+ poolFee: Number(row.poolFee),
40
+ tickSpacing: Number(row.tickSpacing),
41
+ enabled,
42
+ });
43
+ }
44
+ return configs;
45
+ }
46
+ export async function signPonsV2Launch(params) {
47
+ const rpcUrl = params.rpcUrl ?? ROBINHOOD_RPC_URL;
48
+ const wallet = new Wallet(params.privateKey);
49
+ const pairToken = params.pairToken ?? ZeroAddress;
50
+ const launchConfigId = params.launchConfigId ?? 0n;
51
+ const salt = params.salt ?? randomPonsV2Salt();
52
+ const creatorFeeRecipient = params.creatorFeeRecipient?.trim()
53
+ ? params.creatorFeeRecipient
54
+ : wallet.address;
55
+ const allowed = await canPonsV2Launch(wallet.address, rpcUrl);
56
+ if (!allowed) {
57
+ throw new Error('pons factory is not allowing this wallet to launch');
58
+ }
59
+ const [launchFee, expectedEconomics] = await Promise.all([
60
+ getPonsV2LaunchFee(rpcUrl),
61
+ previewPonsV2LaunchEconomics(launchConfigId, pairToken, rpcUrl),
62
+ ]);
63
+ const token = {
64
+ name: params.name,
65
+ symbol: params.symbol,
66
+ logo: params.logo,
67
+ description: params.description,
68
+ socials: {
69
+ twitter: params.twitter ?? '',
70
+ telegram: params.telegram ?? '',
71
+ website: params.website ?? '',
72
+ },
73
+ creatorFeeRecipient,
74
+ creatorTaxBps: params.creatorTaxBps,
75
+ buybackEnabled: params.buybackEnabled,
76
+ expectedEconomics,
77
+ salt,
78
+ };
79
+ const call = buildPonsV2LaunchCall({
80
+ token,
81
+ launchConfigId,
82
+ pairToken,
83
+ launchFee,
84
+ quoteIn: params.quoteIn,
85
+ minTokensOut: params.minTokensOut,
86
+ recipient: wallet.address,
87
+ snipeTaxExemptions: params.snipeTaxExemptions,
88
+ });
89
+ const signed = await signRobinhoodTx({
90
+ rpcUrl,
91
+ privateKey: params.privateKey,
92
+ to: call.to,
93
+ data: call.data,
94
+ value: call.value,
95
+ gasLimit: PONS_V2_LAUNCH_GAS_LIMIT,
96
+ gasPrice: params.gasPrice,
97
+ nonce: params.nonce,
98
+ });
99
+ return {
100
+ ...signed,
101
+ launchFee,
102
+ expectedEconomics,
103
+ salt,
104
+ to: call.to,
105
+ value: call.value,
106
+ };
107
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,103 @@
1
+ import { id, Interface, keccak256, toUtf8Bytes, ZeroAddress } from 'ethers';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { ADDRESSES, CHAINS, isRobinhoodChain } from '../../core/constants/index.js';
4
+ import { encodeLongetfDeployVault, encodeLongetfRegister, longetfEffectiveSalt, longetfVaultInitCodeHash, mineLongetfVaultSalt, parseLongetfTokenFromLogs, patchLongetfLaunchCalldata, predictLongetfVault, } from './longetf/encode.js';
5
+ import { LONGETF_LAUNCH_TEMPLATE } from './longetf/assets.js';
6
+ import { encodePonsV2LaunchAndBuy, encodePonsV2LaunchToken, parsePonsV2TokenFromLogs, PONS_V2_TOKEN_LAUNCHED_TOPIC, } from './pons-v2/encode.js';
7
+ import { LONGETF_FACTORY, LONG_XYZ_PROTOCOL_BENEFICIARY, PONS_V2_FACTORY, PONS_V2_LAUNCH_AND_BUY, } from './constants.js';
8
+ describe('Robinhood LONGETF / pons v2 launch encoding', () => {
9
+ const creator = '0x1111111111111111111111111111111111111111';
10
+ it('exposes Robinhood chain id 4663', () => {
11
+ expect(CHAINS.ROBINHOOD.chainId).toBe(4663);
12
+ expect(isRobinhoodChain(4663)).toBe(true);
13
+ expect(ADDRESSES.ROBINHOOD.LongetfFactory.toLowerCase()).toBe(LONGETF_FACTORY.toLowerCase());
14
+ });
15
+ it('mines a vault that sorts above the long.xyz protocol beneficiary', () => {
16
+ const mined = mineLongetfVaultSalt(creator);
17
+ expect(predictLongetfVault(mined.salt, creator)).toBe(mined.vault);
18
+ expect(BigInt(mined.vault) > BigInt(LONG_XYZ_PROTOCOL_BENEFICIARY)).toBe(true);
19
+ expect(longetfEffectiveSalt(mined.salt, creator)).toMatch(/^0x[0-9a-f]{64}$/);
20
+ expect(longetfVaultInitCodeHash()).toMatch(/^0x[0-9a-f]{64}$/);
21
+ });
22
+ it('encodes deployVault / register with the live selectors', () => {
23
+ const salt = '0x' + '11'.repeat(32);
24
+ const deploy = encodeLongetfDeployVault(salt, creator);
25
+ expect(deploy.slice(0, 10)).toBe(id('deployVault(bytes32,address,bytes)').slice(0, 10));
26
+ const token = '0x2222222221111111111111111111111111111111';
27
+ const register = encodeLongetfRegister(token, creator);
28
+ expect(register.slice(0, 10)).toBe(id('register(address,address)').slice(0, 10));
29
+ });
30
+ it('patches long.xyz create() template name, ticker, anchor and vault', () => {
31
+ const vault = '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
32
+ const anchor = '0xaf3d76f1834a1d425780943c99ea8a608f8a93f9';
33
+ const data = patchLongetfLaunchCalldata({
34
+ name: 'TestCoin',
35
+ symbol: 'HOOD',
36
+ anchor,
37
+ vault,
38
+ metaUri: '',
39
+ launchSalt: '0x' + '22'.repeat(32),
40
+ });
41
+ expect(data.startsWith('0x882db707')).toBe(true);
42
+ expect(data.length).toBe(LONGETF_LAUNCH_TEMPLATE.length);
43
+ expect(data.toLowerCase()).toContain(vault.slice(2).toLowerCase());
44
+ expect(data.toLowerCase()).toContain(anchor.slice(2).toLowerCase());
45
+ expect(data).toContain('484f4f44');
46
+ });
47
+ it('rejects illegal LONGETF tickers', () => {
48
+ expect(() => patchLongetfLaunchCalldata({
49
+ name: 'X',
50
+ symbol: 'H00D',
51
+ anchor: creator,
52
+ vault: creator,
53
+ })).toThrow(/letters only/i);
54
+ });
55
+ it('parses a mint Transfer as the LONGETF token', () => {
56
+ const token = '0x3333333333333333333333333333333333333333';
57
+ const topic = keccak256(toUtf8Bytes('Transfer(address,address,uint256)'));
58
+ const parsed = parseLongetfTokenFromLogs([
59
+ {
60
+ address: token,
61
+ topics: [topic, '0x' + '00'.repeat(32), '0x' + creator.slice(2).padStart(64, '0')],
62
+ },
63
+ ]);
64
+ expect(parsed?.toLowerCase()).toBe(token);
65
+ });
66
+ it('encodes pons v2 launchToken and launchAndBuy', () => {
67
+ const economics = '0x' + 'ab'.repeat(32);
68
+ const launch = encodePonsV2LaunchToken({
69
+ name: 'Example',
70
+ symbol: 'EXMPL',
71
+ expectedEconomics: economics,
72
+ salt: '0x' + '33'.repeat(32),
73
+ }, 0n, ZeroAddress);
74
+ expect(launch.startsWith('0x')).toBe(true);
75
+ expect(launch.length).toBeGreaterThan(10);
76
+ const buy = encodePonsV2LaunchAndBuy({
77
+ name: 'Example',
78
+ symbol: 'EXMPL',
79
+ expectedEconomics: economics,
80
+ salt: '0x' + '33'.repeat(32),
81
+ creatorFeeRecipient: creator,
82
+ }, 0n, ZeroAddress, 10n ** 16n, 0n, creator, []);
83
+ expect(buy.startsWith('0x')).toBe(true);
84
+ expect(PONS_V2_LAUNCH_AND_BUY.toLowerCase()).toMatch(/^0x[0-9a-f]{40}$/);
85
+ expect(PONS_V2_FACTORY.toLowerCase()).toMatch(/^0x[0-9a-f]{40}$/);
86
+ });
87
+ it('requires creatorFeeRecipient on launchAndBuy', () => {
88
+ expect(() => encodePonsV2LaunchAndBuy({ name: 'A', symbol: 'B', expectedEconomics: '0x' + '00'.repeat(32) }, 0n, ZeroAddress, 1n, 0n, creator)).toThrow(/creatorFeeRecipient/);
89
+ });
90
+ it('parses TokenLaunched topic0 as the pons token', () => {
91
+ const token = '0x4444444444444444444444444444444444444444';
92
+ const parsed = parsePonsV2TokenFromLogs([
93
+ {
94
+ address: PONS_V2_FACTORY,
95
+ topics: [PONS_V2_TOKEN_LAUNCHED_TOPIC, '0x' + token.slice(2).padStart(64, '0')],
96
+ },
97
+ ]);
98
+ expect(parsed?.toLowerCase()).toBe(token);
99
+ expect(PONS_V2_TOKEN_LAUNCHED_TOPIC).toBe(new Interface([
100
+ 'event TokenLaunched(address indexed token, address indexed curve, address indexed deployer, address pairToken, uint256 launchConfigId, uint256 graduationThreshold)',
101
+ ]).getEvent('TokenLaunched').topicHash);
102
+ });
103
+ });
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Robinhood Chain EIP-1559 签名
3
+ */
4
+ export interface RobinhoodSignParams {
5
+ rpcUrl?: string;
6
+ privateKey: string;
7
+ to: string;
8
+ data: string;
9
+ value?: bigint;
10
+ gasLimit: bigint;
11
+ gasPrice?: bigint;
12
+ nonce?: number;
13
+ }
14
+ export interface RobinhoodSignedTx {
15
+ signedTx: string;
16
+ from: string;
17
+ nonce: number;
18
+ hash: string;
19
+ }
20
+ export declare function signRobinhoodTx(params: RobinhoodSignParams): Promise<RobinhoodSignedTx>;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Robinhood Chain EIP-1559 签名
3
+ */
4
+ import { JsonRpcProvider, Transaction, Wallet } from 'ethers';
5
+ import { ROBINHOOD_CHAIN_ID, ROBINHOOD_RPC_URL } from './constants.js';
6
+ export async function signRobinhoodTx(params) {
7
+ const rpcUrl = params.rpcUrl ?? ROBINHOOD_RPC_URL;
8
+ const provider = new JsonRpcProvider(rpcUrl, ROBINHOOD_CHAIN_ID, { staticNetwork: true });
9
+ const wallet = new Wallet(params.privateKey, provider);
10
+ const nonce = params.nonce ?? (await wallet.getNonce());
11
+ const feeData = await provider.getFeeData();
12
+ const maxFeePerGas = params.gasPrice ?? feeData.maxFeePerGas ?? feeData.gasPrice ?? 1000000000n;
13
+ const signedTx = await wallet.signTransaction({
14
+ to: params.to,
15
+ value: params.value ?? 0n,
16
+ data: params.data,
17
+ nonce,
18
+ chainId: ROBINHOOD_CHAIN_ID,
19
+ type: 2,
20
+ maxFeePerGas,
21
+ maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? 0n,
22
+ gasLimit: params.gasLimit,
23
+ });
24
+ const tx = Transaction.from(signedTx);
25
+ return {
26
+ signedTx,
27
+ from: wallet.address,
28
+ nonce,
29
+ hash: tx.hash ?? '',
30
+ };
31
+ }
@@ -138,6 +138,24 @@ export declare const ADDRESSES: {
138
138
  readonly IroSwapV2Router: "0x93b1199fA96f6b9b1f993AA0226eB6076968a9BF";
139
139
  readonly IroSwapV2Factory: "0xdE4360F6Cd386FD70Db1386F920138177e3967df";
140
140
  };
141
+ readonly ROBINHOOD: {
142
+ readonly WETH: "0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73";
143
+ readonly Multicall3: "0xcA11bde05977b3631167028862bE2a173976CA11";
144
+ /** LONGETF factory — deployVault / register */
145
+ readonly LongetfFactory: "0xd2ba46aeffec4bfddde62c2dd3e8c76c5c9aeedc";
146
+ /** long.xyz Airlock launcher create() */
147
+ readonly LongXyzLauncher: "0x22e99278308B393ea1260859B181AD7E78f5eeED";
148
+ /** long.xyz pool hook — getBeneficiaries / updateBeneficiary */
149
+ readonly LongXyzHook: "0x4e3468951d49f2eea976ed0d6e75ffcb44a9a544";
150
+ /** long.xyz 5% protocol fee recipient; vault CREATE2 must sort above this */
151
+ readonly LongXyzProtocolBeneficiary: "0x21e2ce70511e4fe542a97708e89520471daa7a66";
152
+ /** pons v2 factory — launchToken */
153
+ readonly PonsV2Factory: "0x7eD598BcEf8bd9Edd8C97A195C6d13f40801EC7e";
154
+ /** pons v2 launch-and-buy router */
155
+ readonly PonsV2LaunchAndBuy: "0xe33E9E479dF8802cb0866d5d05258bEc4cF62948";
156
+ readonly PonsV2MemeHook: "0xE5e702641Ea86F4ae6cC3cDaeD2B886f976Be044";
157
+ readonly PonsV2Locker: "0x267444D099b10fB5Ed7c3Cc7B7c767AdcA574952";
158
+ };
141
159
  };
142
160
  export declare const ENI_SPENDERS: {
143
161
  /** Phase 1 Direct 模式: 需要 approve 3 个 spender (含 IROSwap) */
@@ -202,6 +202,27 @@ export const ADDRESSES = {
202
202
  IroSwapV2Router: '0x93b1199fA96f6b9b1f993AA0226eB6076968a9BF',
203
203
  IroSwapV2Factory: '0xdE4360F6Cd386FD70Db1386F920138177e3967df',
204
204
  },
205
+ // ========================================================================
206
+ // Robinhood Chain (Chain ID: 4663)
207
+ // ========================================================================
208
+ ROBINHOOD: {
209
+ WETH: '0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73',
210
+ Multicall3: '0xcA11bde05977b3631167028862bE2a173976CA11',
211
+ /** LONGETF factory — deployVault / register */
212
+ LongetfFactory: '0xd2ba46aeffec4bfddde62c2dd3e8c76c5c9aeedc',
213
+ /** long.xyz Airlock launcher create() */
214
+ LongXyzLauncher: '0x22e99278308B393ea1260859B181AD7E78f5eeED',
215
+ /** long.xyz pool hook — getBeneficiaries / updateBeneficiary */
216
+ LongXyzHook: '0x4e3468951d49f2eea976ed0d6e75ffcb44a9a544',
217
+ /** long.xyz 5% protocol fee recipient; vault CREATE2 must sort above this */
218
+ LongXyzProtocolBeneficiary: '0x21e2ce70511e4fe542a97708e89520471daa7a66',
219
+ /** pons v2 factory — launchToken */
220
+ PonsV2Factory: '0x7eD598BcEf8bd9Edd8C97A195C6d13f40801EC7e',
221
+ /** pons v2 launch-and-buy router */
222
+ PonsV2LaunchAndBuy: '0xe33E9E479dF8802cb0866d5d05258bEc4cF62948',
223
+ PonsV2MemeHook: '0xE5e702641Ea86F4ae6cC3cDaeD2B886f976Be044',
224
+ PonsV2Locker: '0x267444D099b10fB5Ed7c3Cc7B7c767AdcA574952',
225
+ },
205
226
  };
206
227
  // ============================================================================
207
228
  // ENI Spender 配置(一键授权用)
@@ -262,6 +283,8 @@ export function getWrappedNativeAddress(chainId) {
262
283
  return ADDRESSES.BASE.WETH;
263
284
  case 173:
264
285
  return ADDRESSES.ENI.WEGAS;
286
+ case 4663:
287
+ return ADDRESSES.ROBINHOOD.WETH;
265
288
  default:
266
289
  return undefined;
267
290
  }
@@ -104,6 +104,18 @@ export declare const CHAINS: {
104
104
  readonly explorer: "https://scan.eniac.network";
105
105
  readonly confirmationTimeout: 60000;
106
106
  };
107
+ readonly ROBINHOOD: {
108
+ readonly chainId: 4663;
109
+ readonly name: "Robinhood Chain";
110
+ readonly nativeCurrency: "ETH";
111
+ readonly wrappedNative: "WETH";
112
+ readonly rpcUrls: {
113
+ readonly default: "https://rpc.mainnet.chain.robinhood.com";
114
+ };
115
+ readonly blockTime: 250;
116
+ readonly explorer: "https://robinhoodchain.blockscout.com";
117
+ readonly confirmationTimeout: 120000;
118
+ };
107
119
  };
108
120
  export declare const CHAIN_ID_MAP: Record<string, number>;
109
121
  export type ChainKey = keyof typeof CHAINS;
@@ -140,6 +152,10 @@ export declare function isMonadChain(chainId: number): boolean;
140
152
  * 判断是否为 ENI 链
141
153
  */
142
154
  export declare function isEniChain(chainId: number): boolean;
155
+ /**
156
+ * 判断是否为 Robinhood Chain
157
+ */
158
+ export declare function isRobinhoodChain(chainId: number): boolean;
143
159
  /**
144
160
  * 获取确认超时时间
145
161
  */
@@ -107,6 +107,18 @@ export const CHAINS = {
107
107
  explorer: 'https://scan.eniac.network',
108
108
  confirmationTimeout: 60000,
109
109
  },
110
+ ROBINHOOD: {
111
+ chainId: 4663,
112
+ name: 'Robinhood Chain',
113
+ nativeCurrency: 'ETH',
114
+ wrappedNative: 'WETH',
115
+ rpcUrls: {
116
+ default: 'https://rpc.mainnet.chain.robinhood.com',
117
+ },
118
+ blockTime: 250,
119
+ explorer: 'https://robinhoodchain.blockscout.com',
120
+ confirmationTimeout: 120000,
121
+ },
110
122
  };
111
123
  // ============================================================================
112
124
  // Chain ID 映射表(用于快速查找)
@@ -120,9 +132,11 @@ export const CHAIN_ID_MAP = {
120
132
  MORPH: CHAINS.MORPH.chainId,
121
133
  ETHEREUM: CHAINS.ETHEREUM.chainId,
122
134
  ENI: CHAINS.ENI.chainId,
135
+ ROBINHOOD: CHAINS.ROBINHOOD.chainId,
123
136
  // 别名
124
137
  'X Layer': CHAINS.XLAYER.chainId,
125
138
  'Arbitrum One': CHAINS.ARBITRUM_ONE.chainId,
139
+ 'Robinhood Chain': CHAINS.ROBINHOOD.chainId,
126
140
  ETH: CHAINS.ETHEREUM.chainId,
127
141
  };
128
142
  // ============================================================================
@@ -176,6 +190,12 @@ export function isMonadChain(chainId) {
176
190
  export function isEniChain(chainId) {
177
191
  return chainId === CHAINS.ENI.chainId;
178
192
  }
193
+ /**
194
+ * 判断是否为 Robinhood Chain
195
+ */
196
+ export function isRobinhoodChain(chainId) {
197
+ return chainId === CHAINS.ROBINHOOD.chainId;
198
+ }
179
199
  /**
180
200
  * 获取确认超时时间
181
201
  */
@@ -43,6 +43,14 @@ export declare const GAS_LIMITS: {
43
43
  readonly AA_PRE_VERIFICATION: 60000n;
44
44
  /** AA 默认 Call Gas Limit */
45
45
  readonly AA_CALL_GAS_LIMIT: 800000n;
46
+ /** LONGETF deployVault(页面固定 3,000,000) */
47
+ readonly ROBINHOOD_LONGETF_DEPLOY_VAULT: 3000000n;
48
+ /** LONGETF long.xyz create() */
49
+ readonly ROBINHOOD_LONGETF_LAUNCH: 3000000n;
50
+ /** LONGETF factory register */
51
+ readonly ROBINHOOD_LONGETF_REGISTER: 500000n;
52
+ /** pons v2 launchToken / launchAndBuy */
53
+ readonly ROBINHOOD_PONS_LAUNCH: 3000000n;
46
54
  };
47
55
  export declare const GAS_CONFIG: {
48
56
  /** Gas 估算安全余量(20%) */
@@ -52,6 +52,15 @@ export const GAS_LIMITS = {
52
52
  AA_PRE_VERIFICATION: 60000n,
53
53
  /** AA 默认 Call Gas Limit */
54
54
  AA_CALL_GAS_LIMIT: 800000n,
55
+ // ==================== Robinhood Chain 发币 ====================
56
+ /** LONGETF deployVault(页面固定 3,000,000) */
57
+ ROBINHOOD_LONGETF_DEPLOY_VAULT: 3000000n,
58
+ /** LONGETF long.xyz create() */
59
+ ROBINHOOD_LONGETF_LAUNCH: 3000000n,
60
+ /** LONGETF factory register */
61
+ ROBINHOOD_LONGETF_REGISTER: 500000n,
62
+ /** pons v2 launchToken / launchAndBuy */
63
+ ROBINHOOD_PONS_LAUNCH: 3000000n,
55
64
  };
56
65
  // ============================================================================
57
66
  // Gas 相关配置