2022-04-13 07:41:15 -07:00
use std ::{ env , time ::Duration } ;
2022-04-09 12:18:07 -07:00
2022-04-13 07:41:15 -07:00
use anchor_client ::{ Client , Cluster , Program } ;
2022-04-13 10:51:57 -07:00
use anyhow ::ensure ;
2022-04-13 07:41:15 -07:00
use clap ::{ Parser , Subcommand } ;
2022-04-13 10:47:31 -07:00
use log ::{ error , info } ;
2022-04-13 07:41:15 -07:00
use mango_v4 ::state ::Bank ;
use solana_client ::rpc_client ::RpcClient ;
use solana_client ::rpc_filter ::{ Memcmp , MemcmpEncodedBytes , RpcFilterType } ;
use solana_sdk ::signature ::Keypair ;
use solana_sdk ::{
commitment_config ::CommitmentConfig ,
instruction ::Instruction ,
pubkey ::Pubkey ,
signer ::{ keypair , Signer } ,
} ;
2022-04-09 12:18:07 -07:00
use tokio ::time ;
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-04-13 07:41:15 -07:00
/// Wrapper around anchor client with some mango specific useful things
pub struct MangoClient {
pub program : Program ,
pub rpc : RpcClient ,
pub cluster : Cluster ,
pub commitment : CommitmentConfig ,
pub payer : Keypair ,
pub admin : Keypair ,
}
impl MangoClient {
pub fn new (
cluster : Cluster ,
commitment : CommitmentConfig ,
payer : Keypair ,
admin : Keypair ,
) -> Self {
let program = Client ::new_with_options (
cluster . clone ( ) ,
std ::rc ::Rc ::new ( Keypair ::from_bytes ( & payer . to_bytes ( ) ) . unwrap ( ) ) ,
commitment ,
)
. program ( mango_v4 ::ID ) ;
let rpc = program . rpc ( ) ;
Self {
program ,
rpc ,
cluster ,
commitment ,
admin ,
payer ,
}
}
pub fn payer ( & self ) -> Pubkey {
self . payer . pubkey ( )
}
pub fn admin ( & self ) -> Pubkey {
self . payer . pubkey ( )
}
}
#[ derive(Parser) ]
#[ clap() ]
struct Cli {
#[ clap(short, long, env = " RPC_URL " ) ]
rpc_url : Option < String > ,
#[ clap(short, long, env = " PAYER_KEYPAIR " ) ]
payer : Option < std ::path ::PathBuf > ,
#[ clap(short, long, env = " ADMIN_KEYPAIR " ) ]
admin : Option < std ::path ::PathBuf > ,
#[ clap(subcommand) ]
command : Command ,
}
// future: more subcommands e.g. Liquidator
#[ derive(Subcommand) ]
enum Command {
Crank { } ,
}
2022-04-09 12:18:07 -07:00
fn main ( ) {
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 " ) ,
) ;
dotenv ::dotenv ( ) . ok ( ) ;
let Cli {
rpc_url ,
payer ,
admin ,
command ,
} = Cli ::parse ( ) ;
let payer = match payer {
Some ( p ) = > keypair ::read_keypair_file ( & p )
. unwrap_or_else ( | _ | panic! ( " Failed to read keypair from {} " , p . to_string_lossy ( ) ) ) ,
None = > match env ::var ( " PAYER_KEYPAIR " ) . ok ( ) {
Some ( k ) = > {
keypair ::read_keypair ( & mut k . as_bytes ( ) ) . expect ( " Failed to parse $PAYER_KEYPAIR " )
}
None = > panic! ( " Payer keypair not provided... " ) ,
} ,
} ;
let admin = match admin {
Some ( p ) = > keypair ::read_keypair_file ( & p )
. unwrap_or_else ( | _ | panic! ( " Failed to read keypair from {} " , p . to_string_lossy ( ) ) ) ,
None = > match env ::var ( " ADMIN_KEYPAIR " ) . ok ( ) {
Some ( k ) = > {
keypair ::read_keypair ( & mut k . as_bytes ( ) ) . expect ( " Failed to parse $ADMIN_KEYPAIR " )
}
None = > panic! ( " Admin keypair not provided... " ) ,
} ,
} ;
let rpc_url = match rpc_url {
Some ( rpc_url ) = > rpc_url ,
None = > match env ::var ( " RPC_URL " ) . ok ( ) {
Some ( rpc_url ) = > rpc_url ,
None = > panic! ( " Rpc URL not provided... " ) ,
} ,
} ;
let ws_url = rpc_url . replace ( " https " , " wss " ) ;
let cluster = Cluster ::Custom ( rpc_url , ws_url ) ;
let commitment = match command {
Command ::Crank { .. } = > CommitmentConfig ::processed ( ) ,
} ;
let mango_client = MangoClient ::new ( cluster , commitment , payer , admin ) ;
2022-04-09 12:18:07 -07:00
let rt = tokio ::runtime ::Builder ::new_multi_thread ( )
. enable_all ( )
. build ( )
. unwrap ( ) ;
2022-04-13 07:41:15 -07:00
// future: match on various subcommands
rt . block_on ( update_index_runner ( & mango_client ) )
2022-04-09 12:18:07 -07:00
. expect ( " Something went wrong here... " ) ;
}
2022-04-13 07:41:15 -07:00
pub async fn update_index_runner ( mango_client : & MangoClient ) -> anyhow ::Result < ( ) > {
// future: make configurable
2022-04-09 12:18:07 -07:00
let mut interval = time ::interval ( Duration ::from_millis ( 10 ) ) ;
loop {
interval . tick ( ) . await ;
2022-04-14 02:18:11 -07:00
update_index ( mango_client ) . await ? ;
2022-04-09 12:18:07 -07:00
}
}
2022-04-13 07:41:15 -07:00
pub async fn update_index ( mango_client : & MangoClient ) -> anyhow ::Result < ( ) > {
// Collect all banks for a group belonging to an admin
let banks = mango_client
. program
. accounts ::< Bank > ( vec! [ RpcFilterType ::Memcmp ( Memcmp {
offset : 24 ,
bytes : MemcmpEncodedBytes ::Base58 ( {
// find group belonging to admin
Pubkey ::find_program_address (
2022-04-14 02:17:42 -07:00
& [ " Group " . as_ref ( ) , mango_client . admin . pubkey ( ) . as_ref ( ) ] ,
2022-04-13 07:41:15 -07:00
& mango_client . program . id ( ) ,
)
. 0
. to_string ( )
} ) ,
encoding : None ,
} ) ] ) ? ;
2022-04-09 12:18:07 -07:00
2022-04-13 10:51:57 -07:00
ensure! ( ! banks . is_empty ( ) ) ;
2022-04-09 12:18:07 -07:00
2022-04-13 07:41:15 -07:00
// Call update index ix
for bank in banks {
2022-04-13 10:47:31 -07:00
let sig_result = mango_client
2022-04-13 07:41:15 -07:00
. program
. request ( )
. instruction ( Instruction {
program_id : mango_v4 ::id ( ) ,
accounts : anchor_lang ::ToAccountMetas ::to_account_metas (
& mango_v4 ::accounts ::UpdateIndex { bank : bank . 0 } ,
None ,
) ,
data : anchor_lang ::InstructionData ::data ( & mango_v4 ::instruction ::UpdateIndex { } ) ,
} )
2022-04-13 10:47:31 -07:00
. send ( ) ;
match sig_result {
Ok ( sig ) = > {
info! ( " Crank: update_index ix signature: {:?} " , sig ) ;
}
Err ( e ) = > error! ( " Crank: {:?} " , e ) ,
}
2022-04-13 07:41:15 -07:00
}
2022-04-09 12:18:07 -07:00
2022-04-13 07:41:15 -07:00
Ok ( ( ) )
2022-04-09 12:18:07 -07:00
}