From dbec40d3696cd9daa1eeefaf0771c50447e9eaf3 Mon Sep 17 00:00:00 2001 From: Amir Ekbatanifard Date: Sun, 1 Dec 2024 09:51:18 +0330 Subject: [PATCH 1/3] fix: loop isssue in Edit pool component --- .../stake/pool/commonTasks/editPool/Edit.tsx | 6 +- .../pool/commonTasks/editPool/Review.tsx | 156 +++++++++++------- .../commonTasks/editPool/ShowPoolRole.tsx | 6 +- .../stake/pool/commonTasks/editPool/index.tsx | 4 +- 4 files changed, 103 insertions(+), 69 deletions(-) diff --git a/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/Edit.tsx b/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/Edit.tsx index c6f8573b3..3829a6399 100644 --- a/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/Edit.tsx +++ b/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/Edit.tsx @@ -30,7 +30,7 @@ interface Props { changes: ChangesProps | undefined; } -export default function Edit ({ api, chain, changes, onClose, pool, setChanges, setStep }: Props): React.ReactElement { +function Edit ({ api, chain, changes, onClose, pool, setChanges, setStep }: Props): React.ReactElement { const { t } = useTranslation(); const theme = useTheme(); const { hierarchy } = useContext(AccountContext); @@ -117,7 +117,7 @@ export default function Edit ({ api, chain, changes, onClose, pool, setChanges, const nextBtnDisable = changes && Object.values(changes).every((value) => { if (typeof value === 'object' && value !== null) { - return Object.values(value as { [s: string]: unknown }).every((nestedValue) => nestedValue === undefined); + return Object.values(value as Record).every((nestedValue) => nestedValue === undefined); } return value === undefined; @@ -255,3 +255,5 @@ export default function Edit ({ api, chain, changes, onClose, pool, setChanges, ); } + +export default React.memo(Edit); diff --git a/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/Review.tsx b/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/Review.tsx index 0b86b364e..10cff3da4 100644 --- a/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/Review.tsx +++ b/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/Review.tsx @@ -37,89 +37,115 @@ interface Props { step: number; } -export default function Review ({ address, api, chain, changes, formatted, pool, setRefresh, setStep, setTxInfo, step }: Props): React.ReactElement { - const { t } = useTranslation(); - const proxies = useProxies(api, formatted); +const getRole = (role: string | undefined | null) => { + if (role === undefined) { + return 'Noop'; + } else if (role === null) { + return 'Remove'; + } else { + return { set: role }; + } +}; - const [selectedProxy, setSelectedProxy] = useState(); - const [proxyItems, setProxyItems] = useState(); - const [isPasswordError, setIsPasswordError] = useState(false); - const [estimatedFee, setEstimatedFee] = useState(); +const usePoolTransactionPreparation = (api: ApiPromise | undefined, changes: ChangesProps | undefined, pool: MyPoolInfo, formatted: string, maybeCurrentCommissionPayee?: string) => { const [inputs, setInputs] = useState(); - - const selectedProxyAddress = selectedProxy?.delegate as unknown as string; - - //@ts-ignore - const maybeCurrentCommissionPayee = pool?.bondedPool?.commission?.current?.[1]?.toString() as string | undefined; - - useEffect((): void => { - const fetchedProxyItems = proxies?.map((p: Proxy) => ({ proxy: p, status: 'current' })) as ProxyItem[]; - - setProxyItems(fetchedProxyItems); - }, [proxies]); - - const extraInfo = useMemo(() => ({ - action: 'Pool Staking', - fee: String(estimatedFee || 0), - subAction: 'Edit Pool' - }), [estimatedFee]); + const [estimatedFee, setEstimatedFee] = useState(); useEffect(() => { if (!api || !changes) { return; } - const batchAll = api.tx['utility']['batchAll']; - const setMetadata = api.tx['nominationPools']['setMetadata']; - const updateRoles = api.tx['nominationPools']['updateRoles']; - const setCommission = api.tx['nominationPools']['setCommission']; + const { nominationPools: { setCommission, setMetadata, updateRoles }, utility: { batchAll } } = api.tx; const txs: { call: SubmittableExtrinsicFunction<'promise', AnyTuple>, params: unknown[] }[] = []; - const getRole = (role: string | undefined | null) => { - if (role === undefined) { - return 'Noop'; - } else if (role === null) { - return 'Remove'; - } else { - return { set: role }; - } - }; - - changes.newPoolName !== undefined && - txs.push({ call: setMetadata, params: [pool.poolId, changes?.newPoolName] }); + if (changes.newPoolName !== undefined) { + txs.push({ call: setMetadata, params: [pool.poolId, changes.newPoolName] }); + } - changes.newRoles !== undefined && !Object.values(changes.newRoles).every((value) => value === undefined) && - txs.push({ call: updateRoles, params: [pool.poolId, getRole(changes.newRoles.newRoot), getRole(changes.newRoles.newNominator), getRole(changes.newRoles.newBouncer)] }); + if (changes.newRoles && !Object.values(changes.newRoles).every((value) => value === undefined)) { + txs.push({ + call: updateRoles, + params: [ + pool.poolId, + getRole(changes.newRoles?.newRoot), + getRole(changes.newRoles?.newNominator), + getRole(changes.newRoles?.newBouncer) + ] + }); + } - changes.commission !== undefined && (changes.commission.value !== undefined || changes.commission.payee) && - txs.push({ call: setCommission, params: [pool.poolId, [(changes.commission.value || 0) * 10 ** 7, changes.commission.payee || maybeCurrentCommissionPayee]] }); + if (changes.commission && + (changes.commission.value !== undefined || changes.commission.payee)) { + txs.push({ + call: setCommission, + params: [ + pool.poolId, + [ + (changes.commission.value || 0) * 10 ** 7, + changes.commission.payee || maybeCurrentCommissionPayee + ] + ] + }); + } const call = txs.length > 1 ? batchAll : txs[0].call; const params = txs.length > 1 ? [txs.map(({ call, params }) => call(...params))] : txs[0].params; + let fee: Balance = api.createType('Balance', BN_ONE) as Balance; + + // Estimate transaction fee + if (!api?.call?.['transactionPaymentApi']) { + fee = api.createType('Balance', BN_ONE) as Balance; + setEstimatedFee(fee); + } + + call(...params).paymentInfo(formatted) + .then((i) => { + fee = api.createType('Balance', i?.partialFee) as Balance; + setEstimatedFee(fee); + }) + .catch(console.error); + + const extraInfo = { + action: 'Pool Staking', + fee: String(fee ?? 0), + subAction: 'Edit Pool' + }; + setInputs({ call, extraInfo, params }); - }, [api, changes, extraInfo, maybeCurrentCommissionPayee, pool.poolId]); + }, [api, changes, pool, formatted, maybeCurrentCommissionPayee]); - useEffect(() => { - if (!api || !inputs?.call) { - return; - } + return { estimatedFee, inputs }; +}; - if (!api?.call?.['transactionPaymentApi']) { - return setEstimatedFee(api.createType('Balance', BN_ONE) as Balance); - } +function Review ({ address, api, chain, changes, formatted, pool, setRefresh, setStep, setTxInfo, step }: Props): React.ReactElement { + const { t } = useTranslation(); + const proxies = useProxies(api, formatted); - inputs.call(...inputs.params)?.paymentInfo(formatted).then((i) => { - setEstimatedFee(api.createType('Balance', i?.partialFee) as Balance); - }).catch(console.error); - }, [api, formatted, inputs]); + const [selectedProxy, setSelectedProxy] = useState(); + const [proxyItems, setProxyItems] = useState(); + const [isPasswordError, setIsPasswordError] = useState(false); + + const selectedProxyAddress = selectedProxy?.delegate as unknown as string; + + //@ts-ignore + const maybeCurrentCommissionPayee = pool?.bondedPool?.commission?.current?.[1]?.toString() as string | undefined; + + useEffect((): void => { + const fetchedProxyItems = proxies?.map((p: Proxy) => ({ proxy: p, status: 'current' })) as ProxyItem[]; + + setProxyItems(fetchedProxyItems); + }, [proxies]); + + const { estimatedFee, inputs } = usePoolTransactionPreparation(api, changes, pool, formatted,maybeCurrentCommissionPayee); const closeProxy = useCallback(() => setStep(STEPS.REVIEW), [setStep]); @@ -149,7 +175,7 @@ export default function Review ({ address, api, chain, changes, formatted, pool, - {t('Pool name')} + {t('Pool name')} @@ -164,7 +190,7 @@ export default function Review ({ address, api, chain, changes, formatted, pool, ('Root')} + roleTitle={t('Root')} showDivider /> } @@ -172,7 +198,7 @@ export default function Review ({ address, api, chain, changes, formatted, pool, ('Nominator')} + roleTitle={t('Nominator')} showDivider /> } @@ -180,7 +206,7 @@ export default function Review ({ address, api, chain, changes, formatted, pool, ('Bouncer')} + roleTitle={t('Bouncer')} showDivider /> } @@ -201,7 +227,7 @@ export default function Review ({ address, api, chain, changes, formatted, pool, ('Commission payee')} + roleTitle={t('Commission payee')} showDivider /> } @@ -218,13 +244,13 @@ export default function Review ({ address, api, chain, changes, formatted, pool, ('Confirm')} + primaryBtnText={t('Confirm')} proxyTypeFilter={PROXY_TYPE.NOMINATION_POOLS} - secondaryBtnText={t('Back')} + secondaryBtnText={t('Back')} selectedProxy={selectedProxy} setIsPasswordError={setIsPasswordError} setRefresh={setRefresh} @@ -252,3 +278,5 @@ export default function Review ({ address, api, chain, changes, formatted, pool, ); } + +export default React.memo(Review); diff --git a/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/ShowPoolRole.tsx b/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/ShowPoolRole.tsx index 469cf0426..5a0459b85 100644 --- a/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/ShowPoolRole.tsx +++ b/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/ShowPoolRole.tsx @@ -18,7 +18,7 @@ interface Props { chain: Chain | null | undefined; } -export default function ShowPoolRole ({ chain, roleAddress, roleTitle, showDivider }: Props) { +function ShowPoolRole ({ chain, roleAddress, roleTitle, showDivider }: Props) { const { t } = useTranslation(); return ( @@ -41,7 +41,7 @@ export default function ShowPoolRole ({ chain, roleAddress, roleTitle, showDivid /> : - {t('To be Removed')} + {t('To be Removed')} } {showDivider && @@ -50,3 +50,5 @@ export default function ShowPoolRole ({ chain, roleAddress, roleTitle, showDivid ); } + +export default React.memo(ShowPoolRole); diff --git a/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/index.tsx b/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/index.tsx index 91e03efb6..bb8400b9e 100644 --- a/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/index.tsx +++ b/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/index.tsx @@ -43,7 +43,7 @@ export interface ChangesProps { } | undefined } -export default function ManageEditPool ({ address, api, chain, onClose, pool, setRefresh }: Props): React.ReactElement { +function ManageEditPool ({ address, api, chain, onClose, pool, setRefresh }: Props): React.ReactElement { const { t } = useTranslation(); const formatted = useFormatted(address); @@ -109,3 +109,5 @@ export default function ManageEditPool ({ address, api, chain, onClose, pool, se ); } + +export default React.memo(ManageEditPool); From 502b791e9108f9366622fe2a83e2d1c9b94d03bd Mon Sep 17 00:00:00 2001 From: Amir Ekbatanifard Date: Sun, 1 Dec 2024 09:51:49 +0330 Subject: [PATCH 2/3] fix: negative issue in old staker which leads to error --- .../fullscreen/stake/pool/unstake/index.tsx | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/packages/extension-polkagate/src/fullscreen/stake/pool/unstake/index.tsx b/packages/extension-polkagate/src/fullscreen/stake/pool/unstake/index.tsx index 117a46847..6e714e1ce 100644 --- a/packages/extension-polkagate/src/fullscreen/stake/pool/unstake/index.tsx +++ b/packages/extension-polkagate/src/fullscreen/stake/pool/unstake/index.tsx @@ -78,7 +78,7 @@ export default function Unstake ({ address, setRefresh, setShow, show }: Props): }, [amountAsBN, staked, unstakeMaxAmount]); const unlockingLen = myPool?.stashIdAccount?.stakingLedger?.unlocking?.length; - const maxUnlockingChunks = api && (api.consts['staking']['maxUnlockingChunks'] as any)?.toNumber(); + const maxUnlockingChunks = api?.consts['staking']['maxUnlockingChunks']?.toPrimitive() as unknown as number; const isPoolRoot = useMemo(() => String(formatted) === String(myPool?.bondedPool?.roles?.root), [formatted, myPool?.bondedPool?.roles?.root]); const isPoolDepositor = useMemo(() => String(formatted) === String(myPool?.bondedPool?.roles?.depositor), [formatted, myPool?.bondedPool?.roles?.depositor]); const poolState = useMemo(() => String(myPool?.bondedPool?.state), [myPool?.bondedPool?.state]); @@ -131,7 +131,7 @@ export default function Unstake ({ address, setRefresh, setShow, show }: Props): }, [amountAsBN, api, poolConsts, decimal, staked, t, unstakeMaxAmount, isPoolDepositor, poolMemberCounter, poolState, token]); useEffect(() => { - if (!api) { + if (!api || !formatted || !unbonded || !amountAsBN) { return; } @@ -141,23 +141,23 @@ export default function Unstake ({ address, setRefresh, setShow, show }: Props): return setEstimatedFee(api?.createType('Balance', BN_ONE) as Balance); } - // eslint-disable-next-line no-void - poolWithdrawUnbonded && maxUnlockingChunks && unlockingLen !== undefined && unbonded && formatted && void unbonded(...params).paymentInfo(formatted).then((i) => { - const fee = i?.partialFee; - - if (unlockingLen < maxUnlockingChunks) { - setEstimatedFee(fee); - } else { - const dummyParams = [1, 1]; - - poolWithdrawUnbonded(...dummyParams) - .paymentInfo(formatted) - .then( - (j) => setEstimatedFee(api.createType('Balance', fee.add(j?.partialFee || BN_ZERO)) as Balance) - ) - .catch(console.error); - } - }).catch(console.error); + poolWithdrawUnbonded && maxUnlockingChunks && unlockingLen !== undefined && + unbonded(...params).paymentInfo(formatted).then((i) => { + const fee = i?.partialFee; + + if (unlockingLen < maxUnlockingChunks) { + setEstimatedFee(fee); + } else { + const dummyParams = [1, 1]; + + poolWithdrawUnbonded(...dummyParams) + .paymentInfo(formatted) + .then( + (j) => setEstimatedFee(api.createType('Balance', fee.add(j?.partialFee || BN_ZERO)) as Balance) + ) + .catch(console.error); + } + }).catch(console.error); }, [amountAsBN, api, decimal, formatted, maxUnlockingChunks, poolWithdrawUnbonded, unbonded, unlockingLen]); const onChangeAmount = useCallback((value: string) => { @@ -191,6 +191,13 @@ export default function Unstake ({ address, setRefresh, setShow, show }: Props): setUnstakeMaxAmount(false); + if (partial.isNeg()) { // This rare condition occurs only for old stakers who staked before the poolConsts.minCreateBond value was updated. + setAmountAsBN(staked); + setAmount(amountToHuman(staked, decimal)); + + return; + } + if (!partial.isZero()) { setAmountAsBN(partial); setAmount(amountToHuman(partial, decimal)); From 64b3cd6eba1f5463624934ef4c4cf4190cba2451 Mon Sep 17 00:00:00 2001 From: Amir Ekbatanifard Date: Sun, 1 Dec 2024 10:02:18 +0330 Subject: [PATCH 3/3] fix: ci error --- .../src/fullscreen/stake/pool/commonTasks/editPool/Review.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/Review.tsx b/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/Review.tsx index 10cff3da4..c434963db 100644 --- a/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/Review.tsx +++ b/packages/extension-polkagate/src/fullscreen/stake/pool/commonTasks/editPool/Review.tsx @@ -13,7 +13,7 @@ import type { StakingInputs } from '../../../type'; import type { ChangesProps } from '.'; import { Divider, Grid, Typography } from '@mui/material'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import SelectProxyModal2 from '@polkadot/extension-polkagate/src/fullscreen/governance/components/SelectProxyModal2'; import { PROXY_TYPE } from '@polkadot/extension-polkagate/src/util/constants';