quarry_mint_wrapper/
lib.rs

1//! Proxy program for interacting with the token mint.
2#![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    // --------------------------------
39    // [MintWrapper] instructions
40    // --------------------------------
41
42    /// Creates a new [MintWrapper].
43    #[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    /// Creates a new [MintWrapper].
50    ///
51    /// The V2 variant removes the need for supplying the bump.
52    #[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    /// Transfers admin to another account.
58    #[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    /// Accepts the new admin.
72    #[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    // --------------------------------
88    // [Minter] instructions
89    // --------------------------------
90
91    /// Creates a new [Minter].
92    #[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    /// Creates a new [Minter].
99    ///
100    /// The V2 variant removes the need for supplying the bump.
101    #[access_control(ctx.accounts.validate())]
102    pub fn new_minter_v2(ctx: Context<NewMinter>) -> Result<()> {
103        instructions::new_minter::handler(ctx)
104    }
105
106    /// Updates a [Minter]'s allowance.
107    #[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    /// Performs a mint.
129    #[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        // extra sanity checks
160        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// --------------------------------
174// Instructions
175// --------------------------------
176
177/// Updates a minter.
178#[derive(Accounts)]
179pub struct MinterUpdate<'info> {
180    /// Owner of the [MintWrapper].
181    pub auth: OnlyAdmin<'info>,
182    /// Information about the minter.
183    #[account(mut)]
184    pub minter: Account<'info, Minter>,
185}
186
187#[derive(Accounts)]
188pub struct TransferAdmin<'info> {
189    /// The [MintWrapper].
190    #[account(mut)]
191    pub mint_wrapper: Account<'info, MintWrapper>,
192
193    /// The previous admin.
194    pub admin: Signer<'info>,
195
196    /// The next admin.
197    /// CHECK: OK
198    pub next_admin: UncheckedAccount<'info>,
199}
200
201#[derive(Accounts)]
202pub struct AcceptAdmin<'info> {
203    /// The mint wrapper.
204    #[account(mut)]
205    pub mint_wrapper: Account<'info, MintWrapper>,
206
207    /// The new admin.
208    pub pending_admin: Signer<'info>,
209}
210
211/// Accounts for the perform_mint instruction.
212#[derive(Accounts, Clone)]
213pub struct PerformMint<'info> {
214    /// [MintWrapper].
215    #[account(mut)]
216    pub mint_wrapper: Account<'info, MintWrapper>,
217
218    /// [Minter]'s authority.
219    pub minter_authority: Signer<'info>,
220
221    /// Token [Mint].
222    #[account(mut)]
223    pub token_mint: Account<'info, Mint>,
224
225    /// Destination [TokenAccount] for minted tokens.
226    #[account(mut)]
227    pub destination: Account<'info, TokenAccount>,
228
229    /// [Minter] information.
230    #[account(mut)]
231    pub minter: Account<'info, Minter>,
232
233    /// SPL Token program.
234    pub token_program: Program<'info, Token>,
235}
236
237// --------------------------------
238// Account structs
239// --------------------------------
240
241/// Only an admin is allowed to use instructions containing this struct.
242#[derive(Accounts)]
243pub struct OnlyAdmin<'info> {
244    /// The [MintWrapper].
245    #[account(mut, has_one = admin @ ErrorCode::Unauthorized)]
246    pub mint_wrapper: Account<'info, MintWrapper>,
247    /// [MintWrapper::admin].
248    pub admin: Signer<'info>,
249}
250
251// --------------------------------
252// Events
253// --------------------------------
254
255/// Emitted when a [MintWrapper] is created.
256#[event]
257pub struct NewMintWrapperEvent {
258    /// The [MintWrapper].
259    #[index]
260    pub mint_wrapper: Pubkey,
261
262    /// Hard cap.
263    pub hard_cap: u64,
264    /// The admin.
265    pub admin: Pubkey,
266    /// The [Mint] of the token.
267    pub token_mint: Pubkey,
268}
269
270/// Emitted when a [MintWrapper]'s admin is proposed.
271#[event]
272pub struct MintWrapperAdminProposeEvent {
273    /// The [MintWrapper].
274    #[index]
275    pub mint_wrapper: Pubkey,
276
277    /// The [MintWrapper]'s current admin.
278    pub current_admin: Pubkey,
279    /// The [MintWrapper]'s pending admin.
280    pub pending_admin: Pubkey,
281}
282
283/// Emitted when a [MintWrapper]'s admin is transferred.
284#[event]
285pub struct MintWrapperAdminUpdateEvent {
286    /// The [MintWrapper].
287    #[index]
288    pub mint_wrapper: Pubkey,
289
290    /// The [MintWrapper]'s previous admin.
291    pub previous_admin: Pubkey,
292    /// The [MintWrapper]'s new admin.
293    pub admin: Pubkey,
294}
295
296/// Emitted when a [Minter] is created.
297#[event]
298pub struct NewMinterEvent {
299    /// The [MintWrapper].
300    #[index]
301    pub mint_wrapper: Pubkey,
302    /// The [Minter].
303    #[index]
304    pub minter: Pubkey,
305
306    /// The [Minter]'s index.
307    pub index: u64,
308    /// The [Minter]'s authority.
309    pub minter_authority: Pubkey,
310}
311
312/// Emitted when a [Minter]'s allowance is updated.
313#[event]
314pub struct MinterAllowanceUpdateEvent {
315    /// The [MintWrapper].
316    #[index]
317    pub mint_wrapper: Pubkey,
318    /// The [Minter].
319    #[index]
320    pub minter: Pubkey,
321
322    /// The [Minter]'s previous allowance.
323    pub previous_allowance: u64,
324    /// The [Minter]'s new allowance.
325    pub allowance: u64,
326}
327
328/// Emitted when a [Minter] performs a mint.
329#[event]
330pub struct MinterMintEvent {
331    /// The [MintWrapper].
332    #[index]
333    pub mint_wrapper: Pubkey,
334    /// The [Minter].
335    #[index]
336    pub minter: Pubkey,
337
338    /// Amount minted.
339    pub amount: u64,
340    /// Mint destination.
341    pub destination: Pubkey,
342}
343
344/// Error Codes
345#[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}