Skip to content

Commit f667e65

Browse files
authored
fix: Correct bag threshold display with CurrencyToVote conversion (#12058)
* fix: bag threshold display with CurrencyToVote conversion * chore: update comments
1 parent 7724d9c commit f667e65

File tree

3 files changed

+89
-4
lines changed

3 files changed

+89
-4
lines changed

packages/page-staking/src/Bags/useBagsList.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import type { PalletBagsListListBag } from '@polkadot/types/lookup';
66
import type { BN } from '@polkadot/util';
77
import type { BagInfo } from './types.js';
88

9-
import { useEffect, useState } from 'react';
9+
import { useEffect, useMemo, useState } from 'react';
1010

11-
import { createNamedHook, useCall, useMapKeys } from '@polkadot/react-hooks';
11+
import { createNamedHook, useApi, useCall, useCurrencyToVote, useMapKeys } from '@polkadot/react-hooks';
1212
import { BN_ZERO } from '@polkadot/util';
1313

1414
import useQueryModule from './useQueryModule.js';
@@ -54,13 +54,39 @@ function merge (prev: BagInfo[] | undefined, curr: BagInfo[]): BagInfo[] {
5454

5555
function useBagsListImpl (): BagInfo[] | undefined {
5656
const mod = useQueryModule();
57+
const { api } = useApi();
58+
const { toCurrency } = useCurrencyToVote();
5759
const [result, setResult] = useState<BagInfo[] | undefined>();
5860
const ids = useMapKeys(mod.listBags, [], KEY_OPTS);
5961
const query = useCall(ids && ids.length !== 0 && mod.listBags.multi, [ids], MULTI_OPTS);
6062

63+
const converted = useMemo(
64+
() => {
65+
if (!query || query.length === 0) {
66+
return undefined;
67+
}
68+
69+
// Westend Asset Hub uses SaturatingCurrencyToVote (no conversion needed)
70+
// See: https://github.com/paritytech/polkadot-sdk/pull/8307
71+
const chainName = api.runtimeChain.toString().toLowerCase();
72+
const isWestendAssetHub = chainName.includes('westend') && chainName.includes('asset');
73+
74+
if (isWestendAssetHub) {
75+
return query;
76+
}
77+
78+
return query.map((bag) => ({
79+
...bag,
80+
bagLower: toCurrency(bag.bagLower),
81+
bagUpper: toCurrency(bag.bagUpper)
82+
}));
83+
},
84+
[api.runtimeChain, query, toCurrency]
85+
);
86+
6187
useEffect((): void => {
62-
query && setResult((prev) => merge(prev, query));
63-
}, [query]);
88+
converted && setResult((prev) => merge(prev, converted));
89+
}, [converted]);
6490

6591
return result;
6692
}

packages/react-hooks/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export { useCoreDescriptor } from './useCoreDescriptor.js';
3838
export { useCoretimeConsts } from './useCoretimeConsts.js';
3939
export { useCoretimeEndpoint } from './useCoretimeEndpoint.js';
4040
export { useCoretimeInformation } from './useCoretimeInformation.js';
41+
export { useCurrencyToVote } from './useCurrencyToVote.js';
4142
export { useDebounce } from './useDebounce.js';
4243
export { useDelegations } from './useDelegations.js';
4344
export { useDeriveAccountFlags } from './useDeriveAccountFlags.js';
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright 2017-2025 @polkadot/react-hooks authors & contributors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { useMemo } from 'react';
5+
6+
import { BN, BN_ONE } from '@polkadot/util';
7+
8+
import { createNamedHook } from './createNamedHook.js';
9+
import { useApi } from './useApi.js';
10+
import { useCall } from './useCall.js';
11+
12+
// u64::MAX = 2^64 - 1 = 18,446,744,073,709,551,615
13+
const U64_MAX = new BN('18446744073709551615');
14+
15+
interface CurrencyToVote {
16+
// The factor to convert from currency to vote
17+
factor: BN;
18+
// Convert a currency balance to voting power
19+
toVote: (balance: BN) => BN;
20+
// Convert voting power back to currency balance
21+
toCurrency: (vote: BN) => BN;
22+
}
23+
24+
/**
25+
* Calculate the CurrencyToVote factor based on total issuance.
26+
*
27+
* - If total_issuance < 2^64, then factor = 1 (no conversion needed)
28+
* - If total_issuance >= 2^64, then factor = (total_issuance / 2^64).max(1)
29+
*
30+
* The conversion formulas is:
31+
* - vote = balance / factor
32+
*/
33+
function calculateFactor (totalIssuance: BN): BN {
34+
if (totalIssuance.lte(U64_MAX)) {
35+
return BN_ONE;
36+
}
37+
38+
const factor = totalIssuance.div(U64_MAX);
39+
40+
return factor.isZero() ? BN_ONE : factor;
41+
}
42+
43+
function useCurrencyToVoteImpl (): CurrencyToVote {
44+
const { api } = useApi();
45+
const totalIssuance = useCall<BN>(api.query.balances?.totalIssuance);
46+
47+
return useMemo((): CurrencyToVote => {
48+
const factor = totalIssuance ? calculateFactor(totalIssuance) : BN_ONE;
49+
50+
return {
51+
factor,
52+
toCurrency: (vote: BN) => vote.mul(factor),
53+
toVote: (balance: BN) => balance.div(factor)
54+
};
55+
}, [totalIssuance]);
56+
}
57+
58+
export const useCurrencyToVote = createNamedHook('useCurrencyToVote', useCurrencyToVoteImpl);

0 commit comments

Comments
 (0)