mpl_gumdrop/
lib.rs

1//! Program for distributing tokens efficiently via uploading a Merkle root.
2use anchor_lang::{
3    prelude::*,
4    solana_program::{
5        instruction::{AccountMeta, Instruction},
6        program::{invoke, invoke_signed},
7        system_instruction, sysvar,
8    },
9};
10use anchor_spl::token::{self, Token, TokenAccount};
11use mpl_token_metadata;
12use std::io::Write;
13
14pub mod merkle_proof;
15
16declare_id!("gdrpGjVffourzkdDRrQmySw4aTHr8a3xmQzzxSwFD1a");
17pub const CANDY_MACHINE_V1_PROGRAM_ID: Pubkey = Pubkey::new_from_array([
18    0x09, 0x2a, 0xee, 0x40, 0xbb, 0xdd, 0x63, 0x1e, 0xef, 0xfb, 0x7c, 0x96, 0xf6, 0x15, 0x65, 0x76,
19    0x84, 0x65, 0xf3, 0xc1, 0x9c, 0xf9, 0x90, 0xcd, 0x7f, 0x74, 0x8c, 0x8d, 0x79, 0x95, 0x08, 0x20,
20]);
21
22pub const CANDY_MACHINE_V2_PROGRAM_ID: Pubkey = Pubkey::new_from_array([
23    0x09, 0x2a, 0xee, 0x3d, 0xfc, 0x2d, 0x0e, 0x55, 0x78, 0x23, 0x13, 0x83, 0x79, 0x69, 0xea, 0xf5,
24    0x21, 0x51, 0xc0, 0x96, 0xc0, 0x6b, 0x5c, 0x2a, 0x82, 0xf0, 0x86, 0xa5, 0x03, 0xe8, 0x2c, 0x34,
25]);
26
27fn verify_candy(candy_machine_program_account: &Pubkey) -> Result<()> {
28    if candy_machine_program_account != &CANDY_MACHINE_V1_PROGRAM_ID
29        && candy_machine_program_account != &CANDY_MACHINE_V2_PROGRAM_ID
30    {
31        return Err(GumdropError::MustUseOfficialCandyMachine.into());
32    }
33    Ok(())
34}
35
36const CLAIM_COUNT: &[u8] = b"ClaimCount";
37const CLAIM_STATUS: &[u8] = b"ClaimStatus";
38
39fn verify_temporal<'a>(
40    distributor: &Account<'a, MerkleDistributor>,
41    temporal: &Signer<'a>,
42    claimant_secret: Pubkey,
43) -> Result<()> {
44    require!(
45        // got the OTP auth from the signer specified by the creator
46        temporal.key() == distributor.temporal
47        // the secret used in the hash was a Pubkey (wallet) so proof-of-ownership is achieved by
48        // signing for this transaction
49        || temporal.key() == claimant_secret
50        // the creator decided not to use a temporal signer
51        || distributor.temporal == Pubkey::default(),
52        GumdropError::TemporalMismatch
53    );
54
55    Ok(())
56}
57
58fn verify_claim_bump<'a>(
59    claim_account: &AccountInfo<'a>,
60    claim_prefix: &[u8],
61    claim_bump: u8,
62    index: u64,
63    distributor: &Account<'a, MerkleDistributor>,
64) -> Result<()> {
65    require!(
66        claim_prefix == CLAIM_COUNT || claim_prefix == CLAIM_STATUS,
67        GumdropError::InvalidClaimBump,
68    );
69
70    let (claim_account_key, claim_account_bump) = Pubkey::find_program_address(
71        &[
72            claim_prefix,
73            &index.to_le_bytes(),
74            &distributor.key().to_bytes(),
75        ],
76        &ID,
77    );
78    require!(
79        claim_account_key == *claim_account.key && claim_account_bump == claim_bump,
80        GumdropError::InvalidClaimBump,
81    );
82
83    Ok(())
84}
85
86fn get_or_create_claim_count<'a>(
87    distributor: &Account<'a, MerkleDistributor>,
88    claim_count: &AccountInfo<'a>,
89    temporal: &Signer<'a>,
90    payer: &Signer<'a>,
91    system_program: &Program<'a, System>,
92    claim_bump: u8,
93    index: u64,
94    claimant_secret: Pubkey,
95) -> Result<Account<'a, ClaimCount>> {
96    let rent = &Rent::get()?;
97    let space = 8 + ClaimCount::default().try_to_vec().unwrap().len();
98
99    verify_claim_bump(claim_count, CLAIM_COUNT, claim_bump, index, distributor)?;
100
101    let create_claim_state = claim_count.lamports() == 0; // TODO: support initial lamports?
102    if create_claim_state {
103        let lamports = rent.minimum_balance(space);
104        let claim_count_seeds = [
105            CLAIM_COUNT.as_ref(),
106            &index.to_le_bytes(),
107            &distributor.key().to_bytes(),
108            &[claim_bump],
109        ];
110
111        invoke_signed(
112            &system_instruction::create_account(
113                &payer.key(),
114                claim_count.key,
115                lamports,
116                space as u64,
117                &ID,
118            ),
119            &[
120                payer.to_account_info().clone(),
121                claim_count.clone(),
122                system_program.to_account_info().clone(),
123            ],
124            &[&claim_count_seeds],
125        )?;
126
127        let mut data = claim_count.try_borrow_mut_data()?;
128        let dst: &mut [u8] = &mut data;
129        let mut cursor = std::io::Cursor::new(dst);
130        cursor
131            .write_all(&<ClaimCount as anchor_lang::Discriminator>::discriminator())
132            .unwrap();
133    }
134
135    // anchor_lang::Account::try_from(&claim_count)?;
136    let mut pa: Account<ClaimCount> = Account::try_from(&claim_count)?;
137
138    if create_claim_state {
139        verify_temporal(distributor, temporal, claimant_secret)?;
140        pa.claimant = payer.key();
141    } else {
142        require!(pa.claimant == payer.key(), GumdropError::OwnerMismatch);
143    }
144
145    Ok(pa)
146}
147
148/// The [gumdrop] program.
149#[program]
150pub mod gumdrop {
151    use super::*;
152
153    /// Creates a new [MerkleDistributor].
154    /// After creating this [MerkleDistributor], the account should be seeded with tokens via
155    /// delegates
156    pub fn new_distributor(
157        ctx: Context<NewDistributor>,
158        _bump: u8,
159        root: [u8; 32],
160        temporal: Pubkey,
161    ) -> Result<()> {
162        let distributor = &mut ctx.accounts.distributor;
163
164        distributor.base = ctx.accounts.base.key();
165        distributor.bump = *ctx
166            .bumps
167            .get("distributor")
168            .ok_or(GumdropError::BumpSeedNotInHashMap)?;
169
170        distributor.root = root;
171        distributor.temporal = temporal;
172
173        Ok(())
174    }
175
176    /// Closes distributor-owned token accounts. Normal tokens should just use a delegate but we
177    /// need to transfer ownership for edition minting ATM.
178    pub fn close_distributor_token_account(
179        ctx: Context<CloseDistributorTokenAccount>,
180        _bump: u8,
181    ) -> Result<()> {
182        let distributor = &ctx.accounts.distributor;
183
184        // should be implicit in the PDA
185        require!(
186            distributor.base == ctx.accounts.base.key(),
187            GumdropError::Unauthorized
188        );
189
190        let seeds = [
191            b"MerkleDistributor".as_ref(),
192            &distributor.base.to_bytes(),
193            &[ctx.accounts.distributor.bump],
194        ];
195
196        token::transfer(
197            CpiContext::new(
198                ctx.accounts.token_program.to_account_info(),
199                token::Transfer {
200                    from: ctx.accounts.from.to_account_info(),
201                    to: ctx.accounts.to.to_account_info(),
202                    authority: ctx.accounts.distributor.to_account_info(),
203                },
204            )
205            .with_signer(&[&seeds[..]]),
206            ctx.accounts.from.amount,
207        )?;
208
209        token::close_account(
210            CpiContext::new(
211                ctx.accounts.token_program.to_account_info(),
212                token::CloseAccount {
213                    account: ctx.accounts.from.to_account_info(),
214                    destination: ctx.accounts.receiver.to_account_info(),
215                    authority: ctx.accounts.distributor.to_account_info(),
216                },
217            )
218            .with_signer(&[&seeds[..]]),
219        )?;
220
221        Ok(())
222    }
223
224    /// Closes an existing [MerkleDistributor].
225    /// Moves all tokens from the [MerkleDistributor] to the specified account and closes
226    /// distributor accounts.
227    /// Must `close_distributor_token_account` first
228    pub fn close_distributor<'info>(
229        ctx: Context<'_, '_, '_, 'info, CloseDistributor<'info>>,
230        _bump: u8,
231        _wallet_bump: u8,
232    ) -> Result<()> {
233        let distributor = &ctx.accounts.distributor;
234
235        // should be implicit in the PDA
236        require!(
237            distributor.base == ctx.accounts.base.key(),
238            GumdropError::Unauthorized
239        );
240
241        let wallet_seeds = [
242            b"Wallet".as_ref(),
243            &distributor.key().to_bytes(),
244            &[_wallet_bump],
245        ];
246
247        if !ctx.remaining_accounts.is_empty() {
248            // transfer authority out
249            let candy_machine_info = &ctx.remaining_accounts[0];
250            let candy_machine_program_info = &ctx.remaining_accounts[1];
251            verify_candy(candy_machine_program_info.key)?;
252            // TODO. global::update_authority instruction...
253            let mut data = vec![0x20, 0x2e, 0x40, 0x1c, 0x95, 0x4b, 0xf3, 0x58];
254
255            data.push(0x01);
256            data.extend_from_slice(&ctx.accounts.receiver.key.to_bytes());
257
258            invoke_signed(
259                &Instruction {
260                    program_id: *candy_machine_program_info.key,
261                    accounts: vec![
262                        AccountMeta::new(*candy_machine_info.key, false),
263                        AccountMeta::new(*ctx.accounts.distributor_wallet.key, true),
264                    ],
265                    data: data,
266                },
267                &[
268                    candy_machine_info.clone(),
269                    ctx.accounts.distributor_wallet.clone(),
270                ],
271                &[&wallet_seeds],
272            )?;
273        }
274
275        invoke_signed(
276            &system_instruction::transfer(
277                ctx.accounts.distributor_wallet.key,
278                ctx.accounts.receiver.key,
279                ctx.accounts.distributor_wallet.lamports(),
280            ),
281            &[
282                ctx.accounts.distributor_wallet.clone(),
283                ctx.accounts.receiver.clone(),
284                ctx.accounts.system_program.to_account_info().clone(),
285            ],
286            &[&wallet_seeds],
287        )?;
288
289        Ok(())
290    }
291
292    pub fn prove_claim<'info>(
293        ctx: Context<ProveClaim>,
294        claim_prefix: Vec<u8>,
295        claim_bump: u8,
296        index: u64,
297        amount: u64,
298        claimant_secret: Pubkey,
299        resource: Pubkey,
300        resource_nonce: Vec<u8>,
301        proof: Vec<[u8; 32]>,
302    ) -> Result<()> {
303        // The logic here is that we will allow the proof to be whichever prefix matches the claim
304        // type. The ClaimProof will live at the same place as V1 ClaimCount and V1 ClaimStatus so
305        // that users can't claim with both endpoints but also maintain some backwards
306        // compatibility. The account is created wherever this prefix points to and since the
307        // resource is unique per gumdrop, if this is messed up, they shouldn't be able to claim
308        // extra resources.
309        require!(
310            claim_prefix.as_slice() == CLAIM_COUNT || claim_prefix.as_slice() == CLAIM_STATUS,
311            GumdropError::InvalidProof,
312        );
313
314        let claim_proof = &mut ctx.accounts.claim_proof;
315        let distributor = &ctx.accounts.distributor;
316
317        verify_claim_bump(
318            &claim_proof.to_account_info(),
319            claim_prefix.as_slice(),
320            claim_bump,
321            index,
322            distributor,
323        )?;
324
325        // Verify the merkle proof.
326        let node = if resource_nonce.is_empty() {
327            solana_program::keccak::hashv(&[
328                &[0x00],
329                &index.to_le_bytes(),
330                &claimant_secret.to_bytes(),
331                &resource.to_bytes(),
332                &amount.to_le_bytes(),
333            ])
334        } else {
335            solana_program::keccak::hashv(&[
336                &[0x00],
337                &index.to_le_bytes(),
338                &claimant_secret.to_bytes(),
339                &resource.to_bytes(),
340                &amount.to_le_bytes(),
341                resource_nonce.as_slice(),
342            ])
343        };
344        require!(
345            merkle_proof::verify(proof, distributor.root, node.0),
346            GumdropError::InvalidProof,
347        );
348
349        verify_temporal(distributor, &ctx.accounts.temporal, claimant_secret)?;
350
351        claim_proof.amount = amount;
352        claim_proof.count = 0;
353        claim_proof.claimant = ctx.accounts.payer.key();
354        claim_proof.resource = resource;
355        claim_proof.resource_nonce = resource_nonce;
356
357        Ok(())
358    }
359
360    /// Claims tokens from the [MerkleDistributor].
361    pub fn claim(
362        ctx: Context<Claim>,
363        claim_bump: u8,
364        index: u64,
365        amount: u64,
366        claimant_secret: Pubkey,
367        proof: Vec<[u8; 32]>,
368    ) -> Result<()> {
369        let claim_status = &mut ctx.accounts.claim_status;
370        require!(
371            *claim_status.to_account_info().owner == ID,
372            GumdropError::OwnerMismatch
373        );
374        require!(
375            // This check is redudant, we should not be able to initialize a claim status account at the same key.
376            !claim_status.is_claimed && claim_status.claimed_at == 0,
377            GumdropError::DropAlreadyClaimed
378        );
379
380        let distributor = &ctx.accounts.distributor;
381        let mint = ctx.accounts.from.mint;
382
383        verify_claim_bump(
384            &claim_status.to_account_info(),
385            CLAIM_STATUS,
386            claim_bump,
387            index,
388            distributor,
389        )?;
390
391        // Verify the merkle proof.
392        let node = solana_program::keccak::hashv(&[
393            &[0x00],
394            &index.to_le_bytes(),
395            &claimant_secret.to_bytes(),
396            &mint.to_bytes(),
397            &amount.to_le_bytes(),
398        ]);
399        require!(
400            merkle_proof::verify(proof, distributor.root, node.0),
401            GumdropError::InvalidProof
402        );
403
404        // Mark it claimed and send the tokens.
405        claim_status.amount = amount;
406        claim_status.is_claimed = true;
407        let clock = Clock::get()?;
408        claim_status.claimed_at = clock.unix_timestamp;
409        claim_status.claimant = ctx.accounts.payer.key();
410
411        let seeds = [
412            b"MerkleDistributor".as_ref(),
413            &distributor.base.to_bytes(),
414            &[ctx.accounts.distributor.bump],
415        ];
416
417        verify_temporal(distributor, &ctx.accounts.temporal, claimant_secret)?;
418        token::transfer(
419            CpiContext::new(
420                ctx.accounts.token_program.to_account_info(),
421                token::Transfer {
422                    from: ctx.accounts.from.to_account_info(),
423                    to: ctx.accounts.to.to_account_info(),
424                    authority: ctx.accounts.distributor.to_account_info(),
425                },
426            )
427            .with_signer(&[&seeds[..]]),
428            amount,
429        )?;
430
431        emit!(ClaimedEvent {
432            index,
433            claimant: ctx.accounts.payer.key(),
434            amount
435        });
436        Ok(())
437    }
438
439    /// Claims NFTs directly from the candy machine through the [MerkleDistributor].
440    pub fn claim_candy<'info>(
441        ctx: Context<'_, '_, '_, 'info, ClaimCandy<'info>>,
442        wallet_bump: u8,
443        claim_bump: u8,
444        index: u64,
445        amount: u64,
446        claimant_secret: Pubkey,
447        proof: Vec<[u8; 32]>,
448    ) -> Result<()> {
449        let distributor = &ctx.accounts.distributor;
450        let mut claim_count = get_or_create_claim_count(
451            &ctx.accounts.distributor,
452            &ctx.accounts.claim_count,
453            &ctx.accounts.temporal,
454            &ctx.accounts.payer,
455            &ctx.accounts.system_program,
456            claim_bump,
457            index,
458            claimant_secret,
459        )?;
460        require!(
461            *claim_count.to_account_info().owner == ID,
462            GumdropError::OwnerMismatch
463        );
464
465        // TODO: this is a bit weird but we verify elsewhere that the candy_machine_config is
466        // actually a config thing and not a mint
467        // Verify the merkle proof.
468        let node = solana_program::keccak::hashv(&[
469            &[0x00],
470            &index.to_le_bytes(),
471            &claimant_secret.to_bytes(),
472            &ctx.accounts.candy_machine_config.key.to_bytes(),
473            &amount.to_le_bytes(),
474        ]);
475        require!(
476            merkle_proof::verify(proof, distributor.root, node.0),
477            GumdropError::InvalidProof
478        );
479
480        // This user is whitelisted to mint at most `amount` NFTs from the candy machine
481        require!(claim_count.count < amount, GumdropError::DropAlreadyClaimed);
482
483        // Mark it claimed
484        claim_count.count = claim_count
485            .count
486            .checked_add(1)
487            .ok_or(GumdropError::NumericalOverflow)?;
488
489        issue_mint_nft(
490            &distributor,
491            &ctx.accounts.distributor_wallet,
492            &ctx.accounts.payer,
493            &ctx.accounts.candy_machine_config,
494            &ctx.accounts.candy_machine,
495            &ctx.accounts.candy_machine_wallet,
496            &ctx.accounts.candy_machine_mint,
497            &ctx.accounts.candy_machine_metadata,
498            &ctx.accounts.candy_machine_master_edition,
499            &ctx.accounts.system_program,
500            &ctx.accounts.token_program,
501            &ctx.accounts.token_metadata_program,
502            &ctx.accounts.candy_machine_program,
503            &ctx.accounts.rent,
504            &ctx.accounts.clock,
505            &ctx.remaining_accounts,
506            wallet_bump,
507        )?;
508
509        // reserialize claim_count
510        {
511            let mut claim_count_data: &mut [u8] =
512                &mut ctx.accounts.claim_count.try_borrow_mut_data()?;
513            claim_count.try_serialize(&mut claim_count_data)?;
514        }
515
516        Ok(())
517    }
518
519    /// Claims NFTs by calling MintNewEditionFromMasterEditionViaToken
520    pub fn claim_edition(
521        ctx: Context<ClaimEdition>,
522        claim_bump: u8,
523        index: u64,
524        amount: u64,
525        edition: u64,
526        claimant_secret: Pubkey,
527        proof: Vec<[u8; 32]>,
528    ) -> Result<()> {
529        let distributor = &ctx.accounts.distributor;
530        let mut claim_count = get_or_create_claim_count(
531            &ctx.accounts.distributor,
532            &ctx.accounts.claim_count,
533            &ctx.accounts.temporal,
534            &ctx.accounts.payer,
535            &ctx.accounts.system_program,
536            claim_bump,
537            index,
538            claimant_secret,
539        )?;
540        require!(
541            *claim_count.to_account_info().owner == ID,
542            GumdropError::OwnerMismatch
543        );
544
545        // TODO: master_edition or something else? should we has the edition here also?
546        let node = solana_program::keccak::hashv(&[
547            &[0x00],
548            &index.to_le_bytes(),
549            &claimant_secret.to_bytes(),
550            &ctx.accounts.metadata_master_mint.key.to_bytes(),
551            &amount.to_le_bytes(),
552            &edition.to_le_bytes(),
553        ]);
554        require!(
555            merkle_proof::verify(proof, distributor.root, node.0),
556            GumdropError::InvalidProof
557        );
558
559        // This user is whitelisted to mint at most `amount` NFTs from the candy machine
560        require!(claim_count.count < amount, GumdropError::DropAlreadyClaimed);
561
562        // Mark it claimed
563        claim_count.count = claim_count
564            .count
565            .checked_add(1)
566            .ok_or(GumdropError::NumericalOverflow)?;
567
568        let seeds = [
569            b"MerkleDistributor".as_ref(),
570            &distributor.base.to_bytes(),
571            &[ctx.accounts.distributor.bump],
572        ];
573
574        let metadata_infos = [
575            ctx.accounts.token_metadata_program.clone(),
576            ctx.accounts.metadata_new_metadata.clone(),
577            ctx.accounts.metadata_new_edition.clone(),
578            ctx.accounts.metadata_master_edition.clone(),
579            ctx.accounts.metadata_new_mint.clone(),
580            ctx.accounts.metadata_edition_mark_pda.clone(),
581            ctx.accounts
582                .metadata_new_mint_authority
583                .to_account_info()
584                .clone(),
585            ctx.accounts.payer.to_account_info().clone(),
586            ctx.accounts.distributor.to_account_info().clone(),
587            ctx.accounts.metadata_master_token_account.clone(),
588            ctx.accounts.metadata_new_update_authority.clone(),
589            ctx.accounts.metadata_master_metadata.clone(),
590            ctx.accounts.metadata_master_mint.clone(),
591            ctx.accounts.rent.to_account_info().clone(),
592        ];
593
594        invoke_signed(
595            &mpl_token_metadata::instruction::mint_new_edition_from_master_edition_via_token(
596                *ctx.accounts.token_metadata_program.key,
597                *ctx.accounts.metadata_new_metadata.key,
598                *ctx.accounts.metadata_new_edition.key,
599                *ctx.accounts.metadata_master_edition.key,
600                *ctx.accounts.metadata_new_mint.key,
601                *ctx.accounts.metadata_new_mint_authority.key,
602                *ctx.accounts.payer.key,
603                ctx.accounts.distributor.key(),
604                *ctx.accounts.metadata_master_token_account.key,
605                *ctx.accounts.metadata_new_update_authority.key,
606                *ctx.accounts.metadata_master_metadata.key,
607                *ctx.accounts.metadata_master_mint.key,
608                edition,
609            ),
610            &metadata_infos,
611            &[&seeds],
612        )?;
613
614        // reserialize claim_count
615        {
616            let mut claim_count_data: &mut [u8] =
617                &mut ctx.accounts.claim_count.try_borrow_mut_data()?;
618            claim_count.try_serialize(&mut claim_count_data)?;
619        }
620
621        Ok(())
622    }
623
624    pub fn claim_candy_proven<'info>(
625        ctx: Context<'_, '_, '_, 'info, ClaimCandyProven<'info>>,
626        wallet_bump: u8,
627        _claim_bump: u8, // proof is not created
628        _index: u64,
629    ) -> Result<()> {
630        let claim_proof = &mut ctx.accounts.claim_proof;
631        let distributor = &ctx.accounts.distributor;
632
633        require!(
634            claim_proof.claimant == ctx.accounts.payer.key(),
635            GumdropError::InvalidProof,
636        );
637
638        require!(
639            claim_proof.resource == *ctx.accounts.candy_machine_config.key,
640            GumdropError::InvalidProof,
641        );
642
643        // At least 1 remaining
644        require!(
645            claim_proof.count < claim_proof.amount,
646            GumdropError::DropAlreadyClaimed,
647        );
648
649        // Mark it claimed
650        claim_proof.count = claim_proof
651            .count
652            .checked_add(1)
653            .ok_or(GumdropError::NumericalOverflow)?;
654
655        issue_mint_nft(
656            &distributor,
657            &ctx.accounts.distributor_wallet,
658            &ctx.accounts.payer,
659            &ctx.accounts.candy_machine_config,
660            &ctx.accounts.candy_machine,
661            &ctx.accounts.candy_machine_wallet,
662            &ctx.accounts.candy_machine_mint,
663            &ctx.accounts.candy_machine_metadata,
664            &ctx.accounts.candy_machine_master_edition,
665            &ctx.accounts.system_program,
666            &ctx.accounts.token_program,
667            &ctx.accounts.token_metadata_program,
668            &ctx.accounts.candy_machine_program,
669            &ctx.accounts.rent,
670            &ctx.accounts.clock,
671            &ctx.remaining_accounts,
672            wallet_bump,
673        )?;
674
675        Ok(())
676    }
677
678    pub fn recover_update_authority(
679        ctx: Context<RecoverUpdateAuthority>,
680        _bump: u8,
681        wallet_bump: u8,
682    ) -> Result<()> {
683        let wallet_seeds = [
684            b"Wallet".as_ref(),
685            &ctx.accounts.distributor.key().to_bytes(),
686            &[wallet_bump],
687        ];
688
689        invoke_signed(
690            &mpl_token_metadata::instruction::update_metadata_accounts(
691                *ctx.accounts.token_metadata_program.key,
692                *ctx.accounts.metadata.key,
693                *ctx.accounts.distributor_wallet.key,
694                Some(*ctx.accounts.new_update_authority.key),
695                None,
696                None,
697            ),
698            &[
699                ctx.accounts.token_metadata_program.to_account_info(),
700                ctx.accounts.metadata.to_account_info(),
701                ctx.accounts.distributor_wallet.to_account_info(),
702            ],
703            &[&wallet_seeds],
704        )?;
705
706        Ok(())
707    }
708}
709
710fn issue_mint_nft<'info>(
711    distributor: &Account<'info, MerkleDistributor>,
712    distributor_wallet: &AccountInfo<'info>,
713    payer: &Signer<'info>,
714    candy_machine_config: &AccountInfo<'info>,
715    candy_machine: &AccountInfo<'info>,
716    candy_machine_wallet: &AccountInfo<'info>,
717    candy_machine_mint: &AccountInfo<'info>,
718    candy_machine_metadata: &AccountInfo<'info>,
719    candy_machine_master_edition: &AccountInfo<'info>,
720    system_program: &Program<'info, System>,
721    token_program: &Program<'info, Token>,
722    token_metadata_program: &AccountInfo<'info>,
723    candy_machine_program: &AccountInfo<'info>,
724    rent: &Sysvar<'info, Rent>,
725    clock: &Sysvar<'info, Clock>,
726    claim_remaining_accounts: &[AccountInfo<'info>],
727    wallet_bump: u8,
728) -> Result<()> {
729    // Transfer the required SOL from the payer
730    let required_lamports;
731    let remaining_accounts;
732    {
733        let rent = &Rent::get()?;
734        let mut candy_machine_data: &[u8] = &candy_machine.try_borrow_data()?;
735        verify_candy(candy_machine_program.key)?;
736        let candy_machine = CandyMachine::try_deserialize(&mut candy_machine_data)?;
737        let required_rent = rent.minimum_balance(mpl_token_metadata::state::MAX_METADATA_LEN)
738            + rent.minimum_balance(mpl_token_metadata::state::MAX_MASTER_EDITION_LEN);
739
740        if candy_machine.token_mint.is_some() {
741            required_lamports = required_rent;
742
743            // checked by candy machine
744            let token_account_info = &claim_remaining_accounts[0];
745            let transfer_authority_info = &claim_remaining_accounts[1];
746            remaining_accounts = vec![token_account_info.clone(), transfer_authority_info.clone()];
747        } else {
748            required_lamports = candy_machine.data.price + required_rent;
749            remaining_accounts = vec![];
750        }
751    }
752    msg!(
753        "Transferring {} lamports to distributor wallet for candy machine mint",
754        required_lamports,
755    );
756    invoke(
757        &system_instruction::transfer(payer.key, distributor_wallet.key, required_lamports),
758        &[
759            payer.to_account_info().clone(),
760            distributor_wallet.clone(),
761            system_program.to_account_info().clone(),
762        ],
763    )?;
764
765    let wallet_seeds = [
766        b"Wallet".as_ref(),
767        &distributor.key().to_bytes(),
768        &[wallet_bump],
769    ];
770    let mut account_metas = vec![
771        AccountMeta::new_readonly(candy_machine_config.key(), false),
772        AccountMeta::new(candy_machine.key(), false),
773        AccountMeta::new(distributor_wallet.key(), true),
774        AccountMeta::new(candy_machine_wallet.key(), false),
775        AccountMeta::new(candy_machine_metadata.key(), false),
776        AccountMeta::new(candy_machine_mint.key(), false),
777        AccountMeta::new_readonly(payer.key(), true),
778        AccountMeta::new_readonly(payer.key(), true),
779        AccountMeta::new(candy_machine_master_edition.key(), false),
780        AccountMeta::new_readonly(token_metadata_program.key(), false),
781        AccountMeta::new_readonly(token_program.key(), false),
782        AccountMeta::new_readonly(system_program.key(), false),
783        AccountMeta::new_readonly(sysvar::rent::id(), false),
784        AccountMeta::new_readonly(sysvar::clock::id(), false),
785    ];
786    for a in &remaining_accounts {
787        account_metas.push(AccountMeta::new(a.key(), false));
788    }
789    let mut candy_machine_infos = vec![
790        candy_machine_config.clone(),
791        candy_machine.to_account_info().clone(),
792        distributor_wallet.clone(),
793        candy_machine_wallet.clone(),
794        candy_machine_metadata.clone(),
795        candy_machine_mint.clone(),
796        payer.to_account_info().clone(),
797        candy_machine_master_edition.clone(),
798        token_metadata_program.clone(),
799        token_program.to_account_info().clone(),
800        system_program.to_account_info().clone(),
801        rent.to_account_info().clone(),
802        clock.to_account_info().clone(),
803    ];
804    candy_machine_infos.extend(remaining_accounts);
805
806    invoke_signed(
807        &Instruction {
808            program_id: candy_machine_program.key(),
809            accounts: account_metas,
810            // TODO. global::mint_nft instruction...
811            data: vec![0xd3, 0x39, 0x06, 0xa7, 0x0f, 0xdb, 0x23, 0xfb],
812        },
813        &candy_machine_infos,
814        &[&wallet_seeds],
815    )?;
816
817    // point back to the gumdrop authority
818    let mut cm_config_data: &[u8] = &candy_machine_config.try_borrow_data()?;
819    let cm_config = Config::try_deserialize(&mut cm_config_data)?;
820    if cm_config.data.retain_authority {
821        invoke_signed(
822            &mpl_token_metadata::instruction::update_metadata_accounts(
823                *token_metadata_program.key,
824                *candy_machine_metadata.key,
825                *distributor_wallet.key,
826                Some(distributor.base),
827                None,
828                None,
829            ),
830            &[
831                token_metadata_program.to_account_info(),
832                candy_machine_metadata.to_account_info(),
833                distributor_wallet.to_account_info(),
834            ],
835            &[&wallet_seeds],
836        )?;
837    }
838
839    Ok(())
840}
841
842/// Accounts for [gumdrop::new_distributor].
843#[derive(Accounts)]
844#[instruction(bump: u8)]
845pub struct NewDistributor<'info> {
846    /// Base key of the distributor.
847    pub base: Signer<'info>,
848
849    /// [MerkleDistributor].
850    #[account(
851    init,
852    seeds = [
853    b"MerkleDistributor".as_ref(),
854    base.key().to_bytes().as_ref()
855    ],
856    space = 8+97,
857    bump,
858    payer = payer
859    )]
860    pub distributor: Account<'info, MerkleDistributor>,
861
862    /// Payer to create the distributor.
863    #[account(mut)]
864    pub payer: Signer<'info>,
865
866    /// The [System] program.
867    pub system_program: Program<'info, System>,
868}
869
870/// [gumdrop::close_distributor_token_acconut] accounts.
871#[derive(Accounts)]
872#[instruction(_bump: u8)]
873pub struct CloseDistributorTokenAccount<'info> {
874    /// Base key of the distributor.
875    pub base: Signer<'info>,
876
877    /// [MerkleDistributor].
878    #[account(
879    seeds = [
880    b"MerkleDistributor".as_ref(),
881    base.key().to_bytes().as_ref()
882    ],
883    bump = _bump,
884    )]
885    pub distributor: Account<'info, MerkleDistributor>,
886
887    /// Distributor containing the tokens to distribute.
888    #[account(mut)]
889    pub from: Account<'info, TokenAccount>,
890
891    /// Account to send the claimed tokens to.
892    #[account(mut)]
893    pub to: Account<'info, TokenAccount>,
894
895    /// Who is receiving the remaining rent allocation.
896    #[account(mut)]
897    /// CHECK: just a destination
898    pub receiver: AccountInfo<'info>,
899
900    /// The [System] program.
901    pub system_program: Program<'info, System>,
902
903    /// SPL [Token] program.
904    pub token_program: Program<'info, Token>,
905}
906
907/// [gumdrop::close_distributor] accounts.
908#[derive(Accounts)]
909#[instruction(_bump: u8, _wallet_bump: u8)]
910pub struct CloseDistributor<'info> {
911    /// Base key of the distributor.
912    pub base: Signer<'info>,
913
914    /// [MerkleDistributor].
915    #[account(
916    seeds = [
917    b"MerkleDistributor".as_ref(),
918    base.key().to_bytes().as_ref()
919    ],
920    bump = _bump,
921    mut,
922    close = receiver,
923    )]
924    pub distributor: Account<'info, MerkleDistributor>,
925
926    #[account(
927    seeds = [
928    b"Wallet".as_ref(),
929    distributor.key().to_bytes().as_ref()
930    ],
931    bump = _wallet_bump,
932    mut,
933    )]
934    /// CHECK: PDA Checked
935    pub distributor_wallet: AccountInfo<'info>,
936
937    /// Who is receiving the remaining tokens and rent allocations.
938    /// CHECK: just a destination
939    pub receiver: AccountInfo<'info>,
940
941    /// The [System] program.
942    pub system_program: Program<'info, System>,
943
944    /// SPL [Token] program.
945    pub token_program: Program<'info, Token>,
946}
947
948/// [gumdrop::prove_claim] accounts.
949#[derive(Accounts)]
950#[instruction(
951claim_prefix: Vec < u8 >,
952claim_bump: u8,
953index: u64,
954_amount: u64,
955_claimant_secret: Pubkey,
956_resource: Pubkey,
957resource_nonce: Vec < u8 >,
958)]
959pub struct ProveClaim<'info> {
960    /// The [MerkleDistributor].
961    #[account(mut)]
962    pub distributor: Account<'info, MerkleDistributor>,
963
964    /// Status of the claim.
965    #[account(
966    init,
967    seeds = [
968    claim_prefix.as_slice(),
969    index.to_le_bytes().as_ref(),
970    distributor.key().to_bytes().as_ref()
971    ],
972    bump,
973    payer = payer,
974    space = 8 // discriminator
975    + 8   // amount
976    + 8   // count
977    + 32  // claimant
978    + 32  // resource
979    + 4 + resource_nonce.len() // resource_nonce vec
980    )]
981    pub claim_proof: Account<'info, ClaimProof>,
982
983    /// Extra signer expected for claims
984    pub temporal: Signer<'info>,
985
986    /// Payer of the claim.
987    #[account(mut)]
988    pub payer: Signer<'info>,
989
990    /// The [System] program.
991    pub system_program: Program<'info, System>,
992}
993
994/// [gumdrop::claim] accounts.
995#[derive(Accounts)]
996#[instruction(claim_bump: u8, index: u64)]
997pub struct Claim<'info> {
998    /// The [MerkleDistributor].
999    #[account(mut)]
1000    pub distributor: Account<'info, MerkleDistributor>,
1001
1002    /// Status of the claim.
1003    #[account(
1004    init,
1005    seeds = [
1006    CLAIM_STATUS.as_ref(),
1007    index.to_le_bytes().as_ref(),
1008    distributor.key().to_bytes().as_ref()
1009    ],
1010    space = 8+49,
1011    bump,
1012    payer = payer
1013    )]
1014    pub claim_status: Account<'info, ClaimStatus>,
1015
1016    /// Distributor containing the tokens to distribute.
1017    #[account(mut)]
1018    pub from: Account<'info, TokenAccount>,
1019
1020    /// Account to send the claimed tokens to.
1021    #[account(mut)]
1022    pub to: Account<'info, TokenAccount>,
1023
1024    /// Extra signer expected for claims
1025    pub temporal: Signer<'info>,
1026
1027    /// Payer of the claim.
1028    #[account(mut)]
1029    pub payer: Signer<'info>,
1030
1031    /// The [System] program.
1032    pub system_program: Program<'info, System>,
1033
1034    /// SPL [Token] program.
1035    pub token_program: Program<'info, Token>,
1036}
1037
1038/// [gumdrop::claim_candy] accounts.
1039#[derive(Accounts)]
1040#[instruction(_wallet_bump: u8, claim_bump: u8, index: u64)]
1041pub struct ClaimCandy<'info> {
1042    /// The [MerkleDistributor].
1043    #[account(mut)]
1044    pub distributor: Account<'info, MerkleDistributor>,
1045
1046    /// The [MerkleDistributor] wallet
1047    #[account(
1048    seeds = [
1049    b"Wallet".as_ref(),
1050    distributor.key().to_bytes().as_ref()
1051    ],
1052    bump = _wallet_bump,
1053    mut
1054    )]
1055    /// CHECK: PDA check enforced
1056    pub distributor_wallet: AccountInfo<'info>,
1057
1058    /// Status of the claim. Created on first invocation of this function
1059    #[account(
1060    seeds = [
1061    CLAIM_COUNT.as_ref(),
1062    index.to_le_bytes().as_ref(),
1063    distributor.key().to_bytes().as_ref()
1064    ],
1065    bump = claim_bump,
1066    mut,
1067    )]
1068    /// CHECK: PDA check enforced
1069    pub claim_count: AccountInfo<'info>,
1070
1071    /// Extra signer expected for claims
1072    pub temporal: Signer<'info>,
1073
1074    /// Payer of the claim. Should be `mint_authority` for `candy_machine_mint` and will be
1075    /// `update_authority` for `candy_machine_metadata`
1076    pub payer: Signer<'info>,
1077
1078    /// Candy-machine Config
1079    /// CHECK: PDA check enforced in cpi
1080    pub candy_machine_config: AccountInfo<'info>,
1081
1082    /// Candy-Machine. Verified through CPI
1083    #[account(mut)]
1084    /// CHECK: PDA check enforced in cpi
1085    pub candy_machine: AccountInfo<'info>,
1086
1087    /// Candy-Machine-Wallet. Verified through CPI
1088    #[account(mut)]
1089    /// CHECK: PDA check enforced in cpi
1090    pub candy_machine_wallet: AccountInfo<'info>,
1091
1092    /// Generated mint
1093    #[account(mut)]
1094    /// CHECK: PDA check enforced in cpi
1095    pub candy_machine_mint: AccountInfo<'info>,
1096
1097    /// PDA of `candy_machine_mint`
1098    #[account(mut)]
1099    /// CHECK: PDA check enforced in cpi
1100    pub candy_machine_metadata: AccountInfo<'info>,
1101
1102    /// PDA of `candy_machine_mint`
1103    #[account(mut)]
1104    /// CHECK: PDA check enforced in cpi
1105    pub candy_machine_master_edition: AccountInfo<'info>,
1106
1107    /// The [System] program.
1108    pub system_program: Program<'info, System>,
1109
1110    /// SPL [Token] program.
1111    pub token_program: Program<'info, Token>,
1112
1113    /// SPL [TokenMetadata] program.
1114    #[account(address = mpl_token_metadata::id())]
1115    /// CHECK: Address Checked
1116    pub token_metadata_program: AccountInfo<'info>,
1117
1118    /// SPL [CandyMachine] program.
1119    /// CHECK: account checked in handler
1120    pub candy_machine_program: AccountInfo<'info>,
1121
1122    rent: Sysvar<'info, Rent>,
1123    clock: Sysvar<'info, Clock>,
1124}
1125
1126/// [gumdrop::claim_edition] accounts. Wrapper around
1127/// MintNewEditionFromMasterEditionViaToken
1128#[derive(Accounts)]
1129#[instruction(claim_bump: u8, index: u64)]
1130pub struct ClaimEdition<'info> {
1131    /// Given a token account containing the master edition token to prove authority, and a brand new non-metadata-ed mint with one token
1132    /// make a new Metadata + Edition that is a child of the master edition denoted by this authority token.
1133    ///   4. `[writable]` Edition pda to mark creation - will be checked for pre-existence. (pda of ['metadata', program id, master metadata mint id, 'edition', edition_number])
1134    ///   where edition_number is NOT the edition number you pass in args but actually edition_number = floor(edition/EDITION_MARKER_BIT_SIZE).
1135    ///   8. `[]` token account containing token from master metadata mint
1136
1137    /// The [MerkleDistributor].
1138    #[account(mut)]
1139    pub distributor: Account<'info, MerkleDistributor>,
1140
1141    /// Status of the claim. Created on first invocation of this function
1142    #[account(
1143    seeds = [
1144    CLAIM_COUNT.as_ref(),
1145    index.to_le_bytes().as_ref(),
1146    distributor.key().to_bytes().as_ref()
1147    ],
1148    bump = claim_bump,
1149    mut,
1150    )]
1151    /// CHECK: PDA Check enforced, and hashcheck
1152    pub claim_count: AccountInfo<'info>,
1153
1154    /// Extra signer expected for claims
1155    pub temporal: Signer<'info>,
1156
1157    /// Payer of the claim. Should be `mint_authority` for `candy_machine_mint` and will be
1158    /// `update_authority` for `candy_machine_metadata`
1159    pub payer: Signer<'info>,
1160
1161    /// PDA of `metadata_new_mint`
1162    #[account(mut)]
1163    /// CHECK: Checked via CPI
1164    pub metadata_new_metadata: AccountInfo<'info>,
1165
1166    /// PDA of `metadata_new_mint`
1167    #[account(mut)]
1168    /// CHECK: Checked via CPI
1169    pub metadata_new_edition: AccountInfo<'info>,
1170
1171    /// PDA of `metadata_master_mint`
1172    #[account(mut)]
1173    /// CHECK: Checked via CPI
1174    pub metadata_master_edition: AccountInfo<'info>,
1175
1176    /// Generated mint
1177    #[account(mut)]
1178    /// CHECK: Checked via CPI
1179    pub metadata_new_mint: AccountInfo<'info>,
1180
1181    /// PDA of `metadata_master_mint` and edition number
1182    #[account(mut)]
1183    /// CHECK: Checked via CPI
1184    pub metadata_edition_mark_pda: AccountInfo<'info>,
1185
1186    /// Mint authority for `metadata_new_mint`
1187    pub metadata_new_mint_authority: Signer<'info>,
1188
1189    /// Owner of token account containing master token (#8)
1190    /// distributor
1191
1192    /// Token account
1193    /// CHECK: Checked via CPI
1194    pub metadata_master_token_account: AccountInfo<'info>,
1195
1196    /// Update authority for new metadata
1197    /// CHECK: Checked via CPI
1198    pub metadata_new_update_authority: AccountInfo<'info>,
1199
1200    /// Master record metadata account
1201    /// CHECK: Checked via CPI
1202    pub metadata_master_metadata: AccountInfo<'info>,
1203
1204    /// Master metadata mint account
1205    /// CHECK: Checked via CPI
1206    pub metadata_master_mint: AccountInfo<'info>,
1207
1208    /// The [System] program.
1209    pub system_program: Program<'info, System>,
1210
1211    /// SPL [Token] program.
1212    pub token_program: Program<'info, Token>,
1213
1214    /// SPL [TokenMetadata] program.
1215    #[account(address = mpl_token_metadata::id())]
1216    /// CHECK: Address Check
1217    pub token_metadata_program: AccountInfo<'info>,
1218
1219    rent: Sysvar<'info, Rent>,
1220}
1221
1222/// [gumdrop::claim_candy_proven] accounts.
1223#[derive(Accounts)]
1224#[instruction(wallet_bump: u8, claim_bump: u8, index: u64)]
1225pub struct ClaimCandyProven<'info> {
1226    /// The [MerkleDistributor].
1227    #[account(mut)]
1228    pub distributor: Account<'info, MerkleDistributor>,
1229
1230    /// The [MerkleDistributor] wallet
1231    #[account(
1232    seeds = [
1233    b"Wallet".as_ref(),
1234    distributor.key().to_bytes().as_ref()
1235    ],
1236    bump = wallet_bump,
1237    mut
1238    )]
1239    /// CHECK: PDA checked
1240    pub distributor_wallet: AccountInfo<'info>,
1241
1242    /// Status of the claim. Created with prove_claim
1243    #[account(
1244    seeds = [
1245    CLAIM_COUNT.as_ref(),
1246    index.to_le_bytes().as_ref(),
1247    distributor.key().to_bytes().as_ref()
1248    ],
1249    bump = claim_bump,
1250    mut,
1251    )]
1252    pub claim_proof: Account<'info, ClaimProof>,
1253
1254    /// Payer of the claim. Should be `mint_authority` for `candy_machine_mint` and will be
1255    /// `update_authority` for `candy_machine_metadata`
1256    pub payer: Signer<'info>,
1257
1258    /// Candy-machine Config
1259    /// CHECK: Checked via CPI
1260    pub candy_machine_config: AccountInfo<'info>,
1261
1262    /// Candy-Machine. Verified through CPI
1263    #[account(mut)]
1264    /// CHECK: Checked via CPI
1265    pub candy_machine: AccountInfo<'info>,
1266
1267    /// Candy-Machine-Wallet. Verified through CPI
1268    #[account(mut)]
1269    /// CHECK: Checked via CPI
1270    pub candy_machine_wallet: AccountInfo<'info>,
1271
1272    /// Generated mint
1273    #[account(mut)]
1274    /// CHECK: Checked via CPI
1275    pub candy_machine_mint: AccountInfo<'info>,
1276
1277    /// PDA of `candy_machine_mint`
1278    #[account(mut)]
1279    /// CHECK: Checked via CPI
1280    pub candy_machine_metadata: AccountInfo<'info>,
1281
1282    /// PDA of `candy_machine_mint`
1283    #[account(mut)]
1284    /// CHECK: Checked via CPI
1285    pub candy_machine_master_edition: AccountInfo<'info>,
1286
1287    /// The [System] program.
1288    pub system_program: Program<'info, System>,
1289
1290    /// SPL [Token] program.
1291    pub token_program: Program<'info, Token>,
1292
1293    /// SPL [TokenMetadata] program.
1294    #[account(address = mpl_token_metadata::id())]
1295    /// CHECK:
1296    pub token_metadata_program: AccountInfo<'info>,
1297
1298    /// SPL [CandyMachine] program.
1299    /// CHECK: account checked in handler
1300    pub candy_machine_program: AccountInfo<'info>,
1301
1302    rent: Sysvar<'info, Rent>,
1303    clock: Sysvar<'info, Clock>,
1304}
1305
1306/// [gumdrop::recover_update_authority] accounts.
1307#[derive(Accounts)]
1308#[instruction(_bump: u8, wallet_bump: u8)]
1309pub struct RecoverUpdateAuthority<'info> {
1310    /// Base key of the distributor.
1311    pub base: Signer<'info>,
1312
1313    /// [MerkleDistributor].
1314    #[account(
1315    seeds = [
1316    b"MerkleDistributor".as_ref(),
1317    base.key().to_bytes().as_ref()
1318    ],
1319    bump = _bump,
1320    )]
1321    pub distributor: Account<'info, MerkleDistributor>,
1322
1323    /// The [MerkleDistributor] wallet
1324    #[account(
1325    seeds = [
1326    b"Wallet".as_ref(),
1327    distributor.key().to_bytes().as_ref()
1328    ],
1329    bump = wallet_bump,
1330    )]
1331    /// CHECK: Checked via PDA seed and base sign
1332    pub distributor_wallet: AccountInfo<'info>,
1333
1334    /// New update authority
1335    /// CHECK: No need to check this is input
1336    pub new_update_authority: AccountInfo<'info>,
1337
1338    /// Metadata account to update authority for
1339    #[account(mut)]
1340    /// CHECK: Checked via CPI
1341    pub metadata: AccountInfo<'info>,
1342
1343    /// The [System] program.
1344    pub system_program: Program<'info, System>,
1345
1346    /// SPL [TokenMetadata] program.
1347    #[account(address = mpl_token_metadata::id())]
1348    /// CHECK: Address Checked
1349    pub token_metadata_program: AccountInfo<'info>,
1350}
1351
1352/// State for the account which distributes tokens.
1353#[account]
1354#[derive(Default)]
1355pub struct MerkleDistributor {
1356    /// Base key used to generate the PDA.
1357    pub base: Pubkey,
1358    /// Bump seed.
1359    pub bump: u8,
1360
1361    /// The 256-bit merkle root.
1362    pub root: [u8; 32],
1363
1364    /// Third-party signer expected on claims. Verified by OTP with off-chain distribution method
1365    pub temporal: Pubkey,
1366}
1367
1368#[account]
1369#[derive(Default)]
1370pub struct ClaimStatus {
1371    /// If true, the tokens have been claimed.
1372    pub is_claimed: bool,
1373    /// Authority that claimed the tokens.
1374    pub claimant: Pubkey,
1375    /// When the tokens were claimed.
1376    pub claimed_at: i64,
1377    /// Amount of tokens claimed.
1378    pub amount: u64,
1379}
1380
1381#[account]
1382#[derive(Default)]
1383pub struct ClaimCount {
1384    /// Number of NFTs claimed. Compared versus `amount` in merkle tree data / proof
1385    pub count: u64,
1386    /// Authority that claimed the tokens.
1387    pub claimant: Pubkey,
1388}
1389
1390/// Allows for proof and candy minting in separate transactions to avoid transaction-size limit.
1391///
1392/// Used for all resources (tokens, candy claims, and edition mints)
1393#[account]
1394#[derive(Default)]
1395pub struct ClaimProof {
1396    /// Total number of NFTs that can be claimed
1397    pub amount: u64,
1398    /// Number of NFTs claimed. Compared versus `amount` in merkle tree data / proof
1399    pub count: u64,
1400    /// Authority that claimed the tokens.
1401    pub claimant: Pubkey,
1402    /// Resource allocated for this gumdrop. There should only be 1 per gumdrop
1403    pub resource: Pubkey,
1404    pub resource_nonce: Vec<u8>,
1405}
1406
1407/// Emitted when tokens are claimed.
1408#[event]
1409pub struct ClaimedEvent {
1410    /// Index of the claim.
1411    pub index: u64,
1412    /// User that claimed.
1413    pub claimant: Pubkey,
1414    /// Amount of tokens to distribute.
1415    pub amount: u64,
1416}
1417
1418#[error_code]
1419pub enum GumdropError {
1420    #[msg("Invalid Merkle proof.")]
1421    InvalidProof,
1422    #[msg("Drop already claimed.")]
1423    DropAlreadyClaimed,
1424    #[msg("Account is not authorized to execute this instruction")]
1425    Unauthorized,
1426    #[msg("Token account owner did not match intended owner")]
1427    OwnerMismatch,
1428    #[msg("Temporal signer did not match distributor")]
1429    TemporalMismatch,
1430    #[msg("Numerical Overflow")]
1431    NumericalOverflow,
1432    #[msg("Invalid Claim Bump")]
1433    InvalidClaimBump,
1434    #[msg("Gumdrop only supports the official Metaplex Candy machine contracts")]
1435    MustUseOfficialCandyMachine,
1436    #[msg("Bump seed not in hash map")]
1437    BumpSeedNotInHashMap,
1438}
1439
1440#[account]
1441#[derive(Default)]
1442pub struct CandyMachine {
1443    pub authority: Pubkey,
1444    pub wallet: Pubkey,
1445    pub token_mint: Option<Pubkey>,
1446    pub config: Pubkey,
1447    pub data: CandyMachineData,
1448    pub items_redeemed: u64,
1449    pub bump: u8,
1450}
1451
1452#[account]
1453#[derive(Default)]
1454pub struct Config {
1455    pub authority: Pubkey,
1456    pub data: ConfigData,
1457}
1458
1459#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]
1460pub struct ConfigData {
1461    pub uuid: String,
1462    /// The symbol for the asset
1463    pub symbol: String,
1464    /// Royalty basis points that goes to creators in secondary sales (0-10000)
1465    pub seller_fee_basis_points: u16,
1466    pub creators: Vec<Creator>,
1467    pub max_supply: u64,
1468    pub is_mutable: bool,
1469    pub retain_authority: bool,
1470    pub max_number_of_lines: u32,
1471}
1472
1473#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
1474pub struct Creator {
1475    pub address: Pubkey,
1476    pub verified: bool,
1477    // In percentages, NOT basis points ;) Watch out!
1478    pub share: u8,
1479}
1480
1481#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]
1482pub struct CandyMachineData {
1483    pub uuid: String,
1484    pub price: u64,
1485    pub items_available: u64,
1486    pub go_live_date: Option<i64>,
1487}