From 114147cdcd7df70080df61014b98b3b9fd6241f3 Mon Sep 17 00:00:00 2001 From: Marco van Dijk Date: Fri, 18 Mar 2022 18:41:28 +0100 Subject: [PATCH] ENS support and other stuff --- backend/package.json | 1 + backend/src/config.js | 6 +- backend/src/routes/livepeer.js | 174 +++++++++++++++++++++---- public/ens.png | Bin 0 -> 8382 bytes src/actions/livepeer.js | 35 ++++- src/components/OrchAddressViewer.js | 86 +++++++++++- src/components/eventButton.js | 4 +- src/components/eventButtonAddress.js | 77 ++++++++++- src/pages/livepeer.js | 2 +- src/pages/loadingScreen.js | 14 +- src/reducers/livepeer/livepeerstate.js | 19 ++- src/util/livepeer.js | 29 ++++- 12 files changed, 406 insertions(+), 41 deletions(-) create mode 100644 public/ens.png diff --git a/backend/package.json b/backend/package.json index 70690fe..63ed9a1 100644 --- a/backend/package.json +++ b/backend/package.json @@ -20,6 +20,7 @@ "connect-mongo": "^3.1.2", "crypto-js": "^3.1.9-1", "esm": "^3.2.20", + "ethers": "^5.6.1", "express": "^4.17.1", "express-session": "^1.17.0", "graphql-request": "^4.0.0", diff --git a/backend/src/config.js b/backend/src/config.js index a2bec61..f1a7b04 100644 --- a/backend/src/config.js +++ b/backend/src/config.js @@ -1,4 +1,3 @@ -//Server configuration variables export const { NODE_PORT = 42609, NODE_ENV = 'local', @@ -17,7 +16,10 @@ export const { CONF_TIMEOUT_CMC = 360000, CONF_TIMEOUT_ALCHEMY = 2000, CONF_TIMEOUT_LIVEPEER = 60000, + CONF_TIMEOUT_ENS_DOMAIN = 86400000, + CONF_TIMEOUT_ENS_INFO = 3600000, CONF_DISABLE_SYNC = false, CONF_DISABLE_DB = false, - CONF_DISABLE_CMC = false + CONF_DISABLE_CMC = false, + CONF_DISABLE_ENS = false } = process.env; diff --git a/backend/src/routes/livepeer.js b/backend/src/routes/livepeer.js index 0a329e5..5e5054d 100644 --- a/backend/src/routes/livepeer.js +++ b/backend/src/routes/livepeer.js @@ -8,9 +8,10 @@ import { API_CMC, API_L1_HTTP, API_L2_HTTP, API_L2_WS, CONF_DEFAULT_ORCH, CONF_SIMPLE_MODE, CONF_TIMEOUT_CMC, CONF_TIMEOUT_ALCHEMY, CONF_TIMEOUT_LIVEPEER, CONF_DISABLE_SYNC, - CONF_DISABLE_DB, - CONF_DISABLE_CMC + CONF_DISABLE_DB, CONF_DISABLE_CMC, CONF_TIMEOUT_ENS_DOMAIN, + CONF_TIMEOUT_ENS_INFO, CONF_DISABLE_ENS } from "../config"; + // Do API requests to other API's const https = require('https'); // Read ABI files @@ -45,8 +46,14 @@ if (!CONF_SIMPLE_MODE) { } // For listening to blockchain events +// ENS stuff TODO: CONF_DISABLE_ENS +const { ethers } = require("ethers"); +const provider = new ethers.providers.JsonRpcProvider(API_L1_HTTP); +// const ens = new ENS({ provider: web3layer1, ensAddress: getEnsAddress('1') }); +let ensDomainCache = []; +let ensInfoCache = []; + // Update CoinMarketCap related api calls every 5 minutes -const timeoutCMC = CONF_TIMEOUT_CMC; let cmcPriceGet = 0; let ethPrice = 0; let lptPrice = 0; @@ -54,7 +61,6 @@ let cmcQuotes = {}; let cmcCache = {}; // Update Alchemy related API calls every 2 seconds -const timeoutAlchemy = CONF_TIMEOUT_ALCHEMY; let l2Gwei = 0; let l1Gwei = 0; let l2block = 0; @@ -82,8 +88,6 @@ let commissionFeeCostL2 = 0; let serviceUriFeeCostL1 = 0; let serviceUriFeeCostL2 = 0; -// Update O info from thegraph every 1 minute -const timeoutTheGraph = CONF_TIMEOUT_LIVEPEER; // Will contain addr, lastGet, and obj of any requested O's let orchestratorCache = []; // Contains delegator addr and the address of the O they are bounded to @@ -461,12 +465,12 @@ apiRouter.get("/grafana", async (req, res) => { try { const now = new Date().getTime(); // Update blockchain data if the cached data has expired - if (now - arbGet > timeoutAlchemy) { + if (now - arbGet > CONF_TIMEOUT_ALCHEMY) { await parseEthBlockchain(); arbGet = now; } // Update coin prices once their data has expired - if (now - cmcPriceGet > timeoutCMC) { + if (now - cmcPriceGet > CONF_TIMEOUT_CMC) { await parseCmc(); cmcPriceGet = now; } @@ -502,7 +506,7 @@ apiRouter.get("/cmc", async (req, res) => { try { const now = new Date().getTime(); // Update cmc once their data has expired - if (now - cmcPriceGet > timeoutCMC) { + if (now - cmcPriceGet > CONF_TIMEOUT_CMC) { cmcPriceGet = now; await parseCmc(); } @@ -517,7 +521,7 @@ apiRouter.get("/blockchains", async (req, res) => { try { const now = new Date().getTime(); // Update blockchain data if the cached data has expired - if (now - arbGet > timeoutAlchemy) { + if (now - arbGet > CONF_TIMEOUT_ALCHEMY) { arbGet = now; await parseEthBlockchain(); } @@ -551,7 +555,7 @@ apiRouter.get("/quotes", async (req, res) => { try { const now = new Date().getTime(); // Update cmc once their data has expired - if (now - cmcPriceGet > timeoutCMC) { + if (now - cmcPriceGet > CONF_TIMEOUT_CMC) { cmcPriceGet = now; await parseCmc(); } @@ -596,17 +600,18 @@ const parseOrchestrator = async function (reqAddr) { } } if (wasCached) { - if (now - orchestratorObj.lastGet < timeoutTheGraph) { + if (now - orchestratorObj.lastGet < CONF_TIMEOUT_LIVEPEER) { needsUpdate = false; } } if (!wasCached || needsUpdate) { const orchQuery = gql`{ - transcoders(where: {id: "${reqAddr}"}) { + transcoder(id: "${reqAddr}") { id activationRound deactivationRound active + status lastRewardRound { id length @@ -629,7 +634,7 @@ const parseOrchestrator = async function (reqAddr) { totalVolumeETH totalVolumeUSD serviceURI - delegators { + delegators(first: 1000) { id bondedAmount startRound @@ -643,20 +648,22 @@ const parseOrchestrator = async function (reqAddr) { } `; orchestratorObj = await request("https://api.thegraph.com/subgraphs/name/livepeer/arbitrum-one", orchQuery); - orchestratorObj = orchestratorObj.transcoders[0]; + orchestratorObj = orchestratorObj.transcoder; // Not found if (!orchestratorObj) { return {}; } orchestratorObj.lastGet = now; if (wasCached) { - for (var orch of orchestratorCache) { - if (orch.id == reqAddr) { - orch = orchestratorObj; + for (var idx = 0; idx < orchestratorCache.length; idx++) { + if (orchestratorCache[idx].id == reqAddr) { + console.log("Updating outdated orchestrator " + orchestratorObj.id + " @ " + now); + orchestratorCache[idx] = orchestratorObj; break; } } } else { + console.log("Pushing new orchestrator " + orchestratorObj.id + " @ " + now); orchestratorCache.push(orchestratorObj); } } @@ -714,7 +721,7 @@ const parseDelegator = async function (reqAddr) { } } if (wasCached) { - if (now - delegatorObj.lastGet < timeoutTheGraph) { + if (now - delegatorObj.lastGet < CONF_TIMEOUT_LIVEPEER) { needsUpdate = false; } } @@ -738,13 +745,15 @@ const parseDelegator = async function (reqAddr) { } delegatorObj.lastGet = now; if (wasCached) { - for (var delegator of delegatorCache) { - if (delegator.id == reqAddr) { - delegator = delegatorObj; + for (var idx = 0; idx < delegatorCache.length; idx++) { + if (delegatorCache[idx].id == reqAddr) { + console.log("Updating outdated delegator " + delegatorObj.id + " @ " + now); + delegatorCache[idx] = delegatorObj; break; } } } else { + console.log("Pushing new delegator " + delegatorObj.id + " @ " + now); delegatorCache.push(delegatorObj); } } @@ -804,12 +813,12 @@ apiRouter.get("/prometheus/:orchAddr", async (req, res) => { try { const now = new Date().getTime(); // Update blockchain data if the cached data has expired - if (now - arbGet > timeoutAlchemy) { + if (now - arbGet > CONF_TIMEOUT_ALCHEMY) { await parseEthBlockchain(); arbGet = now; } // Update coin prices once their data has expired - if (now - cmcPriceGet > timeoutCMC) { + if (now - cmcPriceGet > CONF_TIMEOUT_CMC) { await parseCmc(); cmcPriceGet = now; } @@ -950,4 +959,121 @@ apiRouter.get("/prometheus/:orchAddr", async (req, res) => { } }); +const getEnsDomain = async function (addr) { + const now = new Date().getTime(); + let wasInCache = false; + // See if it is cached + for (const thisAddr of ensDomainCache) { + if (thisAddr.address === addr) { + // Check timeout + if (now - thisAddr.timestamp < CONF_TIMEOUT_ENS_DOMAIN ){ + return thisAddr.domain; + } + wasInCache = true; + } + } + // Else get it and cache it + const ensDomain = await provider.lookupAddress(addr.toLowerCase()); + let ensObj; + if (!ensDomain){ + ensObj = { + domain: null, + address: addr, + timestamp: now + }; + } else { + ensObj = { + domain: ensDomain, + address: addr, + timestamp: now + }; + } + if (wasInCache){ + for (var idx = 0; idx < ensDomainCache.length; idx++) { + if (ensDomainCache[idx].address == addr) { + console.log("Updating outdated domain " + ensObj.domain + " owned by " + ensObj.address + " @ " + ensObj.timestamp); + ensDomainCache[idx] = ensObj; + break; + } + } + } else { + console.log("Caching new domain " + ensObj.domain + " owned by " + ensObj.address + " @ " + ensObj.timestamp); + ensDomainCache.push(ensObj); + } + return ensObj.domain; +} + +const getEnsInfo = async function (addr) { + const now = new Date().getTime(); + let wasInCache = false; + // See if it is cached + for (const thisAddr of ensInfoCache) { + if (thisAddr.domain === addr) { + // Check timeout + if (now - thisAddr.timestamp < CONF_TIMEOUT_ENS_INFO ){ + return thisAddr; + } + wasInCache = true; + } + } + // Else get it and cache it + const resolver = await provider.getResolver(addr); + const description = await resolver.getText("description"); + const url = await resolver.getText("url"); + const avatar = await resolver.getAvatar(); + const ensObj = { + domain: addr, + description, + url, + avatar, + timestamp: now + }; + if (wasInCache){ + for (var idx = 0; idx < ensInfoCache.length; idx++) { + if (ensInfoCache[idx].domain == addr) { + console.log("Updating outdated info " + ensObj.domain + " @ " + ensObj.timestamp); + ensInfoCache[idx] = ensObj; + break; + } + } + } else { + console.log("Caching new info " + ensObj.domain + " @ " + ensObj.timestamp); + ensInfoCache.push(ensObj); + } + return ensObj; +} + +// Gets and caches info for a single address +apiRouter.get("/getENS/:orch", async (req, res) => { + try { + // First resolve addr => domain name + const ensDomain = await getEnsDomain(req.params.orch); + if (!ensDomain){ + res.send({domain: null}); + return; + } + // Then resolve address to info + const ensInfo = await getEnsInfo(ensDomain); + res.send(ensInfo); + } catch (err) { + res.status(400).send(err); + } +}); +// Returns entire ENS domain mapping cache +apiRouter.get("/getEnsDomains", async (req, res) => { + try { + res.send(ensDomainCache); + } catch (err) { + res.status(400).send(err); + } +}); +// Returns entire ENS info mapping cache +apiRouter.get("/getEnsInfo", async (req, res) => { + try { + res.send(ensInfoCache); + } catch (err) { + res.status(400).send(err); + } +}); + export default apiRouter; \ No newline at end of file diff --git a/public/ens.png b/public/ens.png new file mode 100644 index 0000000000000000000000000000000000000000..585dfe6040da601671d7a48b2f680b640d2d7080 GIT binary patch literal 8382 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 literal 0 HcmV?d00001 diff --git a/src/actions/livepeer.js b/src/actions/livepeer.js index e448fc2..ba3f162 100644 --- a/src/actions/livepeer.js +++ b/src/actions/livepeer.js @@ -24,6 +24,8 @@ export const RECEIVE_CURRENT_ORCHESTRATOR = "RECEIVE_CURRENT_ORCHESTRATOR"; export const RECEIVE_ORCHESTRATOR = "RECEIVE_ORCHESTRATOR"; export const CLEAR_ORCHESTRATOR = "CLEAR_ORCHESTRATOR"; export const RECEIVE_TICKETS = "RECEIVE_TICKETS"; +export const SET_ALL_ENS_INFO = "SET_ALL_ENS_INFO"; +export const SET_ALL_ENS_DOMAINS = "SET_ALL_ENS_DOMAINS"; const setQuotes = message => ({ type: RECEIVE_QUOTES, message @@ -46,6 +48,13 @@ const clearOrchestratorInfo = () => ({ const setTickets = message => ({ type: RECEIVE_TICKETS, message }); +const setAllEnsInfo = message => ({ + type: SET_ALL_ENS_INFO, message +}); +const setAllEnsDomains = message => ({ + type: SET_ALL_ENS_DOMAINS, message +}); + export const getQuotes = () => async dispatch => { const response = await apiUtil.getQuotes(); @@ -522,4 +531,28 @@ export const getOrchestratorInfo = (orchAddr) => async dispatch => { export const clearOrchestrator = () => async dispatch => { return dispatch(clearOrchestratorInfo({})); -}; \ No newline at end of file +}; + +export const getAllEnsDomains = () => async dispatch => { + const response = await apiUtil.getAllEnsDomains(); + const data = await response.json(); + if (response.ok) { + return dispatch(setAllEnsDomains(data)); + } + return dispatch(receiveErrors(data)); +}; + +export const getAllEnsInfo = () => async dispatch => { + const response = await apiUtil.getAllEnsInfo(); + const data = await response.json(); + if (response.ok) { + return dispatch(setAllEnsInfo(data)); + } + return dispatch(receiveErrors(data)); +}; + +export const getEnsInfo = async (addr) => { + const response = await apiUtil.getEnsInfo(addr); + const data = await response.json(); +}; + \ No newline at end of file diff --git a/src/components/OrchAddressViewer.js b/src/components/OrchAddressViewer.js index b9365c3..c20fbeb 100644 --- a/src/components/OrchAddressViewer.js +++ b/src/components/OrchAddressViewer.js @@ -1,14 +1,94 @@ -import React from "react"; +import React, { useState } from "react"; +import { useSelector } from 'react-redux'; +import { getEnsInfo } from "../actions/livepeer"; +import { + getOrchestratorInfo +} from "../actions/livepeer"; const Address = (obj) => { + const livepeer = useSelector((state) => state.livepeerstate); + const [hasRefreshed, setRefresh] = useState(false); + let thisDomain = null; + let thisInfo = null; + const now = new Date().getTime(); + // Lookup domain in cache + if (livepeer.ensDomainMapping){ + for (const thisAddr of livepeer.ensDomainMapping) { + if (thisAddr.address === obj.address) { + thisDomain = thisAddr; + // Check timeout + if (now - thisAddr.timestamp < 86400000) { + break; + } + // Is outdated + if (!hasRefreshed) { + getEnsInfo(obj.address); + setRefresh(true); + } + } + } + // If it was not cached at all + if (thisDomain == null && !hasRefreshed) { + setRefresh(true); + getEnsInfo(obj.address); + } + } + // Lookup current info in cache only if this addr has a mapped ENS domain + if (thisDomain && thisDomain.domain) { + for (const thisAddr of livepeer.ensInfoMapping) { + if (thisAddr.domain === thisDomain.domain) { + thisInfo = thisAddr; + // Check timeout + if (now - thisAddr.timestamp < 86400000) { + break; + } + // Is outdated + if (!hasRefreshed) { + getEnsInfo(obj.address); + setRefresh(true); + } + } + } + // If it was not cached at all + if (thisInfo == null && !hasRefreshed) { + getEnsInfo(obj.address); + setRefresh(true); + } + } + + let thisName; + let thisIcon; + if (thisInfo) { + thisName = thisInfo.domain; + if (thisInfo.avatar) { + thisIcon = + +
+ +
+
+ } else { + thisIcon = + +
+ +
+
+ } + } else { + thisName = obj.address; + thisIcon = null; + } + return (
- +
- {obj.address}
+ {thisIcon} + {thisName}
) } diff --git a/src/components/eventButton.js b/src/components/eventButton.js index 5f9c0f8..f8bce9a 100644 --- a/src/components/eventButton.js +++ b/src/components/eventButton.js @@ -35,9 +35,9 @@ const EventButton = (obj) => { } return ( -
+
{blockNumber} -
+
{eventCaller} diff --git a/src/components/eventButtonAddress.js b/src/components/eventButtonAddress.js index 7a6edba..dc3fab7 100644 --- a/src/components/eventButtonAddress.js +++ b/src/components/eventButtonAddress.js @@ -1,14 +1,85 @@ -import React from "react"; +import React, { useState } from "react"; import { getOrchestratorInfo } from "../actions/livepeer"; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; +import { getEnsInfo } from "../actions/livepeer"; const EventButtonAddress = (obj) => { const dispatch = useDispatch(); + const livepeer = useSelector((state) => state.livepeerstate); + const [hasRefreshed, setRefresh] = useState(false); + let thisDomain = null; + let thisInfo = null; + const now = new Date().getTime(); + // Lookup domain in cache + if (livepeer.ensDomainMapping) { + for (const thisAddr of livepeer.ensDomainMapping) { + if (thisAddr.address === obj.address) { + thisDomain = thisAddr; + // Check timeout + if (now - thisAddr.timestamp < 86400000) { + break; + } + // Is outdated + if (!hasRefreshed) { + getEnsInfo(obj.address); + setRefresh(true); + } + } + } + // If it was not cached at all + if (thisDomain == null && !hasRefreshed) { + setRefresh(true); + getEnsInfo(obj.address); + } + } + // Lookup current info in cache only if this addr has a mapped ENS domain + if (thisDomain && thisDomain.domain) { + for (const thisAddr of livepeer.ensInfoMapping) { + if (thisAddr.domain === thisDomain.domain) { + thisInfo = thisAddr; + // Check timeout + if (now - thisAddr.timestamp < 86400000) { + break; + } + // Is outdated + if (!hasRefreshed) { + getEnsInfo(obj.address); + setRefresh(true); + } + } + } + // If it was not cached at all + if (thisInfo == null && !hasRefreshed) { + getEnsInfo(obj.address); + setRefresh(true); + } + } + + let thisName; + let thisIcon; + if (thisInfo) { + thisName =

{thisInfo.domain}

; + if (thisInfo.avatar) { + thisIcon = + + + + } else { + thisIcon = + + + + } + } else { + thisName = {obj.address}; + thisIcon = null; + } return (
+ {thisIcon} @@ -17,7 +88,7 @@ const EventButtonAddress = (obj) => { {obj.name}
) diff --git a/src/pages/livepeer.js b/src/pages/livepeer.js index 43bfc65..c537d9e 100644 --- a/src/pages/livepeer.js +++ b/src/pages/livepeer.js @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react' import '../style.css'; import { Navigate, useSearchParams } from "react-router-dom"; -import { useSelector, useDispatch } from 'react-redux' +import { useSelector, useDispatch } from 'react-redux'; import { getOrchestratorInfo, clearOrchestrator } from "../actions/livepeer"; import EventViewer from "../components/eventViewer"; import Orchestrator from "../components/orchestratorViewer"; diff --git a/src/pages/loadingScreen.js b/src/pages/loadingScreen.js index d1a16ce..8131bb9 100644 --- a/src/pages/loadingScreen.js +++ b/src/pages/loadingScreen.js @@ -4,7 +4,8 @@ import { getVisitorStats } from "../actions/user"; import { - getQuotes, getBlockchainData, getEvents, getCurrentOrchestratorInfo, getTickets + getQuotes, getBlockchainData, getEvents, getCurrentOrchestratorInfo, getTickets, + getAllEnsDomains, getAllEnsInfo } from "../actions/livepeer"; import { login } from "../actions/session"; @@ -18,7 +19,7 @@ const Startup = (obj) => { const dispatch = useDispatch(); const refreshAllZeData = () => { - console.log("Refreshing data..."); + console.log("Refreshing Livepeer data..."); batch(() => { dispatch(getQuotes()); dispatch(getEvents()); @@ -35,10 +36,19 @@ const Startup = (obj) => { dispatch(getVisitorStats()); }); } + + const refreshENS = () => { + console.log("Refreshing ENS data..."); + batch(() => { + dispatch(getAllEnsDomains()); + dispatch(getAllEnsInfo()); + }); + } useEffect(() => { refreshLogin(); refreshAllZeData(); + refreshENS(); setIsLoaded(true); if (refreshInterval) { const interval = setInterval(refreshAllZeData, refreshInterval); diff --git a/src/reducers/livepeer/livepeerstate.js b/src/reducers/livepeer/livepeerstate.js index 421cfbb..15da7c1 100644 --- a/src/reducers/livepeer/livepeerstate.js +++ b/src/reducers/livepeer/livepeerstate.js @@ -5,10 +5,21 @@ import { RECEIVE_ORCHESTRATOR, RECEIVE_CURRENT_ORCHESTRATOR, CLEAR_ORCHESTRATOR, - RECEIVE_TICKETS + RECEIVE_TICKETS, + SET_ALL_ENS_INFO, + SET_ALL_ENS_DOMAINS } from "../../actions/livepeer"; -export default (state = {}, { type, message }) => { +export default (state = { + quotes: [], + blockchains: [], + events: [], + thisOrchestrator: null, + selectedOrchestrator: null, + tickets: [], + ensInfoMapping: [], + ensDomainMapping: [] +}, { type, message }) => { Object.freeze(state); switch (type) { case RECEIVE_QUOTES: @@ -25,6 +36,10 @@ export default (state = {}, { type, message }) => { return { ...state, selectedOrchestrator: null }; case RECEIVE_TICKETS: return { ...state, tickets: message }; + case SET_ALL_ENS_INFO: + return { ...state, ensInfoMapping: message }; + case SET_ALL_ENS_DOMAINS: + return { ...state, ensDomainMapping: message }; default: return { ...state }; } diff --git a/src/util/livepeer.js b/src/util/livepeer.js index b80122c..5179024 100644 --- a/src/util/livepeer.js +++ b/src/util/livepeer.js @@ -61,4 +61,31 @@ export const getOrchestratorByDelegator = (delAddr) => ( "Content-Type": "application/json" } }) -); \ No newline at end of file +); + +export const getAllEnsDomains = () => ( + fetch("api/livepeer/getEnsDomains/", { + method: "GET", + headers: { + "Content-Type": "application/json" + } + }) +); + +export const getAllEnsInfo = () => ( + fetch("api/livepeer/getEnsInfo/", { + method: "GET", + headers: { + "Content-Type": "application/json" + } + }) +); + +export const getEnsInfo = (addr) => ( + fetch("api/livepeer/getENS/" + addr, { + method: "GET", + headers: { + "Content-Type": "application/json" + } + }) +);