diff --git a/package-lock.json b/package-lock.json index 465caa4..ac44d74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,6 @@ "@types/react": "^17.0.31", "@types/react-blockies": "^1.4.1", "@types/react-dom": "^17.0.10", - "@types/react-helmet": "^6.1.4", "@types/react-highlight": "^0.12.5", "@types/react-router-dom": "^5.3.1", "@types/react-syntax-highlighter": "^13.5.2", @@ -43,7 +42,7 @@ "react-chartjs-2": "^3.2.0", "react-dom": "^17.0.2", "react-error-boundary": "^3.1.3", - "react-helmet": "^6.1.0", + "react-helmet-async": "^1.1.2", "react-image": "^4.0.3", "react-router-dom": "^5.3.0", "react-scripts": "4.0.3", @@ -3119,14 +3118,6 @@ "@types/react": "*" } }, - "node_modules/@types/react-helmet": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.4.tgz", - "integrity": "sha512-jyx50RNZXVaTGHY3MsoRPNpeiVk8b0XTPgD/O6KHF6COTDnG/+lRjPYvTK5nfWtR3xDOux0w6bHLAsaHo2ZLTA==", - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/react-highlight": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/@types/react-highlight/-/react-highlight-0.12.5.tgz", @@ -9839,6 +9830,14 @@ "node": ">= 0.4" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/ip": { "version": "1.1.5", "license": "MIT" @@ -14588,18 +14587,20 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" }, - "node_modules/react-helmet": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", - "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", + "node_modules/react-helmet-async": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.1.2.tgz", + "integrity": "sha512-LTTzDDkyIleT/JJ6T/uqx7Y8qi1EuPPSiJawQY/nHHz0h7SPDT6HxP1YDDQx/fzcVxCqpWEEMS3QdrSrNkJYhg==", "dependencies": { - "object-assign": "^4.1.1", + "@babel/runtime": "^7.12.5", + "invariant": "^2.2.4", "prop-types": "^15.7.2", - "react-fast-compare": "^3.1.1", - "react-side-effect": "^2.1.0" + "react-fast-compare": "^3.2.0", + "shallowequal": "^1.1.0" }, "peerDependencies": { - "react": ">=16.3.0" + "react": "^16.6.0 || ^17.0.0", + "react-dom": "^16.6.0 || ^17.0.0" } }, "node_modules/react-image": { @@ -14949,14 +14950,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-side-effect": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.1.tgz", - "integrity": "sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==", - "peerDependencies": { - "react": "^16.3.0 || ^17.0.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", @@ -16314,6 +16307,11 @@ "sha.js": "bin.js" } }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "license": "MIT", @@ -21703,14 +21701,6 @@ "@types/react": "*" } }, - "@types/react-helmet": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.4.tgz", - "integrity": "sha512-jyx50RNZXVaTGHY3MsoRPNpeiVk8b0XTPgD/O6KHF6COTDnG/+lRjPYvTK5nfWtR3xDOux0w6bHLAsaHo2ZLTA==", - "requires": { - "@types/react": "*" - } - }, "@types/react-highlight": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/@types/react-highlight/-/react-highlight-0.12.5.tgz", @@ -26271,6 +26261,14 @@ "side-channel": "^1.0.4" } }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, "ip": { "version": "1.1.5" }, @@ -29420,15 +29418,16 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" }, - "react-helmet": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", - "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", + "react-helmet-async": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.1.2.tgz", + "integrity": "sha512-LTTzDDkyIleT/JJ6T/uqx7Y8qi1EuPPSiJawQY/nHHz0h7SPDT6HxP1YDDQx/fzcVxCqpWEEMS3QdrSrNkJYhg==", "requires": { - "object-assign": "^4.1.1", + "@babel/runtime": "^7.12.5", + "invariant": "^2.2.4", "prop-types": "^15.7.2", - "react-fast-compare": "^3.1.1", - "react-side-effect": "^2.1.0" + "react-fast-compare": "^3.2.0", + "shallowequal": "^1.1.0" } }, "react-image": { @@ -29675,12 +29674,6 @@ } } }, - "react-side-effect": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.1.tgz", - "integrity": "sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==", - "requires": {} - }, "react-syntax-highlighter": { "version": "15.4.4", "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.4.4.tgz", @@ -30615,6 +30608,11 @@ "safe-buffer": "^5.0.1" } }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "shebang-command": { "version": "2.0.0", "requires": { diff --git a/package.json b/package.json index 0369b84..79b4887 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,6 @@ "@types/react": "^17.0.31", "@types/react-blockies": "^1.4.1", "@types/react-dom": "^17.0.10", - "@types/react-helmet": "^6.1.4", "@types/react-highlight": "^0.12.5", "@types/react-router-dom": "^5.3.1", "@types/react-syntax-highlighter": "^13.5.2", @@ -38,7 +37,7 @@ "react-chartjs-2": "^3.2.0", "react-dom": "^17.0.2", "react-error-boundary": "^3.1.3", - "react-helmet": "^6.1.0", + "react-helmet-async": "^1.1.2", "react-image": "^4.0.3", "react-router-dom": "^5.3.0", "react-scripts": "4.0.3", diff --git a/src/address/ContractABI.tsx b/src/address/ContractABI.tsx new file mode 100644 index 0000000..edd713c --- /dev/null +++ b/src/address/ContractABI.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { Tab } from "@headlessui/react"; +import ModeTab from "../components/ModeTab"; +import Copy from "../components/Copy"; +import DecodedABI from "./DecodedABI"; +import RawABI from "./RawABI"; + +type ContractABIProps = { + abi: any[]; +}; + +const ContractABI: React.FC = ({ abi }) => ( +
+ + +
+ ABI + +
+ Decoded + Raw +
+ + + + + + + + +
+
+); + +export default React.memo(ContractABI); diff --git a/src/address/Contracts.tsx b/src/address/Contracts.tsx index 0745d73..dbaaf83 100644 --- a/src/address/Contracts.tsx +++ b/src/address/Contracts.tsx @@ -5,13 +5,12 @@ 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"; import { Metadata } from "../useSourcify"; import ExternalLink from "../components/ExternalLink"; import { openInRemixURL } from "../url"; +import ContractABI from "./ContractABI"; type ContractsProps = { checksummedAddress: string; @@ -70,13 +69,7 @@ const Contracts: React.FC = ({ {rawMetadata !== undefined && rawMetadata !== null && ( <> {rawMetadata.output.abi && ( -
-
- ABI - -
- -
+ )}
diff --git a/src/address/DecodedABI.tsx b/src/address/DecodedABI.tsx new file mode 100644 index 0000000..83faabc --- /dev/null +++ b/src/address/DecodedABI.tsx @@ -0,0 +1,20 @@ +import { Interface } from "@ethersproject/abi"; +import React from "react"; +import DecodedFragment from "./DecodedFragment"; + +type DecodedABIProps = { + abi: any[]; +}; + +const DecodedABI: React.FC = ({ abi }) => { + const intf = new Interface(abi); + return ( +
+ {intf.fragments.map((f, i) => ( + + ))} +
+ ); +}; + +export default React.memo(DecodedABI); diff --git a/src/address/DecodedFragment.tsx b/src/address/DecodedFragment.tsx new file mode 100644 index 0000000..ac06a16 --- /dev/null +++ b/src/address/DecodedFragment.tsx @@ -0,0 +1,78 @@ +import React from "react"; +import { + ConstructorFragment, + EventFragment, + Fragment, + FunctionFragment, + Interface, +} from "@ethersproject/abi"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faCaretRight } from "@fortawesome/free-solid-svg-icons/faCaretRight"; + +type DecodedFragmentProps = { + intf: Interface; + fragment: Fragment; +}; + +const DecodedFragment: React.FC = ({ + intf, + fragment, +}) => { + let fragmentType: "constructor" | "event" | "function" | undefined; + let sig: string | undefined; + let letter: string | undefined; + let letterBg: string | undefined; + let hashBg: string | undefined; + + if (FunctionFragment.isFunctionFragment(fragment)) { + fragmentType = "function"; + sig = intf.getSighash(fragment); + letter = "F"; + letterBg = "bg-purple-500"; + hashBg = "bg-purple-50"; + } else if (EventFragment.isEventFragment(fragment)) { + fragmentType = "event"; + sig = intf.getEventTopic(fragment); + letter = "E"; + letterBg = "bg-green-300"; + hashBg = "bg-green-50"; + } else if (ConstructorFragment.isConstructorFragment(fragment)) { + fragmentType = "constructor"; + letter = "C"; + letterBg = "bg-blue-500"; + } + + return ( +
+ + + + {letter && ( + + {letter} + + )} + + {fragment.format("full")} + + {sig && ( + + {sig} + + )} +
+ ); +}; + +export default React.memo(DecodedFragment); diff --git a/src/address/ABI.tsx b/src/address/RawABI.tsx similarity index 74% rename from src/address/ABI.tsx rename to src/address/RawABI.tsx index a714367..bc07e81 100644 --- a/src/address/ABI.tsx +++ b/src/address/RawABI.tsx @@ -1,11 +1,11 @@ import React from "react"; import { SyntaxHighlighter, docco } from "../highlight-init"; -type ABIProps = { +type RawABIProps = { abi: any[]; }; -const ABI: React.FC = ({ abi }) => ( +const RawABI: React.FC = ({ abi }) => ( = ({ abi }) => ( ); -export default React.memo(ABI); +export default React.memo(RawABI); diff --git a/src/index.tsx b/src/index.tsx index 29ad212..11695da 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,6 +1,6 @@ import React from "react"; import ReactDOM from "react-dom"; -import { Helmet } from "react-helmet"; +import { HelmetProvider, Helmet } from "react-helmet-async"; import "@fontsource/space-grotesk/index.css"; import "@fontsource/roboto/index.css"; import "@fontsource/roboto-mono/index.css"; @@ -11,16 +11,18 @@ import reportWebVitals from "./reportWebVitals"; ReactDOM.render( - - - - + + + + + + , document.getElementById("root") );