Skip to content

Commit

Permalink
Fabo+Jordan/livepeer improvements (#210)
Browse files Browse the repository at this point in the history
* add livepeer first files

* first tentative. add transcoder type and queries

* progress

* progress + trying

* eslint fix

* working

* recover missing config enabletestnet

* delete livepeer from resolver and schema

* try getallvalidators in livepeerv0-source

* todos: own subscription and own reducers

* eslint fix

* refactored into graphql data source

* add livepeer to networks json

* little hack (temporary)

* fix silly eslint error

* start playing

* fetch works. add to apollo

* fix lint

* get last round id/height

* fix lint

* set all feature flags to false

* forward. add livepeer reducer. 400 bad request

* fix lint

* kind of working

* fix networkId

* colin/ make node subscription great

* use transcoders no orchestrators. getValidator

* connecting to block height and validators display

* minor lint

* add validator name

* provisory getselfstake

* improve validator reducer

* use bignumber lib. add getdelegatorsstake

* fix lint

* fix gettotal stake delegators

* fix lint

* clean up

* get self stake

* delete livepeer-queries

* handle url with node url class

* delete extra back ticks

* invisible lint fix

* better prevent than regret

* delete getlastrounid function

* fix total stake and self stake

* remove console log

* use getblockheight to find last block

* get validators from db

* get more names

* get ALL validators

* use validator query string

* use bignumber instead of parseint

* minor lint

* fix validators names

* improve condition

* get all validators for real

* fix search filter for operator address

* fix search filter

* finishing fixes (formatbech32 and bignumber)

* try implementing votingpower. no api support

* refactoring and renaming

* enable votingpower

* livepeer is mainnet

* fixed values to be correct

* comments

* cleanup

* refactored

* removed file

* fix getexpectedreturns

* fix validator name logic

* add todos

* lint
  • Loading branch information
faboweb authored Dec 17, 2019
1 parent d15ee5f commit c178ce1
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 92 deletions.
87 changes: 25 additions & 62 deletions lib/livepeerV0-source.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const { GraphQLDataSource } = require('./helpers/GraphQLDataSource')
const BigNumber = require('bignumber.js')
const _ = require('lodash')

const LPT_CONVERSION = `1000000000000000000`

class LivepeerV0API extends GraphQLDataSource {
constructor(network) {
Expand All @@ -12,20 +13,8 @@ class LivepeerV0API extends GraphQLDataSource {
id
active
status
lastRewardRound {
id
}
rewardCut
feeShare
pricePerSegment
pendingRewardCut
pendingFeeShare
pendingPricePerSegment
totalStake
delegators {
id
bondedAmount
}
`
}

Expand All @@ -48,12 +37,7 @@ class LivepeerV0API extends GraphQLDataSource {
`
rounds(where: { id: ${blockHeight} }) {
id
initialized
length
timestamp
lastInitializedRound
startBlock
mintableTokens
}
`
)
Expand All @@ -77,7 +61,7 @@ class LivepeerV0API extends GraphQLDataSource {
async getValidators(active) {
const { transcoders } = await this.query(
`
transcoders(where: {active: ${active}, status_not: null}) {
transcoders(where:{active:${active ? 'true' : 'false'}}) {
${this.validatorQueryString}
}
`
Expand All @@ -86,52 +70,19 @@ class LivepeerV0API extends GraphQLDataSource {
}

async getTotalStakedTokens(transcoders) {
let totalStakedTokens = 0
transcoders.forEach(validator => {
totalStakedTokens = BigNumber(totalStakedTokens)
.plus(BigNumber(validator.totalStake))
.plus(BigNumber(validator.totalDelegatorsStake))
})
return totalStakedTokens.div('10000000000000000')
}

async getDelegatorsStake(validator) {
let totalDelegatorsStake = 0
validator.delegators.forEach(delegator => {
totalDelegatorsStake = BigNumber(totalDelegatorsStake).plus(
BigNumber(delegator.bondedAmount)
)
})
validator = {
...validator,
totalDelegatorsStake: totalDelegatorsStake.toString()
}
return validator
}

async getValidator(id) {
const { transcoder } = await this.query(
`
transcoders(where: { id: ${id} }) {
${this.validatorQueryString}
}
`
)
return this.reducers.validatorReducer(transcoder)
const totalStakedTokens = transcoders.reduce((sum, validator) => {
return BigNumber(sum).plus(BigNumber(validator.totalStake))
}, BigNumber(0))
// LPT is represented by 1:1000000000000000000 internally
return totalStakedTokens.div(LPT_CONVERSION)
}

async getAllValidators() {
// Until they fix their API we need to query separately for active and inactive validators
const inactiveTranscoders = await this.getValidators(false)
const activeTranscoders = await this.getValidators(true)
const transcoders = _.union(inactiveTranscoders, activeTranscoders)
const modTranscoders = await Promise.all(
transcoders.map(
async validator => await this.getDelegatorsStake(validator)
)
)
const totalStakedTokens = await this.getTotalStakedTokens(modTranscoders)
return modTranscoders.map(validator =>
const totalStakedTokens = await this.getTotalStakedTokens(activeTranscoders)
const transcoders = inactiveTranscoders.concat(activeTranscoders)
return transcoders.map(validator =>
this.reducers.validatorReducer(
this.networkId,
validator,
Expand All @@ -140,9 +91,21 @@ class LivepeerV0API extends GraphQLDataSource {
)
}

async getSelfStake(validator) {
return validator.selfStake
async getSelfStake() {
return undefined
}

getExpectedReturns(validator) {
return this.reducers.livepeerExpectedRewardsReducer({
rewardCut: validator.rewardCut,
// assuming following fixed values which is not true and needs to be queried via the future protocol query
inflation: 1172, // TODO change to API call
inflationChange: 3, // TODO change to API call
totalSupply: '17919760877408440511808797', // TODO change to API call
totalStaked: '11426550355221975909835117' // TODO change to API call
})
}
}

module.exports = LivepeerV0API
module.exports.LPT_CONVERSION = LPT_CONVERSION
95 changes: 66 additions & 29 deletions lib/reducers/livepeerV0-reducers.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const BigNumber = require('bignumber.js')
const { LPT_CONVERSION } = require('../livepeerV0-source')

function blockReducer(networkId, block) {
return {
Expand All @@ -11,23 +12,16 @@ function blockReducer(networkId, block) {
}

function validatorCommissionReducer(commission) {
return commission
? BigNumber(commission)
.div('1000000')
.toString()
: null
// precision of commision is 1:1000000
return BigNumber(commission).div('1000000')
}

function validatorStatusEnumReducer(active) {
return active ? 'ACTIVE' : 'INACTIVE'
}

function bigNumberReducer(bignumber) {
return bignumber
? BigNumber(bignumber)
.div('1000000000000000000')
.toString()
: null
return bignumber ? BigNumber(bignumber).div(LPT_CONVERSION) : null
}

function formatBech32Reducer(address) {
Expand All @@ -36,39 +30,82 @@ function formatBech32Reducer(address) {
}
}

function totalStakeReducer(validator) {
return BigNumber(validator.totalStake)
.plus(BigNumber(validator.totalDelegatorsStake))
.div('1000000000000000000')
.toString()
}

function votingPowerReducer(validator, totalStakedTokens) {
const totalValidatorStake = BigNumber(validator.totalStake).plus(
BigNumber(validator.totalDelegatorsStake)
return BigNumber(bigNumberReducer(validator.totalStake)).div(
totalStakedTokens
)
return totalValidatorStake
.div('10000000000000000')
.div(totalStakedTokens)
.toString()
}

function livepeerExpectedRewardsReducer({
rewardCut,
inflation,
inflationChange,
totalSupply,
totalStaked
}) {
return expectedRewardsPerToken({
rewardCut: validatorCommissionReducer(rewardCut).toNumber(),
inflation: validatorCommissionReducer(inflation).toNumber(),
inflationChange: validatorCommissionReducer(inflationChange).toNumber(),
totalSupply: bigNumberReducer(totalSupply).toNumber(),
totalStaked: bigNumberReducer(totalStaked).toNumber()
})
}

// extracted from the livepeer explorer
function expectedRewardsPerToken({
rewardCut,
inflation,
inflationChange,
totalSupply,
totalStaked
}) {
const principle = 1

let hoursPerYear = 8760
let averageHoursPerRound = 21
let roundsPerYear = hoursPerYear / averageHoursPerRound

let totalRewardTokens = 0
let roi = 0
let percentOfTotalStaked = principle / totalStaked
let participationRate = totalStaked / totalSupply
let totalRewardTokensMinusFee
let currentMintableTokens

for (let i = 0; i < roundsPerYear; i++) {
if (inflation < 0) break
currentMintableTokens = totalSupply * inflation
totalRewardTokens = percentOfTotalStaked * currentMintableTokens
totalRewardTokensMinusFee =
totalRewardTokens - totalRewardTokens * rewardCut
roi = roi + totalRewardTokensMinusFee
totalSupply = totalSupply + currentMintableTokens
inflation =
participationRate > 0.5
? inflation - inflationChange
: inflation + inflationChange
}
return roi
}

function validatorReducer(networkId, validator, totalStakedTokens) {
return {
networkId,
operatorAddress: validator.id,
tokens: totalStakeReducer(validator),
selfStake: bigNumberReducer(validator.totalStake),
tokens: bigNumberReducer(validator.totalStake),
selfStake: bigNumberReducer(validator.pendingStake), // TODO (when we have the new API up and running)
commission: validatorCommissionReducer(validator.rewardCut),
status: validatorStatusEnumReducer(validator.active),
statusDetailed: validator.status, // Registered/Unregistered
votingPower: votingPowerReducer(validator, totalStakedTokens),
expectedReturns: validatorCommissionReducer(validator.feeShare) // This percentage means how much from the reward the delegator is going to get (each round I presume)
statusDetailed: validator.status, // Registered/Unregistered,
rewardCut: validator.rewardCut,
votingPower: votingPowerReducer(validator, totalStakedTokens)
}
}

module.exports = {
validatorReducer,
blockReducer,
formatBech32Reducer
formatBech32Reducer,
livepeerExpectedRewardsReducer
}
4 changes: 3 additions & 1 deletion lib/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ async function enrichValidator(dataSources, networkId, validator) {
return {
...validator,
name: validatorInfo
? validatorInfo.name
? validatorInfo.name ||
validatorInfo.operatorAddress ||
validator.operatorAddress
: validator.name || validator.operatorAddress,
picture: validatorInfo ? validatorInfo.picture : undefined
}
Expand Down

0 comments on commit c178ce1

Please sign in to comment.