@ -1,27 +1,38 @@
// eslint-disable-next-line no-use-before-define
import React , { useEffect , useRef , useState } from 'react'
import { FormattedMessage , useIntl } from 'react-intl'
import React , { useEffect , useRef , useState } from 'react'
import { FormattedMessage , useIntl } from 'react-intl'
import * as remixLib from '@remix-project/remix-lib'
import { ContractGUIProps } from '../types'
import { CopyToClipboard } from '@remix-ui/clipboard'
import { CustomTooltip , ProxyAddressToggle , ProxyDropdownMenu , shortenDate , shortenProxyAddress , unavailableProxyLayoutMsg , upgradeReportMsg } from '@remix-ui/helper'
import { Dropdown } from 'react-bootstrap'
import { ContractGUIProps } from '../types'
import { CopyToClipboard } from '@remix-ui/clipboard'
import {
CustomTooltip ,
ProxyAddressToggle ,
ProxyDropdownMenu ,
shortenDate ,
shortenProxyAddress ,
unavailableProxyLayoutMsg ,
upgradeReportMsg
} from '@remix-ui/helper'
import { Dropdown } from 'react-bootstrap'
const txFormat = remixLib . execution . txFormat
const txHelper = remixLib . execution . txHelper
export function ContractGUI ( props : ContractGUIProps ) {
export function ContractGUI ( props : ContractGUIProps ) {
const [ title , setTitle ] = useState < string > ( '' )
const [ basicInput , setBasicInput ] = useState < string > ( '' )
const [ toggleContainer , setToggleContainer ] = useState < boolean > ( false )
const [ buttonOptions , setButtonOptions ] = useState < {
title : string ,
content : string ,
classList : string ,
title : string
content : string
classList : string
dataId : string
} > ( { title : '' , content : '' , classList : '' , dataId : '' } )
} > ( { title : '' , content : '' , classList : '' , dataId : '' } )
const [ toggleDeployProxy , setToggleDeployProxy ] = useState < boolean > ( false )
const [ toggleUpgradeImp , setToggleUpgradeImp ] = useState < boolean > ( false )
const [ deployState , setDeployState ] = useState < { deploy : boolean , upgrade : boolean } > ( { deploy : false , upgrade : false } )
const [ deployState , setDeployState ] = useState < {
deploy : boolean
upgrade : boolean
} > ( { deploy : false , upgrade : false } )
const [ proxyAddress , setProxyAddress ] = useState < string > ( '' )
const [ proxyAddressError , setProxyAddressError ] = useState < string > ( '' )
const [ showDropdown , setShowDropdown ] = useState < boolean > ( false )
@ -32,8 +43,18 @@ export function ContractGUI (props: ContractGUIProps) {
useEffect ( ( ) = > {
if ( props . deployOption && Array . isArray ( props . deployOption ) ) {
if ( props . deployOption [ 0 ] && props . deployOption [ 0 ] . title === 'Deploy with Proxy' && props . deployOption [ 0 ] . active ) handleDeployProxySelect ( true )
else if ( props . deployOption [ 1 ] && props . deployOption [ 1 ] . title === 'Upgrade with Proxy' && props . deployOption [ 1 ] . active ) handleUpgradeImpSelect ( true )
if (
props . deployOption [ 0 ] &&
props . deployOption [ 0 ] . title === 'Deploy with Proxy' &&
props . deployOption [ 0 ] . active
)
handleDeployProxySelect ( true )
else if (
props . deployOption [ 1 ] &&
props . deployOption [ 1 ] . title === 'Upgrade with Proxy' &&
props . deployOption [ 1 ] . active
)
handleUpgradeImpSelect ( true )
}
} , [ props . deployOption ] )
@ -48,21 +69,26 @@ export function ContractGUI (props: ContractGUIProps) {
setBasicInput ( '' )
// we have the reset the fields before reseting the previous references.
basicInputRef . current . value = ''
multiFields . current . filter ( ( el ) = > el !== null && el !== undefined ) . forEach ( ( el ) = > el . value = '' )
multiFields . current
. filter ( ( el ) = > el !== null && el !== undefined )
. forEach ( ( el ) = > ( el . value = '' ) )
multiFields . current = [ ]
} , [ props . title , props . funcABI ] )
useEffect ( ( ) = > {
if ( props . lookupOnly ) {
// // call. stateMutability is either pure or view
// // call. stateMutability is either pure or view
setButtonOptions ( {
title : title + ' - call' ,
content : 'call' ,
classList : 'btn-info' ,
dataId : title + ' - call'
} )
} else if ( props . funcABI . stateMutability === 'payable' || props . funcABI . payable ) {
// // transact. stateMutability = payable
} else if (
props . funcABI . stateMutability === 'payable' ||
props . funcABI . payable
) {
// // transact. stateMutability = payable
setButtonOptions ( {
title : title + ' - transact (payable)' ,
content : 'transact' ,
@ -70,7 +96,7 @@ export function ContractGUI (props: ContractGUIProps) {
dataId : title + ' - transact (payable)'
} )
} else {
// // transact. stateMutability = nonpayable
// // transact. stateMutability = nonpayable
setButtonOptions ( {
title : title + ' - transact (not payable)' ,
content : 'transact' ,
@ -91,7 +117,8 @@ export function ContractGUI (props: ContractGUIProps) {
const encodeObj = txFormat . encodeData (
props . funcABI ,
multiJSON ,
props . funcABI . type === 'constructor' ? props.evmBC : null )
props . funcABI . type === 'constructor' ? props.evmBC : null
)
if ( encodeObj . error ) {
console . error ( encodeObj . error )
@ -138,7 +165,10 @@ export function ContractGUI (props: ContractGUIProps) {
valArrayTest . push ( elVal )
elVal = elVal . replace ( /(^|,\s+|,)(\d+)(\s+,|,|$)/g , '$1"$2"$3' ) // replace non quoted number by quoted number
elVal = elVal . replace ( /(^|,\s+|,)(0[xX][0-9a-fA-F]+)(\s+,|,|$)/g , '$1"$2"$3' ) // replace non quoted hex string by quoted hex string
elVal = elVal . replace (
/(^|,\s+|,)(0[xX][0-9a-fA-F]+)(\s+,|,|$)/g ,
'$1"$2"$3'
) // replace non quoted hex string by quoted hex string
if ( elVal ) {
try {
JSON . parse ( elVal )
@ -161,7 +191,8 @@ export function ContractGUI (props: ContractGUIProps) {
const inputString = basicInput
if ( inputString ) {
const inputJSON = remixLib . execution . txFormat . parseFunctionParams ( inputString )
const inputJSON =
remixLib . execution . txFormat . parseFunctionParams ( inputString )
const multiInputs = multiFields . current
for ( let k = 0 ; k < multiInputs . length ; k ++ ) {
@ -175,26 +206,61 @@ export function ContractGUI (props: ContractGUIProps) {
const handleActionClick = async ( ) = > {
if ( deployState . deploy ) {
const proxyInitializeString = getMultiValsString ( initializeFields . current )
props . clickCallBack ( props . initializerOptions . inputs . inputs , proxyInitializeString , [ 'Deploy with Proxy' ] )
props . clickCallBack (
props . initializerOptions . inputs . inputs ,
proxyInitializeString ,
[ 'Deploy with Proxy' ]
)
} else if ( deployState . upgrade ) {
if ( proxyAddress === '' ) {
setProxyAddressError ( 'proxy address cannot be empty' )
} else {
const isValidProxyAddress = await props . isValidProxyAddress ( proxyAddress )
const isValidProxyAddress = await props . isValidProxyAddress (
proxyAddress
)
if ( isValidProxyAddress ) {
setProxyAddressError ( '' )
const upgradeReport : any = await props . isValidProxyUpgrade ( proxyAddress )
const upgradeReport : any = await props . isValidProxyUpgrade (
proxyAddress
)
if ( upgradeReport . ok ) {
! proxyAddressError && props . clickCallBack ( props . funcABI . inputs , proxyAddress , [ 'Upgrade with Proxy' ] )
! proxyAddressError &&
props . clickCallBack ( props . funcABI . inputs , proxyAddress , [
'Upgrade with Proxy'
] )
} else {
if ( upgradeReport . warning ) {
props . modal ( 'Proxy Upgrade Warning' , unavailableProxyLayoutMsg ( ) , 'Proceed' , ( ) = > {
! proxyAddressError && props . clickCallBack ( props . funcABI . inputs , proxyAddress , [ 'Upgrade with Proxy' ] )
} , 'Cancel' , ( ) = > { } , 'btn-warning' , 'btn-secondary' )
props . modal (
'Proxy Upgrade Warning' ,
unavailableProxyLayoutMsg ( ) ,
'Proceed' ,
( ) = > {
! proxyAddressError &&
props . clickCallBack ( props . funcABI . inputs , proxyAddress , [
'Upgrade with Proxy'
] )
} ,
'Cancel' ,
( ) = > { } ,
'btn-warning' ,
'btn-secondary'
)
} else {
props . modal ( 'Proxy Upgrade Error' , upgradeReportMsg ( upgradeReport ) , 'Continue anyway ' , ( ) = > {
! proxyAddressError && props . clickCallBack ( props . funcABI . inputs , proxyAddress , [ 'Upgrade with Proxy' ] )
} , 'Cancel' , ( ) = > { } , 'btn-warning' , 'btn-secondary' )
props . modal (
'Proxy Upgrade Error' ,
upgradeReportMsg ( upgradeReport ) ,
'Continue anyway ' ,
( ) = > {
! proxyAddressError &&
props . clickCallBack ( props . funcABI . inputs , proxyAddress , [
'Upgrade with Proxy'
] )
} ,
'Cancel' ,
( ) = > { } ,
'btn-warning' ,
'btn-secondary'
)
}
}
} else {
@ -229,7 +295,7 @@ export function ContractGUI (props: ContractGUIProps) {
const handleDeployProxySelect = ( value : boolean ) = > {
if ( value ) setToggleUpgradeImp ( false )
setToggleDeployProxy ( value )
setDeployState ( { upgrade : false , deploy : value } )
setDeployState ( { upgrade : false , deploy : value } )
}
const handleToggleUpgradeImp = ( ) = > {
@ -241,7 +307,7 @@ export function ContractGUI (props: ContractGUIProps) {
if ( value ) {
setToggleDeployProxy ( false )
}
setDeployState ( { deploy : false , upgrade : value } )
setDeployState ( { deploy : false , upgrade : value } )
}
const switchProxyAddress = ( address : string ) = > {
@ -262,50 +328,63 @@ export function ContractGUI (props: ContractGUIProps) {
< div
className = { ` udapp_contractProperty ${
( props . funcABI . inputs && props . funcABI . inputs . length > 0 ) ||
props . funcABI . type === "fallback" ||
props . funcABI . type === "receive"
? "udapp_hasArgs"
: ""
props . funcABI . type === 'fallback' ||
props . funcABI . type === 'receive'
? 'udapp_hasArgs'
: ''
} ` }
>
< div
className = "udapp_contractActionsContainerSingle pt-2"
style = { { display : toggleContainer ? "none" : "flex" } }
style = { { display : toggleContainer ? 'none' : 'flex' } }
>
< div
className = 'd-flex'
onClick = { handleActionClick }
< CustomTooltip
delay = { 0 }
placement = { 'right' }
tooltipClasses = "text-wrap"
tooltipId = "remixUdappInstanceButtonTooltip"
tooltipText = {
toggleUpgradeImp && ! proxyAddress
? 'Proxy address cannot be empty'
: props . inputs !== '' && basicInput === ''
? 'Input required'
: buttonOptions . title
}
>
< button
className = { ` udapp_instanceButton text-nowrap overflow-hidden text-truncate ${ props . widthClass } btn btn-sm ${ buttonOptions . classList } ` }
< div
className = "d-flex wrapperElement"
onClick = { handleActionClick }
data - id = { buttonOptions . dataId }
data - title = { buttonOptions . title }
disabled = { ( toggleUpgradeImp && ! proxyAddress ) || props . disabled || ( props . inputs !== '' && basicInput === '' ) }
>
< CustomTooltip
delay = { 0 }
placement = { "right" }
tooltipClasses = "text-wrap"
tooltipId = "remixUdappInstanceButtonTooltip"
tooltipText = { toggleUpgradeImp && ! proxyAddress ? 'Proxy address cannot be empty' : ( props . inputs !== '' && basicInput === '' ) ? 'Input required' : buttonOptions . title }
< button
className = { ` udapp_instanceButton text-nowrap overflow-hidden text-truncate ${ props . widthClass } btn btn-sm ${ buttonOptions . classList } ` }
data - id = { buttonOptions . dataId }
data - title = { buttonOptions . title }
disabled = {
( toggleUpgradeImp && ! proxyAddress ) ||
props . disabled ||
( props . inputs !== '' && basicInput === '' )
}
style = { { pointerEvents : 'none' } }
>
< div > { title } < / div >
< / CustomTooltip >
< / button >
< / div >
{ title }
< / button >
< / div >
< / CustomTooltip >
< input
className = "form-control"
data - id = {
props . funcABI . type === "fallback" ||
props . funcABI . type === "receive"
props . funcABI . type === 'fallback' ||
props . funcABI . type === 'receive'
? ` '( ${ props . funcABI . type } ') `
: "multiParamManagerBasicInputField"
: 'multiParamManagerBasicInputField'
}
placeholder = { props . inputs }
onChange = { handleBasicInput }
data - title = {
props . funcABI . type === "fallback" ||
props . funcABI . type === "receive"
props . funcABI . type === 'fallback' ||
props . funcABI . type === 'receive'
? ` '( ${ props . funcABI . type } ') `
: props . inputs
}
@ -314,11 +393,11 @@ export function ContractGUI (props: ContractGUIProps) {
height : '2rem' ,
visibility : ! (
( props . funcABI . inputs && props . funcABI . inputs . length > 0 ) ||
props . funcABI . type === "fallback" ||
props . funcABI . type === "receive"
props . funcABI . type === 'fallback' ||
props . funcABI . type === 'receive'
)
? "hidden"
: "visible" ,
? 'hidden'
: 'visible'
} }
/ >
< i
@ -328,14 +407,14 @@ export function ContractGUI (props: ContractGUIProps) {
visibility : ! (
props . funcABI . inputs && props . funcABI . inputs . length > 0
)
? "hidden"
: "visible" ,
? 'hidden'
: 'visible'
} }
> < / i >
< / div >
< div
className = "udapp_contractActionsContainerMulti"
style = { { display : toggleContainer ? "flex" : "none" } }
style = { { display : toggleContainer ? 'flex' : 'none' } }
>
< div className = "udapp_contractActionsContainerMultiInner text-dark" >
< div onClick = { switchMethodViewOff } className = "udapp_multiHeader" >
@ -357,7 +436,7 @@ export function ContractGUI (props: ContractGUIProps) {
>
< input
ref = { ( el ) = > {
multiFields . current [ index ] = el ;
multiFields . current [ index ] = el
} }
className = "form-control"
placeholder = { inp . type }
@ -366,14 +445,14 @@ export function ContractGUI (props: ContractGUIProps) {
/ >
< / CustomTooltip >
< / div >
) ;
)
} ) }
< / div >
< div className = "d-flex udapp_group udapp_multiArg" >
< CopyToClipboard
tip = { intl . formatMessage ( { id : 'udapp.copyCalldata' } ) }
tip = { intl . formatMessage ( { id : 'udapp.copyCalldata' } ) }
icon = "fa-clipboard"
direction = { "bottom" }
direction = { 'bottom' }
getContent = { getEncodedCall }
>
< button className = "btn remixui_copyButton" >
@ -386,9 +465,9 @@ export function ContractGUI (props: ContractGUIProps) {
< / button >
< / CopyToClipboard >
< CopyToClipboard
tip = { intl . formatMessage ( { id : 'udapp.copyParameters' } ) }
tip = { intl . formatMessage ( { id : 'udapp.copyParameters' } ) }
icon = "fa-clipboard"
direction = { "bottom" }
direction = { 'bottom' }
getContent = { getEncodedParams }
>
< button className = "btn remixui_copyButton" >
@ -397,23 +476,25 @@ export function ContractGUI (props: ContractGUIProps) {
className = "m-0 remixui_copyIcon far fa-copy"
aria - hidden = "true"
> < / i >
< label htmlFor = "copyParameters" > < FormattedMessage id = 'udapp.parameters' / > < / label >
< label htmlFor = "copyParameters" >
< FormattedMessage id = "udapp.parameters" / >
< / label >
< / button >
< / CopyToClipboard >
< CustomTooltip
placement = { "right" }
placement = { 'right' }
tooltipClasses = "text-nowrap"
tooltipId = "remixUdappInstanceButtonTooltip"
tooltipText = { buttonOptions . title }
>
< div
onClick = { handleExpandMultiClick }
>
< div onClick = { handleExpandMultiClick } >
< button
type = "button"
data - id = { buttonOptions . dataId }
className = { ` udapp_instanceButton btn ${ buttonOptions . classList } ` }
disabled = { props . disabled || ( props . inputs !== '' && basicInput === '' ) }
disabled = {
props . disabled || ( props . inputs !== '' && basicInput === '' )
}
>
{ buttonOptions . content }
< / button >
@ -439,14 +520,19 @@ export function ContractGUI (props: ContractGUIProps) {
data - id = "contractGUIDeployWithProxyLabel"
className = "m-0 form-check-label w-100 custom-control-label udapp_checkboxAlign"
>
< FormattedMessage id = 'udapp.deployWithProxy' / >
< FormattedMessage id = "udapp.deployWithProxy" / >
< / label >
< / div >
< div >
{ props . initializerOptions &&
props . initializerOptions . initializeInputs ? (
< span onClick = { handleToggleDeployProxy } >
< i className = { ! toggleDeployProxy ? "fas fa-angle-right pt-2" : "fas fa-angle-down" }
< i
className = {
! toggleDeployProxy
? 'fas fa-angle-right pt-2'
: 'fas fa-angle-down'
}
aria - hidden = "true"
> < / i >
< / span >
@ -457,7 +543,7 @@ export function ContractGUI (props: ContractGUIProps) {
props . initializerOptions . initializeInputs ? (
< div
className = { ` pl-4 flex-column ${
toggleDeployProxy ? "d-flex" : "d-none"
toggleDeployProxy ? 'd-flex' : 'd-none'
} ` }
>
< div className = { ` flex-column 'd-flex'} ` } >
@ -468,19 +554,19 @@ export function ContractGUI (props: ContractGUIProps) {
className = "mt-2 text-left d-block"
htmlFor = { inp . name }
>
{ " " }
{ inp . name } : { " " }
{ ' ' }
{ inp . name } : { ' ' }
< / label >
< input
ref = { ( el ) = > {
initializeFields . current [ index ] = el ;
initializeFields . current [ index ] = el
} }
style = { { height : 32 } }
style = { { height : 32 } }
className = "form-control udapp_input"
placeholder = { inp . type }
/ >
< / div >
) ;
)
} ) }
< / div >
< / div >
@ -500,15 +586,15 @@ export function ContractGUI (props: ContractGUIProps) {
data - id = "contractGUIUpgradeImplementationLabel"
className = "m-0 form-check-label custom-control-label udapp_checkboxAlign"
>
< FormattedMessage id = 'udapp.upgradeWithProxy' / >
< FormattedMessage id = "udapp.upgradeWithProxy" / >
< / label >
< / div >
< span onClick = { handleToggleUpgradeImp } >
< i
className = {
! toggleUpgradeImp
? "fas fa-angle-right pt-2"
: "fas fa-angle-down"
? 'fas fa-angle-right pt-2'
: 'fas fa-angle-down'
}
aria - hidden = "true"
> < / i >
@ -516,50 +602,79 @@ export function ContractGUI (props: ContractGUIProps) {
< / div >
< div
className = { ` pl-4 flex-column ${
toggleUpgradeImp ? "d-flex" : "d-none"
toggleUpgradeImp ? 'd-flex' : 'd-none'
} ` }
>
< div data - id = "proxy-dropdown-items" >
< Dropdown onToggle = { toggleDropdown } show = { showDropdown } >
< Dropdown.Toggle id = "dropdown-custom-components" as = { ProxyAddressToggle } address = { proxyAddress } onChange = { handleAddressChange } className = "d-inline-block border border-dark bg-dark" / >
{ props . proxy . deployments . length > 0 &&
< Dropdown.Menu as = { ProxyDropdownMenu } className = 'w-100 custom-dropdown-items' style = { { overflow : 'hidden' } } >
{
props . proxy . deployments . map ( ( deployment , index ) = > (
< CustomTooltip
placement = { "right" }
tooltipClasses = "text-nowrap"
tooltipId = { ` proxyAddressTooltip ${ index } ` }
tooltipText = { 'Deployed ' + shortenDate ( deployment . date ) }
< Dropdown.Toggle
id = "dropdown-custom-components"
as = { ProxyAddressToggle }
address = { proxyAddress }
onChange = { handleAddressChange }
className = "d-inline-block border border-dark bg-dark"
/ >
{ props . proxy . deployments . length > 0 && (
< Dropdown.Menu
as = { ProxyDropdownMenu }
className = "w-100 custom-dropdown-items"
style = { { overflow : 'hidden' } }
>
{ props . proxy . deployments . map ( ( deployment , index ) = > (
< CustomTooltip
placement = { 'right' }
tooltipClasses = "text-nowrap"
tooltipId = { ` proxyAddressTooltip ${ index } ` }
tooltipText = { 'Deployed ' + shortenDate ( deployment . date ) }
key = { index }
>
< Dropdown.Item
key = { index }
onClick = { ( ) = > {
switchProxyAddress ( deployment . address )
} }
data - id = { ` proxyAddress ${ index } ` }
>
< Dropdown.Item
key = { index }
onClick = { ( ) = > { switchProxyAddress ( deployment . address ) } }
data - id = { ` proxyAddress ${ index } ` }
>
< span >
{ proxyAddress === deployment . address ?
< span > & # 10003 ; { deployment . contractName + ' ' + shortenProxyAddress ( deployment . address ) } < / span >
: < span className = "pl-3" > { deployment . contractName + ' ' + shortenProxyAddress ( deployment . address ) } < / span > }
< / span >
< / Dropdown.Item >
< / CustomTooltip >
) )
}
< span >
{ proxyAddress === deployment . address ? (
< span >
& # 10003 ; { ' ' }
{ deployment . contractName +
' ' +
shortenProxyAddress ( deployment . address ) } { ' ' }
< / span >
) : (
< span className = "pl-3" >
{ deployment . contractName +
' ' +
shortenProxyAddress ( deployment . address ) }
< / span >
) }
< / span >
< / Dropdown.Item >
< / CustomTooltip >
) ) }
< / Dropdown.Menu >
}
) }
< / Dropdown >
< / div >
< div className = 'd-flex' >
< div className = "d-flex" >
< div className = "mb-2" >
{ proxyAddressError && < span className = 'text-lowercase text-danger' data - id = "errorMsgProxyAddress" style = { { fontSize : '.8em' } } > { proxyAddressError } < / span > }
{ proxyAddressError && (
< span
className = "text-lowercase text-danger"
data - id = "errorMsgProxyAddress"
style = { { fontSize : '.8em' } }
>
{ proxyAddressError }
< / span >
) }
< / div >
< / div >
< / div >
< / >
) : null }
< / div >
) ;
)
}