This commit is contained in:
Hardhat Chad
2025-05-29 09:58:05 -07:00
parent 6507372413
commit 79a9ac3b40
17 changed files with 182 additions and 167 deletions

View File

@@ -9,18 +9,18 @@
- [`Instruction`](api/src/instruction.rs)  Declared instructions and arguments.
## Instructions
- [`Bet`](program/src/bet.rs) - Open a wager.
- [`Bury`](program/src/bury.rs) - Swap wagered tokens into ORE and burns it.
- [`Close`](program/src/close.rs) - Close a wager account.
- [`Bury`](program/src/bury.rs) - Swap committed tokens into ORE and burns it.
- [`Close`](program/src/close.rs) - Close a commit account.
- [`Deploy`](program/src/deploy.rs) - Deploy capital to mine the current block.
- [`Initialize`](program/src/initialize.rs) - Initialize the program.
- [`Payout`](program/src/payout.rs) - Payout the block reward to the winning wager.
- [`Payout`](program/src/payout.rs) - Payout the block reward to the winning commit.
- [`Reset`](program/src/reset.rs) - Start the next block.
## State
- [`Block`](api/src/state/block.rs) - A singleton account tracking rounds of wagering.
- [`Block`](api/src/state/block.rs) - A singleton account tracking rounds of commits.
- [`Proof`](api/src/state/proof.rs) - (Deprecated) An account which tracks a miner's current hash and current stake.
- [`Treasury`](api/src/state/treasury.rs) The mint authority on the ORE token.
- [`Wager`](api/src/state/wager.rs) - A bet placed by a user.
- [`Commit`](api/src/state/commit.rs) - Capital deployed by a miner in the current block.
## Tests

View File

@@ -20,8 +20,8 @@ pub const MAX_SUPPLY: u64 = ONE_ORE * 5_000_000;
/// The seed of the block account PDA.
pub const BLOCK: &[u8] = b"block";
/// The seed of the wager account PDA.
pub const WAGER: &[u8] = b"wager";
/// The seed of the commit account PDA.
pub const COMMIT: &[u8] = b"commit";
/// The seed of the config account PDA.
pub const CONFIG: &[u8] = b"config";

View File

