Merge branch 'master' into modal-check

pull/5370/head
bunsenstraat 2 years ago committed by GitHub
commit fca9b025f6
  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. 20
      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. 43
      apps/remix-ide/src/app/editor/editor.js
  10. 3
      apps/remix-ide/src/app/tabs/compile-tab.js
  11. 2
      apps/remix-ide/src/app/tabs/web3-provider.js
  12. 2
      apps/remix-ide/src/blockchain/execution-context.js
  13. 3
      apps/remix-ide/src/blockchain/helper.ts
  14. 3
      apps/remix-ide/src/remixAppManager.js
  15. 5
      libs/remix-core-plugin/src/lib/compiler-artefacts.ts
  16. 3
      libs/remix-debug/src/init.ts
  17. 1
      libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx
  18. 1
      libs/remix-ui/panel/src/lib/dragbar/dragbar.tsx
  19. 17
      libs/remix-ui/run-tab/src/lib/actions/events.ts
  20. 10
      libs/remix-ui/run-tab/src/lib/actions/payload.ts
  21. 21
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  22. 1
      libs/remix-ui/run-tab/src/lib/constants/index.ts
  23. 16
      libs/remix-ui/run-tab/src/lib/reducers/runTab.ts
  24. 1
      libs/remix-ui/run-tab/src/lib/types/index.ts
  25. 10
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  26. 55
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx
  27. 28
      libs/remixd/src/services/hardhatClient.ts
  28. 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,8 +114,22 @@ 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)
}
}
function startRemixd (browser: NightwatchBrowser) {

@ -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

@ -5,6 +5,7 @@ import { EditorUI } from '@remix-ui/editor' // eslint-disable-line
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../../package.json'
import { PluginViewWrapper } from '@remix-ui/helper'
import { exists } from 'fs'
const EventManager = require('../../lib/events')
@ -74,30 +75,15 @@ class Editor extends Plugin {
updateComponent(state) {
return <EditorUI
editorAPI={state.api}
themeType={state.currentThemeType}
currentFile={state.currentFile}
events={state.events}
plugin={state.plugin}
/>
editorAPI={state.api}
themeType={state.currentThemeType}
currentFile={state.currentFile}
events={state.events}
plugin={state.plugin}
/>
}
render () {
/* if (this.el) return this.el
this.el = document.createElement('div')
this.el.setAttribute('id', 'editorView')
this.el.currentContent = () => this.currentContent() // used by e2e test
this.el.setCurrentContent = (value) => {
if (this.sessions[this.currentFile]) {
this.sessions[this.currentFile].setValue(value)
this._onChange(this.currentFile)
}
}
this.el.gotoLine = (line, column) => this.gotoLine(line, column || 0)
this.el.getCursorPosition = () => this.getCursorPosition() */
return <div ref={(element)=>{
this.ref = element
this.ref.currentContent = () => this.currentContent() // used by e2e test
@ -113,7 +99,7 @@ class Editor extends Plugin {
this.ref.clearDecorationsByPlugin = (filePath, plugin, typeOfDecoration) => this.clearDecorationsByPlugin(filePath, plugin, typeOfDecoration)
this.ref.keepDecorationsFor = (name, typeOfDecoration) => this.keepDecorationsFor(name, typeOfDecoration)
}} id='editorView'>
<PluginViewWrapper plugin={this} />
<PluginViewWrapper plugin={this} />
</div>
}
@ -229,15 +215,20 @@ class Editor extends Plugin {
try {
// we can't use the fileManager plugin call directly
// because it's itself called in a plugin context, and that causes a timeout in the plugin stack
const contentDep = await readFile(pathDep)
if (contentDep !== null) {
this.emit('addModel', contentDep, 'typescript', pathDep, false)
const pathExists = await exists(pathDep)
let contentDep = ''
if (pathExists) {
contentDep = await readFile(pathDep)
if (contentDep !== '') {
this.emit('addModel', contentDep, 'typescript', pathDep, false)
}
} else {
console.log("The file ", pathDep, " can't be found.")
}
} catch (e) {
console.log(e)
}
}
}
}

@ -8,6 +8,7 @@ import { QueryParams } from '@remix-project/remix-lib'
// import { ICompilerApi } from '@remix-project/remix-lib-ts'
import * as packageJson from '../../../../../package.json'
import { compilerConfigChangedToastMsg, compileToastMsg } from '@remix-ui/helper'
import { isNative } from '../../remixAppManager'
const profile = {
name: 'solidity',
@ -104,7 +105,7 @@ class CompileTab extends CompilerApiMixin(ViewPlugin) { // implements ICompilerA
}
compile (fileName) {
this.call('notification', 'toast', compileToastMsg(this.currentRequest.from, fileName))
if (!isNative(this.currentRequest.from)) this.call('notification', 'toast', compileToastMsg(this.currentRequest.from, fileName))
super.compile(fileName)
}

@ -30,7 +30,7 @@ export class Web3ProviderModule extends Plugin {
// see https://github.com/ethereum/web3.js/pull/1018/files#diff-d25786686c1053b786cc2626dc6e048675050593c0ebaafbf0814e1996f22022R129
provider[provider.sendAsync ? 'sendAsync' : 'send'](payload, async (error, message) => {
if (error) {
const errorData = error.data ? error.data : error.message
const errorData = error.data ? error.data : error.message ? error.message : error
// See: https://github.com/ethers-io/ethers.js/issues/901
if (!(typeof errorData === 'string' && errorData.includes("unknown method eth_chainId"))) this.call('terminal', 'log', error.data ? error.data : error.message)
return reject(errorData)

@ -93,6 +93,7 @@ export class ExecutionContext {
else if (id === 4) name = 'Rinkeby'
else if (id === 5) name = 'Goerli'
else if (id === 42) name = 'Kovan'
else if (id === 11155111) name = 'Sepolia'
else name = 'Custom'
if (id === '1') {
@ -250,6 +251,7 @@ export class ExecutionContext {
Main: 'https://www.etherscan.io/tx/',
Rinkeby: 'https://rinkeby.etherscan.io/tx/',
Ropsten: 'https://ropsten.etherscan.io/tx/',
Sepolia: 'https://sepolia.etherscan.io/tx/',
Kovan: 'https://kovan.etherscan.io/tx/',
Goerli: 'https://goerli.etherscan.io/tx/'
}

@ -3,7 +3,8 @@ const transactionDetailsLinks = {
Rinkeby: 'https://rinkeby.etherscan.io/tx/',
Ropsten: 'https://ropsten.etherscan.io/tx/',
Kovan: 'https://kovan.etherscan.io/tx/',
Goerli: 'https://goerli.etherscan.io/tx/'
Goerli: 'https://goerli.etherscan.io/tx/',
Sepolia: 'https://sepolia.etherscan.io/tx/'
}
export function etherScanLink (network: string, hash: string): string {

@ -25,7 +25,8 @@ const sensitiveCalls = {
export function isNative(name) {
// nativePlugin allows to bypass the permission request
const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'solidity-logic', 'solidityStaticAnalysis', 'solidityUnitTesting',
'layout', 'notification', 'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected-optimism-provider', 'injected-arbitrum-one-provider']
'layout', 'notification', 'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected-optimism-provider',
'tabs', 'injected-arbitrum-one-provider']
return nativePlugins.includes(name) || requiredModules.includes(name)
}

@ -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)
})
}
/**

@ -22,7 +22,8 @@ export function web3DebugNode (network) {
Main: 'https://rpc.archivenode.io/e50zmkroshle2e2e50zm0044i7ao04ym',
Rinkeby: 'https://remix-rinkeby.ethdevops.io',
Ropsten: 'https://remix-ropsten.ethdevops.io',
Goerli: 'https://remix-goerli.ethdevops.io'
Goerli: 'https://remix-goerli.ethdevops.io',
Sepolia: 'https://remix-sepolia.ethdevops.io'
}
if (web3DebugNodes[network]) {
return loadWeb3(web3DebugNodes[network])

@ -45,6 +45,7 @@ const DragBar = (props: IRemixDragBarUi) => {
}, [props.resetTrigger])
const handleResize = () => {
if (!props.refObject.current) return
setOffSet(props.refObject.current.offsetLeft)
setDragBarPosX(props.refObject.current.offsetLeft + props.refObject.current.offsetWidth)
}

@ -23,6 +23,7 @@ const DragBar = (props: IRemixDragBarUi) => {
props.setHideStatus(false)
}
const handleResize = () => {
if (!props.refObject.current) return
setDragBarPosY(window.innerHeight - props.refObject.current.offsetHeight)
}

@ -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,9 +227,12 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
return (
<div className="udapp_container" data-id="contractDropdownContainer">
<label className="udapp_settingsLabel">Contract</label>
<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' }}>
<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) => {
return <option key={index} value={contract.alias}>{contract.alias} - {contract.file}</option>
}) }

@ -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,

@ -863,7 +863,13 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
</OverlayTrigger>
</button>
<div className='d-flex align-items-center'>
<button id="compileAndRunBtn" data-id="compilerContainerCompileAndRunBtn" className="btn btn-secondary btn-block d-block w-100 text-break remixui_solidityCompileAndRunButton d-inline-block remixui_disabled mb-1 mt-3" onClick={compileAndRun} disabled={(configFilePath === '' && state.useFileConfiguration) || disableCompileButton}>
<button
id="compileAndRunBtn"
data-id="compilerContainerCompileAndRunBtn"
className="btn btn-secondary btn-block d-block w-100 text-break remixui_solidityCompileAndRunButton d-inline-block remixui_disabled mb-1 mt-3"
onClick={compileAndRun}
disabled={(configFilePath === '' && state.useFileConfiguration) || disableCompileButton}
>
<OverlayTrigger overlay={
<Tooltip id="overlay-tooltip-compile-run">
<div className="text-left">
@ -875,7 +881,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<span>
Compile and Run script
</span>
</OverlayTrigger>
</OverlayTrigger>
</button>
<OverlayTrigger overlay={
<Tooltip id="overlay-tooltip-compile-run-doc">

@ -1,8 +1,8 @@
import { fileDecoration, FileDecorationIcons } from '@remix-ui/file-decorators'
import { Plugin } from '@remixproject/engine'
import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line
import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'
import './remix-ui-tabs.css'
@ -17,26 +17,25 @@ export interface TabsUIProps {
onReady: (api: any) => void
themeQuality: string
}
export interface TabsUIApi {
activateTab: (namee: string) => void
activateTab: (name: string) => void
active: () => string
}
interface ITabsState {
selectedIndex: number,
fileDecorations: fileDecoration[],
currentExt: string
}
interface ITabsAction {
type: string,
payload: any,
ext?: string,
}
const initialTabsState: ITabsState = {
selectedIndex: -1,
fileDecorations: [],
currentExt: ''
}
const tabsReducer = (state: ITabsState, action: ITabsAction) => {
@ -44,6 +43,7 @@ const tabsReducer = (state: ITabsState, action: ITabsAction) => {
case 'SELECT_INDEX':
return {
...state,
currentExt: action.ext,
selectedIndex: action.payload,
}
case 'SET_FILE_DECORATIONS':
@ -71,8 +71,6 @@ export const TabsUI = (props: TabsUIProps) => {
}
}, [tabsState.selectedIndex])
const getFileDecorationClasses = (tab: any) => {
const fileDecoration = tabsState.fileDecorations.find((fileDecoration: fileDecoration) => {
if(`${fileDecoration.workspace.name}/${fileDecoration.path}` === tab.name) return true
@ -84,8 +82,7 @@ export const TabsUI = (props: TabsUIProps) => {
return <FileDecorationIcons file={{path: tab.name}} fileDecorations={tabsState.fileDecorations} />
}
const renderTab = (tab, index) => {
const renderTab = (tab, index) => {
const classNameImg = 'my-1 mr-1 text-dark ' + tab.iconClass
const classNameTab = 'nav-item nav-link d-flex justify-content-center align-items-center px-2 py-1 tab' + (index === currentIndexRef.current ? ' active' : '')
const invert = props.themeQuality === 'dark' ? 'invert(1)' : 'invert(0)'
@ -106,10 +103,11 @@ export const TabsUI = (props: TabsUIProps) => {
if (currentIndexRef.current < 0) return ''
return tabs.current[currentIndexRef.current].name
}
const activateTab = (name: string) => {
const index = tabs.current.findIndex((tab) => tab.name === name)
currentIndexRef.current = index
dispatch({ type: 'SELECT_INDEX', payload: index })
dispatch({ type: 'SELECT_INDEX', payload: index, ext: getExt(name)})
}
const setFileDecorations = (fileStates: fileDecoration[]) => {
@ -135,10 +133,41 @@ export const TabsUI = (props: TabsUIProps) => {
return () => { tabsElement.current.removeEventListener('wheel', transformScroll) }
}, [])
const getExt = (path) => {
const root = path.split('#')[0].split('?')[0]
const ext = root.indexOf('.') !== -1 ? /[^.]+$/.exec(root) : null
if (ext) return ext[0].toLowerCase()
else return ''
}
return (
<div className="remix-ui-tabs d-flex justify-content-between border-0 header nav-tabs" data-id="tabs-component">
<div className="d-flex flex-row" style={{ maxWidth: 'fit-content', width: '97%' }}>
<div className="d-flex flex-row justify-content-center align-items-center m-1 mt-2">
<div className="d-flex flex-row justify-content-center align-items-center m-1 mt-1">
<button
className="btn text-success py-0"
disabled={!(tabsState.currentExt === 'js' || tabsState.currentExt === 'ts' || tabsState.currentExt === 'sol')}
onClick={async () => {
const path = active().substr(active().indexOf('/') + 1, active().length)
const content = await props.plugin.call('fileManager', "readFile", path)
if (tabsState.currentExt === 'js' || tabsState.currentExt === 'ts') {
await props.plugin.call('scriptRunner', 'execute', content)
} else if (tabsState.currentExt === 'sol' || tabsState.currentExt === 'yul') {
await props.plugin.call('solidity', 'compile', path)
}
}}
>
<OverlayTrigger placement="bottom" overlay={
<Tooltip id="overlay-tooltip-run-script">
<span>
{(tabsState.currentExt === 'js' || tabsState.currentExt === 'ts') ? "Run script (CTRL + SHIFT + S)" :
tabsState.currentExt === 'sol' || tabsState.currentExt === 'yul'? "Compile CTRL + S" : "Select .sol or .yul file to compile or a .ts or .js file and run it"}
</span>
</Tooltip>
}>
<i className="fad fa-play"></i>
</OverlayTrigger>
</button>
<span data-id="tabProxyZoomOut" className="btn btn-sm px-2 fas fa-search-minus text-dark" title="Zoom out" onClick={() => props.onZoomOut()}></span>
<span data-id="tabProxyZoomIn" className="btn btn-sm px-2 fas fa-search-plus text-dark" title="Zoom in" onClick={() => props.onZoomIn()}></span>
</div>
@ -153,7 +182,7 @@ export const TabsUI = (props: TabsUIProps) => {
onSelect={(index) => {
props.onSelect(index)
currentIndexRef.current = index
dispatch({ type: 'SELECT_INDEX', payload: index })
dispatch({ type: 'SELECT_INDEX', payload: index, ext: getExt(props.tabs[currentIndexRef.current].name)})
}}
>
<TabList className="d-flex flex-row align-items-center">

@ -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