quarry_merge_mine/
lib.rs

1//! Holds tokens to allow one depositor to mine multiple quarries at the same time.
2#![deny(rustdoc::all)]
3#![allow(rustdoc::missing_doc_code_examples)]
4#![allow(deprecated)]
5
6#[macro_use]
7mod macros;
8
9mod account_validators;
10mod processor;
11use processor::*;
12
13pub(crate) mod account_conversions;
14pub(crate) mod mm_cpi;
15
16pub mod events;
17pub mod state;
18
19use anchor_lang::prelude::*;
20use anchor_spl::token::{Mint, Token, TokenAccount};
21use vipers::prelude::*;
22
23pub use state::*;
24
25#[cfg(not(feature = "no-entrypoint"))]
26solana_security_txt::security_txt! {
27    name: "Quarry Merge Mine",
28    project_url: "https://quarry.so",
29    contacts: "email:team@quarry.so",
30    policy: "https://github.com/QuarryProtocol/quarry/blob/master/SECURITY.md",
31
32    source_code: "https://github.com/QuarryProtocol/quarry",
33    auditors: "Quantstamp"
34}
35
36declare_id!("QMMD16kjauP5knBwxNUJRZ1Z5o3deBuFrqVjBVmmqto");
37
38#[deny(clippy::integer_arithmetic, clippy::float_arithmetic)]
39#[program]
40/// Quarry merge mining program.
41pub mod quarry_merge_mine {
42    use super::*;
43
44    /// Creates a new [MergePool].
45    /// Anyone can call this.
46    #[deprecated(since = "5.0.0", note = "Use `new_pool_v2` instead.")]
47    #[access_control(ctx.accounts.validate())]
48    pub fn new_pool(ctx: Context<NewPool>, _bump: u8, _mint_bump: u8) -> Result<()> {
49        processor::init::new_pool(ctx)
50    }
51
52    /// Creates a new [MergePool].
53    /// Anyone can call this.
54    ///
55    /// The V2 variant removes the need for supplying the bump.
56    #[access_control(ctx.accounts.validate())]
57    pub fn new_pool_v2(ctx: Context<NewPool>) -> Result<()> {
58        processor::init::new_pool(ctx)
59    }
60
61    /// Creates a new [MergeMiner].
62    /// Anyone can call this.
63    #[deprecated(since = "5.0.0", note = "Use `init_merge_miner_v2` instead.")]
64    #[access_control(ctx.accounts.validate())]
65    pub fn init_merge_miner(ctx: Context<InitMergeMiner>, _bump: u8) -> Result<()> {
66        processor::init::init_merge_miner(ctx)
67    }
68
69    /// Creates a new [MergeMiner].
70    /// Anyone can call this.
71    ///
72    /// The V2 variant removes the need for supplying the bump.
73    #[access_control(ctx.accounts.validate())]
74    pub fn init_merge_miner_v2(ctx: Context<InitMergeMiner>) -> Result<()> {
75        processor::init::init_merge_miner(ctx)
76    }
77
78    /// Initializes a [quarry_mine::Miner] owned by the [MergeMiner].
79    #[deprecated(since = "5.0.0", note = "Use `init_miner_v2` instead.")]
80    #[access_control(ctx.accounts.validate())]
81    pub fn init_miner(ctx: Context<InitMiner>, _bump: u8) -> Result<()> {
82        processor::init::init_miner(ctx)
83    }
84
85    /// Initializes a [quarry_mine::Miner] owned by the [MergeMiner].
86    #[access_control(ctx.accounts.validate())]
87    pub fn init_miner_v2(ctx: Context<InitMiner>) -> Result<()> {
88        processor::init::init_miner(ctx)
89    }
90
91    // --------------------------------
92    // Deposit
93    // --------------------------------
94
95    /// Deposits tokens into the [MergeMiner].
96    /// Before calling this, the owner should call the [anchor_spl::token::transfer] instruction
97    /// to transfer to the [MergeMiner]'s primary token ATA.
98    #[access_control(ctx.accounts.validate())]
99    pub fn stake_primary_miner(ctx: Context<QuarryStakePrimary>) -> Result<()> {
100        processor::deposit::stake_primary_miner(ctx)
101    }
102
103    /// Stakes all possible replica tokens into a [quarry_mine::Quarry].
104    /// Before calling this, the owner should call [stake_primary_miner] with the tokens
105    /// they would like to stake.
106    #[access_control(ctx.accounts.validate())]
107    pub fn stake_replica_miner(ctx: Context<QuarryStakeReplica>) -> Result<()> {
108        processor::deposit::stake_replica_miner(ctx)
109    }
110
111    // --------------------------------
112    // Withdraw
113    // --------------------------------
114
115    /// Withdraws tokens from the [MergeMiner].
116    #[access_control(ctx.accounts.validate())]
117    pub fn unstake_primary_miner(ctx: Context<QuarryStakePrimary>, amount: u64) -> Result<()> {
118        processor::withdraw::unstake_primary_miner(ctx, amount)
119    }
120
121    /// Unstakes all of a [MergeMiner]'s replica tokens for a [quarry_mine::Miner].
122    #[access_control(ctx.accounts.validate())]
123    pub fn unstake_all_replica_miner(ctx: Context<QuarryStakeReplica>) -> Result<()> {
124        processor::withdraw::unstake_all_replica_miner(ctx)
125    }
126
127    /// Withdraws tokens from the [MergeMiner].
128    #[access_control(ctx.accounts.validate())]
129    pub fn withdraw_tokens(ctx: Context<WithdrawTokens>) -> Result<()> {
130        processor::withdraw::withdraw_tokens(ctx)
131    }
132
133    /// Rescues stuck tokens in miners owned by a [MergeMiner].
134    #[access_control(ctx.accounts.validate())]
135    pub fn rescue_tokens(ctx: Context<RescueTokens>) -> Result<()> {
136        processor::rescue_tokens::handler(ctx)
137    }
138
139    // --------------------------------
140    // Claim
141    // --------------------------------
142
143    /// Claims [quarry_mine] rewards on behalf of the [MergeMiner].
144    #[access_control(ctx.accounts.validate())]
145    pub fn claim_rewards(ctx: Context<ClaimRewards>) -> Result<()> {
146        processor::claim::claim_rewards(ctx)
147    }
148}
149
150// --------------------------------
151// Instruction account structs
152// --------------------------------
153
154/// [quarry_merge_mine::new_pool] accounts
155#[derive(Accounts)]
156pub struct NewPool<'info> {
157    /// [MergePool].
158    #[account(
159        init,
160        seeds = [
161          b"MergePool".as_ref(),
162          primary_mint.key().to_bytes().as_ref()
163        ],
164        bump,
165        payer = payer,
166        space = 8 + MergePool::LEN
167    )]
168    pub pool: Account<'info, MergePool>,
169
170    /// [Mint] of the primary (underlying) token.
171    pub primary_mint: Account<'info, Mint>,
172
173    /// [Mint] of the replica token.
174    #[account(
175        init,
176        seeds = [
177            b"ReplicaMint".as_ref(),
178            pool.key().to_bytes().as_ref()
179        ],
180        mint::decimals = primary_mint.decimals,
181        mint::authority = pool,
182        bump,
183        payer = payer
184    )]
185    pub replica_mint: Account<'info, Mint>,
186
187    /// Payer of the created [MergePool].
188    #[account(mut)]
189    pub payer: Signer<'info>,
190
191    /// [Token] program.
192    pub token_program: Program<'info, Token>,
193
194    /// [System] program.
195    pub system_program: Program<'info, System>,
196
197    /// [Rent] sysvar.
198    pub rent: Sysvar<'info, Rent>,
199}
200
201/// [quarry_merge_mine::init_merge_miner] accounts
202#[derive(Accounts)]
203pub struct InitMergeMiner<'info> {
204    /// [MergePool] of the underlying LP token.
205    pub pool: Account<'info, MergePool>,
206
207    /// Owner of the [MergeMiner].
208    /// CHECK: Ok
209    pub owner: UncheckedAccount<'info>,
210
211    /// [MergeMiner].
212    #[account(
213        init,
214        seeds = [
215          b"MergeMiner".as_ref(),
216          pool.key().to_bytes().as_ref(),
217          owner.key().to_bytes().as_ref()
218        ],
219        bump,
220        payer = payer,
221        space = 8 + MergeMiner::LEN
222    )]
223    pub mm: Account<'info, MergeMiner>,
224
225    /// Payer of the created [MergeMiner].
226    #[account(mut)]
227    pub payer: Signer<'info>,
228
229    /// System program.
230    pub system_program: Program<'info, System>,
231}
232
233/// [quarry_merge_mine::init_miner] accounts
234#[derive(Accounts)]
235pub struct InitMiner<'info> {
236    /// The [MergePool].
237    pub pool: Account<'info, MergePool>,
238
239    /// The [MergeMiner], aka the authority of the [quarry_mine::Miner].
240    pub mm: Account<'info, MergeMiner>,
241
242    /// [quarry_mine::Miner] to be created.
243    #[account(mut)]
244    pub miner: SystemAccount<'info>,
245
246    /// [quarry_mine::Quarry] to create a [quarry_mine::Miner] for.
247    #[account(mut)]
248    pub quarry: Box<Account<'info, quarry_mine::Quarry>>,
249
250    /// [quarry_mine::Rewarder].
251    pub rewarder: Box<Account<'info, quarry_mine::Rewarder>>,
252
253    /// [Mint] of the Quarry token.
254    pub token_mint: Box<Account<'info, Mint>>,
255
256    /// [TokenAccount] holding the token [Mint].
257    pub miner_vault: Account<'info, TokenAccount>,
258
259    /// Payer of [quarry_mine::Miner] creation.
260    #[account(mut)]
261    pub payer: Signer<'info>,
262
263    /// The program at [quarry_mine::ID].
264    pub mine_program: Program<'info, quarry_mine::program::QuarryMine>,
265
266    /// System program.
267    pub system_program: Program<'info, System>,
268
269    /// SPL Token program.
270    pub token_program: Program<'info, Token>,
271}
272
273/// [quarry_merge_mine::withdraw_tokens] accounts
274#[derive(Accounts)]
275pub struct WithdrawTokens<'info> {
276    /// Owner of the [MergeMiner].
277    pub owner: Signer<'info>,
278    /// The [MergePool] to withdraw from.
279    pub pool: Account<'info, MergePool>,
280    /// The [MergeMiner] to withdraw from.
281    #[account(mut)]
282    pub mm: Account<'info, MergeMiner>,
283
284    /// The [Mint] being withdrawn from the [MergeMiner].
285    pub withdraw_mint: Account<'info, Mint>,
286    /// A [TokenAccount] owned by the [MergeMiner] to withdraw from.
287    /// Must be the [MergePool::primary_mint] or the [MergePool::replica_mint].
288    #[account(mut)]
289    pub mm_token_account: Account<'info, TokenAccount>,
290    /// Account to send tokens to.
291    #[account(mut)]
292    pub token_destination: Account<'info, TokenAccount>,
293
294    /// The token program
295    pub token_program: Program<'info, Token>,
296}
297
298/// [quarry_merge_mine::claim_rewards] accounts
299#[derive(Accounts)]
300pub struct ClaimRewards<'info> {
301    /// Mint wrapper.
302    #[account(mut)]
303    pub mint_wrapper: Box<Account<'info, quarry_mint_wrapper::MintWrapper>>,
304    /// Mint wrapper program.
305    pub mint_wrapper_program: Program<'info, quarry_mint_wrapper::program::QuarryMintWrapper>,
306    /// [quarry_mint_wrapper::Minter].
307    #[account(mut)]
308    pub minter: Box<Account<'info, quarry_mint_wrapper::Minter>>,
309
310    /// [Mint] of the [quarry_mine] rewards token.
311    #[account(mut)]
312    pub rewards_token_mint: Box<Account<'info, Mint>>,
313
314    /// Account to claim rewards for.
315    #[account(mut)]
316    pub rewards_token_account: Box<Account<'info, TokenAccount>>,
317
318    /// Account to send claim fees to.
319    #[account(mut)]
320    pub claim_fee_token_account: Box<Account<'info, TokenAccount>>,
321
322    /// Arbitrary account holding the [Mint] of the quarry staked token.
323    /// Passed to [quarry_mine] but unused.
324    #[account(mut)]
325    pub stake_token_account: Box<Account<'info, TokenAccount>>,
326
327    /// User's stake.
328    pub stake: QuarryStake<'info>,
329}
330
331/// [quarry_merge_mine::stake_primary_miner] accounts
332#[derive(Accounts)]
333pub struct QuarryStakePrimary<'info> {
334    /// The [MergeMiner::owner].
335    #[account(constraint = mm_owner.key() == stake.mm.owner)]
336    pub mm_owner: Signer<'info>,
337
338    /// The [TokenAccount] holding the [MergeMiner]'s primary tokens.
339    #[account(mut)]
340    pub mm_primary_token_account: Account<'info, TokenAccount>,
341
342    /// Staking accounts for the [quarry_mine::Quarry].
343    pub stake: QuarryStake<'info>,
344}
345
346/// [quarry_merge_mine::stake_replica_miner] accounts
347#[derive(Accounts)]
348pub struct QuarryStakeReplica<'info> {
349    /// The [MergeMiner::owner].
350    #[account(constraint = mm_owner.key() == stake.mm.owner)]
351    pub mm_owner: Signer<'info>,
352
353    /// [Mint] of a token that can be staked into a farming program.
354    /// This token should not be distributed to users, as it can depeg and can cause minters to lose their funds.
355    /// The [MergePool] must be the `mint_authority` and the `freeze_authority`.
356    #[account(mut)]
357    pub replica_mint: Account<'info, Mint>,
358
359    /// The [TokenAccount] holding the [MergeMiner]'s minted pool tokens.
360    #[account(mut)]
361    pub replica_mint_token_account: Account<'info, TokenAccount>,
362
363    /// Staking accounts for the [quarry_mine::Quarry].
364    pub stake: QuarryStake<'info>,
365}
366
367// --------------------------------
368// Context Structs
369// --------------------------------
370
371/// Staking accounts for a [quarry_mine::Quarry].
372#[derive(Accounts)]
373pub struct QuarryStake<'info> {
374    /// The [MergePool].
375    #[account(mut)]
376    pub pool: Account<'info, MergePool>,
377
378    /// The [MergeMiner] (also the [quarry_mine::Miner] authority).
379    #[account(mut, has_one = pool)]
380    pub mm: Account<'info, MergeMiner>,
381
382    /// The [quarry_mine::Rewarder] to stake into.
383    pub rewarder: Box<Account<'info, quarry_mine::Rewarder>>,
384
385    /// The [quarry_mine::Quarry] to claim from.
386    #[account(mut, has_one = rewarder)]
387    pub quarry: Box<Account<'info, quarry_mine::Quarry>>,
388
389    /// The [quarry_mine::Miner].
390    #[account(
391        mut,
392        has_one = quarry,
393        constraint = miner.authority == mm.key()
394    )]
395    pub miner: Box<Account<'info, quarry_mine::Miner>>,
396
397    /// The [TokenAccount] of the [quarry_mine::Miner] that holds the staked tokens.
398    #[account(mut, constraint = miner_vault.key() == miner.token_vault_key)]
399    pub miner_vault: Account<'info, TokenAccount>,
400
401    /// [anchor_spl::token] program.
402    pub token_program: Program<'info, Token>,
403
404    /// [quarry_mine] program.
405    pub mine_program: Program<'info, quarry_mine::program::QuarryMine>,
406}
407
408/// Error Codes
409#[error_code]
410pub enum ErrorCode {
411    #[msg("Unauthorized.")]
412    Unauthorized,
413    #[msg("Insufficient balance.")]
414    InsufficientBalance,
415    #[msg("Invalid miner for the given quarry.")]
416    InvalidMiner,
417    #[msg("Cannot withdraw a replica mint.")]
418    CannotWithdrawReplicaMint,
419    #[msg("User must first withdraw from all replica quarries.")]
420    OutstandingReplicaTokens,
421    #[msg("The replica mint must have the same number of decimals as the primary mint.")]
422    ReplicaDecimalsMismatch,
423    #[msg("The replica mint must have zero supply.")]
424    ReplicaNonZeroSupply,
425}