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: {