1//! Placement: mints bonds to a registered holder's associated token account, before the first record moment and up to
2//! the bond's issue size. Updates the holder's quantity and the bond's supply, which the program alone changes.
3
4use anchor_lang::prelude::*;
5use anchor_spl::token_2022::{self, MintTo, Token2022};
6use anchor_spl::token_interface::{Mint, TokenAccount};
7
8use crate::constants::*;
9use crate::error::BondError;
10use crate::events::BondsPlaced;
11use crate::state::{Bond, HolderRecord};
12
13#[derive(Accounts)]
14pub struct Place<'info> {
15 pub registrar: Signer<'info>,
16
17 #[account(mut, has_one = registrar, has_one = mint)]
18 pub bond: Box<Account<'info, Bond>>,
19
20 #[account(mut)]
21 pub mint: Box<InterfaceAccount<'info, Mint>>,
22
23 #[account(mut, has_one = bond, seeds = [HOLDER_SEED, mint.key().as_ref(), holder.owner.as_ref()], bump = holder.bump)]
24 pub holder: Box<Account<'info, HolderRecord>>,
25
26 #[account(
27 mut,
28 associated_token::mint = mint,
29 associated_token::authority = holder.owner,
30 associated_token::token_program = token_program,
31 )]
32 pub holder_bonds: Box<InterfaceAccount<'info, TokenAccount>>,
33
34 pub token_program: Program<'info, Token2022>,
35}
36
37pub fn handle_place(ctx: Context<Place>, amount: u64) -> Result<()> {
38 let bond = &ctx.accounts.bond;
39 require!(bond.now()? < bond.record_time(0)?, BondError::PlacementClosed);
40 let new_supply = bond.supply.quantity.checked_add(amount).ok_or(BondError::IssueTooLarge)?;
41 require!(new_supply <= bond.max_supply, BondError::ExceedsIssueSize);
42 bond.check_serviceable(new_supply)?;
43 let mint_key = bond.mint;
44 let seeds: &[&[u8]] = &[BOND_SEED, mint_key.as_ref(), &[bond.bump]];
45 token_2022::mint_to(
46 CpiContext::new_with_signer(
47 ctx.accounts.token_program.key(),
48 MintTo {
49 mint: ctx.accounts.mint.to_account_info(),
50 to: ctx.accounts.holder_bonds.to_account_info(),
51 authority: ctx.accounts.bond.to_account_info(),
52 },
53 &[seeds],
54 ),
55 amount,
56 )?;
57
58 // Before the first record moment no history needs saving.
59 let holder = &mut ctx.accounts.holder;
60 holder.position.quantity = holder.position.quantity.checked_add(amount).ok_or(BondError::Overflow)?;
61 let bond = &mut ctx.accounts.bond;
62 bond.supply.quantity = bond.supply.quantity.checked_add(amount).ok_or(BondError::Overflow)?;
63 emit!(BondsPlaced { bond: bond.key(), owner: holder.owner, amount });
64 Ok(())
65}