Skip to content

Commit

Permalink
CreateNewProposalWithInsufficientPower
Browse files Browse the repository at this point in the history
  • Loading branch information
ppsimatikas committed Sep 12, 2024
1 parent 6f282aa commit 9396e6e
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 55 deletions.
24 changes: 17 additions & 7 deletions components/MultiChoiceForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { XCircleIcon } from '@heroicons/react/solid'
import useGovernanceAssets from '@hooks/useGovernanceAssets'
import Input from '@components/inputs/Input'
import GovernedAccountSelect from '../pages/dao/[symbol]/proposal/components/GovernedAccountSelect'
import { PublicKey } from '@solana/web3.js'
import { AccountType, AssetAccount } from '@utils/uiTypes/assets'
import { useLegacyVoterWeight } from '@hooks/queries/governancePower'
import { Governance, ProgramAccount } from '@solana/spl-governance'
import { useEffect } from 'react'

const MultiChoiceForm = ({
multiChoiceForm,
Expand All @@ -17,7 +18,7 @@ const MultiChoiceForm = ({
updateMultiFormErrors,
}: {
multiChoiceForm: {
governance: PublicKey | undefined
governance: ProgramAccount<Governance> | null
options: string[]
}
updateMultiChoiceForm: any
Expand All @@ -37,6 +38,17 @@ const MultiChoiceForm = ({
updateMultiChoiceForm({ ...multiChoiceForm, [propertyName]: value })
}

const governedAccounts = assetAccounts.filter((x) =>
ownVoterWeight?.canCreateProposal(x.governance.account.config)
)

useEffect(() => {
handleMultiForm({
value: governedAccounts.length ? governedAccounts[0].governance : null,
propertyName: 'governance'
})
}, [governedAccounts.length]);

const handleNotaButton = () => {
const options = [...multiChoiceForm.options]
options.push(nota)
Expand Down Expand Up @@ -78,20 +90,18 @@ const MultiChoiceForm = ({
<div className="mt-8 mb-8">
<GovernedAccountSelect
label="Which wallet’s rules should this proposal follow?"
governedAccounts={assetAccounts.filter((x) =>
ownVoterWeight?.canCreateProposal(x.governance.account.config)
)}
governedAccounts={governedAccounts}
onChange={(value: AssetAccount) => {
handleMultiForm({
value: value.governance.pubkey,
value: value.governance,
propertyName: 'governance',
})
}}
value={
governance
? assetAccounts.find(
(x) =>
x.governance.pubkey.equals(governance) &&
x.governance.pubkey.equals(governance.pubkey) &&
x.type === AccountType.SOL
)
: null
Expand Down
62 changes: 62 additions & 0 deletions hooks/useCreateProposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ import { proposalQueryKeys } from './queries/proposal'
import { createLUTProposal } from 'actions/createLUTproposal'
import { useLegacyVoterWeight } from './queries/governancePower'
import {useVotingClients} from "@hooks/useVotingClients";
import { useRealmVoterWeightPlugins } from '@hooks/useRealmVoterWeightPlugins'
import useRealm from '@hooks/useRealm'
import useWalletOnePointOh from '@hooks/useWalletOnePointOh'
import BN from 'bn.js'
import { formatNumber } from '@utils/formatNumber'
import { BigNumber } from 'bignumber.js'
import { Governance, ProgramAccount } from '@solana/spl-governance'

export default function useCreateProposal() {
const connection = useLegacyConnectionContext()
Expand Down Expand Up @@ -202,3 +209,58 @@ export default function useCreateProposal() {

return { handleCreateProposal, propose, proposeMultiChoice }
}

export const useCanCreateProposal = (
governance?: ProgramAccount<Governance> | null
) => {
const wallet = useWalletOnePointOh()
const connected = !!wallet?.connected

const realm = useRealmQuery().data?.result

const {
ownVoterWeight: communityOwnVoterWeight,
} = useRealmVoterWeightPlugins('community')
const {
isReady,
ownVoterWeight: councilOwnVoterWeight,
} = useRealmVoterWeightPlugins('council')
const {
toManyCommunityOutstandingProposalsForUser,
toManyCouncilOutstandingProposalsForUse,
} = useRealm()

const communityOwnVoterWeightValue = communityOwnVoterWeight && communityOwnVoterWeight.value
const councilOwnVoterWeightValue = councilOwnVoterWeight && councilOwnVoterWeight.value

const minWeightToCreateProposal = (governance?.pubkey == realm?.account.communityMint ?
governance?.account.config.minCommunityTokensToCreateProposal :
governance?.account.config.minCouncilTokensToCreateProposal) || new BN(1)

const votingPower = communityOwnVoterWeightValue || councilOwnVoterWeightValue

const hasEnoughVotingPower = votingPower?.gt(minWeightToCreateProposal)

const canCreateProposal =
realm &&
hasEnoughVotingPower &&
!toManyCommunityOutstandingProposalsForUser &&
!toManyCouncilOutstandingProposalsForUse

const error = !connected
? 'Connect your wallet to create new proposal'
: isReady && !communityOwnVoterWeight && !councilOwnVoterWeight
? 'There is no governance configuration to create a new proposal'
: !hasEnoughVotingPower
? `Please select only one account with at least ${formatNumber(new BigNumber(minWeightToCreateProposal.toString()))} governance power to create a new proposal.`
: toManyCommunityOutstandingProposalsForUser
? 'Too many community outstanding proposals. You need to finalize them before creating a new one.'
: toManyCouncilOutstandingProposalsForUse
? 'Too many council outstanding proposals. You need to finalize them before creating a new one.'
: ''

return {
canCreateProposal,
error
}
}
44 changes: 3 additions & 41 deletions pages/dao/[symbol]/proposal/components/NewProposalBtn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,14 @@ import { PlusCircleIcon } from '@heroicons/react/outline'
import useQueryContext from '@hooks/useQueryContext'
import useRealm from '@hooks/useRealm'
import Tooltip from '@components/Tooltip'
import useWalletOnePointOh from '@hooks/useWalletOnePointOh'
import { useRealmQuery } from '@hooks/queries/realm'
import { useRealmVoterWeightPlugins } from '@hooks/useRealmVoterWeightPlugins'
import { useCanCreateProposal } from '@hooks/useCreateProposal'

const NewProposalBtn = () => {
const { fmtUrlWithCluster } = useQueryContext()

const wallet = useWalletOnePointOh()
const connected = !!wallet?.connected
const { symbol } = useRealm()

const realm = useRealmQuery().data?.result
// const { result: ownVoterWeight } = useLegacyVoterWeight()
const {
ownVoterWeight: communityOwnVoterWeight,
} = useRealmVoterWeightPlugins('community')
const {
isReady,
ownVoterWeight: councilOwnVoterWeight,
} = useRealmVoterWeightPlugins('council')
const {
symbol,
toManyCommunityOutstandingProposalsForUser,
toManyCouncilOutstandingProposalsForUse,
} = useRealm()

const hasVotingPower =
(communityOwnVoterWeight && communityOwnVoterWeight.value?.gtn(0)) ||
(councilOwnVoterWeight && councilOwnVoterWeight.value?.gtn(0))

const canCreateProposal =
realm &&
hasVotingPower &&
!toManyCommunityOutstandingProposalsForUser &&
!toManyCouncilOutstandingProposalsForUse

const tooltipContent = !connected
? 'Connect your wallet to create new proposal'
: isReady && !communityOwnVoterWeight && !councilOwnVoterWeight
? 'There is no governance configuration to create a new proposal'
: !hasVotingPower
? "You don't have enough governance power to create a new proposal"
: toManyCommunityOutstandingProposalsForUser
? 'Too many community outstanding proposals. You need to finalize them before creating a new one.'
: toManyCouncilOutstandingProposalsForUse
? 'Too many council outstanding proposals. You need to finalize them before creating a new one.'
: ''
const { canCreateProposal, error: tooltipContent } = useCanCreateProposal()

return (
<>
Expand Down
17 changes: 10 additions & 7 deletions pages/dao/[symbol]/proposal/new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ import DeactivateValidatorStake from './components/instructions/Validators/Deact
import WithdrawValidatorStake from './components/instructions/Validators/WithdrawStake'
import DelegateStake from './components/instructions/Validators/DelegateStake'
import SplitStake from './components/instructions/Validators/SplitStake'
import useCreateProposal from '@hooks/useCreateProposal'
import useCreateProposal, { useCanCreateProposal } from '@hooks/useCreateProposal'
import RealmConfig from './components/instructions/RealmConfig'
import CloseTokenAccount from './components/instructions/CloseTokenAccount'
import CloseMultipleTokenAccounts from './components/instructions/CloseMultipleTokenAccounts'
Expand Down Expand Up @@ -151,7 +151,7 @@ const schema = yup.object().shape({
})

const multiChoiceSchema = yup.object().shape({
governance: yup.string().required('Governance is required'),
governance: yup.object().required('Governance is required'),

options: yup.array().of(yup.string().required('Option cannot be empty')),
})
Expand Down Expand Up @@ -206,10 +206,10 @@ const New = () => {
setVoteByCouncil,
} = useVoteByCouncilToggle()
const [multiChoiceForm, setMultiChoiceForm] = useState<{
governance: PublicKey | undefined
governance: ProgramAccount<Governance> | null
options: string[]
}>({
governance: undefined,
governance: null,
options: ['', ''], // the multichoice form starts with 2 blank options for the poll
})
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand Down Expand Up @@ -325,7 +325,7 @@ const New = () => {
proposalAddress = await proposeMultiChoice({
title: form.title,
description: form.description,
governance: multiChoiceForm.governance,
governance: multiChoiceForm.governance.pubkey,
instructionsData: [],
voteByCouncil,
options,
Expand Down Expand Up @@ -635,6 +635,8 @@ const New = () => {
[governance?.pubkey?.toBase58()]
)

const { canCreateProposal, error } = useCanCreateProposal(isMulti ? multiChoiceForm.governance : governance)

return (
<div className="grid grid-cols-12 gap-4">
<div
Expand Down Expand Up @@ -833,20 +835,21 @@ const New = () => {
)}
<div className="border-t border-fgd-4 flex justify-end mt-6 pt-6 space-x-4">
<SecondaryButton
disabled={isLoading}
disabled={isLoading || !canCreateProposal}
isLoading={isLoadingDraft}
onClick={() => handleCreate(true)}
>
Save draft
</SecondaryButton>
<Button
isLoading={isLoadingSignedProposal}
disabled={isLoading}
disabled={isLoading || !canCreateProposal}
onClick={() => handleCreate(false)}
>
Add proposal
</Button>
</div>
{error && <p className="p-2 text-right text-red-400">{error}</p>}
</div>
</>
</div>
Expand Down

0 comments on commit 9396e6e

Please sign in to comment.