@@ -20,6 +20,7 @@ interface StageConfig {
2020 walletLimit ?: number ;
2121 maxSupply ?: number ;
2222 whitelistPath ?: string ;
23+ variableWalletLimitPath ?: string ;
2324}
2425
2526export const setStages = async (
@@ -41,46 +42,94 @@ export const setStages = async (
4142 if ( args . gaslimit ) {
4243 overrides . gasLimit = ethers . BigNumber . from ( args . gaslimit ) ;
4344 }
45+
46+ /*
47+ * Merkle root generation logic:
48+ * - for `whitelist`, leaves are `solidityKeccak256(['address', 'uint32'], [address, 0])`
49+ * - for `variable wallet limit list`, leaves are `solidityKeccak256(['address', 'uint32'], [address, limit])`
50+ */
4451 const merkleRoots = await Promise . all (
4552 stagesConfig . map ( ( stage ) => {
46- if ( ! stage . whitelistPath ) {
47- return ethers . utils . hexZeroPad ( '0x' , 32 ) ;
48- }
49- const whitelist = JSON . parse (
50- fs . readFileSync ( stage . whitelistPath , 'utf-8' ) ,
51- ) ;
52-
53- // Clean up whitelist
54- const filteredWhitelist = whitelist . filter ( ( address : string ) =>
55- ethers . utils . isAddress ( address ) ,
56- ) ;
57- console . log (
58- `Filtered whitelist: ${ filteredWhitelist . length } addresses. ${ whitelist . length - filteredWhitelist . length } invalid addresses removed.` ,
59- ) ;
60- const invalidWhitelist = whitelist . filter (
61- ( address : string ) => ! ethers . utils . isAddress ( address ) ,
62- ) ;
63- console . log (
64- `❌ Invalid whitelist: ${ invalidWhitelist . length } addresses.\r\n${ invalidWhitelist . join ( ', \r\n' ) } ` ,
65- ) ;
66-
67- if ( invalidWhitelist . length > 0 ) {
68- console . log ( `🔄 🚨 updating whitelist file: ${ stage . whitelistPath } ` ) ;
69- fs . writeFileSync (
70- stage . whitelistPath ,
71- JSON . stringify ( filteredWhitelist , null , 2 ) ,
53+ if ( stage . whitelistPath ) {
54+ const whitelist = JSON . parse (
55+ fs . readFileSync ( stage . whitelistPath , 'utf-8' ) ,
7256 ) ;
73- }
7457
75- const mt = new MerkleTree (
76- filteredWhitelist . map ( ethers . utils . getAddress ) ,
77- ethers . utils . keccak256 ,
78- {
58+ // Clean up whitelist
59+ const filteredWhitelist = whitelist . filter ( ( address : string ) =>
60+ ethers . utils . isAddress ( address ) ,
61+ ) ;
62+ console . log (
63+ `Filtered whitelist: ${ filteredWhitelist . length } addresses. ${ whitelist . length - filteredWhitelist . length } invalid addresses removed.` ,
64+ ) ;
65+ const invalidWhitelist = whitelist . filter (
66+ ( address : string ) => ! ethers . utils . isAddress ( address ) ,
67+ ) ;
68+ console . log (
69+ `❌ Invalid whitelist: ${ invalidWhitelist . length } addresses.\r\n${ invalidWhitelist . join ( ', \r\n' ) } ` ,
70+ ) ;
71+
72+ if ( invalidWhitelist . length > 0 ) {
73+ console . log ( `🔄 🚨 updating whitelist file: ${ stage . whitelistPath } ` ) ;
74+ fs . writeFileSync (
75+ stage . whitelistPath ,
76+ JSON . stringify ( filteredWhitelist , null , 2 ) ,
77+ ) ;
78+ }
79+
80+ const mt = new MerkleTree (
81+ filteredWhitelist . map ( ( address : string ) =>
82+ ethers . utils . solidityKeccak256 (
83+ [ 'address' , 'uint32' ] ,
84+ [ ethers . utils . getAddress ( address ) , 0 ] ,
85+ ) ,
86+ ) ,
87+ ethers . utils . keccak256 ,
88+ {
89+ sortPairs : true ,
90+ hashLeaves : true ,
91+ } ,
92+ ) ;
93+ return mt . getHexRoot ( ) ;
94+ } else if ( stage . variableWalletLimitPath ) {
95+ const leaves : any [ ] = [ ] ;
96+ const file = fs . readFileSync ( stage . variableWalletLimitPath , 'utf-8' ) ;
97+ file
98+ . split ( '\n' )
99+ . filter ( ( line ) => line )
100+ . forEach ( ( line ) => {
101+ const [ addressStr , limitStr ] = line . split ( ',' ) ;
102+
103+ if ( ! ethers . utils . isAddress ( addressStr . trim ( ) . toLowerCase ( ) ) ) {
104+ console . log ( `Ignored invalid address: ${ addressStr } ` ) ;
105+ return ;
106+ }
107+
108+ const address = ethers . utils . getAddress (
109+ addressStr . trim ( ) . toLowerCase ( ) ,
110+ ) ;
111+ const limit = parseInt ( limitStr , 10 ) ;
112+
113+ if ( ! Number . isInteger ( limit ) ) {
114+ console . log ( `Ignored invalid limit for address: ${ addressStr } ` ) ;
115+ return ;
116+ }
117+
118+ const digest = ethers . utils . solidityKeccak256 (
119+ [ 'address' , 'uint32' ] ,
120+ [ address , limit ] ,
121+ ) ;
122+ leaves . push ( digest ) ;
123+ } ) ;
124+
125+ const mt = new MerkleTree ( leaves , ethers . utils . keccak256 , {
79126 sortPairs : true ,
80- hashLeaves : true ,
81- } ,
82- ) ;
83- return mt . getHexRoot ( ) ;
127+ hashLeaves : false ,
128+ } ) ;
129+ return mt . getHexRoot ( ) ;
130+ }
131+
132+ return ethers . utils . hexZeroPad ( '0x' , 32 ) ;
84133 } ) ,
85134 ) ;
86135
0 commit comments