First version of frontend using the new preparsed events and tickets

This commit is contained in:
Marco van Dijk 2022-04-21 13:25:37 +02:00
parent 95be1e5614
commit f6238bfc32
11 changed files with 910 additions and 692 deletions

View File

@ -13,6 +13,14 @@ const ClaimEventSchema = new mongoose.Schema({
type: Number, type: Number,
required: true required: true
}, },
startRound: {
type: Number,
required: true
},
endRound: {
type: Number,
required: true
},
transactionHash: { transactionHash: {
type: String, type: String,
required: true required: true

View File

@ -172,6 +172,94 @@ let stakeEventCache = [];
let monthlyStatCache = []; let monthlyStatCache = [];
apiRouter.get("/getAllMonthlyStats", async (req, res) => {
try {
res.send(monthlyStatCache);
} catch (err) {
res.status(400).send(err);
}
});
apiRouter.get("/getAllUpdateEvents", async (req, res) => {
try {
res.send(updateEventCache);
} catch (err) {
res.status(400).send(err);
}
});
apiRouter.get("/getAllRewardEvents", async (req, res) => {
try {
res.send(rewardEventCache);
} catch (err) {
res.status(400).send(err);
}
});
apiRouter.get("/getAllClaimEvents", async (req, res) => {
try {
res.send(claimEventCache);
} catch (err) {
res.status(400).send(err);
}
});
apiRouter.get("/getAllWithdrawStakeEvents", async (req, res) => {
try {
res.send(withdrawStakeEventCache);
} catch (err) {
res.status(400).send(err);
}
});
apiRouter.get("/getAllWithdrawFeesEvents", async (req, res) => {
try {
res.send(withdrawFeesEventCache);
} catch (err) {
res.status(400).send(err);
}
});
apiRouter.get("/getAllTransferTicketEvents", async (req, res) => {
try {
res.send(transferTicketEventCache);
} catch (err) {
res.status(400).send(err);
}
});
apiRouter.get("/getAllRedeemTicketEvents", async (req, res) => {
try {
res.send(redeemTicketEventCache);
} catch (err) {
res.status(400).send(err);
}
});
apiRouter.get("/getAllActivateEvents", async (req, res) => {
try {
res.send(activateEventCache);
} catch (err) {
res.status(400).send(err);
}
});
apiRouter.get("/getAllUnbondEvents", async (req, res) => {
try {
res.send(unbondEventCache);
} catch (err) {
res.status(400).send(err);
}
});
apiRouter.get("/getAllStakeEvents", async (req, res) => {
try {
res.send(stakeEventCache);
} catch (err) {
res.status(400).send(err);
}
});
/* /*
SMART CONTRACT EVENTS - MONTHLY STATS UPDATING SMART CONTRACT EVENTS - MONTHLY STATS UPDATING
@ -712,6 +800,8 @@ const parseAnyEvent = async function (thisEvent) {
address: thisEvent.data.delegator.toLowerCase(), address: thisEvent.data.delegator.toLowerCase(),
fees: parseFloat(thisEvent.data.rewards) / 1000000000000000000, fees: parseFloat(thisEvent.data.rewards) / 1000000000000000000,
rewards: parseFloat(thisEvent.data.fees) / 1000000000000000000, rewards: parseFloat(thisEvent.data.fees) / 1000000000000000000,
startRound: parseInt(thisEvent.data.startRound),
endRound: parseInt(thisEvent.data.endRound),
transactionHash: thisEvent.transactionHash, transactionHash: thisEvent.transactionHash,
blockNumber: thisEvent.blockNumber, blockNumber: thisEvent.blockNumber,
blockTime: thisEvent.blockTime blockTime: thisEvent.blockTime
@ -988,8 +1078,11 @@ 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 to block " + toBlock);
isEventSyncing = true; isEventSyncing = true;
let lastTxSynced = 0; let lastTxSynced = 0;
if (lastBlockTickets != 'latest'){
lastBlockTickets += 1;
}
// Then do a sync from last found until latest known // Then do a sync from last found until latest known
bondingManagerContract.getPastEvents("allEvents", { fromBlock: lastBlockEvents + 1, toBlock: toBlock }, async (error, events) => { bondingManagerContract.getPastEvents("allEvents", { fromBlock: lastBlockEvents, toBlock: toBlock }, async (error, events) => {
try { try {
if (error) { if (error) {
throw error throw error
@ -1042,8 +1135,11 @@ const syncEvents = function (toBlock) {
const syncTickets = function (toBlock) { 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 to block " + toBlock);
isTicketSyncing = true; isTicketSyncing = true;
if (lastBlockTickets != 'latest'){
lastBlockTickets += 1;
}
// Then do a sync from last found until latest known // Then do a sync from last found until latest known
ticketBrokerContract.getPastEvents("allEvents", { fromBlock: lastBlockTickets + 1, toBlock: toBlock }, async (error, events) => { ticketBrokerContract.getPastEvents("allEvents", { fromBlock: lastBlockTickets, toBlock: toBlock }, async (error, events) => {
try { try {
if (error) { if (error) {
throw error throw error

View File

@ -3,7 +3,7 @@ import Home from './pages/home.js';
import Startup from './pages/loadingScreen.js'; import Startup from './pages/loadingScreen.js';
import Grafana from './pages/grafana.js'; import Grafana from './pages/grafana.js';
import Livepeer from './pages/livepeer.js'; import Livepeer from './pages/livepeer.js';
import Tickets from './pages/tickets.js'; import Stats from './pages/stats.js';
import { import {
BrowserRouter as Router, BrowserRouter as Router,
@ -17,7 +17,8 @@ export default function App() {
<Router> <Router>
<Routes> <Routes>
<Route exact path='/livepeer' element={<Livepeer />} /> <Route exact path='/livepeer' element={<Livepeer />} />
<Route exact path='/tickets' element={<Tickets />} /> <Route exact path='/tickets' element={<Stats />} />
<Route exact path='/stats' element={<Stats />} />
<Route exact path='/orchestrator' element={<Grafana />} /> <Route exact path='/orchestrator' element={<Grafana />} />
<Route path='/' element={<Home />} /> <Route path='/' element={<Home />} />
</Routes> </Routes>

View File

@ -1,21 +1,5 @@
import * as apiUtil from "../util/livepeer"; import * as apiUtil from "../util/livepeer";
import { receiveErrors } from "./error"; import { receiveErrors } from "./error";
import React from "react";
import Ticket from "../components/TicketViewer";
const claimColour = "rgba(25, 158, 29, 0.3)";
const stakeColour = "rgba(25, 158, 147, 0.3)";
const ticketRedeemColour = "rgba(25, 98, 158, 0.3)";
const rewardColour = "rgba(25, 27, 158, 0.3)";
const unbondColour = "rgba(105, 25, 158, 0.3)";
const updateColour = "rgba(158, 25, 52, 0.3)";
const withdrawStakeColour = "rgba(158, 98, 25, 0.3)";
const activationColour = "rgba(154, 158, 25, 0.3)";
const ticketTransferColour = "rgba(88, 91, 42, 0.3)";
const thresholdStaking = 0.001;
const thresholdFees = 0.00009;
export const RECEIVE_QUOTES = "RECEIVE_QUOTES"; export const RECEIVE_QUOTES = "RECEIVE_QUOTES";
export const RECEIVE_BLOCKCHAIN_DATA = "RECEIVE_BLOCKCHAIN_DATA"; export const RECEIVE_BLOCKCHAIN_DATA = "RECEIVE_BLOCKCHAIN_DATA";
@ -32,6 +16,17 @@ export const SET_ALL_THREEBOX_INFO = "SET_ALL_THREEBOX_INFO";
export const SET_ALL_ORCH_SCORES = "SET_ALL_ORCH_SCORES"; export const SET_ALL_ORCH_SCORES = "SET_ALL_ORCH_SCORES";
export const SET_ALL_ORCH_INFO = "SET_ALL_ORCH_INFO"; export const SET_ALL_ORCH_INFO = "SET_ALL_ORCH_INFO";
export const SET_ALL_DEL_INFO = "SET_ALL_DEL_INFO"; export const SET_ALL_DEL_INFO = "SET_ALL_DEL_INFO";
export const SET_ALL_MONTHLY_STATS = "SET_ALL_MONTHLY_STATS";
export const SET_ALL_UPDATE_EVENTS = "SET_ALL_UPDATE_EVENTS";
export const SET_ALL_REWARD_EVENTS = "SET_ALL_REWARD_EVENTS";
export const SET_ALL_CLAIM_EVENTS = "SET_ALL_CLAIM_EVENTS";
export const SET_ALL_WITHDRAW_STAKE_EVENTS = "SET_ALL_WITHDRAW_STAKE_EVENTS";
export const SET_ALL_WITHDRAW_FEES_EVENTS = "SET_ALL_WITHDRAW_FEES_EVENTS";
export const SET_ALL_TRANSFER_TICKET_EVENTS = "SET_ALL_TRANSFER_TICKET_EVENTS";
export const SET_ALL_REDEEM_TICKET_EVENTS = "SET_ALL_REDEEM_TICKET_EVENTS";
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";
const setQuotes = message => ({ const setQuotes = message => ({
type: RECEIVE_QUOTES, message type: RECEIVE_QUOTES, message
@ -80,7 +75,49 @@ const setAllDelInfo = message => ({
type: SET_ALL_DEL_INFO, message type: SET_ALL_DEL_INFO, message
}); });
const setAllMonthlyStats = message => ({
type: SET_ALL_MONTHLY_STATS, message
});
const setAllUpdateEvents = message => ({
type: SET_ALL_UPDATE_EVENTS, message
});
const setAllRewardEvents = message => ({
type: SET_ALL_REWARD_EVENTS, message
});
const setAllClaimEvents = message => ({
type: SET_ALL_CLAIM_EVENTS, message
});
const setAllWithdrawStakeEvents = message => ({
type: SET_ALL_WITHDRAW_STAKE_EVENTS, message
});
const setAllWithdrawFeesEvents = message => ({
type: SET_ALL_WITHDRAW_FEES_EVENTS, message
});
const setAllTransferTicketEvents = message => ({
type: SET_ALL_TRANSFER_TICKET_EVENTS, message
});
const setAllRedeemTicketEvents = message => ({
type: SET_ALL_REDEEM_TICKET_EVENTS, message
});
const setAllActivateEvents = message => ({
type: SET_ALL_ACTIVATE_EVENTS, message
});
const setAllUnbondEvents = message => ({
type: SET_ALL_UNBOND_EVENTS, message
});
const setAllStakeEvents = message => ({
type: SET_ALL_STAKE_EVENTS, message
});
export const getQuotes = () => async dispatch => { export const getQuotes = () => async dispatch => {
const response = await apiUtil.getQuotes(); const response = await apiUtil.getQuotes();
@ -100,426 +137,6 @@ export const getBlockchainData = () => async dispatch => {
return dispatch(receiveErrors(data)); return dispatch(receiveErrors(data));
}; };
export const getEvents = () => async dispatch => {
const response = await apiUtil.getEvents();
const data = await response.json();
// Combine raw list of events into a list of useful Events
if (response.ok) {
let finalEventList = [];
// Current transaction we are processing
let txCounter = 0;
let currentTx = "";
let currentUrl = "";
let currentBlock = 0;
let currentTime = 0;
// Current Event we are processing
let eventType = ""; // Named type: Withdraw, Update, Claim, Reward, Activate, Unbond, Stake
let eventDescription = ""; // Descriptive text, also containing Amount, AmountOther and When
let eventCaller = ""; // address we will display on the left side
let eventFrom = ""; // address from which X gets taken
let eventTo = ""; // address to which X gets sent
let eventColour = "rgba(255,255,255,0.5)";
let eventContainsBond = false;
let eventContainsTranscoderActivated = false;
let eventContainsUnbond = false;
let eventContainsRebond = false;
let eventContainsTransferBond = false;
let eventContainsReward = false;
// Temp vars for the current Event we are processing
let tmpAmount = 0;
let tmpAmountOther = "";
let tmpWhen = "";
// Group Events into transactions. Always reset when the transactionID changes
{
for (const eventObj of data) {
if (currentTx === "") {
currentTx = eventObj.transactionHash;
currentUrl = eventObj.transactionUrl;
currentBlock = eventObj.blockNumber;
currentTime = eventObj.blockTime;
}
// New transaction found
if (currentTx !== eventObj.transactionHash) {
// Unbond <> TransferBond <> (eventContainsEarningsClaimed) <> Rebond => Stake Event
if (eventContainsUnbond && eventContainsTransferBond && eventContainsRebond) {
eventType = "Stake";
eventColour = stakeColour;
}
// (Bond <>) TranscoderActivated => Activate Event
else if (eventContainsTranscoderActivated) {
eventType = "Activate";
eventColour = activationColour;
eventFrom = "";
eventTo = "";
if (eventContainsBond) {
const subtext = "activated";
const descriptions = [
tmpAmount.toFixed(2) + " LPT stake",
"round " + tmpWhen
]
eventDescription = <Ticket seed={currentTx+"-"+txCounter} icon={"🚀"} subtext={subtext} descriptions={descriptions} />
} else {
const subtext = "reactivated";
const descriptions = [
"round " + tmpWhen
]
eventDescription = <Ticket seed={currentTx+"-"+txCounter} icon={"🚀"} subtext={subtext} descriptions={descriptions} />
}
}
// Lone Unbond => Unbond Event
else if (eventContainsUnbond) {
eventType = "Unbond";
eventColour = unbondColour;
const subtext = "unbonded";
const descriptions = [
tmpAmount.toFixed(2) + " LPT",
"round " + tmpWhen
]
eventDescription =
<Ticket seed={currentTx+"-"+txCounter} icon={"❌"} subtext={subtext} descriptions={descriptions} />
}
// Lone Bond => Stake Event
else if (eventContainsBond) {
eventType = "Stake";
eventColour = stakeColour;
}
// Lone Rebond => Stake Event
else if (eventContainsRebond) {
eventType = "Stake";
eventColour = stakeColour;
const subtext = "changed stake";
const descriptions = [
tmpAmount.toFixed(2) + " LPT"
]
eventDescription =
<Ticket seed={currentTx+"-"+txCounter} icon={"⌛"} subtext={subtext} descriptions={descriptions} />
}
// Fill description of Stake Event if it wasn't set yet
if (eventType === "Stake" && eventDescription === "") {
if (eventFrom === "0x0000000000000000000000000000000000000000") {
const subtext = "is now staking";
const descriptions = [
tmpAmount.toFixed(2) + " LPT"
]
eventDescription =
<Ticket seed={currentTx+"-"+txCounter} icon={"⌛"} subtext={subtext} descriptions={descriptions} />
} else if (eventFrom === eventTo) {
eventFrom = "";
const subtext = "changed stake";
const descriptions = [
tmpAmount.toFixed(2) + " LPT"
]
eventDescription =
<Ticket seed={currentTx+"-"+txCounter} icon={"⌛"} subtext={subtext} descriptions={descriptions} />
} else {
const subtext = "moved stake";
const descriptions = [
tmpAmount.toFixed(2) + " LPT"
]
eventDescription =
<Ticket seed={currentTx+"-"+txCounter} icon={"⌛"} subtext={subtext} descriptions={descriptions} />
}
}
// If we have an eventType at this point, we have a completed Event from the previous transaction
if (eventType !== "") {
finalEventList.push({
eventType,
eventDescription,
eventCaller,
eventFrom,
eventTo,
eventColour,
transactionHash: currentTx,
transactionUrl: currentUrl,
transactionBlock: currentBlock,
transactionTime: currentTime,
eventValue: (tmpAmount > tmpAmountOther) ? tmpAmount : tmpAmountOther
});
}
// Reset event data
eventType = "";
eventDescription = "";
eventCaller = "";
eventFrom = "";
eventTo = "";
eventColour = "";
tmpAmount = 0;
tmpAmountOther = "";
tmpWhen = "";
eventContainsBond = false;
eventContainsTranscoderActivated = false;
eventContainsUnbond = false;
eventContainsRebond = false;
eventContainsTransferBond = false;
eventContainsReward = false;
txCounter++;
currentTx = eventObj.transactionHash;
currentUrl = eventObj.transactionUrl;
currentBlock = eventObj.blockNumber;
currentTime = eventObj.blockTime;
}
// Always split off WithdrawStake as a separate Withdraw Event
if (eventObj.name === "WithdrawStake") {
const amount = parseFloat(eventObj.data.amount) / 1000000000000000000;
if (amount < thresholdFees) {
continue;
}
const subtext = "withdrew stake";
const descriptions = [
amount.toFixed(2) + " LPT",
"round " + eventObj.data.withdrawRound
]
const txt =
<Ticket seed={currentTx+"-"+txCounter} icon={"🏦"} subtext={subtext} descriptions={descriptions} />
finalEventList.push({
eventType: "Withdraw",
eventDescription: txt,
eventCaller: eventObj.data.delegator.toLowerCase(),
eventFrom: "",
eventTo: "",
eventColour: withdrawStakeColour,
transactionHash: currentTx,
transactionUrl: currentUrl,
transactionBlock: currentBlock,
transactionTime: currentTime,
eventValue: amount
});
} else if (eventObj.name === "WithdrawFees") {
const amount = parseFloat(eventObj.data.amount) / 1000000000000000000;
if (amount < thresholdFees) {
continue;
}
const subtext = "withdrew fees";
const descriptions = [
amount.toFixed(4) + " Eth"
]
const txt =
<Ticket seed={currentTx+"-"+txCounter} icon={"🏦"} subtext={subtext} descriptions={descriptions} />
finalEventList.push({
eventType: "Withdraw",
eventDescription: txt,
eventCaller: eventObj.data.delegator.toLowerCase(),
eventFrom: eventObj.data.delegator.toLowerCase(),
eventTo: "",
eventColour: withdrawStakeColour,
transactionHash: currentTx,
transactionUrl: currentUrl,
transactionBlock: currentBlock,
transactionTime: currentTime,
eventValue: amount
});
}
// Always split off TranscoderUpdate as a separate Update Event
else if (eventObj.name === "TranscoderUpdate") {
const amount1 = parseFloat(eventObj.data.rewardCut) / 10000;
const amount2 = 100 - (eventObj.data.feeShare / 10000);
const subtext = "changed commission";
const descriptions = [
amount1.toFixed(2) + "% on staking rewards",
amount2.toFixed(2) + "% on transcoding fees"
]
const txt =
<Ticket seed={currentTx+"-"+txCounter} icon={"🔄"} subtext={subtext} descriptions={descriptions} />
finalEventList.push({
eventType: "Update",
eventDescription: txt,
eventCaller: eventObj.data.transcoder.toLowerCase(),
eventFrom: "",
eventTo: "",
eventColour: updateColour,
transactionHash: currentTx,
transactionUrl: currentUrl,
transactionBlock: currentBlock,
transactionTime: currentTime,
eventValue: (amount1 > amount2) ? amount1 : amount2
});
}
// Always split off EarningsClaimed as a separate Claim Event
else if (eventObj.name === "EarningsClaimed") {
const amount1 = parseFloat(eventObj.data.rewards) / 1000000000000000000;
const amount2 = parseFloat(eventObj.data.fees) / 1000000000000000000;
if (amount1 < thresholdStaking && amount2 < thresholdFees) {
continue;
}
const subtext = "delegator claimed";
const descriptions = [
(eventObj.data.endRound - eventObj.data.startRound + 1) + " rounds",
"+" + amount1.toFixed(2) + " LPT rewards",
"+" + amount2.toFixed(4) + " Eth fees"
]
let txt =
<Ticket seed={currentTx+"-"+txCounter} icon={"💰"} subtext={subtext} descriptions={descriptions} />
finalEventList.push({
eventType: "Claim",
eventDescription: txt,
eventCaller: eventObj.data.delegator.toLowerCase(),
eventFrom: eventObj.data.delegate.toLowerCase(),
eventTo: "",
eventColour: claimColour,
transactionHash: currentTx,
transactionUrl: currentUrl,
transactionBlock: currentBlock,
transactionTime: currentTime,
eventValue: (amount1 > amount2) ? amount1 : amount2
});
}
// Always split off Reward as a separate Reward Event
else if (eventObj.name === "Reward") {
eventContainsReward = true;
const amount1 = parseFloat(eventObj.data.amount) / 1000000000000000000;
const subtext = "called reward";
const descriptions = [
"+" + amount1.toFixed(2) + " LPT" + (Math.floor(amount1) == 69 ? "... Nice!" : "")
]
const txt =
<Ticket seed={currentTx+"-"+txCounter} icon={"💸"} subtext={subtext} descriptions={descriptions} />
finalEventList.push({
eventType: "Reward",
eventDescription: txt,
eventCaller: eventObj.data.transcoder.toLowerCase(),
eventFrom: "",
eventTo: "",
eventColour: rewardColour,
transactionHash: currentTx,
transactionUrl: currentUrl,
transactionBlock: currentBlock,
transactionTime: currentTime,
eventValue: amount1
});
}
// Extract useful info from other types of Event
else if (eventObj.name === "Bond") {
eventContainsBond = true;
eventCaller = eventObj.data.delegator.toLowerCase();
eventFrom = eventObj.data.oldDelegate.toLowerCase();
eventTo = eventObj.data.newDelegate.toLowerCase();
tmpAmount = parseFloat(eventObj.data.bondedAmount) / 1000000000000000000;
tmpAmountOther = parseFloat(eventObj.data.additionalAmount) / 1000000000000000000;
}
else if (eventObj.name === "TranscoderActivated") {
eventContainsTranscoderActivated = true;
eventCaller = eventObj.data.transcoder.toLowerCase();
tmpWhen = eventObj.data.activationRound;
}
else if (eventObj.name === "Unbond") {
eventContainsUnbond = true;
eventCaller = eventObj.data.delegator.toLowerCase();
eventFrom = eventObj.data.delegate.toLowerCase();
tmpAmount = parseFloat(eventObj.data.amount) / 1000000000000000000;
tmpWhen = eventObj.data.withdrawRound;
}
else if (eventObj.name === "Rebond") {
eventContainsRebond = true;
eventCaller = eventObj.data.delegator.toLowerCase();
eventTo = eventObj.data.delegate.toLowerCase();
tmpAmount = parseFloat(eventObj.data.amount) / 1000000000000000000;
}
else if (eventObj.name === "TransferBond") {
eventContainsTransferBond = true;
if (!eventContainsUnbond) {
eventFrom = eventObj.data.oldDelegator.toLowerCase();
}
if (!eventContainsRebond) {
eventTo = eventObj.data.newDelegator.toLowerCase();
}
tmpAmount = parseFloat(eventObj.data.amount) / 1000000000000000000;
} else {
console.log("UNIMPLEMENTED: " + eventObj.name);
}
}
}
// NOTE: We are throwing away the very oldest Event now, which should be fine.
// We can fix this once above wall of text becomes a separate function
return dispatch(setEvents(finalEventList));
}
return dispatch(receiveErrors(data));
};
export const getTickets = () => async dispatch => {
const response = await apiUtil.getTickets();
const data = await response.json();
// Combine raw list of events into a list of useful Events
if (response.ok) {
let finalTicketList = [];
let winningTicketList = [];
// Current transaction we are processing
let txCounter = 0;
let currentTx = "";
let currentUrl = "";
let currentBlock = 0;
let currentTime = 0;
// Parse Tickets
{
for (const eventObj of data) {
if (currentTx === "") {
currentTx = eventObj.transactionHash;
currentUrl = eventObj.transactionUrl;
currentBlock = eventObj.blockNumber;
currentTime = eventObj.blockTime;
}
// New transaction found
if (currentTx !== eventObj.transactionHash) {
// Reset event data
txCounter++;
currentTx = eventObj.transactionHash;
currentUrl = eventObj.transactionUrl;
currentBlock = eventObj.blockNumber;
currentTime = eventObj.blockTime;
}
// Always split off WithdrawStake as a separate Withdraw Event
if (eventObj.name === "WinningTicketRedeemed") {
const amount = parseFloat(eventObj.data.faceValue) / 1000000000000000000;
const subtext = "winning ticket";
const descriptions = [
"+" + amount.toFixed(4) + " Eth"
]
const txt =
<Ticket seed={currentTx+descriptions+currentTime} icon={"🎟️"} subtext={subtext} descriptions={descriptions} />
finalTicketList.push({
eventType: "RedeemTicket",
eventDescription: txt,
eventCaller: eventObj.data.recipient.toLowerCase(),
eventFrom: "",//eventObj.data.sender.toLowerCase(),
eventTo: "",//eventObj.data.recipient.toLowerCase(),
eventColour: ticketRedeemColour,
transactionHash: currentTx,
transactionUrl: currentUrl,
transactionBlock: currentBlock,
transactionTime: currentTime,
eventValue: amount
});
} else if (eventObj.name === "WinningTicketTransfer") {
const amount = parseFloat(eventObj.data.amount) / 1000000000000000000;
const txt = " broadcaster payed out " + amount.toFixed(4) + " Eth";
winningTicketList.push({
eventType: "TransferTicket",
eventDescription: txt,
eventCaller: "",
eventFrom: eventObj.data.sender.toLowerCase(),
eventTo: eventObj.data.recipient.toLowerCase(),
eventColour: ticketTransferColour,
transactionHash: currentTx,
transactionUrl: currentUrl,
transactionBlock: currentBlock,
transactionTime: currentTime,
eventValue: amount
});
} else {
console.log("UNIMPLEMENTED: " + eventObj.name);
}
}
}
// NOTE: We are throwing away the very oldest Ticket now, which should be fine.
// We can fix this once above wall of text becomes a separate function
dispatch(setWinningTickets(winningTicketList));
return dispatch(setTickets(finalTicketList));
}
return dispatch(receiveErrors(data));
};
export const getCurrentOrchestratorInfo = () => async dispatch => { export const getCurrentOrchestratorInfo = () => async dispatch => {
const response = await apiUtil.getCurrentOrchestratorInfo(); const response = await apiUtil.getCurrentOrchestratorInfo();
const data = await response.json(); const data = await response.json();
@ -632,3 +249,102 @@ export const getAllDelInfo = () => async dispatch => {
} }
return dispatch(receiveErrors(data)); return dispatch(receiveErrors(data));
}; };
export const getAllMonthlyStats = () => async dispatch => {
const response = await apiUtil.getAllMonthlyStats();
const data = await response.json();
if (response.ok) {
return dispatch(setAllMonthlyStats(data));
}
return dispatch(receiveErrors(data));
};
export const getAllUpdateEvents = () => async dispatch => {
const response = await apiUtil.getAllUpdateEvents();
const data = await response.json();
if (response.ok) {
return dispatch(setAllUpdateEvents(data));
}
return dispatch(receiveErrors(data));
};
export const getAllRewardEvents = () => async dispatch => {
const response = await apiUtil.getAllRewardEvents();
const data = await response.json();
if (response.ok) {
return dispatch(setAllRewardEvents(data));
}
return dispatch(receiveErrors(data));
};
export const getAllClaimEvents = () => async dispatch => {
const response = await apiUtil.getAllClaimEvents();
const data = await response.json();
if (response.ok) {
return dispatch(setAllClaimEvents(data));
}
return dispatch(receiveErrors(data));
};
export const getAllWithdrawStakeEvents = () => async dispatch => {
const response = await apiUtil.getAllWithdrawStakeEvents();
const data = await response.json();
if (response.ok) {
return dispatch(setAllWithdrawStakeEvents(data));
}
return dispatch(receiveErrors(data));
};
export const getAllWithdrawFeesEvents = () => async dispatch => {
const response = await apiUtil.getAllWithdrawFeesEvents();
const data = await response.json();
if (response.ok) {
return dispatch(setAllWithdrawFeesEvents(data));
}
return dispatch(receiveErrors(data));
};
export const getAllTransferTicketEvents = () => async dispatch => {
const response = await apiUtil.getAllTransferTicketEvents();
const data = await response.json();
if (response.ok) {
return dispatch(setAllTransferTicketEvents(data));
}
return dispatch(receiveErrors(data));
};
export const getAllRedeemTicketEvents = () => async dispatch => {
const response = await apiUtil.getAllRedeemTicketEvents();
const data = await response.json();
if (response.ok) {
return dispatch(setAllRedeemTicketEvents(data));
}
return dispatch(receiveErrors(data));
};
export const getAllActivateEvents = () => async dispatch => {
const response = await apiUtil.getAllActivateEvents();
const data = await response.json();
if (response.ok) {
return dispatch(setAllActivateEvents(data));
}
return dispatch(receiveErrors(data));
};
export const getAllUnbondEvents = () => async dispatch => {
const response = await apiUtil.getAllUnbondEvents();
const data = await response.json();
if (response.ok) {
return dispatch(setAllUnbondEvents(data));
}
return dispatch(receiveErrors(data));
};
export const getAllStakeEvents = () => async dispatch => {
const response = await apiUtil.getAllStakeEvents();
const data = await response.json();
if (response.ok) {
return dispatch(setAllStakeEvents(data));
}
return dispatch(receiveErrors(data));
};

View File

@ -1,13 +1,119 @@
import React from "react"; import React from "react";
import Block from "./BlockViewer"; import Block from "./BlockViewer";
import EventButtonAddress from "./eventButtonAddress"; import EventButtonAddress from "./eventButtonAddress";
import Ticket from "../components/TicketViewer";
const claimColour = "rgba(25, 158, 29, 0.4)";
const stakeColour = "rgba(25, 158, 147, 0.4)";
const ticketRedeemColour = "rgba(25, 98, 158, 0.4)";
const rewardColour = "rgba(25, 27, 158, 0.4)";
const unbondColour = "rgba(105, 25, 158, 0.4)";
const updateColour = "rgba(158, 25, 52, 0.4)";
const withdrawStakeColour = "rgba(158, 98, 25, 0.4)";
const activationColour = "rgba(154, 158, 25, 0.4)";
const ticketTransferColour = "rgba(88, 91, 42, 0.3)";
const greyColour = "rgba(122, 128, 127, 0.4)";
/// Displays a single event. Sets selected Orchestrator info in the redux store /// Displays a single event. Sets selected Orchestrator info in the redux store
const EventButton = (obj) => { const EventButton = (obj) => {
let eventTo; let eventTo = "";
let eventFrom; let eventFrom = "";
let eventCaller; let eventCaller = "";
let eventDescription = "";
let eventColour = greyColour;
if (obj.type == "update") {
eventCaller = obj.eventObj.address;
eventDescription = <Ticket seed={obj.seed + "-desc-"} icon={"🔄"} subtext={"changed commission"} descriptions={[
obj.eventObj.rewardCommission.toFixed(2) + "% on staking rewards",
obj.eventObj.feeCommission.toFixed(2) + "% on transcoding fees"
]} />
eventColour = updateColour;
} else if (obj.type == "reward") {
eventCaller = obj.eventObj.address;
eventDescription = <Ticket seed={obj.seed + "-desc-"} icon={"💸"} subtext={"called reward"} descriptions={[
"+" + obj.eventObj.amount.toFixed(2) + " LPT" + (Math.floor(obj.eventObj.amount) == 69 ? "... Nice!" : "")
]} />
eventColour = rewardColour;
} else if (obj.type == "claim") {
eventCaller = obj.eventObj.address;
eventDescription = <Ticket seed={obj.seed + "-desc-"} icon={"💰"} subtext={"delegator claimed"} descriptions={[
(eventObj.data.endRound - eventObj.data.startRound + 1) + " rounds",
"+" + obj.eventObj.rewards.toFixed(2) + " LPT rewards",
"+" + obj.eventObj.fees.toFixed(4) + " Eth fees"
]} />
eventColour = claimColour;
} else if (obj.type == "withdrawStake") {
eventCaller = obj.eventObj.address;
eventDescription = <Ticket seed={obj.seed + "-desc-"} icon={"🏦"} subtext={"withdrew stake"} descriptions={[
(eventObj.endRound - eventObj.startRound + 1) + " rounds",
"+" + obj.eventObj.rewards.toFixed(2) + " LPT rewards",
"+" + obj.eventObj.fees.toFixed(4) + " Eth fees"
]} />
eventColour = withdrawStakeColour;
} else if (obj.type == "withdrawFees") {
eventCaller = obj.eventObj.address;
eventDescription = <Ticket seed={obj.seed + "-desc-"} icon={"🏦"} subtext={"withdrew fees"} descriptions={[
"+" + obj.eventObj.amount.toFixed(4) + " Eth fees"
]} />
eventColour = withdrawStakeColour;
} else if (obj.type == "activate") {
eventCaller = obj.eventObj.address;
if (obj.eventObj.initialStake) {
eventDescription = <Ticket seed={obj.seed + "-desc-"} icon={"🚀"} subtext={"activated"} descriptions={[
obj.eventObj.initialStake.toFixed(2) + " LPT stake",
"round " + obj.eventObj.round
]} />
} else {
eventDescription = <Ticket seed={obj.seed + "-desc-"} icon={"🚀"} subtext={"reactivated"} descriptions={[
"round " + obj.eventObj.round
]} />
}
eventColour = activationColour;
} else if (obj.type == "unbond") {
eventCaller = obj.eventObj.address;
eventFrom = obj.eventObj.from;
eventDescription = <Ticket seed={obj.seed + "-desc-"} icon={"❌"} subtext={"unbond"} descriptions={[
obj.eventObj.stake.toFixed(2) + " LPT",
"round " + obj.eventObj.round
]} />
eventColour = unbondColour;
} else if (obj.type == "transferTicket") {
eventCaller = obj.eventObj.address;
eventTo = obj.eventObj.to;
eventDescription = <Ticket seed={obj.seed + "-desc-"} icon={"🎟️"} subtext={"sent winning ticket"} descriptions={[
"+" + obj.eventObj.amount.toFixed(4) + " Eth"
]} />
eventColour = ticketTransferColour;
} else if (obj.type == "redeemTicket") {
eventCaller = obj.eventObj.address;
eventDescription = <Ticket seed={obj.seed + "-desc-"} icon={"🎟️"} subtext={"redeemed winning ticket"} descriptions={[
"+" + obj.eventObj.amount.toFixed(4) + " Eth"
]} />
eventColour = ticketRedeemColour;
} else if (obj.type == "stake") {
eventCaller = obj.eventObj.address;
eventFrom = obj.eventObj.from;
eventTo = obj.eventObj.to;
if (eventFrom === "0x0000000000000000000000000000000000000000") {
eventFrom = "";
eventDescription = <Ticket seed={currentTx + "-" + txCounter} icon={"⌛"} subtext={"is now staking"} descriptions={[
obj.eventObj.stake.toFixed(2) + " LPT"
]} />
} else if (eventFrom === eventTo) {
eventFrom = "";
eventDescription = <Ticket seed={currentTx + "-" + txCounter} icon={"⌛"} subtext={"changed stake"} descriptions={[
obj.eventObj.stake.toFixed(2) + " LPT"
]} />
} else {
eventDescription = <Ticket seed={currentTx + "-" + txCounter} icon={"⌛"} subtext={"moved stake"} descriptions={[
obj.eventObj.stake.toFixed(2) + " LPT"
]} />
}
eventColour = stakeColour;
}
if (obj.eventObj.eventFrom === "0x0000000000000000000000000000000000000000") { if (obj.eventObj.eventFrom === "0x0000000000000000000000000000000000000000") {
obj.eventObj.eventFrom = ""; obj.eventObj.eventFrom = "";
} }
@ -31,21 +137,21 @@ const EventButton = (obj) => {
let blockNumber; let blockNumber;
if (obj.isFirstOfBlock) { if (obj.isFirstOfBlock) {
blockNumber = <Block block={obj.isFirstOfBlock} time={obj.time} url={obj.eventObj.transactionUrl} /> blockNumber = <Block block={obj.isFirstOfBlock} time={obj.time} url={"https://arbiscan.io/tx/" + obj.eventObj.transactionHash} />
} }
return ( return (
<div className="strokeSmollLeft" style={{ width: '100%', minWidth: '200px', maxWidth: '500px'}}> <div className="strokeSmollLeft" style={{ width: '100%', minWidth: '200px', maxWidth: '500px' }}>
{blockNumber} {blockNumber}
<div className="strokeSmollLeft" style={{ width: "100%", borderRadius: "1.2em", backgroundColor: obj.eventObj.eventColour, opacity: 0.9, border: '0.1em solid rgba(54, 46, 46, 0.1)', boxShadow: "4px 2px 3px 2px rgba(54, 46, 46, 0.1)" }}> <div className="strokeSmollLeft" style={{ width: "100%", borderRadius: "1.2em", backgroundColor: eventColour, opacity: 0.9, border: '0.1em solid rgba(54, 46, 46, 0.1)', boxShadow: "4px 2px 3px 2px rgba(54, 46, 46, 0.1)" }}>
<div className="halfVerticalDivider" /> <div className="halfVerticalDivider" />
<div className="rowAlignLeft"> <div className="rowAlignLeft">
{eventCaller} {eventCaller}
</div> </div>
<div className="halfVerticalDivider" /> <div className="halfVerticalDivider" />
<div className="row"> <div className="row">
<div className="row" style={{ justifyContent: 'space-between', alignItems: 'stretch', maxWidth: '61.8%', textAlign: 'justify', padding: '0.5em', backgroundColor: obj.eventObj.eventColour, border: '0.1em solid rgba(54, 46, 46, 0.1)' }}> <div className="row" style={{ justifyContent: 'space-between', alignItems: 'stretch', maxWidth: '61.8%', textAlign: 'justify', padding: '0.5em', backgroundColor: eventColour, border: '0.1em solid rgba(54, 46, 46, 0.1)' }}>
{obj.eventObj.eventDescription} {eventDescription}
</div> </div>
</div> </div>
<div className="halfVerticalDivider" /> <div className="halfVerticalDivider" />

View File

@ -2,8 +2,9 @@ import React, { useState, useRef } from "react";
import EventButton from "./eventButton"; import EventButton from "./eventButton";
import ScrollContainer from 'react-indiana-drag-scroll'; import ScrollContainer from 'react-indiana-drag-scroll';
import Filter from './filterComponent'; import Filter from './filterComponent';
import ReactPaginate from 'react-paginate';
/// A scrollable and filterable list of EventButtons const thresholdStaking = 0.001;
const thresholdFees = 0.00009;
const claimColour = "rgba(25, 158, 29, 0.4)"; const claimColour = "rgba(25, 158, 29, 0.4)";
const stakeColour = "rgba(25, 158, 147, 0.4)"; const stakeColour = "rgba(25, 158, 147, 0.4)";
@ -13,6 +14,7 @@ const unbondColour = "rgba(105, 25, 158, 0.4)";
const updateColour = "rgba(158, 25, 52, 0.4)"; const updateColour = "rgba(158, 25, 52, 0.4)";
const withdrawStakeColour = "rgba(158, 98, 25, 0.4)"; const withdrawStakeColour = "rgba(158, 98, 25, 0.4)";
const activationColour = "rgba(154, 158, 25, 0.4)"; const activationColour = "rgba(154, 158, 25, 0.4)";
const ticketTransferColour = "rgba(88, 91, 42, 0.3)";
const greyColour = "rgba(122, 128, 127, 0.4)"; const greyColour = "rgba(122, 128, 127, 0.4)";
const defaultIncrementMaxShown = 50; const defaultIncrementMaxShown = 50;
@ -65,123 +67,383 @@ const EventViewer = (obj) => {
let eventList = []; let eventList = [];
let thisEvent = {}; let thisEvent = {};
let eventIdx = obj.events.length - 1; let updateEventsIdx = obj.updateEvents.length - 1;
let ticketIdx = obj.tickets.length - 1; let rewardEventsIdx = obj.rewardEvents.length - 1;
while (eventIdx >= 0 || ticketIdx >= 0) { let claimEventsIdx = obj.claimEvents.length - 1;
const latestEvent = obj.events[eventIdx]; let withdrawStakeEventsIdx = obj.withdrawStakeEvents.length - 1;
let latestEventTime = 0; let withdrawFeesEventsIdx = obj.withdrawFeesEvents.length - 1;
if (eventIdx >= 0) { let activateEventsIdx = obj.activateEvents.length - 1;
latestEventTime = latestEvent.transactionTime; let stakeEventsIdx = obj.stakeEvents.length - 1;
let unbondEventsIdx = obj.unbondEvents.length - 1;
let transferTicketEventsIdx = obj.transferTicketEvents.length - 1;
let redeemTicketEventsIdx = obj.redeemTicketEvents.length - 1;
if (!filterActivated) {
filtered += activateEventsIdx + 1;
activateEventsIdx = -1;
}
if (!rewardActivated) {
filtered += rewardEventsIdx + 1;
rewardEventsIdx = -1;
}
if (!updateActivated) {
filtered += updateEventsIdx + 1;
updateEventsIdx = -1;
}
if (!withdrawActivated) {
filtered += withdrawStakeEventsIdx + 1;
filtered += withdrawFeesEventsIdx + 1;
withdrawStakeEventsIdx = -1;
withdrawFeesEventsIdx = -1;
}
if (!stakeActivated) {
filtered += stakeEventsIdx + 1;
stakeEventsIdx = -1;
}
if (!unbondActivated) {
filtered += unbondEventsIdx + 1;
unbondEventsIdx = -1;
}
if (!delegatorRewardActivated) {
filtered += rewardEventsIdx + 1;
rewardEventsIdx = -1;
}
if (!rewardActivated) {
filtered += claimEventsIdx + 1;
claimEventsIdx = -1;
}
if (!ticketRedemptionActivated) {
filtered += transferTicketEventsIdx + 1;
filtered += redeemTicketEventsIdx + 1;
transferTicketEventsIdx = -1;
redeemTicketEventsIdx = -1;
}
while (updateEventsIdx >= 0 ||
rewardEventsIdx >= 0 ||
claimEventsIdx >= 0 ||
withdrawStakeEventsIdx >= 0 ||
withdrawFeesEventsIdx >= 0 ||
transferTicketEventsIdx >= 0 ||
redeemTicketEventsIdx >= 0 ||
activateEventsIdx >= 0 ||
unbondEventsIdx >= 0 ||
stakeEventsIdx >= 0) {
let latestTime = 0;
let thisEvent;
let latestType;
// Find latest event of enabled lists
if (updateEventsIdx >= 0) {
const thisObj = obj.updateEvents[updateEventsIdx];
if (thisObj.blockTime > latestTime) {
latestTime = thisObj.blockTime;
thisEvent = thisObj;
latestType = "update"
}
} }
const latestTicket = obj.tickets[ticketIdx]; if (rewardEventsIdx >= 0) {
let latestTicketTime = 0; const thisObj = obj.rewardEvents[rewardEventsIdx];
if (ticketIdx >= 0) { if (thisObj.blockTime > latestTime) {
latestTicketTime = latestTicket.transactionTime; latestTime = thisObj.blockTime;
thisEvent = thisObj;
latestType = "reward"
}
} }
if (latestEventTime > latestTicketTime) { if (claimEventsIdx >= 0) {
thisEvent = latestEvent; const thisObj = obj.claimEvents[claimEventsIdx];
eventIdx -= 1; if (thisObj.blockTime > latestTime) {
} else if (latestTicketTime) { latestTime = thisObj.blockTime;
thisEvent = latestTicket; thisEvent = thisObj;
ticketIdx -= 1; latestType = "claim"
}
}
if (withdrawStakeEventsIdx >= 0) {
const thisObj = obj.withdrawStakeEvents[withdrawStakeEventsIdx];
if (thisObj.blockTime > latestTime) {
latestTime = thisObj.blockTime;
thisEvent = thisObj;
latestType = "withdrawStake"
}
}
if (withdrawFeesEventsIdx >= 0) {
const thisObj = obj.withdrawFeesEvents[withdrawFeesEventsIdx];
if (thisObj.blockTime > latestTime) {
latestTime = thisObj.blockTime;
thisEvent = thisObj;
latestType = "withdrawFees"
}
}
if (activateEventsIdx >= 0) {
const thisObj = obj.activateEvents[activateEventsIdx];
if (thisObj.blockTime > latestTime) {
latestTime = thisObj.blockTime;
thisEvent = thisObj;
latestType = "activate"
}
}
if (updateEventsIdx >= 0) {
const thisObj = obj.updateEvents[updateEventsIdx];
if (thisObj.blockTime > latestTime) {
latestTime = thisObj.blockTime;
thisEvent = thisObj;
latestType = "update"
}
}
if (stakeEventsIdx >= 0) {
const thisObj = obj.stakeEvents[stakeEventsIdx];
if (thisObj.blockTime > latestTime) {
latestTime = thisObj.blockTime;
thisEvent = thisObj;
latestType = "stake"
}
}
if (unbondEventsIdx >= 0) {
const thisObj = obj.unbondEvents[unbondEventsIdx];
if (thisObj.blockTime > latestTime) {
latestTime = thisObj.blockTime;
thisEvent = thisObj;
latestType = "unbond"
}
}
if (transferTicketEventsIdx >= 0) {
const thisObj = obj.transferTicketEvents[transferTicketEventsIdx];
if (thisObj.blockTime > latestTime) {
latestTime = thisObj.blockTime;
thisEvent = thisObj;
latestType = "transferTicket"
}
}
if (redeemTicketEventsIdx >= 0) {
const thisObj = obj.redeemTicketEvents[redeemTicketEventsIdx];
if (thisObj.blockTime > latestTime) {
latestTime = thisObj.blockTime;
thisEvent = thisObj;
latestType = "redeemTicket"
}
}
// Decrement IDX and check filter
if (latestType == "update") {
updateEventsIdx--;
// Filter name on from, to, caller
if (obj.searchTerm !== "") {
let isFiltered = true;
if (thisEvent.address.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false;
if (isFiltered) {
filtered++;
continue;
}
}
} else if (latestType == "reward") {
rewardEventsIdx--;
// Filter by minimum value
if (obj.amountFilter !== "") {
if (parseFloat(obj.amountFilter) > thisEvent.amount) {
filtered++;
continue;
}
}
// Filter name on from, to, caller
if (obj.searchTerm !== "") {
let isFiltered = true;
if (thisEvent.address.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false;
if (isFiltered) {
filtered++;
continue;
}
}
} else if (latestType == "claim") {
claimEventsIdx--;
// Filter by minimum value
if (obj.amountFilter !== "") {
if (parseFloat(obj.amountFilter) > thisEvent.fees ||
parseFloat(obj.amountFilter) > thisEvent.rewards) {
filtered++;
continue;
}
}
// Filter name on from, to, caller
if (obj.searchTerm !== "") {
let isFiltered = true;
if (thisEvent.address.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false;
if (isFiltered) {
filtered++;
continue;
}
}
} else if (latestType == "withdrawStake") {
withdrawStakeEventsIdx--;
// Filter by minimum value
if (obj.amountFilter !== "") {
if (parseFloat(obj.amountFilter) > thisEvent.amount) {
filtered++;
continue;
}
}
// Filter name on from, to, caller
if (obj.searchTerm !== "") {
let isFiltered = true;
if (thisEvent.address.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false;
if (isFiltered) {
filtered++;
continue;
}
}
} else if (latestType == "withdrawFees") {
withdrawFeesEventsIdx--;
// Filter by minimum value
if (obj.amountFilter !== "") {
if (parseFloat(obj.amountFilter) > thisEvent.amount) {
filtered++;
continue;
}
}
// Filter name on from, to, caller
if (obj.searchTerm !== "") {
let isFiltered = true;
if (thisEvent.address.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false;
if (isFiltered) {
filtered++;
continue;
}
}
} else if (latestType == "activate") {
activateEventsIdx--;
// Filter by minimum value
if (obj.amountFilter !== "") {
if (parseFloat(obj.amountFilter) > thisEvent.initialStake) {
filtered++;
continue;
}
}
// Filter name on from, to, caller
if (obj.searchTerm !== "") {
let isFiltered = true;
if (thisEvent.address.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false;
if (isFiltered) {
filtered++;
continue;
}
}
} else if (latestType == "update") {
updateEventsIdx--;
// Filter by minimum value
if (obj.amountFilter !== "") {
if (parseFloat(obj.amountFilter) > thisEvent.amount) {
filtered++;
continue;
}
}
// Filter name on from, to, caller
if (obj.searchTerm !== "") {
let isFiltered = true;
if (thisEvent.address.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false;
if (isFiltered) {
filtered++;
continue;
}
}
} else if (latestType == "stake") {
stakeEventsIdx--;
// Filter by minimum value
if (obj.amountFilter !== "") {
if (parseFloat(obj.amountFilter) > thisEvent.stake) {
filtered++;
continue;
}
}
// Filter name on from, to, caller
if (obj.searchTerm !== "") {
let isFiltered = true;
if (thisEvent.address.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false;
if (thisEvent.from && thisEvent.from.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false;
if (thisEvent.to.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false;
if (isFiltered) {
filtered++;
continue;
}
}
} else if (latestType == "unbond") {
unbondEventsIdx--;
// Filter by minimum value
if (obj.amountFilter !== "") {
if (parseFloat(obj.amountFilter) > thisEvent.stake) {
filtered++;
continue;
}
}
// Filter name on from, to, caller
if (obj.searchTerm !== "") {
let isFiltered = true;
if (thisEvent.address.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false;
if (thisEvent.from.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false;
if (isFiltered) {
filtered++;
continue;
}
}
} else if (latestType == "transferTicket") {
transferTicketEventsIdx--;
// Filter by minimum value
if (obj.amountFilter !== "") {
if (parseFloat(obj.amountFilter) > thisEvent.amount) {
filtered++;
continue;
}
}
// Filter name on from, to, caller
if (obj.searchTerm !== "") {
let isFiltered = true;
if (thisEvent.address.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false;
if (thisEvent.to.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false;
if (isFiltered) {
filtered++;
continue;
}
}
} else if (latestType == "redeemTicket") {
redeemTicketEventsIdx--;
// Filter by minimum value
if (obj.amountFilter !== "") {
if (parseFloat(obj.amountFilter) > thisEvent.amount) {
filtered++;
continue;
}
}
// Filter name on from, to, caller
if (obj.searchTerm !== "") {
let isFiltered = true;
if (thisEvent.address.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false;
if (isFiltered) {
filtered++;
continue;
}
}
} else { } else {
console.log("error, breaky breaky"); console.log("bork");
break;
}
// Filter by minimum value
if (obj.amountFilter !== "") {
if (parseFloat(obj.amountFilter) > thisEvent.eventValue) {
filtered++;
continue;
}
}
// Filter name on from, to, caller
if (obj.searchTerm !== "") {
let isFiltered = true;
if (thisEvent.eventCaller.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false;
if (thisEvent.eventFrom.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false;
if (thisEvent.eventTo.toLowerCase().includes(obj.searchTerm.toLowerCase())) isFiltered = false;
if (isFiltered) {
filtered++;
continue;
}
}
// Filter Events on filter buttons
let isFiltered = true;
// Check boolean filters on thisEvent.eventType
let count = 0;
if (filterActivated) {
if (thisEvent.eventType === "Activate") {
isFiltered = false;
}
count++;
}
if (rewardActivated) {
if (thisEvent.eventType === "Reward") {
isFiltered = false;
}
count++;
}
if (updateActivated) {
if (thisEvent.eventType === "Update") {
isFiltered = false;
}
count++;
}
if (withdrawActivated) {
if (thisEvent.eventType === "Withdraw") {
isFiltered = false;
}
count++;
}
if (stakeActivated) {
if (thisEvent.eventType === "Stake") {
isFiltered = false;
}
count++;
}
if (stakeActivated) {
if (thisEvent.eventType === "Migrate") {
isFiltered = false;
}
count++;
}
if (unbondActivated) {
if (thisEvent.eventType === "Unbond") {
isFiltered = false;
}
count++;
}
if (delegatorRewardActivated) {
if (thisEvent.eventType === "Claim") {
isFiltered = false;
}
count++;
}
if (ticketRedemptionActivated) {
if (thisEvent.eventType === "RedeemTicket") {
isFiltered = false;
}
count++;
}
if (isFiltered && count) {
filtered++;
continue;
} }
if (unfiltered < obj.maxAmount) { if (unfiltered < obj.maxAmount) {
unfiltered++; unfiltered++;
if (prevBlock === thisEvent.transactionBlock) { if (prevBlock === thisEvent.transactionBlock) {
eventList.push(<EventButton eventList.push(<EventButton
key={thisEvent.transactionHash + unfiltered} key={thisEvent.transactionHash + unfiltered}
seed={thisEvent.transactionHash + unfiltered}
eventObj={thisEvent} eventObj={thisEvent}
type={latestType}
setSearchTerm={obj.setSearchTerm} setSearchTerm={obj.setSearchTerm}
/>); />);
} else { } else {
prevBlock = thisEvent.transactionBlock; prevBlock = thisEvent.transactionBlock;
eventList.push(<EventButton eventList.push(<EventButton
key={thisEvent.transactionHash + unfiltered} key={thisEvent.transactionHash + unfiltered}
seed={thisEvent.transactionHash + unfiltered}
eventObj={thisEvent} eventObj={thisEvent}
type={latestType}
isFirstOfBlock={prevBlock} isFirstOfBlock={prevBlock}
time={thisEvent.transactionTime} time={thisEvent.transactionTime}
setSearchTerm={obj.setSearchTerm} setSearchTerm={obj.setSearchTerm}

View File

@ -34,16 +34,6 @@ const Livepeer = (obj) => {
return <Navigate push to="/" />; return <Navigate push to="/" />;
} }
let eventsList = [];
if (livepeer.events) {
eventsList = livepeer.events;
}
let ticketList = [];
if (livepeer.tickets) {
ticketList = livepeer.tickets;
}
let thisOrchObj; let thisOrchObj;
let headerString; let headerString;
if (livepeer.selectedOrchestrator) { if (livepeer.selectedOrchestrator) {
@ -109,9 +99,20 @@ const Livepeer = (obj) => {
<div id='bodyContent'> <div id='bodyContent'>
{sidebar} {sidebar}
<div className="mainContent"> <div className="mainContent">
<EventViewer events={eventsList} searchTerm={searchTerm} setSearchTerm={setSearchTerm} <EventViewer searchTerm={searchTerm} setSearchTerm={setSearchTerm}
forceVertical={true} showFilter={showFilter} setAmountFilter={setAmountFilter} amountFilter={amountFilter} forceVertical={true} showFilter={showFilter} setAmountFilter={setAmountFilter} amountFilter={amountFilter}
maxAmount={maxAmount} setMaxAmount={setMaxAmount} tickets={ticketList} /> maxAmount={maxAmount} setMaxAmount={setMaxAmount}
updateEvents={livepeer.updateEvents}
rewardEvents={livepeer.rewardEvents}
claimEvents={livepeer.claimEvents}
withdrawStakeEvents={livepeer.withdrawStakeEvents}
withdrawFeesEvents={livepeer.withdrawFeesEvents}
transferTicketEvents={livepeer.transferTicketEvents}
redeemTicketEvents={livepeer.redeemTicketEvents}
activateEvents={livepeer.activateEvents}
unbondEvents={livepeer.unbondEvents}
stakeEvents={livepeer.stakeEvents}
/>
</div> </div>
</div> </div>
</div > </div >

View File

@ -6,7 +6,7 @@ import {
import { import {
getQuotes, getBlockchainData, getEvents, getCurrentOrchestratorInfo, getTickets, getQuotes, getBlockchainData, getEvents, getCurrentOrchestratorInfo, getTickets,
getAllEnsDomains, getAllEnsInfo, getAllThreeBoxInfo, getAllOrchScores, getAllOrchInfo, getAllEnsDomains, getAllEnsInfo, getAllThreeBoxInfo, getAllOrchScores, getAllOrchInfo,
getAllDelInfo getAllDelInfo, getAllMonthlyStats
} from "../actions/livepeer"; } from "../actions/livepeer";
import { login } from "../actions/session"; import { login } from "../actions/session";
@ -23,10 +23,18 @@ const Startup = (obj) => {
console.log("Refreshing Livepeer data..."); console.log("Refreshing Livepeer data...");
batch(() => { batch(() => {
dispatch(getQuotes()); dispatch(getQuotes());
dispatch(getEvents());
dispatch(getBlockchainData()); dispatch(getBlockchainData());
dispatch(getCurrentOrchestratorInfo()); dispatch(getCurrentOrchestratorInfo());
dispatch(getTickets()); dispatch(getAllUpdateEvents());
dispatch(getAllRewardEvents());
dispatch(getAllClaimEvents());
dispatch(getAllWithdrawStakeEvents());
dispatch(getAllWithdrawFeesEvents());
dispatch(getAllTransferTicketEvents());
dispatch(getAllRedeemTicketEvents());
dispatch(getAllActivateEvents());
dispatch(getAllUnbondEvents());
dispatch(getAllStakeEvents());
}); });
} }
@ -53,6 +61,7 @@ const Startup = (obj) => {
dispatch(getAllOrchInfo()); dispatch(getAllOrchInfo());
dispatch(getAllDelInfo()); dispatch(getAllDelInfo());
dispatch(getAllOrchScores()); dispatch(getAllOrchScores());
dispatch(getAllMonthlyStats());
}); });
} }

View File

@ -6,136 +6,14 @@ import { Accordion } from '@mantine/core';
import ScrollContainer from 'react-indiana-drag-scroll'; import ScrollContainer from 'react-indiana-drag-scroll';
import WinnerMonth from '../components/WinnerMonth'; import WinnerMonth from '../components/WinnerMonth';
import StakeOverview from '../components/StakeOverview'; import StakeOverview from '../components/StakeOverview';
import { VictoryPie } from 'victory';
const Tickets = (obj) => { const Stats = (obj) => {
const livepeer = useSelector((state) => state.livepeerstate); const livepeer = useSelector((state) => state.livepeerstate);
const [ticketsPerMonth, setTicketsPerMonth] = useState([]); const [ticketsPerMonth, setTicketsPerMonth] = useState([]);
const [redirectToHome, setRedirectToHome] = useState(false); const [redirectToHome, setRedirectToHome] = useState(false);
console.log("Rendering Stats Viewer"); console.log("Rendering Stats Viewer");
useEffect(() => {
// Process Winning tickets as:
// List of Months containing
// List of Orchestrators (sorted by earnings) containing
// List of winning tickets
let ticketsPerMonth = [];
let ticketIdx = livepeer.winningTickets.length - 1;
let currentMonth = 99;
let currentYear = 99;
let currentOrchCounter = [];
while (ticketIdx >= 0) {
const thisTicket = livepeer.winningTickets[ticketIdx];
const thisTime = new Date(thisTicket.transactionTime * 1000);
const thisYear = thisTime.getFullYear();
const thisMonth = thisTime.getMonth();
ticketIdx -= 1;
// On a new month
if (thisMonth != currentMonth) {
// Push this months data
if (currentOrchCounter.length) {
// Sort this months data
let sortedList = []
let currentSum = 0;
while (currentOrchCounter.length) {
let ticketIdx2 = currentOrchCounter.length - 1;
let largestIdx = 0;
let largestValue = 0;
// Find current O with most ticket wins in Eth
while (ticketIdx2 >= 0) {
const currentOrch = currentOrchCounter[ticketIdx2];
if (currentOrch.sum > largestValue) {
largestIdx = ticketIdx2;
largestValue = currentOrch.sum;
}
ticketIdx2 -= 1;
}
currentSum += largestValue;
// Push current biggest list
sortedList.push(currentOrchCounter[largestIdx]);
// Remove from list
currentOrchCounter.splice(largestIdx, 1);
}
ticketsPerMonth.push(
{
year: currentYear,
month: currentMonth,
orchestrators: sortedList,
total: currentSum
}
);
}
// clear data
currentMonth = thisMonth;
currentYear = thisYear;
currentOrchCounter = [];
}
// Find orch in list
let thisIdx = 0;
let thisFound = false;
let ticketIdx2 = currentOrchCounter.length - 1;
while (ticketIdx2 >= 0) {
const currentOrch = currentOrchCounter[ticketIdx2];
if (currentOrch.address == thisTicket.eventTo) {
thisFound = true;
thisIdx = ticketIdx2;
break;
}
ticketIdx2 -= 1;
}
// If not in list, append at the end
if (!thisFound) {
currentOrchCounter.push({
address: thisTicket.eventTo,
sum: thisTicket.eventValue
});
} else {
// Else update that entry
currentOrchCounter[thisIdx].sum += thisTicket.eventValue;
}
}
if (currentOrchCounter.length) {
// Sort this months data
let sortedList = []
let currentSum = 0;
while (currentOrchCounter.length) {
let ticketIdx2 = currentOrchCounter.length - 1;
let largestIdx = 0;
let largestValue = 0;
// Find current O with most ticket wins in Eth
while (ticketIdx2 >= 0) {
const currentOrch = currentOrchCounter[ticketIdx2];
if (currentOrch.sum > largestValue) {
largestIdx = ticketIdx2;
largestValue = currentOrch.sum;
}
ticketIdx2 -= 1;
}
currentSum += largestValue;
// Push current biggest list
sortedList.push(currentOrchCounter[largestIdx]);
// Remove from list
currentOrchCounter.splice(largestIdx, 1);
}
ticketsPerMonth.push(
{
year: currentYear,
month: currentMonth,
orchestrators: sortedList,
total: currentSum
}
);
}
setTicketsPerMonth(ticketsPerMonth);
}, [livepeer.winningTickets]);
if (redirectToHome) { if (redirectToHome) {
return <Navigate push to="/" />; return <Navigate push to="/" />;
} }
@ -272,4 +150,4 @@ const Tickets = (obj) => {
); );
} }
export default Tickets; export default Stats;

View File

@ -13,20 +13,40 @@ import {
SET_ALL_ORCH_SCORES, SET_ALL_ORCH_SCORES,
SET_ALL_ORCH_INFO, SET_ALL_ORCH_INFO,
SET_ALL_DEL_INFO, SET_ALL_DEL_INFO,
CACHE_ORCHESTRATOR CACHE_ORCHESTRATOR,
SET_ALL_MONTHLY_STATS,
SET_ALL_UPDATE_EVENTS,
SET_ALL_REWARD_EVENTS,
SET_ALL_CLAIM_EVENTS,
SET_ALL_WITHDRAW_STAKE_EVENTS,
SET_ALL_WITHDRAW_FEES_EVENTS,
SET_ALL_TRANSFER_TICKET_EVENTS,
SET_ALL_REDEEM_TICKET_EVENTS,
SET_ALL_ACTIVATE_EVENTS,
SET_ALL_UNBOND_EVENTS,
SET_ALL_STAKE_EVENTS
} from "../../actions/livepeer"; } from "../../actions/livepeer";
export default (state = { export default (state = {
quotes: [], quotes: [],
blockchains: [], blockchains: [],
events: [],
thisOrchestrator: null, thisOrchestrator: null,
selectedOrchestrator: null, selectedOrchestrator: null,
tickets: [],
ensInfoMapping: [], ensInfoMapping: [],
ensDomainMapping: [], ensDomainMapping: [],
winningTickets: [], winningTickets: [],
orchScores: [] orchScores: [],
monthlyStats: [],
updateEvents: [],
rewardEvents: [],
claimEvents: [],
withdrawStakeEvents: [],
withdrawFeesEvents: [],
transferTicketEvents: [],
redeemTicketEvents: [],
activateEvents: [],
unbondEvents: [],
stakeEvents: [],
}, { type, message }) => { }, { type, message }) => {
Object.freeze(state); Object.freeze(state);
switch (type) { switch (type) {
@ -89,6 +109,28 @@ export default (state = {
} }
case SET_ALL_DEL_INFO: case SET_ALL_DEL_INFO:
return { ...state, delInfo: message }; return { ...state, delInfo: message };
case SET_ALL_MONTHLY_STATS:
return { ...state, monthlyStats: message };
case SET_ALL_UPDATE_EVENTS:
return { ...state, updateEvents: message };
case SET_ALL_REWARD_EVENTS:
return { ...state, rewardEvents: message };
case SET_ALL_CLAIM_EVENTS:
return { ...state, claimEvents: message };
case SET_ALL_WITHDRAW_STAKE_EVENTS:
return { ...state, withdrawStakeEvents: message };
case SET_ALL_WITHDRAW_FEES_EVENTS:
return { ...state, withdrawFeesEvents: message };
case SET_ALL_TRANSFER_TICKET_EVENTS:
return { ...state, transferTicketEvents: message };
case SET_ALL_REDEEM_TICKET_EVENTS:
return { ...state, redeemTicketEvents: message };
case SET_ALL_ACTIVATE_EVENTS:
return { ...state, activateEvents: message };
case SET_ALL_UNBOND_EVENTS:
return { ...state, unbondEvents: message };
case SET_ALL_STAKE_EVENTS:
return { ...state, stakeEvents: message };
default: default:
return { ...state }; return { ...state };
} }

View File

@ -144,3 +144,102 @@ export const getAllDelInfo = () => (
} }
}) })
); );
export const getAllMonthlyStats = () => (
fetch("api/livepeer/getAllMonthlyStats", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
})
);
export const getAllUpdateEvents = () => (
fetch("api/livepeer/getAllUpdateEvents", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
})
);
export const getAllRewardEvents = () => (
fetch("api/livepeer/getAllRewardEvents", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
})
);
export const getAllClaimEvents = () => (
fetch("api/livepeer/getAllClaimEvents", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
})
);
export const getAllWithdrawStakeEvents = () => (
fetch("api/livepeer/getAllWithdrawStakeEvents", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
})
);
export const getAllWithdrawFeesEvents = () => (
fetch("api/livepeer/getAllWithdrawFeesEvents", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
})
);
export const getAllTransferTicketEvents = () => (
fetch("api/livepeer/getAllTransferTicketEvents", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
})
);
export const getAllRedeemTicketEvents = () => (
fetch("api/livepeer/getAllRedeemTicketEvents", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
})
);
export const getAllActivateEvents = () => (
fetch("api/livepeer/getAllActivateEvents", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
})
);
export const getAllUnbondEvents = () => (
fetch("api/livepeer/getAllUnbondEvents", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
})
);
export const getAllStakeEvents = () => (
fetch("api/livepeer/getAllStakeEvents", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
})
);