-
Notifications
You must be signed in to change notification settings - Fork 5
/
page.tsx
181 lines (161 loc) Β· 6.36 KB
/
page.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
'use client'
import { useState, Fragment, ChangeEvent } from 'react'
import { StandardMerkleTree } from '@openzeppelin/merkle-tree'
import { parse } from 'csv-parse/sync'
import ErrorMessage from './error'
const encoding = ['address', 'uint256']
const placeholder = '"0x2222222222222222222222222222222222222222", "2500000000000000000"'
const defaultValues = [
['0x0000000000000000000000000000000000000000', '0987654345678999878']
]
const defaultTree = StandardMerkleTree.of(defaultValues, encoding)
export default function Home() {
const [tree, setTree] = useState(defaultTree)
const [error, setError] = useState('')
const [text, setText] = useState('')
const updateTree = () => {
try {
let values = parse(text, {
skip_empty_lines: true,
trim: true
})
setTree(StandardMerkleTree.of(values, encoding))
setError('')
} catch (e) {
let message: string
if (typeof e === 'string') {
message = e
} else if (e instanceof Error) {
message = e.message
} else {
message = 'nidea'
}
setError(message)
}
}
const resetTree = () => {
setTree(defaultTree)
}
const parseFile = (e: ChangeEvent<HTMLInputElement>) => {
const target = e.target
const file = target.files![0]
if (file.size > 1024 * 1024 * 500) {
setError('File size must be less than 500mb')
return
}
const reader = new FileReader()
reader.onload = (e) => {
const t = e.target!.result as string
setText(t)
resetTree()
}
reader.readAsText(file)
target.value = '' // allows re-submitting same file
}
const downloadTree = () => {
const exportName = 'merklefy-tree'
const dataStr = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(tree.dump()))
const downloadAnchorNode = document.createElement('a')
downloadAnchorNode.setAttribute('href', dataStr)
downloadAnchorNode.setAttribute('download', exportName + '.json')
document.body.appendChild(downloadAnchorNode)
downloadAnchorNode.click()
downloadAnchorNode.remove()
}
return (
<main className='flex min-h-screen flex-col items-center justify-between p-0 md:p-24'>
<div className='container px-8 max-w-3xl mx-auto'>
<h1 className='py-4 text-5xl font-bold text-center gradient'>
Merklefy π
</h1>
<form
className=''
onSubmit={e => {
e.preventDefault()
updateTree()
}}
>
<pre className='px-4 py-3 mt-8 font-mono text-left bg-transparent border rounded border-zinc-600 focus:border-zinc-100/80 focus:ring-0 sm:text-sm text-zinc-100'>
<div className='flex items-start px-1 text-sm'>
<div aria-hidden='true' className='pr-4 font-mono border-r select-none border-zinc-300/5 text-zinc-700'>
{Array.from({
length: text.split('\n').length,
}).map((_, index) => (
<Fragment key={index}>
{(index + 1).toString().padStart(2, '0')}
<br />
</Fragment>
))}
</div>
<textarea
id='text'
name='text'
value={text}
minLength={1}
onChange={ e => {
setText(e.target.value)
resetTree()
} }
rows={Math.max(5, text.split('\n').length)}
placeholder={placeholder}
className='w-full p-0 text-base bg-transparent border-0 appearance-none resize-none hover:resize text-zinc-100 placeholder-zinc-500 focus:ring-0 sm:text-sm'
/>
</div>
</pre>
<div className='flex flex-col items-center justify-center w-full gap-4 mt-4 sm:flex-row'>
<div className='w-full sm:w-1/5'>
<label
className='flex items-center justify-center h-16 px-3 py-2 text-sm whitespace-no-wrap duration-150 border rounded hover:border-zinc-100/80 border-zinc-600 focus:border-zinc-100/80 focus:ring-0 text-zinc-100 hover:text-white hover:cursor-pointer '
htmlFor='file_input'
>
Or upload a file
</label>
<input
className='hidden'
id='file_input'
type='file'
onChange={ parseFile }
/>
</div>
<button
type='submit'
disabled={text.length <= 0}
className={`w-full h-16 px-3 py-2 duration-150 rounded sm:w-4/5 text-base font-semibold leading-7 bg-zinc-200 ring-1 ring-transparent duration-150 ${
text.length <= 0
? 'text-zinc-400 cursor-not-allowed'
: 'text-zinc-900 hover:text-zinc-100 hover:ring-zinc-600/80 hover:bg-zinc-900/20'
}`}
>
<span>Generate π</span>
</button>
</div>
</form>
{ tree.root !== defaultTree.root
? <>
<div className='relative w-full h-16 px-3 py-2 mt-5 duration-150 border rounded text-lime-500 border-lime-100 focus-within:border-zinc-100/80 focus-within:ring-0 overflow-auto'>
<label htmlFor='reads' className='block text-xs font-medium text-lime-100'>
Root
</label>
<p>{tree.root}</p>
</div>
<button
type='submit'
onClick={ downloadTree }
disabled={text.length <= 0}
className={`w-full h-12 px-3 py-2 mt-2 duration-150 rounded text-base font-semibold leading-7 bg-lime-200 ring-1 ring-transparent duration-150 text-zinc-900 hover:text-zinc-100 hover:ring-zinc-600/80 hover:bg-lime-800/20`}
>
<span>Download tree π³πͺ</span>
</button>
</>
: <></>
}
{error ? <ErrorMessage message={error} /> : null}
<div className='mt-8'>
<p className='space-y-2 text-xs text-zinc-500'>
made with <a className='underline hover:text-white' href='https://github.com/OpenZeppelin/merkle-tree' target='_blank'>@openzeppelin/merkle-tree</a> by <a className='underline hover:text-white' href='https://marto.lol' target='_blank'>marto.lol</a>
</p>
</div>
</div>
</main>
)
}