1#![deny(rustdoc::all)]
11#![allow(rustdoc::missing_doc_code_examples)]
12#![allow(deprecated)]
13
14#[macro_use]
15mod macros;
16mod state;
17
18use anchor_lang::prelude::*;
19use anchor_spl::token::Token;
20use anchor_spl::token::{self, Mint, TokenAccount, Transfer};
21use payroll::Payroll;
22pub use state::*;
23use std::cmp;
24use vipers::prelude::*;
25
26pub mod account_validators;
27pub mod addresses;
28pub mod payroll;
29pub mod quarry;
30pub mod rewarder;
31
32mod instructions;
33pub use instructions::*;
34
35use crate::quarry::StakeAction;
36
37declare_id!("9nj87mvFWstkzn56qYWDmBadr4v5fZRhkhkqgSKNRVQ5");
39
40pub const MAX_ANNUAL_REWARDS_RATE: u64 = u64::MAX >> 3;
42
43pub const DEFAULT_CLAIM_FEE_MILLIBPS: u64 = 1_000;
46
47pub const MAX_BPS: u64 = 10_000;
49
50#[cfg(not(feature = "no-entrypoint"))]
51solana_security_txt::security_txt! {
52 name: "Quarry Mine",
53 project_url: "https://quarry.so",
54 contacts: "email:team@quarry.so",
55 policy: "https://github.com/QuarryProtocol/quarry/blob/master/SECURITY.md",
56
57 source_code: "https://github.com/QuarryProtocol/quarry",
58 auditors: "Quantstamp"
59}
60
61#[program]
63pub mod quarry_mine {
64 use super::*;
65
66 #[deprecated(since = "5.0.0", note = "Use `new_rewarder_v2` instead.")]
72 #[access_control(ctx.accounts.validate())]
73 pub fn new_rewarder(ctx: Context<NewRewarder>, _bump: u8) -> Result<()> {
74 instructions::new_rewarder::handler(ctx)
75 }
76
77 #[access_control(ctx.accounts.validate())]
81 pub fn new_rewarder_v2(ctx: Context<NewRewarderV2>) -> Result<()> {
82 instructions::new_rewarder_v2::handler(ctx)
83 }
84
85 #[access_control(ctx.accounts.validate())]
87 pub fn set_pause_authority(ctx: Context<SetPauseAuthority>) -> Result<()> {
88 let rewarder = &mut ctx.accounts.auth.rewarder;
89 rewarder.pause_authority = ctx.accounts.new_pause_authority.key();
90 Ok(())
91 }
92
93 #[access_control(ctx.accounts.validate())]
95 pub fn pause(ctx: Context<MutableRewarderWithPauseAuthority>) -> Result<()> {
96 let rewarder = &mut ctx.accounts.rewarder;
97 rewarder.is_paused = true;
98 Ok(())
99 }
100
101 #[access_control(ctx.accounts.validate())]
103 pub fn unpause(ctx: Context<MutableRewarderWithPauseAuthority>) -> Result<()> {
104 let rewarder = &mut ctx.accounts.rewarder;
105 rewarder.is_paused = false;
106 Ok(())
107 }
108
109 #[access_control(ctx.accounts.validate())]
111 pub fn transfer_authority(
112 ctx: Context<TransferAuthority>,
113 new_authority: Pubkey,
114 ) -> Result<()> {
115 let rewarder = &mut ctx.accounts.rewarder;
116 rewarder.pending_authority = new_authority;
117 Ok(())
118 }
119
120 #[access_control(ctx.accounts.validate())]
122 pub fn accept_authority(ctx: Context<AcceptAuthority>) -> Result<()> {
123 let rewarder = &mut ctx.accounts.rewarder;
124 let next_authority = rewarder.pending_authority;
125 rewarder.authority = next_authority;
126 rewarder.pending_authority = Pubkey::default();
127 Ok(())
128 }
129
130 #[access_control(ctx.accounts.validate())]
132 pub fn set_annual_rewards(ctx: Context<SetAnnualRewards>, new_rate: u64) -> Result<()> {
133 invariant!(
134 new_rate <= MAX_ANNUAL_REWARDS_RATE,
135 MaxAnnualRewardsRateExceeded
136 );
137 let rewarder = &mut ctx.accounts.auth.rewarder;
138 let previous_rate = rewarder.annual_rewards_rate;
139 rewarder.annual_rewards_rate = new_rate;
140
141 let current_ts = Clock::get()?.unix_timestamp;
142 emit!(RewarderAnnualRewardsUpdateEvent {
143 previous_rate,
144 new_rate,
145 timestamp: current_ts,
146 });
147
148 Ok(())
149 }
150
151 #[deprecated(since = "5.0.0", note = "Use `create_quarry_v2` instead.")]
158 #[access_control(ctx.accounts.validate())]
159 pub fn create_quarry(ctx: Context<CreateQuarry>, _bump: u8) -> Result<()> {
160 instructions::create_quarry::handler(ctx)
161 }
162
163 #[access_control(ctx.accounts.validate())]
168 pub fn create_quarry_v2(ctx: Context<CreateQuarryV2>) -> Result<()> {
169 instructions::create_quarry_v2::handler(ctx)
170 }
171
172 #[access_control(ctx.accounts.validate())]
174 pub fn set_rewards_share(ctx: Context<SetRewardsShare>, new_share: u64) -> Result<()> {
175 let rewarder = &mut ctx.accounts.auth.rewarder;
176 let quarry = &mut ctx.accounts.quarry;
177 rewarder.total_rewards_shares = unwrap_int!(rewarder
178 .total_rewards_shares
179 .checked_add(new_share)
180 .and_then(|v| v.checked_sub(quarry.rewards_share)));
181
182 let now = Clock::get()?.unix_timestamp;
183 quarry.last_update_ts = cmp::min(now, quarry.famine_ts);
184 quarry.annual_rewards_rate = rewarder.compute_quarry_annual_rewards_rate(new_share)?;
185 quarry.rewards_share = new_share;
186
187 emit!(QuarryRewardsUpdateEvent {
188 token_mint: quarry.token_mint_key,
189 annual_rewards_rate: quarry.annual_rewards_rate,
190 rewards_share: quarry.rewards_share,
191 timestamp: now,
192 });
193
194 Ok(())
195 }
196
197 #[access_control(ctx.accounts.validate())]
199 pub fn set_famine(ctx: Context<SetFamine>, famine_ts: i64) -> Result<()> {
200 let quarry = &mut ctx.accounts.quarry;
201 quarry.famine_ts = famine_ts;
202
203 Ok(())
204 }
205
206 #[access_control(ctx.accounts.validate())]
209 pub fn update_quarry_rewards(ctx: Context<UpdateQuarryRewards>) -> Result<()> {
210 let current_ts = Clock::get()?.unix_timestamp;
211 let rewarder = &ctx.accounts.rewarder;
212 let payroll: Payroll = (*ctx.accounts.quarry).into();
213 let quarry = &mut ctx.accounts.quarry;
214 quarry.update_rewards_internal(current_ts, rewarder, &payroll)?;
215
216 emit!(QuarryRewardsUpdateEvent {
217 token_mint: quarry.token_mint_key,
218 annual_rewards_rate: quarry.annual_rewards_rate,
219 rewards_share: quarry.rewards_share,
220 timestamp: current_ts,
221 });
222
223 Ok(())
224 }
225
226 #[deprecated(since = "5.0.0", note = "Use `create_miner_v2` instead.")]
234 #[access_control(ctx.accounts.validate())]
235 pub fn create_miner(ctx: Context<CreateMiner>, _bump: u8) -> Result<()> {
236 instructions::create_miner::handler(ctx)
237 }
238
239 #[access_control(ctx.accounts.validate())]
245 pub fn create_miner_v2(ctx: Context<CreateMiner>) -> Result<()> {
246 instructions::create_miner::handler(ctx)
247 }
248
249 #[deprecated(since = "5.0.0", note = "Use `claim_rewards_v2` instead.")]
251 #[access_control(ctx.accounts.validate())]
252 pub fn claim_rewards(ctx: Context<ClaimRewards>) -> Result<()> {
253 instructions::claim_rewards::handler(ctx)
254 }
255
256 #[access_control(ctx.accounts.validate())]
260 pub fn claim_rewards_v2(ctx: Context<ClaimRewardsV2>) -> Result<()> {
261 instructions::claim_rewards_v2::handler(ctx)
262 }
263
264 #[access_control(ctx.accounts.validate())]
266 pub fn stake_tokens(ctx: Context<UserStake>, amount: u64) -> Result<()> {
267 if amount == 0 {
268 return Ok(());
270 }
271
272 let quarry = &mut ctx.accounts.quarry;
273 let clock = Clock::get()?;
274 quarry.process_stake_action_internal(
275 StakeAction::Stake,
276 clock.unix_timestamp,
277 &ctx.accounts.rewarder,
278 &mut ctx.accounts.miner,
279 amount,
280 )?;
281
282 let cpi_accounts = Transfer {
283 from: ctx.accounts.token_account.to_account_info(),
284 to: ctx.accounts.miner_vault.to_account_info(),
285 authority: ctx.accounts.authority.to_account_info(),
286 };
287 let cpi_program = ctx.accounts.token_program.to_account_info();
288 let cpi_context = CpiContext::new(cpi_program, cpi_accounts);
289 token::transfer(cpi_context, amount)?;
291
292 emit!(StakeEvent {
293 timestamp: clock.unix_timestamp,
294 authority: ctx.accounts.authority.key(),
295 amount,
296 token: ctx.accounts.token_account.mint,
297 });
298 Ok(())
299 }
300
301 #[access_control(ctx.accounts.validate())]
303 pub fn withdraw_tokens(ctx: Context<UserStake>, amount: u64) -> Result<()> {
304 if amount == 0 {
305 return Ok(());
307 }
308 invariant!(
309 amount <= ctx.accounts.miner_vault.amount,
310 InsufficientBalance
311 );
312
313 let clock = Clock::get()?;
314 let quarry = &mut ctx.accounts.quarry;
315 quarry.process_stake_action_internal(
316 StakeAction::Withdraw,
317 clock.unix_timestamp,
318 &ctx.accounts.rewarder,
319 &mut ctx.accounts.miner,
320 amount,
321 )?;
322
323 let miner_seeds = &[
325 b"Miner".as_ref(),
326 ctx.accounts.miner.quarry.as_ref(),
327 ctx.accounts.miner.authority.as_ref(),
328 &[ctx.accounts.miner.bump],
329 ];
330 let signer_seeds = &[&miner_seeds[..]];
331 let cpi_accounts = token::Transfer {
332 from: ctx.accounts.miner_vault.to_account_info(),
333 to: ctx.accounts.token_account.to_account_info(),
334 authority: ctx.accounts.miner.to_account_info(),
335 };
336 let cpi_ctx = CpiContext::new_with_signer(
337 ctx.accounts.token_program.to_account_info(),
338 cpi_accounts,
339 signer_seeds,
340 );
341 token::transfer(cpi_ctx, amount)?;
343
344 emit!(WithdrawEvent {
345 timestamp: clock.unix_timestamp,
346 authority: ctx.accounts.authority.key(),
347 amount,
348 token: ctx.accounts.token_account.mint,
349 });
350 Ok(())
351 }
352
353 #[access_control(ctx.accounts.validate())]
358 pub fn rescue_tokens(ctx: Context<RescueTokens>) -> Result<()> {
359 instructions::rescue_tokens::handler(ctx)
360 }
361
362 #[access_control(ctx.accounts.validate())]
369 pub fn extract_fees(ctx: Context<ExtractFees>) -> Result<()> {
370 let seeds = gen_rewarder_signer_seeds!(ctx.accounts.rewarder);
371 let signer_seeds = &[&seeds[..]];
372
373 token::transfer(
375 CpiContext::new_with_signer(
376 ctx.accounts.token_program.to_account_info(),
377 token::Transfer {
378 from: ctx.accounts.claim_fee_token_account.to_account_info(),
379 to: ctx.accounts.fee_to_token_account.to_account_info(),
380 authority: ctx.accounts.rewarder.to_account_info(),
381 },
382 signer_seeds,
383 ),
384 ctx.accounts.claim_fee_token_account.amount,
385 )?;
386
387 Ok(())
388 }
389}
390
391#[derive(Accounts)]
399pub struct SetPauseAuthority<'info> {
400 pub auth: MutableRewarderWithAuthority<'info>,
402
403 pub new_pause_authority: UncheckedAccount<'info>,
406}
407
408#[derive(Accounts)]
410pub struct TransferAuthority<'info> {
411 pub authority: Signer<'info>,
413
414 #[account(mut)]
416 pub rewarder: Account<'info, Rewarder>,
417}
418
419#[derive(Accounts)]
421pub struct AcceptAuthority<'info> {
422 pub authority: Signer<'info>,
424
425 #[account(mut)]
427 pub rewarder: Account<'info, Rewarder>,
428}
429
430#[derive(Accounts, Clone)]
432pub struct MutableRewarderWithAuthority<'info> {
433 pub authority: Signer<'info>,
435
436 #[account(mut, has_one = authority @ ErrorCode::Unauthorized)]
438 pub rewarder: Account<'info, Rewarder>,
439}
440
441#[derive(Accounts)]
443pub struct ReadOnlyRewarderWithAuthority<'info> {
444 pub authority: Signer<'info>,
446
447 #[account(has_one = authority)]
449 pub rewarder: Account<'info, Rewarder>,
450}
451
452#[derive(Accounts)]
454pub struct SetAnnualRewards<'info> {
455 pub auth: MutableRewarderWithAuthority<'info>,
457}
458
459#[derive(Accounts)]
463pub struct SetFamine<'info> {
464 pub auth: ReadOnlyRewarderWithAuthority<'info>,
466
467 #[account(mut, constraint = quarry.rewarder == auth.rewarder.key())]
469 pub quarry: Account<'info, Quarry>,
470}
471
472#[derive(Accounts)]
474pub struct SetRewardsShare<'info> {
475 pub auth: MutableRewarderWithAuthority<'info>,
477
478 #[account(mut)]
480 pub quarry: Account<'info, Quarry>,
481}
482
483#[derive(Accounts)]
485pub struct UpdateQuarryRewards<'info> {
486 #[account(mut)]
488 pub quarry: Account<'info, Quarry>,
489
490 pub rewarder: Account<'info, Rewarder>,
492}
493
494#[derive(Accounts, Clone)]
503pub struct UserStake<'info> {
504 pub authority: Signer<'info>,
506
507 #[account(mut)]
509 pub miner: Account<'info, Miner>,
510
511 #[account(mut)]
513 pub quarry: Account<'info, Quarry>,
514
515 #[account(mut)]
517 pub miner_vault: Account<'info, TokenAccount>,
518
519 #[account(mut)]
521 pub token_account: Account<'info, TokenAccount>,
522
523 pub token_program: Program<'info, Token>,
525
526 pub rewarder: Account<'info, Rewarder>,
528}
529
530#[derive(Accounts)]
532pub struct ExtractFees<'info> {
533 #[account(has_one = claim_fee_token_account)]
535 pub rewarder: Account<'info, Rewarder>,
536
537 #[account(mut)]
539 pub claim_fee_token_account: Account<'info, TokenAccount>,
540
541 #[account(mut)]
544 pub fee_to_token_account: Account<'info, TokenAccount>,
545
546 pub token_program: Program<'info, Token>,
548}
549
550#[derive(Accounts)]
552pub struct MutableRewarderWithPauseAuthority<'info> {
553 pub pause_authority: Signer<'info>,
555
556 #[account(mut)]
558 pub rewarder: Account<'info, Rewarder>,
559}
560
561#[event]
567pub struct StakeEvent {
568 #[index]
570 pub authority: Pubkey,
571 #[index]
573 pub token: Pubkey,
574 pub amount: u64,
576 pub timestamp: i64,
578}
579
580#[event]
582pub struct WithdrawEvent {
583 #[index]
585 pub authority: Pubkey,
586 #[index]
588 pub token: Pubkey,
589 pub amount: u64,
591 pub timestamp: i64,
593}
594
595#[event]
597pub struct RewarderAnnualRewardsUpdateEvent {
598 pub previous_rate: u64,
600 pub new_rate: u64,
602 pub timestamp: i64,
604}
605
606#[event]
608pub struct QuarryRewardsUpdateEvent {
609 pub token_mint: Pubkey,
611 pub annual_rewards_rate: u64,
613 pub rewards_share: u64,
615 pub timestamp: i64,
617}
618
619#[error_code]
621pub enum ErrorCode {
622 #[msg("You are not authorized to perform this action.")]
623 Unauthorized,
624 #[msg("Insufficient staked balance for withdraw request.")]
625 InsufficientBalance,
626 #[msg("Pending authority not set")]
627 PendingAuthorityNotSet,
628 #[msg("Invalid quarry rewards share")]
629 InvalidRewardsShare,
630 #[msg("Insufficient allowance.")]
631 InsufficientAllowance,
632 #[msg("New vault not empty.")]
633 NewVaultNotEmpty,
634 #[msg("Not enough tokens.")]
635 NotEnoughTokens,
636 #[msg("Invalid timestamp.")]
637 InvalidTimestamp,
638 #[msg("Invalid max claim fee.")]
639 InvalidMaxClaimFee,
640 #[msg("Max annual rewards rate exceeded.")]
641 MaxAnnualRewardsRateExceeded,
642 #[msg("Rewarder is paused.")]
643 Paused,
644 #[msg("Rewards earned exceeded quarry's upper bound.")]
645 UpperboundExceeded,
646}