769 lines
28 KiB
Rust
769 lines
28 KiB
Rust
use std::collections::HashMap;
|
|
use std::fs::File;
|
|
use std::io::Write;
|
|
use std::mem::size_of;
|
|
use std::str::FromStr;
|
|
|
|
use anyhow::Result;
|
|
use clap::Clap;
|
|
use common::{Cluster, convert_assertion_error, create_account_rent_exempt, create_signer_key_and_nonce, create_token_account, read_keypair_file, send_instructions};
|
|
use fixed::types::U64F64;
|
|
use mango::state::{Loadable, MangoGroup, MarginAccount, NUM_TOKENS};
|
|
use serde_json::{json, Value};
|
|
use solana_client::rpc_client::RpcClient;
|
|
use solana_client::rpc_request::TokenAccountsFilter;
|
|
use solana_sdk::account::{Account};
|
|
use solana_sdk::commitment_config::CommitmentConfig;
|
|
use solana_sdk::program_pack::Pack;
|
|
use solana_sdk::pubkey::Pubkey;
|
|
use solana_sdk::signature::{Signer};
|
|
use mango::instruction::{init_mango_group, init_margin_account, withdraw, borrow, deposit, settle_borrow, change_borrow_limit};
|
|
|
|
#[derive(Clap, Debug)]
|
|
pub struct Opts {
|
|
#[clap(default_value = "mainnet")]
|
|
pub cluster: Cluster,
|
|
#[clap(subcommand)]
|
|
pub command: Command,
|
|
}
|
|
|
|
#[derive(Clap, Debug)]
|
|
pub enum Command {
|
|
InitMangoGroup {
|
|
#[clap(long, short)]
|
|
payer: String,
|
|
#[clap(long, short)]
|
|
ids_path: String,
|
|
#[clap(long, short)]
|
|
tokens: Vec<String>,
|
|
#[clap(long, short)]
|
|
mango_program_id: Option<String>,
|
|
#[clap(long, short)]
|
|
borrow_limits: Vec<f64>,
|
|
},
|
|
InitMarginAccount {
|
|
#[clap(long, short)]
|
|
payer: String,
|
|
#[clap(long, short)]
|
|
ids_path: String,
|
|
#[clap(long, short)]
|
|
mango_group_name: String,
|
|
},
|
|
Deposit {
|
|
#[clap(long, short)]
|
|
payer: String,
|
|
#[clap(long, short)]
|
|
ids_path: String,
|
|
#[clap(long, short)]
|
|
mango_group_name: String,
|
|
#[clap(long)]
|
|
token_symbol: String,
|
|
#[clap(long)]
|
|
margin_account: String,
|
|
#[clap(long)]
|
|
quantity: f64
|
|
},
|
|
Withdraw {
|
|
#[clap(long, short)]
|
|
payer: String,
|
|
#[clap(long, short)]
|
|
ids_path: String,
|
|
#[clap(long, short)]
|
|
mango_group_name: String,
|
|
#[clap(long)]
|
|
token_symbol: String,
|
|
#[clap(long)]
|
|
margin_account: String,
|
|
#[clap(long)]
|
|
quantity: f64
|
|
},
|
|
Borrow {
|
|
#[clap(long, short)]
|
|
payer: String,
|
|
#[clap(long, short)]
|
|
ids_path: String,
|
|
#[clap(long)]
|
|
mango_group_name: String,
|
|
#[clap(long)]
|
|
margin_account: String,
|
|
#[clap(long, short)]
|
|
token_symbol: String,
|
|
#[clap(long, short)]
|
|
quantity: f64
|
|
},
|
|
SettleBorrow {
|
|
#[clap(long, short)]
|
|
payer: String,
|
|
#[clap(long, short)]
|
|
ids_path: String,
|
|
#[clap(long)]
|
|
mango_group_name: String,
|
|
#[clap(long)]
|
|
margin_account: String,
|
|
#[clap(long, short)]
|
|
token_symbol: String,
|
|
#[clap(long, short)]
|
|
quantity: Option<f64>
|
|
},
|
|
ConvertAssertionError {
|
|
#[clap(long, short)]
|
|
code: u32,
|
|
},
|
|
PrintBs58 {
|
|
#[clap(long, short)]
|
|
keypair: String,
|
|
#[clap(long, short)]
|
|
filepath: Option<String>,
|
|
},
|
|
|
|
PlaceOrder {
|
|
|
|
},
|
|
SettleFunds {
|
|
|
|
},
|
|
CancelOrder {
|
|
|
|
},
|
|
ChangeBorrowLimit {
|
|
#[clap(long, short)]
|
|
payer: String, // assumes for now payer is same as admin
|
|
#[clap(long, short)]
|
|
ids_path: String,
|
|
#[clap(long)]
|
|
mango_group_name: String,
|
|
#[clap(long)]
|
|
token_symbol: String,
|
|
#[clap(long)]
|
|
borrow_limit: f64
|
|
},
|
|
SwitchOracle {
|
|
#[clap(long, short)]
|
|
payer: String, // assumes for now payer is same as admin
|
|
#[clap(long, short)]
|
|
ids_path: String,
|
|
#[clap(long)]
|
|
mango_group_name: String,
|
|
#[clap(long)]
|
|
token_symbol: String,
|
|
#[clap(long)]
|
|
oracle: String,
|
|
},
|
|
}
|
|
|
|
impl Opts {
|
|
fn client(&self) -> RpcClient {
|
|
RpcClient::new_with_commitment(self.cluster.url().to_string(),
|
|
CommitmentConfig::confirmed())
|
|
}
|
|
}
|
|
|
|
#[allow(unused)]
|
|
fn get_accounts(client: &RpcClient, pks: &[Pubkey]) -> Vec<(Pubkey, Account)> {
|
|
client.get_multiple_accounts(pks)
|
|
.unwrap()
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(i, a)| (pks[i], a.as_ref().unwrap().clone()))
|
|
.collect()
|
|
}
|
|
|
|
|
|
#[derive(Clone)]
|
|
struct ClusterIds {
|
|
pub mango_program_id: Pubkey,
|
|
pub dex_program_id: Pubkey,
|
|
pub mango_groups: HashMap<String, MangoGroupIds>,
|
|
pub oracles: HashMap<String, Pubkey>,
|
|
pub spot_markets: HashMap<String, Pubkey>,
|
|
pub symbols: HashMap<String, Pubkey>
|
|
}
|
|
|
|
|
|
impl ClusterIds {
|
|
pub fn load(value: &Value) -> Self {
|
|
let mango_groups: HashMap<String, MangoGroupIds> = value["mango_groups"].as_object().unwrap().iter().map(
|
|
|(k, v)| (k.clone(), MangoGroupIds::load(v))
|
|
).collect();
|
|
|
|
ClusterIds {
|
|
mango_program_id: get_pk(value, "mango_program_id"),
|
|
dex_program_id: get_pk(value, "dex_program_id"),
|
|
mango_groups,
|
|
oracles: get_map_pks(&value["oracles"]),
|
|
spot_markets: get_map_pks(&value["spot_markets"]),
|
|
symbols: get_map_pks(&value["symbols"])
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn to_json(&self) -> Value {
|
|
json!({"hello": "world"})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct MangoGroupIds {
|
|
pub mango_group_pk: Pubkey,
|
|
pub mint_pks: Vec<Pubkey>,
|
|
pub spot_market_pks: Vec<Pubkey>,
|
|
pub vault_pks: Vec<Pubkey>,
|
|
pub oracle_pks: Vec<Pubkey>
|
|
}
|
|
|
|
impl MangoGroupIds {
|
|
pub fn load(value: &Value) -> Self {
|
|
MangoGroupIds {
|
|
mango_group_pk: get_pk(value, "mango_group_pk"),
|
|
mint_pks: get_vec_pks(&value["mint_pks"]),
|
|
spot_market_pks: get_vec_pks(&value["spot_market_pks"]),
|
|
vault_pks: get_vec_pks(&value["vault_pks"]),
|
|
oracle_pks: get_vec_pks(&value["oracle_pks"])
|
|
}
|
|
}
|
|
pub fn get_token_index(&self, token_pk: &Pubkey) -> Option<usize> {
|
|
self.mint_pks.iter().position(|pk| pk == token_pk)
|
|
}
|
|
}
|
|
|
|
|
|
pub fn start(opts: Opts) -> Result<()> {
|
|
let client = opts.client();
|
|
match opts.command {
|
|
Command::InitMangoGroup {
|
|
payer,
|
|
ids_path,
|
|
tokens,
|
|
mango_program_id,
|
|
borrow_limits
|
|
} => {
|
|
println!("InitMangoGroup");
|
|
let payer = read_keypair_file(payer.as_str())?;
|
|
let mut ids: Value = serde_json::from_reader(File::open(&ids_path)?)?;
|
|
|
|
let cluster_name = opts.cluster.name();
|
|
let cluster_ids = &ids[cluster_name];
|
|
let mango_program_id = if let Some(pk_str) = mango_program_id {
|
|
Pubkey::from_str(pk_str.as_str())?
|
|
} else {
|
|
let mango_program_id = cluster_ids["mango_program_id"].as_str().unwrap();
|
|
Pubkey::from_str(mango_program_id)?
|
|
};
|
|
|
|
let dex_program_id = cluster_ids["dex_program_id"].as_str().unwrap();
|
|
|
|
// let mango_group_kp = Keypair::new();
|
|
// let create_mango_group_acc = create_account_instr(
|
|
// &client,
|
|
// &payer,
|
|
// &mango_group_kp,
|
|
// size_of::<MangoGroup>(),
|
|
// &mango_program_id
|
|
// )?;
|
|
//
|
|
// let mango_group_pk = mango_group_kp.pubkey();
|
|
// println!("mango_group_pk: {}", mango_group_pk.to_string());
|
|
|
|
let mango_group_pk = create_account_rent_exempt(
|
|
&client, &payer, size_of::<MangoGroup>(), &mango_program_id
|
|
)?.pubkey();
|
|
|
|
|
|
let (signer_key, signer_nonce) = create_signer_key_and_nonce(&mango_program_id, &mango_group_pk);
|
|
let dex_program_id = Pubkey::from_str(dex_program_id)?;
|
|
assert!(tokens.len() <= NUM_TOKENS && tokens.len() >= 2);
|
|
|
|
let symbols = &cluster_ids["symbols"];
|
|
let mint_pks: Vec<Pubkey> = tokens.iter().map(
|
|
|token| get_symbol_pk(symbols, token.as_str())
|
|
).collect();
|
|
|
|
let symbol_pks: HashMap<String, String> = tokens.iter().map(
|
|
|token| (token.clone(), get_symbol_pk(symbols, token.as_str()).to_string())
|
|
).collect();
|
|
|
|
// Create vaults owned by mango program id and check if SRM in mango group
|
|
let srm_mint_pk = get_symbol_pk(symbols, "SRM");
|
|
let mut srm_in_mango_group_index: Option<usize> = None;
|
|
let mut vault_pks = vec![];
|
|
for i in 0..mint_pks.len() {
|
|
println!("Creating vault for: {}", &tokens[i]);
|
|
let vault_pk = create_token_account(
|
|
&client, &mint_pks[i], &signer_key, &payer
|
|
)?.pubkey();
|
|
if &mint_pks[i] == &srm_mint_pk {
|
|
srm_in_mango_group_index = Some(i);
|
|
}
|
|
vault_pks.push(vault_pk);
|
|
}
|
|
|
|
// If SRM is in mango group don't create a new vault for it, instead use existing
|
|
let srm_vault_pk: Pubkey;
|
|
if srm_in_mango_group_index.is_some() {
|
|
println!("Reusing existing vault for: SRM");
|
|
srm_vault_pk = vault_pks[srm_in_mango_group_index.unwrap()];
|
|
} else {
|
|
println!("Creating vault for: SRM");
|
|
srm_vault_pk = create_token_account(
|
|
&client, &srm_mint_pk, &signer_key, &payer
|
|
)?.pubkey();
|
|
};
|
|
|
|
println!("set up spot markets");
|
|
// Find corresponding spot markets
|
|
let mut spot_market_pks = vec![];
|
|
let mut oracle_pks = vec![];
|
|
let spot_markets = &cluster_ids["spot_markets"];
|
|
let oracles = &cluster_ids["oracles"];
|
|
let quote_symbol = &tokens[tokens.len() - 1].as_str();
|
|
let mut spot_market_symbols: HashMap<String, String> = HashMap::new();
|
|
for i in 0..(tokens.len() - 1) {
|
|
let base_symbol = tokens[i].as_str();
|
|
let market_symbol = format!("{}/{}", base_symbol, quote_symbol);
|
|
println!("{}", market_symbol);
|
|
spot_market_pks.push(get_symbol_pk(spot_markets, market_symbol.as_str()));
|
|
oracle_pks.push(get_symbol_pk(oracles, market_symbol.as_str()));
|
|
spot_market_symbols.insert(market_symbol.clone(), spot_markets[market_symbol.as_str()].as_str().expect("spot market not found").to_string());
|
|
}
|
|
|
|
println!("borrow limits");
|
|
let mut borr_lims = [0u64; NUM_TOKENS];
|
|
for i in 0..NUM_TOKENS {
|
|
let mint_acc = client.get_account(&mint_pks[i])?;
|
|
let mint = spl_token::state::Mint::unpack(mint_acc.data.as_slice())?;
|
|
borr_lims[i] = spl_token::ui_amount_to_amount(borrow_limits[i], mint.decimals);
|
|
}
|
|
|
|
// Send out instruction
|
|
|
|
let instruction = init_mango_group(
|
|
&mango_program_id,
|
|
&mango_group_pk,
|
|
&signer_key,
|
|
&dex_program_id,
|
|
&srm_vault_pk,
|
|
&payer.pubkey(),
|
|
mint_pks.as_slice(),
|
|
vault_pks.as_slice(),
|
|
spot_market_pks.as_slice(),
|
|
oracle_pks.as_slice(),
|
|
signer_nonce,
|
|
U64F64::from_num(1.1),
|
|
U64F64::from_num(1.2),
|
|
borr_lims
|
|
)?;
|
|
let instructions = vec![instruction];
|
|
let signers = vec![&payer];
|
|
send_instructions(&client, instructions, signers, &payer.pubkey())?;
|
|
println!("InitMangoGroup success");
|
|
// Edit the json file and add the keys associated with this mango group
|
|
let group_name: String = tokens.join("_");
|
|
let vault_pk_strs: Vec<String> = vault_pks.iter().map(|pk| pk.to_string()).collect();
|
|
let spot_market_pk_strs: Vec<String> = spot_market_pks.iter().map(|pk| pk.to_string()).collect();
|
|
let mint_pk_strs: Vec<String> = mint_pks.iter().map(|pk| pk.to_string()).collect();
|
|
let oracle_pk_strs: Vec<String> = oracle_pks.iter().map(|pk| pk.to_string()).collect();
|
|
|
|
let group_keys = json!({
|
|
"mango_group_pk": mango_group_pk.to_string(),
|
|
"vault_pks": vault_pk_strs,
|
|
"mint_pks": mint_pk_strs,
|
|
"spot_market_pks": spot_market_pk_strs,
|
|
"oracle_pks": oracle_pk_strs,
|
|
"symbols": symbol_pks,
|
|
"srm_vault_pk": srm_vault_pk.to_string(),
|
|
"spot_market_symbols": spot_market_symbols
|
|
});
|
|
|
|
let ids = ids.as_object_mut().unwrap();
|
|
let cluster_ids = ids.get_mut(cluster_name).unwrap().as_object_mut().unwrap();
|
|
cluster_ids.insert("mango_program_id".to_string(), Value::from(mango_program_id.to_string()));
|
|
let mango_groups = cluster_ids.get_mut("mango_groups").unwrap().as_object_mut().unwrap();
|
|
mango_groups.insert(group_name, group_keys);
|
|
let f = File::create(ids_path.as_str()).unwrap();
|
|
serde_json::to_writer_pretty(&f, &ids).unwrap();
|
|
|
|
}
|
|
Command::InitMarginAccount {
|
|
payer,
|
|
ids_path,
|
|
mango_group_name
|
|
} => {
|
|
|
|
println!("InitMarginAccount");
|
|
let payer = read_keypair_file(payer.as_str())?;
|
|
let ids: Value = serde_json::from_reader(File::open(&ids_path)?)?;
|
|
let cluster_name = opts.cluster.name();
|
|
let cluster_ids = &ids[cluster_name];
|
|
let mango_program_id = cluster_ids["mango_program_id"].as_str().unwrap();
|
|
let mango_program_id = Pubkey::from_str(mango_program_id)?;
|
|
|
|
let group_ids = &cluster_ids["mango_groups"][mango_group_name.as_str()];
|
|
let mango_group_pk = Pubkey::from_str(group_ids["mango_group_pk"].as_str().unwrap())?;
|
|
|
|
let margin_account_pk = create_account_rent_exempt(
|
|
&client, &payer, size_of::<MarginAccount>(), &mango_program_id
|
|
)?.pubkey();
|
|
|
|
|
|
// Send out instruction
|
|
let instruction = init_margin_account(
|
|
&mango_program_id,
|
|
&mango_group_pk,
|
|
&margin_account_pk,
|
|
&payer.pubkey(),
|
|
)?;
|
|
let instructions = vec![instruction];
|
|
let signers = vec![&payer];
|
|
send_instructions(&client, instructions, signers, &payer.pubkey())?;
|
|
|
|
println!("MarginAccount created");
|
|
println!("{}", margin_account_pk.to_string());
|
|
}
|
|
Command::Withdraw {
|
|
payer,
|
|
ids_path,
|
|
mango_group_name,
|
|
token_symbol,
|
|
margin_account,
|
|
quantity
|
|
} => {
|
|
println!("Withdraw");
|
|
let payer = read_keypair_file(payer.as_str())?;
|
|
let ids: Value = serde_json::from_reader(File::open(&ids_path)?)?;
|
|
let cluster_name = opts.cluster.name();
|
|
let cluster_ids = &ids[cluster_name];
|
|
let cids = ClusterIds::load(cluster_ids);
|
|
let mgids = &cids.mango_groups[&mango_group_name];
|
|
|
|
let mint_pk = cids.symbols[&token_symbol].clone();
|
|
let token_accounts = client.get_token_accounts_by_owner_with_commitment(
|
|
&payer.pubkey(),
|
|
TokenAccountsFilter::Mint(mint_pk),
|
|
CommitmentConfig::confirmed()
|
|
)?.value;
|
|
assert!(token_accounts.len() > 0);
|
|
// Take first token account
|
|
let rka = &token_accounts[0];
|
|
let token_account_pk = Pubkey::from_str(rka.pubkey.as_str())?;
|
|
|
|
let mint_acc = client.get_account(&mint_pk)?;
|
|
let mint = spl_token::state::Mint::unpack(mint_acc.data.as_slice())?;
|
|
|
|
let margin_account_pk = Pubkey::from_str(margin_account.as_str())?;
|
|
let margin_account = client.get_account(&margin_account_pk)?;
|
|
let margin_account = MarginAccount::load_from_bytes(margin_account.data.as_slice())?;
|
|
|
|
let mango_group_acc = client.get_account(&mgids.mango_group_pk)?;
|
|
let mango_group = MangoGroup::load_from_bytes(mango_group_acc.data.as_slice())?;
|
|
let token_index = mango_group.get_token_index(&mint_pk).unwrap();
|
|
let vault_pk: &Pubkey = &mgids.vault_pks[token_index];
|
|
|
|
let instruction = withdraw(
|
|
&cids.mango_program_id,
|
|
&mgids.mango_group_pk,
|
|
&margin_account_pk,
|
|
&payer.pubkey(), // TODO fetch margin account and determine owner
|
|
&token_account_pk,
|
|
vault_pk,
|
|
&mango_group.signer_key,
|
|
&margin_account.open_orders,
|
|
mgids.oracle_pks.as_slice(),
|
|
spl_token::ui_amount_to_amount(quantity, mint.decimals)
|
|
)?;
|
|
|
|
let instructions = vec![instruction];
|
|
let signers = vec![&payer];
|
|
send_instructions(&client, instructions, signers, &payer.pubkey())?;
|
|
|
|
}
|
|
Command::Borrow {
|
|
payer,
|
|
ids_path,
|
|
mango_group_name,
|
|
margin_account,
|
|
token_symbol,
|
|
quantity
|
|
} => {
|
|
let payer = read_keypair_file(payer.as_str())?;
|
|
let ids: Value = serde_json::from_reader(File::open(&ids_path)?)?;
|
|
let cluster_name = opts.cluster.name();
|
|
|
|
let cluster_ids = &ids[cluster_name];
|
|
let mango_group_ids = &cluster_ids["mango_groups"][mango_group_name.as_str()];
|
|
|
|
let mango_program_id = get_pk(cluster_ids, "mango_program_id");
|
|
let mango_group_pk = get_pk(mango_group_ids, "mango_group_pk");
|
|
let margin_account_pk = Pubkey::from_str(margin_account.as_str())?;
|
|
|
|
let margin_account = client.get_account(&margin_account_pk)?;
|
|
let margin_account = MarginAccount::load_from_bytes(margin_account.data.as_slice())?;
|
|
assert_eq!(margin_account.owner, payer.pubkey());
|
|
let open_orders_pks = margin_account.open_orders;
|
|
|
|
let tokens: Vec<&str> = mango_group_name.split("_").collect();
|
|
let quote_symbol = tokens.last().unwrap();
|
|
let oracles = &cluster_ids["oracles"];
|
|
let mut oracle_pks = vec![];
|
|
for i in 0..(tokens.len() - 1) {
|
|
let market_symbol = format!("{}/{}", tokens[i], quote_symbol);
|
|
oracle_pks.push(get_pk(oracles, market_symbol.as_str()));
|
|
}
|
|
|
|
let mint_pks = get_vec_pks(&mango_group_ids["mint_pks"]);
|
|
|
|
let token_index = tokens.iter().position(|t| *t == token_symbol.as_str()).unwrap();
|
|
let mint_acc = client.get_account(&mint_pks[token_index])?;
|
|
let mint = spl_token::state::Mint::unpack(mint_acc.data.as_slice())?;
|
|
|
|
let instruction = borrow(
|
|
&mango_program_id,
|
|
&mango_group_pk,
|
|
&margin_account_pk,
|
|
&margin_account.owner,
|
|
&open_orders_pks,
|
|
oracle_pks.as_slice(),
|
|
token_index,
|
|
spl_token::ui_amount_to_amount(quantity, mint.decimals)
|
|
)?;
|
|
|
|
let instructions = vec![instruction];
|
|
let signers = vec![&payer];
|
|
send_instructions(&client, instructions, signers, &payer.pubkey())?;
|
|
|
|
}
|
|
Command::ConvertAssertionError {
|
|
code
|
|
} => {
|
|
println!("ConvertAssertionError");
|
|
let (line, file_id) = convert_assertion_error(code);
|
|
println!("file {} line {}", file_id, line);
|
|
}
|
|
Command::Deposit {
|
|
payer,
|
|
ids_path,
|
|
mango_group_name,
|
|
token_symbol,
|
|
margin_account,
|
|
quantity
|
|
} => {
|
|
let payer = read_keypair_file(payer.as_str())?;
|
|
let ids: Value = serde_json::from_reader(File::open(&ids_path)?)?;
|
|
let cluster_name = opts.cluster.name();
|
|
let cluster_ids = &ids[cluster_name];
|
|
let mango_group_ids = &cluster_ids["mango_groups"][mango_group_name.as_str()];
|
|
|
|
let mango_program_id = cluster_ids["mango_program_id"].as_str().unwrap();
|
|
let mango_program_id = Pubkey::from_str(mango_program_id)?;
|
|
let symbols = &cluster_ids["symbols"];
|
|
let mint_pk = get_symbol_pk(symbols, token_symbol.as_str());
|
|
|
|
// Fetch the token wallet for this user
|
|
let token_accounts = client.get_token_accounts_by_owner_with_commitment(
|
|
&payer.pubkey(),
|
|
TokenAccountsFilter::Mint(mint_pk),
|
|
CommitmentConfig::confirmed()
|
|
)?.value;
|
|
assert!(token_accounts.len() > 0);
|
|
// Take first token account
|
|
let rka = &token_accounts[0];
|
|
let token_account_pk = Pubkey::from_str(rka.pubkey.as_str())?;
|
|
|
|
let mint_acc = client.get_account(&mint_pk)?;
|
|
let mint = spl_token::state::Mint::unpack(mint_acc.data.as_slice())?;
|
|
let margin_account_pk = Pubkey::from_str(margin_account.as_str())?;
|
|
|
|
let mango_group_pk = Pubkey::from_str(mango_group_ids["mango_group_pk"].as_str().unwrap())?;
|
|
let mango_group_acc = client.get_account(&mango_group_pk)?;
|
|
let mango_group = MangoGroup::load_from_bytes(mango_group_acc.data.as_slice())?;
|
|
let token_index = mango_group.get_token_index(&mint_pk).unwrap();
|
|
let vault_pk: &Pubkey = &mango_group.vaults[token_index];
|
|
|
|
// Send out instruction
|
|
let instruction = deposit(
|
|
&mango_program_id,
|
|
&mango_group_pk,
|
|
&margin_account_pk,
|
|
&payer.pubkey(),
|
|
&token_account_pk,
|
|
vault_pk,
|
|
spl_token::ui_amount_to_amount(quantity, mint.decimals)
|
|
)?;
|
|
let instructions = vec![instruction];
|
|
let signers = vec![&payer];
|
|
send_instructions(&client, instructions, signers, &payer.pubkey())?;
|
|
|
|
println!("Deposited");
|
|
let margin_account_acc = client.get_account(&margin_account_pk)?;
|
|
let margin_account = MarginAccount::load_from_bytes(margin_account_acc.data.as_slice())?;
|
|
let mval: u64 = margin_account.deposits[token_index].to_num();
|
|
println!("{}", mval);
|
|
}
|
|
|
|
Command::PrintBs58 {
|
|
keypair,
|
|
filepath
|
|
} => {
|
|
|
|
let keypair = read_keypair_file(keypair.as_str())?;
|
|
match filepath {
|
|
None => {
|
|
println!("{}", keypair.to_base58_string());
|
|
}
|
|
Some(filepath) => {
|
|
let mut f = File::create(filepath.as_str()).unwrap();
|
|
write!(&mut f, "{}", keypair.to_base58_string())?;
|
|
}
|
|
}
|
|
}
|
|
Command::SettleBorrow {
|
|
payer,
|
|
ids_path,
|
|
mango_group_name,
|
|
margin_account,
|
|
token_symbol,
|
|
quantity
|
|
} => {
|
|
let payer = read_keypair_file(payer.as_str())?;
|
|
let ids: Value = serde_json::from_reader(File::open(&ids_path)?)?;
|
|
let cluster_name = opts.cluster.name();
|
|
let cluster_ids = &ids[cluster_name];
|
|
let cids = ClusterIds::load(cluster_ids);
|
|
let mgids = cids.mango_groups[&mango_group_name].clone();
|
|
|
|
let margin_account_pk = Pubkey::from_str(margin_account.as_str())?;
|
|
let margin_account = client.get_account(&margin_account_pk)?;
|
|
let margin_account = MarginAccount::load_from_bytes(margin_account.data.as_slice())?;
|
|
assert_eq!(margin_account.owner, payer.pubkey());
|
|
|
|
let token_pk = &cids.symbols[&token_symbol];
|
|
let token_i = mgids.get_token_index(token_pk).unwrap();
|
|
|
|
let mint_acc = client.get_account(token_pk)?;
|
|
let mint = spl_token::state::Mint::unpack(mint_acc.data.as_slice())?;
|
|
|
|
let quantity = match quantity {
|
|
None => unimplemented!(),
|
|
Some(q) => spl_token::ui_amount_to_amount(q, mint.decimals)
|
|
};
|
|
let instruction = settle_borrow(
|
|
&cids.mango_program_id,
|
|
&mgids.mango_group_pk,
|
|
&margin_account_pk,
|
|
&margin_account.owner,
|
|
token_i,
|
|
quantity
|
|
)?;
|
|
let instructions = vec![instruction];
|
|
let signers = vec![&payer];
|
|
send_instructions(&client, instructions, signers, &payer.pubkey())?;
|
|
|
|
}
|
|
Command::PlaceOrder { .. } => {}
|
|
Command::SettleFunds { .. } => {}
|
|
Command::CancelOrder { .. } => {}
|
|
Command::ChangeBorrowLimit {
|
|
payer,
|
|
ids_path,
|
|
mango_group_name,
|
|
token_symbol,
|
|
borrow_limit
|
|
} => {
|
|
println!("ChangeBorrowLimit");
|
|
let payer = read_keypair_file(payer.as_str())?;
|
|
let ids: Value = serde_json::from_reader(File::open(&ids_path)?)?;
|
|
let cluster_name = opts.cluster.name();
|
|
let cluster_ids = &ids[cluster_name];
|
|
let cids = ClusterIds::load(cluster_ids);
|
|
let mgids = cids.mango_groups[&mango_group_name].clone();
|
|
|
|
let token_pk = &cids.symbols[&token_symbol];
|
|
let token_i = mgids.get_token_index(token_pk).unwrap();
|
|
|
|
let mint_acc = client.get_account(token_pk)?;
|
|
let mint = spl_token::state::Mint::unpack(mint_acc.data.as_slice())?;
|
|
let instruction = change_borrow_limit(
|
|
&cids.mango_program_id,
|
|
&mgids.mango_group_pk,
|
|
&payer.pubkey(),
|
|
token_i,
|
|
spl_token::ui_amount_to_amount(borrow_limit, mint.decimals)
|
|
|
|
)?;
|
|
let instructions = vec![instruction];
|
|
let signers = vec![&payer];
|
|
send_instructions(&client, instructions, signers, &payer.pubkey())?;
|
|
}
|
|
Command::SwitchOracle {
|
|
payer,
|
|
ids_path,
|
|
mango_group_name,
|
|
token_symbol,
|
|
oracle,
|
|
} => {
|
|
println!("SwitchOracle");
|
|
let payer = read_keypair_file(payer.as_str())?;
|
|
let ids: Value = serde_json::from_reader(File::open(&ids_path)?)?;
|
|
let cluster_name = opts.cluster.name();
|
|
let cluster_ids = &ids[cluster_name];
|
|
let cids = ClusterIds::load(cluster_ids);
|
|
let mgids = cids.mango_groups[&mango_group_name].clone();
|
|
// replace old oracle with new one when assembling instruction
|
|
let old_oracle_pk = &cids.oracles[&token_symbol];
|
|
let new_oracle_pks = mgids
|
|
.oracles
|
|
.iter()
|
|
.map(|o| {
|
|
if o == old_oracle_pk {
|
|
Pubkey::from_str(oracle.as_str())?
|
|
} else {
|
|
o
|
|
}
|
|
})
|
|
.collect();
|
|
let instruction = switch_oracles(
|
|
&cids.mango_program_id,
|
|
&mgids.mango_group_pk,
|
|
&payer.pubkey(),
|
|
new_oracle_pks,
|
|
);
|
|
let instructions = vec![instruction];
|
|
let signers = vec![&payer];
|
|
send_instructions(&client, instructions, signers, &payer.pubkey())?;
|
|
// update ids
|
|
let oracle_pks = ids[cluster_name][mango_group_name].get_mut("oracle_pks")?;
|
|
oracle_pks = new_oracle_pks.iter().map(|pk| pk.to_string);
|
|
let f = File::create(ids_path.as_str()).unwrap();
|
|
serde_json::to_writer_pretty(&f, &ids).unwrap();
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn get_pk(json: &Value, name: &str) -> Pubkey {
|
|
Pubkey::from_str(json[name].as_str().unwrap()).unwrap()
|
|
}
|
|
|
|
fn get_symbol_pk(symbols: &Value, symbol: &str) -> Pubkey {
|
|
Pubkey::from_str(symbols[symbol].as_str().expect("get_symbol_pk symbol not found")).unwrap()
|
|
}
|
|
|
|
fn get_vec_pks(value: &Value) -> Vec<Pubkey> {
|
|
value.as_array().unwrap().iter().map(|s| Pubkey::from_str(s.as_str().unwrap()).unwrap()).collect()
|
|
}
|
|
|
|
fn get_map_pks(value: &Value) -> HashMap<String, Pubkey> {
|
|
value.as_object().unwrap().iter().map(
|
|
|(k, v)| (k.clone(), Pubkey::from_str(v.as_str().unwrap()).unwrap())
|
|
).collect()
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
fn map_of_pks_to_strs(map: HashMap<String, Pubkey>) -> HashMap<String, String> {
|
|
map.iter().map(|(k, v)| (k.clone(), v.to_string())).collect()
|
|
}
|
|
|
|
fn main() {
|
|
let opts = Opts::parse();
|
|
start(opts).unwrap();
|
|
}
|