1#![deny(rustdoc::all)]
3#![allow(rustdoc::missing_doc_code_examples)]
4#![allow(deprecated)]
5
6#[macro_use]
7mod macros;
8
9use anchor_lang::prelude::*;
10use anchor_spl::token::Token;
11use anchor_spl::token::{self, Mint, TokenAccount};
12use vipers::prelude::*;
13
14mod account_validators;
15mod instructions;
16mod state;
17
18use instructions::*;
19pub use state::*;
20
21declare_id!("QMWoBmAyJLAsA1Lh9ugMTw2gciTihncciphzdNzdZYV");
22
23#[cfg(not(feature = "no-entrypoint"))]
24solana_security_txt::security_txt! {
25 name: "Quarry Mint Wrapper",
26 project_url: "https://quarry.so",
27 contacts: "email:team@quarry.so",
28 policy: "https://github.com/QuarryProtocol/quarry/blob/master/SECURITY.md",
29
30 source_code: "https://github.com/QuarryProtocol/quarry",
31 auditors: "Quantstamp"
32}
33
34#[program]
35pub mod quarry_mint_wrapper {
36 use super::*;
37
38 #[deprecated(since = "5.0.0", note = "Use `new_wrapper_v2` instead.")]
44 #[access_control(ctx.accounts.validate())]
45 pub fn new_wrapper(ctx: Context<NewWrapper>, _bump: u8, hard_cap: u64) -> Result<()> {
46 instructions::new_wrapper::handler(ctx, hard_cap)
47 }
48
49 #[access_control(ctx.accounts.validate())]
53 pub fn new_wrapper_v2(ctx: Context<NewWrapper>, hard_cap: u64) -> Result<()> {
54 instructions::new_wrapper::handler(ctx, hard_cap)
55 }
56
57 #[access_control(ctx.accounts.validate())]
59 pub fn transfer_admin(ctx: Context<TransferAdmin>) -> Result<()> {
60 let mint_wrapper = &mut ctx.accounts.mint_wrapper;
61 mint_wrapper.pending_admin = ctx.accounts.next_admin.key();
62
63 emit!(MintWrapperAdminProposeEvent {
64 mint_wrapper: mint_wrapper.key(),
65 current_admin: mint_wrapper.admin,
66 pending_admin: mint_wrapper.pending_admin,
67 });
68 Ok(())
69 }
70
71 #[access_control(ctx.accounts.validate())]
73 pub fn accept_admin(ctx: Context<AcceptAdmin>) -> Result<()> {
74 let mint_wrapper = &mut ctx.accounts.mint_wrapper;
75 let previous_admin = mint_wrapper.admin;
76 mint_wrapper.admin = ctx.accounts.pending_admin.key();
77 mint_wrapper.pending_admin = Pubkey::default();
78
79 emit!(MintWrapperAdminUpdateEvent {
80 mint_wrapper: mint_wrapper.key(),
81 previous_admin,
82 admin: mint_wrapper.admin,
83 });
84 Ok(())
85 }
86
87 #[deprecated(since = "5.0.0", note = "Use `new_minter_v2` instead.")]
93 #[access_control(ctx.accounts.validate())]
94 pub fn new_minter(ctx: Context<NewMinter>, _bump: u8) -> Result<()> {
95 instructions::new_minter::handler(ctx)
96 }
97
98 #[access_control(ctx.accounts.validate())]
102 pub fn new_minter_v2(ctx: Context<NewMinter>) -> Result<()> {
103 instructions::new_minter::handler(ctx)
104 }
105
106 #[access_control(ctx.accounts.validate())]
108 pub fn minter_update(ctx: Context<MinterUpdate>, allowance: u64) -> Result<()> {
109 let minter = &mut ctx.accounts.minter;
110 let previous_allowance = minter.allowance;
111 minter.allowance = allowance;
112
113 let mint_wrapper = &mut ctx.accounts.auth.mint_wrapper;
114 mint_wrapper.total_allowance = unwrap_int!(mint_wrapper
115 .total_allowance
116 .checked_add(allowance)
117 .and_then(|v| v.checked_sub(previous_allowance)));
118
119 emit!(MinterAllowanceUpdateEvent {
120 mint_wrapper: minter.mint_wrapper,
121 minter: minter.key(),
122 previous_allowance,
123 allowance: minter.allowance,
124 });
125 Ok(())
126 }
127
128 #[access_control(ctx.accounts.validate())]
130 pub fn perform_mint(ctx: Context<PerformMint>, amount: u64) -> Result<()> {
131 let mint_wrapper = &ctx.accounts.mint_wrapper;
132 let minter = &mut ctx.accounts.minter;
133 invariant!(minter.allowance >= amount, MinterAllowanceExceeded);
134
135 let new_supply = unwrap_int!(ctx.accounts.token_mint.supply.checked_add(amount));
136 invariant!(new_supply <= mint_wrapper.hard_cap, HardcapExceeded);
137
138 minter.allowance = unwrap_int!(minter.allowance.checked_sub(amount));
139 minter.total_minted = unwrap_int!(minter.total_minted.checked_add(amount));
140
141 let seeds = gen_wrapper_signer_seeds!(mint_wrapper);
142 let proxy_signer = &[&seeds[..]];
143 let cpi_ctx = CpiContext::new_with_signer(
144 ctx.accounts.token_program.to_account_info(),
145 token::MintTo {
146 mint: ctx.accounts.token_mint.to_account_info(),
147 to: ctx.accounts.destination.to_account_info(),
148 authority: ctx.accounts.mint_wrapper.to_account_info(),
149 },
150 proxy_signer,
151 );
152 token::mint_to(cpi_ctx, amount)?;
153
154 let mint_wrapper = &mut ctx.accounts.mint_wrapper;
155 mint_wrapper.total_allowance =
156 unwrap_int!(mint_wrapper.total_allowance.checked_sub(amount));
157 mint_wrapper.total_minted = unwrap_int!(mint_wrapper.total_minted.checked_add(amount));
158
159 ctx.accounts.token_mint.reload()?;
161 invariant!(new_supply == ctx.accounts.token_mint.supply, Unauthorized);
162
163 emit!(MinterMintEvent {
164 mint_wrapper: mint_wrapper.key(),
165 minter: minter.key(),
166 amount,
167 destination: ctx.accounts.destination.key(),
168 });
169 Ok(())
170 }
171}
172
173#[derive(Accounts)]
179pub struct MinterUpdate<'info> {
180 pub auth: OnlyAdmin<'info>,
182 #[account(mut)]
184 pub minter: Account<'info, Minter>,
185}
186
187#[derive(Accounts)]
188pub struct TransferAdmin<'info> {
189 #[account(mut)]
191 pub mint_wrapper: Account<'info, MintWrapper>,
192
193 pub admin: Signer<'info>,
195
196 pub next_admin: UncheckedAccount<'info>,
199}
200
201#[derive(Accounts)]
202pub struct AcceptAdmin<'info> {
203 #[account(mut)]
205 pub mint_wrapper: Account<'info, MintWrapper>,
206
207 pub pending_admin: Signer<'info>,
209}
210
211#[derive(Accounts, Clone)]
213pub struct PerformMint<'info> {
214 #[account(mut)]
216 pub mint_wrapper: Account<'info, MintWrapper>,
217
218 pub minter_authority: Signer<'info>,
220
221 #[account(mut)]
223 pub token_mint: Account<'info, Mint>,
224
225 #[account(mut)]
227 pub destination: Account<'info, TokenAccount>,
228
229 #[account(mut)]
231 pub minter: Account<'info, Minter>,
232
233 pub token_program: Program<'info, Token>,
235}
236
237#[derive(Accounts)]
243pub struct OnlyAdmin<'info> {
244 #[account(mut, has_one = admin @ ErrorCode::Unauthorized)]
246 pub mint_wrapper: Account<'info, MintWrapper>,
247 pub admin: Signer<'info>,
249}
250
251#[event]
257pub struct NewMintWrapperEvent {
258 #[index]
260 pub mint_wrapper: Pubkey,
261
262 pub hard_cap: u64,
264 pub admin: Pubkey,
266 pub token_mint: Pubkey,
268}
269
270#[event]
272pub struct MintWrapperAdminProposeEvent {
273 #[index]
275 pub mint_wrapper: Pubkey,
276
277 pub current_admin: Pubkey,
279 pub pending_admin: Pubkey,
281}
282
283#[event]
285pub struct MintWrapperAdminUpdateEvent {
286 #[index]
288 pub mint_wrapper: Pubkey,
289
290 pub previous_admin: Pubkey,
292 pub admin: Pubkey,
294}
295
296#[event]
298pub struct NewMinterEvent {
299 #[index]
301 pub mint_wrapper: Pubkey,
302 #[index]
304 pub minter: Pubkey,
305
306 pub index: u64,
308 pub minter_authority: Pubkey,
310}
311
312#[event]
314pub struct MinterAllowanceUpdateEvent {
315 #[index]
317 pub mint_wrapper: Pubkey,
318 #[index]
320 pub minter: Pubkey,
321
322 pub previous_allowance: u64,
324 pub allowance: u64,
326}
327
328#[event]
330pub struct MinterMintEvent {
331 #[index]
333 pub mint_wrapper: Pubkey,
334 #[index]
336 pub minter: Pubkey,
337
338 pub amount: u64,
340 pub destination: Pubkey,
342}
343
344#[error_code]
346pub enum ErrorCode {
347 #[msg("You are not authorized to perform this action.")]
348 Unauthorized,
349 #[msg("Cannot mint over hard cap.")]
350 HardcapExceeded,
351 #[msg("Minter allowance exceeded.")]
352 MinterAllowanceExceeded,
353}