From 794474a38f4a9fe06538f86570c1b9be278b0ef6 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 27 Aug 2024 17:59:04 +0200 Subject: [PATCH] feat: add delegate form fill on click from public delegates list --- .../providers/DelegationFormContext.tsx | 25 +++++++++- modules/delegation/publicDelegates.ts | 9 +--- .../DelegationForm/DelegationAddressInput.tsx | 3 +- .../ui/DelegationForm/DelegationForm.tsx | 13 ++--- .../DelegationSettings/DelegationSettings.tsx | 23 +++++++-- .../PublicDelegateList/PublicDelegateList.tsx | 36 +++++++++----- .../PublicDelegateListStyle.ts | 3 +- .../useProcessedPublicDelegatesList.ts | 48 ++++++++++++++----- 8 files changed, 115 insertions(+), 45 deletions(-) diff --git a/modules/delegation/providers/DelegationFormContext.tsx b/modules/delegation/providers/DelegationFormContext.tsx index fd670130..38fae9ab 100644 --- a/modules/delegation/providers/DelegationFormContext.tsx +++ b/modules/delegation/providers/DelegationFormContext.tsx @@ -5,6 +5,7 @@ import { useContext, useCallback, useState, + useEffect, } from 'react' import { useGovernanceBalance } from 'modules/tokens/hooks/useGovernanceBalance' import { useDelegationInfo } from '../hooks/useDelegationInfo' @@ -19,6 +20,7 @@ import { import { useDelegationFormSubmit } from '../hooks/useDelegationFormSubmit' import { useDelegationRevoke } from '../hooks/useDelegationRevoke' import { ToastSuccess } from '@lidofinance/lido-ui' +import { isValidAddress } from 'modules/shared/utils/addressValidation' // // Data context @@ -116,17 +118,36 @@ const useDelegationFormActions = ( // // Data provider // -export const DelegationFormProvider: FC<{ mode: DelegationFormMode }> = ({ +export type DelegationFormProviderProps = { + mode: DelegationFormMode + presetDelegateAddress?: string +} + +export const DelegationFormProvider: FC = ({ children, mode, + presetDelegateAddress, }) => { const networkData = useDelegationFormNetworkData() const formObject = useForm({ - defaultValues: { delegateAddress: null }, + defaultValues: { delegateAddress: '' }, mode: 'onChange', }) + useEffect(() => { + const currentValue = formObject.getValues('delegateAddress') + if ( + presetDelegateAddress && + isValidAddress(presetDelegateAddress) && + currentValue?.toLowerCase() !== presetDelegateAddress.toLowerCase() + ) { + console.log('setting default address', presetDelegateAddress) + formObject.setValue('delegateAddress', presetDelegateAddress) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [presetDelegateAddress]) + const { isSubmitting, txAragonDelegate, diff --git a/modules/delegation/publicDelegates.ts b/modules/delegation/publicDelegates.ts index f0940350..dbd82b4f 100644 --- a/modules/delegation/publicDelegates.ts +++ b/modules/delegation/publicDelegates.ts @@ -1,3 +1,4 @@ +// The list of public delegates was provided by DAO Ops workstream member export const PUBLIC_DELEGATES = [ { name: 'Matt Stam', @@ -125,14 +126,6 @@ export const PUBLIC_DELEGATES = [ lido: 'https://research.lido.fi/t/nodesoda-com-delegate-thread/8031', twitter: 'https://x.com/pablolema85', }, - { - name: 'eboadom (Ernesto)', - avatar: - 'https://dub1.discourse-cdn.com/business20/user_avatar/research.lido.fi/eboadom/288/3764_2.png', - address: '0x000b4369B71B6634F27F5De9Cbaaabb0D21B8be5', - lido: 'https://research.lido.fi/t/eboadom-delegate-thread/8079', - twitter: 'https://x.com/eboadom', - }, { name: 'ReservoirDAO', avatar: diff --git a/modules/delegation/ui/DelegationForm/DelegationAddressInput.tsx b/modules/delegation/ui/DelegationForm/DelegationAddressInput.tsx index 0fc0daca..eb18343d 100644 --- a/modules/delegation/ui/DelegationForm/DelegationAddressInput.tsx +++ b/modules/delegation/ui/DelegationForm/DelegationAddressInput.tsx @@ -12,12 +12,11 @@ export function DelegationAddressInput() { aragonDelegateAddress, snapshotDelegateAddress, mode, - register, } = useDelegationFormData() return ( void } -export function DelegationForm({ mode, onCustomizeClick }: Props) { +export function DelegationForm({ onCustomizeClick, ...providerProps }: Props) { return ( - + diff --git a/modules/delegation/ui/DelegationSettings/DelegationSettings.tsx b/modules/delegation/ui/DelegationSettings/DelegationSettings.tsx index b64629c7..ed9c097a 100644 --- a/modules/delegation/ui/DelegationSettings/DelegationSettings.tsx +++ b/modules/delegation/ui/DelegationSettings/DelegationSettings.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useCallback, useState } from 'react' import { Button, Text } from '@lidofinance/lido-ui' import { FormTitle, FormWrap, Wrap } from './DelegationSettingsStyle' @@ -7,6 +7,14 @@ import { PublicDelegateList } from '../PublicDelegateList' export function DelegationSettings() { const [isSimpleModeOn, setIsSimpleModeOn] = useState(true) + const [delegateFromPublicList, setDelegateFromPublicList] = useState() + + const handleDelegatePick = useCallback( + (address: string) => () => { + setDelegateFromPublicList(address) + }, + [], + ) return ( @@ -28,16 +36,23 @@ export function DelegationSettings() { {isSimpleModeOn ? ( setIsSimpleModeOn(false)} /> ) : ( <> - - + + )} - + ) } diff --git a/modules/delegation/ui/PublicDelegateList/PublicDelegateList.tsx b/modules/delegation/ui/PublicDelegateList/PublicDelegateList.tsx index b5553a9e..7e33e973 100644 --- a/modules/delegation/ui/PublicDelegateList/PublicDelegateList.tsx +++ b/modules/delegation/ui/PublicDelegateList/PublicDelegateList.tsx @@ -1,4 +1,5 @@ import { Button, Text, trimAddress } from '@lidofinance/lido-ui' +import Image from 'next/image' import { DelegateInfo, Header, @@ -14,15 +15,18 @@ import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' import { useProcessedPublicDelegatesList } from './useProcessedPublicDelegatesList' import { PageLoader } from 'modules/shared/ui/Common/PageLoader' import { isValidAddress } from 'modules/shared/utils/addressValidation' +import { formatBalance } from 'modules/blockChain/utils/formatBalance' +import { AddressPop } from 'modules/shared/ui/Common/AddressPop' import AragonSvg from 'assets/aragon.com.svg.react' import XSocialSvg from 'assets/x.social.com.svg.react' import LidoSocialSvg from 'assets/lido.social.com.svg.react' -import Image from 'next/image' -import { formatBalance } from 'modules/blockChain/utils/formatBalance' -import { AddressPop } from 'modules/shared/ui/Common/AddressPop' -export function PublicDelegateList() { +type Props = { + onDelegatePick: (address: string) => () => void +} + +export function PublicDelegateList({ onDelegatePick }: Props) { const { isWalletConnected } = useWeb3() const { data, initialLoading } = useProcessedPublicDelegatesList() @@ -58,12 +62,17 @@ export function PublicDelegateList() { - src} - /> + {delegate.avatar ? ( + src} + unoptimized + /> + ) : ( +
+ )}
@@ -93,7 +102,12 @@ export function PublicDelegateList() { {isWalletConnected && ( - )} diff --git a/modules/delegation/ui/PublicDelegateList/PublicDelegateListStyle.ts b/modules/delegation/ui/PublicDelegateList/PublicDelegateListStyle.ts index 7956b67d..0bd4766d 100644 --- a/modules/delegation/ui/PublicDelegateList/PublicDelegateListStyle.ts +++ b/modules/delegation/ui/PublicDelegateList/PublicDelegateListStyle.ts @@ -92,7 +92,8 @@ export const AvatarWrap = styled.div` background-color: var(--lido-color-primaryVisited); overflow: hidden; - & > img { + & > img, + div { width: 100%; height: 100%; object-fit: cover; diff --git a/modules/delegation/ui/PublicDelegateList/useProcessedPublicDelegatesList.ts b/modules/delegation/ui/PublicDelegateList/useProcessedPublicDelegatesList.ts index 814872f2..b6ab280b 100644 --- a/modules/delegation/ui/PublicDelegateList/useProcessedPublicDelegatesList.ts +++ b/modules/delegation/ui/PublicDelegateList/useProcessedPublicDelegatesList.ts @@ -5,7 +5,16 @@ import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' import { DELEGATORS_FETCH_TOTAL } from 'modules/delegation/constants' import { PUBLIC_DELEGATES } from 'modules/delegation/publicDelegates' import { useEnsResolvers } from 'modules/shared/hooks/useEnsResolvers' -import { isValidEns } from 'modules/shared/utils/addressValidation' +import { + isValidAddress, + isValidEns, +} from 'modules/shared/utils/addressValidation' + +type ProcessedDelegate = typeof PUBLIC_DELEGATES[number] & { + delegatorsCount: string + delegatedVotingPower: BigNumber | string + resolvedDelegateAddress: string | null +} export const useProcessedPublicDelegatesList = () => { const { chainId } = useWeb3() @@ -13,45 +22,55 @@ export const useProcessedPublicDelegatesList = () => { const { resolveName } = useEnsResolvers() - return useLidoSWRImmutable( + return useLidoSWRImmutable( [`swr:useProcessedPublicDelegatesList`, chainId], async () => { - const parsedList = await Promise.all( + const parsedList: ProcessedDelegate[] = await Promise.all( PUBLIC_DELEGATES.map(async delegate => { - let delegateAddress: string | null = delegate.address + let resolvedDelegateAddress: string | null = + delegate.address.toLowerCase() // If `address` was provided as ENS name, convert it to address - if (isValidEns(delegateAddress)) { - delegateAddress = await resolveName(delegateAddress) + if (isValidEns(delegate.address)) { + resolvedDelegateAddress = + (await resolveName(delegate.address))?.toLowerCase() ?? null // If ENS name wasn't not resolved, return delegate with N/A values - if (!delegateAddress) { + if (!resolvedDelegateAddress) { return { ...delegate, delegatorsCount: 'N/A', delegatedVotingPower: 'N/A', + resolvedDelegateAddress: null, } } + } else if (!isValidAddress(delegate.address)) { + return { + ...delegate, + delegatorsCount: 'N/A', + delegatedVotingPower: 'N/A', + resolvedDelegateAddress: null, + } } const delegatorsCount = await voting.getDelegatedVotersCount( - delegateAddress, + resolvedDelegateAddress, ) if (delegatorsCount.isZero()) { return { ...delegate, delegatorsCount: '0', - delegatedVotingPower: '0', + delegatedVotingPower: BigNumber.from(0), + resolvedDelegateAddress, } } const delegatorsAddresses = await voting.getDelegatedVoters( - delegateAddress, + resolvedDelegateAddress, 0, DELEGATORS_FETCH_TOTAL, ) - console.log('addresses', delegatorsAddresses) const delegatorsBalances = await voting.getVotingPowerMultiple( delegatorsAddresses, ) @@ -64,6 +83,7 @@ export const useProcessedPublicDelegatesList = () => { ...delegate, delegatorsCount: delegatorsCount.toString(), delegatedVotingPower: delegatedVotingPower, + resolvedDelegateAddress, } }), ) @@ -72,6 +92,9 @@ export const useProcessedPublicDelegatesList = () => { if (typeof a.delegatedVotingPower === 'string') { return 1 } + if (typeof b.delegatedVotingPower === 'string') { + return -1 + } if (a.delegatedVotingPower.lt(b.delegatedVotingPower)) { return 1 } @@ -81,5 +104,8 @@ export const useProcessedPublicDelegatesList = () => { return 0 }) }, + { + onError: (error, key) => console.error(key, error), + }, ) }