From 4e38dcad70f7a7a0b39124d5f52e09f422f35fda Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 3 Aug 2022 12:24:20 +0200 Subject: [PATCH 01/10] add etherscan plugin --- apps/etherscan/.babelrc | 13 + apps/etherscan/.browserslistrc | 16 + apps/etherscan/.eslintrc.json | 34 ++ apps/etherscan/src/app/App.css | 7 + apps/etherscan/src/app/AppContext.tsx | 24 ++ apps/etherscan/src/app/app.tsx | 157 +++++++++ .../src/app/components/HeaderWithSettings.tsx | 159 +++++++++ .../src/app/components/SubmitButton.tsx | 34 ++ apps/etherscan/src/app/components/index.ts | 2 + .../src/app/hooks/useLocalStorage.tsx | 37 +++ apps/etherscan/src/app/layouts/Default.tsx | 19 ++ apps/etherscan/src/app/layouts/index.ts | 1 + apps/etherscan/src/app/logo.svg | 17 + apps/etherscan/src/app/routes.tsx | 51 +++ apps/etherscan/src/app/star.svg | 11 + apps/etherscan/src/app/types/Receipt.ts | 6 + apps/etherscan/src/app/types/ThemeType.ts | 1 + apps/etherscan/src/app/types/index.ts | 2 + apps/etherscan/src/app/utils/index.ts | 1 + apps/etherscan/src/app/utils/utilities.ts | 34 ++ .../src/app/views/CaptureKeyView.tsx | 59 ++++ apps/etherscan/src/app/views/ErrorView.tsx | 31 ++ apps/etherscan/src/app/views/HomeView.tsx | 36 +++ apps/etherscan/src/app/views/ReceiptsView.tsx | 135 ++++++++ apps/etherscan/src/app/views/VerifyView.tsx | 302 ++++++++++++++++++ apps/etherscan/src/app/views/index.ts | 4 + apps/etherscan/src/assets/.gitkeep | 0 .../src/environments/environment.prod.ts | 3 + .../etherscan/src/environments/environment.ts | 6 + apps/etherscan/src/favicon.ico | Bin 0 -> 15086 bytes apps/etherscan/src/index.html | 14 + apps/etherscan/src/main.tsx | 7 + apps/etherscan/src/polyfills.ts | 7 + apps/etherscan/src/styles.css | 1 + apps/etherscan/tsconfig.app.json | 14 + apps/etherscan/tsconfig.json | 20 ++ nx.json | 3 + package.json | 2 + workspace.json | 83 ++++- yarn.lock | 62 ++++ 40 files changed, 1414 insertions(+), 1 deletion(-) create mode 100644 apps/etherscan/.babelrc create mode 100644 apps/etherscan/.browserslistrc create mode 100644 apps/etherscan/.eslintrc.json create mode 100644 apps/etherscan/src/app/App.css create mode 100644 apps/etherscan/src/app/AppContext.tsx create mode 100644 apps/etherscan/src/app/app.tsx create mode 100644 apps/etherscan/src/app/components/HeaderWithSettings.tsx create mode 100644 apps/etherscan/src/app/components/SubmitButton.tsx create mode 100644 apps/etherscan/src/app/components/index.ts create mode 100644 apps/etherscan/src/app/hooks/useLocalStorage.tsx create mode 100644 apps/etherscan/src/app/layouts/Default.tsx create mode 100644 apps/etherscan/src/app/layouts/index.ts create mode 100644 apps/etherscan/src/app/logo.svg create mode 100644 apps/etherscan/src/app/routes.tsx create mode 100644 apps/etherscan/src/app/star.svg create mode 100644 apps/etherscan/src/app/types/Receipt.ts create mode 100644 apps/etherscan/src/app/types/ThemeType.ts create mode 100644 apps/etherscan/src/app/types/index.ts create mode 100644 apps/etherscan/src/app/utils/index.ts create mode 100644 apps/etherscan/src/app/utils/utilities.ts create mode 100644 apps/etherscan/src/app/views/CaptureKeyView.tsx create mode 100644 apps/etherscan/src/app/views/ErrorView.tsx create mode 100644 apps/etherscan/src/app/views/HomeView.tsx create mode 100644 apps/etherscan/src/app/views/ReceiptsView.tsx create mode 100644 apps/etherscan/src/app/views/VerifyView.tsx create mode 100644 apps/etherscan/src/app/views/index.ts create mode 100644 apps/etherscan/src/assets/.gitkeep create mode 100644 apps/etherscan/src/environments/environment.prod.ts create mode 100644 apps/etherscan/src/environments/environment.ts create mode 100644 apps/etherscan/src/favicon.ico create mode 100644 apps/etherscan/src/index.html create mode 100644 apps/etherscan/src/main.tsx create mode 100644 apps/etherscan/src/polyfills.ts create mode 100644 apps/etherscan/src/styles.css create mode 100644 apps/etherscan/tsconfig.app.json create mode 100644 apps/etherscan/tsconfig.json 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..d46b66fccc --- /dev/null +++ b/apps/etherscan/src/app/app.tsx @@ -0,0 +1,157 @@ +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(() => { + console.log("Remix Etherscan loading...") + const client = new PluginClient() + createClient(client) + const loadClient = async () => { + await client.onload() + setClientInstance(client) + console.log("Remix Etherscan Plugin has been loaded") + + client.on("solidity", + "compilationFinished", + ( + fileName: string, + source: CompilationFileSources, + languageVersion: string, + data: CompilationResult + ) => { + console.log("New compilation received") + 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..f0ba96ae6b --- /dev/null +++ b/apps/etherscan/src/app/components/HeaderWithSettings.tsx @@ -0,0 +1,159 @@ +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..7fe0fa4f05 --- /dev/null +++ b/apps/etherscan/src/app/components/SubmitButton.tsx @@ -0,0 +1,34 @@ +import React from "react" + +interface Props { + text: string + isSubmitting?: boolean +} + +export const SubmitButton: React.FC = ({ + text, + 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..cca4a83a72 --- /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.log(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.log(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..17ef6fc8a0 --- /dev/null +++ b/apps/etherscan/src/app/utils/utilities.ts @@ -0,0 +1,34 @@ +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") + } + const name = network.name!.toLowerCase() + // TODO : remove that when https://github.com/ethereum/remix-ide/issues/2017 is fixe + return name === "görli" ? "goerli" : name +} + +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.log("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..2bac179293 --- /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..5c9e623231 --- /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..a57b23a8cb --- /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.log("Error, something wrong happened", 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 0000000000000000000000000000000000000000..317ebcb2336e0833a22dddf0ab287849f26fda57 GIT binary patch literal 15086 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 + + + + 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/nx.json b/nx.json index 2afa03ec50..e5fa51dd58 100644 --- a/nx.json +++ b/nx.json @@ -198,6 +198,9 @@ }, "vyper": { "tags": [] + }, + "etherscan": { + "tags": [] } }, "targetDependencies": { diff --git a/package.json b/package.json index 06b9c286cf..11c4897478 100644 --- a/package.json +++ b/package.json @@ -182,6 +182,7 @@ "file-path-filter": "^3.0.2", "file-saver": "^2.0.5", "form-data": "^4.0.0", + "formik": "^2.2.9", "fs-extra": "^3.0.1", "html-react-parser": "^1.3.0", "http-server": "^0.11.1", @@ -201,6 +202,7 @@ "react-dom": "^17.0.2", "react-draggable": "^4.4.4", "react-json-view": "^1.21.3", + "react-router-dom": "^6.3.0", "react-tabs": "^3.2.2", "regenerator-runtime": "0.13.7", "rss-parser": "^3.12.0", diff --git a/workspace.json b/workspace.json index a2eca1cee7..df7e8f9d28 100644 --- a/workspace.json +++ b/workspace.json @@ -1566,7 +1566,88 @@ "linter": "eslint", "config": "apps/vyper/.eslintrc", "files": [ - "apps/vyper/src/**/*.js", "apps/vyper/src/**/*.ts" + "apps/vyper/src/**/*.js", + "apps/vyper/src/**/*.ts" + ], + "exclude": [ + "**/node_modules/**" + ] + } + } + } + }, + "etherscan": { + "root": "apps/etherscan", + "sourceRoot": "apps/etherscan/src", + "projectType": "application", + "architect": { + "build": { + "builder": "@nrwl/web:build", + "outputs": [ + "{options.outputPath}" + ], + "options": { + "outputPath": "dist/apps/etherscan", + "index": "apps/etherscan/src/index.html", + "main": "apps/etherscan/src/main.tsx", + "polyfills": "apps/etherscan/src/polyfills.ts", + "tsConfig": "apps/etherscan/tsconfig.app.json", + "assets": [ + "apps/etherscan/src/favicon.ico", + "apps/etherscan/src/assets" + ], + "styles": [ + "apps/etherscan/src/styles.css" + ], + "scripts": [], + "webpackConfig": "@nrwl/react/plugins/webpack" + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "apps/etherscan/src/environments/environment.ts", + "with": "apps/etherscan/src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "extractCss": true, + "namedChunks": false, + "extractLicenses": true, + "vendorChunk": false, + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + } + ] + } + } + }, + "serve": { + "builder": "@nrwl/web:dev-server", + "options": { + "buildTarget": "etherscan:build", + "port": 5003 + }, + "configurations": { + "production": { + "buildTarget": "etherscan:build:production", + "hmr": false + } + } + }, + "lint": { + "builder": "@nrwl/linter:lint", + "options": { + "linter": "eslint", + "config": "apps/vyper/.eslintrc", + "files": [ + "apps/vyper/src/**/*.js", + "apps/vyper/src/**/*.ts" ], "exclude": [ "**/node_modules/**" diff --git a/yarn.lock b/yarn.lock index bba528ba28..cd49915370 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1622,6 +1622,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.7.6": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" + integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@7.0.0-beta.53": version "7.0.0-beta.53" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.53.tgz#3322290900d0b187b0a7174381e1f3bb71050d2e" @@ -9136,6 +9143,11 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +deepmerge@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170" + integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA== + deepmerge@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" @@ -11250,6 +11262,19 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +formik@^2.2.9: + version "2.2.9" + resolved "https://registry.yarnpkg.com/formik/-/formik-2.2.9.tgz#8594ba9c5e2e5cf1f42c5704128e119fc46232d0" + integrity sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA== + dependencies: + deepmerge "^2.1.1" + hoist-non-react-statics "^3.3.0" + lodash "^4.17.21" + lodash-es "^4.17.21" + react-fast-compare "^2.0.1" + tiny-warning "^1.0.2" + tslib "^1.10.0" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -12331,6 +12356,13 @@ header-case@^2.0.4: capital-case "^1.0.4" tslib "^2.0.3" +history@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b" + integrity sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ== + dependencies: + "@babel/runtime" "^7.7.6" + hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -15292,6 +15324,11 @@ lockfile@~1.0.3: resolved "https://registry.yarnpkg.com/lockfile/-/lockfile-1.0.3.tgz#2638fc39a0331e9cac1a04b71799931c9c50df79" integrity sha1-Jjj8OaAzHpysGgS3F5mTHJxQ33k= +lodash-es@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + lodash._arraycopy@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz#76e7b7c1f1fb92547374878a562ed06a3e50f6e1" @@ -19584,6 +19621,11 @@ react-draggable@^4.4.4: clsx "^1.1.1" prop-types "^15.6.0" +react-fast-compare@^2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" + integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== + react-is@^16.3.2, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -19645,6 +19687,21 @@ react-refresh@^0.9.0: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.9.0.tgz#71863337adc3e5c2f8a6bfddd12ae3bfe32aafbf" integrity sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ== +react-router-dom@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.3.0.tgz#a0216da813454e521905b5fa55e0e5176123f43d" + integrity sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw== + dependencies: + history "^5.2.0" + react-router "6.3.0" + +react-router@6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557" + integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ== + dependencies: + history "^5.2.0" + react-tabs@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/react-tabs/-/react-tabs-3.2.2.tgz#07bdc3cdb17bdffedd02627f32a93cd4b3d6e4d0" @@ -22487,6 +22544,11 @@ tiny-invariant@^1.0.6: resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9" integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg== +tiny-warning@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + tmp@0.0.33, tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" From 144404bd04365824f8037dc5004bf100b5333e8a Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 3 Aug 2022 12:48:14 +0200 Subject: [PATCH 02/10] add e2e for etherscan --- .circleci/config.yml | 50 +++++++++++++++++++ apps/remix-ide-e2e/src/tests/etherscan_api.ts | 24 +++++++++ .../ci/browser_tests_etherscan_plugin.sh | 25 ++++++++++ 3 files changed, 99 insertions(+) create mode 100644 apps/remix-ide-e2e/src/tests/etherscan_api.ts create mode 100755 apps/remix-ide/ci/browser_tests_etherscan_plugin.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index bccbf0146a..2095b373fe 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -310,6 +310,50 @@ jobs: 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: @@ -467,6 +511,9 @@ workflows: - remix-ide-vyper-plugin: requires: - build + - remix-ide-etherscan-plugin: + requires: + - build - remix-ide-chrome: requires: - build @@ -481,6 +528,7 @@ workflows: - remix-ide-firefox - remix-ide-plugin-api - remix-ide-vyper-plugin + - remix-ide-etherscan-plugin filters: branches: only: remix_live @@ -492,6 +540,7 @@ workflows: - remix-ide-firefox - remix-ide-plugin-api - remix-ide-vyper-plugin + - remix-ide-etherscan-plugin filters: branches: only: master @@ -503,6 +552,7 @@ workflows: - remix-ide-firefox - remix-ide-plugin-api - remix-ide-vyper-plugin + - remix-ide-etherscan-plugin filters: branches: only: remix_beta 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..719db61972 --- /dev/null +++ b/apps/remix-ide-e2e/src/tests/etherscan_api.ts @@ -0,0 +1,24 @@ +'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) + .waitForElementNotVisible('input[name="apiKey"]') + } +} 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 From a40a18f158b7489700feaede9ff6970e2b26eb5a Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 4 Aug 2022 09:34:28 +0200 Subject: [PATCH 03/10] remove console.log --- apps/etherscan/src/app/app.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/etherscan/src/app/app.tsx b/apps/etherscan/src/app/app.tsx index d46b66fccc..653d90bfb1 100644 --- a/apps/etherscan/src/app/app.tsx +++ b/apps/etherscan/src/app/app.tsx @@ -43,14 +43,11 @@ const App = () => { contractsRef.current = contracts useEffect(() => { - console.log("Remix Etherscan loading...") const client = new PluginClient() createClient(client) const loadClient = async () => { await client.onload() setClientInstance(client) - console.log("Remix Etherscan Plugin has been loaded") - client.on("solidity", "compilationFinished", ( @@ -59,7 +56,6 @@ const App = () => { languageVersion: string, data: CompilationResult ) => { - console.log("New compilation received") const newContractsNames = getNewContractNames(data) const newContractsToSave: string[] = [ From 46a989d8e41cd6d16913f29b97d08f9365158874 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 4 Aug 2022 09:35:38 +0200 Subject: [PATCH 04/10] use console.error --- apps/etherscan/src/app/hooks/useLocalStorage.tsx | 4 ++-- apps/etherscan/src/app/utils/utilities.ts | 2 +- apps/etherscan/src/app/views/VerifyView.tsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/etherscan/src/app/hooks/useLocalStorage.tsx b/apps/etherscan/src/app/hooks/useLocalStorage.tsx index cca4a83a72..dbef5378a8 100644 --- a/apps/etherscan/src/app/hooks/useLocalStorage.tsx +++ b/apps/etherscan/src/app/hooks/useLocalStorage.tsx @@ -11,7 +11,7 @@ export function useLocalStorage(key: string, initialValue: any) { return item ? JSON.parse(item) : initialValue } catch (error) { // If error also return initialValue - console.log(error) + console.error(error) return initialValue } }) @@ -29,7 +29,7 @@ export function useLocalStorage(key: string, initialValue: any) { window.localStorage.setItem(key, JSON.stringify(valueToStore)) } catch (error) { // A more advanced implementation would handle the error case - console.log(error) + console.error(error) } } diff --git a/apps/etherscan/src/app/utils/utilities.ts b/apps/etherscan/src/app/utils/utilities.ts index 17ef6fc8a0..748babf9e5 100644 --- a/apps/etherscan/src/app/utils/utilities.ts +++ b/apps/etherscan/src/app/utils/utilities.ts @@ -29,6 +29,6 @@ export const getReceiptStatus = async ( const { result } = response.data return result } catch (error) { - console.log("Error", error) + console.error(error) } } diff --git a/apps/etherscan/src/app/views/VerifyView.tsx b/apps/etherscan/src/app/views/VerifyView.tsx index a57b23a8cb..0cf383dc8b 100644 --- a/apps/etherscan/src/app/views/VerifyView.tsx +++ b/apps/etherscan/src/app/views/VerifyView.tsx @@ -170,7 +170,7 @@ export const VerifyView: React.FC = ({ } return result } catch (error) { - console.log("Error, something wrong happened", error) + console.error(error) setResults("Something wrong happened, try again") } } From b964e748891d77a4a22436813cf24fcad162a02e Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 4 Aug 2022 09:39:31 +0200 Subject: [PATCH 05/10] =?UTF-8?q?use=20goerli=20instead=20of=20g=C3=B6rli?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/etherscan/src/app/utils/utilities.ts | 4 +--- libs/remix-core-plugin/src/lib/compiler-metadata.ts | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/etherscan/src/app/utils/utilities.ts b/apps/etherscan/src/app/utils/utilities.ts index 748babf9e5..d7300806a3 100644 --- a/apps/etherscan/src/app/utils/utilities.ts +++ b/apps/etherscan/src/app/utils/utilities.ts @@ -13,9 +13,7 @@ export const getNetworkName = async (client: RemixClient) => { if (!network) { throw new Error("no known network to verify against") } - const name = network.name!.toLowerCase() - // TODO : remove that when https://github.com/ethereum/remix-ide/issues/2017 is fixe - return name === "görli" ? "goerli" : name + return network.name!.toLowerCase() } export const getReceiptStatus = async ( diff --git a/libs/remix-core-plugin/src/lib/compiler-metadata.ts b/libs/remix-core-plugin/src/lib/compiler-metadata.ts index 9a69e616f4..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' } From 6cf0474e3e869a42621017053f88e78e4b676ff4 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 4 Aug 2022 09:40:52 +0200 Subject: [PATCH 06/10] update issue url --- apps/etherscan/src/app/views/ErrorView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/etherscan/src/app/views/ErrorView.tsx b/apps/etherscan/src/app/views/ErrorView.tsx index 5c9e623231..69e022e47c 100644 --- a/apps/etherscan/src/app/views/ErrorView.tsx +++ b/apps/etherscan/src/app/views/ErrorView.tsx @@ -21,7 +21,7 @@ export const ErrorView: React.FC = () => { Please raise an issue:{" "} Here From 85303fec002f6f4f68e485257c20c8f46c5b8f33 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 4 Aug 2022 09:52:52 +0200 Subject: [PATCH 07/10] fix e2e --- .../src/app/components/HeaderWithSettings.tsx | 1 + .../src/app/components/SubmitButton.tsx | 5 +- .../src/app/views/CaptureKeyView.tsx | 2 +- apps/etherscan/src/app/views/VerifyView.tsx | 4 +- apps/remix-ide-e2e/src/tests/etherscan_api.ts | 87 ++++++++++++++++++- 5 files changed, 93 insertions(+), 6 deletions(-) diff --git a/apps/etherscan/src/app/components/HeaderWithSettings.tsx b/apps/etherscan/src/app/components/HeaderWithSettings.tsx index f0ba96ae6b..14e048d6cd 100644 --- a/apps/etherscan/src/app/components/HeaderWithSettings.tsx +++ b/apps/etherscan/src/app/components/HeaderWithSettings.tsx @@ -18,6 +18,7 @@ interface IconProps { const HomeIcon: React.FC = ({ from, themeType }: IconProps) => { return ( = ({ text, + dataId, isSubmitting = false, }) => { return (
)} diff --git a/apps/etherscan/src/app/views/CaptureKeyView.tsx b/apps/etherscan/src/app/views/CaptureKeyView.tsx index 2bac179293..9703ec10ff 100644 --- a/apps/etherscan/src/app/views/CaptureKeyView.tsx +++ b/apps/etherscan/src/app/views/CaptureKeyView.tsx @@ -48,7 +48,7 @@ export const CaptureKeyView: React.FC = () => {
- +
)} diff --git a/apps/etherscan/src/app/views/VerifyView.tsx b/apps/etherscan/src/app/views/VerifyView.tsx index 0cf383dc8b..3e85d471b7 100644 --- a/apps/etherscan/src/app/views/VerifyView.tsx +++ b/apps/etherscan/src/app/views/VerifyView.tsx @@ -284,12 +284,12 @@ export const VerifyView: React.FC = ({ /> - + )} -
diff --git a/apps/remix-ide-e2e/src/tests/etherscan_api.ts b/apps/remix-ide-e2e/src/tests/etherscan_api.ts index 719db61972..7089d5b563 100644 --- a/apps/remix-ide-e2e/src/tests/etherscan_api.ts +++ b/apps/remix-ide-e2e/src/tests/etherscan_api.ts @@ -19,6 +19,89 @@ module.exports = { .pause(5000) // @ts-ignore .frame(0) - .waitForElementNotVisible('input[name="apiKey"]') - } + .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; + } +}` From 9d8b7eeeba912e9a9cadc3a39edaa2513c62a1e0 Mon Sep 17 00:00:00 2001 From: lianahus Date: Wed, 20 Jul 2022 12:10:19 +0200 Subject: [PATCH 08/10] fixing null workspace --- apps/remix-ide/src/app/panels/file-panel.js | 2 +- libs/remix-ui/search/src/lib/context/context.tsx | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) 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/libs/remix-ui/search/src/lib/context/context.tsx b/libs/remix-ui/search/src/lib/context/context.tsx index 9af59cce28..2b372a24c8 100644 --- a/libs/remix-ui/search/src/lib/context/context.tsx +++ b/libs/remix-ui/search/src/lib/context/context.tsx @@ -338,8 +338,10 @@ export const SearchProvider = ({ async function fetchWorkspace() { try { const workspace = await plugin.call('filePanel', 'getCurrentWorkspace') - if (workspace) value.setCurrentWorkspace(workspace.name) - setFiles(await getDirectory('/', plugin)) + if (workspace) { + value.setCurrentWorkspace(workspace.name) + setFiles(await getDirectory('/', plugin)) + } } catch (e) { console.log(e) } From f60dc9805e6cc2870bc72e697466a04d2f8c9a7e Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 1 Aug 2022 13:41:03 +0200 Subject: [PATCH 09/10] add action to copy parameters --- .../src/lib/components/contractGUI.tsx | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) 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 c5f6f394ce..fb16d4a49f 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('') @@ -74,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) { @@ -95,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() @@ -251,9 +266,20 @@ export function ContractGUI (props: ContractGUIProps) { })}
- - +
+ + + + + + { props.deployOption && (props.deployOption || []).length > 0 ? From dcfacb3041d8eeeb79605f1b345451196f187ec7 Mon Sep 17 00:00:00 2001 From: lianahus Date: Tue, 2 Aug 2022 22:04:40 +0200 Subject: [PATCH 10/10] UI inprovements --- .../src/lib/components/contractGUI.tsx | 24 ++++++++++++------- .../src/lib/components/multiDeployInput.tsx | 8 +++---- libs/remix-ui/run-tab/src/lib/css/run-tab.css | 2 +- 3 files changed, 21 insertions(+), 13 deletions(-) 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 fb16d4a49f..20965b644f 100644 --- a/libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx @@ -265,21 +265,29 @@ export function ContractGUI (props: ContractGUIProps) { ) })} -
- -
- +
+ + +
{ props.deployOption && (props.deployOption || []).length > 0 ? diff --git a/libs/remix-ui/run-tab/src/lib/components/multiDeployInput.tsx b/libs/remix-ui/run-tab/src/lib/components/multiDeployInput.tsx index cec20b223c..6f3d355852 100644 --- a/libs/remix-ui/run-tab/src/lib/components/multiDeployInput.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/multiDeployInput.tsx @@ -2,13 +2,13 @@ import React, { useRef, useState } from 'react' import { MultiDeployInputProps } from '../types' import { DeployButton } from './deployButton' -export function MultiDeployInput (props: MultiDeployInputProps) { +export function MultiDeployInput(props: MultiDeployInputProps) { const multiFields = useRef>([]) - - return ( + + return (
-
+
Deploy
diff --git a/libs/remix-ui/run-tab/src/lib/css/run-tab.css b/libs/remix-ui/run-tab/src/lib/css/run-tab.css index 2921381007..1c9cd7fe5b 100644 --- a/libs/remix-ui/run-tab/src/lib/css/run-tab.css +++ b/libs/remix-ui/run-tab/src/lib/css/run-tab.css @@ -357,7 +357,7 @@ border-bottom-left-radius: 0; } .udapp_contractProperty button { - min-width: 100px; + min-width: 80px; width: 100px; margin:0; word-break: inherit;