2022-05-06 05:19:49 -07:00
mod crank ;
2022-05-27 22:05:34 -07:00
mod taker ;
2022-05-06 01:13:33 -07:00
2022-07-14 03:57:19 -07:00
use std ::str ::FromStr ;
2022-05-06 07:45:04 -07:00
use std ::sync ::Arc ;
2022-04-09 12:18:07 -07:00
2022-05-27 22:05:34 -07:00
use anchor_client ::Cluster ;
2022-05-06 01:13:33 -07:00
2022-04-13 07:41:15 -07:00
use clap ::{ Parser , Subcommand } ;
2022-06-18 07:31:28 -07:00
use client ::MangoClient ;
2022-07-14 03:57:19 -07:00
use solana_sdk ::pubkey ::Pubkey ;
use solana_sdk ::{
commitment_config ::CommitmentConfig ,
signature ::Signer ,
signer ::{ keypair , keypair ::Keypair } ,
} ;
2022-05-06 07:45:04 -07:00
use tokio ::time ;
2022-04-09 12:18:07 -07:00
2022-04-14 03:42:13 -07:00
// TODO
// - may be nice to have one-shot cranking as well as the interval cranking
// - doing a gPA for all banks call every 10millis may be too often,
// might make sense that we maintain a service when users should query group for changes
// - I'm really annoyed about Keypair not being clonable. Seems everyone works around that manually. Should make a PR to solana to newtype it and provide that function.
// keypair_from_arg_or_env could be a function
2022-07-19 04:43:14 -07:00
#[ derive(Parser, Debug) ]
#[ clap() ]
struct CliDotenv {
// When --dotenv <file> is passed, read the specified dotenv file before parsing args
#[ clap(long) ]
dotenv : std ::path ::PathBuf ,
remaining_args : Vec < std ::ffi ::OsString > ,
}
2022-04-13 07:41:15 -07:00
#[ derive(Parser) ]
#[ clap() ]
struct Cli {
2022-07-19 00:59:30 -07:00
#[ clap(short, long, env) ]
rpc_url : String ,
2022-04-13 07:41:15 -07:00
2022-05-27 22:05:34 -07:00
#[ clap(short, long, env = " PAYER_KEYPAIR " ) ]
2022-07-19 00:59:30 -07:00
payer : std ::path ::PathBuf ,
2022-04-13 07:41:15 -07:00
2022-07-19 00:59:30 -07:00
#[ clap(short, long, env) ]
2022-07-14 03:57:19 -07:00
group : Option < Pubkey > ,
// These exist only as a shorthand to make testing easier. Normal users would provide the group.
2022-07-19 00:59:30 -07:00
#[ clap(long, env) ]
2022-07-14 03:57:19 -07:00
group_from_admin_keypair : Option < std ::path ::PathBuf > ,
2022-07-19 00:59:30 -07:00
#[ clap(long, env, default_value = " 0 " ) ]
2022-07-14 03:57:19 -07:00
group_from_admin_num : u32 ,
2022-04-13 07:41:15 -07:00
2022-07-19 00:59:30 -07:00
#[ clap(short, long, env) ]
2022-05-29 03:25:12 -07:00
mango_account_name : String ,
2022-04-13 07:41:15 -07:00
#[ clap(subcommand) ]
command : Command ,
}
2022-07-14 03:57:19 -07:00
fn keypair_from_path ( p : & std ::path ::PathBuf ) -> Keypair {
let path = std ::path ::PathBuf ::from_str ( & * shellexpand ::tilde ( p . to_str ( ) . unwrap ( ) ) ) . unwrap ( ) ;
keypair ::read_keypair_file ( path )
. unwrap_or_else ( | _ | panic! ( " Failed to read keypair from {} " , p . to_string_lossy ( ) ) )
}
2022-04-13 07:41:15 -07:00
#[ derive(Subcommand) ]
enum Command {
Crank { } ,
2022-05-27 22:05:34 -07:00
Taker { } ,
2022-04-13 07:41:15 -07:00
}
2022-05-06 01:13:33 -07:00
fn main ( ) -> Result < ( ) , anyhow ::Error > {
2022-04-13 07:41:15 -07:00
env_logger ::init_from_env (
env_logger ::Env ::default ( ) . filter_or ( env_logger ::DEFAULT_FILTER_ENV , " info " ) ,
) ;
2022-07-19 04:43:14 -07:00
let args = if let Ok ( cli_dotenv ) = CliDotenv ::try_parse ( ) {
dotenv ::from_path ( cli_dotenv . dotenv ) ? ;
cli_dotenv . remaining_args
} else {
dotenv ::dotenv ( ) . ok ( ) ;
std ::env ::args_os ( ) . collect ( )
} ;
let cli = Cli ::parse_from ( args ) ;
2022-04-13 07:41:15 -07:00
2022-07-19 00:59:30 -07:00
let payer = keypair_from_path ( & cli . payer ) ;
2022-04-13 07:41:15 -07:00
2022-07-19 00:59:30 -07:00
let rpc_url = cli . rpc_url ;
2022-04-13 07:41:15 -07:00
let ws_url = rpc_url . replace ( " https " , " wss " ) ;
let cluster = Cluster ::Custom ( rpc_url , ws_url ) ;
2022-07-14 03:57:19 -07:00
let commitment = match cli . command {
2022-05-06 01:13:33 -07:00
Command ::Crank { .. } = > CommitmentConfig ::confirmed ( ) ,
2022-05-27 22:05:34 -07:00
Command ::Taker { .. } = > CommitmentConfig ::confirmed ( ) ,
2022-04-13 07:41:15 -07:00
} ;
2022-07-14 03:57:19 -07:00
let group = if let Some ( group ) = cli . group {
group
} else if let Some ( p ) = cli . group_from_admin_keypair {
let admin = keypair_from_path ( & p ) ;
MangoClient ::group_for_admin ( admin . pubkey ( ) , cli . group_from_admin_num )
} else {
panic! ( " Must provide either group or group_from_admin_keypair " ) ;
} ;
2022-05-29 03:25:12 -07:00
let mango_client = Arc ::new ( MangoClient ::new (
cluster ,
commitment ,
2022-07-14 03:57:19 -07:00
group ,
2022-07-16 05:37:15 -07:00
payer ,
2022-07-14 03:57:19 -07:00
& cli . mango_account_name ,
2022-05-29 03:25:12 -07:00
) ? ) ;
2022-05-11 04:33:01 -07:00
2022-04-09 12:18:07 -07:00
let rt = tokio ::runtime ::Builder ::new_multi_thread ( )
. enable_all ( )
. build ( )
. unwrap ( ) ;
2022-05-06 07:45:04 -07:00
let debugging_handle = async {
let mut interval = time ::interval ( time ::Duration ::from_secs ( 5 ) ) ;
loop {
interval . tick ( ) . await ;
let client = mango_client . clone ( ) ;
tokio ::task ::spawn_blocking ( move | | {
log ::info! (
2022-05-27 22:05:34 -07:00
" Arc<MangoClient>::strong_count() {} " ,
2022-05-06 07:45:04 -07:00
Arc ::< MangoClient > ::strong_count ( & client )
)
} ) ;
}
} ;
2022-07-14 03:57:19 -07:00
match cli . command {
2022-05-06 01:13:33 -07:00
Command ::Crank { .. } = > {
2022-05-06 07:45:04 -07:00
let client = mango_client . clone ( ) ;
2022-05-27 22:05:34 -07:00
rt . block_on ( crank ::runner ( client , debugging_handle ) )
2022-04-13 10:47:31 -07:00
}
2022-05-27 22:05:34 -07:00
Command ::Taker { .. } = > {
let client = mango_client . clone ( ) ;
rt . block_on ( taker ::runner ( client , debugging_handle ) )
2022-05-06 05:19:49 -07:00
}
2022-04-13 07:41:15 -07:00
}
2022-04-09 12:18:07 -07:00
}