Skip to content

Commit

Permalink
Revert "CreateNewProposalWithInsufficientPower (#2440)" (#2454)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xShuk authored Oct 7, 2024
1 parent a55b94f commit 289c36a
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 214 deletions.
24 changes: 7 additions & 17 deletions components/MultiChoiceForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ 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 @@ -18,7 +17,7 @@ const MultiChoiceForm = ({
updateMultiFormErrors,
}: {
multiChoiceForm: {
governance: ProgramAccount<Governance> | null
governance: PublicKey | undefined
options: string[]
}
updateMultiChoiceForm: any
Expand All @@ -38,17 +37,6 @@ 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 @@ -90,18 +78,20 @@ const MultiChoiceForm = ({
<div className="mt-8 mb-8">
<GovernedAccountSelect
label="Which wallet’s rules should this proposal follow?"
governedAccounts={governedAccounts}
governedAccounts={assetAccounts.filter((x) =>
ownVoterWeight?.canCreateProposal(x.governance.account.config)
)}
onChange={(value: AssetAccount) => {
handleMultiForm({
value: value.governance,
value: value.governance.pubkey,
propertyName: 'governance',
})
}}
value={
governance
? assetAccounts.find(
(x) =>
x.governance.pubkey.equals(governance.pubkey) &&
x.governance.pubkey.equals(governance) &&
x.type === AccountType.SOL
)
: null
Expand Down
26 changes: 9 additions & 17 deletions hooks/queries/addresses/tokenOwnerRecord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,8 @@ import { useQuery } from '@tanstack/react-query'
import { useRealmQuery } from '../realm'
import { useSelectedDelegatorStore } from 'stores/useSelectedDelegatorStore'

export const useAddressQuery_CouncilTokenOwnerByPK = (owner: PublicKey | undefined) => {
const realm = useRealmQuery().data?.result
return useAddressQuery_TokenOwnerRecord(
realm?.owner,
realm?.pubkey,
realm?.account.config.councilMint,
owner
)
}

export const useAddressQuery_CouncilTokenOwner = () => {
const realm = useRealmQuery().data?.result
const wallet = useWalletOnePointOh()
const selectedCouncilDelegator = useSelectedDelegatorStore(
(s) => s.councilDelegator
Expand All @@ -27,20 +18,16 @@ export const useAddressQuery_CouncilTokenOwner = () => {
? selectedCouncilDelegator
: wallet?.publicKey ?? undefined

return useAddressQuery_CouncilTokenOwnerByPK(owner)
}

export const useAddressQuery_CommunityTokenOwnerByPK = (owner: PublicKey | undefined) => {
const realm = useRealmQuery().data?.result
return useAddressQuery_TokenOwnerRecord(
realm?.owner,
realm?.pubkey,
realm?.account.communityMint,
realm?.account.config.councilMint,
owner
)
}

export const useAddressQuery_CommunityTokenOwner = () => {
const realm = useRealmQuery().data?.result
const wallet = useWalletOnePointOh()
const selectedCommunityDelegator = useSelectedDelegatorStore(
(s) => s.communityDelegator
Expand All @@ -53,7 +40,12 @@ export const useAddressQuery_CommunityTokenOwner = () => {
: // I wanted to eliminate `null` as a possible type
wallet?.publicKey ?? undefined

return useAddressQuery_CommunityTokenOwnerByPK(owner)
return useAddressQuery_TokenOwnerRecord(
realm?.owner,
realm?.pubkey,
realm?.account.communityMint,
owner
)
}

export const useAddressQuery_TokenOwnerRecord = (
Expand Down
12 changes: 0 additions & 12 deletions hooks/queries/tokenOwnerRecord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import asFindable from '@utils/queries/asFindable'
import {
useAddressQuery_CommunityTokenOwner,
useAddressQuery_CouncilTokenOwner,
useAddressQuery_CommunityTokenOwnerByPK,
useAddressQuery_CouncilTokenOwnerByPK
} from './addresses/tokenOwnerRecord'
import { useRealmQuery } from './realm'
import useLegacyConnectionContext from '@hooks/useLegacyConnectionContext'
Expand Down Expand Up @@ -286,13 +284,3 @@ export const useUserCouncilTokenOwnerRecord = () => {
const { data: tokenOwnerRecordPubkey } = useAddressQuery_CouncilTokenOwner()
return useTokenOwnerRecordByPubkeyQuery(tokenOwnerRecordPubkey)
}

export const useUserCommunityTokenOwnerRecordByPK = (pk: PublicKey | undefined) => {
const { data: tokenOwnerRecordPubkey } = useAddressQuery_CommunityTokenOwnerByPK(pk)
return useTokenOwnerRecordByPubkeyQuery(tokenOwnerRecordPubkey)
}

export const useUserCouncilTokenOwnerRecordByPK = (pk: PublicKey | undefined) => {
const { data: tokenOwnerRecordPubkey } = useAddressQuery_CouncilTokenOwnerByPK(pk)
return useTokenOwnerRecordByPubkeyQuery(tokenOwnerRecordPubkey)
}
168 changes: 14 additions & 154 deletions hooks/useCreateProposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,41 +15,16 @@ import useLegacyConnectionContext from './useLegacyConnectionContext'
import queryClient from './queries/queryClient'
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 { BigNumber } from 'bignumber.js'
import { Governance, ProgramAccount } from '@solana/spl-governance'
import {
useTokenOwnerRecordsDelegatedToUser,
useUserCommunityTokenOwnerRecordByPK, useUserCouncilTokenOwnerRecordByPK
} from '@hooks/queries/tokenOwnerRecord'
import { useSelectedDelegatorStore } from '../stores/useSelectedDelegatorStore'
import { shortenAddress } from '@utils/address'

export default function useCreateProposal() {
const connection = useLegacyConnectionContext()
const realm = useRealmQuery().data?.result
const config = useRealmConfigQuery().data?.result
const mint = useRealmCommunityMintInfoQuery().data?.result
const councilMint = useRealmCouncilMintInfoQuery().data?.result
// const { result: ownVoterWeight } = useLegacyVoterWeight()
const {
power: communityPower,
proposer: communityProposer,
} = useProposeAs('community')
const { data: communityProposerData } = useUserCommunityTokenOwnerRecordByPK(communityProposer)
const communityProposerTokenRecord = communityProposerData?.result

const {
power: councilPower,
proposer: councilProposer,
} = useProposeAs('council')

const { data: councilProposerData } = useUserCouncilTokenOwnerRecordByPK(councilProposer)
const councilProposerTokenRecord = councilProposerData?.result
const { result: ownVoterWeight } = useLegacyVoterWeight()

const { getRpcContext } = useRpcContext()
const votingClients = useVotingClients();
Expand Down Expand Up @@ -77,26 +52,21 @@ export default function useCreateProposal() {
governance.pubkey
)
const minCouncilTokensToCreateProposal = selectedGovernance?.account.config.minCouncilTokensToCreateProposal
const minCommunityTokensToCreateProposal = selectedGovernance?.account.config.minCommunityTokensToCreateProposal
const councilPower = ownVoterWeight?.councilTokenRecord?.account.governingTokenDepositAmount

const useCouncilPower = minCouncilTokensToCreateProposal && councilPower && councilPower.gte(minCouncilTokensToCreateProposal)
const ownTokenRecord =
useCouncilPower ?
councilProposerTokenRecord :
communityProposerTokenRecord
const ownTokenRecord =
minCouncilTokensToCreateProposal && councilPower && councilPower >= minCouncilTokensToCreateProposal ?
ownVoterWeight?.councilTokenRecord :
ownVoterWeight?.communityTokenRecord

if (!ownTokenRecord) throw new Error('token owner record does not exist')
if (!selectedGovernance) throw new Error('governance not found')
if (!realm) throw new Error()

if (!useCouncilPower && communityPower && minCommunityTokensToCreateProposal && communityPower.lt(minCommunityTokensToCreateProposal)) {
throw new Error('Not enough voting power')
}

// this is somewhat confusing - the basic idea is:
// although a vote may be by community vote, the proposer may create it with their council token
// The choice of which token to use is made when the token record is selected
const proposeByCouncil = councilProposer?.toBase58() === (ownTokenRecord?.pubkey.toBase58() ?? "");
const proposeByCouncil = ownVoterWeight?.councilTokenRecord?.pubkey.toBase58() === (ownTokenRecord?.pubkey.toBase58() ?? "");
// now we can we identify whether we are using the community or council voting client (to decide which (if any) plugins to use)
const votingClient = votingClients(proposeByCouncil ? 'council' : 'community');

Expand Down Expand Up @@ -173,17 +143,12 @@ export default function useCreateProposal() {
)

const minCouncilTokensToCreateProposal = selectedGovernance?.account.config.minCouncilTokensToCreateProposal
const minCommunityTokensToCreateProposal = selectedGovernance?.account.config.minCommunityTokensToCreateProposal
const councilPower = ownVoterWeight?.councilTokenRecord?.account.governingTokenDepositAmount

const useCouncilPower = minCouncilTokensToCreateProposal && councilPower && councilPower.gte(minCouncilTokensToCreateProposal)
const ownTokenRecord =
useCouncilPower ?
councilProposerTokenRecord :
communityProposerTokenRecord

if (!useCouncilPower && communityPower && minCommunityTokensToCreateProposal && communityPower.lt(minCommunityTokensToCreateProposal)) {
throw new Error('Not enough voting power')
}
const ownTokenRecord =
minCouncilTokensToCreateProposal && councilPower && councilPower >= minCouncilTokensToCreateProposal ?
ownVoterWeight?.councilTokenRecord :
ownVoterWeight?.communityTokenRecord

if (!ownTokenRecord) throw new Error('token owner record does not exist')
if (!selectedGovernance) throw new Error('governance not found')
Expand All @@ -192,7 +157,7 @@ export default function useCreateProposal() {
// this is somewhat confusing - the basic idea is:
// although a vote may be by community vote, the proposer may create it with their council token
// The choice of which token to use is made when the token record is selected
const proposeByCouncil = councilProposer?.toBase58() === (ownTokenRecord?.pubkey.toBase58() ?? "");
const proposeByCouncil = ownVoterWeight?.councilTokenRecord?.pubkey.toBase58() === (ownTokenRecord?.pubkey.toBase58() ?? "");
// now we can we identify whether we are using the community or council voting client (to decide which (if any) plugins to use)
const votingClient = votingClients(proposeByCouncil ? 'council' : 'community');

Expand Down Expand Up @@ -237,108 +202,3 @@ export default function useCreateProposal() {

return { handleCreateProposal, propose, proposeMultiChoice }
}

const useProposeAs = (
role: 'community' | 'council',
) => {
const wallet = useWalletOnePointOh()
const { voterWeightForWallet, isReady } = useRealmVoterWeightPlugins(role)

const { councilDelegator, communityDelegator} = useSelectedDelegatorStore()
const { data: delegatesArray } = useTokenOwnerRecordsDelegatedToUser()


let proposer = councilDelegator
? councilDelegator
: communityDelegator
? communityDelegator
: wallet?.publicKey || undefined

let maxPower = proposer ? voterWeightForWallet(proposer)?.value : new BN(0)
// The user hasn't selected a specific delegator to perform actions as
// We will use the delegator with the maximum power, or the user's wallet
if (!councilDelegator && !communityDelegator && delegatesArray) {
for (const delegator of delegatesArray) {
const p = voterWeightForWallet(delegator.account.governingTokenOwner)?.value
if (p && maxPower && p.gt(maxPower)) {
maxPower = p
proposer = delegator.account.governingTokenOwner
}
}
}

return {
power: maxPower,
proposer,
isReady
}
}

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

const realm = useRealmQuery().data?.result

const {
power: communityPower,
proposer: communityProposer,
isReady: communityReady
} = useProposeAs('community')

const {
power: councilPower,
proposer: councilProposer,
isReady: councilReady
} = useProposeAs('council')

const power = communityPower || councilPower
const proposer = communityPower ? communityProposer : councilProposer
const isReady = communityReady && councilReady

const {
toManyCommunityOutstandingProposalsForUser,
toManyCouncilOutstandingProposalsForUse,
} = useRealm()


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

const hasEnoughVotingPower = power?.gt(minWeightToCreateProposal || new BN(1))

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

const minWeightToCreateProposalS = minWeightToCreateProposal
? new BigNumber(minWeightToCreateProposal.toString()).toString()
: "1"

const error = !connected
? 'Connect your wallet to create new proposal'
: isReady && !communityPower && !councilPower
? 'There is no governance configuration to create a new proposal'
: !hasEnoughVotingPower
? `Please select only one account with at least ${minWeightToCreateProposalS} 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 warning = proposer
? `Add a proposal as: ${shortenAddress(proposer.toString())}.`
: ''

return {
canCreateProposal,
error,
warning
}
}
Loading

0 comments on commit 289c36a

Please sign in to comment.