diff --git a/api/src/instruction.rs b/api/src/instruction.rs index 3d91fd7..59a7f97 100644 --- a/api/src/instruction.rs +++ b/api/src/instruction.rs @@ -68,6 +68,7 @@ pub struct Swap { pub amount: [u8; 8], pub direction: u8, pub precision: u8, + pub seed: [u8; 32], } #[repr(C)] diff --git a/api/src/sdk.rs b/api/src/sdk.rs index 11b7855..756dbe1 100644 --- a/api/src/sdk.rs +++ b/api/src/sdk.rs @@ -21,26 +21,41 @@ pub fn program_log(accounts: &[AccountInfo], msg: &[u8]) -> Result<(), ProgramEr invoke_signed(&log(*accounts[0].key, msg), accounts, &crate::ID, &[MARKET]) } +pub fn initialize(signer: Pubkey) -> Instruction { + let config_address = config_pda().0; + let market_address = market_pda().0; + let mint_address = MINT_ADDRESS; + let treasury_address = TREASURY_ADDRESS; + let treasury_tokens_address = treasury_tokens_address(); + let vault_address = vault_pda().0; + Instruction { + program_id: crate::ID, + accounts: vec![ + AccountMeta::new(signer, true), + AccountMeta::new(config_address, false), + AccountMeta::new(market_address, false), + AccountMeta::new(mint_address, false), + AccountMeta::new(treasury_address, false), + AccountMeta::new(treasury_tokens_address, false), + AccountMeta::new(vault_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), + ], + data: Initialize {}.to_bytes(), + } +} + pub fn mine(signer: Pubkey, authority: Pubkey, id: u64, nonce: u64) -> Instruction { let block_adddress = block_pda(id).0; - let market_address = market_pda().0; let miner_address = miner_pda(authority).0; - let recipient = get_associated_token_address(&authority, &MINT_ADDRESS); Instruction { program_id: crate::ID, accounts: vec![ AccountMeta::new(signer, true), AccountMeta::new(authority, false), AccountMeta::new(block_adddress, false), - AccountMeta::new(market_address, false), AccountMeta::new(miner_address, false), - AccountMeta::new(MINT_ADDRESS, false), - AccountMeta::new(recipient, false), - AccountMeta::new(TREASURY_ADDRESS, false), - AccountMeta::new_readonly(system_program::ID, false), - AccountMeta::new_readonly(spl_token::ID, false), - AccountMeta::new_readonly(crate::ID, false), - AccountMeta::new_readonly(sysvar::slot_hashes::ID, false), ], data: Mine { nonce: nonce.to_le_bytes(), @@ -49,15 +64,94 @@ pub fn mine(signer: Pubkey, authority: Pubkey, id: u64, nonce: u64) -> Instructi } } +pub fn open(signer: Pubkey, id: u64) -> Instruction { + let block_adddress = block_pda(id).0; + let market_address = market_pda().0; + Instruction { + program_id: crate::ID, + accounts: vec![ + AccountMeta::new(signer, true), + AccountMeta::new(block_adddress, false), + AccountMeta::new(market_address, false), + AccountMeta::new_readonly(system_program::ID, false), + ], + data: Open { + id: id.to_le_bytes(), + } + .to_bytes(), + } +} + +pub fn close(signer: Pubkey, opener: Pubkey, winner: Pubkey, id: u64) -> Instruction { + let block_adddress = block_pda(id).0; + let miner_tokens_address = get_associated_token_address(&winner, &MINT_ADDRESS); + let mint_address = MINT_ADDRESS; + let treasury_address = TREASURY_ADDRESS; + let treasury_tokens_address = treasury_tokens_address(); + Instruction { + program_id: crate::ID, + accounts: vec![ + AccountMeta::new(signer, true), + AccountMeta::new(block_adddress, false), + AccountMeta::new(winner, false), + AccountMeta::new(miner_tokens_address, false), + AccountMeta::new(mint_address, false), + AccountMeta::new(opener, false), + AccountMeta::new(treasury_address, 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), + ], + data: Close {}.to_bytes(), + } +} + +pub fn reset(signer: Pubkey, fee_collector: Pubkey, id: u64) -> Instruction { + let block_prev_adddress = block_pda(id).0; + let block_next_adddress = block_pda(id + 1).0; + let config_address = config_pda().0; + let market_address = market_pda().0; + let mint_address = MINT_ADDRESS; + let treasury_address = TREASURY_ADDRESS; + let treasury_tokens_address = treasury_tokens_address(); + let vault_address = vault_pda().0; + Instruction { + program_id: crate::ID, + accounts: vec![ + AccountMeta::new(signer, true), + AccountMeta::new(block_prev_adddress, false), + AccountMeta::new(block_next_adddress, false), + AccountMeta::new_readonly(config_address, false), + AccountMeta::new(fee_collector, false), + AccountMeta::new(market_address, false), + AccountMeta::new(mint_address, false), + AccountMeta::new(treasury_address, false), + AccountMeta::new(treasury_tokens_address, false), + AccountMeta::new(vault_address, false), + AccountMeta::new_readonly(system_program::ID, false), + AccountMeta::new_readonly(spl_token::ID, false), + AccountMeta::new_readonly(crate::ID, false), + AccountMeta::new_readonly(sysvar::slot_hashes::ID, false), + ], + data: Reset {}.to_bytes(), + } +} +// let [signer_info, block_info, config_info, fee_collector_info, market_info, miner_info, mint_info, tokens_info, vault_info, system_program, token_program, associated_token_program, ore_program] = + pub fn swap( signer: Pubkey, id: u64, + fee_collector: Pubkey, amount: u64, direction: SwapDirection, precision: SwapPrecision, + seed: [u8; 32], ) -> Instruction { let block_adddress = block_pda(id).0; + let config_address = config_pda().0; let market_address = market_pda().0; + let miner_address = miner_pda(signer).0; let tokens_quote_address = get_associated_token_address(&signer, &MINT_ADDRESS); let vault_address = vault_pda().0; Instruction { @@ -65,7 +159,10 @@ pub fn swap( accounts: vec![ AccountMeta::new(signer, true), AccountMeta::new(block_adddress, false), + AccountMeta::new(config_address, false), + AccountMeta::new(fee_collector, false), AccountMeta::new(market_address, false), + AccountMeta::new(miner_address, false), AccountMeta::new(MINT_ADDRESS, false), AccountMeta::new(tokens_quote_address, false), AccountMeta::new(vault_address, false), @@ -78,6 +175,7 @@ pub fn swap( amount: amount.to_le_bytes(), direction: direction as u8, precision: precision as u8, + seed: seed, } .to_bytes(), } diff --git a/api/src/state/miner.rs b/api/src/state/miner.rs index 169bb07..5d790de 100644 --- a/api/src/state/miner.rs +++ b/api/src/state/miner.rs @@ -13,9 +13,6 @@ pub struct Miner { /// The ID of the last block this miner mined in. pub block_id: u64, - /// The hash of the last block this miner mined in. - pub hash: [u8; 32], - /// The amount of hashpower this miner has committed to the current block. pub hashpower: u64, diff --git a/api/src/state/mod.rs b/api/src/state/mod.rs index 7c199fe..7b82ef9 100644 --- a/api/src/state/mod.rs +++ b/api/src/state/mod.rs @@ -55,3 +55,7 @@ pub fn vault_pda() -> (Pubkey, u8) { pub fn treasury_pda() -> (Pubkey, u8) { Pubkey::find_program_address(&[TREASURY], &crate::ID) } + +pub fn treasury_tokens_address() -> Pubkey { + spl_associated_token_account::get_associated_token_address(&TREASURY_ADDRESS, &MINT_ADDRESS) +} diff --git a/cli/src/main.rs b/cli/src/main.rs index 8d66764..2d385e4 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -35,9 +35,18 @@ async fn main() { "blocks" => { log_blocks(&rpc).await.unwrap(); } + "initialize" => { + initialize(&rpc, &payer).await.unwrap(); + } + "open" => { + open(&rpc, &payer).await.unwrap(); + } "swap" => { swap(&rpc, &payer).await.unwrap(); } + "miner" => { + log_miner(&rpc, payer.pubkey()).await.unwrap(); + } "set_admin" => { set_admin(&rpc, &payer).await.unwrap(); } @@ -48,18 +57,42 @@ async fn main() { }; } +async fn initialize( + rpc: &RpcClient, + payer: &solana_sdk::signer::keypair::Keypair, +) -> Result<(), anyhow::Error> { + let ix = ore_api::sdk::initialize(payer.pubkey()); + submit_transaction(rpc, payer, &[ix]).await?; + Ok(()) +} + +async fn open( + rpc: &RpcClient, + payer: &solana_sdk::signer::keypair::Keypair, +) -> Result<(), anyhow::Error> { + let id_str = std::env::var("ID").expect("Missing ID env var"); + let id = id_str.parse::()?; + let ix = ore_api::sdk::open(payer.pubkey(), id); + submit_transaction(rpc, payer, &[ix]).await?; + Ok(()) +} + async fn swap( rpc: &RpcClient, payer: &solana_sdk::signer::keypair::Keypair, ) -> Result<(), anyhow::Error> { let id_str = std::env::var("ID").expect("Missing ID env var"); let id = id_str.parse::()?; + let config = get_config(rpc).await?; + let fee_collector = config.fee_collector; let ix = ore_api::sdk::swap( payer.pubkey(), id, - 10000000, + fee_collector, + 100_000_000, SwapDirection::Buy, SwapPrecision::ExactIn, + [0; 32], ); submit_transaction(rpc, payer, &[ix]).await?; Ok(()) @@ -74,6 +107,17 @@ async fn set_admin( Ok(()) } +async fn log_miner(rpc: &RpcClient, authority: Pubkey) -> Result<(), anyhow::Error> { + let miner = get_miner(&rpc, authority).await?; + println!("Miner"); + println!(" authority: {}", authority); + println!(" block_id: {}", miner.block_id); + println!(" hashpower: {}", miner.hashpower); + println!(" total_hashpower: {}", miner.total_hashpower); + println!(" total_rewards: {}", miner.total_rewards); + Ok(()) +} + async fn log_clock(rpc: &RpcClient) -> Result<(), anyhow::Error> { let clock = get_clock(&rpc).await?; println!("Clock"); @@ -126,6 +170,13 @@ async fn get_config(rpc: &RpcClient) -> Result { Ok(*config) } +async fn get_miner(rpc: &RpcClient, authority: Pubkey) -> Result { + let miner_pda = ore_api::state::miner_pda(authority); + let account = rpc.get_account(&miner_pda.0).await?; + let miner = Miner::try_from_bytes(&account.data)?; + Ok(*miner) +} + async fn get_clock(rpc: &RpcClient) -> Result { let data = rpc.get_account_data(&solana_sdk::sysvar::clock::ID).await?; let clock = bincode::deserialize::(&data)?; diff --git a/program/src/close.rs b/program/src/close.rs index a51f2ee..e246159 100644 --- a/program/src/close.rs +++ b/program/src/close.rs @@ -5,7 +5,7 @@ use steel::*; pub fn process_close(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult { // Load accounts. let clock = Clock::get()?; - let [signer_info, block_info, miner_info, miner_tokens_info, mint_info, opener_info, recipient_info, treasury_info, treasury_tokens_info, system_program, token_program, associated_token_program] = + let [signer_info, block_info, miner_info, miner_tokens_info, mint_info, opener_info, treasury_info, treasury_tokens_info, system_program, token_program, associated_token_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); @@ -41,11 +41,10 @@ pub fn process_close(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul // Payout block reward to winning miner. if block.best_hash_miner != Pubkey::default() { - // Load recipient. - recipient_info.as_associated_token_account(&block.best_hash_miner, &mint_info.key)?; + // Load winning miner. let miner = miner_info - .as_account_mut::(&ore_api::ID)? - .assert_mut(|m| m.authority == block.best_hash_miner)?; + .has_address(&block.best_hash_miner)? + .as_account_mut::(&ore_api::ID)?; // Update stats. miner.total_rewards += block.reward; diff --git a/program/src/mine.rs b/program/src/mine.rs index 51405ec..74eb7ea 100644 --- a/program/src/mine.rs +++ b/program/src/mine.rs @@ -38,7 +38,7 @@ pub fn process_mine(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult // If hash is best hash, update best hash. if h < block.best_hash { block.best_hash = h; - block.best_hash_miner = miner.authority; + block.best_hash_miner = *miner_info.key; } Ok(()) diff --git a/program/src/open.rs b/program/src/open.rs index f59c4d4..f2cde77 100644 --- a/program/src/open.rs +++ b/program/src/open.rs @@ -8,7 +8,7 @@ pub fn process_open(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult let id = u64::from_le_bytes(args.id); // Load accounts. - let [signer_info, block_info, market_info, system_program, ore_program] = accounts else { + let [signer_info, block_info, market_info, system_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; signer_info.is_signer()?; @@ -16,7 +16,6 @@ pub fn process_open(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult .as_account::(&ore_api::ID)? .assert(|m| m.block_id < id)?; // Only allow opening blocks in forward bias system_program.is_program(&system_program::ID)?; - ore_program.is_program(&ore_api::ID)?; // Create block, if it doesn't exist. if block_info.data_is_empty() { diff --git a/program/src/swap.rs b/program/src/swap.rs index b469c11..fe01e6d 100644 --- a/program/src/swap.rs +++ b/program/src/swap.rs @@ -30,9 +30,6 @@ pub fn process_swap(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult .assert_mut(|m| m.block_id == block.id)? .assert_mut(|m| m.base.liquidity() > 0)? .assert_mut(|m| m.quote.liquidity() > 0)?; - let miner = miner_info - .as_account_mut::(&ore_api::ID)? - .assert_mut(|m| m.authority == *signer_info.key)?; mint_info .has_address(&market.quote.mint)? .has_address(&MINT_ADDRESS)? @@ -46,10 +43,35 @@ pub fn process_swap(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult associated_token_program.is_program(&spl_associated_token_account::ID)?; ore_program.is_program(&ore_api::ID)?; + // Load miner. + let miner = if miner_info.data_is_empty() { + create_program_account::( + miner_info, + system_program, + signer_info, + &ore_api::ID, + &[MINER, &signer_info.key.to_bytes()], + )?; + let miner = miner_info.as_account_mut::(&ore_api::ID)?; + miner.authority = *signer_info.key; + miner.block_id = block.id; + miner.hashpower = 0; + miner.seed = [0; 32]; + miner.total_hashpower = 0; + miner.total_rewards = 0; + miner + } else { + miner_info + .as_account_mut::(&ore_api::ID)? + .assert_mut(|m| m.authority == *signer_info.key)? + .assert_mut(|m| m.block_id <= block.id)? + }; + // Reset miner. if miner.block_id != block.id { miner.block_id = block.id; miner.hashpower = 0; + miner.seed = args.seed; } // Pay swap fee.