Skip to content

Commit

Permalink
feat: Use pool points for unbonding / leaving pool (#2369)
Browse files Browse the repository at this point in the history
  • Loading branch information
rossbulat authored Dec 17, 2024
1 parent e94d92d commit e1c3a08
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 77 deletions.
31 changes: 31 additions & 0 deletions packages/app/src/api/runtimeApi/poolBalanceToPoints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors
// SPDX-License-Identifier: GPL-3.0-only

import { Base } from 'api/base'
import type { ChainId } from 'common-types'

export class PoolBalanceToPoints extends Base {
#poolId: number
#balance: bigint

constructor(network: ChainId, poolId: number, balance: bigint) {
super(network)
this.#poolId = poolId
this.#balance = balance
}

async fetch() {
try {
const result =
await this.unsafeApi.apis.NominationPoolsApi.balance_to_points(
this.#poolId,
this.#balance,
{ at: 'best' }
)
return result
} catch (e) {
// Silently fail.
}
return undefined
}
}
8 changes: 4 additions & 4 deletions packages/app/src/api/tx/poolUnbond.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import type { ChainId } from 'common-types'

export class PoolUnbond extends Base {
#who: string
#bond: bigint
#points: bigint

constructor(network: ChainId, who: string, bond: bigint) {
constructor(network: ChainId, who: string, points: bigint) {
super(network)
this.#who = who
this.#bond = bond
this.#points = points
}

tx() {
Expand All @@ -21,7 +21,7 @@ export class PoolUnbond extends Base {
type: 'Id',
value: this.#who,
},
unbonding_points: this.#bond,
unbonding_points: this.#points,
})
} catch (e) {
return null
Expand Down
1 change: 0 additions & 1 deletion packages/app/src/controllers/Subscan/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ export class Subscan {
poolId: entry.pool_id,
}))
.reverse()
.splice(0, result.list.length - 1)
}

