Merge branch 'importautocomplete' of https://github.com/ethereum/remix-project into importautocomplete

pull/2917/head
filip mertens 2 years ago
commit 39c5e2798b
  1. 2
      apps/remix-ide-e2e/.eslintrc
  2. 1
      apps/remix-ide-e2e/src/helpers/hardhat_compilation.ts
  3. 113556
      apps/remix-ide-e2e/src/helpers/hardhat_compilation_8a7ab689ec618720f53ce867a3031c03.json
  4. 18
      apps/remix-ide-e2e/src/tests/remixd.test.ts
  5. 3
      apps/remix-ide-e2e/tsconfig.e2e.json
  6. 3
      apps/remix-ide-e2e/tsconfig.json
  7. 0
      apps/remix-ide/contracts/artifacts/build-info/.gitgnore
  8. 1
      apps/remix-ide/contracts/hardhat.config.js
  9. 5
      libs/remix-core-plugin/src/lib/compiler-artefacts.ts
  10. 17
      libs/remix-ui/run-tab/src/lib/actions/events.ts
  11. 10
      libs/remix-ui/run-tab/src/lib/actions/payload.ts
  12. 17
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  13. 1
      libs/remix-ui/run-tab/src/lib/constants/index.ts
  14. 16
      libs/remix-ui/run-tab/src/lib/reducers/runTab.ts
  15. 1
      libs/remix-ui/run-tab/src/lib/types/index.ts
  16. 28
      libs/remixd/src/services/hardhatClient.ts
  17. 12
      libs/remixd/src/services/remixdClient.ts

@ -12,5 +12,5 @@
}
],
"extends": ["../../.eslintrc"],
"ignorePatterns": ["!**/*"]
"ignorePatterns": ["!**/*", "hardhat_compilation.ts"]
}

File diff suppressed because one or more lines are too long

