From 38f76e9e2bcdc7fa9926112c9bab8235d9f02f04 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Fri, 23 Jul 2021 15:10:08 -0300 Subject: [PATCH 01/33] First working prototype of contract tab --- package-lock.json | 62 +++++++++++++++++++ package.json | 3 + src/AddressTransactions.tsx | 115 ++++++++++++++++++++++-------------- src/Transaction.tsx | 5 +- src/address/Contract.tsx | 76 ++++++++++++++++++++++++ src/components/TabGroup.tsx | 7 +++ src/react-app-env.d.ts | 1 + tailwind.config.js | 1 + 8 files changed, 225 insertions(+), 45 deletions(-) create mode 100644 src/address/Contract.tsx create mode 100644 src/components/TabGroup.tsx diff --git a/package-lock.json b/package-lock.json index 21ccd93..af73e5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,15 +30,18 @@ "@types/react": "^17.0.19", "@types/react-blockies": "^1.4.1", "@types/react-dom": "^17.0.9", + "@types/react-highlight": "^0.12.3", "@types/react-router-dom": "^5.1.8", "chart.js": "^3.5.1", "ethers": "^5.4.1", + "highlightjs-solidity": "^1.2.0", "query-string": "^7.0.1", "react": "^17.0.2", "react-blockies": "^1.4.1", "react-chartjs-2": "^3.0.4", "react-dom": "^17.0.2", "react-error-boundary": "^3.1.3", + "react-highlight": "^0.14.0", "react-image": "^4.0.3", "react-router-dom": "^5.2.1", "react-scripts": "4.0.3", @@ -3103,6 +3106,14 @@ "@types/react": "*" } }, + "node_modules/@types/react-highlight": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/@types/react-highlight/-/react-highlight-0.12.3.tgz", + "integrity": "sha512-mfhuHdE3dUjvRv1lvZIvda2B+VW7rkG1ufnFLKbDcRUp/L73bGUmEuEfpnjgdLgeWYho88ahQZRcMSh9GsZA0g==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-router": { "version": "5.1.15", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.15.tgz", @@ -9207,6 +9218,19 @@ "version": "1.1.0", "license": "MIT" }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, + "node_modules/highlightjs-solidity": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/highlightjs-solidity/-/highlightjs-solidity-1.2.0.tgz", + "integrity": "sha512-KXYcVzBRof3CBWHsxGffsSEAJF0YsPaOk1jgIYv2xSzrBSxkfNUJFXrlE2oZEWvYQKbPqLe4qprJyNbSDV+LZA==" + }, "node_modules/history": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", @@ -14419,6 +14443,18 @@ "version": "6.0.9", "license": "MIT" }, + "node_modules/react-highlight": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/react-highlight/-/react-highlight-0.14.0.tgz", + "integrity": "sha512-kWE+KXOXidS7SABhVopOgMnowbI3RAfeGZbnrduLNlWrYAED8sycL9l/Fvw3w0PFpIIawB7mRDnyhDcM/cIIGA==", + "dependencies": { + "highlight.js": "^10.5.0" + }, + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, "node_modules/react-image": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/react-image/-/react-image-4.0.3.tgz", @@ -21465,6 +21501,14 @@ "@types/react": "*" } }, + "@types/react-highlight": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/@types/react-highlight/-/react-highlight-0.12.3.tgz", + "integrity": "sha512-mfhuHdE3dUjvRv1lvZIvda2B+VW7rkG1ufnFLKbDcRUp/L73bGUmEuEfpnjgdLgeWYho88ahQZRcMSh9GsZA0g==", + "requires": { + "@types/react": "*" + } + }, "@types/react-router": { "version": "5.1.15", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.15.tgz", @@ -25605,6 +25649,16 @@ "hex-color-regex": { "version": "1.1.0" }, + "highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" + }, + "highlightjs-solidity": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/highlightjs-solidity/-/highlightjs-solidity-1.2.0.tgz", + "integrity": "sha512-KXYcVzBRof3CBWHsxGffsSEAJF0YsPaOk1jgIYv2xSzrBSxkfNUJFXrlE2oZEWvYQKbPqLe4qprJyNbSDV+LZA==" + }, "history": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", @@ -29065,6 +29119,14 @@ "react-error-overlay": { "version": "6.0.9" }, + "react-highlight": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/react-highlight/-/react-highlight-0.14.0.tgz", + "integrity": "sha512-kWE+KXOXidS7SABhVopOgMnowbI3RAfeGZbnrduLNlWrYAED8sycL9l/Fvw3w0PFpIIawB7mRDnyhDcM/cIIGA==", + "requires": { + "highlight.js": "^10.5.0" + } + }, "react-image": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/react-image/-/react-image-4.0.3.tgz", diff --git a/package.json b/package.json index fb6e41e..00b4172 100644 --- a/package.json +++ b/package.json @@ -25,15 +25,18 @@ "@types/react": "^17.0.19", "@types/react-blockies": "^1.4.1", "@types/react-dom": "^17.0.9", + "@types/react-highlight": "^0.12.3", "@types/react-router-dom": "^5.1.8", "chart.js": "^3.5.1", "ethers": "^5.4.1", + "highlightjs-solidity": "^1.2.0", "query-string": "^7.0.1", "react": "^17.0.2", "react-blockies": "^1.4.1", "react-chartjs-2": "^3.0.4", "react-dom": "^17.0.2", "react-error-boundary": "^3.1.3", + "react-highlight": "^0.14.0", "react-image": "^4.0.3", "react-router-dom": "^5.2.1", "react-scripts": "4.0.3", diff --git a/src/AddressTransactions.tsx b/src/AddressTransactions.tsx index f4fd2ff..ae60c21 100644 --- a/src/AddressTransactions.tsx +++ b/src/AddressTransactions.tsx @@ -1,5 +1,11 @@ import React, { useState, useEffect, useMemo, useContext } from "react"; -import { useParams, useLocation, useHistory } from "react-router-dom"; +import { + useParams, + useLocation, + useHistory, + Switch, + Route, +} from "react-router-dom"; import { BlockTag } from "@ethersproject/abstract-provider"; import { getAddress, isAddress } from "@ethersproject/address"; import queryString from "query-string"; @@ -8,6 +14,9 @@ import StandardFrame from "./StandardFrame"; import StandardSubtitle from "./StandardSubtitle"; import Copy from "./components/Copy"; import ContentFrame from "./ContentFrame"; +import TabGroup from "./components/TabGroup"; +import Tab from "./components/Tab"; +import Contract from "./address/Contract"; import UndefinedPageControl from "./search/UndefinedPageControl"; import ResultHeader from "./search/ResultHeader"; import PendingResults from "./search/PendingResults"; @@ -194,59 +203,79 @@ const AddressTransactions: React.FC = () => { )} - -
-
- {page === undefined ? ( - <>Waiting for search results... - ) : ( - <>{page.length} transactions on this page - )} -
- -
- - {controller ? ( - - {controller.getPage().map((tx) => ( - - ))} + + Overview + + Contract + + + + +
- {page !== undefined && ( + {page === undefined ? ( + <>Waiting for search results... + ) : ( <>{page.length} transactions on this page )}
-
- ) : ( - - )} -
+ + {controller ? ( + + {controller.getPage().map((tx) => ( + + ))} +
+
+ {page === undefined ? ( + <>Waiting for search results... + ) : ( + <>{page.length} transactions on this page + )} +
+ +
+ +
+ ) : ( + + )} + + + + + + ) )} diff --git a/src/Transaction.tsx b/src/Transaction.tsx index 5d10101..f24cbbe 100644 --- a/src/Transaction.tsx +++ b/src/Transaction.tsx @@ -3,6 +3,7 @@ import { Route, Switch, useParams } from "react-router-dom"; import StandardFrame from "./StandardFrame"; import StandardSubtitle from "./StandardSubtitle"; import ContentFrame from "./ContentFrame"; +import TabGroup from "./components/TabGroup"; import Tab from "./components/Tab"; import Details from "./transaction/Details"; import Logs from "./transaction/Logs"; @@ -55,14 +56,14 @@ const Transaction: React.FC = () => { )} {txData && ( -
+ Overview {txData.confirmedData?.blockNumber !== undefined && ( Logs{txData && ` (${txData.confirmedData?.logs?.length ?? 0})`} )} -
+
= ({ checksummedAddress }) => { + const [sources, setSources] = useState< + { [fileName: string]: any } | undefined | null + >(undefined); + useEffect(() => { + if (!checksummedAddress) { + return; + } + + const fetchMetadata = async () => { + try { + const result = await fetch( + `https://repo.sourcify.dev/contracts/full_match/1/${checksummedAddress}/metadata.json` + ); + if (result.ok) { + const json = await result.json(); + console.log(json); + setSources(json.sources); + setSelected(Object.keys(json.sources)[0]); + } else { + setSources(null); + } + } catch (err) { + console.error(err); + setSources(null); + } + }; + fetchMetadata(); + }, [checksummedAddress]); + + const [selected, setSelected] = useState(); + + return ( + +
+ {sources === null && ( + Couldn't find contract metadata in Sourcify repository. + )} + {sources !== undefined && sources !== null && ( + <> + {Object.entries(sources).map(([k]) => ( + + ))} + {selected && ( + + {sources[selected].content} + + )} + + )} +
+
+ ); +}; + +export default React.memo(Contract); diff --git a/src/components/TabGroup.tsx b/src/components/TabGroup.tsx new file mode 100644 index 0000000..f1bdecb --- /dev/null +++ b/src/components/TabGroup.tsx @@ -0,0 +1,7 @@ +const TabGroup: React.FC = ({ children }) => ( +
+ {children} +
+); + +export default TabGroup; diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts index 58b3bbc..625fe40 100644 --- a/src/react-app-env.d.ts +++ b/src/react-app-env.d.ts @@ -1,2 +1,3 @@ /// declare module "use-keyboard-shortcut"; +declare module "highlightjs-solidity"; diff --git a/tailwind.config.js b/tailwind.config.js index a6eafd8..012af47 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -27,6 +27,7 @@ module.exports = { data: ["Roboto Mono"], balance: ["Fira Code"], blocknum: ["Roboto"], + code: ["Fira Code"], }, borderColor: { skin: { From 851a791375662667ddd28e39528c44ce4dd080f5 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Fri, 23 Jul 2021 15:36:31 -0300 Subject: [PATCH 02/33] Add more compilation info --- src/address/Contract.tsx | 46 +++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/src/address/Contract.tsx b/src/address/Contract.tsx index d7f9de6..ae019a4 100644 --- a/src/address/Contract.tsx +++ b/src/address/Contract.tsx @@ -1,6 +1,8 @@ import React, { useState, useEffect } from "react"; +import { ethers } from "ethers"; import ContentFrame from "../ContentFrame"; import Highlight from "react-highlight"; +import InfoRow from "../components/InfoRow"; import "highlight.js/styles/stackoverflow-light.css"; import hljs from "highlight.js"; @@ -13,9 +15,7 @@ type ContractProps = { }; const Contract: React.FC = ({ checksummedAddress }) => { - const [sources, setSources] = useState< - { [fileName: string]: any } | undefined | null - >(undefined); + const [rawMetadata, setRawMetadata] = useState(); useEffect(() => { if (!checksummedAddress) { return; @@ -29,14 +29,14 @@ const Contract: React.FC = ({ checksummedAddress }) => { if (result.ok) { const json = await result.json(); console.log(json); - setSources(json.sources); + setRawMetadata(json); setSelected(Object.keys(json.sources)[0]); } else { - setSources(null); + setRawMetadata(null); } } catch (err) { console.error(err); - setSources(null); + setRawMetadata(null); } }; fetchMetadata(); @@ -44,15 +44,37 @@ const Contract: React.FC = ({ checksummedAddress }) => { const [selected, setSelected] = useState(); + const optimizer = rawMetadata?.settings?.optimizer; + return ( + {rawMetadata && ( + <> + + {rawMetadata.compiler?.version} + + + {optimizer?.enabled ? ( + + Yes with{" "} + + {ethers.utils.commify(optimizer?.runs)} + {" "} + runs + + ) : ( + No + )} + + + )}
- {sources === null && ( + {rawMetadata === null && ( Couldn't find contract metadata in Sourcify repository. )} - {sources !== undefined && sources !== null && ( - <> - {Object.entries(sources).map(([k]) => ( + {rawMetadata !== undefined && rawMetadata !== null && ( +
+ {Object.entries(rawMetadata.sources).map(([k]) => (
)}
From 0b328123d36dbf3009e53b1a8527e26f1a46930a Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sat, 24 Jul 2021 20:04:49 -0300 Subject: [PATCH 03/33] Using local sourcify repo for tests --- src/address/Contract.tsx | 9 ++++++--- src/url.ts | 6 ++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/address/Contract.tsx b/src/address/Contract.tsx index ae019a4..357e606 100644 --- a/src/address/Contract.tsx +++ b/src/address/Contract.tsx @@ -1,8 +1,10 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useContext } from "react"; import { ethers } from "ethers"; import ContentFrame from "../ContentFrame"; import Highlight from "react-highlight"; import InfoRow from "../components/InfoRow"; +import { sourcifyMetadata } from "../url"; +import { RuntimeContext } from "../useRuntime"; import "highlight.js/styles/stackoverflow-light.css"; import hljs from "highlight.js"; @@ -15,6 +17,7 @@ type ContractProps = { }; const Contract: React.FC = ({ checksummedAddress }) => { + const { provider } = useContext(RuntimeContext); const [rawMetadata, setRawMetadata] = useState(); useEffect(() => { if (!checksummedAddress) { @@ -24,7 +27,7 @@ const Contract: React.FC = ({ checksummedAddress }) => { const fetchMetadata = async () => { try { const result = await fetch( - `https://repo.sourcify.dev/contracts/full_match/1/${checksummedAddress}/metadata.json` + sourcifyMetadata(checksummedAddress, provider!.network.chainId) ); if (result.ok) { const json = await result.json(); @@ -40,7 +43,7 @@ const Contract: React.FC = ({ checksummedAddress }) => { } }; fetchMetadata(); - }, [checksummedAddress]); + }, [provider, checksummedAddress]); const [selected, setSelected] = useState(); diff --git a/src/url.ts b/src/url.ts index 00e3dc8..0d3c9e6 100644 --- a/src/url.ts +++ b/src/url.ts @@ -13,3 +13,9 @@ export const tokenLogoURL = ( export const blockURL = (blockNum: BlockTag) => `/block/${blockNum}`; export const blockTxsURL = (blockNum: BlockTag) => `/block/${blockNum}/txs`; + +export const sourcifyMetadata = ( + checksummedAddress: string, + networkId: number +) => + `http://localhost:7000/sourcify/contracts/full_match/${networkId}/${checksummedAddress}/metadata.json`; From f15c75bb8e76eb22e760aa6e8da99de505ca9d15 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sat, 24 Jul 2021 20:33:45 -0300 Subject: [PATCH 04/33] Add support for external files --- src/AddressTransactions.tsx | 4 +- src/address/Contract.tsx | 107 ++++++++++-------------------------- src/address/Contracts.tsx | 98 +++++++++++++++++++++++++++++++++ src/url.ts | 7 +++ 4 files changed, 137 insertions(+), 79 deletions(-) create mode 100644 src/address/Contracts.tsx diff --git a/src/AddressTransactions.tsx b/src/AddressTransactions.tsx index ae60c21..1d124de 100644 --- a/src/AddressTransactions.tsx +++ b/src/AddressTransactions.tsx @@ -16,7 +16,7 @@ import Copy from "./components/Copy"; import ContentFrame from "./ContentFrame"; import TabGroup from "./components/TabGroup"; import Tab from "./components/Tab"; -import Contract from "./address/Contract"; +import Contracts from "./address/Contracts"; import UndefinedPageControl from "./search/UndefinedPageControl"; import ResultHeader from "./search/ResultHeader"; import PendingResults from "./search/PendingResults"; @@ -273,7 +273,7 @@ const AddressTransactions: React.FC = () => { - + diff --git a/src/address/Contract.tsx b/src/address/Contract.tsx index 357e606..317348d 100644 --- a/src/address/Contract.tsx +++ b/src/address/Contract.tsx @@ -1,100 +1,53 @@ -import React, { useState, useEffect, useContext } from "react"; -import { ethers } from "ethers"; -import ContentFrame from "../ContentFrame"; +import React, { useState } from "react"; import Highlight from "react-highlight"; -import InfoRow from "../components/InfoRow"; -import { sourcifyMetadata } from "../url"; -import { RuntimeContext } from "../useRuntime"; import "highlight.js/styles/stackoverflow-light.css"; import hljs from "highlight.js"; import hljsDefineSolidity from "highlightjs-solidity"; +import { useEffect } from "react"; +import { sourcifySourceFile } from "../url"; hljsDefineSolidity(hljs); hljs.initHighlightingOnLoad(); type ContractProps = { checksummedAddress: string; + networkId: number; + filename: string; + source: any; }; -const Contract: React.FC = ({ checksummedAddress }) => { - const { provider } = useContext(RuntimeContext); - const [rawMetadata, setRawMetadata] = useState(); +const Contract: React.FC = ({ + checksummedAddress, + networkId, + filename, + source, +}) => { + const [content, setContent] = useState(source.content); useEffect(() => { - if (!checksummedAddress) { + if (source.content) { return; } - const fetchMetadata = async () => { - try { - const result = await fetch( - sourcifyMetadata(checksummedAddress, provider!.network.chainId) - ); - if (result.ok) { - const json = await result.json(); - console.log(json); - setRawMetadata(json); - setSelected(Object.keys(json.sources)[0]); - } else { - setRawMetadata(null); - } - } catch (err) { - console.error(err); - setRawMetadata(null); + const readContent = async () => { + const normalizedFilename = filename.replaceAll("@", "_"); + const url = sourcifySourceFile( + checksummedAddress, + networkId, + normalizedFilename + ); + const res = await fetch(url); + if (res.ok) { + const _content = await res.text(); + setContent(_content); } }; - fetchMetadata(); - }, [provider, checksummedAddress]); - - const [selected, setSelected] = useState(); - - const optimizer = rawMetadata?.settings?.optimizer; + readContent(); + }, [checksummedAddress, networkId, filename, source.content]); return ( - - {rawMetadata && ( - <> - - {rawMetadata.compiler?.version} - - - {optimizer?.enabled ? ( - - Yes with{" "} - - {ethers.utils.commify(optimizer?.runs)} - {" "} - runs - - ) : ( - No - )} - - - )} -
- {rawMetadata === null && ( - Couldn't find contract metadata in Sourcify repository. - )} - {rawMetadata !== undefined && rawMetadata !== null && ( -
- {Object.entries(rawMetadata.sources).map(([k]) => ( - - ))} - {selected && ( - - {rawMetadata.sources[selected].content} - - )} -
- )} -
-
+ + {content} + ); }; diff --git a/src/address/Contracts.tsx b/src/address/Contracts.tsx new file mode 100644 index 0000000..1fcc81c --- /dev/null +++ b/src/address/Contracts.tsx @@ -0,0 +1,98 @@ +import React, { useState, useEffect, useContext } from "react"; +import { ethers } from "ethers"; +import ContentFrame from "../ContentFrame"; +import InfoRow from "../components/InfoRow"; +import Contract from "./Contract"; +import { sourcifyMetadata } from "../url"; +import { RuntimeContext } from "../useRuntime"; + +type ContractsProps = { + checksummedAddress: string; +}; + +const Contracts: React.FC = ({ checksummedAddress }) => { + const { provider } = useContext(RuntimeContext); + const [rawMetadata, setRawMetadata] = useState(); + useEffect(() => { + if (!checksummedAddress) { + return; + } + + const fetchMetadata = async () => { + try { + const result = await fetch( + sourcifyMetadata(checksummedAddress, provider!.network.chainId) + ); + if (result.ok) { + const json = await result.json(); + console.log(json); + setRawMetadata(json); + setSelected(Object.keys(json.sources)[0]); + } else { + setRawMetadata(null); + } + } catch (err) { + console.error(err); + setRawMetadata(null); + } + }; + fetchMetadata(); + }, [provider, checksummedAddress]); + + const [selected, setSelected] = useState(); + + const optimizer = rawMetadata?.settings?.optimizer; + + return ( + + {rawMetadata && ( + <> + + {rawMetadata.compiler?.version} + + + {optimizer?.enabled ? ( + + Yes with{" "} + + {ethers.utils.commify(optimizer?.runs)} + {" "} + runs + + ) : ( + No + )} + + + )} +
+ {rawMetadata === null && ( + Couldn't find contract metadata in Sourcify repository. + )} + {rawMetadata !== undefined && rawMetadata !== null && ( +
+ {Object.entries(rawMetadata.sources).map(([k]) => ( + + ))} + {selected && ( + + )} +
+ )} +
+
+ ); +}; + +export default React.memo(Contracts); diff --git a/src/url.ts b/src/url.ts index 0d3c9e6..1b4da9c 100644 --- a/src/url.ts +++ b/src/url.ts @@ -19,3 +19,10 @@ export const sourcifyMetadata = ( networkId: number ) => `http://localhost:7000/sourcify/contracts/full_match/${networkId}/${checksummedAddress}/metadata.json`; + +export const sourcifySourceFile = ( + checksummedAddress: string, + networkId: number, + filepath: string +) => + `http://localhost:7000/sourcify/contracts/full_match/${networkId}/${checksummedAddress}/sources/${filepath}`; From 4adace4913944a16ad98aeec31f757cc91838f1e Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sat, 24 Jul 2021 20:47:09 -0300 Subject: [PATCH 05/33] Add support for switching between source files --- src/address/Contracts.tsx | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/address/Contracts.tsx b/src/address/Contracts.tsx index 1fcc81c..b0d7e47 100644 --- a/src/address/Contracts.tsx +++ b/src/address/Contracts.tsx @@ -71,15 +71,20 @@ const Contracts: React.FC = ({ checksummedAddress }) => { )} {rawMetadata !== undefined && rawMetadata !== null && (
- {Object.entries(rawMetadata.sources).map(([k]) => ( - - ))} +
+ {Object.entries(rawMetadata.sources).map(([k]) => ( + + ))} +
{selected && ( Date: Sun, 25 Jul 2021 04:03:27 -0300 Subject: [PATCH 06/33] Replace syntax highlighter component for line numbers --- package-lock.json | 409 ++++++++++++++++++++++++++++++++++++--- package.json | 3 +- src/address/Contract.tsx | 19 +- 3 files changed, 398 insertions(+), 33 deletions(-) diff --git a/package-lock.json b/package-lock.json index af73e5c..bc1199f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "@types/react-dom": "^17.0.9", "@types/react-highlight": "^0.12.3", "@types/react-router-dom": "^5.1.8", + "@types/react-syntax-highlighter": "^13.5.2", "chart.js": "^3.5.1", "ethers": "^5.4.1", "highlightjs-solidity": "^1.2.0", @@ -41,10 +42,10 @@ "react-chartjs-2": "^3.0.4", "react-dom": "^17.0.2", "react-error-boundary": "^3.1.3", - "react-highlight": "^0.14.0", "react-image": "^4.0.3", "react-router-dom": "^5.2.1", "react-scripts": "4.0.3", + "react-syntax-highlighter": "^15.4.4", "serve": "^12.0.0", "typescript": "^4.4.2", "use-keyboard-shortcut": "^1.0.6", @@ -3007,6 +3008,14 @@ "@types/node": "*" } }, + "node_modules/@types/hast": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.2.tgz", + "integrity": "sha512-Op5W7jYgZI7AWKY5wQ0/QNMzQM7dGQPyW1rXKNiymVCy5iTfdPuGu4HhYNOM2sIv8gUfIuIdcYlXmAepwaowow==", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/history": { "version": "4.7.8", "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz", @@ -3133,6 +3142,14 @@ "@types/react-router": "*" } }, + "node_modules/@types/react-syntax-highlighter": { + "version": "13.5.2", + "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-13.5.2.tgz", + "integrity": "sha512-sRZoKZBGKaE7CzMvTTgz+0x/aVR58ZYUTfB7HN76vC+yQnvo1FWtzNARBt0fGqcLGEVakEzMu/CtPzssmanu8Q==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/resolve": { "version": "0.0.8", "license": "MIT", @@ -3170,6 +3187,11 @@ "source-map": "^0.6.1" } }, + "node_modules/@types/unist": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" + }, "node_modules/@types/webpack": { "version": "4.41.26", "license": "MIT", @@ -5586,6 +5608,33 @@ "node": ">=10" } }, + "node_modules/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chart.js": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.5.1.tgz", @@ -5879,6 +5928,15 @@ "node": ">= 0.8" } }, + "node_modules/comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "2.20.3", "license": "MIT" @@ -8437,6 +8495,18 @@ "reusify": "^1.0.4" } }, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/faye-websocket": { "version": "0.11.3", "license": "Apache-2.0", @@ -8813,6 +8883,14 @@ "node": ">= 0.12" } }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/forwarded": { "version": "0.1.2", "license": "MIT", @@ -9207,6 +9285,31 @@ "minimalistic-assert": "^1.0.1" } }, + "node_modules/hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/he": { "version": "1.2.0", "license": "MIT", @@ -9780,6 +9883,28 @@ "node": ">=0.10.0" } }, + "node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-arguments": { "version": "1.1.0", "license": "MIT", @@ -9880,6 +10005,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-descriptor": { "version": "1.0.2", "license": "MIT", @@ -9957,6 +10091,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-module": { "version": "1.0.0", "license": "MIT" @@ -11324,6 +11467,19 @@ "tslib": "^2.0.3" } }, + "node_modules/lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "dependencies": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "license": "ISC", @@ -12478,6 +12634,23 @@ "safe-buffer": "^5.1.1" } }, + "node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "dependencies": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/parse-json": { "version": "5.2.0", "license": "MIT", @@ -13921,6 +14094,11 @@ "node": ">= 0.8" } }, + "node_modules/prismjs": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.24.1.tgz", + "integrity": "sha512-mNPsedLuk90RVJioIky8ANZEwYm5w9LcvCXrxHlwf4fNVSn8jEipMybMkWUyyF0JhnC+C4VcOVSBuHRKs1L5Ow==" + }, "node_modules/process": { "version": "0.11.10", "license": "MIT", @@ -13974,6 +14152,18 @@ "version": "16.13.1", "license": "MIT" }, + "node_modules/property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "dependencies": { + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/proxy-addr": { "version": "2.0.6", "license": "MIT", @@ -14443,18 +14633,6 @@ "version": "6.0.9", "license": "MIT" }, - "node_modules/react-highlight": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-highlight/-/react-highlight-0.14.0.tgz", - "integrity": "sha512-kWE+KXOXidS7SABhVopOgMnowbI3RAfeGZbnrduLNlWrYAED8sycL9l/Fvw3w0PFpIIawB7mRDnyhDcM/cIIGA==", - "dependencies": { - "highlight.js": "^10.5.0" - }, - "peerDependencies": { - "react": "^15.0.0 || ^16.0.0 || ^17.0.0", - "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0" - } - }, "node_modules/react-image": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/react-image/-/react-image-4.0.3.tgz", @@ -14802,6 +14980,21 @@ "node": ">=0.10.0" } }, + "node_modules/react-syntax-highlighter": { + "version": "15.4.4", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.4.4.tgz", + "integrity": "sha512-PsOFHNTzkb3OroXdoR897eKN5EZ6grht1iM+f1lJSq7/L0YVnkJaNVwC3wEUYPOAmeyl5xyer1DjL6MrumO6Zw==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "highlight.js": "^10.4.1", + "lowlight": "^1.17.0", + "prismjs": "^1.22.0", + "refractor": "^3.2.0" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } + }, "node_modules/read-pkg": { "version": "5.2.0", "license": "MIT", @@ -14965,6 +15158,20 @@ "postcss-value-parser": "^3.3.0" } }, + "node_modules/refractor": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.4.0.tgz", + "integrity": "sha512-dBeD02lC5eytm9Gld2Mx0cMcnR+zhSnsTfPpWqFaMgUMJfC9A6bcN3Br/NaXrnBJcuxnLFR90k1jrkaSyV8umg==", + "dependencies": { + "hastscript": "^6.0.0", + "parse-entities": "^2.0.0", + "prismjs": "~1.24.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/regenerate": { "version": "1.4.2", "license": "MIT" @@ -16497,6 +16704,15 @@ "version": "1.4.8", "license": "MIT" }, + "node_modules/space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/spdx-correct": { "version": "3.1.1", "license": "Apache-2.0", @@ -21414,6 +21630,14 @@ "@types/node": "*" } }, + "@types/hast": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.2.tgz", + "integrity": "sha512-Op5W7jYgZI7AWKY5wQ0/QNMzQM7dGQPyW1rXKNiymVCy5iTfdPuGu4HhYNOM2sIv8gUfIuIdcYlXmAepwaowow==", + "requires": { + "@types/unist": "*" + } + }, "@types/history": { "version": "4.7.8", "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz", @@ -21528,6 +21752,14 @@ "@types/react-router": "*" } }, + "@types/react-syntax-highlighter": { + "version": "13.5.2", + "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-13.5.2.tgz", + "integrity": "sha512-sRZoKZBGKaE7CzMvTTgz+0x/aVR58ZYUTfB7HN76vC+yQnvo1FWtzNARBt0fGqcLGEVakEzMu/CtPzssmanu8Q==", + "requires": { + "@types/react": "*" + } + }, "@types/resolve": { "version": "0.0.8", "requires": { @@ -21558,6 +21790,11 @@ "source-map": "^0.6.1" } }, + "@types/unist": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" + }, "@types/webpack": { "version": "4.41.26", "requires": { @@ -23230,6 +23467,21 @@ "char-regex": { "version": "1.0.2" }, + "character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==" + }, + "character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==" + }, + "character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==" + }, "chart.js": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.5.1.tgz", @@ -23447,6 +23699,11 @@ "delayed-stream": "~1.0.0" } }, + "comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==" + }, "commander": { "version": "2.20.3" }, @@ -25150,6 +25407,14 @@ "reusify": "^1.0.4" } }, + "fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "requires": { + "format": "^0.2.0" + } + }, "faye-websocket": { "version": "0.11.3", "requires": { @@ -25393,6 +25658,11 @@ "mime-types": "^2.1.12" } }, + "format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=" + }, "forwarded": { "version": "0.1.2" }, @@ -25643,6 +25913,23 @@ "minimalistic-assert": "^1.0.1" } }, + "hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==" + }, + "hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "requires": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + } + }, "he": { "version": "1.2.0" }, @@ -26026,6 +26313,20 @@ } } }, + "is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==" + }, + "is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "requires": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + } + }, "is-arguments": { "version": "1.1.0", "requires": { @@ -26084,6 +26385,11 @@ "is-date-object": { "version": "1.0.2" }, + "is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==" + }, "is-descriptor": { "version": "1.0.2", "requires": { @@ -26121,6 +26427,11 @@ "is-extglob": "^2.1.1" } }, + "is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==" + }, "is-module": { "version": "1.0.0" }, @@ -27030,6 +27341,15 @@ "tslib": "^2.0.3" } }, + "lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "requires": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + } + }, "lru-cache": { "version": "6.0.0", "requires": { @@ -27774,6 +28094,19 @@ "safe-buffer": "^5.1.1" } }, + "parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, "parse-json": { "version": "5.2.0", "requires": { @@ -28762,6 +29095,11 @@ "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", "dev": true }, + "prismjs": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.24.1.tgz", + "integrity": "sha512-mNPsedLuk90RVJioIky8ANZEwYm5w9LcvCXrxHlwf4fNVSn8jEipMybMkWUyyF0JhnC+C4VcOVSBuHRKs1L5Ow==" + }, "process": { "version": "0.11.10" }, @@ -28800,6 +29138,14 @@ } } }, + "property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "requires": { + "xtend": "^4.0.0" + } + }, "proxy-addr": { "version": "2.0.6", "requires": { @@ -29119,14 +29465,6 @@ "react-error-overlay": { "version": "6.0.9" }, - "react-highlight": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-highlight/-/react-highlight-0.14.0.tgz", - "integrity": "sha512-kWE+KXOXidS7SABhVopOgMnowbI3RAfeGZbnrduLNlWrYAED8sycL9l/Fvw3w0PFpIIawB7mRDnyhDcM/cIIGA==", - "requires": { - "highlight.js": "^10.5.0" - } - }, "react-image": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/react-image/-/react-image-4.0.3.tgz", @@ -29371,6 +29709,18 @@ } } }, + "react-syntax-highlighter": { + "version": "15.4.4", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.4.4.tgz", + "integrity": "sha512-PsOFHNTzkb3OroXdoR897eKN5EZ6grht1iM+f1lJSq7/L0YVnkJaNVwC3wEUYPOAmeyl5xyer1DjL6MrumO6Zw==", + "requires": { + "@babel/runtime": "^7.3.1", + "highlight.js": "^10.4.1", + "lowlight": "^1.17.0", + "prismjs": "^1.22.0", + "refractor": "^3.2.0" + } + }, "read-pkg": { "version": "5.2.0", "requires": { @@ -29490,6 +29840,16 @@ "postcss-value-parser": "^3.3.0" } }, + "refractor": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.4.0.tgz", + "integrity": "sha512-dBeD02lC5eytm9Gld2Mx0cMcnR+zhSnsTfPpWqFaMgUMJfC9A6bcN3Br/NaXrnBJcuxnLFR90k1jrkaSyV8umg==", + "requires": { + "hastscript": "^6.0.0", + "parse-entities": "^2.0.0", + "prismjs": "~1.24.0" + } + }, "regenerate": { "version": "1.4.2" }, @@ -30554,6 +30914,11 @@ "sourcemap-codec": { "version": "1.4.8" }, + "space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==" + }, "spdx-correct": { "version": "3.1.1", "requires": { @@ -32645,4 +33010,4 @@ "version": "0.1.0" } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index 00b4172..0a2ed88 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@types/react-dom": "^17.0.9", "@types/react-highlight": "^0.12.3", "@types/react-router-dom": "^5.1.8", + "@types/react-syntax-highlighter": "^13.5.2", "chart.js": "^3.5.1", "ethers": "^5.4.1", "highlightjs-solidity": "^1.2.0", @@ -36,10 +37,10 @@ "react-chartjs-2": "^3.0.4", "react-dom": "^17.0.2", "react-error-boundary": "^3.1.3", - "react-highlight": "^0.14.0", "react-image": "^4.0.3", "react-router-dom": "^5.2.1", "react-scripts": "4.0.3", + "react-syntax-highlighter": "^15.4.4", "serve": "^12.0.0", "typescript": "^4.4.2", "use-keyboard-shortcut": "^1.0.6", diff --git a/src/address/Contract.tsx b/src/address/Contract.tsx index 317348d..8873e00 100644 --- a/src/address/Contract.tsx +++ b/src/address/Contract.tsx @@ -1,13 +1,12 @@ -import React, { useState } from "react"; -import Highlight from "react-highlight"; - -import "highlight.js/styles/stackoverflow-light.css"; +import React, { useState, useEffect } from "react"; +import SyntaxHighlighter from "react-syntax-highlighter"; import hljs from "highlight.js"; -import hljsDefineSolidity from "highlightjs-solidity"; -import { useEffect } from "react"; +import { docco } from "react-syntax-highlighter/dist/esm/styles/hljs"; + import { sourcifySourceFile } from "../url"; + +import hljsDefineSolidity from "highlightjs-solidity"; hljsDefineSolidity(hljs); -hljs.initHighlightingOnLoad(); type ContractProps = { checksummedAddress: string; @@ -45,9 +44,9 @@ const Contract: React.FC = ({ }, [checksummedAddress, networkId, filename, source.content]); return ( - - {content} - + + {content ?? ""} + ); }; From 65ee6e9182ac9e7e003bd215f97e62a419b3c4b8 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sun, 25 Jul 2021 04:10:42 -0300 Subject: [PATCH 07/33] Use light imports --- src/address/Contract.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/address/Contract.tsx b/src/address/Contract.tsx index 8873e00..e946391 100644 --- a/src/address/Contract.tsx +++ b/src/address/Contract.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from "react"; -import SyntaxHighlighter from "react-syntax-highlighter"; +import { Light as SyntaxHighlighter } from "react-syntax-highlighter"; import hljs from "highlight.js"; -import { docco } from "react-syntax-highlighter/dist/esm/styles/hljs"; +import docco from "react-syntax-highlighter/dist/esm/styles/hljs/docco"; import { sourcifySourceFile } from "../url"; @@ -44,7 +44,12 @@ const Contract: React.FC = ({ }, [checksummedAddress, networkId, filename, source.content]); return ( - + {content ?? ""} ); From aeae3c3825f419bc3e2c5d1ccb8cafa9bb8de3fe Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sun, 25 Jul 2021 04:16:01 -0300 Subject: [PATCH 08/33] Add hover animations --- src/address/Contracts.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/address/Contracts.tsx b/src/address/Contracts.tsx index b0d7e47..5fd1d1b 100644 --- a/src/address/Contracts.tsx +++ b/src/address/Contracts.tsx @@ -77,7 +77,7 @@ const Contracts: React.FC = ({ checksummedAddress }) => { className={`border-b-2 border-transparent rounded-t text-sm px-2 py-1 ${ selected === k ? "border-orange-300 font-bold bg-gray-200 text-gray-500" - : "bg-gray-100 text-gray-400 transform origin-bottom scale-95" + : "hover:border-orange-200 bg-gray-100 hover:text-gray-500 text-gray-400 transition-transform transition-colors duration-75 transform origin-bottom scale-95 hover:scale-100" }`} onClick={() => setSelected(k)} > From 5d247899986dcc0c82d95a5666680bdf09f5b6e9 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sun, 25 Jul 2021 05:09:30 -0300 Subject: [PATCH 09/33] Add language file info --- src/address/Contracts.tsx | 48 ++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/src/address/Contracts.tsx b/src/address/Contracts.tsx index 5fd1d1b..a19a194 100644 --- a/src/address/Contracts.tsx +++ b/src/address/Contracts.tsx @@ -6,13 +6,47 @@ import Contract from "./Contract"; import { sourcifyMetadata } from "../url"; import { RuntimeContext } from "../useRuntime"; +type Metadata = { + version: string; + language: string; + compiler: { + version: string; + keccak256?: string | undefined; + }; + sources: { + [filename: string]: { + keccak256: string; + content?: string | undefined; + urls?: string[]; + }; + }; + settings: { + remappings: string[]; + optimizer?: { + enabled: boolean; + runs: number; + }; + compilationTarget: { + [filename: string]: string; + }; + libraries: { + [filename: string]: string; + }; + }; + output: { + abi: any[]; + userdocs: any[]; + devdoc: any[]; + }; +}; + type ContractsProps = { checksummedAddress: string; }; const Contracts: React.FC = ({ checksummedAddress }) => { const { provider } = useContext(RuntimeContext); - const [rawMetadata, setRawMetadata] = useState(); + const [rawMetadata, setRawMetadata] = useState(); useEffect(() => { if (!checksummedAddress) { return; @@ -24,10 +58,9 @@ const Contracts: React.FC = ({ checksummedAddress }) => { sourcifyMetadata(checksummedAddress, provider!.network.chainId) ); if (result.ok) { - const json = await result.json(); - console.log(json); - setRawMetadata(json); - setSelected(Object.keys(json.sources)[0]); + const _metadata = await result.json(); + setRawMetadata(_metadata); + setSelected(Object.keys(_metadata.sources)[0]); } else { setRawMetadata(null); } @@ -47,8 +80,11 @@ const Contracts: React.FC = ({ checksummedAddress }) => { {rawMetadata && ( <> + + {rawMetadata.language} + - {rawMetadata.compiler?.version} + {rawMetadata.compiler.version} {optimizer?.enabled ? ( From 9f7962b6d4289d64ba326f67da87114aacd7dc9c Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sun, 25 Jul 2021 17:52:47 -0300 Subject: [PATCH 10/33] Addd license field --- src/address/Contracts.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/address/Contracts.tsx b/src/address/Contracts.tsx index a19a194..3b370e4 100644 --- a/src/address/Contracts.tsx +++ b/src/address/Contracts.tsx @@ -18,6 +18,7 @@ type Metadata = { keccak256: string; content?: string | undefined; urls?: string[]; + license?: string; }; }; settings: { From fd8bdf8765b4a5891df36a34179998ace15715f0 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sun, 5 Sep 2021 16:55:37 -0300 Subject: [PATCH 11/33] Use default overridable gateway for ipfs/sourcify --- src/url.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/url.ts b/src/url.ts index 1b4da9c..d9f8459 100644 --- a/src/url.ts +++ b/src/url.ts @@ -14,15 +14,20 @@ export const blockURL = (blockNum: BlockTag) => `/block/${blockNum}`; export const blockTxsURL = (blockNum: BlockTag) => `/block/${blockNum}/txs`; +// const ipfsGatewayPrefix = "http://localhost:7000/sourcify"; +const sourcifyRootHash = + "k51qzi5uqu5dll0ocge71eudqnrgnogmbr37gsgl12uubsinphjoknl6bbi41p"; +const ipfsGatewayPrefix = `https://ipfs.io/ipns/${sourcifyRootHash}`; + export const sourcifyMetadata = ( checksummedAddress: string, networkId: number ) => - `http://localhost:7000/sourcify/contracts/full_match/${networkId}/${checksummedAddress}/metadata.json`; + `${ipfsGatewayPrefix}/contracts/full_match/${networkId}/${checksummedAddress}/metadata.json`; export const sourcifySourceFile = ( checksummedAddress: string, networkId: number, filepath: string ) => - `http://localhost:7000/sourcify/contracts/full_match/${networkId}/${checksummedAddress}/sources/${filepath}`; + `${ipfsGatewayPrefix}/contracts/full_match/${networkId}/${checksummedAddress}/sources/${filepath}`; From 37b253ab399634ee740f4cc9465955e029045afd Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sun, 5 Sep 2021 17:19:50 -0300 Subject: [PATCH 12/33] Remove unused code --- src/url.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/url.ts b/src/url.ts index d9f8459..1c01cc9 100644 --- a/src/url.ts +++ b/src/url.ts @@ -14,7 +14,6 @@ export const blockURL = (blockNum: BlockTag) => `/block/${blockNum}`; export const blockTxsURL = (blockNum: BlockTag) => `/block/${blockNum}/txs`; -// const ipfsGatewayPrefix = "http://localhost:7000/sourcify"; const sourcifyRootHash = "k51qzi5uqu5dll0ocge71eudqnrgnogmbr37gsgl12uubsinphjoknl6bbi41p"; const ipfsGatewayPrefix = `https://ipfs.io/ipns/${sourcifyRootHash}`; From 16ffe567b69f34d0d2a17e61c1fb14dc0d5db297 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sun, 5 Sep 2021 17:39:11 -0300 Subject: [PATCH 13/33] Use headless-ui Tabs --- src/AddressTransactions.tsx | 126 ++++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 55 deletions(-) diff --git a/src/AddressTransactions.tsx b/src/AddressTransactions.tsx index 1d124de..c5b96a5 100644 --- a/src/AddressTransactions.tsx +++ b/src/AddressTransactions.tsx @@ -1,4 +1,10 @@ -import React, { useState, useEffect, useMemo, useContext } from "react"; +import React, { + useState, + useEffect, + useMemo, + useContext, + Fragment, +} from "react"; import { useParams, useLocation, @@ -8,14 +14,14 @@ import { } from "react-router-dom"; import { BlockTag } from "@ethersproject/abstract-provider"; import { getAddress, isAddress } from "@ethersproject/address"; +import { Tab } from "@headlessui/react"; import queryString from "query-string"; import Blockies from "react-blockies"; import StandardFrame from "./StandardFrame"; import StandardSubtitle from "./StandardSubtitle"; import Copy from "./components/Copy"; import ContentFrame from "./ContentFrame"; -import TabGroup from "./components/TabGroup"; -import Tab from "./components/Tab"; +import CustomTab from "./components/Tab"; import Contracts from "./address/Contracts"; import UndefinedPageControl from "./search/UndefinedPageControl"; import ResultHeader from "./search/ResultHeader"; @@ -203,48 +209,23 @@ const AddressTransactions: React.FC = () => { )}
- - Overview - - Contract - - - - - -
-
- {page === undefined ? ( - <>Waiting for search results... - ) : ( - <>{page.length} transactions on this page - )} -
- -
- - {controller ? ( - - {controller.getPage().map((tx) => ( - - ))} + + + + + Overview + + + + + Contract + + + + + + +
{page === undefined ? ( @@ -266,16 +247,51 @@ const AddressTransactions: React.FC = () => { feeDisplay={feeDisplay} feeDisplayToggler={feeDisplayToggler} /> - - ) : ( - - )} - - - - - - + {controller ? ( + + {controller.getPage().map((tx) => ( + + ))} +
+
+ {page === undefined ? ( + <>Waiting for search results... + ) : ( + <>{page.length} transactions on this page + )} +
+ +
+ +
+ ) : ( + + )} + + + + + + + + ) )} From 605302ee64a0def569ef2bea5596cdbf58cac398 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sun, 5 Sep 2021 21:08:06 -0300 Subject: [PATCH 14/33] Extract sourcify hook --- src/address/Contracts.tsx | 72 ++++++--------------------------------- src/useSourcify.ts | 72 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 62 deletions(-) create mode 100644 src/useSourcify.ts diff --git a/src/address/Contracts.tsx b/src/address/Contracts.tsx index 3b370e4..bea7485 100644 --- a/src/address/Contracts.tsx +++ b/src/address/Contracts.tsx @@ -3,43 +3,8 @@ import { ethers } from "ethers"; import ContentFrame from "../ContentFrame"; import InfoRow from "../components/InfoRow"; import Contract from "./Contract"; -import { sourcifyMetadata } from "../url"; import { RuntimeContext } from "../useRuntime"; - -type Metadata = { - version: string; - language: string; - compiler: { - version: string; - keccak256?: string | undefined; - }; - sources: { - [filename: string]: { - keccak256: string; - content?: string | undefined; - urls?: string[]; - license?: string; - }; - }; - settings: { - remappings: string[]; - optimizer?: { - enabled: boolean; - runs: number; - }; - compilationTarget: { - [filename: string]: string; - }; - libraries: { - [filename: string]: string; - }; - }; - output: { - abi: any[]; - userdocs: any[]; - devdoc: any[]; - }; -}; +import { useSourcify } from "../useSourcify"; type ContractsProps = { checksummedAddress: string; @@ -47,34 +12,17 @@ type ContractsProps = { const Contracts: React.FC = ({ checksummedAddress }) => { const { provider } = useContext(RuntimeContext); - const [rawMetadata, setRawMetadata] = useState(); - useEffect(() => { - if (!checksummedAddress) { - return; - } - - const fetchMetadata = async () => { - try { - const result = await fetch( - sourcifyMetadata(checksummedAddress, provider!.network.chainId) - ); - if (result.ok) { - const _metadata = await result.json(); - setRawMetadata(_metadata); - setSelected(Object.keys(_metadata.sources)[0]); - } else { - setRawMetadata(null); - } - } catch (err) { - console.error(err); - setRawMetadata(null); - } - }; - fetchMetadata(); - }, [provider, checksummedAddress]); + const rawMetadata = useSourcify( + checksummedAddress, + provider?.network.chainId + ); const [selected, setSelected] = useState(); - + useEffect(() => { + if (rawMetadata) { + setSelected(Object.keys(rawMetadata.sources)[0]); + } + }, [rawMetadata]); const optimizer = rawMetadata?.settings?.optimizer; return ( diff --git a/src/useSourcify.ts b/src/useSourcify.ts new file mode 100644 index 0000000..cb67767 --- /dev/null +++ b/src/useSourcify.ts @@ -0,0 +1,72 @@ +import { useState, useEffect } from "react"; +import { sourcifyMetadata } from "./url"; + +export type Metadata = { + version: string; + language: string; + compiler: { + version: string; + keccak256?: string | undefined; + }; + sources: { + [filename: string]: { + keccak256: string; + content?: string | undefined; + urls?: string[]; + license?: string; + }; + }; + settings: { + remappings: string[]; + optimizer?: { + enabled: boolean; + runs: number; + }; + compilationTarget: { + [filename: string]: string; + }; + libraries: { + [filename: string]: string; + }; + }; + output: { + abi: any[]; + userdocs: any[]; + devdoc: any[]; + }; +}; + +export const useSourcify = ( + checksummedAddress: string | undefined, + chainId: number | undefined +) => { + const [rawMetadata, setRawMetadata] = useState(); + + useEffect(() => { + if (!checksummedAddress || chainId === undefined) { + return; + } + + const fetchMetadata = async () => { + try { + const contractMetadataURL = sourcifyMetadata( + checksummedAddress, + chainId + ); + const result = await fetch(contractMetadataURL); + if (result.ok) { + const _metadata = await result.json(); + setRawMetadata(_metadata); + } else { + setRawMetadata(null); + } + } catch (err) { + console.error(err); + setRawMetadata(null); + } + }; + fetchMetadata(); + }, [checksummedAddress, chainId]); + + return rawMetadata; +}; From 71e359eb924160a5f20eb903d2ab5e0eaee1703a Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sun, 5 Sep 2021 21:13:01 -0300 Subject: [PATCH 15/33] Move metadata reading one level up --- src/AddressTransactions.tsx | 10 +++++++++- src/address/Contracts.tsx | 12 ++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/AddressTransactions.tsx b/src/AddressTransactions.tsx index c5b96a5..1970b8c 100644 --- a/src/AddressTransactions.tsx +++ b/src/AddressTransactions.tsx @@ -33,6 +33,7 @@ import { useENSCache } from "./useReverseCache"; import { useFeeToggler } from "./search/useFeeToggler"; import { SelectionContext, useSelection } from "./useSelection"; import { useMultipleETHUSDOracle } from "./usePriceOracle"; +import { useSourcify } from "./useSourcify"; type BlockParams = { addressOrName: string; @@ -180,6 +181,10 @@ const AddressTransactions: React.FC = () => { const [feeDisplay, feeDisplayToggler] = useFeeToggler(); const selectionCtx = useSelection(); + const rawMetadata = useSourcify( + checksummedAddress, + provider?.network.chainId + ); return ( @@ -287,7 +292,10 @@ const AddressTransactions: React.FC = () => { - + diff --git a/src/address/Contracts.tsx b/src/address/Contracts.tsx index bea7485..892fe67 100644 --- a/src/address/Contracts.tsx +++ b/src/address/Contracts.tsx @@ -4,18 +4,18 @@ import ContentFrame from "../ContentFrame"; import InfoRow from "../components/InfoRow"; import Contract from "./Contract"; import { RuntimeContext } from "../useRuntime"; -import { useSourcify } from "../useSourcify"; +import { Metadata } from "../useSourcify"; type ContractsProps = { checksummedAddress: string; + rawMetadata: Metadata | null | undefined; }; -const Contracts: React.FC = ({ checksummedAddress }) => { +const Contracts: React.FC = ({ + checksummedAddress, + rawMetadata, +}) => { const { provider } = useContext(RuntimeContext); - const rawMetadata = useSourcify( - checksummedAddress, - provider?.network.chainId - ); const [selected, setSelected] = useState(); useEffect(() => { From dfcbc1a15004f4911f7cec87ab85ab7758046701 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 6 Sep 2021 02:09:12 -0300 Subject: [PATCH 16/33] Extract hook --- src/address/Contract.tsx | 27 +++------------------------ src/useSourcify.ts | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/address/Contract.tsx b/src/address/Contract.tsx index e946391..1a30583 100644 --- a/src/address/Contract.tsx +++ b/src/address/Contract.tsx @@ -1,11 +1,10 @@ -import React, { useState, useEffect } from "react"; +import React from "react"; import { Light as SyntaxHighlighter } from "react-syntax-highlighter"; import hljs from "highlight.js"; import docco from "react-syntax-highlighter/dist/esm/styles/hljs/docco"; -import { sourcifySourceFile } from "../url"; - import hljsDefineSolidity from "highlightjs-solidity"; +import { useContract } from "../useSourcify"; hljsDefineSolidity(hljs); type ContractProps = { @@ -21,27 +20,7 @@ const Contract: React.FC = ({ filename, source, }) => { - const [content, setContent] = useState(source.content); - useEffect(() => { - if (source.content) { - return; - } - - const readContent = async () => { - const normalizedFilename = filename.replaceAll("@", "_"); - const url = sourcifySourceFile( - checksummedAddress, - networkId, - normalizedFilename - ); - const res = await fetch(url); - if (res.ok) { - const _content = await res.text(); - setContent(_content); - } - }; - readContent(); - }, [checksummedAddress, networkId, filename, source.content]); + const content = useContract(checksummedAddress, networkId, filename, source); return ( { + const [content, setContent] = useState(source.content); + + useEffect(() => { + if (source.content) { + return; + } + + const readContent = async () => { + const normalizedFilename = filename.replaceAll("@", "_"); + const url = sourcifySourceFile( + checksummedAddress, + networkId, + normalizedFilename + ); + const res = await fetch(url); + if (res.ok) { + const _content = await res.text(); + setContent(_content); + } + }; + readContent(); + }, [checksummedAddress, networkId, filename, source.content]); + + return content; +}; From a76a2d71a2f3adf5e89228100b8ed9669e661e3e Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 6 Sep 2021 03:34:13 -0300 Subject: [PATCH 17/33] Rename component --- src/AddressTransactions.tsx | 10 +++++----- src/Transaction.tsx | 8 ++++---- src/components/{Tab.tsx => NavTab.tsx} | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) rename src/components/{Tab.tsx => NavTab.tsx} (75%) diff --git a/src/AddressTransactions.tsx b/src/AddressTransactions.tsx index 1970b8c..cb17b4b 100644 --- a/src/AddressTransactions.tsx +++ b/src/AddressTransactions.tsx @@ -21,7 +21,7 @@ import StandardFrame from "./StandardFrame"; import StandardSubtitle from "./StandardSubtitle"; import Copy from "./components/Copy"; import ContentFrame from "./ContentFrame"; -import CustomTab from "./components/Tab"; +import NavTab from "./components/NavTab"; import Contracts from "./address/Contracts"; import UndefinedPageControl from "./search/UndefinedPageControl"; import ResultHeader from "./search/ResultHeader"; @@ -217,14 +217,14 @@ const AddressTransactions: React.FC = () => { - + Overview - + - + Contract - + diff --git a/src/Transaction.tsx b/src/Transaction.tsx index f24cbbe..63ec3ca 100644 --- a/src/Transaction.tsx +++ b/src/Transaction.tsx @@ -4,7 +4,7 @@ import StandardFrame from "./StandardFrame"; import StandardSubtitle from "./StandardSubtitle"; import ContentFrame from "./ContentFrame"; import TabGroup from "./components/TabGroup"; -import Tab from "./components/Tab"; +import NavTab from "./components/NavTab"; import Details from "./transaction/Details"; import Logs from "./transaction/Logs"; import { RuntimeContext } from "./useRuntime"; @@ -57,11 +57,11 @@ const Transaction: React.FC = () => { {txData && ( - Overview + Overview {txData.confirmedData?.blockNumber !== undefined && ( - + Logs{txData && ` (${txData.confirmedData?.logs?.length ?? 0})`} - + )} diff --git a/src/components/Tab.tsx b/src/components/NavTab.tsx similarity index 75% rename from src/components/Tab.tsx rename to src/components/NavTab.tsx index 4e9352a..c3b6d54 100644 --- a/src/components/Tab.tsx +++ b/src/components/NavTab.tsx @@ -1,11 +1,11 @@ import React from "react"; import { NavLink } from "react-router-dom"; -type TabProps = { +type NavTabProps = { href: string; }; -const Tab: React.FC = ({ href, children }) => ( +const NavTab: React.FC = ({ href, children }) => ( = ({ href, children }) => ( ); -export default Tab; +export default NavTab; From fe336c5c4327b0bfe3e4f83a0c20ee6b26df9fbe Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 6 Sep 2021 03:43:20 -0300 Subject: [PATCH 18/33] Refactorings --- src/AddressTransactions.tsx | 16 ++++++--------- src/Transaction.tsx | 21 +++++++++++--------- src/address/Contracts.tsx | 39 +++++++++++++++++++++---------------- src/components/NavTab.tsx | 23 ++++++++++++---------- src/components/TabGroup.tsx | 7 ------- 5 files changed, 53 insertions(+), 53 deletions(-) delete mode 100644 src/components/TabGroup.tsx diff --git a/src/AddressTransactions.tsx b/src/AddressTransactions.tsx index cb17b4b..659c1eb 100644 --- a/src/AddressTransactions.tsx +++ b/src/AddressTransactions.tsx @@ -216,16 +216,12 @@ const AddressTransactions: React.FC = () => { - - - Overview - - - - - Contract - - + + Overview + + + Contract + diff --git a/src/Transaction.tsx b/src/Transaction.tsx index 63ec3ca..e204633 100644 --- a/src/Transaction.tsx +++ b/src/Transaction.tsx @@ -1,9 +1,9 @@ import React, { useMemo, useContext } from "react"; import { Route, Switch, useParams } from "react-router-dom"; +import { Tab } from "@headlessui/react"; import StandardFrame from "./StandardFrame"; import StandardSubtitle from "./StandardSubtitle"; import ContentFrame from "./ContentFrame"; -import TabGroup from "./components/TabGroup"; import NavTab from "./components/NavTab"; import Details from "./transaction/Details"; import Logs from "./transaction/Logs"; @@ -56,14 +56,17 @@ const Transaction: React.FC = () => { )} {txData && ( - - Overview - {txData.confirmedData?.blockNumber !== undefined && ( - - Logs{txData && ` (${txData.confirmedData?.logs?.length ?? 0})`} - - )} - + + + Overview + {txData.confirmedData?.blockNumber !== undefined && ( + + Logs + {txData && ` (${txData.confirmedData?.logs?.length ?? 0})`} + + )} + +
= ({ Yes with{" "} - {ethers.utils.commify(optimizer?.runs)} + {commify(optimizer?.runs)} {" "} runs @@ -56,20 +57,24 @@ const Contracts: React.FC = ({ )} {rawMetadata !== undefined && rawMetadata !== null && (
-
- {Object.entries(rawMetadata.sources).map(([k]) => ( - - ))} -
+ + + {Object.entries(rawMetadata.sources).map(([k]) => ( + + + + ))} + + {selected && ( = ({ href, children }) => ( - - {children} - + + + {children} + + ); export default NavTab; diff --git a/src/components/TabGroup.tsx b/src/components/TabGroup.tsx deleted file mode 100644 index f1bdecb..0000000 --- a/src/components/TabGroup.tsx +++ /dev/null @@ -1,7 +0,0 @@ -const TabGroup: React.FC = ({ children }) => ( -
- {children} -
-); - -export default TabGroup; From 7b5ecb91baadd7e35667dc8d65b0dc46e2616f67 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 6 Sep 2021 04:14:49 -0300 Subject: [PATCH 19/33] Open in remix feature #54 --- src/address/Contracts.tsx | 14 ++++++++++++++ src/url.ts | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/src/address/Contracts.tsx b/src/address/Contracts.tsx index 5237320..9e8eaef 100644 --- a/src/address/Contracts.tsx +++ b/src/address/Contracts.tsx @@ -6,6 +6,8 @@ import InfoRow from "../components/InfoRow"; import Contract from "./Contract"; import { RuntimeContext } from "../useRuntime"; import { Metadata } from "../useSourcify"; +import ExternalLink from "../components/ExternalLink"; +import { openInRemixURL } from "../url"; type ContractsProps = { checksummedAddress: string; @@ -57,6 +59,18 @@ const Contracts: React.FC = ({ )} {rawMetadata !== undefined && rawMetadata !== null && (
+ {provider && ( +
+ + Open in Remix + +
+ )} {Object.entries(rawMetadata.sources).map(([k]) => ( diff --git a/src/url.ts b/src/url.ts index 1c01cc9..8aaa6f0 100644 --- a/src/url.ts +++ b/src/url.ts @@ -17,6 +17,7 @@ export const blockTxsURL = (blockNum: BlockTag) => `/block/${blockNum}/txs`; const sourcifyRootHash = "k51qzi5uqu5dll0ocge71eudqnrgnogmbr37gsgl12uubsinphjoknl6bbi41p"; const ipfsGatewayPrefix = `https://ipfs.io/ipns/${sourcifyRootHash}`; +// const ipfsGatewayPrefix = `https://repo.sourcify.dev`; export const sourcifyMetadata = ( checksummedAddress: string, @@ -30,3 +31,6 @@ export const sourcifySourceFile = ( filepath: string ) => `${ipfsGatewayPrefix}/contracts/full_match/${networkId}/${checksummedAddress}/sources/${filepath}`; + +export const openInRemixURL = (checksummedAddress: string, networkId: number) => + `https://remix.ethereum.org/#call=source-verification//fetchAndSave//${checksummedAddress}//${networkId}`; From 1804d83c646b05cc8b1b9a09108ea9190a97a054 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 18:32:20 +0000 Subject: [PATCH 20/33] Bump @types/react from 17.0.19 to 17.0.20 (#60) --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 21ccd93..0a36bfb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "@testing-library/user-event": "^12.1.10", "@types/jest": "^26.0.24", "@types/node": "^14.17.5", - "@types/react": "^17.0.19", + "@types/react": "^17.0.20", "@types/react-blockies": "^1.4.1", "@types/react-dom": "^17.0.9", "@types/react-router-dom": "^5.1.8", @@ -3078,9 +3078,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "17.0.19", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.19.tgz", - "integrity": "sha512-sX1HisdB1/ZESixMTGnMxH9TDe8Sk709734fEQZzCV/4lSu9kJCPbo2PbTRoZM+53Pp0P10hYVyReUueGwUi4A==", + "version": "17.0.20", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.20.tgz", + "integrity": "sha512-wWZrPlihslrPpcKyCSlmIlruakxr57/buQN1RjlIeaaTWDLtJkTtRW429MoQJergvVKc4IWBpRhWw7YNh/7GVA==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -21440,9 +21440,9 @@ "version": "1.5.4" }, "@types/react": { - "version": "17.0.19", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.19.tgz", - "integrity": "sha512-sX1HisdB1/ZESixMTGnMxH9TDe8Sk709734fEQZzCV/4lSu9kJCPbo2PbTRoZM+53Pp0P10hYVyReUueGwUi4A==", + "version": "17.0.20", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.20.tgz", + "integrity": "sha512-wWZrPlihslrPpcKyCSlmIlruakxr57/buQN1RjlIeaaTWDLtJkTtRW429MoQJergvVKc4IWBpRhWw7YNh/7GVA==", "requires": { "@types/prop-types": "*", "@types/scheduler": "*", diff --git a/package.json b/package.json index fb6e41e..c7e9870 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "@testing-library/user-event": "^12.1.10", "@types/jest": "^26.0.24", "@types/node": "^14.17.5", - "@types/react": "^17.0.19", + "@types/react": "^17.0.20", "@types/react-blockies": "^1.4.1", "@types/react-dom": "^17.0.9", "@types/react-router-dom": "^5.1.8", From a1f79ff0c0c05aa316b140778835ba308ffcc0b4 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 6 Sep 2021 18:32:11 -0300 Subject: [PATCH 21/33] Toggle ipfs on/off --- src/AddressTransactions.tsx | 6 +++++- src/address/Contract.tsx | 10 +++++++++- src/address/Contracts.tsx | 22 +++++++++++++++++++++- src/url.ts | 16 +++++++++++----- src/useSourcify.ts | 16 ++++++++++------ 5 files changed, 56 insertions(+), 14 deletions(-) diff --git a/src/AddressTransactions.tsx b/src/AddressTransactions.tsx index 659c1eb..371ff92 100644 --- a/src/AddressTransactions.tsx +++ b/src/AddressTransactions.tsx @@ -181,9 +181,11 @@ const AddressTransactions: React.FC = () => { const [feeDisplay, feeDisplayToggler] = useFeeToggler(); const selectionCtx = useSelection(); + const [useIPFS, setUseIPFS] = useState(true); const rawMetadata = useSourcify( checksummedAddress, - provider?.network.chainId + provider?.network.chainId, + useIPFS ); return ( @@ -291,6 +293,8 @@ const AddressTransactions: React.FC = () => { diff --git a/src/address/Contract.tsx b/src/address/Contract.tsx index 1a30583..0fd7c89 100644 --- a/src/address/Contract.tsx +++ b/src/address/Contract.tsx @@ -12,6 +12,7 @@ type ContractProps = { networkId: number; filename: string; source: any; + useIPFS: boolean; }; const Contract: React.FC = ({ @@ -19,8 +20,15 @@ const Contract: React.FC = ({ networkId, filename, source, + useIPFS, }) => { - const content = useContract(checksummedAddress, networkId, filename, source); + const content = useContract( + checksummedAddress, + networkId, + filename, + source, + useIPFS + ); return ( void; }; const Contracts: React.FC = ({ checksummedAddress, rawMetadata, + useIPFS, + setUseIPFS, }) => { const { provider } = useContext(RuntimeContext); @@ -30,6 +34,21 @@ const Contracts: React.FC = ({ return ( + + + + + {rawMetadata && ( <> @@ -95,6 +114,7 @@ const Contracts: React.FC = ({ networkId={provider!.network.chainId} filename={selected} source={rawMetadata.sources[selected]} + useIPFS={useIPFS} /> )}
diff --git a/src/url.ts b/src/url.ts index 8aaa6f0..e2a191d 100644 --- a/src/url.ts +++ b/src/url.ts @@ -17,20 +17,26 @@ export const blockTxsURL = (blockNum: BlockTag) => `/block/${blockNum}/txs`; const sourcifyRootHash = "k51qzi5uqu5dll0ocge71eudqnrgnogmbr37gsgl12uubsinphjoknl6bbi41p"; const ipfsGatewayPrefix = `https://ipfs.io/ipns/${sourcifyRootHash}`; -// const ipfsGatewayPrefix = `https://repo.sourcify.dev`; +const sourcifyHttpRepoPrefix = `https://repo.sourcify.dev`; export const sourcifyMetadata = ( checksummedAddress: string, - networkId: number + networkId: number, + useIPFS: boolean ) => - `${ipfsGatewayPrefix}/contracts/full_match/${networkId}/${checksummedAddress}/metadata.json`; + `${ + useIPFS ? ipfsGatewayPrefix : sourcifyHttpRepoPrefix + }/contracts/full_match/${networkId}/${checksummedAddress}/metadata.json`; export const sourcifySourceFile = ( checksummedAddress: string, networkId: number, - filepath: string + filepath: string, + useIPFS: boolean ) => - `${ipfsGatewayPrefix}/contracts/full_match/${networkId}/${checksummedAddress}/sources/${filepath}`; + `${ + useIPFS ? ipfsGatewayPrefix : sourcifyHttpRepoPrefix + }/contracts/full_match/${networkId}/${checksummedAddress}/sources/${filepath}`; export const openInRemixURL = (checksummedAddress: string, networkId: number) => `https://remix.ethereum.org/#call=source-verification//fetchAndSave//${checksummedAddress}//${networkId}`; diff --git a/src/useSourcify.ts b/src/useSourcify.ts index 5a8d0fd..40c0bea 100644 --- a/src/useSourcify.ts +++ b/src/useSourcify.ts @@ -38,7 +38,8 @@ export type Metadata = { export const useSourcify = ( checksummedAddress: string | undefined, - chainId: number | undefined + chainId: number | undefined, + useIPFS: boolean ) => { const [rawMetadata, setRawMetadata] = useState(); @@ -51,7 +52,8 @@ export const useSourcify = ( try { const contractMetadataURL = sourcifyMetadata( checksummedAddress, - chainId + chainId, + useIPFS ); const result = await fetch(contractMetadataURL); if (result.ok) { @@ -66,7 +68,7 @@ export const useSourcify = ( } }; fetchMetadata(); - }, [checksummedAddress, chainId]); + }, [checksummedAddress, chainId, useIPFS]); return rawMetadata; }; @@ -75,7 +77,8 @@ export const useContract = ( checksummedAddress: string, networkId: number, filename: string, - source: any + source: any, + useIPFS: boolean = true ) => { const [content, setContent] = useState(source.content); @@ -89,7 +92,8 @@ export const useContract = ( const url = sourcifySourceFile( checksummedAddress, networkId, - normalizedFilename + normalizedFilename, + useIPFS ); const res = await fetch(url); if (res.ok) { @@ -98,7 +102,7 @@ export const useContract = ( } }; readContent(); - }, [checksummedAddress, networkId, filename, source.content]); + }, [checksummedAddress, networkId, filename, source.content, useIPFS]); return content; }; From 7b0137c7fe151abe8dcff6b72ef3f9d4390c01b8 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 7 Sep 2021 00:44:28 -0300 Subject: [PATCH 22/33] Add replacement rule --- src/useSourcify.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/useSourcify.ts b/src/useSourcify.ts index 40c0bea..ac0cbde 100644 --- a/src/useSourcify.ts +++ b/src/useSourcify.ts @@ -88,7 +88,7 @@ export const useContract = ( } const readContent = async () => { - const normalizedFilename = filename.replaceAll("@", "_"); + const normalizedFilename = filename.replaceAll(/[@:]/g, "_"); const url = sourcifySourceFile( checksummedAddress, networkId, From dd490ee905f6fb6494cea5c64392ff95b9119a63 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 7 Sep 2021 01:10:19 -0300 Subject: [PATCH 23/33] Style source selector menu --- src/address/Contracts.tsx | 72 +++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/src/address/Contracts.tsx b/src/address/Contracts.tsx index f22756b..8e73a23 100644 --- a/src/address/Contracts.tsx +++ b/src/address/Contracts.tsx @@ -1,6 +1,8 @@ import React, { useState, useEffect, useContext, Fragment } from "react"; import { commify } from "@ethersproject/units"; -import { Switch, Tab } from "@headlessui/react"; +import { Menu, Switch } from "@headlessui/react"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown"; import ContentFrame from "../ContentFrame"; import InfoRow from "../components/InfoRow"; import Contract from "./Contract"; @@ -78,36 +80,46 @@ const Contracts: React.FC = ({ )} {rawMetadata !== undefined && rawMetadata !== null && (
- {provider && ( -
- - Open in Remix - -
- )} - - - {Object.entries(rawMetadata.sources).map(([k]) => ( - - - - ))} - - + Open in Remix + +
+ )} +
+
+ + {Object.entries(rawMetadata.sources).map(([k]) => ( + + + + ))} + +
+ {selected && ( Date: Fri, 10 Sep 2021 04:31:24 -0300 Subject: [PATCH 24/33] Improve sourcify sync indicators --- src/AddressTransactions.tsx | 33 +++++++++++++++++++++++++-------- src/address/Contracts.tsx | 5 ++++- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/AddressTransactions.tsx b/src/AddressTransactions.tsx index 371ff92..041f339 100644 --- a/src/AddressTransactions.tsx +++ b/src/AddressTransactions.tsx @@ -1,10 +1,4 @@ -import React, { - useState, - useEffect, - useMemo, - useContext, - Fragment, -} from "react"; +import React, { useState, useEffect, useMemo, useContext } from "react"; import { useParams, useLocation, @@ -17,6 +11,10 @@ import { getAddress, isAddress } from "@ethersproject/address"; import { Tab } from "@headlessui/react"; import queryString from "query-string"; import Blockies from "react-blockies"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faCircleNotch } from "@fortawesome/free-solid-svg-icons/faCircleNotch"; +import { faCheckCircle } from "@fortawesome/free-regular-svg-icons/faCheckCircle"; +import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons/faQuestionCircle"; import StandardFrame from "./StandardFrame"; import StandardSubtitle from "./StandardSubtitle"; import Copy from "./components/Copy"; @@ -222,7 +220,26 @@ const AddressTransactions: React.FC = () => { Overview - Contract + + Contract{" "} + {rawMetadata === undefined ? ( + + + + ) : rawMetadata === null ? ( + + + + ) : ( + + + + )} + diff --git a/src/address/Contracts.tsx b/src/address/Contracts.tsx index 8e73a23..470da58 100644 --- a/src/address/Contracts.tsx +++ b/src/address/Contracts.tsx @@ -76,7 +76,10 @@ const Contracts: React.FC = ({ )}
{rawMetadata === null && ( - Couldn't find contract metadata in Sourcify repository. + + Address is not a contract or couldn't find contract metadata in + Sourcify repository. + )} {rawMetadata !== undefined && rawMetadata !== null && (
From 274705ce700df3465b46df45a184ed63512230c7 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Fri, 10 Sep 2021 05:16:06 -0300 Subject: [PATCH 25/33] Fix animation --- src/AddressTransactions.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/AddressTransactions.tsx b/src/AddressTransactions.tsx index 041f339..47c9653 100644 --- a/src/AddressTransactions.tsx +++ b/src/AddressTransactions.tsx @@ -221,21 +221,24 @@ const AddressTransactions: React.FC = () => { - Contract{" "} + Contract {rawMetadata === undefined ? ( - - + + ) : rawMetadata === null ? ( - + ) : ( - + )} From 05c230ef9d38fbad2c6e2e60e6f7c2864950b070 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Fri, 10 Sep 2021 05:17:10 -0300 Subject: [PATCH 26/33] Add abi section --- src/address/ABI.tsx | 24 +++++++++ src/address/Contract.tsx | 2 +- src/address/Contracts.tsx | 109 +++++++++++++++++++++----------------- 3 files changed, 85 insertions(+), 50 deletions(-) create mode 100644 src/address/ABI.tsx diff --git a/src/address/ABI.tsx b/src/address/ABI.tsx new file mode 100644 index 0000000..7190f4e --- /dev/null +++ b/src/address/ABI.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import { Light as SyntaxHighlighter } from "react-syntax-highlighter"; +import hljs from "highlight.js"; +import docco from "react-syntax-highlighter/dist/esm/styles/hljs/docco"; + +import hljsDefineSolidity from "highlightjs-solidity"; +hljsDefineSolidity(hljs); + +type ABIProps = { + abi: any[]; +}; + +const ABI: React.FC = ({ abi }) => ( + + {JSON.stringify(abi, null, " ") ?? ""} + +); + +export default React.memo(ABI); diff --git a/src/address/Contract.tsx b/src/address/Contract.tsx index 0fd7c89..8a18d86 100644 --- a/src/address/Contract.tsx +++ b/src/address/Contract.tsx @@ -2,9 +2,9 @@ import React from "react"; import { Light as SyntaxHighlighter } from "react-syntax-highlighter"; import hljs from "highlight.js"; import docco from "react-syntax-highlighter/dist/esm/styles/hljs/docco"; +import { useContract } from "../useSourcify"; import hljsDefineSolidity from "highlightjs-solidity"; -import { useContract } from "../useSourcify"; hljsDefineSolidity(hljs); type ContractProps = { diff --git a/src/address/Contracts.tsx b/src/address/Contracts.tsx index 470da58..94f25c4 100644 --- a/src/address/Contracts.tsx +++ b/src/address/Contracts.tsx @@ -5,6 +5,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown"; import ContentFrame from "../ContentFrame"; import InfoRow from "../components/InfoRow"; +import ABI from "./ABI"; import Contract from "./Contract"; import { RuntimeContext } from "../useRuntime"; import { Metadata } from "../useSourcify"; @@ -82,57 +83,67 @@ const Contracts: React.FC = ({ )} {rawMetadata !== undefined && rawMetadata !== null && ( -
- -
- - {selected} - - - - - {provider && ( -
- - Open in Remix - -
- )} + <> + {rawMetadata.output.abi && ( +
+
+ ABI +
+
-
- - {Object.entries(rawMetadata.sources).map(([k]) => ( - - - - ))} - -
-
- {selected && ( - )} -
+
+ +
+ + {selected} + + + + + {provider && ( +
+ + Open in Remix + +
+ )} +
+
+ + {Object.entries(rawMetadata.sources).map(([k]) => ( + + + + ))} + +
+
+ {selected && ( + + )} +
+ )}
From 21ed9ce43123383020698f5f610116b47c5d916b Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Fri, 10 Sep 2021 18:27:42 -0300 Subject: [PATCH 27/33] Add third way to integrate with Sourcify --- src/AddressTransactions.tsx | 11 +++++---- src/address/Contract.tsx | 7 +++--- src/address/Contracts.tsx | 46 ++++++++++++++++++++----------------- src/address/RadioButton.tsx | 24 +++++++++++++++++++ src/url.ts | 43 ++++++++++++++++++++++++++-------- src/useSourcify.ts | 15 ++++++------ 6 files changed, 101 insertions(+), 45 deletions(-) create mode 100644 src/address/RadioButton.tsx diff --git a/src/AddressTransactions.tsx b/src/AddressTransactions.tsx index 47c9653..45a07ca 100644 --- a/src/AddressTransactions.tsx +++ b/src/AddressTransactions.tsx @@ -32,6 +32,7 @@ import { useFeeToggler } from "./search/useFeeToggler"; import { SelectionContext, useSelection } from "./useSelection"; import { useMultipleETHUSDOracle } from "./usePriceOracle"; import { useSourcify } from "./useSourcify"; +import { SourcifySource } from "./url"; type BlockParams = { addressOrName: string; @@ -179,11 +180,13 @@ const AddressTransactions: React.FC = () => { const [feeDisplay, feeDisplayToggler] = useFeeToggler(); const selectionCtx = useSelection(); - const [useIPFS, setUseIPFS] = useState(true); + const [sourcifySource, setSourcifySource] = useState( + SourcifySource.IPFS_IPNS + ); const rawMetadata = useSourcify( checksummedAddress, provider?.network.chainId, - useIPFS + sourcifySource ); return ( @@ -313,8 +316,8 @@ const AddressTransactions: React.FC = () => { diff --git a/src/address/Contract.tsx b/src/address/Contract.tsx index 8a18d86..eece5fd 100644 --- a/src/address/Contract.tsx +++ b/src/address/Contract.tsx @@ -3,6 +3,7 @@ import { Light as SyntaxHighlighter } from "react-syntax-highlighter"; import hljs from "highlight.js"; import docco from "react-syntax-highlighter/dist/esm/styles/hljs/docco"; import { useContract } from "../useSourcify"; +import { SourcifySource } from "../url"; import hljsDefineSolidity from "highlightjs-solidity"; hljsDefineSolidity(hljs); @@ -12,7 +13,7 @@ type ContractProps = { networkId: number; filename: string; source: any; - useIPFS: boolean; + sourcifySource: SourcifySource; }; const Contract: React.FC = ({ @@ -20,14 +21,14 @@ const Contract: React.FC = ({ networkId, filename, source, - useIPFS, + sourcifySource, }) => { const content = useContract( checksummedAddress, networkId, filename, source, - useIPFS + sourcifySource ); return ( diff --git a/src/address/Contracts.tsx b/src/address/Contracts.tsx index 94f25c4..fb2f3a7 100644 --- a/src/address/Contracts.tsx +++ b/src/address/Contracts.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect, useContext, Fragment } from "react"; import { commify } from "@ethersproject/units"; -import { Menu, Switch } from "@headlessui/react"; +import { Menu, RadioGroup } from "@headlessui/react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown"; import ContentFrame from "../ContentFrame"; @@ -10,20 +10,21 @@ import Contract from "./Contract"; import { RuntimeContext } from "../useRuntime"; import { Metadata } from "../useSourcify"; import ExternalLink from "../components/ExternalLink"; -import { openInRemixURL } from "../url"; +import { openInRemixURL, SourcifySource } from "../url"; +import RadioButton from "./RadioButton"; type ContractsProps = { checksummedAddress: string; rawMetadata: Metadata | null | undefined; - useIPFS: boolean; - setUseIPFS: (useIPFS: boolean) => void; + sourcifySource: SourcifySource; + setSourcifySource: (sourcifySource: SourcifySource) => void; }; const Contracts: React.FC = ({ checksummedAddress, rawMetadata, - useIPFS, - setUseIPFS, + sourcifySource, + setSourcifySource, }) => { const { provider } = useContext(RuntimeContext); @@ -37,20 +38,20 @@ const Contracts: React.FC = ({ return ( - - - - + + +
+ + Resolve IPNS + + + Sourcify Servers + + + Local Snapshot + +
+
{rawMetadata && ( <> @@ -76,6 +77,9 @@ const Contracts: React.FC = ({ )}
+ {rawMetadata === undefined && ( + Getting data from Sourcify repository... + )} {rawMetadata === null && ( Address is not a contract or couldn't find contract metadata in @@ -139,7 +143,7 @@ const Contracts: React.FC = ({ networkId={provider!.network.chainId} filename={selected} source={rawMetadata.sources[selected]} - useIPFS={useIPFS} + sourcifySource={sourcifySource} /> )}
diff --git a/src/address/RadioButton.tsx b/src/address/RadioButton.tsx new file mode 100644 index 0000000..e93fad3 --- /dev/null +++ b/src/address/RadioButton.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import { RadioGroup } from "@headlessui/react"; +import { SourcifySource } from "../url"; + +type RadioButtonProps = { + value: SourcifySource; +}; + +const RadioButton: React.FC = ({ value, children }) => ( + + `border rounded px-2 py-1 cursor-pointer ${ + checked + ? "bg-blue-400 hover:bg-blue-500 text-white" + : "hover:bg-gray-200" + }` + } + value={value} + > + {children} + +); + +export default RadioButton; diff --git a/src/url.ts b/src/url.ts index e2a191d..ffbca9b 100644 --- a/src/url.ts +++ b/src/url.ts @@ -14,29 +14,52 @@ export const blockURL = (blockNum: BlockTag) => `/block/${blockNum}`; export const blockTxsURL = (blockNum: BlockTag) => `/block/${blockNum}/txs`; -const sourcifyRootHash = +export enum SourcifySource { + // Resolve trusted IPNS for root IPFS + IPFS_IPNS, + + // Centralized Sourcify servers + CENTRAL_SERVER, + + // Snapshot server + CUSTOM_SNAPSHOT_SERVER, +} + +const sourcifyIPNS = "k51qzi5uqu5dll0ocge71eudqnrgnogmbr37gsgl12uubsinphjoknl6bbi41p"; -const ipfsGatewayPrefix = `https://ipfs.io/ipns/${sourcifyRootHash}`; +const ipfsGatewayPrefix = `https://ipfs.io/ipns/${sourcifyIPNS}`; +// const ipfsGatewayPrefix = "http://localhost:8080/ipfs/QmWQoGfrLcizHueg3YkgDCh1S7SkfSP9A7H8YeZmUDfbnn" const sourcifyHttpRepoPrefix = `https://repo.sourcify.dev`; +const snapshotPrefix = "http://localhost:3006"; + +const resolveSourcifySource = (source: SourcifySource) => { + if (source === SourcifySource.IPFS_IPNS) { + return ipfsGatewayPrefix; + } + if (source === SourcifySource.CENTRAL_SERVER) { + return sourcifyHttpRepoPrefix; + } + return snapshotPrefix; +}; export const sourcifyMetadata = ( checksummedAddress: string, networkId: number, - useIPFS: boolean + source: SourcifySource ) => - `${ - useIPFS ? ipfsGatewayPrefix : sourcifyHttpRepoPrefix - }/contracts/full_match/${networkId}/${checksummedAddress}/metadata.json`; + `${resolveSourcifySource( + source + )}/contracts/full_match/${networkId}/${checksummedAddress}/metadata.json`; export const sourcifySourceFile = ( checksummedAddress: string, networkId: number, filepath: string, - useIPFS: boolean + source: SourcifySource ) => - `${ - useIPFS ? ipfsGatewayPrefix : sourcifyHttpRepoPrefix - }/contracts/full_match/${networkId}/${checksummedAddress}/sources/${filepath}`; + `${resolveSourcifySource( + source + )}/contracts/full_match/${networkId}/${checksummedAddress}/sources/${filepath}`; export const openInRemixURL = (checksummedAddress: string, networkId: number) => `https://remix.ethereum.org/#call=source-verification//fetchAndSave//${checksummedAddress}//${networkId}`; diff --git a/src/useSourcify.ts b/src/useSourcify.ts index ac0cbde..e8806b9 100644 --- a/src/useSourcify.ts +++ b/src/useSourcify.ts @@ -1,5 +1,5 @@ import { useState, useEffect } from "react"; -import { sourcifyMetadata, sourcifySourceFile } from "./url"; +import { sourcifyMetadata, SourcifySource, sourcifySourceFile } from "./url"; export type Metadata = { version: string; @@ -39,7 +39,7 @@ export type Metadata = { export const useSourcify = ( checksummedAddress: string | undefined, chainId: number | undefined, - useIPFS: boolean + source: SourcifySource ) => { const [rawMetadata, setRawMetadata] = useState(); @@ -48,12 +48,13 @@ export const useSourcify = ( return; } + setRawMetadata(undefined); const fetchMetadata = async () => { try { const contractMetadataURL = sourcifyMetadata( checksummedAddress, chainId, - useIPFS + source ); const result = await fetch(contractMetadataURL); if (result.ok) { @@ -68,7 +69,7 @@ export const useSourcify = ( } }; fetchMetadata(); - }, [checksummedAddress, chainId, useIPFS]); + }, [checksummedAddress, chainId, source]); return rawMetadata; }; @@ -78,7 +79,7 @@ export const useContract = ( networkId: number, filename: string, source: any, - useIPFS: boolean = true + sourcifySource: SourcifySource ) => { const [content, setContent] = useState(source.content); @@ -93,7 +94,7 @@ export const useContract = ( checksummedAddress, networkId, normalizedFilename, - useIPFS + sourcifySource ); const res = await fetch(url); if (res.ok) { @@ -102,7 +103,7 @@ export const useContract = ( } }; readContent(); - }, [checksummedAddress, networkId, filename, source.content, useIPFS]); + }, [checksummedAddress, networkId, filename, source.content, sourcifySource]); return content; }; From 599efc6300d017eadbab1134a8ff6080eb0ba195 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Fri, 10 Sep 2021 18:38:15 -0300 Subject: [PATCH 28/33] Add gitcoin page --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bbfc214..2f9a5f9 100644 --- a/README.md +++ b/README.md @@ -180,4 +180,4 @@ Follow the creator on Twitter for updates ([@wmitsuda](https://twitter.com/wmits ### Donation address -If you like this project, feel free to send donations to `otterscan.eth` +If you like this project, feel free to send donations to `otterscan.eth` or use our gitcoin grant page: https://gitcoin.co/grants/3224/otterscan From ea86619e916e1f6aa27d95f7f3a1737495f5a590 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Fri, 10 Sep 2021 18:55:54 -0300 Subject: [PATCH 29/33] Add sourcify doc instructions --- README.md | 12 +++++++++++- docs/sourcify.md | 23 +++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 docs/sourcify.md diff --git a/README.md b/README.md index 2f9a5f9..d1aef5b 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,15 @@ This is the preferred way to run Otterscan. You can read about other ways [here] You can make sure it is working correctly if the homepage is able to show the latest block/timestamp your Erigon node is at just bellow the search button. -## Kudos +## Source verification + +We make use of [Sourcify](https://sourcify.dev/) for displaying contract verification info. + +More info [here](docs/sourcify.md). + +## Kudos (in no particular order) + +We make use of many open-source software and integrate many public datasources, mainly: To the [Geth](https://geth.ethereum.org/) team whose code Erigon is based on. @@ -150,6 +158,8 @@ To [Trust Wallet](https://github.com/trustwallet/assets) who sponsor and make av To the owners of the [4bytes repository](https://github.com/ethereum-lists/4bytes) that we import and use to translate the method selectors to human-friendly strings. +To [Sourcify](https://sourcify.dev/), a public decentralized source code and metadata verification service. + To [Ethers](https://github.com/ethers-io/ethers.js/) which is the client library we used to interact with the ETH node. It is high level enough to hide most jsonrpc particularities, but flexible enough to allow easy interaction with custom jsonrpc methods. ## Future diff --git a/docs/sourcify.md b/docs/sourcify.md new file mode 100644 index 0000000..f966517 --- /dev/null +++ b/docs/sourcify.md @@ -0,0 +1,23 @@ +# Sourcify + +We get the contract source code and metadata from [Sourcify](https://sourcify.dev/). + +There are multiple ways to consume their data we support, each one with pros and cons: + +## IPNS/IPFS + +This is the default integration method, we resolve the public Sourcify IPNS to get the latest known IPFS root hash of their repository. + +The downside is that recently verified contracts may not have yet been added to the root hash and republished into IPNS. + +## Direct HTTP connection to Sourcify's repository + +Standard HTTP connection to their repo at https://repo.sourcify.dev/ + +Fast access to fresh verified data. On the other hand it is less private and centralized. + +## Local snapshot + +As a midterm solution, we are making available a snapshot docker image of their repository, containing only mainnet full verified contracts. + +This would allow you to play with existing contracts up to the snapshot date/time locally, not depending on their service or IPFS connectivity availability. From 303458e431a5e2692c23da7d5697368c9f5d1fb8 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sat, 11 Sep 2021 19:30:19 -0300 Subject: [PATCH 30/33] Bugfix: workaround for https://github.com/ethers-io/ethers.js/issues/2036 --- src/Block.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Block.tsx b/src/Block.tsx index caa309b..7f9e552 100644 --- a/src/Block.tsx +++ b/src/Block.tsx @@ -165,7 +165,9 @@ const Block: React.FC = () => { - {commify(block.difficulty)} + + {block.difficulty ? commify(block.difficulty) : "?"} + {commify(block.totalDifficulty.toString())} From daae37a6ab684f74ef65eba34e95da8affd644e5 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sun, 12 Sep 2021 21:30:42 -0300 Subject: [PATCH 31/33] Add copy operation to abi --- src/address/Contracts.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/address/Contracts.tsx b/src/address/Contracts.tsx index fb2f3a7..07bf386 100644 --- a/src/address/Contracts.tsx +++ b/src/address/Contracts.tsx @@ -5,6 +5,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown"; import ContentFrame from "../ContentFrame"; import InfoRow from "../components/InfoRow"; +import Copy from "../components/Copy"; import ABI from "./ABI"; import Contract from "./Contract"; import { RuntimeContext } from "../useRuntime"; @@ -91,7 +92,8 @@ const Contracts: React.FC = ({ {rawMetadata.output.abi && (
- ABI + ABI +
From 22fd8ad5c25135d25d346cbaa3e3fc6000446869 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sun, 12 Sep 2021 22:03:56 -0300 Subject: [PATCH 32/33] Use localhost gateway by default to avoid leaking queries by accident --- src/address/Contracts.tsx | 4 ++-- src/url.ts | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/address/Contracts.tsx b/src/address/Contracts.tsx index 07bf386..4d322c3 100644 --- a/src/address/Contracts.tsx +++ b/src/address/Contracts.tsx @@ -43,13 +43,13 @@ const Contracts: React.FC = ({
- Resolve IPNS + Resolve IPNS @localhost:8080 gateway Sourcify Servers - Local Snapshot + Local Snapshot @localhost:3006
diff --git a/src/url.ts b/src/url.ts index ffbca9b..9b80e34 100644 --- a/src/url.ts +++ b/src/url.ts @@ -27,8 +27,7 @@ export enum SourcifySource { const sourcifyIPNS = "k51qzi5uqu5dll0ocge71eudqnrgnogmbr37gsgl12uubsinphjoknl6bbi41p"; -const ipfsGatewayPrefix = `https://ipfs.io/ipns/${sourcifyIPNS}`; -// const ipfsGatewayPrefix = "http://localhost:8080/ipfs/QmWQoGfrLcizHueg3YkgDCh1S7SkfSP9A7H8YeZmUDfbnn" +const ipfsGatewayPrefix = `http://localhost:8080/ipns/${sourcifyIPNS}`; const sourcifyHttpRepoPrefix = `https://repo.sourcify.dev`; const snapshotPrefix = "http://localhost:3006"; From 45920cb55f2b9f55dec3a98f0fac8f114b521173 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sun, 12 Sep 2021 22:12:52 -0300 Subject: [PATCH 33/33] Updated instructions --- docs/sourcify.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/sourcify.md b/docs/sourcify.md index f966517..cede6d3 100644 --- a/docs/sourcify.md +++ b/docs/sourcify.md @@ -10,6 +10,10 @@ This is the default integration method, we resolve the public Sourcify IPNS to g The downside is that recently verified contracts may not have yet been added to the root hash and republished into IPNS. +It assumes a local IPFS gateway at localhost:8080 to avoid leaking your queries to public gateways. + +> This option is actually not working, but it is provided for completeness, follow https://github.com/ethereum/sourcify/issues/495 + ## Direct HTTP connection to Sourcify's repository Standard HTTP connection to their repo at https://repo.sourcify.dev/ @@ -21,3 +25,19 @@ Fast access to fresh verified data. On the other hand it is less private and cen As a midterm solution, we are making available a snapshot docker image of their repository, containing only mainnet full verified contracts. This would allow you to play with existing contracts up to the snapshot date/time locally, not depending on their service or IPFS connectivity availability. + +> It is very likely this run mode will be deprecated in future. + +The Sourcify snapshot is provided as a nginx image at: https://hub.docker.com/repository/docker/otterscan/sourcify-snapshot + +You can run it with: + +``` +docker run --rm -d -p 3006:80 --name sourcify-snapshot otterscan/sourcify-snapshot:2021-09 +``` + +Stop it with: + +``` +docker stop sourcify-snapshot +```