Merge pull request #2987 from ethereum/dropdowncontracts

Dropdowncontracts
pull/2990/head
bunsenstraat 2 years ago committed by GitHub
commit 21f68ba9bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 21
      libs/remix-ui/run-tab/src/lib/actions/events.ts
  2. 16
      libs/remix-ui/run-tab/src/lib/actions/payload.ts
  3. 82
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  4. 2
      libs/remix-ui/run-tab/src/lib/constants/index.ts
  5. 32
      libs/remix-ui/run-tab/src/lib/reducers/runTab.ts
  6. 3
      libs/remix-ui/run-tab/src/lib/run-tab.tsx
  7. 1
      libs/remix-ui/run-tab/src/lib/types/index.ts
  8. 46
      libs/remixd/src/services/hardhatClient.ts

@ -2,10 +2,11 @@ import { envChangeNotification } from "@remix-ui/helper"
import { RunTab } from "../types/run-tab" import { RunTab } from "../types/run-tab"
import { setExecutionContext, setFinalContext, updateAccountBalances } from "./account" import { setExecutionContext, setFinalContext, updateAccountBalances } from "./account"
import { addExternalProvider, addInstance, removeExternalProvider, setNetworkNameFromProvider } from "./actions" import { addExternalProvider, addInstance, removeExternalProvider, setNetworkNameFromProvider } from "./actions"
import { addDeployOption, clearAllInstances, clearRecorderCount, fetchContractListSuccess, resetUdapp, setCompilationSource, setCurrentContract, setCurrentFile, setLoadType, setProxyEnvAddress, setRecorderCount, setSendValue } from "./payload" import { addDeployOption, clearAllInstances, clearRecorderCount, fetchContractListSuccess, resetUdapp, setCurrentContract, setCurrentFile, setLoadType, setProxyEnvAddress, setRecorderCount, setRemixDActivated, setSendValue } from "./payload"
import { CompilerAbstract } from '@remix-project/remix-solidity' import { CompilerAbstract } from '@remix-project/remix-solidity'
import * as ethJSUtil from 'ethereumjs-util' import * as ethJSUtil from 'ethereumjs-util'
import Web3 from 'web3' import Web3 from 'web3'
import { Plugin } from "@remixproject/engine"
const _paq = window._paq = window._paq || [] const _paq = window._paq = window._paq || []
export const setupEvents = (plugin: RunTab, dispatch: React.Dispatch<any>) => { export const setupEvents = (plugin: RunTab, dispatch: React.Dispatch<any>) => {
@ -74,6 +75,21 @@ export const setupEvents = (plugin: RunTab, dispatch: React.Dispatch<any>) => {
plugin.on('filePanel', 'setWorkspace', () => { plugin.on('filePanel', 'setWorkspace', () => {
dispatch(resetUdapp()) dispatch(resetUdapp())
resetAndInit(plugin) resetAndInit(plugin)
plugin.call('manager', 'isActive', 'remixd').then((activated) => {
dispatch(setRemixDActivated(activated))
})
})
plugin.on('manager', 'pluginActivated', (plugin: Plugin) => {
if (plugin.name === 'remixd') {
dispatch(setRemixDActivated(true))
}
})
plugin.on('manager', 'pluginDeactivated', (plugin: Plugin) => {
if (plugin.name === 'remixd') {
dispatch(setRemixDActivated(false))
}
}) })
plugin.fileManager.events.on('currentFileChanged', (currentFile: string) => { plugin.fileManager.events.on('currentFileChanged', (currentFile: string) => {
@ -107,7 +123,7 @@ const broadcastCompilationResult = async (compilerName: string, plugin: RunTab,
plugin.compilersArtefacts.__last = compiler plugin.compilersArtefacts.__last = compiler
const contracts = getCompiledContracts(compiler).map((contract) => { const contracts = getCompiledContracts(compiler).map((contract) => {
return { name: languageVersion, alias: contract.name, file: contract.file, compiler } return { name: languageVersion, alias: contract.name, file: contract.file, compiler, compilerName }
}) })
if ((contracts.length > 0)) { if ((contracts.length > 0)) {
const contractsInCompiledFile = contracts.filter(obj => obj.file === file) const contractsInCompiledFile = contracts.filter(obj => obj.file === file)
@ -127,7 +143,6 @@ const broadcastCompilationResult = async (compilerName: string, plugin: RunTab,
} }
dispatch(fetchContractListSuccess({ [file]: contracts })) dispatch(fetchContractListSuccess({ [file]: contracts }))
dispatch(setCurrentFile(file)) dispatch(setCurrentFile(file))
dispatch(setCompilationSource(compilerName))
// TODO: set current contract // TODO: set current contract
} }

@ -1,6 +1,6 @@
import { ContractList } from '../reducers/runTab' import { ContractList } from '../reducers/runTab'
import { ContractData } from '@remix-project/core-plugin' import { ContractData } from '@remix-project/core-plugin'
import { ADD_DEPLOY_OPTION, ADD_INSTANCE, ADD_PROVIDER, CLEAR_INSTANCES, CLEAR_RECORDER_COUNT, DISPLAY_NOTIFICATION, DISPLAY_POPUP_MESSAGE, FETCH_ACCOUNTS_LIST_FAILED, FETCH_ACCOUNTS_LIST_REQUEST, FETCH_ACCOUNTS_LIST_SUCCESS, FETCH_CONTRACT_LIST_FAILED, FETCH_CONTRACT_LIST_REQUEST, FETCH_CONTRACT_LIST_SUCCESS, HIDE_NOTIFICATION, HIDE_POPUP_MESSAGE, REMOVE_DEPLOY_OPTION, REMOVE_INSTANCE, REMOVE_PROVIDER, RESET_STATE, SET_BASE_FEE_PER_GAS, SET_CONFIRM_SETTINGS, SET_CURRENT_CONTRACT, SET_CURRENT_FILE, SET_COMPILATION_SOURCE, SET_DECODED_RESPONSE, SET_DEPLOY_OPTIONS, SET_EXECUTION_ENVIRONMENT, SET_EXTERNAL_WEB3_ENDPOINT, SET_GAS_LIMIT, SET_GAS_PRICE, SET_GAS_PRICE_STATUS, SET_IPFS_CHECKED_STATE, SET_LOAD_TYPE, SET_MATCH_PASSPHRASE, SET_MAX_FEE, SET_MAX_PRIORITY_FEE, SET_NETWORK_NAME, SET_PASSPHRASE, SET_PATH_TO_SCENARIO, SET_PERSONAL_MODE, SET_PROXY_ENV_ADDRESS, SET_RECORDER_COUNT, SET_SELECTED_ACCOUNT, SET_SEND_UNIT, SET_SEND_VALUE } from '../constants' import { ADD_DEPLOY_OPTION, ADD_INSTANCE, ADD_PROVIDER, CLEAR_INSTANCES, CLEAR_RECORDER_COUNT, DISPLAY_NOTIFICATION, DISPLAY_POPUP_MESSAGE, FETCH_ACCOUNTS_LIST_FAILED, FETCH_ACCOUNTS_LIST_REQUEST, FETCH_ACCOUNTS_LIST_SUCCESS, FETCH_CONTRACT_LIST_FAILED, FETCH_CONTRACT_LIST_REQUEST, FETCH_CONTRACT_LIST_SUCCESS, HIDE_NOTIFICATION, HIDE_POPUP_MESSAGE, REMOVE_DEPLOY_OPTION, REMOVE_INSTANCE, REMOVE_PROVIDER, RESET_STATE, SET_BASE_FEE_PER_GAS, SET_CONFIRM_SETTINGS, SET_CURRENT_CONTRACT, SET_CURRENT_FILE, SET_DECODED_RESPONSE, SET_DEPLOY_OPTIONS, SET_EXECUTION_ENVIRONMENT, SET_EXTERNAL_WEB3_ENDPOINT, SET_GAS_LIMIT, SET_GAS_PRICE, SET_GAS_PRICE_STATUS, SET_IPFS_CHECKED_STATE, SET_LOAD_TYPE, SET_MATCH_PASSPHRASE, SET_MAX_FEE, SET_MAX_PRIORITY_FEE, SET_NETWORK_NAME, SET_PASSPHRASE, SET_PATH_TO_SCENARIO, SET_PERSONAL_MODE, SET_PROXY_ENV_ADDRESS, SET_RECORDER_COUNT, SET_SELECTED_ACCOUNT, SET_SEND_UNIT, SET_SEND_VALUE, SET_REMIXD_ACTIVATED } from '../constants'
import { DeployMode, DeployOptions } from '../types' import { DeployMode, DeployOptions } from '../types'
export const fetchAccountsListRequest = () => { export const fetchAccountsListRequest = () => {
@ -168,13 +168,6 @@ export const setCurrentFile = (file: string) => {
} }
} }
export const setCompilationSource = (source: string) => {
return {
type: SET_COMPILATION_SOURCE,
payload: source
}
}
export const setIpfsCheckedState = (state: boolean) => { export const setIpfsCheckedState = (state: boolean) => {
return { return {
type: SET_IPFS_CHECKED_STATE, type: SET_IPFS_CHECKED_STATE,
@ -315,3 +308,10 @@ export const setProxyEnvAddress = (key: string) => {
type: SET_PROXY_ENV_ADDRESS type: SET_PROXY_ENV_ADDRESS
} }
} }
export const setRemixDActivated = (activated: boolean) => {
return {
payload: activated,
type: SET_REMIXD_ACTIVATED
}
}

@ -8,7 +8,7 @@ import { deployWithProxyMsg, upgradeWithProxyMsg } from '@remix-ui/helper'
import { OverlayTrigger, Tooltip } from 'react-bootstrap' import { OverlayTrigger, Tooltip } from 'react-bootstrap'
const _paq = window._paq = window._paq || [] const _paq = window._paq = window._paq || []
export function ContractDropdownUI (props: ContractDropdownProps) { export function ContractDropdownUI(props: ContractDropdownProps) {
const [abiLabel, setAbiLabel] = useState<{ const [abiLabel, setAbiLabel] = useState<{
display: string, display: string,
content: string content: string
@ -16,18 +16,19 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
display: '', display: '',
content: '' content: ''
}) })
const [atAddressOptions, setAtAddressOptions] = useState<{title: string, disabled: boolean}>({ const [atAddressOptions, setAtAddressOptions] = useState<{ title: string, disabled: boolean }>({
title: 'address of contract', title: 'address of contract',
disabled: true disabled: true
}) })
const [loadedAddress, setLoadedAddress] = useState<string>('') const [loadedAddress, setLoadedAddress] = useState<string>('')
const [contractOptions, setContractOptions] = useState<{title: string, disabled: boolean}>({ const [contractOptions, setContractOptions] = useState<{ title: string, disabled: boolean }>({
title: 'Please compile *.sol file to deploy or access a contract', title: 'Please compile *.sol file to deploy or access a contract',
disabled: true disabled: true
}) })
const [loadedContractData, setLoadedContractData] = useState<ContractData>(null) const [loadedContractData, setLoadedContractData] = useState<ContractData>(null)
const [constructorInterface, setConstructorInterface] = useState<FuncABI>(null) const [constructorInterface, setConstructorInterface] = useState<FuncABI>(null)
const [constructorInputs, setConstructorInputs] = useState(null) const [constructorInputs, setConstructorInputs] = useState(null)
const [compilerName, setCompilerName] = useState<string>('')
const contractsRef = useRef<HTMLSelectElement>(null) const contractsRef = useRef<HTMLSelectElement>(null)
const atAddressValue = useRef<HTMLInputElement>(null) const atAddressValue = useRef<HTMLInputElement>(null)
const { contractList, loadType, currentFile, compilationSource, currentContract, compilationCount, deployOptions, proxyKey } = props.contracts const { contractList, loadType, currentFile, compilationSource, currentContract, compilationCount, deployOptions, proxyKey } = props.contracts
@ -98,20 +99,23 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
useEffect(() => { useEffect(() => {
initSelectedContract() initSelectedContract()
updateCompilerName()
}, [contractList]) }, [contractList])
useEffect(() => { useEffect(() => {
// if the file change the ui is already feed with another bunch of contracts. // if the file change the ui is already feed with another bunch of contracts.
// we also need to update the state // we also need to update the state
const contracts = contractList[currentFile] const contracts = contractList[currentFile]
if (contracts && contracts.length > 0) { if (contracts && contracts.length > 0) {
props.setSelectedContract(contracts[0].alias) props.setSelectedContract(contracts[0].alias)
} }
updateCompilerName()
}, [currentFile]) }, [currentFile])
const initSelectedContract = () => { const initSelectedContract = () => {
const contracts = contractList[currentFile] const contracts = contractList[currentFile]
if (contracts && contracts.length > 0) { if (contracts && contracts.length > 0) {
const contract = contracts.find(contract => contract.alias === currentContract) const contract = contracts.find(contract => contract.alias === currentContract)
@ -123,9 +127,9 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
const isContractFile = (file) => { const isContractFile = (file) => {
return /.(.sol)$/.exec(file) || return /.(.sol)$/.exec(file) ||
/.(.vy)$/.exec(file) || // vyper /.(.vy)$/.exec(file) || // vyper
/.(.lex)$/.exec(file) || // lexon /.(.lex)$/.exec(file) || // lexon
/.(.contract)$/.exec(file) /.(.contract)$/.exec(file)
} }
const enableAtAddress = (enable: boolean) => { const enableAtAddress = (enable: boolean) => {
@ -162,20 +166,20 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
const createInstance = (selectedContract, args, deployMode?: DeployMode[]) => { const createInstance = (selectedContract, args, deployMode?: DeployMode[]) => {
if (selectedContract.bytecodeObject.length === 0) { if (selectedContract.bytecodeObject.length === 0) {
return props.modal('Alert', 'This contract may be abstract, it may not implement an abstract parent\'s methods completely or it may not invoke an inherited contract\'s constructor correctly.', 'OK', () => {}) return props.modal('Alert', 'This contract may be abstract, it may not implement an abstract parent\'s methods completely or it may not invoke an inherited contract\'s constructor correctly.', 'OK', () => { })
} }
if ((selectedContract.name !== currentContract) && (selectedContract.name === 'ERC1967Proxy')) selectedContract.name = currentContract if ((selectedContract.name !== currentContract) && (selectedContract.name === 'ERC1967Proxy')) selectedContract.name = currentContract
const isProxyDeployment = (deployMode || []).find(mode => mode === 'Deploy with Proxy') const isProxyDeployment = (deployMode || []).find(mode => mode === 'Deploy with Proxy')
const isContractUpgrade = (deployMode || []).find(mode => mode === 'Upgrade with Proxy') const isContractUpgrade = (deployMode || []).find(mode => mode === 'Upgrade with Proxy')
if (isProxyDeployment) { if (isProxyDeployment) {
props.modal('Deploy Implementation & Proxy (ERC1967)', deployWithProxyMsg(), 'Proceed', () => { props.modal('Deploy Implementation & Proxy (ERC1967)', deployWithProxyMsg(), 'Proceed', () => {
props.createInstance(loadedContractData, props.gasEstimationPrompt, props.passphrasePrompt, props.publishToStorage, props.mainnetPrompt, isOverSizePrompt, args, deployMode) props.createInstance(loadedContractData, props.gasEstimationPrompt, props.passphrasePrompt, props.publishToStorage, props.mainnetPrompt, isOverSizePrompt, args, deployMode)
}, 'Cancel', () => {}) }, 'Cancel', () => { })
} else if (isContractUpgrade) { } else if (isContractUpgrade) {
props.modal('Deploy Implementation & Update Proxy', upgradeWithProxyMsg(), 'Proceed', () => { props.modal('Deploy Implementation & Update Proxy', upgradeWithProxyMsg(), 'Proceed', () => {
props.createInstance(loadedContractData, props.gasEstimationPrompt, props.passphrasePrompt, props.publishToStorage, props.mainnetPrompt, isOverSizePrompt, args, deployMode) props.createInstance(loadedContractData, props.gasEstimationPrompt, props.passphrasePrompt, props.publishToStorage, props.mainnetPrompt, isOverSizePrompt, args, deployMode)
}, 'Cancel', () => {}) }, 'Cancel', () => { })
} else { } else {
props.createInstance(loadedContractData, props.gasEstimationPrompt, props.passphrasePrompt, props.publishToStorage, props.mainnetPrompt, isOverSizePrompt, args, deployMode) props.createInstance(loadedContractData, props.gasEstimationPrompt, props.passphrasePrompt, props.publishToStorage, props.mainnetPrompt, isOverSizePrompt, args, deployMode)
} }
@ -212,9 +216,21 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
window.localStorage.setItem(`ipfs/${props.exEnvironment}/${props.networkName}`, checkedState.toString()) window.localStorage.setItem(`ipfs/${props.exEnvironment}/${props.networkName}`, checkedState.toString())
} }
const updateCompilerName = () => {
if (contractsRef.current.value) {
contractList[currentFile].forEach(contract => {
if (contract.alias === contractsRef.current.value) {
setCompilerName(contract.compilerName)
}
})
} else{
setCompilerName('')
}
}
const handleContractChange = (e) => { const handleContractChange = (e) => {
const value = e.target.value const value = e.target.value
updateCompilerName()
props.setSelectedContract(value) props.setSelectedContract(value)
} }
@ -231,7 +247,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
const isOverSizePrompt = () => { const isOverSizePrompt = () => {
return ( return (
<div>Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails. <br /> <div>Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails. <br />
More info: <a href="https://github.com/ethereum/EIPs/blob/master/EIPS/eip-170.md" target="_blank" rel="noreferrer">eip-170</a> More info: <a href="https://github.com/ethereum/EIPs/blob/master/EIPS/eip-170.md" target="_blank" rel="noreferrer">eip-170</a>
</div> </div>
) )
} }
@ -241,33 +257,37 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
<div className='d-flex justify-content-between'> <div className='d-flex justify-content-between'>
<div className="d-flex justify-content-between align-items-end"> <div className="d-flex justify-content-between align-items-end">
<label className="udapp_settingsLabel pr-1">Contract</label> <label className="udapp_settingsLabel pr-1">Contract</label>
<div className="d-flex">{ Object.keys(props.contracts.contractList).length > 0 && compilationSource !== '' && <label className="text-capitalize" style={{maxHeight: '0.6rem', lineHeight: '1rem'}} data-id="udappCompiledBy">(Compiled by {compilationSource})</label>}</div> <div className="d-flex">{compilerName && compilerName !== '' && <label className="text-capitalize" style={{ maxHeight: '0.6rem', lineHeight: '1rem' }} data-id="udappCompiledBy">(Compiled by {compilerName})</label>}</div>
</div> </div>
<OverlayTrigger placement={'right'} overlay={ {props.remixdActivated ?
<Tooltip className="text-nowrap" id="info-sync-compiled-contract"> <OverlayTrigger placement={'right'} overlay={
<div>Click here to import contracts compiled from an external framework.</div> <Tooltip className="text-nowrap" id="info-sync-compiled-contract">
<div>This action is enabled when Remix is connected to an external framework (hardhat, truffle, foundry) through remixd.</div> <div>Click here to import contracts compiled from an external framework.</div>
</Tooltip> <div>This action is enabled when Remix is connected to an external framework (hardhat, truffle, foundry) through remixd.</div>
}> </Tooltip>
<button className="btn d-flex py-0" onClick={_ => { }>
<button className="btn d-flex py-0" onClick={_ => {
props.syncContracts() props.syncContracts()
_paq.push(['trackEvent', 'udapp', 'syncContracts', compilationSource]) _paq.push(['trackEvent', 'udapp', 'syncContracts', compilationSource])
}}> }}>
<i style={{ cursor: 'pointer' }} className="fa fa-refresh mr-2 mt-2" aria-hidden="true"></i> <i style={{ cursor: 'pointer' }} className="fa fa-refresh mr-2 mt-2" aria-hidden="true"></i>
</button> </button>
</OverlayTrigger> </OverlayTrigger>
: null}
</div> </div>
<div className="udapp_subcontainer"> <div className="udapp_subcontainer">
<select ref={contractsRef} value={currentContract} onChange={handleContractChange} className="udapp_contractNames custom-select" disabled={contractOptions.disabled} title={contractOptions.title} style={{ display: loadType === 'abi' && !isContractFile(currentFile) ? 'none' : 'block' }}> <select ref={contractsRef} value={currentContract} onChange={handleContractChange} className="udapp_contractNames custom-select" disabled={contractOptions.disabled} title={contractOptions.title} style={{ display: loadType === 'abi' && !isContractFile(currentFile) ? 'none' : 'block' }}>
{ (contractList[currentFile] || []).map((contract, index) => { {(contractList[currentFile] || []).map((contract, index) => {
return <option key={index} value={contract.alias}>{contract.alias} - {contract.file}</option> return <option key={index} value={contract.alias}>
}) } {contract.alias} - {contract.file}
</option>
})}
</select> </select>
<span className="py-1" style={{ display: abiLabel.display }}>{ abiLabel.content }</span> <span className="py-1" style={{ display: abiLabel.display }}>{abiLabel.content}</span>
</div> </div>
<div> <div>
<div className="udapp_deployDropdown"> <div className="udapp_deployDropdown">
{ ((contractList[currentFile] && contractList[currentFile].filter(contract => contract)) || []).length <= 0 ? 'No compiled contracts' {((contractList[currentFile] && contractList[currentFile].filter(contract => contract)) || []).length <= 0 ? 'No compiled contracts'
: loadedContractData ? <div> : loadedContractData ? <div>
<ContractGUI <ContractGUI
title='Deploy' title='Deploy'

@ -25,7 +25,6 @@ export const FETCH_CONTRACT_LIST_SUCCESS = 'FETCH_CONTRACT_LIST_SUCCESS'
export const FETCH_CONTRACT_LIST_FAILED = 'FETCH_CONTRACT_LIST_FAILED' export const FETCH_CONTRACT_LIST_FAILED = 'FETCH_CONTRACT_LIST_FAILED'
export const SET_LOAD_TYPE = 'SET_LOAD_TYPE' export const SET_LOAD_TYPE = 'SET_LOAD_TYPE'
export const SET_CURRENT_FILE = 'SET_CURRENT_FILE' export const SET_CURRENT_FILE = 'SET_CURRENT_FILE'
export const SET_COMPILATION_SOURCE = 'SET_COMPILATION_SOURCE'
export const SET_IPFS_CHECKED_STATE = 'SET_IPFS_CHECKED_STATE' export const SET_IPFS_CHECKED_STATE = 'SET_IPFS_CHECKED_STATE'
export const SET_GAS_PRICE_STATUS = 'SET_GAS_PRICE_STATUS' export const SET_GAS_PRICE_STATUS = 'SET_GAS_PRICE_STATUS'
export const SET_CONFIRM_SETTINGS = 'SET_CONFIRM_SETTINGS' export const SET_CONFIRM_SETTINGS = 'SET_CONFIRM_SETTINGS'
@ -46,3 +45,4 @@ export const REMOVE_DEPLOY_OPTION = 'REMOVE_DEPLOY_OPTION'
export const SET_DEPLOY_OPTIONS = 'SET_DEPLOY_OPTIONS' export const SET_DEPLOY_OPTIONS = 'SET_DEPLOY_OPTIONS'
export const SET_CURRENT_CONTRACT = 'SET_CURRENT_CONTRACT' export const SET_CURRENT_CONTRACT = 'SET_CURRENT_CONTRACT'
export const SET_PROXY_ENV_ADDRESS = 'SET_PROXY_ENV_ADDRESS' export const SET_PROXY_ENV_ADDRESS = 'SET_PROXY_ENV_ADDRESS'
export const SET_REMIXD_ACTIVATED = 'SET_REMIXD_ACTIVATED'

@ -1,7 +1,7 @@
import { CompilerAbstract } from '@remix-project/remix-solidity-ts' import { CompilerAbstract } from '@remix-project/remix-solidity-ts'
import { ContractData } from '@remix-project/core-plugin' import { ContractData } from '@remix-project/core-plugin'
import { DeployOptions } from '../types' import { DeployOptions } from '../types'
import { ADD_INSTANCE, ADD_PROVIDER, CLEAR_INSTANCES, CLEAR_RECORDER_COUNT, DISPLAY_NOTIFICATION, DISPLAY_POPUP_MESSAGE, FETCH_ACCOUNTS_LIST_FAILED, FETCH_ACCOUNTS_LIST_REQUEST, FETCH_ACCOUNTS_LIST_SUCCESS, FETCH_CONTRACT_LIST_FAILED, FETCH_CONTRACT_LIST_REQUEST, FETCH_CONTRACT_LIST_SUCCESS, FETCH_PROVIDER_LIST_FAILED, FETCH_PROVIDER_LIST_REQUEST, FETCH_PROVIDER_LIST_SUCCESS, HIDE_NOTIFICATION, HIDE_POPUP_MESSAGE, REMOVE_INSTANCE, REMOVE_PROVIDER, RESET_STATE, SET_BASE_FEE_PER_GAS, SET_CONFIRM_SETTINGS, SET_CURRENT_CONTRACT, SET_CURRENT_FILE, SET_DECODED_RESPONSE, SET_DEPLOY_OPTIONS, SET_EXECUTION_ENVIRONMENT, SET_EXTERNAL_WEB3_ENDPOINT, SET_GAS_LIMIT, SET_GAS_PRICE, SET_GAS_PRICE_STATUS, SET_IPFS_CHECKED_STATE, SET_LOAD_TYPE, SET_MATCH_PASSPHRASE, SET_MAX_FEE, SET_MAX_PRIORITY_FEE, SET_NETWORK_NAME, SET_PASSPHRASE, SET_PATH_TO_SCENARIO, SET_PERSONAL_MODE, SET_RECORDER_COUNT, SET_SELECTED_ACCOUNT, SET_SEND_UNIT, SET_SEND_VALUE, SET_PROXY_ENV_ADDRESS, ADD_DEPLOY_OPTION, REMOVE_DEPLOY_OPTION, SET_COMPILATION_SOURCE } from '../constants' import { ADD_INSTANCE, ADD_PROVIDER, CLEAR_INSTANCES, CLEAR_RECORDER_COUNT, DISPLAY_NOTIFICATION, DISPLAY_POPUP_MESSAGE, FETCH_ACCOUNTS_LIST_FAILED, FETCH_ACCOUNTS_LIST_REQUEST, FETCH_ACCOUNTS_LIST_SUCCESS, FETCH_CONTRACT_LIST_FAILED, FETCH_CONTRACT_LIST_REQUEST, FETCH_CONTRACT_LIST_SUCCESS, FETCH_PROVIDER_LIST_FAILED, FETCH_PROVIDER_LIST_REQUEST, FETCH_PROVIDER_LIST_SUCCESS, HIDE_NOTIFICATION, HIDE_POPUP_MESSAGE, REMOVE_INSTANCE, REMOVE_PROVIDER, RESET_STATE, SET_BASE_FEE_PER_GAS, SET_CONFIRM_SETTINGS, SET_CURRENT_CONTRACT, SET_CURRENT_FILE, SET_DECODED_RESPONSE, SET_DEPLOY_OPTIONS, SET_EXECUTION_ENVIRONMENT, SET_EXTERNAL_WEB3_ENDPOINT, SET_GAS_LIMIT, SET_GAS_PRICE, SET_GAS_PRICE_STATUS, SET_IPFS_CHECKED_STATE, SET_LOAD_TYPE, SET_MATCH_PASSPHRASE, SET_MAX_FEE, SET_MAX_PRIORITY_FEE, SET_NETWORK_NAME, SET_PASSPHRASE, SET_PATH_TO_SCENARIO, SET_PERSONAL_MODE, SET_RECORDER_COUNT, SET_SELECTED_ACCOUNT, SET_SEND_UNIT, SET_SEND_VALUE, SET_PROXY_ENV_ADDRESS, ADD_DEPLOY_OPTION, REMOVE_DEPLOY_OPTION, SET_REMIXD_ACTIVATED } from '../constants'
declare const window: any declare const window: any
interface Action { interface Action {
@ -12,7 +12,8 @@ export interface Contract {
name: string, name: string,
alias: string, alias: string,
file: string, file: string,
compiler: CompilerAbstract compiler: CompilerAbstract,
compilerName: string
} }
export interface ContractList { export interface ContractList {
@ -64,6 +65,7 @@ export interface RunTabState {
alias: string, alias: string,
file: string, file: string,
compiler: CompilerAbstract compiler: CompilerAbstract
compilerName: string
}[] }[]
}, },
deployOptions: { [file: string]: { [name: string]: DeployOptions } }, deployOptions: { [file: string]: { [name: string]: DeployOptions } },
@ -99,6 +101,7 @@ export interface RunTabState {
pathToScenario: string, pathToScenario: string,
transactionCount: number transactionCount: number
} }
remixdActivated: boolean
} }
export const runTabInitialState: RunTabState = { export const runTabInitialState: RunTabState = {
@ -180,7 +183,8 @@ export const runTabInitialState: RunTabState = {
recorder: { recorder: {
pathToScenario: 'scenario.json', pathToScenario: 'scenario.json',
transactionCount: 0 transactionCount: 0
} },
remixdActivated: false
} }
type AddProvider = { type AddProvider = {
@ -518,19 +522,7 @@ export const runTabReducer = (state: RunTabState = runTabInitialState, action: A
} }
} }
} }
case SET_COMPILATION_SOURCE: {
const payload: string = action.payload
return {
...state,
contracts: {
...state.contracts,
compilationSource: payload,
}
}
}
case SET_IPFS_CHECKED_STATE: { case SET_IPFS_CHECKED_STATE: {
const payload: boolean = action.payload const payload: boolean = action.payload
@ -734,6 +726,14 @@ export const runTabReducer = (state: RunTabState = runTabInitialState, action: A
} }
} }
case SET_REMIXD_ACTIVATED: {
const payload: boolean = action.payload
return {
...state,
remixdActivated: payload
}
}
default: default:
return state return state
} }

@ -34,7 +34,7 @@ import { PublishToStorage } from '@remix-ui/publish-to-storage'
import { PassphrasePrompt } from './components/passphrase' import { PassphrasePrompt } from './components/passphrase'
import { MainnetPrompt } from './components/mainnet' import { MainnetPrompt } from './components/mainnet'
import { ScenarioPrompt } from './components/scenario' import { ScenarioPrompt } from './components/scenario'
import { setIpfsCheckedState } from './actions/payload' import { setIpfsCheckedState, setRemixDActivated } from './actions/payload'
export function RunTabUI (props: RunTabProps) { export function RunTabUI (props: RunTabProps) {
const { plugin } = props const { plugin } = props
@ -241,6 +241,7 @@ export function RunTabUI (props: RunTabProps) {
networkName={runTab.networkName} networkName={runTab.networkName}
setNetworkName={setNetworkName} setNetworkName={setNetworkName}
setSelectedContract={updateSelectedContract} setSelectedContract={updateSelectedContract}
remixdActivated={runTab.remixdActivated}
/> />
<RecorderUI <RecorderUI
gasEstimationPrompt={gasEstimationPrompt} gasEstimationPrompt={gasEstimationPrompt}

@ -165,6 +165,7 @@ export interface ContractDropdownProps {
networkName: string, networkName: string,
setNetworkName: (name: string) => void, setNetworkName: (name: string) => void,
setSelectedContract: (contractName: string) => void setSelectedContract: (contractName: string) => void
remixdActivated: boolean
} }
export interface RecorderProps { export interface RecorderProps {

@ -3,7 +3,7 @@ import { PluginClient } from '@remixproject/plugin'
import * as chokidar from 'chokidar' import * as chokidar from 'chokidar'
import * as utils from '../utils' import * as utils from '../utils'
import * as fs from 'fs-extra' import * as fs from 'fs-extra'
import { basename, join } from 'path' import { join } from 'path'
const { spawn } = require('child_process') // eslint-disable-line const { spawn } = require('child_process') // eslint-disable-line
export class HardhatClient extends PluginClient { export class HardhatClient extends PluginClient {
@ -14,12 +14,12 @@ export class HardhatClient extends PluginClient {
warnLog: boolean warnLog: boolean
buildPath: string buildPath: string
constructor (private readOnly = false) { constructor(private readOnly = false) {
super() super()
this.methods = ['compile', 'sync'] this.methods = ['compile', 'sync']
} }
setWebSocket (websocket: WS): void { setWebSocket(websocket: WS): void {
this.websocket = websocket this.websocket = websocket
this.websocket.addEventListener('close', () => { this.websocket.addEventListener('close', () => {
this.warnLog = false this.warnLog = false
@ -27,13 +27,13 @@ export class HardhatClient extends PluginClient {
}) })
} }
sharedFolder (currentSharedFolder: string): void { sharedFolder(currentSharedFolder: string): void {
this.currentSharedFolder = currentSharedFolder this.currentSharedFolder = currentSharedFolder
this.buildPath = utils.absolutePath('artifacts/contracts', this.currentSharedFolder) this.buildPath = utils.absolutePath('artifacts/contracts', this.currentSharedFolder)
this.listenOnHardhatCompilation() this.listenOnHardhatCompilation()
} }
compile (configPath: string) { compile(configPath: string) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (this.readOnly) { if (this.readOnly) {
const errMsg = '[Hardhat Compilation]: Cannot compile in read-only mode' const errMsg = '[Hardhat Compilation]: Cannot compile in read-only mode'
@ -60,9 +60,10 @@ export class HardhatClient extends PluginClient {
}) })
} }
private async processArtifact () { private async processArtifact() {
// resolving the files // resolving the files
const folderFiles = await fs.readdir(this.buildPath) const folderFiles = await fs.readdir(this.buildPath)
const targetsSynced = []
// name of folders are file names // name of folders are file names
for (const file of folderFiles) { // ["artifacts/contracts/Greeter.sol/"] for (const file of folderFiles) { // ["artifacts/contracts/Greeter.sol/"]
const contractFilePath = join(this.buildPath, file) const contractFilePath = join(this.buildPath, file)
@ -87,15 +88,19 @@ export class HardhatClient extends PluginClient {
const jsonStd = JSON.parse(contentStd) const jsonStd = JSON.parse(contentStd)
compilationResult.target = jsonStd.sourceName compilationResult.target = jsonStd.sourceName
// this is the full compilation result targetsSynced.push(compilationResult.target)
console.log('Processing Hardhat artifact for file: ', file)
const path = join(contractFilePath, jsonDbg.buildInfo) const path = join(contractFilePath, jsonDbg.buildInfo)
const content = await fs.readFile(path, { encoding: 'utf-8' }) const content = await fs.readFile(path, { encoding: 'utf-8' })
await this.feedContractArtifactFile(content, compilationResult) await this.feedContractArtifactFile(content, compilationResult)
} }
if (compilationResult.target) { if (compilationResult.target) {
this.emit('compilationFinished', compilationResult.target, { sources: compilationResult.input }, 'soljson', compilationResult.output, compilationResult.solcVersion) // we are only interested in the contracts that are in the target of the compilation
compilationResult.output = {
...compilationResult.output,
contracts: { [compilationResult.target]: compilationResult.output.contracts[compilationResult.target] }
}
this.emit('compilationFinished', compilationResult.target, { sources: compilationResult.input }, 'soljson', compilationResult.output, compilationResult.solcVersion)
} }
} }
} }
@ -104,29 +109,32 @@ export class HardhatClient extends PluginClient {
this.call('terminal', 'log', 'receiving compilation result from Hardhat') this.call('terminal', 'log', 'receiving compilation result from Hardhat')
this.warnLog = true this.warnLog = true
} }
if (targetsSynced.length) {
console.log(`Processing artifacts for files: ${[...new Set(targetsSynced)].join(', ')}`)
// @ts-ignore
this.call('terminal', 'log', `synced with Hardhat: ${[...new Set(targetsSynced)].join(', ')}`)
}
} }
listenOnHardhatCompilation () { listenOnHardhatCompilation() {
try { try {
this.watcher = chokidar.watch(this.buildPath, { depth: 1, ignorePermissionErrors: true, ignoreInitial: true }) this.watcher = chokidar.watch(this.buildPath, { depth: 1, ignorePermissionErrors: true, ignoreInitial: true })
this.watcher.on('change', () => this.processArtifact()) this.watcher.on('change', () => this.processArtifact())
this.watcher.on('add', () => this.processArtifact()) this.watcher.on('add', () => this.processArtifact())
// process the artifact on activation // process the artifact on activation
setTimeout(() => this.processArtifact(), 1000) setTimeout(() => this.processArtifact(), 1000)
} catch (e) { } catch (e) {
console.log(e) console.log(e)
} }
} }
async sync () { async sync() {
console.log('syncing from Hardhat') console.log('syncing from Hardhat')
this.processArtifact() this.processArtifact()
// @ts-ignore
this.call('terminal', 'log', 'synced with Hardhat')
} }
async feedContractArtifactFile (artifactContent, compilationResultPart) { async feedContractArtifactFile(artifactContent, compilationResultPart) {
const contentJSON = JSON.parse(artifactContent) const contentJSON = JSON.parse(artifactContent)
compilationResultPart.solcVersion = contentJSON.solcVersion compilationResultPart.solcVersion = contentJSON.solcVersion
for (const file in contentJSON.input.sources) { for (const file in contentJSON.input.sources) {
@ -139,10 +147,10 @@ export class HardhatClient extends PluginClient {
compilationResultPart.output['sources'][file] = contentJSON.output.sources[file] compilationResultPart.output['sources'][file] = contentJSON.output.sources[file]
compilationResultPart.output['contracts'][file] = contentJSON.output.contracts[file] compilationResultPart.output['contracts'][file] = contentJSON.output.contracts[file]
if (contentJSON.output.errors && contentJSON.output.errors.length) { if (contentJSON.output.errors && contentJSON.output.errors.length) {
compilationResultPart.output['errors'] = contentJSON.output.errors.filter(error => error.sourceLocation.file === file) compilationResultPart.output['errors'] = contentJSON.output.errors.filter(error => error.sourceLocation.file === file)
} }
} }
} }
} }
} }
} }

Loading…
Cancel
Save