@ -1,6 +1,8 @@
'use strict'
import { NightwatchBrowser } from 'nightwatch'
import { writeFileSync } from 'fs'
import init from '../helpers/init'
import * as hardhatCompilation from '../helpers/hardhat_compilation_8a7ab689ec618720f53ce867a3031c03.json'
const assetsTestContract = `import "./contract.sol";
contract Assets {
@ -112,7 +114,21 @@ module.exports = {
browser
.clickLaunchIcon('pluginManager')
.scrollAndClick('#pluginManager *[data-id="pluginManagerComponentDeactivateButtonremixd"]')
.end()
},
'Should listen on compilation result from hardhat #group5': function (browser: NightwatchBrowser) {
browser.perform((done) => {
console.log('working directory', process.cwd())
writeFileSync('./apps/remix-ide/contracts/artifacts/build-info/c7062fdd360381a85af23eeef31c98f8.json', JSON.stringify(hardhatCompilation))
done()
})
.expect.element('*[data-id="terminalJournal"]').text.to.contain('received compilation result from hardhat').before(60000)
browser.clickLaunchIcon('udapp')
.assert.textContains('*[data-id="udappCompiledBy"]', 'Compiled by hardhat')
.selectContract('Lock')
.createContract('1')
.expect.element('*[data-id="terminalJournal"]').text.to.contain('Unlock time should be in the future').before(60000)
}
}

@ -3,7 +3,8 @@
"compilerOptions": {
"sourceMap": false,
"outDir": "../../dist",
"allowJs": true
"allowJs": true,
"resolveJsonModule": true
},
"include": ["**/*.ts", "**/*.js"]
}

@ -2,7 +2,8 @@
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"types": ["node", "nightwatch"],
"esModuleInterop": true
"esModuleInterop": true,
"resolveJsonModule": true
},
"include": ["**/*.ts", "**/*.js"]
}

@ -0,0 +1 @@
// simulate a hardhat project

@ -58,6 +58,11 @@ export class CompilerArtefacts extends Plugin {
this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source)
saveCompilationPerFileResult(file, source, languageVersion, data)
})
this.on('hardhat', 'compilationFinished', (file, source, languageVersion, data) => {
this.compilersArtefacts.__last = new CompilerAbstract(languageVersion, data, source)
saveCompilationPerFileResult(file, source, languageVersion, data)
})
}
/**

@ -2,7 +2,7 @@ import { envChangeNotification } from "@remix-ui/helper"
import { RunTab } from "../types/run-tab"
import { setExecutionContext, setFinalContext, updateAccountBalances } from "./account"
import { addExternalProvider, addInstance, removeExternalProvider, setNetworkNameFromProvider } from "./actions"
import { addDeployOption, clearAllInstances, clearRecorderCount, fetchContractListSuccess, resetUdapp, setCurrentContract, setCurrentFile, setLoadType, setProxyEnvAddress, setRecorderCount, setSendValue } from "./payload"
import { addDeployOption, clearAllInstances, clearRecorderCount, fetchContractListSuccess, resetUdapp, setCompilationSource, setCurrentContract, setCurrentFile, setLoadType, setProxyEnvAddress, setRecorderCount, setSendValue } from "./payload"
import { CompilerAbstract } from '@remix-project/remix-solidity'
import * as ethJSUtil from 'ethereumjs-util'
import Web3 from 'web3'
@ -41,15 +41,17 @@ export const setupEvents = (plugin: RunTab, dispatch: React.Dispatch<any>) => {
plugin.blockchain.event.register('removeProvider', name => removeExternalProvider(dispatch, name))
plugin.on('solidity', 'compilationFinished', (file, source, languageVersion, data, input, version) => broadcastCompilationResult(plugin, dispatch, file, source, languageVersion, data, input))
plugin.on('solidity', 'compilationFinished', (file, source, languageVersion, data, input, version) => broadcastCompilationResult('remix', plugin, dispatch, file, source, languageVersion, data, input))
plugin.on('vyper', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(plugin, dispatch, file, source, languageVersion, data))
plugin.on('vyper', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult('vyper', plugin, dispatch, file, source, languageVersion, data))
plugin.on('lexon', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(plugin, dispatch, file, source, languageVersion, data))
plugin.on('lexon', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult('lexon', plugin, dispatch, file, source, languageVersion, data))
plugin.on('yulp', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(plugin, dispatch, file, source, languageVersion, data))
plugin.on('yulp', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult('yulp', plugin, dispatch, file, source, languageVersion, data))
plugin.on('nahmii-compiler', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(plugin, dispatch, file, source, languageVersion, data))
plugin.on('nahmii-compiler', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult('nahmii', plugin, dispatch, file, source, languageVersion, data))
plugin.on('hardhat', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult('hardhat', plugin, dispatch, file, source, languageVersion, data))
plugin.on('udapp', 'setEnvironmentModeReducer', (env: { context: string, fork: string }, from: string) => {
plugin.call('notification', 'toast', envChangeNotification(env, from))
@ -92,7 +94,7 @@ export const setupEvents = (plugin: RunTab, dispatch: React.Dispatch<any>) => {
})
}
const broadcastCompilationResult = async (plugin: RunTab, dispatch: React.Dispatch<any>, file, source, languageVersion, data, input?) => {
const broadcastCompilationResult = async (compilerName: string, plugin: RunTab, dispatch: React.Dispatch<any>, file, source, languageVersion, data, input?) => {
// TODO check whether the tab is configured
const compiler = new CompilerAbstract(languageVersion, data, source, input)
plugin.compilersArtefacts[languageVersion] = compiler
@ -119,6 +121,7 @@ const broadcastCompilationResult = async (plugin: RunTab, dispatch: React.Dispat
}
dispatch(fetchContractListSuccess({ [file]: contracts }))
dispatch(setCurrentFile(file))
dispatch(setCompilationSource(compilerName))
// TODO: set current contract
}

@ -1,6 +1,6 @@
import { ContractList } from '../reducers/runTab'
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_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_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 { DeployMode, DeployOptions } from '../types'
export const fetchAccountsListRequest = () => {
@ -167,6 +167,14 @@ export const setCurrentFile = (file: string) => {
payload: file
}
}
export const setCompilationSource = (source: string) => {
return {
type: SET_COMPILATION_SOURCE,
payload: source
}
}
export const setIpfsCheckedState = (state: boolean) => {
return {
type: SET_IPFS_CHECKED_STATE,

@ -23,13 +23,16 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
title: 'Please compile *.sol file to deploy or access a contract',
disabled: true
})
const [compFails, setCompFails] = useState<'none' | 'block'>('none')
const [loadedContractData, setLoadedContractData] = useState<ContractData>(null)
const [constructorInterface, setConstructorInterface] = useState<FuncABI>(null)
const [constructorInputs, setConstructorInputs] = useState(null)
const contractsRef = useRef<HTMLSelectElement>(null)
const atAddressValue = useRef<HTMLInputElement>(null)
const { contractList, loadType, currentFile, currentContract, compilationCount, deployOptions, proxyKey } = props.contracts
const { contractList, loadType, currentFile, compilationSource, currentContract, compilationCount, deployOptions, proxyKey } = props.contracts
useEffect(() => {
enableContractNames(Object.keys(props.contracts.contractList).length > 0)
}, [Object.keys(props.contracts.contractList).length])
useEffect(() => {
enableAtAddress(false)
@ -72,13 +75,6 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
})
if (!currentContract) enableAtAddress(false)
}
if (currentFile) {
enableContractNames(true)
setCompFails('none')
} else {
enableContractNames(false)
setCompFails('block')
}
initSelectedContract()
}, [loadType, currentFile, compilationCount])
@ -231,7 +227,10 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
return (
<div className="udapp_container" data-id="contractDropdownContainer">
<div className='d-flex justify-content-between'>
<label className="udapp_settingsLabel">Contract</label>
{ Object.keys(props.contracts.contractList).length > 0 && compilationSource !== '' && <label data-id="udappCompiledBy">Compiled by {compilationSource} </label> }
</div>
<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' }}>
{ (contractList[currentFile] || []).map((contract, index) => {

@ -25,6 +25,7 @@ export const FETCH_CONTRACT_LIST_SUCCESS = 'FETCH_CONTRACT_LIST_SUCCESS'
export const FETCH_CONTRACT_LIST_FAILED = 'FETCH_CONTRACT_LIST_FAILED'
export const SET_LOAD_TYPE = 'SET_LOAD_TYPE'
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_GAS_PRICE_STATUS = 'SET_GAS_PRICE_STATUS'
export const SET_CONFIRM_SETTINGS = 'SET_CONFIRM_SETTINGS'

@ -1,7 +1,7 @@
import { CompilerAbstract } from '@remix-project/remix-solidity-ts'
import { ContractData } from '@remix-project/core-plugin'
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 } 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_COMPILATION_SOURCE } from '../constants'
declare const window: any
interface Action {
@ -70,6 +70,7 @@ export interface RunTabState {
proxyKey: string,
loadType: 'abi' | 'sol' | 'other'
currentFile: string,
compilationSource: string,
currentContract: string,
compilationCount: number,
isRequesting: boolean,
@ -156,6 +157,7 @@ export const runTabInitialState: RunTabState = {
contractList: {},
deployOptions: {} as any,
proxyKey: '',
compilationSource: '',
loadType: 'other',
currentFile: '',
currentContract: '',
@ -517,6 +519,18 @@ 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: {
const payload: boolean = action.payload

@ -132,6 +132,7 @@ export interface ContractDropdownProps {
proxyKey: string,
loadType: 'abi' | 'sol' | 'other',
currentFile: string,
compilationSource: string
currentContract: string,
compilationCount: number,
isRequesting: boolean,

@ -1,11 +1,15 @@
import * as WS from 'ws' // eslint-disable-line
import { PluginClient } from '@remixproject/plugin'
import * as chokidar from 'chokidar'
import * as utils from '../utils'
import * as fs from 'fs-extra'
const { spawn } = require('child_process') // eslint-disable-line
export class HardhatClient extends PluginClient {
methods: Array<string>
websocket: WS
currentSharedFolder: string
watcher: chokidar.FSWatcher
constructor (private readOnly = false) {
super()
@ -14,10 +18,14 @@ export class HardhatClient extends PluginClient {
setWebSocket (websocket: WS): void {
this.websocket = websocket
this.websocket.addEventListener('close', () => {
if (this.watcher) this.watcher.close()
})
}
sharedFolder (currentSharedFolder: string): void {
this.currentSharedFolder = currentSharedFolder
this.listenOnHardhatCompilation()
}
compile (configPath: string) {
@ -46,4 +54,24 @@ export class HardhatClient extends PluginClient {
})
})
}
listenOnHardhatCompilation () {
try {
const buildPath = utils.absolutePath('artifacts/build-info', this.currentSharedFolder)
this.watcher = chokidar.watch(buildPath, { depth: 0, ignorePermissionErrors: true })
const processArtifact = async (path: string) => {
const content = await fs.readFile(path, { encoding: 'utf-8' })
const compilationResult = JSON.parse(content)
// @ts-ignore
this.call('terminal', 'log', 'received compilation result from hardhat')
this.emit('compilationFinished', '', compilationResult.input, 'soljson', compilationResult.output, compilationResult.solcVersion)
}
this.watcher.on('change', (path: string) => processArtifact(path))
this.watcher.on('add', (path: string) => processArtifact(path))
} catch (e) {
console.log(e)
}
}
}

@ -11,6 +11,7 @@ export class RemixdClient extends PluginClient {
trackDownStreamUpdate: TrackDownStreamUpdate = {}
websocket: WS
currentSharedFolder: string
watcher: chokidar.FSWatcher
constructor (private readOnly = false) {
super()
@ -19,6 +20,9 @@ export class RemixdClient extends PluginClient {
setWebSocket (websocket: WS): void {
this.websocket = websocket
this.websocket.addEventListener('close', () => {
if (this.watcher) this.watcher.close()
})
}
sharedFolder (currentSharedFolder: string): void {
@ -246,7 +250,7 @@ export class RemixdClient extends PluginClient {
const absPath = utils.absolutePath('./', path)
if (!isRealPath(absPath)) return
const watcher = chokidar.watch(path, { depth: 0, ignorePermissionErrors: true })
this.watcher = chokidar.watch(path, { depth: 0, ignorePermissionErrors: true })
console.log('setup notifications for ' + path)
/* we can't listen on created file / folder
watcher.on('add', (f, stat) => {
@ -260,7 +264,7 @@ export class RemixdClient extends PluginClient {
this.emit('created', { path: utils.relativePath(f, this.currentSharedFolder), isReadOnly: false, isFolder: true })
})
*/
watcher.on('change', async (f: string) => {
this.watcher.on('change', async (f: string) => {
if (this.trackDownStreamUpdate[f]) {
delete this.trackDownStreamUpdate[f]
return
@ -269,12 +273,12 @@ export class RemixdClient extends PluginClient {
this.emit('changed', utils.relativePath(f, this.currentSharedFolder))
}
})
watcher.on('unlink', async (f: string) => {
this.watcher.on('unlink', async (f: string) => {
if (this.isLoaded) {
this.emit('removed', utils.relativePath(f, this.currentSharedFolder), false)
}
})
watcher.on('unlinkDir', async (f: string) => {
this.watcher.on('unlinkDir', async (f: string) => {
if (this.isLoaded) {
this.emit('removed', utils.relativePath(f, this.currentSharedFolder), true)
}

Loading…
Cancel
Save