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

Allow voting abstain on proposals. #454

Merged
merged 1 commit into from
Apr 9, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 65 additions & 43 deletions apps/dapp/components/ProposalDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
ExternalLinkIcon,
EyeIcon,
EyeOffIcon,
MinusIcon,
SparklesIcon,
XIcon,
} from '@heroicons/react/outline'
Expand Down Expand Up @@ -65,7 +66,7 @@ import { CosmosMessageDisplay } from './CosmosMessageDisplay'
import { getEnd } from './ProposalList'

function executeProposalVote(
vote: 'yes' | 'no',
vote: 'yes' | 'no' | 'abstain',
id: number,
contractAddress: string,
signingClient: SigningCosmWasmClient | null,
Expand Down Expand Up @@ -148,6 +149,7 @@ function LoadingButton() {
function ProposalVoteButtons({
yesCount,
noCount,
abstainCount,
proposalId,
contractAddress,
voted,
Expand All @@ -156,6 +158,7 @@ function ProposalVoteButtons({
}: {
yesCount: string
noCount: string
abstainCount: string
proposalId: number
contractAddress: string
voted: boolean
Expand Down Expand Up @@ -196,15 +199,17 @@ function ProposalVoteButtons({
position,
children,
}: {
position: 'yes' | 'no'
position: 'yes' | 'no' | 'abstain'
children: ReactNode
}) => (
<button
className={
'btn btn-sm btn-outline normal-case border-base-300 shadow w-36 font-normal rounded-md px-1' +
(position === 'yes' ? ' hover:bg-green-500' : ' hover:bg-red-500') +
(ready ? '' : ' btn-disabled bg-base-300')
}
className={`btn btn-sm btn-outline normal-case border-base-300 shadow w-36 font-normal rounded-md px-1 ${
position === 'yes'
? ' hover:bg-green-500 hover:border-green-500'
: position === 'no'
? ' hover:bg-red-500 hover:border-red-500'
: ' hover:bg-primary hover:border-primary'
} ${ready ? '' : ' btn-disabled bg-base-300'}`}
onClick={() =>
executeProposalVote(
position,
Expand Down Expand Up @@ -238,6 +243,11 @@ function ProposalVoteButtons({
No
<p className="text-secondary ml-2">{noCount}</p>
</VoteButton>
<VoteButton position="abstain">
<MinusIcon className="w-4 h-4 inline mr-2" />
Abstain
<p className="text-secondary ml-2">{abstainCount}</p>
</VoteButton>
</div>
</div>
)
Expand Down Expand Up @@ -355,6 +365,14 @@ export function ProposalDetailsSidebar({
tokenDecimals
)
)
const abstainVotes = Number(
multisig
? proposalTally?.votes.abstain
: convertMicroDenomToDenomWithDecimals(
proposalTally?.votes.abstain ?? 0,
tokenDecimals
)
)
const totalWeight = Number(
multisig
? proposalTally.total_weight
Expand All @@ -365,7 +383,7 @@ export function ProposalDetailsSidebar({
)

const turnoutPercent = (
((yesVotes + noVotes) / totalWeight) *
((yesVotes + noVotes) / (totalWeight - abstainVotes)) *
100
).toLocaleString(undefined, localeOptions)
const yesPercent = ((yesVotes / totalWeight) * 100).toLocaleString(
Expand Down Expand Up @@ -446,6 +464,8 @@ export function ProposalDetailsSidebar({
<div className="col-span-2">
{noVotes} <p className="text-secondary inline">({noPercent}%)</p>
</div>
<p className="text-secondary">Abstain votes</p>
<div className="col-span-2">{abstainVotes}</div>
<p className="text-secondary">Threshold</p>
<div className="col-span-2">{threshold}</div>
{quorum && (
Expand Down Expand Up @@ -595,6 +615,14 @@ export function ProposalDetails({
tokenDecimals
)
)
const abstainVotes = Number(
multisig
? proposalTally?.votes.abstain
: convertMicroDenomToDenomWithDecimals(
proposalTally?.votes.abstain ?? 0,
tokenDecimals
)
)

const decodedMessages = decodeMessages(proposal.msgs)

Expand All @@ -603,46 +631,40 @@ export function ProposalDetails({
<div className="max-w-prose">
<h1 className="text-4xl font-semibold">{proposal.title}</h1>
</div>
{actionLoading && (
<div className="mt-3">
<LoadingButton />
</div>
)}
{!actionLoading && proposal.status === 'open' && (
<div className="mt-3 flex flex-row flex-wrap items-center gap-3">
{tokenBalancesLoading ? (
{actionLoading ||
(tokenBalancesLoading && (
<div className="mt-3">
<LoadingButton />
) : (
<>
<ProposalVoteButtons
yesCount={yesVotes.toString()}
noCount={noVotes.toString()}
proposalId={proposalId}
contractAddress={contractAddress}
voted={voted}
setLoading={setActionLoading}
multisig={!!multisig}
/>
<ProposalVoteStatus
contractAddress={contractAddress}
proposalId={proposalId}
/>
</>
)}
</div>
)}
{!actionLoading && proposal.status === 'passed' && (
<div className="mt-3">
{tokenBalancesLoading ? (
<LoadingButton />
) : (
<ProposalExecuteButton
</div>
))}
{!actionLoading && proposal.status === 'open' && (
<div className="mt-3 flex flex-col flex-wrap justify-center gap-3">
<>
<ProposalVoteStatus
contractAddress={contractAddress}
proposalId={proposalId}
/>
<ProposalVoteButtons
yesCount={yesVotes.toString()}
noCount={noVotes.toString()}
abstainCount={abstainVotes.toString()}
proposalId={proposalId}
contractAddress={contractAddress}
member={member.member}
voted={voted}
setLoading={setActionLoading}
multisig={!!multisig}
/>
)}
</>
</div>
)}
{!actionLoading && proposal.status === 'passed' && (
<div className="mt-3">
<ProposalExecuteButton
proposalId={proposalId}
contractAddress={contractAddress}
member={member.member}
setLoading={setActionLoading}
/>
</div>
)}
<div className="py-4">
Expand Down