diff --git a/apps/ui/src/components/Modal/VotingPower.vue b/apps/ui/src/components/Modal/VotingPower.vue index 688ebc73e..a2618eca8 100644 --- a/apps/ui/src/components/Modal/VotingPower.vue +++ b/apps/ui/src/components/Modal/VotingPower.vue @@ -59,7 +59,7 @@ const loading = computed(
- {{ _n(Number(strategy.value) / 10 ** strategy.decimals) }} + {{ _n(Number(strategy.value) / 10 ** strategy.displayDecimals) }} {{ strategy.symbol || 'units' }}
diff --git a/apps/ui/src/helpers/utils.ts b/apps/ui/src/helpers/utils.ts index 0071df4bb..c5da8fcd3 100644 --- a/apps/ui/src/helpers/utils.ts +++ b/apps/ui/src/helpers/utils.ts @@ -610,8 +610,13 @@ export function getSocialNetworksLink(data: any) { export function getFormattedVotingPower(votingPower?: VotingPowerItem) { if (!votingPower) return; - const { totalVotingPower, decimals, symbol } = votingPower; - const value = _vp(Number(totalVotingPower) / 10 ** decimals); + const { votingPowers, symbol } = votingPower; + const value = _vp( + votingPowers.reduce( + (acc, b) => acc + Number(b.value) / 10 ** b.cumulativeDecimals, + 0 + ) + ); return symbol ? `${value} ${symbol}` : value; } diff --git a/apps/ui/src/networks/evm/actions.ts b/apps/ui/src/networks/evm/actions.ts index 1445961e7..7b6a8f6e4 100644 --- a/apps/ui/src/networks/evm/actions.ts +++ b/apps/ui/src/networks/evm/actions.ts @@ -736,11 +736,22 @@ export function createActions( if (snapshotInfo.at === null) throw new Error('EVM requires block number to be defined'); + const cumulativeDecimals = Math.max( + ...strategiesMetadata.map(metadata => metadata.decimals ?? 0) + ); + return Promise.all( strategiesAddresses.map(async (address, i) => { const strategy = getEvmStrategy(address, networkConfig); if (!strategy) - return { address, value: 0n, decimals: 0, token: null, symbol: '' }; + return { + address, + value: 0n, + displayDecimals: 0, + cumulativeDecimals: 0, + token: null, + symbol: '' + }; const strategyMetadata = await parseStrategyMetadata( strategiesMetadata[i].payload @@ -761,7 +772,8 @@ export function createActions( return { address, value, - decimals: strategiesMetadata[i]?.decimals ?? 0, + cumulativeDecimals, + displayDecimals: strategiesMetadata[i]?.decimals ?? 0, symbol: strategiesMetadata[i]?.symbol ?? '', token, swapLink: getSwapLink(strategy.type, address, chainId) diff --git a/apps/ui/src/networks/offchain/actions.ts b/apps/ui/src/networks/offchain/actions.ts index b8452faf3..db2368769 100644 --- a/apps/ui/src/networks/offchain/actions.ts +++ b/apps/ui/src/networks/offchain/actions.ts @@ -242,7 +242,14 @@ export function createActions( if (!strategy || !isAddress(voterAddress)) { return [ - { address: name, value: 0n, decimals: 0, token: null, symbol: '' } + { + address: name, + value: 0n, + cumulativeDecimals: 0, + displayDecimals: 0, + token: null, + symbol: '' + } ]; } @@ -257,7 +264,8 @@ export function createActions( return [ { address: strategiesNames[0], - decimals: 0, + cumulativeDecimals: 0, + displayDecimals: 0, symbol: '', token: '', chainId: snapshotInfo.chainId, @@ -273,7 +281,8 @@ export function createActions( return { address: strategy.name, value, - decimals, + cumulativeDecimals: decimals, + displayDecimals: decimals, symbol: strategy.params.symbol, token: strategy.params.address, chainId: strategy.network ? parseInt(strategy.network) : undefined, diff --git a/apps/ui/src/networks/offchain/index.test.ts b/apps/ui/src/networks/offchain/index.test.ts index 51398774d..08828d8b1 100644 --- a/apps/ui/src/networks/offchain/index.test.ts +++ b/apps/ui/src/networks/offchain/index.test.ts @@ -34,7 +34,8 @@ describe('offchain network', () => { ).resolves.toEqual([ { address: 'ticket', - decimals: 0, + cumulativeDecimals: 0, + displayDecimals: 0, symbol: '', token: null, value: 0n @@ -88,7 +89,8 @@ describe('offchain network', () => { ).resolves.toEqual([ { address: 'ticket', - decimals: 9, + cumulativeDecimals: 9, + displayDecimals: 9, symbol: 'SYM', token: 'TOKEN', value: result[0], @@ -97,7 +99,8 @@ describe('offchain network', () => { }, { address: 'math', - decimals: 18, + cumulativeDecimals: 18, + displayDecimals: 18, symbol: undefined, token: undefined, value: result[1], @@ -106,7 +109,8 @@ describe('offchain network', () => { }, { address: 'api', - decimals: 18, + cumulativeDecimals: 18, + displayDecimals: 18, symbol: undefined, token: undefined, value: result[2], @@ -141,7 +145,8 @@ describe('offchain network', () => { ).resolves.toEqual([ { address: 'basic', - decimals: 0, + cumulativeDecimals: 0, + displayDecimals: 0, symbol: '', token: null, value: 0n @@ -167,7 +172,8 @@ describe('offchain network', () => { { address: 'only-members', value: 1n, - decimals: 0, + cumulativeDecimals: 0, + displayDecimals: 0, symbol: '', token: '', chainId: undefined @@ -191,7 +197,8 @@ describe('offchain network', () => { { address: 'only-members', value: 0n, - decimals: 0, + cumulativeDecimals: 0, + displayDecimals: 0, symbol: '', token: '', chainId: undefined diff --git a/apps/ui/src/networks/starknet/actions.ts b/apps/ui/src/networks/starknet/actions.ts index 34bea9659..6518560d9 100644 --- a/apps/ui/src/networks/starknet/actions.ts +++ b/apps/ui/src/networks/starknet/actions.ts @@ -731,11 +731,22 @@ export function createActions( voterAddress: string, snapshotInfo: SnapshotInfo ): Promise => { + const cumulativeDecimals = Math.max( + ...strategiesMetadata.map(metadata => metadata.decimals ?? 0) + ); + return Promise.all( strategiesAddresses.map(async (address, i) => { const strategy = getStarknetStrategy(address, networkConfig); if (!strategy) - return { address, value: 0n, decimals: 0, token: null, symbol: '' }; + return { + address, + value: 0n, + cumulativeDecimals: 0, + displayDecimals: 0, + token: null, + symbol: '' + }; const strategyMetadata = await parseStrategyMetadata( strategiesMetadata[i].payload @@ -756,7 +767,8 @@ export function createActions( return { address, value, - decimals: strategiesMetadata[i]?.decimals ?? 0, + cumulativeDecimals, + displayDecimals: strategiesMetadata[i]?.decimals ?? 0, symbol: strategiesMetadata[i]?.symbol ?? '', token: strategiesMetadata[i]?.token ?? null }; diff --git a/apps/ui/src/networks/types.ts b/apps/ui/src/networks/types.ts index 6fcfcaf7e..bcbadc9ba 100644 --- a/apps/ui/src/networks/types.ts +++ b/apps/ui/src/networks/types.ts @@ -110,7 +110,14 @@ export type SnapshotInfo = { export type VotingPower = { address: string; value: bigint; - decimals: number; + /** + * Decimals used to interpret value in context of final (total) VP. + */ + cumulativeDecimals: number; + /** + * Decimals used to display this strategy value. + */ + displayDecimals: number; token: string | null; symbol: string; chainId?: number; diff --git a/apps/ui/src/stores/votingPowers.ts b/apps/ui/src/stores/votingPowers.ts index 613796c49..5eb31f388 100644 --- a/apps/ui/src/stores/votingPowers.ts +++ b/apps/ui/src/stores/votingPowers.ts @@ -10,10 +10,8 @@ const LATEST_BLOCK_NAME = 'latest'; type SpaceDetails = Proposal['space']; export type VotingPowerItem = { votingPowers: VotingPower[]; - totalVotingPower: bigint; status: VotingPowerStatus; symbol: string; - decimals: number; error: utils.errors.VotingPowerDetailsError | null; canPropose: boolean; canVote: boolean; @@ -63,8 +61,6 @@ export const useVotingPowersStore = defineStore('votingPowers', () => { let vpItem: VotingPowerItem = { status: 'loading', votingPowers: [], - totalVotingPower: 0n, - decimals: 18, symbol: space.voting_power_symbol, error: null, canPropose: false, @@ -106,19 +102,20 @@ export const useVotingPowersStore = defineStore('votingPowers', () => { vpItem = { ...vpItem, votingPowers: vp, - totalVotingPower: vp.reduce((acc, b) => acc + b.value, 0n), - status: 'success', - decimals: Math.max(...vp.map(votingPower => votingPower.decimals), 0) + status: 'success' }; if (isSpace(item) && proposeVp) { - const totalProposeVp = proposeVp.reduce((acc, b) => acc + b.value, 0n); + const totalProposeVp = proposeVp.reduce( + (acc, b) => acc + Number(b.value) / 10 ** b.cumulativeDecimals, + 0 + ); vpItem.canPropose = totalProposeVp >= BigInt(item.proposal_threshold) || isSpaceMember(space as Space, account); } else { - vpItem.canVote = vpItem.totalVotingPower > 0n; + vpItem.canVote = vp.some(vp => vp.value > 0n); } } catch (e: unknown) { if (e instanceof utils.errors.VotingPowerDetailsError) { diff --git a/apps/ui/src/views/Proposal.vue b/apps/ui/src/views/Proposal.vue index 20d3acb27..bf6871f95 100644 --- a/apps/ui/src/views/Proposal.vue +++ b/apps/ui/src/views/Proposal.vue @@ -310,7 +310,7 @@ watchEffect(() => { getSocialNetworksLink(user.value)); const cb = computed(() => getCacheHash(user.value?.avatar)); const formattedVotingPower = computed(() => { - const votingPower = votingPowers.value.reduce((acc, b) => acc + b.value, 0n); - const decimals = Math.max( - ...votingPowers.value.map(votingPower => votingPower.decimals), - 0 + const votingPower = _vp( + votingPowers.value.reduce( + (acc, b) => acc + Number(b.value) / 10 ** b.cumulativeDecimals, + 0 + ) ); - const value = _vp(Number(votingPower) / 10 ** decimals); - if (props.space.voting_power_symbol) { - return `${value} ${props.space.voting_power_symbol}`; + return `${votingPower} ${props.space.voting_power_symbol}`; } - return value; + return votingPower; }); const navigation = computed(() => [