Refactor UI for proxy deployment

pull/2260/head
David Disu 3 years ago
parent 12edbae587
commit c62756251c
  1. 4
      libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts
  2. 4
      libs/remix-core-plugin/src/types/contract.ts
  3. 4
      libs/remix-lib/src/execution/txFormat.ts
  4. 1
      libs/remix-ui/run-tab/src/lib/actions/deploy.ts
  5. 4
      libs/remix-ui/run-tab/src/lib/actions/events.ts
  6. 2
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  7. 240
      libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx
  8. 33
      libs/remix-ui/run-tab/src/lib/components/deployButton.tsx
  9. 20
      libs/remix-ui/run-tab/src/lib/components/deployInput.tsx
  10. 37
      libs/remix-ui/run-tab/src/lib/components/multiDeployInput.tsx
  11. 67
      libs/remix-ui/run-tab/src/lib/types/index.ts

@ -109,7 +109,7 @@ export class OpenZeppelinProxy extends Plugin {
async isConcerned(ast: ContractAST) { async isConcerned(ast: ContractAST) {
// check in the AST if it's an upgradable contract // check in the AST if it's an upgradable contract
if (ast.nodes.find(node => node.absolutePath === UUPS)) { if (ast.nodes && ast.nodes.find(node => node.absolutePath === UUPS)) {
this.kind = 'UUPS' this.kind = 'UUPS'
return true return true
} }
@ -144,6 +144,7 @@ export class OpenZeppelinProxy extends Plugin {
async execute(implAddress: string, args: string = '', initializeABI, implementationContractObject) { async execute(implAddress: string, args: string = '', initializeABI, implementationContractObject) {
// deploy the proxy, or use an existing one // deploy the proxy, or use an existing one
const _data = await this.blockchain.getEncodedFunctionHex(args, initializeABI) const _data = await this.blockchain.getEncodedFunctionHex(args, initializeABI)
console.log('_data: ', _data)
if (this.kind === 'UUPS') this.deployUUPSProxy(implAddress, _data, implementationContractObject) if (this.kind === 'UUPS') this.deployUUPSProxy(implAddress, _data, implementationContractObject)
} }
@ -151,6 +152,7 @@ export class OpenZeppelinProxy extends Plugin {
async deployUUPSProxy (implAddress: string, _data: string, implementationContractObject) { async deployUUPSProxy (implAddress: string, _data: string, implementationContractObject) {
const args = [implAddress, _data] const args = [implAddress, _data]
const constructorData = await this.blockchain.getEncodedParams(args, UUPSfunAbi) const constructorData = await this.blockchain.getEncodedParams(args, UUPSfunAbi)
console.log('constructorData: ', constructorData)
const proxyName = 'ERC1967Proxy' const proxyName = 'ERC1967Proxy'
const data = { const data = {
contractABI: UUPSABI, contractABI: UUPSABI,

@ -3,8 +3,8 @@ export interface FuncABI {
type: string, type: string,
inputs: { name: string, type: string }[], inputs: { name: string, type: string }[],
stateMutability: string, stateMutability: string,
payable: boolean, payable?: boolean,
constant: any constant?: any
} }
export interface ContractData { export interface ContractData {

@ -63,7 +63,8 @@ export function encodeParams (params, funAbi, callback) {
try { try {
params = params.replace(/(^|,\s+|,)(\d+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted number by quoted number params = params.replace(/(^|,\s+|,)(\d+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted number by quoted number
params = params.replace(/(^|,\s+|,)(0[xX][0-9a-fA-F]+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted hex string by quoted hex string params = params.replace(/(^|,\s+|,)(0[xX][0-9a-fA-F]+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted hex string by quoted hex string
funArgs = JSON.parse('[' + params + ']') params = JSON.stringify([params])
funArgs = JSON.parse(params)
} catch (e) { } catch (e) {
return callback('Error encoding arguments: ' + e) return callback('Error encoding arguments: ' + e)
} }
@ -93,6 +94,7 @@ export function encodeParams (params, funAbi, callback) {
* @param {Function} callback - callback * @param {Function} callback - callback
*/ */
export function encodeFunctionCall (params, funAbi, callback) { export function encodeFunctionCall (params, funAbi, callback) {
console.log('params: ', params)
encodeParams(params, funAbi, (error, encodedParam) => { encodeParams(params, funAbi, (error, encodedParam) => {
if (error) return callback(error) if (error) return callback(error)
callback(null, { dataHex: encodeFunctionId(funAbi) + encodedParam.dataHex, funAbi, funArgs: encodedParam.funArgs }) callback(null, { dataHex: encodeFunctionId(funAbi) + encodedParam.dataHex, funAbi, funArgs: encodedParam.funArgs })

@ -160,6 +160,7 @@ export const createInstance = async (
if (isProxyDeployment) { if (isProxyDeployment) {
const initABI = contractObject.abi.find(abi => abi.name === 'initialize') const initABI = contractObject.abi.find(abi => abi.name === 'initialize')
console.log('args: ', args)
plugin.call('openzeppelin-proxy', 'execute', addressToString(address), args, initABI, contractObject) plugin.call('openzeppelin-proxy', 'execute', addressToString(address), args, initABI, contractObject)
} }
} }

@ -103,12 +103,12 @@ const broadcastCompilationResult = async (plugin: RunTab, dispatch: React.Dispat
const index = contracts.findIndex(contract => contract.alias === plugin.REACT_API.contracts.currentContract) const index = contracts.findIndex(contract => contract.alias === plugin.REACT_API.contracts.currentContract)
if (index < 0) dispatch(setCurrentContract(contracts[0].alias)) if (index < 0) dispatch(setCurrentContract(contracts[0].alias))
const isUpgradeable = await plugin.call('openzeppelin-proxy', 'isConcerned', data.sources[file].ast) const isUpgradeable = await plugin.call('openzeppelin-proxy', 'isConcerned', data.sources[file] ? data.sources[file].ast : {})
if (isUpgradeable) { if (isUpgradeable) {
const options = await plugin.call('openzeppelin-proxy', 'getDeployOptions', data.contracts[file]) const options = await plugin.call('openzeppelin-proxy', 'getDeployOptions', data.contracts[file])
dispatch(setDeployOptions({ options: [{ title: 'Deploy with Proxy', active: false }, { title: 'Upgrade Proxy', active: false }], initializeOptions: options })) dispatch(setDeployOptions({ options: [{ title: 'Deploy with Proxy', active: false }], initializeOptions: options }))
} }
else dispatch(setDeployOptions({} as any)) else dispatch(setDeployOptions({} as any))
dispatch(fetchContractListSuccess({ [file]: contracts })) dispatch(fetchContractListSuccess({ [file]: contracts }))

@ -144,6 +144,8 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
} }
const clickCallback = (inputs, value, deployMode?: DeployMode[]) => { const clickCallback = (inputs, value, deployMode?: DeployMode[]) => {
console.log('value: ', value)
console.log('deployMode: ', deployMode)
createInstance(loadedContractData, value, deployMode) createInstance(loadedContractData, value, deployMode)
} }

@ -1,25 +1,34 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React, { useEffect, useRef, useState } from 'react' import React, { Ref, useEffect, useRef, useState } from 'react'
import * as remixLib from '@remix-project/remix-lib' import * as remixLib from '@remix-project/remix-lib'
import { ContractGUIProps } from '../types' import { ContractGUIProps } from '../types'
import { CopyToClipboard } from '@remix-ui/clipboard' import { CopyToClipboard } from '@remix-ui/clipboard'
import { ButtonGroup, Dropdown } from 'react-bootstrap' import { DeployButton } from './deployButton'
import { MultiDeployInput } from './multiDeployInput'
import { DeployInput } from './deployInput'
const txFormat = remixLib.execution.txFormat const txFormat = remixLib.execution.txFormat
export function ContractGUI (props: ContractGUIProps) { export function ContractGUI (props: ContractGUIProps) {
const [title, setTitle] = useState<string>('') const [title, setTitle] = useState<string>('')
const [basicInput, setBasicInput] = useState<string>('') const [basicInput, setBasicInput] = useState<string>('')
// const [toggleContainer, setToggleContainer] = useState<boolean>(false) const [toggleContainer, setToggleContainer] = useState<boolean>(false)
const [buttonOptions, setButtonOptions] = useState<{ const [buttonOptions, setButtonOptions] = useState<{
title: string, title: string,
content: string, content: string,
classList: string, classList: string,
dataId: string dataId: string,
}>({ title: '', content: '', classList: '', dataId: '' }) widthClass: string
const [selectedDeployIndex, setSelectedDeployIndex] = useState<number[]>([]) }>({ title: '', content: '', classList: '', dataId: '', widthClass: '' })
const [selectedDeployIndex, setSelectedDeployIndex] = useState<number>(null)
const [showOptions, setShowOptions] = useState<boolean>(false) const [showOptions, setShowOptions] = useState<boolean>(false)
const [hasArgs, setHasArgs] = useState<boolean>(false) const [hasArgs, setHasArgs] = useState<boolean>(false)
const [isMultiField, setIsMultiField] = useState<boolean>(false) const [isMultiField, setIsMultiField] = useState<boolean>(false)
const [deployInputs, setDeployInputs] = useState<{
internalType?: string,
name: string,
type: string
}[]>([])
const [deployPlaceholder, setDeployPlaceholder] = useState<string>('')
const multiFields = useRef<Array<HTMLInputElement | null>>([]) const multiFields = useRef<Array<HTMLInputElement | null>>([])
const basicInputRef = useRef<HTMLInputElement>() const basicInputRef = useRef<HTMLInputElement>()
@ -36,13 +45,6 @@ export function ContractGUI (props: ContractGUIProps) {
if (basicInputRef.current) basicInputRef.current.value = '' if (basicInputRef.current) 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 = [] multiFields.current = []
const hasArgs = (props.funcABI.inputs && props.funcABI.inputs.length > 0) ||
(props.funcABI.type === 'fallback') ||
(props.funcABI.type === 'receive') ||
(props.isDeploy && props.initializerOptions && props.initializerOptions.inputs && (props.initializerOptions.inputs.inputs.length > 0))
setHasArgs(hasArgs)
}, [props.title, props.funcABI]) }, [props.title, props.funcABI])
useEffect(() => { useEffect(() => {
@ -52,7 +54,8 @@ export function ContractGUI (props: ContractGUIProps) {
title: title + ' - call', title: title + ' - call',
content: 'call', content: 'call',
classList: 'btn-info', classList: 'btn-info',
dataId: title + ' - call' dataId: title + ' - call',
widthClass: props.widthClass
}) })
} else if (props.funcABI.stateMutability === 'payable' || props.funcABI.payable) { } else if (props.funcABI.stateMutability === 'payable' || props.funcABI.payable) {
// // transact. stateMutability = payable // // transact. stateMutability = payable
@ -60,7 +63,8 @@ export function ContractGUI (props: ContractGUIProps) {
title: title + ' - transact (payable)', title: title + ' - transact (payable)',
content: 'transact', content: 'transact',
classList: 'btn-danger', classList: 'btn-danger',
dataId: title + ' - transact (payable)' dataId: title + ' - transact (payable)',
widthClass: props.widthClass
}) })
} else { } else {
// // transact. stateMutability = nonpayable // // transact. stateMutability = nonpayable
@ -68,13 +72,59 @@ export function ContractGUI (props: ContractGUIProps) {
title: title + ' - transact (not payable)', title: title + ' - transact (not payable)',
content: 'transact', content: 'transact',
classList: 'btn-warning', classList: 'btn-warning',
dataId: title + ' - transact (not payable)' dataId: title + ' - transact (not payable)',
widthClass: props.widthClass
}) })
} }
}, [props.lookupOnly, props.funcABI, title]) }, [props.lookupOnly, props.funcABI, title])
const getContentOnCTC = () => { useEffect(() => {
const multiString = getMultiValsString() if (props.deployOption && props.deployOption[selectedDeployIndex]) {
if (props.deployOption[selectedDeployIndex].title === 'Deploy with Proxy') {
if (props.initializerOptions) {
setDeployInputs(props.initializerOptions.inputs.inputs)
setDeployPlaceholder(props.initializerOptions.initializeInputs)
setHasArgs(true)
if (props.initializerOptions.inputs.inputs.length > 1) setIsMultiField(true)
else setIsMultiField(false)
} else {
setDeployInputs([])
setDeployPlaceholder('')
setHasArgs(false)
setIsMultiField(false)
}
} else {
if (props.funcABI) {
setDeployInputs(props.funcABI.inputs)
setDeployPlaceholder(props.inputs)
setHasArgs(true)
if (props.funcABI.inputs.length > 1) setIsMultiField(true)
else setIsMultiField(false)
} else {
setDeployInputs([])
setDeployPlaceholder('')
setHasArgs(false)
setIsMultiField(false)
}
}
} else {
if (props.funcABI) {
setDeployInputs(props.funcABI.inputs)
setDeployPlaceholder(props.inputs)
setHasArgs(true)
if (props.funcABI.inputs.length > 1) setIsMultiField(true)
else setIsMultiField(false)
} else {
setDeployInputs([])
setDeployPlaceholder('')
setHasArgs(false)
setIsMultiField(false)
}
}
}, [selectedDeployIndex, props.funcABI, props.initializerOptions])
const getContentOnCTC = (fields: HTMLInputElement[]) => {
const multiString = getMultiValsString(fields)
// copy-to-clipboard icon is only visible for method requiring input params // copy-to-clipboard icon is only visible for method requiring input params
if (!multiString) { if (!multiString) {
return 'cannot encode empty arguments' return 'cannot encode empty arguments'
@ -94,31 +144,21 @@ export function ContractGUI (props: ContractGUIProps) {
} }
} }
useEffect(() => { const switchMethodViewOn = () => {
if (props.initializerOptions) { setToggleContainer(true)
if (props.initializerOptions.inputs.inputs.length > 1) setIsMultiField(true) makeMultiVal()
else setIsMultiField(false) }
} else if (props.funcABI) {
if (props.funcABI.inputs.length > 1) setIsMultiField(true)
else setIsMultiField(false)
} else setIsMultiField(false)
}, [props.initializerOptions, props.funcABI])
// const switchMethodViewOn = () => {
// setToggleContainer(true)
// makeMultiVal()
// }
const switchMethodViewOff = () => { const switchMethodViewOff = () => {
// setToggleContainer(false) setToggleContainer(false)
let multiValString = getMultiValsString() const multiValString = getMultiValsString(multiFields.current)
multiValString = multiValString.replace(/["]+/g, '')
if (multiValString) setBasicInput(multiValString) if (multiValString) setBasicInput(multiValString)
} }
const getMultiValsString = () => { const getMultiValsString = (fields: HTMLInputElement[]) => {
const valArray = multiFields.current const valArray = fields
console.log('valArray: ', valArray)
let ret = '' let ret = ''
const valArrayTest = [] const valArrayTest = []
@ -154,7 +194,6 @@ export function ContractGUI (props: ContractGUIProps) {
inputString = inputString.replace(/(^|,\s+|,)(\d+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted number by quoted number inputString = inputString.replace(/(^|,\s+|,)(\d+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted number by quoted number
inputString = inputString.replace(/(^|,\s+|,)(0[xX][0-9a-fA-F]+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted hex string by quoted hex string inputString = inputString.replace(/(^|,\s+|,)(0[xX][0-9a-fA-F]+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted hex string by quoted hex string
inputString = JSON.stringify([inputString]) inputString = JSON.stringify([inputString])
const inputJSON = JSON.parse(inputString) const inputJSON = JSON.parse(inputString)
const multiInputs = multiFields.current const multiInputs = multiFields.current
@ -167,8 +206,9 @@ export function ContractGUI (props: ContractGUIProps) {
} }
const handleActionClick = () => { const handleActionClick = () => {
const deployMode = selectedDeployIndex.map(index => props.deployOption[index].title) const deployMode = selectedDeployIndex !== null ? [props.deployOption[selectedDeployIndex].title] : []
console.log('basicInput: ', basicInput)
props.clickCallBack(props.funcABI.inputs, basicInput, deployMode) props.clickCallBack(props.funcABI.inputs, basicInput, deployMode)
} }
@ -178,9 +218,10 @@ export function ContractGUI (props: ContractGUIProps) {
setBasicInput(value) setBasicInput(value)
} }
const handleMultiValsSubmit = () => { const handleMultiValsSubmit = (fields: HTMLInputElement[]) => {
const valsString = getMultiValsString() console.log('fields: ', fields)
const deployMode = selectedDeployIndex.map(index => props.deployOption[index].title) const valsString = getMultiValsString(fields)
const deployMode = selectedDeployIndex !== null ? [props.deployOption[selectedDeployIndex].title] : []
if (valsString) { if (valsString) {
props.clickCallBack(props.funcABI.inputs, valsString, deployMode) props.clickCallBack(props.funcABI.inputs, valsString, deployMode)
@ -190,12 +231,9 @@ export function ContractGUI (props: ContractGUIProps) {
} }
const setSelectedDeploy = (index: number) => { const setSelectedDeploy = (index: number) => {
const indexes = selectedDeployIndex.slice() setSelectedDeployIndex(index !== selectedDeployIndex ? index : null)
const existingIndex = indexes.findIndex(value => value === index) if (basicInputRef.current) basicInputRef.current.value = ''
setBasicInput('')
if (existingIndex > -1) indexes.splice(existingIndex, 1)
else indexes.push(index)
setSelectedDeployIndex(indexes)
} }
const toggleOptions = () => { const toggleOptions = () => {
@ -204,35 +242,30 @@ export function ContractGUI (props: ContractGUIProps) {
return ( return (
<div className={`udapp_contractProperty ${hasArgs ? 'udapp_hasArgs' : ''}`}> <div className={`udapp_contractProperty ${hasArgs ? 'udapp_hasArgs' : ''}`}>
<div className="udapp_contractActionsContainerSingle pt-2" style={{ display: 'flex' }}>
{
props.isDeploy && !isMultiField && (props.deployOption || []).length > 0 ?
<Dropdown as={ButtonGroup} show={showOptions}>
<button onClick={handleActionClick} title={buttonOptions.title} className={`udapp_instanceButton ${props.widthClass} btn btn-sm ${buttonOptions.classList}`} data-id={buttonOptions.dataId}>Deploy</button>
<Dropdown.Toggle split id="dropdown-split-basic" className={`btn btn-sm dropdown-toggle dropdown-toggle-split ${buttonOptions.classList}`} style={{ maxWidth: 25, minWidth: 0, height: 32 }} onClick={toggleOptions} />
<Dropdown.Menu className="deploy-items border-0">
{
(props.deployOption).map(({ title, active }, index) => <Dropdown.Item onClick={() => setSelectedDeploy(index)} key={index}> { selectedDeployIndex.includes(index) ? <span>&#10003; {title} </span> : <span className="pl-3">{title}</span> }</Dropdown.Item>)
}
</Dropdown.Menu>
</Dropdown> :
!isMultiField ?
<button onClick={handleActionClick} title={buttonOptions.title} className={`udapp_instanceButton ${props.widthClass} btn btn-sm ${buttonOptions.classList}`} data-id={buttonOptions.dataId}>
{title}
</button> : null
}
{ {
props.isDeploy && !isMultiField && props.initializerOptions ? props.isDeploy ? !isMultiField ?
<input <DeployInput
className="form-control" buttonOptions={buttonOptions}
data-id={props.initializerOptions.inputs.type === 'fallback' || props.initializerOptions.inputs.type === 'receive' ? `'(${props.initializerOptions.inputs.type}')` : 'multiParamManagerBasicInputField'} funcABI={props.initializerOptions ? props.initializerOptions.inputs : props.funcABI}
placeholder={props.initializerOptions.initializeInputs} inputs={deployPlaceholder}
title={props.initializerOptions.inputs.type === 'fallback' || props.initializerOptions.inputs.type === 'receive' ? `'(${props.initializerOptions.inputs.type}')` : props.initializerOptions.initializeInputs} handleBasicInput={handleBasicInput}
onChange={handleBasicInput} basicInputRef={basicInputRef}
ref={basicInputRef} selectedIndex={selectedDeployIndex}
style={{ visibility: !((props.initializerOptions.inputs.inputs && props.initializerOptions.inputs.inputs.length > 0) || (props.initializerOptions.inputs.type === 'fallback') || (props.initializerOptions.inputs.type === 'receive')) ? 'hidden' : 'visible' }} setSelectedIndex={setSelectedDeploy}
/> handleActionClick={handleActionClick}
: !isMultiField && props.funcABI ? deployOptions={props.deployOption}
/> : <MultiDeployInput
buttonOptions={buttonOptions}
selectedIndex={selectedDeployIndex}
setSelectedIndex={setSelectedDeploy}
handleMultiValsSubmit={handleMultiValsSubmit}
inputs={deployInputs}
getMultiValsString={getMultiValsString}
deployOptions={props.deployOption}
/> :
<>
<div className="udapp_contractActionsContainerSingle pt-2" style={{ display: toggleContainer ? 'none' : 'flex' }}>
<button onClick={handleActionClick} title={buttonOptions.title} className={`udapp_instanceButton ${props.widthClass} btn btn-sm ${buttonOptions.classList}`} data-id={buttonOptions.dataId}>{title}</button>
<input <input
className="form-control" className="form-control"
data-id={props.funcABI.type === 'fallback' || props.funcABI.type === 'receive' ? `'(${props.funcABI.type}')` : 'multiParamManagerBasicInputField'} data-id={props.funcABI.type === 'fallback' || props.funcABI.type === 'receive' ? `'(${props.funcABI.type}')` : 'multiParamManagerBasicInputField'}
@ -240,48 +273,18 @@ export function ContractGUI (props: ContractGUIProps) {
title={props.funcABI.type === 'fallback' || props.funcABI.type === 'receive' ? `'(${props.funcABI.type}')` : props.inputs} title={props.funcABI.type === 'fallback' || props.funcABI.type === 'receive' ? `'(${props.funcABI.type}')` : props.inputs}
onChange={handleBasicInput} onChange={handleBasicInput}
ref={basicInputRef} ref={basicInputRef}
style={{ visibility: !((props.funcABI.inputs && props.funcABI.inputs.length > 0) || (props.funcABI.type === 'fallback') || (props.funcABI.type === 'receive')) ? 'hidden' : 'visible' }} style={{ visibility: !((props.funcABI.inputs && props.funcABI.inputs.length > 0) || (props.funcABI.type === 'fallback') || (props.funcABI.type === 'receive')) ? 'hidden' : 'visible' }} />
/> : null <i
} className="fas fa-angle-down udapp_methCaret"
</div> onClick={switchMethodViewOn}
{ isMultiField && props.isDeploy && props.initializerOptions ? title={title}
<div className="udapp_contractActionsContainerMulti" style={{ display: 'flex' }}> style={{ visibility: !(props.funcABI.inputs && props.funcABI.inputs.length > 0) ? 'hidden' : 'visible' }}></i>
<div className="udapp_contractActionsContainerMultiInner text-dark">
<div className="udapp_multiHeader">
<div className="udapp_multiTitle run-instance-multi-title">{title}</div>
</div>
<div>
{props.initializerOptions.inputs.inputs.map((inp, index) => {
return (
<div className="udapp_multiArg" key={index}>
<label htmlFor={inp.name}> {inp.name}: </label>
<input ref={el => { multiFields.current[index] = el }} className="form-control" placeholder={inp.type} title={inp.name} data-id={`multiParamManagerInput${inp.name}`} />
</div>)
})}
</div> </div>
<div className="udapp_group udapp_multiArg"> <div className="udapp_contractActionsContainerMulti" style={{ display: toggleContainer ? 'flex' : 'none' }}>
{/* <CopyToClipboard tip='Encode values of input fields & copy to clipboard' icon='fa-clipboard' direction={'left'} getContent={getContentOnCTC} /> */}
{
(props.deployOption || []).length > 0 ?
<Dropdown as={ButtonGroup} show={showOptions}>
<button onClick={handleMultiValsSubmit} title={buttonOptions.title} className={`udapp_instanceButton ${props.widthClass} btn btn-sm ${buttonOptions.classList}`} data-id={buttonOptions.dataId}>Deploy</button>
<Dropdown.Toggle split id="dropdown-split-basic" className={`btn btn-sm dropdown-toggle dropdown-toggle-split ${buttonOptions.classList}`} style={{ maxWidth: 25, minWidth: 0, height: 32 }} onClick={toggleOptions} />
<Dropdown.Menu className="deploy-items border-0">
{
(props.deployOption).map(({ title, active }, index) => <Dropdown.Item onClick={() => setSelectedDeploy(index)} key={index}> { selectedDeployIndex.includes(index) ? <span>&#10003; {title} </span> : <span className="pl-3">{title}</span> }</Dropdown.Item>)
}
</Dropdown.Menu>
</Dropdown> :
<button onClick={handleMultiValsSubmit} title={buttonOptions.title} data-id={buttonOptions.dataId} className={`udapp_instanceButton ${buttonOptions.classList}`}>{ title }</button>
}
</div>
</div>
</div> :
isMultiField && props.funcABI ?
<div className="udapp_contractActionsContainerMulti" style={{ display: 'flex' }}>
<div className="udapp_contractActionsContainerMultiInner text-dark"> <div className="udapp_contractActionsContainerMultiInner text-dark">
<div className="udapp_multiHeader"> <div onClick={switchMethodViewOff} className="udapp_multiHeader">
<div className="udapp_multiTitle run-instance-multi-title">{title}</div> <div className="udapp_multiTitle run-instance-multi-title">{title}</div>
<i className='fas fa-angle-up udapp_methCaret'></i>
</div> </div>
<div> <div>
{props.funcABI.inputs.map((inp, index) => { {props.funcABI.inputs.map((inp, index) => {
@ -293,11 +296,12 @@ export function ContractGUI (props: ContractGUIProps) {
})} })}
</div> </div>
<div className="udapp_group udapp_multiArg"> <div className="udapp_group udapp_multiArg">
<CopyToClipboard tip='Encode values of input fields & copy to clipboard' icon='fa-clipboard' direction={'left'} getContent={getContentOnCTC} /> <CopyToClipboard tip='Encode values of input fields & copy to clipboard' icon='fa-clipboard' direction={'left'} getContent={() => getContentOnCTC(multiFields.current)} />
<button onClick={handleMultiValsSubmit} title={buttonOptions.title} data-id={buttonOptions.dataId} className={`udapp_instanceButton ${buttonOptions.classList}`}>{ buttonOptions.content }</button> <button onClick={() => handleMultiValsSubmit(multiFields.current)} title={buttonOptions.title} data-id={buttonOptions.dataId} className={`udapp_instanceButton ${buttonOptions.classList}`}>{ buttonOptions.content }</button>
</div>
</div> </div>
</div> </div>
</div> : null </>
} }
</div> </div>
) )

@ -0,0 +1,33 @@
import React, { useState } from 'react'
import { DeployButtonProps } from '../types'
import { ButtonGroup, Dropdown } from 'react-bootstrap'
export function DeployButton (props: DeployButtonProps) {
const [showOptions, setShowOptions] = useState<boolean>(false)
const toggleOptions = () => {
setShowOptions(!showOptions)
}
return (
<>
{ props.deployOptions && (props.deployOptions || []).length > 0 ?
<Dropdown as={ButtonGroup} show={showOptions}>
<button onClick={props.handleActionClick} title={props.buttonOptions.title} className={`udapp_instanceButton ${props.buttonOptions.widthClass} btn btn-sm ${props.buttonOptions.classList}`} data-id={props.buttonOptions.dataId}>Deploy</button>
<Dropdown.Toggle split id="dropdown-split-basic" className={`btn btn-sm dropdown-toggle dropdown-toggle-split ${props.buttonOptions.classList}`} style={{ maxWidth: 25, minWidth: 0, height: 32 }} onClick={toggleOptions} />
<Dropdown.Menu className="deploy-items border-0">
{
(props.deployOptions).map(({ title, active }, index) => <Dropdown.Item onClick={() => {
props.setSelectedIndex(index)
toggleOptions()
}} key={index}> { props.selectedIndex === index ? <span>&#10003; {title} </span> : <span className="pl-3">{title}</span> }</Dropdown.Item>)
}
</Dropdown.Menu>
</Dropdown> :
<button onClick={props.handleActionClick} title={props.buttonOptions.title} className={`udapp_instanceButton ${props.buttonOptions.widthClass} btn btn-sm ${props.buttonOptions.classList}`} data-id={props.buttonOptions.dataId}>
Deploy
</button>
}
</>
)
}

@ -0,0 +1,20 @@
import React from 'react'
import { DeployInputProps } from '../types'
import { DeployButton } from './deployButton'
export function DeployInput (props: DeployInputProps) {
return (
<div className="udapp_contractActionsContainerSingle pt-2" style={{ display: 'flex' }}>
<DeployButton buttonOptions={props.buttonOptions} selectedIndex={props.selectedIndex} setSelectedIndex={props.setSelectedIndex} handleActionClick={props.handleActionClick} deployOptions={props.deployOptions} />
<input
className="form-control"
data-id={props.funcABI.type === 'fallback' || props.funcABI.type === 'receive' ? `'(${props.funcABI.type}')` : 'multiParamManagerBasicInputField'}
placeholder={props.inputs}
title={props.funcABI.type === 'fallback' || props.funcABI.type === 'receive' ? `'(${props.funcABI.type}')` : props.inputs}
onChange={props.handleBasicInput}
ref={props.basicInputRef}
style={{ visibility: !props.inputs ? 'hidden' : 'visible' }}
/>
</div>
)
}

@ -0,0 +1,37 @@
import React, { useRef, useState } from 'react'
import { MultiDeployInputProps } from '../types'
import { DeployButton } from './deployButton'
export function MultiDeployInput (props: MultiDeployInputProps) {
const [showOptions, setShowOptions] = useState<boolean>(false)
const multiFields = useRef<Array<HTMLInputElement | null>>([])
const toggleOptions = () => {
setShowOptions(!showOptions)
}
// TODO: Clear multiFields after deployment.
return (
<div className="udapp_contractActionsContainerMulti" style={{ display: 'flex' }}>
<div className="udapp_contractActionsContainerMultiInner text-dark">
<div className="udapp_multiHeader">
<div className="udapp_multiTitle run-instance-multi-title">Deploy</div>
</div>
<div>
{props.inputs.map((inp, index) => {
return (
<div className="udapp_multiArg" key={index}>
<label htmlFor={inp.name}> {inp.name}: </label>
<input ref={el => { multiFields.current[index] = el }} className="form-control" placeholder={inp.type} title={inp.name} data-id={`multiParamManagerInput${inp.name}`} />
</div>)
})}
</div>
<div className="udapp_group udapp_multiArg">
{/* <CopyToClipboard tip='Encode values of input fields & copy to clipboard' icon='fa-clipboard' direction={'left'} getContent={getContentOnCTC} /> */}
<DeployButton buttonOptions={props.buttonOptions} selectedIndex={props.selectedIndex} setSelectedIndex={props.setSelectedIndex} handleActionClick={() => { props.handleMultiValsSubmit(multiFields.current) }} deployOptions={props.deployOptions} />
</div>
</div>
</div>
)
}

@ -1,3 +1,4 @@
import { Ref } from 'react'
import { CompilerAbstract } from '@remix-project/remix-solidity-ts' import { CompilerAbstract } from '@remix-project/remix-solidity-ts'
import { ContractData, FuncABI } from '@remix-project/core-plugin' import { ContractData, FuncABI } from '@remix-project/core-plugin'
import { ContractList } from '../reducers/runTab' import { ContractList } from '../reducers/runTab'
@ -223,17 +224,17 @@ export type DeployMode = 'Deploy with Proxy' | 'Upgrade Proxy'
export type DeployOption = { export type DeployOption = {
initializeInputs: string, initializeInputs: string,
inputs: { inputs: {
inputs: [ inputs: {
{ internalType?: string,
internalType: string,
name: string, name: string,
type: string type: string
} }[],
],
name: "initialize", name: "initialize",
outputs: any[], outputs?: any[],
stateMutability: string, stateMutability: string,
type: string type: string,
payable?: boolean,
constant?: any
} }
} }
export interface DeployOptions { export interface DeployOptions {
@ -305,3 +306,55 @@ export interface UdappProps {
sendValue: string, sendValue: string,
getFuncABIInputs: (funcABI: FuncABI) => string getFuncABIInputs: (funcABI: FuncABI) => string
} }
export interface DeployButtonProps {
deployOptions: { title: DeployMode, active: boolean }[],
buttonOptions: {
title: string,
content: string,
classList: string,
dataId: string,
widthClass: string
},
selectedIndex: number,
setSelectedIndex: (index: number) => void,
handleActionClick: () => void
}
export interface DeployInputProps {
funcABI: FuncABI,
inputs: string,
handleBasicInput: (e) => void,
basicInputRef: Ref<HTMLInputElement>,
buttonOptions: {
title: string,
content: string,
classList: string,
dataId: string,
widthClass: string
},
selectedIndex: number,
setSelectedIndex: (index: number) => void,
handleActionClick: (fields?: HTMLInputElement[]) => void,
deployOptions: { title: DeployMode, active: boolean }[]
}
export interface MultiDeployInputProps {
deployOptions?: { title: DeployMode, active: boolean }[],
buttonOptions: {
title: string,
content: string,
classList: string,
dataId: string,
widthClass: string
},
selectedIndex: number,
setSelectedIndex: (index: number) => void,
handleMultiValsSubmit: (fields?: HTMLInputElement[]) => void,
inputs: {
internalType?: string,
name: string,
type: string
}[],
getMultiValsString: (fields: HTMLInputElement[]) => void
}

Loading…
Cancel
Save