diff --git a/backend/src/models/ActivateEvent.js b/backend/src/models/ActivateEvent.js new file mode 100644 index 0000000..f52e90a --- /dev/null +++ b/backend/src/models/ActivateEvent.js @@ -0,0 +1,32 @@ +import mongoose from 'mongoose'; + +const ActivateEventSchema = new mongoose.Schema({ + address: { + type: String, + required: true + }, + initialStake: { + type: Number, + required: true + }, + round: { + type: Number, + required: false, + default: null + }, + transactionHash: { + type: String, + required: true + }, + blockNumber: { + type: Number, + required: true + }, + blockTime: { + type: Number, + required: true + } +}, { timestamps: false }); + +const ActivateEvent = mongoose.model('ActivateEvent', ActivateEventSchema); +export default ActivateEvent; \ No newline at end of file diff --git a/backend/src/models/ClaimEvent.js b/backend/src/models/ClaimEvent.js new file mode 100644 index 0000000..bee011e --- /dev/null +++ b/backend/src/models/ClaimEvent.js @@ -0,0 +1,31 @@ +import mongoose from 'mongoose'; + +const ClaimEventSchema = new mongoose.Schema({ + address: { + type: String, + required: true + }, + fees: { + type: Number, + required: true + }, + rewards: { + type: Number, + required: true + }, + transactionHash: { + type: String, + required: true + }, + blockNumber: { + type: Number, + required: true + }, + blockTime: { + type: Number, + required: true + } +}, { timestamps: false }); + +const ClaimEvent = mongoose.model('ClaimEvent', ClaimEventSchema); +export default ClaimEvent; \ No newline at end of file diff --git a/backend/src/models/RedeemEvent.js b/backend/src/models/RedeemEvent.js new file mode 100644 index 0000000..d313faf --- /dev/null +++ b/backend/src/models/RedeemEvent.js @@ -0,0 +1,27 @@ +import mongoose from 'mongoose'; + +const RedeemEventSchema = new mongoose.Schema({ + address: { + type: String, + required: true + }, + amount: { + type: Number, + required: true + }, + transactionHash: { + type: String, + required: true + }, + blockNumber: { + type: Number, + required: true + }, + blockTime: { + type: Number, + required: true + } +}, { timestamps: false }); + +const RedeemEvent = mongoose.model('RedeemEvent', RedeemEventSchema); +export default RedeemEvent; \ No newline at end of file diff --git a/backend/src/models/RewardEvent.js b/backend/src/models/RewardEvent.js new file mode 100644 index 0000000..7c1d23e --- /dev/null +++ b/backend/src/models/RewardEvent.js @@ -0,0 +1,27 @@ +import mongoose from 'mongoose'; + +const RewardEventSchema = new mongoose.Schema({ + address: { + type: String, + required: true + }, + amount: { + type: Number, + required: true + }, + transactionHash: { + type: String, + required: true + }, + blockNumber: { + type: Number, + required: true + }, + blockTime: { + type: Number, + required: true + } +}, { timestamps: false }); + +const RewardEvent = mongoose.model('RewardEvent', RewardEventSchema); +export default RewardEvent; \ No newline at end of file diff --git a/backend/src/models/StakeEvent.js b/backend/src/models/StakeEvent.js new file mode 100644 index 0000000..b53c0ad --- /dev/null +++ b/backend/src/models/StakeEvent.js @@ -0,0 +1,35 @@ +import mongoose from 'mongoose'; + +const StakeEventSchema = new mongoose.Schema({ + address: { + type: String, + required: true + }, + from: { + type: String, + required: false + }, + to: { + type: String, + required: false + }, + stake: { + type: Number, + required: true + }, + transactionHash: { + type: String, + required: true + }, + blockNumber: { + type: Number, + required: true + }, + blockTime: { + type: Number, + required: true + } +}, { timestamps: false }); + +const StakeEvent = mongoose.model('StakeEvent', StakeEventSchema); +export default StakeEvent; \ No newline at end of file diff --git a/backend/src/models/TransferEvent.js b/backend/src/models/TransferEvent.js new file mode 100644 index 0000000..77f2392 --- /dev/null +++ b/backend/src/models/TransferEvent.js @@ -0,0 +1,31 @@ +import mongoose from 'mongoose'; + +const TransferEventSchema = new mongoose.Schema({ + address: { + type: String, + required: true + }, + to: { + type: String, + required: true + }, + amount: { + type: number, + required: true + }, + transactionHash: { + type: String, + required: true + }, + blockNumber: { + type: Number, + required: true + }, + blockTime: { + type: Number, + required: true + } +}, { timestamps: false }); + +const TransferEvent = mongoose.model('TransferEvent', TransferEventSchema); +export default TransferEvent; \ No newline at end of file diff --git a/backend/src/models/UnbondEvent.js b/backend/src/models/UnbondEvent.js new file mode 100644 index 0000000..3e94bff --- /dev/null +++ b/backend/src/models/UnbondEvent.js @@ -0,0 +1,27 @@ +import mongoose from 'mongoose'; + +const UnbondEventSchema = new mongoose.Schema({ + address: { + type: String, + required: true + }, + stake: { + type: Number, + required: true + }, + transactionHash: { + type: String, + required: true + }, + blockNumber: { + type: Number, + required: true + }, + blockTime: { + type: Number, + required: true + } +}, { timestamps: false }); + +const UnbondEvent = mongoose.model('UnbondEvent', UnbondEventSchema); +export default UnbondEvent; \ No newline at end of file diff --git a/backend/src/models/UpdateEvent.js b/backend/src/models/UpdateEvent.js new file mode 100644 index 0000000..4f1ca72 --- /dev/null +++ b/backend/src/models/UpdateEvent.js @@ -0,0 +1,31 @@ +import mongoose from 'mongoose'; + +const UpdateEventSchema = new mongoose.Schema({ + address: { + type: String, + required: true + }, + rewardCommission: { + type: Number, + required: true + }, + feeCommission: { + type: Number, + required: true + }, + transactionHash: { + type: String, + required: true + }, + blockNumber: { + type: Number, + required: true + }, + blockTime: { + type: Number, + required: true + } +}, { timestamps: false }); + +const UpdateEvent = mongoose.model('UpdateEvent', UpdateEventSchema); +export default UpdateEvent; \ No newline at end of file diff --git a/backend/src/models/WithdrawFeesEvent.js b/backend/src/models/WithdrawFeesEvent.js new file mode 100644 index 0000000..20725a8 --- /dev/null +++ b/backend/src/models/WithdrawFeesEvent.js @@ -0,0 +1,31 @@ +import mongoose from 'mongoose'; + +const WithdrawEventSchema = new mongoose.Schema({ + address: { + type: String, + required: true + }, + from: { + type: String, + required: true + }, + amount: { + type: Number, + required: true + }, + transactionHash: { + type: String, + required: true + }, + blockNumber: { + type: Number, + required: true + }, + blockTime: { + type: Number, + required: true + } +}, { timestamps: false }); + +const WithdrawEvent = mongoose.model('WithdrawEvent', WithdrawEventSchema); +export default WithdrawEvent; \ No newline at end of file diff --git a/backend/src/models/WithdrawStakeEvent.js b/backend/src/models/WithdrawStakeEvent.js new file mode 100644 index 0000000..27cd855 --- /dev/null +++ b/backend/src/models/WithdrawStakeEvent.js @@ -0,0 +1,35 @@ +import mongoose from 'mongoose'; + +const WithdrawEventSchema = new mongoose.Schema({ + address: { + type: String, + required: true + }, + from: { + type: String, + required: true + }, + round: { + type: Number, + required: true + }, + amount: { + type: Number, + required: true + }, + transactionHash: { + type: String, + required: true + }, + blockNumber: { + type: Number, + required: true + }, + blockTime: { + type: Number, + required: true + } +}, { timestamps: false }); + +const WithdrawEvent = mongoose.model('WithdrawEvent', WithdrawEventSchema); +export default WithdrawEvent; \ No newline at end of file diff --git a/backend/src/models/monthlyStat.js b/backend/src/models/monthlyStat.js new file mode 100644 index 0000000..e08a4aa --- /dev/null +++ b/backend/src/models/monthlyStat.js @@ -0,0 +1,148 @@ +import mongoose from 'mongoose'; + +const MonthlyStatSchema = new mongoose.Schema({ + // Static props + year: { + type: Number, + required: true + }, + month: { + type: Number, + required: true + }, + name: { + type: String, + required: true + }, + // Counters based on Smart Contract Events + // Any TranscoderUpdate event: commission rates are done with thegraph query of current data, no historical data + // Any TranscoderActivated event + activationCount: { + type: Number, + required: false, + default: 0 + }, + activationInitialSum: { + type: Number, + required: false, + default: 0 + }, + // Lone Unbond event + unbondCount: { + type: Number, + required: false, + default: 0 + }, + unbondStakeSum: { + type: Number, + required: false, + default: 0 + }, + // Any Reward event + rewardCount: { + type: Number, + required: false, + default: 0 + }, + rewardAmountSum: { + type: Number, + required: false, + default: 0 + }, + // Any EarningsClaimed event + claimCount: { + type: Number, + required: false, + default: 0 + }, + claimRewardSum: { + type: Number, + required: false, + default: 0 + }, + claimFeeSum: { + type: Number, + required: false, + default: 0 + }, + // Any WithdrawStake event + withdrawStakeCount: { + type: Number, + required: false, + default: 0 + }, + withdrawStakeAmountSum: { + type: Number, + required: false, + default: 0 + }, + // Any WithdrawFees event + withdrawFeesCount: { + type: Number, + required: false, + default: 0 + }, + withdrawFeesAmountSum: { + type: Number, + required: false, + default: 0 + }, + // Lone Bond event + bondCount: { + type: Number, + required: false, + default: 0 + }, + bondStakeSum: { + type: Number, + required: false, + default: 0 + }, + // Unbond->TransferBond->Rebond event + moveStakeCount: { + type: Number, + required: false, + default: 0 + }, + moveStakeSum: { + type: Number, + required: false, + default: 0 + }, + // Any TransferTicket event + winningTicketsReceivedCount: { + type: Number, + required: false, + default: 0 + }, + winningTicketsReceivedSum: { + type: Number, + required: false, + default: 0 + }, + // Any RedeemTicket event + winningTicketsRedeemedCount: { + type: Number, + required: false, + default: 0 + }, + winningTicketsRedeemedSum: { + type: Number, + required: false, + default: 0 + }, + // Dynamic stats (until the month has passed) + orchestratorStats: { + type: [Object], + required: false, + default: null + }, + testScores: { + type: Object, + required: false, + default: null + }, +}, { timestamps: false }); + +const MonthlyStat = mongoose.model('MonthlyStat', MonthlyStatSchema); +export default MonthlyStat; \ No newline at end of file diff --git a/backend/src/routes/livepeer.js b/backend/src/routes/livepeer.js index 9ce3d21..1afaa0a 100644 --- a/backend/src/routes/livepeer.js +++ b/backend/src/routes/livepeer.js @@ -150,6 +150,12 @@ const getBlock = async function (blockNumber) { return thisBlock; } +/* + +SMART CONTRACT EVENTS + +*/ + // Set special flag to make sure also get blocks that pass us by while we are syncing let isSyncing = true; let isEventSyncing = false; @@ -157,6 +163,13 @@ let isTicketSyncing = false; // Start Listening for live updates var BondingManagerProxyListener; var TicketBrokerProxyListener; + +/* + +SMART CONTRACT EVENTS - LIVE DATA + +*/ + if (!CONF_SIMPLE_MODE) { BondingManagerProxyListener = bondingManagerContract.events.allEvents(async (error, event) => { try { @@ -232,6 +245,12 @@ if (!CONF_SIMPLE_MODE) { console.log("Listening for tickets on " + TicketBrokerTargetAddr); } +/* + +SMART CONTRACT EVENTS - SYNC OF MISSED BLOCKS + +*/ + // Syncs events database const syncEvents = function () { console.log("Starting sync process for Bonding Manager events"); @@ -412,6 +431,30 @@ if (!isEventSyncing && !CONF_SIMPLE_MODE && !CONF_DISABLE_SYNC) { handleSync(); } +// Exports list of smart contract events +apiRouter.get("/getEvents", async (req, res) => { + try { + res.send(eventsCache); + } catch (err) { + res.status(400).send(err); + } +}); + +// Exports list of smart contract ticket events +apiRouter.get("/getTickets", async (req, res) => { + try { + res.send(ticketsCache); + } catch (err) { + res.status(400).send(err); + } +}); + +/* + +COINMARKETCAP + +*/ + // Splits of raw CMC object into coin quote data const parseCmc = async function () { try { @@ -436,6 +479,42 @@ const parseCmc = async function () { } } +// Exports raw CoinMarketCap info +apiRouter.get("/cmc", async (req, res) => { + try { + const now = new Date().getTime(); + // Update cmc once their data has expired + if (now - cmcPriceGet > CONF_TIMEOUT_CMC) { + cmcPriceGet = now; + await parseCmc(); + } + res.send(cmcCache); + } catch (err) { + res.status(400).send(err); + } +}); + +// Exports top 200 coin quotes +apiRouter.get("/quotes", async (req, res) => { + try { + const now = new Date().getTime(); + // Update cmc once their data has expired + if (now - cmcPriceGet > CONF_TIMEOUT_CMC) { + cmcPriceGet = now; + await parseCmc(); + } + res.send(cmcQuotes); + } catch (err) { + res.status(400).send(err); + } +}); + +/* + +ARBITRUM DATA + +*/ + // Queries Alchemy for block info and gas fees const parseL1Blockchain = async function () { const l1Wei = await web3layer1.eth.getGasPrice(); @@ -463,62 +542,6 @@ const parseEthBlockchain = async function () { await Promise.all([parseL1Blockchain(), parseL2Blockchain()]); } -// Export livepeer and eth coin prices and L1 Eth gas price -apiRouter.get("/grafana", async (req, res) => { - try { - const now = new Date().getTime(); - // Update blockchain data if the cached data has expired - if (now - arbGet > CONF_TIMEOUT_ALCHEMY) { - await parseEthBlockchain(); - arbGet = now; - } - // Update coin prices once their data has expired - if (now - cmcPriceGet > CONF_TIMEOUT_CMC) { - await parseCmc(); - cmcPriceGet = now; - } - res.send({ - timestamp: now, - cmcTime: cmcPriceGet, - blockchainTime: arbGet, - l1GasFeeInGwei: l1Gwei, - l2GasFeeInGwei: l2Gwei, - ethPriceInDollar: ethPrice, - lptPriceInDollar: lptPrice, - redeemRewardCostL1, - redeemRewardCostL2, - claimTicketCostL1, - claimTicketCostL2, - withdrawFeeCostL1, - withdrawFeeCostL2, - stakeFeeCostL1, - stakeFeeCostL2, - commissionFeeCostL1, - commissionFeeCostL2, - serviceUriFeeCostL1, - serviceUriFeeCostL2, - quotes: cmcQuotes - }); - } catch (err) { - res.status(400).send(err); - } -}); - -// Exports raw CoinMarketCap info -apiRouter.get("/cmc", async (req, res) => { - try { - const now = new Date().getTime(); - // Update cmc once their data has expired - if (now - cmcPriceGet > CONF_TIMEOUT_CMC) { - cmcPriceGet = now; - await parseCmc(); - } - res.send(cmcCache); - } catch (err) { - res.status(400).send(err); - } -}); - // Exports gas fees and contract prices apiRouter.get("/blockchains", async (req, res) => { try { @@ -553,38 +576,11 @@ apiRouter.get("/blockchains", async (req, res) => { } }); -// Exports top 200 coin quotes -apiRouter.get("/quotes", async (req, res) => { - try { - const now = new Date().getTime(); - // Update cmc once their data has expired - if (now - cmcPriceGet > CONF_TIMEOUT_CMC) { - cmcPriceGet = now; - await parseCmc(); - } - res.send(cmcQuotes); - } catch (err) { - res.status(400).send(err); - } -}); +/* -// Exports list of smart contract events -apiRouter.get("/getEvents", async (req, res) => { - try { - res.send(eventsCache); - } catch (err) { - res.status(400).send(err); - } -}); +THEGRAPH - ORCHESTRATOR -// Exports list of smart contract ticket events -apiRouter.get("/getTickets", async (req, res) => { - try { - res.send(ticketsCache); - } catch (err) { - res.status(400).send(err); - } -}); +*/ // Gets info on a given Orchestrator const parseOrchestrator = async function (reqAddr) { @@ -720,6 +716,20 @@ apiRouter.post("/getOrchestrator", async (req, res) => { } }); +// Returns entire orch info cache +apiRouter.get("/getAllOrchInfo", async (req, res) => { + try { + res.send(orchestratorCache); + } catch (err) { + res.status(400).send(err); + } +}); + +/* + +THEGRAPH - DELEGATOR + +*/ // Gets info on a given Delegator const parseDelegator = async function (reqAddr) { @@ -824,6 +834,61 @@ apiRouter.post("/getOrchestratorByDelegator", async (req, res) => { } }); +// Returns entire delegator info cache +apiRouter.get("/getAllDelInfo", async (req, res) => { + try { + res.send(delegatorCache); + } catch (err) { + res.status(400).send(err); + } +}); + +/* + +PROMETHEUS - GRAFANA + +*/ + +// Export livepeer and eth coin prices and L1 Eth gas price +apiRouter.get("/grafana", async (req, res) => { + try { + const now = new Date().getTime(); + // Update blockchain data if the cached data has expired + if (now - arbGet > CONF_TIMEOUT_ALCHEMY) { + await parseEthBlockchain(); + arbGet = now; + } + // Update coin prices once their data has expired + if (now - cmcPriceGet > CONF_TIMEOUT_CMC) { + await parseCmc(); + cmcPriceGet = now; + } + res.send({ + timestamp: now, + cmcTime: cmcPriceGet, + blockchainTime: arbGet, + l1GasFeeInGwei: l1Gwei, + l2GasFeeInGwei: l2Gwei, + ethPriceInDollar: ethPrice, + lptPriceInDollar: lptPrice, + redeemRewardCostL1, + redeemRewardCostL2, + claimTicketCostL1, + claimTicketCostL2, + withdrawFeeCostL1, + withdrawFeeCostL2, + stakeFeeCostL1, + stakeFeeCostL2, + commissionFeeCostL1, + commissionFeeCostL2, + serviceUriFeeCostL1, + serviceUriFeeCostL2, + quotes: cmcQuotes + }); + } catch (err) { + res.status(400).send(err); + } +}); // Export livepeer and eth coin prices and L1 Eth gas price apiRouter.get("/prometheus/:orchAddr", async (req, res) => { @@ -976,6 +1041,13 @@ apiRouter.get("/prometheus/:orchAddr", async (req, res) => { } }); + +/* + +ENS DATA + +*/ + const getEnsDomain = async function (addr) { const now = new Date().getTime(); let wasInCache = false; @@ -1093,17 +1165,18 @@ apiRouter.get("/getEnsInfo", async (req, res) => { }); +/* + +3BOX DATA + +*/ + const getThreeBoxInfo = async function (addr) { const now = new Date().getTime(); - let wasInCache = false; // See if it is cached for (const thisAddr of threeboxCache) { if (thisAddr.address === addr) { - // Check timeout - if (now - thisAddr.timestamp < CONF_TIMEOUT_ENS_INFO) { - return thisAddr; - } - wasInCache = true; + return thisAddr; } } // Else get it and cache it @@ -1124,18 +1197,8 @@ const getThreeBoxInfo = async function (addr) { image: data.image, timestamp: now } - if (wasInCache) { - for (var idx = 0; idx < threeboxCache.length; idx++) { - if (threeboxCache[idx].address == addr) { - console.log("Updating outdated 3box info " + threeBoxObj.address + " @ " + threeBoxObj.timestamp); - threeboxCache[idx] = threeBoxObj; - break; - } - } - } else { - console.log("Caching new 3box info " + threeBoxObj.address + " @ " + threeBoxObj.timestamp); - threeboxCache.push(threeBoxObj); - } + console.log("Caching new 3box info " + threeBoxObj.address + " @ " + threeBoxObj.timestamp); + threeboxCache.push(threeBoxObj); } catch (error) { console.error(error.message); }; @@ -1163,6 +1226,11 @@ apiRouter.get("/getAllThreeBox", async (req, res) => { } }); +/* + +LEADERBOARD TEST SCORES + +*/ const zeroPad = (num, places) => String(num).padStart(places, '0') const getScoreAtMonthYear = async function (month, year) { @@ -1251,23 +1319,12 @@ apiRouter.get("/getAllOrchScores", async (req, res) => { } }); -// Returns entire orch info cache -apiRouter.get("/getAllOrchInfo", async (req, res) => { - try { - res.send(orchestratorCache); - } catch (err) { - res.status(400).send(err); - } -}); -// Returns entire delegator info cache -apiRouter.get("/getAllDelInfo", async (req, res) => { - try { - res.send(delegatorCache); - } catch (err) { - res.status(400).send(err); - } -}); +/* + +RESERVED + +*/ diff --git a/src/actions/livepeer.js b/src/actions/livepeer.js index d7ea4ce..f149a37 100644 --- a/src/actions/livepeer.js +++ b/src/actions/livepeer.js @@ -124,8 +124,6 @@ export const getEvents = () => async dispatch => { let eventContainsUnbond = false; let eventContainsRebond = false; let eventContainsTransferBond = false; - let eventContainsTranscoderUpdate = false; - let eventContainsEarningsClaimed = false; let eventContainsReward = false; // Temp vars for the current Event we are processing let tmpAmount = 0; @@ -256,8 +254,6 @@ export const getEvents = () => async dispatch => { eventContainsUnbond = false; eventContainsRebond = false; eventContainsTransferBond = false; - eventContainsTranscoderUpdate = false; - eventContainsEarningsClaimed = false; eventContainsReward = false; txCounter++; currentTx = eventObj.transactionHash; @@ -318,7 +314,6 @@ export const getEvents = () => async dispatch => { } // Always split off TranscoderUpdate as a separate Update Event else if (eventObj.name === "TranscoderUpdate") { - eventContainsTranscoderUpdate = true; const amount1 = parseFloat(eventObj.data.rewardCut) / 10000; const amount2 = 100 - (eventObj.data.feeShare / 10000); const subtext = "changed commission"; @@ -344,7 +339,6 @@ export const getEvents = () => async dispatch => { } // Always split off EarningsClaimed as a separate Claim Event else if (eventObj.name === "EarningsClaimed") { - eventContainsEarningsClaimed = true; const amount1 = parseFloat(eventObj.data.rewards) / 1000000000000000000; const amount2 = parseFloat(eventObj.data.fees) / 1000000000000000000; if (amount1 < thresholdStaking && amount2 < thresholdFees) { diff --git a/src/components/scoreViewer.js b/src/components/scoreViewer.js index 9522dd4..4adfe38 100644 --- a/src/components/scoreViewer.js +++ b/src/components/scoreViewer.js @@ -1,8 +1,6 @@ import React from "react"; const ScoreView = (obj) => { - - console.log(obj.score); return (
diff --git a/src/pages/home.js b/src/pages/home.js index 8c07684..9a2c6f8 100644 --- a/src/pages/home.js +++ b/src/pages/home.js @@ -58,7 +58,7 @@ const Home = (obj) => {