Skip to content

Commit

Permalink
Merge pull request #51 from pyth-network/fix
Browse files Browse the repository at this point in the history
fix
  • Loading branch information
guibescos authored Nov 16, 2023
2 parents 69a6565 + 9756304 commit 1175c4a
Show file tree
Hide file tree
Showing 39 changed files with 1,192 additions and 423 deletions.
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.

205 changes: 176 additions & 29 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,8 @@ import {
VoteKind,
VoteType,
withPostChatMessage,
withCreateTokenOwnerRecord,
getVoteRecordAddress,
} from '@solana/spl-governance'
import { ProgramAccount } from '@solana/spl-governance'
import { RpcContext } from '@solana/spl-governance'
Expand All @@ -28,12 +30,17 @@ 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'
import { fetchVoteRecordByPubkey } from '@hooks/queries/voteRecord'

const getVetoTokenMint = (
proposal: ProgramAccount<Proposal>,
Expand All @@ -50,6 +57,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 +147,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 +168,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 +239,39 @@ 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(async (tokenOwnerRecordPk) => {
// Skip vote if already voted
const voteRecordPk = await getVoteRecordAddress(
realm.owner,
proposal.pubkey,
tokenOwnerRecordPk
)
const voteRecord = await fetchVoteRecordByPubkey(
connection,
voteRecordPk
)
if (voteRecord.found) return undefined

return createDelegatorVote({
connection,
realmPk: realm.pubkey,
proposalPk: proposal.pubkey,
tokenOwnerRecordPk,
userPk: walletPubkey,
vote,
})
})
)
).filter((x): x is NonNullable<typeof x> => x !== undefined)
: []

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

await withPostChatMessage(
postMessageIxs,
signers,
chatMessageSigners,
GOVERNANCE_CHAT_PROGRAM_ID,
programId,
realm.pubkey,
Expand All @@ -182,22 +301,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 +362,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 @@ -249,16 +394,18 @@ export async function castVote(
[...createCastNftVoteTicketIxs, ...createPostMessageTicketIxs],
1
)
const otherChunks = chunks(
[
...pluginCastVoteIxs,
...castVoteIxs,
...pluginPostMessageIxs,
...postMessageIxs,
],
2

// last element of pluginCastVoteIxs
const last = pluginCastVoteIxs[pluginCastVoteIxs.length - 1]
// everything except last element of pluginCastVoteIxs
const nftCountingChunks = pluginCastVoteIxs.slice(0, -1)
const voteChunk = [last, ...castVoteIxs] // the final nft-voter.CastNftVote instruction has to in same tx as the vote
const chunkedIxs = [...chunks(nftCountingChunks, 2), voteChunk].filter(
(x) => x.length > 0
)

// note that we are not chunking postMessageIxs, not yet supported (somehow)

const instructionsChunks = [
...createNftVoteTicketsChunks.map((txBatch, batchIdx) => {
return {
Expand All @@ -270,11 +417,11 @@ export async function castVote(
sequenceType: SequenceType.Parallel,
}
}),
...otherChunks.map((txBatch, batchIdx) => {
...chunkedIxs.map((txBatch, batchIdx) => {
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
24 changes: 14 additions & 10 deletions components/GovernancePower/GovernancePowerCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ const GovernancePowerTitle = () => {
</div>
)
}
/*
// TODO: refactor deposit components to their own generic DepositForRole component
const VanillaDeposit = ({ role }: { role: 'community' | 'council' }) => {
const { connection } = useConnection()
const realmPk = useSelectedRealmPubkey()
const { result: kind } = useAsync(async () => {
if (realmPk === undefined) return undefined
return determineVotingPowerType(connection, realmPk, role)
}, [connection, realmPk, role])
return kind === 'vanilla' ? <Deposit role={role} /> : <></>
} */

const GovernancePowerCard = () => {
const connected = useWalletOnePointOh()?.connected ?? false
Expand All @@ -38,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 @@ -58,10 +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>
) : (
<div className="flex flex-col gap-2">
{realmConfig?.account.communityTokenConfig.tokenType ===
Expand Down
Loading

0 comments on commit 1175c4a

Please sign in to comment.