basic stub oracle integration in tests and withdraw
Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
parent
9a50325718
commit
5299e31ff8
|
@ -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;
|
|
@ -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;
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<()> {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue