Skip to content
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

Merged
merged 28 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
3e01627
Update polkadot packages
kryzasada Oct 28, 2024
43d2ce7
Fix fetching erasStakers
kryzasada Nov 3, 2024
7e10cc3
Add getPagedErasStakers
kryzasada Nov 3, 2024
b692a5b
Fix validators stats
kryzasada Nov 3, 2024
d7e727c
Fix validators list
kryzasada Nov 3, 2024
4fdf27d
Clean code
kryzasada Nov 3, 2024
1faced3
Merge pull request #1 from kryzasada/update-to-polkadot-api-v14
kryzasada Nov 4, 2024
a74974f
Delete comment
kryzasada Nov 4, 2024
2b75251
Merge branch 'update-to-polkadot-api-v14'
kryzasada Nov 4, 2024
7fad511
Fix new bugs
kryzasada Nov 5, 2024
62aaaed
Reset fetchBondedPools
kryzasada Nov 5, 2024
391a496
Merge branch 'update-to-polkadot-api-v14'
kryzasada Nov 5, 2024
ef0cc5d
Fix Payouts
kryzasada Nov 7, 2024
5565481
Fix bugs
kryzasada Nov 8, 2024
651b9f8
Fix claim and payout
kryzasada Nov 11, 2024
d86ffc4
Fix redundant
kryzasada Nov 13, 2024
a8d13dc
Fix Pending Payouts
kryzasada Nov 14, 2024
d2f9e07
Update src/locale/en/modals.json
kryzasada Nov 14, 2024
1bf4865
Update src/locale/en/tips.json
kryzasada Nov 14, 2024
6cb78f0
Add old erasStakers to data fetch
kryzasada Nov 18, 2024
3edcaf2
Fix review issues
kryzasada Nov 18, 2024
d026117
Merge branch 'master' of https://github.com/kryzasada/aleph-zero-dash…
kryzasada Nov 18, 2024
a55bdbc
Fix Claim Payouts
kryzasada Nov 19, 2024
62ad65a
Fix code review issues
kryzasada Nov 20, 2024
aa97966
Fix lint
kryzasada Dec 6, 2024
899d675
Fix package
kryzasada Dec 6, 2024
07360da
Fix build error
kryzasada Dec 6, 2024
658dd4f
Merge branch 'master' into master
youPickItUp Dec 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@
"@polkadot-cloud/core": "^0.1.20",
"@polkadot-cloud/react": "^0.1.39",
"@polkadot-cloud/utils": "^0.0.11",
"@polkadot/api": "^10.9.1",
"@polkadot/keyring": "^12.1.1",
"@polkadot/rpc-provider": "^10.9.1",
"@polkadot/util": "^12.4.2",
"@polkadot/util-crypto": "12.4.2",
"@polkadot/api": "^14.2.1",
"@polkadot/keyring": "^13.2.2",
"@polkadot/rpc-provider": "^14.2.1",
"@polkadot/types": "^15.0.1",
"@polkadot/util": "^13.2.2",
"@polkadot/util-crypto": "13.2.2",
"@substrate/connect": "^0.7.31",
"@zondax/ledger-substrate": "^0.41.2",
"bignumber.js": "^9.1.2",
Expand Down
6 changes: 6 additions & 0 deletions src/config/networks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ReactComponent as AzeroIconSVG } from 'img/a0_icon.svg';
import { ReactComponent as AzeroInlineSVG } from 'img/a0_inline.svg';
import { ReactComponent as AzeroLogoSVG } from 'img/a0_logo.svg';
import type { Networks } from 'types';
import BigNumber from 'bignumber.js';

export const NetworkList: Networks = {};

Expand Down Expand Up @@ -63,6 +64,7 @@ if (import.meta.env.VITE_DISABLE_MAINNET !== '1') {
stakeTarget: 0.5,
},
defaultFeeReserve: 0.1,
maxExposurePageSize: new BigNumber(1024),
} as const;

NetworkList[alephZero.name] = alephZero;
Expand Down Expand Up @@ -122,6 +124,7 @@ if (import.meta.env.VITE_DISABLE_TESTNET !== '1') {
stakeTarget: 0.5,
},
defaultFeeReserve: 0.1,
maxExposurePageSize: new BigNumber(1024),
} as const;

NetworkList[alephZeroTestnet.name] = alephZeroTestnet;
Expand Down Expand Up @@ -180,6 +183,7 @@ if (import.meta.env.VITE_ENABLE_CUSTOM_NETWORK === '1') {
stakeTarget: 0.5,
},
defaultFeeReserve: 0.1,
maxExposurePageSize: new BigNumber(1024),
} as const;

NetworkList[azeroCustom.name] = azeroCustom;
Expand Down Expand Up @@ -239,6 +243,7 @@ if (import.meta.env.VITE_DISABLE_DEVNET !== '1') {
stakeTarget: 0.5,
},
defaultFeeReserve: 0.1,
maxExposurePageSize: new BigNumber(1024),
} as const;

