Skip to content

Commit bf3d3c5

Browse files
authored
add Token Voter plugin (Mythic-Project#99)
* add Token Voter plugin * resolve merge conflicts 1 * update yarn.lock * fix test error
1 parent a5b7e3d commit bf3d3c5

File tree

17 files changed

+708
-110
lines changed

17 files changed

+708
-110
lines changed

BonkVotePlugin/client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export class BonkClient extends Client<BonkPlugin> {
2828
constructor(
2929
public program: Program<BonkPlugin>,
3030
tokenVoterProgram: Program<TokenVoter>,
31-
splTokenProgram: Program<SplTokenStaking>,
31+
splTokenProgram: Program<SplTokenStaking>
3232
) {
3333
super(program)
3434
this.tokenPlugin = tokenVoterProgram
@@ -219,4 +219,4 @@ export class BonkClient extends Client<BonkPlugin> {
219219
): Promise<TransactionInstruction | null> {
220220
return null
221221
}
222-
}
222+
}

BonkVotePlugin/token-client.ts

Lines changed: 0 additions & 95 deletions
This file was deleted.
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { SequenceType } from "@blockworks-foundation/mangolana/lib/globalTypes"
2+
import { BN, Program } from "@coral-xyz/anchor"
3+
import { TOKEN_2022_PROGRAM_ID, getAssociatedTokenAddressSync } from "@solana/spl-token-new"
4+
import { WalletContextState } from "@solana/wallet-adapter-react"
5+
import { Connection, PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY, TransactionInstruction } from "@solana/web3.js"
6+
import { sendTransactionsV3, txBatchesToInstructionSetWithSigners } from "@utils/sendTransactions"
7+
import { TokenVoter } from "BonkVotePlugin/token-type"
8+
import { tokenRegistrarKey, tokenVoterKey, tokenVwrKey } from "BonkVotePlugin/utils"
9+
import { SplGovernance } from "governance-idl-sdk"
10+
11+
async function addTokensHandler(
12+
connection: Connection,
13+
wallet: WalletContextState,
14+
ixClient: SplGovernance,
15+
realmAccount: PublicKey,
16+
tokenMint: PublicKey,
17+
depositMint: PublicKey,
18+
tokenAccount: PublicKey,
19+
userAccount: PublicKey,
20+
amount: BN,
21+
tokenVoterClient?: Program<TokenVoter> | undefined
22+
) {
23+
const ixs: TransactionInstruction[] = []
24+
25+
// Plugin Deposit
26+
if (tokenVoterClient) {
27+
const tokenOwnerRecordKey = ixClient.pda.tokenOwnerRecordAccount({
28+
realmAccount, governingTokenMintAccount: tokenMint, governingTokenOwner: userAccount
29+
}).publicKey
30+
31+
const tokenOwnerRecord = await connection.getAccountInfo(tokenOwnerRecordKey)
32+
33+
if (!tokenOwnerRecord) {
34+
const createTokenOwnerRecordIx = await ixClient.createTokenOwnerRecordInstruction(
35+
realmAccount, userAccount, tokenMint, userAccount
36+
)
37+
ixs.push(createTokenOwnerRecordIx)
38+
}
39+
40+
const registrarKey = tokenRegistrarKey(realmAccount, tokenMint, tokenVoterClient.programId)
41+
const [voterKey] = tokenVoterKey(realmAccount, tokenMint, userAccount, tokenVoterClient.programId)
42+
const [tokenVwr] = tokenVwrKey(realmAccount, tokenMint, userAccount, tokenVoterClient.programId)
43+
const vault = getAssociatedTokenAddressSync(depositMint, voterKey, true, TOKEN_2022_PROGRAM_ID)
44+
45+
try {
46+
await tokenVoterClient.account.voter.fetch(voterKey)
47+
} catch {
48+
const createVoterIx = await tokenVoterClient.methods.createVoterWeightRecord()
49+
.accounts({
50+
registrar: registrarKey,
51+
voter: voterKey,
52+
voterWeightRecord: tokenVwr,
53+
voterAuthority: userAccount,
54+
instructions: SYSVAR_INSTRUCTIONS_PUBKEY
55+
}).instruction()
56+
57+
ixs.push(createVoterIx)
58+
}
59+
60+
const depositEntryIndex = 0
61+
62+
const depositIx = await tokenVoterClient.methods.deposit(
63+
depositEntryIndex,
64+
amount
65+
).accounts({
66+
mint: depositMint,
67+
tokenOwnerRecord: tokenOwnerRecordKey,
68+
depositAuthority: wallet.publicKey ?? undefined,
69+
tokenProgram: TOKEN_2022_PROGRAM_ID,
70+
registrar: registrarKey,
71+
instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
72+
voter: voterKey,
73+
vault,
74+
voterWeightRecord: tokenVwr,
75+
depositToken: tokenAccount,
76+
}).instruction()
77+
78+
ixs.push(depositIx)
79+
} else {
80+
const vanillaDepositIx = await ixClient.depositGoverningTokensInstruction(
81+
realmAccount,
82+
tokenMint,
83+
tokenAccount,
84+
userAccount,
85+
userAccount,
86+
userAccount,
87+
amount,
88+
)
89+
90+
ixs.push(vanillaDepositIx)
91+
}
92+
93+
await sendTransactionsV3({
94+
connection,
95+
wallet,
96+
transactionInstructions: [{
97+
instructionsSet: txBatchesToInstructionSetWithSigners(ixs, []),
98+
sequenceType: SequenceType.Sequential
99+
}]
100+
})
101+
}
102+
103+
export default addTokensHandler
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { BN, Program } from "@coral-xyz/anchor"
2+
import { PublicKey } from "@metaplex-foundation/js"
3+
import { TOKEN_2022_PROGRAM_ID, createAssociatedTokenAccountInstruction, getAssociatedTokenAddressSync } from "@solana/spl-token-new"
4+
import { WalletContextState } from "@solana/wallet-adapter-react"
5+
import { Connection, TransactionInstruction } from "@solana/web3.js"
6+
import { SequenceType, sendTransactionsV3, txBatchesToInstructionSetWithSigners } from "@utils/sendTransactions"
7+
import { TokenVoter } from "BonkVotePlugin/token-type"
8+
import { tokenRegistrarKey, tokenVoterKey, tokenVwrKey } from "BonkVotePlugin/utils"
9+
import { SplGovernance, TokenOwnerRecord } from "governance-idl-sdk"
10+
11+
export async function withdrawTokensHandler(
12+
connection: Connection,
13+
wallet: WalletContextState,
14+
govClient: SplGovernance,
15+
realmAccount: PublicKey,
16+
tokenMint: PublicKey,
17+
depositMint: PublicKey,
18+
userAccount: PublicKey,
19+
amount: BN,
20+
tokenOwnerRecord: TokenOwnerRecord,
21+
tokenVoterClient?: Program<TokenVoter> | undefined,
22+
) {
23+
const ixs: TransactionInstruction[] = []
24+
25+
if (tokenOwnerRecord.outstandingProposalCount > 0) {
26+
throw new Error("The user has the outstanding proposals. Can't withdraw the tokens.")
27+
}
28+
29+
const userAta = getAssociatedTokenAddressSync(depositMint, userAccount, undefined, TOKEN_2022_PROGRAM_ID)
30+
const doesUserAtaExist = await connection.getAccountInfo(userAta)
31+
32+
if (!doesUserAtaExist) {
33+
const createAtaIx = createAssociatedTokenAccountInstruction(
34+
userAccount,
35+
userAta,
36+
userAccount,
37+
depositMint,
38+
TOKEN_2022_PROGRAM_ID
39+
)
40+
41+
ixs.push(createAtaIx)
42+
}
43+
44+
if (tokenVoterClient) {
45+
const registrarKey = tokenRegistrarKey(realmAccount, tokenMint, tokenVoterClient.programId)
46+
const [voterKey] = tokenVoterKey(realmAccount, tokenMint, userAccount, tokenVoterClient.programId)
47+
const [tokenVwr] = tokenVwrKey(realmAccount, tokenMint, userAccount, tokenVoterClient.programId)
48+
const vault = getAssociatedTokenAddressSync(depositMint, voterKey, true, TOKEN_2022_PROGRAM_ID)
49+
50+
const withdawIx = await tokenVoterClient.methods.withdraw(
51+
0,
52+
amount
53+
).accounts({
54+
registrar: registrarKey,
55+
voter: voterKey,
56+
voterAuthority: userAccount,
57+
tokenOwnerRecord: tokenOwnerRecord.publicKey,
58+
mint: depositMint,
59+
voterWeightRecord: tokenVwr,
60+
tokenProgram: TOKEN_2022_PROGRAM_ID,
61+
vault,
62+
destination: userAta
63+
})
64+
.instruction()
65+
66+
ixs.push(withdawIx)
67+
}
68+
69+
await sendTransactionsV3({
70+
connection,
71+
wallet,
72+
transactionInstructions: [{
73+
instructionsSet: txBatchesToInstructionSetWithSigners(ixs, []),
74+
sequenceType: SequenceType.Sequential
75+
}]
76+
})
77+
}

0 commit comments

Comments
 (0)