diff --git a/hub/components/EditRealmConfig/gql.ts b/hub/components/EditRealmConfig/gql.ts index fcc6ce807..2227bf731 100644 --- a/hub/components/EditRealmConfig/gql.ts +++ b/hub/components/EditRealmConfig/gql.ts @@ -102,7 +102,7 @@ export const getGovernanceResp = IT.type({ maxVoteDays: IT.number, minInstructionHoldupDays: IT.number, version: IT.number, - walletAddress: PublicKey, + walletAddress: PublicKey }), }), }); diff --git a/hub/components/EditRealmConfig/index.tsx b/hub/components/EditRealmConfig/index.tsx index 8f1864f08..2c760958b 100644 --- a/hub/components/EditRealmConfig/index.tsx +++ b/hub/components/EditRealmConfig/index.tsx @@ -3,11 +3,9 @@ import ChevronLeftIcon from '@carbon/icons-react/lib/ChevronLeft'; import EditIcon from '@carbon/icons-react/lib/Edit'; import { createInstructionData, - getRealm, GoverningTokenType, } from '@solana/spl-governance'; import { PublicKey } from '@solana/web3.js'; -import { pipe } from 'fp-ts/lib/function'; import { TypeOf } from 'io-ts'; import Head from 'next/head'; import { useRouter } from 'next/router'; @@ -20,9 +18,7 @@ import useQueryContext from '@hooks/useQueryContext'; import { useRealmVoterWeightPlugins } from '@hooks/useRealmVoterWeightPlugins'; import useWalletOnePointOh from '@hooks/useWalletOnePointOh'; import { Primary, Secondary } from '@hub/components/controls/Button'; -import { useQuery } from '@hub/hooks/useQuery'; import cx from '@hub/lib/cx'; -import * as RE from '@hub/types/Result'; import { notify } from '@utils/notifications'; @@ -32,6 +28,12 @@ import { Form } from './Form'; import * as gql from './gql'; import { RealmHeader } from './RealmHeader'; import { Summary } from './Summary'; +import { useGovernanceByPubkeyQuery } from '@hooks/queries/governance'; +import getGovernanceRules from '../EditWalletRules/utils'; +import { useMintInfoByPubkeyQuery } from '@hooks/queries/mintInfo'; +import useProgramVersion from '@hooks/useProgramVersion'; +import BigNumber from 'bignumber.js'; +import useRealm from '@hooks/useRealm'; type Governance = TypeOf< typeof gql.getGovernanceResp @@ -69,29 +71,34 @@ export function EditRealmConfig(props: Props) { const connection = useLegacyConnectionContext(); const { fmtUrlWithCluster } = useQueryContext(); const realm = useRealmQuery().data?.result; + const {realmInfo} = useRealm(); + const govData = useGovernanceByPubkeyQuery(realm?.account.authority).data?.result; + const communityMint = useMintInfoByPubkeyQuery(realm?.account.communityMint).data?.result + const councilMint = useMintInfoByPubkeyQuery(realm?.account.config.councilMint).data?.result + const version = useProgramVersion(); + const wallet = useWalletOnePointOh(); const [step, setStep] = useState(Step.Form); - const [realmAuthority, setRealmAuthority] = useState( - undefined, - ); - const [result] = useQuery(gql.getRealmResp, { - query: gql.getRealm, - variables: { - realmUrlId: props.realmUrlId, - }, - }); + const [realmName, setRealmName] = useState(""); + + // const [result] = useQuery(gql.getRealmResp, { + // query: gql.getRealm, + // variables: { + // realmUrlId: props.realmUrlId, + // }, + // }); const { plugins } = useRealmVoterWeightPlugins(); const { propose } = useCreateProposal(); const [governance, setGovernance] = useState(null); - const [governanceResult] = useQuery(gql.getGovernanceResp, { - query: gql.getGovernance, - variables: { - realmUrlId: props.realmUrlId, - governancePublicKey: realmAuthority?.toBase58(), - }, - pause: !realmAuthority, - }); + // const [governanceResult] = useQuery(gql.getGovernanceResp, { + // query: gql.getGovernance, + // variables: { + // realmUrlId: props.realmUrlId, + // governancePublicKey: realmAuthority?.toBase58(), + // }, + // pause: !realmAuthority, + // }); const router = useRouter(); const [submitting, setSubmitting] = useState(false); @@ -113,18 +120,20 @@ export function EditRealmConfig(props: Props) { }, [step]); useEffect(() => { - if (RE.isOk(result)) { + if (realm) { + setRealmName(realm.account.name); + fetchConfig( connection.current, - result.data.realmByUrlId.publicKey, + realm.pubkey, plugins?.voterWeight ?? [], ) .then((config) => { setConfig({ ...config }); setProposalTitle( - `Update Realms Config for "${result.data.realmByUrlId.name}"`, + `Update Realms Config for "${realm.account.name}"`, ); - + existingConfig.current = { ...config, config: { ...config.config }, @@ -139,222 +148,241 @@ export function EditRealmConfig(props: Props) { }, }; }) - .then(() => - getRealm(connection.current, result.data.realmByUrlId.publicKey).then( - (realm) => { - setRealmAuthority(realm.account.authority); - }, - ), - ); } - }, [result._tag]); + }, [realm]); useEffect(() => { - if (RE.isOk(governanceResult)) { - setGovernance(governanceResult.data.realmByUrlId.governance); + if (govData && realm) { + const data = getGovernanceRules(realm.owner, govData, realm) + + const [walletAddress] = PublicKey.findProgramAddressSync([ + Buffer.from("native-treasury"), + govData.pubkey.toBuffer() + ], realm.owner) + + data.communityTokenRules.votingPowerToCreateProposals = communityMint ? + data.communityTokenRules.votingPowerToCreateProposals.shiftedBy( + -communityMint.decimals + ) + : data.communityTokenRules.votingPowerToCreateProposals + + if (data.councilTokenRules) { + data.councilTokenRules.votingPowerToCreateProposals = councilMint ? + data.councilTokenRules.votingPowerToCreateProposals.shiftedBy( + -councilMint.decimals + ) + : data.councilTokenRules.votingPowerToCreateProposals + } + + let updateGovernance: Governance = { + version: version ?? 3, + walletAddress, + ...data, + communityTokenRules: { + ...data.communityTokenRules, + totalSupply: communityMint ? + new BigNumber(communityMint.supply.toString()).shiftedBy(-communityMint.decimals) : + new BigNumber(0), + tokenMintDecimals: communityMint ? new BigNumber(communityMint.decimals) : new BigNumber(0), + }, + councilTokenRules: councilMint && data.councilTokenRules ? { + ...data.councilTokenRules, + canCreateProposal: data.councilTokenRules.canCreateProposal!, + totalSupply: councilMint ? + new BigNumber(councilMint.supply.toString()).shiftedBy(-councilMint.decimals) : + new BigNumber(0), + tokenMintDecimals: communityMint ? new BigNumber(communityMint.decimals) : new BigNumber(0), + } : null + } + + setGovernance(updateGovernance) if (existingConfig.current) { if ( existingConfig.current.config.councilMint && (existingConfig.current.configAccount.communityTokenConfig .tokenType === GoverningTokenType.Dormant || - !governanceResult.data.realmByUrlId.governance.communityTokenRules + !data.communityTokenRules .canVote) ) { setProposalVoteType('council'); } } } - }, [governanceResult._tag]); + }, [govData, realm]); - return pipe( - result, - RE.match( - () =>
, - () =>
, - ({ realmByUrlId }) => { - if (!wallet?.publicKey) { - return ( -
- - Edit Org Config - {realmByUrlId.name} - - -
-
-
- Please sign in to edit the realm config -
- for "{realmByUrlId.name}" -
-
-
+ return ( + !wallet?.publicKey ? +
+ + Edit Org Config - {realmName} + + +
+
+
+ Please sign in to edit the realm config +
+ for "{realmName}"
- ); - } - - if (!(config && existingConfig.current && governance)) { - return
; - } - - const userPublicKey = wallet.publicKey; - - return ( -
-
- - Edit Org Config - {realmByUrlId.name} - +
+
+ : !(config && existingConfig.current && governance) ? +
+ : +
+
+ + Edit Org Config - {realmName} + + +
+
+ Step {stepNum(step)} of 2 +
+
+ {stepName(step)} +
+
+
+ + {step === Step.Form && ( + <> +
- -
-
- Step {stepNum(step)} of 2 -
-
- {stepName(step)} -
-
-
- + + setStep(Step.Summary)} + > + Continue + + + + )} + {step === Step.Summary && ( + <> + - {step === Step.Form && ( - <> - -
- - setStep(Step.Summary)} - > - Continue - -
- - )} - {step === Step.Summary && ( - <> - -
- - { - if (!existingConfig.current) { - return; - } - if (!wallet.publicKey) throw new Error(); - if (!realm) throw new Error(); +
+ + { + if (!existingConfig.current) { + return; + } + if (!wallet.publicKey) throw new Error(); + if (!realm) throw new Error(); - setSubmitting(true); + setSubmitting(true); - const userPublicKey = wallet.publicKey; + const userPublicKey = wallet.publicKey; - const instructions = await createTransaction( - realm.pubkey, - governance.governanceAddress, - config, - existingConfig.current, - connection.current, - connection.cluster === 'devnet', - { - publicKey: userPublicKey, - signAllTransactions: wallet.signAllTransactions, - signTransaction: wallet.signTransaction, - }, - ); + const instructions = await createTransaction( + realm.pubkey, + governance.governanceAddress, + config, + existingConfig.current, + connection.current, + connection.cluster === 'devnet', + { + publicKey: userPublicKey, + signAllTransactions: wallet.signAllTransactions, + signTransaction: wallet.signTransaction, + }, + ); - try { - const proposalAddress = await propose({ - title: proposalTitle, - description: proposalDescription, - voteByCouncil: proposalVoteType === 'council', - instructionsData: instructions.map((ix) => ({ - data: createInstructionData(ix), - holdUpTime: - 60 * - 60 * - 24 * - governance.minInstructionHoldupDays, - prerequisiteInstructions: [], - })), - governance: governance.governanceAddress, - }); + try { + const proposalAddress = await propose({ + title: proposalTitle, + description: proposalDescription, + voteByCouncil: proposalVoteType === 'council', + instructionsData: instructions.map((ix) => ({ + data: createInstructionData(ix), + holdUpTime: + 60 * + 60 * + 24 * + governance.minInstructionHoldupDays, + prerequisiteInstructions: [], + })), + governance: governance.governanceAddress, + }); - if (proposalAddress) { - router.push( - fmtUrlWithCluster( - `/dao/${ - props.realmUrlId - }/proposal/${proposalAddress.toBase58()}`, - ), - ); - } - } catch (e) { - console.error(e); - notify({ - type: 'error', - message: - 'Could not create proposal: ' + String(e), - }); - } + if (proposalAddress) { + router.push( + fmtUrlWithCluster( + `/dao/${ + props.realmUrlId + }/proposal/${proposalAddress.toBase58()}`, + ), + ); + } + } catch (e) { + console.error(e); + notify({ + type: 'error', + message: + 'Could not create proposal: ' + String(e), + }); + } - setSubmitting(false); - }} - > - - Create Proposal - -
- - )} -
-
+ setSubmitting(false); + }} + > + + Create Proposal + + + + )}
- ); - }, - ), - ); +
+
+ ) } diff --git a/hub/components/EditWalletRules/gql.ts b/hub/components/EditWalletRules/gql.ts index 6c16dffa9..4a5250d70 100644 --- a/hub/components/EditWalletRules/gql.ts +++ b/hub/components/EditWalletRules/gql.ts @@ -61,6 +61,7 @@ const Rules = IT.type({ vetoQuorumPercent: IT.number, voteTipping: GovernanceVoteTipping, votingPowerToCreateProposals: BigNumber, + tokenMintAddress: PublicKey }); export const getGovernanceRulesResp = IT.type({ diff --git a/hub/components/EditWalletRules/index.tsx b/hub/components/EditWalletRules/index.tsx index 7c8fd304d..10f1b2119 100644 --- a/hub/components/EditWalletRules/index.tsx +++ b/hub/components/EditWalletRules/index.tsx @@ -6,7 +6,6 @@ import { useConnection } from '@solana/wallet-adapter-react'; import { PublicKey } from '@solana/web3.js'; import { BigNumber } from 'bignumber.js'; import { hoursToSeconds, secondsToHours } from 'date-fns'; -import { pipe } from 'fp-ts/function'; import Head from 'next/head'; import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; @@ -18,19 +17,21 @@ import useQueryContext from '@hooks/useQueryContext'; import useRealm from '@hooks/useRealm'; import useWalletOnePointOh from '@hooks/useWalletOnePointOh'; import { Primary, Secondary } from '@hub/components/controls/Button'; -import { useQuery } from '@hub/hooks/useQuery'; import cx from '@hub/lib/cx'; import { GovernanceTokenType } from '@hub/types/GovernanceTokenType'; import { GovernanceVoteTipping } from '@hub/types/GovernanceVoteTipping'; -import * as RE from '@hub/types/Result'; import { notify } from '@utils/notifications'; import { createTransaction } from './createTransaction'; import { EditWalletForm } from './Form'; -import * as gql from './gql'; import { EditWalletSummary } from './Summary'; import { CommunityRules, CouncilRules } from './types'; +import { useGovernanceByPubkeyQuery } from '@hooks/queries/governance'; +import useGovernanceAssets from '@hooks/useGovernanceAssets'; +import useProgramVersion from '@hooks/useProgramVersion'; +import { useMintInfoByPubkeyQuery } from '@hooks/queries/mintInfo'; +import getGovernanceRules from './utils'; enum Step { Form, @@ -66,16 +67,13 @@ export function EditWalletRules(props: Props) { const wallet = useWalletOnePointOh(); const { propose } = useCreateProposal(); const realm = useRealmQuery().data?.result; + const version = useProgramVersion(); + const communityMint = useMintInfoByPubkeyQuery(realm?.account.communityMint).data?.result + const councilMint = useMintInfoByPubkeyQuery(realm?.account.config.councilMint).data?.result + const { symbol } = useRealm(); const { connection } = useConnection(); - - const [result] = useQuery(gql.getGovernanceRulesResp, { - query: gql.getGovernanceRules, - variables: { - realmUrlId: props.realmPk, - governancePublicKey: props.governanceAddress.toBase58(), - }, - }); + const {assetAccounts} = useGovernanceAssets() const router = useRouter(); const [step, setStep] = useState(Step.Form); @@ -84,6 +82,29 @@ export function EditWalletRules(props: Props) { >('community'); const [proposalDescription, setProposalDescription] = useState(''); const [proposalTitle, setProposalTitle] = useState(''); + const [walletName, setWalletName] = useState(''); + const [walletAddress, setWalletAddress] = useState(PublicKey.default); + + const [initialCommunityRules, setInitialCommunityRules] = useState({ + canCreateProposal: true, + 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), + //totalSupply: new BigNumber(1), + vetoQuorumPercent: 100, + voteTipping: GovernanceVoteTipping.Disabled, + votingPowerToCreateProposals: new BigNumber(1), + }) + const [initialCouncilRules, setInitialCouncilRules] = useState(null) + const [initialCoolOffHours, setInitialCoolOffHours] = useState(0) + const [initialBaseVoteDays, setInitialBaseVoteDays] = useState(0) + const [initialDepositExemptProposalCount, setInitialDepositExemptProposalCount] = useState(0) + const [initialMinInstructionHoldupDays, setInitialMinInstructionHoldupDays] = useState(0) const [communityRules, setCommunityRules] = useState({ canCreateProposal: true, @@ -93,7 +114,7 @@ export function EditWalletRules(props: Props) { 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, + tokenMintAddress: props.governanceAddress, //tokenMintDecimals: new BigNumber(0), //totalSupply: new BigNumber(1), vetoQuorumPercent: 100, @@ -111,6 +132,7 @@ export function EditWalletRules(props: Props) { const [minInstructionHoldupDays, setMinInstructionHoldupDays] = useState(0); const [submitting, setSubmitting] = useState(false); + const govData = useGovernanceByPubkeyQuery(props.governanceAddress).data?.result useEffect(() => { if (typeof window !== 'undefined') { @@ -119,13 +141,36 @@ export function EditWalletRules(props: Props) { }, [step]); useEffect(() => { - if (RE.isOk(result)) { - const data = result.data.realmByUrlId.governance; + if (govData && realm) { + const data = getGovernanceRules(realm.owner, govData, realm) + + const [walletAddress] = PublicKey.findProgramAddressSync([ + Buffer.from("native-treasury"), + govData.pubkey.toBuffer() + ], realm.owner) + data.communityTokenRules.votingPowerToCreateProposals = communityMint ? + data.communityTokenRules.votingPowerToCreateProposals.shiftedBy( + -communityMint.decimals + ) + : data.communityTokenRules.votingPowerToCreateProposals + + if (data.councilTokenRules) { + data.councilTokenRules.votingPowerToCreateProposals = councilMint ? + data.councilTokenRules.votingPowerToCreateProposals.shiftedBy( + -councilMint.decimals + ) + : data.councilTokenRules.votingPowerToCreateProposals + } + setCommunityRules(data.communityTokenRules); + setInitialCommunityRules(data.communityTokenRules); setCoolOffHours(data.coolOffHours); + setInitialCoolOffHours(data.coolOffHours); setCouncilRules(data.councilTokenRules); + setInitialCouncilRules(data.councilTokenRules); setDepositExemptProposalCount(data.depositExemptProposalCount); + setInitialDepositExemptProposalCount(data.depositExemptProposalCount); // maxVotingDays is actually misnamed on-chain. It should be `baseVotingDays` const baseVotingSeconds = hoursToSeconds(24 * data.maxVoteDays); @@ -133,8 +178,11 @@ export function EditWalletRules(props: Props) { const maxVotingSeconds = baseVotingSeconds + coolOffSeconds; setBaseVoteDays(data.maxVoteDays); + setInitialBaseVoteDays(data.maxVoteDays); + setMaxVoteDays(maxVotingSeconds / 60 / 60 / 24); setMinInstructionHoldupDays(data.minInstructionHoldupDays); + setInitialMinInstructionHoldupDays(data.minInstructionHoldupDays); if (!data.councilTokenRules) { setProposalVoteType('community'); @@ -143,244 +191,231 @@ export function EditWalletRules(props: Props) { } const walletName = - getAccountName(data.walletAddress) || + getAccountName(walletAddress) || getAccountName(data.governanceAddress) || - data.walletAddress.toBase58(); + walletAddress.toBase58(); const title = `Update Wallet Rules for “${walletName}”`; setProposalTitle(title); + setWalletName(walletName); + setWalletAddress(walletAddress); } - }, [result._tag]); - - return pipe( - result, - RE.match( - () =>
, - () =>
, - ({ realmByUrlId: { governance } }) => { - const walletName = - getAccountName(governance.walletAddress) || - getAccountName(governance.governanceAddress) || - governance.walletAddress.toBase58(); - - if (!wallet?.publicKey) { - return ( -
- - Edit Wallet Rules - {walletName} - - -
-
-
- Please sign in to edit wallet rules -
- for "{walletName}" -
-
-
+ }, [govData, realm, communityMint, councilMint]); + + return ( + !wallet?.publicKey ? +
+ + Edit Wallet Rules - {walletName} + + +
+
+
+ Please sign in to edit wallet rules +
+ for "{walletName}" +
+
+
+
+ : + +
+
+ + Edit Wallet Rules - {walletName} + + +
+
+ Step {stepNum(step)} of 2
- ); - } - - return ( -
-
- - Edit Wallet Rules - {walletName} - + {stepName(step)} +
+
+
+ {step === Step.Form && ( + <> + { + setCoolOffHours(coolOffHours); + const maxVotingSeconds = hoursToSeconds( + maxVoteDays * 24, + ); + const coolOffSeconds = hoursToSeconds(coolOffHours); + const baseVotingSeconds = + maxVotingSeconds - coolOffSeconds; + setBaseVoteDays(secondsToHours(baseVotingSeconds) / 24); + }} + onCouncilRulesChange={setCouncilRules} + onDepositExemptProposalCountChange={ + setDepositExemptProposalCount + } + onMaxVoteDaysChange={(votingDays) => { + setMaxVoteDays(votingDays); + const maxVotingSeconds = hoursToSeconds( + 24 * votingDays, + ); + const coolOffSeconds = hoursToSeconds(coolOffHours); + const baseVotingSeconds = + maxVotingSeconds - coolOffSeconds; + setBaseVoteDays(secondsToHours(baseVotingSeconds) / 24); + }} + onMinInstructionHoldupDaysChange={ + setMinInstructionHoldupDays + } /> - -
-
- Step {stepNum(step)} of 2 -
-
- {stepName(step)} -
-
-
- {step === Step.Form && ( - <> - { - setCoolOffHours(coolOffHours); - const maxVotingSeconds = hoursToSeconds( - maxVoteDays * 24, - ); - const coolOffSeconds = hoursToSeconds(coolOffHours); - const baseVotingSeconds = - maxVotingSeconds - coolOffSeconds; - setBaseVoteDays(secondsToHours(baseVotingSeconds) / 24); - }} - onCouncilRulesChange={setCouncilRules} - onDepositExemptProposalCountChange={ - setDepositExemptProposalCount - } - onMaxVoteDaysChange={(votingDays) => { - setMaxVoteDays(votingDays); - const maxVotingSeconds = hoursToSeconds( - 24 * votingDays, - ); - const coolOffSeconds = hoursToSeconds(coolOffHours); - const baseVotingSeconds = - maxVotingSeconds - coolOffSeconds; - setBaseVoteDays(secondsToHours(baseVotingSeconds) / 24); - }} - onMinInstructionHoldupDaysChange={ - setMinInstructionHoldupDays - } - /> -
- - setStep(Step.Summary)} - > - Continue - -
- - )} - {step === Step.Summary && ( - <> - -
- - { - if (!realm) throw new Error(); - - setSubmitting(true); - - const instruction = await createTransaction( - connection, - realm.owner, - governance.version, - governance.governanceAddress, - realm.pubkey, +
+ + setStep(Step.Summary)} + > + Continue + +
+ + )} + {step === Step.Summary && ( + <> + +
+ + { + if (!realm) throw new Error(); + + setSubmitting(true); + + const instruction = await createTransaction( + connection, + realm.owner, + version ?? 3, + props.governanceAddress, + realm.pubkey, + { + coolOffHours, + depositExemptProposalCount, + maxVoteDays, + minInstructionHoldupDays, + communityTokenRules: communityRules, + councilTokenRules: councilRules, + governanceAddress: props.governanceAddress, + version: version ?? 3, + walletAddress: walletAddress, + }, + ); + + try { + const proposalAddress = await propose({ + title: proposalTitle, + description: proposalDescription, + voteByCouncil: proposalVoteType === 'council', + instructionsData: [ { - coolOffHours, - depositExemptProposalCount, - maxVoteDays, - minInstructionHoldupDays, - communityTokenRules: communityRules, - councilTokenRules: councilRules, - governanceAddress: governance.governanceAddress, - version: governance.version, - walletAddress: governance.walletAddress, + data: createInstructionData(instruction), + holdUpTime: + 60 * + 60 * + 24 * + minInstructionHoldupDays, + prerequisiteInstructions: [], }, + ], + governance: props.governanceAddress, + }); + + if (proposalAddress) { + router.push( + fmtUrlWithCluster( + `/dao/${symbol}/proposal/${proposalAddress.toBase58()}`, + ), ); + } + } catch (e) { + notify({ + type: 'error', + message: + 'Could not create proposal: ' + String(e), + }); + } - try { - const proposalAddress = await propose({ - title: proposalTitle, - description: proposalDescription, - voteByCouncil: proposalVoteType === 'council', - instructionsData: [ - { - data: createInstructionData(instruction), - holdUpTime: - 60 * - 60 * - 24 * - governance.minInstructionHoldupDays, - prerequisiteInstructions: [], - }, - ], - governance: props.governanceAddress, - }); - - if (proposalAddress) { - router.push( - fmtUrlWithCluster( - `/dao/${symbol}/proposal/${proposalAddress.toBase58()}`, - ), - ); - } - } catch (e) { - notify({ - type: 'error', - message: - 'Could not create proposal: ' + String(e), - }); - } - - setSubmitting(false); - }} - > - - Create Proposal - -
- - )} -
-
+ setSubmitting(false); + }} + > + + Create Proposal + + + + )}
- ); - }, - ), +
+
); -} +} \ No newline at end of file diff --git a/hub/components/EditWalletRules/utils.ts b/hub/components/EditWalletRules/utils.ts new file mode 100644 index 000000000..14e03a6c7 --- /dev/null +++ b/hub/components/EditWalletRules/utils.ts @@ -0,0 +1,125 @@ +import { PublicKey } from "@metaplex-foundation/js"; +import { Governance, ProgramAccount, Realm, VoteThresholdType, VoteTipping } from "@solana/spl-governance"; +import BigNumber from "bignumber.js"; +import { secondsToHours } from "date-fns"; +import { MAX_NUM } from "./constants"; +import { GovernanceTokenType } from "@hub/types/GovernanceTokenType"; +import { isNil } from "lodash"; +import { GovernanceVoteTipping } from "@hub/types/GovernanceVoteTipping"; + +function voteTippingToGovernanceVoteTipping(voteTipping: VoteTipping | string) { + switch (voteTipping) { + case VoteTipping.Disabled: + return GovernanceVoteTipping.Disabled; + case VoteTipping.Early: + return GovernanceVoteTipping.Early; + case VoteTipping.Strict: + return GovernanceVoteTipping.Strict; + case 'DISABLED': + return GovernanceVoteTipping.Disabled; + case 'EARLY': + return GovernanceVoteTipping.Early; + case 'STRICT': + return GovernanceVoteTipping.Strict; + default: + return GovernanceVoteTipping.Disabled; + } +} + +/** + * Get the rules for a governance + */ +function getGovernanceRules( + programPublicKey: PublicKey, + governanceAccount: ProgramAccount, + realm: ProgramAccount +) { + const onChainConfig = governanceAccount.account.config; + + const councilMint = realm.account.config.councilMint?.toBase58(); + const communityMint = realm.account.communityMint.toBase58(); + + const rules = { + governanceAddress: governanceAccount.pubkey, + coolOffHours: secondsToHours(onChainConfig.votingCoolOffTime || 0), + councilTokenRules: councilMint + ? { + canCreateProposal: new BigNumber( + onChainConfig.minCouncilTokensToCreateProposal.toString(), + ).isLessThan(MAX_NUM), + canVeto: + onChainConfig.councilVetoVoteThreshold?.type === + VoteThresholdType.YesVotePercentage || + onChainConfig.councilVetoVoteThreshold?.type === VoteThresholdType.QuorumPercentage + ? true + : false, + canVote: + onChainConfig.councilVoteThreshold?.type === VoteThresholdType.Disabled + ? false + : true, + quorumPercent: onChainConfig.councilVoteThreshold + ? onChainConfig.councilVoteThreshold.type === VoteThresholdType.Disabled + ? 60 + : onChainConfig.councilVoteThreshold.value || 60 + : 60, + tokenMintAddress: new PublicKey(councilMint), + // tokenMintDecimals: new BigNumber(councilMintInfo.account.decimals), + tokenType: GovernanceTokenType.Council, + // totalSupply: new BigNumber(councilMintInfo.account.supply.toString()).shiftedBy( + // -councilMintInfo.account.decimals, + // ), + vetoQuorumPercent: onChainConfig.councilVetoVoteThreshold + ? onChainConfig.councilVetoVoteThreshold.type === VoteThresholdType.Disabled + ? 60 + : onChainConfig.councilVetoVoteThreshold.value || 60 + : 60, + voteTipping: voteTippingToGovernanceVoteTipping(onChainConfig.councilVoteTipping), + votingPowerToCreateProposals: new BigNumber( + onChainConfig.minCouncilTokensToCreateProposal.toString(), + ) + } + : null, + communityTokenRules: { + canCreateProposal: new BigNumber( + onChainConfig.minCommunityTokensToCreateProposal.toString(), + ).isLessThan(MAX_NUM), + canVeto: + onChainConfig.communityVetoVoteThreshold?.type === VoteThresholdType.YesVotePercentage || + onChainConfig.communityVetoVoteThreshold?.type === VoteThresholdType.QuorumPercentage + ? true + : false, + canVote: + onChainConfig.communityVoteThreshold?.type === VoteThresholdType.Disabled ? false : true, + quorumPercent: onChainConfig.communityVoteThreshold + ? onChainConfig.communityVoteThreshold.type === VoteThresholdType.Disabled + ? 60 + : onChainConfig.communityVoteThreshold.value || 60 + : 60, + tokenMintAddress: new PublicKey(communityMint), + // tokenMintDecimals: new BigNumber(communityMintInfo.account.decimals), + tokenType: GovernanceTokenType.Community, + // totalSupply: new BigNumber(communityMintInfo.account.supply.toString()).shiftedBy( + // -communityMintInfo.account.decimals, + // ), + vetoQuorumPercent: onChainConfig.communityVetoVoteThreshold + ? onChainConfig.communityVetoVoteThreshold.type === VoteThresholdType.Disabled + ? 60 + : onChainConfig.communityVetoVoteThreshold.value || 60 + : 60, + voteTipping: voteTippingToGovernanceVoteTipping(onChainConfig.communityVoteTipping), + votingPowerToCreateProposals: new BigNumber( + onChainConfig.minCommunityTokensToCreateProposal.toString(), + ) + }, + depositExemptProposalCount: isNil((onChainConfig as any)['depositExemptProposalCount']) + ? 10 + : (onChainConfig as any)['depositExemptProposalCount'], + maxVoteDays: secondsToHours(onChainConfig.baseVotingTime) / 24, + minInstructionHoldupDays: secondsToHours(onChainConfig.minInstructionHoldUpTime) / 24, + // version: programVersion, + }; + + return rules; +} + +export default getGovernanceRules; \ No newline at end of file diff --git a/hub/components/NewWallet/useGovernanceDefaults.ts b/hub/components/NewWallet/useGovernanceDefaults.ts index ec40448d6..a005c8b09 100644 --- a/hub/components/NewWallet/useGovernanceDefaults.ts +++ b/hub/components/NewWallet/useGovernanceDefaults.ts @@ -45,6 +45,7 @@ const configs2defaults = (configs: GovernanceConfig[]) => { const x: Omit = { communityTokenRules: { + tokenMintAddress: PublicKey.default, canCreateProposal: enableCommunityVote && !highestMinCommunityTokensToCreateProposal.eq(DISABLED_VOTER_WEIGHT), @@ -59,6 +60,7 @@ const configs2defaults = (configs: GovernanceConfig[]) => { voteTipping: GovernanceVoteTipping.Disabled, }, councilTokenRules: { + tokenMintAddress: PublicKey.default, canCreateProposal: enableCouncilVote && !highestMinCouncilTokensToCreateProposal.eq(DISABLED_VOTER_WEIGHT), diff --git a/hub/providers/Root.tsx b/hub/providers/Root.tsx index 1639f4558..ba8adbf00 100644 --- a/hub/providers/Root.tsx +++ b/hub/providers/Root.tsx @@ -4,8 +4,6 @@ import React from 'react'; import cx from '@hub/lib/cx'; import { ClusterProvider } from './Cluster'; -import { GraphQLProvider } from './GraphQL'; -import { JWTProvider } from './JWT'; import { ToastProvider } from './Toast'; import { UserPrefsProvider } from './UserPrefs'; import { WalletProvider } from './Wallet'; @@ -28,17 +26,13 @@ export function RootProvider(props: Props) { 'px-4', )} > - - {props.children} - - ); } diff --git a/pages/dao/[symbol]/editConfig.tsx b/pages/dao/[symbol]/editConfig.tsx index b414ba92b..5f7ec3c9a 100644 --- a/pages/dao/[symbol]/editConfig.tsx +++ b/pages/dao/[symbol]/editConfig.tsx @@ -1,7 +1,5 @@ import Head from 'next/head' -import { GraphQLProvider } from '@hub/providers/GraphQL' -import { JWTProvider } from '@hub/providers/JWT' import { EditRealmConfig } from '@hub/components/EditRealmConfig' import useSelectedRealmPubkey from '@hooks/selectedRealm/useSelectedRealmPubkey' @@ -10,23 +8,19 @@ export default function EditConfigPage() { return ( <> - - - {/* We should obviously eventually refactor the queries used by EditWalletRules so that these providers aren't needed */} - - Edit Org Config - - -
- {realmPk && ( - - )} -
-
-
+ {/* We should obviously eventually refactor the queries used by EditWalletRules so that these providers aren't needed */} + + Edit Org Config + + +
+ {realmPk && ( + + )} +
) } diff --git a/pages/dao/[symbol]/treasury/governance/[governanceId]/edit.tsx b/pages/dao/[symbol]/treasury/governance/[governanceId]/edit.tsx index 9127a7ea0..d46b72b5b 100644 --- a/pages/dao/[symbol]/treasury/governance/[governanceId]/edit.tsx +++ b/pages/dao/[symbol]/treasury/governance/[governanceId]/edit.tsx @@ -3,8 +3,6 @@ import { useRouter } from 'next/router' import { PublicKey } from '@solana/web3.js' import { EditWalletRules } from '@hub/components/EditWalletRules' -import { GraphQLProvider } from '@hub/providers/GraphQL' -import { JWTProvider } from '@hub/providers/JWT' import useSelectedRealmPubkey from '@hooks/selectedRealm/useSelectedRealmPubkey' export default function EditWallet() { @@ -21,23 +19,18 @@ export default function EditWallet() { governanceAddress && realmPk && ( <> - - - {/* We should obviously eventually refactor the queries used by EditWalletRules so that these providers aren't needed */} - - Edit Wallet Rules - - -
-
- -
-
-
-
+ + Edit Wallet Rules + + +
+
+ +
+
) )