NetworkList[azeroDevnet.name] = azeroDevnet;
Expand Down Expand Up @@ -298,6 +303,7 @@ if (import.meta.env.MODE === 'development') {
stakeTarget: 0.5,
},
defaultFeeReserve: 0.1,
maxExposurePageSize: new BigNumber(1024),
} as const;

NetworkList[azeroLocal.name] = azeroLocal;
Expand Down
1 change: 1 addition & 0 deletions src/contexts/Api/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const consts: APIConstants = {
bondDuration: new BigNumber(0),
maxNominations: new BigNumber(0),
sessionsPerEra: new BigNumber(0),
maxExposurePageSize: new BigNumber(0),
maxNominatorRewardedPerValidator: new BigNumber(0),
historyDepth: new BigNumber(0),
maxElectingVoters: new BigNumber(0),
Expand Down
6 changes: 6 additions & 0 deletions src/contexts/Api/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ export const APIProvider = ({ children }: { children: React.ReactNode }) => {
newApi.consts.staking.historyDepth,
null, // newApi.consts.fastUnstake.deposit, // we don't use fastUnstake
newApi.consts.nominationPools.palletId,
newApi.consts.staking.maxExposurePageSize,
]);

// format constants.
Expand Down Expand Up @@ -274,6 +275,10 @@ export const APIProvider = ({ children }: { children: React.ReactNode }) => {

const expectedEraTime = FallbackExpectedEraTime;

const maxExposurePageSize = result[11]
? new BigNumber(result[11].toString())
: NetworkList[network.name].maxExposurePageSize;

setConsts({
chainDecimals: newApi.registry.chainDecimals[0],
bondDuration,
Expand All @@ -288,6 +293,7 @@ export const APIProvider = ({ children }: { children: React.ReactNode }) => {
poolsPalletId,
existentialDeposit,
fastUnstakeDeposit,
maxExposurePageSize,
});
setApi(newApi);
};
Expand Down
1 change: 1 addition & 0 deletions src/contexts/Api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface APIConstants {
bondDuration: BigNumber;
maxNominations: BigNumber;
sessionsPerEra: BigNumber;
maxExposurePageSize: BigNumber;
maxNominatorRewardedPerValidator: BigNumber;
historyDepth: BigNumber;
maxElectingVoters: BigNumber;
Expand Down
3 changes: 2 additions & 1 deletion src/contexts/FastUnstake/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const FastUnstakeProvider = ({
const { metrics, activeEra } = useNetworkMetrics();
const { getNominationStatus } = useNominationStatus();
const { fastUnstakeErasToCheckPerBlock } = metrics;
const { bondDuration } = consts;
const { bondDuration, maxExposurePageSize } = consts;
const { nominees } = getNominationStatus(activeAccount, 'nominator');

// store whether a fast unstake check is in progress.
Expand Down Expand Up @@ -242,6 +242,7 @@ export const FastUnstakeProvider = ({
who: activeAccount,
networkName: network.name,
exitOnExposed: true,
maxExposurePageSize: maxExposurePageSize.toString(),
exposures,
});
};
Expand Down
2 changes: 1 addition & 1 deletion src/contexts/Payouts/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const setLocalUnclaimedPayouts = (
network: NetworkName,
era: string,
who: string,
unclaimdPayouts: Record<string, string>,
unclaimdPayouts: Record<string, [number, string]>,
endEra: string
) => {
const current = JSON.parse(
Expand Down
131 changes: 107 additions & 24 deletions src/contexts/Payouts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ export const PayoutsProvider = ({
}: {
children: React.ReactNode;
}) => {
const { api, network } = useApi();
const { api, consts, network } = useApi();
const { activeAccount } = useConnect();
const { activeEra } = useNetworkMetrics();
const { isNominating, fetchEraStakers } = useStaking();
const { maxExposurePageSize } = consts;

// Store active accont's payout state.
const [unclaimedPayouts, setUnclaimedPayouts] =
Expand Down Expand Up @@ -87,6 +88,7 @@ export const PayoutsProvider = ({
era: String(era),
who: activeAccount,
networkName: network.name,
maxExposurePageSize: maxExposurePageSize.toString(),
exposures,
});
}
Expand Down Expand Up @@ -151,8 +153,8 @@ export const PayoutsProvider = ({
const exposedEras: string[] = [];
for (const era of erasToCheck)
if (
Object.values(
Object.keys(getLocalEraExposure(network.name, era, activeAccount))
Object.keys(
getLocalEraExposure(network.name, era, activeAccount)
)?.[0] === validator
)
exposedEras.push(era);
Expand All @@ -165,49 +167,126 @@ export const PayoutsProvider = ({
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`.
// Start old unclaimed payouts logic
const ledgerResults = await api.query.staking.ledger.multi<AnyApi>(
Object.values(validatorControllers)
);
const unclaimedRewards: Record<string, string[]> = {};

const oldClaimedRewards: Record<string, string[]> = {};
for (const ledgerResult of ledgerResults) {
const ledger = ledgerResult.unwrapOr(null)?.toHuman();
if (ledger) {
if (ledger && ledger.legacyClaimedRewards.length > 0) {
// get claimed eras within `erasToCheck`.
const erasClaimed = ledger.claimedRewards
const erasClaimed = ledger.legacyClaimedRewards
.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
oldClaimedRewards[ledger.stash] = erasToCheck
.map((e) => e.toString())
.filter((r: string) => validatorExposedEras(ledger.stash).includes(r))
.filter((r: string) => !erasClaimed.includes(r));
.filter((r: string) => erasClaimed.includes(r));
}
}

// Reformat old unclaimed rewards to match new format.
const oldClaimedByEra: Record<string, string[]> = {};
erasToCheck.forEach((era) => {
const eraValidators: string[] = [];
Object.entries(oldClaimedRewards).forEach(([validator, eras]) => {
if (eras.includes(era)) {
eraValidators.push(validator);
}
});
if (eraValidators.length > 0) {
oldClaimedByEra[era] = eraValidators;
}
});

const unclaimedRewards: Record<string, string[]> = {};

// 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)
kryzasada marked this conversation as resolved.
Show resolved Hide resolved
)
);

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)
: '0';

// Add to `unclaimedRewards` if payout page has not yet been claimed.
if (!pages.includes(exposedPage)) {
kryzasada marked this conversation as resolved.
Show resolved Hide resolved
if (unclaimedRewards?.[validator]) {
unclaimedRewards[validator].push(era);
} else {
unclaimedRewards[validator] = [era];
}
}
}

// Reformat unclaimed rewards to be { era: validators[] }.
const unclaimedByEra: Record<string, string[]> = {};
let unclaimedRewardsByEra: Record<string, string[]> = {};
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) {
unclaimedRewardsByEra[era] = eraValidators;
}
});

function mergeClaimedData(
unclaimedByEra: Record<string, string[]>,
claimedByEra: Record<string, string[]>
): Record<string, string[]> {
const result = { ...unclaimedByEra };

for (const era in claimedByEra) {
if (era in result) {
result[era] = result[era].filter(
(validator) => !claimedByEra[era].includes(validator)
);

if (result[era].length === 0) {
delete result[era];
}
}
}

return result;
}
unclaimedRewardsByEra = mergeClaimedData(
unclaimedRewardsByEra,
oldClaimedByEra
);

// Accumulate calls needed to fetch data to calculate rewards.
const calls: AnyApi[] = [];
Object.entries(unclaimedByEra).forEach(([era, validators]) => {
if (validators.length > 0)
Object.entries(unclaimedRewardsByEra).forEach(([era, validators]) => {
if (validators.length > 0) {
calls.push(
Promise.all([
api.query.staking.erasValidatorReward<AnyApi>(era),
Expand All @@ -217,16 +296,17 @@ export const PayoutsProvider = ({
),
])
);
}
});

// Iterate calls and determine unclaimed payouts.
const unclaimed: UnclaimedPayouts = {};
let i = 0;
for (const [reward, points, ...prefs] of await Promise.all(calls)) {
const era = Object.keys(unclaimedByEra)[i];
const era = Object.keys(unclaimedRewardsByEra)[i];
const eraTotalPayout = new BigNumber(rmCommas(reward.toHuman()));
const eraRewardPoints = points.toHuman();
const unclaimedValidators = unclaimedByEra[era];
const unclaimedValidators = unclaimedRewardsByEra[era];

let j = 0;
for (const pref of prefs) {
Expand All @@ -247,6 +327,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(
Expand All @@ -269,11 +350,13 @@ export const PayoutsProvider = ({
.dividedBy(total)
.plus(isValidator ? valCut : 0);

unclaimed[era] = {
...unclaimed[era],
[validator]: unclaimedPayout.toString(),
};
j++;
if (!unclaimedPayout.isZero()) {
youPickItUp marked this conversation as resolved.
Show resolved Hide resolved
unclaimed[era] = {
...unclaimed[era],
[validator]: [exposedPage, unclaimedPayout.toString()],
youPickItUp marked this conversation as resolved.
Show resolved Hide resolved
};
j++;
}
}

// This is not currently useful for preventing re-syncing. Need to know the eras that have
Expand Down
3 changes: 2 additions & 1 deletion src/contexts/Payouts/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ export type PayoutsContextInterface = {

export type UnclaimedPayouts = Record<string, EraUnclaimedPayouts> | null;

export type EraUnclaimedPayouts = Record<string, string>;
export type EraUnclaimedPayouts = Record<string, [number, string]>;

export interface LocalValidatorExposure {
staked: string;
total: string;
share: string;
isValidator: boolean;
exposedPage: number;
}
Loading
Loading