mirror of
https://github.com/stronk-dev/LivepeerEvents.git
synced 2025-07-05 18:55:09 +02:00
Added winning ticket viewer
This commit is contained in:
parent
eb6db0c518
commit
36f32037d4
12
package.json
12
package.json
@ -3,15 +3,23 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@mantine/core": "^4.1.3",
|
||||||
|
"@mantine/dates": "^4.1.3",
|
||||||
|
"@mantine/form": "^4.1.3",
|
||||||
|
"@mantine/hooks": "^4.1.3",
|
||||||
|
"@mantine/modals": "^4.1.3",
|
||||||
|
"@mantine/next": "^4.1.3",
|
||||||
|
"@mantine/notifications": "^4.1.3",
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
"@testing-library/react": "^9.3.2",
|
"@testing-library/react": "^9.3.2",
|
||||||
"@testing-library/user-event": "^7.1.2",
|
"@testing-library/user-event": "^7.1.2",
|
||||||
|
"dayjs": "^1.11.1",
|
||||||
"ethers": "^5.4.4",
|
"ethers": "^5.4.4",
|
||||||
"http": "^0.0.1-security",
|
"http": "^0.0.1-security",
|
||||||
"https": "^1.0.0",
|
"https": "^1.0.0",
|
||||||
"md5": "^2.3.0",
|
"md5": "^2.3.0",
|
||||||
"react": "^16.12.0",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^16.12.0",
|
"react-dom": "^17.0.2",
|
||||||
"react-indiana-drag-scroll": "^2.1.0",
|
"react-indiana-drag-scroll": "^2.1.0",
|
||||||
"react-markdown": "^7.1.1",
|
"react-markdown": "^7.1.1",
|
||||||
"react-paginate": "^8.1.2",
|
"react-paginate": "^8.1.2",
|
||||||
|
@ -3,6 +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 {
|
import {
|
||||||
BrowserRouter as Router,
|
BrowserRouter as Router,
|
||||||
@ -16,6 +17,7 @@ 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='/orchestrator' element={<Grafana />} />
|
<Route exact path='/orchestrator' element={<Grafana />} />
|
||||||
<Route path='/' element={<Home />} />
|
<Route path='/' element={<Home />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
@ -24,6 +24,7 @@ export const RECEIVE_CURRENT_ORCHESTRATOR = "RECEIVE_CURRENT_ORCHESTRATOR";
|
|||||||
export const RECEIVE_ORCHESTRATOR = "RECEIVE_ORCHESTRATOR";
|
export const RECEIVE_ORCHESTRATOR = "RECEIVE_ORCHESTRATOR";
|
||||||
export const CLEAR_ORCHESTRATOR = "CLEAR_ORCHESTRATOR";
|
export const CLEAR_ORCHESTRATOR = "CLEAR_ORCHESTRATOR";
|
||||||
export const RECEIVE_TICKETS = "RECEIVE_TICKETS";
|
export const RECEIVE_TICKETS = "RECEIVE_TICKETS";
|
||||||
|
export const RECEIVE_WINNING_TICKETS = "RECEIVE_WINNING_TICKETS";
|
||||||
export const SET_ALL_ENS_INFO = "SET_ALL_ENS_INFO";
|
export const SET_ALL_ENS_INFO = "SET_ALL_ENS_INFO";
|
||||||
export const SET_ALL_ENS_DOMAINS = "SET_ALL_ENS_DOMAINS";
|
export const SET_ALL_ENS_DOMAINS = "SET_ALL_ENS_DOMAINS";
|
||||||
export const SET_ALL_THREEBOX_INFO = "SET_ALL_THREEBOX_INFO";
|
export const SET_ALL_THREEBOX_INFO = "SET_ALL_THREEBOX_INFO";
|
||||||
@ -49,6 +50,9 @@ const clearOrchestratorInfo = () => ({
|
|||||||
const setTickets = message => ({
|
const setTickets = message => ({
|
||||||
type: RECEIVE_TICKETS, message
|
type: RECEIVE_TICKETS, message
|
||||||
});
|
});
|
||||||
|
const setWinningTickets = message => ({
|
||||||
|
type: RECEIVE_WINNING_TICKETS, message
|
||||||
|
});
|
||||||
const setAllEnsInfo = message => ({
|
const setAllEnsInfo = message => ({
|
||||||
type: SET_ALL_ENS_INFO, message
|
type: SET_ALL_ENS_INFO, message
|
||||||
});
|
});
|
||||||
@ -428,6 +432,7 @@ export const getTickets = () => async dispatch => {
|
|||||||
// Combine raw list of events into a list of useful Events
|
// Combine raw list of events into a list of useful Events
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
let finalTicketList = [];
|
let finalTicketList = [];
|
||||||
|
let winningTicketList = [];
|
||||||
// Current transaction we are processing
|
// Current transaction we are processing
|
||||||
let txCounter = 0;
|
let txCounter = 0;
|
||||||
let currentTx = "";
|
let currentTx = "";
|
||||||
@ -475,11 +480,9 @@ export const getTickets = () => async dispatch => {
|
|||||||
eventValue: amount
|
eventValue: amount
|
||||||
});
|
});
|
||||||
} else if (eventObj.name === "WinningTicketTransfer") {
|
} else if (eventObj.name === "WinningTicketTransfer") {
|
||||||
// For now lets just ignore these, they are boring
|
|
||||||
continue;
|
|
||||||
const amount = parseFloat(eventObj.data.amount) / 1000000000000000000;
|
const amount = parseFloat(eventObj.data.amount) / 1000000000000000000;
|
||||||
const txt = " broadcaster payed out " + amount.toFixed(4) + " Eth";
|
const txt = " broadcaster payed out " + amount.toFixed(4) + " Eth";
|
||||||
finalTicketList.push({
|
winningTicketList.push({
|
||||||
eventType: "TransferTicket",
|
eventType: "TransferTicket",
|
||||||
eventDescription: txt,
|
eventDescription: txt,
|
||||||
eventCaller: "",
|
eventCaller: "",
|
||||||
@ -499,6 +502,7 @@ export const getTickets = () => async dispatch => {
|
|||||||
}
|
}
|
||||||
// NOTE: We are throwing away the very oldest Ticket now, which should be fine.
|
// 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
|
// We can fix this once above wall of text becomes a separate function
|
||||||
|
dispatch(setWinningTickets(winningTicketList));
|
||||||
return dispatch(setTickets(finalTicketList));
|
return dispatch(setTickets(finalTicketList));
|
||||||
}
|
}
|
||||||
return dispatch(receiveErrors(data));
|
return dispatch(receiveErrors(data));
|
||||||
|
@ -75,7 +75,6 @@ const Address = (obj) => {
|
|||||||
let thisIcon;
|
let thisIcon;
|
||||||
if (hasENS) {
|
if (hasENS) {
|
||||||
thisName = thisInfo.domain;
|
thisName = thisInfo.domain;
|
||||||
console.log(thisInfo.avatar);
|
|
||||||
if (thisInfo.avatar) {
|
if (thisInfo.avatar) {
|
||||||
thisIcon =
|
thisIcon =
|
||||||
<a className="selectOrchLight" style={{ padding: '0.2em', cursor: 'alias' }} target="_blank" rel="noopener noreferrer" href={"https://app.ens.domains/name/" + thisInfo.domain + "/details"} >
|
<a className="selectOrchLight" style={{ padding: '0.2em', cursor: 'alias' }} target="_blank" rel="noopener noreferrer" href={"https://app.ens.domains/name/" + thisInfo.domain + "/details"} >
|
||||||
|
@ -11,12 +11,16 @@ const Home = (obj) => {
|
|||||||
const userstate = useSelector((state) => state.userstate);
|
const userstate = useSelector((state) => state.userstate);
|
||||||
const [redirectToGrafana, setRedirectToGrafana] = useState(false);
|
const [redirectToGrafana, setRedirectToGrafana] = useState(false);
|
||||||
const [redirectToLPT, setRedirectToLPT] = useState(false);
|
const [redirectToLPT, setRedirectToLPT] = useState(false);
|
||||||
|
const [redirectToTickets, setRedirectToTickets] = useState(false);
|
||||||
if (redirectToGrafana) {
|
if (redirectToGrafana) {
|
||||||
return <Navigate push to="/orchestrator" />;
|
return <Navigate push to="/orchestrator" />;
|
||||||
}
|
}
|
||||||
if (redirectToLPT) {
|
if (redirectToLPT) {
|
||||||
return <Navigate push to="/livepeer" />;
|
return <Navigate push to="/livepeer" />;
|
||||||
}
|
}
|
||||||
|
if (redirectToTickets) {
|
||||||
|
return <Navigate push to="/tickets" />;
|
||||||
|
}
|
||||||
// Get amount of unique IP's which have visited this website
|
// Get amount of unique IP's which have visited this website
|
||||||
var totalVisitorCount = 0;
|
var totalVisitorCount = 0;
|
||||||
if (userstate.visitorStats) {
|
if (userstate.visitorStats) {
|
||||||
@ -57,6 +61,13 @@ const Home = (obj) => {
|
|||||||
<p>🔎 Blockchain 🕵️</p>
|
<p>🔎 Blockchain 🕵️</p>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="row">
|
||||||
|
<button className="waveButton" onClick={() => {
|
||||||
|
setRedirectToTickets(true);
|
||||||
|
}}>
|
||||||
|
<p>💵 Tickets 💰</p>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div className="verticalDivider" />
|
<div className="verticalDivider" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
201
src/pages/tickets.js
Normal file
201
src/pages/tickets.js
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
import React, { useState, useEffect } from 'react'
|
||||||
|
import '../style.css';
|
||||||
|
import { Navigate } from "react-router-dom";
|
||||||
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
|
import { Accordion } from '@mantine/core';
|
||||||
|
import ScrollContainer from 'react-indiana-drag-scroll';
|
||||||
|
import Address from '../components/OrchAddressViewer';
|
||||||
|
|
||||||
|
const Tickets = (obj) => {
|
||||||
|
const livepeer = useSelector((state) => state.livepeerstate);
|
||||||
|
const [ticketsPerMonth, setTicketsPerMonth] = useState([]);
|
||||||
|
const [redirectToHome, setRedirectToHome] = useState(false);
|
||||||
|
|
||||||
|
console.log("Rendering Winning Ticket 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 = []
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
// Push current biggest list
|
||||||
|
sortedList.push(currentOrchCounter[largestIdx]);
|
||||||
|
// Remove from list
|
||||||
|
currentOrchCounter.splice(largestIdx, 1);
|
||||||
|
}
|
||||||
|
ticketsPerMonth.push(
|
||||||
|
{
|
||||||
|
year: currentYear,
|
||||||
|
month: currentMonth,
|
||||||
|
orchestrators: sortedList
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTicketsPerMonth(ticketsPerMonth);
|
||||||
|
}, [livepeer.winningTickets]);
|
||||||
|
|
||||||
|
if (redirectToHome) {
|
||||||
|
return <Navigate push to="/" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ margin: 0, padding: 0, height: '100%', width: '100%', overflow: 'hidden' }}>
|
||||||
|
<div id='header'>
|
||||||
|
<div className='rowAlignLeft'>
|
||||||
|
<button className="homeButton" onClick={() => {
|
||||||
|
setRedirectToHome(true);
|
||||||
|
}}>
|
||||||
|
<h1>🏠</h1>
|
||||||
|
</button>
|
||||||
|
<h4 className="rowAlignLeft withWrap showNeverOnMobile">Winning Tickets</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id='bodyContent'>
|
||||||
|
<div className="mainContent">
|
||||||
|
<div className="strokeSmollLeft" style={{ height: 'calc( 100vh - 50px)' }}>
|
||||||
|
<div className="row" style={{ width: '100%', height: '100%' }}>
|
||||||
|
<div className="stroke roundedOpaque onlyVerticalScroll" style={{ width: '40vw', minWidth: '400px', height: 'calc( 100vh - 50px - 2em)', marginTop: '2em' }}>
|
||||||
|
<div className="content-wrapper" style={{ width: '100%' }}>
|
||||||
|
<ScrollContainer activationDistance={1} className="overflow-container" hideScrollbars={false}>
|
||||||
|
<div className="overflow-content" style={{ cursor: 'grab', paddingTop: 0 }}>
|
||||||
|
<div className={obj.forceVertical ? "flexContainer forceWrap" : "flexContainer"} >
|
||||||
|
<Accordion initialItem={0} className="stroke"
|
||||||
|
styles={{
|
||||||
|
item: { padding: 0 },
|
||||||
|
itemOpened: { padding: 0 },
|
||||||
|
itemTitle: { padding: 0 },
|
||||||
|
control: { padding: 0 },
|
||||||
|
label: { padding: 0 },
|
||||||
|
icon: { padding: 0 },
|
||||||
|
content: { padding: 0 },
|
||||||
|
contentInner: { padding: 0 },
|
||||||
|
}}>
|
||||||
|
<div className="verticalDivider" />
|
||||||
|
{
|
||||||
|
ticketsPerMonth.map(function (data) {
|
||||||
|
let thisMonth = "";
|
||||||
|
let monthAsNum = data.month;
|
||||||
|
if (monthAsNum == 0) {
|
||||||
|
thisMonth = "Januari";;
|
||||||
|
} else if (monthAsNum == 1) {
|
||||||
|
thisMonth = "Februari";;
|
||||||
|
} else if (monthAsNum == 2) {
|
||||||
|
thisMonth = "March";;
|
||||||
|
} else if (monthAsNum == 3) {
|
||||||
|
thisMonth = "April";
|
||||||
|
} else if (monthAsNum == 4) {
|
||||||
|
thisMonth = "May";
|
||||||
|
} else if (monthAsNum == 5) {
|
||||||
|
thisMonth = "June";
|
||||||
|
} else if (monthAsNum == 6) {
|
||||||
|
thisMonth = "July";
|
||||||
|
} else if (monthAsNum == 7) {
|
||||||
|
thisMonth = "August";
|
||||||
|
} else if (monthAsNum == 8) {
|
||||||
|
thisMonth = "September";
|
||||||
|
} else if (monthAsNum == 9) {
|
||||||
|
thisMonth = "October";
|
||||||
|
} else if (monthAsNum == 10) {
|
||||||
|
thisMonth = "November";
|
||||||
|
} else if (monthAsNum == 11) {
|
||||||
|
thisMonth = "December";;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Accordion.Item label={data.year + "-" + thisMonth + ": " + data.orchestrators.length + " orchestrators"} className="stroke" key={data.year + "-" + thisMonth}>
|
||||||
|
<div className="flexContainer forceWrap">
|
||||||
|
{
|
||||||
|
data.orchestrators.map(function (orch) {
|
||||||
|
return (
|
||||||
|
<div className="row" key={"delegator" + orch.address}>
|
||||||
|
<div className="rowAlignLeft">
|
||||||
|
<Address address={orch.address} seed={"delegator" + orch.address + orch.sum} />
|
||||||
|
</div>
|
||||||
|
<div className="rowAlignRight">
|
||||||
|
<h4>{orch.sum} Eth</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className="verticalDivider" />
|
||||||
|
</Accordion.Item>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Accordion>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ScrollContainer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div >
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Tickets;
|
@ -6,6 +6,7 @@ import {
|
|||||||
RECEIVE_CURRENT_ORCHESTRATOR,
|
RECEIVE_CURRENT_ORCHESTRATOR,
|
||||||
CLEAR_ORCHESTRATOR,
|
CLEAR_ORCHESTRATOR,
|
||||||
RECEIVE_TICKETS,
|
RECEIVE_TICKETS,
|
||||||
|
RECEIVE_WINNING_TICKETS,
|
||||||
SET_ALL_ENS_INFO,
|
SET_ALL_ENS_INFO,
|
||||||
SET_ALL_ENS_DOMAINS,
|
SET_ALL_ENS_DOMAINS,
|
||||||
SET_ALL_THREEBOX_INFO
|
SET_ALL_THREEBOX_INFO
|
||||||
@ -19,7 +20,8 @@ export default (state = {
|
|||||||
selectedOrchestrator: null,
|
selectedOrchestrator: null,
|
||||||
tickets: [],
|
tickets: [],
|
||||||
ensInfoMapping: [],
|
ensInfoMapping: [],
|
||||||
ensDomainMapping: []
|
ensDomainMapping: [],
|
||||||
|
winningTickets: []
|
||||||
}, { type, message }) => {
|
}, { type, message }) => {
|
||||||
Object.freeze(state);
|
Object.freeze(state);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -37,6 +39,8 @@ export default (state = {
|
|||||||
return { ...state, selectedOrchestrator: null };
|
return { ...state, selectedOrchestrator: null };
|
||||||
case RECEIVE_TICKETS:
|
case RECEIVE_TICKETS:
|
||||||
return { ...state, tickets: message };
|
return { ...state, tickets: message };
|
||||||
|
case RECEIVE_WINNING_TICKETS:
|
||||||
|
return { ...state, winningTickets: message };
|
||||||
case SET_ALL_ENS_INFO:
|
case SET_ALL_ENS_INFO:
|
||||||
return { ...state, ensInfoMapping: message };
|
return { ...state, ensInfoMapping: message };
|
||||||
case SET_ALL_ENS_DOMAINS:
|
case SET_ALL_ENS_DOMAINS:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user