diff --git a/apps/remix-ide/src/app/tabs/locales/en/filePanel.json b/apps/remix-ide/src/app/tabs/locales/en/filePanel.json index 8a09760ae9..c241f23f60 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/filePanel.json +++ b/apps/remix-ide/src/app/tabs/locales/en/filePanel.json @@ -27,6 +27,10 @@ "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.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.slitherghaction": "Slither Workflow", "filePanel.workspace.helperscripts": "Adds convenient scripts to the 'scripts' directory", diff --git a/libs/remix-ui/helper/src/index.ts b/libs/remix-ui/helper/src/index.ts index 94dab78f43..0b19458914 100644 --- a/libs/remix-ui/helper/src/index.ts +++ b/libs/remix-ui/helper/src/index.ts @@ -3,4 +3,5 @@ export * from './lib/bleach' export * from './lib/helper-components' export * from './lib/components/PluginViewWrapper' export * from './lib/components/custom-dropdown' -export * from './lib/components/custom-tooltip' \ No newline at end of file +export * from './lib/components/custom-tooltip' +export * from './lib/components/dropdown-submenu' \ No newline at end of file diff --git a/libs/remix-ui/helper/src/lib/components/dropdown-submenu.tsx b/libs/remix-ui/helper/src/lib/components/dropdown-submenu.tsx new file mode 100644 index 0000000000..2647c0a7c9 --- /dev/null +++ b/libs/remix-ui/helper/src/lib/components/dropdown-submenu.tsx @@ -0,0 +1,92 @@ +import * as React from 'react'; +import {DropdownProps} from 'react-bootstrap/Dropdown'; +import {useRef} from "react"; + +interface Props extends DropdownProps { + id?: string; + className?: string; + href?: string; + title: string +} + +export const DropdownSubmenu: React.FC = (props:Props) => { + let refSubMenuContent = useRef(null as HTMLDivElement | null); + + let className = 'dropdown-submenu-container'; + className = props.className + ? className + ' ' + props.className + : className; + + const onClick = (event: React.SyntheticEvent) => { + event.preventDefault(); + event.stopPropagation(); + + if (refSubMenuContent.current) { + let show = false; + if (refSubMenuContent.current.classList.contains('show')) { + hideChildren(refSubMenuContent.current); + } else { + show = true; + hideSiblings(); + } + refSubMenuContent.current.classList.toggle('show'); + if (typeof props.onToggle === 'function') { + props.onToggle(show, event, { source: 'select'}); + } + } + }; + + const hideSiblings = () => { + if (refSubMenuContent.current) { + const parents = getParents( + refSubMenuContent.current, + '.dropdown-menu.show' + ); + if (parents.length > 1) { + hideChildren(parents[1]); + } + } + }; + + const hideChildren = (parent: any) => { + const children = parent.querySelectorAll('.dropdown-menu.show') as any; + for (const child of children) { + child.classList.remove('show'); + } + } + + const getParents = (elem: any, selector: string) => { + const nodes = []; + let element = elem; + nodes.push(element); + while (element.parentNode) { + if ( + typeof element.parentNode.matches === 'function' && + element.parentNode.matches(selector) + ) { + nodes.push(element.parentNode); + } + element = element.parentNode; + } + return nodes; + } + + return ( +
+ + {props.title} + +
+ {props.children} +
+
+ ); + +} \ No newline at end of file diff --git a/libs/remix-ui/workspace/src/lib/actions/workspace.ts b/libs/remix-ui/workspace/src/lib/actions/workspace.ts index f83ace346b..b59e756ac0 100644 --- a/libs/remix-ui/workspace/src/lib/actions/workspace.ts +++ b/libs/remix-ui/workspace/src/lib/actions/workspace.ts @@ -13,8 +13,7 @@ import { ROOT_PATH, slitherYml, solTestYml, tsSolTestYml } from '../utils/consta import { IndexedDBStorage } from '../../../../../../apps/remix-ide/src/app/files/filesystems/indexedDB' import { getUncommittedFiles } from '../utils/gitStatusFilter' import { AppModal, ModalTypes } from '@remix-ui/app' -import { contractDeployerScripts } from '../scripts/contract-deployer' -import { etherscanScripts } from '../scripts/etherscan' +import { scripts } from '../scripts' declare global { interface Window { remixFileSystemCallback: IndexedDBStorage; } @@ -679,9 +678,9 @@ export const createSlitherGithubAction = async () => { plugin.call('fileManager', 'open', path) } -export const createHelperScripts = async () => { - await contractDeployerScripts(plugin) - await etherscanScripts(plugin) +export const createHelperScripts = async (script: string) => { + if (!scripts[script]) return + await scripts[script](plugin) plugin.call('notification', 'toast', 'scripts added in the "scripts" folder') } diff --git a/libs/remix-ui/workspace/src/lib/components/workspace-hamburger-item.tsx b/libs/remix-ui/workspace/src/lib/components/workspace-hamburger-item.tsx index 082746eec5..9651c94c09 100644 --- a/libs/remix-ui/workspace/src/lib/components/workspace-hamburger-item.tsx +++ b/libs/remix-ui/workspace/src/lib/components/workspace-hamburger-item.tsx @@ -1,6 +1,6 @@ import React from 'react' -import { CustomTooltip } from '@remix-ui/helper' -import { Dropdown } from 'react-bootstrap' +import { CustomTooltip, CustomMenu, CustomIconsToggle } from '@remix-ui/helper' +import { Dropdown, NavDropdown} from 'react-bootstrap' import { FormattedMessage } from 'react-intl' const _paq = window._paq = window._paq || [] @@ -48,4 +48,66 @@ export function HamburgerMenuItem (props: HamburgerMenuItemProps) { ) + } + + // keeping the following for a later use: + export function NavHamburgerMenuItem (props: HamburgerMenuItemProps) { + const { hideOption } = props + const uid = 'workspace' + props.kind + return ( + <> + + } + > +
{ + props.actionOnClick() + _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', uid]) + }} + > + + + + +
+
+
+ + ) + } + + export interface HamburgerSubMenuItemProps { + id: string + title: string + subMenus: Array + } + + export function HamburgerSubMenuItem (props: HamburgerSubMenuItemProps) { + return ( + <> + + {props.subMenus.map(item => )} + + + ) } \ No newline at end of file diff --git a/libs/remix-ui/workspace/src/lib/components/workspace-hamburger.tsx b/libs/remix-ui/workspace/src/lib/components/workspace-hamburger.tsx index b2400613b6..bf00c5aa56 100644 --- a/libs/remix-ui/workspace/src/lib/components/workspace-hamburger.tsx +++ b/libs/remix-ui/workspace/src/lib/components/workspace-hamburger.tsx @@ -1,6 +1,6 @@ import React from 'react' import { Dropdown } from 'react-bootstrap' -import { HamburgerMenuItem } from './workspace-hamburger-item' +import { HamburgerMenuItem, HamburgerSubMenuItem } from './workspace-hamburger-item' export interface HamburgerMenuProps { createWorkspace: () => void, @@ -15,7 +15,7 @@ export interface HamburgerMenuProps { addGithubAction: () => void, addTsSolTestGithubAction: () => void, addSlitherGithubAction: () => void, - addHelperScripts: () => void, + addHelperScripts: (script: string) => void, showIconsMenu: boolean, hideWorkspaceOptions: boolean, hideLocalhostOptions: boolean @@ -72,10 +72,43 @@ export function HamburgerMenu (props: HamburgerMenuProps) { props.hideIconsMenu(!showIconsMenu) }}> - { - props.addHelperScripts() + { + props.addHelperScripts('etherscan') + props.hideIconsMenu(!showIconsMenu) + }}> + { + props.addHelperScripts('deployer') props.hideIconsMenu(!showIconsMenu) }}> ) - } \ No newline at end of file + } + + // keep for later use + /* { + 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) + } + } + ]} + > + */ \ No newline at end of file diff --git a/libs/remix-ui/workspace/src/lib/contexts/index.ts b/libs/remix-ui/workspace/src/lib/contexts/index.ts index 9af5f8e19d..3423323a90 100644 --- a/libs/remix-ui/workspace/src/lib/contexts/index.ts +++ b/libs/remix-ui/workspace/src/lib/contexts/index.ts @@ -45,7 +45,7 @@ export const FileSystemContext = createContext<{ dispatchCreateSolidityGithubAction: () => Promise, dispatchCreateTsSolGithubAction: () => Promise, dispatchCreateSlitherGithubAction: () => Promise - dispatchCreateHelperScripts: () => Promise + dispatchCreateHelperScripts: (script: string) => Promise }>(null) \ No newline at end of file diff --git a/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx b/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx index 8fb509e37a..29399275e1 100644 --- a/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx +++ b/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx @@ -183,8 +183,8 @@ export const FileSystemProvider = (props: WorkspaceProps) => { await createSlitherGithubAction() } - const dispatchCreateHelperScripts = async () => { - await createHelperScripts() + const dispatchCreateHelperScripts = async (script: string) => { + await createHelperScripts(script) } useEffect(() => { diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index 615180fd93..e14135302f 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -209,8 +209,8 @@ export function Workspace () { global.dispatchCreateSlitherGithubAction() } - const addHelperScripts = () => { - global.dispatchCreateHelperScripts() + const addHelperScripts = (script: string) => { + global.dispatchCreateHelperScripts(script) } const downloadWorkspaces = async () => { diff --git a/libs/remix-ui/workspace/src/lib/scripts/contract-deployer/basic-contract-deploy.ts b/libs/remix-ui/workspace/src/lib/scripts/contract-deployer/basic-contract-deploy.ts new file mode 100644 index 0000000000..e875db9a43 --- /dev/null +++ b/libs/remix-ui/workspace/src/lib/scripts/contract-deployer/basic-contract-deploy.ts @@ -0,0 +1,29 @@ +import { ethers } from 'ethers' + +/** + * Deploy the given contract + * @param {string} contractName name of the contract to deploy + * @param {Array} 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, accountIndex?: number): Promise => { + + 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 +} \ No newline at end of file diff --git a/libs/remix-ui/workspace/src/lib/scripts/contract-deployer/create2-factory-deploy.ts b/libs/remix-ui/workspace/src/lib/scripts/contract-deployer/create2-factory-deploy.ts index 35a7c1b4e7..13b6d690a7 100644 --- a/libs/remix-ui/workspace/src/lib/scripts/contract-deployer/create2-factory-deploy.ts +++ b/libs/remix-ui/workspace/src/lib/scripts/contract-deployer/create2-factory-deploy.ts @@ -5,7 +5,7 @@ export const CREATE2_DEPLOYER_ADDRESS = "0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2"; /** - * Deploy the given contract + * 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} args list of constructor' parameters diff --git a/libs/remix-ui/workspace/src/lib/scripts/contract-deployer/index.ts b/libs/remix-ui/workspace/src/lib/scripts/contract-deployer/index.ts index bcf3e80186..0fbeeb1f48 100644 --- a/libs/remix-ui/workspace/src/lib/scripts/contract-deployer/index.ts +++ b/libs/remix-ui/workspace/src/lib/scripts/contract-deployer/index.ts @@ -3,4 +3,9 @@ export const contractDeployerScripts = async (plugin) => { '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) } \ No newline at end of file diff --git a/libs/remix-ui/workspace/src/lib/scripts/index.ts b/libs/remix-ui/workspace/src/lib/scripts/index.ts new file mode 100644 index 0000000000..4f2c005e98 --- /dev/null +++ b/libs/remix-ui/workspace/src/lib/scripts/index.ts @@ -0,0 +1,7 @@ +import { contractDeployerScripts } from '../scripts/contract-deployer' +import { etherscanScripts } from '../scripts/etherscan' + +export const scripts = { + 'etherscan': etherscanScripts, + 'deployer': contractDeployerScripts +} \ No newline at end of file