This commit is contained in:
Hardhat Chad
2025-06-09 14:25:02 -07:00
parent b3c4f205d8
commit 8be4f68a00
10 changed files with 228 additions and 5 deletions

View File

@@ -5,3 +5,8 @@
## Programs
- [`ore`](ore/)  ORE mining program.
- [`ore-delegate`](ore-delegate/)  Delegate mining to another party.
TODO
- [ ] Hash token metadata
- [ ]

View File

@@ -32,6 +32,9 @@ pub const MINER: &[u8] = b"miner";
/// The seed of the mint account PDA.
pub const MINT: &[u8] = b"mint";
/// The seed of the permit account PDA.
pub const PERMIT: &[u8] = b"permit";
/// The seed of the treasury account PDA.
pub const TREASURY: &[u8] = b"treasury";

View File

@@ -5,8 +5,10 @@ use steel::*;
pub enum OreInstruction {
Open = 0,
Close = 1,
Mine = 2,
Swap = 3,
Commit = 2,
Decommit = 3,
Mine = 4,
Swap = 5,
}
#[repr(C)]
@@ -19,6 +21,18 @@ pub struct Open {
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct Close {}
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct Commit {
pub amount: [u8; 8],
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct Decommit {
pub amount: [u8; 8],
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct Mine {
@@ -35,5 +49,7 @@ pub struct Swap {
instruction!(OreInstruction, Open);
instruction!(OreInstruction, Close);
instruction!(OreInstruction, Commit);
instruction!(OreInstruction, Decommit);
instruction!(OreInstruction, Mine);
instruction!(OreInstruction, Swap);

View File

@@ -2,12 +2,14 @@ mod block;
mod config;
mod market;
mod miner;
mod permit;
mod treasury;
pub use block::*;
pub use config::*;
pub use market::*;
pub use miner::*;
pub use permit::*;
pub use treasury::*;
use crate::consts::*;
@@ -21,7 +23,8 @@ pub enum OreAccount {
Config = 101,
Market = 102,
Miner = 103,
Treasury = 104,
Permit = 104,
Treasury = 105,
}
pub fn block_pda(id: u64) -> (Pubkey, u8) {
@@ -44,6 +47,13 @@ pub fn mint_pda(id: u64) -> (Pubkey, u8) {
Pubkey::find_program_address(&[MINT, &id.to_le_bytes()], &crate::ID)
}
pub fn permit_pda(authority: Pubkey, block_id: u64) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[PERMIT, &authority.to_bytes(), &block_id.to_le_bytes()],
&crate::ID,
)
}
pub fn treasury_pda() -> (Pubkey, u8) {
Pubkey::find_program_address(&[TREASURY], &crate::ID)
}

View File

@@ -0,0 +1,26 @@
use steel::*;
use crate::state::permit_pda;
use super::OreAccount;
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct Permit {
/// The amount of ORE this miner has mined.
pub amount: u64,
/// The authority of this miner account.
pub authority: Pubkey,
/// The ID of the last block this miner mined in.
pub block_id: u64,
}
impl Permit {
pub fn pda(&self) -> (Pubkey, u8) {
permit_pda(self.authority, self.block_id)
}
}
account!(OreAccount, Permit);

73
ore/program/src/commit.rs Normal file
View File

@@ -0,0 +1,73 @@
use ore_api::prelude::*;
use steel::*;
/// Commit to a block.
pub fn process_commit(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
// Parse data.
let args = Commit::try_from_bytes(data)?;
let amount = u64::from_le_bytes(args.amount);
// Load accounts.
let clock = Clock::get()?;
let [signer_info, block_info, commitment_info, market_info, miner_info, mint_info, permit_info, sender_info, system_program, token_program, slot_hashes_sysvar] =
accounts
else {
return Err(ProgramError::NotEnoughAccountKeys);
};
signer_info.is_signer()?;
let block = block_info
.as_account::<Block>(&ore_api::ID)?
.assert(|b| clock.slot < b.start_slot)?;
commitment_info.as_associated_token_account(block_info.key, mint_info.key)?;
let market = market_info
.as_account::<Market>(&ore_api::ID)?
.assert(|m| m.id == block.id)?;
miner_info
.as_account::<Miner>(&ore_api::ID)?
.assert(|m| m.authority == *signer_info.key)?;
mint_info.has_address(&market.base.mint)?.as_mint()?;
let sender = sender_info
.is_writable()?
.as_associated_token_account(signer_info.key, &mint_info.key)?;
system_program.is_program(&system_program::ID)?;
token_program.is_program(&spl_token::ID)?;
slot_hashes_sysvar.is_sysvar(&sysvar::slot_hashes::ID)?;
// Normalize amount.
let amount = sender.amount().min(amount);
// Load permit account.
let permit = if permit_info.data_is_empty() {
create_program_account::<Permit>(
permit_info,
system_program,
signer_info,
&ore_api::ID,
&[PERMIT, &signer_info.key.to_bytes(), &block.id.to_le_bytes()],
)?;
let permit = permit_info.as_account_mut::<Permit>(&ore_api::ID)?;
permit.authority = *signer_info.key;
permit.block_id = block.id;
permit.amount = 0;
permit
} else {
permit_info
.as_account_mut::<Permit>(&ore_api::ID)?
.assert_mut(|p| p.authority == *signer_info.key)?
.assert_mut(|p| p.block_id == block.id)?
};
// Transfer hash tokens.
transfer(
signer_info,
sender_info,
commitment_info,
token_program,
amount,
)?;
// Update block.
permit.amount += amount;
Ok(())
}

View File

@@ -0,0 +1,64 @@
use ore_api::prelude::*;
use steel::*;
/// Decommit from a block.
pub fn process_decommit(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
// Parse data.
let args = Decommit::try_from_bytes(data)?;
let amount = u64::from_le_bytes(args.amount);
// Load accounts.
let clock = Clock::get()?;
let [signer_info, block_info, commitment_info, market_info, miner_info, mint_info, permit_info, recipient_info, system_program, token_program, slot_hashes_sysvar] =
accounts
else {
return Err(ProgramError::NotEnoughAccountKeys);
};
signer_info.is_signer()?;
let block = block_info
.as_account::<Block>(&ore_api::ID)?
.assert(|b| clock.slot < b.start_slot)?;
commitment_info
.is_writable()?
.as_associated_token_account(block_info.key, mint_info.key)?;
let market = market_info
.as_account::<Market>(&ore_api::ID)?
.assert(|m| m.id == block.id)?;
miner_info
.as_account::<Miner>(&ore_api::ID)?
.assert(|m| m.authority == *signer_info.key)?;
mint_info.has_address(&market.base.mint)?.as_mint()?;
let permit = permit_info
.as_account_mut::<Permit>(&ore_api::ID)?
.assert_mut(|p| p.authority == *signer_info.key)?
.assert_mut(|p| p.block_id == block.id)?;
recipient_info
.is_writable()?
.as_associated_token_account(signer_info.key, &mint_info.key)?;
system_program.is_program(&system_program::ID)?;
token_program.is_program(&spl_token::ID)?;
slot_hashes_sysvar.is_sysvar(&sysvar::slot_hashes::ID)?;
// Normalize amount.
let amount = permit.amount.min(amount);
// Transfer hash tokens.
transfer_signed(
block_info,
commitment_info,
recipient_info,
token_program,
amount,
&[BLOCK, &block.id.to_le_bytes()],
)?;
// Update block.
permit.amount -= amount;
// Close permit, if empty.
if permit.amount == 0 {
permit_info.close(signer_info)?;
}
Ok(())
}

View File

@@ -1,9 +1,15 @@
mod close;
mod commit;
mod decommit;
mod mine;
mod open;
mod swap;
use core::panic;
use close::*;
use commit::*;
use decommit::*;
use mine::*;
use open::*;
use swap::*;
@@ -21,6 +27,8 @@ pub fn process_instruction(
match ix {
OreInstruction::Open => process_open(accounts, data)?,
OreInstruction::Close => process_close(accounts, data)?,
OreInstruction::Commit => process_commit(accounts, data)?,
OreInstruction::Decommit => process_decommit(accounts, data)?,
OreInstruction::Mine => process_mine(accounts, data)?,
OreInstruction::Swap => process_swap(accounts, data)?,
}

View File

@@ -11,7 +11,7 @@ pub fn process_mine(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult
// Load accounts.
let clock = Clock::get()?;
let [signer_info, block_info, market_info, miner_info, mint_hash_info, mint_ore_info, recipient_info, sender_info, treasury_info, system_program, token_program, slot_hashes_sysvar] =
let [signer_info, block_info, market_info, miner_info, mint_hash_info, mint_ore_info, permit_info, recipient_info, sender_info, treasury_info, system_program, token_program, slot_hashes_sysvar] =
accounts
else {
return Err(ProgramError::NotEnoughAccountKeys);
@@ -26,6 +26,9 @@ pub fn process_mine(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult
.assert(|m| m.id == block.id)?;
mint_hash_info.has_address(&market.base.mint)?.as_mint()?;
mint_ore_info.has_address(&MINT_ADDRESS)?.as_mint()?;
permit_info
.as_account::<Permit>(&ore_api::ID)?
.assert(|p| p.authority == *signer_info.key)?;
recipient_info
.is_writable()?
.as_associated_token_account(signer_info.key, &MINT_ADDRESS)?;

View File

@@ -11,7 +11,7 @@ pub fn process_open(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult
// Load accounts.
let clock = Clock::get()?;
let [signer_info, block_info, market_info, mint_base_info, mint_quote_info, vault_base_info, vault_quote_info, system_program, token_program, associated_token_program, rent_sysvar] =
let [signer_info, block_info, commitment_info, market_info, mint_base_info, mint_quote_info, vault_base_info, vault_quote_info, system_program, token_program, associated_token_program, rent_sysvar] =
accounts
else {
return Err(ProgramError::NotEnoughAccountKeys);
@@ -116,6 +116,21 @@ pub fn process_open(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult
// TODO Initialize hash token metadata.
// Initialize commitment token account.
if commitment_info.data_is_empty() {
create_associated_token_account(
signer_info,
block_info,
commitment_info,
mint_base_info,
system_program,
token_program,
associated_token_program,
)?;
} else {
commitment_info.as_associated_token_account(block_info.key, mint_base_info.key)?;
}
// Initialize vault token accounts.
if vault_base_info.data_is_empty() {
create_associated_token_account(