Skip to content

Commit

Permalink
Merge pull request #16 from mrgnlabs/j/pyth-pull-support
Browse files Browse the repository at this point in the history
Pyth Push Oracle Support
  • Loading branch information
LevBeta authored Jul 20, 2024
2 parents ceb82bf + 96ad858 commit e1e1741
Show file tree
Hide file tree
Showing 9 changed files with 650 additions and 902 deletions.
1,363 changes: 514 additions & 849 deletions Cargo.lock

Large diffs are not rendered by default.

13 changes: 9 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anchor-client = { version = "0.30.1", features = ["async"] }
anchor-lang = "0.30.1"
anchor-spl = "0.30.1"
anchor-lang = { git = "https://github.com/mrgnlabs/anchor.git", rev = "fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" }
anchor-spl = { git = "https://github.com/mrgnlabs/anchor.git", features = [
"token_2022",
], rev = "fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d" }
anchor-client = { git = "https://github.com/mrgnlabs/anchor.git", rev = "fdcf299dc55ecf7cfa8c4d598aecb1363b99c02d", features = [
"async",
] }

anyhow = "1.0.79"
backoff = { version = "0.4.0", features = ["futures", "tokio"] }
bincode = "1.3.3"
Expand All @@ -23,7 +28,7 @@ futures = "0.3.30"
futures-sink = "0.3.30"
jupiter-swap-api-client = "0.1.0"
log = "0.4.21"
marginfi = { git = "https://github.com/mrgnlabs/marginfi-v2", branch = "cavey/t22-upgraded-anchor", features = [
marginfi = { git = "https://github.com/mrgnlabs/marginfi-v2", branch = "main", features = [
"mainnet-beta",
"client",
"no-entrypoint",
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ RUN apt-get update && \
COPY . .

# Build the application
RUN cargo build --release
RUN cargo build --release --locked

# Final stage
FROM debian:bookworm-slim
Expand Down
58 changes: 39 additions & 19 deletions src/liquidator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use crate::{
config::{GeneralConfig, LiquidatorCfg},
geyser::{AccountType, GeyserUpdate},
transaction_manager::BatchTransactions,
utils::{batch_get_multiple_accounts, BankAccountWithPriceFeedEva, BatchLoadingConfig},
utils::{
batch_get_multiple_accounts, find_oracle_keys, BankAccountWithPriceFeedEva,
BatchLoadingConfig,
},
wrappers::{
bank::BankWrapper, liquidator_account::LiquidatorAccount,
marginfi_account::MarginfiAccountWrapper, oracle::OracleWrapper,
Expand All @@ -13,7 +16,7 @@ use anchor_lang::Discriminator;
use crossbeam::channel::{Receiver, Sender};
use fixed::types::I80F48;
use fixed_macro::types::I80F48;
use log::{debug, error, info};
use log::{debug, error, info, warn};
use marginfi::{
constants::EXP_10_I80F48,
state::{
Expand All @@ -30,7 +33,9 @@ use solana_client::{
rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType},
};
use solana_program::pubkey::Pubkey;
use solana_sdk::{account_info::IntoAccountInfo, bs58, signature::Keypair};
use solana_sdk::{
account::Account, account_info::IntoAccountInfo, bs58, clock::Clock, signature::Keypair,
};
use std::{
cmp::min,
collections::HashMap,
Expand Down Expand Up @@ -147,8 +152,8 @@ impl Liquidator {
OraclePriceFeedAdapter::try_from_bank_config_with_max_age(
&bank_to_update.bank.config,
&[oracle_ai.clone()],
0,
u64::MAX,
&Clock::default(),
i64::MAX as u64,
)
.unwrap();
}
Expand Down Expand Up @@ -633,44 +638,59 @@ impl Liquidator {

let oracle_keys = banks
.iter()
.map(|(_, bank)| bank.config.oracle_keys[0])
.map(|(_, bank)| find_oracle_keys(&bank.config))
.flatten()
.collect::<Vec<_>>();

let mut oracle_accounts =
batch_get_multiple_accounts(rpc_client, &oracle_keys, BatchLoadingConfig::DEFAULT)?;

info!("Found {:?} oracle accounts", oracle_accounts.len());

let mut oracle_with_address = oracle_keys
let oracle_map: HashMap<Pubkey, Option<Account>> = oracle_keys
.iter()
.zip(oracle_accounts.iter_mut())
.collect::<Vec<_>>();
.map(|(pk, account)| (*pk, account.take()))
.collect();

for ((bank_address, bank), (oracle_address, maybe_oracle_address)) in
banks.iter().zip(oracle_with_address.iter_mut())
{
let oracle_account_info =
(*oracle_address, maybe_oracle_address.as_mut().unwrap()).into_account_info();
info!("Found {:?} oracle accounts", oracle_accounts.len());

for (bank_address, bank) in banks.iter() {
let (oracle_address, mut oracle_account) = {
let oracle_addresses = find_oracle_keys(&bank.config);
let mut oracle_account = None;
let mut oracle_address = None;

for address in oracle_addresses.iter() {
if let Some(Some(account)) = oracle_map.get(&address) {
oracle_account = Some(account.clone());
oracle_address = Some(*address);
break;
}
}

(oracle_address.unwrap(), oracle_account.unwrap())
};

let oracle_account_info = (&oracle_address, &mut oracle_account).into_account_info();

self.banks.insert(
*bank_address,
BankWrapper::new(
*bank_address,
*bank,
OracleWrapper::new(
**oracle_address,
oracle_address,
OraclePriceFeedAdapter::try_from_bank_config_with_max_age(
&bank.config,
&[oracle_account_info],
0,
u64::MAX,
&Clock::default(),
i64::MAX as u64,
)
.unwrap(),
),
),
);

self.oracle_to_bank.insert(**oracle_address, *bank_address);
self.oracle_to_bank.insert(oracle_address, *bank_address);
}

Ok(())
Expand Down
6 changes: 3 additions & 3 deletions src/rebalancer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use marginfi::{
use solana_client::rpc_client::RpcClient;
use solana_program::pubkey::Pubkey;
use solana_sdk::{
account_info::IntoAccountInfo, commitment_config::CommitmentConfig,
account_info::IntoAccountInfo, clock::Clock, commitment_config::CommitmentConfig,
signature::read_keypair_file, transaction::VersionedTransaction,
};
use std::{
Expand Down Expand Up @@ -191,8 +191,8 @@ impl Rebalancer {
OraclePriceFeedAdapter::try_from_bank_config_with_max_age(
&bank_to_update.bank.config,
&[oracle_ai.clone()],
0,
u64::MAX,
&Clock::default(),
i64::MAX as u64,
)
.unwrap();
}
Expand Down
78 changes: 57 additions & 21 deletions src/token_account_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ use std::{
sync::{Arc, RwLock},
};

use anchor_lang::accounts::program;
use anchor_spl::associated_token;
use crossbeam::epoch::Owned;
use log::{debug, error, info};
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use sha2::{Digest, Sha256};
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
account::Account,
pubkey::Pubkey,
signature::Keypair,
signer::{SeedDerivable, Signer},
Expand All @@ -30,7 +33,7 @@ pub enum TokenAccountManagerError {

#[derive(Clone)]
pub struct TokenAccountManager {
mint_to_account: Arc<RwLock<HashMap<Pubkey, Pubkey>>>,
mint_to_account: Arc<RwLock<HashMap<Pubkey, (Pubkey, Pubkey)>>>,
rpc_client: Arc<RpcClient>,
}

Expand All @@ -47,15 +50,31 @@ impl TokenAccountManager {
mints: &[Pubkey],
signer: Pubkey,
) -> Result<(), TokenAccountManagerError> {
let mint_owners = batch_get_multiple_accounts(
self.rpc_client.clone(),
mints,
BatchLoadingConfig::DEFAULT,
)
.map_err(|e| {
error!("Failed to load mint accounts: {:?}", e);
TokenAccountManagerError::SetupFailed("Failed to find missing accounts")
})?
.iter()
.map(|a| a.as_ref().unwrap().owner)
.collect::<Vec<_>>();

let mut mint_to_account = self.mint_to_account.write().unwrap();

mints.iter().try_for_each(|mint| {
let address = get_address_for_token_account(signer, *mint, TOKEN_ACCOUNT_SEED)?;
mints
.iter()
.zip(mint_owners)
.try_for_each(|(mint, program_id)| {
let address = get_address_for_token_account(signer, *mint, program_id)?;

mint_to_account.insert(*mint, address);
mint_to_account.insert(*mint, (address, program_id));

Ok::<_, TokenAccountManagerError>(())
})
Ok::<_, TokenAccountManagerError>(())
})
}

pub fn get_mints_and_token_account_addresses(&self) -> (Vec<Pubkey>, Vec<Pubkey>) {
Expand All @@ -72,7 +91,7 @@ impl TokenAccountManager {
.map(|mint| *self.mint_to_account.read().unwrap().get(mint).unwrap())
.collect::<Vec<_>>();

(mints, addresses)
(mints, addresses.iter().map(|(a, _)| *a).collect())
}

pub fn create_token_accounts(
Expand Down Expand Up @@ -107,7 +126,11 @@ impl TokenAccountManager {

// Create missing token accounts
{
let addresses = tas.iter().map(|(_, address)| *address).collect::<Vec<_>>();
let addresses = tas
.iter()
.map(|(mint, address)| vec![*mint, *address])
.flatten()
.collect::<Vec<_>>();

let res = batch_get_multiple_accounts(
rpc_client.clone(),
Expand All @@ -119,20 +142,30 @@ impl TokenAccountManager {
TokenAccountManagerError::SetupFailed("Failed to find missing accounts")
})?;

let tas_to_create = res
let address_to_account_map: HashMap<Pubkey, Option<Account>> = res
.iter()
.zip(tas.iter())
.filter_map(|(res, (mint, address))| {
if res.is_none() {
debug!("Creating token account for mint: {:?}", mint);
Some((address, mint))
.zip(addresses.iter())
.map(|(account, address)| (*address, account.clone()))
.collect();

let tas_to_create = tas.iter()
.filter_map(|(mint, address)| {
let mint_account = address_to_account_map.get(mint).unwrap().as_ref().unwrap();
let maybe_token_account = address_to_account_map.get(address).unwrap();

let program_id = mint_account.owner;
debug!("Token account {} for mint {} program {}, exists {}", address, mint, program_id, maybe_token_account.is_some());
if maybe_token_account.is_none() {
debug!("Creating token account for mint: {:?}, program_id: {}", mint, program_id);
Some((address, mint, program_id))
} else {
None
}

})
.map(|(_, mint)| -> Result<_, TokenAccountManagerError> {
.map(|(_, mint, program_id)| -> Result<_, TokenAccountManagerError> {
let signer_pk = signer.pubkey();
let ix = spl_associated_token_account::instruction::create_associated_token_account_idempotent(&signer_pk, &signer_pk, mint, &spl_token::ID);
let ix = spl_associated_token_account::instruction::create_associated_token_account_idempotent(&signer_pk, &signer_pk, mint, &program_id);

Ok(ix)
})
Expand Down Expand Up @@ -177,7 +210,12 @@ impl TokenAccountManager {
}

pub fn get_address_for_mint(&self, mint: Pubkey) -> Option<Pubkey> {
self.mint_to_account.read().unwrap().get(&mint).copied()
self.mint_to_account
.read()
.unwrap()
.get(&mint)
.as_ref()
.map(|(a, _)| *a)
}
}

Expand Down Expand Up @@ -205,9 +243,7 @@ fn get_keypair_for_token_account(
fn get_address_for_token_account(
signer: Pubkey,
mint: Pubkey,
_seed: &[u8],
program_id: Pubkey,
) -> Result<Pubkey, TokenAccountManagerError> {
Ok(associated_token::get_associated_token_address(
&signer, &mint,
))
Ok(associated_token::get_associated_token_address_with_program_id(&signer, &mint, &program_id))
}
26 changes: 24 additions & 2 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ use backoff::ExponentialBackoff;
use fixed::types::I80F48;
use marginfi::{
bank_authority_seed, bank_seed,
constants::{PYTH_PUSH_MARGINFI_SPONSORED_SHARD_ID, PYTH_PUSH_PYTH_SPONSORED_SHARD_ID},
prelude::MarginfiResult,
state::{
marginfi_account::{calc_value, Balance, BalanceSide, LendingAccount, RequirementType},
marginfi_group::{Bank, BankVaultType, RiskTier},
price::{PriceAdapter, PriceBias},
marginfi_group::{Bank, BankConfig, BankVaultType, RiskTier},
price::{PriceAdapter, PriceBias, PythPushOraclePriceFeed},
},
};
use rayon::{iter::ParallelIterator, slice::ParallelSlice};
Expand Down Expand Up @@ -543,3 +544,24 @@ pub fn calc_weighted_liabs(
Some(liability_weight),
)?)
}

pub fn find_oracle_keys(bank_config: &BankConfig) -> Vec<Pubkey> {
match bank_config.oracle_setup {
marginfi::state::price::OracleSetup::PythPushOracle => {
let feed_id = bank_config.get_pyth_push_oracle_feed_id().unwrap();
vec![
PythPushOraclePriceFeed::find_oracle_address(
PYTH_PUSH_MARGINFI_SPONSORED_SHARD_ID,
feed_id,
)
.0,
PythPushOraclePriceFeed::find_oracle_address(
PYTH_PUSH_PYTH_SPONSORED_SHARD_ID,
feed_id,
)
.0,
]
}
_ => vec![bank_config.oracle_keys.first().unwrap().clone()],
}
}
4 changes: 2 additions & 2 deletions src/wrappers/liquidator_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ impl LiquidatorAccount {
*self.token_program_per_mint.get(&liab_mint).unwrap(),
liquidator_observation_accounts,
liquidatee_observation_accounts,
asset_bank.bank.config.oracle_keys[0],
liab_bank.bank.config.oracle_keys[0],
asset_bank.oracle_adapter.address,
liab_bank.oracle_adapter.address,
liab_mint,
asset_amount,
);
Expand Down
2 changes: 1 addition & 1 deletion src/wrappers/marginfi_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ impl MarginfiAccountWrapper {
.flat_map(|b| {
let bank = banks.get(b).unwrap();

vec![bank.address, bank.bank.config.oracle_keys[0]]
vec![bank.address, bank.oracle_adapter.address]
})
.collect::<Vec<_>>();

Expand Down

0 comments on commit e1e1741

Please sign in to comment.