From e33098a9c8b3886c310a9720ea5225b24b6a4d08 Mon Sep 17 00:00:00 2001 From: hernandoagf Date: Wed, 19 Jul 2023 15:55:09 -0300 Subject: [PATCH 1/3] fix: Alchemy API rate limitation of 1000 batched requests --- .../executive/api/fetchExecutiveVoteTally.ts | 68 ++++++++++++------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/modules/executive/api/fetchExecutiveVoteTally.ts b/modules/executive/api/fetchExecutiveVoteTally.ts index 50d653e11..7bf844b9c 100644 --- a/modules/executive/api/fetchExecutiveVoteTally.ts +++ b/modules/executive/api/fetchExecutiveVoteTally.ts @@ -12,6 +12,14 @@ import { getChiefDeposits } from 'modules/web3/api/getChiefDeposits'; import { getSlateAddresses } from '../helpers/getSlateAddresses'; import { formatValue } from 'lib/string'; import { paddedBytes32ToAddress } from 'lib/utils'; +import { BigNumber } from 'ethers'; + +type AddressWithVotes = { + votes: string[]; + slate: string; + address: string; + deposits: BigNumber; +}; export async function fetchExecutiveVoteTally(chief: Chief): Promise { const filter = { @@ -32,36 +40,48 @@ export async function fetchExecutiveVoteTally(chief: Chief): Promise } }); - const withDeposits = await Promise.all( - voters.map(voter => - getChiefDeposits(voter, chief).then(deposits => ({ - address: voter, - deposits - })) - ) + // Split voters array into chunks of 1000 addresses + const chunkSize = 1000; + const voterChunks = Array.from({ length: Math.ceil(voters.length / chunkSize) }, (_, i) => + voters.slice(i * chunkSize, i * chunkSize + chunkSize) ); - const withSlates = await Promise.all( - withDeposits.map(addressDeposit => - chief.votes(addressDeposit.address).then(slate => ({ - ...addressDeposit, - slate - })) - ) - ); + const addressesWithVotes: AddressWithVotes[] = []; - const withVotes = await Promise.all( - withSlates.map(withSlate => - getSlateAddresses(chief, withSlate.slate).then(addresses => ({ - ...withSlate, - votes: addresses - })) - ) - ); + for (const chunk of voterChunks) { + const withDeposits = await Promise.all( + chunk.map(voter => + getChiefDeposits(voter, chief).then(deposits => ({ + address: voter, + deposits + })) + ) + ); + + const withSlates = await Promise.all( + withDeposits.map(addressDeposit => + chief.votes(addressDeposit.address).then(slate => ({ + ...addressDeposit, + slate + })) + ) + ); + + const withVotes = await Promise.all( + withSlates.map(withSlate => + getSlateAddresses(chief, withSlate.slate).then(addresses => ({ + ...withSlate, + votes: addresses + })) + ) + ); + + addressesWithVotes.push(...withVotes); + } const voteTally = {}; - for (const voteObj of withVotes) { + for (const voteObj of addressesWithVotes) { for (let vote of voteObj.votes) { vote = vote.toLowerCase(); if (voteTally[vote] === undefined) { From d90a306dfecd1d9c2256d9cf1bf3190083d5ae07 Mon Sep 17 00:00:00 2001 From: hernandoagf Date: Wed, 19 Jul 2023 16:06:31 -0300 Subject: [PATCH 2/3] fix: Aligned delegate names in smaller screens --- modules/address/components/AddressIconBox.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/address/components/AddressIconBox.tsx b/modules/address/components/AddressIconBox.tsx index 5d464d1ac..bf67a80bf 100644 --- a/modules/address/components/AddressIconBox.tsx +++ b/modules/address/components/AddressIconBox.tsx @@ -74,9 +74,15 @@ export default function AddressIconBox({ {delegate ? ( - - {limitTextLength ? limitString(delegate.name, limitTextLength, '...') : delegate.name} - + limitTextLength ? ( + + {delegate.name.split(' - ').map((name, i) => ( + {limitString(name, limitTextLength, '...')} + ))} + + ) : ( + {delegate.name} + ) ) : (
From 18ed9d102126416a680875ff7414e17e46c2e1f6 Mon Sep 17 00:00:00 2001 From: hernandoagf Date: Thu, 20 Jul 2023 14:57:32 -0300 Subject: [PATCH 3/3] Add helper function to split delegate names --- modules/address/components/AddressIconBox.tsx | 6 +++--- modules/delegates/helpers/splitDelegateName.ts | 15 +++++++++++++++ modules/executive/api/fetchExecutiveVoteTally.ts | 3 ++- 3 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 modules/delegates/helpers/splitDelegateName.ts diff --git a/modules/address/components/AddressIconBox.tsx b/modules/address/components/AddressIconBox.tsx index bf67a80bf..a1565e81d 100644 --- a/modules/address/components/AddressIconBox.tsx +++ b/modules/address/components/AddressIconBox.tsx @@ -16,8 +16,8 @@ import { useWeb3 } from 'modules/web3/hooks/useWeb3'; import { useAccount } from 'modules/app/hooks/useAccount'; import { useSingleDelegateInfo } from 'modules/delegates/hooks/useSingleDelegateInfo'; import { useVoteProxyAddress } from 'modules/app/hooks/useVoteProxyAddress'; -import { limitString } from 'lib/string'; import EtherscanLink from 'modules/web3/components/EtherscanLink'; +import splitDelegateName from 'modules/delegates/helpers/splitDelegateName'; type PropTypes = { address: string; @@ -76,8 +76,8 @@ export default function AddressIconBox({ {delegate ? ( limitTextLength ? ( - {delegate.name.split(' - ').map((name, i) => ( - {limitString(name, limitTextLength, '...')} + {splitDelegateName(delegate.name, limitTextLength).map((name, i) => ( + {name} ))} ) : ( diff --git a/modules/delegates/helpers/splitDelegateName.ts b/modules/delegates/helpers/splitDelegateName.ts new file mode 100644 index 000000000..6e56afef3 --- /dev/null +++ b/modules/delegates/helpers/splitDelegateName.ts @@ -0,0 +1,15 @@ +/* + +SPDX-FileCopyrightText: © 2023 Dai Foundation + +SPDX-License-Identifier: AGPL-3.0-or-later + +*/ + +import { limitString } from 'lib/string'; + +// Splits an Aligned Delegate name into two strings: one for the AVC name and one +// for the actual delegate name, useful when displaying the name in small screens +export default function splitDelegateName(delegateName: string, limitTextLength: number): string[] { + return delegateName.split(' - ').map(name => limitString(name, limitTextLength, '...')); +} diff --git a/modules/executive/api/fetchExecutiveVoteTally.ts b/modules/executive/api/fetchExecutiveVoteTally.ts index 7bf844b9c..44bf04b96 100644 --- a/modules/executive/api/fetchExecutiveVoteTally.ts +++ b/modules/executive/api/fetchExecutiveVoteTally.ts @@ -40,7 +40,8 @@ export async function fetchExecutiveVoteTally(chief: Chief): Promise } }); - // Split voters array into chunks of 1000 addresses + // We need to split the voters array into chunks of 1000 addresses because our + // Alchemy key doesn't support batch requests larger than 1000 queries each const chunkSize = 1000; const voterChunks = Array.from({ length: Math.ceil(voters.length / chunkSize) }, (_, i) => voters.slice(i * chunkSize, i * chunkSize + chunkSize)