From ad63e1dd17eaeb890652a509fbea28c942fcdb9b Mon Sep 17 00:00:00 2001 From: Giovani Date: Wed, 11 May 2022 01:35:14 -0400 Subject: [PATCH] feat: query blockchain for ens text records --- config-overrides | 21 ++++ package-lock.json | 53 +++++++++ package.json | 24 +++- src/App.js | 185 +++++++++++++++++++++++-------- src/asset/image/github-icon.svg | 1 + src/asset/image/twitter-icon.svg | 1 + src/asset/image/website-icon.png | Bin 0 -> 3927 bytes src/utility.js | 71 +++++++++--- 8 files changed, 288 insertions(+), 68 deletions(-) create mode 100644 config-overrides create mode 100644 src/asset/image/github-icon.svg create mode 100644 src/asset/image/twitter-icon.svg create mode 100644 src/asset/image/website-icon.png diff --git a/config-overrides b/config-overrides new file mode 100644 index 0000000..253164f --- /dev/null +++ b/config-overrides @@ -0,0 +1,21 @@ +const webpack = require("webpack"); +module.exports = function override(config) { + const fallback = config.resolve.fallback || {}; + Object.assign(fallback, { + crypto: require.resolve("crypto-browserify"), + stream: require.resolve("stream-browserify"), + assert: require.resolve("assert"), + http: require.resolve("stream-http"), + https: require.resolve("https-browserify"), + os: require.resolve("os-browserify"), + url: require.resolve("url"), + }); + config.resolve.fallback = fallback; + config.plugins = (config.plugins || []).concat([ + new webpack.ProvidePlugin({ + process: "process/browser", + Buffer: ["buffer", "Buffer"], + }), + ]); + return config; +}; diff --git a/package-lock.json b/package-lock.json index 83024ae..9fa0aa5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,18 @@ "sha3": "^2.1.4", "web-vitals": "^2.1.4", "web3": "^1.7.3" + }, + "devDependencies": { + "assert": "^1.5.0", + "buffer": "^6.0.3", + "crypto-browserify": "^3.12.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "process": "^0.11.10", + "react-app-rewired": "^2.2.1", + "stream-browserify": "^3.0.0", + "stream-http": "^3.2.0", + "url": "^0.11.0" } }, "node_modules/@ampproject/remapping": { @@ -18531,6 +18543,30 @@ "node": ">=14" } }, + "node_modules/react-app-rewired": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-app-rewired/-/react-app-rewired-2.2.1.tgz", + "integrity": "sha512-uFQWTErXeLDrMzOJHKp0h8P1z0LV9HzPGsJ6adOtGlA/B9WfT6Shh4j2tLTTGlXOfiVx6w6iWpp7SOC5pvk+gA==", + "dev": true, + "dependencies": { + "semver": "^5.6.0" + }, + "bin": { + "react-app-rewired": "bin/index.js" + }, + "peerDependencies": { + "react-scripts": ">=2.1.3" + } + }, + "node_modules/react-app-rewired/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -36007,6 +36043,23 @@ "whatwg-fetch": "^3.6.2" } }, + "react-app-rewired": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-app-rewired/-/react-app-rewired-2.2.1.tgz", + "integrity": "sha512-uFQWTErXeLDrMzOJHKp0h8P1z0LV9HzPGsJ6adOtGlA/B9WfT6Shh4j2tLTTGlXOfiVx6w6iWpp7SOC5pvk+gA==", + "dev": true, + "requires": { + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, "react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", diff --git a/package.json b/package.json index 44e3855..8283534 100644 --- a/package.json +++ b/package.json @@ -25,12 +25,12 @@ "web-vitals": "^2.1.4", "web3": "^1.7.3" }, - "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, + "scripts": { + "start": "react-app-rewired start", + "build": "react-app-rewired build", + "test": "react-app-rewired test", + "eject": "react-scripts eject" + }, "eslintConfig": { "extends": [ "react-app", @@ -48,5 +48,17 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "assert": "^1.5.0", + "buffer": "^6.0.3", + "crypto-browserify": "^3.12.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "process": "^0.11.10", + "react-app-rewired": "^2.2.1", + "stream-browserify": "^3.0.0", + "stream-http": "^3.2.0", + "url": "^0.11.0" } } diff --git a/src/App.js b/src/App.js index 522a65b..1346835 100644 --- a/src/App.js +++ b/src/App.js @@ -2,20 +2,24 @@ import "./App.css"; import "animate.css"; import { useState, useEffect } from "react"; -//import namehash from "eth-ens-namehash"; // DO NOT REMOVE COMMENT +import namehash from "eth-ens-namehash"; import { Keccak } from "sha3"; //import axiosClient from "axios"; // DO NOT REMOVE COMMENT import Card from "@mui/material/Card"; import CardActions from "@mui/material/CardActions"; import ButtonBase from "@mui/material/ButtonBase"; +import Tooltip from "@mui/material/Tooltip"; import OpenSeaIcon from "./asset/image/opensea-icon.svg"; import EnsIcon from "./asset/image/ens-icon.jpeg"; +import GithubIcon from "./asset/image/github-icon.svg"; +import TwitterIcon from "./asset/image/twitter-icon.svg"; +import WebsiteIcon from "./asset/image/website-icon.png"; -import { hexToDec } from "./utility"; +import { EnsContract, hexToDec, textRecordToUrl } from "./utility"; -//var ensContract = new EnsContract(); +var ensContract = new EnsContract(); /* vanilla js to adapt height to actual viewport vs therotical */ let vh = window.innerHeight * 0.01; @@ -30,6 +34,12 @@ window.addEventListener("resize", () => { function App() { const [seconds, setSeconds] = useState("00"); const [currentEnsImage, setCurrentEnsImage] = useState(""); + const [currentEnsMetadata, setCurrentEnsMetadata] = useState({ + url: "", + twitter: "", + github: "", + }); + const ensImages = {}; const donationLink = "https://heliowallet.com/ethereum-donation?address=0x2652CBE035CF346A4E085D4C2Fb30F2B5Abf3d3d&amount=0.01&accessWallet=1&qrCode=1ðBalance=1"; @@ -37,11 +47,14 @@ function App() { useEffect(() => { setSeconds(getSeconds()); setInterval(() => { + if (getSeconds() === "59") { + getEnsMetadata(getEnsName(1)); + } setSeconds(getSeconds()); setCurrentEnsImage(ensImages[getEnsName()]); }, 1000); - preloadImages(); + getEnsMetadata(getEnsName()); // eslint-disable-next-line }, []); @@ -82,6 +95,31 @@ function App() { await new Promise((r) => setTimeout(r, timeDelay)); }; + const getEnsMetadata = async (ensName) => { + const urlPromise = ensContract.getTextRecord("url", getNameHash(ensName)); + const twitterPromise = ensContract.getTextRecord( + "com.twitter", + getNameHash(ensName) + ); + const githubPromise = ensContract.getTextRecord( + "com.github", + getNameHash(ensName) + ); + + const results = await Promise.all([ + urlPromise, + twitterPromise, + githubPromise, + ]); + + const ensMetadata = { + url: results[0] ? textRecordToUrl(results[0], "uri") : "", + twitter: results[1] ? textRecordToUrl(results[1], "twitter") : "", + github: results[2] ? textRecordToUrl(results[2], "github") : "", + }; + setCurrentEnsMetadata(ensMetadata); + }; + const getEnsImageUri = (ensName) => { return `https://metadata.ens.domains/mainnet/0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85/${getLabelHash( ensName @@ -93,21 +131,21 @@ function App() { }; const getOpenSeaUri = (ensName) => { - const tokenId = hexToDec(getLabelHash(ensName).replace("0x", "")); + const tokenId = hexToDec(getLabelHash(ensName, true)); return `https://opensea.io/assets/0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85/${tokenId}`; }; - // const getNameHash = (ensName) => { // DO NOT REMOVE COMMENT - // return namehash.hash(ensName); - // }; + const getNameHash = (ensName) => { + return namehash.hash(ensName); + }; - const getLabelHash = (ensName) => { + const getLabelHash = (ensName, omitPrefix) => { const label = ensName.replace(".eth", ""); const hasher = new Keccak(256); hasher.update(label); - return "0x" + hasher.digest("hex"); + return (!omitPrefix ? "0x" : "") + hasher.digest("hex"); }; const getEnsName = (minutesToAdd) => { @@ -167,11 +205,102 @@ function App() { ); }; + const renderIcon = (icon, uri, tooltip, style) => { + return ( + + + + {tooltip + + + + ); + }; + + const websiteIconStyle = { + backgroundColor: "white", + borderRadius: "50%", + }; + const ensIconStyle = { + borderRadius: "50%", + }; + + const hasEnsMetadata = () => { + return ( + currentEnsMetadata.url || + currentEnsMetadata.twitter || + currentEnsMetadata.github + ); + }; + + const renderIcons = () => { + return ( + <> + {currentEnsMetadata.url ? ( + renderIcon( + WebsiteIcon, + currentEnsMetadata.url, + "Website", + websiteIconStyle + ) + ) : ( + <> + )} + {currentEnsMetadata.twitter ? ( + renderIcon(TwitterIcon, currentEnsMetadata.twitter, "Twitter") + ) : ( + <> + )} + {currentEnsMetadata.github ? ( + renderIcon(GithubIcon, currentEnsMetadata.github, "Github") + ) : ( + <> + )} + {!hasEnsMetadata() ? ( + renderIcon(OpenSeaIcon, getOpenSeaUri(getEnsName()), "OpenSea") + ) : ( + <> + )} + {!hasEnsMetadata() ? ( + renderIcon( + EnsIcon, + getEnsDetailsUri(getEnsName()), + "ENS", + ensIconStyle + ) + ) : ( + <> + )} + + ); + }; + return (
-
+
+ + Website + + icon by + + Icons8 +
@@ -182,39 +311,7 @@ function App() { ? renderLoadingRipple() : renderCardContent()} -
- - - opensea logo - - - - - ens logo - - -
+
{renderIcons()}
diff --git a/src/asset/image/github-icon.svg b/src/asset/image/github-icon.svg new file mode 100644 index 0000000..fd37ebf --- /dev/null +++ b/src/asset/image/github-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/asset/image/twitter-icon.svg b/src/asset/image/twitter-icon.svg new file mode 100644 index 0000000..7c5d896 --- /dev/null +++ b/src/asset/image/twitter-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/asset/image/website-icon.png b/src/asset/image/website-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7110972833984e4f050db054d9da56e6d1095997 GIT binary patch literal 3927 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 llP6D}JbCiu$&=^s@_&C2AM { let s = hexString; @@ -40,3 +41,37 @@ export const hexToDec = (hexString) => { } return digits.reverse().join(""); }; + +export const textRecordToUrl = (textRecord, type) => { + if (type === "uri") { + if (textRecord.startsWith("http")) { + return textRecord; + } else { + return `http://${textRecord}`; + } + } + + if (type === "twitter") { + if (textRecord.includes("twitter.com")) { + return textRecord; + } + + if (textRecord.includes("@")) { + return `https://twitter.com/${textRecord.replace("@", "")}`; + } else { + return `https://twitter.com/${textRecord}`; + } + } + + if (type === "github") { + if (textRecord.includes("github.com")) { + return textRecord; + } + + if (textRecord.includes("@")) { + return `https://github.com/${textRecord.replace("@", "")}`; + } else { + return `https://github.com/${textRecord}`; + } + } +};