basic stub oracle integration in tests and withdraw

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
microwavedcola1 2022-02-28 17:17:01 +01:00
parent 9a50325718
commit 5299e31ff8
7 changed files with 151 additions and 19 deletions

View File

@ -5,7 +5,7 @@ use fixed::types::I80F48;
use crate::state::*;
#[derive(Accounts)]
pub struct InitStubOracle<'info> {
pub struct CreateStubOracle<'info> {
#[account(
init,
seeds = [b"stub_oracle".as_ref(), token_mint.key().as_ref()],
@ -23,7 +23,7 @@ pub struct InitStubOracle<'info> {
pub system_program: Program<'info, System>,
}
pub fn init_stub_oracle(ctx: Context<InitStubOracle>, price: I80F48) -> Result<()> {
pub fn create_stub_oracle(ctx: Context<CreateStubOracle>, price: I80F48) -> Result<()> {
let mut oracle = ctx.accounts.oracle.load_init()?;
oracle.price = price;
oracle.last_updated = Clock::get()?.unix_timestamp;

View File

@ -1,15 +1,15 @@
pub use create_account::*;
pub use create_group::*;
pub use create_stub_oracle::*;
pub use deposit::*;
pub use init_stub_oracle::*;
pub use register_token::*;
pub use set_stub_oracle::*;
pub use withdraw::*;
mod create_account;
mod create_group;
mod create_stub_oracle;
mod deposit;
mod init_stub_oracle;
mod register_token;
mod set_stub_oracle;
mod withdraw;

View File

@ -6,13 +6,13 @@ use crate::state::*;
#[derive(Accounts)]
pub struct SetStubOracle<'info> {
#[account(mut)]
pub stub_oracle: AccountLoader<'info, StubOracle>,
pub oracle: AccountLoader<'info, StubOracle>,
}
pub fn set_stub_oracle(ctx: Context<SetStubOracle>, price: I80F48) -> Result<()> {
let mut stub_oracle = ctx.accounts.stub_oracle.load_init()?;
stub_oracle.price = price;
stub_oracle.last_updated = Clock::get()?.unix_timestamp;
let mut oracle = ctx.accounts.oracle.load_mut()?;
oracle.price = price;
oracle.last_updated = Clock::get()?.unix_timestamp;
Ok(())
}

View File

@ -48,6 +48,14 @@ impl<'info> Withdraw<'info> {
}
}
macro_rules! zip {
($x: expr) => ($x);
($x: expr, $($y: expr), +) => (
$x.zip(
zip!($($y), +))
)
}
// TODO: It may make sense to have the token_index passed in from the outside.
// That would save a lot of computation that needs to go into finding the
// right index for the mint.
@ -101,17 +109,17 @@ pub fn withdraw(ctx: Context<Withdraw>, amount: u64, allow_borrow: bool) -> Resu
//
let active_len = account.indexed_positions.iter_active().count();
require!(
ctx.remaining_accounts.len() == active_len,
ctx.remaining_accounts.len() == active_len * 2, // banks + oracles
MangoError::SomeError
);
let mut assets = I80F48::ZERO;
let mut liabilities = I80F48::ZERO; // absolute value
for (position, bank_ai) in account
.indexed_positions
.iter_active()
.zip(ctx.remaining_accounts.iter())
{
for (position, (bank_ai, oracle_ai)) in zip!(
account.indexed_positions.iter_active(),
ctx.remaining_accounts.iter(),
ctx.remaining_accounts.iter().skip(active_len)
) {
let bank_loader = AccountLoader::<'_, TokenBank>::try_from(bank_ai)?;
let bank = bank_loader.load()?;
@ -122,11 +130,17 @@ pub fn withdraw(ctx: Context<Withdraw>, amount: u64, allow_borrow: bool) -> Resu
);
// converts the token value to the basis token value for health computations
// TODO: oracles
// TODO: health basis token == USDC?
let dummy_price = I80F48::ONE;
let oracle_type = determine_oracle_type(oracle_ai)?;
let price = match oracle_type {
OracleType::Stub => {
AccountLoader::<'_, StubOracle>::try_from(oracle_ai)?
.load()?
.price
}
};
let native_basis = position.native(&bank) * dummy_price;
let native_basis = position.native(&bank) * price;
if native_basis.is_positive() {
assets += bank.init_asset_weight * native_basis;
} else {

View File

@ -52,8 +52,8 @@ pub mod mango_v4 {
// because generic anchor clients won't know how to deal with it
// and it's tricky to use in typescript generally
// lets do an interface pass later
pub fn init_stub_oracle(ctx: Context<InitStubOracle>, price: I80F48) -> Result<()> {
instructions::init_stub_oracle(ctx, price)
pub fn create_stub_oracle(ctx: Context<CreateStubOracle>, price: I80F48) -> Result<()> {
instructions::create_stub_oracle(ctx, price)
}
pub fn set_stub_oracle(ctx: Context<SetStubOracle>, price: I80F48) -> Result<()> {

View File

@ -2,6 +2,8 @@ use anchor_lang::prelude::*;
use anchor_lang::solana_program::sysvar::{self, SysvarId};
use anchor_lang::Key;
use anchor_spl::token::{Token, TokenAccount};
use fixed::types::I80F48;
use solana_program::instruction::Instruction;
use solana_sdk::instruction;
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::transport::TransportError;
@ -77,6 +79,7 @@ pub struct WithdrawInstruction<'keypair> {
pub token_account: Pubkey,
pub banks: Vec<Pubkey>,
pub oracles: Vec<Pubkey>,
}
#[async_trait::async_trait(?Send)]
impl<'keypair> ClientInstruction for WithdrawInstruction<'keypair> {
@ -133,6 +136,13 @@ impl<'keypair> ClientInstruction for WithdrawInstruction<'keypair> {
is_writable: false,
is_signer: false,
}));
instruction
.accounts
.extend(self.oracles.iter().map(|&pubkey| AccountMeta {
pubkey,
is_writable: false,
is_signer: false,
}));
(accounts, instruction)
}
@ -281,6 +291,81 @@ impl<'keypair> ClientInstruction for RegisterTokenInstruction<'keypair> {
}
}
pub struct SetStubOracle<'keypair> {
pub mint: Pubkey,
pub payer: &'keypair Keypair,
}
#[async_trait::async_trait(?Send)]
impl<'keypair> ClientInstruction for SetStubOracle<'keypair> {
type Accounts = mango_v4::accounts::SetStubOracle;
type Instruction = mango_v4::instruction::SetStubOracle;
async fn to_instruction(
&self,
loader: impl ClientAccountLoader + 'async_trait,
) -> (Self::Accounts, Instruction) {
let program_id = mango_v4::id();
let instruction = Self::Instruction {
price: I80F48::from_num(1.0),
};
let oracle = Pubkey::find_program_address(
&[b"stub_oracle".as_ref(), self.mint.as_ref()],
&program_id,
)
.0;
let accounts = Self::Accounts { oracle };
let instruction = make_instruction(program_id, &accounts, instruction);
(accounts, instruction)
}
fn signers(&self) -> Vec<&Keypair> {
vec![]
}
}
pub struct CreateStubOracle<'keypair> {
pub mint: Pubkey,
pub payer: &'keypair Keypair,
}
#[async_trait::async_trait(?Send)]
impl<'keypair> ClientInstruction for CreateStubOracle<'keypair> {
type Accounts = mango_v4::accounts::CreateStubOracle;
type Instruction = mango_v4::instruction::CreateStubOracle;
async fn to_instruction(
&self,
loader: impl ClientAccountLoader + 'async_trait,
) -> (Self::Accounts, Instruction) {
let program_id = mango_v4::id();
let instruction = Self::Instruction {
price: I80F48::from_num(1.0),
};
let oracle = Pubkey::find_program_address(
&[b"stub_oracle".as_ref(), self.mint.as_ref()],
&program_id,
)
.0;
let accounts = Self::Accounts {
oracle,
token_mint: self.mint,
payer: self.payer.pubkey(),
system_program: System::id(),
};
let instruction = make_instruction(program_id, &accounts, instruction);
(accounts, instruction)
}
fn signers(&self) -> Vec<&Keypair> {
vec![self.payer]
}
}
pub struct CreateGroupInstruction<'keypair> {
pub admin: &'keypair Keypair,
pub payer: &'keypair Keypair,

View File

@ -5,6 +5,7 @@ use solana_program::pubkey::Pubkey;
use solana_program_test::*;
use solana_sdk::instruction::Instruction;
use solana_sdk::{signature::Keypair, signer::Signer, transport::TransportError};
use std::cmp::min;
use mango_v4::state::*;
use program_test::*;
@ -48,6 +49,37 @@ async fn test_basic() -> Result<(), TransportError> {
.unwrap()
.account;
let create_stub_oracle_accounts = send_tx(
solana,
CreateStubOracle {
mint: mint0.pubkey,
payer,
},
)
.await
.unwrap();
let oracle = create_stub_oracle_accounts.oracle;
send_tx(
solana,
SetStubOracle {
mint: mint0.pubkey,
payer,
},
)
.await
.unwrap();
send_tx(
solana,
CreateStubOracle {
mint: mint0.pubkey,
payer,
},
)
.await
.unwrap();
let register_token_accounts = send_tx(
solana,
RegisterTokenInstruction {
@ -119,6 +151,7 @@ async fn test_basic() -> Result<(), TransportError> {
owner,
token_account: payer_mint0_account,
banks: vec![bank],
oracles: vec![oracle],
},
)
.await