From ededc0910c0fbc661b29e29842ee69ae26fa3c1e Mon Sep 17 00:00:00 2001 From: agrippa Date: Fri, 22 Sep 2023 16:01:06 -0400 Subject: [PATCH] Delegation & Govpower rework part 1 (#1781) --- .../components/LockTokensAccount.tsx | 2 +- .../components/VotingPowerCard.tsx | 4 +- .../components/Account/LockTokensAccount.tsx | 2 +- .../Account/LockTokensAccountWithdraw.tsx | 4 +- .../LockPluginTokenBalanceCard.tsx | 4 +- actions/castVote.ts | 9 +- actions/chat/postMessage.ts | 2 +- actions/createLUTproposal.ts | 2 +- actions/createProposal.ts | 2 +- .../GovernancePower/GovernancePowerCard.tsx | 71 ++++++ .../GovernancePowerForRole.tsx | 56 +++++ .../GovernancePower/Vanilla/Deposit.tsx | 75 ++++++ .../Vanilla/VanillaVotingPower.tsx | 148 ++++++++++++ .../Vanilla/useDepositCallback.tsx | 91 ++++++++ .../CommunityVotingPower.tsx | 111 --------- .../CouncilVotingPower.tsx | 119 ---------- .../ProposalVotingPower/VotingPower.tsx | 58 +---- .../ProposalVotingPower/VotingPowerPct.tsx | 6 +- .../ProposalVotingPower/depositTokens.ts | 84 ------- components/ProposalVotingPower/index.tsx | 13 +- components/SelectPrimaryDelegators.tsx | 159 +++++++++++++ .../TokenBalance/DelegateTokenBalanceCard.tsx | 211 ----------------- .../TokenBalance/TokenBalanceCardWrapper.tsx | 35 ++- ...{TokenBalanceCard.tsx => TokenDeposit.tsx} | 220 ++---------------- .../TokenBalance/VanillaAccountDetails.tsx | 60 +++++ components/inputs/Checkbox.tsx | 12 +- constants/flags.ts | 1 + hooks/queries/useProgramVersionQuery.ts | 18 +- hooks/selectedRealm/useGoverningTokenMint.ts | 8 + hooks/useFormatTokenAmount.ts | 22 ++ hooks/useSubmitVote.ts | 114 ++++++++- hooks/useVotingTokenOwnerRecords.ts | 60 +++++ .../NewWallet/useNewWalletTransaction.ts | 2 +- package.json | 1 + .../dao/[symbol]/account}/Account.tsx | 15 +- .../dao/[symbol]/account/DelegatorOptions.tsx | 18 ++ pages/dao/[symbol]/account/DelegatorsList.tsx | 88 +++++++ pages/dao/[symbol]/account/me.tsx | 2 +- pages/dao/[symbol]/index.tsx | 2 +- tailwind.config.js | 3 +- utils/uiTypes/VotePlugin.ts | 22 +- yarn.lock | 12 + 42 files changed, 1093 insertions(+), 855 deletions(-) create mode 100644 components/GovernancePower/GovernancePowerCard.tsx create mode 100644 components/GovernancePower/GovernancePowerForRole.tsx create mode 100644 components/GovernancePower/Vanilla/Deposit.tsx create mode 100644 components/GovernancePower/Vanilla/VanillaVotingPower.tsx create mode 100644 components/GovernancePower/Vanilla/useDepositCallback.tsx delete mode 100644 components/ProposalVotingPower/CommunityVotingPower.tsx delete mode 100644 components/ProposalVotingPower/CouncilVotingPower.tsx delete mode 100644 components/ProposalVotingPower/depositTokens.ts create mode 100644 components/SelectPrimaryDelegators.tsx delete mode 100644 components/TokenBalance/DelegateTokenBalanceCard.tsx rename components/TokenBalance/{TokenBalanceCard.tsx => TokenDeposit.tsx} (66%) create mode 100644 components/TokenBalance/VanillaAccountDetails.tsx create mode 100644 hooks/selectedRealm/useGoverningTokenMint.ts create mode 100644 hooks/useFormatTokenAmount.ts create mode 100644 hooks/useVotingTokenOwnerRecords.ts rename {components => pages/dao/[symbol]/account}/Account.tsx (84%) create mode 100644 pages/dao/[symbol]/account/DelegatorOptions.tsx create mode 100644 pages/dao/[symbol]/account/DelegatorsList.tsx diff --git a/HeliumVotePlugin/components/LockTokensAccount.tsx b/HeliumVotePlugin/components/LockTokensAccount.tsx index 0cd17c0b9f..c3bb1450f6 100644 --- a/HeliumVotePlugin/components/LockTokensAccount.tsx +++ b/HeliumVotePlugin/components/LockTokensAccount.tsx @@ -14,7 +14,7 @@ import { getMintNaturalAmountFromDecimalAsBN, } from '@tools/sdk/units' import PreviousRouteBtn from '@components/PreviousRouteBtn' -import { TokenDeposit } from '@components/TokenBalance/TokenBalanceCard' +import { TokenDeposit } from '@components/TokenBalance/TokenDeposit' import { GoverningTokenRole } from '@solana/spl-governance' import InlineNotification from '@components/InlineNotification' import tokenPriceService from '@utils/services/tokenPrice' diff --git a/HeliumVotePlugin/components/VotingPowerCard.tsx b/HeliumVotePlugin/components/VotingPowerCard.tsx index d372996ba1..3b20531b05 100644 --- a/HeliumVotePlugin/components/VotingPowerCard.tsx +++ b/HeliumVotePlugin/components/VotingPowerCard.tsx @@ -5,8 +5,7 @@ import useRealm from '@hooks/useRealm' import { fmtMintAmount } from '@tools/sdk/units' import { getMintMetadata } from '@components/instructions/programs/splToken' import InlineNotification from '@components/InlineNotification' -import DelegateTokenBalanceCard from '@components/TokenBalance/DelegateTokenBalanceCard' -import { TokenDeposit } from '@components/TokenBalance/TokenBalanceCard' +import { TokenDeposit } from '@components/TokenBalance/TokenDeposit' import useHeliumVsrStore from 'HeliumVotePlugin/hooks/useHeliumVsrStore' import { MintInfo } from '@solana/spl-token' import { VotingPowerBox } from './VotingPowerBox' @@ -69,7 +68,6 @@ export const VotingPowerCard: React.FC<{ /> )} - ) : ( <> diff --git a/VoteStakeRegistry/components/Account/LockTokensAccount.tsx b/VoteStakeRegistry/components/Account/LockTokensAccount.tsx index 314c71a766..5fd56d98dd 100644 --- a/VoteStakeRegistry/components/Account/LockTokensAccount.tsx +++ b/VoteStakeRegistry/components/Account/LockTokensAccount.tsx @@ -33,7 +33,7 @@ import { } from '@heroicons/react/outline' import { getMintMetadata } from '@components/instructions/programs/splToken' import { abbreviateAddress } from '@utils/formatting' -import { TokenDeposit } from '@components/TokenBalance/TokenBalanceCard' +import { TokenDeposit } from '@components/TokenBalance/TokenDeposit' import useWalletOnePointOh from '@hooks/useWalletOnePointOh' import { useRealmQuery } from '@hooks/queries/realm' import { useTokenOwnerRecordByPubkeyQuery } from '@hooks/queries/tokenOwnerRecord' diff --git a/VoteStakeRegistry/components/Account/LockTokensAccountWithdraw.tsx b/VoteStakeRegistry/components/Account/LockTokensAccountWithdraw.tsx index 256796419a..c38c6c77f0 100644 --- a/VoteStakeRegistry/components/Account/LockTokensAccountWithdraw.tsx +++ b/VoteStakeRegistry/components/Account/LockTokensAccountWithdraw.tsx @@ -27,9 +27,9 @@ import { LockClosedIcon, } from '@heroicons/react/outline' import { getMintMetadata } from '@components/instructions/programs/splToken' -import Account from '../../../components/Account' +import Account from '../../../pages/dao/[symbol]/account/Account' import { abbreviateAddress } from '@utils/formatting' -import { TokenDeposit } from '@components/TokenBalance/TokenBalanceCard' +import { TokenDeposit } from '@components/TokenBalance/TokenDeposit' import { VsrClient } from 'VoteStakeRegistry/sdk/client' import useWalletOnePointOh from '@hooks/useWalletOnePointOh' import { useRealmQuery } from '@hooks/queries/realm' diff --git a/VoteStakeRegistry/components/TokenBalance/LockPluginTokenBalanceCard.tsx b/VoteStakeRegistry/components/TokenBalance/LockPluginTokenBalanceCard.tsx index 33606bb19d..12ee93f108 100644 --- a/VoteStakeRegistry/components/TokenBalance/LockPluginTokenBalanceCard.tsx +++ b/VoteStakeRegistry/components/TokenBalance/LockPluginTokenBalanceCard.tsx @@ -16,8 +16,7 @@ import { useEffect, useState } from 'react' import { ChevronRightIcon } from '@heroicons/react/solid' import InlineNotification from '@components/InlineNotification' import Link from 'next/link' -import DelegateTokenBalanceCard from '@components/TokenBalance/DelegateTokenBalanceCard' -import { TokenDeposit } from '@components/TokenBalance/TokenBalanceCard' +import { TokenDeposit } from '@components/TokenBalance/TokenDeposit' import useWalletOnePointOh from '@hooks/useWalletOnePointOh' import { useRealmQuery } from '@hooks/queries/realm' import { useRouter } from 'next/router' @@ -138,7 +137,6 @@ const LockPluginTokenBalanceCard = ({ /> )} - ) : ( <> diff --git a/actions/castVote.ts b/actions/castVote.ts index 4afd58ffa8..b3e050272b 100644 --- a/actions/castVote.ts +++ b/actions/castVote.ts @@ -76,12 +76,13 @@ export async function castVote( const plugin = await votingPlugin?.withCastPluginVote( instructions, proposal, - tokenOwnerRecord, + tokenOwnerRecord.pubkey, createCastNftVoteTicketIxs ) - const isMulti = proposal.account.voteType !== VoteType.SINGLE_CHOICE - && proposal.account.accountType === GovernanceAccountType.ProposalV2 + const isMulti = + proposal.account.voteType !== VoteType.SINGLE_CHOICE && + proposal.account.accountType === GovernanceAccountType.ProposalV2 // It is not clear that defining these extraneous fields, `deny` and `veto`, is actually necessary. // See: https://discord.com/channels/910194960941338677/910630743510777926/1044741454175674378 @@ -151,7 +152,7 @@ export async function castVote( if (message) { const plugin = await votingPlugin?.withUpdateVoterWeightRecord( instructions, - tokenOwnerRecord, + tokenOwnerRecord.pubkey, 'commentProposal', createPostMessageTicketIxs ) diff --git a/actions/chat/postMessage.ts b/actions/chat/postMessage.ts index 5168450008..bf4ba72373 100644 --- a/actions/chat/postMessage.ts +++ b/actions/chat/postMessage.ts @@ -40,7 +40,7 @@ export async function postChatMessage( //will run only if plugin is connected with realm const plugin = await client?.withUpdateVoterWeightRecord( instructions, - tokeOwnerRecord, + tokeOwnerRecord.pubkey, 'commentProposal', createNftTicketsIxs ) diff --git a/actions/createLUTproposal.ts b/actions/createLUTproposal.ts index 40659c3353..8e51f95bd3 100644 --- a/actions/createLUTproposal.ts +++ b/actions/createLUTproposal.ts @@ -75,7 +75,7 @@ export const createLUTProposal = async ( //will run only if plugin is connected with realm const plugin = await client?.withUpdateVoterWeightRecord( instructions, - tokenOwnerRecord, + tokenOwnerRecord.pubkey, 'createProposal' ) diff --git a/actions/createProposal.ts b/actions/createProposal.ts index e6a6b1dd33..f357fd100a 100644 --- a/actions/createProposal.ts +++ b/actions/createProposal.ts @@ -109,7 +109,7 @@ export const createProposal = async ( //will run only if plugin is connected with realm const plugin = await client?.withUpdateVoterWeightRecord( instructions, - tokenOwnerRecord, + tokenOwnerRecord.pubkey, 'createProposal', createNftTicketsIxs ) diff --git a/components/GovernancePower/GovernancePowerCard.tsx b/components/GovernancePower/GovernancePowerCard.tsx new file mode 100644 index 0000000000..18f94cd461 --- /dev/null +++ b/components/GovernancePower/GovernancePowerCard.tsx @@ -0,0 +1,71 @@ +import { ChevronRightIcon } from '@heroicons/react/solid' +import { useGovernancePowerAsync } from '@hooks/queries/governancePower' +import useQueryContext from '@hooks/useQueryContext' +import useWalletOnePointOh from '@hooks/useWalletOnePointOh' +import Link from 'next/link' +import { useRouter } from 'next/router' +import GovernancePowerForRole from './GovernancePowerForRole' + +const GovernancePowerTitle = () => { + const { symbol } = useRouter().query + const { fmtUrlWithCluster } = useQueryContext() + const connected = useWalletOnePointOh()?.connected ?? undefined + + return ( +
+

My governance power

+ + + View + + + +
+ ) +} + +const GovernancePowerCard = () => { + const connected = useWalletOnePointOh()?.connected ?? false + + const communityPower = useGovernancePowerAsync('community') + const councilPower = useGovernancePowerAsync('council') + + const bothLoading = communityPower.loading && councilPower.loading + + const bothZero = + communityPower.result !== undefined && + councilPower.result !== undefined && + communityPower.result.isZero() && + councilPower.result.isZero() + + return ( +
+ + {!connected ? ( +
+ Connect your wallet to see governance power +
+ ) : bothLoading ? ( + <> +
+
+ + ) : bothZero ? ( +
+ You do not have any governance power in this dao +
+ ) : ( +
+ + +
+ )} +
+ ) +} + +export default GovernancePowerCard diff --git a/components/GovernancePower/GovernancePowerForRole.tsx b/components/GovernancePower/GovernancePowerForRole.tsx new file mode 100644 index 0000000000..4d3c18700e --- /dev/null +++ b/components/GovernancePower/GovernancePowerForRole.tsx @@ -0,0 +1,56 @@ +import classNames from 'classnames' + +import useWalletOnePointOh from '@hooks/useWalletOnePointOh' +import { useAsync } from 'react-async-hook' +import { determineVotingPowerType } from '@hooks/queries/governancePower' +import { useConnection } from '@solana/wallet-adapter-react' +import useSelectedRealmPubkey from '@hooks/selectedRealm/useSelectedRealmPubkey' +import LockedCommunityVotingPower from '@components/ProposalVotingPower/LockedCommunityVotingPower' +import NftVotingPower from '@components/ProposalVotingPower/NftVotingPower' +import LockedCommunityNFTRecordVotingPower from '@components/ProposalVotingPower/LockedCommunityNFTRecordVotingPower' +import VanillaVotingPower from './Vanilla/VanillaVotingPower' + +export default function GovernancePowerForRole({ + role, + ...props +}: { + role: 'community' | 'council' + className?: string +}) { + const { connection } = useConnection() + + const realmPk = useSelectedRealmPubkey() + + const wallet = useWalletOnePointOh() + const connected = !!wallet?.connected + + const { result: kind } = useAsync(async () => { + if (realmPk === undefined) return undefined + + return determineVotingPowerType(connection, realmPk, role) + }, [connection, realmPk, role]) + + if (connected && kind === undefined) { + return ( +
+ ) + } + + return ( +
+ {role === 'community' ? ( + kind === 'vanilla' ? ( + + ) : kind === 'VSR' ? ( + + ) : kind === 'NFT' ? ( + + ) : kind === 'HeliumVSR' ? ( + + ) : null + ) : kind === 'vanilla' ? ( + + ) : null} +
+ ) +} diff --git a/components/GovernancePower/Vanilla/Deposit.tsx b/components/GovernancePower/Vanilla/Deposit.tsx new file mode 100644 index 0000000000..fd5c5ee17e --- /dev/null +++ b/components/GovernancePower/Vanilla/Deposit.tsx @@ -0,0 +1,75 @@ +import { BigNumber } from 'bignumber.js' +import { SecondaryButton } from '@components/Button' +import useWalletOnePointOh from '@hooks/useWalletOnePointOh' +import { useRealmQuery } from '@hooks/queries/realm' +import { useConnection } from '@solana/wallet-adapter-react' +import { useAsync } from 'react-async-hook' +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + Token, + TOKEN_PROGRAM_ID, +} from '@solana/spl-token' +import BN from 'bn.js' +import { fetchMintInfoByPubkey } from '@hooks/queries/mintInfo' +import { fetchTokenAccountByPubkey } from '@hooks/queries/tokenAccount' +import { useDepositCallback } from './useDepositCallback' +import { getMintMetadata } from '@components/instructions/programs/splToken' + +export const Deposit = ({ role }: { role: 'community' | 'council' }) => { + const realm = useRealmQuery().data?.result + const wallet = useWalletOnePointOh() + const walletPk = wallet?.publicKey ?? undefined + + const { connection } = useConnection() + + const { result } = useAsync(async () => { + if (realm === undefined || walletPk === undefined) return undefined + const mint = + role === 'community' + ? realm.account.communityMint + : realm.account.config.councilMint + if (mint === undefined) return undefined + + const userAtaPk = await Token.getAssociatedTokenAddress( + ASSOCIATED_TOKEN_PROGRAM_ID, // always ASSOCIATED_TOKEN_PROGRAM_ID + TOKEN_PROGRAM_ID, // always TOKEN_PROGRAM_ID + mint, // mint + walletPk // owner + ) + + const { result: userAta } = await fetchTokenAccountByPubkey( + connection, + userAtaPk + ) + const { result: mintInfo } = await fetchMintInfoByPubkey(connection, mint) + return { mint, userAta, mintInfo } as const + }, [connection, realm, role, walletPk]) + + const depositAmount = result?.userAta?.amount + ? new BigNumber(result.userAta.amount.toString()) + : new BigNumber(0) + + const tokenName = + getMintMetadata(result?.mint)?.name ?? realm?.account.name ?? '' + + const deposit = useDepositCallback(role) + + return !depositAmount.isGreaterThan(0) ? null : ( + <> +
+ You have{' '} + {result?.mintInfo + ? depositAmount.shiftedBy(-result.mintInfo.decimals).toFormat() + : depositAmount.toFormat()}{' '} + more {tokenName} votes in your wallet. Do you want to deposit them to + increase your voting power in this Dao? +
+ deposit(new BN(depositAmount.toString()))} + > + Deposit + + + ) +} diff --git a/components/GovernancePower/Vanilla/VanillaVotingPower.tsx b/components/GovernancePower/Vanilla/VanillaVotingPower.tsx new file mode 100644 index 0000000000..9e804c4d3c --- /dev/null +++ b/components/GovernancePower/Vanilla/VanillaVotingPower.tsx @@ -0,0 +1,148 @@ +import { BigNumber } from 'bignumber.js' +import { useMemo } from 'react' +import classNames from 'classnames' + +import useRealm from '@hooks/useRealm' + +import { useTokenOwnerRecordsDelegatedToUser } from '@hooks/queries/tokenOwnerRecord' +import { useRealmQuery } from '@hooks/queries/realm' +import { useMintInfoByPubkeyQuery } from '@hooks/queries/mintInfo' +import { useConnection } from '@solana/wallet-adapter-react' +import { getVanillaGovpower } from '@hooks/queries/governancePower' +import { + useAddressQuery_CommunityTokenOwner, + useAddressQuery_CouncilTokenOwner, +} from '@hooks/queries/addresses/tokenOwnerRecord' +import { useAsync } from 'react-async-hook' +import BN from 'bn.js' +import { Deposit } from './Deposit' +import { getMintMetadata } from '@components/instructions/programs/splToken' +import VotingPowerPct from '@components/ProposalVotingPower/VotingPowerPct' +import { useSelectedDelegatorStore } from 'stores/useSelectedDelegatorStore' +import { abbreviateAddress } from '@utils/formatting' + +interface Props { + className?: string + role: 'community' | 'council' +} + +export default function VanillaVotingPower({ role, ...props }: Props) { + const realm = useRealmQuery().data?.result + const { realmInfo } = useRealm() + + const { data: communityTOR } = useAddressQuery_CommunityTokenOwner() + const { data: councilTOR } = useAddressQuery_CouncilTokenOwner() + + const { connection } = useConnection() + + const relevantTOR = role === 'community' ? communityTOR : councilTOR + const relevantMint = + role === 'community' + ? realm?.account.communityMint + : realm?.account.config.councilMint + + const mintInfo = useMintInfoByPubkeyQuery(relevantMint).data?.result + + const { result: personalAmount } = useAsync( + async () => relevantTOR && getVanillaGovpower(connection, relevantTOR), + [connection, relevantTOR] + ) + + // If the user is using a delegator, we want to show that and not count the other delegators + const selectedDelegator = useSelectedDelegatorStore((s) => + role === 'community' ? s.communityDelegator : s.councilDelegator + ) + + const torsDelegatedToUser = useTokenOwnerRecordsDelegatedToUser() + + const { result: delegatorsAmount } = useAsync( + async () => + selectedDelegator !== undefined + ? new BN(0) + : torsDelegatedToUser === undefined || relevantMint === undefined + ? undefined + : ( + await Promise.all( + torsDelegatedToUser + .filter((x) => + x.account.governingTokenMint.equals(relevantMint) + ) + .map((x) => getVanillaGovpower(connection, x.pubkey)) + ) + ).reduce((partialSum, a) => partialSum.add(a), new BN(0)), + [connection, relevantMint, selectedDelegator, torsDelegatedToUser] + ) + + const totalAmount = (delegatorsAmount ?? new BN(0)).add( + personalAmount ?? new BN(0) + ) + + const formattedTotal = useMemo( + () => + mintInfo && totalAmount !== undefined + ? new BigNumber(totalAmount.toString()) + .shiftedBy(-mintInfo.decimals) + .toString() + : undefined, + [totalAmount, mintInfo] + ) + + const formattedDelegatorsAmount = useMemo( + () => + mintInfo && delegatorsAmount !== undefined + ? new BigNumber(delegatorsAmount.toString()) + .shiftedBy(-mintInfo.decimals) + .toString() + : undefined, + [delegatorsAmount, mintInfo] + ) + + const tokenName = + getMintMetadata(relevantMint)?.name ?? realm?.account.name ?? '' + + if (!(realm && realmInfo)) { + return ( +
+ ) + } + + return ( +
+ {totalAmount === undefined || totalAmount.isZero() ? ( +
+ You do not have any voting power in this dao. +
+ ) : ( +
+
{tokenName} Votes
+
+
+
+ {formattedTotal} +
+
+ {selectedDelegator !== undefined ? ( + // if we're acting as a specific delegator, show that instead of the delegator aggregation + <>(as {abbreviateAddress(selectedDelegator)}) + ) : formattedDelegatorsAmount !== undefined && + formattedDelegatorsAmount !== '0' ? ( + <>({formattedDelegatorsAmount} from delegators) + ) : null} +
+
+ + {mintInfo && ( + + )} +
+
+ )} + +
+ ) +} diff --git a/components/GovernancePower/Vanilla/useDepositCallback.tsx b/components/GovernancePower/Vanilla/useDepositCallback.tsx new file mode 100644 index 0000000000..890d27d10e --- /dev/null +++ b/components/GovernancePower/Vanilla/useDepositCallback.tsx @@ -0,0 +1,91 @@ +import { useCallback } from 'react' +import useWalletOnePointOh from '@hooks/useWalletOnePointOh' +import { fetchRealmByPubkey } from '@hooks/queries/realm' +import { useConnection } from '@solana/wallet-adapter-react' +import { Keypair, Transaction, TransactionInstruction } from '@solana/web3.js' +import { approveTokenTransfer } from '@utils/tokens' +import useSelectedRealmPubkey from '@hooks/selectedRealm/useSelectedRealmPubkey' +import { + getGovernanceProgramVersion, + withDepositGoverningTokens, +} from '@solana/spl-governance' +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + Token, + TOKEN_PROGRAM_ID, +} from '@solana/spl-token' +import BN from 'bn.js' +import { sendTransaction } from '@utils/send' + +export const useDepositCallback = ( + role: 'community' | 'council' | 'undefined' +) => { + const wallet = useWalletOnePointOh() + const walletPk = wallet?.publicKey ?? undefined + const realmPk = useSelectedRealmPubkey() + const { connection } = useConnection() + return useCallback( + async (amount: BN) => { + if (realmPk === undefined || walletPk === undefined) throw new Error() + const { result: realm } = await fetchRealmByPubkey(connection, realmPk) + if (realm === undefined) throw new Error() + + const mint = + role === 'community' + ? realm.account.communityMint + : realm.account.config.councilMint + if (mint === undefined) throw new Error() + + const userAtaPk = await Token.getAssociatedTokenAddress( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + mint, + walletPk // owner + ) + + const instructions: TransactionInstruction[] = [] + const signers: Keypair[] = [] + + const transferAuthority = approveTokenTransfer( + instructions, + [], + userAtaPk, + wallet!.publicKey!, + amount + ) + + signers.push(transferAuthority) + + const programVersion = await getGovernanceProgramVersion( + connection, + realm.owner + ) + + await withDepositGoverningTokens( + instructions, + realm.owner, + programVersion, + realm.pubkey, + userAtaPk, + mint, + walletPk, + transferAuthority.publicKey, + walletPk, + amount + ) + + const transaction = new Transaction() + transaction.add(...instructions) + + await sendTransaction({ + connection, + wallet: wallet!, + transaction, + signers, + sendingMessage: 'Depositing tokens', + successMessage: 'Tokens have been deposited', + }) + }, + [connection, realmPk, role, wallet, walletPk] + ) +} diff --git a/components/ProposalVotingPower/CommunityVotingPower.tsx b/components/ProposalVotingPower/CommunityVotingPower.tsx deleted file mode 100644 index 9a896368a9..0000000000 --- a/components/ProposalVotingPower/CommunityVotingPower.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import { BigNumber } from 'bignumber.js' -import { useCallback } from 'react' -import classNames from 'classnames' - -import useRealm from '@hooks/useRealm' -import { calculateMaxVoteScore } from '@models/proposal/calulateMaxVoteScore' -import { SecondaryButton } from '@components/Button' - -import { getMintMetadata } from '../instructions/programs/splToken' -import getNumTokens from './getNumTokens' -import depositTokens from './depositTokens' -import VotingPowerPct from './VotingPowerPct' -import useWalletOnePointOh from '@hooks/useWalletOnePointOh' -import { useUserCommunityTokenOwnerRecord } from '@hooks/queries/tokenOwnerRecord' -import { useRealmQuery } from '@hooks/queries/realm' -import { useRealmCommunityMintInfoQuery } from '@hooks/queries/mintInfo' -import { useRouteProposalQuery } from '@hooks/queries/proposal' -import { useConnection } from '@solana/wallet-adapter-react' -import { useLegacyVoterWeight } from '@hooks/queries/governancePower' - -interface Props { - className?: string -} - -export default function CommunityVotingPower(props: Props) { - const ownTokenRecord = useUserCommunityTokenOwnerRecord().data?.result - const realm = useRealmQuery().data?.result - const mint = useRealmCommunityMintInfoQuery().data?.result - const { result: ownVoterWeight } = useLegacyVoterWeight() - const { realmInfo, realmTokenAccount } = useRealm() - const proposal = useRouteProposalQuery().data?.result - const { connection } = useConnection() - const wallet = useWalletOnePointOh() - - // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO please fix, it can cause difficult bugs. You might wanna check out https://bobbyhadz.com/blog/react-hooks-exhaustive-deps for info. -@asktree - const depositAmount = realmTokenAccount - ? new BigNumber(realmTokenAccount.account.amount.toString()) - : new BigNumber(0) - - const depositMint = realm?.account.communityMint - const tokenName = - getMintMetadata(depositMint)?.name ?? realm?.account.name ?? '' - - const amount = ownVoterWeight - ? getNumTokens(ownVoterWeight, ownTokenRecord, mint, realmInfo) - : new BigNumber(0) - const max = - realm && proposal && mint - ? new BigNumber( - calculateMaxVoteScore(realm, proposal, mint).toString() - ).shiftedBy(-mint.decimals) - : null - - const deposit = useCallback(async () => { - if (depositAmount && realmTokenAccount && realmInfo && realm && wallet) { - await depositTokens({ - connection, - realmInfo, - realm, - wallet, - amount: depositAmount, - depositTokenAccount: realmTokenAccount, - }) - } - }, [depositAmount, connection, realmTokenAccount, realmInfo, realm, wallet]) - - if (!(realm && realmInfo)) { - return ( -
- ) - } - - return ( -
- {amount.isZero() ? ( -
- You do not have any voting power in this dao. -
- ) : ( -
-
{tokenName} Votes
-
-
- {amount.toFormat()} -
- {max && !max.isZero() && ( - - )} -
-
- )} - {depositAmount.isGreaterThan(0) && ( - <> -
- You have{' '} - {mint - ? depositAmount.shiftedBy(-mint.decimals).toFormat() - : depositAmount.toFormat()}{' '} - more {tokenName} votes in your wallet. Do you want to deposit them - to increase your voting power in this Dao? -
- - Deposit - - - )} -
- ) -} diff --git a/components/ProposalVotingPower/CouncilVotingPower.tsx b/components/ProposalVotingPower/CouncilVotingPower.tsx deleted file mode 100644 index fec74cbf0c..0000000000 --- a/components/ProposalVotingPower/CouncilVotingPower.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import { BigNumber } from 'bignumber.js' -import { useCallback, useMemo } from 'react' -import classNames from 'classnames' - -import useRealm from '@hooks/useRealm' -import { calculateMaxVoteScore } from '@models/proposal/calulateMaxVoteScore' -import { SecondaryButton } from '@components/Button' - -import { getMintMetadata } from '../instructions/programs/splToken' -import getNumTokens from './getNumTokens' -import depositTokens from './depositTokens' -import VotingPowerPct from './VotingPowerPct' -import useWalletOnePointOh from '@hooks/useWalletOnePointOh' -import { useUserCouncilTokenOwnerRecord } from '@hooks/queries/tokenOwnerRecord' -import { useRealmQuery } from '@hooks/queries/realm' -import { useRealmCouncilMintInfoQuery } from '@hooks/queries/mintInfo' -import { useRouteProposalQuery } from '@hooks/queries/proposal' -import { useConnection } from '@solana/wallet-adapter-react' -import { useLegacyVoterWeight } from '@hooks/queries/governancePower' - -interface Props { - className?: string -} - -export default function CouncilVotingPower(props: Props) { - const ownCouncilTokenRecord = useUserCouncilTokenOwnerRecord().data?.result - const realm = useRealmQuery().data?.result - const councilMint = useRealmCouncilMintInfoQuery().data?.result - const { result: ownVoterWeight } = useLegacyVoterWeight() - const { councilTokenAccount, realmInfo } = useRealm() - const proposal = useRouteProposalQuery().data?.result - const { connection } = useConnection() - const wallet = useWalletOnePointOh() - - const depositAmount = useMemo( - () => - councilTokenAccount - ? new BigNumber(councilTokenAccount.account.amount.toString()) - : new BigNumber(0), - [councilTokenAccount] - ) - const depositMint = realm?.account.config.councilMint - const tokenName = - getMintMetadata(depositMint)?.name ?? realm?.account.name ?? '' - - const amount = ownVoterWeight - ? getNumTokens( - ownVoterWeight, - ownCouncilTokenRecord, - councilMint, - realmInfo - ) - : new BigNumber(0) - - const max = - realm && proposal && councilMint - ? new BigNumber( - calculateMaxVoteScore(realm, proposal, councilMint).toString() - ).shiftedBy(-councilMint.decimals) - : null - - const deposit = useCallback(async () => { - if (depositAmount && councilTokenAccount && realmInfo && realm && wallet) { - await depositTokens({ - connection, - realmInfo, - realm, - wallet, - amount: depositAmount, - depositTokenAccount: councilTokenAccount, - }) - } - }, [depositAmount, connection, councilTokenAccount, realmInfo, realm, wallet]) - - if (!(realm && realmInfo)) { - return ( -
- ) - } - - return ( -
- {amount.isZero() ? ( -
- You do not have any council voting power in this dao. -
- ) : ( -
-
{tokenName} Council Votes
-
-
- {amount.toFormat()} -
- {max && !max.isZero() && ( - - )} -
-
- )} - {depositAmount.isGreaterThan(0) && ( - <> -
- You have{' '} - {councilMint - ? depositAmount.shiftedBy(-councilMint.decimals).toFormat() - : depositAmount.toFormat()}{' '} - more {tokenName} council votes in your wallet. Do you want to - deposit them to increase your voting power in this Dao? -
- - Deposit - - - )} -
- ) -} diff --git a/components/ProposalVotingPower/VotingPower.tsx b/components/ProposalVotingPower/VotingPower.tsx index ec00e55922..5ffdabeac8 100644 --- a/components/ProposalVotingPower/VotingPower.tsx +++ b/components/ProposalVotingPower/VotingPower.tsx @@ -1,57 +1,11 @@ -import classNames from 'classnames' - -import CommunityVotingPower from './CommunityVotingPower' -import CouncilVotingPower from './CouncilVotingPower' -import LockedCommunityVotingPower from './LockedCommunityVotingPower' -import NftVotingPower from './NftVotingPower' -import LockedCommunityNFTRecordVotingPower from './LockedCommunityNFTRecordVotingPower' -import useWalletOnePointOh from '@hooks/useWalletOnePointOh' import { useVotingPop } from '@components/VotePanel/hooks' -import { useAsync } from 'react-async-hook' -import { determineVotingPowerType } from '@hooks/queries/governancePower' -import { useConnection } from '@solana/wallet-adapter-react' -import useSelectedRealmPubkey from '@hooks/selectedRealm/useSelectedRealmPubkey' - -interface Props { - className?: string -} +import GovernancePowerForRole from '@components/GovernancePower/GovernancePowerForRole' -export default function VotingPower(props: Props) { - const { connection } = useConnection() - - const realmPk = useSelectedRealmPubkey() +export default function VotingPower() { const votePop = useVotingPop() - - const wallet = useWalletOnePointOh() - const connected = !!wallet?.connected - - const { result: kind } = useAsync(async () => { - if (votePop === undefined || realmPk === undefined) return undefined - - return determineVotingPowerType(connection, realmPk, votePop) - }, [connection, realmPk, votePop]) - - if (connected && kind === undefined) { - return ( -
- ) - } - - return ( -
- {votePop === 'community' ? ( - kind === 'vanilla' ? ( - - ) : kind === 'VSR' ? ( - - ) : kind === 'NFT' ? ( - - ) : kind === 'HeliumVSR' ? ( - - ) : null - ) : kind === 'vanilla' ? ( - - ) : null} -
+ return votePop === undefined ? ( +
+ ) : ( + ) } diff --git a/components/ProposalVotingPower/VotingPowerPct.tsx b/components/ProposalVotingPower/VotingPowerPct.tsx index b05897c124..a65707ed19 100644 --- a/components/ProposalVotingPower/VotingPowerPct.tsx +++ b/components/ProposalVotingPower/VotingPowerPct.tsx @@ -27,14 +27,12 @@ export default function VotingPowerPct(props: Props) { className={classNames( props.className, 'leading-[15px]', - 'text-[11px]', + 'text-xs', 'text-right', - 'text-white/70' + 'text-fgd-2' )} > {getPct(props.amount, props.total)}% of total -
- voting power
) } diff --git a/components/ProposalVotingPower/depositTokens.ts b/components/ProposalVotingPower/depositTokens.ts deleted file mode 100644 index 8233284999..0000000000 --- a/components/ProposalVotingPower/depositTokens.ts +++ /dev/null @@ -1,84 +0,0 @@ -import type BigNumber from 'bignumber.js' -import type { SignerWalletAdapter } from '@solana/wallet-adapter-base' -import { - ProgramAccount, - Realm, - withDepositGoverningTokens, -} from '@solana/spl-governance' -import { - Connection, - Keypair, - Transaction, - TransactionInstruction, -} from '@solana/web3.js' - -import { RealmInfo, getProgramVersionForRealm } from '@models/registry/api' -import { - approveTokenTransfer, - TokenAccount, - TokenProgramAccount, -} from '@utils/tokens' -import { sendTransaction } from '@utils/send' -import BN from 'bn.js' - -interface Args { - amount: BigNumber - connection: Connection - depositTokenAccount: TokenProgramAccount - realm: ProgramAccount - realmInfo: RealmInfo - wallet: SignerWalletAdapter -} - -export default async function depositTokens({ - amount, - connection, - depositTokenAccount, - realm, - realmInfo, - wallet, -}: Args) { - if (!wallet.publicKey) { - throw new Error('Could not complete deposit; wallet missing publicKey') - } - - const instructions: TransactionInstruction[] = [] - const signers: Keypair[] = [] - const amountBN = new BN(amount.toString()) - - const transferAuthority = approveTokenTransfer( - instructions, - [], - depositTokenAccount.publicKey, - wallet.publicKey, - amountBN - ) - - signers.push(transferAuthority) - - await withDepositGoverningTokens( - instructions, - realmInfo.programId, - getProgramVersionForRealm(realmInfo!), - realm.pubkey, - depositTokenAccount.publicKey, - depositTokenAccount.account.mint, - wallet!.publicKey, - transferAuthority.publicKey, - wallet!.publicKey, - amountBN, - true - ) - - const transaction = new Transaction() - transaction.add(...instructions) - - await sendTransaction({ - connection, - signers, - transaction, - wallet, - sendingMessage: 'Depositing tokens', - successMessage: 'Tokens have been deposited', - }) -} diff --git a/components/ProposalVotingPower/index.tsx b/components/ProposalVotingPower/index.tsx index 9979835c7c..d95e95573c 100644 --- a/components/ProposalVotingPower/index.tsx +++ b/components/ProposalVotingPower/index.tsx @@ -1,12 +1,6 @@ import classNames from 'classnames' -import { GATEWAY_PLUGINS_PKS } from '@constants/plugins' -import TokenBalanceCardWrapper from '@components/TokenBalance/TokenBalanceCardWrapper' -import { option } from '@tools/core/option' - import VotingPower from './VotingPower' -import { useRealmConfigQuery } from '@hooks/queries/realmConfig' -import { useRouteProposalQuery } from '@hooks/queries/proposal' import useWalletOnePointOh from '@hooks/useWalletOnePointOh' interface Props { @@ -15,16 +9,13 @@ interface Props { export default function ProposalVotingPower(props: Props) { const connected = useWalletOnePointOh()?.connected - const config = useRealmConfigQuery().data?.result - const proposal = useRouteProposalQuery().data?.result - const currentPluginPk = config?.account?.communityTokenConfig.voterWeightAddin - + /* const isUsingGatewayPlugin = currentPluginPk && GATEWAY_PLUGINS_PKS.includes(currentPluginPk.toBase58()) if (isUsingGatewayPlugin) { return - } + } */ return (
{ + const wallet = useWalletOnePointOh() + const walletId = wallet?.publicKey?.toBase58() + const realm = useRealmQuery().data?.result + + const delegatesArray = useTokenOwnerRecordsDelegatedToUser() + + // returns array of community tokenOwnerRecords that connected wallet has been delegated + const communityTorsDelegatedToUser = useMemo( + () => + realm === undefined + ? undefined + : delegatesArray?.filter((x) => + x.account.governingTokenMint.equals(realm.account.communityMint) + ), + [delegatesArray, realm] + ) + + const councilMintAddr = realm?.account.config.councilMint + + // returns array of council tokenOwnerRecords that connected wallet has been delegated + const councilTorsDelegatedToUser = useMemo( + () => + councilMintAddr === undefined + ? undefined + : delegatesArray?.filter((x) => + x.account.governingTokenMint.equals(councilMintAddr) + ), + [delegatesArray, councilMintAddr] + ) + + const { + councilDelegator, + communityDelegator, + setCommunityDelegator, + setCouncilDelegator, + } = useSelectedDelegatorStore() + + const handleCouncilSelect = (councilTokenRecord: string | undefined) => { + setCouncilDelegator( + councilTokenRecord !== undefined + ? new PublicKey(councilTokenRecord) + : undefined + ) + } + + const handleCommunitySelect = (communityPubKey?: string) => { + setCommunityDelegator( + communityPubKey ? new PublicKey(communityPubKey) : undefined + ) + } + + return ( + <> + {walletId && + communityTorsDelegatedToUser && + communityTorsDelegatedToUser.length > 0 && ( + + )} + {walletId && + councilTorsDelegatedToUser && + councilTorsDelegatedToUser.length > 0 && ( + + )} + + ) +} + +export default SelectPrimaryDelegators + +function PrimaryDelegatorSelect({ + selectedDelegator, + handleSelect, + + kind, + tors, +}: { + selectedDelegator: PublicKey | undefined + handleSelect: (tokenRecordPk: string) => void + kind: 'community' | 'council' + tors: ProgramAccount[] +}) { + const connection = useLegacyConnectionContext() + return ( +
+
+

+ Perform {capitalize(kind)} actions as: +

+ +
+
+ ) +} diff --git a/components/TokenBalance/DelegateTokenBalanceCard.tsx b/components/TokenBalance/DelegateTokenBalanceCard.tsx deleted file mode 100644 index 4a626bcc18..0000000000 --- a/components/TokenBalance/DelegateTokenBalanceCard.tsx +++ /dev/null @@ -1,211 +0,0 @@ -import { DisplayAddress } from '@cardinal/namespaces-components' -import Select from '@components/inputs/Select' -import useWalletOnePointOh from '@hooks/useWalletOnePointOh' -import { - useTokenOwnerRecordsDelegatedToUser, - useUserCommunityTokenOwnerRecord, - useUserCouncilTokenOwnerRecord, -} from '@hooks/queries/tokenOwnerRecord' -import { useSelectedDelegatorStore } from 'stores/useSelectedDelegatorStore' -import { PublicKey } from '@solana/web3.js' -import useLegacyConnectionContext from '@hooks/useLegacyConnectionContext' -import { useRealmQuery } from '@hooks/queries/realm' -import { useMemo } from 'react' - -const DelegateBalanceCard = () => { - const wallet = useWalletOnePointOh() - const connection = useLegacyConnectionContext() - const walletId = wallet?.publicKey?.toBase58() - const ownTokenRecord = useUserCommunityTokenOwnerRecord().data?.result - const ownCouncilTokenRecord = useUserCouncilTokenOwnerRecord().data?.result - const realm = useRealmQuery().data?.result - - const delegatesArray = useTokenOwnerRecordsDelegatedToUser() - - // returns array of community tokenOwnerRecords that connected wallet has been delegated - const communityTorsDelegatedToUser = useMemo( - () => - realm === undefined - ? undefined - : delegatesArray?.filter((x) => - x.account.governingTokenMint.equals(realm.account.communityMint) - ), - [delegatesArray, realm] - ) - - const councilMintAddr = realm?.account.config.councilMint - - // returns array of council tokenOwnerRecords that connected wallet has been delegated - const councilTorsDelegatedToUser = useMemo( - () => - councilMintAddr === undefined - ? undefined - : delegatesArray?.filter((x) => - x.account.governingTokenMint.equals(councilMintAddr) - ), - [delegatesArray, councilMintAddr] - ) - - const { - setCommunityDelegator, - setCouncilDelegator, - } = useSelectedDelegatorStore() - - const handleCouncilSelect = (councilTokenRecord?: string) => { - setCouncilDelegator( - councilTokenRecord ? new PublicKey(councilTokenRecord) : undefined - ) - } - - const handleCommunitySelect = (communityPubKey?: string) => { - setCommunityDelegator( - communityPubKey ? new PublicKey(communityPubKey) : undefined - ) - } - - const hasDelegators = - (communityTorsDelegatedToUser?.length ?? 0) > 0 || - (councilTorsDelegatedToUser?.length ?? 0) > 0 - - if (!walletId || !hasDelegators) { - return null - } - - return ( - <> -

Your Delegates

- {walletId && - councilTorsDelegatedToUser && - councilTorsDelegatedToUser.length > 0 && ( -
-
-
-
-

Delegate Accounts

-

- {councilTorsDelegatedToUser?.length ?? 0} -

-
-
- -

Selected Delegate

- -
-
- )} - {walletId && - communityTorsDelegatedToUser && - communityTorsDelegatedToUser.length > 0 && ( -
-
-
-
-

Delegate Accounts

-

- {communityTorsDelegatedToUser.length ?? 0} -

-
-
- -

Selected Delegate

- -
-
- )} - - ) -} - -export default DelegateBalanceCard diff --git a/components/TokenBalance/TokenBalanceCardWrapper.tsx b/components/TokenBalance/TokenBalanceCardWrapper.tsx index 529795c4c6..e9e35a3058 100644 --- a/components/TokenBalance/TokenBalanceCardWrapper.tsx +++ b/components/TokenBalance/TokenBalanceCardWrapper.tsx @@ -1,5 +1,3 @@ -import { Proposal } from '@solana/spl-governance' -import { Option } from 'tools/core/option' import useRealm from '@hooks/useRealm' import dynamic from 'next/dynamic' import { ChevronRightIcon } from '@heroicons/react/solid' @@ -16,6 +14,9 @@ import { } from '@hooks/queries/tokenOwnerRecord' import { useRealmConfigQuery } from '@hooks/queries/realmConfig' import ClaimUnreleasedPositions from 'HeliumVotePlugin/components/ClaimUnreleasedPositions' +import VanillaAccountDetails from './VanillaAccountDetails' +import GovernancePowerCard from '@components/GovernancePower/GovernancePowerCard' +import SelectPrimaryDelegators from '@components/SelectPrimaryDelegators' const LockPluginTokenBalanceCard = dynamic( () => @@ -31,12 +32,11 @@ const HeliumVotingPowerCard = dynamic(() => }) ) -const TokenBalanceCard = dynamic(() => import('./TokenBalanceCard')) const NftVotingPower = dynamic( () => import('../ProposalVotingPower/NftVotingPower') ) -const GovernancePowerTitle = () => { +export const GovernancePowerTitle = () => { const { symbol } = useRealm() const { fmtUrlWithCluster } = useQueryContext() const wallet = useWalletOnePointOh() @@ -63,10 +63,8 @@ const GovernancePowerTitle = () => { } const TokenBalanceCardInner = ({ - proposal, inAccountDetails, }: { - proposal?: Option inAccountDetails?: boolean }) => { const ownTokenRecord = useUserCommunityTokenOwnerRecord().data?.result @@ -116,10 +114,11 @@ const TokenBalanceCardInner = ({ <> {!inAccountDetails && } - + {inAccountDetails ? ( + + ) : ( + + )} ) : ( @@ -136,30 +135,24 @@ const TokenBalanceCardInner = ({ //Default return ( <> - {!inAccountDetails && } - - {/*Add the gateway card if this is a gated DAO*/} - {isGatewayMode && } - + {inAccountDetails ? : } + + {isGatewayMode && } ) } const TokenBalanceCardWrapper = ({ - proposal, inAccountDetails, }: { - proposal?: Option inAccountDetails?: boolean }) => { return (
- + +
) } diff --git a/components/TokenBalance/TokenBalanceCard.tsx b/components/TokenBalance/TokenDeposit.tsx similarity index 66% rename from components/TokenBalance/TokenBalanceCard.tsx rename to components/TokenBalance/TokenDeposit.tsx index 4b3ec1e38d..8b4af9f79c 100644 --- a/components/TokenBalance/TokenBalanceCard.tsx +++ b/components/TokenBalance/TokenDeposit.tsx @@ -1,42 +1,29 @@ -import { BigNumber } from 'bignumber.js' import { ASSOCIATED_TOKEN_PROGRAM_ID, MintInfo, Token, TOKEN_PROGRAM_ID, } from '@solana/spl-token' -import { - Keypair, - PublicKey, - Transaction, - TransactionInstruction, -} from '@solana/web3.js' +import { PublicKey, Transaction, TransactionInstruction } from '@solana/web3.js' import BN from 'bn.js' import useRealm from '@hooks/useRealm' -import { getProposal, Proposal, ProposalState } from '@solana/spl-governance' +import { getProposal, ProposalState } from '@solana/spl-governance' import { getUnrelinquishedVoteRecords } from '@models/api' -import { withDepositGoverningTokens } from '@solana/spl-governance' import { withRelinquishVote } from '@solana/spl-governance' import { withWithdrawGoverningTokens } from '@solana/spl-governance' import { sendTransaction } from '@utils/send' -import { approveTokenTransfer } from '@utils/tokens' import Button, { SecondaryButton } from '../Button' -import { Option } from '@tools/core/option' import { GoverningTokenRole } from '@solana/spl-governance' -import { fmtMintAmount, getMintDecimalAmount } from '@tools/sdk/units' +import { fmtMintAmount } from '@tools/sdk/units' import { getMintMetadata } from '../instructions/programs/splToken' import { withFinalizeVote } from '@solana/spl-governance' import { chunks } from '@utils/helpers' import { getProgramVersionForRealm } from '@models/registry/api' import { notify } from '@utils/notifications' import { ExclamationIcon } from '@heroicons/react/outline' -import { useEffect, useState } from 'react' +import { useEffect } from 'react' import useVotePluginsClientStore from 'stores/useVotePluginsClientStore' import { VSR_PLUGIN_PKS } from '@constants/plugins' -import DelegateTokenBalanceCard from '@components/TokenBalance/DelegateTokenBalanceCard' -import SerumGovernanceTokenWrapper from './SerumGovernanceTokenWrapper' -import getNumTokens from '@components/ProposalVotingPower/getNumTokens' -import VotingPowerPct from '@components/ProposalVotingPower/VotingPowerPct' import { useMaxVoteRecord } from '@hooks/useMaxVoteRecord' import useWalletOnePointOh from '@hooks/useWalletOnePointOh' import { @@ -45,106 +32,13 @@ import { } from '@hooks/queries/tokenOwnerRecord' import { useRealmQuery } from '@hooks/queries/realm' import { useRealmConfigQuery } from '@hooks/queries/realmConfig' -import { - useRealmCommunityMintInfoQuery, - useRealmCouncilMintInfoQuery, -} from '@hooks/queries/mintInfo' import { fetchGovernanceByPubkey } from '@hooks/queries/governance' import { useConnection } from '@solana/wallet-adapter-react' import queryClient from '@hooks/queries/queryClient' import { proposalQueryKeys } from '@hooks/queries/proposal' import asFindable from '@utils/queries/asFindable' -import { useLegacyVoterWeight } from '@hooks/queries/governancePower' - -const TokenBalanceCard = ({ - proposal, - inAccountDetails = false, - children, -}: { - proposal?: Option - inAccountDetails?: boolean - children?: React.ReactNode -}) => { - const [hasGovPower, setHasGovPower] = useState(false) - const realm = useRealmQuery().data?.result - const realmProgramId = realm?.owner - const mint = useRealmCommunityMintInfoQuery().data?.result - const councilMint = useRealmCouncilMintInfoQuery().data?.result - const wallet = useWalletOnePointOh() - const connected = !!wallet?.connected - const isDepositVisible = ( - depositMint: MintInfo | undefined, - realmMint: PublicKey | undefined - ) => - depositMint && - (!proposal || - (proposal.isSome() && - proposal.value.governingTokenMint.toBase58() === realmMint?.toBase58())) - - const communityDepositVisible = - // If there is no council then community deposit is the only option to show - !realm?.account.config.councilMint || - isDepositVisible(mint, realm?.account.communityMint) - - const councilDepositVisible = isDepositVisible( - councilMint, - realm?.account.config.councilMint - ) - const hasLoaded = mint || councilMint - - return ( - <> - {hasLoaded ? ( -
- {!hasGovPower && !inAccountDetails && connected && ( -
- You do not have any governance power in this dao -
- )} - {!connected && ( -
- Connect your wallet to see governance power -
- )} - {communityDepositVisible && ( - - )} - {councilDepositVisible && ( - - )} - -
- ) : ( - <> -
-
- - )} - {/* TODO: Restrict to Serum DAO */} - {realmProgramId?.toBase58() === - 'G41fmJzd29v7Qmdi8ZyTBBYa98ghh3cwHBTexqCG1PQJ' ? ( - - ) : null} - {children} - - ) -} +import { useDepositCallback } from '@components/GovernancePower/Vanilla/useDepositCallback' +import VanillaVotingPower from '@components/GovernancePower/Vanilla/VanillaVotingPower' export const TokenDeposit = ({ mint, @@ -172,8 +66,6 @@ export const TokenDeposit = ({ const ownCouncilTokenRecord = useUserCouncilTokenOwnerRecord().data?.result const realm = useRealmQuery().data?.result const config = useRealmConfigQuery().data?.result - const councilMint = useRealmCouncilMintInfoQuery().data?.result - const { result: ownVoterWeight } = useLegacyVoterWeight() const { realmInfo, @@ -183,24 +75,6 @@ export const TokenDeposit = ({ toManyCouncilOutstandingProposalsForUse, } = useRealm() - const amount = ownVoterWeight - ? councilMint && tokenRole === GoverningTokenRole.Council - ? getNumTokens( - ownVoterWeight, - ownCouncilTokenRecord, - councilMint, - realmInfo - ) - : getNumTokens(ownVoterWeight, ownCouncilTokenRecord, mint, realmInfo) - : new BigNumber(0) - - const max: BigNumber | undefined = - councilMint && tokenRole === GoverningTokenRole.Council - ? getMintDecimalAmount(councilMint, councilMint.supply) - : mint - ? getMintDecimalAmount(mint, mint.supply) - : undefined - const depositTokenRecord = tokenRole === GoverningTokenRole.Community ? ownTokenRecord @@ -222,45 +96,9 @@ export const TokenDeposit = ({ tokenRole === GoverningTokenRole.Community ? '' : 'Council' }` - const depositTokens = async function (amount: BN) { - const instructions: TransactionInstruction[] = [] - const signers: Keypair[] = [] - - const transferAuthority = approveTokenTransfer( - instructions, - [], - depositTokenAccount!.publicKey, - wallet!.publicKey!, - amount - ) - - signers.push(transferAuthority) - - await withDepositGoverningTokens( - instructions, - realmInfo!.programId, - getProgramVersionForRealm(realmInfo!), - realm!.pubkey, - depositTokenAccount!.publicKey, - depositTokenAccount!.account.mint, - wallet!.publicKey!, - transferAuthority.publicKey, - wallet!.publicKey!, - amount - ) - - const transaction = new Transaction() - transaction.add(...instructions) - - await sendTransaction({ - connection, - wallet: wallet!, - transaction, - signers, - sendingMessage: 'Depositing tokens', - successMessage: 'Tokens have been deposited', - }) - } + const depositTokens = useDepositCallback( + tokenRole === GoverningTokenRole.Community ? 'community' : 'council' + ) const depositAllTokens = async () => await depositTokens(depositTokenAccount!.account.amount) @@ -353,18 +191,18 @@ export const TokenDeposit = ({ let ata: PublicKey | null = null if (!depositTokenAccount) { ata = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, // always ASSOCIATED_TOKEN_PROGRAM_ID - TOKEN_PROGRAM_ID, // always TOKEN_PROGRAM_ID - depositMint!, // mint - wallet!.publicKey!, // owner + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + depositMint!, + wallet!.publicKey!, true ) const ataIx = Token.createAssociatedTokenAccountInstruction( - ASSOCIATED_TOKEN_PROGRAM_ID, // always ASSOCIATED_TOKEN_PROGRAM_ID - TOKEN_PROGRAM_ID, // always TOKEN_PROGRAM_ID - depositMint!, // mint - ata, // ata - wallet!.publicKey!, // owner of token account + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + depositMint!, + ata, + wallet!.publicKey!, wallet!.publicKey! // fee payer ) instructions.push(ataIx) @@ -474,20 +312,11 @@ export const TokenDeposit = ({ {(availableTokens != '0' || inAccountDetails) && (
-
-
-

{depositTokenName} Votes

-
-

- {availableTokens} -

-
-
- {amount > new BigNumber(0) - ? max && - !max.isZero() && - : null} -
+ {tokenRole === GoverningTokenRole.Community ? ( + + ) : ( + + )}
)} @@ -552,7 +381,6 @@ export const TokenDeposit = ({ ) } - const TokenDepositWrapper = ({ children, inAccountDetails, @@ -566,5 +394,3 @@ const TokenDepositWrapper = ({ return
{children}
} } - -export default TokenBalanceCard diff --git a/components/TokenBalance/VanillaAccountDetails.tsx b/components/TokenBalance/VanillaAccountDetails.tsx new file mode 100644 index 0000000000..7d35da1b41 --- /dev/null +++ b/components/TokenBalance/VanillaAccountDetails.tsx @@ -0,0 +1,60 @@ +import { GoverningTokenRole } from '@solana/spl-governance' +import useWalletOnePointOh from '@hooks/useWalletOnePointOh' +import { useRealmQuery } from '@hooks/queries/realm' +import { + useRealmCommunityMintInfoQuery, + useRealmCouncilMintInfoQuery, +} from '@hooks/queries/mintInfo' +import { TokenDeposit } from './TokenDeposit' + +const VanillaAccountDetails = () => { + const realm = useRealmQuery().data?.result + const mint = useRealmCommunityMintInfoQuery().data?.result + const councilMint = useRealmCouncilMintInfoQuery().data?.result + const wallet = useWalletOnePointOh() + const connected = !!wallet?.connected + + const communityDepositVisible = + // If there is no council then community deposit is the only option to show + !realm?.account.config.councilMint || + realm?.account.communityMint !== undefined + const councilDepositVisible = realm?.account.config.councilMint !== undefined + const hasLoaded = mint || councilMint + + return ( + <> + {hasLoaded ? ( +
+ {!connected && ( +
+ Connect your wallet to see governance power +
+ )} + {communityDepositVisible && ( + + )} + {councilDepositVisible && ( + + )} +
+ ) : ( + <> +
+
+ + )} + + ) +} + +export default VanillaAccountDetails diff --git a/components/inputs/Checkbox.tsx b/components/inputs/Checkbox.tsx index 5ef8f57aeb..1a24cf1627 100644 --- a/components/inputs/Checkbox.tsx +++ b/components/inputs/Checkbox.tsx @@ -1,7 +1,15 @@ -import React from 'react' import { CheckIcon } from '@heroicons/react/solid' -const Checkbox = ({ checked, label = '', disabled = false, ...props }) => ( +const Checkbox = ({ + checked, + label = '', + disabled = false, + ...props +}: { + checked: boolean + label?: string + disabled?: boolean +} & React.ComponentProps<'input'>) => (