Skip to content

Commit

Permalink
feat(fix): Abstract LeavePool, use as depositor unbond (#2370)
Browse files Browse the repository at this point in the history
  • Loading branch information
rossbulat authored Dec 17, 2024
1 parent e1c3a08 commit a97eaaf
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 147 deletions.
17 changes: 10 additions & 7 deletions packages/app/src/contexts/Balances/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { useActiveBalances } from 'hooks/useActiveBalances'
import { useCreatePoolAccounts } from 'hooks/useCreatePoolAccounts'
import type { ReactNode } from 'react'
import { createContext, useContext, useEffect, useRef } from 'react'
import type { MaybeAddress } from 'types'
import type { ActivePoolItem, MaybeAddress } from 'types'
import { useEventListener } from 'usehooks-ts'
import * as defaults from './defaults'
import type { BalancesContextInterface } from './types'
Expand Down Expand Up @@ -61,12 +61,15 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => {

// If a pool membership exists, let `ActivePools` know of pool membership to re-sync pool
// details and nominations.
if (isReady && poolMembership) {
const { poolId } = poolMembership
const newPools = ActivePools.getformattedPoolItems(address).concat({
id: String(poolId),
addresses: { ...createPoolAccounts(Number(poolId)) },
})
if (isReady) {
let newPools: ActivePoolItem[] = []
if (poolMembership) {
const { poolId } = poolMembership
newPools = ActivePools.getformattedPoolItems(address).concat({
id: String(poolId),
addresses: { ...createPoolAccounts(Number(poolId)) },
})
}

const peopleApi = Apis.getApi(`people-${network}` as SystemChainId)
if (peopleApi) {
Expand Down
30 changes: 18 additions & 12 deletions packages/app/src/contexts/Pools/ActivePool/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { useActivePools } from 'hooks/useActivePools'
import { useCreatePoolAccounts } from 'hooks/useCreatePoolAccounts'
import type { ReactNode } from 'react'
import { createContext, useContext, useRef, useState } from 'react'
import type { ActivePoolItem } from 'types'
import { useApi } from '../../Api'
import { defaultActivePoolContext, defaultPoolRoles } from './defaults'
import type { ActivePoolContextState } from './types'
Expand Down Expand Up @@ -62,19 +63,21 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => {

// Sync active pool subscriptions.
const syncActivePools = async () => {
if (isReady && accountPoolId) {
const newActivePool = [
{
id: accountPoolId,
addresses: { ...createPoolAccounts(Number(accountPoolId)) },
},
]

Syncs.dispatch('active-pools', 'syncing')
if (isReady) {
let newActivePool: ActivePoolItem[] = []
if (accountPoolId) {
newActivePool = [
{
id: accountPoolId,
addresses: { ...createPoolAccounts(Number(accountPoolId)) },
},
]
Syncs.dispatch('active-pools', 'syncing')
} else {
// No active pools to sync. Mark as complete.
Syncs.dispatch('active-pools', 'complete')
}
ActivePools.syncPools(network, activeAccount, newActivePool)
} else {
// No active pools to sync. Mark as complete.
Syncs.dispatch('active-pools', 'complete')
}
}

Expand All @@ -84,6 +87,9 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => {
if (initialActivePoolId && !activePool) {
setActivePoolId(String(initialActivePoolId))
}
if (activePool && !initialActivePoolId) {
setActivePoolId(null)
}
}

// Reset `activePoolId`.
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/overlay/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { DiscordSupport } from './modals/DiscordSupport'
import { ImportLedger } from './modals/ImportLedger'
import { ImportVault } from './modals/ImportVault'
import { ImportWalletConnect } from './modals/ImportWalletConnect'
import { LeavePool } from './modals/LeavePool'
import { MailSupport } from './modals/MailSupport'
import { ManageFastUnstake } from './modals/ManageFastUnstake'
import { ManagePool } from './modals/ManagePool'
Expand Down Expand Up @@ -52,6 +53,7 @@ export const Overlays = () => {
Connect,
Accounts,
DiscordSupport,
LeavePool,
MailSupport,
ImportLedger,
ImportVault,
Expand Down
140 changes: 140 additions & 0 deletions packages/app/src/overlay/modals/LeavePool/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors
// SPDX-License-Identifier: GPL-3.0-only

import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'
import { planckToUnit } from '@w3ux/utils'
import { PoolUnbond } from 'api/tx/poolUnbond'
import { useActiveAccounts } from 'contexts/ActiveAccounts'
import { useApi } from 'contexts/Api'
import { useBalances } from 'contexts/Balances'
import { useNetwork } from 'contexts/Network'
import { useActivePool } from 'contexts/Pools/ActivePool'
import { useTransferOptions } from 'contexts/TransferOptions'
import { getUnixTime } from 'date-fns'
import { useErasToTimeLeft } from 'hooks/useErasToTimeLeft'
import { useSignerWarnings } from 'hooks/useSignerWarnings'
import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'
import { useOverlay } from 'kits/Overlay/Provider'
import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'
import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'
import { ActionItem } from 'library/ActionItem'
import { Warning } from 'library/Form/Warning'
import { SubmitTx } from 'library/SubmitTx'
import { StaticNote } from 'overlay/modals/Utils/StaticNote'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ButtonSubmitInvert } from 'ui-buttons'
import { planckToUnitBn, timeleftAsString } from 'utils'

export const LeavePool = ({
onResize,
onClick,
}: {
onResize?: () => void
onClick?: () => void
}) => {
const { t } = useTranslation('modals')
const { consts } = useApi()
const {
network,
networkData: { units, unit },
} = useNetwork()
const { activePool } = useActivePool()
const { getPoolMembership } = useBalances()
const { erasToSeconds } = useErasToTimeLeft()
const { setModalStatus } = useOverlay().modal
const { activeAccount } = useActiveAccounts()
const { getSignerWarnings } = useSignerWarnings()
const { getTransferOptions } = useTransferOptions()

const allTransferOptions = getTransferOptions(activeAccount)
const { active: activeBn } = allTransferOptions.pool
const { bondDuration } = consts
const pendingRewards = activePool?.pendingRewards || 0n
const membership = getPoolMembership(activeAccount)
const bondDurationFormatted = timeleftAsString(
t,
getUnixTime(new Date()) + 1,
erasToSeconds(bondDuration),
true
)
const freeToUnbond = planckToUnitBn(activeBn, units)
const pendingRewardsUnit = planckToUnit(pendingRewards, units)

const [paramsValid, setParamsValid] = useState<boolean>(false)

useEffect(() => {
setParamsValid(BigInt(membership?.points || 0) > 0 && !!activePool?.id)
}, [freeToUnbond.toString()])

const getTx = () => {
let tx = null
if (!activeAccount || !membership) {
return tx
}
tx = new PoolUnbond(network, activeAccount, BigInt(membership.points)).tx()
return tx
}

const submitExtrinsic = useSubmitExtrinsic({
tx: getTx(),
from: activeAccount,
shouldSubmit: paramsValid,
callbackSubmit: () => {
setModalStatus('closing')
},
})

const warnings = getSignerWarnings(
activeAccount,
false,
submitExtrinsic.proxySupported
)

if (pendingRewards > 0) {
warnings.push(
`${t('unbondingWithdraw')} ${pendingRewardsUnit.toString()} ${unit}.`
)
}

return (
<>
<ModalPadding horizontalOnly>
{warnings.length > 0 ? (
<ModalWarnings withMargin>
{warnings.map((text, i) => (
<Warning key={`warning${i}`} text={text} />
))}
</ModalWarnings>
) : null}
<ActionItem
text={`${t('unbond')} ${freeToUnbond.toString()} ${unit}`}
/>
<StaticNote
value={bondDurationFormatted}
tKey="onceUnbonding"
valueKey="bondDurationFormatted"
deps={[bondDuration]}
/>
</ModalPadding>
<SubmitTx
valid={paramsValid}
buttons={
onClick
? [
<ButtonSubmitInvert
key="button_back"
text={t('back')}
iconLeft={faChevronLeft}
iconTransform="shrink-1"
onClick={onClick}
/>,
]
: undefined
}
onResize={onResize}
{...submitExtrinsic}
/>
</>
)
}
129 changes: 3 additions & 126 deletions packages/app/src/overlay/modals/ManagePool/Forms/LeavePool/index.tsx
Original file line number Diff line number Diff line change
@@ -1,136 +1,13 @@
// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors
// SPDX-License-Identifier: GPL-3.0-only

import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'
import { planckToUnit } from '@w3ux/utils'
import { PoolUnbond } from 'api/tx/poolUnbond'
import { useActiveAccounts } from 'contexts/ActiveAccounts'
import { useApi } from 'contexts/Api'
import { useBalances } from 'contexts/Balances'
import { useNetwork } from 'contexts/Network'
import { useActivePool } from 'contexts/Pools/ActivePool'
import { useTransferOptions } from 'contexts/TransferOptions'
import { getUnixTime } from 'date-fns'
import { useErasToTimeLeft } from 'hooks/useErasToTimeLeft'
import { useSignerWarnings } from 'hooks/useSignerWarnings'
import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'
import { useOverlay } from 'kits/Overlay/Provider'
import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'
import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'
import { ActionItem } from 'library/ActionItem'
import { Warning } from 'library/Form/Warning'
import { SubmitTx } from 'library/SubmitTx'
import { StaticNote } from 'overlay/modals/Utils/StaticNote'
import { useEffect, useState, type Dispatch, type SetStateAction } from 'react'
import { useTranslation } from 'react-i18next'
import { ButtonSubmitInvert } from 'ui-buttons'
import { planckToUnitBn, timeleftAsString } from 'utils'
import { LeavePool as LeavePoolModal } from 'overlay/modals/LeavePool'
import { type Dispatch, type SetStateAction } from 'react'

export const LeavePool = ({
setSection,
onResize,
}: {
setSection: Dispatch<SetStateAction<number>>
onResize: () => void
}) => {
const { t } = useTranslation('modals')
const { consts } = useApi()
const {
network,
networkData: { units, unit },
} = useNetwork()
const { activePool } = useActivePool()
const { getPoolMembership } = useBalances()
const { erasToSeconds } = useErasToTimeLeft()
const { setModalStatus } = useOverlay().modal
const { activeAccount } = useActiveAccounts()
const { getSignerWarnings } = useSignerWarnings()
const { getTransferOptions } = useTransferOptions()

const allTransferOptions = getTransferOptions(activeAccount)
const { active: activeBn } = allTransferOptions.pool
const { bondDuration } = consts
const pendingRewards = activePool?.pendingRewards || 0n
const membership = getPoolMembership(activeAccount)
const bondDurationFormatted = timeleftAsString(
t,
getUnixTime(new Date()) + 1,
erasToSeconds(bondDuration),
true
)
const freeToUnbond = planckToUnitBn(activeBn, units)
const pendingRewardsUnit = planckToUnit(pendingRewards, units)

const [paramsValid, setParamsValid] = useState<boolean>(false)

useEffect(() => {
setParamsValid(BigInt(membership?.points || 0) > 0 && !!activePool?.id)
}, [freeToUnbond.toString()])

const getTx = () => {
let tx = null
if (!activeAccount || !membership) {
return tx
}
tx = new PoolUnbond(network, activeAccount, BigInt(membership.points)).tx()
return tx
}

const submitExtrinsic = useSubmitExtrinsic({
tx: getTx(),
from: activeAccount,
shouldSubmit: paramsValid,
callbackSubmit: () => {
setModalStatus('closing')
},
})

const warnings = getSignerWarnings(
activeAccount,
false,
submitExtrinsic.proxySupported
)

if (pendingRewards > 0) {
warnings.push(
`${t('unbondingWithdraw')} ${pendingRewardsUnit.toString()} ${unit}.`
)
}

return (
<>
<ModalPadding horizontalOnly>
{warnings.length > 0 ? (
<ModalWarnings withMargin>
{warnings.map((text, i) => (
<Warning key={`warning${i}`} text={text} />
))}
</ModalWarnings>
) : null}
<ActionItem
text={`${t('unbond')} ${freeToUnbond.toString()} ${unit}`}
/>
<StaticNote
value={bondDurationFormatted}
tKey="onceUnbonding"
valueKey="bondDurationFormatted"
deps={[bondDuration]}
/>
</ModalPadding>
<SubmitTx
valid={paramsValid}
buttons={[
<ButtonSubmitInvert
key="button_back"
text={t('back')}
iconLeft={faChevronLeft}
iconTransform="shrink-1"
onClick={() => setSection(0)}
/>,
]}
onResize={onResize}
{...submitExtrinsic}
/>
</>
)
}
}) => <LeavePoolModal onResize={onResize} onClick={() => setSection(0)} />
2 changes: 1 addition & 1 deletion packages/app/src/pages/Pools/ClosurePrompts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export const ClosurePrompts = () => {
}
onClick={() =>
openModal({
key: 'Unbond',
key: 'LeavePool',
options: { bondFor: 'pool' },
size: 'sm',
})
Expand Down
2 changes: 1 addition & 1 deletion packages/locales/src/resources/en/pages.json
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@
"outstandingReward": "outstanding reward balance.",
"overview": "Overview",
"ownerOfPool": "Owner of Pool",
"permissionToUnbond": "You have permission to unbond and withdraw funds of any pool member. Use a member's's menu",
"permissionToUnbond": "You have permission to unbond and withdraw funds of any pool member. Use a member's menu",
"poolCommission": "Pool Commission",
"poolCreator": " As the pool creator, you will consume your pool's <b>Depositor</b> role.",
"poolCurrentlyLocked": "Pool Currently Locked",
Expand Down

0 comments on commit a97eaaf

Please sign in to comment.