diff --git a/Cargo.lock b/Cargo.lock index 3ffc70d..e81be82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2211,14 +2211,14 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "steel" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b09eae425248a8afcef3cf3300dfa548791194fa9fe519dadd5c57d7964dbc" +version = "1.3.0" dependencies = [ "bytemuck", + "num_enum", "solana-program", "spl-associated-token-account", "spl-token", + "thiserror", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3bb3b18..7ea337d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,9 @@ solana-program = "^1.18" spl-token = { version = "^4", features = ["no-entrypoint"] } spl-associated-token-account = { version = "^2.3", features = [ "no-entrypoint" ] } static_assertions = "1.1.0" -steel = { features = ["spl"], version = "0.1.0" } +steel = { features = ["spl"], version = "1.0" } thiserror = "1.0.57" +[patch.crates-io] +steel = { path = "../steel/lib" } diff --git a/api/src/error.rs b/api/src/error.rs index 408681d..33fdb75 100644 --- a/api/src/error.rs +++ b/api/src/error.rs @@ -1,6 +1,4 @@ -use num_enum::IntoPrimitive; use steel::*; -use thiserror::Error; #[derive(Debug, Error, Clone, Copy, PartialEq, Eq, IntoPrimitive)] #[repr(u32)] diff --git a/api/src/event.rs b/api/src/event.rs index b7068b3..28b266d 100644 --- a/api/src/event.rs +++ b/api/src/event.rs @@ -1,4 +1,3 @@ -use bytemuck::{Pod, Zeroable}; use steel::*; #[repr(C)] diff --git a/api/src/instruction.rs b/api/src/instruction.rs index 923b2c5..141df9a 100644 --- a/api/src/instruction.rs +++ b/api/src/instruction.rs @@ -1,5 +1,3 @@ -use bytemuck::{Pod, Zeroable}; -use num_enum::TryFromPrimitive; use steel::*; #[repr(u8)] diff --git a/api/src/lib.rs b/api/src/lib.rs index 75b6aba..c0e78a6 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -6,6 +6,16 @@ pub mod loaders; pub mod sdk; pub mod state; -use solana_program::declare_id; +pub mod prelude { + pub use crate::consts::*; + pub use crate::error::*; + pub use crate::event::*; + pub use crate::instruction::*; + pub use crate::loaders::*; + pub use crate::sdk::*; + pub use crate::state::*; +} + +use steel::*; declare_id!("oreV2ZymfyeXgNgBdqMkumTqqAprVqgBWQfoYkrtKWQ"); diff --git a/api/src/loaders.rs b/api/src/loaders.rs index 925335d..ebc7383 100644 --- a/api/src/loaders.rs +++ b/api/src/loaders.rs @@ -1,234 +1,28 @@ -use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; use steel::*; use crate::{ consts::*, - state::{Bus, Config, Proof, Treasury}, + state::{Config, Treasury}, }; -/// Errors if: -/// - Owner is not Ore program. -/// - Address does not match the expected bus address. -/// - Data is empty. -/// - Data cannot deserialize into a bus account. -/// - Bus ID does not match the expected ID. -/// - Expected to be writable, but is not. -pub fn load_bus(info: &AccountInfo<'_>, id: u64, is_writable: bool) -> Result<(), ProgramError> { - if info.owner.ne(&crate::id()) { - return Err(ProgramError::InvalidAccountOwner); - } - - if info.key.ne(&BUS_ADDRESSES[id as usize]) { - return Err(ProgramError::InvalidSeeds); - } - - if info.data_is_empty() { - return Err(ProgramError::UninitializedAccount); - } - - let bus_data = info.data.borrow(); - let bus = Bus::try_from_bytes(&bus_data)?; - - if bus.id.ne(&id) { - return Err(ProgramError::InvalidAccountData); - } - - if is_writable && !info.is_writable { - return Err(ProgramError::InvalidAccountData); - } - - Ok(()) +pub trait OreAccountInfoValidation { + fn is_config(&self) -> Result<&Self, ProgramError>; + fn is_treasury(&self) -> Result<&Self, ProgramError>; + fn is_treasury_tokens(&self) -> Result<&Self, ProgramError>; } -/// Errors if: -/// - Owner is not Ore program. -/// - Data is empty. -/// - Data cannot deserialize into a bus account. -/// - Bus ID is not in the expected range. -/// - Address is not in set of valid bus address. -/// - Expected to be writable, but is not. -pub fn load_any_bus(info: &AccountInfo<'_>, is_writable: bool) -> Result<(), ProgramError> { - if info.owner.ne(&crate::id()) { - return Err(ProgramError::InvalidAccountOwner); +impl OreAccountInfoValidation for AccountInfo<'_> { + fn is_config(&self) -> Result<&Self, ProgramError> { + self.has_address(&CONFIG_ADDRESS)? + .is_type::(&crate::ID) } - if info.data_is_empty() { - return Err(ProgramError::UninitializedAccount); + fn is_treasury(&self) -> Result<&Self, ProgramError> { + self.has_address(&TREASURY_ADDRESS)? + .is_type::(&crate::ID) } - if info.data.borrow()[0].ne(&(Bus::discriminator() as u8)) { - return Err(solana_program::program_error::ProgramError::InvalidAccountData); + fn is_treasury_tokens(&self) -> Result<&Self, ProgramError> { + self.has_address(&TREASURY_TOKENS_ADDRESS) } - - if !BUS_ADDRESSES.contains(info.key) { - return Err(ProgramError::InvalidSeeds); - } - - if is_writable && !info.is_writable { - return Err(ProgramError::InvalidAccountData); - } - - Ok(()) -} - -/// Errors if: -/// - Owner is not Ore program. -/// - Address does not match the expected address. -/// - Data is empty. -/// - Data cannot deserialize into a config account. -/// - Expected to be writable, but is not. -pub fn load_config(info: &AccountInfo<'_>, is_writable: bool) -> Result<(), ProgramError> { - if info.owner.ne(&crate::id()) { - return Err(ProgramError::InvalidAccountOwner); - } - - if info.key.ne(&CONFIG_ADDRESS) { - return Err(ProgramError::InvalidSeeds); - } - - if info.data_is_empty() { - return Err(ProgramError::UninitializedAccount); - } - - if info.data.borrow()[0].ne(&(Config::discriminator() as u8)) { - return Err(solana_program::program_error::ProgramError::InvalidAccountData); - } - - if is_writable && !info.is_writable { - return Err(ProgramError::InvalidAccountData); - } - - Ok(()) -} - -/// Errors if: -/// - Owner is not Ore program. -/// - Data is empty. -/// - Data cannot deserialize into a proof account. -/// - Proof authority does not match the expected address. -/// - Expected to be writable, but is not. -pub fn load_proof( - info: &AccountInfo<'_>, - authority: &Pubkey, - is_writable: bool, -) -> Result<(), ProgramError> { - if info.owner.ne(&crate::id()) { - return Err(ProgramError::InvalidAccountOwner); - } - - if info.data_is_empty() { - return Err(ProgramError::UninitializedAccount); - } - - let proof_data = info.data.borrow(); - let proof = Proof::try_from_bytes(&proof_data)?; - - if proof.authority.ne(&authority) { - return Err(ProgramError::InvalidAccountData); - } - - if is_writable && !info.is_writable { - return Err(ProgramError::InvalidAccountData); - } - - Ok(()) -} - -/// Errors if: -/// - Owner is not Ore program. -/// - Data is empty. -/// - Data cannot deserialize into a proof account. -/// - Proof miner does not match the expected address. -/// - Expected to be writable, but is not. -pub fn load_proof_with_miner( - info: &AccountInfo<'_>, - miner: &Pubkey, - is_writable: bool, -) -> Result<(), ProgramError> { - if info.owner.ne(&crate::id()) { - return Err(ProgramError::InvalidAccountOwner); - } - - if info.data_is_empty() { - return Err(ProgramError::UninitializedAccount); - } - - let proof_data = info.data.borrow(); - let proof = Proof::try_from_bytes(&proof_data)?; - - if proof.miner.ne(&miner) { - return Err(ProgramError::InvalidAccountData); - } - - if is_writable && !info.is_writable { - return Err(ProgramError::InvalidAccountData); - } - - Ok(()) -} - -/// Errors if: -/// - Owner is not Ore program. -/// - Data is empty. -/// - Data cannot deserialize into a proof account. -/// - Expected to be writable, but is not. -pub fn load_any_proof(info: &AccountInfo<'_>, is_writable: bool) -> Result<(), ProgramError> { - if info.owner.ne(&crate::id()) { - return Err(ProgramError::InvalidAccountOwner); - } - - if info.data_is_empty() { - return Err(ProgramError::UninitializedAccount); - } - - if info.data.borrow()[0].ne(&(Proof::discriminator() as u8)) { - return Err(solana_program::program_error::ProgramError::InvalidAccountData); - } - - if is_writable && !info.is_writable { - return Err(ProgramError::InvalidAccountData); - } - - Ok(()) -} - -/// Errors if: -/// - Owner is not Ore program. -/// - Address does not match the expected address. -/// - Data is empty. -/// - Data cannot deserialize into a treasury account. -/// - Expected to be writable, but is not. -pub fn load_treasury(info: &AccountInfo<'_>, is_writable: bool) -> Result<(), ProgramError> { - if info.owner.ne(&crate::id()) { - return Err(ProgramError::InvalidAccountOwner); - } - - if info.key.ne(&TREASURY_ADDRESS) { - return Err(ProgramError::InvalidSeeds); - } - - if info.data_is_empty() { - return Err(ProgramError::UninitializedAccount); - } - - if info.data.borrow()[0].ne(&(Treasury::discriminator() as u8)) { - return Err(solana_program::program_error::ProgramError::InvalidAccountData); - } - - if is_writable && !info.is_writable { - return Err(ProgramError::InvalidAccountData); - } - - Ok(()) -} - -/// Errors if: -/// - Address does not match the expected treasury tokens address. -/// - Cannot load as a token account -pub fn load_treasury_tokens(info: &AccountInfo<'_>, is_writable: bool) -> Result<(), ProgramError> { - if info.key.ne(&TREASURY_TOKENS_ADDRESS) { - return Err(ProgramError::InvalidSeeds); - } - - load_token_account(info, Some(&TREASURY_ADDRESS), &MINT_ADDRESS, is_writable) } diff --git a/api/src/sdk.rs b/api/src/sdk.rs index 67793dc..0c5d797 100644 --- a/api/src/sdk.rs +++ b/api/src/sdk.rs @@ -1,9 +1,5 @@ use drillx::Solution; -use solana_program::{ - instruction::{AccountMeta, Instruction}, - pubkey::Pubkey, - system_program, sysvar, -}; +use steel::*; use crate::{ consts::*, @@ -23,10 +19,6 @@ pub fn auth(proof: Pubkey) -> Instruction { /// Builds a claim instruction. pub fn claim(signer: Pubkey, beneficiary: Pubkey, amount: u64) -> Instruction { let proof = proof_pda(signer).0; - let treasury_tokens = spl_associated_token_account::get_associated_token_address( - &TREASURY_ADDRESS, - &MINT_ADDRESS, - ); Instruction { program_id: crate::id(), accounts: vec![ @@ -34,7 +26,7 @@ pub fn claim(signer: Pubkey, beneficiary: Pubkey, amount: u64) -> Instruction { AccountMeta::new(beneficiary, false), AccountMeta::new(proof, false), AccountMeta::new_readonly(TREASURY_ADDRESS, false), - AccountMeta::new(treasury_tokens, false), + AccountMeta::new(TREASURY_TOKENS_ADDRESS, false), AccountMeta::new_readonly(spl_token::id(), false), ], data: Claim { @@ -98,10 +90,6 @@ pub fn open(signer: Pubkey, miner: Pubkey, payer: Pubkey) -> Instruction { /// Builds a reset instruction. pub fn reset(signer: Pubkey) -> Instruction { - let treasury_tokens = spl_associated_token_account::get_associated_token_address( - &TREASURY_ADDRESS, - &MINT_ADDRESS, - ); Instruction { program_id: crate::id(), accounts: vec![ @@ -117,7 +105,7 @@ pub fn reset(signer: Pubkey) -> Instruction { AccountMeta::new(CONFIG_ADDRESS, false), AccountMeta::new(MINT_ADDRESS, false), AccountMeta::new(TREASURY_ADDRESS, false), - AccountMeta::new(treasury_tokens, false), + AccountMeta::new(TREASURY_TOKENS_ADDRESS, false), AccountMeta::new_readonly(spl_token::id(), false), ], data: Reset {}.to_bytes(), @@ -127,17 +115,13 @@ pub fn reset(signer: Pubkey) -> Instruction { /// Build a stake instruction. pub fn stake(signer: Pubkey, sender: Pubkey, amount: u64) -> Instruction { let proof = proof_pda(signer).0; - let treasury_tokens = spl_associated_token_account::get_associated_token_address( - &TREASURY_ADDRESS, - &MINT_ADDRESS, - ); Instruction { program_id: crate::id(), accounts: vec![ AccountMeta::new(signer, true), AccountMeta::new(proof, false), AccountMeta::new(sender, false), - AccountMeta::new(treasury_tokens, false), + AccountMeta::new(TREASURY_TOKENS_ADDRESS, false), AccountMeta::new_readonly(spl_token::id(), false), ], data: Stake { @@ -196,8 +180,6 @@ pub fn initialize(signer: Pubkey) -> Instruction { let config_pda = config_pda(); let mint_pda = Pubkey::find_program_address(&[MINT, MINT_NOISE.as_slice()], &crate::id()); let treasury_pda = treasury_pda(); - let treasury_tokens = - spl_associated_token_account::get_associated_token_address(&treasury_pda.0, &mint_pda.0); let metadata_pda = Pubkey::find_program_address( &[ METADATA, @@ -222,7 +204,7 @@ pub fn initialize(signer: Pubkey) -> Instruction { AccountMeta::new(metadata_pda.0, false), AccountMeta::new(mint_pda.0, false), AccountMeta::new(treasury_pda.0, false), - AccountMeta::new(treasury_tokens, false), + AccountMeta::new(TREASURY_TOKENS_ADDRESS, false), AccountMeta::new_readonly(system_program::id(), false), AccountMeta::new_readonly(spl_token::id(), false), AccountMeta::new_readonly(spl_associated_token_account::id(), false), diff --git a/api/src/state/bus.rs b/api/src/state/bus.rs index 2c12776..bf32edc 100644 --- a/api/src/state/bus.rs +++ b/api/src/state/bus.rs @@ -1,9 +1,5 @@ -use bytemuck::{Pod, Zeroable}; -use solana_program::pubkey::Pubkey; use steel::*; -use crate::consts::BUS; - use super::OreAccount; /// Bus accounts are responsible for distributing mining rewards. There are 8 busses total @@ -25,9 +21,4 @@ pub struct Bus { pub top_balance: u64, } -/// Fetch the PDA of a bus account. -pub fn bus_pda(id: u8) -> (Pubkey, u8) { - Pubkey::find_program_address(&[BUS, &[id]], &crate::id()) -} - account!(OreAccount, Bus); diff --git a/api/src/state/config.rs b/api/src/state/config.rs index e35216c..52b21cc 100644 --- a/api/src/state/config.rs +++ b/api/src/state/config.rs @@ -1,9 +1,5 @@ -use bytemuck::{Pod, Zeroable}; -use solana_program::pubkey::Pubkey; use steel::*; -use crate::consts::CONFIG; - use super::OreAccount; /// Config is a singleton account which manages program global variables. @@ -23,9 +19,4 @@ pub struct Config { pub top_balance: u64, } -/// Derive the PDA of the config account. -pub fn config_pda() -> (Pubkey, u8) { - Pubkey::find_program_address(&[CONFIG], &crate::id()) -} - account!(OreAccount, Config); diff --git a/api/src/state/mod.rs b/api/src/state/mod.rs index 6803553..20baa13 100644 --- a/api/src/state/mod.rs +++ b/api/src/state/mod.rs @@ -8,7 +8,9 @@ pub use config::*; pub use proof::*; pub use treasury::*; -use num_enum::{IntoPrimitive, TryFromPrimitive}; +use steel::*; + +use crate::consts::*; #[repr(u8)] #[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)] @@ -18,3 +20,23 @@ pub enum OreAccount { Proof = 102, Treasury = 103, } + +/// Fetch the PDA of a bus account. +pub fn bus_pda(id: u8) -> (Pubkey, u8) { + Pubkey::find_program_address(&[BUS, &[id]], &crate::id()) +} + +/// Derive the PDA of the config account. +pub fn config_pda() -> (Pubkey, u8) { + Pubkey::find_program_address(&[CONFIG], &crate::id()) +} + +/// Derive the PDA of a proof account. +pub fn proof_pda(authority: Pubkey) -> (Pubkey, u8) { + Pubkey::find_program_address(&[PROOF, authority.as_ref()], &crate::id()) +} + +/// Derive the PDA of the treasury account. +pub fn treasury_pda() -> (Pubkey, u8) { + Pubkey::find_program_address(&[TREASURY], &crate::id()) +} diff --git a/api/src/state/proof.rs b/api/src/state/proof.rs index 33c0f4a..733e115 100644 --- a/api/src/state/proof.rs +++ b/api/src/state/proof.rs @@ -1,9 +1,5 @@ -use bytemuck::{Pod, Zeroable}; -use solana_program::pubkey::Pubkey; use steel::*; -use crate::consts::PROOF; - use super::OreAccount; /// Proof accounts track a miner's current hash, claimable rewards, and lifetime stats. @@ -39,9 +35,4 @@ pub struct Proof { pub total_rewards: u64, } -/// Derive the PDA of a proof account. -pub fn proof_pda(authority: Pubkey) -> (Pubkey, u8) { - Pubkey::find_program_address(&[PROOF, authority.as_ref()], &crate::id()) -} - account!(OreAccount, Proof); diff --git a/api/src/state/treasury.rs b/api/src/state/treasury.rs index 289fc35..2268ae0 100644 --- a/api/src/state/treasury.rs +++ b/api/src/state/treasury.rs @@ -1,9 +1,5 @@ -use bytemuck::{Pod, Zeroable}; -use solana_program::pubkey::Pubkey; use steel::*; -use crate::consts::TREASURY; - use super::OreAccount; /// Treasury is a singleton account which is the mint authority for the ORE token and the authority of @@ -12,9 +8,4 @@ use super::OreAccount; #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] pub struct Treasury {} -/// Derive the PDA of the treasury account. -pub fn treasury_pda() -> (Pubkey, u8) { - Pubkey::find_program_address(&[TREASURY], &crate::id()) -} - account!(OreAccount, Treasury); diff --git a/program/src/claim.rs b/program/src/claim.rs index 7cf33cc..c4a9201 100644 --- a/program/src/claim.rs +++ b/program/src/claim.rs @@ -1,7 +1,4 @@ -use ore_api::{consts::*, error::OreError, instruction::*, loaders::*, state::Proof}; -use solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, -}; +use ore_api::prelude::*; use steel::*; /// Claim distributes claimable ORE from the treasury to a miner. @@ -11,21 +8,24 @@ pub fn process_claim(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult let amount = u64::from_le_bytes(args.amount); // Load accounts. - let [signer, beneficiary_info, proof_info, treasury_info, treasury_tokens_info, token_program] = + let [signer_info, beneficiary_info, proof_info, treasury_info, treasury_tokens_info, token_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; - load_signer(signer)?; - load_token_account(beneficiary_info, None, &MINT_ADDRESS, true)?; - load_proof(proof_info, signer.key, true)?; - load_treasury(treasury_info, false)?; - load_treasury_tokens(treasury_tokens_info, true)?; - load_program(token_program, spl_token::id())?; + signer_info.is_signer()?; + beneficiary_info + .is_writable()? + .to_token_account()? + .check(|t| t.mint == MINT_ADDRESS)?; + let proof = proof_info + .to_account_mut::(&ore_api::ID)? + .check_mut(|p| p.authority == *signer_info.key)?; + treasury_info.is_treasury()?; + treasury_tokens_info.is_writable()?.is_treasury_tokens()?; + token_program.is_program(&spl_token::ID)?; // Update miner balance. - let mut proof_data = proof_info.data.borrow_mut(); - let proof = Proof::try_from_bytes_mut(&mut proof_data)?; proof.balance = proof .balance .checked_sub(amount) diff --git a/program/src/close.rs b/program/src/close.rs index 4529e39..dd58ead 100644 --- a/program/src/close.rs +++ b/program/src/close.rs @@ -1,33 +1,25 @@ -use ore_api::{loaders::*, state::Proof}; -use solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, - system_program, -}; +use ore_api::prelude::*; use steel::*; /// Close closes a proof account and returns the rent to the owner. pub fn process_close(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult { // Load accounts. - let [signer, proof_info, system_program] = accounts else { + let [signer_info, proof_info, system_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; - load_signer(signer)?; - load_proof(proof_info, signer.key, true)?; - load_program(system_program, system_program::id())?; - - // Validate balance is zero. - let proof_data = proof_info.data.borrow(); - let proof = Proof::try_from_bytes(&proof_data)?; - if proof.balance.gt(&0) { - return Err(ProgramError::InvalidAccountData); - } - drop(proof_data); + signer_info.is_signer()?; + proof_info + .is_writable()? + .to_account::(&ore_api::ID)? + .check(|p| p.authority == *signer_info.key)? + .check(|p| p.balance == 0)?; + system_program.is_program(&system_program::ID)?; // Realloc data to zero. proof_info.realloc(0, true)?; // Send remaining lamports to signer. - **signer.lamports.borrow_mut() += proof_info.lamports(); + **signer_info.lamports.borrow_mut() += proof_info.lamports(); **proof_info.lamports.borrow_mut() = 0; Ok(()) diff --git a/program/src/initialize.rs b/program/src/initialize.rs index b4255d0..4d7d8b3 100644 --- a/program/src/initialize.rs +++ b/program/src/initialize.rs @@ -1,14 +1,5 @@ -use std::mem::size_of; - -use ore_api::{ - consts::*, - instruction::*, - state::{Bus, Config, Treasury}, -}; -use solana_program::{ - self, account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, - program_pack::Pack, system_program, sysvar, -}; +use ore_api::prelude::*; +use solana_program::program_pack::Pack; use spl_token::state::Mint; use steel::*; @@ -18,23 +9,49 @@ pub fn process_initialize(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramR let args = Initialize::try_from_bytes(data)?; // Load accounts. - let [signer, bus_0_info, bus_1_info, bus_2_info, bus_3_info, bus_4_info, bus_5_info, bus_6_info, bus_7_info, config_info, metadata_info, mint_info, treasury_info, treasury_tokens_info, system_program, token_program, associated_token_program, metadata_program, rent_sysvar] = + let [signer_info, bus_0_info, bus_1_info, bus_2_info, bus_3_info, bus_4_info, bus_5_info, bus_6_info, bus_7_info, config_info, metadata_info, mint_info, treasury_info, treasury_tokens_info, system_program, token_program, associated_token_program, metadata_program, rent_sysvar] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; - load_signer(signer)?; - load_uninitialized_pda(bus_0_info, &[BUS, &[0]], args.bus_0_bump, &ore_api::id())?; - load_uninitialized_pda(bus_1_info, &[BUS, &[1]], args.bus_1_bump, &ore_api::id())?; - load_uninitialized_pda(bus_2_info, &[BUS, &[2]], args.bus_2_bump, &ore_api::id())?; - load_uninitialized_pda(bus_3_info, &[BUS, &[3]], args.bus_3_bump, &ore_api::id())?; - load_uninitialized_pda(bus_4_info, &[BUS, &[4]], args.bus_4_bump, &ore_api::id())?; - load_uninitialized_pda(bus_5_info, &[BUS, &[5]], args.bus_5_bump, &ore_api::id())?; - load_uninitialized_pda(bus_6_info, &[BUS, &[6]], args.bus_6_bump, &ore_api::id())?; - load_uninitialized_pda(bus_7_info, &[BUS, &[7]], args.bus_7_bump, &ore_api::id())?; - load_uninitialized_pda(config_info, &[CONFIG], args.config_bump, &ore_api::id())?; - load_uninitialized_pda( - metadata_info, + signer_info.is_signer()?.has_address(&INITIALIZER_ADDRESS)?; + bus_0_info + .is_empty()? + .is_writable()? + .has_seeds(&[BUS, &[0]], args.bus_0_bump, &ore_api::ID)?; + bus_1_info + .is_empty()? + .is_writable()? + .has_seeds(&[BUS, &[1]], args.bus_1_bump, &ore_api::ID)?; + bus_2_info + .is_empty()? + .is_writable()? + .has_seeds(&[BUS, &[2]], args.bus_2_bump, &ore_api::ID)?; + bus_3_info + .is_empty()? + .is_writable()? + .has_seeds(&[BUS, &[3]], args.bus_3_bump, &ore_api::ID)?; + bus_4_info + .is_empty()? + .is_writable()? + .has_seeds(&[BUS, &[4]], args.bus_4_bump, &ore_api::ID)?; + bus_5_info + .is_empty()? + .is_writable()? + .has_seeds(&[BUS, &[5]], args.bus_5_bump, &ore_api::ID)?; + bus_6_info + .is_empty()? + .is_writable()? + .has_seeds(&[BUS, &[6]], args.bus_6_bump, &ore_api::ID)?; + bus_7_info + .is_empty()? + .is_writable()? + .has_seeds(&[BUS, &[7]], args.bus_7_bump, &ore_api::ID)?; + config_info + .is_empty()? + .is_writable()? + .has_seeds(&[CONFIG], args.config_bump, &ore_api::ID)?; + metadata_info.is_empty()?.is_writable()?.has_seeds( &[ METADATA, mpl_token_metadata::ID.as_ref(), @@ -43,29 +60,22 @@ pub fn process_initialize(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramR args.metadata_bump, &mpl_token_metadata::ID, )?; - load_uninitialized_pda( - mint_info, + mint_info.is_empty()?.is_writable()?.has_seeds( &[MINT, MINT_NOISE.as_slice()], args.mint_bump, - &ore_api::id(), + &ore_api::ID, )?; - load_uninitialized_pda( - treasury_info, + treasury_info.is_empty()?.is_writable()?.has_seeds( &[TREASURY], args.treasury_bump, - &ore_api::id(), + &ore_api::ID, )?; - load_system_account(treasury_tokens_info, true)?; - load_program(system_program, system_program::id())?; - load_program(token_program, spl_token::id())?; - load_program(associated_token_program, spl_associated_token_account::id())?; - load_program(metadata_program, mpl_token_metadata::ID)?; - load_sysvar(rent_sysvar, sysvar::rent::id())?; - - // Check signer. - if signer.key.ne(&INITIALIZER_ADDRESS) { - return Err(ProgramError::MissingRequiredSignature); - } + treasury_tokens_info.is_empty()?.is_writable()?; + system_program.is_program(&system_program::ID)?; + token_program.is_program(&spl_token::ID)?; + associated_token_program.is_program(&spl_associated_token_account::ID)?; + metadata_program.is_program(&mpl_token_metadata::ID)?; + rent_sysvar.is_sysvar(&sysvar::rent::ID)?; // Initialize bus accounts. let bus_infos = [ @@ -83,17 +93,14 @@ pub fn process_initialize(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramR args.bus_7_bump, ]; for i in 0..BUS_COUNT { - create_pda( + create_account::( bus_infos[i], - &ore_api::id(), - 8 + size_of::(), + &ore_api::ID, &[BUS, &[i as u8], &[bus_bumps[i]]], system_program, - signer, + signer_info, )?; - let mut bus_data = bus_infos[i].try_borrow_mut_data()?; - bus_data[0] = Bus::discriminator() as u8; - let bus = Bus::try_from_bytes_mut(&mut bus_data)?; + let bus = bus_infos[i].to_account_mut::(&ore_api::ID)?; bus.id = i as u64; bus.rewards = 0; bus.theoretical_rewards = 0; @@ -101,47 +108,40 @@ pub fn process_initialize(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramR } // Initialize config. - create_pda( + create_account::( config_info, - &ore_api::id(), - 8 + size_of::(), + &ore_api::ID, &[CONFIG, &[args.config_bump]], system_program, - signer, + signer_info, )?; - let mut config_data = config_info.data.borrow_mut(); - config_data[0] = Config::discriminator() as u8; - let config = Config::try_from_bytes_mut(&mut config_data)?; + let config = config_info.to_account_mut::(&ore_api::ID)?; config.base_reward_rate = INITIAL_BASE_REWARD_RATE; config.last_reset_at = 0; config.min_difficulty = INITIAL_MIN_DIFFICULTY as u64; config.top_balance = 0; // Initialize treasury. - create_pda( + create_account::( treasury_info, - &ore_api::id(), - 8 + size_of::(), + &ore_api::ID, &[TREASURY, &[args.treasury_bump]], system_program, - signer, + signer_info, )?; - let mut treasury_data = treasury_info.data.borrow_mut(); - treasury_data[0] = Treasury::discriminator() as u8; - drop(treasury_data); // Initialize mint. - create_pda( + allocate_account( mint_info, - &spl_token::id(), + &spl_token::ID, Mint::LEN, &[MINT, MINT_NOISE.as_slice(), &[args.mint_bump]], system_program, - signer, + signer_info, )?; solana_program::program::invoke_signed( &spl_token::instruction::initialize_mint( - &spl_token::id(), + &spl_token::ID, mint_info.key, treasury_info.key, None, @@ -162,8 +162,8 @@ pub fn process_initialize(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramR metadata: metadata_info, mint: mint_info, mint_authority: treasury_info, - payer: signer, - update_authority: (signer, true), + payer: signer_info, + update_authority: (signer_info, true), system_program, rent: Some(rent_sysvar), __args: mpl_token_metadata::instructions::CreateMetadataAccountV3InstructionArgs { @@ -183,8 +183,8 @@ pub fn process_initialize(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramR .invoke_signed(&[&[TREASURY, &[args.treasury_bump]]])?; // Initialize treasury token account. - create_ata( - signer, + create_associated_token_account( + signer_info, treasury_info, treasury_tokens_info, mint_info, diff --git a/program/src/lib.rs b/program/src/lib.rs index 0bb046f..1523004 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -19,27 +19,16 @@ use update::*; use upgrade::*; use ore_api::instruction::*; -use solana_program::{ - self, account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, - pubkey::Pubkey, -}; - -solana_program::entrypoint!(process_instruction); +use steel::*; pub fn process_instruction( program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8], ) -> ProgramResult { - if program_id.ne(&ore_api::id()) { - return Err(ProgramError::IncorrectProgramId); - } + let (ix, data) = parse_instruction(&ore_api::ID, program_id, data)?; - let (tag, data) = data - .split_first() - .ok_or(ProgramError::InvalidInstructionData)?; - - match OreInstruction::try_from(*tag).or(Err(ProgramError::InvalidInstructionData))? { + match ix { OreInstruction::Claim => process_claim(accounts, data)?, OreInstruction::Close => process_close(accounts, data)?, OreInstruction::Mine => process_mine(accounts, data)?, @@ -53,3 +42,5 @@ pub fn process_instruction( Ok(()) } + +entrypoint!(process_instruction); diff --git a/program/src/mine.rs b/program/src/mine.rs index 40aa1ee..eb8e37b 100644 --- a/program/src/mine.rs +++ b/program/src/mine.rs @@ -1,47 +1,38 @@ use std::mem::size_of; use drillx::Solution; -use ore_api::{ - consts::*, - error::OreError, - event::MineEvent, - instruction::Mine, - loaders::*, - state::{Bus, Config, Proof}, -}; +use ore_api::prelude::*; use solana_program::program::set_return_data; #[allow(deprecated)] use solana_program::{ - account_info::AccountInfo, - clock::Clock, - entrypoint::ProgramResult, keccak::hashv, - program_error::ProgramError, - pubkey::Pubkey, sanitize::SanitizeError, serialize_utils::{read_pubkey, read_u16}, slot_hashes::SlotHash, - sysvar::{self, Sysvar}, }; use steel::*; /// Mine validates hashes and increments a miner's collectable balance. -pub fn process_mine(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult { +pub fn process_mine(accounts: &[AccountInfo], data: &[u8]) -> ProgramResult { // Parse args. let args = Mine::try_from_bytes(data)?; // Load accounts. - let [signer, bus_info, config_info, proof_info, instructions_sysvar, slot_hashes_sysvar] = + let [signer_info, bus_info, config_info, proof_info, instructions_sysvar, slot_hashes_sysvar] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; - load_signer(signer)?; - load_any_bus(bus_info, true)?; - load_config(config_info, false)?; - load_proof_with_miner(proof_info, signer.key, true)?; - load_sysvar(instructions_sysvar, sysvar::instructions::id())?; - load_sysvar(slot_hashes_sysvar, sysvar::slot_hashes::id())?; + signer_info.is_signer()?; + let bus = bus_info.to_account_mut::(&ore_api::ID)?; + let config = config_info + .is_config()? + .to_account::(&ore_api::ID)?; + let proof = proof_info + .to_account_mut::(&ore_api::ID)? + .check_mut(|p| p.miner == *signer_info.key)?; + instructions_sysvar.is_sysvar(&sysvar::instructions::ID)?; + slot_hashes_sysvar.is_sysvar(&sysvar::slot_hashes::ID)?; // Authenticate the proof account. // @@ -50,9 +41,9 @@ pub fn process_mine(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult authenticate(&instructions_sysvar.data.borrow(), proof_info.key)?; // Validate epoch is active. - let config_data = config_info.data.borrow(); - let config = Config::try_from_bytes(&config_data)?; - let clock = Clock::get().or(Err(ProgramError::InvalidAccountData))?; + // let config_data = config_info.data.borrow(); + // let config = Config::try_from_bytes(&config_data)?; + let clock = Clock::get()?; if config .last_reset_at .saturating_add(EPOCH_DURATION) @@ -65,8 +56,8 @@ pub fn process_mine(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult // // Here we use drillx to validate the provided solution is a valid hash of the challenge. // If invalid, we return an error. - let mut proof_data = proof_info.data.borrow_mut(); - let proof = Proof::try_from_bytes_mut(&mut proof_data)?; + // let mut proof_data = proof_info.data.borrow_mut(); + // let proof = Proof::try_from_bytes_mut(&mut proof_data)?; let solution = Solution::new(args.digest, args.nonce); if !solution.is_valid(&proof.challenge) { return Err(OreError::HashInvalid.into()); @@ -110,8 +101,8 @@ pub fn process_mine(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult // If user has greater than or equal to the max stake on the network, they receive 2x multiplier. // Any stake less than this will receives between 1x and 2x multipler. The multipler is only active // if the miner's last stake deposit was more than one minute ago to protect against flash loan attacks. - let mut bus_data = bus_info.data.borrow_mut(); - let bus = Bus::try_from_bytes_mut(&mut bus_data)?; + // let mut bus_data = bus_info.data.borrow_mut(); + // let bus = Bus::try_from_bytes_mut(&mut bus_data)?; if proof.balance.gt(&0) && proof.last_stake_at.saturating_add(ONE_MINUTE).lt(&t) { // Calculate staking reward. if config.top_balance.gt(&0) { diff --git a/program/src/open.rs b/program/src/open.rs index f914af8..bc9c9f3 100644 --- a/program/src/open.rs +++ b/program/src/open.rs @@ -1,16 +1,7 @@ use std::mem::size_of; -use ore_api::{consts::*, instruction::Open, state::Proof}; -use solana_program::{ - account_info::AccountInfo, - clock::Clock, - entrypoint::ProgramResult, - keccak::hashv, - program_error::ProgramError, - slot_hashes::SlotHash, - system_program, - sysvar::{self, Sysvar}, -}; +use ore_api::prelude::*; +use solana_program::{keccak::hashv, slot_hashes::SlotHash}; use steel::*; /// Open creates a new proof account to track a miner's state. @@ -19,39 +10,35 @@ pub fn process_open(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult let args = Open::try_from_bytes(data)?; // Load accounts. - let [signer, miner_info, payer_info, proof_info, system_program, slot_hashes_info] = accounts + let [signer_info, miner_info, payer_info, proof_info, system_program, slot_hashes_info] = + accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; - load_signer(signer)?; - load_any(miner_info, false)?; - load_signer(payer_info)?; - load_uninitialized_pda( - proof_info, - &[PROOF, signer.key.as_ref()], + signer_info.is_signer()?; + payer_info.is_signer()?; + proof_info.is_empty()?.is_writable()?.has_seeds( + &[PROOF, signer_info.key.as_ref()], args.bump, - &ore_api::id(), + &ore_api::ID, )?; - load_program(system_program, system_program::id())?; - load_sysvar(slot_hashes_info, sysvar::slot_hashes::id())?; + system_program.is_program(&system_program::ID)?; + slot_hashes_info.is_sysvar(&sysvar::slot_hashes::ID)?; // Initialize proof. - create_pda( + create_account::( proof_info, - &ore_api::id(), - 8 + size_of::(), - &[PROOF, signer.key.as_ref(), &[args.bump]], + &ore_api::ID, + &[PROOF, signer_info.key.as_ref(), &[args.bump]], system_program, payer_info, )?; - let clock = Clock::get().or(Err(ProgramError::InvalidAccountData))?; - let mut proof_data = proof_info.data.borrow_mut(); - proof_data[0] = Proof::discriminator() as u8; - let proof = Proof::try_from_bytes_mut(&mut proof_data)?; - proof.authority = *signer.key; + let clock = Clock::get()?; + let proof = proof_info.to_account_mut::(&ore_api::ID)?; + proof.authority = *signer_info.key; proof.balance = 0; proof.challenge = hashv(&[ - signer.key.as_ref(), + signer_info.key.as_ref(), &slot_hashes_info.data.borrow()[0..size_of::()], ]) .0; diff --git a/program/src/reset.rs b/program/src/reset.rs index a3abcfa..b00a6ba 100644 --- a/program/src/reset.rs +++ b/program/src/reset.rs @@ -1,47 +1,54 @@ -use ore_api::{ - consts::*, - error::OreError, - loaders::*, - state::{Bus, Config}, -}; -use solana_program::{ - account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, - program_error::ProgramError, program_pack::Pack, sysvar::Sysvar, -}; -use spl_token::state::Mint; +use ore_api::prelude::*; use steel::*; /// Reset tops up the bus balances, updates the base reward rate, and sets up the ORE program for the next epoch. pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult { // Load accounts. - let [signer, bus_0_info, bus_1_info, bus_2_info, bus_3_info, bus_4_info, bus_5_info, bus_6_info, bus_7_info, config_info, mint_info, treasury_info, treasury_tokens_info, token_program] = + let [signer_info, bus_0_info, bus_1_info, bus_2_info, bus_3_info, bus_4_info, bus_5_info, bus_6_info, bus_7_info, config_info, mint_info, treasury_info, treasury_tokens_info, token_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; - load_signer(signer)?; - load_bus(bus_0_info, 0, true)?; - load_bus(bus_1_info, 1, true)?; - load_bus(bus_2_info, 2, true)?; - load_bus(bus_3_info, 3, true)?; - load_bus(bus_4_info, 4, true)?; - load_bus(bus_5_info, 5, true)?; - load_bus(bus_6_info, 6, true)?; - load_bus(bus_7_info, 7, true)?; - load_config(config_info, true)?; - load_mint(mint_info, MINT_ADDRESS, true)?; - load_treasury(treasury_info, true)?; - load_treasury_tokens(treasury_tokens_info, true)?; - load_program(token_program, spl_token::id())?; - let busses: [&AccountInfo; BUS_COUNT] = [ - bus_0_info, bus_1_info, bus_2_info, bus_3_info, bus_4_info, bus_5_info, bus_6_info, - bus_7_info, - ]; + signer_info.is_signer()?; + let bus_0 = bus_0_info + .to_account_mut::(&ore_api::ID)? + .check_mut(|b| b.id == 0)?; + let bus_1 = bus_1_info + .to_account_mut::(&ore_api::ID)? + .check_mut(|b| b.id == 1)?; + let bus_2 = bus_2_info + .to_account_mut::(&ore_api::ID)? + .check_mut(|b| b.id == 2)?; + let bus_3 = bus_3_info + .to_account_mut::(&ore_api::ID)? + .check_mut(|b| b.id == 3)?; + let bus_4 = bus_4_info + .to_account_mut::(&ore_api::ID)? + .check_mut(|b| b.id == 4)?; + let bus_5 = bus_5_info + .to_account_mut::(&ore_api::ID)? + .check_mut(|b| b.id == 5)?; + let bus_6 = bus_6_info + .to_account_mut::(&ore_api::ID)? + .check_mut(|b| b.id == 6)?; + let bus_7 = bus_7_info + .to_account_mut::(&ore_api::ID)? + .check_mut(|b| b.id == 7)?; + let config = config_info + .is_config()? + .to_account_mut::(&ore_api::ID)?; + let mint = mint_info + .has_address(&MINT_ADDRESS)? + .is_writable()? + .to_mint()?; + treasury_info.is_treasury()?.is_writable()?; + treasury_tokens_info.is_treasury_tokens()?.is_writable()?; + token_program.is_program(&spl_token::ID)?; // Validate enough time has passed since the last reset. - let mut config_data = config_info.data.borrow_mut(); - let config = Config::try_from_bytes_mut(&mut config_data)?; - let clock = Clock::get().or(Err(ProgramError::InvalidAccountData))?; + // let mut config_data = config_info.data.borrow_mut(); + // let config = Config::try_from_bytes_mut(&mut config_data)?; + let clock = Clock::get()?; if config .last_reset_at .saturating_add(EPOCH_DURATION) @@ -54,14 +61,11 @@ pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul config.last_reset_at = clock.unix_timestamp; // Reset bus accounts and calculate actual rewards mined since last reset. + let busses = [bus_0, bus_1, bus_2, bus_3, bus_4, bus_5, bus_6, bus_7]; let mut total_remaining_rewards = 0u64; let mut total_theoretical_rewards = 0u64; let mut top_balance = 0u64; - for i in 0..BUS_COUNT { - // Parse bus account. - let mut bus_data = busses[i].data.borrow_mut(); - let bus = Bus::try_from_bytes_mut(&mut bus_data)?; - + for bus in busses { // Track top balance. if bus.top_balance.gt(&top_balance) { top_balance = bus.top_balance; @@ -99,7 +103,6 @@ pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul } // Max supply check. - let mint = Mint::unpack(&mint_info.data.borrow()).expect("Failed to parse mint"); if mint.supply.ge(&MAX_SUPPLY) { return Err(OreError::MaxSupply.into()); } diff --git a/program/src/stake.rs b/program/src/stake.rs index 08f33b5..7a42d80 100644 --- a/program/src/stake.rs +++ b/program/src/stake.rs @@ -1,8 +1,4 @@ -use ore_api::{consts::*, instruction::Stake, loaders::*, state::Proof}; -use solana_program::{ - account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, - program_error::ProgramError, sysvar::Sysvar, -}; +use ore_api::prelude::*; use steel::*; /// Stake deposits ORE into a proof account to earn multiplier. @@ -12,27 +8,32 @@ pub fn process_stake(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult let amount = u64::from_le_bytes(args.amount); // Load accounts. - let [signer, proof_info, sender_info, treasury_tokens_info, token_program] = accounts else { + let [signer_info, proof_info, sender_info, treasury_tokens_info, token_program] = accounts + else { return Err(ProgramError::NotEnoughAccountKeys); }; - load_signer(signer)?; - load_proof(proof_info, signer.key, true)?; - load_token_account(sender_info, Some(signer.key), &MINT_ADDRESS, true)?; - load_treasury_tokens(treasury_tokens_info, true)?; - load_program(token_program, spl_token::id())?; + signer_info.is_signer()?; + let proof = proof_info + .to_account_mut::(&ore_api::ID)? + .check_mut(|p| p.authority == *signer_info.key)?; + sender_info + .is_writable()? + .to_token_account()? + .check(|t| t.owner == *signer_info.key)? + .check(|t| t.mint == MINT_ADDRESS)?; + treasury_tokens_info.is_writable()?.is_treasury_tokens()?; + token_program.is_program(&spl_token::ID)?; // Update the proof balance. - let mut proof_data = proof_info.data.borrow_mut(); - let proof = Proof::try_from_bytes_mut(&mut proof_data)?; proof.balance = proof.balance.checked_add(amount).unwrap(); // Update deposit timestamp. - let clock = Clock::get().or(Err(ProgramError::InvalidAccountData))?; + let clock = Clock::get()?; proof.last_stake_at = clock.unix_timestamp; // Transfer tokens from signer to treasury. transfer( - signer, + signer_info, sender_info, treasury_tokens_info, token_program, diff --git a/program/src/update.rs b/program/src/update.rs index 6ffdf3b..4f153a6 100644 --- a/program/src/update.rs +++ b/program/src/update.rs @@ -1,22 +1,18 @@ -use ore_api::{loaders::*, state::Proof}; -use solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, -}; +use ore_api::prelude::*; use steel::*; /// Update changes the miner authority on a proof account. pub fn process_update(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult { // Load accounts. - let [signer, miner_info, proof_info] = accounts else { + let [signer_info, miner_info, proof_info] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; - load_signer(signer)?; - load_any(miner_info, false)?; - load_proof(proof_info, signer.key, true)?; + signer_info.is_signer()?; + let proof = proof_info + .to_account_mut::(&ore_api::ID)? + .check_mut(|p| p.authority == *signer_info.key)?; // Update the proof's miner authority. - let mut proof_data = proof_info.data.borrow_mut(); - let proof = Proof::try_from_bytes_mut(&mut proof_data)?; proof.miner = *miner_info.key; Ok(()) diff --git a/program/src/upgrade.rs b/program/src/upgrade.rs index 7530117..74090d0 100644 --- a/program/src/upgrade.rs +++ b/program/src/upgrade.rs @@ -1,9 +1,4 @@ -use ore_api::{consts::*, error::OreError, instruction::Stake}; -use solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, - program_pack::Pack, -}; -use spl_token::state::Mint; +use ore_api::prelude::*; use steel::*; /// Upgrade allows a user to migrate a v1 token to a v2 token at a 1:1 exchange rate. @@ -13,17 +8,32 @@ pub fn process_upgrade(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResu let amount = u64::from_le_bytes(args.amount); // Load accounts - let [signer, beneficiary_info, mint_info, mint_v1_info, sender_info, treasury_info, token_program] = + let [signer_info, beneficiary_info, mint_info, mint_v1_info, sender_info, treasury_info, token_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; - load_signer(signer)?; - load_token_account(beneficiary_info, Some(&signer.key), &MINT_ADDRESS, true)?; - load_mint(mint_info, MINT_ADDRESS, true)?; - load_mint(mint_v1_info, MINT_V1_ADDRESS, true)?; - load_token_account(sender_info, Some(signer.key), &MINT_V1_ADDRESS, true)?; - load_program(token_program, spl_token::id())?; + signer_info.is_signer()?; + beneficiary_info + .is_writable()? + .to_token_account()? + .check(|t| t.owner == *signer_info.key)? + .check(|t| t.mint == MINT_ADDRESS)?; + let mint = mint_info + .is_writable()? + .has_address(&MINT_ADDRESS)? + .to_mint()?; + mint_v1_info + .is_writable()? + .has_address(&MINT_V1_ADDRESS)? + .to_mint()?; + sender_info + .is_writable()? + .to_token_account()? + .check(|t| t.owner == *signer_info.key)? + .check(|t| t.mint == MINT_V1_ADDRESS)?; + treasury_info.is_treasury()?; + token_program.is_program(&spl_token::ID)?; // Burn v1 tokens solana_program::program::invoke( @@ -31,15 +41,15 @@ pub fn process_upgrade(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResu &spl_token::id(), sender_info.key, mint_v1_info.key, - signer.key, - &[signer.key], + signer_info.key, + &[signer_info.key], amount, )?, &[ token_program.clone(), sender_info.clone(), mint_v1_info.clone(), - signer.clone(), + signer_info.clone(), ], )?; @@ -48,14 +58,11 @@ pub fn process_upgrade(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResu let amount_to_mint = amount.saturating_mul(100); // Cap at max supply. - let mint_data = mint_info.data.borrow(); - let mint = Mint::unpack(&mint_data)?; if mint.supply.saturating_add(amount_to_mint).gt(&MAX_SUPPLY) { return Err(OreError::MaxSupply.into()); } // Mint to the beneficiary account - drop(mint_data); mint_to_signed( mint_info, beneficiary_info,