Merge pull request #3707 from ethereum/script

Add 'web3 scripts' (to FE context menu)
pull/3744/head
yann300 2 years ago committed by GitHub
commit 4b8cec5791
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      apps/remix-ide-e2e/src/tests/erc721.test.ts
  2. 3
      apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts
  3. 30
      apps/remix-ide-e2e/src/tests/workspace.test.ts
  4. 6
      apps/remix-ide-e2e/src/tests/workspace_git.test.ts
  5. 6
      apps/remix-ide/src/app/tabs/locales/en/filePanel.json
  6. 7
      libs/remix-ui/workspace/src/lib/actions/workspace.ts
  7. 66
      libs/remix-ui/workspace/src/lib/components/workspace-hamburger-item.tsx
  8. 43
      libs/remix-ui/workspace/src/lib/components/workspace-hamburger.tsx
  9. 1
      libs/remix-ui/workspace/src/lib/contexts/index.ts
  10. 9
      libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx
  11. 100
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  12. 29
      libs/remix-ui/workspace/src/lib/scripts/contract-deployer/basic-contract-deploy.ts
  13. 258
      libs/remix-ui/workspace/src/lib/scripts/contract-deployer/create2-factory-deploy.ts
  14. 11
      libs/remix-ui/workspace/src/lib/scripts/contract-deployer/index.ts
  15. 11
      libs/remix-ui/workspace/src/lib/scripts/etherscan/index.ts
  16. 8
      libs/remix-ui/workspace/src/lib/scripts/etherscan/receiptGuidScript.ts
  17. 13
      libs/remix-ui/workspace/src/lib/scripts/etherscan/verifyScript.ts
  18. 7
      libs/remix-ui/workspace/src/lib/scripts/index.ts

