diff --git a/src/header.tsx b/src/header.tsx index 2014de0..5b38bdf 100644 --- a/src/header.tsx +++ b/src/header.tsx @@ -23,7 +23,7 @@ import { ConnectionDialog } from 'dot-connect/react.js' // MenubarTrigger, // } from '@/components/ui/menubar' import { useAccounts } from './contexts/AccountsContext' -import { useEffect, useState } from 'react' +import { Fragment, useEffect, useState } from 'react' import { SupportedNetworkNames, useNetwork } from './contexts/NetworkContext' import { useTheme } from './components/theme-provider' import { Link, useLocation } from 'react-router-dom' @@ -189,7 +189,7 @@ export const Header = () => { {accounts.map((account, index) => ( - <> + { {index !== accounts.length - 1 && ( )} - + ))} { const { api } = useNetwork() - const derive = sr25519CreateDerive( - entropyToMiniSecret(mnemonicToEntropy(DEV_PHRASE)), + const derive = useMemo( + () => + sr25519CreateDerive(entropyToMiniSecret(mnemonicToEntropy(DEV_PHRASE))), + [], ) - const aliceKeyPair = derive('//Alice') - const aliceSigner = getPolkadotSigner( - aliceKeyPair.publicKey, - 'Sr25519', - aliceKeyPair.sign, + const aliceKeyPair = useMemo(() => derive('//Alice'), [derive]) + const aliceSigner = useMemo( + () => + getPolkadotSigner(aliceKeyPair.publicKey, 'Sr25519', aliceKeyPair.sign), + [aliceKeyPair.publicKey, aliceKeyPair.sign], ) const isExhaustsResources = useCallback( diff --git a/src/pages/Delegate/MultiTransactionDialog.tsx b/src/pages/Delegate/MultiTransactionDialog.tsx index c82957e..91aece6 100644 --- a/src/pages/Delegate/MultiTransactionDialog.tsx +++ b/src/pages/Delegate/MultiTransactionDialog.tsx @@ -10,7 +10,7 @@ import { useAccounts } from '@/contexts/AccountsContext' import { useNetwork } from '@/contexts/NetworkContext' import { DelegateTxs } from '@/hooks/useGetDelegateTx' import { useTestTx } from '@/hooks/useTestTx' -import { useState } from 'react' +import { useCallback, useState } from 'react' import { TooLargeDialog } from './TooLargeDialog' import { useGetSigningCallback } from '@/hooks/useGetSigningCallback' @@ -42,7 +42,7 @@ export const MultiTransactionDialog = ({ step === 2 && onSignStep2() } - const onSignStep1 = async () => { + const onSignStep1 = useCallback(async () => { if (!api || !selectedAccount) return setIsTxInitiated(true) @@ -76,9 +76,16 @@ export const MultiTransactionDialog = ({ ;(await step1Txs) .signSubmitAndWatch(selectedAccount?.polkadotSigner, { at: 'best' }) .subscribe(subscriptionCallBack1) - } - - const onSignStep2 = async () => { + }, [ + api, + delegateTxs.removeDelegationsTxs, + delegateTxs.removeVotesTxs, + getSubscriptionCallBack, + isExhaustsResources, + selectedAccount, + ]) + + const onSignStep2 = useCallback(async () => { if (!api || !selectedAccount) return setIsTxInitiated(true) @@ -114,7 +121,14 @@ export const MultiTransactionDialog = ({ await step2Txs .signSubmitAndWatch(selectedAccount?.polkadotSigner, { at: 'best' }) .subscribe(subscriptionCallBack2) - } + }, [ + api, + delegateTxs.delegationTxs, + getSubscriptionCallBack, + isExhaustsResources, + onProcessFinished, + selectedAccount, + ]) if (promptForHelpCallData) return ( diff --git a/src/pages/Delegate/index.tsx b/src/pages/Delegate/index.tsx index 5a1b6f1..a692e46 100644 --- a/src/pages/Delegate/index.tsx +++ b/src/pages/Delegate/index.tsx @@ -6,7 +6,13 @@ import { } from '@/contexts/DelegatesContext' import { useNetwork } from '@/contexts/NetworkContext' import { VotingConviction } from '@polkadot-api/descriptors' -import { SetStateAction, useEffect, useMemo, useState } from 'react' +import { + SetStateAction, + useCallback, + useEffect, + useMemo, + useState, +} from 'react' import { Button } from '@/components/ui/button' import { useAccounts } from '@/contexts/AccountsContext' import { Slider } from '@/components/ui/slider' @@ -15,7 +21,7 @@ import { ArrowLeft, Loader2 } from 'lucide-react' import { msgs } from '@/lib/constants' import { evalUnits, planckToUnit } from '@polkadot-ui/utils' import { useLocks } from '@/contexts/LocksContext' -import { DelegateTxs, useGetDelegateTx } from '@/hooks/useGetDelegateTx' +import { useGetDelegateTx } from '@/hooks/useGetDelegateTx' import { AlertNote } from '@/components/Alert' import { useTestTx } from '@/hooks/useTestTx' import { MultiTransactionDialog } from './MultiTransactionDialog' @@ -46,8 +52,50 @@ export const Delegate = () => { const [isTxInitiated, setIsTxInitiated] = useState(false) const { isExhaustsResources } = useTestTx() const [isMultiTxDialogOpen, setIsMultiTxDialogOpen] = useState(false) - const [delegateTxs, setDelegateTxs] = useState({} as DelegateTxs) const [noDelegateFound, setNoDelegateFound] = useState(false) + const [allTracks, setAllTracks] = useState([]) + const [isExhaustsResourcesError, setIsExhaustsResourcesError] = useState< + boolean | null + >(false) + const { + delegationTxs = [], + removeDelegationsTxs = [], + removeVotesTxs = [], + } = useMemo(() => { + if (!delegate) return {} + + return getDelegateTx({ + delegateAddress: delegate.address, + conviction: conviction, + amount, + tracks: allTracks || [], + }) + }, [allTracks, amount, conviction, delegate, getDelegateTx]) + + const allTxs = useMemo(() => { + if (!api) return + + return api.tx.Utility.batch_all({ + calls: [...removeVotesTxs, ...removeDelegationsTxs, ...delegationTxs].map( + (tx) => tx.decodedCall, + ), + }) + }, [api, delegationTxs, removeDelegationsTxs, removeVotesTxs]) + + useEffect(() => { + if (!allTxs) return + + // check if we have an exhausted limit on the whole tx + isExhaustsResources(allTxs) + .then(setIsExhaustsResourcesError) + .catch(console.error) + }, [ + allTxs, + delegationTxs, + isExhaustsResources, + removeDelegationsTxs, + removeVotesTxs, + ]) useEffect(() => { // the delegate list may still be loading @@ -66,7 +114,12 @@ export const Delegate = () => { }, [address, delegate, getDelegateByAddress, isLoadingDelegates]) const { display: convictionTimeDisplay, multiplier: convictionMultiplier } = - getConvictionLockTimeDisplay(convictionNo) + useMemo( + () => + getConvictionLockTimeDisplay(convictionNo) || + getConvictionLockTimeDisplay(convictionNo), + [convictionNo, getConvictionLockTimeDisplay], + ) const voteAmount = useMemo(() => { if (!convictionMultiplier) return @@ -101,77 +154,62 @@ export const Delegate = () => { setAmountVisible('0') }, [api]) - const onChangeAmount = ( - e: React.ChangeEvent, - decimals: number, - ) => { - setIsAmountDirty(true) - setAmountError('') - const [bnResult, errorMessage] = evalUnits(e.target.value, decimals) - setAmount(bnResult || 0n) - if (errorMessage) setAmountError(errorMessage) - setAmountVisible(e.target.value) - } - - const onChangeSplitTransactionDialog = (isOpen: boolean) => { + useEffect(() => { + if (!api) return + + api.constants.Referenda.Tracks() + .then((tracks) => { + const trackIds = tracks.map(([track]) => track) + setAllTracks(trackIds) + }) + .catch(console.error) + }, [api]) + + const onChangeAmount = useCallback( + (e: React.ChangeEvent) => { + setIsAmountDirty(true) + setAmountError('') + const [bnResult, errorMessage] = evalUnits( + e.target.value, + assetInfo.precision, + ) + setAmount(bnResult || 0n) + if (errorMessage) setAmountError(errorMessage) + setAmountVisible(e.target.value) + }, + [assetInfo.precision], + ) + + const onOpenChangeSplitTransactionDialog = useCallback((isOpen: boolean) => { setIsMultiTxDialogOpen(isOpen) setIsTxInitiated(false) - } + }, []) - const onProcessFinished = () => { + const onProcessFinished = useCallback(() => { refreshLocks() navigate(`/${search}`) setIsTxInitiated(false) - onChangeSplitTransactionDialog(false) - } + onOpenChangeSplitTransactionDialog(false) + }, [navigate, onOpenChangeSplitTransactionDialog, refreshLocks, search]) - const onSign = async () => { + const onSign = useCallback(async () => { if (!delegate || !selectedAccount || !amount || !api) return setIsTxInitiated(true) - const allTracks = await api.constants.Referenda.Tracks() - .then((tracks) => { - return tracks.map(([track]) => track) - }) - .catch((e) => { - console.error(e) - setIsTxInitiated(false) - }) - - const { - delegationTxs = [], - removeDelegationsTxs = [], - removeVotesTxs = [], - } = getDelegateTx({ - delegateAddress: delegate.address, - conviction: conviction, - amount, - tracks: allTracks || [], - }) - - setDelegateTxs({ - removeVotesTxs, - removeDelegationsTxs, - delegationTxs, - }) - - const allTxs = api.tx.Utility.batch_all({ - calls: [...removeVotesTxs, ...removeDelegationsTxs, ...delegationTxs].map( - (tx) => tx.decodedCall, - ), - }) + if ( + !removeDelegationsTxs.length && + !removeVotesTxs.length && + !delegationTxs.length + ) { + return + } if (!allTxs) { setIsTxInitiated(false) return } - - // check if we have an exhausted limit on the whole tx - const isExhaustsRessouces = await isExhaustsResources(allTxs) - - // this is too big of a batch we need to split it up - if (isExhaustsRessouces) { + if (isExhaustsResourcesError) { setIsMultiTxDialogOpen(true) return } @@ -184,7 +222,19 @@ export const Delegate = () => { await allTxs .signSubmitAndWatch(selectedAccount?.polkadotSigner, { at: 'best' }) .subscribe(subscriptionCallBack) - } + }, [ + allTxs, + amount, + api, + delegate, + delegationTxs.length, + getSubscriptionCallBack, + isExhaustsResourcesError, + onProcessFinished, + removeDelegationsTxs.length, + removeVotesTxs.length, + selectedAccount, + ]) if (noDelegateFound) return ( @@ -230,7 +280,7 @@ export const Delegate = () => {
onChangeAmount(value, assetInfo.precision)} + onChange={onChangeAmount} value={amountVisible} error={amountErrorDisplay} /> @@ -272,12 +322,18 @@ export const Delegate = () => { Delegate with {voteAmount} {assetInfo.symbol} votes
- + {isMultiTxDialogOpen && ( + + )} ) }