show stake overview

This commit is contained in:
Marco van Dijk 2022-04-17 12:28:28 +02:00
parent 362660d690
commit bf708e83c2
6 changed files with 301 additions and 93 deletions

57
dumps/orchInfo.json Normal file
View File

@ -0,0 +1,57 @@
{
"id": "0x1a196b031ea1a74a53ecbe6148772648e02f9d51",
"activationRound": "2467",
"deactivationRound": "57896044618658097711785492504343953926634992332820282019728792003956564819967",
"active": true,
"status": "Registered",
"lastRewardRound": {
"id": "2535",
"length": "5760",
"startBlock": "14601600",
"endBlock": "14607360",
"mintableTokens": "6289.390217651615209644",
"volumeETH": "0",
"volumeUSD": "0",
"totalActiveStake": "12660572.689057253001431262",
"totalSupply": "25315641.406774142960762968",
"participationRate": "0.5001087069304688888807674462863896",
"movedStake": "0",
"newStake": "0"
},
"rewardCut": "2500",
"feeShare": "100000",
"pendingFeeShare": "0",
"pendingRewardCut": "0",
"totalStake": "10359.571442424819689212",
"totalVolumeETH": "0",
"totalVolumeUSD": "0",
"serviceURI": "https://lpt.4v1.in:8935",
"delegators": [
{
"id": "0x1a196b031ea1a74a53ecbe6148772648e02f9d51",
"bondedAmount": "33.050240717610249021",
"startRound": "2467"
},
{
"id": "0x683fc3000a300575657713124fcf84adc53f9f90",
"bondedAmount": "11.81774",
"startRound": "2468"
},
{
"id": "0x9785c9c3258573810c69ddf93221b6a8dea1c777",
"bondedAmount": "0.000000000000000001",
"startRound": "2467"
},
{
"id": "0xfb9849b0b53f66b747bfa47396964a3fa22400a0",
"bondedAmount": "10071.316871799952124909",
"startRound": "2487"
}
],
"delegator": {
"id": "0x1a196b031ea1a74a53ecbe6148772648e02f9d51",
"bondedAmount": "33.050240717610249021",
"startRound": "2467"
},
"lastGet": 1650188936614
}

View File

