refactor/bugfix: new wallet form being wrong about mint supply (#1667)

This commit is contained in:
agrippa 2023-06-07 20:17:14 -04:00 committed by GitHub
parent dcd194eb91
commit bca2760d41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 93 additions and 39 deletions

View File

@ -6,11 +6,7 @@ import asFindable from '@utils/queries/asFindable'
import queryClient from './queryClient'
export const governanceAccountQueryKeys = {
all: (cluster: string, kind: string) => [
cluster,
'Governance Account',
`${kind}`,
],
all: (cluster: string, kind: string) => [cluster, `${kind}`],
byPubkey: (cluster: string, kind: string, k: PublicKey) => [
...governanceAccountQueryKeys.all(cluster, kind),
k.toString(),
@ -53,7 +49,8 @@ export async function fetchGovernanceAccountByPubkey<
kindLabel: string,
pubkey: PublicKey
) {
const f = () => getGovernanceAccount(connection, pubkey, kind)
const f = () =>
asFindable(() => getGovernanceAccount(connection, pubkey, kind))()
return queryClient.fetchQuery({
queryKey: governanceAccountQueryKeys.byPubkey(
connection.rpcEndpoint,

View File

@ -29,6 +29,7 @@ interface Props
}
function Form(props: Props & { title: string; description: string }) {
console.log('form props', props);
const [showCouncilOptions, setShowCouncilOptions] = useState(
props.initialCouncilRules?.canVote || false,
);

View File

@ -1,6 +1,11 @@
import { BN } from '@coral-xyz/anchor';
import { BigNumber } from 'bignumber.js';
import { produce } from 'immer';
import {
useRealmCommunityMintInfoQuery,
useRealmCouncilMintInfoQuery,
} from '@hooks/queries/mintInfo';
import { ButtonToggle } from '@hub/components/controls/ButtonToggle';
import { Input } from '@hub/components/controls/Input';
import { Slider } from '@hub/components/controls/Slider';
@ -12,6 +17,8 @@ import { capitalize } from '@hub/lib/capitalize';
import { formatNumber } from '@hub/lib/formatNumber';
import { FormProps } from '@hub/types/FormProps';
import { GovernanceTokenType } from '@hub/types/GovernanceTokenType';
import { SliderValue } from './SliderValue';
import { ValueBlock } from './ValueBlock';
import { VoteTippingSelector } from './VoteTippingSelector';
@ -111,9 +118,27 @@ export function CanVote(props: Props) {
}
export function VotingPowerToCreateProposals(props: Props) {
const councilPowerPercent = props.rules.votingPowerToCreateProposals
.dividedBy(props.rules.totalSupply)
.multipliedBy(100);
const communityTokenInfo = useRealmCommunityMintInfoQuery();
const councilTokenInfo = useRealmCouncilMintInfoQuery();
const tokenInfoQuery =
props.rules.tokenType === GovernanceTokenType.Community
? communityTokenInfo
: councilTokenInfo;
const supply = tokenInfoQuery.data?.result
? new BigNumber(
tokenInfoQuery.data?.result.supply
.div(new BN(Math.pow(10, tokenInfoQuery.data.result.decimals)))
.toString(),
)
: undefined;
const councilPowerPercent = supply
? props.rules.votingPowerToCreateProposals
.dividedBy(supply)
.multipliedBy(100)
: undefined;
return (
<ValueBlock
@ -144,8 +169,8 @@ export function VotingPowerToCreateProposals(props: Props) {
Tokens
</div>
</div>
<div className="flex items-center justify-end">
{props.rules.totalSupply.isGreaterThan(0) && (
{councilPowerPercent && supply?.isGreaterThan(0) && (
<div className="flex items-center justify-end">
<div className="mt-1 text-xs text-neutral-500">
{councilPowerPercent.isGreaterThan(0)
? councilPowerPercent.isLessThan(0.01)
@ -157,8 +182,8 @@ export function VotingPowerToCreateProposals(props: Props) {
: 0}
% of token supply
</div>
)}
</div>
</div>
)}
</ValueBlock>
);
}

View File

@ -1,12 +1,17 @@
import {
createSetGovernanceConfig,
GovernanceConfig,
Realm,
VoteThresholdType,
VoteTipping,
} from '@solana/spl-governance';
import type { PublicKey } from '@solana/web3.js';
import type { Connection, PublicKey } from '@solana/web3.js';
import BigNumber from 'bignumber.js';
import BN from 'bn.js';
import { fetchGovernanceAccountByPubkey } from '@hooks/queries/governanceAccount';
import { fetchMintInfoByPubkey } from '@hooks/queries/mintInfo';
import queryClient from '@hooks/queries/queryClient';
import { GovernanceVoteTipping } from '@hub/types/GovernanceVoteTipping';
import { MAX_NUM } from './constants';
@ -32,13 +37,15 @@ function convertVoteTipping(tipping: GovernanceVoteTipping): VoteTipping {
}
}
export function createTransaction(
export async function createTransaction(
connection: Connection,
programId: PublicKey,
programVersion: number,
governance: PublicKey,
realmPk: PublicKey,
rules: Rules,
) {
const newConfig = rules2governanceConfig(rules);
const newConfig = await rules2governanceConfig(connection, realmPk, rules);
const instruction = createSetGovernanceConfig(
programId,
@ -50,15 +57,40 @@ export function createTransaction(
return instruction;
}
export function rules2governanceConfig(
export async function rules2governanceConfig(
connection: Connection,
realmPk: PublicKey,
rules: Omit<Rules, 'governanceAddress' | 'walletAddress'>,
) {
const realm = await fetchGovernanceAccountByPubkey(
connection,
Realm,
'Realm',
realmPk,
);
if (!realm.result) throw new Error('realm fetch failed');
const communityMintInfo = await fetchMintInfoByPubkey(
connection,
realm.result.account.communityMint,
);
const councilMintInfo =
realm.result.account.config.councilMint !== undefined
? await fetchMintInfoByPubkey(
connection,
realm.result.account.config.councilMint,
)
: undefined;
const communityRules = rules.communityTokenRules;
const councilRules = rules.councilTokenRules;
const minCommunityTokensToCreateProposal = new BN(
(communityRules.canCreateProposal
? communityRules.votingPowerToCreateProposals.shiftedBy(
communityRules.tokenMintDecimals.toNumber(),
communityMintInfo.result?.decimals ??
(() => {
throw new Error('communityMintInfo failed to fetch');
})(),
)
: MAX_NUM
).toString(),
@ -67,7 +99,12 @@ export function rules2governanceConfig(
const minCouncilTokensToCreateProposal = new BN(
(councilRules && councilRules.canCreateProposal
? councilRules.votingPowerToCreateProposals.shiftedBy(
councilRules.tokenMintDecimals.toNumber(),
councilMintInfo?.result?.decimals ??
(() => {
throw new Error(
'councilMintInfo failed to fetch or doesnt exist',
);
})(),
)
: MAX_NUM
).toString(),

View File

@ -57,10 +57,7 @@ const Rules = IT.type({
canVeto: IT.boolean,
canVote: IT.boolean,
quorumPercent: IT.number,
tokenMintAddress: PublicKey,
tokenMintDecimals: BigNumber,
tokenType: GovernanceTokenType,
totalSupply: BigNumber,
vetoQuorumPercent: IT.number,
voteTipping: GovernanceVoteTipping,
votingPowerToCreateProposals: BigNumber,

View File

@ -2,6 +2,7 @@ import CheckmarkIcon from '@carbon/icons-react/lib/Checkmark';
import ChevronLeftIcon from '@carbon/icons-react/lib/ChevronLeft';
import EditIcon from '@carbon/icons-react/lib/Edit';
import { createInstructionData } from '@solana/spl-governance';
import { useConnection } from '@solana/wallet-adapter-react';
import { PublicKey } from '@solana/web3.js';
import { BigNumber } from 'bignumber.js';
import { hoursToSeconds, secondsToHours } from 'date-fns';
@ -65,6 +66,7 @@ export function EditWalletRules(props: Props) {
const { propose } = useCreateProposal();
const realm = useRealmQuery().data?.result;
const { symbol } = useRealm();
const { connection } = useConnection();
const [result] = useQuery(gql.getGovernanceRulesResp, {
query: gql.getGovernanceRules,
@ -87,12 +89,12 @@ export function EditWalletRules(props: Props) {
canVeto: false,
canVote: false,
quorumPercent: 1,
tokenType: GovernanceTokenType.Community,
// this isn't a valid value, but it's just to satisfy the types for the
// default initialized value
tokenMintAddress: props.governanceAddress,
tokenMintDecimals: new BigNumber(0),
tokenType: GovernanceTokenType.Community,
totalSupply: new BigNumber(1),
//tokenMintAddress: props.governanceAddress,
//tokenMintDecimals: new BigNumber(0),
//totalSupply: new BigNumber(1),
vetoQuorumPercent: 100,
voteTipping: GovernanceVoteTipping.Disabled,
votingPowerToCreateProposals: new BigNumber(1),
@ -311,10 +313,12 @@ export function EditWalletRules(props: Props) {
setSubmitting(true);
const instruction = createTransaction(
const instruction = await createTransaction(
connection,
realm.owner,
governance.version,
governance.governanceAddress,
realm.pubkey,
{
coolOffHours,
depositExemptProposalCount,

View File

@ -45,7 +45,6 @@ interface Props {
className?: string;
}
// TODO generate defaults
function NewWalletWithDefaults({
defaults,
...props

View File

@ -51,12 +51,7 @@ const configs2defaults = (configs: GovernanceConfig[]) => {
votingPowerToCreateProposals: new BigNumber(
highestMinCommunityTokensToCreateProposal.toString(),
),
// START totally fake dummy values 💀
tokenMintAddress: new PublicKey(0),
tokenMintDecimals: new BigNumber(0),
tokenType: GovernanceTokenType.Community,
totalSupply: new BigNumber(1),
// END
canVeto: false,
vetoQuorumPercent: 60,
canVote: enableCommunityVote,
@ -70,12 +65,7 @@ const configs2defaults = (configs: GovernanceConfig[]) => {
votingPowerToCreateProposals: new BigNumber(
highestMinCouncilTokensToCreateProposal.toString(),
),
// START totally fake dummy values 💀
tokenMintAddress: new PublicKey(0),
tokenMintDecimals: new BigNumber(0),
tokenType: GovernanceTokenType.Council,
totalSupply: new BigNumber(1),
// END
canVeto: enableCouncilVetoVote,
vetoQuorumPercent: 60,
canVote: enableCouncilVote,

View File

@ -44,7 +44,11 @@ const useNewWalletCallback = (
if (tokenOwnerRecord === undefined)
throw new Error('insufficient voting power');
const config = rules2governanceConfig(rules);
const config = await rules2governanceConfig(
connection.current,
realm.pubkey,
rules,
);
const instructions: TransactionInstruction[] = [];