mirror of
https://github.com/stronk-dev/LivepeerEvents.git
synced 2025-07-05 10:45:10 +02:00
Display rounds in the event viewer
This commit is contained in:
parent
fe2e75bb41
commit
2fb915fee4
557
backend/src/abi/RoundsManagerTarget.json
Normal file
557
backend/src/abi/RoundsManagerTarget.json
Normal file
File diff suppressed because one or more lines are too long
@ -5,51 +5,63 @@ const RoundSchema = new mongoose.Schema({
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
lengthBlocks: {
|
||||
transactionHash: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
blockNumber: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
blockTime: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
lengthBlocks: {
|
||||
type: Number,
|
||||
required: false
|
||||
},
|
||||
startBlock: {
|
||||
type: Number,
|
||||
required: true
|
||||
required: false
|
||||
},
|
||||
endBlock: {
|
||||
type: Number,
|
||||
required: true
|
||||
required: false
|
||||
},
|
||||
mintableTokens: {
|
||||
type: Number,
|
||||
required: true
|
||||
required: false
|
||||
},
|
||||
volumeEth: {
|
||||
type: Number,
|
||||
required: true
|
||||
required: false
|
||||
},
|
||||
volumeUsd: {
|
||||
type: Number,
|
||||
required: true
|
||||
required: false
|
||||
},
|
||||
totalActiveStake: {
|
||||
type: Number,
|
||||
required: true
|
||||
required: false
|
||||
},
|
||||
totalSupply: {
|
||||
type: Number,
|
||||
required: true
|
||||
required: false
|
||||
},
|
||||
participationRate: {
|
||||
type: Number,
|
||||
required: true
|
||||
required: false
|
||||
},
|
||||
movedStake: {
|
||||
type: Number,
|
||||
required: true
|
||||
required: false
|
||||
},
|
||||
newStake: {
|
||||
type: Number,
|
||||
required: true
|
||||
required: false
|
||||
}
|
||||
}, { timestamps: false });
|
||||
|
||||
const Round = mongoose.model('RoundSchema', RoundSchema);
|
||||
const Round = mongoose.model('Round', RoundSchema);
|
||||
export default Round;
|
@ -79,6 +79,10 @@ let TicketBrokerTargetJson;
|
||||
let TicketBrokerTargetAbi;
|
||||
let TicketBrokerTargetAddr;
|
||||
let ticketBrokerContract;
|
||||
let RoundsManagerTargetJson;
|
||||
let RoundsManagerTargetAbi;
|
||||
let RoundsManagerTargetAddr;
|
||||
let roundsManagerContract;
|
||||
if (!CONF_SIMPLE_MODE) {
|
||||
console.log("Loading contracts for smart contract events");
|
||||
// Listen for events on the bonding manager contract
|
||||
@ -91,6 +95,11 @@ if (!CONF_SIMPLE_MODE) {
|
||||
TicketBrokerTargetAbi = JSON.parse(TicketBrokerTargetJson);
|
||||
TicketBrokerTargetAddr = "0xa8bB618B1520E284046F3dFc448851A1Ff26e41B";
|
||||
ticketBrokerContract = new web3layer2.eth.Contract(TicketBrokerTargetAbi.abi, TicketBrokerTargetAddr);
|
||||
// Listen for events on the rounds manager contract
|
||||
RoundsManagerTargetJson = fs.readFileSync('src/abi/RoundsManagerTarget.json');
|
||||
RoundsManagerTargetAbi = JSON.parse(RoundsManagerTargetJson);
|
||||
RoundsManagerTargetAddr = "0xdd6f56DcC28D3F5f27084381fE8Df634985cc39f";
|
||||
roundsManagerContract = new web3layer2.eth.Contract(RoundsManagerTargetAbi.abi, RoundsManagerTargetAddr);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -155,11 +164,14 @@ let startedInitSync = false;
|
||||
let isSyncing = false;
|
||||
let isEventSyncing = false;
|
||||
let isTicketSyncing = false;
|
||||
let isRoundSyncing = false;
|
||||
|
||||
let eventsCache = [];
|
||||
let latestBlockInChain = 0;
|
||||
let latestL1Block = 0;
|
||||
let lastBlockEvents = 0;
|
||||
let lastBlockTickets = 0;
|
||||
let lastBlockRounds = 0;
|
||||
let ticketsCache = [];
|
||||
|
||||
let alreadyHasAnyRefresh = {};
|
||||
@ -1439,10 +1451,10 @@ Mutates the Event in the database to contain the round number
|
||||
|
||||
let roundCache = [];
|
||||
|
||||
const getRoundInfo = async function (blockNumber) {
|
||||
const getRoundInfo = async function (roundNumber) {
|
||||
// Get round info from gql
|
||||
const roundQuery = gql`{
|
||||
rounds(where: {startBlock_lte: "${blockNumber}"}) {
|
||||
rounds(where: {id: "${roundNumber}"}) {
|
||||
id
|
||||
length
|
||||
startBlock
|
||||
@ -1460,56 +1472,97 @@ const getRoundInfo = async function (blockNumber) {
|
||||
`;
|
||||
const roundObj = await request("https://api.thegraph.com/subgraphs/name/livepeer/arbitrum-one", roundQuery);
|
||||
// Not found
|
||||
if (!roundObj) {
|
||||
console.log("No round found at block " + blockNumber);
|
||||
if (!roundObj || !roundObj.rounds || !roundObj.rounds.length) {
|
||||
console.log("No round found with number " + roundNumber);
|
||||
return {};
|
||||
}
|
||||
console.log("This functions is not implemented yet. Logging element to console...")
|
||||
console.log(roundObj);
|
||||
// TODO filter out down to 1 round
|
||||
return roundObj;
|
||||
const thisRoundObj = roundObj.rounds[0];
|
||||
// Update cache
|
||||
roundCache.push(roundObj);
|
||||
// Only save if the endBlock is elapsed
|
||||
if (latestBlockInChain > roundObj.endBlock) {
|
||||
const data = {
|
||||
number: roundObj.number,
|
||||
var wasCached = false;
|
||||
for (var idx = 0; idx < roundCache.length; idx++) {
|
||||
if (roundCache[idx].number == roundNumber){
|
||||
wasCached = true;
|
||||
roundCache[idx].lengthBlocks = thisRoundObj.lengthBlocks;
|
||||
roundCache[idx].startBlock = thisRoundObj.startBlock;
|
||||
roundCache[idx].endBlock = thisRoundObj.endBlock;
|
||||
roundCache[idx].mintableTokens = thisRoundObj.mintableTokens;
|
||||
roundCache[idx].volumeEth = thisRoundObj.volumeETH;
|
||||
roundCache[idx].volumeUsd = thisRoundObj.volumeUSD;
|
||||
roundCache[idx].totalActiveStake = thisRoundObj.totalActiveStake;
|
||||
roundCache[idx].totalSupply = thisRoundObj.totalSupply;
|
||||
roundCache[idx].participationRate = thisRoundObj.participationRate;
|
||||
roundCache[idx].movedStake = thisRoundObj.movedStake;
|
||||
roundCache[idx].newStake = thisRoundObj.newStake;
|
||||
}
|
||||
}
|
||||
// FindAndUpdate
|
||||
if (!CONF_DISABLE_DB) {
|
||||
// Update DB entry
|
||||
const doc = await MonthlyStat.findOneAndUpdate({
|
||||
number: roundNumber
|
||||
}, {
|
||||
lengthBlocks: roundObj.lengthBlocks,
|
||||
startBlock: roundObj.startBlock,
|
||||
endBlock: roundObj.endBlock,
|
||||
mintableTokens: roundObj.mintableTokens,
|
||||
volumeEth: roundObj.volumeEth,
|
||||
volumeUsd: roundObj.volumeUsd,
|
||||
volumeEth: roundObj.volumeETH,
|
||||
volumeUsd: roundObj.volumeUSD,
|
||||
totalActiveStake: roundObj.totalActiveStake,
|
||||
totalSupply: roundObj.totalSupply,
|
||||
participationRate: roundObj.participationRate,
|
||||
movedStake: roundObj.movedStake,
|
||||
newStake: roundObj.newStake
|
||||
}
|
||||
if (!CONF_DISABLE_DB) {
|
||||
// TODO only create if nonexistent (find and update with upsert or something)
|
||||
const dbObj = new Round(data);
|
||||
await dbObj.save();
|
||||
}, {
|
||||
new: true
|
||||
});
|
||||
if (!wasCached){
|
||||
roundCache.push({
|
||||
number: doc.number,
|
||||
transactionHash: doc.transactionHash,
|
||||
blockNumber: doc.blockNumber,
|
||||
lengthBlocks: doc.lengthBlocks,
|
||||
startBlock: doc.startBlock,
|
||||
endBlock: doc.endBlock,
|
||||
mintableTokens: doc.mintableTokens,
|
||||
volumeETH: doc.volumeETH,
|
||||
volumeUSD: doc.volumeUSD,
|
||||
totalActiveStake: doc.totalActiveStake,
|
||||
totalSupply: doc.totalSupply,
|
||||
participationRate: doc.participationRate,
|
||||
movedStake: doc.movedStake,
|
||||
newStake: doc.newStake
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
apiRouter.post("/getRoundAtBlock", async (req, res) => {
|
||||
apiRouter.post("/getRoundInfo", async (req, res) => {
|
||||
try {
|
||||
const { blockNumber } = req.body;
|
||||
if (blockNumber) {
|
||||
console.log("Getting round info for block " + blockNumber);
|
||||
const now = new Date().getTime();
|
||||
const { roundNumber } = req.body;
|
||||
if (roundNumber) {
|
||||
console.log("Getting round info for round " + roundNumber);
|
||||
// See if it is cached
|
||||
for (const thisRound of roundCache) {
|
||||
if (thisRound.startBlock <= blockNumber && thisRound.endBlock >= blockNumber) {
|
||||
res.send(thisRound);
|
||||
return;
|
||||
if (thisRound.round == roundNumber) {
|
||||
// Check if it contains one of the detailed fields
|
||||
if (thisRound.endBlock) {
|
||||
// Check timeout if the round has not elapsed
|
||||
if (thisRound.endBlock >= latestL1Block
|
||||
&& (now - thisRound.blockTime) > 1800000) {
|
||||
console.log('Round should update round details');
|
||||
break;
|
||||
}
|
||||
res.send(thisRound);
|
||||
return;
|
||||
} else {
|
||||
console.log('Round should init round details');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Get block info from thegraph
|
||||
console.log("Getting round info for block " + blockNumber);
|
||||
const thisRoundInfo = await getRoundInfo(blockNumber);
|
||||
const thisRoundInfo = await getRoundInfo(roundNumber);
|
||||
res.send(thisRoundInfo);
|
||||
return;
|
||||
}
|
||||
@ -1539,7 +1592,7 @@ let hasError = false;
|
||||
|
||||
// Syncs events database
|
||||
const syncEvents = function (toBlock) {
|
||||
console.log("Starting sync process for Bonding Manager events to block " + toBlock);
|
||||
console.log("Starting sync process for Bonding Manager events for blocks " + lastBlockEvents + "->" + toBlock);
|
||||
isEventSyncing = true;
|
||||
let lastTxSynced = 0;
|
||||
// Then do a sync from last found until latest known
|
||||
@ -1601,7 +1654,7 @@ const syncEvents = function (toBlock) {
|
||||
}
|
||||
// Syncs tickets database
|
||||
const syncTickets = function (toBlock) {
|
||||
console.log("Starting sync process for Ticket Broker events to block " + toBlock);
|
||||
console.log("Starting sync process for Ticket Broker events for blocks " + lastBlockTickets + "->" + toBlock);
|
||||
isTicketSyncing = true;
|
||||
// Then do a sync from last found until latest known
|
||||
ticketBrokerContract.getPastEvents("allEvents", { fromBlock: lastBlockTickets + 1, toBlock: toBlock }, async (error, events) => {
|
||||
@ -1650,6 +1703,58 @@ const syncTickets = function (toBlock) {
|
||||
isTicketSyncing = false;
|
||||
});
|
||||
}
|
||||
// Syncs rounds database
|
||||
const syncRounds = function (toBlock) {
|
||||
console.log("Starting sync process for Rounds Manager events for blocks " + lastBlockRounds + "->" + toBlock);
|
||||
isRoundSyncing = true;
|
||||
// Then do a sync from last found until latest known
|
||||
roundsManagerContract.getPastEvents("allEvents", { fromBlock: lastBlockRounds + 1, toBlock: toBlock }, async (error, events) => {
|
||||
try {
|
||||
if (error) {
|
||||
throw error
|
||||
}
|
||||
let size = events.length;
|
||||
console.log("Parsing " + size + " rounds");
|
||||
if (!size) {
|
||||
if (toBlock == 'latest') {
|
||||
lastBlockRounds = latestBlockInChain;
|
||||
} else {
|
||||
lastBlockRounds = toBlock;
|
||||
}
|
||||
}
|
||||
for (const event of events) {
|
||||
if (event.blockNumber > lastBlockRounds) {
|
||||
lastBlockRounds = event.blockNumber;
|
||||
}
|
||||
// Only parse initRound events
|
||||
if (event.event != 'NewRound') {
|
||||
console.log('Skipping Round Event of type ' + event.event);
|
||||
continue;
|
||||
}
|
||||
const thisBlock = await getBlock(event.blockNumber);
|
||||
const eventObj = {
|
||||
number: event.returnValues.round,
|
||||
transactionHash: event.transactionHash,
|
||||
blockNumber: thisBlock.number,
|
||||
blockTime: thisBlock.timestamp
|
||||
}
|
||||
// Cache & store
|
||||
if (!CONF_DISABLE_DB) {
|
||||
const dbObj = new Round(eventObj);
|
||||
await dbObj.save();
|
||||
}
|
||||
roundCache.push(eventObj);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.log("FATAL ERROR: ", err);
|
||||
hasError = true;
|
||||
isRoundSyncing = false;
|
||||
return;
|
||||
}
|
||||
isRoundSyncing = false;
|
||||
});
|
||||
}
|
||||
|
||||
// Retrieves stuff from DB on first boot
|
||||
const initSync = async function () {
|
||||
@ -1863,6 +1968,9 @@ const initSync = async function () {
|
||||
// Get all round info
|
||||
roundCache = await Round.find({}, {
|
||||
number: 1,
|
||||
transactionHash: 1,
|
||||
blockNumber: 1,
|
||||
blockTime: 1,
|
||||
lengthBlocks: 1,
|
||||
startBlock: 1,
|
||||
endBlock: 1,
|
||||
@ -1876,6 +1984,15 @@ const initSync = async function () {
|
||||
newStake: 1,
|
||||
_id: 0
|
||||
})
|
||||
console.log("Retrieved existing rounds of size " + roundCache.length);
|
||||
// Then determine latest block number parsed based on collection
|
||||
for (var idx = 0; idx < roundCache.length; idx++) {
|
||||
const thisRound = roundCache[idx];
|
||||
if (thisRound.blockNumber > lastBlockRounds) {
|
||||
lastBlockRounds = thisRound.blockNumber;
|
||||
}
|
||||
}
|
||||
console.log("Latest Round block parsed is " + lastBlockRounds);
|
||||
}
|
||||
|
||||
let cycle = 0;
|
||||
@ -1889,8 +2006,13 @@ const handleSync = async function () {
|
||||
cycle++;
|
||||
console.log('Starting new sync cycle #' + cycle);
|
||||
isSyncing = true;
|
||||
// Get latest block in chain
|
||||
const latestBlock = await web3layer2.eth.getBlockNumber();
|
||||
// Get latest blocks in chain
|
||||
var latestBlock = await web3layer1.eth.getBlockNumber();
|
||||
if (latestBlock > latestL1Block) {
|
||||
latestL1Block = latestBlock;
|
||||
console.log("Latest L1 Eth block changed to " + latestL1Block);
|
||||
}
|
||||
latestBlock = await web3layer2.eth.getBlockNumber();
|
||||
if (latestBlock > latestBlockInChain) {
|
||||
latestBlockInChain = latestBlock;
|
||||
console.log("Latest L2 Eth block changed to " + latestBlockInChain);
|
||||
@ -1904,6 +2026,7 @@ const handleSync = async function () {
|
||||
}
|
||||
console.log("Needs to sync " + (latestBlockInChain - lastBlockEvents) + " blocks for Events sync");
|
||||
console.log("Needs to sync " + (latestBlockInChain - lastBlockTickets) + " blocks for Tickets sync");
|
||||
console.log("Needs to sync " + (latestBlockInChain - lastBlockRounds) + " blocks for Rounds sync");
|
||||
// Batch requests when sync is large, mark if we are going to reach latestBlockInChain in this round
|
||||
let getFinalTickets = false;
|
||||
let toTickets = 'latest';
|
||||
@ -1919,11 +2042,20 @@ const handleSync = async function () {
|
||||
} else {
|
||||
getFinalEvents = true;
|
||||
}
|
||||
let getFinalRounds = false;
|
||||
let toRounds = 'latest';
|
||||
if (latestBlock - lastBlockRounds > 1000000) {
|
||||
toRounds = lastBlockRounds + 1000000;
|
||||
} else {
|
||||
getFinalRounds = true;
|
||||
}
|
||||
// Start initial sync for this sync round
|
||||
syncTickets(toTickets);
|
||||
syncEvents(toEvents);
|
||||
syncRounds(toRounds);
|
||||
// Then loop until we have reached the last known block
|
||||
while (isEventSyncing || isTicketSyncing || !getFinalTickets || !getFinalEvents) {
|
||||
while (isEventSyncing || isTicketSyncing || isRoundSyncing
|
||||
|| !getFinalTickets || !getFinalEvents || !getFinalRounds) {
|
||||
await sleep(500);
|
||||
if (hasError) {
|
||||
throw ("Error while syncing");
|
||||
@ -1952,6 +2084,18 @@ const handleSync = async function () {
|
||||
}
|
||||
syncTickets(toTickets);
|
||||
}
|
||||
if (isRoundSyncing) {
|
||||
console.log("Parsed " + lastBlockRounds + " out of " + latestBlockInChain + " blocks for Round sync");
|
||||
} else if (!getFinalRounds) {
|
||||
// Start next batch for tickets
|
||||
toRounds = 'latest';
|
||||
if (latestBlock - lastBlockRounds > 1000000) {
|
||||
toRounds = lastBlockRounds + 1000000;
|
||||
} else {
|
||||
getFinalRounds = true;
|
||||
}
|
||||
syncRounds(toRounds);
|
||||
}
|
||||
}
|
||||
isSyncing = false;
|
||||
setTimeout(() => {
|
||||
@ -1965,6 +2109,7 @@ const handleSync = async function () {
|
||||
console.log("latestBlockInChain " + latestBlockInChain);
|
||||
console.log("lastBlockEvents " + lastBlockEvents);
|
||||
console.log("lastBlockTickets " + lastBlockTickets);
|
||||
console.log("lastBlockRounds " + lastBlockRounds);
|
||||
isSyncing = false;
|
||||
setTimeout(() => {
|
||||
handleSync();
|
||||
@ -2305,7 +2450,7 @@ const mutateDynamicStatsFromDB = async function (orchestratorObj) {
|
||||
latestCommission: 1,
|
||||
latestTotalStake: 1
|
||||
});
|
||||
if (!doc){
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
let oldFeeCommission = -1;
|
||||
|
25
dumps/rounds.json
Normal file
25
dumps/rounds.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
address: '0xdd6f56DcC28D3F5f27084381fE8Df634985cc39f',
|
||||
blockHash: '0x733753ab3a1ecf3edbbebc075f85827b259295b3a7443d45c633b1b6c3672eef',
|
||||
blockNumber: 8761883,
|
||||
logIndex: 1,
|
||||
removed: false,
|
||||
transactionHash: '0xd8609ae0a52e0bb8a1eda28634e5a804607eb76a156e9e30682dd1ff748a107a',
|
||||
transactionIndex: 0,
|
||||
id: 'log_cbc32efd',
|
||||
returnValues: Result {
|
||||
'0': '2513',
|
||||
'1': '0x2f180b9a2c759abc9c77372f2a339b16191fe782304318e818b0b3ed0706a216',
|
||||
round: '2513',
|
||||
blockHash: '0x2f180b9a2c759abc9c77372f2a339b16191fe782304318e818b0b3ed0706a216'
|
||||
},
|
||||
event: 'NewRound',
|
||||
signature: '0x22f2fc17c5daf07db2379b3a03a8ef20a183f761097a58fce219c8a14619e786',
|
||||
raw: {
|
||||
data: '0x2f180b9a2c759abc9c77372f2a339b16191fe782304318e818b0b3ed0706a216',
|
||||
topics: [
|
||||
'0x22f2fc17c5daf07db2379b3a03a8ef20a183f761097a58fce219c8a14619e786',
|
||||
'0x00000000000000000000000000000000000000000000000000000000000009d1'
|
||||
]
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ export const SET_ALL_ACTIVATE_EVENTS = "SET_ALL_ACTIVATE_EVENTS";
|
||||
export const SET_ALL_UNBOND_EVENTS = "SET_ALL_UNBOND_EVENTS";
|
||||
export const SET_ALL_STAKE_EVENTS = "SET_ALL_STAKE_EVENTS";
|
||||
export const SET_ALL_ROUNDS = "SET_ALL_ROUNDS";
|
||||
export const SET_ADD_ROUNDS = "SET_ADD_ROUNDS";
|
||||
export const SET_ROUND = "SET_ROUND";
|
||||
|
||||
const setQuotes = message => ({
|
||||
type: RECEIVE_QUOTES, message
|
||||
@ -131,8 +131,8 @@ const setAllRounds = message => ({
|
||||
type: SET_ALL_ROUNDS, message
|
||||
});
|
||||
|
||||
const setAddRound = message => ({
|
||||
type: SET_ADD_ROUNDS, message
|
||||
const populateRound = message => ({
|
||||
type: SET_ROUND, message
|
||||
});
|
||||
|
||||
export const getQuotes = () => async dispatch => {
|
||||
@ -434,10 +434,10 @@ export const getAllRounds = () => async dispatch => {
|
||||
return dispatch(receiveErrors(data));
|
||||
};
|
||||
|
||||
export const getRoundAtBlock = (addr) => async dispatch => {
|
||||
const response = await apiUtil.getRoundAtBlock(addr);
|
||||
export const getRoundInfo = (round) => async dispatch => {
|
||||
const response = await apiUtil.getRoundInfo(round);
|
||||
const data = await response.json();
|
||||
if (response.ok) {
|
||||
return dispatch(setAddRound(data));
|
||||
return dispatch(populateRound(data));
|
||||
}
|
||||
};
|
||||
|
@ -1,49 +1,12 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
getRoundAtBlock
|
||||
} from "../actions/livepeer";
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import React from "react";
|
||||
|
||||
const Block = (obj) => {
|
||||
const dispatch = useDispatch();
|
||||
const livepeer = useSelector((state) => state.livepeerstate);
|
||||
const [roundInfo, setRoundInfo] = useState(null);
|
||||
const [hasRefreshed, setRefresh] = useState(false);
|
||||
|
||||
// useEffect(() => {
|
||||
// let thisInfo = null;
|
||||
// for (const round of livepeer.rounds) {
|
||||
// if (round.startBlock <= obj.block && round.endBlock >= obj.block) {
|
||||
// thisInfo = round;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// // If it was not cached at all
|
||||
// if (thisInfo == null && !hasRefreshed) {
|
||||
// console.log("Refresh due to non-existing round containing this block");
|
||||
// setRefresh(true);
|
||||
// dispatch(getRoundAtBlock(obj.block));
|
||||
// }
|
||||
// if (thisInfo && thisInfo != roundInfo) {
|
||||
// console.log("Setting block info obj");
|
||||
// setRoundInfo(thisInfo);
|
||||
// }
|
||||
// }, [livepeer.rounds]);
|
||||
|
||||
const thisEpoch = obj.time;
|
||||
var dateObj = new Date(0);
|
||||
dateObj.setUTCSeconds(thisEpoch);
|
||||
const thisLocalDate = dateObj.toLocaleString();
|
||||
const thisOffset = (-dateObj.getTimezoneOffset() / 60);
|
||||
|
||||
// Get round info
|
||||
let thisRoundInfo;
|
||||
if (roundInfo) {
|
||||
thisRoundInfo = <p style={{ overflowWrap: 'break-word' }}>
|
||||
Round {thisRoundInfo.number}
|
||||
</p>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rowAlignLeft" style={{ margin: 0, marginTop: '1em', width: '100%' }}>
|
||||
<a className="selectOrch" style={{ cursor: 'alias', margin: 0 }} target="_blank" rel="noopener noreferrer" href={obj.url}>
|
||||
@ -52,7 +15,6 @@ const Block = (obj) => {
|
||||
<a className="selectOrch" style={{ cursor: 'alias', margin: 0 }} target="_blank" rel="noopener noreferrer" href={"https://arbiscan.io/block/" + obj.block}>
|
||||
<h3 style={{ padding: '0.2em', cursor: 'alias' }}>🔗</h3>
|
||||
</a>
|
||||
{thisRoundInfo}
|
||||
<span className="rowAlignRight darkText mobileSmallerFont" style={{ margin: 0 }}>
|
||||
<p style={{ overflowWrap: 'break-word' }}>
|
||||
📅 {thisLocalDate}
|
||||
|
110
src/components/RoundViewer.js
Normal file
110
src/components/RoundViewer.js
Normal file
@ -0,0 +1,110 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
getRoundInfo
|
||||
} from "../actions/livepeer";
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { Popover } from '@mantine/core';
|
||||
import { getOrchestratorByDelegator } from "../util/livepeer";
|
||||
|
||||
const Round = (obj) => {
|
||||
const [opened, setOpened] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const [hasRefreshed, setRefresh] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// If it was not cached at all
|
||||
if (obj.round && obj.round.number && !obj.round.endBlock && !hasRefreshed) {
|
||||
console.log("Pulling round info for round " + obj.round.number);
|
||||
setRefresh(true);
|
||||
dispatch(getRoundInfo(obj.round.number));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const thisEpoch = obj.time;
|
||||
var dateObj = new Date(0);
|
||||
dateObj.setUTCSeconds(thisEpoch);
|
||||
const thisLocalDate = dateObj.toLocaleString();
|
||||
const thisOffset = (-dateObj.getTimezoneOffset() / 60);
|
||||
|
||||
return (
|
||||
<div className="row" style={{ paddingLeft: '1em', paddingRight: '1em' }}>
|
||||
<div className="rowAlignLeft" style={{ margin: 0, marginTop: '1em', width: '100%' }}>
|
||||
<a className="selectOrch" style={{ cursor: 'alias', margin: 0 }} target="_blank" rel="noopener noreferrer" href={"https://arbiscan.io/block/" + obj.round.blockNumber}>
|
||||
<h3 style={{ padding: '0.2em', cursor: 'alias' }}>🔗</h3>
|
||||
</a>
|
||||
<Popover className="strokeSmollLeft" style={{ cursor: 'pointer', marginTop: '0.2em', marginBottom: '0.2em' }}
|
||||
opened={opened}
|
||||
onClose={() => setOpened(false)}
|
||||
target={
|
||||
<p className="darkText" style={{ overflowWrap: 'break-word' }} onClick={() => setOpened((o) => !o)}>
|
||||
Round {obj.round.number}
|
||||
</p>
|
||||
}
|
||||
width={260}
|
||||
position="right"
|
||||
withArrow
|
||||
>
|
||||
<div className="strokeSmollLeft">
|
||||
<div className="row">
|
||||
<p className="darkText" style={{ overflowWrap: 'break-word' }}>
|
||||
Round {obj.round.number}
|
||||
</p>
|
||||
</div>
|
||||
{
|
||||
obj.round.mintableTokens ?
|
||||
<div className="row">
|
||||
<p className="darkText" style={{ overflowWrap: 'break-word' }}>
|
||||
Has {obj.round.mintableTokens.toFixed(2)} mintable tokens
|
||||
</p>
|
||||
</div> : null
|
||||
}
|
||||
{
|
||||
obj.round.volumeEth && obj.round.volumeUsd ?
|
||||
<div className="row">
|
||||
<p className="darkText" style={{ overflowWrap: 'break-word' }}>
|
||||
A volume of {obj.round.volumeEth.toFixed(2)} Eth ({obj.round.volumeUsd.toFixed(2)}$)
|
||||
</p>
|
||||
</div> : null
|
||||
}
|
||||
{
|
||||
obj.round.totalSupply && obj.round.totalActiveStake ?
|
||||
<div className="row">
|
||||
<p className="darkText" style={{ overflowWrap: 'break-word' }}>
|
||||
A total supply of {obj.round.totalSupply.toFixed(2)} LPT, of which {obj.round.totalActiveStake.toFixed(2)} is staked ({(obj.round.participationRate * 100).toFixed(2)}%)
|
||||
</p>
|
||||
</div> : null
|
||||
}
|
||||
{
|
||||
obj.round.newStake ?
|
||||
<div className="row">
|
||||
<p className="darkText" style={{ overflowWrap: 'break-word' }}>
|
||||
{obj.round.newStake.toFixed(2)} LPT new stake
|
||||
</p>
|
||||
</div> : null
|
||||
}
|
||||
{
|
||||
obj.round.movedStake ?
|
||||
<div className="row">
|
||||
<p className="darkText" style={{ overflowWrap: 'break-word' }}>
|
||||
{obj.round.movedStake.toFixed(2)} LPT stake moved around
|
||||
</p>
|
||||
</div> : null
|
||||
}
|
||||
</div>
|
||||
|
||||
</Popover>
|
||||
<span className="rowAlignRight darkText mobileSmallerFont" style={{ margin: 0 }}>
|
||||
<p style={{ overflowWrap: 'break-word' }}>
|
||||
📅 {thisLocalDate}
|
||||
</p>
|
||||
{thisOffset != 0 ? <p className='darkTextSmoll' style={{ overflowWrap: 'break-word' }}>
|
||||
({thisOffset > 0 ? "+" : ""}{thisOffset})
|
||||
</p> : null
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Round;
|
@ -4,6 +4,7 @@ import ScrollContainer from 'react-indiana-drag-scroll';
|
||||
import { Pagination, Title } from "@mantine/core";
|
||||
import Filter from './filterComponent';
|
||||
import { Dialog, Stack, Button, Group } from '@mantine/core';
|
||||
import Round from "./RoundViewer";
|
||||
|
||||
const thresholdStaking = 0.001;
|
||||
const thresholdFees = 0.00009;
|
||||
@ -71,6 +72,7 @@ const EventViewer = (obj) => {
|
||||
let unbondEventsIdx = obj.unbondEvents.length - 1;
|
||||
let transferTicketEventsIdx = obj.transferTicketEvents.length - 1;
|
||||
let redeemTicketEventsIdx = obj.redeemTicketEvents.length - 1;
|
||||
let roundsIdx = obj.rounds.length - 1;
|
||||
|
||||
if (!filterActivated) {
|
||||
filtered += activateEventsIdx + 1;
|
||||
@ -119,7 +121,8 @@ const EventViewer = (obj) => {
|
||||
redeemTicketEventsIdx >= 0 ||
|
||||
activateEventsIdx >= 0 ||
|
||||
unbondEventsIdx >= 0 ||
|
||||
stakeEventsIdx >= 0) {
|
||||
stakeEventsIdx >= 0 ||
|
||||
roundsIdx >= 0) {
|
||||
|
||||
let latestTime = 0;
|
||||
let thisEvent;
|
||||
@ -214,6 +217,14 @@ const EventViewer = (obj) => {
|
||||
latestType = "redeemTicket"
|
||||
}
|
||||
}
|
||||
if (roundsIdx >= 0) {
|
||||
const thisObj = obj.rounds[roundsIdx];
|
||||
if (thisObj.blockTime > latestTime) {
|
||||
latestTime = thisObj.blockTime;
|
||||
thisEvent = thisObj;
|
||||
latestType = "round"
|
||||
}
|
||||
}
|
||||
|
||||
// Decrement IDX and check filter
|
||||
if (latestType == "update") {
|
||||
@ -412,6 +423,15 @@ const EventViewer = (obj) => {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (latestType == "round") {
|
||||
roundsIdx--;
|
||||
eventList.push(<Round
|
||||
key={thisEvent.transactionHash + unfiltered + roundsIdx}
|
||||
seed={thisEvent.transactionHash + unfiltered + roundsIdx}
|
||||
round={thisEvent}
|
||||
time={thisEvent.blockTime}
|
||||
/>);
|
||||
continue;
|
||||
} else {
|
||||
console.log("bork");
|
||||
}
|
||||
|
@ -118,6 +118,7 @@ const Livepeer = (obj) => {
|
||||
unbondEvents={livepeer.unbondEvents}
|
||||
stakeEvents={livepeer.stakeEvents}
|
||||
monthlyStats={livepeer.monthlyStats}
|
||||
rounds={livepeer.rounds}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
getAllClaimEvents, getAllWithdrawStakeEvents, getAllWithdrawFeesEvents,
|
||||
getAllTransferTicketEvents, getAllRedeemTicketEvents, getAllActivateEvents,
|
||||
getAllUnbondEvents, getAllStakeEvents, getAllCommissions, getAllTotalStakes,
|
||||
hasAnyRefresh
|
||||
hasAnyRefresh, getAllRounds
|
||||
} from "../actions/livepeer";
|
||||
import { login } from "../actions/session";
|
||||
|
||||
@ -91,6 +91,8 @@ const Startup = (obj) => {
|
||||
dispatch(getAllActivateEvents(false));
|
||||
dispatch(getAllUnbondEvents(false));
|
||||
dispatch(getAllStakeEvents(false));
|
||||
// TODO make it part of the hasAnyUpdate check
|
||||
dispatch(getAllRounds());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ import {
|
||||
SET_ALL_UNBOND_EVENTS,
|
||||
SET_ALL_STAKE_EVENTS,
|
||||
SET_ALL_ROUNDS,
|
||||
SET_ADD_ROUNDS
|
||||
SET_ROUND
|
||||
} from "../../actions/livepeer";
|
||||
|
||||
export default (state = {
|
||||
@ -137,11 +137,23 @@ export default (state = {
|
||||
return { ...state, stakeEvents: message };
|
||||
case SET_ALL_ROUNDS:
|
||||
return { ...state, rounds: message };
|
||||
case SET_ADD_ROUNDS:
|
||||
return {
|
||||
...state,
|
||||
rounds: [...state.rounds, message]
|
||||
};
|
||||
case SET_ROUND:
|
||||
// Check to see if it is already cached
|
||||
if (state.rounds) {
|
||||
return {
|
||||
...state,
|
||||
contents: state.rounds.map(
|
||||
(content) => {
|
||||
if (content.number == message.number) {
|
||||
return message;
|
||||
} else {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
return { ...state };
|
||||
default:
|
||||
return { ...state };
|
||||
}
|
||||
|
@ -273,10 +273,10 @@ export const getAllRounds = () => (
|
||||
})
|
||||
);
|
||||
|
||||
export const getRoundAtBlock = (blockNumber) => (
|
||||
fetch("api/livepeer/getRoundAtBlock", {
|
||||
export const getRoundInfo = (roundNumber) => (
|
||||
fetch("api/livepeer/getRoundInfo", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ blockNumber }),
|
||||
body: JSON.stringify({ roundNumber }),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user