@@ -3,13 +3,49 @@ import classNames from 'classnames'
33import { useCallback , useEffect , useMemo , useState } from 'react'
44import { isErrorResponse , createMerkleRoot } from 'utils/api'
55import Button from 'components/Button'
6- import { parseAddressesFromText } from 'utils/addressParsing'
6+ import { parseAddressesFromText , prepareAddresses } from 'utils/addressParsing'
77import { useRouter } from 'next/router'
88import { randomBytes } from 'crypto'
9+ import { resolveEnsDomains } from 'utils/ens'
910
1011const useCreateMerkleRoot = ( ) => {
11- const [ { value, status } , create ] = useAsync (
12- async ( addresses : string [ ] ) => await createMerkleRoot ( addresses ) ,
12+ const [ ensMap , setEnsMap ] = useState < Record < string , string > > ( { } )
13+
14+ const [ { value, status, error : reqError } , create ] = useAsync (
15+ async ( addressesOrENSNames : string [ ] ) => {
16+ let prepared = prepareAddresses ( addressesOrENSNames , ensMap )
17+
18+ if ( prepared . unresolvedEnsNames . length > 0 ) {
19+ const ensAddresses = await resolveEnsDomains (
20+ prepared . unresolvedEnsNames ,
21+ )
22+
23+ setEnsMap ( ( prev ) => ( {
24+ ...prev ,
25+ ...ensAddresses ,
26+ } ) )
27+
28+ prepared = prepareAddresses ( addressesOrENSNames , {
29+ ...ensMap ,
30+ ...ensAddresses ,
31+ } )
32+
33+ if ( prepared . unresolvedEnsNames . length > 0 ) {
34+ throw new Error ( `Could not resolve all ENS names` )
35+ }
36+ }
37+
38+ if ( prepared . addresses . length !== prepared . dedupedAddresses . length ) {
39+ return (
40+ await Promise . all ( [
41+ createMerkleRoot ( prepared . dedupedAddresses ) ,
42+ createMerkleRoot ( prepared . addresses ) ,
43+ ] )
44+ ) [ 0 ]
45+ }
46+
47+ return await createMerkleRoot ( prepared . dedupedAddresses )
48+ } ,
1349 )
1450
1551 const merkleRoot = useMemo ( ( ) => {
@@ -20,10 +56,12 @@ const useCreateMerkleRoot = () => {
2056
2157 const error = useMemo ( ( ) => {
2258 if ( isErrorResponse ( value ) ) return value
59+ if ( reqError !== undefined )
60+ return { error : true , message : reqError . message }
2361 return undefined
24- } , [ value ] )
62+ } , [ value , reqError ] )
2563
26- return { merkleRoot, error, status, create }
64+ return { merkleRoot, error, status, create, parsedEnsNames : ensMap }
2765}
2866
2967const randomAddress = ( ) => `0x${ randomBytes ( 20 ) . toString ( 'hex' ) } `
@@ -34,6 +72,7 @@ export default function CreateRoot() {
3472 error : errorResponse ,
3573 status,
3674 create,
75+ parsedEnsNames,
3776 } = useCreateMerkleRoot ( )
3877 const [ addressInput , addressInputSet ] = useState ( '' )
3978
@@ -46,13 +85,10 @@ export default function CreateRoot() {
4685 create ( addresses )
4786 } , [ addressInput , create ] )
4887
49- const parsedAddresses = useMemo ( ( ) => {
50- if ( addressInput . trim ( ) . length === 0 ) {
51- return [ ]
52- }
53-
54- return parseAddressesFromText ( addressInput )
55- } , [ addressInput ] )
88+ const parsedAddresses = useMemo (
89+ ( ) => parseAddressesFromText ( addressInput ) ,
90+ [ addressInput ] ,
91+ )
5692
5793 const parsedAddressesCount = useMemo (
5894 ( ) => parsedAddresses . length ,
@@ -75,6 +111,26 @@ export default function CreateRoot() {
75111 addressInputSet ( addresses . join ( '\n' ) )
76112 } , [ ] )
77113
114+ const handleRemoveInvalidENSNames = useCallback ( ( ) => {
115+ const addresses = parsedAddresses
116+ . filter ( ( address ) => {
117+ return (
118+ ! address . includes ( '.' ) ||
119+ parsedEnsNames [ address . toLowerCase ( ) ] !== undefined
120+ )
121+ } )
122+ . join ( '\n' )
123+ addressInputSet ( addresses )
124+ } , [ parsedAddresses , parsedEnsNames ] )
125+
126+ const showRemoveInvalidENSNames = useMemo (
127+ // show the button if the error message contains `Could not resolve all ENS names`
128+ ( ) =>
129+ errorResponse ?. message ?. includes ( 'Could not resolve all ENS names' ) ??
130+ false ,
131+ [ errorResponse ?. message ] ,
132+ )
133+
78134 const buttonPending = status === 'loading' || merkleRoot !== undefined
79135
80136 return (
@@ -90,7 +146,7 @@ export default function CreateRoot() {
90146 ) }
91147 value = { addressInput }
92148 onChange = { ( e ) => addressInputSet ( e . target . value ) }
93- placeholder = "Paste addresses here, separated by commas, spaces or new lines"
149+ placeholder = "Paste addresses or ENS names here, separated by commas, spaces or new lines"
94150 />
95151 </ div >
96152
@@ -118,9 +174,21 @@ export default function CreateRoot() {
118174 ) }
119175 </ div >
120176
121- { status === 'success' && errorResponse !== undefined && (
177+ { errorResponse !== undefined && (
122178 < div className = "text-center sm:text-left w-full" >
123179 Error: { errorResponse . message }
180+ { showRemoveInvalidENSNames && (
181+ < >
182+ { ' ' }
183+ < button
184+ className = "underline"
185+ onClick = { handleRemoveInvalidENSNames }
186+ type = "button"
187+ >
188+ Remove invalid ENS names
189+ </ button >
190+ </ >
191+ ) }
124192 </ div >
125193 ) }
126194 </ div >
0 commit comments