Program
G7ccWHbdA4iqGXzBND8CkYTPpK9SqPt4AqgTk5h96A9Q · Solana mainnet · Anchor 0.31 · 506 lines
The source the deployed program was built from. A verified build is not published yet, and the program is still upgradeable by its deploy key until we freeze it. Check on Solscan ↗
Instructions
- launchthe launcher
- pledgethe dev
- releasethe dev
- claimthe dev
- burnanyone
- collect_curveanyone
- collect_ammanyone
Accounts, in order
- launch(name, symbol, uri)
- pact (PDA "pact", mint) · dev, signer · mint, signer · pump mint authority · bonding curve · curve ATA · pump global · metaplex · metadata · system · token · associated token · rent · pump event authority · pump
- pledge(amount)
- pact · dev, signer · mint · dev ATA · vault (ATA of the pact) · token · associated token · system
- release()
- pact · dev, signer · mint · dev ATA · vault · token
- claim()
- pact · dev, signer
- burn()
- pact · incinerator 1nc1nerator11111111111111111111111111111111
- collect_curve()
- pact · pump creator vault (PDA "creator-vault", pact) · system · pump event authority · pump
- collect_amm()
- pact · payer, signer · pact WSOL ATA · PumpSwap vault authority (PDA "creator_vault", pact) · its WSOL ATA · WSOL mint · token · associated token · system · PumpSwap event authority · PumpSwap
Instruction data is Anchor's: the first 8 bytes of sha256("global:<name>"), then the arguments; strings are u32 length plus bytes, amounts u64 little-endian. Events are "Program data:" lines with sha256("event:<Name>") as the first 8 bytes.
programs/holvo/src/lib.rs · 506 lines · scrollsraw ↗
//! HOLVO - the dev earns the coin's fees only while the dev's bag stays in the program.//!//! A coin launched here has the `pact` PDA as its pump.fun creator, so its creator fees land on the//! pact: `collect_curve` on the bonding curve, `collect_amm` after graduation (PumpSwap pays in//! wrapped SOL; the pact signs to unwrap it). Anyone can call both, and both leave a receipt.//!//! The dev locks tokens in the pact's vault with `pledge`. While the pledge is in, `claim` pays the//! collected fees to the dev. `release` hands the whole bag back to the dev and forfeits the fees for//! good: from then on `burn` sends every lamport the pact collects to the incinerator, an address//! nobody can spend from. The vault has no other way out and the fees have no other destination.//!//! No fee to us and no admin instruction. The program is upgradeable by its deploy key until frozen. use anchor_lang::prelude::*;use anchor_lang::solana_program::instruction::{AccountMeta, Instruction};use anchor_lang::solana_program::program::{invoke, invoke_signed}; declare_id!("G7ccWHbdA4iqGXzBND8CkYTPpK9SqPt4AqgTk5h96A9Q"); pub const PUMP_PROGRAM_ID: Pubkey = anchor_lang::pubkey!("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P");pub const PUMP_AMM_PROGRAM_ID: Pubkey = anchor_lang::pubkey!("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA");pub const TOKEN_PROGRAM_ID: Pubkey = anchor_lang::pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");pub const ATA_PROGRAM_ID: Pubkey = anchor_lang::pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");pub const WSOL_MINT: Pubkey = anchor_lang::pubkey!("So11111111111111111111111111111111111111112");pub const INCINERATOR: Pubkey = anchor_lang::pubkey!("1nc1nerator11111111111111111111111111111111");pub const PUMP_CREATE_DISC: [u8; 8] = [24, 30, 200, 40, 5, 28, 7, 119];pub const PUMP_COLLECT_DISC: [u8; 8] = [20, 22, 86, 123, 198, 28, 219, 132];pub const AMM_COLLECT_DISC: [u8; 8] = [160, 57, 89, 42, 181, 139, 43, 66];pub const TOKEN_ACCOUNT_LEN: usize = 165; pub const PACT_SEED: &[u8] = b"pact";pub const MAX_NAME: usize = 32;pub const MAX_SYMBOL: usize = 10;pub const MAX_URI: usize = 200; pub const SOURCE_CURVE: u8 = 0;pub const SOURCE_AMM: u8 = 1; #[program]pub mod holvo { use super::*; /// Create the coin on pump.fun with the pact PDA as its creator. The signer becomes the dev. pub fn launch(ctx: Context<Launch>, name: String, symbol: String, uri: String) -> Result<()> { require!(!name.is_empty() && name.len() <= MAX_NAME, HolvoError::BadText); require!(!symbol.is_empty() && symbol.len() <= MAX_SYMBOL, HolvoError::BadText); require!(!uri.is_empty() && uri.len() <= MAX_URI, HolvoError::BadText); let a = &ctx.accounts; require!(a.mint.is_signer, HolvoError::MintMustSign); let pact_key = a.pact.key(); let mut data = Vec::with_capacity(8 + 4 + MAX_NAME + 4 + MAX_SYMBOL + 4 + MAX_URI + 32); data.extend_from_slice(&PUMP_CREATE_DISC); put_str(&mut data, &name); put_str(&mut data, &symbol); put_str(&mut data, &uri); data.extend_from_slice(pact_key.as_ref()); invoke( &Instruction { program_id: PUMP_PROGRAM_ID, accounts: vec![ AccountMeta::new(a.mint.key(), true), AccountMeta::new_readonly(a.mint_authority.key(), false), AccountMeta::new(a.bonding_curve.key(), false), AccountMeta::new(a.curve_ata.key(), false), AccountMeta::new_readonly(a.global.key(), false), AccountMeta::new_readonly(a.mpl.key(), false), AccountMeta::new(a.metadata.key(), false), AccountMeta::new(a.dev.key(), true), AccountMeta::new_readonly(a.system_program.key(), false), AccountMeta::new_readonly(a.token_program.key(), false), AccountMeta::new_readonly(a.ata_program.key(), false), AccountMeta::new_readonly(a.rent.key(), false), AccountMeta::new_readonly(a.event_authority.key(), false), AccountMeta::new_readonly(a.pump_program.key(), false), ], data, }, &[ a.mint.to_account_info(), a.mint_authority.to_account_info(), a.bonding_curve.to_account_info(), a.curve_ata.to_account_info(), a.global.to_account_info(), a.mpl.to_account_info(), a.metadata.to_account_info(), a.dev.to_account_info(), a.system_program.to_account_info(), a.token_program.to_account_info(), a.ata_program.to_account_info(), a.rent.to_account_info(), a.event_authority.to_account_info(), a.pump_program.to_account_info(), ], )?; let now = Clock::get()?.unix_timestamp; let p = &mut ctx.accounts.pact; p.mint = ctx.accounts.mint.key(); p.dev = ctx.accounts.dev.key(); p.pledged = 0; p.forfeited = false; p.collected_total = 0; p.claimed_total = 0; p.burned_total = 0; p.created_ts = now; p.released_ts = 0; p.bump = ctx.bumps.pact; emit!(Launched { pact: pact_key, mint: p.mint, dev: p.dev, ts: now }); Ok(()) } /// Dev: lock `amount` tokens in the pact's vault. Can be called again to add more. pub fn pledge(ctx: Context<Pledge>, amount: u64) -> Result<()> { require!(amount > 0, HolvoError::Nothing); require!(!ctx.accounts.pact.forfeited, HolvoError::Forfeited); let a = &ctx.accounts; let pact_key = a.pact.key(); let (vault, _) = Pubkey::find_program_address(&[pact_key.as_ref(), TOKEN_PROGRAM_ID.as_ref(), a.mint.key().as_ref()], &ATA_PROGRAM_ID); require_keys_eq!(a.vault.key(), vault, HolvoError::BadAccount); // the vault is the pact's associated token account; the dev pays its rent the first time if a.vault.to_account_info().data_is_empty() { invoke( &Instruction { program_id: ATA_PROGRAM_ID, accounts: vec![ AccountMeta::new(a.dev.key(), true), AccountMeta::new(a.vault.key(), false), AccountMeta::new_readonly(pact_key, false), AccountMeta::new_readonly(a.mint.key(), false), AccountMeta::new_readonly(a.system_program.key(), false), AccountMeta::new_readonly(a.token_program.key(), false), ], data: vec![1], }, &[a.dev.to_account_info(), a.vault.to_account_info(), a.pact.to_account_info(), a.mint.to_account_info(), a.system_program.to_account_info(), a.token_program.to_account_info(), a.ata_program.to_account_info()], )?; } let before = token_amount(&a.vault.to_account_info())?; let mut data = vec![3u8]; // spl-token Transfer data.extend_from_slice(&amount.to_le_bytes()); invoke( &Instruction { program_id: TOKEN_PROGRAM_ID, accounts: vec![AccountMeta::new(a.dev_ata.key(), false), AccountMeta::new(a.vault.key(), false), AccountMeta::new_readonly(a.dev.key(), true)], data, }, &[a.dev_ata.to_account_info(), a.vault.to_account_info(), a.dev.to_account_info(), a.token_program.to_account_info()], )?; let after = token_amount(&a.vault.to_account_info())?; let added = after.checked_sub(before).ok_or(HolvoError::Math)?; let p = &mut ctx.accounts.pact; p.pledged = after; emit!(Pledged { pact: pact_key, added, pledged: after, ts: Clock::get()?.unix_timestamp }); Ok(()) } /// Dev: take the whole bag back. The fees are forfeited for good from this moment. pub fn release(ctx: Context<Release>) -> Result<()> { require!(!ctx.accounts.pact.forfeited, HolvoError::Forfeited); let a = &ctx.accounts; let pact_key = a.pact.key(); let (vault, _) = Pubkey::find_program_address(&[pact_key.as_ref(), TOKEN_PROGRAM_ID.as_ref(), a.mint.key().as_ref()], &ATA_PROGRAM_ID); require_keys_eq!(a.vault.key(), vault, HolvoError::BadAccount); let amount = token_amount(&a.vault.to_account_info())?; require!(amount > 0, HolvoError::Nothing); let mint_key = a.mint.key(); let seeds: &[&[u8]] = &[PACT_SEED, mint_key.as_ref(), &[a.pact.bump]]; let mut data = vec![3u8]; data.extend_from_slice(&amount.to_le_bytes()); invoke_signed( &Instruction { program_id: TOKEN_PROGRAM_ID, accounts: vec![AccountMeta::new(a.vault.key(), false), AccountMeta::new(a.dev_ata.key(), false), AccountMeta::new_readonly(pact_key, true)], data, }, &[a.vault.to_account_info(), a.dev_ata.to_account_info(), a.pact.to_account_info(), a.token_program.to_account_info()], &[seeds], )?; let now = Clock::get()?.unix_timestamp; let p = &mut ctx.accounts.pact; p.pledged = 0; p.forfeited = true; p.released_ts = now; emit!(Released { pact: pact_key, amount, ts: now }); Ok(()) } /// Dev: take the collected fees, while the pledge is in. pub fn claim(ctx: Context<Claim>) -> Result<()> { let p = &ctx.accounts.pact; require!(!p.forfeited, HolvoError::Forfeited); require!(p.pledged > 0, HolvoError::NoPledge); let info = ctx.accounts.pact.to_account_info(); let amount = above_rent(&info)?; require!(amount > 0, HolvoError::Nothing); **info.try_borrow_mut_lamports()? -= amount; **ctx.accounts.dev.to_account_info().try_borrow_mut_lamports()? += amount; let p = &mut ctx.accounts.pact; p.claimed_total = p.claimed_total.checked_add(amount).ok_or(HolvoError::Math)?; emit!(Claimed { pact: info.key(), lamports: amount, ts: Clock::get()?.unix_timestamp }); Ok(()) } /// Anyone: after a release, send what the pact holds to the incinerator. pub fn burn(ctx: Context<Burn>) -> Result<()> { require!(ctx.accounts.pact.forfeited, HolvoError::NotForfeited); let info = ctx.accounts.pact.to_account_info(); let amount = above_rent(&info)?; require!(amount > 0, HolvoError::Nothing); **info.try_borrow_mut_lamports()? -= amount; **ctx.accounts.incinerator.to_account_info().try_borrow_mut_lamports()? += amount; let p = &mut ctx.accounts.pact; p.burned_total = p.burned_total.checked_add(amount).ok_or(HolvoError::Math)?; emit!(Burned { pact: info.key(), lamports: amount, ts: Clock::get()?.unix_timestamp }); Ok(()) } /// Anyone: move the coin's bonding-curve creator fees onto the pact. pub fn collect_curve(ctx: Context<CollectCurve>) -> Result<()> { let a = &ctx.accounts; let info = a.pact.to_account_info(); let before = info.lamports(); invoke( &Instruction { program_id: PUMP_PROGRAM_ID, accounts: vec![ AccountMeta::new(info.key(), false), AccountMeta::new(a.creator_vault.key(), false), AccountMeta::new_readonly(a.system_program.key(), false), AccountMeta::new_readonly(a.event_authority.key(), false), AccountMeta::new_readonly(a.pump_program.key(), false), ], data: PUMP_COLLECT_DISC.to_vec(), }, &[info.clone(), a.creator_vault.to_account_info(), a.system_program.to_account_info(), a.event_authority.to_account_info(), a.pump_program.to_account_info()], )?; let got = info.lamports().checked_sub(before).ok_or(HolvoError::Math)?; require!(got > 0, HolvoError::Nothing); let p = &mut ctx.accounts.pact; p.collected_total = p.collected_total.checked_add(got).ok_or(HolvoError::Math)?; emit!(Collected { pact: info.key(), source: SOURCE_CURVE, lamports: got, ts: Clock::get()?.unix_timestamp }); Ok(()) } /// Anyone: move the coin's PumpSwap creator fees onto the pact as SOL. If the caller had to /// create the pact's WSOL account, its rent goes back to the caller. pub fn collect_amm(ctx: Context<CollectAmm>) -> Result<()> { let a = &ctx.accounts; let info = a.pact.to_account_info(); let pact_key = info.key(); let (expected, _) = Pubkey::find_program_address(&[pact_key.as_ref(), TOKEN_PROGRAM_ID.as_ref(), WSOL_MINT.as_ref()], &ATA_PROGRAM_ID); require_keys_eq!(a.pact_wsol.key(), expected, HolvoError::BadAccount); let created_here = a.pact_wsol.to_account_info().data_is_empty(); if created_here { invoke( &Instruction { program_id: ATA_PROGRAM_ID, accounts: vec![ AccountMeta::new(a.payer.key(), true), AccountMeta::new(a.pact_wsol.key(), false), AccountMeta::new_readonly(pact_key, false), AccountMeta::new_readonly(a.wsol_mint.key(), false), AccountMeta::new_readonly(a.system_program.key(), false), AccountMeta::new_readonly(a.token_program.key(), false), ], data: vec![1], }, &[a.payer.to_account_info(), a.pact_wsol.to_account_info(), info.clone(), a.wsol_mint.to_account_info(), a.system_program.to_account_info(), a.token_program.to_account_info(), a.ata_program.to_account_info()], )?; } invoke( &Instruction { program_id: PUMP_AMM_PROGRAM_ID, accounts: vec![ AccountMeta::new_readonly(a.wsol_mint.key(), false), AccountMeta::new_readonly(a.token_program.key(), false), AccountMeta::new_readonly(pact_key, false), AccountMeta::new_readonly(a.vault_authority.key(), false), AccountMeta::new(a.vault_ata.key(), false), AccountMeta::new(a.pact_wsol.key(), false), AccountMeta::new_readonly(a.amm_event_authority.key(), false), AccountMeta::new_readonly(a.amm_program.key(), false), ], data: AMM_COLLECT_DISC.to_vec(), }, &[a.wsol_mint.to_account_info(), a.token_program.to_account_info(), info.clone(), a.vault_authority.to_account_info(), a.vault_ata.to_account_info(), a.pact_wsol.to_account_info(), a.amm_event_authority.to_account_info(), a.amm_program.to_account_info()], )?; let before = info.lamports(); let mint_key = a.pact.mint; let seeds: &[&[u8]] = &[PACT_SEED, mint_key.as_ref(), &[a.pact.bump]]; invoke_signed( &Instruction { program_id: TOKEN_PROGRAM_ID, accounts: vec![AccountMeta::new(a.pact_wsol.key(), false), AccountMeta::new(pact_key, false), AccountMeta::new_readonly(pact_key, true)], data: vec![9], // CloseAccount }, &[a.pact_wsol.to_account_info(), info.clone(), a.token_program.to_account_info()], &[seeds], )?; let landed = info.lamports().checked_sub(before).ok_or(HolvoError::Math)?; let rent = Rent::get()?.minimum_balance(TOKEN_ACCOUNT_LEN); let refund = if created_here { rent.min(landed) } else { 0 }; if refund > 0 { **info.try_borrow_mut_lamports()? -= refund; **a.payer.to_account_info().try_borrow_mut_lamports()? += refund; } let got = landed - refund; require!(got > 0, HolvoError::Nothing); let p = &mut ctx.accounts.pact; p.collected_total = p.collected_total.checked_add(got).ok_or(HolvoError::Math)?; emit!(Collected { pact: pact_key, source: SOURCE_AMM, lamports: got, ts: Clock::get()?.unix_timestamp }); Ok(()) }} fn put_str(v: &mut Vec<u8>, s: &str) { v.extend_from_slice(&(s.len() as u32).to_le_bytes()); v.extend_from_slice(s.as_bytes());} /// lamports on an account above its own rentfn above_rent(info: &AccountInfo) -> Result<u64> { let rent = Rent::get()?.minimum_balance(info.data_len()); Ok(info.lamports().saturating_sub(rent))} /// spl-token account amount (u64 at offset 64), checked to be a token account of the token programfn token_amount(info: &AccountInfo) -> Result<u64> { require_keys_eq!(*info.owner, TOKEN_PROGRAM_ID, HolvoError::BadAccount); let d = info.try_borrow_data()?; require!(d.len() >= 72, HolvoError::BadAccount); Ok(u64::from_le_bytes(d[64..72].try_into().unwrap()))} #[account]#[derive(InitSpace)]pub struct Pact { pub mint: Pubkey, pub dev: Pubkey, pub pledged: u64, pub forfeited: bool, pub collected_total: u64, pub claimed_total: u64, pub burned_total: u64, pub created_ts: i64, pub released_ts: i64, pub bump: u8,} #[derive(Accounts)]pub struct Launch<'info> { #[account(init, payer = dev, space = 8 + Pact::INIT_SPACE, seeds = [PACT_SEED, mint.key().as_ref()], bump)] pub pact: Account<'info, Pact>, #[account(mut)] pub dev: Signer<'info>, /// CHECK: new mint, must sign #[account(mut)] pub mint: UncheckedAccount<'info>, /// CHECK: pump pda pub mint_authority: UncheckedAccount<'info>, /// CHECK: pump pda #[account(mut)] pub bonding_curve: UncheckedAccount<'info>, /// CHECK: pump ata #[account(mut)] pub curve_ata: UncheckedAccount<'info>, /// CHECK: pump global pub global: UncheckedAccount<'info>, /// CHECK: metaplex pub mpl: UncheckedAccount<'info>, /// CHECK: metadata pda #[account(mut)] pub metadata: UncheckedAccount<'info>, pub system_program: Program<'info, System>, /// CHECK: spl token #[account(address = TOKEN_PROGRAM_ID)] pub token_program: UncheckedAccount<'info>, /// CHECK: ata program #[account(address = ATA_PROGRAM_ID)] pub ata_program: UncheckedAccount<'info>, /// CHECK: rent sysvar pub rent: UncheckedAccount<'info>, /// CHECK: pump event authority pub event_authority: UncheckedAccount<'info>, /// CHECK: pump program #[account(address = PUMP_PROGRAM_ID)] pub pump_program: UncheckedAccount<'info>,} #[derive(Accounts)]pub struct Pledge<'info> { #[account(mut, seeds = [PACT_SEED, pact.mint.as_ref()], bump = pact.bump, has_one = dev, has_one = mint)] pub pact: Account<'info, Pact>, #[account(mut)] pub dev: Signer<'info>, /// CHECK: the coin's mint, matched against the pact pub mint: UncheckedAccount<'info>, /// CHECK: the dev's token account; spl-token checks the dev owns it #[account(mut)] pub dev_ata: UncheckedAccount<'info>, /// CHECK: the pact's associated token account, checked in the handler #[account(mut)] pub vault: UncheckedAccount<'info>, /// CHECK: spl token #[account(address = TOKEN_PROGRAM_ID)] pub token_program: UncheckedAccount<'info>, /// CHECK: ata program #[account(address = ATA_PROGRAM_ID)] pub ata_program: UncheckedAccount<'info>, pub system_program: Program<'info, System>,} #[derive(Accounts)]pub struct Release<'info> { #[account(mut, seeds = [PACT_SEED, pact.mint.as_ref()], bump = pact.bump, has_one = dev, has_one = mint)] pub pact: Account<'info, Pact>, #[account(mut)] pub dev: Signer<'info>, /// CHECK: the coin's mint, matched against the pact pub mint: UncheckedAccount<'info>, /// CHECK: where the bag goes; spl-token checks the mint #[account(mut)] pub dev_ata: UncheckedAccount<'info>, /// CHECK: the pact's associated token account, checked in the handler #[account(mut)] pub vault: UncheckedAccount<'info>, /// CHECK: spl token #[account(address = TOKEN_PROGRAM_ID)] pub token_program: UncheckedAccount<'info>,} #[derive(Accounts)]pub struct Claim<'info> { #[account(mut, seeds = [PACT_SEED, pact.mint.as_ref()], bump = pact.bump, has_one = dev)] pub pact: Account<'info, Pact>, #[account(mut)] pub dev: Signer<'info>,} #[derive(Accounts)]pub struct Burn<'info> { #[account(mut, seeds = [PACT_SEED, pact.mint.as_ref()], bump = pact.bump)] pub pact: Account<'info, Pact>, /// CHECK: the incinerator, an address with no key #[account(mut, address = INCINERATOR)] pub incinerator: UncheckedAccount<'info>,} #[derive(Accounts)]pub struct CollectCurve<'info> { #[account(mut, seeds = [PACT_SEED, pact.mint.as_ref()], bump = pact.bump)] pub pact: Account<'info, Pact>, /// CHECK: pump.fun's creator vault for this pact #[account(mut, seeds = [b"creator-vault", pact.key().as_ref()], bump, seeds::program = PUMP_PROGRAM_ID)] pub creator_vault: UncheckedAccount<'info>, pub system_program: Program<'info, System>, /// CHECK: pump.fun event authority #[account(seeds = [b"__event_authority"], bump, seeds::program = PUMP_PROGRAM_ID)] pub event_authority: UncheckedAccount<'info>, /// CHECK: pump.fun #[account(address = PUMP_PROGRAM_ID)] pub pump_program: UncheckedAccount<'info>,} #[derive(Accounts)]pub struct CollectAmm<'info> { #[account(mut, seeds = [PACT_SEED, pact.mint.as_ref()], bump = pact.bump)] pub pact: Account<'info, Pact>, /// pays the pact's WSOL account rent if it has to be created, and gets it back #[account(mut)] pub payer: Signer<'info>, /// CHECK: the pact's WSOL associated token account, checked in the handler #[account(mut)] pub pact_wsol: UncheckedAccount<'info>, /// CHECK: PumpSwap's creator vault authority for this pact #[account(seeds = [b"creator_vault", pact.key().as_ref()], bump, seeds::program = PUMP_AMM_PROGRAM_ID)] pub vault_authority: UncheckedAccount<'info>, /// CHECK: the vault authority's WSOL account, PumpSwap checks it #[account(mut)] pub vault_ata: UncheckedAccount<'info>, /// CHECK: wrapped SOL #[account(address = WSOL_MINT)] pub wsol_mint: UncheckedAccount<'info>, /// CHECK: spl token #[account(address = TOKEN_PROGRAM_ID)] pub token_program: UncheckedAccount<'info>, /// CHECK: ata program #[account(address = ATA_PROGRAM_ID)] pub ata_program: UncheckedAccount<'info>, pub system_program: Program<'info, System>, /// CHECK: PumpSwap event authority #[account(seeds = [b"__event_authority"], bump, seeds::program = PUMP_AMM_PROGRAM_ID)] pub amm_event_authority: UncheckedAccount<'info>, /// CHECK: PumpSwap #[account(address = PUMP_AMM_PROGRAM_ID)] pub amm_program: UncheckedAccount<'info>,} #[event]pub struct Launched { pub pact: Pubkey, pub mint: Pubkey, pub dev: Pubkey, pub ts: i64 }#[event]pub struct Pledged { pub pact: Pubkey, pub added: u64, pub pledged: u64, pub ts: i64 }#[event]pub struct Released { pub pact: Pubkey, pub amount: u64, pub ts: i64 }#[event]pub struct Claimed { pub pact: Pubkey, pub lamports: u64, pub ts: i64 }#[event]pub struct Burned { pub pact: Pubkey, pub lamports: u64, pub ts: i64 }#[event]pub struct Collected { pub pact: Pubkey, pub source: u8, pub lamports: u64, pub ts: i64 } #[error_code]pub enum HolvoError { #[msg("text out of range")] BadText, #[msg("the mint keypair must sign")] MintMustSign, #[msg("nothing to move")] Nothing, #[msg("the fees of this coin are forfeited: the dev took the bag back")] Forfeited, #[msg("the dev has not pledged a bag yet")] NoPledge, #[msg("the fees are not forfeited")] NotForfeited, #[msg("wrong account")] BadAccount, #[msg("math")] Math,}