diff --git a/.circleci/config.yml b/.circleci/config.yml index 988b8940ea..2095b373fe 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,7 +8,7 @@ parameters: type: boolean default: false orbs: - browser-tools: circleci/browser-tools@1.2.3 + browser-tools: circleci/browser-tools@1.3.0 jobs: build: docker: @@ -35,10 +35,10 @@ jobs: key: v1-deps-{{ checksum "yarn.lock" }} paths: - node_modules - - run: yarn run downloadsolc_assets + - run: yarn run downloadsolc_assets - run: npx nx build remix-ide - run: npx nx build remix-ide-e2e-src-local-plugin - + - run: yarn run build:libs - run: mkdir persist && zip -r persist/dist.zip dist - persist_to_workspace: @@ -266,6 +266,94 @@ jobs: path: ./reports/tests - store_artifacts: path: ./reports/screenshots + + remix-ide-vyper-plugin: + docker: + # specify the version you desire here + - image: cimg/node:14.17.6-browsers + + # Specify service dependencies here if necessary + # CircleCI maintains a library of pre-built images + # documented at https://circleci.com/docs/2.0/circleci-images/ + resource_class: xlarge + # - image: circleci/mongo:3.4.4 + environment: + - COMMIT_AUTHOR_EMAIL: "yann@ethereum.org" + - COMMIT_AUTHOR: "Circle CI" + working_directory: ~/remix-project + parallelism: 10 + steps: + - browser-tools/install-chrome + - browser-tools/install-chromedriver + - run: + command: | + google-chrome --version + chromedriver --version + java -jar /usr/local/bin/selenium.jar --version + name: Check install + - checkout + - checkout + - attach_workspace: + at: . + - run: unzip ./persist/dist.zip + - restore_cache: + keys: + - v1-deps-{{ checksum "yarn.lock" }} + - run: yarn install + - run: + name: Start Selenium + command: java -jar /usr/local/bin/selenium.jar + background: true + - run: npx nx build vyper + - run: ./apps/remix-ide/ci/browser_tests_vyper_plugin.sh + - store_test_results: + path: ./reports/tests + - store_artifacts: + path: ./reports/screenshots + + remix-ide-etherscan-plugin: + docker: + # specify the version you desire here + - image: cimg/node:14.17.6-browsers + + # Specify service dependencies here if necessary + # CircleCI maintains a library of pre-built images + # documented at https://circleci.com/docs/2.0/circleci-images/ + resource_class: xlarge + # - image: circleci/mongo:3.4.4 + environment: + - COMMIT_AUTHOR_EMAIL: "yann@ethereum.org" + - COMMIT_AUTHOR: "Circle CI" + working_directory: ~/remix-project + parallelism: 10 + steps: + - browser-tools/install-chrome + - browser-tools/install-chromedriver + - run: + command: | + google-chrome --version + chromedriver --version + java -jar /usr/local/bin/selenium.jar --version + name: Check install + - checkout + - checkout + - attach_workspace: + at: . + - run: unzip ./persist/dist.zip + - restore_cache: + keys: + - v1-deps-{{ checksum "yarn.lock" }} + - run: yarn install + - run: + name: Start Selenium + command: java -jar /usr/local/bin/selenium.jar + background: true + - run: npx nx build etherscan + - run: ./apps/remix-ide/ci/browser_tests_etherscan_plugin.sh + - store_test_results: + path: ./reports/tests + - store_artifacts: + path: ./reports/screenshots remix-ide-plugin-api: docker: @@ -420,6 +508,12 @@ workflows: - remix-ide-plugin-api: requires: - build + - remix-ide-vyper-plugin: + requires: + - build + - remix-ide-etherscan-plugin: + requires: + - build - remix-ide-chrome: requires: - build @@ -433,6 +527,8 @@ workflows: - remix-ide-chrome - remix-ide-firefox - remix-ide-plugin-api + - remix-ide-vyper-plugin + - remix-ide-etherscan-plugin filters: branches: only: remix_live @@ -443,6 +539,8 @@ workflows: - remix-ide-chrome - remix-ide-firefox - remix-ide-plugin-api + - remix-ide-vyper-plugin + - remix-ide-etherscan-plugin filters: branches: only: master @@ -453,6 +551,8 @@ workflows: - remix-ide-chrome - remix-ide-firefox - remix-ide-plugin-api + - remix-ide-vyper-plugin + - remix-ide-etherscan-plugin filters: branches: only: remix_beta diff --git a/README.md b/README.md index 84cc44a09b..8692c7c4ba 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,7 @@ To do this you need to: browser.waitForElementPresent('*[data-id="verticalIconsKindfilePanel"]') ``` -- add '@disable': true to the test file you want to split: +- add '@disabled': true to the test file you want to split: ``` module.exports = { diff --git a/apps/etherscan/.babelrc b/apps/etherscan/.babelrc new file mode 100644 index 0000000000..b1fc975456 --- /dev/null +++ b/apps/etherscan/.babelrc @@ -0,0 +1,13 @@ +{ + "presets": [ + [ + "@nrwl/react/babel", { + "runtime": "automatic" + + } + ] + ], + "plugins": [ + + ] +} diff --git a/apps/etherscan/.browserslistrc b/apps/etherscan/.browserslistrc new file mode 100644 index 0000000000..f1d12df4fa --- /dev/null +++ b/apps/etherscan/.browserslistrc @@ -0,0 +1,16 @@ +# This file is used by: +# 1. autoprefixer to adjust CSS to support the below specified browsers +# 2. babel preset-env to adjust included polyfills +# +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries +# +# If you need to support different browsers in production, you may tweak the list below. + +last 1 Chrome version +last 1 Firefox version +last 2 Edge major versions +last 2 Safari major version +last 2 iOS major versions +Firefox ESR +not IE 9-11 # For IE 9-11 support, remove 'not'. \ No newline at end of file diff --git a/apps/etherscan/.eslintrc.json b/apps/etherscan/.eslintrc.json new file mode 100644 index 0000000000..a92d0f887a --- /dev/null +++ b/apps/etherscan/.eslintrc.json @@ -0,0 +1,34 @@ +{ + "extends": [ + "plugin:@nrwl/nx/react", + "../../.eslintrc.json" + ], + "ignorePatterns": [ + "!**/*" + ], + "overrides": [ + { + "files": [ + "*.ts", + "*.tsx", + "*.js", + "*.jsx" + ], + "rules": {} + }, + { + "files": [ + "*.ts", + "*.tsx" + ], + "rules": {} + }, + { + "files": [ + "*.js", + "*.jsx" + ], + "rules": {} + } + ] +} \ No newline at end of file diff --git a/apps/etherscan/src/app/App.css b/apps/etherscan/src/app/App.css new file mode 100644 index 0000000000..27083bfb90 --- /dev/null +++ b/apps/etherscan/src/app/App.css @@ -0,0 +1,7 @@ +body { + margin: 0; +} + +#root { + padding: 5px; +} \ No newline at end of file diff --git a/apps/etherscan/src/app/AppContext.tsx b/apps/etherscan/src/app/AppContext.tsx new file mode 100644 index 0000000000..52b0c1fda8 --- /dev/null +++ b/apps/etherscan/src/app/AppContext.tsx @@ -0,0 +1,24 @@ +import React from "react" +import { PluginClient } from "@remixproject/plugin" + +import { Receipt, ThemeType } from "./types" + +export const AppContext = React.createContext({ + apiKey: "", + setAPIKey: (value: string) => { + console.log("Set API Key from Context") + }, + clientInstance: {} as PluginClient, + receipts: [] as Receipt[], + setReceipts: (receipts: Receipt[]) => { + console.log("Calling Set Receipts") + }, + contracts: [] as string[], + setContracts: (contracts: string[]) => { + console.log("Calling Set Contract Names") + }, + themeType: "dark" as ThemeType, + setThemeType: (themeType: ThemeType) => { + console.log("Calling Set Theme Type") + }, +}) diff --git a/apps/etherscan/src/app/app.tsx b/apps/etherscan/src/app/app.tsx new file mode 100644 index 0000000000..653d90bfb1 --- /dev/null +++ b/apps/etherscan/src/app/app.tsx @@ -0,0 +1,153 @@ +import React, { useState, useEffect, useRef } from "react" + +import { + CompilationFileSources, + CompilationResult, +} from "@remixproject/plugin-api" + +import { PluginClient } from "@remixproject/plugin"; +import { createClient } from "@remixproject/plugin-webview"; + +import { AppContext } from "./AppContext" +import { DisplayRoutes } from "./routes" + +import { useLocalStorage } from "./hooks/useLocalStorage" + +import { getReceiptStatus, getEtherScanApi, getNetworkName } from "./utils" +import { Receipt, ThemeType } from "./types" + +import "./App.css" + +export const getNewContractNames = (compilationResult: CompilationResult) => { + const compiledContracts = compilationResult.contracts + let result: string[] = [] + + for (const file of Object.keys(compiledContracts)) { + const newContractNames = Object.keys(compiledContracts[file]) + result = [...result, ...newContractNames] + } + + return result +} + +const App = () => { + const [apiKey, setAPIKey] = useLocalStorage("apiKey", "") + const [clientInstance, setClientInstance] = useState(undefined as any) + const [receipts, setReceipts] = useLocalStorage("receipts", []) + const [contracts, setContracts] = useState([] as string[]) + const [themeType, setThemeType] = useState("dark" as ThemeType) + + const clientInstanceRef = useRef(clientInstance) + clientInstanceRef.current = clientInstance + const contractsRef = useRef(contracts) + contractsRef.current = contracts + + useEffect(() => { + const client = new PluginClient() + createClient(client) + const loadClient = async () => { + await client.onload() + setClientInstance(client) + client.on("solidity", + "compilationFinished", + ( + fileName: string, + source: CompilationFileSources, + languageVersion: string, + data: CompilationResult + ) => { + const newContractsNames = getNewContractNames(data) + + const newContractsToSave: string[] = [ + ...contractsRef.current, + ...newContractsNames, + ] + + const uniqueContracts: string[] = [...new Set(newContractsToSave)] + + setContracts(uniqueContracts) + } + ) + + //const currentTheme = await client.call("theme", "currentTheme") + //setThemeType(currentTheme.quality) + //client.on("theme", "themeChanged", (theme) => { + // setThemeType(theme.quality) + //}) + } + + loadClient() + }, []) + + useEffect(() => { + if (!clientInstance) { + return + } + + const receiptsNotVerified: Receipt[] = receipts.filter((item: Receipt) => { + return item.status !== "Verified" + }) + + if (receiptsNotVerified.length > 0) { + let timer1 = setInterval(() => { + for (const item in receiptsNotVerified) { + + } + receiptsNotVerified.forEach(async (item) => { + if (!clientInstanceRef.current) { + return {} + } + const network = await getNetworkName(clientInstanceRef.current) + if (network === "vm") { + return {} + } + const status = await getReceiptStatus( + item.guid, + apiKey, + getEtherScanApi(network) + ) + if (status === "Pass - Verified") { + const newReceipts = receipts.map((currentReceipt: Receipt) => { + if (currentReceipt.guid === item.guid) { + return { + ...currentReceipt, + status: "Verified", + } + } + return currentReceipt + }) + + clearInterval(timer1) + + setReceipts(newReceipts) + + return () => { + clearInterval(timer1) + } + } + return {} + }) + }, 5000) + } + }, [receipts, clientInstance, apiKey, setReceipts]) + + return ( + + + + ) +} + +export default App diff --git a/apps/etherscan/src/app/components/HeaderWithSettings.tsx b/apps/etherscan/src/app/components/HeaderWithSettings.tsx new file mode 100644 index 0000000000..14e048d6cd --- /dev/null +++ b/apps/etherscan/src/app/components/HeaderWithSettings.tsx @@ -0,0 +1,160 @@ +import React from "react" + +import { NavLink } from "react-router-dom" +import { AppContext } from "../AppContext" +import { ThemeType } from "../types" + +interface Props { + title?: string + showBackButton?: boolean + from: string +} + +interface IconProps { + from: string + themeType: ThemeType +} + +const HomeIcon: React.FC = ({ from, themeType }: IconProps) => { + return ( + { + return { + ...(isActive ? getStyleFilterIcon(themeType) : {}), ...{ marginRight: "0.4em" } + } + }} + > + + + + + + ) +} + +const SettingsIcon: React.FC = ({ from, themeType }: IconProps) => { + return ( + { + return { + ...(isActive ? getStyleFilterIcon(themeType) : {}), ...{ marginRight: "0.4em" } + } + }} + > + + + + + + + ) +} + +const getStyleFilterIcon = (themeType: ThemeType) => { + const invert = themeType === "dark" ? 1 : 0 + const brightness = themeType === "dark" ? "150" : "0" // should be >100 for icons with color + return { + filter: `invert(${invert}) grayscale(1) brightness(${brightness}%)`, + } +} + +const ReceiptsIcon: React.FC = ({ from, themeType }: IconProps) => { + return ( + { + return { + ...(isActive ? getStyleFilterIcon(themeType) : {}), ...{ marginRight: "0.4em" } + } + }} + > + + + + + + + ) +} +export const HeaderWithSettings: React.FC = ({ + title = "", + showBackButton = false, + from, +}) => { + return ( + + {({ themeType }) => ( +
+
{title}
+
+ + + + + +
+
+ )} +
+ ) +} diff --git a/apps/etherscan/src/app/components/SubmitButton.tsx b/apps/etherscan/src/app/components/SubmitButton.tsx new file mode 100644 index 0000000000..c89f12bf02 --- /dev/null +++ b/apps/etherscan/src/app/components/SubmitButton.tsx @@ -0,0 +1,37 @@ +import React from "react" + +interface Props { + text: string + isSubmitting?: boolean + dataId?: string +} + +export const SubmitButton: React.FC = ({ + text, + dataId, + isSubmitting = false, +}) => { + return ( + + ) +} diff --git a/apps/etherscan/src/app/components/index.ts b/apps/etherscan/src/app/components/index.ts new file mode 100644 index 0000000000..c52e3712f0 --- /dev/null +++ b/apps/etherscan/src/app/components/index.ts @@ -0,0 +1,2 @@ +export { HeaderWithSettings } from "./HeaderWithSettings" +export { SubmitButton } from "./SubmitButton" diff --git a/apps/etherscan/src/app/hooks/useLocalStorage.tsx b/apps/etherscan/src/app/hooks/useLocalStorage.tsx new file mode 100644 index 0000000000..dbef5378a8 --- /dev/null +++ b/apps/etherscan/src/app/hooks/useLocalStorage.tsx @@ -0,0 +1,37 @@ +import { useState } from "react" + +export function useLocalStorage(key: string, initialValue: any) { + // State to store our value + // Pass initial state function to useState so logic is only executed once + const [storedValue, setStoredValue] = useState(() => { + try { + // Get from local storage by key + const item = window.localStorage.getItem(key) + // Parse stored json or if none return initialValue + return item ? JSON.parse(item) : initialValue + } catch (error) { + // If error also return initialValue + console.error(error) + return initialValue + } + }) + + // Return a wrapped version of useState's setter function that ... + // ... persists the new value to localStorage. + const setValue = (value: any) => { + try { + // Allow value to be a function so we have same API as useState + const valueToStore = + value instanceof Function ? value(storedValue) : value + // Save state + setStoredValue(valueToStore) + // Save to local storage + window.localStorage.setItem(key, JSON.stringify(valueToStore)) + } catch (error) { + // A more advanced implementation would handle the error case + console.error(error) + } + } + + return [storedValue, setValue] +} diff --git a/apps/etherscan/src/app/layouts/Default.tsx b/apps/etherscan/src/app/layouts/Default.tsx new file mode 100644 index 0000000000..93dd89b851 --- /dev/null +++ b/apps/etherscan/src/app/layouts/Default.tsx @@ -0,0 +1,19 @@ +import React, { PropsWithChildren } from "react" + +import { HeaderWithSettings } from "../components" + +interface Props { + from: string +} + +export const DefaultLayout: React.FC> = ({ + children, + from, +}) => { + return ( +
+ + {children} +
+ ) +} diff --git a/apps/etherscan/src/app/layouts/index.ts b/apps/etherscan/src/app/layouts/index.ts new file mode 100644 index 0000000000..9b8e6166d5 --- /dev/null +++ b/apps/etherscan/src/app/layouts/index.ts @@ -0,0 +1 @@ +export { DefaultLayout } from "./Default" diff --git a/apps/etherscan/src/app/logo.svg b/apps/etherscan/src/app/logo.svg new file mode 100644 index 0000000000..8fa84ab509 --- /dev/null +++ b/apps/etherscan/src/app/logo.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/apps/etherscan/src/app/routes.tsx b/apps/etherscan/src/app/routes.tsx new file mode 100644 index 0000000000..68fd76318f --- /dev/null +++ b/apps/etherscan/src/app/routes.tsx @@ -0,0 +1,51 @@ +import React from "react" +import { + BrowserRouter as Router, + Route, + Routes, + RouteProps, +} from "react-router-dom" + +import { ErrorView, HomeView, ReceiptsView, CaptureKeyView } from "./views" +import { DefaultLayout } from "./layouts" + +interface Props extends RouteProps { + component: any // TODO: new (props: any) => React.Component + from: string +} + +const RouteWithHeader = ({ component: Component, ...rest }: Props) => { + return ( + + + + + + ) +} + +export const DisplayRoutes = () => ( + + + + + } /> + } /> + + + } /> + + + } /> + + +) diff --git a/apps/etherscan/src/app/star.svg b/apps/etherscan/src/app/star.svg new file mode 100644 index 0000000000..901053d385 --- /dev/null +++ b/apps/etherscan/src/app/star.svg @@ -0,0 +1,11 @@ + + + + + diff --git a/apps/etherscan/src/app/types/Receipt.ts b/apps/etherscan/src/app/types/Receipt.ts new file mode 100644 index 0000000000..57de417094 --- /dev/null +++ b/apps/etherscan/src/app/types/Receipt.ts @@ -0,0 +1,6 @@ +export type ReceiptStatus = "Verified" | "Queue" + +export interface Receipt { + guid: string + status: ReceiptStatus +} diff --git a/apps/etherscan/src/app/types/ThemeType.ts b/apps/etherscan/src/app/types/ThemeType.ts new file mode 100644 index 0000000000..13b3710cd0 --- /dev/null +++ b/apps/etherscan/src/app/types/ThemeType.ts @@ -0,0 +1 @@ +export type ThemeType = "dark" | "light" diff --git a/apps/etherscan/src/app/types/index.ts b/apps/etherscan/src/app/types/index.ts new file mode 100644 index 0000000000..1a8733d6ff --- /dev/null +++ b/apps/etherscan/src/app/types/index.ts @@ -0,0 +1,2 @@ +export * from "./Receipt" +export * from "./ThemeType" diff --git a/apps/etherscan/src/app/utils/index.ts b/apps/etherscan/src/app/utils/index.ts new file mode 100644 index 0000000000..b23d52e6e0 --- /dev/null +++ b/apps/etherscan/src/app/utils/index.ts @@ -0,0 +1 @@ +export * from "./utilities" diff --git a/apps/etherscan/src/app/utils/utilities.ts b/apps/etherscan/src/app/utils/utilities.ts new file mode 100644 index 0000000000..d7300806a3 --- /dev/null +++ b/apps/etherscan/src/app/utils/utilities.ts @@ -0,0 +1,32 @@ +import { PluginClient } from "@remixproject/plugin" +import axios from 'axios' +type RemixClient = PluginClient + +export const getEtherScanApi = (network: string) => { + return network === "main" + ? `https://api.etherscan.io/api` + : `https://api-${network}.etherscan.io/api` +} + +export const getNetworkName = async (client: RemixClient) => { + const network = await client.call("network", "detectNetwork") + if (!network) { + throw new Error("no known network to verify against") + } + return network.name!.toLowerCase() +} + +export const getReceiptStatus = async ( + receiptGuid: string, + apiKey: string, + etherscanApi: string +) => { + const params = `guid=${receiptGuid}&module=contract&action=checkverifystatus&apiKey=${apiKey}` + try { + const response = await axios.get(`${etherscanApi}?${params}`) + const { result } = response.data + return result + } catch (error) { + console.error(error) + } +} diff --git a/apps/etherscan/src/app/views/CaptureKeyView.tsx b/apps/etherscan/src/app/views/CaptureKeyView.tsx new file mode 100644 index 0000000000..9703ec10ff --- /dev/null +++ b/apps/etherscan/src/app/views/CaptureKeyView.tsx @@ -0,0 +1,59 @@ +import React from "react" + +import { Formik, ErrorMessage, Field } from "formik" +import { useNavigate, useLocation } from "react-router-dom" + +import { AppContext } from "../AppContext" +import { SubmitButton } from "../components" + +export const CaptureKeyView: React.FC = () => { + const location = useLocation() + const navigate = useNavigate() + return ( + + {({ apiKey, setAPIKey }) => ( + { + const errors = {} as any + if (!values.apiKey) { + errors.apiKey = "Required" + } + return errors + }} + onSubmit={(values) => { + setAPIKey(values.apiKey) + navigate((location.state as any).from) + }} + > + {({ errors, touched, handleSubmit }) => ( +
+
+ + + +
+ +
+ +
+
+ )} +
+ )} +
+ ) +} diff --git a/apps/etherscan/src/app/views/ErrorView.tsx b/apps/etherscan/src/app/views/ErrorView.tsx new file mode 100644 index 0000000000..69e022e47c --- /dev/null +++ b/apps/etherscan/src/app/views/ErrorView.tsx @@ -0,0 +1,31 @@ +import React from "react" + +export const ErrorView: React.FC = () => { + return ( +
+ Error page +
Sorry, something unexpected happened.
+
+ Please raise an issue:{" "} + + Here + +
+
+ ) +} diff --git a/apps/etherscan/src/app/views/HomeView.tsx b/apps/etherscan/src/app/views/HomeView.tsx new file mode 100644 index 0000000000..0b9d2e3820 --- /dev/null +++ b/apps/etherscan/src/app/views/HomeView.tsx @@ -0,0 +1,36 @@ +import React from "react" + +import { Navigate } from "react-router-dom" + +import { AppContext } from "../AppContext" +import { Receipt } from "../types" + +import { VerifyView } from "./VerifyView" + +export const HomeView: React.FC = () => { + // const [hasError, setHasError] = useState(false) + return ( + + {({ apiKey, clientInstance, setReceipts, receipts, contracts }) => + !apiKey ? ( + + ) : ( + { + const newReceipts = [...receipts, receipt] + + setReceipts(newReceipts) + }} + /> + ) + } + + ) +} diff --git a/apps/etherscan/src/app/views/ReceiptsView.tsx b/apps/etherscan/src/app/views/ReceiptsView.tsx new file mode 100644 index 0000000000..ad13fbee33 --- /dev/null +++ b/apps/etherscan/src/app/views/ReceiptsView.tsx @@ -0,0 +1,135 @@ +import React, { useState } from "react" + +import { Formik, ErrorMessage, Field } from "formik" +import { getEtherScanApi, getNetworkName, getReceiptStatus } from "../utils" +import { Receipt } from "../types" +import { AppContext } from "../AppContext" +import { SubmitButton } from "../components" +import { Navigate } from "react-router-dom" + +interface FormValues { + receiptGuid: string +} + +export const ReceiptsView: React.FC = () => { + const [results, setResults] = useState("") + const onGetReceiptStatus = async ( + values: FormValues, + clientInstance: any, + apiKey: string + ) => { + try { + const network = await getNetworkName(clientInstance) + if (network === "vm") { + setResults("Cannot verify in the selected network") + return + } + const etherscanApi = getEtherScanApi(network) + const result = await getReceiptStatus( + values.receiptGuid, + apiKey, + etherscanApi + ) + setResults(result) + } catch (error: any) { + setResults(error.message) + } + } + + return ( + + {({ apiKey, clientInstance, receipts }) => + !apiKey ? ( + + ) : ( +
+ { + const errors = {} as any + if (!values.receiptGuid) { + errors.receiptGuid = "Required" + } + return errors + }} + onSubmit={(values) => + onGetReceiptStatus(values, clientInstance, apiKey) + } + > + {({ errors, touched, handleSubmit }) => ( +
+
+
Get your Receipt GUID status
+ + + +
+ + + + )} +
+ +
+ + +
+ ) + } + + ) +} + +const ReceiptsTable: React.FC<{ receipts: Receipt[] }> = ({ receipts }) => { + return ( +
+
Receipts
+ + + + + + + + + {receipts && + receipts.length > 0 && + receipts.map((item: Receipt, index) => { + return ( + + + + + ) + })} + +
GuidStatus
{item.guid}{item.status}
+
+ ) +} diff --git a/apps/etherscan/src/app/views/VerifyView.tsx b/apps/etherscan/src/app/views/VerifyView.tsx new file mode 100644 index 0000000000..3e85d471b7 --- /dev/null +++ b/apps/etherscan/src/app/views/VerifyView.tsx @@ -0,0 +1,302 @@ +import React, { useState } from "react" + +import { + PluginClient, +} from "@remixproject/plugin" +import { Formik, ErrorMessage, Field } from "formik" + +import { getNetworkName, getEtherScanApi, getReceiptStatus } from "../utils" +import { SubmitButton } from "../components" +import { Receipt } from "../types" +import { CompilationResult } from "@remixproject/plugin-api" +import axios from 'axios' + +interface Props { + client: PluginClient + apiKey: string + onVerifiedContract: (receipt: Receipt) => void + contracts: string[] +} + +interface FormValues { + contractName: string + contractArguments: string + contractAddress: string +} + +export const getContractFileName = ( + compilationResult: CompilationResult, + contractName: string +) => { + const compiledContracts = compilationResult.contracts + let fileName = "" + + for (const file of Object.keys(compiledContracts)) { + for (const contract of Object.keys(compiledContracts[file])) { + if (contract === contractName) { + fileName = file + break + } + } + } + return fileName +} + +export const getContractMetadata = ( + compilationResult: CompilationResult, + contractName: string +) => { + const compiledContracts = compilationResult.contracts + let contractMetadata = "" + + for (const file of Object.keys(compiledContracts)) { + for (const contract of Object.keys(compiledContracts[file])) { + if (contract === contractName) { + contractMetadata = compiledContracts[file][contract].metadata + if (contractMetadata) { + break + } + } + } + } + return contractMetadata +} + +export const VerifyView: React.FC = ({ + apiKey, + client, + contracts, + onVerifiedContract, +}) => { + const [results, setResults] = useState("") + + const onVerifyContract = async (values: FormValues) => { + const compilationResult = (await client.call( + "solidity", + "getCompilationResult" + )) as any + + if (!compilationResult) { + throw new Error("no compilation result available") + } + + const contractArguments = values.contractArguments.replace("0x", "") + + const verify = async ( + apiKeyParam: string, + contractAddress: string, + contractArgumentsParam: string, + contractName: string, + compilationResultParam: any + ) => { + const network = await getNetworkName(client) + if (network === "vm") { + return "Cannot verify in the selected network" + } + const etherscanApi = getEtherScanApi(network) + + try { + const contractMetadata = getContractMetadata( + compilationResultParam.data, + contractName + ) + + if (!contractMetadata) { + return "Please recompile contract" + } + + const contractMetadataParsed = JSON.parse(contractMetadata) + + const fileName = getContractFileName( + compilationResultParam.data, + contractName + ) + + const jsonInput = { + language: 'Solidity', + sources: compilationResultParam.source.sources, + settings: { + optimizer: { + enabled: contractMetadataParsed.settings.optimizer.enabled, + runs: contractMetadataParsed.settings.optimizer.runs + } + } + } + + const data: { [key: string]: string | any } = { + apikey: apiKeyParam, // A valid API-Key is required + module: "contract", // Do not change + action: "verifysourcecode", // Do not change + codeformat: "solidity-standard-json-input", + contractaddress: contractAddress, // Contract Address starts with 0x... + sourceCode: JSON.stringify(jsonInput), + contractname: fileName + ':' + contractName, + compilerversion: `v${contractMetadataParsed.compiler.version}`, // see http://etherscan.io/solcversions for list of support versions + constructorArguements: contractArgumentsParam, // if applicable + } + + const body = new FormData() + Object.keys(data).forEach((key) => body.append(key, data[key])) + + client.emit("statusChanged", { + key: "loading", + type: "info", + title: "Verifying ...", + }) + const response = await axios.post(etherscanApi, body) + const { message, result, status } = await response.data + + if (message === "OK" && status === "1") { + resetAfter10Seconds() + const receiptStatus = await getReceiptStatus( + result, + apiKey, + etherscanApi + ) + + onVerifiedContract({ + guid: result, + status: receiptStatus, + }) + return `Contract verified correctly
Receipt GUID ${result}` + } + if (message === "NOTOK") { + client.emit("statusChanged", { + key: "failed", + type: "error", + title: result, + }) + resetAfter10Seconds() + } + return result + } catch (error) { + console.error(error) + setResults("Something wrong happened, try again") + } + } + + const resetAfter10Seconds = () => { + setTimeout(() => { + client.emit("statusChanged", { key: "none" }) + setResults("") + }, 10000) + } + + const verificationResult = await verify( + apiKey, + values.contractAddress, + contractArguments, + values.contractName, + compilationResult + ) + + setResults(verificationResult) + } + + return ( +
+ { + const errors = {} as any + if (!values.contractName) { + errors.contractName = "Required" + } + if (!values.contractAddress) { + errors.contractAddress = "Required" + } + if (values.contractAddress.trim() === "") { + errors.contractAddress = "Please enter a valid contract address" + } + return errors + }} + onSubmit={(values) => onVerifyContract(values)} + > + {({ errors, touched, handleSubmit, isSubmitting }) => ( +
+
+
Verify your smart contracts
+ + + + {contracts.map((item) => ( + + ))} + + +
+ +
+ + + +
+ +
+ + + +
+ + + + )} +
+ +
+ + {/*
+ View Receipts +
*/} +
+ ) +} diff --git a/apps/etherscan/src/app/views/index.ts b/apps/etherscan/src/app/views/index.ts new file mode 100644 index 0000000000..c483228ece --- /dev/null +++ b/apps/etherscan/src/app/views/index.ts @@ -0,0 +1,4 @@ +export { HomeView } from "./HomeView" +export { ErrorView } from "./ErrorView" +export { ReceiptsView } from "./ReceiptsView" +export { CaptureKeyView } from "./CaptureKeyView" diff --git a/apps/etherscan/src/assets/.gitkeep b/apps/etherscan/src/assets/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/etherscan/src/environments/environment.prod.ts b/apps/etherscan/src/environments/environment.prod.ts new file mode 100644 index 0000000000..3612073bc3 --- /dev/null +++ b/apps/etherscan/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true +}; diff --git a/apps/etherscan/src/environments/environment.ts b/apps/etherscan/src/environments/environment.ts new file mode 100644 index 0000000000..d9370e924b --- /dev/null +++ b/apps/etherscan/src/environments/environment.ts @@ -0,0 +1,6 @@ +// This file can be replaced during build by using the `fileReplacements` array. +// When building for production, this file is replaced with `environment.prod.ts`. + +export const environment = { + production: false +}; diff --git a/apps/etherscan/src/favicon.ico b/apps/etherscan/src/favicon.ico new file mode 100644 index 0000000000..317ebcb233 Binary files /dev/null and b/apps/etherscan/src/favicon.ico differ diff --git a/apps/etherscan/src/index.html b/apps/etherscan/src/index.html new file mode 100644 index 0000000000..b58279cb25 --- /dev/null +++ b/apps/etherscan/src/index.html @@ -0,0 +1,14 @@ + + + + + Etherscan + + + + + + +
+ + diff --git a/apps/etherscan/src/main.tsx b/apps/etherscan/src/main.tsx new file mode 100644 index 0000000000..353ad43f6d --- /dev/null +++ b/apps/etherscan/src/main.tsx @@ -0,0 +1,7 @@ +import { StrictMode } from 'react'; +import * as ReactDOM from 'react-dom'; + + +import App from './app/app'; + +ReactDOM.render(, document.getElementById('root')); diff --git a/apps/etherscan/src/polyfills.ts b/apps/etherscan/src/polyfills.ts new file mode 100644 index 0000000000..2adf3d05b6 --- /dev/null +++ b/apps/etherscan/src/polyfills.ts @@ -0,0 +1,7 @@ +/** + * Polyfill stable language features. These imports will be optimized by `@babel/preset-env`. + * + * See: https://github.com/zloirock/core-js#babel + */ +import 'core-js/stable'; +import 'regenerator-runtime/runtime'; diff --git a/apps/etherscan/src/styles.css b/apps/etherscan/src/styles.css new file mode 100644 index 0000000000..90d4ee0072 --- /dev/null +++ b/apps/etherscan/src/styles.css @@ -0,0 +1 @@ +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/etherscan/tsconfig.app.json b/apps/etherscan/tsconfig.app.json new file mode 100644 index 0000000000..62d6d52c3d --- /dev/null +++ b/apps/etherscan/tsconfig.app.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": ["node"] + }, + "files": [ + + "../../node_modules/@nrwl/react/typings/cssmodule.d.ts", + "../../node_modules/@nrwl/react/typings/image.d.ts" + ], + "exclude": ["**/*.spec.ts", "**/*.spec.tsx"], + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] +} diff --git a/apps/etherscan/tsconfig.json b/apps/etherscan/tsconfig.json new file mode 100644 index 0000000000..a3e71f89f3 --- /dev/null +++ b/apps/etherscan/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + } + ] +} \ No newline at end of file diff --git a/apps/remix-ide-e2e/nightwatch.ts b/apps/remix-ide-e2e/nightwatch.ts index 7ca74fd2a7..3f59c1549e 100644 --- a/apps/remix-ide-e2e/nightwatch.ts +++ b/apps/remix-ide-e2e/nightwatch.ts @@ -29,7 +29,13 @@ module.exports = { javascriptEnabled: true, acceptSslCerts: true, 'goog:chromeOptions': { - args: ['window-size=2560,1440', 'start-fullscreen', '--no-sandbox', '--headless', '--verbose'] + args: ['window-size=2560,1440', + 'start-fullscreen', + '--no-sandbox', + '--headless', + '--verbose', + "--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36", + ] } } }, diff --git a/apps/remix-ide-e2e/src/commands/getEditorValue.ts b/apps/remix-ide-e2e/src/commands/getEditorValue.ts index 0c00a0450d..fb6c78e31e 100644 --- a/apps/remix-ide-e2e/src/commands/getEditorValue.ts +++ b/apps/remix-ide-e2e/src/commands/getEditorValue.ts @@ -9,11 +9,10 @@ class GetEditorValue extends EventEmitter { return elem.currentContent() }, [], (result) => { - done() const value = typeof result.value === 'string' ? result.value : null - callback(value) this.emit('complete') + done() }) }) return this diff --git a/apps/remix-ide-e2e/src/commands/journalChildIncludes.ts b/apps/remix-ide-e2e/src/commands/journalChildIncludes.ts index 689be3bf34..d5a23a3968 100644 --- a/apps/remix-ide-e2e/src/commands/journalChildIncludes.ts +++ b/apps/remix-ide-e2e/src/commands/journalChildIncludes.ts @@ -1,4 +1,4 @@ -import { NightwatchBrowser } from 'nightwatch' +import { ELEMENT_KEY, NightwatchBrowser } from 'nightwatch' import EventEmitter from 'events' /* @@ -11,7 +11,7 @@ class JournalChildIncludes extends EventEmitter { let occurence = 0 this.api.elements('css selector', '*[data-id="terminalJournal"]', (res) => { Array.isArray(res.value) && res.value.forEach(function (jsonWebElement) { - const jsonWebElementId = jsonWebElement.ELEMENT || jsonWebElement[Object.keys(jsonWebElement)[0]] + const jsonWebElementId = jsonWebElement[ELEMENT_KEY] || jsonWebElement[Object.keys(jsonWebElement)[0]] browser.elementIdText(jsonWebElementId, (jsonElement) => { const text = jsonElement.value diff --git a/apps/remix-ide-e2e/src/commands/openFile.ts b/apps/remix-ide-e2e/src/commands/openFile.ts index ad5ec6d06e..8a40c200d5 100644 --- a/apps/remix-ide-e2e/src/commands/openFile.ts +++ b/apps/remix-ide-e2e/src/commands/openFile.ts @@ -23,10 +23,14 @@ function openFile (browser: NightwatchBrowser, name: string, done: VoidFunction) browser.element('css selector', '[data-id="verticalIconsKindfilePanel"] img[data-id="selected"]', (result) => { if (result.status === 0) { done() - } else browser.clickLaunchIcon('filePanel').perform(done()) + } else browser.clickLaunchIcon('filePanel').perform(() => { + done() + }) }) } else { - browser.clickLaunchIcon('filePanel').perform(done()) + browser.clickLaunchIcon('filePanel').perform(() => { + done() + }) } }) diff --git a/apps/remix-ide-e2e/src/commands/switchWorkspace.ts b/apps/remix-ide-e2e/src/commands/switchWorkspace.ts index 6219ddd218..558178b07d 100644 --- a/apps/remix-ide-e2e/src/commands/switchWorkspace.ts +++ b/apps/remix-ide-e2e/src/commands/switchWorkspace.ts @@ -6,7 +6,9 @@ class switchWorkspace extends EventEmitter { this.api.waitForElementVisible('[data-id="workspacesSelect"]') .click('[data-id="workspacesSelect"]') .waitForElementVisible(`[data-id="dropdown-item-${workspaceName}"]`) + .pause(2000) .click(`[data-id="dropdown-item-${workspaceName}"]`) + .pause(3000) .perform((done) => { done() this.emit('complete') @@ -15,4 +17,4 @@ class switchWorkspace extends EventEmitter { } } -module.exports = switchWorkspace +module.exports = switchWorkspace \ No newline at end of file diff --git a/apps/remix-ide-e2e/src/commands/testFunction.ts b/apps/remix-ide-e2e/src/commands/testFunction.ts index 371fec9568..5429a79537 100644 --- a/apps/remix-ide-e2e/src/commands/testFunction.ts +++ b/apps/remix-ide-e2e/src/commands/testFunction.ts @@ -1,4 +1,4 @@ -import { NightwatchBrowser, NightwatchTestFunctionExpectedInput } from 'nightwatch' +import { ELEMENT_KEY, NightwatchBrowser, NightwatchTestFunctionExpectedInput } from 'nightwatch' import EventEmitter from 'events' const deepequal = require('deep-equal') @@ -22,14 +22,17 @@ class TestFunction extends EventEmitter { }) }) .perform((done) => { - browser.waitForElementVisible(`[data-id="block_tx${txHash}"]`, 60000) + browser + .waitForElementVisible(`[data-id="block_tx${txHash}"]`, 60000) + .moveToElement(`[data-id="block_tx${txHash}"]`, 0, 0) + .pause(2000) .click(`[data-id="block_tx${txHash}"]`) .pause(3000) .waitForElementVisible(`*[data-id="txLoggerTable${txHash}"]`, 60000) // fetch and format transaction logs as key => pair object .elements('css selector', `*[data-shared="key_${txHash}"]`, (res) => { Array.isArray(res.value) && res.value.forEach(function (jsonWebElement) { - const jsonWebElementId: string = jsonWebElement.ELEMENT || jsonWebElement[Object.keys(jsonWebElement)[0]] + const jsonWebElementId: string = jsonWebElement[ELEMENT_KEY] || jsonWebElement[Object.keys(jsonWebElement)[0]] browser.elementIdText(jsonWebElementId, (jsonElement) => { const key = typeof jsonElement.value === 'string' ? jsonElement.value.trim() : null @@ -40,7 +43,7 @@ class TestFunction extends EventEmitter { }) .elements('css selector', `*[data-shared="pair_${txHash}"]`, (res) => { Array.isArray(res.value) && res.value.forEach(function (jsonWebElement, index) { - const jsonWebElementId = jsonWebElement.ELEMENT || jsonWebElement[Object.keys(jsonWebElement)[0]] + const jsonWebElementId = jsonWebElement[ELEMENT_KEY] || jsonWebElement[Object.keys(jsonWebElement)[0]] browser.elementIdText(jsonWebElementId, (jsonElement) => { let value = jsonElement.value diff --git a/apps/remix-ide-e2e/src/helpers/init.ts b/apps/remix-ide-e2e/src/helpers/init.ts index 8b56b54b5a..ddf8c13917 100644 --- a/apps/remix-ide-e2e/src/helpers/init.ts +++ b/apps/remix-ide-e2e/src/helpers/init.ts @@ -2,13 +2,28 @@ import { NightwatchBrowser } from 'nightwatch' require('dotenv').config() -export default function (browser: NightwatchBrowser, callback: VoidFunction, url?: string, preloadPlugins = true): void { +type LoadPlugin = { + name: string + url: string +} + +export default function (browser: NightwatchBrowser, callback: VoidFunction, url?: string, preloadPlugins = true, loadPlugin?: LoadPlugin): void { browser .url(url || 'http://127.0.0.1:8080') .pause(6000) .switchBrowserTab(0) .waitForElementVisible('[id="remixTourSkipbtn"]') .click('[id="remixTourSkipbtn"]') + .perform((done) => { + if (!loadPlugin) return done() + browser.execute(function (loadPlugin) { // override a plugin url for testing purpose + localStorage.setItem('test-plugin-name', loadPlugin.name) + localStorage.setItem('test-plugin-url', loadPlugin.url) + }, [loadPlugin]) + .refresh() + .pause(6000) + .perform(done()) + }) .maximizeWindow() .fullscreenWindow(() => { if (preloadPlugins) { diff --git a/apps/remix-ide-e2e/src/select_tests.sh b/apps/remix-ide-e2e/src/select_tests.sh index ee1c4e38be..ae6a7aeb60 100644 --- a/apps/remix-ide-e2e/src/select_tests.sh +++ b/apps/remix-ide-e2e/src/select_tests.sh @@ -3,7 +3,7 @@ # Bash Menu Script Example PS3='Select a browser: ' -BROWSERS=( "chrome" "firefox" "exit" ) +BROWSERS=( "chrome" "chrome headless" "firefox" "exit" ) select opt in "${BROWSERS[@]}" do case $opt in @@ -12,6 +12,11 @@ do BROWSER="chromeDesktop" break ;; + "chrome headless") + echo "Chrome headless selected" + BROWSER="chrome" + break + ;; "firefox") echo "Firefox selected" BROWSER="firefoxDesktop" diff --git a/apps/remix-ide-e2e/src/tests/ballot.test.ts b/apps/remix-ide-e2e/src/tests/ballot.test.ts index 1c8383f047..fc090988e1 100644 --- a/apps/remix-ide-e2e/src/tests/ballot.test.ts +++ b/apps/remix-ide-e2e/src/tests/ballot.test.ts @@ -134,6 +134,23 @@ module.exports = { .sendKeys('*[data-id$="scConfigFilePathInput"]', browser.Keys.ENTER) .openFile('Untitled.sol') .verifyContracts(['Ballot'], {wait: 2000, runs: '300'}) + }, + + 'Compile and deploy sample yul file': function (browser: NightwatchBrowser) { + browser + .addFile('sample.yul', {content: yulSample}) + .clickLaunchIcon('solidity') + .waitForElementVisible('*[data-id="scConfigExpander"]') + .click('*[data-id="scManualConfiguration"]') + .waitForElementVisible('select[id="compilierLanguageSelector"]', 10000) + .click('select[id="compilierLanguageSelector"]') + .click('select[id="compilierLanguageSelector"] option[value=Yul]') + .waitForElementContainsText('[data-id="compiledContracts"]', 'Contract', 60000) + .clickLaunchIcon('udapp') + .click('*[data-id="Deploy - transact (not payable)"]') + .waitForElementPresent('*[data-id="universalDappUiContractActionWrapper"]', 60000) + .journalLastChildIncludes('Contract.(constructor)') + .journalLastChildIncludes('data: 0x602...0565b') .end() } } @@ -387,4 +404,19 @@ const configFile = ` "evmVersion": "byzantium" } } +` + +const yulSample = ` +object "Contract" { + code { + function power(base, exponent) -> result + { + result := 1 + for { let i := 0 } lt(i, exponent) { i := add(i, 1) } + { + result := mul(result, base) + } + } + } +} ` \ No newline at end of file diff --git a/apps/remix-ide-e2e/src/tests/editor_line_text.test.ts b/apps/remix-ide-e2e/src/tests/editor_line_text.test.ts new file mode 100644 index 0000000000..afff1c8f28 --- /dev/null +++ b/apps/remix-ide-e2e/src/tests/editor_line_text.test.ts @@ -0,0 +1,69 @@ +'use strict' + +import { NightwatchBrowser } from 'nightwatch' +import init from '../helpers/init' + +module.exports = { + + before: function (browser: NightwatchBrowser, done: VoidFunction) { + init(browser, done, 'http://127.0.0.1:8080', true) + }, + 'Should add line texts': function (browser: NightwatchBrowser) { + browser + .openFile('contracts') + .openFile('contracts/1_Storage.sol') + .addFile('scripts/addlinetext.ts', {content: addLineText}) + .pause(4000) + .executeScriptInTerminal('remix.exeCurrent()') + .pause(4000) + .openFile('contracts/1_Storage.sol') + .useXpath() + .waitForElementVisible("//*[@class='view-line' and contains(.,'contract')]//span//span[contains(.,'mylinetext1')]") + .waitForElementVisible("//*[@class='view-line' and contains(.,'function')]//span//span[contains(.,'mylinetext2')]") + } +} + +const addLineText = ` +(async () => { + + await remix.call('editor', 'discardLineTexts' as any) + let linetext = { + content: 'mylinetext1', + position: { + start: { + line: 9, + column: 1, + } + }, + hide: false, + className: 'text-muted small', + afterContentClassName: 'text-muted small fas fa-gas-pump pl-4', + hoverMessage: [{ + value: 'hovering1', + }, + ], + } + + await remix.call('editor', 'addLineText' as any, linetext, 'contracts/1_Storage.sol') + + + linetext = { + content: 'mylinetext2', + position: { + start: { + line: 17, + column: 1, + } + }, + hide: false, + className: 'text-muted small', + afterContentClassName: 'text-muted small fas fa-gas-pump pl-4', + hoverMessage: [{ + value: 'hovering2', + }, + ], + } + + await remix.call('editor', 'addLineText' as any, linetext, 'contracts/1_Storage.sol') + +})()` \ No newline at end of file diff --git a/apps/remix-ide-e2e/src/tests/etherscan_api.ts b/apps/remix-ide-e2e/src/tests/etherscan_api.ts new file mode 100644 index 0000000000..7089d5b563 --- /dev/null +++ b/apps/remix-ide-e2e/src/tests/etherscan_api.ts @@ -0,0 +1,107 @@ +'use strict' +import { NightwatchBrowser } from 'nightwatch' +import init from '../helpers/init' + +declare global { + interface Window { testplugin: { name: string, url: string }; } +} + +module.exports = { + '@disabled': true, + before: function (browser: NightwatchBrowser, done: VoidFunction) { + init(browser, done, null, true, { name: 'etherscan', url: 'http://127.0.0.1:5003'}) + }, + + 'Should load etherscan plugin #group1': function (browser: NightwatchBrowser) { + browser.clickLaunchIcon('pluginManager') + .scrollAndClick('[data-id="pluginManagerComponentActivateButtonetherscan"]') + .clickLaunchIcon('etherscan') + .pause(5000) + // @ts-ignore + .frame(0) + .waitForElementVisible('input[name="apiKey"]') + .setValue('input[name="apiKey"]', '2HKUX5ZVASZIKWJM8MIQVCRUVZ6JAWT531') + .click('[data-id="save-api-key"]') + }, + + 'Should verify a contract (contract is already verified) #group1': function (browser: NightwatchBrowser) { + browser + .frameParent() + .clickLaunchIcon('udapp') // switch to Goerli + .switchEnvironment('External Http Provider') + .waitForElementPresent('[data-id="basic-http-provider-modal-footer-ok-react"]') + .execute(() => { + (document.querySelector('*[data-id="basic-http-providerModalDialogContainer-react"] input[data-id="modalDialogCustomPromp"]') as any).focus() + }, [], () => {}) + .setValue('[data-id="modalDialogCustomPromp"]', 'https://remix-goerli.ethdevops.io') + .modalFooterOKClick('basic-http-provider') + .clickLaunchIcon('solidity') // compile + .testContracts('Owner_1.sol', { content: verifiedContract }, ['Owner']) + .clickLaunchIcon('etherscan') // start etherscan verification + // @ts-ignore + .frame(0) + .click('[data-id="home"]') + .setValue('select[name="contractName"]', 'Owner') + .setValue('*[name="contractAddress"]', '0x9981c9d00103da481c3c65b22a79582a3e3ff50b') + .click('[data-id="verify-contract"]') + .waitForElementVisible('[data-id="verify-result"]') + .waitForElementContainsText('[data-id="verify-result"]', 'Contract source code already verified') + } +} + +const verifiedContract = ` +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.7.0 <0.9.0; + +/** + * @title Owner + * @dev Set & change owner + */ +contract Owner { + + address private owner; + + // event for EVM logging + event OwnerSet(address indexed oldOwner, address indexed newOwner); + + // modifier to check if caller is owner + modifier isOwner() { + // If the first argument of 'require' evaluates to 'false', execution terminates and all + // changes to the state and to Ether balances are reverted. + // This used to consume all gas in old EVM versions, but not anymore. + // It is often a good idea to use 'require' to check if functions are called correctly. + // As a second argument, you can also provide an explanation about what went wrong. + require(msg.sender == owner, "Caller is not owner"); + _; + } + + function getInt() public returns (uint) { + return 123498; + } + + /** + * @dev Set contract deployer as owner + */ + constructor() { + owner = msg.sender; // 'msg.sender' is sender of current call, contract deployer for a constructor + emit OwnerSet(address(0), owner); + } + + /** + * @dev Change owner + * @param newOwner address of new owner + */ + function changeOwner(address newOwner) public isOwner { + emit OwnerSet(owner, newOwner); + owner = newOwner; + } + + /** + * @dev Return owner address + * @return address of owner + */ + function getOwner() external view returns (address) { + return owner; + } +}` diff --git a/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts b/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts index 0a15009a89..49a1cf8686 100644 --- a/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts +++ b/apps/remix-ide-e2e/src/tests/fileExplorer.test.ts @@ -99,15 +99,16 @@ module.exports = { }) }, - 'Should open local filesystem explorer #group2': !function (browser: NightwatchBrowser) { - browser.waitForElementVisible('div[data-id="remixIdeSidePanel"]') + 'Should open local filesystem explorer #group2': function (browser: NightwatchBrowser) { + browser + .waitForElementVisible('div[data-id="remixIdeSidePanel"]') .clickLaunchIcon('filePanel') .waitForElementVisible('*[data-id="filePanelFileExplorerTree"]') .click('[data-id="remixUIWorkspaceExplorer"]') .waitForElementPresent('*[data-id="fileExplorerFileUpload"]') - .setValue('*[data-id="fileExplorerFileUpload"]', testData.testFile1) - .setValue('*[data-id="fileExplorerFileUpload"]', testData.testFile2) - .setValue('*[data-id="fileExplorerFileUpload"]', testData.testFile3) + .uploadFile('*[data-id="fileExplorerFileUpload"]', testData.testFile1) + .uploadFile('*[data-id="fileExplorerFileUpload"]', testData.testFile2) + .uploadFile('*[data-id="fileExplorerFileUpload"]', testData.testFile3) .waitForElementVisible('[data-id="treeViewLitreeViewItemeditor.test.js"]') .waitForElementVisible('[data-id="treeViewLitreeViewItemfileExplorer.test.js"]') .waitForElementVisible('[data-id="treeViewLitreeViewItemgeneralSettings.test.js"]') diff --git a/apps/remix-ide-e2e/src/tests/file_decorator.test.ts b/apps/remix-ide-e2e/src/tests/file_decorator.test.ts index 2aa226397c..3483853cad 100644 --- a/apps/remix-ide-e2e/src/tests/file_decorator.test.ts +++ b/apps/remix-ide-e2e/src/tests/file_decorator.test.ts @@ -11,31 +11,50 @@ module.exports = { 'Test decorators with script': function (browser: NightwatchBrowser) { browser - .openFile('contracts') - .openFile('contracts/2_Owner.sol') - .openFile('contracts/1_Storage.sol') - .openFile('contracts/3_Ballot.sol') - .addFile('scripts/decorators.ts', { content: testScript }) - .pause(2000) - .executeScript('remix.exeCurrent()') - .pause(4000) - .useXpath() - .waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-error-contracts/2_Owner.sol"]', '2') - .waitForElementContainsText('//*[@class="mainview"]//*[@data-id="file-decoration-error-contracts/2_Owner.sol"]', '2') - .waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-custom-contracts/2_Owner.sol"]', 'U') - .waitForElementContainsText('//*[@class="mainview"]//*[@data-id="file-decoration-custom-contracts/2_Owner.sol"]', 'U') - .waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-warning-contracts/1_Storage.sol"]', '2') - .waitForElementContainsText('//*[@class="mainview"]//*[@data-id="file-decoration-warning-contracts/1_Storage.sol"]', '2') - .useCss() - .waitForElementNotPresent('[data-id="file-decoration-custom-contracts/3_Ballot.sol"]', 10000) - .useXpath() - .moveToElement('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-error-contracts/2_Owner.sol"]', 0,0) - .waitForElementVisible('//*[@id="error-tooltip-contracts/2_Owner.sol"]') - .waitForElementContainsText('//*[@id="error-tooltip-contracts/2_Owner.sol"]', 'error on owner') + .openFile('contracts') + .openFile('contracts/2_Owner.sol') + .openFile('contracts/1_Storage.sol') + .openFile('contracts/3_Ballot.sol') + .addFile('scripts/decorators.ts', { content: testScriptSet }) + .pause(2000) + .executeScriptInTerminal('remix.exeCurrent()') + .pause(4000) + .useXpath() + .waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-error-contracts/2_Owner.sol"]', '2') + .waitForElementContainsText('//*[@class="mainview"]//*[@data-id="file-decoration-error-contracts/2_Owner.sol"]', '2') + .waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-custom-contracts/2_Owner.sol"]', 'U') + .waitForElementContainsText('//*[@class="mainview"]//*[@data-id="file-decoration-custom-contracts/2_Owner.sol"]', 'U') + .waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-warning-contracts/1_Storage.sol"]', '2') + .waitForElementContainsText('//*[@class="mainview"]//*[@data-id="file-decoration-warning-contracts/1_Storage.sol"]', '2') + .waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-custom-contracts/3_Ballot.sol"]', 'customtext') + .waitForElementContainsText('//*[@class="mainview"]//*[@data-id="file-decoration-custom-contracts/3_Ballot.sol"]', 'customtext') + .moveToElement('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-error-contracts/2_Owner.sol"]', 0, 0) + .waitForElementVisible('//*[@id="error-tooltip-contracts/2_Owner.sol"]') + .waitForElementContainsText('//*[@id="error-tooltip-contracts/2_Owner.sol"]', 'error on owner') + }, + + 'clear ballot decorator': function (browser: NightwatchBrowser) { + browser + .useCss() + .addFile('scripts/clearballot.ts', { content: testScriptClearBallot }) + .pause(2000) + .executeScriptInTerminal('remix.exeCurrent()') + .pause(4000) + .waitForElementNotPresent('[data-id="file-decoration-custom-contracts/3_Ballot.sol"]', 10000) + }, + 'clear all decorators': function (browser: NightwatchBrowser) { + browser + .addFile('scripts/clearall.ts', { content: testScriptClear }) + .pause(2000) + .executeScriptInTerminal('remix.exeCurrent()') + .pause(4000) + .waitForElementNotPresent('[data-id="file-decoration-error-contracts/2_Owner.sol"]', 10000) + .waitForElementNotPresent('[data-id="file-decoration-warning-contracts/1_Storage.sol"]', 10000) } + } -const testScript = ` +const testScriptSet = ` (async () => { remix.call('fileDecorator' as any, 'clearFileDecorators') let decorator: any = { @@ -46,7 +65,6 @@ const testScript = ` fileStateIconClass: '', fileStateIcon: '', text: '2', - owner: 'code-parser', bubble: true, comment: 'error on owner', } @@ -58,11 +76,10 @@ const testScript = ` fileStateIconClass: 'text-success', fileStateIcon: 'U', text: '', - owner: 'code-parser', bubble: true, comment: 'modified', } - remix.call('fileDecorator' as any, 'setFileDecorators', [decorator, decorator2]) + await remix.call('fileDecorator' as any, 'setFileDecorators', [decorator, decorator2]) decorator = { path: 'contracts/1_Storage.sol', @@ -72,11 +89,10 @@ const testScript = ` fileStateIconClass: '', fileStateIcon: '', text: '2', - owner: 'code-parser', bubble: true, comment: 'warning on storage', } - remix.call('fileDecorator' as any, 'setFileDecorators', decorator) + await remix.call('fileDecorator' as any, 'setFileDecorators', decorator) decorator = { path: 'contracts/3_Ballot.sol', @@ -85,13 +101,25 @@ const testScript = ` fileStateLabelClass: '', fileStateIconClass: '', fileStateIcon: 'customtext', - text: 'w', - owner: 'dgit', + text: 'with text', bubble: true, - comment: '', + comment: 'custom comment', } - remix.call('fileDecorator' as any, 'setFileDecorators', decorator) - - remix.call('fileDecorator' as any, 'clearFileDecorators', 'dgit') + await remix.call('fileDecorator' as any, 'setFileDecorators', decorator) - })()` \ No newline at end of file + })()` + + +const testScriptClearBallot = ` + (async () => { + + await remix.call('fileDecorator' as any, 'clearFileDecorators', 'contracts/3_Ballot.sol') + + })()` + +const testScriptClear = ` + (async () => { + await remix.call('fileDecorator' as any, 'clearAllFileDecorators') + + + })()` \ No newline at end of file diff --git a/apps/remix-ide-e2e/src/tests/pluginManager.test.ts b/apps/remix-ide-e2e/src/tests/pluginManager.test.ts index 48f3ae29d8..ab89372ad6 100644 --- a/apps/remix-ide-e2e/src/tests/pluginManager.test.ts +++ b/apps/remix-ide-e2e/src/tests/pluginManager.test.ts @@ -109,7 +109,7 @@ module.exports = { 'Should load back installed plugins after reload': function (browser: NightwatchBrowser) { browser - .waitForElementVisible('*[data-id="remixIdeSidePanel"]') + .waitForElementVisible('*[data-id="remixIdeSidePanel"]',3000) .waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]') .getInstalledPlugins((plugins) => { browser.refresh() diff --git a/apps/remix-ide-e2e/src/tests/publishContract.test.ts b/apps/remix-ide-e2e/src/tests/publishContract.test.ts index 38ee1c3e54..2545a53808 100644 --- a/apps/remix-ide-e2e/src/tests/publishContract.test.ts +++ b/apps/remix-ide-e2e/src/tests/publishContract.test.ts @@ -3,6 +3,7 @@ import { NightwatchBrowser } from 'nightwatch' import init from '../helpers/init' module.exports = { + '@disabled': true, before: function (browser: NightwatchBrowser, done: VoidFunction) { init(browser, done) }, diff --git a/apps/remix-ide-e2e/src/tests/remixd.test.ts b/apps/remix-ide-e2e/src/tests/remixd.test.ts index 6cf256e2cc..8f4428b2f3 100644 --- a/apps/remix-ide-e2e/src/tests/remixd.test.ts +++ b/apps/remix-ide-e2e/src/tests/remixd.test.ts @@ -55,6 +55,7 @@ module.exports = { }, 'start Remixd': function (browser) { startRemixd(browser) + }, 'run Remixd tests #group4': function (browser) { runTests(browser) @@ -131,6 +132,7 @@ function startRemixd (browser: NightwatchBrowser) { .waitForElementVisible('*[data-id="remixdConnect-modal-footer-ok-react"]', 2000) .pause(2000) .click('*[data-id="remixdConnect-modal-footer-ok-react"]') + .pause(10000) // .click('*[data-id="workspacesModalDialog-modal-footer-ok-react"]') } diff --git a/apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts b/apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts index dceb54a420..0d8df82b96 100644 --- a/apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts +++ b/apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts @@ -65,6 +65,29 @@ module.exports = { }) }, + 'Should show and update balance for deployed contract on JavascriptVM #group3': function (browser: NightwatchBrowser) { + let instanceAddress + browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]') + .clickLaunchIcon('filePanel') + .addFile('checkBalance.sol', sources[0]['checkBalance.sol']) + .clickLaunchIcon('udapp') + .setValue('*[data-id="dandrValue"]', '111') + .waitForElementVisible('*[data-id="Deploy - transact (payable)"]', 45000) + .click('*[data-id="Deploy - transact (payable)"]') + .pause(1000) + .clickInstance(1) + .pause(1000) + .getAddressAtPosition(1, (address) => { + instanceAddress = address + browser + .waitForElementVisible(`#instance${instanceAddress} [data-id="instanceContractBal"]`) + .assert.containsText(`#instance${instanceAddress} [data-id="instanceContractBal"]`, 'Balance: 0.000000000000000111 ETH') + .clickFunction('sendSomeEther - transact (not payable)', { types: 'uint256 num', values: '2' }) + .pause(1000) + .assert.containsText(`#instance${instanceAddress} [data-id="instanceContractBal"]`, 'Balance: 0.000000000000000109 ETH') + }) + }, + 'Should run low level interaction (fallback function) #group3': function (browser: NightwatchBrowser) { browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]') .clickInstance(0) @@ -80,7 +103,7 @@ module.exports = { .end() }, - 'Should connect to Goerli Test Network using MetaMask': '' + function (browser: NightwatchBrowser) { + 'Should connect to Goerli Test Network using MetaMask': !function (browser: NightwatchBrowser) { browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]') .setupMetamask(passphrase, password) .click('.network-indicator__down-arrow') @@ -101,7 +124,7 @@ module.exports = { .switchBrowserTab(0) }, - 'Should deploy contract on Goerli Test Network using MetaMask': '' + function (browser: NightwatchBrowser) { + 'Should deploy contract on Goerli Test Network using MetaMask': !function (browser: NightwatchBrowser) { browser.waitForElementPresent('*[data-id="runTabSelectAccount"] option') .clickLaunchIcon('filePanel') .openFile('Greet.sol') @@ -118,7 +141,7 @@ module.exports = { .switchBrowserTab(0) }, - 'Should run low level interaction (fallback function) on Goerli Test Network using MetaMask': '' + function (browser: NightwatchBrowser) { + 'Should run low level interaction (fallback function) on Goerli Test Network using MetaMask': !function (browser: NightwatchBrowser) { browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]') .waitForElementPresent('*[data-id="universalDappUiTitleExpander"]') .click('*[data-id="universalDappUiTitleExpander"]') @@ -135,7 +158,7 @@ module.exports = { .end() }, - 'Should connect to Ethereum Main Network using MetaMask': '' + function (browser: NightwatchBrowser) { + 'Should connect to Ethereum Main Network using MetaMask': !function (browser: NightwatchBrowser) { browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]') .switchBrowserTab(2) .waitForElementPresent('.network-indicator__down-arrow') @@ -153,7 +176,7 @@ module.exports = { .assert.containsText('*[data-id="settingsNetworkEnv"]', 'Main (1) network') }, - 'Should deploy contract on Ethereum Main Network using MetaMask': '' + function (browser: NightwatchBrowser) { + 'Should deploy contract on Ethereum Main Network using MetaMask': !function (browser: NightwatchBrowser) { browser.waitForElementPresent('*[data-id="runTabSelectAccount"] option') .clickLaunchIcon('filePanel') .openFile('Greet.sol') @@ -173,7 +196,7 @@ module.exports = { * - Ropsten node for retrieving the trace and storage * */ - 'Should debug Ropsten transaction with source highlighting using the source verifier service and MetaMask': '' + function (browser: NightwatchBrowser) { + 'Should debug Ropsten transaction with source highlighting using the source verifier service and MetaMask': !function (browser: NightwatchBrowser) { browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]') .waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) .switchBrowserTab(2) @@ -198,7 +221,7 @@ module.exports = { .assert.containsText('*[data-id="solidityLocals"]', 'to: 0x6C3CCC7FBA111707D5A1AAF2758E9D4F4AC5E7B1') }, - 'Call web3.eth.getAccounts() using Injected Provider (Metamask)': '' + function (browser: NightwatchBrowser) { + 'Call web3.eth.getAccounts() using Injected Provider (Metamask)': !function (browser: NightwatchBrowser) { browser .executeScriptInTerminal('web3.eth.getAccounts()') .pause(2000) @@ -224,6 +247,17 @@ const sources = [ message = _message; } }` + }, + 'checkBalance.sol': { + content: `pragma solidity ^0.8.0; + contract checkBalance { + constructor () payable {} + + function sendSomeEther(uint256 num) public { + payable(msg.sender).transfer(num); + } + + }` } } ] diff --git a/apps/remix-ide-e2e/src/tests/search.test.ts b/apps/remix-ide-e2e/src/tests/search.test.ts index 0c1f2110c0..c91531d587 100644 --- a/apps/remix-ide-e2e/src/tests/search.test.ts +++ b/apps/remix-ide-e2e/src/tests/search.test.ts @@ -4,10 +4,11 @@ import { NightwatchBrowser } from 'nightwatch' import init from '../helpers/init' module.exports = { + '@disabled': true, before: function (browser: NightwatchBrowser, done: VoidFunction) { init(browser, done, 'http://127.0.0.1:8080', true) }, - 'Should find text': function (browser: NightwatchBrowser) { + 'Should find text #group1': function (browser: NightwatchBrowser) { browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]') .click('*[plugin="search"]').waitForElementVisible('*[id="search_input"]') .waitForElementVisible('*[id="search_include"]') @@ -26,7 +27,7 @@ module.exports = { Array.isArray(res.value) && browser.assert.equal(res.value.length, 6) }) }, - 'Should find text with exclude': function (browser: NightwatchBrowser) { + 'Should find text with exclude #group1': function (browser: NightwatchBrowser) { browser .clearValue('*[id="search_input"]') .setValue('*[id="search_input"]', 'contract').pause(1000) @@ -42,7 +43,7 @@ module.exports = { .clearValue('*[id="search_include"]').setValue('*[id="search_include"]', '*.sol, *.js, *.txt') .clearValue('*[id="search_exclude"]').setValue('*[id="search_exclude"]', '.*/**/*') }, - 'Should find regex': function (browser: NightwatchBrowser) { + 'Should find regex #group1': function (browser: NightwatchBrowser) { browser .waitForElementVisible('*[id="search_input"]') .clearValue('*[id="search_input"]').pause(2000) @@ -57,7 +58,7 @@ module.exports = { Array.isArray(res.value) && browser.assert.equal(res.value.length, 4) }) }, - 'Should find matchcase': function (browser: NightwatchBrowser) { + 'Should find matchcase #group1': function (browser: NightwatchBrowser) { browser .waitForElementVisible('*[data-id="search_use_regex"]').click('*[data-id="search_use_regex"]') .waitForElementVisible('*[data-id="search_case_sensitive"]').click('*[data-id="search_case_sensitive"]').pause(4000) @@ -71,7 +72,7 @@ module.exports = { }) .waitForElementContainsText('*[data-id="search_results"]', 'STORAGE.TEST.JS', 60000) }, - 'Should find matchword': function (browser: NightwatchBrowser) { + 'Should find matchword #group1': function (browser: NightwatchBrowser) { browser .waitForElementVisible('*[data-id="search_case_sensitive"]').click('*[data-id="search_case_sensitive"]') .waitForElementVisible('*[data-id="search_whole_word"]').click('*[data-id="search_whole_word"]').pause(2000) @@ -81,7 +82,7 @@ module.exports = { Array.isArray(res.value) && browser.assert.equal(res.value.length, 15) }) }, - 'Should replace text': function (browser: NightwatchBrowser) { + 'Should replace text #group1': function (browser: NightwatchBrowser) { browser .waitForElementVisible('*[data-id="toggle_replace"]').click('*[data-id="toggle_replace"]') .waitForElementVisible('*[id="search_replace"]') @@ -95,7 +96,7 @@ module.exports = { browser.assert.ok(content.includes('replacing deployer for a constructor'), 'should replace text ok') }) }, - 'Should replace text without confirmation': function (browser: NightwatchBrowser) { + 'Should replace text without confirmation #group1': function (browser: NightwatchBrowser) { browser.click('*[data-id="confirm_replace_label"]').pause(500) .clearValue('*[id="search_input"]') .setValue('*[id="search_input"]', 'replacing').sendKeys('*[id="search_input"]', browser.Keys.ENTER).pause(1000) @@ -108,7 +109,7 @@ module.exports = { browser.assert.ok(content.includes('replacing2 deployer for a constructor'), 'should replace text ok') }) }, - 'Should replace all & undo': function (browser: NightwatchBrowser) { + 'Should replace all & undo #group1': function (browser: NightwatchBrowser) { browser .clearValue('*[id="search_input"]') .setValue('*[id="search_input"]', 'storage').sendKeys('*[id="search_input"]', browser.Keys.ENTER) @@ -127,7 +128,7 @@ module.exports = { browser.assert.ok(content.includes('title Storage'), 'should undo text ok') }) }, - 'Should replace all & undo & switch between files': function (browser: NightwatchBrowser) { + 'Should replace all & undo & switch between files #group1': function (browser: NightwatchBrowser) { browser.waitForElementVisible('*[id="search_input"]') .clearValue('*[id="search_input"]') .setValue('*[id="search_input"]', 'storage').sendKeys('*[id="search_input"]', browser.Keys.ENTER) @@ -163,14 +164,20 @@ module.exports = { browser.assert.ok(content.includes("Storage' contract"), 'should replace text ok') }) }, - 'Should hide button when edited content is the same': function (browser: NightwatchBrowser) { + 'Should hide button when edited content is the same #group2': function (browser: NightwatchBrowser) { browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]') .addFile('test.sol', { content: '123' }) - .click('*[plugin="search"]').waitForElementVisible('*[id="search_input"]') + .click('*[plugin="search"]') + .waitForElementVisible('*[id="search_input"]') + .waitForElementVisible('*[data-id="toggle_replace"]') + .click('*[data-id="toggle_replace"]') .clearValue('*[id="search_input"]') - .setValue('*[id="search_input"]', '123').sendKeys('*[id="search_input"]', browser.Keys.ENTER) + .setValue('*[id="search_input"]', '123') + .sendKeys('*[id="search_input"]', browser.Keys.ENTER) + .waitForElementVisible('*[id="search_replace"]') .clearValue('*[id="search_replace"]') .setValue('*[id="search_replace"]', '456').pause(1000) + .click('*[data-id="confirm_replace_label"]').pause(500) .waitForElementVisible('*[data-id="replace-all-test.sol"]') .click('*[data-id="replace-all-test.sol"]').pause(2000) .getEditorValue((content) => { @@ -181,13 +188,14 @@ module.exports = { .getEditorValue((content) => { browser.assert.ok(content.includes('123'), 'should have text ok') } - ).pause(1000) + ).pause(5000) .waitForElementNotPresent('*[data-id="undo-replace-test.sol"]') }, - 'Should disable/enable button when edited content changed': function (browser: NightwatchBrowser) { + 'Should disable/enable button when edited content changed #group2': function (browser: NightwatchBrowser) { browser .waitForElementVisible('*[id="search_input"]') .clearValue('*[id="search_input"]') + .clearValue('*[id="search_input"]') .setValue('*[id="search_input"]', '123').sendKeys('*[id="search_input"]', browser.Keys.ENTER) .clearValue('*[id="search_replace"]') .setValue('*[id="search_replace"]', 'replaced').pause(1000) @@ -201,7 +209,7 @@ module.exports = { .getEditorValue((content) => { browser.assert.ok(content.includes('changed'), 'should have text ok') } - ).pause(1000) + ).pause(5000) .waitForElementVisible('*[data-id="undo-replace-test.sol"]') .getAttribute('[data-id="undo-replace-test.sol"]', 'disabled', (result) => { browser.assert.equal(result.value, 'true', 'should be disabled') @@ -222,7 +230,7 @@ module.exports = { .waitForElementNotPresent('*[data-id="undo-replace-test.sol"]') }, - 'should clear search': function (browser: NightwatchBrowser) { + 'should clear search #group2': function (browser: NightwatchBrowser) { browser .waitForElementVisible('*[id="search_input"]') .setValue('*[id="search_input"]', 'nodata').sendKeys('*[id="search_input"]', browser.Keys.ENTER).pause(1000) @@ -230,4 +238,4 @@ module.exports = { Array.isArray(res.value) && browser.assert.equal(res.value.length, 0) }) } -} \ No newline at end of file +} diff --git a/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts b/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts index 7e61167107..a62a2fe932 100644 --- a/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts +++ b/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts @@ -177,10 +177,16 @@ module.exports = { // creating a new workspace .click('*[data-id="workspaceCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') - // eslint-disable-next-line dot-notation - .execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_new' }) + .click('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]') + .setValue('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_new') + .pause(2000) + .getValue('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]', (result) => { + console.log(result) + browser.assert.equal(result.value, 'workspace_new') + }) .waitForElementVisible('*[data-id="fileSystem-modal-footer-ok-react"]') - .execute(function () { (document.querySelector('[data-id="fileSystem-modal-footer-ok-react"]') as HTMLElement).click() }) + .click('*[data-id="fileSystem-modal-footer-ok-react"]') + .pause(3000) .currentWorkspaceIs('workspace_new') .waitForElementVisible('li[data-id="treeViewLitreeViewItem.deps/remix-tests/remix_tests.sol"]') .waitForElementVisible('li[data-id="treeViewLitreeViewItem.deps/remix-tests/remix_accounts.sol"]') diff --git a/apps/remix-ide-e2e/src/tests/terminal.test.ts b/apps/remix-ide-e2e/src/tests/terminal.test.ts index 50cd19e08f..f944b453f9 100644 --- a/apps/remix-ide-e2e/src/tests/terminal.test.ts +++ b/apps/remix-ide-e2e/src/tests/terminal.test.ts @@ -7,7 +7,6 @@ module.exports = { before: function (browser: NightwatchBrowser, done: VoidFunction) { init(browser, done, 'http://127.0.0.1:8080?plugins=solidity,udapp', false) }, - 'Should execution a simple console command #group1 #group999': function (browser: NightwatchBrowser) { browser .waitForElementVisible('*[data-id="terminalCli"]', 10000) @@ -164,10 +163,9 @@ module.exports = { 'Should print hardhat logs #group4': function (browser: NightwatchBrowser) { browser .click('*[data-id="terminalClearConsole"]') // clear the terminal - .addFile('printHardhatlog.sol', { content: hardhatLog }) - .clickLaunchIcon('solidity') .waitForElementVisible('[for="autoCompile"]') .click('[for="autoCompile"]') + .clickLaunchIcon('udapp') .testContracts('printHardhatlog.sol', { content: hardhatLog }, ['OwnerTest']) .clickLaunchIcon('udapp') .click('*[data-id="deployAndRunClearInstances"]') @@ -202,7 +200,31 @@ module.exports = { .sendKeys('body', [browser.Keys.CONTROL, browser.Keys.SHIFT, 's']) .pause(15000) .journalLastChildIncludes('147') - } + }, + + 'Should run a script which log transaction and block using web3.js and ethers #group7': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('udapp') + .switchEnvironment('External Http Provider') + .waitForElementPresent('[data-id="basic-http-provider-modal-footer-ok-react"]') + .execute(() => { + (document.querySelector('*[data-id="basic-http-providerModalDialogContainer-react"] input[data-id="modalDialogCustomPromp"]') as any).focus() + }, [], () => {}) + .setValue('[data-id="modalDialogCustomPromp"]', 'https://remix-goerli.ethdevops.io') + .modalFooterOKClick('basic-http-provider') + .clickLaunchIcon('filePanel') + .openFile('README.txt') + .addFile('scripts/log_tx_block.js', { content: scriptBlockAndTransaction } ) + .pause(1000) + .executeScriptInTerminal('remix.execute(\'scripts/log_tx_block.js\')') + .pause(10000) + // check if the input of the transaction is being logged (web3 call) + .journalChildIncludes('0x775526410000000000000000000000000000000000000000000000000000000000000060464c0335b2f1609abd9de25141c0a3b49db516fc7375970dc737c32b986e88e3000000000000000000000000000000000000000000000000000000000000039e000000000000000000000000000000000000000000000000000000000000000602926b30b10e7a514d92bc71e085f5bff2687fac2856ae43ef7621bf1756fa370516d310bec5727543089be9a4d5f68471174ee528e95a2520b0ca36c2b6c6eb0000000000000000000000000000000000000000000000000000000000046f49036f5e4ea4dd042801c8841e3db8e654124305da0f11824fc1db60c405dbb39f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000') + // check if the logsBloom is being logged (web3 call) + .journalChildIncludes('0x00000000000000000000000000100000000000000000020000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000040000000060000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000100000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000001') + // check if the logsBloom is being logged (ethers.js call) + .journalChildIncludes('"hex":"0x025cd8"') + } } const asyncAwait = ` @@ -607,3 +629,17 @@ const scriptAutoExec = { })() ` } + +const scriptBlockAndTransaction = ` +// Right click on the script name and hit "Run" to execute +(async () => { + try { + web3.eth.getTransaction('0x022ccd55747677ac50f8d9dfd1bf5b843fa2f36438a28c1d0a0958e057bb3e2a').then(console.log) + web3.eth.getBlock('7367447').then(console.log); + let ethersProvider = new ethers.providers.Web3Provider(web3Provider) + ethersProvider.getBlock(7367447).then(console.log) + } catch (e) { + console.log(e.message) + } +})() +` diff --git a/apps/remix-ide-e2e/src/tests/transactionExecution.test.ts b/apps/remix-ide-e2e/src/tests/transactionExecution.test.ts index 635218881f..7df23ff1f3 100644 --- a/apps/remix-ide-e2e/src/tests/transactionExecution.test.ts +++ b/apps/remix-ide-e2e/src/tests/transactionExecution.test.ts @@ -23,8 +23,8 @@ module.exports = { status: 'true Transaction mined and execution succeed', 'decoded output': { 0: 'uint256: 8' } }) - .pause(500) - .checkTerminalFilter('0x12332162e2e31397dc1e07ed0a1cf08f728e9b4487c6f9ed79d2f39410c92782', '') + .pause(120000) + //.checkTerminalFilter('0x12332162e2e31397dc1e07ed0a1cf08f728e9b4487c6f9ed79d2f39410c92782', '') .clickFunction('g - transact (not payable)') .testFunction('last', { diff --git a/apps/remix-ide-e2e/src/tests/txListener.test.ts b/apps/remix-ide-e2e/src/tests/txListener.test.ts index 41dc6882e7..8a3b5d7999 100644 --- a/apps/remix-ide-e2e/src/tests/txListener.test.ts +++ b/apps/remix-ide-e2e/src/tests/txListener.test.ts @@ -9,13 +9,14 @@ const sources = [ ] module.exports = { + '@disabled': true, before: function (browser: NightwatchBrowser, done: VoidFunction) { init(browser, done) }, '@sources': function () { return sources }, - 'The sequence: Compiling / Deploying / Compiling another contract / calling the first contract - should display in the log the transaction with all the decoded information': function (browser: NightwatchBrowser) { + 'The sequence: Compiling / Deploying / Compiling another contract / calling the first contract - should display in the log the transaction with all the decoded information #group1': function (browser: NightwatchBrowser) { // https://github.com/ethereum/remix-ide/issues/2864 browser .waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) @@ -36,8 +37,7 @@ module.exports = { .clickLaunchIcon('solidity') .testContracts('Untitled1.sol', sources[1]['Untitled1.sol'], ['test']) .clickLaunchIcon('udapp') - .clickFunction('delegate - transact (not payable)', { types: 'address to', values: '' }) - .pause(5000) + .clickFunction('delegate - transact (not payable)', { types: 'address to', values: '"0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"' }) .testFunction('last', { status: 'false Transaction mined but execution failed', diff --git a/apps/remix-ide-e2e/src/tests/url.test.ts b/apps/remix-ide-e2e/src/tests/url.test.ts index 7b94cd3abd..832dca69d0 100644 --- a/apps/remix-ide-e2e/src/tests/url.test.ts +++ b/apps/remix-ide-e2e/src/tests/url.test.ts @@ -5,10 +5,43 @@ import init from '../helpers/init' import examples from '../examples/example-contracts' const sources = [ - { 'Untitled.sol': { content: examples.ballot.content } } + { 'Untitled.sol': { content: examples.ballot.content } }, + { + 'myTokenV1.sol': { + content: ` + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.4; + + import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; + import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; + + contract MyToken is Initializable, ERC721Upgradeable, OwnableUpgradeable, UUPSUpgradeable { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize() initializer public { + __ERC721_init("MyToken", "MTK"); + __Ownable_init(); + __UUPSUpgradeable_init(); + } + + function _authorizeUpgrade(address newImplementation) + internal + onlyOwner + override + {} + } + ` + } + } ] module.exports = { + '@disabled': true, before: function (browser: NightwatchBrowser, done: VoidFunction) { init(browser, done, 'http://127.0.0.1:8080/#optimize=true&runs=300&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js', true) }, @@ -17,7 +50,7 @@ module.exports = { return sources }, - 'Should load the code from URL params (code param)': function (browser: NightwatchBrowser) { + 'Should load the code from URL params (code param) #group1': function (browser: NightwatchBrowser) { browser .waitForElementVisible('[for="autoCompile"]') .click('[for="autoCompile"]') // we set it too false in the local storage @@ -34,11 +67,11 @@ module.exports = { .getEditorValue((content) => { browser.assert.ok(content && content.indexOf( 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol') !== -1, - 'code has not been loaded') + 'code has not been loaded') }) }, - 'Should load the code from URL params (url param)': function (browser: NightwatchBrowser) { + 'Should load the code from URL params (url param) #group1': function (browser: NightwatchBrowser) { browser .pause(5000) .url('http://127.0.0.1:8080/#optimize=true&runs=300&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js&url=https://github.com/ethereum/remix-project/blob/master/apps/remix-ide/contracts/app/solidity/mode.sol') @@ -48,11 +81,40 @@ module.exports = { .getEditorValue((content) => { browser.assert.ok(content && content.indexOf( 'proposals.length = _numProposals;') !== -1, - 'url has not been loaded') + 'url has not been loaded') + }) + }, + + 'Should load Etherscan verified contractss from URL "address" param) #group2': function (browser: NightwatchBrowser) { + browser + .pause(5000) + .url('http://127.0.0.1:8080/#address=0x56db08fb78bc6689a1ef66efd079083fed0e4915') + .refresh() + .pause(7000) + .currentWorkspaceIs('etherscan-code-sample') + .assert.elementPresent('*[data-id=treeViewLitreeViewItemropsten]') + .assert.elementPresent('*[data-id=treeViewLitreeViewItemrinkeby]') + .assert.elementPresent('*[data-id="treeViewLitreeViewItemrinkeby/0x56db08fb78bc6689a1ef66efd079083fed0e4915"]') + .assert.elementPresent('*[data-id="treeViewLitreeViewItemrinkeby/0x56db08fb78bc6689a1ef66efd079083fed0e4915/Sample.sol"]') + .getEditorValue((content) => { + browser.assert.ok(content && content.indexOf( + 'contract Sample {') !== -1) + }) + .url('http://127.0.0.1:8080/#address=0xdac17f958d2ee523a2206206994597c13d831ec7') + .refresh() + .pause(7000) + .currentWorkspaceIs('etherscan-code-sample') + .assert.elementPresent('*[data-id=treeViewLitreeViewItemmainnet]') + .assert.elementPresent('*[data-id="treeViewLitreeViewItemmainnet/0xdac17f958d2ee523a2206206994597c13d831ec7"]') + .assert.elementPresent('*[data-id="treeViewLitreeViewItemmainnet/0xdac17f958d2ee523a2206206994597c13d831ec7/TetherToken.sol"]') + .getEditorValue((content) => { + browser.assert.ok(content && content.indexOf( + 'contract TetherToken is Pausable, StandardToken, BlackList {') !== -1) + }) }, - 'Should load the code from URL & code params': function (browser: NightwatchBrowser) { + 'Should load the code from URL & code params #group1': function (browser: NightwatchBrowser) { browser .pause(5000) .url('http://127.0.0.1:8080/#optimize=true&runs=300&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js&url=https://github.com/ethereum/remix-project/blob/master/apps/remix-ide/contracts/app/solidity/mode.sol&code=cHJhZ21hIHNvbGlkaXR5ID49MC42LjAgPDAuNy4wOwoKaW1wb3J0ICJodHRwczovL2dpdGh1Yi5jb20vT3BlblplcHBlbGluL29wZW56ZXBwZWxpbi1jb250cmFjdHMvYmxvYi9tYXN0ZXIvY29udHJhY3RzL2FjY2Vzcy9Pd25hYmxlLnNvbCI7Cgpjb250cmFjdCBHZXRQYWlkIGlzIE93bmFibGUgewogIGZ1bmN0aW9uIHdpdGhkcmF3KCkgZXh0ZXJuYWwgb25seU93bmVyIHsKICB9Cn0') @@ -63,7 +125,7 @@ module.exports = { .getEditorValue((content) => { browser.assert.ok(content && content.indexOf( 'proposals.length = _numProposals;') !== -1, - 'code has not been loaded') + 'code has not been loaded') }) .openFile('contract-76747f6e19.sol') .openFile('ethereum') @@ -76,7 +138,58 @@ module.exports = { .openFile('ethereum/remix-project/apps/remix-ide/contracts/app/solidity/mode.sol') }, - 'Should load using URL compiler params': function (browser: NightwatchBrowser) { + 'Should load the code from language & code params #group1': function (browser: NightwatchBrowser) { + browser + .pause(5000) + .url('http://127.0.0.1:8080/#language=yul&version=soljson-v0.8.7+commit.e28d00a7.js&code=Ly8gQSBjb250cmFjdCBjb25zaXN0cyBvZiBhIHNpbmdsZSBvYmplY3Qgd2l0aCBzdWItb2JqZWN0cyByZXByZXNlbnRpbmcKLy8gdGhlIGNvZGUgdG8gYmUgZGVwbG95ZWQgb3Igb3RoZXIgY29udHJhY3RzIGl0IGNhbiBjcmVhdGUuCi8vIFRoZSBzaW5nbGUgImNvZGUiIG5vZGUgaXMgdGhlIGV4ZWN1dGFibGUgY29kZSBvZiB0aGUgb2JqZWN0LgovLyBFdmVyeSAob3RoZXIpIG5hbWVkIG9iamVjdCBvciBkYXRhIHNlY3Rpb24gaXMgc2VyaWFsaXplZCBhbmQKLy8gbWFkZSBhY2Nlc3NpYmxlIHRvIHRoZSBzcGVjaWFsIGJ1aWx0LWluIGZ1bmN0aW9ucyBkYXRhY29weSAvIGRhdGFvZmZzZXQgLyBkYXRhc2l6ZQovLyBUaGUgY3VycmVudCBvYmplY3QsIHN1Yi1vYmplY3RzIGFuZCBkYXRhIGl0ZW1zIGluc2lkZSB0aGUgY3VycmVudCBvYmplY3QKLy8gYXJlIGluIHNjb3BlLgpvYmplY3QgIkNvbnRyYWN0MSIgewogICAgLy8gVGhpcyBpcyB0aGUgY29uc3RydWN0b3IgY29kZSBvZiB0aGUgY29udHJhY3QuCiAgICBjb2RlIHsKICAgICAgICBmdW5jdGlvbiBhbGxvY2F0ZShzaXplKSAtPiBwdHIgewogICAgICAgICAgICBwdHIgOj0gbWxvYWQoMHg0MCkKICAgICAgICAgICAgaWYgaXN6ZXJvKHB0cikgeyBwdHIgOj0gMHg2MCB9CiAgICAgICAgICAgIG1zdG9yZSgweDQwLCBhZGQocHRyLCBzaXplKSkKICAgICAgICB9CgogICAgICAgIC8vIGZpcnN0IGNyZWF0ZSAiQ29udHJhY3QyIgogICAgICAgIGxldCBzaXplIDo9IGRhdGFzaXplKCJDb250cmFjdDIiKQogICAgICAgIGxldCBvZmZzZXQgOj0gYWxsb2NhdGUoc2l6ZSkKICAgICAgICAvLyBUaGlzIHdpbGwgdHVybiBpbnRvIGNvZGVjb3B5IGZvciBFVk0KICAgICAgICBkYXRhY29weShvZmZzZXQsIGRhdGFvZmZzZXQoIkNvbnRyYWN0MiIpLCBzaXplKQogICAgICAgIC8vIGNvbnN0cnVjdG9yIHBhcmFtZXRlciBpcyBhIHNpbmdsZSBudW1iZXIgMHgxMjM0CiAgICAgICAgbXN0b3JlKGFkZChvZmZzZXQsIHNpemUpLCAweDEyMzQpCiAgICAgICAgcG9wKGNyZWF0ZShvZmZzZXQsIGFkZChzaXplLCAzMiksIDApKQoKICAgICAgICAvLyBub3cgcmV0dXJuIHRoZSBydW50aW1lIG9iamVjdCAodGhlIGN1cnJlbnRseQogICAgICAgIC8vIGV4ZWN1dGluZyBjb2RlIGlzIHRoZSBjb25zdHJ1Y3RvciBjb2RlKQogICAgICAgIHNpemUgOj0gZGF0YXNpemUoIkNvbnRyYWN0MV9kZXBsb3llZCIpCiAgICAgICAgb2Zmc2V0IDo9IGFsbG9jYXRlKHNpemUpCiAgICAgICAgLy8gVGhpcyB3aWxsIHR1cm4gaW50byBhIG1lbW9yeS0+bWVtb3J5IGNvcHkgZm9yIEV3YXNtIGFuZAogICAgICAgIC8vIGEgY29kZWNvcHkgZm9yIEVWTQogICAgICAgIGRhdGFjb3B5KG9mZnNldCwgZGF0YW9mZnNldCgiQ29udHJhY3QxX2RlcGxveWVkIiksIHNpemUpCiAgICAgICAgcmV0dXJuKG9mZnNldCwgc2l6ZSkKICAgIH0KCiAgICBkYXRhICJUYWJsZTIiIGhleCI0MTIzIgoKICAgIG9iamVjdCAiQ29udHJhY3QxX2RlcGxveWVkIiB7CiAgICAgICAgY29kZSB7CiAgICAgICAgICAgIGZ1bmN0aW9uIGFsbG9jYXRlKHNpemUpIC0+IHB0ciB7CiAgICAgICAgICAgICAgICBwdHIgOj0gbWxvYWQoMHg0MCkKICAgICAgICAgICAgICAgIGlmIGlzemVybyhwdHIpIHsgcHRyIDo9IDB4NjAgfQogICAgICAgICAgICAgICAgbXN0b3JlKDB4NDAsIGFkZChwdHIsIHNpemUpKQogICAgICAgICAgICB9CgogICAgICAgICAgICAvLyBydW50aW1lIGNvZGUKCiAgICAgICAgICAgIG1zdG9yZSgwLCAiSGVsbG8sIFdvcmxkISIpCiAgICAgICAgICAgIHJldHVybigwLCAweDIwKQogICAgICAgIH0KICAgIH0KCiAgICAvLyBFbWJlZGRlZCBvYmplY3QuIFVzZSBjYXNlIGlzIHRoYXQgdGhlIG91dHNpZGUgaXMgYSBmYWN0b3J5IGNvbnRyYWN0LAogICAgLy8gYW5kIENvbnRyYWN0MiBpcyB0aGUgY29kZSB0byBiZSBjcmVhdGVkIGJ5IHRoZSBmYWN0b3J5CiAgICBvYmplY3QgIkNvbnRyYWN0MiIgewogICAgICAgIGNvZGUgewogICAgICAgICAgICAvLyBjb2RlIGhlcmUgLi4uCiAgICAgICAgfQoKICAgICAgICBvYmplY3QgIkNvbnRyYWN0Ml9kZXBsb3llZCIgewogICAgICAgICAgICBjb2RlIHsKICAgICAgICAgICAgICAgIC8vIGNvZGUgaGVyZSAuLi4KICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgZGF0YSAiVGFibGUxIiBoZXgiNDEyMyIKICAgIH0KfQ&optimize=false&runs=200&evmVersion=null') + .refresh() + .pause(5000) + .clickLaunchIcon('filePanel') + .currentWorkspaceIs('code-sample') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontract-eaa022e37e.yul"]', 6000) + .openFile('contract-eaa022e37e.yul') + .getEditorValue((content) => { + browser.assert.ok(content && content.indexOf( + 'object "Contract1" {') !== -1) + }) + }, + + 'Should select deploy with proxy option from URL params #group1': function (browser: NightwatchBrowser) { + browser + .url('http://127.0.0.1:8080/#optimize=false&runs=200&deployProxy=true') + .refresh() + .pause(5000) + .switchWorkspace('default_workspace') + .addFile('myTokenV1.sol', sources[1]['myTokenV1.sol']) + .clickLaunchIcon('solidity') + .pause(2000) + .click('[data-id="compilerContainerCompileBtn"]') + .waitForElementPresent('select[id="compiledContracts"] option[value=MyToken]', 60000) + .clickLaunchIcon('udapp') + .click('select.udapp_contractNames') + .click('select.udapp_contractNames option[value=MyToken]') + .waitForElementPresent('[data-id="contractGUIDeployWithProxyLabel"]') + .expect.element('[data-id="contractGUIDeployWithProxy"]').to.be.selected + }, + + 'Should select upgrade with proxy option from URL params #group1': function (browser: NightwatchBrowser) { + browser + .url('http://127.0.0.1:8080/#optimize=false&runs=200&upgradeProxy=true') + .refresh() + .pause(5000) + .openFile('myTokenV1.sol') + .clickLaunchIcon('solidity') + .pause(2000) + .click('[data-id="compilerContainerCompileBtn"]') + .waitForElementPresent('select[id="compiledContracts"] option[value=MyToken]', 60000) + .clickLaunchIcon('udapp') + .click('select.udapp_contractNames') + .click('select.udapp_contractNames option[value=MyToken]') + .waitForElementPresent('[data-id="contractGUIUpgradeImplementationLabel"]') + .expect.element('[data-id="contractGUIUpgradeImplementation"]').to.be.selected + }, + + 'Should load using various URL compiler params #group1': function (browser: NightwatchBrowser) { browser .pause(5000) .url('http://127.0.0.1:8080/#optimize=true&runs=300&autoCompile=true&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js&language=Yul') @@ -90,9 +203,19 @@ module.exports = { .verify.elementPresent('#optimize:checked') .verify.elementPresent('#autoCompile:checked') .verify.attributeEquals('#runs', 'value', '300') + .url('http://127.0.0.1:8080/#version=0.8.7') + .refresh() + .pause(5000) + .clickLaunchIcon('solidity') + .assert.containsText('#versionSelector option[data-id="selected"]', '0.8.7+commit.e28d00a7') + .url('http://127.0.0.1:8080/#version=0.8.15+commit.e14f2714') + .refresh() + .pause(5000) + .clickLaunchIcon('solidity') + .assert.containsText('#versionSelector option[data-id="selected"]', '0.8.15+commit.e14f2714') }, - 'Should load using compiler from link passed in remix URL': function (browser: NightwatchBrowser) { + 'Should load using compiler from link passed in remix URL #group1': function (browser: NightwatchBrowser) { browser .url('http://127.0.0.1:8080/#version=https://solidity-blog.s3.eu-central-1.amazonaws.com/data/08preview/soljson.js&optimize=false') .refresh() @@ -101,7 +224,7 @@ module.exports = { .pause(5000) .click('*[data-id="scConfigExpander"]') .assert.containsText('#versionSelector option[data-id="selected"]', 'custom') - // default values + // default values .assert.containsText('#evmVersionSelector option[data-id="selected"]', 'default') .verify.elementPresent('#optimize') .assert.not.elementPresent('#optimize:checked') @@ -110,7 +233,7 @@ module.exports = { .verify.attributeEquals('#runs', 'value', '200') }, - 'Should load json files from link passed in remix URL': function (browser: NightwatchBrowser) { + 'Should load json files from link passed in remix URL #group1': function (browser: NightwatchBrowser) { browser .url('http://127.0.0.1:8080/#optimize=false&runs=200&evmVersion=null&version=soljson-v0.6.12+commit.27d51765.js&url=https://raw.githubusercontent.com/EthVM/evm-source-verification/main/contracts/1/0x011e5846975c6463a8c6337eecf3cbf64e328884/input.json') .refresh() diff --git a/apps/remix-ide-e2e/src/tests/vyper_api.ts b/apps/remix-ide-e2e/src/tests/vyper_api.ts new file mode 100644 index 0000000000..f168372963 --- /dev/null +++ b/apps/remix-ide-e2e/src/tests/vyper_api.ts @@ -0,0 +1,96 @@ +'use strict' +import { NightwatchBrowser } from 'nightwatch' +import init from '../helpers/init' + +declare global { + interface Window { testplugin: { name: string, url: string }; } +} + +module.exports = { + '@disabled': true, + before: function (browser: NightwatchBrowser, done: VoidFunction) { + init(browser, done, null, true, { name: 'vyper', url: 'http://127.0.0.1:5002'}) + }, + + 'Should connect to vyper plugin #group1': function (browser: NightwatchBrowser) { + browser.clickLaunchIcon('pluginManager') + .scrollAndClick('[data-id="pluginManagerComponentActivateButtonvyper"]') + .clickLaunchIcon('vyper') + .pause(5000) + // @ts-ignore + .frame(0) + }, + + 'Should add the Ballot.vy #group1': function (browser: NightwatchBrowser) { + browser.click('button[data-id="add-ballot"]') + .frameParent() + .openFile('ballot.vy') + }, + + 'Compile ballot.vy should error #group1': function (browser: NightwatchBrowser) { + browser.clickLaunchIcon('vyper') + // @ts-ignore + .frame(0) + .click('[data-id="remote-compiler"]') + .click('[data-id="compile"]') + .assert.containsText('[data-id="error-message"]', 'unexpected indent') + }, + + 'Compile test contract should success #group1': function (browser: NightwatchBrowser) { + let contractAddress + browser + .frameParent() + .addFile('test.vy', { content: testContract }) + .clickLaunchIcon('vyper') + // @ts-ignore + .frame(0) + .click('[data-id="compile"]') + .frameParent() + .clickLaunchIcon('udapp') + .createContract('') + .clickInstance(0) + .clickFunction('totalPokemonCount - call') + .getAddressAtPosition(0, (address) => { + console.log('Vyper contract ' + address) + contractAddress = address + }) + .perform((done) => { + browser.verifyCallReturnValue(contractAddress, ['0:uint256: 0']) + .perform(() => done()) + }) + } +} + +const testContract = ` +# @version >=0.2.4 <0.3.0 + +DNA_DIGITS: constant(uint256) = 16 +DNA_MODULUS: constant(uint256) = 10 ** DNA_DIGITS +# add HP_LIMIT + +struct Pokemon: + name: String[32] + dna: uint256 + HP: uint256 + matches: uint256 + wins: uint256 + +totalPokemonCount: public(uint256) +pokemonList: HashMap[uint256, Pokemon] + +@pure +@internal +def _generateRandomDNA(_name: String[32]) -> uint256: + random: uint256 = convert(keccak256(_name), uint256) + return random % DNA_MODULUS +# modify _createPokemon +@internal +def _createPokemon(_name: String[32], _dna: uint256, _HP: uint256): + self.pokemonList[self.totalPokemonCount] = Pokemon({ + name: _name, + dna: _dna, + HP: _HP, + matches: 0, + wins: 0 + }) + self.totalPokemonCount += 1` \ No newline at end of file diff --git a/apps/remix-ide-e2e/src/tests/workspace.test.ts b/apps/remix-ide-e2e/src/tests/workspace.test.ts index 2bc0ea1811..3e15a18248 100644 --- a/apps/remix-ide-e2e/src/tests/workspace.test.ts +++ b/apps/remix-ide-e2e/src/tests/workspace.test.ts @@ -33,7 +33,7 @@ module.exports = { // WORKSPACE TEMPLATES E2E START - 'Should create Remix default workspace with files': function (browser: NightwatchBrowser) { + 'Should create Remix default workspace with files #group1': function (browser: NightwatchBrowser) { browser .clickLaunchIcon('filePanel') .click('*[data-id="workspaceCreate"]') @@ -44,53 +44,53 @@ module.exports = { .waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') .execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) .pause(1000) - .assert.elementPresent('*[data-id="treeViewLitreeViewItemcontracts"]') - .assert.elementPresent('*[data-id="treeViewLitreeViewItemcontracts/1_Storage.sol"]') - .assert.elementPresent('*[data-id="treeViewLitreeViewItemcontracts/2_Owner.sol"]') - .assert.elementPresent('*[data-id="treeViewLitreeViewItemcontracts/3_Ballot.sol"]') - .assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts"]') - .assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/1_Storage.sol"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/2_Owner.sol"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/3_Ballot.sol"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') // check js and ts files are not transformed .click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') - + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`import { deploy } from './web3-lib'`) !== -1, - 'Incorrect content') + 'Incorrect content') }) - .assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') .pause(100) .getEditorValue((content) => { browser.assert.ok(content.indexOf(`import { deploy } from './ethers-lib'`) !== -1, - 'Incorrect content') + 'Incorrect content') }) - .assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .pause(2000) .getEditorValue((content) => { browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array, from?: string, gas?: number): Promise => {`) !== -1, - 'Incorrect content') + 'Incorrect content') }) - .assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .pause(100) .getEditorValue((content) => { browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array, accountIndex?: number): Promise => {`) !== -1, - 'Incorrect content') + 'Incorrect content') }) - .assert.elementPresent('*[data-id="treeViewLitreeViewItemtests"]') - .assert.elementPresent('*[data-id="treeViewLitreeViewItemtests/storage.test.js"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests/storage.test.js"]') .click('*[data-id="treeViewLitreeViewItemtests/storage.test.js"]') .pause(100) .getEditorValue((content) => { browser.assert.ok(content.indexOf(`const { expect } = require("chai");`) !== -1, - 'Incorrect content') + 'Incorrect content') }) - .assert.elementPresent('*[data-id="treeViewLitreeViewItemtests/Ballot_test.sol"]') - .assert.elementPresent('*[data-id="treeViewLitreeViewItemREADME.txt"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests/Ballot_test.sol"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemREADME.txt"]') }, - 'Should create blank workspace with no files': function (browser: NightwatchBrowser) { + 'Should create blank workspace with no files #group1': function (browser: NightwatchBrowser) { browser .click('*[data-id="workspaceCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') @@ -102,16 +102,16 @@ module.exports = { .waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') .execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) .pause(100) - .assert.elementPresent('*[data-id="treeViewUltreeViewMenu"]') + .waitForElementPresent('*[data-id="treeViewUltreeViewMenu"]') .execute(function () { const fileList = document.querySelector('*[data-id="treeViewUltreeViewMenu"]') return fileList.getElementsByTagName('li').length; - }, [], function(result){ - browser.assert.equal(result.value, 0, 'Incorrect number of files'); + }, [], function (result) { + browser.assert.equal(result.value, 0, 'Incorrect number of files'); }); }, - 'Should create ERC20 workspace with files': function (browser: NightwatchBrowser) { + 'Should create ERC20 workspace with files #group1': function (browser: NightwatchBrowser) { browser .click('*[data-id="workspaceCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') @@ -123,43 +123,43 @@ module.exports = { .waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') .execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) .pause(100) - .assert.elementPresent('*[data-id="treeViewLitreeViewItemcontracts"]') - .assert.elementPresent('*[data-id="treeViewLitreeViewItemcontracts/SampleERC20.sol"]') - .assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts"]') - .assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/SampleERC20.sol"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') // check js and ts files are not transformed .click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') - - .getEditorValue((content) => { + .pause(5000) + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`import { deploy } from './web3-lib'`) !== -1, - 'Incorrect content') + 'Incorrect content') }) - .assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') - .pause(100) - .getEditorValue((content) => { + .pause(4000) + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`import { deploy } from './ethers-lib'`) !== -1, - 'Incorrect content') + 'Incorrect content') }) - .assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') - .pause(100) - .getEditorValue((content) => { + .pause(4000) + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array, from?: string, gas?: number): Promise => {`) !== -1, - 'Incorrect content') + 'Incorrect content') }) - .assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') - .pause(100) - .getEditorValue((content) => { + .pause(4000) + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array, accountIndex?: number): Promise => {`) !== -1, - 'Incorrect content') + 'Incorrect content') }) - .assert.elementPresent('*[data-id="treeViewLitreeViewItemtests"]') - .assert.elementPresent('*[data-id="treeViewLitreeViewItemtests/SampleERC20_test.sol"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests/SampleERC20_test.sol"]') }, - 'Should create ERC721 workspace with files': function (browser: NightwatchBrowser) { + 'Should create ERC721 workspace with files #group1': function (browser: NightwatchBrowser) { browser .click('*[data-id="workspaceCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') @@ -171,72 +171,70 @@ module.exports = { .waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') .execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) .pause(100) - .assert.elementPresent('*[data-id="treeViewLitreeViewItemcontracts"]') - .assert.elementPresent('*[data-id="treeViewLitreeViewItemcontracts/SampleERC721.sol"]') - .assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts"]') - .assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/SampleERC721.sol"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') // check js and ts files are not transformed .click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') - - .getEditorValue((content) => { + .pause(4000) + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`import { deploy } from './web3-lib'`) !== -1, - 'Incorrect content') + 'Incorrect content') }) - .assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/deploy_with_ethers.ts"]') - .pause(100) - .getEditorValue((content) => { + .pause(4000) + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`import { deploy } from './ethers-lib'`) !== -1, - 'Incorrect content') + 'Incorrect content') }) - .assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') - .pause(100) - .getEditorValue((content) => { + .pause(4000) + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array, from?: string, gas?: number): Promise => {`) !== -1, - 'Incorrect content') + 'Incorrect content') }) - .assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') - .pause(100) - .getEditorValue((content) => { + .pause(4000) + .getEditorValue((content) => { browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array, accountIndex?: number): Promise => {`) !== -1, - 'Incorrect content') + 'Incorrect content') }) - .assert.elementPresent('*[data-id="treeViewLitreeViewItemtests"]') - .assert.elementPresent('*[data-id="treeViewLitreeViewItemtests/SampleERC721_test.sol"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests/SampleERC721_test.sol"]') }, // WORKSPACE TEMPLATES E2E END - 'Should create two workspace and switch to the first one': function (browser: NightwatchBrowser) { + 'Should create two workspace and switch to the first one #group1': function (browser: NightwatchBrowser) { browser - .click('*[data-id="workspaceCreate"]') // create workspace_name + .click('*[data-id="workspaceCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') - .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') - .click('*[data-id="modalDialogCustomPromptTextCreate"]') - .clearValue('*[data-id="modalDialogCustomPromptTextCreate"]') - .setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_name') + .click('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]') + .setValue('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_name') .waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') .click('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') .pause(1000) .addFile('test.sol', { content: 'test' }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemtest.sol"]') - .click('*[data-id="workspaceCreate"]') // create workspace_name_1 + .click('*[data-id="workspaceCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') - .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') - .click('*[data-id="modalDialogCustomPromptTextCreate"]') - .clearValue('*[data-id="modalDialogCustomPromptTextCreate"]') - .setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_name_1') + .click('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]') + .setValue('*[data-id="fileSystemModalDialogContainer-react"] input[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_name_1') .waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') - .click('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') + .click('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') + .pause(2000) .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') .pause(2000) .waitForElementNotPresent('*[data-id="treeViewLitreeViewItemtest.sol"]') .pause(2000) .switchWorkspace('workspace_name') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') + .currentWorkspaceIs('workspace_name') }, 'Should rename a workspace #group1': function (browser: NightwatchBrowser) { @@ -251,9 +249,11 @@ module.exports = { .click('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') .switchWorkspace('workspace_name_1') .pause(2000) + .currentWorkspaceIs('workspace_name_1') .waitForElementNotPresent('*[data-id="treeViewLitreeViewItemtest.sol"]') .switchWorkspace('workspace_name_renamed') .pause(2000) + .currentWorkspaceIs('workspace_name_renamed') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtest.sol"]') }, @@ -268,84 +268,85 @@ module.exports = { .waitForElementNotPresent(`[data-id="dropdown-item-workspace_name_1"]`) }, - // CLONE REPOSITORY E2E START + // CLONE REPOSITORY E2E START + + 'Should clone a repository #group2': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('filePanel') + .waitForElementVisible('[data-id="cloneGitRepository"]') + .click('[data-id="cloneGitRepository"]') + .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') + .click('[data-id="fileSystemModalDialogModalBody-react"]') + .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') + .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/awesome-remix') + .click('[data-id="fileSystem-modal-footer-ok-react"]') + .waitForElementPresent('.fa-spinner') + .pause(5000) + .waitForElementNotPresent('.fa-spinner') + .waitForElementVisible('*[data-id="treeViewLitreeViewItem.git"]') + .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix') + }, - 'Should clone a repository #group2': function (browser: NightwatchBrowser) { - browser - .waitForElementVisible('[data-id="cloneGitRepository"]') - .click('[data-id="cloneGitRepository"]') - .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') - .click('[data-id="fileSystemModalDialogModalBody-react"]') - .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') - .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/awesome-remix') - .click('[data-id="fileSystem-modal-footer-ok-react"]') - .waitForElementPresent('.fa-spinner') - .pause(5000) - .waitForElementNotPresent('.fa-spinner') - .waitForElementVisible('*[data-id="treeViewLitreeViewItem.git"]') - .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix') - }, + 'Should display dgit icon for cloned workspace #group2': function (browser: NightwatchBrowser) { + browser + .switchWorkspace('default_workspace') + .waitForElementNotVisible('[data-id="workspacesSelect"] .fa-code-branch') + .switchWorkspace('awesome-remix') + .waitForElementVisible('[data-id="workspacesSelect"] .fa-code-branch') + }, - 'Should display dgit icon for cloned workspace #group2': function (browser: NightwatchBrowser) { - browser - .switchWorkspace('default_workspace') - .waitForElementNotVisible('[data-id="workspacesSelect"] .fa-code-branch') - .switchWorkspace('awesome-remix') - .waitForElementVisible('[data-id="workspacesSelect"] .fa-code-branch') - }, - - 'Should display non-clashing names for duplicate clone #group2': '' + function (browser: NightwatchBrowser) { - browser - .waitForElementVisible('[data-id="cloneGitRepository"]') - .click('[data-id="cloneGitRepository"]') - .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') - .click('[data-id="fileSystemModalDialogModalBody-react"]') - .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') - .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/awesome-remix') - .click('[data-id="fileSystem-modal-footer-ok-react"]') - .pause(5000) - .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix1') - .waitForElementVisible('[data-id="cloneGitRepository"]') - .click('[data-id="cloneGitRepository"]') - .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') - .click('[data-id="fileSystemModalDialogModalBody-react"]') - .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') - .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/awesome-remix') - .click('[data-id="fileSystem-modal-footer-ok-react"]') - .pause(5000) - .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix2') - .waitForElementVisible('[data-id="cloneGitRepository"]') - .click('[data-id="cloneGitRepository"]') - .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') - .click('[data-id="fileSystemModalDialogModalBody-react"]') - .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') - .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/awesome-remix') - .click('[data-id="fileSystem-modal-footer-ok-react"]') - .pause(5000) - .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix3') - .switchWorkspace('awesome-remix') - .switchWorkspace('awesome-remix1') - .switchWorkspace('awesome-remix2') - .switchWorkspace('awesome-remix3') - }, + 'Should display non-clashing names for duplicate clone #group2': '' + function (browser: NightwatchBrowser) { + browser + .waitForElementVisible('[data-id="cloneGitRepository"]') + .click('[data-id="cloneGitRepository"]') + .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') + .click('[data-id="fileSystemModalDialogModalBody-react"]') + .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') + .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/awesome-remix') + .click('[data-id="fileSystem-modal-footer-ok-react"]') + .pause(5000) + .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix1') + .waitForElementVisible('[data-id="cloneGitRepository"]') + .click('[data-id="cloneGitRepository"]') + .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') + .click('[data-id="fileSystemModalDialogModalBody-react"]') + .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') + .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/awesome-remix') + .click('[data-id="fileSystem-modal-footer-ok-react"]') + .pause(5000) + .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix2') + .waitForElementVisible('[data-id="cloneGitRepository"]') + .click('[data-id="cloneGitRepository"]') + .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') + .click('[data-id="fileSystemModalDialogModalBody-react"]') + .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') + .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/awesome-remix') + .click('[data-id="fileSystem-modal-footer-ok-react"]') + .pause(5000) + .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix3') + .switchWorkspace('awesome-remix') + .switchWorkspace('awesome-remix1') + .switchWorkspace('awesome-remix2') + .switchWorkspace('awesome-remix3') + }, - 'Should display error message in modal for failed clone #group2': function (browser: NightwatchBrowser) { - browser - .waitForElementVisible('[data-id="cloneGitRepository"]') - .click('[data-id="cloneGitRepository"]') - .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') - .click('[data-id="fileSystemModalDialogModalBody-react"]') - .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') - .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/non-existent-repo') - .click('[data-id="fileSystem-modal-footer-ok-react"]') - .pause(5000) - .waitForElementVisible('[data-id="cloneGitRepositoryModalDialogModalBody-react"]') - .waitForElementContainsText('[data-id="cloneGitRepositoryModalDialogModalBody-react"]', 'An error occurred: Please check that you have the correct URL for the repo. If the repo is private, you need to add your github credentials (with the valid token permissions) in Settings plugin') - .click('[data-id="cloneGitRepository-modal-footer-ok-react"]') - .end() - }, + 'Should display error message in modal for failed clone #group2': function (browser: NightwatchBrowser) { + browser + .waitForElementVisible('[data-id="cloneGitRepository"]') + .click('[data-id="cloneGitRepository"]') + .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') + .click('[data-id="fileSystemModalDialogModalBody-react"]') + .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') + .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/non-existent-repo') + .click('[data-id="fileSystem-modal-footer-ok-react"]') + .pause(5000) + .waitForElementVisible('[data-id="cloneGitRepositoryModalDialogModalBody-react"]') + .waitForElementContainsText('[data-id="cloneGitRepositoryModalDialogModalBody-react"]', 'An error occurred: Please check that you have the correct URL for the repo. If the repo is private, you need to add your github credentials (with the valid token permissions) in Settings plugin') + .click('[data-id="cloneGitRepository-modal-footer-ok-react"]') + .end() + }, - // CLONE REPOSITORY E2E END + // CLONE REPOSITORY E2E END tearDown: sauce } diff --git a/apps/remix-ide/ci/browser_test.sh b/apps/remix-ide/ci/browser_test.sh index 7a9b536f0c..f621c18f8a 100755 --- a/apps/remix-ide/ci/browser_test.sh +++ b/apps/remix-ide/ci/browser_test.sh @@ -14,8 +14,7 @@ yarn run remixd & sleep 5 yarn run build:e2e - -TESTFILES=$(grep -IRiL "\'@disabled\': \?true" "dist/apps/remix-ide-e2e/src/tests" | grep "\.spec\|\.test" | sort | circleci tests split ) +TESTFILES=$(grep -IRiL "@disabled" "dist/apps/remix-ide-e2e/src/tests" | grep "\.spec\|\.test" | sort | circleci tests split ) for TESTFILE in $TESTFILES; do npx nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js $TESTFILE --env=$1 || TEST_EXITCODE=1 done diff --git a/apps/remix-ide/ci/browser_tests_etherscan_plugin.sh b/apps/remix-ide/ci/browser_tests_etherscan_plugin.sh new file mode 100755 index 0000000000..fdb420fc69 --- /dev/null +++ b/apps/remix-ide/ci/browser_tests_etherscan_plugin.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +set -e + +BUILD_ID=${CIRCLE_BUILD_NUM:-${TRAVIS_JOB_NUMBER}} +echo "$BUILD_ID" +TEST_EXITCODE=0 + +yarn run serve:production & +npx nx serve etherscan & + +sleep 5 + +yarn run build:e2e + +TESTFILES=$(grep -IRiL "\'@disabled\': \?true" "dist/apps/remix-ide-e2e/src/tests" | grep "etherscan_api" | sort | circleci tests split ) +for TESTFILE in $TESTFILES; do + npx nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js $TESTFILE --env=chrome || TEST_EXITCODE=1 +done + +echo "$TEST_EXITCODE" +if [ "$TEST_EXITCODE" -eq 1 ] +then + exit 1 +fi diff --git a/apps/remix-ide/ci/browser_tests_vyper_plugin.sh b/apps/remix-ide/ci/browser_tests_vyper_plugin.sh new file mode 100755 index 0000000000..4c82c9f768 --- /dev/null +++ b/apps/remix-ide/ci/browser_tests_vyper_plugin.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +set -e + +BUILD_ID=${CIRCLE_BUILD_NUM:-${TRAVIS_JOB_NUMBER}} +echo "$BUILD_ID" +TEST_EXITCODE=0 + +yarn run serve:production & +npx nx serve vyper & + +sleep 5 + +yarn run build:e2e + +TESTFILES=$(grep -IRiL "\'@disabled\': \?true" "dist/apps/remix-ide-e2e/src/tests" | grep "vyper_api" | sort | circleci tests split ) +for TESTFILE in $TESTFILES; do + npx nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js $TESTFILE --env=chrome || TEST_EXITCODE=1 +done + +echo "$TEST_EXITCODE" +if [ "$TEST_EXITCODE" -eq 1 ] +then + exit 1 +fi diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index 41ede2d848..1694df5c45 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -33,6 +33,7 @@ import { FoundryProvider } from './app/tabs/foundry-provider' import { ExternalHttpProvider } from './app/tabs/external-http-provider' import { Injected0ptimismProvider } from './app/tabs/injected-optimism-provider' import { InjectedArbitrumOneProvider } from './app/tabs/injected-arbitrum-one-provider' +import { FileDecorator } from './app/plugins/file-decorator' const isElectron = require('is-electron') @@ -158,6 +159,9 @@ class AppComponent { // ----------------- Storage plugin --------------------------------- const storagePlugin = new StoragePlugin() + // ------- FILE DECORATOR PLUGIN ------------------ + const fileDecorator = new FileDecorator() + //----- search const search = new SearchPlugin() @@ -244,6 +248,7 @@ class AppComponent { fetchAndCompile, dGitProvider, storagePlugin, + fileDecorator, hardhatProvider, ganacheProvider, foundryProvider, @@ -415,6 +420,29 @@ class AppComponent { this.appManager.call(...callDetails).catch(console.error) } } + + if (params.calls) { + const calls = params.calls.split("///"); + + // call all functions in the list, one after the other + for (const call of calls) { + const callDetails = call.split("//"); + if (callDetails.length > 1) { + this.appManager.call( + "notification", + "toast", + `initiating ${callDetails[0]} ...` + ); + + // @todo(remove the timeout when activatePlugin is on 0.3.0) + try { + await this.appManager.call(...callDetails) + } catch (e) { + console.error(e) + } + } + } + } }) .catch(console.error) } diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index ba45ff806b..e24b137058 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -57,7 +57,7 @@ module.exports = class Filepanel extends ViewPlugin { this.slitherHandle = new SlitherHandle() this.workspaces = [] this.appManager = appManager - this.currentWorkspaceMetadata = {} + this.currentWorkspaceMetadata = null } render () { diff --git a/apps/remix-ide/src/app/panels/terminal.js b/apps/remix-ide/src/app/panels/terminal.js index bd89f2d7ce..6d830c2054 100644 --- a/apps/remix-ide/src/app/panels/terminal.js +++ b/apps/remix-ide/src/app/panels/terminal.js @@ -100,8 +100,8 @@ class Terminal extends Plugin { this.terminalApi.logHtml(html) } - log (message) { - this.terminalApi.log(message) + log (message, type) { + this.terminalApi.log(message, type) } setDispatch(dispatch) { diff --git a/apps/remix-ide/src/app/plugins/file-decorator.ts b/apps/remix-ide/src/app/plugins/file-decorator.ts index 60a14ef47f..c81c1894ff 100644 --- a/apps/remix-ide/src/app/plugins/file-decorator.ts +++ b/apps/remix-ide/src/app/plugins/file-decorator.ts @@ -8,7 +8,7 @@ import { fileDecoration } from '@remix-ui/file-decorators' const profile = { name: 'fileDecorator', desciption: 'Keeps decorators of the files', - methods: ['setFileDecorators', 'clearFileDecorators'], + methods: ['setFileDecorators', 'clearFileDecorators', 'clearAllFileDecorators'], events: ['fileDecoratorsChanged'], version: '0.0.1' } @@ -18,43 +18,69 @@ export class FileDecorator extends Plugin { constructor() { super(profile) } + + onActivation(): void { + this.on('filePanel', 'setWorkspace', async () => { + await this.clearAllFileDecorators() + }) + } + /** * * @param fileStates Array of file states */ async setFileDecorators(fileStates: fileDecoration[] | fileDecoration) { + const { from } = this.currentRequest const workspace = await this.call('filePanel', 'getCurrentWorkspace') - function sortByPath( a: fileDecoration, b: fileDecoration ) { - if ( a.path < b.path ){ - return -1; - } - if ( a.path > b.path ){ - return 1; - } - return 0; - } - const fileStatesPayload = Array.isArray(fileStates) ? fileStates : [fileStates] // clear all file states in the previous state of this owner on the files called fileStatesPayload.forEach((state) => { state.workspace = workspace + state.owner = from }) const filteredState = this._fileStates.filter((state) => { const index = fileStatesPayload.findIndex((payloadFileState: fileDecoration) => { - return payloadFileState.owner == state.owner && payloadFileState.path == state.path + return from == state.owner && payloadFileState.path == state.path }) return index == -1 }) const newState = [...filteredState, ...fileStatesPayload].sort(sortByPath) - + if (!deepequal(newState, this._fileStates)) { this._fileStates = newState this.emit('fileDecoratorsChanged', this._fileStates) } } - async clearFileDecorators() { + async clearFileDecorators(path?: string) { + const { from } = this.currentRequest + if (!from) return + + const filteredState = this._fileStates.filter((state) => { + if(state.owner != from) return true + if(path && state.path != path) return true + }) + const newState = [...filteredState].sort(sortByPath) + + if (!deepequal(newState, this._fileStates)) { + this._fileStates = newState + this.emit('fileDecoratorsChanged', this._fileStates) + } + + } + + async clearAllFileDecorators() { this._fileStates = [] this.emit('fileDecoratorsChanged', []) } +} + +const sortByPath = (a: fileDecoration, b: fileDecoration) => { + if (a.path < b.path) { + return -1; + } + if (a.path > b.path) { + return 1; + } + return 0; } \ No newline at end of file diff --git a/apps/remix-ide/src/app/tabs/abstract-provider.tsx b/apps/remix-ide/src/app/tabs/abstract-provider.tsx index 31b1a4e317..ae923bd88b 100644 --- a/apps/remix-ide/src/app/tabs/abstract-provider.tsx +++ b/apps/remix-ide/src/app/tabs/abstract-provider.tsx @@ -101,7 +101,7 @@ export abstract class AbstractProvider extends Plugin { reject('Unable to connect') } }, 2000) - await this.provider.ready + await this.provider.detectNetwork() // this throws if the network cannot be detected this.connected = true } catch (e) { this.switchAway(true) @@ -116,6 +116,7 @@ export abstract class AbstractProvider extends Plugin { } private async switchAway (showError) { + if (!this.provider) return this.provider = null this.blocked = true this.connected = false diff --git a/apps/remix-ide/src/app/tabs/web3-provider.js b/apps/remix-ide/src/app/tabs/web3-provider.js index 4bf96f062d..a6690f545e 100644 --- a/apps/remix-ide/src/app/tabs/web3-provider.js +++ b/apps/remix-ide/src/app/tabs/web3-provider.js @@ -29,7 +29,10 @@ export class Web3ProviderModule extends Plugin { const provider = this.blockchain.web3().currentProvider // see https://github.com/ethereum/web3.js/pull/1018/files#diff-d25786686c1053b786cc2626dc6e048675050593c0ebaafbf0814e1996f22022R129 provider[provider.sendAsync ? 'sendAsync' : 'send'](payload, async (error, message) => { - if (error) return reject(error) + if (error) { + this.call('terminal', 'log', error.data ? error.data : error, 'error') + return reject(error.data ? error.data : error) + } if (payload.method === 'eth_sendTransaction') { if (payload.params.length && !payload.params[0].to && message.result) { setTimeout(async () => { diff --git a/apps/remix-ide/src/blockchain/execution-context.js b/apps/remix-ide/src/blockchain/execution-context.js index 1fe181acfb..5e91c6569c 100644 --- a/apps/remix-ide/src/blockchain/execution-context.js +++ b/apps/remix-ide/src/blockchain/execution-context.js @@ -156,7 +156,7 @@ export class ExecutionContext { return cb() } else { if (injectedProvider && injectedProvider._metamask && injectedProvider._metamask.isUnlocked) { - if (!await injectedProvider._metamask.isUnlocked()) this.call('notification', 'toast', 'Please make sure the injected provider is unlocked (e.g Metamask).') + if (!await injectedProvider._metamask.isUnlocked()) infoCb('Please make sure the injected provider is unlocked (e.g Metamask).') } this.askPermission() this.executionContext = context diff --git a/apps/remix-ide/src/remixAppManager.js b/apps/remix-ide/src/remixAppManager.js index 159f704e1d..fcf96659ec 100644 --- a/apps/remix-ide/src/remixAppManager.js +++ b/apps/remix-ide/src/remixAppManager.js @@ -4,14 +4,17 @@ import { QueryParams } from '@remix-project/remix-lib' import { IframePlugin } from '@remixproject/engine-web' const _paq = window._paq = window._paq || [] +// requiredModule removes the plugin from the plugin manager list on UI const requiredModules = [ // services + layout views + system views 'manager', 'config', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme', 'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons', 'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity', 'solidity-logic', 'gistHandler', 'layout', 'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'openzeppelin-proxy', - 'hardhat-provider', 'compileAndRun', 'search', 'recorder'] + 'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected-optimism-provider', 'injected-arbitrum-one-provider', + 'compileAndRun', 'search', 'recorder', 'fileDecorator'] -const dependentModules = ['git', 'hardhat', 'truffle', 'slither'] // module which shouldn't be manually activated (e.g git is activated by remixd) +// dependentModules shouldn't be manually activated (e.g hardhat is activated by remixd) +const dependentModules = ['hardhat', 'truffle', 'slither'] const sensitiveCalls = { 'fileManager': ['writeFile', 'copyFile', 'rename', 'copyDir'], @@ -20,7 +23,9 @@ const sensitiveCalls = { } export function isNative(name) { - 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'] + // 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'] return nativePlugins.includes(name) || requiredModules.includes(name) } @@ -141,9 +146,11 @@ export class RemixAppManager extends PluginManager { } } } - return plugins.map(plugin => { + const testPluginName = localStorage.getItem('test-plugin-name') + const testPluginUrl = localStorage.getItem('test-plugin-url') + return plugins.map(plugin => { + if (plugin.name === testPluginName) plugin.url = testPluginUrl return new IframePlugin(plugin) - // return new IframeReactPlugin(plugin) }) } diff --git a/apps/solidity-compiler/src/app/compiler-api.ts b/apps/solidity-compiler/src/app/compiler-api.ts index 4e755f211d..21a2283ef3 100644 --- a/apps/solidity-compiler/src/app/compiler-api.ts +++ b/apps/solidity-compiler/src/app/compiler-api.ts @@ -223,9 +223,10 @@ export const CompilerApiMixin = (Base) => class extends Base { } this.compiler.event.register('loadingCompiler', this.data.eventHandlers.onLoadingCompiler) - this.data.eventHandlers.onCompilerLoaded = () => { + this.data.eventHandlers.onCompilerLoaded = (version) => { this.data.loading = false this.statusChanged({ key: 'none' }) + this.emit('compilerLoaded', version) } this.compiler.event.register('compilerLoaded', this.data.eventHandlers.onCompilerLoaded) diff --git a/apps/vyper/.babelrc b/apps/vyper/.babelrc new file mode 100644 index 0000000000..b1fc975456 --- /dev/null +++ b/apps/vyper/.babelrc @@ -0,0 +1,13 @@ +{ + "presets": [ + [ + "@nrwl/react/babel", { + "runtime": "automatic" + + } + ] + ], + "plugins": [ + + ] +} diff --git a/apps/vyper/.browserslistrc b/apps/vyper/.browserslistrc new file mode 100644 index 0000000000..f1d12df4fa --- /dev/null +++ b/apps/vyper/.browserslistrc @@ -0,0 +1,16 @@ +# This file is used by: +# 1. autoprefixer to adjust CSS to support the below specified browsers +# 2. babel preset-env to adjust included polyfills +# +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries +# +# If you need to support different browsers in production, you may tweak the list below. + +last 1 Chrome version +last 1 Firefox version +last 2 Edge major versions +last 2 Safari major version +last 2 iOS major versions +Firefox ESR +not IE 9-11 # For IE 9-11 support, remove 'not'. \ No newline at end of file diff --git a/apps/vyper/.eslintrc b/apps/vyper/.eslintrc new file mode 100644 index 0000000000..b7d498eea1 --- /dev/null +++ b/apps/vyper/.eslintrc @@ -0,0 +1,18 @@ +{ + "env": { + "browser": true, + "es6": true + }, + "extends": "../../.eslintrc.json", + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly" + }, + "parserOptions": { + "ecmaVersion": 11, + "sourceType": "module" + }, + "rules": { + "standard/no-callback-literal": "off" + } +} diff --git a/apps/vyper/src/app/app.css b/apps/vyper/src/app/app.css new file mode 100644 index 0000000000..978406926a --- /dev/null +++ b/apps/vyper/src/app/app.css @@ -0,0 +1,124 @@ +* { + box-sizing: border-box; +} +html, body, #root, main { + height: 100%; + margin: 0; +} + +#vyper-plugin header { + display: flex; + justify-content: space-between; + align-items: center; + height: 50px; + padding: 5px 15px; +} + +#vyper-plugin header a, +#vyper-plugin header .title { + display: flex; + height: 60%; +} + +#vyper-plugin header svg, +#vyper-plugin header img { + width: auto; + height: 100%; +} + +#vyper-plugin section { + display: flex; + flex-direction: column; + align-items: center; + height: calc(100% - 50px); +} + +.btn-group-toggle { + width: 90%; + text-transform: uppercase; + margin: 10px 0; +} + +.btn-group-toggle .btn { + cursor: pointer; + font-size: 0.8rem; +} + +#local-url { + width: 90%; +} + +#compile-btn { + width: 90%; +} + +#compile-btn * { + width: 100%; +} + +#result { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + padding-top: 10px; + overflow: hidden; +} + +#result.error { + justify-content: flex-start; +} +#result.error svg { + width: 40px; + height: 40px; + margin: 10px; +} + +#result nav { + width: 100%; + flex-wrap: nowrap; + justify-content: space-evenly; +} + + +#result nav a { + padding: 0.5rem 1rem; + font-size: 0.8rem; +} + +#result .tab-content { + flex: 1; + width: 100%; + padding: 15px; + overflow: auto; +} + +#result .tab-pane.active { + height: 100%; + display: flex; + flex-direction: column; + align-items: stretch; +} + +#result .copy { + padding: 5x; + font-size: 0.8rem; + margin: 10px; +} + +#result p { + text-align: center; + width: 100% !important; +} + +#result .react-json-view { + overflow-y: auto; +} + +#result textarea { + border: none; + height: 100%; + width: 100%; +} \ No newline at end of file diff --git a/apps/vyper/src/app/app.tsx b/apps/vyper/src/app/app.tsx new file mode 100644 index 0000000000..a6ce7c9db9 --- /dev/null +++ b/apps/vyper/src/app/app.tsx @@ -0,0 +1,118 @@ +import React, { useState, useEffect } from 'react' + +import { VyperCompilationOutput, remixClient } from './utils' +import { CompilationResult } from '@remixproject/plugin-api' + +// Components +import CompilerButton from './components/CompilerButton' +import WarnRemote from './components/WarnRemote' +import VyperResult from './components/VyperResult' +import LocalUrlInput from './components/LocalUrl' +import ToggleButtonGroup from 'react-bootstrap/ToggleButtonGroup' +import ToggleButton from 'react-bootstrap/ToggleButton' + +import vyperLogo from './logo.svg' +import './app.css' + +interface AppState { + status: 'idle' | 'inProgress' + environment: 'remote' | 'local' + compilationResult?: CompilationResult + localUrl: string +} + +interface OutputMap { + [fileName: string]: VyperCompilationOutput +} + +const App: React.FC = () => { + const [contract, setContract] = useState() + const [output, setOutput] = useState({}) + const [state, setState] = useState({ + status: 'idle', + environment: 'local', + localUrl: 'http://localhost:8000/compile' + }) + + useEffect(() => { + async function start() { + try { + await remixClient.loaded() + remixClient.onFileChange(name => setContract(name)) + const name = await remixClient.getContractName() + setContract(name) + } catch (err) { + console.log(err) + } + } + start() + }, []) + + /** Update the environment state value */ + function setEnvironment(environment: 'local' | 'remote') { + setState({ ...state, environment }) + } + + function setLocalUrl(url: string) { + setState({ ...state, localUrl: url }) + } + + function compilerUrl() { + return state.environment === 'remote' + ? 'https://vyper.remixproject.org/compile' + : state.localUrl + } + + return ( +
+
+
+ Vyper logo +

yper Compiler

+
+ + + +
+
+ + + Remote Compiler + + + Local Compiler + + + + +
+ + setOutput({ ...output, [name]: update }) + } + /> +
+
+ +
+
+
+ ) +} + +export default App diff --git a/apps/vyper/src/app/components/CompilerButton.tsx b/apps/vyper/src/app/components/CompilerButton.tsx new file mode 100644 index 0000000000..fb0fb6ff05 --- /dev/null +++ b/apps/vyper/src/app/components/CompilerButton.tsx @@ -0,0 +1,75 @@ +import React from 'react' +import { + isVyper, + compile, + toStandardOutput, + VyperCompilationOutput, + isCompilationError, + remixClient +} from '../utils' +import Button from 'react-bootstrap/Button' + +interface Props { + compilerUrl: string + contract?: string, + setOutput: (name: string, output: VyperCompilationOutput) => void +} + +function CompilerButton({ contract, setOutput, compilerUrl }: Props) { + + if (!contract || !contract) { + return + } + + if (!isVyper(contract)) { + return + } + + /** Compile a Contract */ + async function compileContract() { + try { + const _contract = await remixClient.getContract() + remixClient.changeStatus({ + key: 'loading', + type: 'info', + title: 'Compiling' + }) + const output = await compile(compilerUrl, _contract) + setOutput(_contract.name, output) + // ERROR + if (isCompilationError(output)) { + const line = output.line + const lineColumnPos = { + start: { line: line - 1 }, + end: { line: line - 1 } + } + remixClient.highlight(lineColumnPos as any, _contract.name, '#e0b4b4') + throw new Error(output.message) + } + // SUCCESS + remixClient.discardHighlight() + remixClient.changeStatus({ + key: 'succeed', + type: 'success', + title: 'succeed' + }) + const data = toStandardOutput(_contract.name, output) + remixClient.compilationFinish(_contract.name, _contract.content, data) + } catch (err: any) { + remixClient.changeStatus({ + key: 'failed', + type: 'error', + title: err.message + }) + console.error(err) + } + } + + return ( + + ) +} + +export default CompilerButton diff --git a/apps/vyper/src/app/components/LocalUrl.tsx b/apps/vyper/src/app/components/LocalUrl.tsx new file mode 100644 index 0000000000..3659b4094e --- /dev/null +++ b/apps/vyper/src/app/components/LocalUrl.tsx @@ -0,0 +1,36 @@ +import React from 'react' +import Form from 'react-bootstrap/Form' + +interface Props { + url: string + setUrl: (url: string) => void, + environment: 'remote' | 'local' +} + +function LocalUrlInput({ url, setUrl, environment }: Props) { + + if (environment === 'remote') { + return <> + } + + function updateUrl(event: React.FocusEvent) { + setUrl(event.target.value) + } + + return ( +
+ + Local Compiler Url + + + The url to your local compiler + + +
+ ) +} + +export default LocalUrlInput; \ No newline at end of file diff --git a/apps/vyper/src/app/components/VyperResult.tsx b/apps/vyper/src/app/components/VyperResult.tsx new file mode 100644 index 0000000000..f550d17370 --- /dev/null +++ b/apps/vyper/src/app/components/VyperResult.tsx @@ -0,0 +1,70 @@ +import React, { useState } from 'react'; +import { + VyperCompilationResult, + VyperCompilationOutput, + isCompilationError, + remixClient +} from '../utils'; +import Tabs from 'react-bootstrap/Tabs' +import Tab from 'react-bootstrap/Tab' +import { Ballot } from '../examples/ballot'; +import Button from 'react-bootstrap/Button'; +import JSONTree from 'react-json-view' +import { CopyToClipboard } from '@remix-ui/clipboard' + + +interface VyperResultProps { + output?: VyperCompilationOutput; +} + +function VyperResult({ output }: VyperResultProps) { + const [ active, setActive ] = useState('abi'); + + if (!output) return ( +
+

No contract compiled yet.

+ +
+ ) + + if (isCompilationError(output)) { + return ( +
+ +

{output.message}

+
) + } + + return ( + setActive(key)}> + + JSON.stringify(output.abi)}> + + + + + + output.bytecode}> + + + + + + output.bytecode_runtime}> + + + + + + output.ir}> + + + + + + ); +} + +export default VyperResult; \ No newline at end of file diff --git a/apps/vyper/src/app/components/WarnRemote.tsx b/apps/vyper/src/app/components/WarnRemote.tsx new file mode 100644 index 0000000000..3ece01895b --- /dev/null +++ b/apps/vyper/src/app/components/WarnRemote.tsx @@ -0,0 +1,19 @@ +import React from 'react' + +interface Props { + environment: 'remote' | 'local' +} + +function WarnRemoteLabel({ environment }: Props) { + + if (environment === 'local') { + return <> + } + + return ( +
It is really important to not use the remote compiler for production environment. + Please only use it for testing purpose and prefer to using a local compiler for production like environment.
+ ) +} + +export default WarnRemoteLabel; \ No newline at end of file diff --git a/apps/vyper/src/app/examples/ballot.tsx b/apps/vyper/src/app/examples/ballot.tsx new file mode 100644 index 0000000000..4b368c6fda --- /dev/null +++ b/apps/vyper/src/app/examples/ballot.tsx @@ -0,0 +1,161 @@ +export const Ballot = { + name: 'browser/ballot.vy', + content: `# Voting with delegation. + + # Information about voters + struct Voter: + # weight is accumulated by delegation + weight: int128 + # if true, that person already voted (which includes voting by delegating) + voted: bool + # person delegated to + delegate: address + # index of the voted proposal, which is not meaningful unless 'voted' is True. + vote: int128 + + # Users can create proposals + struct Proposal: + # short name (up to 32 bytes) + name: bytes32 + # number of accumulated votes + voteCount: int128 + + voters: public(map(address, Voter)) + proposals: public(map(int128, Proposal)) + voterCount: public(int128) + chairperson: public(address) + int128Proposals: public(int128) + + + @public + @constant + def delegated(addr: address) -> bool: + return self.voters[addr].delegate != ZERO_ADDRESS + + + @public + @constant + def directlyVoted(addr: address) -> bool: + return self.voters[addr].voted and (self.voters[addr].delegate == ZERO_ADDRESS) + + + # Setup global variables + @public + def __init__(_proposalNames: bytes32[2]): + self.chairperson = msg.sender + self.voterCount = 0 + for i in range(2): + self.proposals[i] = Proposal({ + name: _proposalNames[i], + voteCount: 0 + }) + self.int128Proposals += 1 + + # Give a 'voter' the right to vote on this ballot. + # This may only be called by the 'chairperson'. + @public + def giveRightToVote(voter: address): + # Throws if the sender is not the chairperson. + assert msg.sender == self.chairperson + # Throws if the voter has already voted. + assert not self.voters[voter].voted + # Throws if the voter's voting weight isn't 0. + assert self.voters[voter].weight == 0 + self.voters[voter].weight = 1 + self.voterCount += 1 + + # Used by 'delegate' below, and can be called by anyone. + @public + def forwardWeight(delegate_with_weight_to_forward: address): + assert self.delegated(delegate_with_weight_to_forward) + # Throw if there is nothing to do: + assert self.voters[delegate_with_weight_to_forward].weight > 0 + + target: address = self.voters[delegate_with_weight_to_forward].delegate + for i in range(4): + if self.delegated(target): + target = self.voters[target].delegate + # The following effectively detects cycles of length <= 5, + # in which the delegation is given back to the delegator. + # This could be done for any int128ber of loops, + # or even infinitely with a while loop. + # However, cycles aren't actually problematic for correctness; + # they just result in spoiled votes. + # So, in the production version, this should instead be + # the responsibility of the contract's client, and this + # check should be removed. + assert target != delegate_with_weight_to_forward + else: + # Weight will be moved to someone who directly voted or + # hasn't voted. + break + + weight_to_forward: int128 = self.voters[delegate_with_weight_to_forward].weight + self.voters[delegate_with_weight_to_forward].weight = 0 + self.voters[target].weight += weight_to_forward + + if self.directlyVoted(target): + self.proposals[self.voters[target].vote].voteCount += weight_to_forward + self.voters[target].weight = 0 + + # To reiterate: if target is also a delegate, this function will need + # to be called again, similarly to as above. + + # Delegate your vote to the voter 'to'. + @public + def delegate(to: address): + # Throws if the sender has already voted + assert not self.voters[msg.sender].voted + # Throws if the sender tries to delegate their vote to themselves or to + # the default address value of 0x0000000000000000000000000000000000000000 + # (the latter might not be problematic, but I don't want to think about it). + assert to != msg.sender + assert to != ZERO_ADDRESS + + self.voters[msg.sender].voted = True + self.voters[msg.sender].delegate = to + + # This call will throw if and only if this delegation would cause a loop + # of length <= 5 that ends up delegating back to the delegator. + self.forwardWeight(msg.sender) + + # Give your vote (including votes delegated to you) + # to proposal 'proposals[proposal].name'. + @public + def vote(proposal: int128): + # can't vote twice + assert not self.voters[msg.sender].voted + # can only vote on legitimate proposals + assert proposal < self.int128Proposals + + self.voters[msg.sender].vote = proposal + self.voters[msg.sender].voted = True + + # transfer msg.sender's weight to proposal + self.proposals[proposal].voteCount += self.voters[msg.sender].weight + self.voters[msg.sender].weight = 0 + + # Computes the winning proposal taking all + # previous votes into account. + @public + @constant + def winningProposal() -> int128: + winning_vote_count: int128 = 0 + winning_proposal: int128 = 0 + for i in range(2): + if self.proposals[i].voteCount > winning_vote_count: + winning_vote_count = self.proposals[i].voteCount + winning_proposal = i + return winning_proposal + + # Calls winningProposal() function to get the index + # of the winner contained in the proposals array and then + # returns the name of the winner + @public + @constant + def winnerName() -> bytes32: + return self.proposals[self.winningProposal()].name + + ` + } + \ No newline at end of file diff --git a/apps/vyper/src/app/logo.svg b/apps/vyper/src/app/logo.svg new file mode 100644 index 0000000000..78302e19f4 --- /dev/null +++ b/apps/vyper/src/app/logo.svg @@ -0,0 +1,14 @@ + diff --git a/apps/vyper/src/app/star.svg b/apps/vyper/src/app/star.svg new file mode 100644 index 0000000000..901053d385 --- /dev/null +++ b/apps/vyper/src/app/star.svg @@ -0,0 +1,11 @@ + + + + + diff --git a/apps/vyper/src/app/utils/compiler.tsx b/apps/vyper/src/app/utils/compiler.tsx new file mode 100644 index 0000000000..ebc117dba1 --- /dev/null +++ b/apps/vyper/src/app/utils/compiler.tsx @@ -0,0 +1,139 @@ +import { CompilationResult, ABIDescription } from "@remixproject/plugin-api"; + +export interface Contract { + name: string; + content: string; +} + +export interface VyperCompilationResult { + status: 'success', + bytecode: string, + bytecode_runtime: string, + abi: ABIDescription[], + ir: string, + method_identifiers: { + [method: string]: string + } +} + +export interface VyperCompilationError { + status: 'failed' + column: number + line: number + message: string +} + +export type VyperCompilationOutput = VyperCompilationResult | VyperCompilationError + +/** Check if the output is an error */ +export function isCompilationError(output: VyperCompilationOutput): output is VyperCompilationError { + return output.status === 'failed' +} + +/** + * Compile the a contract + * @param url The url of the compiler + * @param contract The name and content of the contract + */ +export async function compile(url: string, contract: Contract): Promise { + if (!contract.name) { + throw new Error('Set your Vyper contract file.') + } + const extension = contract.name.split('.')[1] + if (extension !== 'vy') { + throw new Error('Use extension .vy for Vyper.') + } + const response = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ code: contract.content }) + }) + + if (response.status === 404) { + throw new Error(`Vyper compiler not found at "${url}".`) + } + /*if (response.status === 400) { + throw new Error(`Vyper compilation failed: ${response.statusText}`) + }*/ + return response.json() +} + +/** + * Transform Vyper Output to Solidity-like Compiler output + * @param name Name of the contract file + * @param compilationResult Result returned by the compiler + */ +export function toStandardOutput(fileName: string, compilationResult: VyperCompilationResult): CompilationResult { + const contractName = fileName.split('/').slice(-1)[0].split('.')[0]; + const methodIdentifiers = JSON.parse(JSON.stringify(compilationResult['method_identifiers']).replace(/0x/g,'')); + return { + sources: { + [fileName]: { + id: 1, + ast: {} as any, + legacyAST: {} as any + } + }, + contracts: { + [fileName]: { + // If the language used has no contract names, this field should equal to an empty string + [contractName]: { + // The Ethereum Contract ABI. If empty, it is represented as an empty array. + // See https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI + abi: compilationResult['abi'], + evm: { + bytecode: { + linkReferences: {}, + object: compilationResult['bytecode'].replace('0x',''), + opcodes: "" + }, + deployedBytecode: { + linkReferences: {}, + object: compilationResult['bytecode_runtime'].replace('0x',''), + opcodes: "" + }, + methodIdentifiers: methodIdentifiers + } + } + } as any + } + }; +} + + +/* +export function createCompilationResultMessage(name: string, result: any) { + if(result.status == 'success') { + return { + bytecode: this.state.compilationResult['bytecode'], + bytecode_runtime: this.state.compilationResult['bytecode_runtime'], + abi: JSON.stringify(this.state.compilationResult['abi'], null , "\t"), + ir: this.state.compilationResult['ir'] + } + } else if(result.status == 'failed' && result.column && result.line) { + const header = `${name}:${result.line}:${result.column}` + const body = this.state.compilationResult.message.split(/\r\n|\r|\n/) + const arr = [header].concat(body).join("\n") + return { + bytecode: arr, + bytecode_runtime: arr, + abi: arr, + ir: arr + } + } else if(result.status == 'failed') { + const message = this.state.compilationResult.message + return { + bytecode: message, + bytecode_runtime: message, + abi: message, + ir: message + } + } + return { + bytecode: "", + bytecode_runtime: "", + abi: "", + ir: "" + } +} +*/ \ No newline at end of file diff --git a/apps/vyper/src/app/utils/index.ts b/apps/vyper/src/app/utils/index.ts new file mode 100644 index 0000000000..cb094b5d7b --- /dev/null +++ b/apps/vyper/src/app/utils/index.ts @@ -0,0 +1,12 @@ +export * from './compiler' +export * from './remix-client' + +export function contractName(fileName: string): string { + const parts = fileName.split('/') + return parts[parts.length - 1] +} + +export function isVyper(name: string): boolean { + const parts = name.split('.') + return parts[parts.length - 1] === 'vy' +} \ No newline at end of file diff --git a/apps/vyper/src/app/utils/remix-client.tsx b/apps/vyper/src/app/utils/remix-client.tsx new file mode 100644 index 0000000000..080ba64a75 --- /dev/null +++ b/apps/vyper/src/app/utils/remix-client.tsx @@ -0,0 +1,71 @@ +import { HighlightPosition, CompilationResult, RemixApi } from '@remixproject/plugin-api'; +import { Api, Status } from '@remixproject/plugin-utils'; +import { createClient } from '@remixproject/plugin-webview' +import { PluginClient } from '@remixproject/plugin'; +import { Contract } from './compiler'; + +export class RemixClient extends PluginClient { + private client = createClient>(this); + + loaded() { + return this.client.onload() + } + + /** Emit an event when file changed */ + async onFileChange(cb: (contract: string) => any) { + this.client.on('fileManager', 'currentFileChanged', async (name: string) => { + if (!name) return + cb(name) + }) + } + + /** Load Ballot contract example into the file manager */ + async loadContract({name, content}: Contract) { + try { + await this.client.call('fileManager', 'setFile', name, content) + await this.client.call('fileManager', 'switchFile', name) + } catch (err) { + console.log(err) + } + } + + /** Update the status of the plugin in remix */ + changeStatus(status: Status) { + this.client.emit('statusChanged', status); + } + + /** Highlight a part of the editor */ + highlight(lineColumnPos: HighlightPosition, name: string, color: string) { + return this.client.call('editor', 'highlight', lineColumnPos, name, color) + } + + /** Remove current Hightlight */ + discardHighlight() { + return this.client.call('editor', 'discardHighlight') + } + + /** Get the name of the current contract */ + async getContractName(): Promise { + await this.client.onload() + return this.client.call('fileManager', 'getCurrentFile') + } + + /** Get the current contract file */ + async getContract(): Promise { + const name = await this.getContractName() + if (!name) throw new Error('No contract selected yet') + const content = await this.client.call('fileManager', 'getFile', name) + return { + name, + content, + } + } + + /** Emit an event to Remix with compilation result */ + compilationFinish(title: string, content: string, data: CompilationResult) { + this.client.emit('compilationFinished', title, content, 'vyper', data); + } +} + +export const remixClient = new RemixClient() +// export const RemixClientContext = React.createContext(new RemixClient()) \ No newline at end of file diff --git a/apps/vyper/src/assets/.gitkeep b/apps/vyper/src/assets/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/vyper/src/environments/environment.prod.ts b/apps/vyper/src/environments/environment.prod.ts new file mode 100644 index 0000000000..3612073bc3 --- /dev/null +++ b/apps/vyper/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true +}; diff --git a/apps/vyper/src/environments/environment.ts b/apps/vyper/src/environments/environment.ts new file mode 100644 index 0000000000..d9370e924b --- /dev/null +++ b/apps/vyper/src/environments/environment.ts @@ -0,0 +1,6 @@ +// This file can be replaced during build by using the `fileReplacements` array. +// When building for production, this file is replaced with `environment.prod.ts`. + +export const environment = { + production: false +}; diff --git a/apps/vyper/src/favicon.ico b/apps/vyper/src/favicon.ico new file mode 100644 index 0000000000..317ebcb233 Binary files /dev/null and b/apps/vyper/src/favicon.ico differ diff --git a/apps/vyper/src/index.html b/apps/vyper/src/index.html new file mode 100644 index 0000000000..b6b64f4a61 --- /dev/null +++ b/apps/vyper/src/index.html @@ -0,0 +1,14 @@ + + + + + Vyper + + + + + + +
+ + diff --git a/apps/vyper/src/main.tsx b/apps/vyper/src/main.tsx new file mode 100644 index 0000000000..353ad43f6d --- /dev/null +++ b/apps/vyper/src/main.tsx @@ -0,0 +1,7 @@ +import { StrictMode } from 'react'; +import * as ReactDOM from 'react-dom'; + + +import App from './app/app'; + +ReactDOM.render(, document.getElementById('root')); diff --git a/apps/vyper/src/polyfills.ts b/apps/vyper/src/polyfills.ts new file mode 100644 index 0000000000..2adf3d05b6 --- /dev/null +++ b/apps/vyper/src/polyfills.ts @@ -0,0 +1,7 @@ +/** + * Polyfill stable language features. These imports will be optimized by `@babel/preset-env`. + * + * See: https://github.com/zloirock/core-js#babel + */ +import 'core-js/stable'; +import 'regenerator-runtime/runtime'; diff --git a/apps/vyper/src/styles.css b/apps/vyper/src/styles.css new file mode 100644 index 0000000000..90d4ee0072 --- /dev/null +++ b/apps/vyper/src/styles.css @@ -0,0 +1 @@ +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/vyper/tsconfig.app.json b/apps/vyper/tsconfig.app.json new file mode 100644 index 0000000000..62d6d52c3d --- /dev/null +++ b/apps/vyper/tsconfig.app.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": ["node"] + }, + "files": [ + + "../../node_modules/@nrwl/react/typings/cssmodule.d.ts", + "../../node_modules/@nrwl/react/typings/image.d.ts" + ], + "exclude": ["**/*.spec.ts", "**/*.spec.tsx"], + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] +} diff --git a/apps/vyper/tsconfig.json b/apps/vyper/tsconfig.json new file mode 100644 index 0000000000..a3e71f89f3 --- /dev/null +++ b/apps/vyper/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + } + ] +} \ No newline at end of file diff --git a/libs/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.4.24.ts b/libs/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.4.24.ts index 8052a1870a..9bcaaf7a7b 100644 --- a/libs/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.4.24.ts +++ b/libs/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.4.24.ts @@ -38,7 +38,7 @@ const testFiles: string[] = [ 'forLoopIteratesOverDynamicArray.sol' ] -let compilationResults: Record = {} +const compilationResults: Record = {} test('setup', function (t: test.Test) { solcOrg.loadRemoteVersion('v0.4.24+commit.e67f0147', (error, compiler) => { diff --git a/libs/remix-core-plugin/src/lib/compiler-metadata.ts b/libs/remix-core-plugin/src/lib/compiler-metadata.ts index 2f065e769f..403c22b90b 100644 --- a/libs/remix-core-plugin/src/lib/compiler-metadata.ts +++ b/libs/remix-core-plugin/src/lib/compiler-metadata.ts @@ -15,7 +15,7 @@ export class CompilerMetadata extends Plugin { innerPath: string constructor () { super(profile) - this.networks = ['VM:-', 'main:1', 'ropsten:3', 'rinkeby:4', 'kovan:42', 'görli:5', 'Custom'] + this.networks = ['VM:-', 'main:1', 'ropsten:3', 'rinkeby:4', 'kovan:42', 'goerli:5', 'Custom'] this.innerPath = 'artifacts' } @@ -87,7 +87,7 @@ export class CompilerMetadata extends Plugin { let parsedMetadata try { - parsedMetadata = JSON.parse(contract.object.metadata) + parsedMetadata = contract.object && contract.object.metadata ? JSON.parse(contract.object.metadata) : null } catch (e) { console.log(e) } diff --git a/libs/remix-core-plugin/src/lib/constants/uups.ts b/libs/remix-core-plugin/src/lib/constants/uups.ts index 606d62dd77..e161929f01 100644 --- a/libs/remix-core-plugin/src/lib/constants/uups.ts +++ b/libs/remix-core-plugin/src/lib/constants/uups.ts @@ -102,4 +102,6 @@ export const UUPSupgradeAbi = { "outputs": [], "stateMutability": "nonpayable", "type": "function" -} \ No newline at end of file +} +export const EnableProxyURLParam = 'deployProxy' +export const EnableUpgradeURLParam = 'upgradeProxy' \ No newline at end of file diff --git a/libs/remix-core-plugin/src/lib/helpers/fetch-etherscan.ts b/libs/remix-core-plugin/src/lib/helpers/fetch-etherscan.ts index 16f6b284b8..1592663c38 100644 --- a/libs/remix-core-plugin/src/lib/helpers/fetch-etherscan.ts +++ b/libs/remix-core-plugin/src/lib/helpers/fetch-etherscan.ts @@ -1,24 +1,28 @@ -export const fetchContractFromEtherscan = async (plugin, network, contractAddress, targetPath, key?) => { +export const fetchContractFromEtherscan = async (plugin, network, contractAddress, targetPath, shouldSetFile = true, key?) => { let data const compilationTargets = {} let etherscanKey if (!key) etherscanKey = await plugin.call('config', 'getAppParameter', 'etherscan-access-token') else etherscanKey = key - + if (etherscanKey) { const endpoint = network.id == 1 ? 'api.etherscan.io' : 'api-' + network.name + '.etherscan.io' - data = await fetch('https://' + endpoint + '/api?module=contract&action=getsourcecode&address=' + contractAddress + '&apikey=' + etherscanKey) - data = await data.json() - // etherscan api doc https://docs.etherscan.io/api-endpoints/contracts - if (data.message === 'OK' && data.status === "1") { - if (data.result.length) { - if (data.result[0].SourceCode === '') throw new Error(`contract not verified on Etherscan ${network.name} network`) - if (data.result[0].SourceCode.startsWith('{')) { - data.result[0].SourceCode = JSON.parse(data.result[0].SourceCode.replace(/(?:\r\n|\r|\n)/g, '').replace(/^{{/,'{').replace(/}}$/,'}')) + try { + data = await fetch('https://' + endpoint + '/api?module=contract&action=getsourcecode&address=' + contractAddress + '&apikey=' + etherscanKey) + data = await data.json() + // etherscan api doc https://docs.etherscan.io/api-endpoints/contracts + if (data.message === 'OK' && data.status === "1") { + if (data.result.length) { + if (data.result[0].SourceCode === '') throw new Error(`contract not verified on Etherscan ${network.name} network`) + if (data.result[0].SourceCode.startsWith('{')) { + data.result[0].SourceCode = JSON.parse(data.result[0].SourceCode.replace(/(?:\r\n|\r|\n)/g, '').replace(/^{{/, '{').replace(/}}$/, '}')) + } } - } - } else throw new Error('unable to retrieve contract data ' + data.message) + } else throw new Error('unable to retrieve contract data ' + data.message) + } catch (e) { + throw new Error('unable to retrieve contract data: ' + e.message) + } } else throw new Error('unable to try fetching the source code from etherscan: etherscan access token not found. please go to the Remix settings page and provide an access token.') if (!data || !data.result) { @@ -27,7 +31,7 @@ export const fetchContractFromEtherscan = async (plugin, network, contractAddres if (typeof data.result[0].SourceCode === 'string') { const fileName = `${targetPath}/${data.result[0].ContractName}.sol` - await plugin.call('fileManager', 'setFile', fileName , data.result[0].SourceCode) + if (shouldSetFile) await plugin.call('fileManager', 'setFile', fileName, data.result[0].SourceCode) compilationTargets[fileName] = { content: data.result[0].SourceCode } } else if (data.result[0].SourceCode && typeof data.result[0].SourceCode == 'object') { const sources = data.result[0].SourceCode.sources @@ -36,10 +40,10 @@ export const fetchContractFromEtherscan = async (plugin, network, contractAddres file = file.replace(/^\//g, '') // remove first slash. if (await plugin.call('contentImport', 'isExternalUrl', file)) { // nothing to do, the compiler callback will handle those - } else { + } else { const path = `${targetPath}/${file}` const content = (source as any).content - await plugin.call('fileManager', 'setFile', path, content) + if (shouldSetFile) await plugin.call('fileManager', 'setFile', path, content) compilationTargets[path] = { content } } } @@ -47,7 +51,7 @@ export const fetchContractFromEtherscan = async (plugin, network, contractAddres let runs = 0 try { runs = parseInt(data.result[0].Runs) - } catch (e) {} + } catch (e) { } const settings = { version: data.result[0].CompilerVersion.replace(/^v/, ''), language: 'Solidity', diff --git a/libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts b/libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts index ef03092cb8..879351c58f 100644 --- a/libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts +++ b/libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts @@ -1,6 +1,6 @@ import { Plugin } from '@remixproject/engine' import { ContractAST, ContractSources, DeployOptions } from '../types/contract' -import { UUPS, UUPSABI, UUPSBytecode, UUPSfunAbi, UUPSupgradeAbi } from './constants/uups' +import { EnableProxyURLParam, EnableUpgradeURLParam, UUPS, UUPSABI, UUPSBytecode, UUPSfunAbi, UUPSupgradeAbi } from './constants/uups' const proxyProfile = { name: 'openzeppelin-proxy', @@ -33,31 +33,40 @@ export class OpenZeppelinProxy extends Plugin { async getProxyOptions (data: ContractSources, file: string): Promise<{ [name: string]: DeployOptions }> { const contracts = data.contracts[file] const ast = data.sources[file].ast - const inputs = {} if (this.kind === 'UUPS') { - Object.keys(contracts).map(name => { - if (ast) { - const UUPSSymbol = ast.exportedSymbols[UUPS] ? ast.exportedSymbols[UUPS][0] : null - - ast.absolutePath === file && ast.nodes.map((node) => { - if (node.name === name && node.linearizedBaseContracts.includes(UUPSSymbol)) { - const abi = contracts[name].abi - const initializeInput = abi.find(node => node.name === 'initialize') - - inputs[name] = { - options: [{ title: 'Deploy with Proxy', active: false }, { title: 'Upgrade with Proxy', active: false }], - initializeOptions: { - inputs: initializeInput, - initializeInputs: initializeInput ? this.blockchain.getInputs(initializeInput) : null - } + const options = await (this.getUUPSContractOptions(contracts, ast, file)) + + return options + } + } + + async getUUPSContractOptions (contracts, ast, file) { + const options = {} + + await Promise.all(Object.keys(contracts).map(async (name) => { + if (ast) { + const UUPSSymbol = ast.exportedSymbols[UUPS] ? ast.exportedSymbols[UUPS][0] : null + + await Promise.all(ast.absolutePath === file && ast.nodes.map(async (node) => { + if (node.name === name && node.linearizedBaseContracts.includes(UUPSSymbol)) { + const abi = contracts[name].abi + const initializeInput = abi.find(node => node.name === 'initialize') + const isDeployWithProxyEnabled: boolean = await this.call('config', 'getAppParameter', EnableProxyURLParam) || false + const isDeployWithUpgradeEnabled: boolean = await this.call('config', 'getAppParameter', EnableUpgradeURLParam) || false + + options[name] = { + options: [{ title: 'Deploy with Proxy', active: isDeployWithProxyEnabled }, { title: 'Upgrade with Proxy', active: isDeployWithUpgradeEnabled }], + initializeOptions: { + inputs: initializeInput, + initializeInputs: initializeInput ? this.blockchain.getInputs(initializeInput) : null } } - }) - } - }) - } - return inputs + } + })) + } + })) + return options } async executeUUPSProxy(implAddress: string, args: string | string [] = '', initializeABI, implementationContractObject): Promise { diff --git a/libs/remix-tests/package.json b/libs/remix-tests/package.json index c4a02aef60..7aab939286 100644 --- a/libs/remix-tests/package.json +++ b/libs/remix-tests/package.json @@ -42,6 +42,7 @@ "@remix-project/remix-lib": "^0.5.15", "@remix-project/remix-simulator": "^0.2.15", "@remix-project/remix-solidity": "^0.5.1", + "@remix-project/remix-url-resolver": "^0.0.36", "ansi-gray": "^0.1.1", "async": "^2.6.0", "axios": ">=0.21.1", diff --git a/libs/remix-tests/src/compiler.ts b/libs/remix-tests/src/compiler.ts index f16c925edc..b399f7376e 100644 --- a/libs/remix-tests/src/compiler.ts +++ b/libs/remix-tests/src/compiler.ts @@ -4,6 +4,7 @@ import path from 'path' import deepequal from 'deep-equal' import Log from './logger' import { Compiler as RemixCompiler } from '@remix-project/remix-solidity' +import { RemixURLResolver } from '@remix-project/remix-url-resolver' import { SrcIfc, CompilerConfiguration, CompilationErrors } from './types' const logger = new Log() const log = logger.logger @@ -85,6 +86,17 @@ export function compileFileOrFiles (filename: string, isDirectory: boolean, opts 'remix_accounts.sol': { content: writeTestAccountsContract(accounts) } } const filepath: string = (isDirectory ? filename : path.dirname(filename)) + const importsCallback = (url, cb) => { + try { + if(fs.existsSync(url)) cb(null, fs.readFileSync(url, 'utf-8')) + else { + const urlResolver = new RemixURLResolver() + urlResolver.resolve(url).then((result) => cb(null, result.content)).catch((error) => cb(error.message)) + } + } catch (e) { + cb(e.message) + } + } try { if (!isDirectory && fs.existsSync(filename)) { if (filename.split('.').pop() === 'sol') { @@ -114,13 +126,7 @@ export function compileFileOrFiles (filename: string, isDirectory: boolean, opts } finally { async.waterfall([ function loadCompiler (next) { - compiler = new RemixCompiler((url, cb) => { - try { - cb(null, fs.readFileSync(url, 'utf-8')) - } catch (e) { - cb(e.message) - } - }) + compiler = new RemixCompiler(importsCallback) if (compilerConfig) { const { currentCompilerUrl, evmVersion, optimize, runs } = compilerConfig if (evmVersion) compiler.set('evmVersion', evmVersion) diff --git a/libs/remix-tests/src/run.ts b/libs/remix-tests/src/run.ts index 45b89df5e7..db6f21780e 100644 --- a/libs/remix-tests/src/run.ts +++ b/libs/remix-tests/src/run.ts @@ -4,7 +4,7 @@ import path from 'path' import axios, { AxiosResponse } from 'axios' import { runTestFiles } from './runTestFiles' import fs from './fileSystem' -import { Provider } from '@remix-project/remix-simulator' +import { Provider, extend } from '@remix-project/remix-simulator' import { CompilerConfiguration } from './types' import Log from './logger' import colors from 'colors' @@ -64,11 +64,11 @@ commander // If path is for a file, file name must have `_test.sol` suffix if (!isDirectory && !testsPath.endsWith('_test.sol')) { log.error('Test filename should end with "_test.sol"') - process.exit() + process.exit(1) } // Console message - console.log(colors.white('\n\t👁\t:: Running remix-tests - Unit testing for solidity ::\t👁\n')) + console.log(colors.bold('\n\t👁\t:: Running tests using remix-tests ::\t👁\n')) // Set logger verbosity if (commander.verbose) { @@ -85,7 +85,7 @@ commander const compString = releases ? releases[compVersion] : null if (!compString) { log.error(`No compiler found in releases with version ${compVersion}`) - process.exit() + process.exit(1) } else { compilerConfig.currentCompilerUrl = compString.replace('soljson-', '').replace('.js', '') log.info(`Compiler version set to ${compVersion}. Latest version is ${latestRelease}`) @@ -105,7 +105,7 @@ commander if (commander.runs) { if (!commander.optimize) { log.error('Optimization should be enabled for runs') - process.exit() + process.exit(1) } compilerConfig.runs = commander.runs log.info(`Runs set to ${compilerConfig.runs}`) @@ -115,13 +115,15 @@ commander const provider: any = new Provider() await provider.init() web3.setProvider(provider) - - runTestFiles(path.resolve(testsPath), isDirectory, web3, compilerConfig) + extend(web3) + runTestFiles(path.resolve(testsPath), isDirectory, web3, compilerConfig, (error) => { + if (error) process.exit(1) + }) }) if (!process.argv.slice(2).length) { log.error('Please specify a file or directory path') - process.exit() + process.exit(1) } commander.parse(process.argv) diff --git a/libs/remix-tests/src/runTestFiles.ts b/libs/remix-tests/src/runTestFiles.ts index ebaaf27f98..143f38e45c 100644 --- a/libs/remix-tests/src/runTestFiles.ts +++ b/libs/remix-tests/src/runTestFiles.ts @@ -4,7 +4,7 @@ import { runTest } from './testRunner' import { TestResultInterface, ResultsInterface, CompilerConfiguration, compilationInterface, ASTInterface, Options, AstNode } from './types' import colors from 'colors' import Web3 from 'web3' - +import { format } from 'util' import { compileFileOrFiles } from './compiler' import { deployAll } from './deployer' @@ -22,6 +22,15 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3 opts = opts || {} compilerConfig = compilerConfig || {} as CompilerConfiguration const sourceASTs: any = {} + const printLog = (log: string[]) => { + let formattedLog + if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) { + formattedLog = format(log[0], ...log.slice(1)) + } else { + formattedLog = log.join(' ') + } + signale.log(formattedLog) + } const { Signale } = require('signale') // eslint-disable-line // signale configuration const options = { @@ -34,6 +43,11 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3 name: { badge: '\n\t◼', label: '', + color: 'whiteBright' + }, + log: { + badge: '\t', + label: '', color: 'white' }, error: { @@ -104,17 +118,24 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3 let totalPassing = 0 let totalFailing = 0 let totalTime = 0 - const errors: any[] = [] const _testCallback = function (err: Error | null | undefined, result: TestResultInterface) { if (err) throw err if (result.type === 'contract') { - signale.name(result.value.white) + signale.name(result.value) + console.log('\n') } else if (result.type === 'testPass') { - signale.result(result.value) + if (result?.hhLogs?.length) result.hhLogs.forEach(printLog) + signale.result(result.value.white) } else if (result.type === 'testFailure') { - signale.error(result.value.red) - errors.push(result) + if (result?.hhLogs?.length) result.hhLogs.forEach(printLog) + signale.error(result.value.white) + if (result.assertMethod) { + console.log(colors.green('\t Expected value should be ' + result.assertMethod + ' to: ' + result.expected)) + console.log(colors.red('\t Received: ' + result.returned)) + } + console.log(colors.red('\t Message: ' + result.errMsg)) + console.log('\n') } } const _resultsCallback = (_err: Error | null | undefined, result: ResultsInterface, cb) => { @@ -127,7 +148,7 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3 async.eachOfLimit(contractsToTest, 1, (contractName: string, index, cb) => { try { const fileAST: AstNode = sourceASTs[contracts[contractName]['filename']] - runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts }, _testCallback, (err, result) => { + runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts, web3 }, _testCallback, (err, result) => { if (err) { console.log(err) return cb(err) @@ -141,23 +162,16 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3 if (err) { return next(err) } - console.log('\n') - if (totalPassing > 0) { - console.log(colors.green(totalPassing + ' passing ') + colors.grey('(' + totalTime + 's)')) + console.log(colors.bold.underline('Tests Summary: ')) + + if (totalPassing >= 0) { + console.log(colors.green('Passed: ' + totalPassing)) } - if (totalFailing > 0) { - console.log(colors.red(totalFailing + ' failing')) + if (totalFailing >= 0) { + console.log(colors.red('Failed: ' + totalFailing)) } - console.log('') - - errors.forEach((error, index) => { - console.log(' ' + (index + 1) + ') ' + colors.bold(error.context + ': ') + error.value) - console.log('') - console.log(colors.red('\t error: ' + error.errMsg)) - console.log(colors.green('\t expected value to be ' + error.assertMethod + ' to: ' + error.expected)) - console.log(colors.red('\t returned: ' + error.returned)) - }) + console.log(colors.white('Time Taken: ' + totalTime + 's')) console.log('') next() diff --git a/libs/remix-tests/tests/examples_0/assert_ok_test.sol b/libs/remix-tests/tests/examples_0/assert_ok_test.sol index 04af584e63..497d0dd7ec 100644 --- a/libs/remix-tests/tests/examples_0/assert_ok_test.sol +++ b/libs/remix-tests/tests/examples_0/assert_ok_test.sol @@ -1,7 +1,5 @@ import "remix_tests.sol"; // this import is automatically injected by Remix. - - -import "./hardhat/console.sol"; +import "hardhat/console.sol"; contract AssertOkTest { diff --git a/libs/remix-tests/tests/examples_0/assert_ok_without_console_test.sol b/libs/remix-tests/tests/examples_0/assert_ok_without_console_test.sol deleted file mode 100644 index 030552b16a..0000000000 --- a/libs/remix-tests/tests/examples_0/assert_ok_without_console_test.sol +++ /dev/null @@ -1,12 +0,0 @@ -import "remix_tests.sol"; // this import is automatically injected by Remix. - -contract AssertOkTest { - - function okPassTest() public { - Assert.ok(true, "okPassTest passes"); - } - - function okFailTest() public { - Assert.ok(false, "okFailTest fails"); - } -} \ No newline at end of file diff --git a/libs/remix-tests/tests/examples_0/hardhat/console.sol b/libs/remix-tests/tests/examples_0/hardhat/console.sol deleted file mode 100644 index d65e3b412e..0000000000 --- a/libs/remix-tests/tests/examples_0/hardhat/console.sol +++ /dev/null @@ -1,1532 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >= 0.4.22 <0.9.0; - -library console { - address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); - - function _sendLogPayload(bytes memory payload) private view { - uint256 payloadLength = payload.length; - address consoleAddress = CONSOLE_ADDRESS; - assembly { - let payloadStart := add(payload, 32) - let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0) - } - } - - function log() internal view { - _sendLogPayload(abi.encodeWithSignature("log()")); - } - - function logInt(int p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(int)", p0)); - } - - function logUint(uint p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); - } - - function logString(string memory p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); - } - - function logBool(bool p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); - } - - function logAddress(address p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); - } - - function logBytes(bytes memory p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); - } - - function logBytes1(bytes1 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); - } - - function logBytes2(bytes2 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); - } - - function logBytes3(bytes3 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); - } - - function logBytes4(bytes4 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); - } - - function logBytes5(bytes5 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); - } - - function logBytes6(bytes6 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); - } - - function logBytes7(bytes7 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); - } - - function logBytes8(bytes8 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); - } - - function logBytes9(bytes9 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); - } - - function logBytes10(bytes10 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); - } - - function logBytes11(bytes11 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); - } - - function logBytes12(bytes12 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); - } - - function logBytes13(bytes13 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); - } - - function logBytes14(bytes14 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); - } - - function logBytes15(bytes15 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); - } - - function logBytes16(bytes16 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); - } - - function logBytes17(bytes17 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); - } - - function logBytes18(bytes18 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); - } - - function logBytes19(bytes19 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); - } - - function logBytes20(bytes20 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); - } - - function logBytes21(bytes21 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); - } - - function logBytes22(bytes22 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); - } - - function logBytes23(bytes23 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); - } - - function logBytes24(bytes24 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); - } - - function logBytes25(bytes25 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); - } - - function logBytes26(bytes26 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); - } - - function logBytes27(bytes27 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); - } - - function logBytes28(bytes28 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); - } - - function logBytes29(bytes29 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); - } - - function logBytes30(bytes30 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); - } - - function logBytes31(bytes31 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); - } - - function logBytes32(bytes32 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); - } - - function log(uint p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); - } - - function log(string memory p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); - } - - function log(bool p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); - } - - function log(address p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); - } - - function log(uint p0, uint p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1)); - } - - function log(uint p0, string memory p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1)); - } - - function log(uint p0, bool p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1)); - } - - function log(uint p0, address p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1)); - } - - function log(string memory p0, uint p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1)); - } - - function log(string memory p0, string memory p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); - } - - function log(string memory p0, bool p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); - } - - function log(string memory p0, address p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); - } - - function log(bool p0, uint p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1)); - } - - function log(bool p0, string memory p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); - } - - function log(bool p0, bool p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); - } - - function log(bool p0, address p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); - } - - function log(address p0, uint p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1)); - } - - function log(address p0, string memory p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); - } - - function log(address p0, bool p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); - } - - function log(address p0, address p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); - } - - function log(uint p0, uint p1, uint p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2)); - } - - function log(uint p0, uint p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2)); - } - - function log(uint p0, uint p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2)); - } - - function log(uint p0, uint p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2)); - } - - function log(uint p0, string memory p1, uint p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2)); - } - - function log(uint p0, string memory p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2)); - } - - function log(uint p0, string memory p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2)); - } - - function log(uint p0, string memory p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2)); - } - - function log(uint p0, bool p1, uint p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2)); - } - - function log(uint p0, bool p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2)); - } - - function log(uint p0, bool p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2)); - } - - function log(uint p0, bool p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2)); - } - - function log(uint p0, address p1, uint p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2)); - } - - function log(uint p0, address p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2)); - } - - function log(uint p0, address p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2)); - } - - function log(uint p0, address p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2)); - } - - function log(string memory p0, uint p1, uint p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2)); - } - - function log(string memory p0, uint p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2)); - } - - function log(string memory p0, uint p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2)); - } - - function log(string memory p0, uint p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2)); - } - - function log(string memory p0, string memory p1, uint p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2)); - } - - function log(string memory p0, string memory p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); - } - - function log(string memory p0, string memory p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); - } - - function log(string memory p0, string memory p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); - } - - function log(string memory p0, bool p1, uint p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2)); - } - - function log(string memory p0, bool p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); - } - - function log(string memory p0, bool p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); - } - - function log(string memory p0, bool p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); - } - - function log(string memory p0, address p1, uint p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2)); - } - - function log(string memory p0, address p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); - } - - function log(string memory p0, address p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); - } - - function log(string memory p0, address p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); - } - - function log(bool p0, uint p1, uint p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2)); - } - - function log(bool p0, uint p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2)); - } - - function log(bool p0, uint p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2)); - } - - function log(bool p0, uint p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2)); - } - - function log(bool p0, string memory p1, uint p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2)); - } - - function log(bool p0, string memory p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); - } - - function log(bool p0, string memory p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); - } - - function log(bool p0, string memory p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); - } - - function log(bool p0, bool p1, uint p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2)); - } - - function log(bool p0, bool p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); - } - - function log(bool p0, bool p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); - } - - function log(bool p0, bool p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); - } - - function log(bool p0, address p1, uint p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2)); - } - - function log(bool p0, address p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); - } - - function log(bool p0, address p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); - } - - function log(bool p0, address p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); - } - - function log(address p0, uint p1, uint p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2)); - } - - function log(address p0, uint p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2)); - } - - function log(address p0, uint p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2)); - } - - function log(address p0, uint p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2)); - } - - function log(address p0, string memory p1, uint p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2)); - } - - function log(address p0, string memory p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); - } - - function log(address p0, string memory p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); - } - - function log(address p0, string memory p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); - } - - function log(address p0, bool p1, uint p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2)); - } - - function log(address p0, bool p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); - } - - function log(address p0, bool p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); - } - - function log(address p0, bool p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); - } - - function log(address p0, address p1, uint p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2)); - } - - function log(address p0, address p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); - } - - function log(address p0, address p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); - } - - function log(address p0, address p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); - } - - function log(uint p0, uint p1, uint p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3)); - } - - function log(uint p0, uint p1, uint p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3)); - } - - function log(uint p0, uint p1, uint p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3)); - } - - function log(uint p0, uint p1, uint p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3)); - } - - function log(uint p0, uint p1, string memory p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3)); - } - - function log(uint p0, uint p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3)); - } - - function log(uint p0, uint p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3)); - } - - function log(uint p0, uint p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3)); - } - - function log(uint p0, uint p1, bool p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3)); - } - - function log(uint p0, uint p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3)); - } - - function log(uint p0, uint p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3)); - } - - function log(uint p0, uint p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3)); - } - - function log(uint p0, uint p1, address p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3)); - } - - function log(uint p0, uint p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3)); - } - - function log(uint p0, uint p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3)); - } - - function log(uint p0, uint p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3)); - } - - function log(uint p0, string memory p1, uint p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3)); - } - - function log(uint p0, string memory p1, uint p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3)); - } - - function log(uint p0, string memory p1, uint p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3)); - } - - function log(uint p0, string memory p1, uint p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3)); - } - - function log(uint p0, string memory p1, string memory p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3)); - } - - function log(uint p0, string memory p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3)); - } - - function log(uint p0, string memory p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3)); - } - - function log(uint p0, string memory p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3)); - } - - function log(uint p0, string memory p1, bool p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3)); - } - - function log(uint p0, string memory p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3)); - } - - function log(uint p0, string memory p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3)); - } - - function log(uint p0, string memory p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3)); - } - - function log(uint p0, string memory p1, address p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3)); - } - - function log(uint p0, string memory p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3)); - } - - function log(uint p0, string memory p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3)); - } - - function log(uint p0, string memory p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3)); - } - - function log(uint p0, bool p1, uint p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3)); - } - - function log(uint p0, bool p1, uint p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3)); - } - - function log(uint p0, bool p1, uint p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3)); - } - - function log(uint p0, bool p1, uint p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3)); - } - - function log(uint p0, bool p1, string memory p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3)); - } - - function log(uint p0, bool p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3)); - } - - function log(uint p0, bool p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3)); - } - - function log(uint p0, bool p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3)); - } - - function log(uint p0, bool p1, bool p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3)); - } - - function log(uint p0, bool p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3)); - } - - function log(uint p0, bool p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3)); - } - - function log(uint p0, bool p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3)); - } - - function log(uint p0, bool p1, address p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3)); - } - - function log(uint p0, bool p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3)); - } - - function log(uint p0, bool p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3)); - } - - function log(uint p0, bool p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3)); - } - - function log(uint p0, address p1, uint p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3)); - } - - function log(uint p0, address p1, uint p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3)); - } - - function log(uint p0, address p1, uint p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3)); - } - - function log(uint p0, address p1, uint p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3)); - } - - function log(uint p0, address p1, string memory p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3)); - } - - function log(uint p0, address p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3)); - } - - function log(uint p0, address p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3)); - } - - function log(uint p0, address p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3)); - } - - function log(uint p0, address p1, bool p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3)); - } - - function log(uint p0, address p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3)); - } - - function log(uint p0, address p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3)); - } - - function log(uint p0, address p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3)); - } - - function log(uint p0, address p1, address p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3)); - } - - function log(uint p0, address p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3)); - } - - function log(uint p0, address p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3)); - } - - function log(uint p0, address p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint p1, uint p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint p1, uint p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint p1, uint p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint p1, uint p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint p1, string memory p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint p1, bool p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint p1, address p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, uint p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, uint p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, uint p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, uint p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, string memory p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, bool p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, address p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, uint p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, uint p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, uint p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, uint p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, string memory p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, bool p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, address p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, uint p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, uint p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, uint p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, uint p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, string memory p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, bool p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, address p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); - } - - function log(bool p0, uint p1, uint p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, uint p1, uint p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3)); - } - - function log(bool p0, uint p1, uint p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, uint p1, uint p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3)); - } - - function log(bool p0, uint p1, string memory p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, uint p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3)); - } - - function log(bool p0, uint p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, uint p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3)); - } - - function log(bool p0, uint p1, bool p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, uint p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3)); - } - - function log(bool p0, uint p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, uint p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3)); - } - - function log(bool p0, uint p1, address p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, uint p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3)); - } - - function log(bool p0, uint p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, uint p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, uint p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, uint p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, uint p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, uint p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, string memory p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, bool p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, address p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, uint p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, uint p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, uint p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, uint p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, string memory p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, bool p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, address p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, uint p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, uint p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, uint p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, uint p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, string memory p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, bool p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, address p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); - } - - function log(address p0, uint p1, uint p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3)); - } - - function log(address p0, uint p1, uint p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3)); - } - - function log(address p0, uint p1, uint p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3)); - } - - function log(address p0, uint p1, uint p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3)); - } - - function log(address p0, uint p1, string memory p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3)); - } - - function log(address p0, uint p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3)); - } - - function log(address p0, uint p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3)); - } - - function log(address p0, uint p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3)); - } - - function log(address p0, uint p1, bool p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3)); - } - - function log(address p0, uint p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3)); - } - - function log(address p0, uint p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3)); - } - - function log(address p0, uint p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3)); - } - - function log(address p0, uint p1, address p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3)); - } - - function log(address p0, uint p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3)); - } - - function log(address p0, uint p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3)); - } - - function log(address p0, uint p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, uint p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, uint p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, uint p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, uint p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, string memory p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, bool p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, address p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, uint p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, uint p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, uint p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, uint p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, string memory p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, bool p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, address p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, uint p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, uint p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, uint p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, uint p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, string memory p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, bool p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, address p2, uint p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); - } - -} diff --git a/libs/remix-tests/tests/testRunner.cli.spec.ts b/libs/remix-tests/tests/testRunner.cli.spec.ts index 65bea514da..a7b9953dd9 100644 --- a/libs/remix-tests/tests/testRunner.cli.spec.ts +++ b/libs/remix-tests/tests/testRunner.cli.spec.ts @@ -42,101 +42,103 @@ Commands: }) test('remix-tests running a test file', () => { - const res = spawnSync(executablePath, [resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')]) + const res = spawnSync(executablePath, [resolve(__dirname + '/examples_0/assert_ok_test.sol')]) // match initial lines - expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/) + expect(res.stdout.toString().trim()).toMatch(/:: Running tests using remix-tests ::/) expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../) // match test result expect(res.stdout.toString().trim()).toMatch(/AssertOkTest/) + expect(res.stdout.toString().trim()).toMatch(/AssertOkTest okPassTest/) // check if console.log is printed expect(res.stdout.toString().trim()).toMatch(/Ok pass test/) + expect(res.stdout.toString().trim()).toMatch(/AssertOkTest okFailTest/) // check if console.log is printed expect(res.stdout.toString().trim()).toMatch(/Ok fail test/) // match fail test details - expect(res.stdout.toString().trim()).toMatch(/error: okFailTest fails/) - expect(res.stdout.toString().trim()).toMatch(/expected value to be ok to: true/) - expect(res.stdout.toString().trim()).toMatch(/returned: false/) + expect(res.stdout.toString().trim()).toMatch(/Expected value should be ok to: true/) + expect(res.stdout.toString().trim()).toMatch(/Received: false/) + expect(res.stdout.toString().trim()).toMatch(/Message: okFailTest fails/) }) test('remix-tests running a test file with custom compiler version', () => { - const res = spawnSync(executablePath, ['--compiler', '0.7.4', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')]) + const res = spawnSync(executablePath, ['--compiler', '0.7.4', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) // match initial lines expect(res.stdout.toString().trim().includes('Compiler version set to 0.7.4. Latest version is')).toBeTruthy() expect(res.stdout.toString().trim().includes('Loading remote solc version v0.7.4+commit.3f05b770 ...')).toBeTruthy() - expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/) + expect(res.stdout.toString().trim()).toMatch(/:: Running tests using remix-tests ::/) expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../) // match test result expect(res.stdout.toString().trim()).toMatch(/Ok pass test/) expect(res.stdout.toString().trim()).toMatch(/Ok fail test/) // match fail test details - expect(res.stdout.toString().trim()).toMatch(/error: okFailTest fails/) + expect(res.stdout.toString().trim()).toMatch(/Message: okFailTest fails/) }) test('remix-tests running a test file with unavailable custom compiler version (should fail)', () => { - const res = spawnSync(executablePath, ['--compiler', '1.10.4', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')]) + const res = spawnSync(executablePath, ['--compiler', '1.10.4', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) // match initial lines expect(res.stdout.toString().trim().includes('No compiler found in releases with version 1.10.4')).toBeTruthy() }) test('remix-tests running a test file with custom EVM', () => { - const res = spawnSync(executablePath, ['--evm', 'petersburg', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')]) + const res = spawnSync(executablePath, ['--evm', 'petersburg', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) // match initial lines expect(res.stdout.toString().trim().includes('EVM set to petersburg')).toBeTruthy() - expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/) + expect(res.stdout.toString().trim()).toMatch(/:: Running tests using remix-tests ::/) expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../) // match test result expect(res.stdout.toString().trim()).toMatch(/Ok pass test/) expect(res.stdout.toString().trim()).toMatch(/Ok fail test/) // match fail test details - expect(res.stdout.toString().trim()).toMatch(/error: okFailTest fails/) + expect(res.stdout.toString().trim()).toMatch(/Message: okFailTest fails/) }) test('remix-tests running a test file by enabling optimization', () => { - const res = spawnSync(executablePath, ['--optimize', 'true', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')]) + const res = spawnSync(executablePath, ['--optimize', 'true', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) // match initial lines expect(res.stdout.toString().trim().includes('Optimization is enabled')).toBeTruthy() - expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/) + expect(res.stdout.toString().trim()).toMatch(/:: Running tests using remix-tests ::/) expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../) // match test result expect(res.stdout.toString().trim()).toMatch(/Ok pass test/) expect(res.stdout.toString().trim()).toMatch(/Ok fail test/) // match fail test details - expect(res.stdout.toString().trim()).toMatch(/error: okFailTest fails/) + expect(res.stdout.toString().trim()).toMatch(/Message: okFailTest fails/) }) test('remix-tests running a test file by enabling optimization and setting runs', () => { - const res = spawnSync(executablePath, ['--optimize', 'true', '--runs', '300', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')]) + const res = spawnSync(executablePath, ['--optimize', 'true', '--runs', '300', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) // match initial lines expect(res.stdout.toString().trim().includes('Optimization is enabled')).toBeTruthy() expect(res.stdout.toString().trim().includes('Runs set to 300')).toBeTruthy() - expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/) + expect(res.stdout.toString().trim()).toMatch(/:: Running tests using remix-tests ::/) expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../) // match test result expect(res.stdout.toString().trim()).toMatch(/Ok pass test/) expect(res.stdout.toString().trim()).toMatch(/Ok fail test/) // match fail test details - expect(res.stdout.toString().trim()).toMatch(/error: okFailTest fails/) + expect(res.stdout.toString().trim()).toMatch(/Message: okFailTest fails/) }) test('remix-tests running a test file without enabling optimization and setting runs (should fail)', () => { - const res = spawnSync(executablePath, ['--runs', '300', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')]) + const res = spawnSync(executablePath, ['--runs', '300', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) // match initial lines expect(res.stdout.toString().trim().includes('Optimization should be enabled for runs')).toBeTruthy() }) test('remix-tests running a test file with all options', () => { - const res = spawnSync(executablePath, ['--compiler', '0.7.5', '--evm', 'istanbul', '--optimize', 'true', '--runs', '250', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')]) + const res = spawnSync(executablePath, ['--compiler', '0.7.5', '--evm', 'istanbul', '--optimize', 'true', '--runs', '250', resolve(__dirname + '/examples_0/assert_ok_test.sol')]) // match initial lines expect(res.stdout.toString().trim().includes('Compiler version set to 0.7.5. Latest version is')).toBeTruthy() expect(res.stdout.toString().trim().includes('Loading remote solc version v0.7.5+commit.eb77ed08 ...')).toBeTruthy() expect(res.stdout.toString().trim().includes('EVM set to istanbul')).toBeTruthy() expect(res.stdout.toString().trim().includes('Optimization is enabled')).toBeTruthy() expect(res.stdout.toString().trim().includes('Runs set to 250')).toBeTruthy() - expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/) + expect(res.stdout.toString().trim()).toMatch(/:: Running tests using remix-tests ::/) expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../) // match test result expect(res.stdout.toString().trim()).toMatch(/Ok pass test/) expect(res.stdout.toString().trim()).toMatch(/Ok fail test/) // match fail test details - expect(res.stdout.toString().trim()).toMatch(/error: okFailTest fails/) + expect(res.stdout.toString().trim()).toMatch(/Message: okFailTest fails/) }) }) }) \ No newline at end of file diff --git a/libs/remix-tests/tests/testRunner.spec.ts b/libs/remix-tests/tests/testRunner.spec.ts index 2f6d363a31..c475914522 100644 --- a/libs/remix-tests/tests/testRunner.spec.ts +++ b/libs/remix-tests/tests/testRunner.spec.ts @@ -129,7 +129,7 @@ describe('testRunner', () => { { type: 'accountList', value: accounts }, { type: 'contract', value: 'AssertOkTest', filename: __dirname + '/examples_0/assert_ok_test.sol' }, { type: 'testPass', debugTxHash: '0x5b665752a4faf83229259b9b2811d3295be0af633b0051d4b90042283ef55707', value: 'Ok pass test', filename: __dirname + '/examples_0/assert_ok_test.sol', context: 'AssertOkTest', hhLogs: hhLogs1 }, - { type: 'testFailure', debugTxHash: '0xa0a30ad042a7fc3495f72be7ba788d705888ffbbec7173f60bb27e07721510f2',value: 'Ok fail test', filename: __dirname + '/examples_0/assert_ok_test.sol', errMsg: 'okFailTest fails', context: 'AssertOkTest', hhLogs: hhLogs2, assertMethod: 'ok', location: '370:36:0', expected: 'true', returned: 'false'}, + { type: 'testFailure', debugTxHash: '0xa0a30ad042a7fc3495f72be7ba788d705888ffbbec7173f60bb27e07721510f2',value: 'Ok fail test', filename: __dirname + '/examples_0/assert_ok_test.sol', errMsg: 'okFailTest fails', context: 'AssertOkTest', hhLogs: hhLogs2, assertMethod: 'ok', location: '366:36:0', expected: 'true', returned: 'false'}, ], ['time', 'web3']) }) diff --git a/libs/remix-ui/clipboard/src/lib/copy-to-clipboard/copy-to-clipboard.tsx b/libs/remix-ui/clipboard/src/lib/copy-to-clipboard/copy-to-clipboard.tsx index 2220bc1d1b..a90dc433af 100644 --- a/libs/remix-ui/clipboard/src/lib/copy-to-clipboard/copy-to-clipboard.tsx +++ b/libs/remix-ui/clipboard/src/lib/copy-to-clipboard/copy-to-clipboard.tsx @@ -36,7 +36,7 @@ export const CopyToClipboard = (props: ICopyToClipboard) => { } } - const handleClick = (e) => { + const handleClick = (e: any) => { if (content) { // module `copy` keeps last copied thing in the memory, so don't show tooltip if nothing is copied, because nothing was added to memory copyData() } else { diff --git a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/global-variables.tsx b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/global-variables.tsx index 98713182cd..cf34760ae2 100644 --- a/libs/remix-ui/debugger-ui/src/lib/vm-debugger/global-variables.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/vm-debugger/global-variables.tsx @@ -1,6 +1,7 @@ import React from 'react' // eslint-disable-line import DropdownPanel from './dropdown-panel' // eslint-disable-line import { BN } from 'ethereumjs-util' +import Web3 from 'web3' export const GlobalVariables = ({ block, receipt, tx }) => { // see https://docs.soliditylang.org/en/latest/units-and-global-variables.html#block-and-transaction-properties @@ -17,7 +18,7 @@ export const GlobalVariables = ({ block, receipt, tx }) => { 'tx.origin': tx.from } if (block.baseFeePerGas) { - globals['block.basefee'] = (new BN(block.baseFeePerGas.replace('0x', ''), 'hex')).toString(10) + ` Wei (${block.baseFeePerGas})` + globals['block.basefee'] = Web3.utils.toBN(block.baseFeePerGas).toString(10) + ` Wei (${block.baseFeePerGas})` } return ( diff --git a/libs/remix-ui/editor/src/lib/cairoSyntax.ts b/libs/remix-ui/editor/src/lib/cairoSyntax.ts deleted file mode 100644 index c467b32a64..0000000000 --- a/libs/remix-ui/editor/src/lib/cairoSyntax.ts +++ /dev/null @@ -1,133 +0,0 @@ -/* eslint-disable */ -export const cairoConf = { - comments: { - lineComment: '#' - }, - brackets: [ - ['{', '}'], - ['[', ']'], - ['(', ')'], - ['%{', '%}'] - ], - autoClosingPairs: [ - { open: '{', close: '}' }, - { open: '[', close: ']' }, - { open: '(', close: ')' }, - { open: '%{', close: '%}' }, - { open: "'", close: "'", notIn: ['string', 'comment'] } - ], - surroundingPairs: [ - { open: '{', close: '}' }, - { open: '[', close: ']' }, - { open: '(', close: ')' }, - { open: '%{', close: '%}' }, - { open: "'", close: "'" } - ] -} - - -export const cairoLang = { - defaultToken: '', - tokenPostfix: '.cairo', - - brackets: [ - { token: 'delimiter.curly', open: '{', close: '}' }, - { token: 'delimiter.parenthesis', open: '(', close: ')' }, - { token: 'delimiter.square', open: '[', close: ']' }, - { token: 'delimiter.curly', open: '%{', close: '%}' } - ], - - keywords: [ - // control - 'if', - 'else', - 'end', - - // meta - 'alloc_locals', - 'as', - 'assert', - 'cast', - 'const', - 'dw', - 'felt', - 'from', - 'func', - 'import', - 'let', - 'local', - 'member', - 'nondet', - 'return', - 'static_assert', - 'struct', - 'tempvar', - 'with_attr', - 'with', - - // register - 'ap', - 'fp', - - // opcode - 'call', - 'jmp', - 'ret', - 'abs', - 'rel' - ], - - operators: ['=', ':', '==', '++', '+', '-', '*', '**', '/', '&', '%', '_'], - - // we include these common regular expressions - symbols: /[=>](?!@symbols)/, '@brackets'], - [ - /@symbols/, - { - cases: { - '@operators': 'delimiter', - '@default': '' - } - } - ], - - // numbers - [/(@numberHex)/, 'number.hex'], - [/(@numberDecimal)/, 'number'], - - // strings - [/'[^']*'/, 'string'] - ], - - whitespace: [ - [/\s+/, 'white'], - [/(^#.*$)/, 'comment'] - ] - } -} diff --git a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx index 617a8f4171..73496fe2ad 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -2,8 +2,10 @@ import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint import Editor, { loader, Monaco } from '@monaco-editor/react' import { reducerActions, reducerListener, initialState } from './actions/editor' -import { language, conf } from './syntax' -import { cairoLang, cairoConf } from './cairoSyntax' +import { solidityTokensProvider, solidityLanguageConfig } from './syntaxes/solidity' +import { cairoTokensProvider, cairoLanguageConfig } from './syntaxes/cairo' +import { zokratesTokensProvider, zokratesLanguageConfig } from './syntaxes/zokrates' +import { IMarkdownString } from 'monaco-editor' import './remix-ui-editor.css' import { loadTypes } from './web-types' @@ -51,7 +53,7 @@ export type lineText = { column: number } }, - from: string // plugin name + from?: string // plugin name content: string className: string afterContentClassName: string @@ -267,7 +269,6 @@ export const EditorUI = (props: EditorUIProps) => { defineAndSetTheme(monacoRef.current) }) - useEffect(() => { if (!editorRef.current || !props.currentFile) return currentFileRef.current = props.currentFile @@ -278,11 +279,12 @@ export const EditorUI = (props: EditorUIProps) => { monacoRef.current.editor.setModelLanguage(file.model, 'remix-solidity') } else if (file.language === 'cairo') { monacoRef.current.editor.setModelLanguage(file.model, 'remix-cairo') - } - + } else if (file.language === 'zokrates') { + monacoRef.current.editor.setModelLanguage(file.model, 'remix-zokrates') + } }, [props.currentFile]) - const convertToMonacoDecoration = (decoration: any, typeOfDecoration: string) => { + const convertToMonacoDecoration = (decoration: lineText | sourceAnnotation | sourceMarker, typeOfDecoration: string) => { if (typeOfDecoration === 'sourceAnnotationsPerFile') { decoration = decoration as sourceAnnotation return { @@ -313,6 +315,20 @@ export const EditorUI = (props: EditorUIProps) => { } } if (typeOfDecoration === 'lineTextPerFile') { + const lineTextDecoration = decoration as lineText + return { + type: typeOfDecoration, + range: new monacoRef.current.Range(lineTextDecoration.position.start.line + 1, lineTextDecoration.position.start.column + 1, lineTextDecoration.position.start.line + 1, 1024), + options: { + after: { content: ` ${lineTextDecoration.content}`, inlineClassName: `${lineTextDecoration.className}` }, + afterContentClassName: `${lineTextDecoration.afterContentClassName}`, + hoverMessage : lineTextDecoration.hoverMessage + }, + + } + } + if (typeOfDecoration === 'lineTextPerFile') { +<<<<<<< HEAD decoration = decoration as lineText return { type: typeOfDecoration, @@ -323,6 +339,18 @@ export const EditorUI = (props: EditorUIProps) => { hoverMessage : decoration.hoverMessage }, +======= + const lineTextDecoration = decoration as lineText + return { + type: typeOfDecoration, + range: new monacoRef.current.Range(lineTextDecoration.position.start.line + 1, lineTextDecoration.position.start.column + 1, lineTextDecoration.position.start.line + 1, 1024), + options: { + after: { content: ` ${lineTextDecoration.content}`, inlineClassName: `${lineTextDecoration.className}` }, + afterContentClassName: `${lineTextDecoration.afterContentClassName}`, + hoverMessage : lineTextDecoration.hoverMessage + }, + +>>>>>>> 74becf0e1b2f9500395be90167d3b6c237d13735 } } } @@ -508,13 +536,41 @@ export const EditorUI = (props: EditorUIProps) => { (window as any).addRemixBreakpoint(e.target.position) } }) - editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | (monacoRef.current.KeyCode as any).US_EQUAL, () => { + + // zoomin zoomout + editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.US_EQUAL, () => { editor.updateOptions({ fontSize: editor.getOption(43).fontSize + 1 }) }) editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | (monacoRef.current.KeyCode as any).US_MINUS, () => { editor.updateOptions({ fontSize: editor.getOption(43).fontSize - 1 }) }) + // add context menu items + const zoominAction = { + id: "zoomIn", + label: "Zoom In", + contextMenuOrder: 0, // choose the order + contextMenuGroupId: "zooming", // create a new grouping + keybindings: [ + // eslint-disable-next-line no-bitwise + monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Equal, + ], + run: () => { editor.updateOptions({ fontSize: editor.getOption(43).fontSize + 1 }) }, + } + const zoomOutAction = { + id: "zoomOut", + label: "Zoom Out", + contextMenuOrder: 0, // choose the order + contextMenuGroupId: "zooming", // create a new grouping + keybindings: [ + // eslint-disable-next-line no-bitwise + monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Minus, + ], + run: () => { editor.updateOptions({ fontSize: editor.getOption(43).fontSize - 1 }) }, + } + editor.addAction(zoomOutAction) + editor.addAction(zoominAction) + const editorService = editor._codeEditorService; const openEditorBase = editorService.openCodeEditor.bind(editorService); editorService.openCodeEditor = async (input, source) => { @@ -536,17 +592,14 @@ export const EditorUI = (props: EditorUIProps) => { // Register a new language monacoRef.current.languages.register({ id: 'remix-solidity' }) monacoRef.current.languages.register({ id: 'remix-cairo' }) + monacoRef.current.languages.register({ id: 'remix-zokrates' }) + // Register a tokens provider for the language - monacoRef.current.languages.setMonarchTokensProvider('remix-solidity', language as any) - monacoRef.current.languages.setLanguageConfiguration('remix-solidity', conf as any) - - monacoRef.current.languages.setMonarchTokensProvider('remix-cairo', cairoLang as any) - monacoRef.current.languages.setLanguageConfiguration('remix-cairo', cairoConf as any) + monacoRef.current.languages.setMonarchTokensProvider('remix-solidity', solidityTokensProvider as any) + monacoRef.current.languages.setLanguageConfiguration('remix-solidity', solidityLanguageConfig as any ) - monacoRef.current.languages.typescript.typescriptDefaults.setDiagnosticsOptions({ - noSemanticValidation: false, - noSyntaxValidation: false, - }); + monacoRef.current.languages.setMonarchTokensProvider('remix-cairo', cairoTokensProvider as any) + monacoRef.current.languages.setLanguageConfiguration('remix-cairo', cairoLanguageConfig as any) monacoRef.current.languages.registerDefinitionProvider('remix-solidity', new RemixDefinitionProvider(props, monaco)) monacoRef.current.languages.registerDocumentHighlightProvider('remix-solidity', new RemixHighLightProvider(props, monaco)) diff --git a/libs/remix-ui/editor/src/lib/syntaxes/cairo.ts b/libs/remix-ui/editor/src/lib/syntaxes/cairo.ts new file mode 100644 index 0000000000..f6550946d3 --- /dev/null +++ b/libs/remix-ui/editor/src/lib/syntaxes/cairo.ts @@ -0,0 +1,132 @@ +/* eslint-disable */ +export const cairoLanguageConfig = { + comments: { + lineComment: "#", + }, + brackets: [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["%{", "%}"], + ], + autoClosingPairs: [ + { open: "{", close: "}" }, + { open: "[", close: "]" }, + { open: "(", close: ")" }, + { open: "%{", close: "%}" }, + { open: "'", close: "'", notIn: ["string", "comment"] }, + ], + surroundingPairs: [ + { open: "{", close: "}" }, + { open: "[", close: "]" }, + { open: "(", close: ")" }, + { open: "%{", close: "%}" }, + { open: "'", close: "'" }, + ], +} + +export const cairoTokensProvider = { + defaultToken: "", + tokenPostfix: ".cairo", + + brackets: [ + { token: "delimiter.curly", open: "{", close: "}" }, + { token: "delimiter.parenthesis", open: "(", close: ")" }, + { token: "delimiter.square", open: "[", close: "]" }, + { token: "delimiter.curly", open: "%{", close: "%}" }, + ], + + keywords: [ + // control + "if", + "else", + "end", + + // meta + "alloc_locals", + "as", + "assert", + "cast", + "const", + "dw", + "felt", + "from", + "func", + "import", + "let", + "local", + "member", + "nondet", + "return", + "static_assert", + "struct", + "tempvar", + "with_attr", + "with", + + // register + "ap", + "fp", + + // opcode + "call", + "jmp", + "ret", + "abs", + "rel", + ], + + operators: ["=", ":", "==", "++", "+", "-", "*", "**", "/", "&", "%", "_"], + + // we include these common regular expressions + symbols: /[=>](?!@symbols)/, "@brackets"], + [ + /@symbols/, + { + cases: { + "@operators": "delimiter", + "@default": "", + }, + }, + ], + + // numbers + [/(@numberHex)/, "number.hex"], + [/(@numberDecimal)/, "number"], + + // strings + [/'[^']*'/, "string"], + ], + + whitespace: [ + [/\s+/, "white"], + [/(^#.*$)/, "comment"], + ], + }, +} diff --git a/libs/remix-ui/editor/src/lib/syntax.ts b/libs/remix-ui/editor/src/lib/syntaxes/solidity.ts similarity index 99% rename from libs/remix-ui/editor/src/lib/syntax.ts rename to libs/remix-ui/editor/src/lib/syntaxes/solidity.ts index 121dced50f..dfb388fda4 100644 --- a/libs/remix-ui/editor/src/lib/syntax.ts +++ b/libs/remix-ui/editor/src/lib/syntaxes/solidity.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -export const conf = { +export const solidityLanguageConfig = { comments: { lineComment: '//', blockComment: ['/*', '*/'] @@ -19,7 +19,7 @@ export const conf = { ] } -export const language = { +export const solidityTokensProvider = { defaultToken: '', tokenPostfix: '.sol', diff --git a/libs/remix-ui/editor/src/lib/syntaxes/zokrates.ts b/libs/remix-ui/editor/src/lib/syntaxes/zokrates.ts new file mode 100644 index 0000000000..89a72ff361 --- /dev/null +++ b/libs/remix-ui/editor/src/lib/syntaxes/zokrates.ts @@ -0,0 +1,144 @@ +/* eslint-disable */ +export const zokratesLanguageConfig = { + comments: { + lineComment: '//', + blockComment: ['/*', '*/'] + }, + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'], + ['<', '>'] + ], + autoClosingPairs: [ + { open: '"', close: '"', notIn: ['string', 'comment'] }, + { open: '{', close: '}', notIn: ['string', 'comment'] }, + { open: '[', close: ']', notIn: ['string', 'comment'] }, + { open: '(', close: ')', notIn: ['string', 'comment'] }, + { open: '<', close: '>', notIn: ['string', 'comment'] } + ] +} + +export const zokratesTokensProvider = { + defaultToken: "", + tokenPostfix: ".zok", + + keywords: [ + "log", + "assert", + "as", + "bool", + "const", + "def", + "else", + "false", + "field", + "for", + "if", + "import", + "from", + "in", + "mut", + "private", + "public", + "return", + "struct", + "true", + "type", + "u8", + "u16", + "u32", + "u64", + ], + + typeKeywords: ["bool", "field", "u8", "u16", "u32", "u64"], + + operators: [ + "=", + ">", + "<", + "!", + "?", + ":", + "==", + "<=", + ">=", + "!=", + "&&", + "||", + "+", + "-", + "*", + "**", + "/", + "&", + "|", + "^", + "%", + "<<", + ">>", + ], + + decimalSuffix: /(u16|u32|u64|u8|f)?/, + + // we include these common regular expressions + symbols: /[=>](?!@symbols)/, "@brackets"], + [/@symbols/, { cases: { "@operators": "operator", "@default": "" } }], + + // numbers + [/0[xX][0-9a-fA-F]+/, "number.hex"], + [/\d+(@decimalSuffix)/, "number"], + + // delimiter + [/[;,.]/, "delimiter"], + + // strings + [/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string + [/"/, { token: "string.quote", bracket: "@open", next: "@string" }], + ], + + comment: [ + [/[^\/*]+/, "comment"], + [/\/\*/, "comment", "@push"], // nested comment + ["\\*/", "comment", "@pop"], + [/[\/*]/, "comment"], + ], + + string: [ + [/[^\\"]+/, "string"], + [/@escapes/, "string.escape"], + [/\\./, "string.escape.invalid"], + [/"/, { token: "string.quote", bracket: "@close", next: "@pop" }], + ], + + whitespace: [ + [/[ \t\r\n]+/, "white"], + [/\/\*/, "comment", "@comment"], + [/\/\/.*$/, "comment"], + ], + }, +} diff --git a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-custom-icon.tsx b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-custom-icon.tsx index c157bc66c0..2cfec2ac97 100644 --- a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-custom-icon.tsx +++ b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-custom-icon.tsx @@ -5,7 +5,7 @@ import { fileDecoration } from '../../types' const FileDecorationCustomIcon = (props: { fileDecoration: fileDecoration }) => { - return <> + return <> {props.fileDecoration.fileStateIcon} } diff --git a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-error-icon.tsx b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-error-icon.tsx index 2bb61fd97f..5a9c48b555 100644 --- a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-error-icon.tsx +++ b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-error-icon.tsx @@ -7,7 +7,7 @@ const FileDecorationErrorIcon = (props: { fileDecoration: fileDecoration }) => { return <> - {props.fileDecoration.text} + {props.fileDecoration.text} } diff --git a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-warning-icon.tsx b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-warning-icon.tsx index 1c027c9854..9bfd368506 100644 --- a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-warning-icon.tsx +++ b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-warning-icon.tsx @@ -5,7 +5,7 @@ import { fileDecoration } from '../../types' const FileDecorationWarningIcon = (props: { fileDecoration: fileDecoration }) => { - return <>{props.fileDecoration.text} + return <>{props.fileDecoration.text} } export default FileDecorationWarningIcon \ No newline at end of file diff --git a/libs/remix-ui/file-decorators/src/lib/types/index.ts b/libs/remix-ui/file-decorators/src/lib/types/index.ts index ef6620434a..fa783ec6e5 100644 --- a/libs/remix-ui/file-decorators/src/lib/types/index.ts +++ b/libs/remix-ui/file-decorators/src/lib/types/index.ts @@ -14,7 +14,7 @@ export enum fileDecorationType { fileStateIcon: string | HTMLDivElement | JSX.Element, bubble: boolean, text?: string, - owner: string, + owner?: string, workspace?: any tooltip?: string comment?: string[] | string diff --git a/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx b/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx index 2690dfc840..7266b0d90c 100644 --- a/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx +++ b/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx @@ -237,7 +237,7 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
- Scam Alert: Beware of online videos promoting "liquidity front runner bots". + Scam Alert: There are video tutorials going around that provide urls other than remix.ethereum.org, and could be scams. Also, beware of online videos promoting "liquidity front runner bots". Learn more
diff --git a/libs/remix-ui/publish-to-storage/src/lib/publish-to-storage.tsx b/libs/remix-ui/publish-to-storage/src/lib/publish-to-storage.tsx index 8ff00ca026..51b5d46b01 100644 --- a/libs/remix-ui/publish-to-storage/src/lib/publish-to-storage.tsx +++ b/libs/remix-ui/publish-to-storage/src/lib/publish-to-storage.tsx @@ -31,12 +31,7 @@ export const PublishToStorage = (props: RemixUiPublishToStorageProps) => { modal(`Published ${contract.name}'s Metadata and Sources`, publishMessage(result.uploaded)) } catch (err) { - let parseError = err - try { - parseError = JSON.stringify(err) - } catch (e) { - } - modal('Swarm Publish Failed', publishMessageFailed(storage, parseError)) + modal('Swarm Publish Failed', publishMessageFailed(storage, err.message)) } } else { if (!api.config.get('settings/ipfs-url') && !modalShown) { @@ -81,7 +76,7 @@ export const PublishToStorage = (props: RemixUiPublishToStorageProps) => { const result = await publishToIPFS(contract, api) modal(`Published ${contract.name}'s Metadata and Sources`, publishMessage(result.uploaded)) } catch (err) { - modal('IPFS Publish Failed', publishMessageFailed(storage, err)) + modal('IPFS Publish Failed', publishMessageFailed(storage, err.message)) } setModalShown(true) } diff --git a/libs/remix-ui/run-tab/src/lib/actions/deploy.ts b/libs/remix-ui/run-tab/src/lib/actions/deploy.ts index 440d805664..caf8e6de8a 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/deploy.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/deploy.ts @@ -300,4 +300,14 @@ export const runTransactions = ( export const getFuncABIInputs = (plugin: RunTab, funcABI: FuncABI) => { return plugin.blockchain.getInputs(funcABI) +} + +export const updateInstanceBalance = (plugin: RunTab) => { + if (plugin.REACT_API?.instances?.instanceList?.length) { + for (const instance of plugin.REACT_API.instances.instanceList) { + plugin.blockchain.getBalanceInEther(instance.address, (err, balInEth) => { + if (!err) instance.balance = balInEth + }) + } + } } \ No newline at end of file diff --git a/libs/remix-ui/run-tab/src/lib/actions/events.ts b/libs/remix-ui/run-tab/src/lib/actions/events.ts index 4355f4e87e..46af1dba0d 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/events.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/events.ts @@ -95,17 +95,20 @@ export const setupEvents = (plugin: RunTab, dispatch: React.Dispatch) => { const broadcastCompilationResult = async (plugin: RunTab, dispatch: React.Dispatch, file, source, languageVersion, data, input?) => { // TODO check whether the tab is configured const compiler = new CompilerAbstract(languageVersion, data, source, input) - plugin.compilersArtefacts[languageVersion] = compiler plugin.compilersArtefacts.__last = compiler const contracts = getCompiledContracts(compiler).map((contract) => { return { name: languageVersion, alias: contract.name, file: contract.file, compiler } }) - const index = contracts.findIndex(contract => contract.alias === plugin.REACT_API.contracts.currentContract) - - if ((index < 0) && (contracts.length > 0)) dispatch(setCurrentContract(contracts[0].alias)) - const isUpgradeable = await plugin.call('openzeppelin-proxy', 'isConcerned', data.sources[file] ? data.sources[file].ast : {}) + if ((contracts.length > 0)) { + const contractsInCompiledFile = contracts.filter(obj => obj.file === file) + let currentContract + if (contractsInCompiledFile.length) currentContract = contractsInCompiledFile[0].alias + else currentContract = contracts[0].alias + dispatch(setCurrentContract(currentContract)) + } + const isUpgradeable = await plugin.call('openzeppelin-proxy', 'isConcerned', data.sources && data.sources[file] ? data.sources[file].ast : {}) if (isUpgradeable) { const options = await plugin.call('openzeppelin-proxy', 'getProxyOptions', data, file) diff --git a/libs/remix-ui/run-tab/src/lib/actions/index.ts b/libs/remix-ui/run-tab/src/lib/actions/index.ts index 5dc0eb2de7..6a20e9470d 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/index.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/index.ts @@ -3,8 +3,10 @@ import React from 'react' import { RunTab } from '../types/run-tab' import { resetAndInit, setupEvents } from './events' import { createNewBlockchainAccount, fillAccountsList, setExecutionContext, signMessageWithAddress } from './account' -import { clearInstances, clearPopUp, removeInstance, setAccount, setGasFee, setMatchPassphrasePrompt, setNetworkNameFromProvider, setPassphrasePrompt, setSelectedContract, setSendTransactionValue, setUnit, updateBaseFeePerGas, updateConfirmSettings, updateGasPrice, updateGasPriceStatus, updateMaxFee, updateMaxPriorityFee, updateScenarioPath, updateTxFeeContent } from './actions' -import { createInstance, getContext, getFuncABIInputs, getSelectedContract, loadAddress, runTransactions } from './deploy' +import { clearInstances, clearPopUp, removeInstance, setAccount, setGasFee, setMatchPassphrasePrompt, + setNetworkNameFromProvider, setPassphrasePrompt, setSelectedContract, setSendTransactionValue, setUnit, + updateBaseFeePerGas, updateConfirmSettings, updateGasPrice, updateGasPriceStatus, updateMaxFee, updateMaxPriorityFee, updateScenarioPath, updateTxFeeContent } from './actions' +import { createInstance, getContext, getFuncABIInputs, getSelectedContract, loadAddress, runTransactions, updateInstanceBalance } from './deploy' import { CompilerAbstract as CompilerAbstractType } from '@remix-project/remix-solidity-ts' import { ContractData, FuncABI } from "@remix-project/core-plugin" import { DeployMode, MainnetPrompt } from '../types' @@ -26,6 +28,7 @@ export const initRunTab = (udapp: RunTab) => async (reducerDispatch: React.Dispa setupEvents(plugin, dispatch) setInterval(() => { fillAccountsList(plugin, dispatch) + updateInstanceBalance(plugin) }, 1000) } diff --git a/libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx b/libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx index 7fe409e511..20965b644f 100644 --- a/libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx @@ -5,6 +5,7 @@ import { ContractGUIProps } from '../types' import { CopyToClipboard } from '@remix-ui/clipboard' const txFormat = remixLib.execution.txFormat +const txHelper = remixLib.execution.txHelper export function ContractGUI (props: ContractGUIProps) { const [title, setTitle] = useState('') const [basicInput, setBasicInput] = useState('') @@ -24,6 +25,13 @@ export function ContractGUI (props: ContractGUIProps) { const initializeFields = useRef>([]) const basicInputRef = useRef() + useEffect(() => { + if (props.deployOption && Array.isArray(props.deployOption)) { + if (props.deployOption[0] && props.deployOption[0].title === 'Deploy with Proxy' && props.deployOption[0].active) handleDeployProxySelect(true) + else if (props.deployOption[1] && props.deployOption[1].title === 'Upgrade with Proxy' && props.deployOption[1].active) handleUpgradeImpSelect(true) + } + }, [props.deployOption]) + useEffect(() => { if (props.title) { setTitle(props.title) @@ -67,7 +75,7 @@ export function ContractGUI (props: ContractGUIProps) { } }, [props.lookupOnly, props.funcABI, title]) - const getContentOnCTC = () => { + const getEncodedCall = () => { const multiString = getMultiValsString(multiFields.current) // copy-to-clipboard icon is only visible for method requiring input params if (!multiString) { @@ -88,6 +96,20 @@ export function ContractGUI (props: ContractGUIProps) { } } + const getEncodedParams = () => { + try { + const multiString = getMultiValsString(multiFields.current) + // copy-to-clipboard icon is only visible for method requiring input params + if (!multiString) { + return 'cannot encode empty arguments' + } + const multiJSON = JSON.parse('[' + multiString + ']') + return txHelper.encodeParams(props.funcABI, multiJSON) + } catch (e) { + console.error(e) + } + } + const switchMethodViewOn = () => { setToggleContainer(true) makeMultiVal() @@ -131,12 +153,10 @@ export function ContractGUI (props: ContractGUIProps) { } const makeMultiVal = () => { - let inputString = basicInput + const inputString = basicInput if (inputString) { - inputString = inputString.replace(/(^|,\s+|,)(\d+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted number by quoted number - inputString = inputString.replace(/(^|,\s+|,)(0[xX][0-9a-fA-F]+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted hex string by quoted hex string - const inputJSON = JSON.parse('[' + inputString + ']') + const inputJSON = remixLib.execution.txFormat.parseFunctionParams(inputString) const multiInputs = multiFields.current for (let k = 0; k < multiInputs.length; k++) { @@ -179,9 +199,7 @@ export function ContractGUI (props: ContractGUIProps) { setToggleDeployProxy(!toggleDeployProxy) } - const handleDeployProxySelect = (e) => { - const value = e.target.checked - + const handleDeployProxySelect = (value: boolean) => { if (value) setToggleUpgradeImp(false) setToggleDeployProxy(value) setDeployState({ upgrade: false, deploy: value }) @@ -191,9 +209,7 @@ export function ContractGUI (props: ContractGUIProps) { setToggleUpgradeImp(!toggleUpgradeImp) } - const handleUpgradeImpSelect = (e) => { - const value = e.target.checked - + const handleUpgradeImpSelect = (value: boolean) => { setToggleUpgradeImp(value) if (value) { setToggleDeployProxy(false) @@ -249,9 +265,28 @@ export function ContractGUI (props: ContractGUIProps) {
) })}
-
- - +
+ + + + + + +
@@ -264,7 +299,7 @@ export function ContractGUI (props: ContractGUIProps) { data-id="contractGUIDeployWithProxy" className="form-check-input custom-control-input" type="checkbox" - onChange={handleDeployProxySelect} + onChange={(e) => handleDeployProxySelect(e.target.checked)} checked={deployState.deploy} />