Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delegator rework for vanilla + ux adjustment #1912

Merged
merged 17 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ yarn-error.log*

# Sentry
.sentryclirc

.vscode/settings.json
12 changes: 0 additions & 12 deletions .vscode/settings.json

This file was deleted.

169 changes: 149 additions & 20 deletions actions/castVote.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
Connection,
Keypair,
PublicKey,
Transaction,
TransactionInstruction,
} from '@solana/web3.js'
import {
Expand All @@ -14,6 +14,7 @@ import {
VoteKind,
VoteType,
withPostChatMessage,
withCreateTokenOwnerRecord,
} from '@solana/spl-governance'
import { ProgramAccount } from '@solana/spl-governance'
import { RpcContext } from '@solana/spl-governance'
Expand All @@ -28,11 +29,15 @@ import {
SequenceType,
txBatchesToInstructionSetWithSigners,
} from '@utils/sendTransactions'
import { sendTransaction } from '@utils/send'
import { calcCostOfNftVote, checkHasEnoughSolToVote } from '@tools/nftVoteCalc'
import useNftProposalStore from 'NftVotePlugin/NftProposalStore'
import { HeliumVsrClient } from 'HeliumVotePlugin/sdk/client'
import { NftVoterClient } from '@utils/uiTypes/NftVoterClient'
import { fetchRealmByPubkey } from '@hooks/queries/realm'
import { fetchProposalByPubkeyQuery } from '@hooks/queries/proposal'
import { findPluginName } from '@hooks/queries/governancePower'
import { DELEGATOR_BATCH_VOTE_SUPPORT_BY_PLUGIN } from '@constants/flags'
import { fetchTokenOwnerRecordByPubkey } from '@hooks/queries/tokenOwnerRecord'
import { fetchProgramVersion } from '@hooks/queries/useProgramVersionQuery'

const getVetoTokenMint = (
Expand All @@ -50,6 +55,86 @@ const getVetoTokenMint = (
return vetoTokenMint
}

const createDelegatorVote = async ({
connection,
realmPk,
proposalPk,
tokenOwnerRecordPk,
userPk,
vote,
}: {
connection: Connection
realmPk: PublicKey
proposalPk: PublicKey
tokenOwnerRecordPk: PublicKey
userPk: PublicKey
vote: Vote
}) => {
//
const realm = (await fetchRealmByPubkey(connection, realmPk)).result
if (!realm) throw new Error()
const proposal = (await fetchProposalByPubkeyQuery(connection, proposalPk))
.result
if (!proposal) throw new Error()

const programVersion = await fetchProgramVersion(connection, realm.owner)

const castVoteIxs: TransactionInstruction[] = []
await withCastVote(
castVoteIxs,
realm.owner,
programVersion,
realm.pubkey,
proposal.account.governance,
proposal.pubkey,
proposal.account.tokenOwnerRecord,
tokenOwnerRecordPk,
userPk,
proposal.account.governingTokenMint,
vote,
userPk
//plugin?.voterWeightPk,
//plugin?.maxVoterWeightRecord
)
return castVoteIxs
}

const createTokenOwnerRecordIfNeeded = async ({
connection,
realmPk,
tokenOwnerRecordPk,
payer,
governingTokenMint,
}: {
connection: Connection
realmPk: PublicKey
tokenOwnerRecordPk: PublicKey
payer: PublicKey
governingTokenMint: PublicKey
}) => {
const realm = await fetchRealmByPubkey(connection, realmPk)
if (!realm.result) throw new Error()
const version = await fetchProgramVersion(connection, realm.result.owner)

const tokenOwnerRecord = await fetchTokenOwnerRecordByPubkey(
connection,
tokenOwnerRecordPk
)
if (tokenOwnerRecord.result) return []
// create token owner record
const ixs: TransactionInstruction[] = []
await withCreateTokenOwnerRecord(
ixs,
realm.result.owner,
version,
realmPk,
payer,
governingTokenMint,
payer
)
return ixs
}

export async function castVote(
{ connection, wallet, programId, walletPubkey }: RpcContext,
realm: ProgramAccount<Realm>,
Expand All @@ -60,9 +145,9 @@ export async function castVote(
votingPlugin?: VotingClient,
runAfterConfirmation?: (() => void) | null,
voteWeights?: number[],
_additionalTokenOwnerRecords?: []
additionalTokenOwnerRecords?: PublicKey[]
) {
const signers: Keypair[] = []
const chatMessageSigners: Keypair[] = []

const createCastNftVoteTicketIxs: TransactionInstruction[] = []
const createPostMessageTicketIxs: TransactionInstruction[] = []
Expand All @@ -81,7 +166,6 @@ export async function castVote(
tokenOwnerRecord,
createCastNftVoteTicketIxs
)
console.log('PLUGIN IXS', pluginCastVoteIxs)

const isMulti =
proposal.account.voteType !== VoteType.SINGLE_CHOICE &&
Expand Down Expand Up @@ -153,6 +237,25 @@ export async function castVote(
plugin?.maxVoterWeightRecord
)

const delegatorCastVoteAtoms =
additionalTokenOwnerRecords &&
DELEGATOR_BATCH_VOTE_SUPPORT_BY_PLUGIN[
findPluginName(votingPlugin?.client?.program.programId)
]
? await Promise.all(
additionalTokenOwnerRecords.map((tokenOwnerRecordPk) =>
createDelegatorVote({
connection,
realmPk: realm.pubkey,
proposalPk: proposal.pubkey,
tokenOwnerRecordPk,
userPk: walletPubkey,
vote,
})
)
)
: []

const pluginPostMessageIxs: TransactionInstruction[] = []
const postMessageIxs: TransactionInstruction[] = []
if (message) {
Expand All @@ -165,7 +268,7 @@ export async function castVote(

await withPostChatMessage(
postMessageIxs,
signers,
chatMessageSigners,
GOVERNANCE_CHAT_PROGRAM_ID,
programId,
realm.pubkey,
Expand All @@ -182,22 +285,48 @@ export async function castVote(

const isNftVoter = votingPlugin?.client instanceof NftVoterClient
const isHeliumVoter = votingPlugin?.client instanceof HeliumVsrClient
const tokenOwnerRecordIxs = await createTokenOwnerRecordIfNeeded({
connection,
realmPk: realm.pubkey,
tokenOwnerRecordPk: tokenOwnerRecord,
payer,
governingTokenMint: tokenMint,
})

if (!isNftVoter && !isHeliumVoter) {
const transaction = new Transaction()
transaction.add(
...[
...pluginCastVoteIxs,
...castVoteIxs,
...pluginPostMessageIxs,
...postMessageIxs,
]
const batch1 = [
...tokenOwnerRecordIxs,
...pluginCastVoteIxs,
...castVoteIxs,
...pluginPostMessageIxs,
...postMessageIxs,
]
// chunk size chosen conservatively. "Atoms" refers to atomic clusters of instructions (namely, updatevoterweight? + vote)
const delegatorBatches = chunks(delegatorCastVoteAtoms, 2).map((x) =>
x.flat()
)
const actions = [batch1, ...delegatorBatches].map((ixs) => ({
instructionsSet: ixs.map((ix) => ({
transactionInstruction: ix,
signers: chatMessageSigners.filter((kp) =>
ix.keys.find((key) => key.isSigner && key.pubkey.equals(kp.publicKey))
),
})),
sequenceType: SequenceType.Parallel,
}))

await sendTransaction({ transaction, wallet, connection, signers })
if (runAfterConfirmation) {
runAfterConfirmation()
}
await sendTransactionsV3({
connection,
wallet,
transactionInstructions: actions,
callbacks: {
afterAllTxConfirmed: () => {
if (runAfterConfirmation) {
runAfterConfirmation()
}
},
},
})
}

// we need to chunk instructions
Expand All @@ -217,7 +346,7 @@ export async function castVote(
return {
instructionsSet: txBatchesToInstructionSetWithSigners(
txBatch,
message ? [[], signers] : [],
message ? [[], chatMessageSigners] : [], // seeing signer related bugs when posting chat? This is likely culprit
batchIdx
),
sequenceType: SequenceType.Sequential,
Expand Down Expand Up @@ -276,7 +405,7 @@ export async function castVote(
return {
instructionsSet: txBatchesToInstructionSetWithSigners(
txBatch,
message ? [[], signers] : [],
message ? [[], chatMessageSigners] : [], // seeing signer related bugs when posting chat? This is likely culprit
batchIdx
),
sequenceType: SequenceType.Sequential,
Expand Down
27 changes: 3 additions & 24 deletions components/GovernancePower/GovernancePowerCard.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
import { ChevronRightIcon } from '@heroicons/react/solid'
import {
determineVotingPowerType,
useGovernancePowerAsync,
} from '@hooks/queries/governancePower'
import { useGovernancePowerAsync } from '@hooks/queries/governancePower'
import { useRealmConfigQuery } from '@hooks/queries/realmConfig'
import useSelectedRealmPubkey from '@hooks/selectedRealm/useSelectedRealmPubkey'
import useQueryContext from '@hooks/useQueryContext'
import useWalletOnePointOh from '@hooks/useWalletOnePointOh'
import { GoverningTokenType } from '@solana/spl-governance'
import { useConnection } from '@solana/wallet-adapter-react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useAsync } from 'react-async-hook'
import GovernancePowerForRole from './GovernancePowerForRole'
import { Deposit } from './Vanilla/Deposit'

const GovernancePowerTitle = () => {
const { symbol } = useRouter().query
Expand All @@ -36,7 +29,7 @@ const GovernancePowerTitle = () => {
</div>
)
}

/*
// TODO: refactor deposit components to their own generic DepositForRole component
const VanillaDeposit = ({ role }: { role: 'community' | 'council' }) => {
const { connection } = useConnection()
Expand All @@ -49,7 +42,7 @@ const VanillaDeposit = ({ role }: { role: 'community' | 'council' }) => {
}, [connection, realmPk, role])

return kind === 'vanilla' ? <Deposit role={role} /> : <></>
}
} */

const GovernancePowerCard = () => {
const connected = useWalletOnePointOh()?.connected ?? false
Expand All @@ -59,12 +52,6 @@ const GovernancePowerCard = () => {

const bothLoading = communityPower.loading && councilPower.loading

const bothZero =
communityPower.result !== undefined &&
councilPower.result !== undefined &&
communityPower.result.isZero() &&
councilPower.result.isZero()

const realmConfig = useRealmConfigQuery().data?.result

return (
Expand All @@ -79,14 +66,6 @@ const GovernancePowerCard = () => {
<div className="h-12 mb-4 rounded-lg animate-pulse bg-bkg-3" />
<div className="h-10 rounded-lg animate-pulse bg-bkg-3" />
</>
) : bothZero ? (
<div className={'text-xs text-white/50 mt-8'}>
You do not have any governance power in this dao
<div className="flex flex-col">
<VanillaDeposit role="community" />
<VanillaDeposit role="council" />
</div>
</div>
) : (
<div className="flex flex-col gap-2">
{realmConfig?.account.communityTokenConfig.tokenType ===
Expand Down
Loading
Loading