Compare commits
2 Commits
310dc7776f
...
9820cb4d66
Author | SHA1 | Date |
---|---|---|
|
9820cb4d66 | |
|
f20cb0b4b9 |
|
@ -2616,6 +2616,8 @@ dependencies = [
|
||||||
"anchor-client",
|
"anchor-client",
|
||||||
"anchor-lang 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"anchor-lang 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"anchor-spl 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"anchor-spl 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"anyhow",
|
||||||
|
"bytemuck",
|
||||||
"fixed",
|
"fixed",
|
||||||
"mango-v4",
|
"mango-v4",
|
||||||
"solana-account-decoder",
|
"solana-account-decoder",
|
||||||
|
@ -2626,6 +2628,7 @@ dependencies = [
|
||||||
"switchboard-common",
|
"switchboard-common",
|
||||||
"switchboard-program",
|
"switchboard-program",
|
||||||
"switchboard-solana",
|
"switchboard-solana",
|
||||||
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2959,6 +2962,7 @@ dependencies = [
|
||||||
"serum_dex",
|
"serum_dex",
|
||||||
"solana-address-lookup-table-program",
|
"solana-address-lookup-table-program",
|
||||||
"solana-program",
|
"solana-program",
|
||||||
|
"solana-sdk",
|
||||||
"solana-security-txt",
|
"solana-security-txt",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
"switchboard-program",
|
"switchboard-program",
|
||||||
|
|
|
@ -7,6 +7,10 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
anyhow = "1.0"
|
||||||
|
bytemuck = { version = "^1.7.2", features = ["min_const_generics"] }
|
||||||
|
|
||||||
solana-rpc = "~1.16.7"
|
solana-rpc = "~1.16.7"
|
||||||
solana-client = "~1.16.7"
|
solana-client = "~1.16.7"
|
||||||
solana-account-decoder = "~1.16.7"
|
solana-account-decoder = "~1.16.7"
|
||||||
|
@ -17,7 +21,7 @@ anchor-spl = "0.28.0"
|
||||||
anchor-lang = "0.28.0"
|
anchor-lang = "0.28.0"
|
||||||
anchor-client = "0.28.0"
|
anchor-client = "0.28.0"
|
||||||
|
|
||||||
mango-v4 = { git = "https://github.com/blockworks-foundation/mango-v4.git", branch = "update-solana-1-16-anchor-28", features = ["cpi"] }
|
mango-v4 = { git = "https://github.com/blockworks-foundation/mango-v4.git", branch = "update-solana-1-16-anchor-28", features = ["client"] }
|
||||||
fixed = { git = "https://github.com/blockworks-foundation/fixed.git", branch = "v1.11.0-borsh0_10-mango" }
|
fixed = { git = "https://github.com/blockworks-foundation/fixed.git", branch = "v1.11.0-borsh0_10-mango" }
|
||||||
|
|
||||||
switchboard-program = "0.2.0"
|
switchboard-program = "0.2.0"
|
||||||
|
|
127
src/main.rs
127
src/main.rs
|
@ -1,7 +1,126 @@
|
||||||
fn main() {
|
use anchor_lang::prelude::Pubkey;
|
||||||
println!("Hello, world!");
|
use anyhow::Error;
|
||||||
|
use mango_v4::state::{Bank, MangoAccountValue};
|
||||||
|
use solana_account_decoder::UiAccountEncoding;
|
||||||
|
use solana_client::rpc_config::RpcProgramAccountsConfig;
|
||||||
|
use solana_client::rpc_filter::{Memcmp, RpcFilterType};
|
||||||
|
use solana_client::{
|
||||||
|
nonblocking::rpc_client::RpcClient as RpcClientAsync, rpc_config::RpcAccountInfoConfig,
|
||||||
|
};
|
||||||
|
use solana_sdk::pubkey;
|
||||||
|
|
||||||
// get JITOSOL value of a mango account
|
// metadata information can be found at: https://api.mngo.cloud/data/v4/group-metadata
|
||||||
|
const JITOSOL_TOKEN_INDEX: u16 = 501;
|
||||||
|
const GROUP: Pubkey = pubkey!("78b8f4cGCwmZ9ysPFMWLaLTkkaYnUjwMJYStWe5RTSSX");
|
||||||
|
|
||||||
// get
|
pub async fn fetch_mango_accounts_by_owner(
|
||||||
|
rpc: &RpcClientAsync,
|
||||||
|
program: Pubkey,
|
||||||
|
group: Pubkey,
|
||||||
|
owner: Pubkey,
|
||||||
|
) -> anyhow::Result<Vec<(Pubkey, MangoAccountValue)>> {
|
||||||
|
let config = RpcProgramAccountsConfig {
|
||||||
|
filters: Some(vec![
|
||||||
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(
|
||||||
|
0,
|
||||||
|
[243, 228, 247, 3, 169, 52, 175, 31].to_vec(), // mango discriminator
|
||||||
|
)),
|
||||||
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(8, group.to_bytes().to_vec())),
|
||||||
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(40, owner.to_bytes().to_vec())),
|
||||||
|
]),
|
||||||
|
account_config: RpcAccountInfoConfig {
|
||||||
|
encoding: Some(UiAccountEncoding::Base64),
|
||||||
|
..RpcAccountInfoConfig::default()
|
||||||
|
},
|
||||||
|
..RpcProgramAccountsConfig::default()
|
||||||
|
};
|
||||||
|
rpc.get_program_accounts_with_config(&program, config)
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.map(|(key, account)| Ok((key, MangoAccountValue::from_bytes(&account.data[8..])?)))
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_banks(
|
||||||
|
rpc: &RpcClientAsync,
|
||||||
|
program: Pubkey,
|
||||||
|
group: Pubkey,
|
||||||
|
) -> anyhow::Result<Vec<(Pubkey, Bank)>> {
|
||||||
|
let filters = vec![RpcFilterType::Memcmp(Memcmp::new_raw_bytes(
|
||||||
|
8,
|
||||||
|
group.to_bytes().to_vec(),
|
||||||
|
))];
|
||||||
|
let account_type_filter = RpcFilterType::Memcmp(Memcmp::new_raw_bytes(
|
||||||
|
0,
|
||||||
|
[142, 49, 166, 242, 50, 66, 97, 188].to_vec(), // bank discriminator
|
||||||
|
));
|
||||||
|
let config = RpcProgramAccountsConfig {
|
||||||
|
filters: Some([vec![account_type_filter], filters].concat()),
|
||||||
|
account_config: RpcAccountInfoConfig {
|
||||||
|
encoding: Some(UiAccountEncoding::Base64),
|
||||||
|
..RpcAccountInfoConfig::default()
|
||||||
|
},
|
||||||
|
..RpcProgramAccountsConfig::default()
|
||||||
|
};
|
||||||
|
rpc.get_program_accounts_with_config(&program, config)
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.map(|(key, account)| Ok((key, *bytemuck::from_bytes::<Bank>(&(&account.data[8..])))))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn fetch_jitosol_bank(
|
||||||
|
rpc: &RpcClientAsync,
|
||||||
|
program: Pubkey,
|
||||||
|
group: Pubkey,
|
||||||
|
) -> anyhow::Result<Bank> {
|
||||||
|
let token_banks = fetch_banks(&rpc, program, group).await.unwrap();
|
||||||
|
match token_banks
|
||||||
|
.iter()
|
||||||
|
.find(|(_, b)| b.token_index == JITOSOL_TOKEN_INDEX)
|
||||||
|
{
|
||||||
|
Some(jb) => Ok(jb.1),
|
||||||
|
None => Err(Error::msg("JitoSol token bank not found")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn fetch_jitosol_exposure(
|
||||||
|
rpc: &RpcClientAsync,
|
||||||
|
program: Pubkey,
|
||||||
|
group: Pubkey,
|
||||||
|
owner_pk: Pubkey,
|
||||||
|
jito_bank: Bank,
|
||||||
|
) -> anyhow::Result<f64> {
|
||||||
|
let mut jitosol_amount = 0f64;
|
||||||
|
|
||||||
|
let mango_account_tuples = fetch_mango_accounts_by_owner(&rpc, program, group, owner_pk)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
for (_, acct) in mango_account_tuples.iter() {
|
||||||
|
match acct.token_position(JITOSOL_TOKEN_INDEX) {
|
||||||
|
Ok(token_position) => {
|
||||||
|
// token_position.ui is positive in the case of deposits and negative in the case of borrows
|
||||||
|
jitosol_amount += token_position.ui(&jito_bank).to_num::<f64>().abs()
|
||||||
|
}
|
||||||
|
Err(_) => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(jitosol_amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let program = mango_v4::ID;
|
||||||
|
let owner_pk = pubkey!("Wallet Private Key Here");
|
||||||
|
let rpc =
|
||||||
|
RpcClientAsync::new("RPC HERE".to_string());
|
||||||
|
|
||||||
|
let jito_bank = fetch_jitosol_bank(&rpc, program, GROUP).await.unwrap();
|
||||||
|
let jitosol_exposure = fetch_jitosol_exposure(&rpc, program, GROUP, owner_pk, jito_bank)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
println!(
|
||||||
|
"Mango accounts owned by {:?} have {:?} of JitoSol exposure",
|
||||||
|
owner_pk, jitosol_exposure
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue