@@ -9,24 +9,34 @@ import React, {
9
9
RefObject ,
10
10
} from 'react'
11
11
12
+ import { encode , decode } from '@kunigi/string-compression'
12
13
import cn from 'classnames'
14
+ import copy from 'copy-to-clipboard'
13
15
import { BN } from 'ethereumjs-util'
16
+ import { useRouter } from 'next/router'
14
17
import Select , { OnChangeValue } from 'react-select'
15
18
import SCEditor from 'react-simple-code-editor'
16
19
17
20
import { EthereumContext } from 'context/ethereumContext'
18
21
import { SettingsContext , Setting } from 'context/settingsContext'
19
22
23
+ import { getAbsoluteURL } from 'util/browser'
20
24
import {
21
25
getTargetEvmVersion ,
22
26
compilerSemVer ,
23
27
getBytecodeFromMnemonic ,
24
28
} from 'util/compiler'
25
- import { codeHighlight , isEmpty , isFullHex , isHex } from 'util/string'
29
+ import {
30
+ codeHighlight ,
31
+ isEmpty ,
32
+ isFullHex ,
33
+ isHex ,
34
+ objToQueryString ,
35
+ } from 'util/string'
26
36
27
37
import examples from 'components/Editor/examples'
28
38
import InstructionList from 'components/Editor/Instructions'
29
- import { Button , Input } from 'components/ui'
39
+ import { Button , Input , Icon } from 'components/ui'
30
40
31
41
import Console from './Console'
32
42
import ExecutionState from './ExecutionState'
@@ -53,6 +63,7 @@ const unitOptions = Object.keys(ValueUnit).map((value) => ({
53
63
54
64
const Editor = ( { readOnly = false } : Props ) => {
55
65
const { settingsLoaded, getSetting, setSetting } = useContext ( SettingsContext )
66
+ const router = useRouter ( )
56
67
57
68
const {
58
69
deployContract,
@@ -82,49 +93,94 @@ const Editor = ({ readOnly = false }: Props) => {
82
93
const [ callValue , setCallValue ] = useState ( '' )
83
94
const [ unit , setUnit ] = useState ( ValueUnit . Wei as string )
84
95
85
- const handleWorkerMessage = ( event : MessageEvent ) => {
86
- const { code : byteCode , warning, error } = event . data
96
+ const log = useCallback (
97
+ ( line : string , type = 'info' ) => {
98
+ // See https://blog.logrocket.com/a-guide-to-usestate-in-react-ecb9952e406c/
99
+ setOutput ( ( previous ) => {
100
+ const cloned = previous . map ( ( x ) => ( { ...x } ) )
101
+ cloned . push ( { type, message : line } )
102
+ return cloned
103
+ } )
104
+ } ,
105
+ [ setOutput ] ,
106
+ )
87
107
88
- if ( error ) {
89
- log ( error , 'error' )
90
- setIsCompiling ( false )
91
- return
92
- }
108
+ const getCallValue = useCallback ( ( ) => {
109
+ const _callValue = new BN ( callValue )
93
110
94
- if ( warning ) {
95
- log ( warning , 'warn' )
111
+ if ( unit === ValueUnit . Gwei ) {
112
+ _callValue . imul ( new BN ( '1000000000' ) )
113
+ } else if ( unit === ValueUnit . Finney ) {
114
+ _callValue . imul ( new BN ( '1000000000000000' ) )
115
+ } else if ( unit === ValueUnit . Ether ) {
116
+ _callValue . imul ( new BN ( '1000000000000000000' ) )
96
117
}
97
118
98
- log ( 'Compilation successful' )
119
+ return _callValue
120
+ } , [ callValue , unit ] )
99
121
100
- try {
101
- deployContract ( byteCode , new BN ( callValue ) ) . then ( ( tx ) => {
102
- loadInstructions ( byteCode )
122
+ const handleWorkerMessage = useCallback (
123
+ ( event : MessageEvent ) => {
124
+ const { code : byteCode , warning, error } = event . data
125
+
126
+ if ( error ) {
127
+ log ( error , 'error' )
103
128
setIsCompiling ( false )
104
- startTransaction ( byteCode , tx )
105
- } )
106
- } catch ( error ) {
107
- log ( ( error as Error ) . message , 'error' )
108
- setIsCompiling ( false )
109
- }
110
- }
129
+ return
130
+ }
111
131
112
- const log = useCallback (
113
- ( line : string , type = 'info' ) => {
114
- output . push ( { type, message : line } )
115
- setOutput ( output )
132
+ if ( warning ) {
133
+ log ( warning , 'warn' )
134
+ }
135
+
136
+ log ( 'Compilation successful' )
137
+
138
+ try {
139
+ const _callValue = getCallValue ( )
140
+ deployContract ( byteCode , _callValue ) . then ( ( tx ) => {
141
+ loadInstructions ( byteCode )
142
+ setIsCompiling ( false )
143
+ startTransaction ( byteCode , tx )
144
+ } )
145
+ } catch ( error ) {
146
+ log ( ( error as Error ) . message , 'error' )
147
+ setIsCompiling ( false )
148
+ }
116
149
} ,
117
- [ output , setOutput ] ,
150
+ [
151
+ log ,
152
+ setIsCompiling ,
153
+ deployContract ,
154
+ loadInstructions ,
155
+ startTransaction ,
156
+ getCallValue ,
157
+ ] ,
118
158
)
119
159
120
160
useEffect ( ( ) => {
121
- const initialCodeType : CodeType =
122
- getSetting ( Setting . EditorCodeType ) || CodeType . Yul
161
+ const query = router . query
123
162
124
- setCodeType ( initialCodeType )
125
- setCode ( examples [ initialCodeType ] [ 0 ] )
163
+ if ( 'callValue' in query && 'unit' in query ) {
164
+ setCallValue ( query . callValue as string )
165
+ setUnit ( query . unit as string )
166
+ }
167
+
168
+ if ( 'callData' in query ) {
169
+ setCallData ( query . callData as string )
170
+ }
171
+
172
+ if ( 'codeType' in query && 'code' in query ) {
173
+ setCodeType ( query . codeType as string )
174
+ setCode ( JSON . parse ( '{"a":' + decode ( query . code as string ) + '}' ) . a )
175
+ } else {
176
+ const initialCodeType : CodeType =
177
+ getSetting ( Setting . EditorCodeType ) || CodeType . Yul
178
+
179
+ setCodeType ( initialCodeType )
180
+ setCode ( examples [ initialCodeType ] [ 0 ] )
181
+ }
126
182
// eslint-disable-next-line react-hooks/exhaustive-deps
127
- } , [ settingsLoaded ] )
183
+ } , [ settingsLoaded && router . isReady ] )
128
184
129
185
useEffect ( ( ) => {
130
186
solcWorkerRef . current = new Worker (
@@ -218,18 +274,10 @@ const Editor = ({ readOnly = false }: Props) => {
218
274
return
219
275
}
220
276
221
- const _callData = Buffer . from ( callData . substr ( 2 ) , 'hex' )
222
- const _callValue = new BN ( callValue )
223
-
224
- if ( unit === ValueUnit . Gwei ) {
225
- _callValue . imul ( new BN ( '1000000000' ) )
226
- } else if ( unit === ValueUnit . Finney ) {
227
- _callValue . imul ( new BN ( '1000000000000000' ) )
228
- } else if ( unit === ValueUnit . Ether ) {
229
- _callValue . imul ( new BN ( '1000000000000000000' ) )
230
- }
231
-
232
277
try {
278
+ const _callData = Buffer . from ( callData . substr ( 2 ) , 'hex' )
279
+ const _callValue = getCallValue ( )
280
+
233
281
if ( codeType === CodeType . Mnemonic ) {
234
282
const bytecode = getBytecodeFromMnemonic ( code , opcodes )
235
283
loadInstructions ( bytecode )
@@ -267,12 +315,25 @@ const Editor = ({ readOnly = false }: Props) => {
267
315
selectedFork ,
268
316
callData ,
269
317
callValue ,
270
- unit ,
271
318
loadInstructions ,
272
319
log ,
273
320
startExecution ,
321
+ getCallValue ,
274
322
] )
275
323
324
+ const handleCopyPermalink = useCallback ( ( ) => {
325
+ const params = {
326
+ callValue,
327
+ unit,
328
+ callData,
329
+ codeType,
330
+ code : encodeURIComponent ( encode ( JSON . stringify ( code ) ) ) ,
331
+ }
332
+
333
+ copy ( `${ getAbsoluteURL ( '/playground?' ) } ${ objToQueryString ( params ) } ` )
334
+ log ( 'Link to current code, calldata and value copied to clipboard' )
335
+ } , [ callValue , unit , callData , codeType , code , log ] )
336
+
276
337
const isRunDisabled = useMemo ( ( ) => {
277
338
return compiling || isEmpty ( code )
278
339
} , [ compiling , code ] )
@@ -328,8 +389,8 @@ const Editor = ({ readOnly = false }: Props) => {
328
389
/>
329
390
</ div >
330
391
331
- < div className = "flex items-center justify-between px-4 py-2 md:border-r border-gray-200 dark:border-black-500" >
332
- < div className = "flex flex-row gap-x-4" >
392
+ < div className = "flex flex-col md:flex-row md: items-center md: justify-between px-4 py-4 md: py-2 md:border-r border-gray-200 dark:border-black-500" >
393
+ < div className = "flex flex-col md:flex- row md: gap-x-4 gap-y-2 md:gap-y-0 mb-4 md:mb-0 " >
333
394
{ isCallDataActive && (
334
395
< Input
335
396
placeholder = "Calldata in HEX"
@@ -358,13 +419,26 @@ const Editor = ({ readOnly = false }: Props) => {
358
419
classNamePrefix = "select"
359
420
menuPlacement = "auto"
360
421
/>
422
+
423
+ < Button
424
+ onClick = { handleCopyPermalink }
425
+ transparent
426
+ padded = { false }
427
+ >
428
+ < span
429
+ className = "inline-block mr-4 select-all"
430
+ data-tip = "Share permalink"
431
+ >
432
+ < Icon name = "links-line" className = "text-indigo-500 mr-2" />
433
+ </ span >
434
+ </ Button >
361
435
</ div >
362
436
363
437
< Button
364
438
onClick = { handleRun }
365
439
disabled = { isRunDisabled }
366
440
size = "sm"
367
- className = "ml-3 "
441
+ contentClassName = "justify-center "
368
442
>
369
443
Run
370
444
</ Button >
0 commit comments