diff --git a/components/MultiChoiceForm.tsx b/components/MultiChoiceForm.tsx index d2fb5ab381..b1b605f845 100644 --- a/components/MultiChoiceForm.tsx +++ b/components/MultiChoiceForm.tsx @@ -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, @@ -18,7 +17,7 @@ const MultiChoiceForm = ({ updateMultiFormErrors, }: { multiChoiceForm: { - governance: ProgramAccount | null + governance: PublicKey | undefined options: string[] } updateMultiChoiceForm: any @@ -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) @@ -90,10 +78,12 @@ const MultiChoiceForm = ({
+ ownVoterWeight?.canCreateProposal(x.governance.account.config) + )} onChange={(value: AssetAccount) => { handleMultiForm({ - value: value.governance, + value: value.governance.pubkey, propertyName: 'governance', }) }} @@ -101,7 +91,7 @@ const MultiChoiceForm = ({ governance ? assetAccounts.find( (x) => - x.governance.pubkey.equals(governance.pubkey) && + x.governance.pubkey.equals(governance) && x.type === AccountType.SOL ) : null diff --git a/hooks/queries/addresses/tokenOwnerRecord.ts b/hooks/queries/addresses/tokenOwnerRecord.ts index 04a7c3beb0..96121a8ef0 100644 --- a/hooks/queries/addresses/tokenOwnerRecord.ts +++ b/hooks/queries/addresses/tokenOwnerRecord.ts @@ -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 @@ -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 @@ -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 = ( diff --git a/hooks/queries/tokenOwnerRecord.ts b/hooks/queries/tokenOwnerRecord.ts index 43017bc4ca..8a1a9c11f7 100644 --- a/hooks/queries/tokenOwnerRecord.ts +++ b/hooks/queries/tokenOwnerRecord.ts @@ -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' @@ -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) -} diff --git a/hooks/useCreateProposal.ts b/hooks/useCreateProposal.ts index 4a66af1352..434bdfbbe2 100644 --- a/hooks/useCreateProposal.ts +++ b/hooks/useCreateProposal.ts @@ -15,19 +15,8 @@ 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() @@ -35,21 +24,7 @@ export default function useCreateProposal() { 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(); @@ -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'); @@ -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') @@ -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'); @@ -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 | 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 - } -} diff --git a/pages/dao/[symbol]/proposal/components/NewProposalBtn.tsx b/pages/dao/[symbol]/proposal/components/NewProposalBtn.tsx index b43846bc53..5c1e0bbf93 100644 --- a/pages/dao/[symbol]/proposal/components/NewProposalBtn.tsx +++ b/pages/dao/[symbol]/proposal/components/NewProposalBtn.tsx @@ -3,14 +3,52 @@ import { PlusCircleIcon } from '@heroicons/react/outline' import useQueryContext from '@hooks/useQueryContext' import useRealm from '@hooks/useRealm' import Tooltip from '@components/Tooltip' -import { useCanCreateProposal } from '@hooks/useCreateProposal' +import useWalletOnePointOh from '@hooks/useWalletOnePointOh' +import { useRealmQuery } from '@hooks/queries/realm' +import { useRealmVoterWeightPlugins } from '@hooks/useRealmVoterWeightPlugins' const NewProposalBtn = () => { const { fmtUrlWithCluster } = useQueryContext() - const { symbol } = useRealm() + const wallet = useWalletOnePointOh() + const connected = !!wallet?.connected - const { canCreateProposal, error: tooltipContent } = useCanCreateProposal() + 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.' + : '' return ( <> diff --git a/pages/dao/[symbol]/proposal/new.tsx b/pages/dao/[symbol]/proposal/new.tsx index 72b63d84c1..30fe0ba643 100644 --- a/pages/dao/[symbol]/proposal/new.tsx +++ b/pages/dao/[symbol]/proposal/new.tsx @@ -63,7 +63,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, { useCanCreateProposal } from '@hooks/useCreateProposal' +import useCreateProposal from '@hooks/useCreateProposal' import RealmConfig from './components/instructions/RealmConfig' import CloseTokenAccount from './components/instructions/CloseTokenAccount' import CloseMultipleTokenAccounts from './components/instructions/CloseMultipleTokenAccounts' @@ -157,7 +157,7 @@ const schema = yup.object().shape({ }) const multiChoiceSchema = yup.object().shape({ - governance: yup.object().required('Governance is required'), + governance: yup.string().required('Governance is required'), options: yup.array().of(yup.string().required('Option cannot be empty')), }) @@ -212,10 +212,10 @@ const New = () => { setVoteByCouncil, } = useVoteByCouncilToggle() const [multiChoiceForm, setMultiChoiceForm] = useState<{ - governance: ProgramAccount | null + governance: PublicKey | undefined options: string[] }>({ - governance: null, + governance: undefined, options: ['', ''], // the multichoice form starts with 2 blank options for the poll }) // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -331,7 +331,7 @@ const New = () => { proposalAddress = await proposeMultiChoice({ title: form.title, description: form.description, - governance: multiChoiceForm.governance.pubkey, + governance: multiChoiceForm.governance, instructionsData: [], voteByCouncil, options, @@ -647,8 +647,6 @@ const New = () => { [governance?.pubkey?.toBase58()] ) - const { canCreateProposal, error, warning } = useCanCreateProposal(isMulti ? multiChoiceForm.governance : governance) - return (
{ )}
handleCreate(true)} > @@ -855,14 +853,12 @@ const New = () => {
- {warning &&

{warning}

} - {error &&

{error}

}