diff --git a/js/package.json b/js/package.json index 7c1e06f..709c976 100644 --- a/js/package.json +++ b/js/package.json @@ -31,7 +31,7 @@ }, "scripts": { "client": "PORT=3001 FAST_REFRESH=false react-scripts start", - "server": "node -r dotenv/config --trace-warnings --max-old-space-size=13958 server 2>&1 | tee /tmp/cryptoecon.log", + "server": "node -r dotenv/config --trace-warnings --max-old-space-size=33958 server 2>&1 | tee /tmp/cryptoecon.log", "dev": "tsnd -r dotenv/config --max-old-space-size=16384 ./server", "test-process": "node -r dotenv/config --trace-warnings --max-old-space-size=16384 ./server/worker/process.childprocess.test.js", "refresh-lm-snapshot": "source .env && curl -H 'Content-Type: application/json' -H \"$HEADER_SECRET\" -X POST -d '{\"query\": \"query GetSnapshot { snapshots_new(limit: 1 order_by: {id: desc}) { snapshot_data } }\"}' $SNAPSHOT_URL -o ./snapshots/snapshot_lm_latest.trash.json", diff --git a/js/server/config.js b/js/server/config.js index cdbf68a..d8f101a 100644 --- a/js/server/config.js +++ b/js/server/config.js @@ -1,6 +1,7 @@ function createConfig({ startsAt, durationInWeeks, + weeksToTotalMaturity, intervalDurationMinutes, initialRowan, initialRewardMultiplier, @@ -58,7 +59,9 @@ function createConfig({ DEPOSITS_ALLOWED_DURATION_MS, MULTIPLIER_MATURITY: REWARD_ACCRUAL_DURATION_MS / 1000 / 60 / EVENT_INTERVAL_MINUTES, // 6 weeks in in 200minute intervals, - NUMBER_OF_INTERVALS_TO_RUN: REWARD_ACCRUAL_DURATION_INTERVAL_COUNT * 2, // duration of bucket drain + duration to latest possible multiplier maturity + NUMBER_OF_INTERVALS_TO_RUN: + REWARD_ACCRUAL_DURATION_INTERVAL_COUNT * + (weeksToTotalMaturity / durationInWeeks), // duration of bucket drain + duration to latest possible multiplier maturity REWARD_ACCRUAL_DURATION_INTERVAL_COUNT, INITIAL_REWARD_MULTIPLIER: initialRewardMultiplier, }; @@ -70,6 +73,7 @@ module.exports = { initialRowan: 10_000_000, startsAt: '2021-08-24T20:06:15.000Z', durationInWeeks: 6, + weeksToTotalMaturity: 12, intervalDurationMinutes: 200, initialRewardMultiplier: 0.25, }), @@ -77,6 +81,7 @@ module.exports = { initialRowan: 40_000_000, startsAt: '2021-10-04T00:00:00.000Z', durationInWeeks: 6, + weeksToTotalMaturity: 6.1, intervalDurationMinutes: 59, initialRewardMultiplier: 1, }), @@ -84,6 +89,15 @@ module.exports = { initialRowan: 1_000_000, startsAt: '2021-10-05T19:00:00.000Z', durationInWeeks: 2, + weeksToTotalMaturity: 2.1, + intervalDurationMinutes: 60, + initialRewardMultiplier: 1, + }), + bonus_v1_ixo: createConfig({ + initialRowan: 500_000, + startsAt: '2021-10-12T13:29:01.255Z', + durationInWeeks: 2, + weeksToTotalMaturity: 2.1, intervalDurationMinutes: 60, initialRewardMultiplier: 1, }), diff --git a/js/server/core/load/loadLiquidityMinersSnapshot.js b/js/server/core/load/loadLiquidityMinersSnapshot.js index 2790dbb..49b5843 100644 --- a/js/server/core/load/loadLiquidityMinersSnapshot.js +++ b/js/server/core/load/loadLiquidityMinersSnapshot.js @@ -2,6 +2,7 @@ const { TESTNET } = require('../../constants/snapshot-source-names'); const slonik = require('slonik'); const { getDatabase } = require('./utils/getDatabase'); +const lmHarvestStartingState = require('./lm-harvest-starting-state.json'); /* WARNING: DO NOT ADD MORE QUERIES OR FIELDS TO THE GRAPHQL QUERY. QUERIES ARE CACHED USING THE LENGTH OF THE TEXT CONTENT OF THE RESPONSE OBJECT @@ -88,6 +89,18 @@ const getSQLQueryByNetwork = (network, rewardProgram) => { for (let snapshot of snapshots_new) { const liquidityByTokens = snapshot.snapshot_data[snapshot.address]; for (let token in liquidityByTokens) { + let startingUserState = 0; + const startingPoolState = lmHarvestStartingState[token]; + if ( + !startingPoolState && + Object.values(liquidityByTokens).filter((v) => v.length > 0) + .length > 2 + ) { + debugger; + } + if (startingPoolState) { + startingUserState = startingPoolState[snapshot.address] || 0; + } let liquidityDeltaEvents = liquidityByTokens[token]; let total = 0; for (let deltaEvent of liquidityDeltaEvents) { diff --git a/js/server/core/process/augmentVSData.js b/js/server/core/process/augmentVSData.js index e477822..b9499b8 100644 --- a/js/server/core/process/augmentVSData.js +++ b/js/server/core/process/augmentVSData.js @@ -46,7 +46,9 @@ exports.augmentVSData = ( const userAtTimestamp = timestamp.users[user.address] || new User(); if (userAtTimestamp.totalAccruedCommissionsAndClaimableRewards) { accum[user.address] = - userAtTimestamp.totalAccruedCommissionsAndClaimableRewards; + userAtTimestamp.totalAccruedCommissionsAndClaimableRewards + + userAtTimestamp.dispensed + + userAtTimestamp.claimedCommissionsAndRewardsAwaitingDispensation; } return accum; }, {}); diff --git a/js/server/core/transform/lm-util.js b/js/server/core/transform/lm-util.js index 0396c3c..7a4c8a2 100644 --- a/js/server/core/transform/lm-util.js +++ b/js/server/core/transform/lm-util.js @@ -4,7 +4,9 @@ const { getTimeIndex } = require('../../util/getTimeIndex'); const { DelegateEvent } = require('../types'); const moment = require('moment'); const fetch = require('cross-fetch').fetch; +const lmHarvestStartingState = require('../load/lm-harvest-starting-state.json'); +global.ACCOUNT_FOR_INITIAL_POOL_STATE = true; const getLMTimeseriesFinalIndex = (snapshotData) => { // Snapshot timeseries (generated by Vanir) overshoots by 1 extra interval. Account for this. const finalIndex = [ @@ -50,6 +52,8 @@ function remapLMAddresses(addresses, deltaCoeff, rewardProgram) { EVENT_INTERVAL_MINUTES, amount: interval.delta, //* deltaCoeff, delegateAddress: address, + token: token, + rawTimestamp: interval.unix_timestamp * 1000, }); }) .filter((e) => e.amount !== 0); @@ -57,7 +61,105 @@ function remapLMAddresses(addresses, deltaCoeff, rewardProgram) { return addressTokenEvents; }); - const rawEvents = _.flattenDeep(mapped); + let rawEvents = _.flattenDeep(mapped); + + if (global.ACCOUNT_FOR_INITIAL_POOL_STATE) { + rawEvents = rawEvents + .sort((a, b) => a.amount - b.amount) + .sort((a, b) => a.rawTimestamp - b.rawTimestamp); + let rawTimestampGroupedEvents = _.groupBy(rawEvents, 'rawTimestamp'); + _.mapValues(rawTimestampGroupedEvents, (timeIntervalEvents) => { + return timeIntervalEvents.map((event) => { + if (event.amount < 0 && rewardProgram === 'harvest') { + const subtractMaxFromUserPool = (token) => { + // initial token balances for all users + const tokenBalances = lmHarvestStartingState[token]; + // initial token balance for current user + const remainingInitialRemovableBalance = tokenBalances + ? tokenBalances[event.delegateAddress] || 0 + : 0; + // normalized initial token balance for user + const convertedVal = +remainingInitialRemovableBalance / 10 ** 18; + // subtract (add the negative) the withdrawal amount from the initial amount + let amountRemainingAfterRemoval = convertedVal + event.amount; + // if the result is negative, the withdrawal exceeded the initial amount + if (amountRemainingAfterRemoval < 0) { + // if the token existed at the starting time + if (lmHarvestStartingState[token]) + // the initial amount has now been zero'ed out + lmHarvestStartingState[token][event.delegateAddress] = 0; + // the remaining withdrawal = the amount that it exceeded the initial amount + event.amount = amountRemainingAfterRemoval; + } else { + // if the token existed at program genesis + if (lmHarvestStartingState[token]) + // if there is a positive amount remaining, set to that positive amount. + lmHarvestStartingState[token][event.delegateAddress] = BigInt( + Math.floor(amountRemainingAfterRemoval * 10 ** 18) + ).toString(); + // the withdrawal is covered by the initial amount + event.amount = 0; + } + }; + // if the event doesn't have a pool + if (event.token === 'rowan') { + // guess that the pool is the first non-rowan token that this user also removed + const externalAssetEvents = + timeIntervalEvents.length > 1 + ? timeIntervalEvents.filter( + (ev) => + ev.token !== 'rowan' && + ev.amount < 0 && + ev.delegateAddress === event.delegateAddress + ) + : []; + // if there is a non-rowan withdrawal + if (externalAssetEvents.length) { + // subtract this rowan amount from those + while (event.amount < 0 && externalAssetEvents.length) { + if ( + event.delegateAddress === + 'sif1vcd6gx3jqng2m7tw6k7e766skayv5rkhn3levl' + ) { + // debugger; + } + subtractMaxFromUserPool(externalAssetEvents.pop().token); + } + } else { + for (let tokenKey in lmHarvestStartingState) { + if ( + lmHarvestStartingState[tokenKey][event.delegateAddress] > 0 + ) { + subtractMaxFromUserPool(tokenKey); + } + if ( + event.delegateAddress === + 'sif1vcd6gx3jqng2m7tw6k7e766skayv5rkhn3levl' + ) { + // debugger; + } + if (event.amount === 0) { + break; + } + } + } + } else { + subtractMaxFromUserPool(event.token); + } + } + }); + }); + + const regendudeinitial = Object.entries(lmHarvestStartingState) + .map(([k, v]) => { + return v['sif1vcd6gx3jqng2m7tw6k7e766skayv5rkhn3levl']; + }) + .filter((v) => !!v); + const regendude = rawEvents.filter( + (ev) => + ev.delegateAddress === 'sif1vcd6gx3jqng2m7tw6k7e766skayv5rkhn3levl' + ); + } let allTimeIntervalEvents = _.groupBy(rawEvents, 'timestamp'); allTimeIntervalEvents = _.mapValues( allTimeIntervalEvents, @@ -82,12 +184,21 @@ function remapLMAddresses(addresses, deltaCoeff, rewardProgram) { return _.mapValues( timeIntervalAddressEvents, (addressEvents, address) => { + if (global.ACCOUNT_FOR_INITIAL_POOL_STATE) { + addressEvents = addressEvents.sort((a, b) => { + return a.rawTimestamp - b.rawTimestamp; + }); + } return [ DelegateEvent.fromJSON({ timestamp: parseInt(timeInterval), delegateAddress: address, amount: addressEvents.reduce((accum, addressEvent) => { - return accum + parseFloat(addressEvent.amount); + const nextVal = accum + parseFloat(addressEvent.amount); + if (global.ACCOUNT_FOR_INITIAL_POOL_STATE) { + return Math.max(0, nextVal); + } + return nextVal; }, 0), }), ]; diff --git a/js/server/core/types/DelegateEvent.js b/js/server/core/types/DelegateEvent.js index 2bb2322..eaf4f2e 100644 --- a/js/server/core/types/DelegateEvent.js +++ b/js/server/core/types/DelegateEvent.js @@ -1,26 +1,28 @@ class DelegateEvent { - constructor () { + constructor() { this.timestamp = 0; this.delegateAddress = null; this.validatorRewardAddress = null; this.validatorStakeAddress = null; this.commission = 0; this.amount = 0; + this.token = ''; + this.rawTimestamp = 0; } - clearTimestamp () { + clearTimestamp() { this.timestamp = undefined; } - static fromJSON (props) { + static fromJSON(props) { return Object.assign(new this(), props); } - cloneWith (props) { + cloneWith(props) { return Object.assign(Object.assign(new DelegateEvent(), this), props); } } module.exports = { - DelegateEvent + DelegateEvent, }; diff --git a/js/server/main.js b/js/server/main.js index 94a1d55..48a6b6e 100644 --- a/js/server/main.js +++ b/js/server/main.js @@ -214,8 +214,19 @@ const server = new ApolloServer({ distributionPattern: 'LINEAR', }, { - displayName: `Sif's Bonus Pool`, - description: `Immediately earn and claim rewards by pooling JUNØ.`, + displayName: `Sif's IXO Bonus Pool`, + description: `Earn Rowan every 60 minutes and claim rewards immediately by pooling IXO.`, + rewardProgramType: 'lm', + rewardProgramName: 'bonus_v1_ixo', + incentivizedPoolSymbols: ['ixo'], + documentationURL: + 'https://docs.sifchain.finance/resources/rewards-programs', + isUniversal: false, + distributionPattern: 'LINEAR', + }, + { + displayName: `Sif's Junø Bonus Pool`, + description: `Earn Rowan every 60 minutes and claim rewards immediately by pooling JUNØ.`, rewardProgramType: 'lm', rewardProgramName: 'bonus_v1', incentivizedPoolSymbols: ['juno'], diff --git a/js/server/worker/process.childprocess.test.js b/js/server/worker/process.childprocess.test.js index 029b752..bb64f11 100644 --- a/js/server/worker/process.childprocess.test.js +++ b/js/server/worker/process.childprocess.test.js @@ -2,6 +2,7 @@ const { BackgroundProcessor } = require('./process.childprocess'); const _ = require('lodash'); const { MAINNET } = require('../constants/snapshot-source-names'); const { GET_LM_CURRENT_APY_SUMMARY } = require('../constants/action-names'); +const { getTimeIndex } = require('../util/getTimeIndex'); // simple test setup const describe = async (description, describer) => { @@ -39,11 +40,29 @@ const describe = async (description, describer) => { console.groupEnd(); }; -const runTests = (type, parsedData, network) => { +const runTests = (type, parsedData, network, programName) => { const finalGlobalTimestampState = parsedData.processedData[parsedData.processedData.length - 1]; const users = Object.values(finalGlobalTimestampState.users); + const totalValuePerUser = Object.entries( + parsedData.processedData[getTimeIndex('now', programName)].users + ).reduce((prev, [addr, curr]) => { + if (!curr) return prev; + prev[addr] = + curr.totalAccruedCommissionsAndClaimableRewards + + curr.claimedCommissionsAndRewardsAwaitingDispensation + + curr.forfeitedCommissions + + curr.forfeited + + curr.dispensed; + return prev; + }, {}); + + require('fs').writeFileSync( + './userExitStates.new.json', + Buffer.from(JSON.stringify(totalValuePerUser, null, 2)) + ); + const totalPoolDominanceRatio = _.sum( _.flattenDeep( _.map(users, (u) => u.tickets.map((t) => t.poolDominanceRatio)) @@ -88,17 +107,19 @@ const runTests = (type, parsedData, network) => { }; const bp = new BackgroundProcessor(); +// const bp2 = new BackgroundProcessor(); +const programName = 'bonus_v1_ixo'; bp.reloadAndReprocessSnapshots({ network: MAINNET, - rewardProgram: 'bonus_v1', + rewardProgram: programName, }) // test reload caching // .then(async () => bp.reloadAndReprocessSnapshots({ network: MAINNET })) .then(async () => { - await runTests('lm', bp.lmDataParsed, MAINNET); + await runTests('lm', bp.lmDataParsed, MAINNET, programName); console.log( bp.dispatch(GET_LM_CURRENT_APY_SUMMARY, { - programName: 'bonus_v1', + programName: programName, }) ); // await runTests('vs', bp.vsDataParsed, MAINNET); diff --git a/js/src/App.js b/js/src/App.js index 7dc6318..bc6d81e 100644 --- a/js/src/App.js +++ b/js/src/App.js @@ -1,8 +1,4 @@ -import { - START_DATETIME, - networks, - RECENT_ADDRESS_LIST_STORAGE_KEY, -} from './config'; +import { networks, RECENT_ADDRESS_LIST_STORAGE_KEY } from './config'; import './App.css'; import React, { useEffect, useMemo, useState } from 'react'; import { @@ -19,7 +15,10 @@ import DataChart from './DataChart'; import DataStackAll from './DataStackAll'; import { StatBlocks } from './StatBlocks'; import { UserDataSummary } from './UserDataSummary'; +import serverConfigs from './serverConfig'; +const serverConfig = + serverConfigs[window.sessionStorage.getItem('rewardProgram')]; // show all fields locally const SHOULD_HIDE_NON_USER_FRIENDLY_FIELDS = !!process.env.REACT_APP_DEPLOYMENT_TAG; @@ -92,7 +91,7 @@ class Router { } // const now = moment.utc(Date.parse(new Date())); // function initTimestamp() { -// return moment.duration(now.diff(START_DATETIME)).asMinutes() / 200; +// return moment.duration(now.diff(START_DATETIME)).asMinutes() / serverConfig.EVENT_INTERVAL_MINUTES; // } const CountDown = ({ until = moment() }) => { @@ -191,7 +190,9 @@ class App extends React.Component { time = moment.utc(time); return ( Math.floor( - moment.duration(time.diff(moment.utc(START_DATETIME))).asMinutes() / 200 + moment + .duration(time.diff(moment.utc(serverConfig.START_DATETIME))) + .asMinutes() / serverConfig.EVENT_INTERVAL_MINUTES ) + 1 ); } @@ -314,8 +315,11 @@ class App extends React.Component { updateTimestamp(timeIndex) { // because genesis block is included - const minutes = timeIndex * 200; - const dateObj = moment.utc(START_DATETIME).add(minutes, 'm').utc(); + const minutes = timeIndex * serverConfig.EVENT_INTERVAL_MINUTES; + const dateObj = moment + .utc(serverConfig.START_DATETIME) + .add(minutes, 'm') + .utc(); const date = dateObj; this.setState({ date, @@ -605,7 +609,7 @@ class App extends React.Component { {this.state.date.format( `ddd MMMM Do YYYY[,] [${this.state.date .clone() - .subtract(200, 'minutes') + .subtract(serverConfig.EVENT_INTERVAL_MINUTES, 'minutes') .format(`hh:mm A`)}] - hh:mm A` ) + ' UTC'} @@ -622,7 +626,10 @@ class App extends React.Component { `ddd MMMM Do YYYY[,] [${this.state.date .clone() .local() - .subtract(200, 'minutes') + .subtract( + serverConfig.EVENT_INTERVAL_MINUTES, + 'minutes' + ) .format(`hh:mm A`)}] - hh:mm A` ) + ' LOCAL'}
diff --git a/js/src/DataChart.js b/js/src/DataChart.js index a76ed21..c1e5c78 100644 --- a/js/src/DataChart.js +++ b/js/src/DataChart.js @@ -3,6 +3,9 @@ import { timestampToDate } from './utils'; import { Chart, _adapters, registerables } from 'chart.js'; import { registerChartDateAdapter } from './registerChartDateAdapter'; import zoomPlugin from 'chartjs-plugin-zoom'; +import serverConfigs from './serverConfig'; +const serverConfig = + serverConfigs[window.sessionStorage.getItem('rewardProgram')]; // let margin = { top: 10, right: 30, bottom: 30, left: 60 }; // let width = 860 - margin.left - margin.right; @@ -10,7 +13,7 @@ import zoomPlugin from 'chartjs-plugin-zoom'; Chart.register(...registerables, zoomPlugin); registerChartDateAdapter(_adapters); -export default props => { +export default (props) => { const myRef = useRef(); const containerRef = useRef(); const [chart, setChart] = useState(undefined); @@ -29,12 +32,12 @@ export default props => { const [w, h] = [900, 350]; return ( -
+
chart.resetZoom()} - className='chart' + className="chart" ref={myRef} - id='myChart' + id="myChart" width={w} height={h} /> @@ -42,7 +45,7 @@ export default props => { ); }; -function renderChart (canvasElement, data, chart) { +function renderChart(canvasElement, data, chart) { const createDatasets = () => { return [ { @@ -55,10 +58,12 @@ function renderChart (canvasElement, data, chart) { data: data.map((d, index) => { return { y: typeof d === 'object' ? d.userClaimableReward : d, - x: timestampToDate((index - 1) * 200) + x: timestampToDate( + (index - 1) * serverConfig.EVENT_INTERVAL_MINUTES + ), }; - }) - } + }), + }, // { // borderColor: Utils.CHART_COLORS.blue, // borderWidth: 1, @@ -74,7 +79,7 @@ function renderChart (canvasElement, data, chart) { } const totalDuration = 2000; const delayBetweenPoints = totalDuration / data.length; - const previousY = ctx => + const previousY = (ctx) => ctx.index === 0 ? ctx.chart.scales.y.getPixelForValue(100) : ctx.chart @@ -86,60 +91,60 @@ function renderChart (canvasElement, data, chart) { easing: 'linear', duration: delayBetweenPoints, from: NaN, // the point is initially skipped - delay (ctx) { + delay(ctx) { if (ctx.type !== 'data' || ctx.xStarted) { return 0; } ctx.xStarted = true; return ctx.index * delayBetweenPoints; - } + }, }, y: { type: 'number', easing: 'linear', duration: delayBetweenPoints, from: previousY, - delay (ctx) { + delay(ctx) { if (ctx.type !== 'data' || ctx.yStarted) { return 0; } ctx.yStarted = true; return ctx.index * delayBetweenPoints; - } - } + }, + }, }; const config = { type: 'line', data: { - datasets: createDatasets() + datasets: createDatasets(), }, options: { // animation: false, animation, interaction: { - intersect: false + intersect: false, }, plugins: { legend: false, zoom: { zoom: { wheel: { - enabled: false + enabled: true, }, pinch: { - enabled: true + enabled: true, }, - mode: 'x' - } - } + mode: 'x', + }, + }, }, scales: { x: { type: 'time', ticks: { - color: 'rgba(255,255,255,0.9)' - } + color: 'rgba(255,255,255,0.9)', + }, }, y: { title: { @@ -147,15 +152,15 @@ function renderChart (canvasElement, data, chart) { display: true, color: 'white', fontColor: 'white', - textStrokeColor: 'white' + textStrokeColor: 'white', }, ticks: { - color: 'rgba(255,255,255,0.9)' - } - } - } - } + color: 'rgba(255,255,255,0.9)', + }, + }, + }, + }, }; const ctx = canvasElement.getContext('2d'); diff --git a/js/src/DataStackAll.js b/js/src/DataStackAll.js index 0347af0..be2f23b 100644 --- a/js/src/DataStackAll.js +++ b/js/src/DataStackAll.js @@ -3,25 +3,31 @@ import React from 'react'; import { fetchStack } from './api'; import { Chart, _adapters, registerables } from 'chart.js'; import { registerChartDateAdapter } from './registerChartDateAdapter'; -Chart.register(...registerables); +import zoomPlugin from 'chartjs-plugin-zoom'; + +import serverConfigs from './serverConfig'; +const serverConfig = + serverConfigs[window.sessionStorage.getItem('rewardProgram')]; + +Chart.register(...registerables, zoomPlugin); registerChartDateAdapter(_adapters); // const totalInitialRowan = rewardBucketsTimeSeries[0].totalInitialRowan class DataStackAll extends React.Component { - constructor ({ onLoadingStateChange = isLoading => {}, network, ...props }) { + constructor({ onLoadingStateChange = (isLoading) => {}, network, ...props }) { super({ onLoadingStateChange, network, ...props }); this.myRef = React.createRef(); this.state = {}; console.log('working'); } - renderD3 () { + renderD3() { let self = this; - function * createDatasets () { + function* createDatasets() { let data = self.state.rewardData; let addresses = Object.keys(data[0]) - .filter(k => k !== 'timestamp') + .filter((k) => k !== 'timestamp') .slice(0, 10); for (let addr of addresses) { @@ -35,13 +41,13 @@ class DataStackAll extends React.Component { data: self.state.rewardData.map((d, xIndex) => { return { y: d[addr], - x: timestampToDate(xIndex * 200) + x: timestampToDate(xIndex * serverConfig.EVENT_INTERVAL_MINUTES), }; - }) + }), }; } } - const incrementallyUpdateChart = async chart => { + const incrementallyUpdateChart = async (chart) => { let gen = createDatasets(); chart.data.datasets = []; let datasets = []; @@ -65,42 +71,53 @@ class DataStackAll extends React.Component { const config = { type: 'line', data: { - datasets: [] + datasets: [], }, options: { // animation, interaction: { mode: 'nearest', - intersect: false + intersect: false, }, plugins: { - legend: false + legend: false, + zoom: { + zoom: { + wheel: { + enabled: true, + }, + pinch: { + enabled: true, + }, + mode: 'x', + }, + }, }, scales: { x: { type: 'time', ticks: { - color: 'rgba(255,255,255,0.9)' - } + color: 'rgba(255,255,255,0.9)', + }, }, y: { title: { text: 'Rewards (ROWAN)', display: true, - color: 'rgba(255,255,255,0.9)' + color: 'rgba(255,255,255,0.9)', }, ticks: { - color: 'rgba(255,255,255,0.9)' - } - } - } - } + color: 'rgba(255,255,255,0.9)', + }, + }, + }, + }, }; const ctx = this.myRef.current.getContext('2d'); const chart = new Chart(ctx, config); this.setState( { - chart: chart + chart: chart, }, () => { incrementallyUpdateChart(chart); @@ -108,37 +125,37 @@ class DataStackAll extends React.Component { ); } - clearD3 () { + clearD3() { if (!this.state || !this.state.chart) return; this.state.chart.clear(); } - componentDidMount () { + componentDidMount() { this.props.onLoadingStateChange(true); fetchStack(this.props.type, this.props.network) - .then(rewardData => { + .then((rewardData) => { this.setState({ rewardData }, this.renderD3); this.props.onLoadingStateChange(false); }) - .catch(e => this.props.onLoadingStateChange(false)); + .catch((e) => this.props.onLoadingStateChange(false)); } - componentWillUnmount () { + componentWillUnmount() { this.clearD3(); } - render () { + render() { if (!this.state.rewardData) {
Loading Leaderboard...
; } return ( -
+
); diff --git a/js/src/utils.js b/js/src/utils.js index b1a5366..3a326d6 100644 --- a/js/src/utils.js +++ b/js/src/utils.js @@ -1,8 +1,9 @@ import moment from 'moment'; -import { START_DATETIME } from './config'; - -export const timestampToDate = timestamp => { - const start = moment.utc(START_DATETIME); +import serverConfigs from './serverConfig'; +const serverConfig = + serverConfigs[window.sessionStorage.getItem('rewardProgram')]; +export const timestampToDate = (timestamp) => { + const start = moment.utc(serverConfig.START_DATETIME); const date = start.add(timestamp, 'm'); return date.toDate(); };