@ -159,13 +159,13 @@ export const getEvents = () => async dispatch => {
tmpAmount.toFixed(2) + " LPT stake",
"round " + tmpWhen
]
eventDescription = <Ticket seed={currentTx+descriptions} icon={"🚀"} subtext={subtext} descriptions={descriptions} />
eventDescription = <Ticket seed={currentTx+"-"+txCounter} icon={"🚀"} subtext={subtext} descriptions={descriptions} />
} else {
const subtext = "reactivated";
const descriptions = [
"round " + tmpWhen
]
eventDescription = <Ticket seed={currentTx+descriptions} icon={"🚀"} subtext={subtext} descriptions={descriptions} />
eventDescription = <Ticket seed={currentTx+"-"+txCounter} icon={"🚀"} subtext={subtext} descriptions={descriptions} />
}
}
// Lone Unbond => Unbond Event
@ -178,7 +178,7 @@ export const getEvents = () => async dispatch => {
"round " + tmpWhen
]
eventDescription =
<Ticket seed={currentTx+descriptions} icon={"❌"} subtext={subtext} descriptions={descriptions} />
<Ticket seed={currentTx+"-"+txCounter} icon={"❌"} subtext={subtext} descriptions={descriptions} />
}
// Lone Bond => Stake Event
else if (eventContainsBond) {
@ -194,7 +194,7 @@ export const getEvents = () => async dispatch => {
tmpAmount.toFixed(2) + " LPT"
]
eventDescription =
<Ticket seed={currentTx+descriptions} icon={"⌛"} subtext={subtext} descriptions={descriptions} />
<Ticket seed={currentTx+"-"+txCounter} icon={"⌛"} subtext={subtext} descriptions={descriptions} />
}
// Fill description of Stake Event if it wasn't set yet
@ -205,7 +205,7 @@ export const getEvents = () => async dispatch => {
tmpAmount.toFixed(2) + " LPT"
]
eventDescription =
<Ticket seed={currentTx+descriptions} icon={"⌛"} subtext={subtext} descriptions={descriptions} />
<Ticket seed={currentTx+"-"+txCounter} icon={"⌛"} subtext={subtext} descriptions={descriptions} />
} else if (eventFrom === eventTo) {
eventFrom = "";
const subtext = "changed stake";
@ -213,14 +213,14 @@ export const getEvents = () => async dispatch => {
tmpAmount.toFixed(2) + " LPT"
]
eventDescription =
<Ticket seed={currentTx+descriptions} icon={"⌛"} subtext={subtext} descriptions={descriptions} />
<Ticket seed={currentTx+"-"+txCounter} icon={"⌛"} subtext={subtext} descriptions={descriptions} />
} else {
const subtext = "moved stake";
const descriptions = [
tmpAmount.toFixed(2) + " LPT"
]
eventDescription =
<Ticket seed={currentTx+descriptions} icon={"⌛"} subtext={subtext} descriptions={descriptions} />
<Ticket seed={currentTx+"-"+txCounter} icon={"⌛"} subtext={subtext} descriptions={descriptions} />
}
}
@ -277,7 +277,7 @@ export const getEvents = () => async dispatch => {
"round " + eventObj.data.withdrawRound
]
const txt =
<Ticket seed={currentTx+descriptions} icon={"🏦"} subtext={subtext} descriptions={descriptions} />
<Ticket seed={currentTx+"-"+txCounter} icon={"🏦"} subtext={subtext} descriptions={descriptions} />
finalEventList.push({
eventType: "Withdraw",
eventDescription: txt,
@ -301,7 +301,7 @@ export const getEvents = () => async dispatch => {
amount.toFixed(4) + " Eth"
]
const txt =
<Ticket seed={currentTx+descriptions} icon={"🏦"} subtext={subtext} descriptions={descriptions} />
<Ticket seed={currentTx+"-"+txCounter} icon={"🏦"} subtext={subtext} descriptions={descriptions} />
finalEventList.push({
eventType: "Withdraw",
eventDescription: txt,
@ -327,7 +327,7 @@ export const getEvents = () => async dispatch => {
amount2.toFixed(2) + "% on transcoding fees"
]
const txt =
<Ticket seed={currentTx+descriptions} icon={"🔄"} subtext={subtext} descriptions={descriptions} />
<Ticket seed={currentTx+"-"+txCounter} icon={"🔄"} subtext={subtext} descriptions={descriptions} />
finalEventList.push({
eventType: "Update",
eventDescription: txt,
@ -357,7 +357,7 @@ export const getEvents = () => async dispatch => {
"+" + amount2.toFixed(4) + " Eth fees"
]
let txt =
<Ticket seed={currentTx+descriptions} icon={"💰"} subtext={subtext} descriptions={descriptions} />
<Ticket seed={currentTx+"-"+txCounter} icon={"💰"} subtext={subtext} descriptions={descriptions} />
finalEventList.push({
eventType: "Claim",
eventDescription: txt,
@ -381,7 +381,7 @@ export const getEvents = () => async dispatch => {
"+" + amount1.toFixed(2) + " LPT" + (Math.floor(amount1) == 69 ? "... Nice!" : "")
]
const txt =
<Ticket seed={currentTx+descriptions} icon={"💸"} subtext={subtext} descriptions={descriptions} />
<Ticket seed={currentTx+"-"+txCounter} icon={"💸"} subtext={subtext} descriptions={descriptions} />
finalEventList.push({
eventType: "Reward",
eventDescription: txt,
@ -483,7 +483,7 @@ export const getTickets = () => async dispatch => {
"+" + amount.toFixed(4) + " Eth"
]
const txt =
<Ticket seed={currentTx+descriptions} icon={"🎟️"} subtext={subtext} descriptions={descriptions} />
<Ticket seed={currentTx+descriptions+currentTime} icon={"🎟️"} subtext={subtext} descriptions={descriptions} />
finalTicketList.push({
eventType: "RedeemTicket",
eventDescription: txt,
@ -535,12 +535,21 @@ export const getCurrentOrchestratorInfo = () => async dispatch => {
return dispatch(receiveErrors(data));
};
export const getOrchestratorInfoSilent = (orchAddr) => async dispatch => {
const response = await apiUtil.getOrchestratorInfo(orchAddr);
const data = await response.json();
if (response.ok) {
if (data && data.id) {
return dispatch(cacheNewOrch(data));
}
}
};
export const getOrchestratorInfo = (orchAddr) => async dispatch => {
const response = await apiUtil.getOrchestratorInfo(orchAddr);
const data = await response.json();
if (response.ok) {
if (data && data.id) {
console.log(data);
dispatch(cacheNewOrch(data));
return dispatch(setOrchestratorInfo(data));
} else {
@ -598,7 +607,9 @@ export const getAllThreeBoxInfo = () => async dispatch => {
export const getThreeBoxInfo = async (addr) => {
const response = await apiUtil.getThreeBox(addr);
const data = await response.json();
if (response.ok) {
const data = await response.json();
}
};
export const getOrchestratorScores = (year, month) => async dispatch => {

View File

@ -17,7 +17,7 @@ const Ticket = (obj) => {
<div className="stroke" style={{ flex: 2 }}>
{obj.descriptions.map(function (thisTextItem, i) {
return (
<p className="smallTxt" key={obj.seed}>
<p className="smallTxt" key={obj.seed + i}>
{thisTextItem}
</p>
)

View File

@ -90,10 +90,11 @@ const WinnerMonth = (obj) => {
return (
<div className="stroke">
<h4>Top Earners</h4>
<div className="row">
<VictoryPie padding={100} data={pieList} x="address" y="sum"
sortOrder="descending"
sortKey = "sum"
sortKey="sum"
colorScale={[
"#003f5c",
"#2f4b7c",

View File

@ -1,6 +1,6 @@
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import {
getOrchestratorInfo, getEnsInfo, getThreeBoxInfo, setCachedOrch
getOrchestratorInfo, getEnsInfo, getThreeBoxInfo, setCachedOrch, getOrchestratorInfoSilent
} from "../actions/livepeer";
import { useDispatch, useSelector } from 'react-redux';
@ -8,105 +8,130 @@ const EventButtonAddress = (obj) => {
const dispatch = useDispatch();
const livepeer = useSelector((state) => state.livepeerstate);
const [hasRefreshed, setRefresh] = useState(false);
const [hasENS, setHasENS] = useState(false);
const [hasThreeBox, setHasThreeBox] = useState(false);
const [hasThreeBoxRefreshed, setThreeBoxRefresh] = useState(false);
let thisDomain = null;
let thisInfo = null;
let hasENS = false;
let hasThreeBox = false;
const [hasOrchInfo, setHasOrchInfo] = useState(false);
const [orchInfo, setOrchInfo] = useState(null);
const now = new Date().getTime();
// Lookup domain in cache
if (livepeer.ensDomainMapping) {
for (const thisAddr of livepeer.ensDomainMapping) {
if (thisAddr.address === obj.address) {
thisDomain = thisAddr;
// Check timeout
if (now - thisAddr.timestamp < 86400000) {
break;
}
// Is outdated
if (!hasRefreshed) {
getEnsInfo(obj.address);
setRefresh(true);
}
break;
}
}
// If it was not cached at all
if (thisDomain == null && !hasRefreshed) {
setRefresh(true);
getEnsInfo(obj.address);
}
}
// Lookup current info in cache only if this addr has a mapped ENS domain
if (thisDomain && thisDomain.domain) {
for (const thisAddr of livepeer.ensInfoMapping) {
if (thisAddr.domain === thisDomain.domain) {
thisInfo = thisAddr;
hasENS = true;
// Check timeout
if (now - thisAddr.timestamp < 86400000) {
break;
}
// Is outdated
if (!hasRefreshed) {
getEnsInfo(obj.address);
setRefresh(true);
}
break;
}
}
// If it was not cached at all
if (thisInfo == null && !hasRefreshed) {
getEnsInfo(obj.address);
setRefresh(true);
}
}
// Ugly shit, but temporary for now to quickly enable threebox. Sorry!
if (!hasENS) {
if (livepeer.threeBoxInfo) {
for (const thisAddr of livepeer.threeBoxInfo) {
useEffect(() => {
let thisInfo = null;
let thisDomain = null;
// Lookup domain in cache
if (livepeer.ensDomainMapping && !hasRefreshed) {
for (const thisAddr of livepeer.ensDomainMapping) {
if (thisAddr.address === obj.address) {
thisInfo = thisAddr;
hasThreeBox = true;
thisDomain = thisAddr;
// Check timeout
if (now - thisAddr.timestamp < 86400000) {
break;
}
// Is outdated
if (!hasThreeBoxRefreshed) {
getThreeBoxInfo(obj.address);
setThreeBoxRefresh(true);
if (!hasRefreshed) {
getEnsInfo(obj.address);
setRefresh(true);
}
break;
}
}
// If it was not cached at all
if (thisDomain == null && !hasThreeBoxRefreshed) {
setThreeBoxRefresh(true);
getThreeBoxInfo(obj.address);
if (thisDomain == null && !hasRefreshed) {
setRefresh(true);
getEnsInfo(obj.address);
}
}
}
// Lookup current info in cache only if this addr has a mapped ENS domain
if (thisDomain && thisDomain.domain && !hasRefreshed) {
for (const thisAddr of livepeer.ensInfoMapping) {
if (thisAddr.domain === thisDomain.domain) {
thisInfo = thisAddr;
setHasENS(true);
// Check timeout
if (now - thisAddr.timestamp < 86400000) {
break;
}
// Is outdated
if (!hasRefreshed) {
getEnsInfo(obj.address);
setRefresh(true);
}
break;
}
}
// If it was not cached at all
if (thisInfo == null && !hasRefreshed) {
getEnsInfo(obj.address);
setRefresh(true);
}
}
// Ugly shit, but temporary for now to quickly enable threebox. Sorry!
if (!hasENS && !hasThreeBoxRefreshed) {
if (livepeer.threeBoxInfo) {
for (const thisAddr of livepeer.threeBoxInfo) {
if (thisAddr.address === obj.address) {
thisInfo = thisAddr;
setHasThreeBox(true);
// Check timeout
if (now - thisAddr.timestamp < 86400000) {
break;
}
// Is outdated
if (!hasThreeBoxRefreshed) {
getThreeBoxInfo(obj.address);
setThreeBoxRefresh(true);
}
break;
}
}
// If it was not cached at all
if (thisDomain == null && !hasThreeBoxRefreshed) {
setThreeBoxRefresh(true);
getThreeBoxInfo(obj.address);
}
}
}
if (thisInfo && thisInfo != orchInfo){
setOrchInfo(thisInfo);
}
}, [livepeer.ensDomainMapping, livepeer.threeBoxInfo, hasRefreshed, hasENS, hasThreeBoxRefreshed]);
useEffect(() => {
// Check if cached as an orchestrator
if (livepeer.orchInfo && !hasOrchInfo) {
for (const thisOrch of livepeer.orchInfo) {
if (thisOrch.id === obj.address) {
setHasOrchInfo(true);
break;
}
}
// Preload Orch info
if (!hasOrchInfo) {
dispatch(getOrchestratorInfoSilent(obj.address));
setHasOrchInfo(true);
}
}
}, [livepeer.orchInfo, hasOrchInfo]);
let thisName;
let thisIcon;
if (hasENS) {
thisName = <h4 className="elipsText elipsOnMobileExtra">{thisInfo.domain}</h4>;
if (thisInfo.avatar) {
thisName = <h4 className="elipsText elipsOnMobileExtra">{orchInfo.domain}</h4>;
if (orchInfo.avatar) {
thisIcon =
<a className="selectOrch" style={{ padding: '0', cursor: 'alias' }} target="_blank" rel="noopener noreferrer" href={"https://app.ens.domains/name/" + thisInfo.domain + "/details"} >
<img alt="" src={thisInfo.avatar.url} width="20em" height="20em" style={{ margin: 0, padding: 0 }} />
<a className="selectOrch" style={{ padding: '0', cursor: 'alias' }} target="_blank" rel="noopener noreferrer" href={"https://app.ens.domains/name/" + orchInfo.domain + "/details"} >
<img alt="" src={orchInfo.avatar.url} width="20em" height="20em" style={{ margin: 0, padding: 0 }} />
</a >
}
} else if (hasThreeBox) {
if (thisInfo.name) {
thisName = <h4 className="elipsText elipsOnMobileExtra">{thisInfo.name}</h4>;
if (orchInfo.name) {
thisName = <h4 className="elipsText elipsOnMobileExtra">{orchInfo.name}</h4>;
} else {
thisName = <span className="elipsText elipsOnMobileExtra">{obj.address}</span>;
}
if (thisInfo.image) {
thisIcon = <img alt="" src={"https://cloudflare-ipfs.com/ipfs/" + thisInfo.image} width="20em" height="20em" style={{ margin: 0, padding: 0 }} />
if (orchInfo.image) {
thisIcon = <img alt="" src={"https://cloudflare-ipfs.com/ipfs/" + orchInfo.image} width="20em" height="20em" style={{ margin: 0, padding: 0 }} />
} else {
thisIcon = null;
}

View File

@ -5,11 +5,13 @@ import { useSelector, useDispatch } from 'react-redux';
import { Accordion } from '@mantine/core';
import ScrollContainer from 'react-indiana-drag-scroll';
import WinnerMonth from '../components/WinnerMonth';
import { VictoryPie } from 'victory';
const Tickets = (obj) => {
const livepeer = useSelector((state) => state.livepeerstate);
const [ticketsPerMonth, setTicketsPerMonth] = useState([]);
const [redirectToHome, setRedirectToHome] = useState(false);
const [removeOnlyStakers, setRemoveOnlyStakers] = useState(false);
console.log("Rendering Winning Ticket Viewer");
@ -138,6 +140,117 @@ const Tickets = (obj) => {
return <Navigate push to="/" />;
}
const getName = (address) => {
let thisDomain = null;
// Lookup domain in cache
if (livepeer.ensDomainMapping) {
for (const thisAddr of livepeer.ensDomainMapping) {
if (thisAddr.address === address) {
thisDomain = thisAddr;
break;
}
}
}
// Lookup current info in cache only if this addr has a mapped ENS domain
if (thisDomain && thisDomain.domain) {
for (const thisAddr of livepeer.ensInfoMapping) {
if (thisAddr.domain === thisDomain.domain) {
return thisAddr.domain;
}
}
}
if (livepeer.threeBoxInfo) {
for (const thisAddr of livepeer.threeBoxInfo) {
if (thisAddr.address === address) {
if (thisAddr.name) {
return thisAddr.name;
} else {
return (address.substring(0, 10) + "..");
}
break;
}
}
}
return (address.substring(0, 10) + "..");
}
let pieList = [];
let otherSum = 0;
let pieObj = null;
if (livepeer.orchInfo) {
let orchIdx = livepeer.orchInfo.length - 1;
// calc sum of stake
let totalStake = 0;
while (orchIdx >= 0) {
const thisOrch = livepeer.orchInfo[orchIdx];
orchIdx -= 1;
if (removeOnlyStakers && !parseInt(thisOrch.totalVolumeUSD)) {
continue;
}
totalStake += parseInt(thisOrch.totalStake);
}
// create slices
orchIdx = livepeer.orchInfo.length - 1;
while (orchIdx >= 0) {
const thisOrch = livepeer.orchInfo[orchIdx];
orchIdx -= 1;
if (removeOnlyStakers && !parseInt(thisOrch.totalVolumeUSD)) {
continue;
}
if ((thisOrch.totalStake / totalStake) < 0.04) {
otherSum += thisOrch.totalStake;
} else {
pieList.push({
address: getName(thisOrch.id),
sum: thisOrch.totalStake
});
}
}
pieList.push({
address: "Other",
sum: otherSum
});
pieObj = <div className="stroke">
<h4>Active Orchestrators by Stake</h4>
<div className='row'>
<p>Show non-transcoding Orchestrators?</p>
<div className="toggle-container" onClick={() => setRemoveOnlyStakers(!removeOnlyStakers)}>
<div className={`dialog-button ${removeOnlyStakers ? "" : "disabled"}`}>
{removeOnlyStakers ? "Show" : "Hide"}
</div>
</div>
</div>
<VictoryPie padding={100} data={pieList} x="address" y="sum"
sortOrder="descending"
sortKey="sum"
colorScale={[
"#003f5c",
"#2f4b7c",
"#665191",
"#ff7c43",
"#ffa600",
"#5c3446",
"#83424e",
"#a6544e",
"#c16d46",
"#d18d3c",
"#d3b136",
"#c5d843",
"#a3ff69",
]}
style={{
data: {
fillOpacity: 0.9, stroke: "#636363", strokeWidth: 2
},
labels: {
fontSize: 10, zIndex: 999
}
}} />
</div>
}
return (
<div style={{ margin: 0, padding: 0, height: '100%', width: '100%', overflow: 'hidden' }}>
<div id='header'>
@ -158,7 +271,8 @@ const Tickets = (obj) => {
<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"} >
<div className="flexContainer forceWrap" >
{pieObj}
<Accordion initialItem={0} className="stroke"
styles={{
item: { padding: 0 },