1//! Open a coupon (index < periods) or the redemption (index = periods) once its record moment has passed.
2//! Anyone may call it: everything it writes follows from the terms and the bond's supply history.
3//! - supply at the record moment: from the supply history, no holder count needed;
4//! - budget: the event's amount for the whole supply, rounded down once (each holder's amount is rounded
5//! down on its own, so the holders' amounts never exceed it);
6//! - vault: a cash account of this event alone, owned by the Bond PDA, so events cannot spend each other's
7//! cash.
8
9use anchor_lang::prelude::*;
10use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};
11
12use crate::constants::*;
13use crate::error::BondError;
14use crate::events::EventOpened;
15use crate::state::{Bond, Event, EventKind};
16
17#[derive(Accounts)]
18#[instruction(index: u16)]
19pub struct OpenEvent<'info> {
20 #[account(mut)]
21 pub payer: Signer<'info>,
22
23 #[account(has_one = cash_mint)]
24 pub bond: Box<Account<'info, Bond>>,
25
26 #[account(
27 init,
28 payer = payer,
29 space = 8 + Event::INIT_SPACE,
30 seeds = [EVENT_SEED, bond.key().as_ref(), &index.to_le_bytes()],
31 bump,
32 )]
33 pub event: Box<Account<'info, Event>>,
34
35 pub cash_mint: Box<InterfaceAccount<'info, Mint>>,
36
37 #[account(
38 init,
39 payer = payer,
40 seeds = [VAULT_SEED, event.key().as_ref()],
41 bump,
42 token::mint = cash_mint,
43 token::authority = bond,
44 token::token_program = cash_token_program,
45 )]
46 pub vault: Box<InterfaceAccount<'info, TokenAccount>>,
47
48 pub cash_token_program: Interface<'info, TokenInterface>,
49 pub system_program: Program<'info, System>,
50}
51
52pub fn handle_open_event(ctx: Context<OpenEvent>, index: u16) -> Result<()> {
53 let bond = &ctx.accounts.bond;
54 let period = bond.period_of(index)?;
55 let record_time = bond.record_time(index)?;
56 require!(bond.now()? >= record_time, BondError::RecordMomentNotReached);
57
58 let is_redemption = index == bond.redemption_event();
59 let supply = bond.supply.at_moment(period);
60 let budget = if is_redemption { bond.principal(supply)? } else { bond.coupon(supply, period)? };
61
62 let event = &mut ctx.accounts.event;
63 event.bond = bond.key();
64 event.index = index;
65 event.kind = if is_redemption { EventKind::Redemption } else { EventKind::Coupon };
66 event.record_time = record_time;
67 event.payment_time = bond.payment_time(index)?;
68 event.supply_at_record = supply;
69 event.budget = budget;
70 event.vault = ctx.accounts.vault.key();
71 event.funded = false;
72 event.bump = ctx.bumps.event;
73 emit!(EventOpened { bond: bond.key(), event: index, supply_at_record: supply, budget, vault: event.vault });
74 Ok(())
75}