This commit is contained in:
Hardhat Chad
2024-02-13 19:32:31 +00:00
parent 68bb762e5c
commit 7cba1ff1da
5 changed files with 444 additions and 5 deletions

View File

@@ -1,5 +1,12 @@
use solana_program::{
account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, system_program,
account_info::AccountInfo, program_error::ProgramError, program_pack::Pack, pubkey::Pubkey,
system_program,
};
use spl_token::state::Mint;
use crate::{
state::{Bus, Treasury},
BUS, MINT_ADDRESS, TREASURY,
};
pub fn load_signer<'a, 'info>(
@@ -42,6 +49,97 @@ pub fn load_uninitialized_pda<'a, 'info>(
load_uninitialized_account(info)
}
pub fn load_bus<'a, 'info>(
info: &'a AccountInfo<'info>,
) -> Result<&'a AccountInfo<'info>, ProgramError> {
if !info.owner.eq(&crate::id()) {
return Err(ProgramError::InvalidAccountOwner);
}
if info.data_is_empty() {
return Err(ProgramError::UninitializedAccount);
}
let bus_data = info.data.borrow();
let bus = bytemuck::try_from_bytes::<Bus>(&bus_data).unwrap();
let key =
Pubkey::create_program_address(&[BUS, &[bus.id as u8], &[bus.bump as u8]], &crate::id())?;
if !info.key.eq(&key) {
return Err(ProgramError::InvalidSeeds);
}
Ok(info)
}
pub fn load_treasury<'a, 'info>(
info: &'a AccountInfo<'info>,
) -> Result<&'a AccountInfo<'info>, ProgramError> {
if !info.owner.eq(&crate::id()) {
return Err(ProgramError::InvalidAccountOwner);
}
if info.data_is_empty() {
return Err(ProgramError::UninitializedAccount);
}
let treasury_data = info.data.borrow();
let treasury = bytemuck::try_from_bytes::<Treasury>(&treasury_data).unwrap();
let key = Pubkey::create_program_address(&[TREASURY, &[treasury.bump as u8]], &crate::id())?;
if !info.key.eq(&key) {
return Err(ProgramError::InvalidSeeds);
}
Ok(info)
}
pub fn load_mint<'a, 'info>(
info: &'a AccountInfo<'info>,
) -> Result<&'a AccountInfo<'info>, ProgramError> {
if !info.owner.eq(&spl_token::id()) {
return Err(ProgramError::InvalidAccountOwner);
}
if info.data_is_empty() {
return Err(ProgramError::UninitializedAccount);
}
let mint_data = info.data.borrow();
if Mint::unpack_unchecked(&mint_data).is_err() {
return Err(ProgramError::InvalidAccountData);
}
if !info.key.eq(&MINT_ADDRESS) {
return Err(ProgramError::InvalidAccountData);
}
Ok(info)
}
pub fn load_token_account<'a, 'info>(
info: &'a AccountInfo<'info>,
owner: &Pubkey,
mint: &Pubkey,
) -> Result<&'a AccountInfo<'info>, ProgramError> {
if !info.owner.eq(&spl_token::id()) {
return Err(ProgramError::InvalidAccountOwner);
}
if info.data_is_empty() {
return Err(ProgramError::UninitializedAccount);
}
let account_data = info.data.borrow();
let account = spl_token::state::Account::unpack_unchecked(&account_data)
.or(Err(ProgramError::InvalidAccountData))?;
if !account.mint.eq(mint) {
return Err(ProgramError::InvalidAccountData);
}
if !account.owner.eq(owner) {
return Err(ProgramError::InvalidAccountData);
}
Ok(info)
}
pub fn load_uninitialized_account<'a, 'info>(
info: &'a AccountInfo<'info>,
) -> Result<&'a AccountInfo<'info>, ProgramError> {

View File

@@ -1,13 +1,103 @@
use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey};
use solana_program::{
account_info::{next_account_info, AccountInfo},
clock::Clock,
entrypoint::ProgramResult,
program_error::ProgramError,
pubkey::Pubkey,
sysvar::Sysvar,
};
use crate::{BUS_EPOCH_REWARDS, SMOOTHING_FACTOR, TARGET_EPOCH_REWARDS};
use crate::{
loaders::*,
state::{Bus, Treasury},
BUS, BUS_COUNT, BUS_EPOCH_REWARDS, EPOCH_DURATION, MAX_EPOCH_REWARDS, SMOOTHING_FACTOR,
TARGET_EPOCH_REWARDS, TREASURY,
};
pub fn process_epoch<'a, 'info>(
_program_id: &Pubkey,
accounts: &'a [AccountInfo<'info>],
data: &[u8],
_data: &[u8],
) -> ProgramResult {
// TODO
let accounts_iter = &mut accounts.iter();
// Account 1: Signer
let _signer = load_signer(next_account_info(accounts_iter)?)?;
// Accounts 2-9: Busses
let busses = vec![
load_bus(next_account_info(accounts_iter)?)?,
load_bus(next_account_info(accounts_iter)?)?,
load_bus(next_account_info(accounts_iter)?)?,
load_bus(next_account_info(accounts_iter)?)?,
load_bus(next_account_info(accounts_iter)?)?,
load_bus(next_account_info(accounts_iter)?)?,
load_bus(next_account_info(accounts_iter)?)?,
load_bus(next_account_info(accounts_iter)?)?,
];
// Account 10: Mint
let mint = load_mint(next_account_info(accounts_iter)?)?;
// Account 11: Treasury
let treasury_info = load_treasury(next_account_info(accounts_iter)?)?;
// Account 12: Treasury tokens
let treasury_tokens = load_token_account(
next_account_info(accounts_iter)?,
treasury_info.key,
mint.key,
)?;
// Account 13: Token program
let token_program = load_account(next_account_info(accounts_iter)?, spl_token::id())?;
// Validate epoch has ended
let clock = Clock::get().unwrap();
let mut treasury_data = treasury_info.data.borrow_mut();
let mut treasury = bytemuck::try_from_bytes_mut::<Treasury>(&mut treasury_data).unwrap();
let epoch_end_at = treasury.epoch_start_at.saturating_add(EPOCH_DURATION);
if !clock.unix_timestamp.ge(&epoch_end_at) {
return Err(ProgramError::Custom(1));
}
// Reset busses
let mut total_available_rewards = 0u64;
for i in 0..BUS_COUNT {
let mut bus_data = busses[i].data.borrow_mut();
let mut bus = bytemuck::try_from_bytes_mut::<Bus>(&mut bus_data).unwrap();
total_available_rewards = total_available_rewards.saturating_add(bus.available_rewards);
bus.available_rewards = BUS_EPOCH_REWARDS;
}
// Update the reward rate for the next epoch
let total_epoch_rewards = MAX_EPOCH_REWARDS.saturating_sub(total_available_rewards);
treasury.reward_rate = calculate_new_reward_rate(treasury.reward_rate, total_epoch_rewards);
treasury.epoch_start_at = clock.unix_timestamp;
// Top up treasury token account
let treasury_bump = treasury.bump as u8;
drop(treasury_data);
solana_program::program::invoke_signed(
&spl_token::instruction::mint_to(
&spl_token::id(),
mint.key,
treasury_tokens.key,
treasury_info.key,
&[treasury_info.key],
total_epoch_rewards,
)?,
&[
token_program.clone(),
mint.clone(),
treasury_tokens.clone(),
treasury_info.clone(),
],
&[&[TREASURY, &[treasury_bump]]],
)?;
// TODO Logs?
Ok(())
}