testsvm_quarry/
test_rewarder.rs

1//! # Rewarder Testing Utilities
2//!
3//! Test helpers for Quarry rewarder management.
4//!
5//! This module provides the `TestRewarder` struct for managing reward distribution
6//! systems in the Quarry protocol. Rewarders control the minting and distribution
7//! of reward tokens to miners based on their staked amounts and the configured
8//! reward rates.
9//!
10//! ## Features
11//!
12//! - **Rewarder Creation**: Initialize new rewarders with mint wrappers
13//! - **Quarry Management**: Create and configure quarries under the rewarder
14//! - **Reward Configuration**: Set annual reward rates and distribution parameters
15//! - **Authority Control**: Manage rewarder authority and pause states
16
17use crate::{TestMintWrapper, quarry_mine, quarry_mint_wrapper};
18use testsvm::prelude::*;
19
20/// Test rewarder with labeled accounts
21pub struct TestRewarder {
22    pub label: String,
23    pub rewarder: AccountRef<quarry_mine::accounts::Rewarder>,
24    pub mint_wrapper: TestMintWrapper,
25    pub minter: AccountRef<quarry_mint_wrapper::accounts::Minter>,
26    pub claim_fee_token_account: AccountRef<anchor_spl::token::TokenAccount>,
27    pub authority: Pubkey,
28
29    // Keep the base keypair for signing
30    pub rewarder_base: Keypair,
31}
32
33impl TestRewarder {
34    /// Create a new rewarder with the specified label and authority
35    pub fn new_rewarder(env: &mut TestSVM, label: &str, authority: &Keypair) -> Result<Self> {
36        // Create the mint wrapper using TestMintWrapper
37        let mint_wrapper = TestMintWrapper::new(env, &format!("rewarder[{label}]"), authority)?;
38
39        let rewarder_base = env.new_wallet(&format!("rewarder[{label}].rewarder_base"))?;
40
41        // Calculate rewarder PDA
42        let rewarder = env.get_pda(
43            &format!("rewarder[{label}].rewarder"),
44            &[b"Rewarder", rewarder_base.pubkey().as_ref()],
45            quarry_mine::ID,
46        )?;
47
48        // Create claim fee token account for the rewarder
49        let (create_ata_ix, claim_fee_token_account) = env.create_ata_ix(
50            &format!("rewarder[{label}].claim_fee_tokens"),
51            &rewarder.key,
52            &mint_wrapper.reward_token_mint.key,
53        )?;
54
55        env.execute_ixs(&[create_ata_ix])?;
56
57        // Create rewarder
58        let create_rewarder_ix = anchor_instruction(
59            quarry_mine::ID,
60            quarry_mine::client::accounts::NewRewarderV2 {
61                base: rewarder_base.pubkey(),
62                rewarder: rewarder.key,
63                initial_authority: authority.pubkey(),
64                payer: env.default_fee_payer(),
65                system_program: solana_sdk::system_program::ID,
66                mint_wrapper: mint_wrapper.mint_wrapper.key,
67                rewards_token_mint: mint_wrapper.reward_token_mint.key,
68                claim_fee_token_account: claim_fee_token_account.key,
69            },
70            quarry_mine::client::args::NewRewarderV2 {},
71        );
72
73        env.execute_ixs_with_signers(&[create_rewarder_ix], &[&rewarder_base])?;
74
75        // Create minter for the rewarder with max allowance
76        let minter = mint_wrapper.create_minter(env, &rewarder.key, u64::MAX, authority)?;
77
78        Ok(TestRewarder {
79            label: label.to_string(),
80            rewarder,
81            mint_wrapper,
82            minter,
83            authority: authority.pubkey(),
84            rewarder_base,
85            claim_fee_token_account,
86        })
87    }
88
89    /// Create a replica quarry for this rewarder
90    pub fn create_quarry(
91        &self,
92        env: &mut TestSVM,
93        quarry_name: &str,
94        staked_token_mint: &Pubkey,
95        authority: &Keypair,
96    ) -> Result<crate::TestQuarry> {
97        let quarry_label = format!("rewarder[{}].quarry[{}]", self.label, quarry_name);
98
99        // Calculate quarry PDA
100        let quarry = env.get_pda(
101            &format!("{quarry_label}.quarry"),
102            &[
103                b"Quarry",
104                self.rewarder.key.as_ref(),
105                staked_token_mint.as_ref(),
106            ],
107            quarry_mine::ID,
108        )?;
109
110        // Create quarry
111        use quarry_mine::client::accounts::TransferAuthority;
112
113        let create_quarry_ix = anchor_instruction(
114            quarry_mine::ID,
115            quarry_mine::client::accounts::CreateQuarryV2 {
116                quarry: quarry.key,
117                auth: TransferAuthority {
118                    authority: authority.pubkey(),
119                    rewarder: self.rewarder.key,
120                },
121                token_mint: *staked_token_mint,
122                payer: env.default_fee_payer(),
123                system_program: solana_sdk::system_program::ID,
124            },
125            quarry_mine::client::args::CreateQuarryV2 {},
126        );
127
128        let set_shares_ix = anchor_instruction(
129            quarry_mine::ID,
130            quarry_mine::client::accounts::SetRewardsShare {
131                auth: TransferAuthority {
132                    authority: authority.pubkey(),
133                    rewarder: self.rewarder.key,
134                },
135                quarry: quarry.key,
136            },
137            quarry_mine::client::args::SetRewardsShare { new_share: 1 },
138        );
139
140        env.execute_ixs_with_signers(&[create_quarry_ix, set_shares_ix], &[authority])?;
141
142        Ok(crate::TestQuarry {
143            label: quarry_label,
144            quarry,
145            rewarder: self.rewarder.key,
146            staked_token_mint: AccountRef::new(*staked_token_mint),
147        })
148    }
149
150    /// Create a primary quarry for this rewarder
151    pub fn create_primary_quarry(
152        &self,
153        env: &mut TestSVM,
154        quarry_name: &str,
155        staked_token_mint: &Pubkey,
156        authority: &Keypair,
157    ) -> Result<crate::TestQuarry> {
158        // Primary quarries have the same logic as replica quarries
159        // The distinction is mainly semantic/organizational
160        self.create_quarry(env, quarry_name, staked_token_mint, authority)
161    }
162
163    /// Create a replica quarry using the replica mint from a TestMergePool
164    pub fn create_replica_quarry(
165        &self,
166        env: &mut TestSVM,
167        quarry_name: &str,
168        merge_pool: &crate::TestMergePool,
169        authority: &Keypair,
170    ) -> Result<crate::TestQuarry> {
171        // Use the replica mint from the merge pool
172        self.create_quarry(env, quarry_name, &merge_pool.replica_mint.key, authority)
173    }
174
175    /// Fetch the Rewarder account from chain
176    pub fn fetch_rewarder(&self, env: &TestSVM) -> Result<quarry_mine::accounts::Rewarder> {
177        self.rewarder.load(env)
178    }
179
180    /// Fetch the MintWrapper account from chain
181    pub fn fetch_mint_wrapper(
182        &self,
183        env: &TestSVM,
184    ) -> Result<quarry_mint_wrapper::accounts::MintWrapper> {
185        self.mint_wrapper.mint_wrapper.load(env)
186    }
187
188    /// Helper to set annual rewards rate for a quarry
189    pub fn set_annual_rewards_rate(
190        &self,
191        env: &mut TestSVM,
192        annual_rate: u64,
193        authority: &Keypair,
194    ) -> TXResult {
195        let set_rewards_ix = anchor_instruction(
196            quarry_mine::ID,
197            quarry_mine::client::accounts::SetAnnualRewards {
198                auth: quarry_mine::client::accounts::TransferAuthority {
199                    authority: authority.pubkey(),
200                    rewarder: self.rewarder.key,
201                },
202            },
203            quarry_mine::client::args::SetAnnualRewards {
204                new_rate: annual_rate,
205            },
206        );
207        env.execute_ixs_with_signers(&[set_rewards_ix], &[authority])
208    }
209
210    /// Create a new minter to allow minting
211    pub fn new_minter(
212        &self,
213        env: &mut TestSVM,
214        label: &str,
215        authority: &Keypair,
216    ) -> Result<TXResult> {
217        // Calculate the minter PDA but don't add to address book yet
218        let (minter, minter_bump) = Pubkey::find_program_address(
219            &[
220                b"MintWrapperMinter",
221                self.mint_wrapper.mint_wrapper.key.as_ref(),
222                self.rewarder.key.as_ref(),
223            ],
224            &quarry_mint_wrapper::ID,
225        );
226
227        let new_minter_ix = anchor_instruction(
228            quarry_mint_wrapper::ID,
229            quarry_mint_wrapper::client::accounts::NewMinter {
230                new_minter_auth: quarry_mint_wrapper::client::accounts::NewMinterAuth {
231                    mint_wrapper: self.mint_wrapper.mint_wrapper.key,
232                    admin: authority.pubkey(),
233                },
234                new_minter_authority: self.rewarder.key,
235                minter,
236                payer: env.default_fee_payer(),
237                system_program: solana_sdk::system_program::ID,
238            },
239            quarry_mint_wrapper::client::args::NewMinter { bump: minter_bump },
240        );
241
242        let result = env.execute_ixs_with_signers(&[new_minter_ix], &[authority]);
243
244        // Add the minter to address book after creation
245        if result.is_ok() {
246            env.address_book.add_pda(
247                minter,
248                format!("rewarder[{}].minter[{}]", self.label, label),
249                vec![
250                    "MintWrapperMinter".to_string(),
251                    self.mint_wrapper.mint_wrapper.key.to_string(),
252                    self.rewarder.key.to_string(),
253                ],
254                quarry_mint_wrapper::ID,
255                minter_bump,
256            )?;
257        }
258
259        Ok(result)
260    }
261}