Program
The source the deployed program was built from (crate name metra, from before the rename), Anchor 0.31, 571 lines. A verified build is not published yet, and the program is still upgradeable by its deploy key. Program id 4qkLCTx4Ssp8gFBonFTUeopPhEBVcttD7PjwB7Wg1QvH on Solana mainnet.
programs/metra/src/lib.rs
//! METRA - a prepaid meter for an agent.//!//! An owner opens a meter for one agent key: a cap of lamports per window of time. Anyone can//! top the meter up. A coin launched through `launch` has the meter as its pump.fun creator; its//! creator fees are moved onto the meter by `collect_curve` (bonding curve) or `collect_amm`//! (PumpSwap, after graduation), which anyone can call and which leave a receipt. The agent draws//! SOL to any address with `draw`, never more than the cap inside one window; every draw is a//! receipt (an event in the transaction) and a running total in the meter. A window opens with the//! first draw after the previous one ended and runs its full length.//!//! The owner can raise or lower the cap, cut the meter (no draws until it is turned back on), take//! the balance back, or close a meter that has no coin. The owner can also `seal` a meter, for//! good: after that there is no withdraw and no close, and the cap and window can only move toward//! a slower rate, so SOL leaves only through the agent's draws. The agent key can do nothing else://! it cannot change the cap, cannot cut, cannot withdraw.//!//! No fee to us and no admin instruction. One meter per (owner, agent) pair. use anchor_lang::prelude::*;use anchor_lang::solana_program::instruction::{AccountMeta, Instruction};use anchor_lang::solana_program::program::{invoke, invoke_signed};use anchor_lang::system_program; declare_id!("4qkLCTx4Ssp8gFBonFTUeopPhEBVcttD7PjwB7Wg1QvH"); 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 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 METER_SEED: &[u8] = b"meter";pub const SEAL_SEED: &[u8] = b"seal";pub const MAX_NAME: usize = 32;pub const MAX_SYMBOL: usize = 10;pub const MAX_URI: usize = 200;pub const MAX_LABEL: usize = 24;pub const MIN_WINDOW: i64 = 60; // a minutepub const MAX_WINDOW: i64 = 30 * 86_400; // a month pub const SOURCE_CURVE: u8 = 0;pub const SOURCE_AMM: u8 = 1; #[program]pub mod metra { use super::*; /// Open a meter: `cap` lamports per `window` seconds for `agent`. The owner pays the rent. pub fn open(ctx: Context<Open>, cap: u64, window: i64, label: String) -> Result<()> { require!(window >= MIN_WINDOW && window <= MAX_WINDOW, MetraError::BadWindow); require!(cap > 0, MetraError::BadCap); require!(label.len() <= MAX_LABEL, MetraError::BadText); let now = Clock::get()?.unix_timestamp; let m = &mut ctx.accounts.meter; m.owner = ctx.accounts.owner.key(); m.agent = ctx.accounts.agent.key(); m.cap = cap; m.window = window; m.window_start = now; m.spent_in_window = 0; m.drawn_total = 0; m.topped_total = 0; m.draws = 0; m.cut = false; m.mint = Pubkey::default(); m.opened_ts = now; m.label = label; m.bump = ctx.bumps.meter; emit!(Opened { meter: m.key(), owner: m.owner, agent: m.agent, cap, window, ts: now }); Ok(()) } /// Put SOL on the meter. Anyone can. pub fn top_up(ctx: Context<TopUp>, lamports: u64) -> Result<()> { require!(lamports > 0, MetraError::Nothing); system_program::transfer( CpiContext::new(ctx.accounts.system_program.to_account_info(), system_program::Transfer { from: ctx.accounts.payer.to_account_info(), to: ctx.accounts.meter.to_account_info() }), lamports, )?; let m = &mut ctx.accounts.meter; m.topped_total = m.topped_total.checked_add(lamports).ok_or(MetraError::Math)?; emit!(ToppedUp { meter: m.key(), from: ctx.accounts.payer.key(), lamports, balance: meter_balance(&ctx.accounts.meter.to_account_info())? }); Ok(()) } /// The agent draws `lamports` to `to`. Refused when cut, over the cap for this window, or over the balance. pub fn draw(ctx: Context<Draw>, lamports: u64, memo: String) -> Result<()> { require!(lamports > 0, MetraError::Nothing); require!(memo.len() <= 64, MetraError::BadText); let now = Clock::get()?.unix_timestamp; let meter_info = ctx.accounts.meter.to_account_info(); let available = meter_balance(&meter_info)?; let m = &mut ctx.accounts.meter; require!(!m.cut, MetraError::Cut); if now - m.window_start >= m.window { // a new window starts at the first draw after the old one ended m.window_start = now; m.spent_in_window = 0; } let after = m.spent_in_window.checked_add(lamports).ok_or(MetraError::Math)?; require!(after <= m.cap, MetraError::OverCap); require!(lamports <= available, MetraError::Empty); m.spent_in_window = after; m.drawn_total = m.drawn_total.checked_add(lamports).ok_or(MetraError::Math)?; m.draws += 1; let n = m.draws; let key = m.key(); let agent = m.agent; let cap = m.cap; **meter_info.try_borrow_mut_lamports()? -= lamports; **ctx.accounts.to.to_account_info().try_borrow_mut_lamports()? += lamports; emit!(Drew { meter: key, agent, to: ctx.accounts.to.key(), lamports, n, left_in_window: cap - after, balance: available - lamports, memo, ts: now }); Ok(()) } /// Owner: change the cap or the window. Takes effect on the next draw. /// On a sealed meter only a slower rate is allowed: the cap can only go down, the window only up. pub fn set_cap(ctx: Context<OwnerGuarded>, cap: u64, window: i64) -> Result<()> { require!(window >= MIN_WINDOW && window <= MAX_WINDOW, MetraError::BadWindow); require!(cap > 0, MetraError::BadCap); let sealed = is_sealed(&ctx.accounts.seal); let m = &mut ctx.accounts.meter; if sealed { require!(cap <= m.cap && window >= m.window, MetraError::Sealed); } m.cap = cap; m.window = window; emit!(CapSet { meter: m.key(), cap, window }); Ok(()) } /// Owner: cut the meter (no draws) or turn it back on. pub fn cut(ctx: Context<OwnerOnly>, off: bool) -> Result<()> { let m = &mut ctx.accounts.meter; m.cut = off; emit!(CutSet { meter: m.key(), cut: off, ts: Clock::get()?.unix_timestamp }); Ok(()) } /// Owner: take SOL back off the meter. Refused on a sealed meter. pub fn withdraw(ctx: Context<OwnerGuarded>, lamports: u64) -> Result<()> { require!(!is_sealed(&ctx.accounts.seal), MetraError::Sealed); let meter_info = ctx.accounts.meter.to_account_info(); let available = meter_balance(&meter_info)?; require!(lamports > 0 && lamports <= available, MetraError::Empty); **meter_info.try_borrow_mut_lamports()? -= lamports; **ctx.accounts.owner.to_account_info().try_borrow_mut_lamports()? += lamports; emit!(Withdrawn { meter: ctx.accounts.meter.key(), lamports, balance: available - lamports }); Ok(()) } /// Owner: close a meter that has no coin. The balance and the rent go back to the owner. /// Refused on a sealed meter, and on a meter that launched a coin (it stays that coin's creator). pub fn close(ctx: Context<CloseMeter>) -> Result<()> { require!(!is_sealed(&ctx.accounts.seal), MetraError::Sealed); require!(ctx.accounts.meter.mint == Pubkey::default(), MetraError::HasCoin); let lamports = ctx.accounts.meter.to_account_info().lamports(); emit!(Closed { meter: ctx.accounts.meter.key(), lamports, ts: Clock::get()?.unix_timestamp }); Ok(()) } /// Owner: seal the meter, for good. No instruction removes a seal. pub fn seal(ctx: Context<SealMeter>) -> Result<()> { let now = Clock::get()?.unix_timestamp; let s = &mut ctx.accounts.seal; s.meter = ctx.accounts.meter.key(); s.ts = now; emit!(Sealed { meter: s.meter, ts: now }); Ok(()) } /// Anyone: move the coin's bonding-curve creator fees from pump.fun's creator vault onto the meter. pub fn collect_curve(ctx: Context<CollectCurve>) -> Result<()> { let a = &ctx.accounts; let meter_info = a.meter.to_account_info(); let before = meter_info.lamports(); invoke( &Instruction { program_id: PUMP_PROGRAM_ID, accounts: vec![ AccountMeta::new(meter_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(), }, &[meter_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 = meter_info.lamports().checked_sub(before).ok_or(MetraError::Math)?; require!(got > 0, MetraError::Nothing); emit!(Collected { meter: meter_info.key(), source: SOURCE_CURVE, lamports: got, balance: meter_balance(&meter_info)?, ts: Clock::get()?.unix_timestamp }); Ok(()) } /// Anyone: move the coin's PumpSwap creator fees onto the meter. PumpSwap pays them in wrapped /// SOL to the meter's WSOL account; the meter signs to close that account, so they land as SOL. /// If the caller had to create the WSOL account, its rent goes back to the caller. pub fn collect_amm(ctx: Context<CollectAmm>) -> Result<()> { let a = &ctx.accounts; let meter_info = a.meter.to_account_info(); let meter_key = meter_info.key(); let (expected_ata, _) = Pubkey::find_program_address(&[meter_key.as_ref(), TOKEN_PROGRAM_ID.as_ref(), WSOL_MINT.as_ref()], &ATA_PROGRAM_ID); require_keys_eq!(a.meter_wsol.key(), expected_ata, MetraError::BadAccount); // 1. the meter's WSOL account, created by the caller when it is missing let created_here = a.meter_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.meter_wsol.key(), false), AccountMeta::new_readonly(meter_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], // CreateIdempotent }, &[a.payer.to_account_info(), a.meter_wsol.to_account_info(), meter_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()], )?; } // 2. PumpSwap pays the creator fees into it 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(meter_key, false), AccountMeta::new_readonly(a.vault_authority.key(), false), AccountMeta::new(a.vault_ata.key(), false), AccountMeta::new(a.meter_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(), meter_info.clone(), a.vault_authority.to_account_info(), a.vault_ata.to_account_info(), a.meter_wsol.to_account_info(), a.amm_event_authority.to_account_info(), a.amm_program.to_account_info()], )?; // 3. the meter closes its WSOL account: everything in it lands on the meter as SOL let before = meter_info.lamports(); let m = &a.meter; let seeds: &[&[u8]] = &[METER_SEED, m.owner.as_ref(), m.agent.as_ref(), &[m.bump]]; invoke_signed( &Instruction { program_id: TOKEN_PROGRAM_ID, accounts: vec![ AccountMeta::new(a.meter_wsol.key(), false), AccountMeta::new(meter_key, false), AccountMeta::new_readonly(meter_key, true), ], data: vec![9], // CloseAccount }, &[a.meter_wsol.to_account_info(), meter_info.clone(), a.token_program.to_account_info()], &[seeds], )?; let landed = meter_info.lamports().checked_sub(before).ok_or(MetraError::Math)?; // 4. the WSOL account's rent: back to the caller who paid it, otherwise it stays on the meter let rent = Rent::get()?.minimum_balance(TOKEN_ACCOUNT_LEN); let refund = if created_here { rent.min(landed) } else { 0 }; if refund > 0 { **meter_info.try_borrow_mut_lamports()? -= refund; **a.payer.to_account_info().try_borrow_mut_lamports()? += refund; } let got = landed - refund; require!(got > 0, MetraError::Nothing); emit!(Collected { meter: meter_key, source: SOURCE_AMM, lamports: got, balance: meter_balance(&meter_info)?, ts: Clock::get()?.unix_timestamp }); Ok(()) } /// Owner: create a coin on pump.fun with the meter as its creator. pub fn launch(ctx: Context<Launch>, name: String, symbol: String, uri: String) -> Result<()> { require!(!name.is_empty() && name.len() <= MAX_NAME, MetraError::BadText); require!(!symbol.is_empty() && symbol.len() <= MAX_SYMBOL, MetraError::BadText); require!(!uri.is_empty() && uri.len() <= MAX_URI, MetraError::BadText); require!(ctx.accounts.meter.mint == Pubkey::default(), MetraError::HasCoin); let a = &ctx.accounts; require!(a.mint.is_signer, MetraError::MintMustSign); let meter_key = a.meter.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(meter_key.as_ref()); let metas = 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.owner.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), ]; invoke( &Instruction { program_id: PUMP_PROGRAM_ID, accounts: metas, 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.owner.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 m = &mut ctx.accounts.meter; m.mint = ctx.accounts.mint.key(); emit!(Launched { meter: meter_key, mint: m.mint, 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 the meter above its own rentfn meter_balance(info: &AccountInfo) -> Result<u64> { let rent = Rent::get()?.minimum_balance(info.data_len()); Ok(info.lamports().saturating_sub(rent))} /// a meter is sealed when its seal account exists and belongs to this programfn is_sealed(seal: &UncheckedAccount) -> bool { let info = seal.to_account_info(); info.owner == &crate::ID && !info.data_is_empty()} #[account]#[derive(InitSpace)]pub struct Meter { pub owner: Pubkey, pub agent: Pubkey, pub cap: u64, pub window: i64, pub window_start: i64, pub spent_in_window: u64, pub drawn_total: u64, pub topped_total: u64, pub draws: u64, pub cut: bool, pub mint: Pubkey, pub opened_ts: i64, #[max_len(24)] pub label: String, pub bump: u8,} #[account]#[derive(InitSpace)]pub struct Seal { pub meter: Pubkey, pub ts: i64,} #[derive(Accounts)]pub struct Open<'info> { #[account(init, payer = owner, space = 8 + Meter::INIT_SPACE, seeds = [METER_SEED, owner.key().as_ref(), agent.key().as_ref()], bump)] pub meter: Account<'info, Meter>, #[account(mut)] pub owner: Signer<'info>, /// CHECK: the agent's key, any pubkey, it does not sign here pub agent: UncheckedAccount<'info>, pub system_program: Program<'info, System>,} #[derive(Accounts)]pub struct TopUp<'info> { #[account(mut, seeds = [METER_SEED, meter.owner.as_ref(), meter.agent.as_ref()], bump = meter.bump)] pub meter: Account<'info, Meter>, #[account(mut)] pub payer: Signer<'info>, pub system_program: Program<'info, System>,} #[derive(Accounts)]pub struct Draw<'info> { #[account(mut, seeds = [METER_SEED, meter.owner.as_ref(), meter.agent.as_ref()], bump = meter.bump, has_one = agent)] pub meter: Account<'info, Meter>, pub agent: Signer<'info>, /// CHECK: any address, it only receives lamports #[account(mut)] pub to: UncheckedAccount<'info>,} #[derive(Accounts)]pub struct OwnerOnly<'info> { #[account(mut, seeds = [METER_SEED, meter.owner.as_ref(), meter.agent.as_ref()], bump = meter.bump, has_one = owner)] pub meter: Account<'info, Meter>, #[account(mut)] pub owner: Signer<'info>,} /// owner instructions that a seal restricts#[derive(Accounts)]pub struct OwnerGuarded<'info> { #[account(mut, seeds = [METER_SEED, meter.owner.as_ref(), meter.agent.as_ref()], bump = meter.bump, has_one = owner)] pub meter: Account<'info, Meter>, #[account(mut)] pub owner: Signer<'info>, /// CHECK: the meter's seal PDA; empty while the meter is not sealed #[account(seeds = [SEAL_SEED, meter.key().as_ref()], bump)] pub seal: UncheckedAccount<'info>,} #[derive(Accounts)]pub struct CloseMeter<'info> { #[account(mut, close = owner, seeds = [METER_SEED, meter.owner.as_ref(), meter.agent.as_ref()], bump = meter.bump, has_one = owner)] pub meter: Account<'info, Meter>, #[account(mut)] pub owner: Signer<'info>, /// CHECK: the meter's seal PDA; empty while the meter is not sealed #[account(seeds = [SEAL_SEED, meter.key().as_ref()], bump)] pub seal: UncheckedAccount<'info>,} #[derive(Accounts)]pub struct SealMeter<'info> { #[account(seeds = [METER_SEED, meter.owner.as_ref(), meter.agent.as_ref()], bump = meter.bump, has_one = owner)] pub meter: Account<'info, Meter>, #[account(init, payer = owner, space = 8 + Seal::INIT_SPACE, seeds = [SEAL_SEED, meter.key().as_ref()], bump)] pub seal: Account<'info, Seal>, #[account(mut)] pub owner: Signer<'info>, pub system_program: Program<'info, System>,} #[derive(Accounts)]pub struct CollectCurve<'info> { #[account(mut, seeds = [METER_SEED, meter.owner.as_ref(), meter.agent.as_ref()], bump = meter.bump)] pub meter: Account<'info, Meter>, /// CHECK: pump.fun's creator vault for this meter #[account(mut, seeds = [b"creator-vault", meter.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 = [METER_SEED, meter.owner.as_ref(), meter.agent.as_ref()], bump = meter.bump)] pub meter: Account<'info, Meter>, /// pays the meter's WSOL account rent if it has to be created, and gets it back #[account(mut)] pub payer: Signer<'info>, /// CHECK: the meter's WSOL associated token account, checked in the handler #[account(mut)] pub meter_wsol: UncheckedAccount<'info>, /// CHECK: PumpSwap's creator vault authority for this meter #[account(seeds = [b"creator_vault", meter.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>,} #[derive(Accounts)]pub struct Launch<'info> { #[account(mut, seeds = [METER_SEED, meter.owner.as_ref(), meter.agent.as_ref()], bump = meter.bump, has_one = owner)] pub meter: Account<'info, Meter>, #[account(mut)] pub owner: 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>,} #[event]pub struct Opened { pub meter: Pubkey, pub owner: Pubkey, pub agent: Pubkey, pub cap: u64, pub window: i64, pub ts: i64 }#[event]pub struct ToppedUp { pub meter: Pubkey, pub from: Pubkey, pub lamports: u64, pub balance: u64 }#[event]pub struct Drew { pub meter: Pubkey, pub agent: Pubkey, pub to: Pubkey, pub lamports: u64, pub n: u64, pub left_in_window: u64, pub balance: u64, pub memo: String, pub ts: i64 }#[event]pub struct CapSet { pub meter: Pubkey, pub cap: u64, pub window: i64 }#[event]pub struct CutSet { pub meter: Pubkey, pub cut: bool, pub ts: i64 }#[event]pub struct Withdrawn { pub meter: Pubkey, pub lamports: u64, pub balance: u64 }#[event]pub struct Launched { pub meter: Pubkey, pub mint: Pubkey, pub ts: i64 }#[event]pub struct Collected { pub meter: Pubkey, pub source: u8, pub lamports: u64, pub balance: u64, pub ts: i64 }#[event]pub struct Closed { pub meter: Pubkey, pub lamports: u64, pub ts: i64 }#[event]pub struct Sealed { pub meter: Pubkey, pub ts: i64 } #[error_code]pub enum MetraError { #[msg("a window is between a minute and a month")] BadWindow, #[msg("the cap must be above zero")] BadCap, #[msg("text out of range")] BadText, #[msg("nothing to move")] Nothing, #[msg("the meter is cut")] Cut, #[msg("that draw goes over the cap for this window")] OverCap, #[msg("the meter does not hold that much")] Empty, #[msg("this meter already has a coin")] HasCoin, #[msg("the mint keypair must sign")] MintMustSign, #[msg("math")] Math, #[msg("the meter is sealed: no withdraw, no close, and the rate can only go down")] Sealed, #[msg("wrong account")] BadAccount,}