diff --git a/Cargo.lock b/Cargo.lock index 197b2e6..060b4c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1320,6 +1320,7 @@ version = "3.7.0" dependencies = [ "bincode", "mpl-token-metadata", + "ore-api 3.7.0", "ore-delegate-api", "rand 0.8.5", "solana-program", diff --git a/ore-delegate/program/Cargo.toml b/ore-delegate/program/Cargo.toml index e6541c6..a3ffd4b 100644 --- a/ore-delegate/program/Cargo.toml +++ b/ore-delegate/program/Cargo.toml @@ -20,6 +20,7 @@ default = [] [dependencies] bincode.workspace = true mpl-token-metadata.workspace = true +ore-api.workspace = true ore-delegate-api.workspace = true solana-program.workspace = true spl-token.workspace = true diff --git a/ore-delegate/program/src/deposit.rs b/ore-delegate/program/src/deposit.rs index 8e45aaa..c6fe179 100644 --- a/ore-delegate/program/src/deposit.rs +++ b/ore-delegate/program/src/deposit.rs @@ -1,69 +1,77 @@ +use ore_api::prelude::*; use ore_delegate_api::prelude::*; use steel::*; /// Deposits hash tokens for cranking. -pub fn process_deposit(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult { +pub fn process_deposit(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult { + // Load data. + let args = Deposit::try_from_bytes(data)?; + let amount = u64::from_le_bytes(args.amount); + // Load accounts. - // let clock = Clock::get()?; - // let [signer_info, block_info, market_info, mint_base_info, mint_quote_info, recipient_info, treasury_info, vault_base_info, vault_quote_info, system_program, token_program] = - // accounts - // else { - // return Err(ProgramError::NotEnoughAccountKeys); - // }; - // signer_info.is_signer()?; - // let block = block_info - // .as_account_mut::(&ore_api::ID)? - // .assert_mut(|b| clock.slot >= b.start_slot + 1500)?; - // let market = market_info - // .as_account_mut::(&ore_api::ID)? - // .assert_mut(|m| m.id == block.id)?; - // mint_base_info.has_address(&market.base.mint)?.as_mint()?; - // mint_quote_info.has_address(&market.quote.mint)?.as_mint()?; - // let vault_base = - // vault_base_info.as_associated_token_account(market_info.key, mint_base_info.key)?; - // let vault_quote = - // vault_quote_info.as_associated_token_account(market_info.key, mint_quote_info.key)?; - // system_program.is_program(&system_program::ID)?; - // token_program.is_program(&spl_token::ID)?; + let [signer_info, block_info, delegate_info, escrow_info, market_info, mint_info, sender_info, system_program, token_program, associated_token_program] = + accounts + else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + signer_info.is_signer()?; + let block = block_info.as_account_mut::(&ore_api::ID)?; + let market = market_info + .as_account_mut::(&ore_api::ID)? + .assert_mut(|m| m.id == block.id)?; + mint_info.has_address(&market.base.mint)?.as_mint()?; + sender_info + .is_writable()? + .as_associated_token_account(signer_info.key, &market.base.mint)? + .assert_mut(|t| t.amount() >= amount)?; + system_program.is_program(&system_program::ID)?; + token_program.is_program(&spl_token::ID)?; + associated_token_program.is_program(&spl_associated_token_account::ID)?; - // // Payout block reward. - // if block.best_miner != Pubkey::default() { - // recipient_info.as_associated_token_account(&block.best_miner, &MINT_ADDRESS)?; - // mint_to_signed( - // mint_quote_info, - // recipient_info, - // treasury_info, - // token_program, - // block.reward, - // &[TREASURY], - // )?; - // } + // Initialize delegate. + let delegate = if delegate_info.data_is_empty() { + create_program_account::( + delegate_info, + system_program, + signer_info, + &ore_delegate_api::ID, + &[ + DELEGATE, + &signer_info.key.to_bytes(), + &block.id.to_le_bytes(), + ], + )?; + let delegate = delegate_info.as_account_mut::(&ore_delegate_api::ID)?; + delegate.authority = *signer_info.key; + delegate.block_id = block.id; + delegate.fee = 0; // TODO: Set fee. + delegate + } else { + delegate_info.as_account_mut::(&ore_delegate_api::ID)? + }; - // // Burn base liquidity. - // burn_signed( - // vault_base_info, - // mint_base_info, - // market_info, - // token_program, - // vault_base.amount(), - // &[MARKET, &market.id.to_le_bytes()], - // )?; + // Initialize escrow. + if escrow_info.data_is_empty() { + create_associated_token_account( + signer_info, + delegate_info, + escrow_info, + mint_info, + system_program, + token_program, + associated_token_program, + )?; + } else { + escrow_info + .is_writable()? + .as_associated_token_account(delegate_info.key, &mint_info.key)?; + } - // // Burn quote liquidity. - // burn_signed( - // vault_quote_info, - // mint_quote_info, - // market_info, - // token_program, - // vault_quote.amount(), - // &[MARKET, &market.id.to_le_bytes()], - // )?; + // Update delegate. + delegate.balance += amount; - // // Close block. - // block_info.close(signer_info)?; - - // // Close market. - // market_info.close(signer_info)?; + // Transfer tokens. + transfer(signer_info, sender_info, escrow_info, token_program, amount)?; Ok(()) } diff --git a/ore-delegate/program/src/withdraw.rs b/ore-delegate/program/src/withdraw.rs index 9495d05..ad3354e 100644 --- a/ore-delegate/program/src/withdraw.rs +++ b/ore-delegate/program/src/withdraw.rs @@ -1,79 +1,61 @@ use ore_delegate_api::prelude::*; -use solana_program::keccak; use steel::*; /// Withdraws hash tokens from the delegate. pub fn process_withdraw(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult { - // Parse data. + // Load data. let args = Withdraw::try_from_bytes(data)?; let amount = u64::from_le_bytes(args.amount); // Load accounts. - // let clock = Clock::get()?; - // let [signer_info, block_info, market_info, miner_info, mint_info, sender_info, system_program, token_program] = - // accounts - // else { - // return Err(ProgramError::NotEnoughAccountKeys); - // }; - // signer_info.is_signer()?; - // let block = block_info - // .as_account_mut::(&ore_api::ID)? - // .assert_mut(|b| clock.slot >= b.start_slot)? - // .assert_mut(|b| clock.slot < b.start_slot + 1500)?; - // let market = market_info - // .as_account::(&ore_api::ID)? - // .assert(|m| m.id == block.id)?; - // mint_info.has_address(&market.base.mint)?.as_mint()?; - // sender_info - // .is_writable()? - // .as_associated_token_account(signer_info.key, &mint_info.key)? - // .assert(|t| t.amount() >= amount)?; - // system_program.is_program(&system_program::ID)?; - // token_program.is_program(&spl_token::ID)?; + let [signer_info, delegate_info, escrow_info, mint_info, recipient_info, system_program, token_program] = + accounts + else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + signer_info.is_signer()?; + let delegate = delegate_info + .as_account_mut::(&ore_delegate_api::ID)? + .assert_mut(|d| d.authority == *signer_info.key)?; + let escrow = escrow_info.as_associated_token_account(delegate_info.key, &mint_info.key)?; + 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)?; - // // Load miner account. - // 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 = 0; - // miner.hash = [0; 32]; - // miner.total_hashes = 0; - // miner.total_rewards = 0; - // miner - // } else { - // miner_info - // .as_account_mut::(&ore_api::ID)? - // .assert_mut(|m| m.authority == *signer_info.key)? - // }; + // Update delegate. + delegate.balance -= escrow.amount().min(amount); - // // Reset miner hash if mining new block. - // if miner.block_id != block.id { - // miner.block_id = block.id; - // miner.hash = - // keccak::hashv(&[block.slot_hash.as_ref(), miner.authority.as_ref()]).to_bytes(); - // } + // Transfer tokens. + transfer_signed( + signer_info, + escrow_info, + recipient_info, + token_program, + amount, + &[ + DELEGATE, + &delegate.authority.to_bytes(), + &delegate.block_id.to_le_bytes(), + ], + )?; - // // Mine. - // for _ in 0..amount { - // miner.hash = keccak::hashv(&[miner.hash.as_ref()]).to_bytes(); - // if miner.hash < block.best_hash { - // block.best_hash = miner.hash; - // block.best_miner = miner.authority; - // } - // } - - // // Update miner stats. - // miner.total_hashes += amount; - - // // Burn hash tokens. - // burn(sender_info, mint_info, signer_info, token_program, amount)?; + // Close accounts if empty. + if delegate.balance == 0 { + delegate_info.close(signer_info)?; + close_token_account_signed( + escrow_info, + signer_info, + delegate_info, + token_program, + &[ + DELEGATE, + &delegate.authority.to_bytes(), + &delegate.block_id.to_le_bytes(), + ], + )?; + } Ok(()) } diff --git a/ore/api/src/sdk.rs b/ore/api/src/sdk.rs index b130849..cb1ebde 100644 --- a/ore/api/src/sdk.rs +++ b/ore/api/src/sdk.rs @@ -1,4 +1,4 @@ -use spl_token::native_mint; +use spl_associated_token_account::get_associated_token_address; use steel::*; use crate::{ @@ -6,3 +6,116 @@ use crate::{ instruction::*, state::*, }; + +pub fn open(signer: Pubkey, id: u64) -> Instruction { + let block_adddress = block_pda(id).0; + let market_address = market_pda(id).0; + let base_mint_address = mint_pda(id).0; + Instruction { + program_id: crate::ID, + accounts: vec![ + AccountMeta::new(signer, true), + AccountMeta::new(block_adddress, false), + AccountMeta::new(market_address, false), + AccountMeta::new(base_mint_address, false), + AccountMeta::new(MINT_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), + AccountMeta::new_readonly(sysvar::rent::ID, false), + ], + data: Open { + id: id.to_le_bytes(), + } + .to_bytes(), + } +} + +pub fn close(signer: Pubkey, recipient: Pubkey, id: u64) -> Instruction { + let block_adddress = block_pda(id).0; + let market_address = market_pda(id).0; + let base_mint_address = mint_pda(id).0; + let vault_base = get_associated_token_address(&signer, &base_mint_address); + let vault_quote = get_associated_token_address(&signer, &MINT_ADDRESS); + Instruction { + program_id: crate::ID, + accounts: vec![ + AccountMeta::new(signer, true), + AccountMeta::new(block_adddress, false), + AccountMeta::new(market_address, false), + AccountMeta::new(base_mint_address, false), + AccountMeta::new(MINT_ADDRESS, false), + AccountMeta::new(recipient, false), + AccountMeta::new_readonly(TREASURY_ADDRESS, false), + AccountMeta::new(vault_base, false), + AccountMeta::new(vault_quote, false), + AccountMeta::new_readonly(system_program::ID, false), + AccountMeta::new_readonly(spl_token::ID, false), + ], + data: Close {}.to_bytes(), + } +} + +pub fn mine(signer: Pubkey, id: u64, amount: u64) -> Instruction { + let block_adddress = block_pda(id).0; + let market_address = market_pda(id).0; + let base_mint_address = mint_pda(id).0; + let miner_address = miner_pda(signer).0; + let sender = get_associated_token_address(&signer, &base_mint_address); + Instruction { + program_id: crate::ID, + accounts: vec![ + AccountMeta::new(signer, true), + AccountMeta::new(block_adddress, false), + AccountMeta::new(market_address, false), + AccountMeta::new(miner_address, false), + AccountMeta::new(MINT_ADDRESS, false), + AccountMeta::new(sender, false), + AccountMeta::new_readonly(system_program::ID, false), + AccountMeta::new_readonly(spl_token::ID, false), + AccountMeta::new_readonly(sysvar::slot_hashes::ID, false), + ], + data: Mine { + amount: amount.to_le_bytes(), + } + .to_bytes(), + } +} + +pub fn swap( + signer: Pubkey, + id: u64, + amount: u64, + direction: SwapDirection, + precision: SwapPrecision, +) -> Instruction { + let block_adddress = block_pda(id).0; + let market_address = market_pda(id).0; + let base_mint_address = mint_pda(id).0; + let tokens_base_address = get_associated_token_address(&signer, &base_mint_address); + let tokens_quote_address = get_associated_token_address(&signer, &MINT_ADDRESS); + let vault_base_address = get_associated_token_address(&market_address, &base_mint_address); + let vault_quote_address = get_associated_token_address(&market_address, &MINT_ADDRESS); + Instruction { + program_id: crate::ID, + accounts: vec![ + AccountMeta::new(signer, true), + AccountMeta::new(block_adddress, false), + AccountMeta::new(market_address, false), + AccountMeta::new(base_mint_address, false), + AccountMeta::new(MINT_ADDRESS, false), + AccountMeta::new(tokens_base_address, false), + AccountMeta::new(tokens_quote_address, false), + AccountMeta::new(vault_base_address, false), + AccountMeta::new(vault_quote_address, false), + AccountMeta::new_readonly(system_program::ID, false), + AccountMeta::new_readonly(spl_token::ID, false), + ], + data: Swap { + amount: amount.to_le_bytes(), + direction: direction as u8, + precision: precision as u8, + } + .to_bytes(), + } +} diff --git a/ore/api/src/state/mod.rs b/ore/api/src/state/mod.rs index 69e03d6..afb75b1 100644 --- a/ore/api/src/state/mod.rs +++ b/ore/api/src/state/mod.rs @@ -40,6 +40,10 @@ pub fn miner_pda(authority: Pubkey) -> (Pubkey, u8) { Pubkey::find_program_address(&[MINER, &authority.to_bytes()], &crate::ID) } +pub fn mint_pda(id: u64) -> (Pubkey, u8) { + Pubkey::find_program_address(&[MINT, &id.to_le_bytes()], &crate::ID) +} + pub fn treasury_pda() -> (Pubkey, u8) { Pubkey::find_program_address(&[TREASURY], &crate::ID) }