Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🗳️ Fix past votes stake recovering #2855

Merged
merged 12 commits into from
Apr 24, 2022
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useMemo } from 'react'

import { LockItem } from '@/accounts/components/AccountItem/components/LockItem'
import { useIsVoteStakeLocked } from '@/accounts/hooks/useIsVoteStakeLocked'
import { isRecoverable } from '@/accounts/model/lockTypes'
import { Balances } from '@/accounts/types'
import { RowGapBlock } from '@/common/components/page/PageContent'
Expand All @@ -23,12 +24,11 @@ export const LocksDetails = ({ balance, address }: LocksDetailsProps) => {
[election, address]
)

const isVoteStakeLocked = useMemo(() => {
const candidate = votes?.find((vote) => vote.castBy === address)?.voteFor
// Lock stake if the vote was cast: in current election or to winning candidate
// Enable stake recovery if election is finished
return !!candidate && (!election?.isFinished || candidate.isCouncilMember)
}, [votes, address, election])
const candidate = useMemo(() => {
return votes?.find((vote) => vote.castBy === address)?.voteFor
}, [votes])

const isVoteStakeLocked = !!useIsVoteStakeLocked(candidate, { isElectionFinished: election?.isFinished })

if (!balance || !balance.locks.length) {
return <TextMedium light>No locks found.</TextMedium>
Expand Down
10 changes: 10 additions & 0 deletions packages/ui/src/accounts/hooks/useIsVoteStakeLocked.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Member } from '@/memberships/types'

export const useIsVoteStakeLocked = (
candidate: Member | undefined,
{ isElectionFinished = true, isLatestElection = true }
) => {
// Lock stake if the vote was cast: in current election or to winning candidate
// Enable stake recovery if election is finished
return isLatestElection && !!candidate && (!isElectionFinished || candidate.isCouncilMember)
}
4 changes: 1 addition & 3 deletions packages/ui/src/app/pages/Proposals/ProposalPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { useModal } from '@/common/hooks/useModal'
import { formatBlocksToDuration, formatTokenValue } from '@/common/model/formatters'
import { getUrl } from '@/common/utils/getUrl'
import { MemberInfo } from '@/memberships/components'
import { useIsCouncilMember } from '@/memberships/hooks/useIsCouncilMember'
import { useMyMemberships } from '@/memberships/hooks/useMyMemberships'
import { ProposalDetails } from '@/proposals/components/ProposalDetails/ProposalDetails'
import { ProposalDiscussions } from '@/proposals/components/ProposalDiscussions'
Expand Down Expand Up @@ -64,7 +63,6 @@ export const ProposalPreview = () => {
}, [voteId])

const { active } = useMyMemberships()
const isCouncilMember = useIsCouncilMember(active)
const hasVoted = useHasMemberVotedOnProposal(id, active?.id)

const myVote = proposal?.votes.find((vote) => vote.voter.id === active?.id && vote.votingRound === currentVotingRound)
Expand Down Expand Up @@ -96,7 +94,7 @@ export const ProposalPreview = () => {
<PageTitle>{proposal.title}</PageTitle>
</PreviousPage>
<ButtonsGroup>
{isCouncilMember &&
{active?.isCouncilMember &&
proposal.status === 'deciding' &&
(!hasVoted ? (
<VoteForProposalButton id={id}>Vote on Proposal</VoteForProposalButton>
Expand Down
18 changes: 18 additions & 0 deletions packages/ui/src/bounty/queries/__generated__/bounty.generated.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 10 additions & 3 deletions packages/ui/src/council/components/PastVotes/PastVote/PastVote.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react'

import { AccountInfo } from '@/accounts/components/AccountInfo'
import { useIsVoteStakeLocked } from '@/accounts/hooks/useIsVoteStakeLocked'
import { useMyAccounts } from '@/accounts/hooks/useMyAccounts'
import { RecoverBalanceModalCall, VotingData } from '@/accounts/modals/RecoverBalance'
import { accountOrNamed } from '@/accounts/model/accountOrNamed'
Expand All @@ -16,10 +17,11 @@ import { PastVoteTableListItem, StakeRecoveringButton } from '../styles'

export interface PastVoteProps {
vote: Vote
latestCycleId?: number
$colLayout: string
}

export const PastVote = ({ vote, $colLayout }: PastVoteProps) => {
export const PastVote = ({ vote, latestCycleId, $colLayout }: PastVoteProps) => {
const { allAccounts } = useMyAccounts()
const { showModal } = useModal()
const { isTransactionPending } = useTransactionStatus()
Expand All @@ -36,7 +38,12 @@ export const PastVote = ({ vote, $colLayout }: PastVoteProps) => {
})
}

const isDisabled = !vote.stakeLocked || isTransactionPending
// Reflects if the vote was cast in latest election
const isLatestElection = vote.cycleId === latestCycleId
const isVoteStakeLocked = useIsVoteStakeLocked(vote.voteFor, { isLatestElection })
// Reflects if the stake has been already released by the member.
const isRecovered = !vote.stakeLocked
const isDisabled = isVoteStakeLocked || isRecovered || isTransactionPending

return (
<PastVoteTableListItem $isPast $colLayout={$colLayout}>
Expand All @@ -48,7 +55,7 @@ export const PastVote = ({ vote, $colLayout }: PastVoteProps) => {
<TextInlineMedium>{!vote.voteFor ? 'Sealed' : 'Unsealed'}</TextInlineMedium>
<TransactionButtonWrapper>
<StakeRecoveringButton size="small" disabled={isDisabled} onClick={onClick}>
{vote.stakeLocked ? 'Recover stake' : 'Stake recovered'}
{isRecovered ? 'Stake recovered' : 'Recover stake'}
</StakeRecoveringButton>
</TransactionButtonWrapper>
</PastVoteTableListItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { RowGapBlock } from '@/common/components/page/PageContent'
import { Pagination } from '@/common/components/Pagination'
import { NotFoundText } from '@/common/components/typography/NotFoundText'
import { useSort } from '@/common/hooks/useSort'
import { useLatestElection } from '@/council/hooks/useLatestElection'
RafalPloszka marked this conversation as resolved.
Show resolved Hide resolved
import { useMyPastVotes } from '@/council/hooks/useMyPastVotes'

import { PastVote } from './PastVote/PastVote'
Expand All @@ -17,6 +18,7 @@ import { PastVoteColumns } from './styles'
export const PastVotesList = () => {
const { order, getSortProps } = useSort<CastVoteOrderByInput>('createdAt')
const { votes, isLoading, pagination } = useMyPastVotes({ order })
const { election: latestElection } = useLatestElection()

if (isLoading) {
return <Loading />
Expand All @@ -38,7 +40,7 @@ export const PastVotesList = () => {
</ListHeaders>
<List>
{votes.map((vote) => (
<PastVote vote={vote} key={vote.id} $colLayout={PastVoteColumns} />
<PastVote vote={vote} key={vote.id} latestCycleId={latestElection?.cycleId} $colLayout={PastVoteColumns} />
))}
</List>
<Pagination {...pagination} />
Expand Down
Loading