@ -14,7 +14,8 @@ module.exports = {
}, },
'Deploy SampleERC721 whose bytecode is very similar to ERC721': function (browser: NightwatchBrowser) { 'Deploy SampleERC721 whose bytecode is very similar to ERC721': function (browser: NightwatchBrowser) {
browser.clickLaunchIcon('filePanel') browser.clickLaunchIcon('filePanel')
.click('*[data-id="workspaceCreate"]') .click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacecreate"]')
// create contract // create contract
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')

@ -178,7 +178,8 @@ module.exports = {
.click('*[data-id="testTabGenerateTestFolder"]') .click('*[data-id="testTabGenerateTestFolder"]')
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
// creating a new workspace // creating a new workspace
.click('*[data-id="workspaceCreate"]') .click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacecreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.click('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]') .click('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_new') .setValue('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_new')

@ -36,7 +36,8 @@ module.exports = {
'Should create Remix default workspace with files #group1': function (browser: NightwatchBrowser) { 'Should create Remix default workspace with files #group1': function (browser: NightwatchBrowser) {
browser browser
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.click('*[data-id="workspaceCreate"]') .click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacecreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')
// eslint-disable-next-line dot-notation // eslint-disable-next-line dot-notation
@ -107,7 +108,8 @@ module.exports = {
'Should create blank workspace with no files #group1': function (browser: NightwatchBrowser) { 'Should create blank workspace with no files #group1': function (browser: NightwatchBrowser) {
browser browser
.click('*[data-id="workspaceCreate"]') .click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacecreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')
// eslint-disable-next-line dot-notation // eslint-disable-next-line dot-notation
@ -129,7 +131,8 @@ module.exports = {
'Should create ERC20 workspace with files #group1': function (browser: NightwatchBrowser) { 'Should create ERC20 workspace with files #group1': function (browser: NightwatchBrowser) {
browser browser
.click('*[data-id="workspaceCreate"]') .click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacecreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')
// eslint-disable-next-line dot-notation // eslint-disable-next-line dot-notation
@ -189,7 +192,8 @@ module.exports = {
'Should create ERC721 workspace with files #group1': function (browser: NightwatchBrowser) { 'Should create ERC721 workspace with files #group1': function (browser: NightwatchBrowser) {
browser browser
.click('*[data-id="workspaceCreate"]') .click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacecreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')
// eslint-disable-next-line dot-notation // eslint-disable-next-line dot-notation
@ -249,7 +253,8 @@ module.exports = {
'Should create ERC1155 workspace with files #group1': function (browser: NightwatchBrowser) { 'Should create ERC1155 workspace with files #group1': function (browser: NightwatchBrowser) {
browser browser
.click('*[data-id="workspaceCreate"]') .click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacecreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')
// eslint-disable-next-line dot-notation // eslint-disable-next-line dot-notation
@ -309,7 +314,8 @@ module.exports = {
'Should create ERC1155 workspace with template customizations #group1': function (browser: NightwatchBrowser) { 'Should create ERC1155 workspace with template customizations #group1': function (browser: NightwatchBrowser) {
browser browser
.click('*[data-id="workspaceCreate"]') .click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacecreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')
.click('select[id="wstemplate"]') .click('select[id="wstemplate"]')
@ -381,7 +387,8 @@ module.exports = {
'Should create two workspace and switch to the first one #group1': function (browser: NightwatchBrowser) { 'Should create two workspace and switch to the first one #group1': function (browser: NightwatchBrowser) {
browser browser
.click('*[data-id="workspaceCreate"]') .click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacecreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.click('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]') .click('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_name') .setValue('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_name')
@ -394,7 +401,8 @@ module.exports = {
selector: "//div[contains(@class, 'view-line') and contains(.//span, 'test')]", selector: "//div[contains(@class, 'view-line') and contains(.//span, 'test')]",
locateStrategy: 'xpath' locateStrategy: 'xpath'
}) })
.click('*[data-id="workspaceCreate"]') .click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacecreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.click('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]') .click('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_name_1') .setValue('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_name_1')
@ -449,7 +457,8 @@ module.exports = {
'Should create workspace for test #group2': function (browser: NightwatchBrowser) { 'Should create workspace for test #group2': function (browser: NightwatchBrowser) {
browser browser
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.click('*[data-id="workspaceCreate"]') .click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacecreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')
.click('select[id="wstemplate"]') .click('select[id="wstemplate"]')
@ -475,7 +484,8 @@ module.exports = {
'Should create workspace for next test #group2': function (browser: NightwatchBrowser) { 'Should create workspace for next test #group2': function (browser: NightwatchBrowser) {
browser browser
.click('*[data-id="workspaceCreate"]') .click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacecreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')
// eslint-disable-next-line dot-notation // eslint-disable-next-line dot-notation

@ -11,7 +11,8 @@ module.exports = {
'Should not be able to create GIT without credentials #group1': function (browser: NightwatchBrowser) { 'Should not be able to create GIT without credentials #group1': function (browser: NightwatchBrowser) {
browser browser
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.click('*[data-id="workspaceCreate"]') .click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacecreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')
.waitForElementVisible({ .waitForElementVisible({
@ -43,7 +44,8 @@ module.exports = {
browser browser
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.waitForElementNotVisible('[data-id="workspaceGitPanel"]') .waitForElementNotVisible('[data-id="workspaceGitPanel"]')
.click('*[data-id="workspaceCreate"]') .click('*[data-id="workspacesMenuDropdown"]')
.click('*[data-id="workspacecreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')
// eslint-disable-next-line dot-notation // eslint-disable-next-line dot-notation

@ -27,8 +27,14 @@
"filePanel.solghaction": "Solidity Test Workflow", "filePanel.solghaction": "Solidity Test Workflow",
"filePanel.workspace.tssoltestghaction": "Adds a preset yml file to run mocha and chai tests for solidity on github actions CI", "filePanel.workspace.tssoltestghaction": "Adds a preset yml file to run mocha and chai tests for solidity on github actions CI",
"filePanel.tssoltestghaction": "Mocha Chai Test Workflow", "filePanel.tssoltestghaction": "Mocha Chai Test Workflow",
"filePanel.workspace.addscriptetherscan": "Adds a scripts which can be used to interact with the etherscan API.",
"filePanel.addscriptetherscan": "Add Etherscan script",
"filePanel.workspace.addscriptdeployer": "Adds a scripts which can be used to deploy contracts.",
"filePanel.addscriptdeployer": "Add Contract deployer script",
"filePanel.workspace.slitherghaction": "Adds a preset yml file to run slither analysis on github actions CI", "filePanel.workspace.slitherghaction": "Adds a preset yml file to run slither analysis on github actions CI",
"filePanel.slitherghaction": "Slither Workflow", "filePanel.slitherghaction": "Slither Workflow",
"filePanel.workspace.helperscripts": "Adds convenient scripts to the 'scripts' directory",
"filePanel.helperscripts": "Web3 Scripts",
"filePanel.newFile": "New File", "filePanel.newFile": "New File",
"filePanel.newFolder": "New Folder", "filePanel.newFolder": "New Folder",
"filePanel.rename": "Rename", "filePanel.rename": "Rename",

@ -13,6 +13,7 @@ import { ROOT_PATH, slitherYml, solTestYml, tsSolTestYml } from '../utils/consta
import { IndexedDBStorage } from '../../../../../../apps/remix-ide/src/app/files/filesystems/indexedDB' import { IndexedDBStorage } from '../../../../../../apps/remix-ide/src/app/files/filesystems/indexedDB'
import { getUncommittedFiles } from '../utils/gitStatusFilter' import { getUncommittedFiles } from '../utils/gitStatusFilter'
import { AppModal, ModalTypes } from '@remix-ui/app' import { AppModal, ModalTypes } from '@remix-ui/app'
import { scripts } from '../scripts'
declare global { declare global {
interface Window { remixFileSystemCallback: IndexedDBStorage; } interface Window { remixFileSystemCallback: IndexedDBStorage; }
@ -677,6 +678,12 @@ export const createSlitherGithubAction = async () => {
plugin.call('fileManager', 'open', path) plugin.call('fileManager', 'open', path)
} }
export const createHelperScripts = async (script: string) => {
if (!scripts[script]) return
await scripts[script](plugin)
plugin.call('notification', 'toast', 'scripts added in the "scripts" folder')
}
export const checkoutRemoteBranch = async (branch: string, remote: string) => { export const checkoutRemoteBranch = async (branch: string, remote: string) => {
const localChanges = await hasLocalChanges() const localChanges = await hasLocalChanges()

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import { CustomTooltip } from '@remix-ui/helper' import { CustomTooltip, CustomMenu, CustomIconsToggle } from '@remix-ui/helper'
import { Dropdown } from 'react-bootstrap' import { Dropdown, NavDropdown} from 'react-bootstrap'
import { FormattedMessage } from 'react-intl' import { FormattedMessage } from 'react-intl'
const _paq = window._paq = window._paq || [] const _paq = window._paq = window._paq || []
@ -48,4 +48,66 @@ export function HamburgerMenuItem (props: HamburgerMenuItemProps) {
</Dropdown.Item> </Dropdown.Item>
</> </>
) )
}
// keeping the following for a later use:
export function NavHamburgerMenuItem (props: HamburgerMenuItemProps) {
const { hideOption } = props
const uid = 'workspace' + props.kind
return (
<>
<NavDropdown.Item>
<CustomTooltip
placement="right"
tooltipId={uid + "Tooltip"}
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id={'filePanel.workspace.' + props.kind} />}
>
<div
data-id={uid}
key={uid + '-fe-ws'}
onClick={() => {
props.actionOnClick()
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', uid])
}}
>
<span
hidden={hideOption}
id={uid}
data-id={uid}
className={props.fa + ' pl-2'}
style={{width: '1.4rem'}}
>
</span>
<span className="px-2">
<FormattedMessage id={'filePanel.' + props.kind } />
</span>
</div>
</CustomTooltip>
</NavDropdown.Item>
</>
)
}
export interface HamburgerSubMenuItemProps {
id: string
title: string
subMenus: Array<HamburgerMenuItemProps>
}
export function HamburgerSubMenuItem (props: HamburgerSubMenuItemProps) {
return (
<>
<NavDropdown
title={props.title}
as={CustomMenu}
key={props.id}
id={props.id}
drop='right'
>
{props.subMenus.map(item => <NavHamburgerMenuItem kind={item.kind} fa={item.fa} hideOption={item.hideOption} actionOnClick={item.actionOnClick} />)}
</NavDropdown>
</>
)
} }

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import { Dropdown } from 'react-bootstrap' import { Dropdown } from 'react-bootstrap'
import { HamburgerMenuItem } from './workspace-hamburger-item' import { HamburgerMenuItem, HamburgerSubMenuItem } from './workspace-hamburger-item'
export interface HamburgerMenuProps { export interface HamburgerMenuProps {
createWorkspace: () => void, createWorkspace: () => void,
@ -15,6 +15,7 @@ export interface HamburgerMenuProps {
addGithubAction: () => void, addGithubAction: () => void,
addTsSolTestGithubAction: () => void, addTsSolTestGithubAction: () => void,
addSlitherGithubAction: () => void, addSlitherGithubAction: () => void,
addHelperScripts: (script: string) => void,
showIconsMenu: boolean, showIconsMenu: boolean,
hideWorkspaceOptions: boolean, hideWorkspaceOptions: boolean,
hideLocalhostOptions: boolean hideLocalhostOptions: boolean
@ -70,6 +71,44 @@ export function HamburgerMenu (props: HamburgerMenuProps) {
props.addSlitherGithubAction() props.addSlitherGithubAction()
props.hideIconsMenu(!showIconsMenu) props.hideIconsMenu(!showIconsMenu)
}}></HamburgerMenuItem> }}></HamburgerMenuItem>
<Dropdown.Divider className="border mb-0 mt-0 remixui_menuhr" style={{ pointerEvents: 'none' }} />
<HamburgerMenuItem kind='addscriptetherscan' fa='fak fa-ts-logo' hideOption={hideWorkspaceOptions} actionOnClick={() => {
props.addHelperScripts('etherscan')
props.hideIconsMenu(!showIconsMenu)
}}></HamburgerMenuItem>
<HamburgerMenuItem kind='addscriptdeployer' fa='fak fa-ts-logo' hideOption={hideWorkspaceOptions} actionOnClick={() => {
props.addHelperScripts('deployer')
props.hideIconsMenu(!showIconsMenu)
}}></HamburgerMenuItem>
</> </>
) )
} }
// keep for later use
/*<HamburgerSubMenuItem
id="web3-script-menu"
title="Web3 Scripts"
subMenus={[
{
kind:'etherscan-script',
fa: 'fak fa-ts-logo',
hideOption: hideWorkspaceOptions,
actionOnClick: () => {
alert('etherscan')
props.addHelperScripts()
props.hideIconsMenu(!showIconsMenu)
}
},
{
kind:'contract-deployer-factory-script',
fa: 'fak fa-ts-logo',
hideOption: hideWorkspaceOptions,
actionOnClick: () => {
alert('deloyer')
props.addHelperScripts()
props.hideIconsMenu(!showIconsMenu)
}
}
]}
></HamburgerSubMenuItem>
*/

@ -45,6 +45,7 @@ export const FileSystemContext = createContext<{
dispatchCreateSolidityGithubAction: () => Promise<void>, dispatchCreateSolidityGithubAction: () => Promise<void>,
dispatchCreateTsSolGithubAction: () => Promise<void>, dispatchCreateTsSolGithubAction: () => Promise<void>,
dispatchCreateSlitherGithubAction: () => Promise<void> dispatchCreateSlitherGithubAction: () => Promise<void>
dispatchCreateHelperScripts: (script: string) => Promise<void>
}>(null) }>(null)

@ -8,7 +8,7 @@ import { browserReducer, browserInitialState } from '../reducers/workspace'
import { initWorkspace, fetchDirectory, removeInputField, deleteWorkspace, deleteAllWorkspaces, clearPopUp, publishToGist, createNewFile, setFocusElement, createNewFolder, import { initWorkspace, fetchDirectory, removeInputField, deleteWorkspace, deleteAllWorkspaces, clearPopUp, publishToGist, createNewFile, setFocusElement, createNewFolder,
deletePath, renamePath, downloadPath, copyFile, copyFolder, runScript, emitContextMenuEvent, handleClickFile, handleExpandPath, addInputField, createWorkspace, deletePath, renamePath, downloadPath, copyFile, copyFolder, runScript, emitContextMenuEvent, handleClickFile, handleExpandPath, addInputField, createWorkspace,
fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile, uploadFolder, handleDownloadWorkspace, handleDownloadFiles, restoreBackupZip, cloneRepository, moveFile, moveFolder, fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile, uploadFolder, handleDownloadWorkspace, handleDownloadFiles, restoreBackupZip, cloneRepository, moveFile, moveFolder,
showAllBranches, switchBranch, createNewBranch, checkoutRemoteBranch, createSolidityGithubAction, createTsSolGithubAction, createSlitherGithubAction showAllBranches, switchBranch, createNewBranch, checkoutRemoteBranch, createSolidityGithubAction, createTsSolGithubAction, createSlitherGithubAction, createHelperScripts
} from '../actions' } from '../actions'
import { Modal, WorkspaceProps, WorkspaceTemplate } from '../types' import { Modal, WorkspaceProps, WorkspaceTemplate } from '../types'
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
@ -183,6 +183,10 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
await createSlitherGithubAction() await createSlitherGithubAction()
} }
const dispatchCreateHelperScripts = async (script: string) => {
await createHelperScripts(script)
}
useEffect(() => { useEffect(() => {
dispatchInitWorkspace() dispatchInitWorkspace()
}, []) }, [])
@ -299,7 +303,8 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
dispatchCheckoutRemoteBranch, dispatchCheckoutRemoteBranch,
dispatchCreateSolidityGithubAction, dispatchCreateSolidityGithubAction,
dispatchCreateTsSolGithubAction, dispatchCreateTsSolGithubAction,
dispatchCreateSlitherGithubAction dispatchCreateSlitherGithubAction,
dispatchCreateHelperScripts
} }
return ( return (
<FileSystemContext.Provider value={value}> <FileSystemContext.Provider value={value}>

@ -209,6 +209,10 @@ export function Workspace () {
global.dispatchCreateSlitherGithubAction() global.dispatchCreateSlitherGithubAction()
} }
const addHelperScripts = (script: string) => {
global.dispatchCreateHelperScripts(script)
}
const downloadWorkspaces = async () => { const downloadWorkspaces = async () => {
try { try {
await global.dispatchHandleDownloadFiles() await global.dispatchHandleDownloadFiles()
@ -677,65 +681,45 @@ export function Workspace () {
<div className='d-flex flex-column w-100 remixui_fileexplorer' data-id="remixUIWorkspaceExplorer" onClick={resetFocus}> <div className='d-flex flex-column w-100 remixui_fileexplorer' data-id="remixUIWorkspaceExplorer" onClick={resetFocus}>
<div> <div>
<header> <header>
<div className="mx-2 mb-2 d-flex flex-column"> <div className="mx-2 my-2 d-flex flex-column">
<div className="d-flex justify-content-between"> <div className="d-flex">
<span className="d-flex align-items-end"> {currentWorkspace !== LOCALHOST ? (<span className="remixui_topmenu d-flex">
<label className="pl-1 form-check-label" htmlFor="workspacesSelect" style={{wordBreak: 'keep-all'}}> <Dropdown id="workspacesMenuDropdown" data-id="workspacesMenuDropdown" onToggle={() => hideIconsMenu(!showIconsMenu)} show={showIconsMenu}>
<FormattedMessage id='filePanel.workspace' /> <Dropdown.Toggle
</label> as={CustomIconsToggle}
</span> onClick={() => {
{currentWorkspace !== LOCALHOST ? (<span className="remixui_menu remixui_topmenu d-flex justify-content-between align-items-end w-75"> hideIconsMenu(!showIconsMenu)
<CustomTooltip }}
placement="top" icon={'fas fa-bars'}
tooltipId="createWorkspaceTooltip" ></Dropdown.Toggle>
tooltipClasses="text-nowrap" <Dropdown.Menu as={CustomMenu} data-id="wsdropdownMenu" className='custom-dropdown-items remixui_menuwidth' rootCloseEvent="click">
tooltipText={<FormattedMessage id='filePanel.create' />} <HamburgerMenu
> createWorkspace={createWorkspace}
<span renameCurrentWorkspace={renameCurrentWorkspace}
hidden={currentWorkspace === LOCALHOST} downloadCurrentWorkspace={downloadCurrentWorkspace}
id='workspaceCreate' deleteCurrentWorkspace={deleteCurrentWorkspace}
data-id='workspaceCreate' deleteAllWorkspaces={deleteAllWorkspaces}
onClick={(e) => { cloneGitRepository={cloneGitRepository}
e.stopPropagation() downloadWorkspaces={downloadWorkspaces}
createWorkspace() restoreBackup={restoreBackup}
_paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceCreate']) hideIconsMenu={hideIconsMenu}
}} addGithubAction={addGithubAction}
style={{ fontSize: 'medium' }} addSlitherGithubAction={addSlitherGithubAction}
className='far fa-plus remixui_menuicon d-flex align-self-end' addHelperScripts={addHelperScripts}
> addTsSolTestGithubAction={addTsSolTestGithubAction}
</span> showIconsMenu={showIconsMenu}
</CustomTooltip> hideWorkspaceOptions={ currentWorkspace === LOCALHOST }
<Dropdown id="workspacesMenuDropdown" data-id="workspacesMenuDropdown" onToggle={() => hideIconsMenu(!showIconsMenu)} show={showIconsMenu}> hideLocalhostOptions={ currentWorkspace === NO_WORKSPACE }
<Dropdown.Toggle />
as={CustomIconsToggle} </Dropdown.Menu>
onClick={() => { </Dropdown>
hideIconsMenu(!showIconsMenu) </span>) : null}
}} <span className="d-flex">
icon={'fas fa-bars'} <label className="pl-1 form-check-label" htmlFor="workspacesSelect" style={{wordBreak: 'keep-all'}}>
></Dropdown.Toggle> <FormattedMessage id='filePanel.workspace' />
<Dropdown.Menu as={CustomMenu} data-id="wsdropdownMenu" className='custom-dropdown-items remixui_menuwidth' rootCloseEvent="click"> </label>
<HamburgerMenu </span>
createWorkspace={createWorkspace}
renameCurrentWorkspace={renameCurrentWorkspace}
downloadCurrentWorkspace={downloadCurrentWorkspace}
deleteCurrentWorkspace={deleteCurrentWorkspace}
deleteAllWorkspaces={deleteAllWorkspaces}
cloneGitRepository={cloneGitRepository}
downloadWorkspaces={downloadWorkspaces}
restoreBackup={restoreBackup}
hideIconsMenu={hideIconsMenu}
addGithubAction={addGithubAction}
addSlitherGithubAction={addSlitherGithubAction}
addTsSolTestGithubAction={addTsSolTestGithubAction}
showIconsMenu={showIconsMenu}
hideWorkspaceOptions={ currentWorkspace === LOCALHOST }
hideLocalhostOptions={ currentWorkspace === NO_WORKSPACE }
/>
</Dropdown.Menu>
</Dropdown>
</span>) : null}
</div> </div>
<Dropdown id="workspacesSelect" data-id="workspacesSelect" onToggle={toggleDropdown} show={showDropdown}> <Dropdown id="workspacesSelect" data-id="workspacesSelect" onToggle={toggleDropdown} show={showDropdown}>
<Dropdown.Toggle <Dropdown.Toggle
as={CustomToggle} as={CustomToggle}

@ -0,0 +1,29 @@
import { ethers } from 'ethers'
/**
* Deploy the given contract
* @param {string} contractName name of the contract to deploy
* @param {Array<any>} args list of constructor' parameters
* @param {Number} accountIndex account index from the exposed account
* @return {Contract} deployed contract
*/
export const deploy = async (contractName: string, args: Array<any>, accountIndex?: number): Promise<ethers.Contract> => {
console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated
const artifactsPath = `browser/contracts/artifacts/${contractName}.json` // Change this for different path
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
// 'web3Provider' is a remix global variable object
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner(accountIndex)
const factory = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer)
const contract = await factory.deploy(...args)
// The contract is NOT deployed yet; we must wait until it is mined
await contract.deployed()
return contract
}

@ -0,0 +1,258 @@
import { ethers } from 'ethers'
// https://etherscan.io/address/0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2#code
export const CREATE2_DEPLOYER_ADDRESS =
"0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2";
/**
* Deploy the given contract using a factory
* @param {string} address of the factory contract
* @param {string} contractName name of the contract to deploy
* @param {Array<any>} args list of constructor' parameters
* @param {number} salt (using during address generation)
* @param {number} accountIndex account index from the exposed account
* @return {string} deployed contract address
*/
export const deploy = async (contractName: string, args: Array<any>, salt: string, accountIndex?: number): Promise<string> => {
console.log(`deploying ${contractName}`)
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner(accountIndex)
const factory = new ethers.Contract(CREATE2_DEPLOYER_ADDRESS, contractDeployerAbi, signer);
const contract = await ethers.getContractFactory(contractName)
const initCode = contract.getDeployTransaction(args)
const codeHash = ethers.utils.keccak256(initCode.data)
const saltBytes = ethers.utils.id(salt)
const deployedAddress = await factory.computeAddress(saltBytes, codeHash)
try {
const tx = await factory.deploy(0, saltBytes, initCode.data)
await tx.wait()
return deployedAddress
} catch (e) {
console.error(e.message)
console.error(`Please check a contract isn't already deployed at that address`)
throw e
}
}
export const contractDeployerAbi = [
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "Paused",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "Unpaused",
"type": "event"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "codeHash",
"type": "bytes32"
}
],
"name": "computeAddress",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "codeHash",
"type": "bytes32"
},
{
"internalType": "address",
"name": "deployer",
"type": "address"
}
],
"name": "computeAddressWithDeployer",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "code",
"type": "bytes"
}
],
"name": "deploy",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
}
],
"name": "deployERC1820Implementer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address payable",
"name": "payoutAddress",
"type": "address"
}
],
"name": "killCreate2Deployer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pause",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "paused",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "unpause",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]

@ -0,0 +1,11 @@
export const contractDeployerScripts = async (plugin) => {
await plugin.call('fileManager', 'writeFile',
'scripts/contract-deployer/create2-factory-deploy.ts' ,
// @ts-ignore
(await import('!!raw-loader!./create2-factory-deploy.ts')).default)
await plugin.call('fileManager', 'writeFile',
'scripts/contract-deployer/basic-contract-deploy.ts' ,
// @ts-ignore
(await import('!!raw-loader!./basic-contract-deploy.ts')).default)
}

@ -0,0 +1,11 @@
export const etherscanScripts = async (plugin) => {
await plugin.call('fileManager', 'writeFile',
'scripts/etherscan/verifyScript.ts' ,
// @ts-ignore
(await import('!!raw-loader!./verifyScript.ts')).default)
await plugin.call('fileManager', 'writeFile',
'scripts/etherscan/receiptGuidScript.ts' ,
// @ts-ignore
(await import('!!raw-loader!./receiptGuidScript.ts')).default)
}

@ -0,0 +1,8 @@
/**
* @param {string} apikey - etherscan api key.
* @param {string} guid - receipt id.
* @returns {{ status, message, succeed }} receiptStatus
*/
export const receiptStatus = async (apikey: string, guid: string) => {
return await remix.call('etherscan' as any, 'receiptStatus', guid, apikey)
}

@ -0,0 +1,13 @@
/**
* @param {string} apikey - etherscan api key.
* @param {string} contractAddress - Address of the contract to verify.
* @param {string} contractArguments - Parameters used in the contract constructor during the initial deployment. It should be the hex encoded value.
* @param {string} contractName - Name of the contract
* @param {string} contractFile - File where the contract is located
* @returns {{ guid, status, message, succeed }} verification result
*/
export const verify = async (apikey: string, contractAddress: string, contractArguments: string, contractName: string, contractFile: string) => {
const compilationResultParam = await remix.call('compilerArtefacts' as any, 'getCompilerAbstract', contractFile)
console.log('verifying.. ' + contractName)
return await remix.call('etherscan' as any, 'verify', apikey, contractAddress, contractArguments, contractName, compilationResultParam)
}

@ -0,0 +1,7 @@
import { contractDeployerScripts } from '../scripts/contract-deployer'
import { etherscanScripts } from '../scripts/etherscan'
export const scripts = {
'etherscan': etherscanScripts,
'deployer': contractDeployerScripts
}
Loading…
Cancel
Save