-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update to v14 #84
base: master
Are you sure you want to change the base?
Update to v14 #84
Changes from 15 commits
3e01627
43d2ce7
7e10cc3
b692a5b
d7e727c
4fdf27d
1faced3
a74974f
2b75251
7fad511
62aaaed
391a496
ef0cc5d
5565481
651b9f8
d86ffc4
a8d13dc
d2f9e07
1bf4865
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -146,51 +146,47 @@ export const PayoutsProvider = ({ | |
new BigNumber(b).minus(a).toNumber() | ||
); | ||
|
||
// Helper function to check which eras a validator was exposed in. | ||
const validatorExposedEras = (validator: string) => { | ||
const exposedEras: string[] = []; | ||
for (const era of erasToCheck) | ||
if ( | ||
Object.values( | ||
Object.keys(getLocalEraExposure(network.name, era, activeAccount)) | ||
)?.[0] === validator | ||
) | ||
exposedEras.push(era); | ||
return exposedEras; | ||
}; | ||
|
||
// Fetch controllers in order to query ledgers. | ||
const bondedResults = | ||
await api.query.staking.bonded.multi<AnyApi>(uniqueValidators); | ||
const validatorControllers: Record<string, string> = {}; | ||
for (let i = 0; i < bondedResults.length; i++) { | ||
const ctlr = bondedResults[i].unwrapOr(null); | ||
if (ctlr) validatorControllers[uniqueValidators[i]] = ctlr; | ||
if (ctlr) { | ||
validatorControllers[uniqueValidators[i]] = ctlr; | ||
} | ||
} | ||
|
||
// Fetch ledgers to determine which eras have not yet been claimed per validator. Only includes | ||
// eras that are in `erasToCheck`. | ||
const ledgerResults = await api.query.staking.ledger.multi<AnyApi>( | ||
Object.values(validatorControllers) | ||
); | ||
const unclaimedRewards: Record<string, string[]> = {}; | ||
for (const ledgerResult of ledgerResults) { | ||
const ledger = ledgerResult.unwrapOr(null)?.toHuman(); | ||
if (ledger) { | ||
// get claimed eras within `erasToCheck`. | ||
const erasClaimed = ledger.claimedRewards | ||
.map((e: string) => rmCommas(e)) | ||
.filter( | ||
(e: string) => | ||
new BigNumber(e).isLessThanOrEqualTo(startEra) && | ||
new BigNumber(e).isGreaterThanOrEqualTo(endEra) | ||
); | ||
|
||
// filter eras yet to be claimed | ||
unclaimedRewards[ledger.stash] = erasToCheck | ||
.map((e) => e.toString()) | ||
.filter((r: string) => validatorExposedEras(ledger.stash).includes(r)) | ||
.filter((r: string) => !erasClaimed.includes(r)); | ||
|
||
// Accumulate calls to fetch unclaimed rewards for each era for all validators. | ||
const unclaimedRewardsEntries = erasToCheck | ||
.map((era) => uniqueValidators.map((v) => [era, v])) | ||
.flat(); | ||
|
||
const results = await Promise.all( | ||
unclaimedRewardsEntries.map(([era, v]) => | ||
api.query.staking.claimedRewards<AnyApi>(era, v) | ||
) | ||
); | ||
|
||
for (let i = 0; i < results.length; i++) { | ||
const pages = results[i].toHuman() || []; | ||
const era = unclaimedRewardsEntries[i][0]; | ||
const validator = unclaimedRewardsEntries[i][1]; | ||
const exposure = getLocalEraExposure(network.name, era, activeAccount); | ||
const exposedPage = | ||
exposure?.[validator]?.exposedPage !== undefined | ||
? String(exposure[validator].exposedPage) | ||
: undefined; | ||
|
||
// Add to `unclaimedRewards` if payout page has not yet been claimed. | ||
if (!pages.includes(exposedPage)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems to me this checks if page 0 is claimed. What I think if should do is check if
or
I'm not sure how the |
||
if (unclaimedRewards?.[validator]) { | ||
unclaimedRewards[validator].push(era); | ||
} else { | ||
unclaimedRewards[validator] = [era]; | ||
} | ||
} | ||
} | ||
|
||
|
@@ -199,15 +195,19 @@ export const PayoutsProvider = ({ | |
erasToCheck.forEach((era) => { | ||
const eraValidators: string[] = []; | ||
Object.entries(unclaimedRewards).forEach(([validator, eras]) => { | ||
if (eras.includes(era)) eraValidators.push(validator); | ||
if (eras.includes(era)) { | ||
eraValidators.push(validator); | ||
} | ||
}); | ||
if (eraValidators.length > 0) unclaimedByEra[era] = eraValidators; | ||
if (eraValidators.length > 0) { | ||
unclaimedByEra[era] = eraValidators; | ||
} | ||
}); | ||
|
||
// Accumulate calls needed to fetch data to calculate rewards. | ||
const calls: AnyApi[] = []; | ||
Object.entries(unclaimedByEra).forEach(([era, validators]) => { | ||
if (validators.length > 0) | ||
if (validators.length > 0) { | ||
calls.push( | ||
Promise.all([ | ||
api.query.staking.erasValidatorReward<AnyApi>(era), | ||
|
@@ -217,6 +217,7 @@ export const PayoutsProvider = ({ | |
), | ||
]) | ||
); | ||
} | ||
}); | ||
|
||
// Iterate calls and determine unclaimed payouts. | ||
|
@@ -247,6 +248,7 @@ export const PayoutsProvider = ({ | |
const staked = new BigNumber(localExposed?.staked || '0'); | ||
const total = new BigNumber(localExposed?.total || '0'); | ||
const isValidator = localExposed?.isValidator || false; | ||
const exposedPage = localExposed?.exposedPage || 0; | ||
|
||
// Calculate the validator's share of total era payout. | ||
const totalRewardPoints = new BigNumber( | ||
|
@@ -269,11 +271,13 @@ export const PayoutsProvider = ({ | |
.dividedBy(total) | ||
.plus(isValidator ? valCut : 0); | ||
|
||
unclaimed[era] = { | ||
...unclaimed[era], | ||
[validator]: unclaimedPayout.toString(), | ||
}; | ||
j++; | ||
if (!unclaimedPayout.isZero()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
unclaimed[era] = { | ||
...unclaimed[era], | ||
[validator]: [exposedPage, unclaimedPayout.toString()], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, it’s not always 0. The value of |
||
}; | ||
j++; | ||
} | ||
} | ||
|
||
// This is not currently useful for preventing re-syncing. Need to know the eras that have | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ import { | |
greaterThanZero, | ||
isNotZero, | ||
localStorageOrDefault, | ||
rmCommas, | ||
setStateWithRef, | ||
} from '@polkadot-cloud/utils'; | ||
import BigNumber from 'bignumber.js'; | ||
|
@@ -16,6 +17,7 @@ import type { PayeeConfig, PayeeOptions } from 'contexts/Setup/types'; | |
import type { | ||
EraStakers, | ||
Exposure, | ||
ExposureOther, | ||
StakingContextInterface, | ||
StakingMetrics, | ||
StakingTargets, | ||
|
@@ -207,9 +209,7 @@ export const StakingProvider = ({ | |
if (localExposures) { | ||
exposures = localExposures; | ||
} else { | ||
exposures = formatRawExposures( | ||
await api.query.staking.erasStakers.entries(era) | ||
); | ||
exposures = await getPagedErasStakers(era); | ||
} | ||
|
||
// For resource limitation concerns, only store the current era in local storage. | ||
|
@@ -329,6 +329,63 @@ export const StakingProvider = ({ | |
!activeAccount || | ||
(!hasController() && !isBonding() && !isNominating() && !isUnlocking()); | ||
|
||
// Fetch eras stakers from storage. | ||
const getPagedErasStakers = async (era: string) => { | ||
if (!api) { | ||
return []; | ||
} | ||
|
||
const overview: AnyApi = | ||
await api.query.staking.erasStakersOverview.entries(era); | ||
|
||
const validators = overview.reduce( | ||
(prev: Record<string, Exposure>, [keys, value]: AnyApi) => { | ||
const validator = keys.toHuman()[1]; | ||
const { own, total } = value.toHuman(); | ||
return { ...prev, [validator]: { own, total } }; | ||
}, | ||
{} | ||
); | ||
const validatorKeys = Object.keys(validators); | ||
|
||
const pagedResults = await Promise.all( | ||
validatorKeys.map((v) => | ||
api.query.staking.erasStakersPaged.entries(era, v) | ||
Comment on lines
+339
to
+353
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is an edge case of an era in which migration happens. In that era the |
||
) | ||
); | ||
|
||
const result: Exposure[] = []; | ||
let i = 0; | ||
kryzasada marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for (const pagedResult of pagedResults) { | ||
const validator = validatorKeys[i]; | ||
const { own, total } = validators[validator]; | ||
const others = pagedResult.reduce( | ||
(prev: ExposureOther[], [, v]: AnyApi) => { | ||
const o = v.toHuman()?.others || []; | ||
if (!o.length) { | ||
return prev; | ||
} | ||
return prev.concat(o); | ||
}, | ||
[] | ||
); | ||
|
||
result.push({ | ||
keys: [rmCommas(era), validator], | ||
val: { | ||
total: rmCommas(total), | ||
own: rmCommas(own), | ||
others: others.map(({ who, value }) => ({ | ||
who, | ||
value: rmCommas(value), | ||
})), | ||
}, | ||
}); | ||
i++; | ||
} | ||
return result; | ||
}; | ||
|
||
// Helper function to get the lowest reward from an active validator. | ||
const getLowestRewardFromStaker = (address: MaybeAccount) => { | ||
const staker = eraStakersRef.current.stakers.find( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,7 @@ import { greaterThanZero, shuffle } from '@polkadot-cloud/utils'; | |
import BigNumber from 'bignumber.js'; | ||
import React, { useEffect, useRef, useState } from 'react'; | ||
import { ValidatorCommunity } from '@polkadot-cloud/assets/validators'; | ||
import type { AnyApi, Fn, Sync } from 'types'; | ||
import type { AnyApi, AnyJson, Fn, Sync } from 'types'; | ||
import { useEffectIgnoreInitial } from '@polkadot-cloud/react/hooks'; | ||
import { useApi } from 'contexts/Api'; | ||
import { useBonded } from 'contexts/Bonded'; | ||
|
@@ -239,10 +239,11 @@ export const ValidatorsProvider = ({ | |
// Subscribe to active session validators. | ||
const subscribeSessionValidators = async () => { | ||
if (!api || !isReady) return; | ||
const unsub: AnyApi = await api.query.session.validators((v: AnyApi) => { | ||
setSessionValidators(v.toHuman()); | ||
sessionUnsub.current = unsub; | ||
}); | ||
const sessionValidatorsRaw: AnyApi = | ||
await api.query.staking.validators.entries(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if this should be really session validators or eras validators. Either way This returns not only era's validators, but also as stated here "wannabe". I think we should use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused. On production |
||
setSessionValidators( | ||
sessionValidatorsRaw.map(([keys]: AnyApi) => keys?.toHuman()?.toString()) | ||
); | ||
}; | ||
|
||
// Subscribe to active parachain validators. | ||
|
@@ -289,7 +290,7 @@ export const ValidatorsProvider = ({ | |
|
||
return Object.fromEntries( | ||
Object.entries( | ||
Object.fromEntries(identities.map((k, i) => [addresses[i], k])) | ||
Object.fromEntries(identities.map((k, i) => [addresses[i], k?.[0]])) | ||
).filter(([, v]) => v !== null) | ||
); | ||
}; | ||
|
@@ -319,18 +320,17 @@ export const ValidatorsProvider = ({ | |
await api.query.identity.identityOf.multi( | ||
Object.values(supers).map(({ superOf }) => superOf[0]) | ||
) | ||
).map((superIdentity) => superIdentity.toHuman()); | ||
).map((superIdentity: AnyJson) => superIdentity.toHuman()); | ||
|
||
const supersWithIdentity = Object.fromEntries( | ||
Object.entries(supers).map(([k, v]: AnyApi, i) => [ | ||
k, | ||
{ | ||
...v, | ||
identity: superIdentities[i], | ||
identity: superIdentities[i]?.[0], | ||
}, | ||
]) | ||
); | ||
|
||
return supersWithIdentity; | ||
}; | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this supports only new storage key, but it would be nice to support
ledger.legacyClaimedRewards
as well 😅