@@ -2,15 +2,15 @@ use steel::*;
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct BetEvent {
pub authority: Pubkey,
pub struct BuryEvent {
pub amount: u64,
pub ts: u64,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct BuryEvent {
pub struct DeployEvent {
pub authority: Pubkey,
pub amount: u64,
pub ts: u64,
}
@@ -23,6 +23,6 @@ pub struct PayoutEvent {
pub ts: u64,
}
event!(BetEvent);
event!(BuryEvent);
event!(DeployEvent);
event!(PayoutEvent);

View File

@@ -5,9 +5,9 @@ use steel::*;
pub enum OreInstruction {
// User
Claim = 0,
Bet = 1,
Bury = 2,
Close = 3,
Bury = 1,
Close = 2,
Deploy = 3,
Payout = 4,
Reset = 5,
@@ -17,15 +17,10 @@ pub enum OreInstruction {
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct Bet {
pub struct Bury {
pub amount: [u8; 8],
pub seed: [u8; 32],
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct Bury {}
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct Claim {
@@ -36,6 +31,13 @@ pub struct Claim {
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct Close {}
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct Deploy {
pub amount: [u8; 8],
pub seed: [u8; 32],
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct Reset {}
@@ -49,9 +51,9 @@ pub struct Payout {}
pub struct Initialize {}
instruction!(OreInstruction, Claim);
instruction!(OreInstruction, Bet);
instruction!(OreInstruction, Bury);
instruction!(OreInstruction, Close);
instruction!(OreInstruction, Deploy);
instruction!(OreInstruction, Payout);
instruction!(OreInstruction, Reset);
instruction!(OreInstruction, Initialize);

View File

@@ -8,24 +8,30 @@ use crate::{
state::*,
};
pub fn bet(signer: Pubkey, mint: Pubkey, amount: u64, round: u64, seed: [u8; 32]) -> Instruction {
pub fn deploy(
signer: Pubkey,
mint: Pubkey,
amount: u64,
round: u64,
seed: [u8; 32],
) -> Instruction {
let sender = spl_associated_token_account::get_associated_token_address(&signer, &mint);
let block = block_pda().0;
let block_bets = spl_associated_token_account::get_associated_token_address(&block, &mint);
let wager = wager_pda(round, seed).0;
let block_commits = spl_associated_token_account::get_associated_token_address(&block, &mint);
let commit = commit_pda(round, seed).0;
Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(signer, true),
AccountMeta::new(block, false),
AccountMeta::new(block_bets, false),
AccountMeta::new(block_commits, false),
AccountMeta::new(commit, false),
AccountMeta::new(sender, false),
AccountMeta::new(wager, false),
AccountMeta::new_readonly(system_program::ID, false),
AccountMeta::new_readonly(spl_token::ID, false),
AccountMeta::new_readonly(sysvar::slot_hashes::ID, false),
],
data: Bet {
data: Deploy {
amount: amount.to_le_bytes(),
seed,
}
@@ -33,9 +39,9 @@ pub fn bet(signer: Pubkey, mint: Pubkey, amount: u64, round: u64, seed: [u8; 32]
}
}
pub fn bury(signer: Pubkey, swap: Swap) -> Instruction {
pub fn bury(signer: Pubkey, swap: Swap, amount: u64) -> Instruction {
let block = block_pda().0;
let block_bets = spl_associated_token_account::get_associated_token_address(
let block_commits = spl_associated_token_account::get_associated_token_address(
&block,
&spl_token::native_mint::ID,
);
@@ -47,7 +53,7 @@ pub fn bury(signer: Pubkey, swap: Swap) -> Instruction {
// required accounts
AccountMeta::new(signer, true),
AccountMeta::new(block, false),
AccountMeta::new(block_bets, false),
AccountMeta::new(block_commits, false),
AccountMeta::new(block_ore, false),
AccountMeta::new(spl_token::native_mint::ID, false),
AccountMeta::new(MINT_ADDRESS, false),
@@ -66,18 +72,21 @@ pub fn bury(signer: Pubkey, swap: Swap) -> Instruction {
AccountMeta::new_readonly(spl_token::ID, false),
AccountMeta::new_readonly(meteora_pools_sdk::programs::AMM_ID, false),
],
data: Bury {}.to_bytes(),
data: Bury {
amount: amount.to_le_bytes(),
}
.to_bytes(),
}
}
pub fn close(signer: Pubkey, wager: Pubkey) -> Instruction {
pub fn close(signer: Pubkey, commit: Pubkey) -> Instruction {
let block = block_pda().0;
Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(signer, true),
AccountMeta::new(block, false),
AccountMeta::new(wager, false),
AccountMeta::new(commit, false),
AccountMeta::new_readonly(system_program::ID, false),
],
data: Close {}.to_bytes(),
@@ -86,7 +95,7 @@ pub fn close(signer: Pubkey, wager: Pubkey) -> Instruction {
pub fn initialize(signer: Pubkey) -> Instruction {
let block = block_pda().0;
let block_bets =
let block_commits =
spl_associated_token_account::get_associated_token_address(&block, &native_mint::ID);
let block_ore =
spl_associated_token_account::get_associated_token_address(&block, &MINT_ADDRESS);
@@ -95,7 +104,7 @@ pub fn initialize(signer: Pubkey) -> Instruction {
accounts: vec![
AccountMeta::new(signer, true),
AccountMeta::new(block, false),
AccountMeta::new(block_bets, false),
AccountMeta::new(block_commits, false),
AccountMeta::new(block_ore, false),
AccountMeta::new_readonly(MINT_ADDRESS, false),
AccountMeta::new_readonly(native_mint::ID, false),
@@ -107,15 +116,15 @@ pub fn initialize(signer: Pubkey) -> Instruction {
}
}
pub fn payout(signer: Pubkey, wager: Pubkey, recipient: Pubkey) -> Instruction {
pub fn payout(signer: Pubkey, commit: Pubkey, recipient: Pubkey) -> Instruction {
let block = block_pda().0;
Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(signer, true),
AccountMeta::new(block, false),
AccountMeta::new(commit, false),
AccountMeta::new(MINT_ADDRESS, false),
AccountMeta::new(wager, false),
AccountMeta::new(recipient, false),
AccountMeta::new(TREASURY_ADDRESS, false),
AccountMeta::new(TREASURY_TOKENS_ADDRESS, false),

View File

@@ -5,7 +5,7 @@ use super::OreAccount;
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct Block {
/// The cumulative amount risked in the current round.
/// The cumulative amount deployed in the current round.
pub cumulative_sum: u64,
/// The current round.
@@ -14,7 +14,7 @@ pub struct Block {
/// The slot at which the current round ends.
pub ends_at: u64,
/// The mint used for wagers of the current round.
/// The mint used for commits of the current round.
pub mint: Pubkey,
/// The noise used for the current round for provably fair randomness.
@@ -29,8 +29,8 @@ pub struct Block {
/// The time the current round started at.
pub started_at: u64,
/// The number of wagers made in the current round.
pub total_wagers: u64,
/// The number of commits made in the current round.
pub total_commits: u64,
}
account!(OreAccount, Block);

27
api/src/state/commit.rs Normal file
View File

@@ -0,0 +1,27 @@
use steel::*;
use super::OreAccount;
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct Commit {
/// The amount deployed in this commit.
pub amount: u64,
/// The signer authorized to use this commit.
pub authority: Pubkey,
/// The cumulative amount deployed in the current round prior to this commit.
pub cumulative_sum: u64,
/// The current round this commit is for.
pub round: u64,
/// The ID of the commit, used for provably fair randomness.
pub seed: [u8; 32],
/// The timestamp of the commit.
pub timestamp: u64,
}
account!(OreAccount, Commit);

View File

@@ -1,12 +1,12 @@
mod block;
mod commit;
mod proof;
mod treasury;
mod wager;
pub use block::*;
pub use commit::*;
pub use proof::*;
pub use treasury::*;
pub use wager::*;
use steel::*;
@@ -18,7 +18,7 @@ pub enum OreAccount {
Proof = 102,
Treasury = 103,
Block = 104,
Wager = 105,
Commit = 105,
}
pub fn block_pda() -> (Pubkey, u8) {
@@ -33,6 +33,6 @@ pub fn treasury_pda() -> (Pubkey, u8) {
Pubkey::find_program_address(&[TREASURY], &crate::ID)
}
pub fn wager_pda(round: u64, seed: [u8; 32]) -> (Pubkey, u8) {
Pubkey::find_program_address(&[WAGER, &round.to_le_bytes(), &seed], &crate::ID)
pub fn commit_pda(round: u64, seed: [u8; 32]) -> (Pubkey, u8) {
Pubkey::find_program_address(&[COMMIT, &round.to_le_bytes(), &seed], &crate::ID)
}

View File

@@ -1,27 +0,0 @@
use steel::*;
use super::OreAccount;
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct Wager {
/// The amount bet in this wager.
pub amount: u64,
/// The signer authorized to use this wager.
pub authority: Pubkey,
/// The cumulative amount bet in the current round prior to this wager.
pub cumulative_sum: u64,
/// The current round this wager is for.
pub round: u64,
/// The ID of the wager, used for provably fair randomness.
pub seed: [u8; 32],
/// The timestamp of the wager.
pub timestamp: u64,
}
account!(OreAccount, Wager);

View File

@@ -49,15 +49,15 @@ async fn main() {
"crank" => {
crank(&rpc, &payer).await.unwrap();
}
"bet" => {
bet(&rpc, &payer, sol_to_lamports(1.0)).await.unwrap();
"deploy" => {
deploy(&rpc, &payer, sol_to_lamports(1.0)).await.unwrap();
}
"close" => {
close_all_wagers(&rpc, &payer).await.unwrap();
close_all_commits(&rpc, &payer).await.unwrap();
}
"wagers" => {
let wagers = get_block_wagers(&rpc).await.unwrap();
println!("Wagers: {:?}", wagers);
"commits" => {
let commits = get_block_commits(&rpc).await.unwrap();
println!("Commits: {:?}", commits);
}
"bury" => {
bury_ore_sol(&rpc, &payer).await.unwrap();
@@ -88,10 +88,10 @@ async fn crank(
let slots_remaining = block.ends_at.saturating_sub(clock.slot);
let seconds_remaining = (slots_remaining as f64) * 0.4;
println!(
"Time until payout: {:.1} seconds ({} slots) {} wagers {} SOL",
"Time until payout: {:.1} seconds ({} slots) {} commits {} SOL",
seconds_remaining,
slots_remaining,
block.total_wagers,
block.total_commits,
lamports_to_sol(block.cumulative_sum)
);
}
@@ -107,7 +107,7 @@ async fn crank(
}
}
async fn bet(
async fn deploy(
rpc: &RpcClient,
payer: &solana_sdk::signer::keypair::Keypair,
amount: u64,
@@ -149,10 +149,10 @@ async fn bet(
)
.unwrap();
// Build bet instruction
// Build deploy instruction
let seed = generate_seed(&payer, &block);
println!("Seed: {:?}", seed);
let ix = ore_api::sdk::bet(
let ix = ore_api::sdk::deploy(
payer.pubkey(),
spl_token::native_mint::ID,
amount,
@@ -165,7 +165,7 @@ async fn bet(
// Submit transaction
submit_transaction(rpc, payer, &ixs).await?;
println!("Placed bet of {} lamports", amount);
println!("Deployed {} SOL", lamports_to_sol(amount));
Ok(())
}
@@ -191,7 +191,7 @@ async fn bury_ore_sol(
vault_program: pubkey!("24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi"),
token_program: spl_token::ID,
};
let ix = bury(payer.pubkey(), swap);
let ix = bury(payer.pubkey(), swap, u64::MAX);
submit_transaction(rpc, payer, &[ix]).await?;
Ok(())
}
@@ -201,9 +201,9 @@ async fn build_payout_ix(
payer: &solana_sdk::signer::keypair::Keypair,
) -> Result<Instruction, anyhow::Error> {
let block = get_block(rpc).await?;
let wagers = get_block_wagers(rpc).await?;
let commits = get_block_commits(rpc).await?;
// Return early if no wagers
// Return early if no commits
if block.cumulative_sum == 0 || block.reward == 0 {
return Ok(payout(
payer.pubkey(),
@@ -224,23 +224,23 @@ async fn build_payout_ix(
let w = u64::from_le_bytes(noise[24..32].try_into().unwrap());
let roll = (x ^ y ^ z ^ w) % block.cumulative_sum;
// Find the winning wager
// Find the winning commit
let mut winner = None;
for (pubkey, wager) in wagers {
if roll >= wager.cumulative_sum && roll < wager.cumulative_sum + wager.amount {
for (pubkey, commit) in commits {
if roll >= commit.cumulative_sum && roll < commit.cumulative_sum + commit.amount {
println!("Roll: {}, Winner: {:?}", roll, pubkey);
winner = Some((pubkey, wager));
winner = Some((pubkey, commit));
break;
}
}
// Build payout instruction
let ix = if let Some((pubkey, wager)) = winner {
let ix = if let Some((pubkey, commit)) = winner {
payout(
payer.pubkey(),
pubkey,
spl_associated_token_account::get_associated_token_address(
&wager.authority,
&commit.authority,
&spl_token::native_mint::ID,
),
)
@@ -251,15 +251,15 @@ async fn build_payout_ix(
Ok(ix)
}
async fn close_all_wagers(
async fn close_all_commits(
rpc: &RpcClient,
payer: &solana_sdk::signer::keypair::Keypair,
) -> Result<(), anyhow::Error> {
let block = get_block(rpc).await?;
let wagers = get_my_wagers(rpc, payer).await?;
let commits = get_my_commits(rpc, payer).await?;
let mut ixs = vec![];
for (pubkey, wager) in wagers {
if wager.round != block.current_round {
for (pubkey, commit) in commits {
if commit.round != block.current_round {
let ix = ore_api::sdk::close(payer.pubkey(), pubkey);
ixs.push(ix);
}
@@ -281,26 +281,26 @@ async fn get_clock(rpc: &RpcClient) -> Result<Clock, anyhow::Error> {
Ok(clock)
}
async fn get_block_wagers(rpc: &RpcClient) -> Result<Vec<(Pubkey, Wager)>, anyhow::Error> {
async fn get_block_commits(rpc: &RpcClient) -> Result<Vec<(Pubkey, Commit)>, anyhow::Error> {
let block = get_block(rpc).await?;
let filter = RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
56,
&block.current_round.to_le_bytes(),
));
let wagers = get_program_accounts::<Wager>(rpc, ore_api::ID, vec![filter]).await?;
Ok(wagers)
let commits = get_program_accounts::<Commit>(rpc, ore_api::ID, vec![filter]).await?;
Ok(commits)
}
async fn get_my_wagers(
async fn get_my_commits(
rpc: &RpcClient,
payer: &solana_sdk::signer::keypair::Keypair,
) -> Result<Vec<(Pubkey, Wager)>, anyhow::Error> {
) -> Result<Vec<(Pubkey, Commit)>, anyhow::Error> {
let filter = RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
16,
&payer.pubkey().to_bytes().as_ref(),
));
let wagers = get_program_accounts::<Wager>(rpc, ore_api::ID, vec![filter]).await?;
Ok(wagers)
let commits = get_program_accounts::<Commit>(rpc, ore_api::ID, vec![filter]).await?;
Ok(commits)
}
fn generate_seed(payer: &solana_sdk::signer::keypair::Keypair, block: &Block) -> [u8; 32] {

View File

@@ -2,25 +2,29 @@ use meteora_pools_sdk::instructions::{SwapCpi, SwapCpiAccounts, SwapInstructionA
use ore_api::prelude::*;
use steel::*;
/// Swap wagers into ORE and bury the ORE.
pub fn process_bury(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult {
/// Swap commits into ORE and bury the ORE.
pub fn process_bury(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
// Parse data.
let args = Bury::try_from_bytes(data)?;
let amount = u64::from_le_bytes(args.amount);
// Load accounts.
let clock = Clock::get()?;
let (required_accounts, meteora_accounts) = accounts.split_at(6);
let [signer_info, block_info, block_bets_info, block_ore_info, bet_mint_info, ore_mint_info] =
let [signer_info, block_info, block_commits_info, block_ore_info, mint_info, ore_mint_info] =
required_accounts
else {
return Err(ProgramError::NotEnoughAccountKeys);
};
signer_info.is_signer()?.has_address(&ADMIN_ADDRESS)?;
signer_info.is_signer()?; // .has_address(&ADMIN_ADDRESS)?;
block_info.as_account::<Block>(&ore_api::ID)?;
let block_bets = block_bets_info
let block_commits = block_commits_info
.is_writable()?
.as_associated_token_account(block_info.key, bet_mint_info.key)?;
.as_associated_token_account(block_info.key, mint_info.key)?;
block_ore_info
.is_writable()?
.as_associated_token_account(block_info.key, &MINT_ADDRESS)?;
bet_mint_info.as_mint()?;
mint_info.as_mint()?;
ore_mint_info.has_address(&MINT_ADDRESS)?.as_mint()?;
// Load meteora accounts.
@@ -36,7 +40,7 @@ pub fn process_bury(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult
&meteora_pools_program,
SwapCpiAccounts {
pool: pool_info,
user_source_token: block_bets_info,
user_source_token: block_commits_info,
user_destination_token: block_ore_info,
a_vault: a_vault_info,
b_vault: b_vault_info,
@@ -52,7 +56,7 @@ pub fn process_bury(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult
token_program: token_program_info,
},
SwapInstructionArgs {
in_amount: block_bets.amount(),
in_amount: block_commits.amount().min(amount),
minimum_out_amount: 0, // TODO: Calculate minimum out amount with slippage
},
);

View File

@@ -1,22 +1,22 @@
use ore_api::prelude::*;
use steel::*;
/// Close a wager account.
/// Close a commit account.
pub fn process_close(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult {
// Load accounts.
let [signer_info, block_info, wager_info, system_program] = accounts else {
let [signer_info, block_info, commit_info, system_program] = accounts else {
return Err(ProgramError::NotEnoughAccountKeys);
};
signer_info.is_signer()?;
let block = block_info.as_account::<Block>(&ore_api::ID)?;
wager_info
.as_account_mut::<Wager>(&ore_api::ID)?
.assert_mut(|w| w.authority == *signer_info.key)?
.assert_mut(|w| w.round < block.current_round)?;
commit_info
.as_account_mut::<Commit>(&ore_api::ID)?
.assert_mut(|c| c.authority == *signer_info.key)?
.assert_mut(|c| c.round < block.current_round)?;
system_program.is_program(&system_program::ID)?;
// Close the wager account
wager_info.close(&signer_info)?;
// Close the commit account
commit_info.close(&signer_info)?;
Ok(())
}

View File

@@ -2,16 +2,16 @@ use ore_api::prelude::*;
use solana_program::keccak::hashv;
use steel::*;
/// Open a wager.
pub fn process_bet(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
/// Deploy capital to mine the current block.
pub fn process_deploy(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
// Parse data.
let args = Bet::try_from_bytes(data)?;
let args = Deploy::try_from_bytes(data)?;
let amount = u64::from_le_bytes(args.amount);
let seed = args.seed;
// Load accounts.
let clock = Clock::get()?;
let [signer_info, block_info, block_bets_info, sender_info, wager_info, system_program, token_program, slot_hashes_sysvar] =
let [signer_info, block_info, block_commits_info, commit_info, sender_info, system_program, token_program, slot_hashes_sysvar] =
accounts
else {
return Err(ProgramError::NotEnoughAccountKeys);
@@ -21,54 +21,54 @@ pub fn process_bet(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
.as_account_mut::<Block>(&ore_api::ID)?
.assert_mut(|b| b.ends_at > clock.slot)?
.assert_mut(|b| b.paid == 0)?;
block_bets_info
block_commits_info
.is_writable()?
.as_associated_token_account(block_info.key, &block.mint)?;
commit_info.is_writable()?.is_empty()?.has_seeds(
&[COMMIT, &block.current_round.to_le_bytes(), &seed],
&ore_api::ID,
)?;
sender_info
.is_writable()?
.as_associated_token_account(signer_info.key, &block.mint)?;
wager_info.is_writable()?.is_empty()?.has_seeds(
&[WAGER, &block.current_round.to_le_bytes(), &seed],
&ore_api::ID,
)?;
system_program.is_program(&system_program::ID)?;
token_program.is_program(&spl_token::ID)?;
slot_hashes_sysvar.is_sysvar(&sysvar::slot_hashes::ID)?;
// Create wager account.
create_program_account::<Wager>(
&wager_info,
// Create commit account.
create_program_account::<Commit>(
&commit_info,
&system_program,
&signer_info,
&ore_api::ID,
&[WAGER, &block.current_round.to_le_bytes(), &seed],
&[COMMIT, &block.current_round.to_le_bytes(), &seed],
)?;
let wager = wager_info.as_account_mut::<Wager>(&ore_api::ID)?;
wager.amount = amount;
wager.authority = *signer_info.key;
wager.cumulative_sum = block.cumulative_sum;
wager.round = block.current_round;
wager.seed = seed;
wager.timestamp = clock.unix_timestamp as u64;
let commit = commit_info.as_account_mut::<Commit>(&ore_api::ID)?;
commit.amount = amount;
commit.authority = *signer_info.key;
commit.cumulative_sum = block.cumulative_sum;
commit.round = block.current_round;
commit.seed = seed;
commit.timestamp = clock.unix_timestamp as u64;
// Update block stats.
block.cumulative_sum += amount;
block.total_wagers += 1;
block.total_commits += 1;
// Hash client seed into block noise for provably fair randomness.
block.noise = hashv(&[&block.noise, &seed]).to_bytes();
// Transfer wagers.
// Transfer commits.
transfer(
&signer_info,
&sender_info,
&block_bets_info,
&block_commits_info,
&token_program,
amount,
)?;
// Emit an event.
BetEvent {
DeployEvent {
authority: *signer_info.key,
amount,
ts: clock.unix_timestamp as u64,

View File

@@ -4,17 +4,17 @@ use steel::*;
/// Initialize the program.
pub fn process_initialize(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult {
// Load accounts.
let [signer_info, block_info, block_bets_info, block_ore_info, ore_mint_info, sol_mint_info, system_program, token_program, associated_token_program] =
let [signer_info, block_info, block_commits_info, block_ore_info, ore_mint_info, sol_mint_info, system_program, token_program, associated_token_program] =
accounts
else {
return Err(ProgramError::NotEnoughAccountKeys);
};
signer_info.is_signer()?.has_address(&ADMIN_ADDRESS)?;
signer_info.is_signer()?; // .has_address(&ADMIN_ADDRESS)?;
block_info
.is_empty()?
.is_writable()?
.has_seeds(&[BLOCK], &ore_api::ID)?;
block_bets_info.is_empty()?.is_writable()?;
block_commits_info.is_empty()?.is_writable()?;
block_ore_info.is_empty()?.is_writable()?;
ore_mint_info.has_address(&MINT_ADDRESS)?;
sol_mint_info.has_address(&spl_token::native_mint::ID)?;
@@ -39,13 +39,13 @@ pub fn process_initialize(accounts: &[AccountInfo<'_>], _data: &[u8]) -> Program
block.paid = 0;
block.reward = 0;
block.started_at = 0;
block.total_wagers = 0;
block.total_commits = 0;
// Initialize block token accounts.
create_associated_token_account(
signer_info,
block_info,
block_bets_info,
block_commits_info,
sol_mint_info,
system_program,
token_program,

View File

@@ -1,15 +1,15 @@
mod bet;
mod bury;
mod claim;
mod close;
mod deploy;
mod initialize;
mod payout;
mod reset;
use bet::*;
use bury::*;
use claim::*;
use close::*;
use deploy::*;
use initialize::*;
use payout::*;
use reset::*;
@@ -26,9 +26,9 @@ pub fn process_instruction(
match ix {
// User
OreInstruction::Bet => process_bet(accounts, data)?,
OreInstruction::Claim => process_claim(accounts, data)?,
OreInstruction::Close => process_close(accounts, data)?,
OreInstruction::Deploy => process_deploy(accounts, data)?,
OreInstruction::Payout => process_payout(accounts, data)?,
OreInstruction::Reset => process_reset(accounts, data)?,

View File

@@ -3,11 +3,11 @@ use solana_program::keccak::hashv;
use steel::*;
use sysvar::slot_hashes::SlotHashes;
/// Payout block reward to the winning wager.
/// Payout block reward to the winning commit.
pub fn process_payout(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult {
// Load accounts.
let clock = Clock::get()?;
let [signer_info, block_info, mint_info, wager_info, recipient_info, treasury_info, treasury_tokens_info, system_program, token_program, slot_hashes_sysvar] =
let [signer_info, block_info, commit_info, mint_info, recipient_info, treasury_info, treasury_tokens_info, system_program, token_program, slot_hashes_sysvar] =
accounts
else {
return Err(ProgramError::NotEnoughAccountKeys);
@@ -37,7 +37,7 @@ pub fn process_payout(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResu
return Ok(());
}
// Skip payout if no bets were placed.
// Skip payout if no commits were placed.
if block.cumulative_sum == 0 {
burn_signed(
&treasury_tokens_info,
@@ -55,7 +55,7 @@ pub fn process_payout(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResu
bincode::deserialize::<SlotHashes>(slot_hashes_sysvar.data.borrow().as_ref()).unwrap();
let Some(slot_hash) = slot_hashes.get(&block.ends_at) else {
// If payout is not called within 2.5 minutes of the block ending,
// then the slot hash will be unavailable and the winning wager cannot be determined.
// then the slot hash will be unavailable and the winning commit cannot be determined.
burn_signed(
&treasury_tokens_info,
&mint_info,
@@ -75,12 +75,12 @@ pub fn process_payout(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResu
let w = u64::from_le_bytes(block.noise[24..32].try_into().unwrap());
let roll = (x ^ y ^ z ^ w) % block.cumulative_sum;
// Validate the wager account.
let wager = wager_info
.as_account_mut::<Wager>(&ore_api::ID)?
.assert_mut(|w| roll >= w.cumulative_sum)?
.assert_mut(|w| roll < w.cumulative_sum + w.amount)?;
recipient_info.as_associated_token_account(&wager.authority, &MINT_ADDRESS)?;
// Validate the commit account.
let commit = commit_info
.as_account_mut::<Commit>(&ore_api::ID)?
.assert_mut(|c| roll >= c.cumulative_sum)?
.assert_mut(|c| roll < c.cumulative_sum + c.amount)?;
recipient_info.as_associated_token_account(&commit.authority, &MINT_ADDRESS)?;
// Transfer the winnings to the recipient.
transfer_signed(
@@ -94,7 +94,7 @@ pub fn process_payout(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResu
// Emit an event.
PayoutEvent {
authority: wager.authority,
authority: commit.authority,
amount: block.reward,
ts: clock.unix_timestamp as u64,
}

View File

@@ -49,7 +49,7 @@ pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul
block.paid = 0;
block.reward = net_emissions - boost_reward;
block.started_at = clock.slot;
block.total_wagers = 0;
block.total_commits = 0;
// Fund the treasury.
mint_to_signed(