// Fetch a pool's era points from Subscan.
Expand Down
9 changes: 0 additions & 9 deletions packages/app/src/overlay/canvas/PoolMembers/Lists/Member.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,6 @@ export const Member = ({

const menuItems: AnyJson[] = []

menuItems.push({
icon: <FontAwesomeIcon icon={faUnlockAlt} transform="shrink-3" />,
wrap: null,
title: `${t('pools.withdrawFunds', { ns: 'pages' })}`,
cb: () => {
openPromptWith(<UnbondMember who={who} member={member} />)
},
})

if (member && (canUnbondBlocked || canUnbondDestroying)) {
const { points, unbondingEras } = member

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-only

import { Polkicon } from '@w3ux/react-polkicon'
import { ellipsisFn, rmCommas, unitToPlanck } from '@w3ux/utils'
import { ellipsisFn, rmCommas } from '@w3ux/utils'
import { PoolUnbond } from 'api/tx/poolUnbond'
import BigNumber from 'bignumber.js'
import { useActiveAccounts } from 'contexts/ActiveAccounts'
Expand Down Expand Up @@ -43,51 +43,35 @@ export const UnbondMember = ({
const { erasToSeconds } = useErasToTimeLeft()
const { getSignerWarnings } = useSignerWarnings()

const { bondDuration } = consts
const { points } = member
const { bondDuration } = consts
const freeToUnbond = planckToUnitBn(new BigNumber(rmCommas(points)), units)

const bondDurationFormatted = timeleftAsString(
t,
getUnixTime(new Date()) + 1,
erasToSeconds(bondDuration),
true
)

// local bond value
const [bond, setBond] = useState<{ bond: string }>({
bond: freeToUnbond.toString(),
})

// bond valid
const [bondValid, setBondValid] = useState<boolean>(false)

// unbond all validation
const isValid = (() => freeToUnbond.isGreaterThan(0))()
const [paramsValid, setParamsValid] = useState<boolean>(false)

// update bond value on task change
useEffect(() => {
setBond({ bond: freeToUnbond.toString() })
setBondValid(isValid)
}, [freeToUnbond.toString(), isValid])
setParamsValid(BigInt(points) > 0)
}, [freeToUnbond.toString()])

const getTx = () => {
let tx = null
if (!activeAccount) {
return tx
}
tx = new PoolUnbond(
network,
who,
unitToPlanck(!bondValid ? 0 : bond.bond, units)
).tx()
tx = new PoolUnbond(network, who, BigInt(points)).tx()
return tx
}

const submitExtrinsic = useSubmitExtrinsic({
tx: getTx(),
from: activeAccount,
shouldSubmit: bondValid,
shouldSubmit: paramsValid,
callbackSubmit: () => {
closePrompt()
},
Expand Down Expand Up @@ -115,7 +99,9 @@ export const UnbondMember = ({
&nbsp; {ellipsisFn(who, 7)}
</h3>
<ModalNotes>
<p>{t('amountWillBeUnbonded', { bond: bond.bond, unit })}</p>
<p>
{t('amountWillBeUnbonded', { bond: freeToUnbond.toString(), unit })}
</p>
<StaticNote
value={bondDurationFormatted}
tKey="onceUnbondingPoolMember"
Expand All @@ -124,7 +110,7 @@ export const UnbondMember = ({
/>
</ModalNotes>
</ModalPadding>
<SubmitTx noMargin valid={bondValid} {...submitExtrinsic} />
<SubmitTx noMargin valid={paramsValid} {...submitExtrinsic} />
</>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
// SPDX-License-Identifier: GPL-3.0-only

import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'
import { planckToUnit, unitToPlanck } from '@w3ux/utils'
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'
Expand Down Expand Up @@ -39,66 +40,46 @@ export const LeavePool = ({
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 { setModalStatus, setModalResize } = useOverlay().modal

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 pendingRewardsUnit = planckToUnit(pendingRewards, units)

// convert BigNumber values to number
const freeToUnbond = planckToUnitBn(activeBn, units)
const pendingRewardsUnit = planckToUnit(pendingRewards, units)

// local bond value
const [bond, setBond] = useState<{ bond: string }>({
bond: freeToUnbond.toString(),
})

// bond valid
const [bondValid, setBondValid] = useState<boolean>(false)

// unbond all validation
const isValid = (() => freeToUnbond.isGreaterThan(0))()
const [paramsValid, setParamsValid] = useState<boolean>(false)

// update bond value on task change
useEffect(() => {
setBond({ bond: freeToUnbond.toString() })
setBondValid(isValid)
}, [freeToUnbond.toString(), isValid])

// modal resize on form update
useEffect(() => setModalResize(), [bond])
setParamsValid(BigInt(membership?.points || 0) > 0 && !!activePool?.id)
}, [freeToUnbond.toString()])

const getTx = () => {
let tx = null
if (!activeAccount) {
if (!activeAccount || !membership) {
return tx
}
tx = new PoolUnbond(
network,
activeAccount,
unitToPlanck(!bondValid ? 0 : bond.bond, units)
).tx()
tx = new PoolUnbond(network, activeAccount, BigInt(membership.points)).tx()
return tx
}

const submitExtrinsic = useSubmitExtrinsic({
tx: getTx(),
from: activeAccount,
shouldSubmit: bondValid,
shouldSubmit: paramsValid,
callbackSubmit: () => {
setModalStatus('closing')
},
Expand Down Expand Up @@ -126,7 +107,9 @@ export const LeavePool = ({
))}
</ModalWarnings>
) : null}
<ActionItem text={`${t('unbond')} ${freeToUnbond} ${unit}`} />
<ActionItem
text={`${t('unbond')} ${freeToUnbond.toString()} ${unit}`}
/>
<StaticNote
value={bondDurationFormatted}
tKey="onceUnbonding"
Expand All @@ -135,7 +118,7 @@ export const LeavePool = ({
/>
</ModalPadding>
<SubmitTx
valid={bondValid}
valid={paramsValid}
buttons={[
<ButtonSubmitInvert
key="button_back"
Expand Down
18 changes: 13 additions & 5 deletions packages/app/src/overlay/modals/Unbond/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-only

import { planckToUnit, unitToPlanck } from '@w3ux/utils'
import { PoolBalanceToPoints } from 'api/runtimeApi/poolBalanceToPoints'
import { PoolUnbond } from 'api/tx/poolUnbond'
import { StakingUnbond } from 'api/tx/stakingUnbond'
import BigNumber from 'bignumber.js'
Expand Down Expand Up @@ -82,16 +83,23 @@ export const Unbond = () => {
const minCreateBond = planckToUnitBn(minCreateBondBn, units)
const minNominatorBond = planckToUnitBn(minNominatorBondBn, units)

// local bond value
const [bond, setBond] = useState<{ bond: string }>({
bond: freeToUnbond.toString(),
})
const [points, setPoints] = useState<bigint>(0n)

// bond valid
const [bondValid, setBondValid] = useState<boolean>(false)

// handler to set bond as a string
const handleSetBond = (newBond: { bond: BigNumber }) => {
const handleSetBond = async (newBond: { bond: BigNumber }) => {
if (isPooling && activePool) {
const balancePoints = await new PoolBalanceToPoints(
network,
activePool.id,
unitToPlanck(newBond.bond.toString(), units)
).fetch()
setPoints(balancePoints)
}
setBond({ bond: newBond.bond.toString() })
}

Expand All @@ -114,7 +122,7 @@ export const Unbond = () => {

const bondToSubmit = unitToPlanck(!bondValid ? 0 : bond.bond, units)
if (isPooling) {
tx = new PoolUnbond(network, activeAccount, bondToSubmit).tx()
tx = new PoolUnbond(network, activeAccount, points).tx()
} else if (isStaking) {
tx = new StakingUnbond(network, bondToSubmit).tx()
}
Expand Down Expand Up @@ -173,7 +181,7 @@ export const Unbond = () => {
warnings.push(t('unbondErrorNoFunds', { unit }))
}

// Update bond value on task change.
// Update bond value on task change
useEffect(() => {
handleSetBond({ bond: unbondToMin })
}, [freeToUnbond.toString()])
Expand Down

0 comments on commit e1c3a08

Please sign in to comment.