UNPKG

@tribecahq/tribeca-sdk

Version:

The TypeScript SDK for Tribeca, an open standard and toolkit for launching DAOs on Solana.

252 lines 10.2 kB
import { createMemoInstruction, TransactionEnvelope, } from "@saberhq/solana-contrib"; import { getATAAddress, getOrCreateATA, TOKEN_PROGRAM_ID, } from "@saberhq/token-utils"; import { SystemProgram } from "@solana/web3.js"; import invariant from "tiny-invariant"; import { TRIBECA_ADDRESSES } from "../../constants"; import { GovernorWrapper } from "../govern/governor"; import { findTokenRecordAddress } from "./pda"; /** * Helper methods around a Simple Voter electorate. */ export class SimpleVoterWrapper { constructor(sdk, electorate, governorKey) { this.sdk = sdk; this.electorate = electorate; this.governorKey = governorKey; this.program = sdk.programs.SimpleVoter; this.governor = new GovernorWrapper(sdk, governorKey); } get provider() { return this.sdk.provider; } static async load(sdk, electorateKey) { const electorateData = await sdk.programs.SimpleVoter.account.electorate.fetch(electorateKey); const wrapper = new SimpleVoterWrapper(sdk, electorateKey, electorateData.governor); wrapper.electorateData = electorateData; return wrapper; } async fetchProposalData(proposalKey) { return await this.sdk.programs.Govern.account.proposal.fetch(proposalKey); } async fetchTokenRecord(tokenRecordKey) { return await this.program.account.tokenRecord.fetch(tokenRecordKey); } async fetchVoterMetadata() { invariant(this.electorate, "electorate not set"); this.electorateData = await this.program.account.electorate.fetch(this.electorate); return this.electorateData; } async getOrCreateTokenRecord(authority = this.sdk.provider.wallet.publicKey) { invariant(this.electorate, "electorate not set"); const [tokenRecord] = await findTokenRecordAddress(authority, this.electorate); try { await this.program.account.tokenRecord.fetch(tokenRecord); return { tokenRecord, instruction: null }; } catch { return { tokenRecord, instruction: await this.initializeTokenRecordIx(authority), }; } } async depositTokens(amount, authority = this.sdk.provider.wallet.publicKey) { invariant(this.electorate, "electorate not set"); const { tokenRecord, instruction: initTokenRecordIx } = await this.getOrCreateTokenRecord(authority); const { govTokenAccount, govTokenVault, instructions } = await this._getOrCreateGovTokenATAsInternal(authority, tokenRecord); if (initTokenRecordIx) { instructions.push(initTokenRecordIx); } instructions.push(this.program.instruction.depositTokens(amount, { accounts: { authority, govTokenAccount, govTokenVault, tokenRecord, tokenProgram: TOKEN_PROGRAM_ID, }, })); return new TransactionEnvelope(this.sdk.provider, instructions); } async withdrawTokens(amount, authority) { invariant(this.electorate, "electorate not set"); invariant(this.electorateData, "electorate data not loaded"); const [tokenRecord] = await findTokenRecordAddress(authority, this.electorate); const { govTokenAccount, govTokenVault, instructions } = await this._getOrCreateGovTokenATAsInternal(authority, tokenRecord); instructions.push(this.program.instruction.withdrawTokens(amount, { accounts: { authority, govTokenAccount, govTokenVault, tokenRecord, tokenProgram: TOKEN_PROGRAM_ID, }, })); return new TransactionEnvelope(this.sdk.provider, instructions); } activateProposal(proposal) { const ix = this.program.instruction.activateProposal({ accounts: { electorate: this.electorate, governor: this.governorKey, proposal, governProgram: TRIBECA_ADDRESSES.Govern, }, }); return new TransactionEnvelope(this.sdk.provider, [ix]); } async castVotes(args) { invariant(this.electorateData, "electorate data not loaded"); const { authority = this.provider.wallet.publicKey, proposal, voteSide, reason, } = args; const ixs = []; const { tokenRecord, instruction: tokenRecordIx } = await this.getOrCreateTokenRecord(authority); if (tokenRecordIx) { ixs.push(tokenRecordIx); } const { voteKey, instruction: createVoteIX } = await this.governor.getOrCreateVote({ proposal, voter: authority, }); if (createVoteIX) { ixs.push(createVoteIX); } ixs.push(this.program.instruction.castVotes(voteSide, { accounts: { electorate: this.electorate, authority: authority !== null && authority !== void 0 ? authority : this.sdk.provider.wallet.publicKey, proposal, tokenRecord, vote: voteKey, tribeca: this._genTribecaContext(), }, })); if (reason) { ixs.push(createMemoInstruction(reason, [authority])); } return new TransactionEnvelope(this.sdk.provider, ixs); } async withdrawVotes(proposal, authority = this.sdk.provider.wallet.publicKey) { invariant(this.electorateData, "electorate data not loaded"); const ixs = []; const { tokenRecord, instruction: tokenRecordIx } = await this.getOrCreateTokenRecord(authority); if (tokenRecordIx) { ixs.push(tokenRecordIx); } const { voteKey, instruction: voteRecieptIx } = await this.governor.getOrCreateVote({ proposal, }); if (voteRecieptIx) { ixs.push(voteRecieptIx); } ixs.push(this.program.instruction.withdrawVotes({ accounts: { electorate: this.electorate, authority, proposal, tokenRecord, vote: voteKey, tribeca: this._genTribecaContext(), }, })); return new TransactionEnvelope(this.sdk.provider, ixs); } async depositTokenAndCastVote(args) { const { amount, proposal, voteSide, reason, authority = this.provider.wallet.publicKey, } = args; const { tokenRecord, instruction: tokenRecordIx } = await this.getOrCreateTokenRecord(authority); const { govTokenAccount, govTokenVault, instructions } = await this._getOrCreateGovTokenATAsInternal(authority, tokenRecord); if (tokenRecordIx) { instructions.push(tokenRecordIx); } const { voteKey, instruction: createVoteIX } = await this.governor.getOrCreateVote({ proposal, voter: authority, }); if (createVoteIX) { instructions.push(createVoteIX); } instructions.push(this.program.instruction.depositTokens(amount, { accounts: { authority, govTokenAccount, govTokenVault, tokenRecord, tokenProgram: TOKEN_PROGRAM_ID, }, })); return new TransactionEnvelope(this.sdk.provider, instructions.concat(this._genCastVotesInstructions({ proposal, authority, voteSide, reason, vote: voteKey, tokenRecord, }))); } async initializeTokenRecordIx(authority = this.sdk.provider.wallet.publicKey) { invariant(this.electorate, "electorate not set"); invariant(this.electorateData, "electorate data not loaded"); const [tokenRecord, bump] = await findTokenRecordAddress(authority, this.electorate); return this.program.instruction.initializeTokenRecord(bump, { accounts: { authority, tokenRecord, electorate: this.electorate, govTokenVault: await getATAAddress({ mint: this.electorateData.govTokenMint, owner: tokenRecord, }), payer: this.sdk.provider.wallet.publicKey, systemProgram: SystemProgram.programId, }, }); } _genCastVotesInstructions(args) { var _a; const { proposal, voteSide, reason, tokenRecord, vote } = args; const authority = (_a = args.authority) !== null && _a !== void 0 ? _a : this.provider.wallet.publicKey; const ixs = []; ixs.push(this.program.instruction.castVotes(voteSide, { accounts: { electorate: this.electorate, authority, proposal, tokenRecord, vote, tribeca: this._genTribecaContext(), }, })); if (reason) { ixs.push(createMemoInstruction(reason, [authority])); } return ixs; } _genTribecaContext() { invariant(this.electorateData, "electrate data not loaded"); return { governor: this.electorateData.governor, program: TRIBECA_ADDRESSES.Govern, }; } async _getOrCreateGovTokenATAsInternal(authority, tokenRecord) { invariant(this.electorateData, "electorate data not loaded"); const { provider } = this.sdk; const { address: govTokenAccount, instruction: ix1 } = await getOrCreateATA({ provider, mint: this.electorateData.govTokenMint, owner: authority, payer: authority, }); const { address: govTokenVault, instruction: ix2 } = await getOrCreateATA({ provider, mint: this.electorateData.govTokenMint, owner: tokenRecord, payer: authority, }); return { govTokenAccount, govTokenVault, instructions: [ix1, ix2].filter((ix) => !!ix), }; } } //# sourceMappingURL=electorate.js.map