From 3447801f5b786bcbc185b80a20d2a4c99c393b07 Mon Sep 17 00:00:00 2001 From: Austin Milt Date: Thu, 11 Aug 2022 09:03:42 -0600 Subject: [PATCH 01/17] create governining token holding account instruction --- README.md | 23 +- ...create_governance_token_holding_account.rs | 82 +++ programs/nft-voter/src/instructions/mod.rs | 3 + programs/nft-voter/src/lib.rs | 7 + programs/nft-voter/src/state/registrar.rs | 3 + ...create_governance_token_holding_account.rs | 156 +++++ .../tests/program_test/nft_voter_test.rs | 78 +++ .../tests/program_test/token_metadata_test.rs | 1 + run-release.sh | 3 +- src/gateway/gateway.ts | 318 +++++++++- src/nftVoter/nft_voter.ts | 594 ++++++++++++++++-- src/realmVoter/realm_voter.ts | 386 +++++++++++- 12 files changed, 1562 insertions(+), 92 deletions(-) create mode 100644 programs/nft-voter/src/instructions/create_governance_token_holding_account.rs create mode 100644 programs/nft-voter/tests/create_governance_token_holding_account.rs diff --git a/README.md b/README.md index d669dce5..b35a3f97 100644 --- a/README.md +++ b/README.md @@ -1 +1,22 @@ -# governance-program-library \ No newline at end of file +# governance-program-library + +### Test + +Unit tests contained within all projects can be run with: +```bash +$ cargo test # <-- runs host-based tests +$ cargo test-bpf # <-- runs BPF program tests +``` + +To run a specific program's tests, such as for NFT Voter Plugin: +```bash +$ cd programs/nft-voter +$ cargo test # <-- runs host-based tests +$ cargo test-bpf # <-- runs BPF program tests +``` + +To run a specific test, give the test name (doesnt include the file name) +```bash +$ cargo test test_create_governance_token_holding_account -- --exact # <-- runs host-based tests +$ cargo test-bpf test_create_governance_token_holding_account -- --exact # <-- runs BPF program tests +``` \ No newline at end of file diff --git a/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs b/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs new file mode 100644 index 00000000..54d968f3 --- /dev/null +++ b/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs @@ -0,0 +1,82 @@ +use anchor_lang::prelude::*; +use anchor_spl::{ + associated_token::AssociatedToken, + token::{Mint, Token, TokenAccount}, +}; +use spl_governance::state::realm; + +/// Creates a governance token holding account for a given NFT to boost its voting power +/// This instruction should only be executed once per realm/governing_token_mint/nft +/// to create the account +#[derive(Accounts)] +pub struct CreateGovernanceTokenHoldingAccount<'info> { + //TODO add docs + #[account( + init, + seeds = [ b"nft-power-holding-account".as_ref(), + realm.key().as_ref(), + realm_governing_token_mint.key().as_ref(), + nft.key().as_ref()], + bump, + payer = payer, + token::mint = realm_governing_token_mint, + token::authority = governance_program_id + )] + pub holding_account_info: Account<'info, TokenAccount>, + + /// The program id of the spl-governance program the realm belongs to + /// CHECK: Can be any instance of spl-governance and it's not known at the compilation time + #[account(executable)] + pub governance_program_id: UncheckedAccount<'info>, + + /// CHECK: Owned by spl-governance instance specified in governance_program_id + #[account(owner = governance_program_id.key())] + pub realm: UncheckedAccount<'info>, + + /// Either the realm community mint or the council mint. + pub realm_governing_token_mint: Account<'info, Mint>, + + #[account(mut)] + pub payer: Signer<'info>, + + //TODO add constraint that the nft is the one configured for a realm collection + pub nft: Account<'info, Mint>, + + pub associated_token_program: Program<'info, AssociatedToken>, + pub token_program: Program<'info, Token>, + pub system_program: Program<'info, System>, + pub rent: Sysvar<'info, Rent>, +} + +/// Deposits tokens into the holding account for a given NFT to boost its voting power +pub fn create_governance_token_holding_account( + ctx: Context, +) -> Result<()> { + //TODO extract reused stuff to variables + + // Deserialize the Realm to validate it + let _realm = realm::get_realm_data_for_governing_token_mint( + &ctx.accounts.governance_program_id.key(), + &ctx.accounts.realm, + &ctx.accounts.realm_governing_token_mint.key(), + )?; + + // create_spl_token_account_signed( + // &ctx.accounts.payer, + // &ctx.accounts.realm_governing_token_mint.to_account_info(), + // &get_governing_token_holding_address_seeds( + // &ctx.accounts.realm.key(), + // &ctx.accounts.realm_governing_token_mint.key(), + // &ctx.accounts.nft.key(), + // ), + // &ctx.accounts.realm_governing_token_mint.to_account_info(), + // &ctx.accounts.realm, + // &ctx.accounts.governance_program_id.key(), + // &ctx.accounts.system_program, + // &ctx.accounts.realm_governing_token_mint.to_account_info(), + // &ctx.accounts.rent.to_account_info(), + // &ctx.accounts.rent + // )?; + + Ok(()) +} diff --git a/programs/nft-voter/src/instructions/mod.rs b/programs/nft-voter/src/instructions/mod.rs index 518677c5..e5c26c6e 100644 --- a/programs/nft-voter/src/instructions/mod.rs +++ b/programs/nft-voter/src/instructions/mod.rs @@ -18,3 +18,6 @@ mod relinquish_nft_vote; pub use cast_nft_vote::*; mod cast_nft_vote; + +pub use create_governance_token_holding_account::*; +mod create_governance_token_holding_account; diff --git a/programs/nft-voter/src/lib.rs b/programs/nft-voter/src/lib.rs index fcd118fe..9e10b6f3 100644 --- a/programs/nft-voter/src/lib.rs +++ b/programs/nft-voter/src/lib.rs @@ -61,6 +61,13 @@ pub mod nft_voter { log_version(); instructions::cast_nft_vote(ctx, proposal) } + + pub fn create_governance_token_holding_account( + ctx: Context, + ) -> Result<()> { + log_version(); + instructions::create_governance_token_holding_account(ctx) + } } fn log_version() { diff --git a/programs/nft-voter/src/state/registrar.rs b/programs/nft-voter/src/state/registrar.rs index 990b64ff..576e778f 100644 --- a/programs/nft-voter/src/state/registrar.rs +++ b/programs/nft-voter/src/state/registrar.rs @@ -107,6 +107,9 @@ pub fn resolve_nft_vote_weight_and_mint( let collection_config = registrar.get_collection_config(collection.key)?; + //TODO get the token holding account assigned to this NFT + // TODO compute voter weight based on collection_config.weight and holding account balance + Ok((collection_config.weight, nft_mint)) } diff --git a/programs/nft-voter/tests/create_governance_token_holding_account.rs b/programs/nft-voter/tests/create_governance_token_holding_account.rs new file mode 100644 index 00000000..bc25fbb6 --- /dev/null +++ b/programs/nft-voter/tests/create_governance_token_holding_account.rs @@ -0,0 +1,156 @@ +use program_test::nft_voter_test::NftVoterTest; +use solana_program::{program_option::COption}; +use solana_program_test::*; +use solana_sdk::transport::TransportError; +use spl_token::state::AccountState; + +mod program_test; + +#[tokio::test] +async fn test_create_governance_token_holding_account() -> Result<(), TransportError> { + // Arrange + let mut nft_voter_test = NftVoterTest::start_new().await; + + let realm_cookie = nft_voter_test.governance.with_realm().await?; + + let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + + let voter_cookie = nft_voter_test.bench.with_wallet().await; + + let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; + + let nft_cookie = nft_voter_test + .token_metadata + .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .await?; + + // Act + let governance_token_holding_account_cookie = nft_voter_test + .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) + .await?; + + // Assert + assert_eq_formatted( + 0, + governance_token_holding_account_cookie.account.amount, + "amount", + ); + assert_eq_formatted( + COption::None, + governance_token_holding_account_cookie.account.delegate, + "delegate", + ); + assert_eq_formatted( + 0, + governance_token_holding_account_cookie.account.delegated_amount, + "delegated_amount", + ); + assert_eq_formatted( + COption::None, + governance_token_holding_account_cookie.account.close_authority, + "close_authority", + ); + assert_eq_formatted( + realm_cookie.community_mint_cookie.address, + governance_token_holding_account_cookie.account.mint, + "mint", + ); + assert_eq_formatted( + registrar_cookie.account.governance_program_id, + governance_token_holding_account_cookie.account.owner, + "owner", + ); + assert_eq_formatted( + AccountState::Initialized, + governance_token_holding_account_cookie.account.state, + "state", + ); + + Ok(()) +} + + +#[tokio::test] +async fn test_create_governance_token_holding_account_nft_is_not_part_of_configured_collection_errors() -> Result<(), TransportError> { + // Arrange + let mut nft_voter_test = NftVoterTest::start_new().await; + + let realm_cookie = nft_voter_test.governance.with_realm().await?; + + let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + + let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; + let max_voter_weight_record = nft_voter_test.with_max_voter_weight_record(®istrar_cookie).await?; + assert_eq!(0, registrar_cookie.account.collection_configs.len(), "Unexpected collection already configured for registrar"); + nft_voter_test.with_collection(®istrar_cookie, &nft_collection_cookie, &max_voter_weight_record, None).await?; + let registrar_updated = nft_voter_test.get_registrar_account(®istrar_cookie.address).await; + assert_eq!(1, registrar_updated.collection_configs.len(), "Unable to add collection to registrar"); + + let voter_cookie = nft_voter_test.bench.with_wallet().await; + + let nft_cookie = nft_voter_test + .token_metadata + .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .await?; + + // Act + //TODO add validation to the instruction and/or method and expect it to throw + let error = nft_voter_test + .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) + .await + .err(); + + // Assert + //TODO + // assert!(error.is_some()); + + Ok(()) +} + + +#[tokio::test] +async fn test_create_governance_token_holding_account_already_exists_errors() -> Result<(), TransportError> { + // Arrange + let mut nft_voter_test = NftVoterTest::start_new().await; + + let realm_cookie = nft_voter_test.governance.with_realm().await?; + + let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + + let voter_cookie = nft_voter_test.bench.with_wallet().await; + + let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; + + let nft_cookie = nft_voter_test + .token_metadata + .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .await?; + + // Act + nft_voter_test + .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) + .await?; + + let error = nft_voter_test + .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) + .await + .err(); + + // Assert + assert!(error.is_some()); + + Ok(()) +} + + +fn assert_eq_formatted( + expected: T, + actual: T, + name: &str, +) -> () { + assert_eq!( + expected, actual, + "{} not equal: expected {:?} but got {:?}", + name, expected, actual + ); +} diff --git a/programs/nft-voter/tests/program_test/nft_voter_test.rs b/programs/nft-voter/tests/program_test/nft_voter_test.rs index 75c9e11e..bdb3fcb9 100644 --- a/programs/nft-voter/tests/program_test/nft_voter_test.rs +++ b/programs/nft-voter/tests/program_test/nft_voter_test.rs @@ -1,11 +1,14 @@ +use std::str::FromStr; use std::sync::Arc; use anchor_lang::prelude::{AccountMeta, Pubkey}; +use anchor_spl::token::TokenAccount; use gpl_nft_voter::state::max_voter_weight_record::{ get_max_voter_weight_record_address, MaxVoterWeightRecord, }; use gpl_nft_voter::state::*; +use solana_program::pubkey::ParsePubkeyError; use solana_sdk::transport::TransportError; use spl_governance::instruction::cast_vote; use spl_governance::state::vote_record::{self, Vote, VoteChoice}; @@ -18,6 +21,7 @@ use solana_program_test::ProgramTest; use solana_sdk::instruction::Instruction; use solana_sdk::signature::Keypair; use solana_sdk::signer::Signer; +use spl_token::state::Account; use crate::program_test::governance_test::GovernanceTest; use crate::program_test::program_test_bench::ProgramTestBench; @@ -61,6 +65,11 @@ impl Default for ConfigureCollectionArgs { } } +pub struct GovernanceTokenHoldingAccountCookie { + pub address: Pubkey, + pub account: TokenAccount, +} + #[derive(Debug, PartialEq)] pub struct NftVoteRecordCookie { pub address: Pubkey, @@ -592,4 +601,73 @@ impl NftVoterTest { pub async fn get_voter_weight_record(&self, voter_weight_record: &Pubkey) -> VoterWeightRecord { self.bench.get_anchor_account(*voter_weight_record).await } + + #[allow(dead_code)] + pub async fn with_governance_token_holding_account( + &self, + registrar_cookie: &RegistrarCookie, + nft_cookie: &NftCookie, + ) -> Result { + self.with_governance_token_holding_account_using_ix( + registrar_cookie, + nft_cookie, + NopOverride, + ) + .await + } + + #[allow(dead_code)] + pub async fn with_governance_token_holding_account_using_ix( + &self, + registrar_cookie: &RegistrarCookie, + nft_cookie: &NftCookie, + instruction_override: F, + ) -> Result { + let (holding_account_key, _) = Pubkey::find_program_address( + &[ + b"nft-power-holding-account".as_ref(), + registrar_cookie.account.realm.as_ref(), + registrar_cookie.account.governing_token_mint.as_ref(), + nft_cookie.mint_cookie.address.as_ref(), + ], + &gpl_nft_voter::id(), + ); + + let data = anchor_lang::InstructionData::data( + &gpl_nft_voter::instruction::CreateGovernanceTokenHoldingAccount {}, + ); + + let accounts = gpl_nft_voter::accounts::CreateGovernanceTokenHoldingAccount { + governance_program_id: self.governance.program_id, + realm: registrar_cookie.account.realm, + realm_governing_token_mint: registrar_cookie.account.governing_token_mint, + payer: self.bench.payer.pubkey(), + system_program: solana_sdk::system_program::id(), + holding_account_info: holding_account_key, + nft: nft_cookie.mint_cookie.address, + associated_token_program: anchor_spl::associated_token::ID, + token_program: spl_token::id(), + rent: Pubkey::from_str("SysvarRent111111111111111111111111111111111") + .map_err(|e| TransportError::Custom(e.to_string()))?, + }; + + let mut create_governing_token_holding_account_ix = Instruction { + program_id: gpl_nft_voter::id(), + accounts: anchor_lang::ToAccountMetas::to_account_metas(&accounts, None), + data, + }; + + instruction_override(&mut create_governing_token_holding_account_ix); + + self.bench + .process_transaction(&[create_governing_token_holding_account_ix], Some(&[&self.bench.payer])) + .await?; + + let account = self.bench.get_anchor_account(holding_account_key).await; + + Ok(GovernanceTokenHoldingAccountCookie { + address: holding_account_key, + account, + }) + } } diff --git a/programs/nft-voter/tests/program_test/token_metadata_test.rs b/programs/nft-voter/tests/program_test/token_metadata_test.rs index 3ac0f97f..a99eb127 100644 --- a/programs/nft-voter/tests/program_test/token_metadata_test.rs +++ b/programs/nft-voter/tests/program_test/token_metadata_test.rs @@ -13,6 +13,7 @@ pub struct NftCookie { pub mint_cookie: MintCookie, } +#[derive(Debug, PartialEq)] pub struct NftCollectionCookie { pub mint: Pubkey, pub metadata: Pubkey, diff --git a/run-release.sh b/run-release.sh index 70a8c5f7..8057f889 100755 --- a/run-release.sh +++ b/run-release.sh @@ -24,7 +24,8 @@ anchor build cp ./target/types/nft_voter.ts src/nftVoter/nft_voter.ts cp ./target/types/gateway.ts src/gateway/gateway.ts cp ./target/types/realm_voter.ts src/realmVoter/realm_voter.ts -yarn clean && yarn build && yarn publish +yarn clean && yarn build +# yarn clean && yarn build && yarn publish echo echo Remember to commit and push the version update as well as the changes diff --git a/src/gateway/gateway.ts b/src/gateway/gateway.ts index a55c5854..bc5bbe82 100644 --- a/src/gateway/gateway.ts +++ b/src/gateway/gateway.ts @@ -8,32 +8,63 @@ export type Gateway = { { "name": "registrar", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "The Gateway Registrar", + "There can only be a single registrar per governance Realm and governing mint of the Realm" + ] }, { "name": "governanceProgramId", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The program id of the spl-governance program the realm belongs to" + ] }, { "name": "realm", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "An spl-governance Realm", + "", + "Realm is validated in the instruction:", + "- Realm is owned by the governance_program_id", + "- governing_token_mint must be the community or council mint", + "- realm_authority is realm.authority" + ] }, { "name": "governingTokenMint", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "Either the realm community mint or the council mint.", + "It must match Realm.community_mint or Realm.config.council_mint", + "", + "Note: Once the Civic Pass plugin is enabled the governing_token_mint is used only as identity", + "for the voting population and the tokens of that are no longer used" + ] }, { "name": "realmAuthority", "isMut": false, - "isSigner": true + "isSigner": true, + "docs": [ + "realm_authority must sign and match Realm.authority" + ] }, { "name": "gatekeeperNetwork", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The Identity.com Gateway gatekeeper network that this realm uses", + "(See the registry struct docs for details).", + "Gateway Token belongs to this gatekeeper network, so passing a particular key here is", + "essentially saying \"We trust this gatekeeper network\"." + ] }, { "name": "payer", @@ -59,22 +90,41 @@ export type Gateway = { { "name": "registrar", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "The Gateway Plugin Registrar to be updated" + ] }, { "name": "realm", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "An spl-governance Realm", + "", + "Realm is validated in the instruction:", + "- Realm is owned by the governance_program_id", + "- realm_authority is realm.authority" + ] }, { "name": "realmAuthority", "isMut": false, - "isSigner": true + "isSigner": true, + "docs": [ + "realm_authority must sign and match Realm.authority" + ] }, { "name": "gatekeeperNetwork", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The new Identity.com Gateway gatekeeper network", + "(See the registry struct docs for details).", + "Gateway Token belongs to this gatekeeper network, so passing a particular key here is", + "essentially saying \"We trust this gatekeeper network\"." + ] } ], "args": [ @@ -121,17 +171,28 @@ export type Gateway = { { "name": "registrar", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The Gateway Registrar" + ] }, { "name": "inputVoterWeight", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "An account that is either of type TokenOwnerRecordV2 or VoterWeightRecord", + "depending on whether the registrar includes a predecessor or not" + ] }, { "name": "gatewayToken", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "A gateway token from the gatekeeper network in the registrar.", + "Proves that the holder is permitted to take an action." + ] }, { "name": "voterWeightRecord", @@ -145,33 +206,59 @@ export type Gateway = { "accounts": [ { "name": "registrar", + "docs": [ + "Registrar which stores Civic Pass voting configuration for the given Realm" + ], "type": { "kind": "struct", "fields": [ { "name": "governanceProgramId", + "docs": [ + "spl-governance program the Realm belongs to" + ], "type": "publicKey" }, { "name": "realm", + "docs": [ + "Realm of the Registrar" + ], "type": "publicKey" }, { "name": "governingTokenMint", + "docs": [ + "Governing token mint the Registrar is for", + "It can either be the Community or the Council mint of the Realm", + "When the plugin is used the mint is only used as identity of the governing power (voting population)", + "and the actual token of the mint is not used" + ], "type": "publicKey" }, { "name": "gatekeeperNetwork", + "docs": [ + "The Gatekeeper Network represents the \"Pass Type\" that a", + "user must present." + ], "type": "publicKey" }, { "name": "previousVoterWeightPluginProgramId", + "docs": [ + "If the plugin is one in a sequence, this is the previous plugin program ID", + "If set, then update_voter_weight_record will expect a voter_weight_record owned by this program" + ], "type": { "option": "publicKey" } }, { "name": "reserved", + "docs": [ + "Reserved for future upgrades" + ], "type": { "array": [ "u8", @@ -184,33 +271,66 @@ export type Gateway = { }, { "name": "voterWeightRecord", + "docs": [ + "VoterWeightRecord account as defined in spl-governance-addin-api", + "It's redefined here without account_discriminator for Anchor to treat it as native account", + "", + "The account is used as an api interface to provide voting power to the governance program from external addin contracts" + ], "type": { "kind": "struct", "fields": [ { "name": "realm", + "docs": [ + "The Realm the VoterWeightRecord belongs to" + ], "type": "publicKey" }, { "name": "governingTokenMint", + "docs": [ + "Governing Token Mint the VoterWeightRecord is associated with", + "Note: The addin can take deposits of any tokens and is not restricted to the community or council tokens only" + ], "type": "publicKey" }, { "name": "governingTokenOwner", + "docs": [ + "The owner of the governing token and voter", + "This is the actual owner (voter) and corresponds to TokenOwnerRecord.governing_token_owner" + ], "type": "publicKey" }, { "name": "voterWeight", + "docs": [ + "Voter's weight", + "The weight of the voter provided by the addin for the given realm, governing_token_mint and governing_token_owner (voter)" + ], "type": "u64" }, { "name": "voterWeightExpiry", + "docs": [ + "The slot when the voting weight expires", + "It should be set to None if the weight never expires", + "If the voter weight decays with time, for example for time locked based weights, then the expiry must be set", + "As a common pattern Revise instruction to update the weight should be invoked before governance instruction within the same transaction", + "and the expiry set to the current slot to provide up to date weight" + ], "type": { "option": "u64" } }, { "name": "weightAction", + "docs": [ + "The governance action the voter's weight pertains to", + "It allows to provided voter's weight specific to the particular action the weight is evaluated for", + "When the action is provided then the governance program asserts the executing action is the same as specified by the addin" + ], "type": { "option": { "defined": "VoterWeightAction" @@ -219,12 +339,21 @@ export type Gateway = { }, { "name": "weightActionTarget", + "docs": [ + "The target the voter's weight action pertains to", + "It allows to provided voter's weight specific to the target the weight is evaluated for", + "For example when addin supplies weight to vote on a particular proposal then it must specify the proposal as the action target", + "When the target is provided then the governance program asserts the target is the same as specified by the addin" + ], "type": { "option": "publicKey" } }, { "name": "reserved", + "docs": [ + "Reserved space for future versions" + ], "type": { "array": [ "u8", @@ -263,6 +392,10 @@ export type Gateway = { }, { "name": "VoterWeightAction", + "docs": [ + "VoterWeightAction enum as defined in spl-governance-addin-api", + "It's redefined here for Anchor to export it to IDL" + ], "type": { "kind": "enum", "variants": [ @@ -354,32 +487,63 @@ export const IDL: Gateway = { { "name": "registrar", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "The Gateway Registrar", + "There can only be a single registrar per governance Realm and governing mint of the Realm" + ] }, { "name": "governanceProgramId", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The program id of the spl-governance program the realm belongs to" + ] }, { "name": "realm", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "An spl-governance Realm", + "", + "Realm is validated in the instruction:", + "- Realm is owned by the governance_program_id", + "- governing_token_mint must be the community or council mint", + "- realm_authority is realm.authority" + ] }, { "name": "governingTokenMint", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "Either the realm community mint or the council mint.", + "It must match Realm.community_mint or Realm.config.council_mint", + "", + "Note: Once the Civic Pass plugin is enabled the governing_token_mint is used only as identity", + "for the voting population and the tokens of that are no longer used" + ] }, { "name": "realmAuthority", "isMut": false, - "isSigner": true + "isSigner": true, + "docs": [ + "realm_authority must sign and match Realm.authority" + ] }, { "name": "gatekeeperNetwork", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The Identity.com Gateway gatekeeper network that this realm uses", + "(See the registry struct docs for details).", + "Gateway Token belongs to this gatekeeper network, so passing a particular key here is", + "essentially saying \"We trust this gatekeeper network\"." + ] }, { "name": "payer", @@ -405,22 +569,41 @@ export const IDL: Gateway = { { "name": "registrar", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "The Gateway Plugin Registrar to be updated" + ] }, { "name": "realm", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "An spl-governance Realm", + "", + "Realm is validated in the instruction:", + "- Realm is owned by the governance_program_id", + "- realm_authority is realm.authority" + ] }, { "name": "realmAuthority", "isMut": false, - "isSigner": true + "isSigner": true, + "docs": [ + "realm_authority must sign and match Realm.authority" + ] }, { "name": "gatekeeperNetwork", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The new Identity.com Gateway gatekeeper network", + "(See the registry struct docs for details).", + "Gateway Token belongs to this gatekeeper network, so passing a particular key here is", + "essentially saying \"We trust this gatekeeper network\"." + ] } ], "args": [ @@ -467,17 +650,28 @@ export const IDL: Gateway = { { "name": "registrar", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The Gateway Registrar" + ] }, { "name": "inputVoterWeight", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "An account that is either of type TokenOwnerRecordV2 or VoterWeightRecord", + "depending on whether the registrar includes a predecessor or not" + ] }, { "name": "gatewayToken", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "A gateway token from the gatekeeper network in the registrar.", + "Proves that the holder is permitted to take an action." + ] }, { "name": "voterWeightRecord", @@ -491,33 +685,59 @@ export const IDL: Gateway = { "accounts": [ { "name": "registrar", + "docs": [ + "Registrar which stores Civic Pass voting configuration for the given Realm" + ], "type": { "kind": "struct", "fields": [ { "name": "governanceProgramId", + "docs": [ + "spl-governance program the Realm belongs to" + ], "type": "publicKey" }, { "name": "realm", + "docs": [ + "Realm of the Registrar" + ], "type": "publicKey" }, { "name": "governingTokenMint", + "docs": [ + "Governing token mint the Registrar is for", + "It can either be the Community or the Council mint of the Realm", + "When the plugin is used the mint is only used as identity of the governing power (voting population)", + "and the actual token of the mint is not used" + ], "type": "publicKey" }, { "name": "gatekeeperNetwork", + "docs": [ + "The Gatekeeper Network represents the \"Pass Type\" that a", + "user must present." + ], "type": "publicKey" }, { "name": "previousVoterWeightPluginProgramId", + "docs": [ + "If the plugin is one in a sequence, this is the previous plugin program ID", + "If set, then update_voter_weight_record will expect a voter_weight_record owned by this program" + ], "type": { "option": "publicKey" } }, { "name": "reserved", + "docs": [ + "Reserved for future upgrades" + ], "type": { "array": [ "u8", @@ -530,33 +750,66 @@ export const IDL: Gateway = { }, { "name": "voterWeightRecord", + "docs": [ + "VoterWeightRecord account as defined in spl-governance-addin-api", + "It's redefined here without account_discriminator for Anchor to treat it as native account", + "", + "The account is used as an api interface to provide voting power to the governance program from external addin contracts" + ], "type": { "kind": "struct", "fields": [ { "name": "realm", + "docs": [ + "The Realm the VoterWeightRecord belongs to" + ], "type": "publicKey" }, { "name": "governingTokenMint", + "docs": [ + "Governing Token Mint the VoterWeightRecord is associated with", + "Note: The addin can take deposits of any tokens and is not restricted to the community or council tokens only" + ], "type": "publicKey" }, { "name": "governingTokenOwner", + "docs": [ + "The owner of the governing token and voter", + "This is the actual owner (voter) and corresponds to TokenOwnerRecord.governing_token_owner" + ], "type": "publicKey" }, { "name": "voterWeight", + "docs": [ + "Voter's weight", + "The weight of the voter provided by the addin for the given realm, governing_token_mint and governing_token_owner (voter)" + ], "type": "u64" }, { "name": "voterWeightExpiry", + "docs": [ + "The slot when the voting weight expires", + "It should be set to None if the weight never expires", + "If the voter weight decays with time, for example for time locked based weights, then the expiry must be set", + "As a common pattern Revise instruction to update the weight should be invoked before governance instruction within the same transaction", + "and the expiry set to the current slot to provide up to date weight" + ], "type": { "option": "u64" } }, { "name": "weightAction", + "docs": [ + "The governance action the voter's weight pertains to", + "It allows to provided voter's weight specific to the particular action the weight is evaluated for", + "When the action is provided then the governance program asserts the executing action is the same as specified by the addin" + ], "type": { "option": { "defined": "VoterWeightAction" @@ -565,12 +818,21 @@ export const IDL: Gateway = { }, { "name": "weightActionTarget", + "docs": [ + "The target the voter's weight action pertains to", + "It allows to provided voter's weight specific to the target the weight is evaluated for", + "For example when addin supplies weight to vote on a particular proposal then it must specify the proposal as the action target", + "When the target is provided then the governance program asserts the target is the same as specified by the addin" + ], "type": { "option": "publicKey" } }, { "name": "reserved", + "docs": [ + "Reserved space for future versions" + ], "type": { "array": [ "u8", @@ -609,6 +871,10 @@ export const IDL: Gateway = { }, { "name": "VoterWeightAction", + "docs": [ + "VoterWeightAction enum as defined in spl-governance-addin-api", + "It's redefined here for Anchor to export it to IDL" + ], "type": { "kind": "enum", "variants": [ diff --git a/src/nftVoter/nft_voter.ts b/src/nftVoter/nft_voter.ts index a6e82b72..8d87093d 100644 --- a/src/nftVoter/nft_voter.ts +++ b/src/nftVoter/nft_voter.ts @@ -8,27 +8,52 @@ export type NftVoter = { { "name": "registrar", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "The NFT voting Registrar", + "There can only be a single registrar per governance Realm and governing mint of the Realm" + ] }, { "name": "governanceProgramId", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The program id of the spl-governance program the realm belongs to" + ] }, { "name": "realm", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "An spl-governance Realm", + "", + "Realm is validated in the instruction:", + "- Realm is owned by the governance_program_id", + "- governing_token_mint must be the community or council mint", + "- realm_authority is realm.authority" + ] }, { "name": "governingTokenMint", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "Either the realm community mint or the council mint.", + "It must match Realm.community_mint or Realm.config.council_mint", + "", + "Note: Once the NFT plugin is enabled the governing_token_mint is used only as identity", + "for the voting population and the tokens of that are no longer used" + ] }, { "name": "realmAuthority", "isMut": false, - "isSigner": true + "isSigner": true, + "docs": [ + "realm_authority must sign and match Realm.authority" + ] }, { "name": "payer", @@ -59,7 +84,10 @@ export type NftVoter = { { "name": "governanceProgramId", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The program id of the spl-governance program the realm belongs to" + ] }, { "name": "realm", @@ -69,7 +97,10 @@ export type NftVoter = { { "name": "realmGoverningTokenMint", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "Either the realm community mint or the council mint." + ] }, { "name": "payer", @@ -100,7 +131,10 @@ export type NftVoter = { { "name": "governanceProgramId", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The program id of the spl-governance program the realm belongs to" + ] }, { "name": "realm", @@ -110,7 +144,10 @@ export type NftVoter = { { "name": "realmGoverningTokenMint", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "Either the realm community mint or the council mint." + ] }, { "name": "payer", @@ -131,7 +168,10 @@ export type NftVoter = { { "name": "registrar", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The NFT voting Registrar" + ] }, { "name": "voterWeightRecord", @@ -154,7 +194,10 @@ export type NftVoter = { { "name": "registrar", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The NFT voting Registrar" + ] }, { "name": "voterWeightRecord", @@ -164,7 +207,10 @@ export type NftVoter = { { "name": "governance", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "Governance account the Proposal is for" + ] }, { "name": "proposal", @@ -174,12 +220,20 @@ export type NftVoter = { { "name": "governingTokenOwner", "isMut": false, - "isSigner": true + "isSigner": true, + "docs": [ + "The token owner who cast the original vote" + ] }, { "name": "voteRecord", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The account is used to validate that it doesn't exist and if it doesn't then Anchor owner check throws error", + "The check is disabled here and performed inside the instruction", + "#[account(owner = registrar.governance_program_id)]" + ] }, { "name": "beneficiary", @@ -195,7 +249,10 @@ export type NftVoter = { { "name": "registrar", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "Registrar for which we configure this Collection" + ] }, { "name": "realm", @@ -205,7 +262,10 @@ export type NftVoter = { { "name": "realmAuthority", "isMut": false, - "isSigner": true + "isSigner": true, + "docs": [ + "Authority of the Realm must sign and match Realm.authority" + ] }, { "name": "collection", @@ -235,7 +295,10 @@ export type NftVoter = { { "name": "registrar", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The NFT voting registrar" + ] }, { "name": "voterWeightRecord", @@ -245,12 +308,18 @@ export type NftVoter = { { "name": "governingTokenOwner", "isMut": false, - "isSigner": true + "isSigner": true, + "docs": [ + "The token owner who casts the vote" + ] }, { "name": "payer", "isMut": true, - "isSigner": true + "isSigner": true, + "docs": [ + "The account which pays for the transaction" + ] }, { "name": "systemProgram", @@ -264,24 +333,100 @@ export type NftVoter = { "type": "publicKey" } ] + }, + { + "name": "createGovernanceTokenHoldingAccount", + "accounts": [ + { + "name": "holdingAccountInfo", + "isMut": true, + "isSigner": false + }, + { + "name": "governanceProgramId", + "isMut": false, + "isSigner": false, + "docs": [ + "The program id of the spl-governance program the realm belongs to" + ] + }, + { + "name": "realm", + "isMut": false, + "isSigner": false + }, + { + "name": "realmGoverningTokenMint", + "isMut": false, + "isSigner": false, + "docs": [ + "Either the realm community mint or the council mint." + ] + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "nft", + "isMut": false, + "isSigner": false + }, + { + "name": "associatedTokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [] } ], "accounts": [ { "name": "nftVoteRecord", + "docs": [ + "NftVoteRecord exported to IDL without account_discriminator", + "TODO: Once we can support these accounts in Anchor via remaining_accounts then it should be possible to remove it" + ], "type": { "kind": "struct", "fields": [ { "name": "proposal", + "docs": [ + "Proposal which was voted on" + ], "type": "publicKey" }, { "name": "nftMint", + "docs": [ + "The mint of the NFT which was used for the vote" + ], "type": "publicKey" }, { "name": "governingTokenOwner", + "docs": [ + "The voter who casted this vote", + "It's a Realm member pubkey corresponding to TokenOwnerRecord.governing_token_owner" + ], "type": "publicKey" } ] @@ -289,29 +434,56 @@ export type NftVoter = { }, { "name": "maxVoterWeightRecord", + "docs": [ + "MaxVoterWeightRecord account as defined in spl-governance-addin-api", + "It's redefined here without account_discriminator for Anchor to treat it as native account", + "", + "The account is used as an api interface to provide max voting power to the governance program from external addin contracts" + ], "type": { "kind": "struct", "fields": [ { "name": "realm", + "docs": [ + "The Realm the MaxVoterWeightRecord belongs to" + ], "type": "publicKey" }, { "name": "governingTokenMint", + "docs": [ + "Governing Token Mint the MaxVoterWeightRecord is associated with", + "Note: The addin can take deposits of any tokens and is not restricted to the community or council tokens only" + ], "type": "publicKey" }, { "name": "maxVoterWeight", + "docs": [ + "Max voter weight", + "The max voter weight provided by the addin for the given realm and governing_token_mint" + ], "type": "u64" }, { "name": "maxVoterWeightExpiry", + "docs": [ + "The slot when the max voting weight expires", + "It should be set to None if the weight never expires", + "If the max vote weight decays with time, for example for time locked based weights, then the expiry must be set", + "As a pattern Revise instruction to update the max weight should be invoked before governance instruction within the same transaction", + "and the expiry set to the current slot to provide up to date weight" + ], "type": { "option": "u64" } }, { "name": "reserved", + "docs": [ + "Reserved space for future versions" + ], "type": { "array": [ "u8", @@ -324,23 +496,41 @@ export type NftVoter = { }, { "name": "registrar", + "docs": [ + "Registrar which stores NFT voting configuration for the given Realm" + ], "type": { "kind": "struct", "fields": [ { "name": "governanceProgramId", + "docs": [ + "spl-governance program the Realm belongs to" + ], "type": "publicKey" }, { "name": "realm", + "docs": [ + "Realm of the Registrar" + ], "type": "publicKey" }, { "name": "governingTokenMint", + "docs": [ + "Governing token mint the Registrar is for", + "It can either be the Community or the Council mint of the Realm", + "When the plugin is used the mint is only used as identity of the governing power (voting population)", + "and the actual token of the mint is not used" + ], "type": "publicKey" }, { "name": "collectionConfigs", + "docs": [ + "MPL Collection used for voting" + ], "type": { "vec": { "defined": "CollectionConfig" @@ -349,6 +539,9 @@ export type NftVoter = { }, { "name": "reserved", + "docs": [ + "Reserved for future upgrades" + ], "type": { "array": [ "u8", @@ -361,33 +554,66 @@ export type NftVoter = { }, { "name": "voterWeightRecord", + "docs": [ + "VoterWeightRecord account as defined in spl-governance-addin-api", + "It's redefined here without account_discriminator for Anchor to treat it as native account", + "", + "The account is used as an api interface to provide voting power to the governance program from external addin contracts" + ], "type": { "kind": "struct", "fields": [ { "name": "realm", + "docs": [ + "The Realm the VoterWeightRecord belongs to" + ], "type": "publicKey" }, { "name": "governingTokenMint", + "docs": [ + "Governing Token Mint the VoterWeightRecord is associated with", + "Note: The addin can take deposits of any tokens and is not restricted to the community or council tokens only" + ], "type": "publicKey" }, { "name": "governingTokenOwner", + "docs": [ + "The owner of the governing token and voter", + "This is the actual owner (voter) and corresponds to TokenOwnerRecord.governing_token_owner" + ], "type": "publicKey" }, { "name": "voterWeight", + "docs": [ + "Voter's weight", + "The weight of the voter provided by the addin for the given realm, governing_token_mint and governing_token_owner (voter)" + ], "type": "u64" }, { "name": "voterWeightExpiry", + "docs": [ + "The slot when the voting weight expires", + "It should be set to None if the weight never expires", + "If the voter weight decays with time, for example for time locked based weights, then the expiry must be set", + "As a common pattern Revise instruction to update the weight should be invoked before governance instruction within the same transaction", + "and the expiry set to the current slot to provide up to date weight" + ], "type": { "option": "u64" } }, { "name": "weightAction", + "docs": [ + "The governance action the voter's weight pertains to", + "It allows to provided voter's weight specific to the particular action the weight is evaluated for", + "When the action is provided then the governance program asserts the executing action is the same as specified by the addin" + ], "type": { "option": { "defined": "VoterWeightAction" @@ -396,12 +622,21 @@ export type NftVoter = { }, { "name": "weightActionTarget", + "docs": [ + "The target the voter's weight action pertains to", + "It allows to provided voter's weight specific to the target the weight is evaluated for", + "For example when addin supplies weight to vote on a particular proposal then it must specify the proposal as the action target", + "When the target is provided then the governance program asserts the target is the same as specified by the addin" + ], "type": { "option": "publicKey" } }, { "name": "reserved", + "docs": [ + "Reserved space for future versions" + ], "type": { "array": [ "u8", @@ -416,23 +651,43 @@ export type NftVoter = { "types": [ { "name": "CollectionConfig", + "docs": [ + "Configuration of an NFT collection used for governance power" + ], "type": { "kind": "struct", "fields": [ { "name": "collection", + "docs": [ + "The NFT collection used for governance" + ], "type": "publicKey" }, { "name": "size", + "docs": [ + "The size of the NFT collection used to calculate max voter weight", + "Note: At the moment the size is not captured on Metaplex accounts", + "and it has to be manually updated on the Registrar" + ], "type": "u32" }, { "name": "weight", + "docs": [ + "Governance power weight of the collection", + "Each NFT in the collection has governance power = 1 * weight", + "Note: The weight is scaled accordingly to the governing_token_mint decimals", + "Ex: if the the mint has 2 decimal places then weight of 1 should be stored as 100" + ], "type": "u64" }, { "name": "reserved", + "docs": [ + "Reserved for future upgrades" + ], "type": { "array": [ "u8", @@ -445,6 +700,10 @@ export type NftVoter = { }, { "name": "VoterWeightAction", + "docs": [ + "VoterWeightAction enum as defined in spl-governance-addin-api", + "It's redefined here for Anchor to export it to IDL" + ], "type": { "kind": "enum", "variants": [ @@ -606,27 +865,52 @@ export const IDL: NftVoter = { { "name": "registrar", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "The NFT voting Registrar", + "There can only be a single registrar per governance Realm and governing mint of the Realm" + ] }, { "name": "governanceProgramId", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The program id of the spl-governance program the realm belongs to" + ] }, { "name": "realm", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "An spl-governance Realm", + "", + "Realm is validated in the instruction:", + "- Realm is owned by the governance_program_id", + "- governing_token_mint must be the community or council mint", + "- realm_authority is realm.authority" + ] }, { "name": "governingTokenMint", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "Either the realm community mint or the council mint.", + "It must match Realm.community_mint or Realm.config.council_mint", + "", + "Note: Once the NFT plugin is enabled the governing_token_mint is used only as identity", + "for the voting population and the tokens of that are no longer used" + ] }, { "name": "realmAuthority", "isMut": false, - "isSigner": true + "isSigner": true, + "docs": [ + "realm_authority must sign and match Realm.authority" + ] }, { "name": "payer", @@ -657,7 +941,10 @@ export const IDL: NftVoter = { { "name": "governanceProgramId", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The program id of the spl-governance program the realm belongs to" + ] }, { "name": "realm", @@ -667,7 +954,10 @@ export const IDL: NftVoter = { { "name": "realmGoverningTokenMint", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "Either the realm community mint or the council mint." + ] }, { "name": "payer", @@ -698,7 +988,10 @@ export const IDL: NftVoter = { { "name": "governanceProgramId", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The program id of the spl-governance program the realm belongs to" + ] }, { "name": "realm", @@ -708,7 +1001,10 @@ export const IDL: NftVoter = { { "name": "realmGoverningTokenMint", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "Either the realm community mint or the council mint." + ] }, { "name": "payer", @@ -729,7 +1025,10 @@ export const IDL: NftVoter = { { "name": "registrar", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The NFT voting Registrar" + ] }, { "name": "voterWeightRecord", @@ -752,7 +1051,10 @@ export const IDL: NftVoter = { { "name": "registrar", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The NFT voting Registrar" + ] }, { "name": "voterWeightRecord", @@ -762,7 +1064,10 @@ export const IDL: NftVoter = { { "name": "governance", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "Governance account the Proposal is for" + ] }, { "name": "proposal", @@ -772,12 +1077,20 @@ export const IDL: NftVoter = { { "name": "governingTokenOwner", "isMut": false, - "isSigner": true + "isSigner": true, + "docs": [ + "The token owner who cast the original vote" + ] }, { "name": "voteRecord", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The account is used to validate that it doesn't exist and if it doesn't then Anchor owner check throws error", + "The check is disabled here and performed inside the instruction", + "#[account(owner = registrar.governance_program_id)]" + ] }, { "name": "beneficiary", @@ -793,7 +1106,10 @@ export const IDL: NftVoter = { { "name": "registrar", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "Registrar for which we configure this Collection" + ] }, { "name": "realm", @@ -803,7 +1119,10 @@ export const IDL: NftVoter = { { "name": "realmAuthority", "isMut": false, - "isSigner": true + "isSigner": true, + "docs": [ + "Authority of the Realm must sign and match Realm.authority" + ] }, { "name": "collection", @@ -833,7 +1152,10 @@ export const IDL: NftVoter = { { "name": "registrar", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The NFT voting registrar" + ] }, { "name": "voterWeightRecord", @@ -843,12 +1165,18 @@ export const IDL: NftVoter = { { "name": "governingTokenOwner", "isMut": false, - "isSigner": true + "isSigner": true, + "docs": [ + "The token owner who casts the vote" + ] }, { "name": "payer", "isMut": true, - "isSigner": true + "isSigner": true, + "docs": [ + "The account which pays for the transaction" + ] }, { "name": "systemProgram", @@ -862,24 +1190,100 @@ export const IDL: NftVoter = { "type": "publicKey" } ] + }, + { + "name": "createGovernanceTokenHoldingAccount", + "accounts": [ + { + "name": "holdingAccountInfo", + "isMut": true, + "isSigner": false + }, + { + "name": "governanceProgramId", + "isMut": false, + "isSigner": false, + "docs": [ + "The program id of the spl-governance program the realm belongs to" + ] + }, + { + "name": "realm", + "isMut": false, + "isSigner": false + }, + { + "name": "realmGoverningTokenMint", + "isMut": false, + "isSigner": false, + "docs": [ + "Either the realm community mint or the council mint." + ] + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "nft", + "isMut": false, + "isSigner": false + }, + { + "name": "associatedTokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [] } ], "accounts": [ { "name": "nftVoteRecord", + "docs": [ + "NftVoteRecord exported to IDL without account_discriminator", + "TODO: Once we can support these accounts in Anchor via remaining_accounts then it should be possible to remove it" + ], "type": { "kind": "struct", "fields": [ { "name": "proposal", + "docs": [ + "Proposal which was voted on" + ], "type": "publicKey" }, { "name": "nftMint", + "docs": [ + "The mint of the NFT which was used for the vote" + ], "type": "publicKey" }, { "name": "governingTokenOwner", + "docs": [ + "The voter who casted this vote", + "It's a Realm member pubkey corresponding to TokenOwnerRecord.governing_token_owner" + ], "type": "publicKey" } ] @@ -887,29 +1291,56 @@ export const IDL: NftVoter = { }, { "name": "maxVoterWeightRecord", + "docs": [ + "MaxVoterWeightRecord account as defined in spl-governance-addin-api", + "It's redefined here without account_discriminator for Anchor to treat it as native account", + "", + "The account is used as an api interface to provide max voting power to the governance program from external addin contracts" + ], "type": { "kind": "struct", "fields": [ { "name": "realm", + "docs": [ + "The Realm the MaxVoterWeightRecord belongs to" + ], "type": "publicKey" }, { "name": "governingTokenMint", + "docs": [ + "Governing Token Mint the MaxVoterWeightRecord is associated with", + "Note: The addin can take deposits of any tokens and is not restricted to the community or council tokens only" + ], "type": "publicKey" }, { "name": "maxVoterWeight", + "docs": [ + "Max voter weight", + "The max voter weight provided by the addin for the given realm and governing_token_mint" + ], "type": "u64" }, { "name": "maxVoterWeightExpiry", + "docs": [ + "The slot when the max voting weight expires", + "It should be set to None if the weight never expires", + "If the max vote weight decays with time, for example for time locked based weights, then the expiry must be set", + "As a pattern Revise instruction to update the max weight should be invoked before governance instruction within the same transaction", + "and the expiry set to the current slot to provide up to date weight" + ], "type": { "option": "u64" } }, { "name": "reserved", + "docs": [ + "Reserved space for future versions" + ], "type": { "array": [ "u8", @@ -922,23 +1353,41 @@ export const IDL: NftVoter = { }, { "name": "registrar", + "docs": [ + "Registrar which stores NFT voting configuration for the given Realm" + ], "type": { "kind": "struct", "fields": [ { "name": "governanceProgramId", + "docs": [ + "spl-governance program the Realm belongs to" + ], "type": "publicKey" }, { "name": "realm", + "docs": [ + "Realm of the Registrar" + ], "type": "publicKey" }, { "name": "governingTokenMint", + "docs": [ + "Governing token mint the Registrar is for", + "It can either be the Community or the Council mint of the Realm", + "When the plugin is used the mint is only used as identity of the governing power (voting population)", + "and the actual token of the mint is not used" + ], "type": "publicKey" }, { "name": "collectionConfigs", + "docs": [ + "MPL Collection used for voting" + ], "type": { "vec": { "defined": "CollectionConfig" @@ -947,6 +1396,9 @@ export const IDL: NftVoter = { }, { "name": "reserved", + "docs": [ + "Reserved for future upgrades" + ], "type": { "array": [ "u8", @@ -959,33 +1411,66 @@ export const IDL: NftVoter = { }, { "name": "voterWeightRecord", + "docs": [ + "VoterWeightRecord account as defined in spl-governance-addin-api", + "It's redefined here without account_discriminator for Anchor to treat it as native account", + "", + "The account is used as an api interface to provide voting power to the governance program from external addin contracts" + ], "type": { "kind": "struct", "fields": [ { "name": "realm", + "docs": [ + "The Realm the VoterWeightRecord belongs to" + ], "type": "publicKey" }, { "name": "governingTokenMint", + "docs": [ + "Governing Token Mint the VoterWeightRecord is associated with", + "Note: The addin can take deposits of any tokens and is not restricted to the community or council tokens only" + ], "type": "publicKey" }, { "name": "governingTokenOwner", + "docs": [ + "The owner of the governing token and voter", + "This is the actual owner (voter) and corresponds to TokenOwnerRecord.governing_token_owner" + ], "type": "publicKey" }, { "name": "voterWeight", + "docs": [ + "Voter's weight", + "The weight of the voter provided by the addin for the given realm, governing_token_mint and governing_token_owner (voter)" + ], "type": "u64" }, { "name": "voterWeightExpiry", + "docs": [ + "The slot when the voting weight expires", + "It should be set to None if the weight never expires", + "If the voter weight decays with time, for example for time locked based weights, then the expiry must be set", + "As a common pattern Revise instruction to update the weight should be invoked before governance instruction within the same transaction", + "and the expiry set to the current slot to provide up to date weight" + ], "type": { "option": "u64" } }, { "name": "weightAction", + "docs": [ + "The governance action the voter's weight pertains to", + "It allows to provided voter's weight specific to the particular action the weight is evaluated for", + "When the action is provided then the governance program asserts the executing action is the same as specified by the addin" + ], "type": { "option": { "defined": "VoterWeightAction" @@ -994,12 +1479,21 @@ export const IDL: NftVoter = { }, { "name": "weightActionTarget", + "docs": [ + "The target the voter's weight action pertains to", + "It allows to provided voter's weight specific to the target the weight is evaluated for", + "For example when addin supplies weight to vote on a particular proposal then it must specify the proposal as the action target", + "When the target is provided then the governance program asserts the target is the same as specified by the addin" + ], "type": { "option": "publicKey" } }, { "name": "reserved", + "docs": [ + "Reserved space for future versions" + ], "type": { "array": [ "u8", @@ -1014,23 +1508,43 @@ export const IDL: NftVoter = { "types": [ { "name": "CollectionConfig", + "docs": [ + "Configuration of an NFT collection used for governance power" + ], "type": { "kind": "struct", "fields": [ { "name": "collection", + "docs": [ + "The NFT collection used for governance" + ], "type": "publicKey" }, { "name": "size", + "docs": [ + "The size of the NFT collection used to calculate max voter weight", + "Note: At the moment the size is not captured on Metaplex accounts", + "and it has to be manually updated on the Registrar" + ], "type": "u32" }, { "name": "weight", + "docs": [ + "Governance power weight of the collection", + "Each NFT in the collection has governance power = 1 * weight", + "Note: The weight is scaled accordingly to the governing_token_mint decimals", + "Ex: if the the mint has 2 decimal places then weight of 1 should be stored as 100" + ], "type": "u64" }, { "name": "reserved", + "docs": [ + "Reserved for future upgrades" + ], "type": { "array": [ "u8", @@ -1043,6 +1557,10 @@ export const IDL: NftVoter = { }, { "name": "VoterWeightAction", + "docs": [ + "VoterWeightAction enum as defined in spl-governance-addin-api", + "It's redefined here for Anchor to export it to IDL" + ], "type": { "kind": "enum", "variants": [ diff --git a/src/realmVoter/realm_voter.ts b/src/realmVoter/realm_voter.ts index 95119463..d7cdfca8 100644 --- a/src/realmVoter/realm_voter.ts +++ b/src/realmVoter/realm_voter.ts @@ -8,27 +8,52 @@ export type RealmVoter = { { "name": "registrar", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "The Realm Voter Registrar", + "There can only be a single registrar per governance Realm and governing mint of the Realm" + ] }, { "name": "governanceProgramId", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The program id of the spl-governance program the realm belongs to" + ] }, { "name": "realm", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "An spl-governance Realm", + "", + "Realm is validated in the instruction:", + "- Realm is owned by the governance_program_id", + "- governing_token_mint must be the community or council mint", + "- realm_authority is realm.authority" + ] }, { "name": "governingTokenMint", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "Either the realm community mint or the council mint.", + "It must match Realm.community_mint or Realm.config.council_mint", + "", + "Note: Once the Realm voter plugin is enabled the governing_token_mint is used only as identity", + "for the voting population and the tokens of that are no longer used" + ] }, { "name": "realmAuthority", "isMut": false, - "isSigner": true + "isSigner": true, + "docs": [ + "realm_authority must sign and match Realm.authority" + ] }, { "name": "payer", @@ -111,7 +136,10 @@ export type RealmVoter = { { "name": "registrar", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The RealmVoter voting Registrar" + ] }, { "name": "voterWeightRecord", @@ -121,7 +149,10 @@ export type RealmVoter = { { "name": "tokenOwnerRecord", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "TokenOwnerRecord for any of the configured spl-governance instances" + ] } ], "args": [] @@ -132,7 +163,10 @@ export type RealmVoter = { { "name": "registrar", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "The Registrar for the given realm and governing_token_mint" + ] }, { "name": "realm", @@ -142,12 +176,18 @@ export type RealmVoter = { { "name": "realmAuthority", "isMut": false, - "isSigner": true + "isSigner": true, + "docs": [ + "Authority of the Realm must sign and match realm.authority" + ] }, { "name": "maxVoterWeightRecord", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "MaxVoterWeightRecord for the given registrar.realm and registrar.governing_token_mint" + ] } ], "args": [ @@ -167,7 +207,10 @@ export type RealmVoter = { { "name": "registrar", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "Registrar which we configure the provided spl-governance instance for" + ] }, { "name": "realm", @@ -177,12 +220,19 @@ export type RealmVoter = { { "name": "realmAuthority", "isMut": false, - "isSigner": true + "isSigner": true, + "docs": [ + "Authority of the Realm must sign the transaction and must match realm.authority" + ] }, { "name": "governanceProgramId", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The onus is entirely on the caller side to ensure the provided instance is correct", + "In future versions once we have the registry of spl-governance instances it could be validated against the registry" + ] } ], "args": [ @@ -198,29 +248,56 @@ export type RealmVoter = { "accounts": [ { "name": "maxVoterWeightRecord", + "docs": [ + "MaxVoterWeightRecord account as defined in spl-governance-addin-api", + "It's redefined here without account_discriminator for Anchor to treat it as native account", + "", + "The account is used as an api interface to provide max voting power to the governance program from external addin contracts" + ], "type": { "kind": "struct", "fields": [ { "name": "realm", + "docs": [ + "The Realm the MaxVoterWeightRecord belongs to" + ], "type": "publicKey" }, { "name": "governingTokenMint", + "docs": [ + "Governing Token Mint the MaxVoterWeightRecord is associated with", + "Note: The addin can take deposits of any tokens and is not restricted to the community or council tokens only" + ], "type": "publicKey" }, { "name": "maxVoterWeight", + "docs": [ + "Max voter weight", + "The max voter weight provided by the addin for the given realm and governing_token_mint" + ], "type": "u64" }, { "name": "maxVoterWeightExpiry", + "docs": [ + "The slot when the max voting weight expires", + "It should be set to None if the weight never expires", + "If the max vote weight decays with time, for example for time locked based weights, then the expiry must be set", + "As a pattern Revise instruction to update the max weight should be invoked before governance instruction within the same transaction", + "and the expiry set to the current slot to provide up to date weight" + ], "type": { "option": "u64" } }, { "name": "reserved", + "docs": [ + "Reserved space for future versions" + ], "type": { "array": [ "u8", @@ -233,23 +310,43 @@ export type RealmVoter = { }, { "name": "registrar", + "docs": [ + "Registrar which stores spl-governance configurations for the given Realm" + ], "type": { "kind": "struct", "fields": [ { "name": "governanceProgramId", + "docs": [ + "spl-governance program the Realm belongs to" + ], "type": "publicKey" }, { "name": "realm", + "docs": [ + "Realm of the Registrar" + ], "type": "publicKey" }, { "name": "governingTokenMint", + "docs": [ + "Governing token mint the Registrar is for", + "It can either be the Community or the Council mint of the Realm", + "When the plugin is enabled the mint is only used as the identity of the governing power (voting population)", + "and the actual token of the mint is not used" + ], "type": "publicKey" }, { "name": "governanceProgramConfigs", + "docs": [ + "spl-governance instances used for governance power", + "Any DAO member of any DAO created using the configured spl-governances would be given 1 vote", + "TODO: Once we have on-chain spl-governance registry this configuration won't be needed any longer" + ], "type": { "vec": { "defined": "GovernanceProgramConfig" @@ -258,14 +355,26 @@ export type RealmVoter = { }, { "name": "realmMemberVoterWeight", + "docs": [ + "Vote weight assigned to a member of any of the Realms from the configured spl-governances" + ], "type": "u64" }, { "name": "maxVoterWeight", + "docs": [ + "Max voter weight (expressed in governing_token_mint decimal units) is used to establish the theoretical Max Attendance Quorum which is then used to calculate Approval Quorum", + "This manual configuration is a rough estimate because it's not practical to calculate on-chain the number of all DAO members for the given spl-governance instances", + "", + "Note: This is not a security vulnerability because the plugin is inherently not secure and used only to encourage DAO usage and registration of spl-governance instances" + ], "type": "u64" }, { "name": "reserved", + "docs": [ + "Reserved for future upgrades" + ], "type": { "array": [ "u8", @@ -278,33 +387,66 @@ export type RealmVoter = { }, { "name": "voterWeightRecord", + "docs": [ + "VoterWeightRecord account as defined in spl-governance-addin-api", + "It's redefined here without account_discriminator for Anchor to treat it as native account", + "", + "The account is used as an api interface to provide voting power to the governance program from external addin contracts" + ], "type": { "kind": "struct", "fields": [ { "name": "realm", + "docs": [ + "The Realm the VoterWeightRecord belongs to" + ], "type": "publicKey" }, { "name": "governingTokenMint", + "docs": [ + "Governing Token Mint the VoterWeightRecord is associated with", + "Note: The addin can take deposits of any tokens and is not restricted to the community or council tokens only" + ], "type": "publicKey" }, { "name": "governingTokenOwner", + "docs": [ + "The owner of the governing token and voter", + "This is the actual owner (voter) and corresponds to TokenOwnerRecord.governing_token_owner" + ], "type": "publicKey" }, { "name": "voterWeight", + "docs": [ + "Voter's weight", + "The weight of the voter provided by the addin for the given realm, governing_token_mint and governing_token_owner (voter)" + ], "type": "u64" }, { "name": "voterWeightExpiry", + "docs": [ + "The slot when the voting weight expires", + "It should be set to None if the weight never expires", + "If the voter weight decays with time, for example for time locked based weights, then the expiry must be set", + "As a common pattern Revise instruction to update the weight should be invoked before governance instruction within the same transaction", + "and the expiry set to the current slot to provide up to date weight" + ], "type": { "option": "u64" } }, { "name": "weightAction", + "docs": [ + "The governance action the voter's weight pertains to", + "It allows to provided voter's weight specific to the particular action the weight is evaluated for", + "When the action is provided then the governance program asserts the executing action is the same as specified by the addin" + ], "type": { "option": { "defined": "VoterWeightAction" @@ -313,12 +455,21 @@ export type RealmVoter = { }, { "name": "weightActionTarget", + "docs": [ + "The target the voter's weight action pertains to", + "It allows to provided voter's weight specific to the target the weight is evaluated for", + "For example when addin supplies weight to vote on a particular proposal then it must specify the proposal as the action target", + "When the target is provided then the governance program asserts the target is the same as specified by the addin" + ], "type": { "option": "publicKey" } }, { "name": "reserved", + "docs": [ + "Reserved space for future versions" + ], "type": { "array": [ "u8", @@ -333,15 +484,24 @@ export type RealmVoter = { "types": [ { "name": "GovernanceProgramConfig", + "docs": [ + "Configuration of an spl-governance instance used to grant governance power" + ], "type": { "kind": "struct", "fields": [ { "name": "programId", + "docs": [ + "The program id of the configured spl-governance instance" + ], "type": "publicKey" }, { "name": "reserved", + "docs": [ + "Reserved for future upgrades" + ], "type": { "array": [ "u8", @@ -354,6 +514,9 @@ export type RealmVoter = { }, { "name": "CollectionItemChangeType", + "docs": [ + "Enum defining collection item change type" + ], "type": { "kind": "enum", "variants": [ @@ -368,6 +531,10 @@ export type RealmVoter = { }, { "name": "VoterWeightAction", + "docs": [ + "VoterWeightAction enum as defined in spl-governance-addin-api", + "It's redefined here for Anchor to export it to IDL" + ], "type": { "kind": "enum", "variants": [ @@ -439,27 +606,52 @@ export const IDL: RealmVoter = { { "name": "registrar", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "The Realm Voter Registrar", + "There can only be a single registrar per governance Realm and governing mint of the Realm" + ] }, { "name": "governanceProgramId", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The program id of the spl-governance program the realm belongs to" + ] }, { "name": "realm", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "An spl-governance Realm", + "", + "Realm is validated in the instruction:", + "- Realm is owned by the governance_program_id", + "- governing_token_mint must be the community or council mint", + "- realm_authority is realm.authority" + ] }, { "name": "governingTokenMint", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "Either the realm community mint or the council mint.", + "It must match Realm.community_mint or Realm.config.council_mint", + "", + "Note: Once the Realm voter plugin is enabled the governing_token_mint is used only as identity", + "for the voting population and the tokens of that are no longer used" + ] }, { "name": "realmAuthority", "isMut": false, - "isSigner": true + "isSigner": true, + "docs": [ + "realm_authority must sign and match Realm.authority" + ] }, { "name": "payer", @@ -542,7 +734,10 @@ export const IDL: RealmVoter = { { "name": "registrar", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The RealmVoter voting Registrar" + ] }, { "name": "voterWeightRecord", @@ -552,7 +747,10 @@ export const IDL: RealmVoter = { { "name": "tokenOwnerRecord", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "TokenOwnerRecord for any of the configured spl-governance instances" + ] } ], "args": [] @@ -563,7 +761,10 @@ export const IDL: RealmVoter = { { "name": "registrar", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "The Registrar for the given realm and governing_token_mint" + ] }, { "name": "realm", @@ -573,12 +774,18 @@ export const IDL: RealmVoter = { { "name": "realmAuthority", "isMut": false, - "isSigner": true + "isSigner": true, + "docs": [ + "Authority of the Realm must sign and match realm.authority" + ] }, { "name": "maxVoterWeightRecord", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "MaxVoterWeightRecord for the given registrar.realm and registrar.governing_token_mint" + ] } ], "args": [ @@ -598,7 +805,10 @@ export const IDL: RealmVoter = { { "name": "registrar", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "Registrar which we configure the provided spl-governance instance for" + ] }, { "name": "realm", @@ -608,12 +818,19 @@ export const IDL: RealmVoter = { { "name": "realmAuthority", "isMut": false, - "isSigner": true + "isSigner": true, + "docs": [ + "Authority of the Realm must sign the transaction and must match realm.authority" + ] }, { "name": "governanceProgramId", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "The onus is entirely on the caller side to ensure the provided instance is correct", + "In future versions once we have the registry of spl-governance instances it could be validated against the registry" + ] } ], "args": [ @@ -629,29 +846,56 @@ export const IDL: RealmVoter = { "accounts": [ { "name": "maxVoterWeightRecord", + "docs": [ + "MaxVoterWeightRecord account as defined in spl-governance-addin-api", + "It's redefined here without account_discriminator for Anchor to treat it as native account", + "", + "The account is used as an api interface to provide max voting power to the governance program from external addin contracts" + ], "type": { "kind": "struct", "fields": [ { "name": "realm", + "docs": [ + "The Realm the MaxVoterWeightRecord belongs to" + ], "type": "publicKey" }, { "name": "governingTokenMint", + "docs": [ + "Governing Token Mint the MaxVoterWeightRecord is associated with", + "Note: The addin can take deposits of any tokens and is not restricted to the community or council tokens only" + ], "type": "publicKey" }, { "name": "maxVoterWeight", + "docs": [ + "Max voter weight", + "The max voter weight provided by the addin for the given realm and governing_token_mint" + ], "type": "u64" }, { "name": "maxVoterWeightExpiry", + "docs": [ + "The slot when the max voting weight expires", + "It should be set to None if the weight never expires", + "If the max vote weight decays with time, for example for time locked based weights, then the expiry must be set", + "As a pattern Revise instruction to update the max weight should be invoked before governance instruction within the same transaction", + "and the expiry set to the current slot to provide up to date weight" + ], "type": { "option": "u64" } }, { "name": "reserved", + "docs": [ + "Reserved space for future versions" + ], "type": { "array": [ "u8", @@ -664,23 +908,43 @@ export const IDL: RealmVoter = { }, { "name": "registrar", + "docs": [ + "Registrar which stores spl-governance configurations for the given Realm" + ], "type": { "kind": "struct", "fields": [ { "name": "governanceProgramId", + "docs": [ + "spl-governance program the Realm belongs to" + ], "type": "publicKey" }, { "name": "realm", + "docs": [ + "Realm of the Registrar" + ], "type": "publicKey" }, { "name": "governingTokenMint", + "docs": [ + "Governing token mint the Registrar is for", + "It can either be the Community or the Council mint of the Realm", + "When the plugin is enabled the mint is only used as the identity of the governing power (voting population)", + "and the actual token of the mint is not used" + ], "type": "publicKey" }, { "name": "governanceProgramConfigs", + "docs": [ + "spl-governance instances used for governance power", + "Any DAO member of any DAO created using the configured spl-governances would be given 1 vote", + "TODO: Once we have on-chain spl-governance registry this configuration won't be needed any longer" + ], "type": { "vec": { "defined": "GovernanceProgramConfig" @@ -689,14 +953,26 @@ export const IDL: RealmVoter = { }, { "name": "realmMemberVoterWeight", + "docs": [ + "Vote weight assigned to a member of any of the Realms from the configured spl-governances" + ], "type": "u64" }, { "name": "maxVoterWeight", + "docs": [ + "Max voter weight (expressed in governing_token_mint decimal units) is used to establish the theoretical Max Attendance Quorum which is then used to calculate Approval Quorum", + "This manual configuration is a rough estimate because it's not practical to calculate on-chain the number of all DAO members for the given spl-governance instances", + "", + "Note: This is not a security vulnerability because the plugin is inherently not secure and used only to encourage DAO usage and registration of spl-governance instances" + ], "type": "u64" }, { "name": "reserved", + "docs": [ + "Reserved for future upgrades" + ], "type": { "array": [ "u8", @@ -709,33 +985,66 @@ export const IDL: RealmVoter = { }, { "name": "voterWeightRecord", + "docs": [ + "VoterWeightRecord account as defined in spl-governance-addin-api", + "It's redefined here without account_discriminator for Anchor to treat it as native account", + "", + "The account is used as an api interface to provide voting power to the governance program from external addin contracts" + ], "type": { "kind": "struct", "fields": [ { "name": "realm", + "docs": [ + "The Realm the VoterWeightRecord belongs to" + ], "type": "publicKey" }, { "name": "governingTokenMint", + "docs": [ + "Governing Token Mint the VoterWeightRecord is associated with", + "Note: The addin can take deposits of any tokens and is not restricted to the community or council tokens only" + ], "type": "publicKey" }, { "name": "governingTokenOwner", + "docs": [ + "The owner of the governing token and voter", + "This is the actual owner (voter) and corresponds to TokenOwnerRecord.governing_token_owner" + ], "type": "publicKey" }, { "name": "voterWeight", + "docs": [ + "Voter's weight", + "The weight of the voter provided by the addin for the given realm, governing_token_mint and governing_token_owner (voter)" + ], "type": "u64" }, { "name": "voterWeightExpiry", + "docs": [ + "The slot when the voting weight expires", + "It should be set to None if the weight never expires", + "If the voter weight decays with time, for example for time locked based weights, then the expiry must be set", + "As a common pattern Revise instruction to update the weight should be invoked before governance instruction within the same transaction", + "and the expiry set to the current slot to provide up to date weight" + ], "type": { "option": "u64" } }, { "name": "weightAction", + "docs": [ + "The governance action the voter's weight pertains to", + "It allows to provided voter's weight specific to the particular action the weight is evaluated for", + "When the action is provided then the governance program asserts the executing action is the same as specified by the addin" + ], "type": { "option": { "defined": "VoterWeightAction" @@ -744,12 +1053,21 @@ export const IDL: RealmVoter = { }, { "name": "weightActionTarget", + "docs": [ + "The target the voter's weight action pertains to", + "It allows to provided voter's weight specific to the target the weight is evaluated for", + "For example when addin supplies weight to vote on a particular proposal then it must specify the proposal as the action target", + "When the target is provided then the governance program asserts the target is the same as specified by the addin" + ], "type": { "option": "publicKey" } }, { "name": "reserved", + "docs": [ + "Reserved space for future versions" + ], "type": { "array": [ "u8", @@ -764,15 +1082,24 @@ export const IDL: RealmVoter = { "types": [ { "name": "GovernanceProgramConfig", + "docs": [ + "Configuration of an spl-governance instance used to grant governance power" + ], "type": { "kind": "struct", "fields": [ { "name": "programId", + "docs": [ + "The program id of the configured spl-governance instance" + ], "type": "publicKey" }, { "name": "reserved", + "docs": [ + "Reserved for future upgrades" + ], "type": { "array": [ "u8", @@ -785,6 +1112,9 @@ export const IDL: RealmVoter = { }, { "name": "CollectionItemChangeType", + "docs": [ + "Enum defining collection item change type" + ], "type": { "kind": "enum", "variants": [ @@ -799,6 +1129,10 @@ export const IDL: RealmVoter = { }, { "name": "VoterWeightAction", + "docs": [ + "VoterWeightAction enum as defined in spl-governance-addin-api", + "It's redefined here for Anchor to export it to IDL" + ], "type": { "kind": "enum", "variants": [ From dcd0368823e5e38ed723d6a68b5cbaf2132fa791 Mon Sep 17 00:00:00 2001 From: Austin Milt Date: Fri, 12 Aug 2022 14:23:56 -0600 Subject: [PATCH 02/17] WIP deposit governance tokens --- .../tests/create_voter_weight_record.rs | 4 +- .../tests/program_test/gateway_voter_test.rs | 2 +- .../tests/program_test/program_test_bench.rs | 5 +- ..._voter_weight_record_predecessor_plugin.rs | 2 +- ...create_governance_token_holding_account.rs | 26 +-- .../instructions/deposit_governance_tokens.rs | 145 +++++++++++++ programs/nft-voter/src/instructions/mod.rs | 3 + programs/nft-voter/src/lib.rs | 8 + programs/nft-voter/src/state/mod.rs | 3 + .../src/state/nft_voter_token_owner_record.rs | 108 ++++++++++ programs/nft-voter/tests/cast_nft_vote.rs | 32 +-- ...create_governance_token_holding_account.rs | 60 ++++-- .../tests/create_voter_weight_record.rs | 8 +- .../tests/deposit_governance_tokens.rs | 199 ++++++++++++++++++ .../tests/program_test/nft_voter_test.rs | 135 +++++++++++- .../tests/program_test/program_test_bench.rs | 5 +- .../tests/program_test/token_metadata_test.rs | 2 +- .../nft-voter/tests/relinquish_nft_vote.rs | 12 +- .../tests/update_voter_weight_record.rs | 20 +- .../tests/create_voter_weight_record.rs | 4 +- .../tests/program_test/program_test_bench.rs | 5 +- .../tests/update_voter_weight_record.rs | 10 +- 22 files changed, 701 insertions(+), 97 deletions(-) create mode 100644 programs/nft-voter/src/instructions/deposit_governance_tokens.rs create mode 100644 programs/nft-voter/src/state/nft_voter_token_owner_record.rs create mode 100644 programs/nft-voter/tests/deposit_governance_tokens.rs diff --git a/programs/gateway/tests/create_voter_weight_record.rs b/programs/gateway/tests/create_voter_weight_record.rs index 4033fd51..cf6bb8b2 100644 --- a/programs/gateway/tests/create_voter_weight_record.rs +++ b/programs/gateway/tests/create_voter_weight_record.rs @@ -18,7 +18,7 @@ async fn test_create_voter_weight_record() -> Result<(), TransportError> { .with_registrar(&realm_cookie, &gateway_cookie, None) .await?; - let voter_cookie = gateway_voter_test.bench.with_wallet().await; + let voter_cookie = gateway_voter_test.bench.with_wallet(None).await; // Act let voter_weight_record_cookie = gateway_voter_test @@ -48,7 +48,7 @@ async fn test_create_voter_weight_record_with_already_exists_error() -> Result<( .with_registrar(&realm_cookie, &gateway_cookie, None) .await?; - let voter_cookie = gateway_voter_test.bench.with_wallet().await; + let voter_cookie = gateway_voter_test.bench.with_wallet(None).await; gateway_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) diff --git a/programs/gateway/tests/program_test/gateway_voter_test.rs b/programs/gateway/tests/program_test/gateway_voter_test.rs index 8f20617f..3c66fcaa 100644 --- a/programs/gateway/tests/program_test/gateway_voter_test.rs +++ b/programs/gateway/tests/program_test/gateway_voter_test.rs @@ -241,7 +241,7 @@ impl GatewayVoterTest { .with_registrar(&realm_cookie, &gateway_cookie, predecessor_program_id) .await?; // - let voter_cookie = self.bench.with_wallet().await; + let voter_cookie = self.bench.with_wallet(None).await; let gateway_token_cookie = self .with_gateway_token(&gateway_cookie, &voter_cookie) .await?; diff --git a/programs/gateway/tests/program_test/program_test_bench.rs b/programs/gateway/tests/program_test/program_test_bench.rs index 3bb269fd..24a01556 100644 --- a/programs/gateway/tests/program_test/program_test_bench.rs +++ b/programs/gateway/tests/program_test/program_test_bench.rs @@ -256,14 +256,15 @@ impl ProgramTestBench { } #[allow(dead_code)] - pub async fn with_wallet(&self) -> WalletCookie { + pub async fn with_wallet(&self, lamports: Option) -> WalletCookie { let account_rent = self.rent.minimum_balance(0); let account_keypair = Keypair::new(); + let lamports = account_rent.checked_add(lamports.unwrap_or(0)).unwrap(); let create_account_ix = system_instruction::create_account( &self.context.borrow().payer.pubkey(), &account_keypair.pubkey(), - account_rent, + lamports, 0, &system_program::id(), ); diff --git a/programs/gateway/tests/update_voter_weight_record_predecessor_plugin.rs b/programs/gateway/tests/update_voter_weight_record_predecessor_plugin.rs index 8f7dead4..56853bc0 100644 --- a/programs/gateway/tests/update_voter_weight_record_predecessor_plugin.rs +++ b/programs/gateway/tests/update_voter_weight_record_predecessor_plugin.rs @@ -163,7 +163,7 @@ async fn test_update_fails_with_predecessor_with_a_different_owner() -> Result<( let (realm_cookie, registrar_cookie, _, gateway_token_cookie, voter_cookie) = gateway_voter_test.setup(true).await?; - let different_voter_cookie = gateway_voter_test.bench.with_wallet().await; + let different_voter_cookie = gateway_voter_test.bench.with_wallet(None).await; // the voter weight record from the registered predecessor plugin (will give a constant weight) let predecessor_voter_weight_record_cookie = gateway_voter_test diff --git a/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs b/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs index 54d968f3..e027735d 100644 --- a/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs +++ b/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs @@ -16,7 +16,7 @@ pub struct CreateGovernanceTokenHoldingAccount<'info> { seeds = [ b"nft-power-holding-account".as_ref(), realm.key().as_ref(), realm_governing_token_mint.key().as_ref(), - nft.key().as_ref()], + nft_mint.key().as_ref()], bump, payer = payer, token::mint = realm_governing_token_mint, @@ -34,13 +34,14 @@ pub struct CreateGovernanceTokenHoldingAccount<'info> { pub realm: UncheckedAccount<'info>, /// Either the realm community mint or the council mint. + // TODO revert when you can figure out how to correctly set up/verify the owning program pub realm_governing_token_mint: Account<'info, Mint>, - + // pub realm_governing_token_mint: UncheckedAccount<'info>, #[account(mut)] pub payer: Signer<'info>, //TODO add constraint that the nft is the one configured for a realm collection - pub nft: Account<'info, Mint>, + pub nft_mint: Account<'info, Mint>, pub associated_token_program: Program<'info, AssociatedToken>, pub token_program: Program<'info, Token>, @@ -52,8 +53,6 @@ pub struct CreateGovernanceTokenHoldingAccount<'info> { pub fn create_governance_token_holding_account( ctx: Context, ) -> Result<()> { - //TODO extract reused stuff to variables - // Deserialize the Realm to validate it let _realm = realm::get_realm_data_for_governing_token_mint( &ctx.accounts.governance_program_id.key(), @@ -61,22 +60,5 @@ pub fn create_governance_token_holding_account( &ctx.accounts.realm_governing_token_mint.key(), )?; - // create_spl_token_account_signed( - // &ctx.accounts.payer, - // &ctx.accounts.realm_governing_token_mint.to_account_info(), - // &get_governing_token_holding_address_seeds( - // &ctx.accounts.realm.key(), - // &ctx.accounts.realm_governing_token_mint.key(), - // &ctx.accounts.nft.key(), - // ), - // &ctx.accounts.realm_governing_token_mint.to_account_info(), - // &ctx.accounts.realm, - // &ctx.accounts.governance_program_id.key(), - // &ctx.accounts.system_program, - // &ctx.accounts.realm_governing_token_mint.to_account_info(), - // &ctx.accounts.rent.to_account_info(), - // &ctx.accounts.rent - // )?; - Ok(()) } diff --git a/programs/nft-voter/src/instructions/deposit_governance_tokens.rs b/programs/nft-voter/src/instructions/deposit_governance_tokens.rs new file mode 100644 index 00000000..6e2bce65 --- /dev/null +++ b/programs/nft-voter/src/instructions/deposit_governance_tokens.rs @@ -0,0 +1,145 @@ +use anchor_lang::prelude::*; +use anchor_spl::token::{Mint, Token, TokenAccount}; +use solana_program::{entrypoint::ProgramResult, program::invoke}; +use spl_governance::state::realm; + +use crate::{state::nft_voter_token_owner_record::NftVoterTokenOwnerRecord, id}; + +/// Deposits tokens into the holding account for a given NFT to boost its voting power +#[derive(Accounts)] +pub struct DepositGovernanceTokens<'info> { + #[account( + init_if_needed, + seeds = [b"nft-voter-token-owner-record".as_ref(), + realm.key().as_ref(), + realm_governing_token_mint.key().as_ref(), + nft_mint.key().as_ref(), + governing_token_owner.key().as_ref() + ], + bump, + payer = governing_token_owner, + space = NftVoterTokenOwnerRecord::get_space() + )] + pub token_owner_record: Account<'info, NftVoterTokenOwnerRecord>, + + #[account( + seeds = [ b"nft-power-holding-account".as_ref(), + realm.key().as_ref(), + realm_governing_token_mint.key().as_ref(), + nft_mint.key().as_ref()], + bump, + token::mint = realm_governing_token_mint, + token::authority = governance_program_id + )] + pub holding_account_info: Account<'info, TokenAccount>, + + /// The program id of the spl-governance program the realm belongs to + /// CHECK: Can be any instance of spl-governance and it's not known at the compilation time + #[account(executable)] + pub governance_program_id: UncheckedAccount<'info>, + + /// CHECK: Owned by spl-governance instance specified in governance_program_id + #[account(owner = governance_program_id.key())] + pub realm: UncheckedAccount<'info>, + + /// Either the realm community mint or the council mint. + // TODO revert when you can figure out how to correctly set up/verify the owning program + pub realm_governing_token_mint: Account<'info, Mint>, + // pub realm_governing_token_mint: UncheckedAccount<'info>, + #[account(mut)] + pub governing_token_owner: Signer<'info>, + + //TODO add constraint that the nft is the one configured for a realm collection + pub nft_mint: Account<'info, Mint>, + + #[account(constraint = governing_token_source_account.owner == governing_token_owner.key())] + pub governing_token_source_account: Account<'info, TokenAccount>, + + pub system_program: Program<'info, System>, + pub token_program: Program<'info, Token>, +} + +/// Deposits tokens into the holding account for a given NFT to boost its voting power +pub fn deposit_governance_tokens(ctx: Context, amount: u64) -> Result<()> { + // Deserialize the Realm to validate it + let _realm = realm::get_realm_data_for_governing_token_mint( + &ctx.accounts.governance_program_id.key(), + &ctx.accounts.realm, + &ctx.accounts.realm_governing_token_mint.key(), + )?; + + let token_owner_record = &mut ctx.accounts.token_owner_record; + token_owner_record.set_inner(NftVoterTokenOwnerRecord { + realm: ctx.accounts.realm.key(), + governing_token_mint: ctx.accounts.realm_governing_token_mint.key(), + nft_mint: ctx.accounts.nft_mint.key(), + governing_token_owner: ctx.accounts.governing_token_owner.key(), + governing_token_deposit_amount: token_owner_record + .governing_token_deposit_amount + .checked_add(amount) + .unwrap(), + }); + + spl_governance::tools::spl_token::transfer_spl_tokens_signed( + &ctx.accounts + .governing_token_source_account + .to_account_info(), + &ctx.accounts.holding_account_info.to_account_info(), + &ctx.accounts.governing_token_owner, + //TODO WTF is this? + &[ctx.accounts.governing_token_owner.signer_key().unwrap().as_ref()], + &ctx.accounts.governance_program_id.key(), + amount, + &ctx.accounts.realm_governing_token_mint.to_account_info(), + ) + .unwrap(); + // spl_governance::tools::spl_token::transfer_spl_tokens( + // &ctx.accounts + // .governing_token_source_account + // .to_account_info(), + // &ctx.accounts.holding_account_info.to_account_info(), + // &ctx.accounts.governing_token_owner.to_account_info(), + // amount, + // &ctx.accounts.realm_governing_token_mint.to_account_info(), + // ) + // .unwrap(); + + let token_owner_record = &mut ctx.accounts.token_owner_record; + + token_owner_record.governing_token_deposit_amount = token_owner_record + .governing_token_deposit_amount + .checked_add(amount) + .unwrap(); + + Ok(()) +} + +fn transfer_spl_tokens<'a>( + source_info: &AccountInfo<'a>, + destination_info: &AccountInfo<'a>, + authority_info: &AccountInfo<'a>, + amount: u64, + spl_token_info: &AccountInfo<'a>, +) -> ProgramResult { + let transfer_instruction = spl_token::instruction::transfer( + &spl_token::id(), + source_info.key, + destination_info.key, + authority_info.key, + &[authority_info.key], + amount, + ) + .unwrap(); + + invoke( + &transfer_instruction, + &[ + spl_token_info.clone(), + authority_info.clone(), + source_info.clone(), + destination_info.clone(), + ], + )?; + + Ok(()) +} diff --git a/programs/nft-voter/src/instructions/mod.rs b/programs/nft-voter/src/instructions/mod.rs index e5c26c6e..17d93745 100644 --- a/programs/nft-voter/src/instructions/mod.rs +++ b/programs/nft-voter/src/instructions/mod.rs @@ -21,3 +21,6 @@ mod cast_nft_vote; pub use create_governance_token_holding_account::*; mod create_governance_token_holding_account; + +pub use deposit_governance_tokens::*; +mod deposit_governance_tokens; diff --git a/programs/nft-voter/src/lib.rs b/programs/nft-voter/src/lib.rs index 9e10b6f3..bf140dbe 100644 --- a/programs/nft-voter/src/lib.rs +++ b/programs/nft-voter/src/lib.rs @@ -68,6 +68,14 @@ pub mod nft_voter { log_version(); instructions::create_governance_token_holding_account(ctx) } + + pub fn deposit_governance_tokens( + ctx: Context, + amount: u64, + ) -> Result<()> { + log_version(); + instructions::deposit_governance_tokens(ctx, amount) + } } fn log_version() { diff --git a/programs/nft-voter/src/state/mod.rs b/programs/nft-voter/src/state/mod.rs index 4084a580..0e146625 100644 --- a/programs/nft-voter/src/state/mod.rs +++ b/programs/nft-voter/src/state/mod.rs @@ -13,3 +13,6 @@ pub use voter_weight_record::*; pub mod voter_weight_record; pub mod idl_types; + +pub use nft_voter_token_owner_record::*; +pub mod nft_voter_token_owner_record; diff --git a/programs/nft-voter/src/state/nft_voter_token_owner_record.rs b/programs/nft-voter/src/state/nft_voter_token_owner_record.rs new file mode 100644 index 00000000..d903e504 --- /dev/null +++ b/programs/nft-voter/src/state/nft_voter_token_owner_record.rs @@ -0,0 +1,108 @@ +use anchor_lang::prelude::*; + +use crate::{id, tools::anchor::DISCRIMINATOR_SIZE}; + +/// Receipt for depositing tokens against a voting NFT to enable +/// token owners to withdraw tokens from the NFT's holding account +#[account] +#[derive(Debug, Copy, PartialEq)] +pub struct NftVoterTokenOwnerRecord { + /// The Realm the TokenOwnerRecord belongs to + pub realm: Pubkey, + + /// Governing Token Mint the TokenOwnerRecord holds deposit for + pub governing_token_mint: Pubkey, + + /// The mint of the NFT being backed + pub nft_mint: Pubkey, + + /// The owner (either single or multisig) of the deposited governing SPL Tokens + /// This is who can authorize a withdrawal of the tokens + pub governing_token_owner: Pubkey, + + /// The amount of governing tokens deposited into the NFT's holding account + /// This amount affects the voter weight used when voting on proposals + pub governing_token_deposit_amount: u64, +} + +impl NftVoterTokenOwnerRecord { + pub fn get_space() -> usize { + DISCRIMINATOR_SIZE + + 32 + // realm + 32 + // governing token mint + 32 + // nft mint + 32 + // governing token owner + 8 // deposit amount + } +} + +impl Default for NftVoterTokenOwnerRecord { + fn default() -> Self { + Self { + realm: Default::default(), + governing_token_mint: Default::default(), + nft_mint: Default::default(), + governing_token_owner: Default::default(), + governing_token_deposit_amount: 0, + } + } +} + +/// Returns NftVoterTokenOwnerRecord PDA seeds +pub fn get_nft_voter_token_owner_record_seeds<'a>( + realm: &'a Pubkey, + governing_token_mint: &'a Pubkey, + nft_mint: &'a Pubkey, + governing_token_owner: &'a Pubkey, +) -> [&'a [u8]; 5] { + [ + b"nft-voter-token-owner-record", + realm.as_ref(), + governing_token_mint.as_ref(), + nft_mint.as_ref(), + governing_token_owner.as_ref(), + ] +} + +/// Returns NftVoterTokenOwnerRecord PDA address +pub fn get_nft_voter_token_owner_record_address( + realm: &Pubkey, + governing_token_mint: &Pubkey, + nft_mint: &Pubkey, + governing_token_owner: &Pubkey, +) -> Pubkey { + Pubkey::find_program_address( + &get_nft_voter_token_owner_record_seeds( + realm, + governing_token_mint, + nft_mint, + governing_token_owner, + ), + &id(), + ) + .0 +} + +#[cfg(test)] +mod test { + + use crate::tools::anchor::DISCRIMINATOR_SIZE; + + use super::*; + + #[test] + fn test_get_space() { + // Arrange + let expected_space = NftVoterTokenOwnerRecord::get_space(); + + // Act + let actual_space = DISCRIMINATOR_SIZE + + NftVoterTokenOwnerRecord::default() + .try_to_vec() + .unwrap() + .len(); + + // Assert + assert_eq!(expected_space, actual_space); + } +} diff --git a/programs/nft-voter/tests/cast_nft_vote.rs b/programs/nft-voter/tests/cast_nft_vote.rs index 9b4b6b19..de2f993f 100644 --- a/programs/nft-voter/tests/cast_nft_vote.rs +++ b/programs/nft-voter/tests/cast_nft_vote.rs @@ -36,7 +36,7 @@ async fn test_cast_nft_vote() -> Result<(), TransportError> { ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -126,7 +126,7 @@ async fn test_cast_nft_vote_with_multiple_nfts() -> Result<(), TransportError> { ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -224,7 +224,7 @@ async fn test_cast_nft_vote_with_nft_already_voted_error() -> Result<(), Transpo ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -307,7 +307,7 @@ async fn test_cast_nft_vote_invalid_voter_error() -> Result<(), TransportError> ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -328,7 +328,7 @@ async fn test_cast_nft_vote_invalid_voter_error() -> Result<(), TransportError> .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) .await?; - let voter_cookie2 = nft_voter_test.bench.with_wallet().await; + let voter_cookie2 = nft_voter_test.bench.with_wallet(None).await; // Act @@ -380,7 +380,7 @@ async fn test_cast_nft_vote_with_unverified_collection_error() -> Result<(), Tra ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -458,7 +458,7 @@ async fn test_cast_nft_vote_with_invalid_owner_error() -> Result<(), TransportEr ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -469,7 +469,7 @@ async fn test_cast_nft_vote_with_invalid_owner_error() -> Result<(), TransportEr .with_voter_weight_record(®istrar_cookie, &voter_cookie) .await?; - let voter_cookie2 = nft_voter_test.bench.with_wallet().await; + let voter_cookie2 = nft_voter_test.bench.with_wallet(None).await; let proposal_cookie = nft_voter_test .governance @@ -535,7 +535,7 @@ async fn test_cast_nft_vote_with_invalid_collection_error() -> Result<(), Transp .with_proposal(&realm_cookie) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -607,7 +607,7 @@ async fn test_cast_nft_vote_with_invalid_metadata_error() -> Result<(), Transpor .with_proposal(&realm_cookie) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -689,7 +689,7 @@ async fn test_cast_nft_vote_with_same_nft_error() -> Result<(), TransportError> .with_proposal(&realm_cookie) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -755,7 +755,7 @@ async fn test_cast_nft_vote_with_no_nft_error() -> Result<(), TransportError> { ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -832,7 +832,7 @@ async fn test_cast_nft_vote_with_max_5_nfts() -> Result<(), TransportError> { ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -935,7 +935,7 @@ async fn test_cast_nft_vote_using_multiple_instructions() -> Result<(), Transpor ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -1044,7 +1044,7 @@ async fn test_cast_nft_vote_using_multiple_instructions_with_nft_already_voted_e ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -1133,7 +1133,7 @@ async fn test_cast_nft_vote_using_multiple_instructions_with_attempted_sandwiche ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance diff --git a/programs/nft-voter/tests/create_governance_token_holding_account.rs b/programs/nft-voter/tests/create_governance_token_holding_account.rs index bc25fbb6..d0305652 100644 --- a/programs/nft-voter/tests/create_governance_token_holding_account.rs +++ b/programs/nft-voter/tests/create_governance_token_holding_account.rs @@ -1,5 +1,5 @@ use program_test::nft_voter_test::NftVoterTest; -use solana_program::{program_option::COption}; +use solana_program::program_option::COption; use solana_program_test::*; use solana_sdk::transport::TransportError; use spl_token::state::AccountState; @@ -15,7 +15,7 @@ async fn test_create_governance_token_holding_account() -> Result<(), TransportE let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; @@ -42,12 +42,16 @@ async fn test_create_governance_token_holding_account() -> Result<(), TransportE ); assert_eq_formatted( 0, - governance_token_holding_account_cookie.account.delegated_amount, + governance_token_holding_account_cookie + .account + .delegated_amount, "delegated_amount", ); assert_eq_formatted( COption::None, - governance_token_holding_account_cookie.account.close_authority, + governance_token_holding_account_cookie + .account + .close_authority, "close_authority", ); assert_eq_formatted( @@ -69,25 +73,44 @@ async fn test_create_governance_token_holding_account() -> Result<(), TransportE Ok(()) } - #[tokio::test] -async fn test_create_governance_token_holding_account_nft_is_not_part_of_configured_collection_errors() -> Result<(), TransportError> { +async fn test_create_governance_token_holding_account_nft_is_not_part_of_configured_collection_errors( +) -> Result<(), TransportError> { // Arrange let mut nft_voter_test = NftVoterTest::start_new().await; let realm_cookie = nft_voter_test.governance.with_realm().await?; let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - + let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - let max_voter_weight_record = nft_voter_test.with_max_voter_weight_record(®istrar_cookie).await?; - assert_eq!(0, registrar_cookie.account.collection_configs.len(), "Unexpected collection already configured for registrar"); - nft_voter_test.with_collection(®istrar_cookie, &nft_collection_cookie, &max_voter_weight_record, None).await?; - let registrar_updated = nft_voter_test.get_registrar_account(®istrar_cookie.address).await; - assert_eq!(1, registrar_updated.collection_configs.len(), "Unable to add collection to registrar"); - - let voter_cookie = nft_voter_test.bench.with_wallet().await; - + let max_voter_weight_record = nft_voter_test + .with_max_voter_weight_record(®istrar_cookie) + .await?; + assert_eq!( + 0, + registrar_cookie.account.collection_configs.len(), + "Unexpected collection already configured for registrar" + ); + nft_voter_test + .with_collection( + ®istrar_cookie, + &nft_collection_cookie, + &max_voter_weight_record, + None, + ) + .await?; + let registrar_updated = nft_voter_test + .get_registrar_account(®istrar_cookie.address) + .await; + assert_eq!( + 1, + registrar_updated.collection_configs.len(), + "Unable to add collection to registrar" + ); + + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let nft_cookie = nft_voter_test .token_metadata .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) @@ -107,9 +130,9 @@ async fn test_create_governance_token_holding_account_nft_is_not_part_of_configu Ok(()) } - #[tokio::test] -async fn test_create_governance_token_holding_account_already_exists_errors() -> Result<(), TransportError> { +async fn test_create_governance_token_holding_account_already_exists_errors( +) -> Result<(), TransportError> { // Arrange let mut nft_voter_test = NftVoterTest::start_new().await; @@ -117,7 +140,7 @@ async fn test_create_governance_token_holding_account_already_exists_errors() -> let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(Some(10000000000000)).await; let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; @@ -142,7 +165,6 @@ async fn test_create_governance_token_holding_account_already_exists_errors() -> Ok(()) } - fn assert_eq_formatted( expected: T, actual: T, diff --git a/programs/nft-voter/tests/create_voter_weight_record.rs b/programs/nft-voter/tests/create_voter_weight_record.rs index c23d7858..fef339b8 100644 --- a/programs/nft-voter/tests/create_voter_weight_record.rs +++ b/programs/nft-voter/tests/create_voter_weight_record.rs @@ -15,7 +15,7 @@ async fn test_create_voter_weight_record() -> Result<(), TransportError> { let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; // Act let voter_weight_record_cookie = nft_voter_test @@ -44,7 +44,7 @@ async fn test_create_voter_weight_record_with_invalid_realm_error() -> Result<() let realm_cookie2 = nft_voter_test.governance.with_realm().await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; // Act let err = nft_voter_test @@ -74,7 +74,7 @@ async fn test_create_voter_weight_record_with_invalid_mint_error() -> Result<(), let realm_cookie2 = nft_voter_test.governance.with_realm().await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; // Act let err = nft_voter_test @@ -102,7 +102,7 @@ async fn test_create_voter_weight_record_with_already_exists_error() -> Result<( let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) diff --git a/programs/nft-voter/tests/deposit_governance_tokens.rs b/programs/nft-voter/tests/deposit_governance_tokens.rs new file mode 100644 index 00000000..18af6730 --- /dev/null +++ b/programs/nft-voter/tests/deposit_governance_tokens.rs @@ -0,0 +1,199 @@ +use anchor_spl::token::Mint; +use program_test::nft_voter_test::NftVoterTest; +use program_test::program_test_bench::MintCookie; +use solana_program_test::*; +use solana_sdk::transport::TransportError; + +mod program_test; + +#[tokio::test] +async fn test_deposit_governance_tokens_first_deposit_creates_record() -> Result<(), TransportError> +{ + // Arrange + let mut nft_voter_test = NftVoterTest::start_new().await; + + let realm_cookie = nft_voter_test.governance.with_realm().await?; + + let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + + let owner_cookie = nft_voter_test.bench.with_wallet(Some(100000000000)).await; + + let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; + + let nft_cookie = nft_voter_test + .token_metadata + .with_nft_v2(&nft_collection_cookie, &owner_cookie, None) + .await?; + + let governance_token_holding_account_cookie = nft_voter_test + .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) + .await?; + + let governing_token_source_account_cookie = nft_voter_test + .bench + .with_tokens( + &realm_cookie.community_mint_cookie, + &owner_cookie.address, + 1000, + ) + .await?; + + // Act + + let token_owner_record = nft_voter_test + .with_nft_voter_token_owner_record( + &realm_cookie, + &nft_cookie, + &governance_token_holding_account_cookie, + &owner_cookie, + &governing_token_source_account_cookie, + ) + .await?; + + // Assert + // assert_eq_formatted( + // 0, + // governance_token_holding_account_cookie.account.amount, + // "amount", + // ); + // assert_eq_formatted( + // COption::None, + // governance_token_holding_account_cookie.account.delegate, + // "delegate", + // ); + // assert_eq_formatted( + // 0, + // governance_token_holding_account_cookie + // .account + // .delegated_amount, + // "delegated_amount", + // ); + // assert_eq_formatted( + // COption::None, + // governance_token_holding_account_cookie + // .account + // .close_authority, + // "close_authority", + // ); + // assert_eq_formatted( + // realm_cookie.community_mint_cookie.address, + // governance_token_holding_account_cookie.account.mint, + // "mint", + // ); + // assert_eq_formatted( + // registrar_cookie.account.governance_program_id, + // governance_token_holding_account_cookie.account.owner, + // "owner", + // ); + // assert_eq_formatted( + // AccountState::Initialized, + // governance_token_holding_account_cookie.account.state, + // "state", + // ); + + Ok(()) +} + +#[tokio::test] +async fn test_create_governance_token_holding_account_nft_is_not_part_of_configured_collection_errors( +) -> Result<(), TransportError> { + // Arrange + let mut nft_voter_test = NftVoterTest::start_new().await; + + let realm_cookie = nft_voter_test.governance.with_realm().await?; + + let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + + let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; + let max_voter_weight_record = nft_voter_test + .with_max_voter_weight_record(®istrar_cookie) + .await?; + assert_eq!( + 0, + registrar_cookie.account.collection_configs.len(), + "Unexpected collection already configured for registrar" + ); + nft_voter_test + .with_collection( + ®istrar_cookie, + &nft_collection_cookie, + &max_voter_weight_record, + None, + ) + .await?; + let registrar_updated = nft_voter_test + .get_registrar_account(®istrar_cookie.address) + .await; + assert_eq!( + 1, + registrar_updated.collection_configs.len(), + "Unable to add collection to registrar" + ); + + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + + let nft_cookie = nft_voter_test + .token_metadata + .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .await?; + + // Act + //TODO add validation to the instruction and/or method and expect it to throw + let error = nft_voter_test + .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) + .await + .err(); + + // Assert + //TODO + // assert!(error.is_some()); + + Ok(()) +} + +#[tokio::test] +async fn test_create_governance_token_holding_account_already_exists_errors( +) -> Result<(), TransportError> { + // Arrange + let mut nft_voter_test = NftVoterTest::start_new().await; + + let realm_cookie = nft_voter_test.governance.with_realm().await?; + + let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + + let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; + + let nft_cookie = nft_voter_test + .token_metadata + .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .await?; + + // Act + nft_voter_test + .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) + .await?; + + let error = nft_voter_test + .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) + .await + .err(); + + // Assert + assert!(error.is_some()); + + Ok(()) +} + +fn assert_eq_formatted( + expected: T, + actual: T, + name: &str, +) -> () { + assert_eq!( + expected, actual, + "{} not equal: expected {:?} but got {:?}", + name, expected, actual + ); +} diff --git a/programs/nft-voter/tests/program_test/nft_voter_test.rs b/programs/nft-voter/tests/program_test/nft_voter_test.rs index bdb3fcb9..9de58913 100644 --- a/programs/nft-voter/tests/program_test/nft_voter_test.rs +++ b/programs/nft-voter/tests/program_test/nft_voter_test.rs @@ -31,6 +31,8 @@ use crate::program_test::program_test_bench::WalletCookie; use crate::program_test::token_metadata_test::{NftCollectionCookie, NftCookie, TokenMetadataTest}; use crate::program_test::tools::NopOverride; +use super::program_test_bench::TokenAccountCookie; + #[derive(Debug, PartialEq)] pub struct RegistrarCookie { pub address: Pubkey, @@ -70,6 +72,12 @@ pub struct GovernanceTokenHoldingAccountCookie { pub account: TokenAccount, } +#[derive(Debug, PartialEq)] +pub struct NftVoterTokenOwnerRecordCookie { + pub address: Pubkey, + pub account: NftVoterTokenOwnerRecord, +} + #[derive(Debug, PartialEq)] pub struct NftVoteRecordCookie { pub address: Pubkey, @@ -644,7 +652,7 @@ impl NftVoterTest { payer: self.bench.payer.pubkey(), system_program: solana_sdk::system_program::id(), holding_account_info: holding_account_key, - nft: nft_cookie.mint_cookie.address, + nft_mint: nft_cookie.mint_cookie.address, associated_token_program: anchor_spl::associated_token::ID, token_program: spl_token::id(), rent: Pubkey::from_str("SysvarRent111111111111111111111111111111111") @@ -660,7 +668,10 @@ impl NftVoterTest { instruction_override(&mut create_governing_token_holding_account_ix); self.bench - .process_transaction(&[create_governing_token_holding_account_ix], Some(&[&self.bench.payer])) + .process_transaction( + &[create_governing_token_holding_account_ix], + Some(&[&self.bench.payer]), + ) .await?; let account = self.bench.get_anchor_account(holding_account_key).await; @@ -670,4 +681,124 @@ impl NftVoterTest { account, }) } + + #[allow(dead_code)] + pub async fn with_nft_voter_token_owner_record( + &self, + realm_cookie: &RealmCookie, + nft_cookie: &NftCookie, + governing_token_holding_account_cookie: &GovernanceTokenHoldingAccountCookie, + governing_token_owner_cookie: &WalletCookie, + governing_token_source_cookie: &TokenAccountCookie, + ) -> Result { + self.with_nft_voter_token_owner_record_using_ix( + realm_cookie, + nft_cookie, + governing_token_holding_account_cookie, + governing_token_owner_cookie, + governing_token_source_cookie, + NopOverride, + ) + .await + } + + #[allow(dead_code)] + pub async fn with_nft_voter_token_owner_record_using_ix( + &self, + realm_cookie: &RealmCookie, + nft_cookie: &NftCookie, + governing_token_holding_account_cookie: &GovernanceTokenHoldingAccountCookie, + governing_token_owner_cookie: &WalletCookie, + governing_token_source_cookie: &TokenAccountCookie, + instruction_override: F, + ) -> Result { + let (token_owner_record_pubkey, _) = Pubkey::find_program_address( + &get_nft_voter_token_owner_record_seeds( + &realm_cookie.address, + &realm_cookie.account.community_mint, + &nft_cookie.mint_cookie.address, + &governing_token_owner_cookie.address, + ), + &gpl_nft_voter::id(), + ); + + let data = anchor_lang::InstructionData::data( + &gpl_nft_voter::instruction::DepositGovernanceTokens { amount: 0 }, + ); + + let accounts = gpl_nft_voter::accounts::DepositGovernanceTokens { + token_owner_record: token_owner_record_pubkey, + holding_account_info: governing_token_holding_account_cookie.address, + governance_program_id: self.governance.program_id, + realm: realm_cookie.address, + realm_governing_token_mint: realm_cookie.community_mint_cookie.address, + governing_token_owner: governing_token_owner_cookie.address, + nft_mint: nft_cookie.mint_cookie.address, + governing_token_source_account: governing_token_source_cookie.address, + system_program: solana_sdk::system_program::id(), + token_program: spl_token::id(), + }; + + println!( + "DEPOSIT ACCOUNTS token_owner_record {:?}", + accounts.token_owner_record + ); + println!( + "DEPOSIT ACCOUNTS holding_account_info {:?}", + accounts.holding_account_info + ); + println!( + "DEPOSIT ACCOUNTS governance_program_id {:?}", + accounts.governance_program_id + ); + println!("DEPOSIT ACCOUNTS realm {:?}", accounts.realm); + println!( + "DEPOSIT ACCOUNTS realm_governing_token_mint {:?}", + accounts.realm_governing_token_mint + ); + println!( + "DEPOSIT ACCOUNTS governing_token_owner {:?}", + accounts.governing_token_owner + ); + println!("DEPOSIT ACCOUNTS nft_mint {:?}", accounts.nft_mint); + println!( + "DEPOSIT ACCOUNTS governing_token_source_account {:?}", + accounts.governing_token_source_account + ); + println!( + "DEPOSIT ACCOUNTS system_program {:?}", + accounts.system_program + ); + println!( + "DEPOSIT ACCOUNTS token_program {:?}", + accounts.token_program + ); + + let mut deposit_governing_token_ix = Instruction { + program_id: gpl_nft_voter::id(), + accounts: anchor_lang::ToAccountMetas::to_account_metas(&accounts, None), + data, + }; + + instruction_override(&mut deposit_governing_token_ix); + + println!("PROCESSING TRANSACTION"); + self.bench + .process_transaction( + &[deposit_governing_token_ix], + Some(&[&governing_token_owner_cookie.signer]), + ) + .await?; + + println!("GETTING ACCOUNT"); + let account = self + .bench + .get_anchor_account(token_owner_record_pubkey) + .await; + + Ok(NftVoterTokenOwnerRecordCookie { + address: token_owner_record_pubkey, + account, + }) + } } diff --git a/programs/nft-voter/tests/program_test/program_test_bench.rs b/programs/nft-voter/tests/program_test/program_test_bench.rs index 150a6009..442f94e1 100644 --- a/programs/nft-voter/tests/program_test/program_test_bench.rs +++ b/programs/nft-voter/tests/program_test/program_test_bench.rs @@ -253,14 +253,15 @@ impl ProgramTestBench { } #[allow(dead_code)] - pub async fn with_wallet(&self) -> WalletCookie { + pub async fn with_wallet(&self, lamports: Option) -> WalletCookie { let account_rent = self.rent.minimum_balance(0); let account_keypair = Keypair::new(); + let lamports = account_rent.checked_add(lamports.unwrap_or(0)).unwrap(); let create_account_ix = system_instruction::create_account( &self.context.borrow().payer.pubkey(), &account_keypair.pubkey(), - account_rent, + lamports, 0, &system_program::id(), ); diff --git a/programs/nft-voter/tests/program_test/token_metadata_test.rs b/programs/nft-voter/tests/program_test/token_metadata_test.rs index a99eb127..6fa52caf 100644 --- a/programs/nft-voter/tests/program_test/token_metadata_test.rs +++ b/programs/nft-voter/tests/program_test/token_metadata_test.rs @@ -151,7 +151,7 @@ impl TokenMetadataTest { amount, } = args.unwrap_or_default(); - // Crate NFT + // Create NFT let mint_cookie = self.bench.with_mint().await?; let nft_account_cookie = self .bench diff --git a/programs/nft-voter/tests/relinquish_nft_vote.rs b/programs/nft-voter/tests/relinquish_nft_vote.rs index a942bb80..86858a7b 100644 --- a/programs/nft-voter/tests/relinquish_nft_vote.rs +++ b/programs/nft-voter/tests/relinquish_nft_vote.rs @@ -31,7 +31,7 @@ async fn test_relinquish_nft_vote() -> Result<(), TransportError> { ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -124,7 +124,7 @@ async fn test_relinquish_nft_vote_for_proposal_in_voting_state() -> Result<(), T ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -228,7 +228,7 @@ async fn test_relinquish_nft_vote_for_proposal_in_voting_state_and_vote_record_e ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -307,7 +307,7 @@ async fn test_relinquish_nft_vote_with_invalid_voter_error() -> Result<(), Trans ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -342,7 +342,7 @@ async fn test_relinquish_nft_vote_with_invalid_voter_error() -> Result<(), Trans .await?; // Try to use a different voter - let voter_cookie2 = nft_voter_test.bench.with_wallet().await; + let voter_cookie2 = nft_voter_test.bench.with_wallet(None).await; // Act @@ -394,7 +394,7 @@ async fn test_relinquish_nft_vote_with_unexpired_vote_weight_record() -> Result< ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let voter_token_owner_record_cookie = nft_voter_test .governance diff --git a/programs/nft-voter/tests/update_voter_weight_record.rs b/programs/nft-voter/tests/update_voter_weight_record.rs index d29809a2..900a69ce 100644 --- a/programs/nft-voter/tests/update_voter_weight_record.rs +++ b/programs/nft-voter/tests/update_voter_weight_record.rs @@ -36,7 +36,7 @@ async fn test_update_voter_weight_record() -> Result<(), TransportError> { ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let mut voter_weight_record_cookie = nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) @@ -104,7 +104,7 @@ async fn test_update_voter_weight_with_multiple_nfts() -> Result<(), TransportEr ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let mut voter_weight_record_cookie = nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) @@ -177,7 +177,7 @@ async fn test_update_voter_weight_with_cast_vote_not_allowed_error() -> Result<( ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let mut voter_weight_record_cookie = nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) @@ -233,7 +233,7 @@ async fn test_update_voter_weight_with_unverified_collection_error() -> Result<( ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let mut voter_weight_record_cookie = nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) @@ -297,13 +297,13 @@ async fn test_update_voter_weight_with_invalid_owner_error() -> Result<(), Trans ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let mut voter_weight_record_cookie = nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) .await?; - let voter_cookie2 = nft_voter_test.bench.with_wallet().await; + let voter_cookie2 = nft_voter_test.bench.with_wallet(None).await; let nft1_cookie = nft_voter_test .token_metadata @@ -355,7 +355,7 @@ async fn test_update_voter_weight_with_invalid_collection_error() -> Result<(), ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let mut voter_weight_record_cookie = nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) @@ -413,7 +413,7 @@ async fn test_update_voter_weight_with_invalid_metadata_error() -> Result<(), Tr ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let mut voter_weight_record_cookie = nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) @@ -481,7 +481,7 @@ async fn test_update_voter_weight_with_same_nft_error() -> Result<(), TransportE ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let mut voter_weight_record_cookie = nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) @@ -538,7 +538,7 @@ async fn test_update_voter_weight_record_with_no_nft_error() -> Result<(), Trans ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let voter_cookie = nft_voter_test.bench.with_wallet(None).await; let mut voter_weight_record_cookie = nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) diff --git a/programs/realm-voter/tests/create_voter_weight_record.rs b/programs/realm-voter/tests/create_voter_weight_record.rs index 4b757b29..685523b6 100644 --- a/programs/realm-voter/tests/create_voter_weight_record.rs +++ b/programs/realm-voter/tests/create_voter_weight_record.rs @@ -15,7 +15,7 @@ async fn test_create_voter_weight_record() -> Result<(), TransportError> { let registrar_cookie = realm_voter_test.with_registrar(&realm_cookie).await?; - let voter_cookie = realm_voter_test.bench.with_wallet().await; + let voter_cookie = realm_voter_test.bench.with_wallet(None).await; // Act let voter_weight_record_cookie = realm_voter_test @@ -42,7 +42,7 @@ async fn test_create_voter_weight_record_with_already_exists_error() -> Result<( let registrar_cookie = realm_voter_test.with_registrar(&realm_cookie).await?; - let voter_cookie = realm_voter_test.bench.with_wallet().await; + let voter_cookie = realm_voter_test.bench.with_wallet(None).await; realm_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) diff --git a/programs/realm-voter/tests/program_test/program_test_bench.rs b/programs/realm-voter/tests/program_test/program_test_bench.rs index 150a6009..442f94e1 100644 --- a/programs/realm-voter/tests/program_test/program_test_bench.rs +++ b/programs/realm-voter/tests/program_test/program_test_bench.rs @@ -253,14 +253,15 @@ impl ProgramTestBench { } #[allow(dead_code)] - pub async fn with_wallet(&self) -> WalletCookie { + pub async fn with_wallet(&self, lamports: Option) -> WalletCookie { let account_rent = self.rent.minimum_balance(0); let account_keypair = Keypair::new(); + let lamports = account_rent.checked_add(lamports.unwrap_or(0)).unwrap(); let create_account_ix = system_instruction::create_account( &self.context.borrow().payer.pubkey(), &account_keypair.pubkey(), - account_rent, + lamports, 0, &system_program::id(), ); diff --git a/programs/realm-voter/tests/update_voter_weight_record.rs b/programs/realm-voter/tests/update_voter_weight_record.rs index 11669387..a8aa9574 100644 --- a/programs/realm-voter/tests/update_voter_weight_record.rs +++ b/programs/realm-voter/tests/update_voter_weight_record.rs @@ -16,7 +16,7 @@ async fn test_update_voter_weight_record() -> Result<(), TransportError> { // Create TokenOwnerRecord for other Realm let realm_cookie2 = realm_voter_test.governance.with_realm().await?; - let token_owner_cookie = realm_voter_test.bench.with_wallet().await; + let token_owner_cookie = realm_voter_test.bench.with_wallet(None).await; let token_owner_record_cookie = realm_voter_test .governance .with_token_owner_record(&realm_cookie2, &token_owner_cookie) @@ -84,7 +84,7 @@ async fn test_update_voter_weight_record_with_token_owner_record_from_own_realm_ let registrar_cookie = realm_voter_test.with_registrar(&realm_cookie).await?; - let token_owner_cookie = realm_voter_test.bench.with_wallet().await; + let token_owner_cookie = realm_voter_test.bench.with_wallet(None).await; let token_owner_record_cookie = realm_voter_test .governance .with_token_owner_record(&realm_cookie, &token_owner_cookie) @@ -133,7 +133,7 @@ async fn test_update_voter_weight_record_for_member_from_not_configured_governan // Create TokenOwnerRecord for other Realm let realm_cookie2 = realm_voter_test.governance.with_realm().await?; - let token_owner_cookie = realm_voter_test.bench.with_wallet().await; + let token_owner_cookie = realm_voter_test.bench.with_wallet(None).await; let token_owner_record_cookie = realm_voter_test .governance .with_token_owner_record(&realm_cookie2, &token_owner_cookie) @@ -183,13 +183,13 @@ async fn test_update_voter_weight_record_with_token_owner_record_must_match_erro // Create TokenOwnerRecord for other Realm let realm_cookie2 = realm_voter_test.governance.with_realm().await?; - let token_owner_cookie = realm_voter_test.bench.with_wallet().await; + let token_owner_cookie = realm_voter_test.bench.with_wallet(None).await; let token_owner_record_cookie = realm_voter_test .governance .with_token_owner_record(&realm_cookie2, &token_owner_cookie) .await?; - let token_owner_cookie2 = realm_voter_test.bench.with_wallet().await; + let token_owner_cookie2 = realm_voter_test.bench.with_wallet(None).await; let mut voter_weight_record_cookie = realm_voter_test .with_voter_weight_record(®istrar_cookie, &token_owner_cookie2) From 580d4a39697dacc8e661e34d0bed57502d032d4a Mon Sep 17 00:00:00 2001 From: Austin Milt Date: Fri, 12 Aug 2022 14:38:15 -0600 Subject: [PATCH 03/17] move funding wallet to new method --- .../tests/create_voter_weight_record.rs | 4 +-- .../tests/program_test/gateway_voter_test.rs | 2 +- .../tests/program_test/program_test_bench.rs | 10 ++++-- ..._voter_weight_record_predecessor_plugin.rs | 2 +- programs/nft-voter/tests/cast_nft_vote.rs | 32 +++++++++---------- ...create_governance_token_holding_account.rs | 6 ++-- .../tests/create_voter_weight_record.rs | 8 ++--- .../tests/deposit_governance_tokens.rs | 6 ++-- .../tests/program_test/program_test_bench.rs | 10 ++++-- .../nft-voter/tests/relinquish_nft_vote.rs | 12 +++---- .../tests/update_voter_weight_record.rs | 20 ++++++------ .../tests/create_voter_weight_record.rs | 4 +-- .../tests/program_test/program_test_bench.rs | 10 ++++-- .../tests/update_voter_weight_record.rs | 10 +++--- 14 files changed, 74 insertions(+), 62 deletions(-) diff --git a/programs/gateway/tests/create_voter_weight_record.rs b/programs/gateway/tests/create_voter_weight_record.rs index cf6bb8b2..4033fd51 100644 --- a/programs/gateway/tests/create_voter_weight_record.rs +++ b/programs/gateway/tests/create_voter_weight_record.rs @@ -18,7 +18,7 @@ async fn test_create_voter_weight_record() -> Result<(), TransportError> { .with_registrar(&realm_cookie, &gateway_cookie, None) .await?; - let voter_cookie = gateway_voter_test.bench.with_wallet(None).await; + let voter_cookie = gateway_voter_test.bench.with_wallet().await; // Act let voter_weight_record_cookie = gateway_voter_test @@ -48,7 +48,7 @@ async fn test_create_voter_weight_record_with_already_exists_error() -> Result<( .with_registrar(&realm_cookie, &gateway_cookie, None) .await?; - let voter_cookie = gateway_voter_test.bench.with_wallet(None).await; + let voter_cookie = gateway_voter_test.bench.with_wallet().await; gateway_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) diff --git a/programs/gateway/tests/program_test/gateway_voter_test.rs b/programs/gateway/tests/program_test/gateway_voter_test.rs index 3c66fcaa..8f20617f 100644 --- a/programs/gateway/tests/program_test/gateway_voter_test.rs +++ b/programs/gateway/tests/program_test/gateway_voter_test.rs @@ -241,7 +241,7 @@ impl GatewayVoterTest { .with_registrar(&realm_cookie, &gateway_cookie, predecessor_program_id) .await?; // - let voter_cookie = self.bench.with_wallet(None).await; + let voter_cookie = self.bench.with_wallet().await; let gateway_token_cookie = self .with_gateway_token(&gateway_cookie, &voter_cookie) .await?; diff --git a/programs/gateway/tests/program_test/program_test_bench.rs b/programs/gateway/tests/program_test/program_test_bench.rs index 24a01556..63213c3c 100644 --- a/programs/gateway/tests/program_test/program_test_bench.rs +++ b/programs/gateway/tests/program_test/program_test_bench.rs @@ -256,10 +256,14 @@ impl ProgramTestBench { } #[allow(dead_code)] - pub async fn with_wallet(&self, lamports: Option) -> WalletCookie { + pub async fn with_wallet(&self) -> WalletCookie { let account_rent = self.rent.minimum_balance(0); + self.with_wallet_funded(account_rent).await + } + + #[allow(dead_code)] + pub async fn with_wallet_funded(&self, lamports: u64) -> WalletCookie { let account_keypair = Keypair::new(); - let lamports = account_rent.checked_add(lamports.unwrap_or(0)).unwrap(); let create_account_ix = system_instruction::create_account( &self.context.borrow().payer.pubkey(), @@ -274,7 +278,7 @@ impl ProgramTestBench { .unwrap(); let account = Account { - lamports: account_rent, + lamports, data: vec![], owner: system_program::id(), executable: false, diff --git a/programs/gateway/tests/update_voter_weight_record_predecessor_plugin.rs b/programs/gateway/tests/update_voter_weight_record_predecessor_plugin.rs index 56853bc0..8f7dead4 100644 --- a/programs/gateway/tests/update_voter_weight_record_predecessor_plugin.rs +++ b/programs/gateway/tests/update_voter_weight_record_predecessor_plugin.rs @@ -163,7 +163,7 @@ async fn test_update_fails_with_predecessor_with_a_different_owner() -> Result<( let (realm_cookie, registrar_cookie, _, gateway_token_cookie, voter_cookie) = gateway_voter_test.setup(true).await?; - let different_voter_cookie = gateway_voter_test.bench.with_wallet(None).await; + let different_voter_cookie = gateway_voter_test.bench.with_wallet().await; // the voter weight record from the registered predecessor plugin (will give a constant weight) let predecessor_voter_weight_record_cookie = gateway_voter_test diff --git a/programs/nft-voter/tests/cast_nft_vote.rs b/programs/nft-voter/tests/cast_nft_vote.rs index f02c8129..97e3934e 100644 --- a/programs/nft-voter/tests/cast_nft_vote.rs +++ b/programs/nft-voter/tests/cast_nft_vote.rs @@ -40,7 +40,7 @@ async fn test_cast_nft_vote() -> Result<(), TransportError> { ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -130,7 +130,7 @@ async fn test_cast_nft_vote_with_multiple_nfts() -> Result<(), TransportError> { ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -228,7 +228,7 @@ async fn test_cast_nft_vote_with_nft_already_voted_error() -> Result<(), Transpo ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -311,7 +311,7 @@ async fn test_cast_nft_vote_with_invalid_voter_error() -> Result<(), TransportEr ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -332,7 +332,7 @@ async fn test_cast_nft_vote_with_invalid_voter_error() -> Result<(), TransportEr .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) .await?; - let voter_cookie2 = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie2 = nft_voter_test.bench.with_wallet().await; // Act @@ -384,7 +384,7 @@ async fn test_cast_nft_vote_with_unverified_collection_error() -> Result<(), Tra ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -462,7 +462,7 @@ async fn test_cast_nft_vote_with_invalid_owner_error() -> Result<(), TransportEr ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -473,7 +473,7 @@ async fn test_cast_nft_vote_with_invalid_owner_error() -> Result<(), TransportEr .with_voter_weight_record(®istrar_cookie, &voter_cookie) .await?; - let voter_cookie2 = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie2 = nft_voter_test.bench.with_wallet().await; let proposal_cookie = nft_voter_test .governance @@ -539,7 +539,7 @@ async fn test_cast_nft_vote_with_invalid_collection_error() -> Result<(), Transp .with_proposal(&realm_cookie) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -611,7 +611,7 @@ async fn test_cast_nft_vote_with_invalid_metadata_error() -> Result<(), Transpor .with_proposal(&realm_cookie) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -693,7 +693,7 @@ async fn test_cast_nft_vote_with_same_nft_error() -> Result<(), TransportError> .with_proposal(&realm_cookie) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -759,7 +759,7 @@ async fn test_cast_nft_vote_with_no_nft_error() -> Result<(), TransportError> { ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -836,7 +836,7 @@ async fn test_cast_nft_vote_with_max_5_nfts() -> Result<(), TransportError> { ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -939,7 +939,7 @@ async fn test_cast_nft_vote_using_multiple_instructions() -> Result<(), Transpor ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -1048,7 +1048,7 @@ async fn test_cast_nft_vote_using_multiple_instructions_with_nft_already_voted_e ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -1137,7 +1137,7 @@ async fn test_cast_nft_vote_using_multiple_instructions_with_attempted_sandwiche ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance diff --git a/programs/nft-voter/tests/create_governance_token_holding_account.rs b/programs/nft-voter/tests/create_governance_token_holding_account.rs index d0305652..51d6c255 100644 --- a/programs/nft-voter/tests/create_governance_token_holding_account.rs +++ b/programs/nft-voter/tests/create_governance_token_holding_account.rs @@ -15,7 +15,7 @@ async fn test_create_governance_token_holding_account() -> Result<(), TransportE let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; @@ -109,7 +109,7 @@ async fn test_create_governance_token_holding_account_nft_is_not_part_of_configu "Unable to add collection to registrar" ); - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let nft_cookie = nft_voter_test .token_metadata @@ -140,7 +140,7 @@ async fn test_create_governance_token_holding_account_already_exists_errors( let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - let voter_cookie = nft_voter_test.bench.with_wallet(Some(10000000000000)).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; diff --git a/programs/nft-voter/tests/create_voter_weight_record.rs b/programs/nft-voter/tests/create_voter_weight_record.rs index fef339b8..c23d7858 100644 --- a/programs/nft-voter/tests/create_voter_weight_record.rs +++ b/programs/nft-voter/tests/create_voter_weight_record.rs @@ -15,7 +15,7 @@ async fn test_create_voter_weight_record() -> Result<(), TransportError> { let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; // Act let voter_weight_record_cookie = nft_voter_test @@ -44,7 +44,7 @@ async fn test_create_voter_weight_record_with_invalid_realm_error() -> Result<() let realm_cookie2 = nft_voter_test.governance.with_realm().await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; // Act let err = nft_voter_test @@ -74,7 +74,7 @@ async fn test_create_voter_weight_record_with_invalid_mint_error() -> Result<(), let realm_cookie2 = nft_voter_test.governance.with_realm().await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; // Act let err = nft_voter_test @@ -102,7 +102,7 @@ async fn test_create_voter_weight_record_with_already_exists_error() -> Result<( let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) diff --git a/programs/nft-voter/tests/deposit_governance_tokens.rs b/programs/nft-voter/tests/deposit_governance_tokens.rs index 18af6730..4ccb1475 100644 --- a/programs/nft-voter/tests/deposit_governance_tokens.rs +++ b/programs/nft-voter/tests/deposit_governance_tokens.rs @@ -16,7 +16,7 @@ async fn test_deposit_governance_tokens_first_deposit_creates_record() -> Result let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - let owner_cookie = nft_voter_test.bench.with_wallet(Some(100000000000)).await; + let owner_cookie = nft_voter_test.bench.with_wallet_funded(100000000000).await; let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; @@ -130,7 +130,7 @@ async fn test_create_governance_token_holding_account_nft_is_not_part_of_configu "Unable to add collection to registrar" ); - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let nft_cookie = nft_voter_test .token_metadata @@ -161,7 +161,7 @@ async fn test_create_governance_token_holding_account_already_exists_errors( let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; diff --git a/programs/nft-voter/tests/program_test/program_test_bench.rs b/programs/nft-voter/tests/program_test/program_test_bench.rs index 442f94e1..ba6b4425 100644 --- a/programs/nft-voter/tests/program_test/program_test_bench.rs +++ b/programs/nft-voter/tests/program_test/program_test_bench.rs @@ -253,10 +253,14 @@ impl ProgramTestBench { } #[allow(dead_code)] - pub async fn with_wallet(&self, lamports: Option) -> WalletCookie { + pub async fn with_wallet(&self) -> WalletCookie { let account_rent = self.rent.minimum_balance(0); + self.with_wallet_funded(account_rent).await + } + + #[allow(dead_code)] + pub async fn with_wallet_funded(&self, lamports: u64) -> WalletCookie { let account_keypair = Keypair::new(); - let lamports = account_rent.checked_add(lamports.unwrap_or(0)).unwrap(); let create_account_ix = system_instruction::create_account( &self.context.borrow().payer.pubkey(), @@ -271,7 +275,7 @@ impl ProgramTestBench { .unwrap(); let account = Account { - lamports: account_rent, + lamports, data: vec![], owner: system_program::id(), executable: false, diff --git a/programs/nft-voter/tests/relinquish_nft_vote.rs b/programs/nft-voter/tests/relinquish_nft_vote.rs index 64aaf407..4f6005c1 100644 --- a/programs/nft-voter/tests/relinquish_nft_vote.rs +++ b/programs/nft-voter/tests/relinquish_nft_vote.rs @@ -32,7 +32,7 @@ async fn test_relinquish_nft_vote() -> Result<(), TransportError> { ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -125,7 +125,7 @@ async fn test_relinquish_nft_vote_for_proposal_in_voting_state() -> Result<(), T ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -229,7 +229,7 @@ async fn test_relinquish_nft_vote_for_proposal_in_voting_state_and_vote_record_e ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -308,7 +308,7 @@ async fn test_relinquish_nft_vote_with_invalid_voter_error() -> Result<(), Trans ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance @@ -343,7 +343,7 @@ async fn test_relinquish_nft_vote_with_invalid_voter_error() -> Result<(), Trans .await?; // Try to use a different voter - let voter_cookie2 = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie2 = nft_voter_test.bench.with_wallet().await; // Act @@ -395,7 +395,7 @@ async fn test_relinquish_nft_vote_with_unexpired_vote_weight_record() -> Result< ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let voter_token_owner_record_cookie = nft_voter_test .governance diff --git a/programs/nft-voter/tests/update_voter_weight_record.rs b/programs/nft-voter/tests/update_voter_weight_record.rs index 900a69ce..d29809a2 100644 --- a/programs/nft-voter/tests/update_voter_weight_record.rs +++ b/programs/nft-voter/tests/update_voter_weight_record.rs @@ -36,7 +36,7 @@ async fn test_update_voter_weight_record() -> Result<(), TransportError> { ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let mut voter_weight_record_cookie = nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) @@ -104,7 +104,7 @@ async fn test_update_voter_weight_with_multiple_nfts() -> Result<(), TransportEr ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let mut voter_weight_record_cookie = nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) @@ -177,7 +177,7 @@ async fn test_update_voter_weight_with_cast_vote_not_allowed_error() -> Result<( ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let mut voter_weight_record_cookie = nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) @@ -233,7 +233,7 @@ async fn test_update_voter_weight_with_unverified_collection_error() -> Result<( ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let mut voter_weight_record_cookie = nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) @@ -297,13 +297,13 @@ async fn test_update_voter_weight_with_invalid_owner_error() -> Result<(), Trans ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let mut voter_weight_record_cookie = nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) .await?; - let voter_cookie2 = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie2 = nft_voter_test.bench.with_wallet().await; let nft1_cookie = nft_voter_test .token_metadata @@ -355,7 +355,7 @@ async fn test_update_voter_weight_with_invalid_collection_error() -> Result<(), ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let mut voter_weight_record_cookie = nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) @@ -413,7 +413,7 @@ async fn test_update_voter_weight_with_invalid_metadata_error() -> Result<(), Tr ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let mut voter_weight_record_cookie = nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) @@ -481,7 +481,7 @@ async fn test_update_voter_weight_with_same_nft_error() -> Result<(), TransportE ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let mut voter_weight_record_cookie = nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) @@ -538,7 +538,7 @@ async fn test_update_voter_weight_record_with_no_nft_error() -> Result<(), Trans ) .await?; - let voter_cookie = nft_voter_test.bench.with_wallet(None).await; + let voter_cookie = nft_voter_test.bench.with_wallet().await; let mut voter_weight_record_cookie = nft_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) diff --git a/programs/realm-voter/tests/create_voter_weight_record.rs b/programs/realm-voter/tests/create_voter_weight_record.rs index 685523b6..4b757b29 100644 --- a/programs/realm-voter/tests/create_voter_weight_record.rs +++ b/programs/realm-voter/tests/create_voter_weight_record.rs @@ -15,7 +15,7 @@ async fn test_create_voter_weight_record() -> Result<(), TransportError> { let registrar_cookie = realm_voter_test.with_registrar(&realm_cookie).await?; - let voter_cookie = realm_voter_test.bench.with_wallet(None).await; + let voter_cookie = realm_voter_test.bench.with_wallet().await; // Act let voter_weight_record_cookie = realm_voter_test @@ -42,7 +42,7 @@ async fn test_create_voter_weight_record_with_already_exists_error() -> Result<( let registrar_cookie = realm_voter_test.with_registrar(&realm_cookie).await?; - let voter_cookie = realm_voter_test.bench.with_wallet(None).await; + let voter_cookie = realm_voter_test.bench.with_wallet().await; realm_voter_test .with_voter_weight_record(®istrar_cookie, &voter_cookie) diff --git a/programs/realm-voter/tests/program_test/program_test_bench.rs b/programs/realm-voter/tests/program_test/program_test_bench.rs index 442f94e1..ba6b4425 100644 --- a/programs/realm-voter/tests/program_test/program_test_bench.rs +++ b/programs/realm-voter/tests/program_test/program_test_bench.rs @@ -253,10 +253,14 @@ impl ProgramTestBench { } #[allow(dead_code)] - pub async fn with_wallet(&self, lamports: Option) -> WalletCookie { + pub async fn with_wallet(&self) -> WalletCookie { let account_rent = self.rent.minimum_balance(0); + self.with_wallet_funded(account_rent).await + } + + #[allow(dead_code)] + pub async fn with_wallet_funded(&self, lamports: u64) -> WalletCookie { let account_keypair = Keypair::new(); - let lamports = account_rent.checked_add(lamports.unwrap_or(0)).unwrap(); let create_account_ix = system_instruction::create_account( &self.context.borrow().payer.pubkey(), @@ -271,7 +275,7 @@ impl ProgramTestBench { .unwrap(); let account = Account { - lamports: account_rent, + lamports, data: vec![], owner: system_program::id(), executable: false, diff --git a/programs/realm-voter/tests/update_voter_weight_record.rs b/programs/realm-voter/tests/update_voter_weight_record.rs index a8aa9574..11669387 100644 --- a/programs/realm-voter/tests/update_voter_weight_record.rs +++ b/programs/realm-voter/tests/update_voter_weight_record.rs @@ -16,7 +16,7 @@ async fn test_update_voter_weight_record() -> Result<(), TransportError> { // Create TokenOwnerRecord for other Realm let realm_cookie2 = realm_voter_test.governance.with_realm().await?; - let token_owner_cookie = realm_voter_test.bench.with_wallet(None).await; + let token_owner_cookie = realm_voter_test.bench.with_wallet().await; let token_owner_record_cookie = realm_voter_test .governance .with_token_owner_record(&realm_cookie2, &token_owner_cookie) @@ -84,7 +84,7 @@ async fn test_update_voter_weight_record_with_token_owner_record_from_own_realm_ let registrar_cookie = realm_voter_test.with_registrar(&realm_cookie).await?; - let token_owner_cookie = realm_voter_test.bench.with_wallet(None).await; + let token_owner_cookie = realm_voter_test.bench.with_wallet().await; let token_owner_record_cookie = realm_voter_test .governance .with_token_owner_record(&realm_cookie, &token_owner_cookie) @@ -133,7 +133,7 @@ async fn test_update_voter_weight_record_for_member_from_not_configured_governan // Create TokenOwnerRecord for other Realm let realm_cookie2 = realm_voter_test.governance.with_realm().await?; - let token_owner_cookie = realm_voter_test.bench.with_wallet(None).await; + let token_owner_cookie = realm_voter_test.bench.with_wallet().await; let token_owner_record_cookie = realm_voter_test .governance .with_token_owner_record(&realm_cookie2, &token_owner_cookie) @@ -183,13 +183,13 @@ async fn test_update_voter_weight_record_with_token_owner_record_must_match_erro // Create TokenOwnerRecord for other Realm let realm_cookie2 = realm_voter_test.governance.with_realm().await?; - let token_owner_cookie = realm_voter_test.bench.with_wallet(None).await; + let token_owner_cookie = realm_voter_test.bench.with_wallet().await; let token_owner_record_cookie = realm_voter_test .governance .with_token_owner_record(&realm_cookie2, &token_owner_cookie) .await?; - let token_owner_cookie2 = realm_voter_test.bench.with_wallet(None).await; + let token_owner_cookie2 = realm_voter_test.bench.with_wallet().await; let mut voter_weight_record_cookie = realm_voter_test .with_voter_weight_record(®istrar_cookie, &token_owner_cookie2) From b49f58ba03e54f27c8303eeaa610718f81b3989c Mon Sep 17 00:00:00 2001 From: Austin Milt Date: Fri, 12 Aug 2022 14:44:06 -0600 Subject: [PATCH 04/17] formatting --- .../src/instructions/deposit_governance_tokens.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/programs/nft-voter/src/instructions/deposit_governance_tokens.rs b/programs/nft-voter/src/instructions/deposit_governance_tokens.rs index 6e2bce65..cd9f2019 100644 --- a/programs/nft-voter/src/instructions/deposit_governance_tokens.rs +++ b/programs/nft-voter/src/instructions/deposit_governance_tokens.rs @@ -3,7 +3,7 @@ use anchor_spl::token::{Mint, Token, TokenAccount}; use solana_program::{entrypoint::ProgramResult, program::invoke}; use spl_governance::state::realm; -use crate::{state::nft_voter_token_owner_record::NftVoterTokenOwnerRecord, id}; +use crate::{id, state::nft_voter_token_owner_record::NftVoterTokenOwnerRecord}; /// Deposits tokens into the holding account for a given NFT to boost its voting power #[derive(Accounts)] @@ -87,7 +87,12 @@ pub fn deposit_governance_tokens(ctx: Context, amount: &ctx.accounts.holding_account_info.to_account_info(), &ctx.accounts.governing_token_owner, //TODO WTF is this? - &[ctx.accounts.governing_token_owner.signer_key().unwrap().as_ref()], + &[ctx + .accounts + .governing_token_owner + .signer_key() + .unwrap() + .as_ref()], &ctx.accounts.governance_program_id.key(), amount, &ctx.accounts.realm_governing_token_mint.to_account_info(), From 18db21e53b3a3016f70a5ea477a5329389aacb51 Mon Sep 17 00:00:00 2001 From: Austin Milt Date: Fri, 12 Aug 2022 16:48:32 -0600 Subject: [PATCH 05/17] deposit tokens with essential tests --- .../instructions/deposit_governance_tokens.rs | 83 +---- .../tests/deposit_governance_tokens.rs | 323 +++++++++++++----- .../tests/program_test/nft_voter_test.rs | 42 +-- 3 files changed, 258 insertions(+), 190 deletions(-) diff --git a/programs/nft-voter/src/instructions/deposit_governance_tokens.rs b/programs/nft-voter/src/instructions/deposit_governance_tokens.rs index cd9f2019..a38005d8 100644 --- a/programs/nft-voter/src/instructions/deposit_governance_tokens.rs +++ b/programs/nft-voter/src/instructions/deposit_governance_tokens.rs @@ -1,9 +1,8 @@ use anchor_lang::prelude::*; use anchor_spl::token::{Mint, Token, TokenAccount}; -use solana_program::{entrypoint::ProgramResult, program::invoke}; use spl_governance::state::realm; -use crate::{id, state::nft_voter_token_owner_record::NftVoterTokenOwnerRecord}; +use crate::{state::nft_voter_token_owner_record::NftVoterTokenOwnerRecord}; /// Deposits tokens into the holding account for a given NFT to boost its voting power #[derive(Accounts)] @@ -23,6 +22,7 @@ pub struct DepositGovernanceTokens<'info> { pub token_owner_record: Account<'info, NftVoterTokenOwnerRecord>, #[account( + mut, seeds = [ b"nft-power-holding-account".as_ref(), realm.key().as_ref(), realm_governing_token_mint.key().as_ref(), @@ -52,7 +52,7 @@ pub struct DepositGovernanceTokens<'info> { //TODO add constraint that the nft is the one configured for a realm collection pub nft_mint: Account<'info, Mint>, - #[account(constraint = governing_token_source_account.owner == governing_token_owner.key())] + #[account(mut, constraint = governing_token_source_account.owner == governing_token_owner.key())] pub governing_token_source_account: Account<'info, TokenAccount>, pub system_program: Program<'info, System>, @@ -68,6 +68,17 @@ pub fn deposit_governance_tokens(ctx: Context, amount: &ctx.accounts.realm_governing_token_mint.key(), )?; + spl_governance::tools::spl_token::transfer_spl_tokens( + &ctx.accounts + .governing_token_source_account + .to_account_info(), + &ctx.accounts.holding_account_info.to_account_info(), + &ctx.accounts.governing_token_owner.to_account_info(), + amount, + &ctx.accounts.realm_governing_token_mint.to_account_info(), + ) + .unwrap(); + let token_owner_record = &mut ctx.accounts.token_owner_record; token_owner_record.set_inner(NftVoterTokenOwnerRecord { realm: ctx.accounts.realm.key(), @@ -80,71 +91,5 @@ pub fn deposit_governance_tokens(ctx: Context, amount: .unwrap(), }); - spl_governance::tools::spl_token::transfer_spl_tokens_signed( - &ctx.accounts - .governing_token_source_account - .to_account_info(), - &ctx.accounts.holding_account_info.to_account_info(), - &ctx.accounts.governing_token_owner, - //TODO WTF is this? - &[ctx - .accounts - .governing_token_owner - .signer_key() - .unwrap() - .as_ref()], - &ctx.accounts.governance_program_id.key(), - amount, - &ctx.accounts.realm_governing_token_mint.to_account_info(), - ) - .unwrap(); - // spl_governance::tools::spl_token::transfer_spl_tokens( - // &ctx.accounts - // .governing_token_source_account - // .to_account_info(), - // &ctx.accounts.holding_account_info.to_account_info(), - // &ctx.accounts.governing_token_owner.to_account_info(), - // amount, - // &ctx.accounts.realm_governing_token_mint.to_account_info(), - // ) - // .unwrap(); - - let token_owner_record = &mut ctx.accounts.token_owner_record; - - token_owner_record.governing_token_deposit_amount = token_owner_record - .governing_token_deposit_amount - .checked_add(amount) - .unwrap(); - - Ok(()) -} - -fn transfer_spl_tokens<'a>( - source_info: &AccountInfo<'a>, - destination_info: &AccountInfo<'a>, - authority_info: &AccountInfo<'a>, - amount: u64, - spl_token_info: &AccountInfo<'a>, -) -> ProgramResult { - let transfer_instruction = spl_token::instruction::transfer( - &spl_token::id(), - source_info.key, - destination_info.key, - authority_info.key, - &[authority_info.key], - amount, - ) - .unwrap(); - - invoke( - &transfer_instruction, - &[ - spl_token_info.clone(), - authority_info.clone(), - source_info.clone(), - destination_info.clone(), - ], - )?; - Ok(()) } diff --git a/programs/nft-voter/tests/deposit_governance_tokens.rs b/programs/nft-voter/tests/deposit_governance_tokens.rs index 4ccb1475..d31b6ed3 100644 --- a/programs/nft-voter/tests/deposit_governance_tokens.rs +++ b/programs/nft-voter/tests/deposit_governance_tokens.rs @@ -1,6 +1,6 @@ -use anchor_spl::token::Mint; +use anchor_spl::token::TokenAccount; +use gpl_nft_voter::state::NftVoterTokenOwnerRecord; use program_test::nft_voter_test::NftVoterTest; -use program_test::program_test_bench::MintCookie; use solana_program_test::*; use solana_sdk::transport::TransportError; @@ -39,63 +39,49 @@ async fn test_deposit_governance_tokens_first_deposit_creates_record() -> Result .await?; // Act - - let token_owner_record = nft_voter_test + let token_owner_record_cookie = nft_voter_test .with_nft_voter_token_owner_record( &realm_cookie, &nft_cookie, &governance_token_holding_account_cookie, &owner_cookie, &governing_token_source_account_cookie, + None ) .await?; // Assert - // assert_eq_formatted( - // 0, - // governance_token_holding_account_cookie.account.amount, - // "amount", - // ); - // assert_eq_formatted( - // COption::None, - // governance_token_holding_account_cookie.account.delegate, - // "delegate", - // ); - // assert_eq_formatted( - // 0, - // governance_token_holding_account_cookie - // .account - // .delegated_amount, - // "delegated_amount", - // ); - // assert_eq_formatted( - // COption::None, - // governance_token_holding_account_cookie - // .account - // .close_authority, - // "close_authority", - // ); - // assert_eq_formatted( - // realm_cookie.community_mint_cookie.address, - // governance_token_holding_account_cookie.account.mint, - // "mint", - // ); - // assert_eq_formatted( - // registrar_cookie.account.governance_program_id, - // governance_token_holding_account_cookie.account.owner, - // "owner", - // ); - // assert_eq_formatted( - // AccountState::Initialized, - // governance_token_holding_account_cookie.account.state, - // "state", - // ); + assert_eq_formatted( + 0, + token_owner_record_cookie.account.governing_token_deposit_amount, + "amount", + ); + assert_eq_formatted( + realm_cookie.community_mint_cookie.address, + token_owner_record_cookie.account.governing_token_mint, + "governing_token_mint", + ); + assert_eq_formatted( + owner_cookie.address, + token_owner_record_cookie.account.governing_token_owner, + "governing_token_owner", + ); + assert_eq_formatted( + nft_cookie.mint_cookie.address, + token_owner_record_cookie.account.nft_mint, + "nft_mint", + ); + assert_eq_formatted( + realm_cookie.address, + token_owner_record_cookie.account.realm, + "realm", + ); Ok(()) } #[tokio::test] -async fn test_create_governance_token_holding_account_nft_is_not_part_of_configured_collection_errors( +async fn test_deposit_governance_tokens_record_exists_doesnt_error( ) -> Result<(), TransportError> { // Arrange let mut nft_voter_test = NftVoterTest::start_new().await; @@ -104,56 +90,128 @@ async fn test_create_governance_token_holding_account_nft_is_not_part_of_configu let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + let owner_cookie = nft_voter_test.bench.with_wallet_funded(100000000000).await; + let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - let max_voter_weight_record = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) + + let nft_cookie = nft_voter_test + .token_metadata + .with_nft_v2(&nft_collection_cookie, &owner_cookie, None) .await?; - assert_eq!( - 0, - registrar_cookie.account.collection_configs.len(), - "Unexpected collection already configured for registrar" - ); - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record, - None, + + let governance_token_holding_account_cookie = nft_voter_test + .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) + .await?; + + let governing_token_source_account_cookie = nft_voter_test + .bench + .with_tokens( + &realm_cookie.community_mint_cookie, + &owner_cookie.address, + 1000, ) .await?; - let registrar_updated = nft_voter_test - .get_registrar_account(®istrar_cookie.address) + + // Act + let first_result = nft_voter_test + .with_nft_voter_token_owner_record( + &realm_cookie, + &nft_cookie, + &governance_token_holding_account_cookie, + &owner_cookie, + &governing_token_source_account_cookie, + None + ) .await; - assert_eq!( - 1, - registrar_updated.collection_configs.len(), - "Unable to add collection to registrar" - ); - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let second_result = nft_voter_test + .with_nft_voter_token_owner_record( + &realm_cookie, + &nft_cookie, + &governance_token_holding_account_cookie, + &owner_cookie, + &governing_token_source_account_cookie, + None + ) + .await; + + // Assert + assert!(!second_result.is_err()); + assert_eq_formatted(first_result.unwrap().address, second_result.unwrap().address, "record address"); + + Ok(()) +} + +#[tokio::test] +async fn test_deposit_governance_tokens_transfers_tokens_to_holding_account() -> Result<(), TransportError> +{ + // Arrange + let mut nft_voter_test = NftVoterTest::start_new().await; + + let realm_cookie = nft_voter_test.governance.with_realm().await?; + + let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + + let owner_cookie = nft_voter_test.bench.with_wallet_funded(100000000000).await; + + let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; let nft_cookie = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2(&nft_collection_cookie, &owner_cookie, None) .await?; - // Act - //TODO add validation to the instruction and/or method and expect it to throw - let error = nft_voter_test + let governance_token_holding_account_cookie = nft_voter_test .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) - .await - .err(); + .await?; + + let source_tokens: u64 = 1000; + let deposit_tokens: u64 = ((source_tokens as f64)*0.5) as u64; + + let governing_token_source_account_cookie = nft_voter_test + .bench + .with_tokens( + &realm_cookie.community_mint_cookie, + &owner_cookie.address, + source_tokens, + ) + .await?; + + // Act + let token_owner_record_cookie = nft_voter_test + .with_nft_voter_token_owner_record( + &realm_cookie, + &nft_cookie, + &governance_token_holding_account_cookie, + &owner_cookie, + &governing_token_source_account_cookie, + Some(deposit_tokens) + ) + .await?; // Assert - //TODO - // assert!(error.is_some()); + assert_eq_formatted( + deposit_tokens, + token_owner_record_cookie.account.governing_token_deposit_amount, + "deposit amount", + ); + assert_eq_formatted( + source_tokens - deposit_tokens, + nft_voter_test.bench.get_anchor_account::(governing_token_source_account_cookie.address).await.amount, + "source remaining amount", + ); + assert_eq_formatted( + deposit_tokens, + nft_voter_test.bench.get_anchor_account::(governance_token_holding_account_cookie.address).await.amount, + "holding account amount", + ); Ok(()) } #[tokio::test] -async fn test_create_governance_token_holding_account_already_exists_errors( -) -> Result<(), TransportError> { +async fn test_deposit_governance_tokens_multiple_deposits_holding_account_stores_cumulative() -> Result<(), TransportError> +{ // Arrange let mut nft_voter_test = NftVoterTest::start_new().await; @@ -161,27 +219,126 @@ async fn test_create_governance_token_holding_account_already_exists_errors( let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let owner_cookie = nft_voter_test.bench.with_wallet_funded(100000000000).await; let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; let nft_cookie = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2(&nft_collection_cookie, &owner_cookie, None) .await?; - // Act - nft_voter_test + let governance_token_holding_account_cookie = nft_voter_test .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) .await?; - let error = nft_voter_test + let source_tokens: u64 = 1000; + let first_deposit_tokens: u64 = ((source_tokens as f64)*0.25) as u64; + let second_deposit_tokens: u64 = ((source_tokens as f64)*0.1) as u64; + let total_deposit: u64 = first_deposit_tokens + second_deposit_tokens; + + let governing_token_source_account_cookie = nft_voter_test + .bench + .with_tokens( + &realm_cookie.community_mint_cookie, + &owner_cookie.address, + source_tokens, + ) + .await?; + + // Act + let first_deposit_record_cookie = nft_voter_test + .with_nft_voter_token_owner_record( + &realm_cookie, + &nft_cookie, + &governance_token_holding_account_cookie, + &owner_cookie, + &governing_token_source_account_cookie, + Some(first_deposit_tokens) + ) + .await?; + + let second_deposit_record_cookie = nft_voter_test + .with_nft_voter_token_owner_record( + &realm_cookie, + &nft_cookie, + &governance_token_holding_account_cookie, + &owner_cookie, + &governing_token_source_account_cookie, + Some(second_deposit_tokens) + ) + .await?; + + // Assert + assert_eq_formatted( + total_deposit, + nft_voter_test.bench.get_anchor_account::(governance_token_holding_account_cookie.address).await.amount, + "holding account amount", + ); + assert_eq_formatted( + first_deposit_record_cookie.address, + second_deposit_record_cookie.address, + "deposit record address", + ); + assert_eq_formatted( + total_deposit, + nft_voter_test.bench.get_anchor_account::(first_deposit_record_cookie.address).await.governing_token_deposit_amount, + "record deposit", + ); + + Ok(()) +} + +#[tokio::test] +async fn test_deposit_governance_tokens_source_insufficient_balance_errors() -> Result<(), TransportError> +{ + // Arrange + let mut nft_voter_test = NftVoterTest::start_new().await; + + let realm_cookie = nft_voter_test.governance.with_realm().await?; + + let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + + let owner_cookie = nft_voter_test.bench.with_wallet_funded(100000000000).await; + + let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; + + let nft_cookie = nft_voter_test + .token_metadata + .with_nft_v2(&nft_collection_cookie, &owner_cookie, None) + .await?; + + let governance_token_holding_account_cookie = nft_voter_test .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) - .await - .err(); + .await?; + + let source_tokens: u64 = 1000; + let deposit_tokens: u64 = source_tokens + 1; + + let governing_token_source_account_cookie = nft_voter_test + .bench + .with_tokens( + &realm_cookie.community_mint_cookie, + &owner_cookie.address, + source_tokens, + ) + .await?; + + // Act + let result = nft_voter_test + .with_nft_voter_token_owner_record( + &realm_cookie, + &nft_cookie, + &governance_token_holding_account_cookie, + &owner_cookie, + &governing_token_source_account_cookie, + Some(deposit_tokens) + ) + .await; // Assert - assert!(error.is_some()); + //TODO better error to check for? + assert!(result.is_err()); Ok(()) } diff --git a/programs/nft-voter/tests/program_test/nft_voter_test.rs b/programs/nft-voter/tests/program_test/nft_voter_test.rs index 1b634a24..b4d742dc 100644 --- a/programs/nft-voter/tests/program_test/nft_voter_test.rs +++ b/programs/nft-voter/tests/program_test/nft_voter_test.rs @@ -692,6 +692,7 @@ impl NftVoterTest { governing_token_holding_account_cookie: &GovernanceTokenHoldingAccountCookie, governing_token_owner_cookie: &WalletCookie, governing_token_source_cookie: &TokenAccountCookie, + deposit_amount: Option ) -> Result { self.with_nft_voter_token_owner_record_using_ix( realm_cookie, @@ -699,6 +700,7 @@ impl NftVoterTest { governing_token_holding_account_cookie, governing_token_owner_cookie, governing_token_source_cookie, + deposit_amount, NopOverride, ) .await @@ -712,6 +714,7 @@ impl NftVoterTest { governing_token_holding_account_cookie: &GovernanceTokenHoldingAccountCookie, governing_token_owner_cookie: &WalletCookie, governing_token_source_cookie: &TokenAccountCookie, + deposit_amount: Option, instruction_override: F, ) -> Result { let (token_owner_record_pubkey, _) = Pubkey::find_program_address( @@ -725,7 +728,7 @@ impl NftVoterTest { ); let data = anchor_lang::InstructionData::data( - &gpl_nft_voter::instruction::DepositGovernanceTokens { amount: 0 }, + &gpl_nft_voter::instruction::DepositGovernanceTokens { amount: deposit_amount.unwrap_or(0) }, ); let accounts = gpl_nft_voter::accounts::DepositGovernanceTokens { @@ -741,41 +744,6 @@ impl NftVoterTest { token_program: spl_token::id(), }; - println!( - "DEPOSIT ACCOUNTS token_owner_record {:?}", - accounts.token_owner_record - ); - println!( - "DEPOSIT ACCOUNTS holding_account_info {:?}", - accounts.holding_account_info - ); - println!( - "DEPOSIT ACCOUNTS governance_program_id {:?}", - accounts.governance_program_id - ); - println!("DEPOSIT ACCOUNTS realm {:?}", accounts.realm); - println!( - "DEPOSIT ACCOUNTS realm_governing_token_mint {:?}", - accounts.realm_governing_token_mint - ); - println!( - "DEPOSIT ACCOUNTS governing_token_owner {:?}", - accounts.governing_token_owner - ); - println!("DEPOSIT ACCOUNTS nft_mint {:?}", accounts.nft_mint); - println!( - "DEPOSIT ACCOUNTS governing_token_source_account {:?}", - accounts.governing_token_source_account - ); - println!( - "DEPOSIT ACCOUNTS system_program {:?}", - accounts.system_program - ); - println!( - "DEPOSIT ACCOUNTS token_program {:?}", - accounts.token_program - ); - let mut deposit_governing_token_ix = Instruction { program_id: gpl_nft_voter::id(), accounts: anchor_lang::ToAccountMetas::to_account_metas(&accounts, None), @@ -784,7 +752,6 @@ impl NftVoterTest { instruction_override(&mut deposit_governing_token_ix); - println!("PROCESSING TRANSACTION"); self.bench .process_transaction( &[deposit_governing_token_ix], @@ -792,7 +759,6 @@ impl NftVoterTest { ) .await?; - println!("GETTING ACCOUNT"); let account = self .bench .get_anchor_account(token_owner_record_pubkey) From cccf26574d4da3ee8ddc2ac2fdea8e082d2e9515 Mon Sep 17 00:00:00 2001 From: Austin Milt Date: Wed, 17 Aug 2022 12:20:26 -0600 Subject: [PATCH 06/17] updated deposit instruction and downstream --- programs/nft-voter/src/error.rs | 6 ++ .../instructions/deposit_governance_tokens.rs | 13 ++-- programs/nft-voter/src/instructions/mod.rs | 3 + programs/nft-voter/src/lib.rs | 8 ++ ...ord.rs => delegator_token_owner_record.rs} | 22 +++--- programs/nft-voter/src/state/mod.rs | 4 +- .../tests/deposit_governance_tokens.rs | 75 ++++++++++++------- .../tests/program_test/nft_voter_test.rs | 10 ++- 8 files changed, 91 insertions(+), 50 deletions(-) rename programs/nft-voter/src/state/{nft_voter_token_owner_record.rs => delegator_token_owner_record.rs} (81%) diff --git a/programs/nft-voter/src/error.rs b/programs/nft-voter/src/error.rs index f0abbaa3..32ae26c0 100644 --- a/programs/nft-voter/src/error.rs +++ b/programs/nft-voter/src/error.rs @@ -76,4 +76,10 @@ pub enum NftVoterError { #[msg("Cannot configure collection with voting proposals")] CannotConfigureCollectionWithVotingProposals, + + #[msg("NFT owner must withdraw all votes or voting period must end before you may withdraw tokens")] + CannotWithdrawTokensWithActiveVotes, + + #[msg("NFT owner must withdraw all proposals or voting period must end before you may withdraw tokens")] + CannotWithdrawTokensWithActiveProposals, } diff --git a/programs/nft-voter/src/instructions/deposit_governance_tokens.rs b/programs/nft-voter/src/instructions/deposit_governance_tokens.rs index a38005d8..6e97521e 100644 --- a/programs/nft-voter/src/instructions/deposit_governance_tokens.rs +++ b/programs/nft-voter/src/instructions/deposit_governance_tokens.rs @@ -2,14 +2,14 @@ use anchor_lang::prelude::*; use anchor_spl::token::{Mint, Token, TokenAccount}; use spl_governance::state::realm; -use crate::{state::nft_voter_token_owner_record::NftVoterTokenOwnerRecord}; +use crate::state::delegator_token_owner_record::DelegatorTokenOwnerRecord; /// Deposits tokens into the holding account for a given NFT to boost its voting power #[derive(Accounts)] pub struct DepositGovernanceTokens<'info> { #[account( init_if_needed, - seeds = [b"nft-voter-token-owner-record".as_ref(), + seeds = [b"delegator-token-owner-record".as_ref(), realm.key().as_ref(), realm_governing_token_mint.key().as_ref(), nft_mint.key().as_ref(), @@ -17,9 +17,9 @@ pub struct DepositGovernanceTokens<'info> { ], bump, payer = governing_token_owner, - space = NftVoterTokenOwnerRecord::get_space() + space = DelegatorTokenOwnerRecord::get_space() )] - pub token_owner_record: Account<'info, NftVoterTokenOwnerRecord>, + pub token_owner_record: Account<'info, DelegatorTokenOwnerRecord>, #[account( mut, @@ -43,9 +43,8 @@ pub struct DepositGovernanceTokens<'info> { pub realm: UncheckedAccount<'info>, /// Either the realm community mint or the council mint. - // TODO revert when you can figure out how to correctly set up/verify the owning program pub realm_governing_token_mint: Account<'info, Mint>, - // pub realm_governing_token_mint: UncheckedAccount<'info>, + #[account(mut)] pub governing_token_owner: Signer<'info>, @@ -80,7 +79,7 @@ pub fn deposit_governance_tokens(ctx: Context, amount: .unwrap(); let token_owner_record = &mut ctx.accounts.token_owner_record; - token_owner_record.set_inner(NftVoterTokenOwnerRecord { + token_owner_record.set_inner(DelegatorTokenOwnerRecord { realm: ctx.accounts.realm.key(), governing_token_mint: ctx.accounts.realm_governing_token_mint.key(), nft_mint: ctx.accounts.nft_mint.key(), diff --git a/programs/nft-voter/src/instructions/mod.rs b/programs/nft-voter/src/instructions/mod.rs index 17d93745..db46b956 100644 --- a/programs/nft-voter/src/instructions/mod.rs +++ b/programs/nft-voter/src/instructions/mod.rs @@ -24,3 +24,6 @@ mod create_governance_token_holding_account; pub use deposit_governance_tokens::*; mod deposit_governance_tokens; + +pub use withdraw_governance_tokens::*; +mod withdraw_governance_tokens; \ No newline at end of file diff --git a/programs/nft-voter/src/lib.rs b/programs/nft-voter/src/lib.rs index bf140dbe..782cf397 100644 --- a/programs/nft-voter/src/lib.rs +++ b/programs/nft-voter/src/lib.rs @@ -76,6 +76,14 @@ pub mod nft_voter { log_version(); instructions::deposit_governance_tokens(ctx, amount) } + + pub fn withdraw_governance_tokens( + ctx: Context, + amount: u64, + ) -> Result<()> { + log_version(); + instructions::withdraw_governance_tokens(ctx, amount) + } } fn log_version() { diff --git a/programs/nft-voter/src/state/nft_voter_token_owner_record.rs b/programs/nft-voter/src/state/delegator_token_owner_record.rs similarity index 81% rename from programs/nft-voter/src/state/nft_voter_token_owner_record.rs rename to programs/nft-voter/src/state/delegator_token_owner_record.rs index d903e504..012519a4 100644 --- a/programs/nft-voter/src/state/nft_voter_token_owner_record.rs +++ b/programs/nft-voter/src/state/delegator_token_owner_record.rs @@ -6,11 +6,11 @@ use crate::{id, tools::anchor::DISCRIMINATOR_SIZE}; /// token owners to withdraw tokens from the NFT's holding account #[account] #[derive(Debug, Copy, PartialEq)] -pub struct NftVoterTokenOwnerRecord { - /// The Realm the TokenOwnerRecord belongs to +pub struct DelegatorTokenOwnerRecord { + /// The Realm the token owner is participating in pub realm: Pubkey, - /// Governing Token Mint the TokenOwnerRecord holds deposit for + /// Governing Token Mint the token owner holds deposit for pub governing_token_mint: Pubkey, /// The mint of the NFT being backed @@ -25,7 +25,7 @@ pub struct NftVoterTokenOwnerRecord { pub governing_token_deposit_amount: u64, } -impl NftVoterTokenOwnerRecord { +impl DelegatorTokenOwnerRecord { pub fn get_space() -> usize { DISCRIMINATOR_SIZE + 32 + // realm @@ -36,7 +36,7 @@ impl NftVoterTokenOwnerRecord { } } -impl Default for NftVoterTokenOwnerRecord { +impl Default for DelegatorTokenOwnerRecord { fn default() -> Self { Self { realm: Default::default(), @@ -49,14 +49,14 @@ impl Default for NftVoterTokenOwnerRecord { } /// Returns NftVoterTokenOwnerRecord PDA seeds -pub fn get_nft_voter_token_owner_record_seeds<'a>( +pub fn get_delegator_token_owner_record_seeds<'a>( realm: &'a Pubkey, governing_token_mint: &'a Pubkey, nft_mint: &'a Pubkey, governing_token_owner: &'a Pubkey, ) -> [&'a [u8]; 5] { [ - b"nft-voter-token-owner-record", + b"delegator-token-owner-record", realm.as_ref(), governing_token_mint.as_ref(), nft_mint.as_ref(), @@ -65,14 +65,14 @@ pub fn get_nft_voter_token_owner_record_seeds<'a>( } /// Returns NftVoterTokenOwnerRecord PDA address -pub fn get_nft_voter_token_owner_record_address( +pub fn get_delegator_token_owner_record_address( realm: &Pubkey, governing_token_mint: &Pubkey, nft_mint: &Pubkey, governing_token_owner: &Pubkey, ) -> Pubkey { Pubkey::find_program_address( - &get_nft_voter_token_owner_record_seeds( + &get_delegator_token_owner_record_seeds( realm, governing_token_mint, nft_mint, @@ -93,11 +93,11 @@ mod test { #[test] fn test_get_space() { // Arrange - let expected_space = NftVoterTokenOwnerRecord::get_space(); + let expected_space = DelegatorTokenOwnerRecord::get_space(); // Act let actual_space = DISCRIMINATOR_SIZE - + NftVoterTokenOwnerRecord::default() + + DelegatorTokenOwnerRecord::default() .try_to_vec() .unwrap() .len(); diff --git a/programs/nft-voter/src/state/mod.rs b/programs/nft-voter/src/state/mod.rs index 0e146625..ee00661a 100644 --- a/programs/nft-voter/src/state/mod.rs +++ b/programs/nft-voter/src/state/mod.rs @@ -14,5 +14,5 @@ pub mod voter_weight_record; pub mod idl_types; -pub use nft_voter_token_owner_record::*; -pub mod nft_voter_token_owner_record; +pub use delegator_token_owner_record::*; +pub mod delegator_token_owner_record; diff --git a/programs/nft-voter/tests/deposit_governance_tokens.rs b/programs/nft-voter/tests/deposit_governance_tokens.rs index d31b6ed3..188c8195 100644 --- a/programs/nft-voter/tests/deposit_governance_tokens.rs +++ b/programs/nft-voter/tests/deposit_governance_tokens.rs @@ -1,5 +1,5 @@ use anchor_spl::token::TokenAccount; -use gpl_nft_voter::state::NftVoterTokenOwnerRecord; +use gpl_nft_voter::state::DelegatorTokenOwnerRecord; use program_test::nft_voter_test::NftVoterTest; use solana_program_test::*; use solana_sdk::transport::TransportError; @@ -46,14 +46,16 @@ async fn test_deposit_governance_tokens_first_deposit_creates_record() -> Result &governance_token_holding_account_cookie, &owner_cookie, &governing_token_source_account_cookie, - None + None, ) .await?; // Assert assert_eq_formatted( 0, - token_owner_record_cookie.account.governing_token_deposit_amount, + token_owner_record_cookie + .account + .governing_token_deposit_amount, "amount", ); assert_eq_formatted( @@ -81,8 +83,7 @@ async fn test_deposit_governance_tokens_first_deposit_creates_record() -> Result } #[tokio::test] -async fn test_deposit_governance_tokens_record_exists_doesnt_error( -) -> Result<(), TransportError> { +async fn test_deposit_governance_tokens_record_exists_doesnt_error() -> Result<(), TransportError> { // Arrange let mut nft_voter_test = NftVoterTest::start_new().await; @@ -120,7 +121,7 @@ async fn test_deposit_governance_tokens_record_exists_doesnt_error( &governance_token_holding_account_cookie, &owner_cookie, &governing_token_source_account_cookie, - None + None, ) .await; @@ -131,20 +132,24 @@ async fn test_deposit_governance_tokens_record_exists_doesnt_error( &governance_token_holding_account_cookie, &owner_cookie, &governing_token_source_account_cookie, - None + None, ) .await; // Assert assert!(!second_result.is_err()); - assert_eq_formatted(first_result.unwrap().address, second_result.unwrap().address, "record address"); + assert_eq_formatted( + first_result.unwrap().address, + second_result.unwrap().address, + "record address", + ); Ok(()) } #[tokio::test] -async fn test_deposit_governance_tokens_transfers_tokens_to_holding_account() -> Result<(), TransportError> -{ +async fn test_deposit_governance_tokens_transfers_tokens_to_holding_account( +) -> Result<(), TransportError> { // Arrange let mut nft_voter_test = NftVoterTest::start_new().await; @@ -166,7 +171,7 @@ async fn test_deposit_governance_tokens_transfers_tokens_to_holding_account() -> .await?; let source_tokens: u64 = 1000; - let deposit_tokens: u64 = ((source_tokens as f64)*0.5) as u64; + let deposit_tokens: u64 = ((source_tokens as f64) * 0.5) as u64; let governing_token_source_account_cookie = nft_voter_test .bench @@ -185,24 +190,34 @@ async fn test_deposit_governance_tokens_transfers_tokens_to_holding_account() -> &governance_token_holding_account_cookie, &owner_cookie, &governing_token_source_account_cookie, - Some(deposit_tokens) + Some(deposit_tokens), ) .await?; // Assert assert_eq_formatted( deposit_tokens, - token_owner_record_cookie.account.governing_token_deposit_amount, + token_owner_record_cookie + .account + .governing_token_deposit_amount, "deposit amount", ); assert_eq_formatted( source_tokens - deposit_tokens, - nft_voter_test.bench.get_anchor_account::(governing_token_source_account_cookie.address).await.amount, + nft_voter_test + .bench + .get_anchor_account::(governing_token_source_account_cookie.address) + .await + .amount, "source remaining amount", ); assert_eq_formatted( deposit_tokens, - nft_voter_test.bench.get_anchor_account::(governance_token_holding_account_cookie.address).await.amount, + nft_voter_test + .bench + .get_anchor_account::(governance_token_holding_account_cookie.address) + .await + .amount, "holding account amount", ); @@ -210,8 +225,8 @@ async fn test_deposit_governance_tokens_transfers_tokens_to_holding_account() -> } #[tokio::test] -async fn test_deposit_governance_tokens_multiple_deposits_holding_account_stores_cumulative() -> Result<(), TransportError> -{ +async fn test_deposit_governance_tokens_multiple_deposits_holding_account_stores_cumulative( +) -> Result<(), TransportError> { // Arrange let mut nft_voter_test = NftVoterTest::start_new().await; @@ -233,8 +248,8 @@ async fn test_deposit_governance_tokens_multiple_deposits_holding_account_stores .await?; let source_tokens: u64 = 1000; - let first_deposit_tokens: u64 = ((source_tokens as f64)*0.25) as u64; - let second_deposit_tokens: u64 = ((source_tokens as f64)*0.1) as u64; + let first_deposit_tokens: u64 = ((source_tokens as f64) * 0.25) as u64; + let second_deposit_tokens: u64 = ((source_tokens as f64) * 0.1) as u64; let total_deposit: u64 = first_deposit_tokens + second_deposit_tokens; let governing_token_source_account_cookie = nft_voter_test @@ -254,7 +269,7 @@ async fn test_deposit_governance_tokens_multiple_deposits_holding_account_stores &governance_token_holding_account_cookie, &owner_cookie, &governing_token_source_account_cookie, - Some(first_deposit_tokens) + Some(first_deposit_tokens), ) .await?; @@ -265,14 +280,18 @@ async fn test_deposit_governance_tokens_multiple_deposits_holding_account_stores &governance_token_holding_account_cookie, &owner_cookie, &governing_token_source_account_cookie, - Some(second_deposit_tokens) + Some(second_deposit_tokens), ) .await?; // Assert assert_eq_formatted( total_deposit, - nft_voter_test.bench.get_anchor_account::(governance_token_holding_account_cookie.address).await.amount, + nft_voter_test + .bench + .get_anchor_account::(governance_token_holding_account_cookie.address) + .await + .amount, "holding account amount", ); assert_eq_formatted( @@ -282,7 +301,11 @@ async fn test_deposit_governance_tokens_multiple_deposits_holding_account_stores ); assert_eq_formatted( total_deposit, - nft_voter_test.bench.get_anchor_account::(first_deposit_record_cookie.address).await.governing_token_deposit_amount, + nft_voter_test + .bench + .get_anchor_account::(first_deposit_record_cookie.address) + .await + .governing_token_deposit_amount, "record deposit", ); @@ -290,8 +313,8 @@ async fn test_deposit_governance_tokens_multiple_deposits_holding_account_stores } #[tokio::test] -async fn test_deposit_governance_tokens_source_insufficient_balance_errors() -> Result<(), TransportError> -{ +async fn test_deposit_governance_tokens_source_insufficient_balance_errors( +) -> Result<(), TransportError> { // Arrange let mut nft_voter_test = NftVoterTest::start_new().await; @@ -332,7 +355,7 @@ async fn test_deposit_governance_tokens_source_insufficient_balance_errors() -> &governance_token_holding_account_cookie, &owner_cookie, &governing_token_source_account_cookie, - Some(deposit_tokens) + Some(deposit_tokens), ) .await; diff --git a/programs/nft-voter/tests/program_test/nft_voter_test.rs b/programs/nft-voter/tests/program_test/nft_voter_test.rs index b4d742dc..80bf41a0 100644 --- a/programs/nft-voter/tests/program_test/nft_voter_test.rs +++ b/programs/nft-voter/tests/program_test/nft_voter_test.rs @@ -75,7 +75,7 @@ pub struct GovernanceTokenHoldingAccountCookie { #[derive(Debug, PartialEq)] pub struct NftVoterTokenOwnerRecordCookie { pub address: Pubkey, - pub account: NftVoterTokenOwnerRecord, + pub account: DelegatorTokenOwnerRecord, } #[derive(Debug, PartialEq)] @@ -692,7 +692,7 @@ impl NftVoterTest { governing_token_holding_account_cookie: &GovernanceTokenHoldingAccountCookie, governing_token_owner_cookie: &WalletCookie, governing_token_source_cookie: &TokenAccountCookie, - deposit_amount: Option + deposit_amount: Option, ) -> Result { self.with_nft_voter_token_owner_record_using_ix( realm_cookie, @@ -718,7 +718,7 @@ impl NftVoterTest { instruction_override: F, ) -> Result { let (token_owner_record_pubkey, _) = Pubkey::find_program_address( - &get_nft_voter_token_owner_record_seeds( + &get_delegator_token_owner_record_seeds( &realm_cookie.address, &realm_cookie.account.community_mint, &nft_cookie.mint_cookie.address, @@ -728,7 +728,9 @@ impl NftVoterTest { ); let data = anchor_lang::InstructionData::data( - &gpl_nft_voter::instruction::DepositGovernanceTokens { amount: deposit_amount.unwrap_or(0) }, + &gpl_nft_voter::instruction::DepositGovernanceTokens { + amount: deposit_amount.unwrap_or(0), + }, ); let accounts = gpl_nft_voter::accounts::DepositGovernanceTokens { From c2d707c8e5391b5fae76056454d80bc48ba4da2b Mon Sep 17 00:00:00 2001 From: Austin Milt Date: Wed, 17 Aug 2022 12:44:03 -0600 Subject: [PATCH 07/17] better scoping of accounts and methods --- programs/nft-voter/src/error.rs | 3 - ...create_governance_token_holding_account.rs | 4 +- .../instructions/deposit_governance_tokens.rs | 11 +- programs/nft-voter/src/instructions/mod.rs | 2 +- .../withdraw_governance_tokens.rs | 100 +++++++++++++++ .../src/state/delegator_token_owner_record.rs | 101 ++++++++------- programs/nft-voter/src/tools/governance.rs | 34 +++++ .../tests/program_test/nft_voter_test.rs | 28 ++--- .../tests/withdraw_governance_tokens.rs | 116 ++++++++++++++++++ 9 files changed, 321 insertions(+), 78 deletions(-) create mode 100644 programs/nft-voter/src/instructions/withdraw_governance_tokens.rs create mode 100644 programs/nft-voter/tests/withdraw_governance_tokens.rs diff --git a/programs/nft-voter/src/error.rs b/programs/nft-voter/src/error.rs index 32ae26c0..6bd53766 100644 --- a/programs/nft-voter/src/error.rs +++ b/programs/nft-voter/src/error.rs @@ -79,7 +79,4 @@ pub enum NftVoterError { #[msg("NFT owner must withdraw all votes or voting period must end before you may withdraw tokens")] CannotWithdrawTokensWithActiveVotes, - - #[msg("NFT owner must withdraw all proposals or voting period must end before you may withdraw tokens")] - CannotWithdrawTokensWithActiveProposals, } diff --git a/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs b/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs index e027735d..2ff2a00a 100644 --- a/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs +++ b/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs @@ -5,6 +5,8 @@ use anchor_spl::{ }; use spl_governance::state::realm; +use crate::tools::governance::NFT_POWER_HOLDING_ACCOUNT_SEED_PREFIX; + /// Creates a governance token holding account for a given NFT to boost its voting power /// This instruction should only be executed once per realm/governing_token_mint/nft /// to create the account @@ -13,7 +15,7 @@ pub struct CreateGovernanceTokenHoldingAccount<'info> { //TODO add docs #[account( init, - seeds = [ b"nft-power-holding-account".as_ref(), + seeds = [ &NFT_POWER_HOLDING_ACCOUNT_SEED_PREFIX, realm.key().as_ref(), realm_governing_token_mint.key().as_ref(), nft_mint.key().as_ref()], diff --git a/programs/nft-voter/src/instructions/deposit_governance_tokens.rs b/programs/nft-voter/src/instructions/deposit_governance_tokens.rs index 6e97521e..034182b2 100644 --- a/programs/nft-voter/src/instructions/deposit_governance_tokens.rs +++ b/programs/nft-voter/src/instructions/deposit_governance_tokens.rs @@ -2,14 +2,17 @@ use anchor_lang::prelude::*; use anchor_spl::token::{Mint, Token, TokenAccount}; use spl_governance::state::realm; -use crate::state::delegator_token_owner_record::DelegatorTokenOwnerRecord; +use crate::{ + state::delegator_token_owner_record::DelegatorTokenOwnerRecord, + tools::governance::NFT_POWER_HOLDING_ACCOUNT_SEED_PREFIX, +}; /// Deposits tokens into the holding account for a given NFT to boost its voting power #[derive(Accounts)] pub struct DepositGovernanceTokens<'info> { #[account( init_if_needed, - seeds = [b"delegator-token-owner-record".as_ref(), + seeds = [&DelegatorTokenOwnerRecord::SEED_PREFIX, realm.key().as_ref(), realm_governing_token_mint.key().as_ref(), nft_mint.key().as_ref(), @@ -17,13 +20,13 @@ pub struct DepositGovernanceTokens<'info> { ], bump, payer = governing_token_owner, - space = DelegatorTokenOwnerRecord::get_space() + space = DelegatorTokenOwnerRecord::SPACE )] pub token_owner_record: Account<'info, DelegatorTokenOwnerRecord>, #[account( mut, - seeds = [ b"nft-power-holding-account".as_ref(), + seeds = [ &NFT_POWER_HOLDING_ACCOUNT_SEED_PREFIX, realm.key().as_ref(), realm_governing_token_mint.key().as_ref(), nft_mint.key().as_ref()], diff --git a/programs/nft-voter/src/instructions/mod.rs b/programs/nft-voter/src/instructions/mod.rs index db46b956..e59f6785 100644 --- a/programs/nft-voter/src/instructions/mod.rs +++ b/programs/nft-voter/src/instructions/mod.rs @@ -26,4 +26,4 @@ pub use deposit_governance_tokens::*; mod deposit_governance_tokens; pub use withdraw_governance_tokens::*; -mod withdraw_governance_tokens; \ No newline at end of file +mod withdraw_governance_tokens; diff --git a/programs/nft-voter/src/instructions/withdraw_governance_tokens.rs b/programs/nft-voter/src/instructions/withdraw_governance_tokens.rs new file mode 100644 index 00000000..0b3162e0 --- /dev/null +++ b/programs/nft-voter/src/instructions/withdraw_governance_tokens.rs @@ -0,0 +1,100 @@ +use anchor_lang::prelude::*; +use anchor_spl::token::{Mint, Token, TokenAccount}; +use spl_governance::state::realm; + +use crate::{ + error::NftVoterError, state::delegator_token_owner_record::DelegatorTokenOwnerRecord, + tools::governance::NFT_POWER_HOLDING_ACCOUNT_SEED_PREFIX, +}; + +/// Withdraws tokens from the holding account for a given NFT +#[derive(Accounts)] +#[instruction(amount: u64)] +pub struct WithdrawGovernanceTokens<'info> { + #[account( + mut, + seeds = [b"delegator-token-owner-record".as_ref(), + realm.key().as_ref(), + realm_governing_token_mint.key().as_ref(), + nft_mint.key().as_ref(), + governing_token_owner.key().as_ref() + ], + bump, + constraint = token_owner_record.governing_token_deposit_amount >= amount + )] + pub token_owner_record: Account<'info, DelegatorTokenOwnerRecord>, + + #[account( + mut, + seeds = [ &NFT_POWER_HOLDING_ACCOUNT_SEED_PREFIX, + realm.key().as_ref(), + realm_governing_token_mint.key().as_ref(), + nft_mint.key().as_ref()], + bump, + token::mint = realm_governing_token_mint, + token::authority = governance_program_id, + constraint = holding_account_info.amount >= amount + )] + pub holding_account_info: Account<'info, TokenAccount>, + + /// The program id of the spl-governance program the realm belongs to + /// CHECK: Can be any instance of spl-governance and it's not known at the compilation time + #[account(executable)] + pub governance_program_id: UncheckedAccount<'info>, + + /// CHECK: Owned by spl-governance instance specified in governance_program_id + #[account(owner = governance_program_id.key())] + pub realm: UncheckedAccount<'info>, + + /// Either the realm community mint or the council mint. + pub realm_governing_token_mint: Account<'info, Mint>, + + #[account(mut)] + pub governing_token_owner: Signer<'info>, + + //TODO add constraint that the nft is the one configured for a realm collection + pub nft_mint: Account<'info, Mint>, + + #[account(mut, constraint = governing_token_source_account.owner == governing_token_owner.key())] + pub governing_token_source_account: Account<'info, TokenAccount>, + + pub system_program: Program<'info, System>, + pub token_program: Program<'info, Token>, +} + +/// Withdraws tokens from the holding account for a given NFT to boost its voting power +pub fn withdraw_governance_tokens( + ctx: Context, + amount: u64, +) -> Result<()> { + let realm = realm::get_realm_data_for_governing_token_mint( + &ctx.accounts.governance_program_id.key(), + &ctx.accounts.realm, + &ctx.accounts.realm_governing_token_mint.key(), + )?; + + //TODO check for proposal status (voted on, expired, etc) from NftUsageRecord + // TODO do all proposals have to have an expiration? + require!(false, NftVoterError::CannotWithdrawTokensWithActiveVotes); + + spl_governance::tools::spl_token::transfer_spl_tokens_signed( + &ctx.accounts.holding_account_info.to_account_info(), + &ctx.accounts + .governing_token_source_account + .to_account_info(), + &ctx.accounts.realm.to_account_info(), + &spl_governance::state::realm::get_realm_address_seeds(&realm.name), + &ctx.accounts.governance_program_id.key(), + amount, + &ctx.accounts.realm_governing_token_mint.to_account_info(), + ) + .unwrap(); + + let token_owner_record = &mut ctx.accounts.token_owner_record; + token_owner_record.governing_token_deposit_amount = token_owner_record + .governing_token_deposit_amount + .checked_sub(amount) + .unwrap(); + + Ok(()) +} diff --git a/programs/nft-voter/src/state/delegator_token_owner_record.rs b/programs/nft-voter/src/state/delegator_token_owner_record.rs index 012519a4..d96cdc1c 100644 --- a/programs/nft-voter/src/state/delegator_token_owner_record.rs +++ b/programs/nft-voter/src/state/delegator_token_owner_record.rs @@ -5,7 +5,7 @@ use crate::{id, tools::anchor::DISCRIMINATOR_SIZE}; /// Receipt for depositing tokens against a voting NFT to enable /// token owners to withdraw tokens from the NFT's holding account #[account] -#[derive(Debug, Copy, PartialEq)] +#[derive(Debug, Copy, PartialEq, Default)] pub struct DelegatorTokenOwnerRecord { /// The Realm the token owner is participating in pub realm: Pubkey, @@ -26,61 +26,56 @@ pub struct DelegatorTokenOwnerRecord { } impl DelegatorTokenOwnerRecord { - pub fn get_space() -> usize { - DISCRIMINATOR_SIZE + - 32 + // realm - 32 + // governing token mint - 32 + // nft mint - 32 + // governing token owner - 8 // deposit amount + pub const SEED_PREFIX: [u8; 28] = *b"delegator-token-owner-record"; + + pub const SPACE: usize = DISCRIMINATOR_SIZE + + 32 + // realm + 32 + // governing token mint + 32 + // nft mint + 32 + // governing token owner + 8; // deposit amount + + pub fn make_seeds<'a>( + realm: &'a Pubkey, + governing_token_mint: &'a Pubkey, + nft_mint: &'a Pubkey, + governing_token_owner: &'a Pubkey, + ) -> [&'a [u8]; 5] { + [ + &DelegatorTokenOwnerRecord::SEED_PREFIX, + realm.as_ref(), + governing_token_mint.as_ref(), + nft_mint.as_ref(), + governing_token_owner.as_ref(), + ] } -} -impl Default for DelegatorTokenOwnerRecord { - fn default() -> Self { - Self { - realm: Default::default(), - governing_token_mint: Default::default(), - nft_mint: Default::default(), - governing_token_owner: Default::default(), - governing_token_deposit_amount: 0, - } + pub fn get_seeds(&self) -> [&[u8]; 5] { + DelegatorTokenOwnerRecord::make_seeds( + &self.realm, + &self.governing_token_mint, + &self.nft_mint, + &self.governing_token_owner, + ) } -} -/// Returns NftVoterTokenOwnerRecord PDA seeds -pub fn get_delegator_token_owner_record_seeds<'a>( - realm: &'a Pubkey, - governing_token_mint: &'a Pubkey, - nft_mint: &'a Pubkey, - governing_token_owner: &'a Pubkey, -) -> [&'a [u8]; 5] { - [ - b"delegator-token-owner-record", - realm.as_ref(), - governing_token_mint.as_ref(), - nft_mint.as_ref(), - governing_token_owner.as_ref(), - ] -} - -/// Returns NftVoterTokenOwnerRecord PDA address -pub fn get_delegator_token_owner_record_address( - realm: &Pubkey, - governing_token_mint: &Pubkey, - nft_mint: &Pubkey, - governing_token_owner: &Pubkey, -) -> Pubkey { - Pubkey::find_program_address( - &get_delegator_token_owner_record_seeds( - realm, - governing_token_mint, - nft_mint, - governing_token_owner, - ), - &id(), - ) - .0 + pub fn find_address( + realm: &Pubkey, + governing_token_mint: &Pubkey, + nft_mint: &Pubkey, + governing_token_owner: &Pubkey, + ) -> Pubkey { + Pubkey::find_program_address( + &DelegatorTokenOwnerRecord::make_seeds( + realm, + governing_token_mint, + nft_mint, + governing_token_owner, + ), + &id(), + ) + .0 + } } #[cfg(test)] @@ -93,7 +88,7 @@ mod test { #[test] fn test_get_space() { // Arrange - let expected_space = DelegatorTokenOwnerRecord::get_space(); + let expected_space = DelegatorTokenOwnerRecord::SPACE; // Act let actual_space = DISCRIMINATOR_SIZE diff --git a/programs/nft-voter/src/tools/governance.rs b/programs/nft-voter/src/tools/governance.rs index ccb25732..84449621 100644 --- a/programs/nft-voter/src/tools/governance.rs +++ b/programs/nft-voter/src/tools/governance.rs @@ -1,6 +1,40 @@ use anchor_lang::prelude::Pubkey; use spl_governance::state::{token_owner_record, vote_record}; +use crate::id; + +pub const NFT_POWER_HOLDING_ACCOUNT_SEED_PREFIX: [u8; 25] = *b"nft-power-holding-account"; + +pub fn find_nft_power_holding_account_address( + realm: &Pubkey, + governance_token_mint: &Pubkey, + nft_mint: &Pubkey, +) -> Pubkey { + Pubkey::find_program_address( + &[ + &NFT_POWER_HOLDING_ACCOUNT_SEED_PREFIX, + realm.as_ref(), + governance_token_mint.as_ref(), + nft_mint.as_ref(), + ], + &id(), + ) + .0 +} + +pub fn make_nft_power_holding_account_seeds<'a>( + realm: &'a Pubkey, + governance_token_mint: &'a Pubkey, + nft_mint: &'a Pubkey, +) -> [&'a [u8]; 4] { + [ + &NFT_POWER_HOLDING_ACCOUNT_SEED_PREFIX, + realm.as_ref(), + governance_token_mint.as_ref(), + nft_mint.as_ref(), + ] +} + pub fn get_vote_record_address( program_id: &Pubkey, realm: &Pubkey, diff --git a/programs/nft-voter/tests/program_test/nft_voter_test.rs b/programs/nft-voter/tests/program_test/nft_voter_test.rs index 80bf41a0..ebf7660e 100644 --- a/programs/nft-voter/tests/program_test/nft_voter_test.rs +++ b/programs/nft-voter/tests/program_test/nft_voter_test.rs @@ -8,6 +8,9 @@ use gpl_nft_voter::state::max_voter_weight_record::{ get_max_voter_weight_record_address, MaxVoterWeightRecord, }; use gpl_nft_voter::state::*; +use gpl_nft_voter::tools::governance::{ + find_nft_power_holding_account_address, NFT_POWER_HOLDING_ACCOUNT_SEED_PREFIX, +}; use solana_program::pubkey::ParsePubkeyError; use solana_sdk::transport::TransportError; use spl_governance::instruction::cast_vote; @@ -633,14 +636,10 @@ impl NftVoterTest { nft_cookie: &NftCookie, instruction_override: F, ) -> Result { - let (holding_account_key, _) = Pubkey::find_program_address( - &[ - b"nft-power-holding-account".as_ref(), - registrar_cookie.account.realm.as_ref(), - registrar_cookie.account.governing_token_mint.as_ref(), - nft_cookie.mint_cookie.address.as_ref(), - ], - &gpl_nft_voter::id(), + let holding_account_key = find_nft_power_holding_account_address( + ®istrar_cookie.account.realm, + ®istrar_cookie.account.governing_token_mint, + &nft_cookie.mint_cookie.address, ); let data = anchor_lang::InstructionData::data( @@ -717,14 +716,11 @@ impl NftVoterTest { deposit_amount: Option, instruction_override: F, ) -> Result { - let (token_owner_record_pubkey, _) = Pubkey::find_program_address( - &get_delegator_token_owner_record_seeds( - &realm_cookie.address, - &realm_cookie.account.community_mint, - &nft_cookie.mint_cookie.address, - &governing_token_owner_cookie.address, - ), - &gpl_nft_voter::id(), + let token_owner_record_pubkey = DelegatorTokenOwnerRecord::find_address( + &realm_cookie.address, + &realm_cookie.account.community_mint, + &nft_cookie.mint_cookie.address, + &governing_token_owner_cookie.address, ); let data = anchor_lang::InstructionData::data( diff --git a/programs/nft-voter/tests/withdraw_governance_tokens.rs b/programs/nft-voter/tests/withdraw_governance_tokens.rs new file mode 100644 index 00000000..cffdf3bf --- /dev/null +++ b/programs/nft-voter/tests/withdraw_governance_tokens.rs @@ -0,0 +1,116 @@ +use anchor_spl::token::TokenAccount; +use gpl_nft_voter::state::DelegatorTokenOwnerRecord; +use program_test::nft_voter_test::NftVoterTest; +use solana_program_test::*; +use solana_sdk::transport::TransportError; + +mod program_test; + +#[tokio::test] +async fn test_withdraw_governance_tokens_nothing_deposited_errors() -> Result<(), TransportError> { + // Arrange + let mut nft_voter_test = NftVoterTest::start_new().await; + + let realm_cookie = nft_voter_test.governance.with_realm().await?; + + let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + + let owner_cookie = nft_voter_test.bench.with_wallet_funded(100000000000).await; + + let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; + + let nft_cookie = nft_voter_test + .token_metadata + .with_nft_v2(&nft_collection_cookie, &owner_cookie, None) + .await?; + + let governance_token_holding_account_cookie = nft_voter_test + .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) + .await?; + + let governing_token_source_account_cookie = nft_voter_test + .bench + .with_tokens( + &realm_cookie.community_mint_cookie, + &owner_cookie.address, + 1000, + ) + .await?; + + // Act + let token_owner_record_cookie = nft_voter_test + .with_nft_voter_token_owner_record( + &realm_cookie, + &nft_cookie, + &governance_token_holding_account_cookie, + &owner_cookie, + &governing_token_source_account_cookie, + None, + ) + .await?; + + // Assert + assert_eq_formatted( + 0, + token_owner_record_cookie + .account + .governing_token_deposit_amount, + "amount", + ); + assert_eq_formatted( + realm_cookie.community_mint_cookie.address, + token_owner_record_cookie.account.governing_token_mint, + "governing_token_mint", + ); + assert_eq_formatted( + owner_cookie.address, + token_owner_record_cookie.account.governing_token_owner, + "governing_token_owner", + ); + assert_eq_formatted( + nft_cookie.mint_cookie.address, + token_owner_record_cookie.account.nft_mint, + "nft_mint", + ); + assert_eq_formatted( + realm_cookie.address, + token_owner_record_cookie.account.realm, + "realm", + ); + + Ok(()) +} + +#[tokio::test] +async fn test_withdraw_governance_tokens_try_withdraw_zero_errors() -> Result<(), TransportError> { + todo!() +} + +#[tokio::test] +async fn test_withdraw_governance_tokens_try_withdraw_more_than_deposited_errors( +) -> Result<(), TransportError> { + todo!() +} + +#[tokio::test] +async fn test_withdraw_governance_tokens_nft_has_open_votes_errors() -> Result<(), TransportError> { + todo!() +} + +#[tokio::test] +async fn test_withdraw_governance_tokens_nft_has_open_proposals_errors( +) -> Result<(), TransportError> { + todo!() +} + +fn assert_eq_formatted( + expected: T, + actual: T, + name: &str, +) -> () { + assert_eq!( + expected, actual, + "{} not equal: expected {:?} but got {:?}", + name, expected, actual + ); +} From 1800115d164edfbab3a9733d88936e5048fd507e Mon Sep 17 00:00:00 2001 From: Austin Milt Date: Wed, 17 Aug 2022 12:51:16 -0600 Subject: [PATCH 08/17] cleanup --- .../src/state/delegator_token_owner_record.rs | 10 +++++----- .../nft-voter/tests/program_test/nft_voter_test.rs | 4 +--- programs/nft-voter/tests/withdraw_governance_tokens.rs | 2 -- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/programs/nft-voter/src/state/delegator_token_owner_record.rs b/programs/nft-voter/src/state/delegator_token_owner_record.rs index d96cdc1c..7d4d8164 100644 --- a/programs/nft-voter/src/state/delegator_token_owner_record.rs +++ b/programs/nft-voter/src/state/delegator_token_owner_record.rs @@ -29,11 +29,11 @@ impl DelegatorTokenOwnerRecord { pub const SEED_PREFIX: [u8; 28] = *b"delegator-token-owner-record"; pub const SPACE: usize = DISCRIMINATOR_SIZE + - 32 + // realm - 32 + // governing token mint - 32 + // nft mint - 32 + // governing token owner - 8; // deposit amount + 32 + // realm + 32 + // governing token mint + 32 + // nft mint + 32 + // governing token owner + 8; // deposit amount pub fn make_seeds<'a>( realm: &'a Pubkey, diff --git a/programs/nft-voter/tests/program_test/nft_voter_test.rs b/programs/nft-voter/tests/program_test/nft_voter_test.rs index ebf7660e..0c1c7433 100644 --- a/programs/nft-voter/tests/program_test/nft_voter_test.rs +++ b/programs/nft-voter/tests/program_test/nft_voter_test.rs @@ -9,9 +9,8 @@ use gpl_nft_voter::state::max_voter_weight_record::{ }; use gpl_nft_voter::state::*; use gpl_nft_voter::tools::governance::{ - find_nft_power_holding_account_address, NFT_POWER_HOLDING_ACCOUNT_SEED_PREFIX, + find_nft_power_holding_account_address, }; -use solana_program::pubkey::ParsePubkeyError; use solana_sdk::transport::TransportError; use spl_governance::instruction::cast_vote; use spl_governance::state::vote_record::{self, Vote, VoteChoice}; @@ -24,7 +23,6 @@ use solana_program_test::ProgramTest; use solana_sdk::instruction::Instruction; use solana_sdk::signature::Keypair; use solana_sdk::signer::Signer; -use spl_token::state::Account; use crate::program_test::governance_test::GovernanceTest; use crate::program_test::program_test_bench::ProgramTestBench; diff --git a/programs/nft-voter/tests/withdraw_governance_tokens.rs b/programs/nft-voter/tests/withdraw_governance_tokens.rs index cffdf3bf..53e2916d 100644 --- a/programs/nft-voter/tests/withdraw_governance_tokens.rs +++ b/programs/nft-voter/tests/withdraw_governance_tokens.rs @@ -1,5 +1,3 @@ -use anchor_spl::token::TokenAccount; -use gpl_nft_voter::state::DelegatorTokenOwnerRecord; use program_test::nft_voter_test::NftVoterTest; use solana_program_test::*; use solana_sdk::transport::TransportError; From 6da74b26b4bbd6513ce5a340168c7951cd4c5636 Mon Sep 17 00:00:00 2001 From: Austin Milt Date: Wed, 17 Aug 2022 14:56:01 -0600 Subject: [PATCH 09/17] check nft collection when creating holding account --- programs/nft-voter/src/error.rs | 6 ++ ...create_governance_token_holding_account.rs | 62 ++++++++++++---- ...create_governance_token_holding_account.rs | 71 ++++++++----------- .../tests/program_test/nft_voter_test.rs | 11 ++- 4 files changed, 89 insertions(+), 61 deletions(-) diff --git a/programs/nft-voter/src/error.rs b/programs/nft-voter/src/error.rs index 6bd53766..c48f96a5 100644 --- a/programs/nft-voter/src/error.rs +++ b/programs/nft-voter/src/error.rs @@ -79,4 +79,10 @@ pub enum NftVoterError { #[msg("NFT owner must withdraw all votes or voting period must end before you may withdraw tokens")] CannotWithdrawTokensWithActiveVotes, + + #[msg("NFT must belong to a collection configured for the realm")] + InvalidNftCollection, + + #[msg("Account already initialized")] + AccountAlreadyInitialized, } diff --git a/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs b/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs index 2ff2a00a..f82f683c 100644 --- a/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs +++ b/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs @@ -3,20 +3,26 @@ use anchor_spl::{ associated_token::AssociatedToken, token::{Mint, Token, TokenAccount}, }; -use spl_governance::state::realm; -use crate::tools::governance::NFT_POWER_HOLDING_ACCOUNT_SEED_PREFIX; +use crate::{ + error::NftVoterError, + state::Registrar, + tools::{ + governance::NFT_POWER_HOLDING_ACCOUNT_SEED_PREFIX, + token_metadata::get_token_metadata_for_mint, + }, +}; /// Creates a governance token holding account for a given NFT to boost its voting power /// This instruction should only be executed once per realm/governing_token_mint/nft /// to create the account #[derive(Accounts)] pub struct CreateGovernanceTokenHoldingAccount<'info> { - //TODO add docs + /// Associated fungible token account for the NFT being backed #[account( init, seeds = [ &NFT_POWER_HOLDING_ACCOUNT_SEED_PREFIX, - realm.key().as_ref(), + registrar.realm.as_ref(), realm_governing_token_mint.key().as_ref(), nft_mint.key().as_ref()], bump, @@ -31,23 +37,32 @@ pub struct CreateGovernanceTokenHoldingAccount<'info> { #[account(executable)] pub governance_program_id: UncheckedAccount<'info>, - /// CHECK: Owned by spl-governance instance specified in governance_program_id - #[account(owner = governance_program_id.key())] - pub realm: UncheckedAccount<'info>, + pub registrar: Account<'info, Registrar>, /// Either the realm community mint or the council mint. - // TODO revert when you can figure out how to correctly set up/verify the owning program pub realm_governing_token_mint: Account<'info, Mint>, + // pub realm_governing_token_mint: UncheckedAccount<'info>, #[account(mut)] pub payer: Signer<'info>, - //TODO add constraint that the nft is the one configured for a realm collection + /// Mint of the NFT for which the holding account is being created pub nft_mint: Account<'info, Mint>, + /// Metadata of the NFT for which the holding account is being created. The + /// NFT must have a verified collection configured for the realm. + pub nft_metadata: UncheckedAccount<'info>, + + /// Associated token program that will own the holding account pub associated_token_program: Program<'info, AssociatedToken>, + + /// Token program of the governance token mint pub token_program: Program<'info, Token>, + + /// System program required for creating the holding account pub system_program: Program<'info, System>, + + /// Rent required for creating the holding account pub rent: Sysvar<'info, Rent>, } @@ -55,12 +70,31 @@ pub struct CreateGovernanceTokenHoldingAccount<'info> { pub fn create_governance_token_holding_account( ctx: Context, ) -> Result<()> { - // Deserialize the Realm to validate it - let _realm = realm::get_realm_data_for_governing_token_mint( - &ctx.accounts.governance_program_id.key(), - &ctx.accounts.realm, - &ctx.accounts.realm_governing_token_mint.key(), + let registrar = &ctx.accounts.registrar; + let nft_mint = &ctx.accounts.nft_mint; + let nft_metadata = get_token_metadata_for_mint( + &ctx.accounts.nft_metadata.to_account_info(), + &nft_mint.key(), )?; + // The NFT must have a collection and the collection must be verified + let nft_collection = nft_metadata + .collection + .ok_or(NftVoterError::MissingMetadataCollection)?; + + require!( + nft_collection.verified, + NftVoterError::CollectionMustBeVerified + ); + + require!( + registrar + .collection_configs + .iter() + .map(|c| c.collection.key()) + .any(|c| c.key() == nft_collection.key), + NftVoterError::InvalidNftCollection + ); + Ok(()) } diff --git a/programs/nft-voter/tests/create_governance_token_holding_account.rs b/programs/nft-voter/tests/create_governance_token_holding_account.rs index 51d6c255..29ee3124 100644 --- a/programs/nft-voter/tests/create_governance_token_holding_account.rs +++ b/programs/nft-voter/tests/create_governance_token_holding_account.rs @@ -1,8 +1,10 @@ +use gpl_nft_voter::error::NftVoterError; use program_test::nft_voter_test::NftVoterTest; use solana_program::program_option::COption; use solana_program_test::*; use solana_sdk::transport::TransportError; use spl_token::state::AccountState; +use program_test::tools::assert_nft_voter_err; mod program_test; @@ -13,11 +15,29 @@ async fn test_create_governance_token_holding_account() -> Result<(), TransportE let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let voter_cookie = nft_voter_test.bench.with_wallet().await; + let registrar_cookie = &mut nft_voter_test.with_registrar(&realm_cookie).await?; let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; + let max_voter_weight_record = nft_voter_test + .with_max_voter_weight_record(®istrar_cookie) + .await?; + + nft_voter_test + .with_collection( + ®istrar_cookie, + &nft_collection_cookie, + &max_voter_weight_record, + None, + ) + .await?; + + let registrar_updated = nft_voter_test + .get_registrar_account(®istrar_cookie.address) + .await; + + registrar_cookie.account = registrar_updated; + + let voter_cookie = nft_voter_test.bench.with_wallet().await; let nft_cookie = nft_voter_test .token_metadata @@ -87,11 +107,13 @@ async fn test_create_governance_token_holding_account_nft_is_not_part_of_configu let max_voter_weight_record = nft_voter_test .with_max_voter_weight_record(®istrar_cookie) .await?; + assert_eq!( 0, registrar_cookie.account.collection_configs.len(), "Unexpected collection already configured for registrar" ); + nft_voter_test .with_collection( ®istrar_cookie, @@ -100,9 +122,11 @@ async fn test_create_governance_token_holding_account_nft_is_not_part_of_configu None, ) .await?; + let registrar_updated = nft_voter_test .get_registrar_account(®istrar_cookie.address) .await; + assert_eq!( 1, registrar_updated.collection_configs.len(), @@ -111,59 +135,24 @@ async fn test_create_governance_token_holding_account_nft_is_not_part_of_configu let voter_cookie = nft_voter_test.bench.with_wallet().await; + // create the NFT with a different collection not configured for th realm let nft_cookie = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2(&nft_voter_test.token_metadata.with_nft_collection().await?, &voter_cookie, None) .await?; // Act - //TODO add validation to the instruction and/or method and expect it to throw let error = nft_voter_test .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) .await .err(); // Assert - //TODO - // assert!(error.is_some()); + assert_nft_voter_err(error.unwrap(), NftVoterError::InvalidNftCollection); Ok(()) } -#[tokio::test] -async fn test_create_governance_token_holding_account_already_exists_errors( -) -> Result<(), TransportError> { - // Arrange - let mut nft_voter_test = NftVoterTest::start_new().await; - - let realm_cookie = nft_voter_test.governance.with_realm().await?; - - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let voter_cookie = nft_voter_test.bench.with_wallet().await; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let nft_cookie = nft_voter_test - .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) - .await?; - - // Act - nft_voter_test - .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) - .await?; - - let error = nft_voter_test - .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) - .await - .err(); - - // Assert - assert!(error.is_some()); - - Ok(()) -} fn assert_eq_formatted( expected: T, diff --git a/programs/nft-voter/tests/program_test/nft_voter_test.rs b/programs/nft-voter/tests/program_test/nft_voter_test.rs index 0c1c7433..927f8f01 100644 --- a/programs/nft-voter/tests/program_test/nft_voter_test.rs +++ b/programs/nft-voter/tests/program_test/nft_voter_test.rs @@ -8,9 +8,7 @@ use gpl_nft_voter::state::max_voter_weight_record::{ get_max_voter_weight_record_address, MaxVoterWeightRecord, }; use gpl_nft_voter::state::*; -use gpl_nft_voter::tools::governance::{ - find_nft_power_holding_account_address, -}; +use gpl_nft_voter::tools::governance::find_nft_power_holding_account_address; use solana_sdk::transport::TransportError; use spl_governance::instruction::cast_vote; use spl_governance::state::vote_record::{self, Vote, VoteChoice}; @@ -645,15 +643,16 @@ impl NftVoterTest { ); let accounts = gpl_nft_voter::accounts::CreateGovernanceTokenHoldingAccount { + holding_account_info: holding_account_key, governance_program_id: self.governance.program_id, - realm: registrar_cookie.account.realm, + registrar: registrar_cookie.address, realm_governing_token_mint: registrar_cookie.account.governing_token_mint, payer: self.bench.payer.pubkey(), - system_program: solana_sdk::system_program::id(), - holding_account_info: holding_account_key, nft_mint: nft_cookie.mint_cookie.address, + nft_metadata: nft_cookie.metadata, associated_token_program: anchor_spl::associated_token::ID, token_program: spl_token::id(), + system_program: solana_sdk::system_program::id(), rent: Pubkey::from_str("SysvarRent111111111111111111111111111111111") .map_err(|e| TransportError::Custom(e.to_string()))?, }; From b62b2a3e054175afa10470d793a43ae8b9c0b089 Mon Sep 17 00:00:00 2001 From: Austin Milt Date: Wed, 17 Aug 2022 14:58:16 -0600 Subject: [PATCH 10/17] cleanup --- programs/nft-voter/src/error.rs | 3 --- .../instructions/create_governance_token_holding_account.rs | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/programs/nft-voter/src/error.rs b/programs/nft-voter/src/error.rs index c48f96a5..87d3d684 100644 --- a/programs/nft-voter/src/error.rs +++ b/programs/nft-voter/src/error.rs @@ -82,7 +82,4 @@ pub enum NftVoterError { #[msg("NFT must belong to a collection configured for the realm")] InvalidNftCollection, - - #[msg("Account already initialized")] - AccountAlreadyInitialized, } diff --git a/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs b/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs index f82f683c..53b39ae7 100644 --- a/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs +++ b/programs/nft-voter/src/instructions/create_governance_token_holding_account.rs @@ -51,6 +51,7 @@ pub struct CreateGovernanceTokenHoldingAccount<'info> { /// Metadata of the NFT for which the holding account is being created. The /// NFT must have a verified collection configured for the realm. + /// CHECK: metadata account cant be automatically deserialized by anchor pub nft_metadata: UncheckedAccount<'info>, /// Associated token program that will own the holding account From d531f85addfdd87e839fcb17ed84de5f998f2334 Mon Sep 17 00:00:00 2001 From: Austin Milt Date: Thu, 18 Aug 2022 15:00:11 -0700 Subject: [PATCH 11/17] calculate NFT net power from holdinig account (tests stubbed) --- programs/nft-voter/src/error.rs | 6 +++ .../src/instructions/cast_nft_vote.rs | 3 +- .../update_voter_weight_record.rs | 5 ++- programs/nft-voter/src/state/registrar.rs | 40 +++++++++++++++---- programs/nft-voter/src/tools/governance.rs | 4 +- programs/nft-voter/tests/cast_nft_vote.rs | 21 ++++++++++ ...create_governance_token_holding_account.rs | 13 +++--- .../tests/deposit_governance_tokens.rs | 10 ++--- .../tests/program_test/nft_voter_test.rs | 21 ++++++++++ .../tests/withdraw_governance_tokens.rs | 24 ++++++++++- 10 files changed, 123 insertions(+), 24 deletions(-) diff --git a/programs/nft-voter/src/error.rs b/programs/nft-voter/src/error.rs index 87d3d684..2695a388 100644 --- a/programs/nft-voter/src/error.rs +++ b/programs/nft-voter/src/error.rs @@ -82,4 +82,10 @@ pub enum NftVoterError { #[msg("NFT must belong to a collection configured for the realm")] InvalidNftCollection, + + #[msg("Invalid NFT voting power holding account token mint")] + InvalidHoldingAccountMint, + + #[msg("Provided NFT power holding account address doesnt match expected")] + InvalidHoldingAccountAddress, } diff --git a/programs/nft-voter/src/instructions/cast_nft_vote.rs b/programs/nft-voter/src/instructions/cast_nft_vote.rs index be2c6a7e..0446f4f1 100644 --- a/programs/nft-voter/src/instructions/cast_nft_vote.rs +++ b/programs/nft-voter/src/instructions/cast_nft_vote.rs @@ -71,7 +71,7 @@ pub fn cast_nft_vote<'a, 'b, 'c, 'info>( let rent = Rent::get()?; - for (nft_info, nft_metadata_info, nft_vote_record_info) in + for (nft_info, nft_metadata_info, nft_vote_record_info, nft_power_holding_account_info) in ctx.remaining_accounts.iter().tuples() { let (nft_vote_weight, nft_mint) = resolve_nft_vote_weight_and_mint( @@ -79,6 +79,7 @@ pub fn cast_nft_vote<'a, 'b, 'c, 'info>( &governing_token_owner, nft_info, nft_metadata_info, + nft_power_holding_account_info, &mut unique_nft_mints, )?; diff --git a/programs/nft-voter/src/instructions/update_voter_weight_record.rs b/programs/nft-voter/src/instructions/update_voter_weight_record.rs index 43145e2a..9637f543 100644 --- a/programs/nft-voter/src/instructions/update_voter_weight_record.rs +++ b/programs/nft-voter/src/instructions/update_voter_weight_record.rs @@ -49,12 +49,15 @@ pub fn update_voter_weight_record( // Ensure all nfts are unique let mut unique_nft_mints = vec![]; - for (nft_info, nft_metadata_info) in ctx.remaining_accounts.iter().tuples() { + for (nft_info, nft_metadata_info, nft_power_holding_account_info) in + ctx.remaining_accounts.iter().tuples() + { let (nft_vote_weight, _) = resolve_nft_vote_weight_and_mint( registrar, governing_token_owner, nft_info, nft_metadata_info, + nft_power_holding_account_info, &mut unique_nft_mints, )?; diff --git a/programs/nft-voter/src/state/registrar.rs b/programs/nft-voter/src/state/registrar.rs index 8ce82e5d..38ac735c 100644 --- a/programs/nft-voter/src/state/registrar.rs +++ b/programs/nft-voter/src/state/registrar.rs @@ -1,10 +1,12 @@ +use std::cmp::max; + use crate::{ error::NftVoterError, id, state::{CollectionConfig, VoterWeightRecord}, tools::{ - anchor::DISCRIMINATOR_SIZE, spl_token::get_spl_token_amount, - token_metadata::get_token_metadata_for_mint, + anchor::DISCRIMINATOR_SIZE, governance::find_nft_power_holding_account_address, + spl_token::get_spl_token_amount, token_metadata::get_token_metadata_for_mint, }, }; use anchor_lang::prelude::*; @@ -88,7 +90,7 @@ pub fn resolve_governing_token_owner( voter_token_owner_record.assert_token_owner_or_delegate_is_signer(voter_authority_info)?; // Assert voter TokenOwnerRecord and VoterWeightRecord are for the same governing_token_owner - require_eq!( + require_keys_eq!( voter_token_owner_record.governing_token_owner, voter_weight_record.governing_token_owner, NftVoterError::InvalidTokenOwnerForVoterWeightRecord @@ -103,9 +105,11 @@ pub fn resolve_nft_vote_weight_and_mint( governing_token_owner: &Pubkey, nft_info: &AccountInfo, nft_metadata_info: &AccountInfo, + nft_power_holding_account_info: &AccountInfo, unique_nft_mints: &mut Vec, ) -> Result<(u64, Pubkey)> { let nft_owner = get_spl_token_owner(nft_info)?; + let nft_mint = get_spl_token_mint(nft_info)?; // voter_weight_record.governing_token_owner must be the owner of the NFT require!( @@ -113,7 +117,30 @@ pub fn resolve_nft_vote_weight_and_mint( NftVoterError::VoterDoesNotOwnNft ); - let nft_mint = get_spl_token_mint(nft_info)?; + let expected_nft_power_holding_account_address = find_nft_power_holding_account_address( + ®istrar.realm, + ®istrar.governing_token_mint, + &nft_mint, + ); + + require_keys_eq!( + expected_nft_power_holding_account_address, + *nft_power_holding_account_info.key, + NftVoterError::InvalidHoldingAccountAddress + ); + + // For compatibility with NFT-based DAOs that dont use fungibles to boost the NFT's weight, + // we cant require the holding account to be initialized + let mut nft_power_holding_account_amount = 0; + if !nft_power_holding_account_info.data_is_empty() { + let nft_power_holding_account_mint = get_spl_token_mint(nft_power_holding_account_info)?; + require_keys_eq!( + nft_power_holding_account_mint, + registrar.governing_token_mint, + NftVoterError::InvalidHoldingAccountMint + ); + nft_power_holding_account_amount = get_spl_token_amount(nft_power_holding_account_info)?; + } // Ensure the same NFT was not provided more than once if unique_nft_mints.contains(&nft_mint) { @@ -137,10 +164,9 @@ pub fn resolve_nft_vote_weight_and_mint( let collection_config = registrar.get_collection_config(collection.key)?; - //TODO get the token holding account assigned to this NFT - // TODO compute voter weight based on collection_config.weight and holding account balance + let nft_power = collection_config.weight * max(1, nft_power_holding_account_amount); - Ok((collection_config.weight, nft_mint)) + Ok((nft_power, nft_mint)) } #[cfg(test)] diff --git a/programs/nft-voter/src/tools/governance.rs b/programs/nft-voter/src/tools/governance.rs index 84449621..9c112adf 100644 --- a/programs/nft-voter/src/tools/governance.rs +++ b/programs/nft-voter/src/tools/governance.rs @@ -1,8 +1,6 @@ use anchor_lang::prelude::Pubkey; use spl_governance::state::{token_owner_record, vote_record}; -use crate::id; - pub const NFT_POWER_HOLDING_ACCOUNT_SEED_PREFIX: [u8; 25] = *b"nft-power-holding-account"; pub fn find_nft_power_holding_account_address( @@ -17,7 +15,7 @@ pub fn find_nft_power_holding_account_address( governance_token_mint.as_ref(), nft_mint.as_ref(), ], - &id(), + &spl_token::ID, ) .0 } diff --git a/programs/nft-voter/tests/cast_nft_vote.rs b/programs/nft-voter/tests/cast_nft_vote.rs index 97e3934e..63bc00e5 100644 --- a/programs/nft-voter/tests/cast_nft_vote.rs +++ b/programs/nft-voter/tests/cast_nft_vote.rs @@ -1368,3 +1368,24 @@ async fn test_cast_nft_vote_with_invalid_voter_weight_token_owner_error( Ok(()) } + + +#[tokio::test] +async fn test_cast_nft_vote_no_power_holding_account_ok( +) -> Result<(), TransportError> { + todo!() +} + + +#[tokio::test] +async fn test_cast_nft_vote_power_holding_account_zero_balance_uses_to_collection_weight( +) -> Result<(), TransportError> { + todo!() +} + + +#[tokio::test] +async fn test_cast_nft_vote_power_holding_account_nonzero_balance_uses_collection_weight_multiplied_balance( +) -> Result<(), TransportError> { + todo!() +} \ No newline at end of file diff --git a/programs/nft-voter/tests/create_governance_token_holding_account.rs b/programs/nft-voter/tests/create_governance_token_holding_account.rs index 29ee3124..60a06d99 100644 --- a/programs/nft-voter/tests/create_governance_token_holding_account.rs +++ b/programs/nft-voter/tests/create_governance_token_holding_account.rs @@ -1,10 +1,10 @@ use gpl_nft_voter::error::NftVoterError; use program_test::nft_voter_test::NftVoterTest; +use program_test::tools::assert_nft_voter_err; use solana_program::program_option::COption; use solana_program_test::*; use solana_sdk::transport::TransportError; use spl_token::state::AccountState; -use program_test::tools::assert_nft_voter_err; mod program_test; @@ -46,7 +46,7 @@ async fn test_create_governance_token_holding_account() -> Result<(), TransportE // Act let governance_token_holding_account_cookie = nft_voter_test - .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) + .with_governance_token_holding_account(®istrar_cookie, &nft_cookie, None) .await?; // Assert @@ -138,12 +138,16 @@ async fn test_create_governance_token_holding_account_nft_is_not_part_of_configu // create the NFT with a different collection not configured for th realm let nft_cookie = nft_voter_test .token_metadata - .with_nft_v2(&nft_voter_test.token_metadata.with_nft_collection().await?, &voter_cookie, None) + .with_nft_v2( + &nft_voter_test.token_metadata.with_nft_collection().await?, + &voter_cookie, + None, + ) .await?; // Act let error = nft_voter_test - .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) + .with_governance_token_holding_account(®istrar_cookie, &nft_cookie, None) .await .err(); @@ -153,7 +157,6 @@ async fn test_create_governance_token_holding_account_nft_is_not_part_of_configu Ok(()) } - fn assert_eq_formatted( expected: T, actual: T, diff --git a/programs/nft-voter/tests/deposit_governance_tokens.rs b/programs/nft-voter/tests/deposit_governance_tokens.rs index 188c8195..7c9a8aa1 100644 --- a/programs/nft-voter/tests/deposit_governance_tokens.rs +++ b/programs/nft-voter/tests/deposit_governance_tokens.rs @@ -26,7 +26,7 @@ async fn test_deposit_governance_tokens_first_deposit_creates_record() -> Result .await?; let governance_token_holding_account_cookie = nft_voter_test - .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) + .with_governance_token_holding_account(®istrar_cookie, &nft_cookie, None) .await?; let governing_token_source_account_cookie = nft_voter_test @@ -101,7 +101,7 @@ async fn test_deposit_governance_tokens_record_exists_doesnt_error() -> Result<( .await?; let governance_token_holding_account_cookie = nft_voter_test - .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) + .with_governance_token_holding_account(®istrar_cookie, &nft_cookie, None) .await?; let governing_token_source_account_cookie = nft_voter_test @@ -167,7 +167,7 @@ async fn test_deposit_governance_tokens_transfers_tokens_to_holding_account( .await?; let governance_token_holding_account_cookie = nft_voter_test - .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) + .with_governance_token_holding_account(®istrar_cookie, &nft_cookie, None) .await?; let source_tokens: u64 = 1000; @@ -244,7 +244,7 @@ async fn test_deposit_governance_tokens_multiple_deposits_holding_account_stores .await?; let governance_token_holding_account_cookie = nft_voter_test - .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) + .with_governance_token_holding_account(®istrar_cookie, &nft_cookie, None) .await?; let source_tokens: u64 = 1000; @@ -332,7 +332,7 @@ async fn test_deposit_governance_tokens_source_insufficient_balance_errors( .await?; let governance_token_holding_account_cookie = nft_voter_test - .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) + .with_governance_token_holding_account(®istrar_cookie, &nft_cookie, None) .await?; let source_tokens: u64 = 1000; diff --git a/programs/nft-voter/tests/program_test/nft_voter_test.rs b/programs/nft-voter/tests/program_test/nft_voter_test.rs index 927f8f01..b3253824 100644 --- a/programs/nft-voter/tests/program_test/nft_voter_test.rs +++ b/programs/nft-voter/tests/program_test/nft_voter_test.rs @@ -530,6 +530,13 @@ impl NftVoterTest { ); account_metas.push(AccountMeta::new(nft_vote_record_key, false)); + let nft_power_holding_account_key = find_nft_power_holding_account_address( + ®istrar_cookie.account.realm, + ®istrar_cookie.account.governing_token_mint, + &nft_cookie.mint_cookie.address, + ); + account_metas.push(AccountMeta::new(nft_power_holding_account_key, false)); + let account = NftVoteRecord { proposal: proposal_cookie.address, nft_mint: nft_cookie.mint_cookie.address, @@ -616,10 +623,12 @@ impl NftVoterTest { &self, registrar_cookie: &RegistrarCookie, nft_cookie: &NftCookie, + initial_amount: Option, ) -> Result { self.with_governance_token_holding_account_using_ix( registrar_cookie, nft_cookie, + initial_amount, NopOverride, ) .await @@ -630,6 +639,7 @@ impl NftVoterTest { &self, registrar_cookie: &RegistrarCookie, nft_cookie: &NftCookie, + initial_amount: Option, instruction_override: F, ) -> Result { let holding_account_key = find_nft_power_holding_account_address( @@ -672,6 +682,17 @@ impl NftVoterTest { ) .await?; + if let Some(initial_amount) = initial_amount { + self.bench + .mint_tokens( + ®istrar_cookie.account.governing_token_mint, + ®istrar_cookie.realm_authority, + &holding_account_key, + initial_amount, + ) + .await?; + } + let account = self.bench.get_anchor_account(holding_account_key).await; Ok(GovernanceTokenHoldingAccountCookie { diff --git a/programs/nft-voter/tests/withdraw_governance_tokens.rs b/programs/nft-voter/tests/withdraw_governance_tokens.rs index 53e2916d..714fe21c 100644 --- a/programs/nft-voter/tests/withdraw_governance_tokens.rs +++ b/programs/nft-voter/tests/withdraw_governance_tokens.rs @@ -11,7 +11,27 @@ async fn test_withdraw_governance_tokens_nothing_deposited_errors() -> Result<() let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + let registrar_cookie = &mut nft_voter_test.with_registrar(&realm_cookie).await?; + + let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; + let max_voter_weight_record = nft_voter_test + .with_max_voter_weight_record(®istrar_cookie) + .await?; + + nft_voter_test + .with_collection( + ®istrar_cookie, + &nft_collection_cookie, + &max_voter_weight_record, + None, + ) + .await?; + + let registrar_updated = nft_voter_test + .get_registrar_account(®istrar_cookie.address) + .await; + + registrar_cookie.account = registrar_updated; let owner_cookie = nft_voter_test.bench.with_wallet_funded(100000000000).await; @@ -23,7 +43,7 @@ async fn test_withdraw_governance_tokens_nothing_deposited_errors() -> Result<() .await?; let governance_token_holding_account_cookie = nft_voter_test - .with_governance_token_holding_account(®istrar_cookie, &nft_cookie) + .with_governance_token_holding_account(®istrar_cookie, &nft_cookie, None) .await?; let governing_token_source_account_cookie = nft_voter_test From a9dd6b0c3962d94b873c4b685be6e4a8bcccb772 Mon Sep 17 00:00:00 2001 From: Austin Milt Date: Fri, 19 Aug 2022 09:27:27 -0700 Subject: [PATCH 12/17] cast nft vote tests and cleanup --- programs/nft-voter/src/state/registrar.rs | 2 +- programs/nft-voter/src/tools/governance.rs | 4 +- programs/nft-voter/tests/cast_nft_vote.rs | 240 +++++++++++++++++- ...create_governance_token_holding_account.rs | 69 ++--- programs/nft-voter/tests/create_registrar.rs | 4 + .../tests/deposit_governance_tokens.rs | 43 ++-- .../tests/program_test/nft_voter_test.rs | 58 ++++- .../tests/withdraw_governance_tokens.rs | 2 +- 8 files changed, 321 insertions(+), 101 deletions(-) diff --git a/programs/nft-voter/src/state/registrar.rs b/programs/nft-voter/src/state/registrar.rs index 38ac735c..e9be0c81 100644 --- a/programs/nft-voter/src/state/registrar.rs +++ b/programs/nft-voter/src/state/registrar.rs @@ -129,7 +129,7 @@ pub fn resolve_nft_vote_weight_and_mint( NftVoterError::InvalidHoldingAccountAddress ); - // For compatibility with NFT-based DAOs that dont use fungibles to boost the NFT's weight, + // For compatibility with NFT-based DAOs that dont use fungibles to boost the NFT's weight, // we cant require the holding account to be initialized let mut nft_power_holding_account_amount = 0; if !nft_power_holding_account_info.data_is_empty() { diff --git a/programs/nft-voter/src/tools/governance.rs b/programs/nft-voter/src/tools/governance.rs index 9c112adf..84449621 100644 --- a/programs/nft-voter/src/tools/governance.rs +++ b/programs/nft-voter/src/tools/governance.rs @@ -1,6 +1,8 @@ use anchor_lang::prelude::Pubkey; use spl_governance::state::{token_owner_record, vote_record}; +use crate::id; + pub const NFT_POWER_HOLDING_ACCOUNT_SEED_PREFIX: [u8; 25] = *b"nft-power-holding-account"; pub fn find_nft_power_holding_account_address( @@ -15,7 +17,7 @@ pub fn find_nft_power_holding_account_address( governance_token_mint.as_ref(), nft_mint.as_ref(), ], - &spl_token::ID, + &id(), ) .0 } diff --git a/programs/nft-voter/tests/cast_nft_vote.rs b/programs/nft-voter/tests/cast_nft_vote.rs index 63bc00e5..b9ada9f0 100644 --- a/programs/nft-voter/tests/cast_nft_vote.rs +++ b/programs/nft-voter/tests/cast_nft_vote.rs @@ -1369,23 +1369,245 @@ async fn test_cast_nft_vote_with_invalid_voter_weight_token_owner_error( Ok(()) } - #[tokio::test] -async fn test_cast_nft_vote_no_power_holding_account_ok( -) -> Result<(), TransportError> { - todo!() -} +async fn test_cast_nft_vote_no_power_holding_account_ok() -> Result<(), TransportError> { + // Arrange + let mut nft_voter_test = NftVoterTest::start_new().await; + + let realm_cookie = nft_voter_test.governance.with_realm().await?; + let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + + let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; + + let max_voter_weight_record_cookie = nft_voter_test + .with_max_voter_weight_record(®istrar_cookie) + .await?; + + nft_voter_test + .with_collection( + ®istrar_cookie, + &nft_collection_cookie, + &max_voter_weight_record_cookie, + Some(ConfigureCollectionArgs { + weight: 10, + size: 20, + }), + ) + .await?; + + let voter_cookie = nft_voter_test.bench.with_wallet().await; + + let voter_token_owner_record_cookie = nft_voter_test + .governance + .with_token_owner_record(&realm_cookie, &voter_cookie) + .await?; + + let voter_weight_record_cookie = nft_voter_test + .with_voter_weight_record(®istrar_cookie, &voter_cookie) + .await?; + + let proposal_cookie = nft_voter_test + .governance + .with_proposal(&realm_cookie) + .await?; + + let nft_cookie1 = nft_voter_test + .token_metadata + .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .await?; + + nft_voter_test.bench.advance_clock().await; + + // Act + nft_voter_test + .cast_nft_vote( + ®istrar_cookie, + &voter_weight_record_cookie, + &max_voter_weight_record_cookie, + &proposal_cookie, + &voter_cookie, + &voter_token_owner_record_cookie, + &[&nft_cookie1], + None, + ) + .await?; + + // Assert + let voter_weight_record = nft_voter_test + .get_voter_weight_record(&voter_weight_record_cookie.address) + .await; + + assert_eq!(voter_weight_record.voter_weight, 10); + + Ok(()) +} #[tokio::test] async fn test_cast_nft_vote_power_holding_account_zero_balance_uses_to_collection_weight( ) -> Result<(), TransportError> { - todo!() -} + // Arrange + let mut nft_voter_test = NftVoterTest::start_new().await; + + let realm_cookie = nft_voter_test.governance.with_realm().await?; + + let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + + let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; + + let max_voter_weight_record_cookie = nft_voter_test + .with_max_voter_weight_record(®istrar_cookie) + .await?; + + nft_voter_test + .with_collection( + ®istrar_cookie, + &nft_collection_cookie, + &max_voter_weight_record_cookie, + Some(ConfigureCollectionArgs { + weight: 10, + size: 20, + }), + ) + .await?; + + let voter_cookie = nft_voter_test.bench.with_wallet().await; + + let voter_token_owner_record_cookie = nft_voter_test + .governance + .with_token_owner_record(&realm_cookie, &voter_cookie) + .await?; + + let voter_weight_record_cookie = nft_voter_test + .with_voter_weight_record(®istrar_cookie, &voter_cookie) + .await?; + let proposal_cookie = nft_voter_test + .governance + .with_proposal(&realm_cookie) + .await?; + + let nft_cookie1 = nft_voter_test + .token_metadata + .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .await?; + + nft_voter_test + .with_governance_token_holding_account(®istrar_cookie, &realm_cookie, &nft_cookie1, None) + .await?; + + nft_voter_test.bench.advance_clock().await; + + // Act + nft_voter_test + .cast_nft_vote( + ®istrar_cookie, + &voter_weight_record_cookie, + &max_voter_weight_record_cookie, + &proposal_cookie, + &voter_cookie, + &voter_token_owner_record_cookie, + &[&nft_cookie1], + None, + ) + .await?; + + // Assert + let voter_weight_record = nft_voter_test + .get_voter_weight_record(&voter_weight_record_cookie.address) + .await; + + assert_eq!(voter_weight_record.voter_weight, 10); + + Ok(()) +} #[tokio::test] async fn test_cast_nft_vote_power_holding_account_nonzero_balance_uses_collection_weight_multiplied_balance( ) -> Result<(), TransportError> { - todo!() -} \ No newline at end of file + // Arrange + let collection_weight = 11u64; + let holding_account_balance = 33; + + let mut nft_voter_test = NftVoterTest::start_new().await; + + let realm_cookie = nft_voter_test.governance.with_realm().await?; + + let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + + let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; + + let max_voter_weight_record_cookie = nft_voter_test + .with_max_voter_weight_record(®istrar_cookie) + .await?; + + nft_voter_test + .with_collection( + ®istrar_cookie, + &nft_collection_cookie, + &max_voter_weight_record_cookie, + Some(ConfigureCollectionArgs { + weight: collection_weight, + size: 20, + }), + ) + .await?; + + let voter_cookie = nft_voter_test.bench.with_wallet().await; + + let voter_token_owner_record_cookie = nft_voter_test + .governance + .with_token_owner_record(&realm_cookie, &voter_cookie) + .await?; + + let voter_weight_record_cookie = nft_voter_test + .with_voter_weight_record(®istrar_cookie, &voter_cookie) + .await?; + + let proposal_cookie = nft_voter_test + .governance + .with_proposal(&realm_cookie) + .await?; + + let nft_cookie1 = nft_voter_test + .token_metadata + .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .await?; + + nft_voter_test + .with_governance_token_holding_account( + ®istrar_cookie, + &realm_cookie, + &nft_cookie1, + Some(holding_account_balance), + ) + .await?; + + nft_voter_test.bench.advance_clock().await; + + // Act + nft_voter_test + .cast_nft_vote( + ®istrar_cookie, + &voter_weight_record_cookie, + &max_voter_weight_record_cookie, + &proposal_cookie, + &voter_cookie, + &voter_token_owner_record_cookie, + &[&nft_cookie1], + None, + ) + .await?; + + // Assert + let voter_weight_record = nft_voter_test + .get_voter_weight_record(&voter_weight_record_cookie.address) + .await; + + assert_eq!( + voter_weight_record.voter_weight, + collection_weight * holding_account_balance + ); + + Ok(()) +} diff --git a/programs/nft-voter/tests/create_governance_token_holding_account.rs b/programs/nft-voter/tests/create_governance_token_holding_account.rs index 60a06d99..1d40d667 100644 --- a/programs/nft-voter/tests/create_governance_token_holding_account.rs +++ b/programs/nft-voter/tests/create_governance_token_holding_account.rs @@ -15,38 +15,29 @@ async fn test_create_governance_token_holding_account() -> Result<(), TransportE let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = &mut nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - let max_voter_weight_record = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record, - None, - ) + let registrar_cookie = &mut nft_voter_test + .with_registrar_with_collection(&realm_cookie) .await?; - let registrar_updated = nft_voter_test - .get_registrar_account(®istrar_cookie.address) - .await; - - registrar_cookie.account = registrar_updated; - let voter_cookie = nft_voter_test.bench.with_wallet().await; let nft_cookie = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie, + None, + ) .await?; // Act let governance_token_holding_account_cookie = nft_voter_test - .with_governance_token_holding_account(®istrar_cookie, &nft_cookie, None) + .with_governance_token_holding_account(®istrar_cookie, &realm_cookie, &nft_cookie, None) .await?; // Assert @@ -101,38 +92,10 @@ async fn test_create_governance_token_holding_account_nft_is_not_part_of_configu let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - let max_voter_weight_record = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) + let registrar_cookie = &mut nft_voter_test + .with_registrar_with_collection(&realm_cookie) .await?; - assert_eq!( - 0, - registrar_cookie.account.collection_configs.len(), - "Unexpected collection already configured for registrar" - ); - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record, - None, - ) - .await?; - - let registrar_updated = nft_voter_test - .get_registrar_account(®istrar_cookie.address) - .await; - - assert_eq!( - 1, - registrar_updated.collection_configs.len(), - "Unable to add collection to registrar" - ); - let voter_cookie = nft_voter_test.bench.with_wallet().await; // create the NFT with a different collection not configured for th realm @@ -147,7 +110,7 @@ async fn test_create_governance_token_holding_account_nft_is_not_part_of_configu // Act let error = nft_voter_test - .with_governance_token_holding_account(®istrar_cookie, &nft_cookie, None) + .with_governance_token_holding_account(®istrar_cookie, &realm_cookie, &nft_cookie, None) .await .err(); diff --git a/programs/nft-voter/tests/create_registrar.rs b/programs/nft-voter/tests/create_registrar.rs index 29a9cc30..04fa50ee 100644 --- a/programs/nft-voter/tests/create_registrar.rs +++ b/programs/nft-voter/tests/create_registrar.rs @@ -63,6 +63,7 @@ async fn test_create_registrar_with_realm_authority_must_sign_error() -> Result< let err = nft_voter_test .with_registrar_using_ix( &realm_cookie, + false, |i| i.accounts[4].is_signer = false, // realm_authority Some(&[]), ) @@ -91,6 +92,7 @@ async fn test_create_registrar_with_invalid_spl_gov_program_id_error() -> Result let err = nft_voter_test .with_registrar_using_ix( &realm_cookie, + false, |i| i.accounts[1].pubkey = governance_program_id, //governance_program_id None, ) @@ -115,6 +117,7 @@ async fn test_create_registrar_with_invalid_realm_error() -> Result<(), Transpor let err = nft_voter_test .with_registrar_using_ix( &realm_cookie, + false, |i| i.accounts[2].pubkey = Pubkey::new_unique(), // realm None, ) @@ -143,6 +146,7 @@ async fn test_create_registrar_with_invalid_governing_token_mint_error( let err = nft_voter_test .with_registrar_using_ix( &realm_cookie, + false, |i| i.accounts[3].pubkey = mint_cookie.address, // governing_token_mint None, ) diff --git a/programs/nft-voter/tests/deposit_governance_tokens.rs b/programs/nft-voter/tests/deposit_governance_tokens.rs index 7c9a8aa1..3be4e9d1 100644 --- a/programs/nft-voter/tests/deposit_governance_tokens.rs +++ b/programs/nft-voter/tests/deposit_governance_tokens.rs @@ -14,19 +14,17 @@ async fn test_deposit_governance_tokens_first_deposit_creates_record() -> Result let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + let registrar_cookie = nft_voter_test.with_registrar_with_collection(&realm_cookie).await?; let owner_cookie = nft_voter_test.bench.with_wallet_funded(100000000000).await; - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - let nft_cookie = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &owner_cookie, None) + .with_nft_v2(®istrar_cookie.collection_cookies.as_ref().unwrap().first().unwrap(), &owner_cookie, None) .await?; let governance_token_holding_account_cookie = nft_voter_test - .with_governance_token_holding_account(®istrar_cookie, &nft_cookie, None) + .with_governance_token_holding_account(®istrar_cookie, &realm_cookie, &nft_cookie, None) .await?; let governing_token_source_account_cookie = nft_voter_test @@ -89,19 +87,17 @@ async fn test_deposit_governance_tokens_record_exists_doesnt_error() -> Result<( let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + let registrar_cookie = nft_voter_test.with_registrar_with_collection(&realm_cookie).await?; let owner_cookie = nft_voter_test.bench.with_wallet_funded(100000000000).await; - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - let nft_cookie = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &owner_cookie, None) + .with_nft_v2(®istrar_cookie.collection_cookies.as_ref().unwrap().first().unwrap(), &owner_cookie, None) .await?; let governance_token_holding_account_cookie = nft_voter_test - .with_governance_token_holding_account(®istrar_cookie, &nft_cookie, None) + .with_governance_token_holding_account(®istrar_cookie, &realm_cookie, &nft_cookie, None) .await?; let governing_token_source_account_cookie = nft_voter_test @@ -155,19 +151,17 @@ async fn test_deposit_governance_tokens_transfers_tokens_to_holding_account( let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + let registrar_cookie = nft_voter_test.with_registrar_with_collection(&realm_cookie).await?; let owner_cookie = nft_voter_test.bench.with_wallet_funded(100000000000).await; - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - let nft_cookie = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &owner_cookie, None) + .with_nft_v2(®istrar_cookie.collection_cookies.as_ref().unwrap().first().unwrap(), &owner_cookie, None) .await?; let governance_token_holding_account_cookie = nft_voter_test - .with_governance_token_holding_account(®istrar_cookie, &nft_cookie, None) + .with_governance_token_holding_account(®istrar_cookie, &realm_cookie, &nft_cookie, None) .await?; let source_tokens: u64 = 1000; @@ -232,19 +226,17 @@ async fn test_deposit_governance_tokens_multiple_deposits_holding_account_stores let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + let registrar_cookie = nft_voter_test.with_registrar_with_collection(&realm_cookie).await?; let owner_cookie = nft_voter_test.bench.with_wallet_funded(100000000000).await; - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - let nft_cookie = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &owner_cookie, None) + .with_nft_v2(®istrar_cookie.collection_cookies.as_ref().unwrap().first().unwrap(), &owner_cookie, None) .await?; let governance_token_holding_account_cookie = nft_voter_test - .with_governance_token_holding_account(®istrar_cookie, &nft_cookie, None) + .with_governance_token_holding_account(®istrar_cookie, &realm_cookie, &nft_cookie, None) .await?; let source_tokens: u64 = 1000; @@ -320,19 +312,17 @@ async fn test_deposit_governance_tokens_source_insufficient_balance_errors( let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; + let registrar_cookie = nft_voter_test.with_registrar_with_collection(&realm_cookie).await?; let owner_cookie = nft_voter_test.bench.with_wallet_funded(100000000000).await; - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - let nft_cookie = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &owner_cookie, None) + .with_nft_v2(®istrar_cookie.collection_cookies.as_ref().unwrap().first().unwrap(), &owner_cookie, None) .await?; let governance_token_holding_account_cookie = nft_voter_test - .with_governance_token_holding_account(®istrar_cookie, &nft_cookie, None) + .with_governance_token_holding_account(®istrar_cookie, &realm_cookie, &nft_cookie, None) .await?; let source_tokens: u64 = 1000; @@ -360,9 +350,8 @@ async fn test_deposit_governance_tokens_source_insufficient_balance_errors( .await; // Assert - //TODO better error to check for? assert!(result.is_err()); - + Ok(()) } diff --git a/programs/nft-voter/tests/program_test/nft_voter_test.rs b/programs/nft-voter/tests/program_test/nft_voter_test.rs index b3253824..c15ebfca 100644 --- a/programs/nft-voter/tests/program_test/nft_voter_test.rs +++ b/programs/nft-voter/tests/program_test/nft_voter_test.rs @@ -39,6 +39,10 @@ pub struct RegistrarCookie { pub realm_authority: Keypair, pub max_collections: u8, + + // only populated with using with_registrar_with_collection + pub collection_cookies: Option>, + pub max_voter_weight_record_cookie: Option, } pub struct VoterWeightRecordCookie { @@ -46,6 +50,7 @@ pub struct VoterWeightRecordCookie { pub account: VoterWeightRecord, } +#[derive(Debug, PartialEq)] pub struct MaxVoterWeightRecordCookie { pub address: Pubkey, pub account: MaxVoterWeightRecord, @@ -138,7 +143,16 @@ impl NftVoterTest { &mut self, realm_cookie: &RealmCookie, ) -> Result { - self.with_registrar_using_ix(realm_cookie, NopOverride, None) + self.with_registrar_using_ix(realm_cookie, false, NopOverride, None) + .await + } + + #[allow(dead_code)] + pub async fn with_registrar_with_collection( + &mut self, + realm_cookie: &RealmCookie, + ) -> Result { + self.with_registrar_using_ix(realm_cookie, true, NopOverride, None) .await } @@ -146,6 +160,7 @@ impl NftVoterTest { pub async fn with_registrar_using_ix( &mut self, realm_cookie: &RealmCookie, + include_collection: bool, instruction_override: F, signers_override: Option<&[&Keypair]>, ) -> Result { @@ -183,10 +198,6 @@ impl NftVoterTest { let default_signers = &[&realm_cookie.realm_authority]; let signers = signers_override.unwrap_or(default_signers); - self.bench - .process_transaction(&[create_registrar_ix], Some(signers)) - .await?; - let account = Registrar { governance_program_id: self.governance.program_id, realm: realm_cookie.address, @@ -195,12 +206,38 @@ impl NftVoterTest { reserved: [0; 128], }; - Ok(RegistrarCookie { + let mut registrar_cookie = RegistrarCookie { address: registrar_key, account, realm_authority: realm_cookie.get_realm_authority(), max_collections, - }) + max_voter_weight_record_cookie: None, + collection_cookies: None, + }; + + if include_collection { + self.bench + .process_transaction(&[create_registrar_ix], Some(signers)) + .await?; + + let nft_collection_cookie = self.token_metadata.with_nft_collection().await?; + let max_voter_weight_record = + self.with_max_voter_weight_record(®istrar_cookie).await?; + + self.with_collection( + ®istrar_cookie, + &nft_collection_cookie, + &max_voter_weight_record, + None, + ) + .await?; + + registrar_cookie.account = self.get_registrar_account(®istrar_cookie.address).await; + registrar_cookie.max_voter_weight_record_cookie = Some(max_voter_weight_record); + registrar_cookie.collection_cookies = Some(vec![nft_collection_cookie]); + } + + Ok(registrar_cookie) } #[allow(dead_code)] @@ -622,11 +659,13 @@ impl NftVoterTest { pub async fn with_governance_token_holding_account( &self, registrar_cookie: &RegistrarCookie, + realm_cookie: &RealmCookie, nft_cookie: &NftCookie, initial_amount: Option, ) -> Result { self.with_governance_token_holding_account_using_ix( registrar_cookie, + realm_cookie, nft_cookie, initial_amount, NopOverride, @@ -638,6 +677,7 @@ impl NftVoterTest { pub async fn with_governance_token_holding_account_using_ix( &self, registrar_cookie: &RegistrarCookie, + realm_cookie: &RealmCookie, nft_cookie: &NftCookie, initial_amount: Option, instruction_override: F, @@ -685,8 +725,8 @@ impl NftVoterTest { if let Some(initial_amount) = initial_amount { self.bench .mint_tokens( - ®istrar_cookie.account.governing_token_mint, - ®istrar_cookie.realm_authority, + &realm_cookie.community_mint_cookie.address, + &realm_cookie.community_mint_cookie.mint_authority, &holding_account_key, initial_amount, ) diff --git a/programs/nft-voter/tests/withdraw_governance_tokens.rs b/programs/nft-voter/tests/withdraw_governance_tokens.rs index 714fe21c..7ade8339 100644 --- a/programs/nft-voter/tests/withdraw_governance_tokens.rs +++ b/programs/nft-voter/tests/withdraw_governance_tokens.rs @@ -43,7 +43,7 @@ async fn test_withdraw_governance_tokens_nothing_deposited_errors() -> Result<() .await?; let governance_token_holding_account_cookie = nft_voter_test - .with_governance_token_holding_account(®istrar_cookie, &nft_cookie, None) + .with_governance_token_holding_account(®istrar_cookie, &realm_cookie, &nft_cookie, None) .await?; let governing_token_source_account_cookie = nft_voter_test From 4594f94276243b35ca98499e10829abca399426f Mon Sep 17 00:00:00 2001 From: Austin Milt Date: Fri, 19 Aug 2022 10:14:25 -0700 Subject: [PATCH 13/17] add helper to create registrar with collection --- programs/nft-voter/tests/cast_nft_vote.rs | 719 ++++++++++-------- ...create_governance_token_holding_account.rs | 18 +- programs/nft-voter/tests/create_registrar.rs | 8 +- .../tests/deposit_governance_tokens.rs | 109 ++- .../tests/program_test/nft_voter_test.rs | 11 +- 5 files changed, 517 insertions(+), 348 deletions(-) diff --git a/programs/nft-voter/tests/cast_nft_vote.rs b/programs/nft-voter/tests/cast_nft_vote.rs index b9ada9f0..ad33eee8 100644 --- a/programs/nft-voter/tests/cast_nft_vote.rs +++ b/programs/nft-voter/tests/cast_nft_vote.rs @@ -20,23 +20,13 @@ async fn test_cast_nft_vote() -> Result<(), TransportError> { let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - Some(ConfigureCollectionArgs { + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { weight: 10, size: 20, - }), + }, ) .await?; @@ -58,7 +48,16 @@ async fn test_cast_nft_vote() -> Result<(), TransportError> { let nft_cookie1 = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie, + None, + ) .await?; nft_voter_test.bench.advance_clock().await; @@ -69,7 +68,10 @@ async fn test_cast_nft_vote() -> Result<(), TransportError> { .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -110,23 +112,13 @@ async fn test_cast_nft_vote_with_multiple_nfts() -> Result<(), TransportError> { let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - Some(ConfigureCollectionArgs { + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { weight: 10, size: 20, - }), + }, ) .await?; @@ -148,12 +140,30 @@ async fn test_cast_nft_vote_with_multiple_nfts() -> Result<(), TransportError> { let nft_cookie1 = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie, + None, + ) .await?; let nft_cookie2 = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie, + None, + ) .await?; nft_voter_test.bench.advance_clock().await; @@ -164,7 +174,10 @@ async fn test_cast_nft_vote_with_multiple_nfts() -> Result<(), TransportError> { .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -211,20 +224,13 @@ async fn test_cast_nft_vote_with_nft_already_voted_error() -> Result<(), Transpo let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - None, + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { + weight: 10, + size: 20, + }, ) .await?; @@ -246,14 +252,26 @@ async fn test_cast_nft_vote_with_nft_already_voted_error() -> Result<(), Transpo let nft_cookie1 = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie, + None, + ) .await?; nft_voter_test .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -270,7 +288,10 @@ async fn test_cast_nft_vote_with_nft_already_voted_error() -> Result<(), Transpo .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -294,20 +315,13 @@ async fn test_cast_nft_vote_with_invalid_voter_error() -> Result<(), TransportEr let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - None, + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { + weight: 10, + size: 20, + }, ) .await?; @@ -329,7 +343,16 @@ async fn test_cast_nft_vote_with_invalid_voter_error() -> Result<(), TransportEr let nft_cookie1 = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie, + None, + ) .await?; let voter_cookie2 = nft_voter_test.bench.with_wallet().await; @@ -340,7 +363,10 @@ async fn test_cast_nft_vote_with_invalid_voter_error() -> Result<(), TransportEr .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie2, &voter_token_owner_record_cookie, @@ -364,23 +390,13 @@ async fn test_cast_nft_vote_with_unverified_collection_error() -> Result<(), Tra let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - Some(ConfigureCollectionArgs { + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { weight: 10, size: 20, - }), + }, ) .await?; @@ -404,7 +420,12 @@ async fn test_cast_nft_vote_with_unverified_collection_error() -> Result<(), Tra let nft_cookie1 = nft_voter_test .token_metadata .with_nft_v2( - &nft_collection_cookie, + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), &voter_cookie, Some(CreateNftArgs { verify_collection: false, @@ -418,7 +439,10 @@ async fn test_cast_nft_vote_with_unverified_collection_error() -> Result<(), Tra .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -442,23 +466,13 @@ async fn test_cast_nft_vote_with_invalid_owner_error() -> Result<(), TransportEr let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - Some(ConfigureCollectionArgs { + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { weight: 10, size: 20, - }), + }, ) .await?; @@ -482,7 +496,16 @@ async fn test_cast_nft_vote_with_invalid_owner_error() -> Result<(), TransportEr let nft_cookie = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie2, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie2, + None, + ) .await?; // Act @@ -490,7 +513,10 @@ async fn test_cast_nft_vote_with_invalid_owner_error() -> Result<(), TransportEr .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -514,23 +540,13 @@ async fn test_cast_nft_vote_with_invalid_collection_error() -> Result<(), Transp let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - Some(ConfigureCollectionArgs { + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { weight: 10, size: 20, - }), + }, ) .await?; @@ -562,7 +578,10 @@ async fn test_cast_nft_vote_with_invalid_collection_error() -> Result<(), Transp .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -586,23 +605,13 @@ async fn test_cast_nft_vote_with_invalid_metadata_error() -> Result<(), Transpor let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - Some(ConfigureCollectionArgs { + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { weight: 10, size: 20, - }), + }, ) .await?; @@ -625,7 +634,12 @@ async fn test_cast_nft_vote_with_invalid_metadata_error() -> Result<(), Transpor let mut nft1_cookie = nft_voter_test .token_metadata .with_nft_v2( - &nft_collection_cookie, + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), &voter_cookie, Some(CreateNftArgs { verify_collection: false, @@ -636,7 +650,16 @@ async fn test_cast_nft_vote_with_invalid_metadata_error() -> Result<(), Transpor let nft2_cookie = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie, + None, + ) .await?; // Try to use verified NFT Metadata @@ -647,7 +670,10 @@ async fn test_cast_nft_vote_with_invalid_metadata_error() -> Result<(), Transpor .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -671,20 +697,13 @@ async fn test_cast_nft_vote_with_same_nft_error() -> Result<(), TransportError> let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - None, + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { + weight: 10, + size: 20, + }, ) .await?; @@ -706,7 +725,16 @@ async fn test_cast_nft_vote_with_same_nft_error() -> Result<(), TransportError> let nft_cookie = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie, + None, + ) .await?; // Act @@ -714,7 +742,10 @@ async fn test_cast_nft_vote_with_same_nft_error() -> Result<(), TransportError> .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -739,23 +770,13 @@ async fn test_cast_nft_vote_with_no_nft_error() -> Result<(), TransportError> { let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - Some(ConfigureCollectionArgs { + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { weight: 10, size: 20, - }), + }, ) .await?; @@ -778,7 +799,12 @@ async fn test_cast_nft_vote_with_no_nft_error() -> Result<(), TransportError> { let nft_cookie1 = nft_voter_test .token_metadata .with_nft_v2( - &nft_collection_cookie, + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), &voter_cookie, Some(CreateNftArgs { amount: 0, @@ -792,7 +818,10 @@ async fn test_cast_nft_vote_with_no_nft_error() -> Result<(), TransportError> { .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -816,23 +845,13 @@ async fn test_cast_nft_vote_with_max_5_nfts() -> Result<(), TransportError> { let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - Some(ConfigureCollectionArgs { + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { weight: 10, size: 20, - }), + }, ) .await?; @@ -858,7 +877,16 @@ async fn test_cast_nft_vote_with_max_5_nfts() -> Result<(), TransportError> { nft_voter_test.bench.advance_clock().await; let nft_cookie = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie, + None, + ) .await?; nft_cookies.push(nft_cookie) @@ -872,7 +900,10 @@ async fn test_cast_nft_vote_with_max_5_nfts() -> Result<(), TransportError> { .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -919,23 +950,13 @@ async fn test_cast_nft_vote_using_multiple_instructions() -> Result<(), Transpor let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - Some(ConfigureCollectionArgs { + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { weight: 10, size: 20, - }), + }, ) .await?; @@ -957,7 +978,16 @@ async fn test_cast_nft_vote_using_multiple_instructions() -> Result<(), Transpor let nft_cookie1 = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie, + None, + ) .await?; nft_voter_test.bench.advance_clock().await; @@ -971,7 +1001,10 @@ async fn test_cast_nft_vote_using_multiple_instructions() -> Result<(), Transpor .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -982,7 +1015,16 @@ async fn test_cast_nft_vote_using_multiple_instructions() -> Result<(), Transpor let nft_cookie2 = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie, + None, + ) .await?; // Act @@ -991,7 +1033,10 @@ async fn test_cast_nft_vote_using_multiple_instructions() -> Result<(), Transpor .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -1028,23 +1073,13 @@ async fn test_cast_nft_vote_using_multiple_instructions_with_nft_already_voted_e let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - Some(ConfigureCollectionArgs { + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { weight: 10, size: 20, - }), + }, ) .await?; @@ -1066,7 +1101,16 @@ async fn test_cast_nft_vote_using_multiple_instructions_with_nft_already_voted_e let nft_cookie1 = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie, + None, + ) .await?; let args = CastNftVoteArgs { @@ -1077,7 +1121,10 @@ async fn test_cast_nft_vote_using_multiple_instructions_with_nft_already_voted_e .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -1092,7 +1139,10 @@ async fn test_cast_nft_vote_using_multiple_instructions_with_nft_already_voted_e .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -1117,23 +1167,13 @@ async fn test_cast_nft_vote_using_multiple_instructions_with_attempted_sandwiche let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - Some(ConfigureCollectionArgs { + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { weight: 10, size: 20, - }), + }, ) .await?; @@ -1155,7 +1195,16 @@ async fn test_cast_nft_vote_using_multiple_instructions_with_attempted_sandwiche let nft_cookie1 = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie, + None, + ) .await?; let args = CastNftVoteArgs { @@ -1167,7 +1216,10 @@ async fn test_cast_nft_vote_using_multiple_instructions_with_attempted_sandwiche .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -1196,7 +1248,10 @@ async fn test_cast_nft_vote_using_multiple_instructions_with_attempted_sandwiche .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -1223,20 +1278,13 @@ async fn test_cast_nft_vote_using_delegate() -> Result<(), TransportError> { let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - None, + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { + weight: 10, + size: 20, + }, ) .await?; @@ -1258,7 +1306,16 @@ async fn test_cast_nft_vote_using_delegate() -> Result<(), TransportError> { let nft_cookie1 = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie, + None, + ) .await?; nft_voter_test.bench.advance_clock().await; @@ -1279,7 +1336,10 @@ async fn test_cast_nft_vote_using_delegate() -> Result<(), TransportError> { .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &delegate_cookie, &voter_token_owner_record_cookie, @@ -1306,20 +1366,13 @@ async fn test_cast_nft_vote_with_invalid_voter_weight_token_owner_error( let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - None, + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { + weight: 10, + size: 20, + }, ) .await?; @@ -1343,7 +1396,16 @@ async fn test_cast_nft_vote_with_invalid_voter_weight_token_owner_error( let nft_cookie1 = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie, + None, + ) .await?; // Act @@ -1352,7 +1414,10 @@ async fn test_cast_nft_vote_with_invalid_voter_weight_token_owner_error( .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie2, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -1376,23 +1441,13 @@ async fn test_cast_nft_vote_no_power_holding_account_ok() -> Result<(), Transpor let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - Some(ConfigureCollectionArgs { + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { weight: 10, size: 20, - }), + }, ) .await?; @@ -1414,7 +1469,16 @@ async fn test_cast_nft_vote_no_power_holding_account_ok() -> Result<(), Transpor let nft_cookie1 = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie, + None, + ) .await?; nft_voter_test.bench.advance_clock().await; @@ -1424,7 +1488,10 @@ async fn test_cast_nft_vote_no_power_holding_account_ok() -> Result<(), Transpor .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -1451,23 +1518,13 @@ async fn test_cast_nft_vote_power_holding_account_zero_balance_uses_to_collectio let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - Some(ConfigureCollectionArgs { + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { weight: 10, size: 20, - }), + }, ) .await?; @@ -1489,7 +1546,16 @@ async fn test_cast_nft_vote_power_holding_account_zero_balance_uses_to_collectio let nft_cookie1 = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie, + None, + ) .await?; nft_voter_test @@ -1503,7 +1569,10 @@ async fn test_cast_nft_vote_power_holding_account_zero_balance_uses_to_collectio .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, @@ -1533,23 +1602,13 @@ async fn test_cast_nft_vote_power_holding_account_nonzero_balance_uses_collectio let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - Some(ConfigureCollectionArgs { + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { weight: collection_weight, size: 20, - }), + }, ) .await?; @@ -1571,7 +1630,16 @@ async fn test_cast_nft_vote_power_holding_account_nonzero_balance_uses_collectio let nft_cookie1 = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &voter_cookie, + None, + ) .await?; nft_voter_test @@ -1590,7 +1658,10 @@ async fn test_cast_nft_vote_power_holding_account_nonzero_balance_uses_collectio .cast_nft_vote( ®istrar_cookie, &voter_weight_record_cookie, - &max_voter_weight_record_cookie, + ®istrar_cookie + .max_voter_weight_record_cookie + .as_ref() + .unwrap(), &proposal_cookie, &voter_cookie, &voter_token_owner_record_cookie, diff --git a/programs/nft-voter/tests/create_governance_token_holding_account.rs b/programs/nft-voter/tests/create_governance_token_holding_account.rs index 1d40d667..95c7ff59 100644 --- a/programs/nft-voter/tests/create_governance_token_holding_account.rs +++ b/programs/nft-voter/tests/create_governance_token_holding_account.rs @@ -1,5 +1,5 @@ use gpl_nft_voter::error::NftVoterError; -use program_test::nft_voter_test::NftVoterTest; +use program_test::nft_voter_test::{ConfigureCollectionArgs, NftVoterTest}; use program_test::tools::assert_nft_voter_err; use solana_program::program_option::COption; use solana_program_test::*; @@ -16,7 +16,13 @@ async fn test_create_governance_token_holding_account() -> Result<(), TransportE let realm_cookie = nft_voter_test.governance.with_realm().await?; let registrar_cookie = &mut nft_voter_test - .with_registrar_with_collection(&realm_cookie) + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { + weight: 10, + size: 20, + }, + ) .await?; let voter_cookie = nft_voter_test.bench.with_wallet().await; @@ -93,7 +99,13 @@ async fn test_create_governance_token_holding_account_nft_is_not_part_of_configu let realm_cookie = nft_voter_test.governance.with_realm().await?; let registrar_cookie = &mut nft_voter_test - .with_registrar_with_collection(&realm_cookie) + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { + weight: 10, + size: 20, + }, + ) .await?; let voter_cookie = nft_voter_test.bench.with_wallet().await; diff --git a/programs/nft-voter/tests/create_registrar.rs b/programs/nft-voter/tests/create_registrar.rs index 04fa50ee..bb430008 100644 --- a/programs/nft-voter/tests/create_registrar.rs +++ b/programs/nft-voter/tests/create_registrar.rs @@ -63,7 +63,7 @@ async fn test_create_registrar_with_realm_authority_must_sign_error() -> Result< let err = nft_voter_test .with_registrar_using_ix( &realm_cookie, - false, + None, |i| i.accounts[4].is_signer = false, // realm_authority Some(&[]), ) @@ -92,7 +92,7 @@ async fn test_create_registrar_with_invalid_spl_gov_program_id_error() -> Result let err = nft_voter_test .with_registrar_using_ix( &realm_cookie, - false, + None, |i| i.accounts[1].pubkey = governance_program_id, //governance_program_id None, ) @@ -117,7 +117,7 @@ async fn test_create_registrar_with_invalid_realm_error() -> Result<(), Transpor let err = nft_voter_test .with_registrar_using_ix( &realm_cookie, - false, + None, |i| i.accounts[2].pubkey = Pubkey::new_unique(), // realm None, ) @@ -146,7 +146,7 @@ async fn test_create_registrar_with_invalid_governing_token_mint_error( let err = nft_voter_test .with_registrar_using_ix( &realm_cookie, - false, + None, |i| i.accounts[3].pubkey = mint_cookie.address, // governing_token_mint None, ) diff --git a/programs/nft-voter/tests/deposit_governance_tokens.rs b/programs/nft-voter/tests/deposit_governance_tokens.rs index 3be4e9d1..6b0ed527 100644 --- a/programs/nft-voter/tests/deposit_governance_tokens.rs +++ b/programs/nft-voter/tests/deposit_governance_tokens.rs @@ -1,6 +1,6 @@ use anchor_spl::token::TokenAccount; use gpl_nft_voter::state::DelegatorTokenOwnerRecord; -use program_test::nft_voter_test::NftVoterTest; +use program_test::nft_voter_test::{ConfigureCollectionArgs, NftVoterTest}; use solana_program_test::*; use solana_sdk::transport::TransportError; @@ -14,13 +14,30 @@ async fn test_deposit_governance_tokens_first_deposit_creates_record() -> Result let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar_with_collection(&realm_cookie).await?; + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { + weight: 10, + size: 20, + }, + ) + .await?; let owner_cookie = nft_voter_test.bench.with_wallet_funded(100000000000).await; let nft_cookie = nft_voter_test .token_metadata - .with_nft_v2(®istrar_cookie.collection_cookies.as_ref().unwrap().first().unwrap(), &owner_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &owner_cookie, + None, + ) .await?; let governance_token_holding_account_cookie = nft_voter_test @@ -87,13 +104,30 @@ async fn test_deposit_governance_tokens_record_exists_doesnt_error() -> Result<( let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar_with_collection(&realm_cookie).await?; + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { + weight: 10, + size: 20, + }, + ) + .await?; let owner_cookie = nft_voter_test.bench.with_wallet_funded(100000000000).await; let nft_cookie = nft_voter_test .token_metadata - .with_nft_v2(®istrar_cookie.collection_cookies.as_ref().unwrap().first().unwrap(), &owner_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &owner_cookie, + None, + ) .await?; let governance_token_holding_account_cookie = nft_voter_test @@ -151,13 +185,30 @@ async fn test_deposit_governance_tokens_transfers_tokens_to_holding_account( let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar_with_collection(&realm_cookie).await?; + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { + weight: 10, + size: 20, + }, + ) + .await?; let owner_cookie = nft_voter_test.bench.with_wallet_funded(100000000000).await; let nft_cookie = nft_voter_test .token_metadata - .with_nft_v2(®istrar_cookie.collection_cookies.as_ref().unwrap().first().unwrap(), &owner_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &owner_cookie, + None, + ) .await?; let governance_token_holding_account_cookie = nft_voter_test @@ -226,13 +277,30 @@ async fn test_deposit_governance_tokens_multiple_deposits_holding_account_stores let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar_with_collection(&realm_cookie).await?; + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { + weight: 10, + size: 20, + }, + ) + .await?; let owner_cookie = nft_voter_test.bench.with_wallet_funded(100000000000).await; let nft_cookie = nft_voter_test .token_metadata - .with_nft_v2(®istrar_cookie.collection_cookies.as_ref().unwrap().first().unwrap(), &owner_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &owner_cookie, + None, + ) .await?; let governance_token_holding_account_cookie = nft_voter_test @@ -312,13 +380,30 @@ async fn test_deposit_governance_tokens_source_insufficient_balance_errors( let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar_with_collection(&realm_cookie).await?; + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { + weight: 10, + size: 20, + }, + ) + .await?; let owner_cookie = nft_voter_test.bench.with_wallet_funded(100000000000).await; let nft_cookie = nft_voter_test .token_metadata - .with_nft_v2(®istrar_cookie.collection_cookies.as_ref().unwrap().first().unwrap(), &owner_cookie, None) + .with_nft_v2( + ®istrar_cookie + .collection_cookies + .as_ref() + .unwrap() + .first() + .unwrap(), + &owner_cookie, + None, + ) .await?; let governance_token_holding_account_cookie = nft_voter_test @@ -351,7 +436,7 @@ async fn test_deposit_governance_tokens_source_insufficient_balance_errors( // Assert assert!(result.is_err()); - + Ok(()) } diff --git a/programs/nft-voter/tests/program_test/nft_voter_test.rs b/programs/nft-voter/tests/program_test/nft_voter_test.rs index c15ebfca..376c492a 100644 --- a/programs/nft-voter/tests/program_test/nft_voter_test.rs +++ b/programs/nft-voter/tests/program_test/nft_voter_test.rs @@ -143,7 +143,7 @@ impl NftVoterTest { &mut self, realm_cookie: &RealmCookie, ) -> Result { - self.with_registrar_using_ix(realm_cookie, false, NopOverride, None) + self.with_registrar_using_ix(realm_cookie, None, NopOverride, None) .await } @@ -151,8 +151,9 @@ impl NftVoterTest { pub async fn with_registrar_with_collection( &mut self, realm_cookie: &RealmCookie, + collection_config: ConfigureCollectionArgs, ) -> Result { - self.with_registrar_using_ix(realm_cookie, true, NopOverride, None) + self.with_registrar_using_ix(realm_cookie, Some(collection_config), NopOverride, None) .await } @@ -160,7 +161,7 @@ impl NftVoterTest { pub async fn with_registrar_using_ix( &mut self, realm_cookie: &RealmCookie, - include_collection: bool, + collection_config: Option, instruction_override: F, signers_override: Option<&[&Keypair]>, ) -> Result { @@ -215,7 +216,7 @@ impl NftVoterTest { collection_cookies: None, }; - if include_collection { + if let Some(collection_config) = collection_config { self.bench .process_transaction(&[create_registrar_ix], Some(signers)) .await?; @@ -228,7 +229,7 @@ impl NftVoterTest { ®istrar_cookie, &nft_collection_cookie, &max_voter_weight_record, - None, + Some(collection_config), ) .await?; From fa251103c956bb74bfe639c45f6bfc184e22ccf3 Mon Sep 17 00:00:00 2001 From: Austin Milt Date: Mon, 22 Aug 2022 07:55:32 -0700 Subject: [PATCH 14/17] documentation --- .../instructions/deposit_governance_tokens.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/programs/nft-voter/src/instructions/deposit_governance_tokens.rs b/programs/nft-voter/src/instructions/deposit_governance_tokens.rs index 034182b2..bb9acce6 100644 --- a/programs/nft-voter/src/instructions/deposit_governance_tokens.rs +++ b/programs/nft-voter/src/instructions/deposit_governance_tokens.rs @@ -10,6 +10,8 @@ use crate::{ /// Deposits tokens into the holding account for a given NFT to boost its voting power #[derive(Accounts)] pub struct DepositGovernanceTokens<'info> { + /// Record tracking what amount of the tokens in the holding + /// account belong to this delegator #[account( init_if_needed, seeds = [&DelegatorTokenOwnerRecord::SEED_PREFIX, @@ -24,6 +26,7 @@ pub struct DepositGovernanceTokens<'info> { )] pub token_owner_record: Account<'info, DelegatorTokenOwnerRecord>, + /// Associated fungible token account for the NFT being backed #[account( mut, seeds = [ &NFT_POWER_HOLDING_ACCOUNT_SEED_PREFIX, @@ -48,16 +51,27 @@ pub struct DepositGovernanceTokens<'info> { /// Either the realm community mint or the council mint. pub realm_governing_token_mint: Account<'info, Mint>, + /// Delegator, payer, and wallet that should receive the deposited tokens + /// upon withdrawal #[account(mut)] pub governing_token_owner: Signer<'info>, - //TODO add constraint that the nft is the one configured for a realm collection + /// Mint of the NFT being backed. + // We dont need to check that the NFT has a collection or that the collection + // is one configured for the realm, because a) this already happened when + // creating the holding account, and b) we have a constraint here that the + // holding account's seeds include this mint pub nft_mint: Account<'info, Mint>, + /// Associated token account owned by governing_token_owner from which + /// tokens are being withdrawn for the deposit #[account(mut, constraint = governing_token_source_account.owner == governing_token_owner.key())] pub governing_token_source_account: Account<'info, TokenAccount>, + /// System program required for creating the DelegatorTokenOwnerRecord pub system_program: Program<'info, System>, + + /// Token program required for withdrawing (mutating) the source and holding accounts pub token_program: Program<'info, Token>, } From 4998dde0f56d9a97d844ea651911dec1a187fb90 Mon Sep 17 00:00:00 2001 From: Austin Milt Date: Mon, 22 Aug 2022 08:10:46 -0700 Subject: [PATCH 15/17] fix update_voter_weight_record tests --- .../tests/program_test/nft_voter_test.rs | 7 +++ .../tests/update_voter_weight_record.rs | 52 ++++++------------- 2 files changed, 23 insertions(+), 36 deletions(-) diff --git a/programs/nft-voter/tests/program_test/nft_voter_test.rs b/programs/nft-voter/tests/program_test/nft_voter_test.rs index 376c492a..6623824e 100644 --- a/programs/nft-voter/tests/program_test/nft_voter_test.rs +++ b/programs/nft-voter/tests/program_test/nft_voter_test.rs @@ -397,6 +397,13 @@ impl NftVoterTest { for nft_cookie in nft_cookies { account_metas.push(AccountMeta::new_readonly(nft_cookie.address, false)); account_metas.push(AccountMeta::new_readonly(nft_cookie.metadata, false)); + + let nft_power_holding_account_key = find_nft_power_holding_account_address( + ®istrar_cookie.account.realm, + ®istrar_cookie.account.governing_token_mint, + &nft_cookie.mint_cookie.address, + ); + account_metas.push(AccountMeta::new(nft_power_holding_account_key, false)); } let instructions = vec![Instruction { diff --git a/programs/nft-voter/tests/update_voter_weight_record.rs b/programs/nft-voter/tests/update_voter_weight_record.rs index d29809a2..f792ec7f 100644 --- a/programs/nft-voter/tests/update_voter_weight_record.rs +++ b/programs/nft-voter/tests/update_voter_weight_record.rs @@ -16,25 +16,15 @@ async fn test_update_voter_weight_record() -> Result<(), TransportError> { let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - let _collection_config_cookie = nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - Some(ConfigureCollectionArgs { - weight: 10, - size: 20, - }), - ) - .await?; + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { + weight: 10, + size: 20, + }, + ) + .await?; let voter_cookie = nft_voter_test.bench.with_wallet().await; @@ -44,7 +34,7 @@ async fn test_update_voter_weight_record() -> Result<(), TransportError> { let nft1_cookie = nft_voter_test .token_metadata - .with_nft_v2(&nft_collection_cookie, &voter_cookie, None) + .with_nft_v2(®istrar_cookie.collection_cookies.as_ref().unwrap().first().unwrap(), &voter_cookie, None) .await?; nft_voter_test.bench.advance_clock().await; @@ -518,23 +508,13 @@ async fn test_update_voter_weight_record_with_no_nft_error() -> Result<(), Trans let realm_cookie = nft_voter_test.governance.with_realm().await?; - let registrar_cookie = nft_voter_test.with_registrar(&realm_cookie).await?; - - let nft_collection_cookie = nft_voter_test.token_metadata.with_nft_collection().await?; - - let max_voter_weight_record_cookie = nft_voter_test - .with_max_voter_weight_record(®istrar_cookie) - .await?; - - let _collection_config_cookie = nft_voter_test - .with_collection( - ®istrar_cookie, - &nft_collection_cookie, - &max_voter_weight_record_cookie, - Some(ConfigureCollectionArgs { + let registrar_cookie = nft_voter_test + .with_registrar_with_collection( + &realm_cookie, + ConfigureCollectionArgs { weight: 10, size: 20, - }), + }, ) .await?; @@ -547,7 +527,7 @@ async fn test_update_voter_weight_record_with_no_nft_error() -> Result<(), Trans let nft1_cookie = nft_voter_test .token_metadata .with_nft_v2( - &nft_collection_cookie, + ®istrar_cookie.collection_cookies.as_ref().unwrap().first().unwrap(), &voter_cookie, Some(CreateNftArgs { amount: 0, From 8ba31ff72fce20cc7888990ab5a8e53dac129fce Mon Sep 17 00:00:00 2001 From: Austin Milt Date: Mon, 22 Aug 2022 08:21:41 -0700 Subject: [PATCH 16/17] documentation --- programs/nft-voter/tests/program_test/nft_voter_test.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/programs/nft-voter/tests/program_test/nft_voter_test.rs b/programs/nft-voter/tests/program_test/nft_voter_test.rs index 6623824e..d79dd797 100644 --- a/programs/nft-voter/tests/program_test/nft_voter_test.rs +++ b/programs/nft-voter/tests/program_test/nft_voter_test.rs @@ -40,8 +40,10 @@ pub struct RegistrarCookie { pub realm_authority: Keypair, pub max_collections: u8, - // only populated with using with_registrar_with_collection + /// only populated when using with_registrar_with_collection pub collection_cookies: Option>, + + /// only populated when using with_registrar_with_collection pub max_voter_weight_record_cookie: Option, } From 074634c8d691c71e53bcc2a69bf2ecf89bcf12a7 Mon Sep 17 00:00:00 2001 From: Austin Milt Date: Mon, 22 Aug 2022 08:26:56 -0700 Subject: [PATCH 17/17] fix with_registrar --- programs/nft-voter/tests/program_test/nft_voter_test.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/programs/nft-voter/tests/program_test/nft_voter_test.rs b/programs/nft-voter/tests/program_test/nft_voter_test.rs index d79dd797..1779ae47 100644 --- a/programs/nft-voter/tests/program_test/nft_voter_test.rs +++ b/programs/nft-voter/tests/program_test/nft_voter_test.rs @@ -201,6 +201,10 @@ impl NftVoterTest { let default_signers = &[&realm_cookie.realm_authority]; let signers = signers_override.unwrap_or(default_signers); + self.bench + .process_transaction(&[create_registrar_ix], Some(signers)) + .await?; + let account = Registrar { governance_program_id: self.governance.program_id, realm: realm_cookie.address, @@ -219,9 +223,6 @@ impl NftVoterTest { }; if let Some(collection_config) = collection_config { - self.bench - .process_transaction(&[create_registrar_ix], Some(signers)) - .await?; let nft_collection_cookie = self.token_metadata.with_nft_collection().await?; let max_voter_weight_record =