main.rs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. use {
  2. clap::{builder::BoolishValueParser, crate_description, crate_name, crate_version, Arg, Command}, solana_clap_v3_utils::{
  3. input_parsers::{
  4. parse_url_or_moniker,
  5. signer::{SignerSource, SignerSourceParserBuilder},
  6. },
  7. input_validators::normalize_to_url_if_moniker,
  8. keypair::signer_from_path,
  9. }, solana_client::nonblocking::rpc_client::RpcClient, solana_remote_wallet::remote_wallet::RemoteWalletManager, solana_sdk::{
  10. commitment_config::CommitmentConfig,
  11. message::Message,
  12. pubkey::Pubkey,
  13. signature::{Signature, Signer},
  14. transaction::Transaction,
  15. }, spl_tlv_account_resolution::{account::ExtraAccountMeta, seeds::Seed, state::ExtraAccountMetaList}, spl_transfer_hook_interface::instruction::ExecuteInstruction, std::{error::Error, process::exit, rc::Rc, sync::Arc}
  16. };
  17. struct Config {
  18. commitment_config: CommitmentConfig,
  19. payer: Arc<dyn Signer>,
  20. json_rpc_url: String,
  21. verbose: bool,
  22. }
  23. pub fn get_extra_account_metas_with_source_wallet_block() -> Vec<ExtraAccountMeta> {
  24. vec![
  25. // [5] wallet_block for source token account wallet
  26. ExtraAccountMeta::new_with_seeds(
  27. &[
  28. Seed::Literal {
  29. bytes: b"wallet_block".to_vec(),
  30. },
  31. Seed::AccountData {
  32. account_index: 0,
  33. data_index: 32,
  34. length: 32,
  35. },
  36. ],
  37. false,
  38. false,
  39. ).unwrap(),
  40. ]
  41. }
  42. pub fn get_extra_account_metas_with_both_wallet_blocks() -> Vec<ExtraAccountMeta> {
  43. vec![
  44. // [5] wallet_block for source token account wallet
  45. ExtraAccountMeta::new_with_seeds(
  46. &[
  47. Seed::Literal {
  48. bytes: b"wallet_block".to_vec(),
  49. },
  50. Seed::AccountData {
  51. account_index: 0,
  52. data_index: 32,
  53. length: 32,
  54. },
  55. ],
  56. false,
  57. false,
  58. ).unwrap(),
  59. // [6] wallet_block for destination token account wallet
  60. ExtraAccountMeta::new_with_seeds(
  61. &[
  62. Seed::Literal {
  63. bytes: b"wallet_block".to_vec(),
  64. },
  65. Seed::AccountData {
  66. account_index: 2,
  67. data_index: 32,
  68. length: 32,
  69. },
  70. ],
  71. false,
  72. false,
  73. ).unwrap(),
  74. ]
  75. }
  76. fn create_empty_extra_metas() -> Vec<u8> {
  77. let size = ExtraAccountMetaList::size_of(0).unwrap();
  78. let metas: Vec<ExtraAccountMeta> = vec![];
  79. let mut data = vec![0; size];
  80. ExtraAccountMetaList::init::<ExecuteInstruction>(&mut data, &metas).unwrap();
  81. data
  82. }
  83. fn create_extra_metas_with_source_wallet_block() -> Vec<u8> {
  84. let metas: Vec<ExtraAccountMeta> = get_extra_account_metas_with_source_wallet_block();
  85. let size = ExtraAccountMetaList::size_of(metas.len()).unwrap();
  86. let mut data = vec![0; size];
  87. ExtraAccountMetaList::init::<ExecuteInstruction>(&mut data, &metas).unwrap();
  88. data
  89. }
  90. fn create_extra_metas_with_both_wallet_blocks() -> Vec<u8> {
  91. let metas: Vec<ExtraAccountMeta> = get_extra_account_metas_with_both_wallet_blocks();
  92. let size = ExtraAccountMetaList::size_of(metas.len()).unwrap();
  93. let mut data = vec![0; size];
  94. ExtraAccountMetaList::init::<ExecuteInstruction>(&mut data, &metas).unwrap();
  95. data
  96. }
  97. fn get_extra_metas_account_data() {
  98. let data_empty = create_empty_extra_metas();
  99. let data_source_wallet_block = create_extra_metas_with_source_wallet_block();
  100. let data_both_wallet_blocks = create_extra_metas_with_both_wallet_blocks();
  101. println!("data empty: {:?}", data_empty);
  102. println!("data source wallet block: {:?}", data_source_wallet_block);
  103. println!("data both wallet blocks: {:?}", data_both_wallet_blocks);
  104. }
  105. async fn get_config(rpc_client: &Arc<RpcClient>) {
  106. let config = block_list_client::accounts::Config::find_pda().0;
  107. let data = rpc_client.get_account_data(&config).await.unwrap();
  108. println!("config: {:?}", data);
  109. let config = block_list_client::accounts::Config::from_bytes(&data).unwrap();
  110. println!("config: {:?}", config);
  111. }
  112. async fn get_extra_metas(rpc_client: &Arc<RpcClient>, mint_address: &Pubkey) {
  113. let extra_metas = block_list_client::accounts::ExtraMetas::find_pda(mint_address).0;
  114. let data = rpc_client.get_account_data(&extra_metas).await.unwrap();
  115. println!("extra_metas: {:?}", data);
  116. }
  117. async fn process_setup_extra_metas(
  118. rpc_client: &Arc<RpcClient>,
  119. payer: &Arc<dyn Signer>,
  120. mint_address: &Pubkey,
  121. check_both_wallets: bool,
  122. ) -> Result<Signature, Box<dyn Error>> {
  123. let ix = block_list_client::instructions::SetupExtraMetasBuilder::new()
  124. .authority(payer.pubkey())
  125. .config(block_list_client::accounts::Config::find_pda().0)
  126. .mint(*mint_address)
  127. .extra_metas(block_list_client::accounts::ExtraMetas::find_pda(mint_address).0)
  128. .check_both_wallets(check_both_wallets)
  129. .instruction();
  130. let mut transaction = Transaction::new_unsigned(Message::new(&[ix], Some(&payer.pubkey())));
  131. let blockhash = rpc_client
  132. .get_latest_blockhash()
  133. .await
  134. .map_err(|err| format!("error: unable to get latest blockhash: {}", err))?;
  135. transaction
  136. .try_sign(&[payer], blockhash)
  137. .map_err(|err| format!("error: failed to sign transaction: {}", err))?;
  138. let signature = rpc_client
  139. .send_and_confirm_transaction_with_spinner(&transaction)
  140. .await
  141. .map_err(|err| format!("error: send transaction: {}", err))?;
  142. Ok(signature)
  143. }
  144. async fn process_init(
  145. rpc_client: &Arc<RpcClient>,
  146. payer: &Arc<dyn Signer>,
  147. ) -> Result<Signature, Box<dyn Error>> {
  148. let ix = block_list_client::instructions::InitBuilder::new()
  149. .authority(payer.pubkey())
  150. .config(block_list_client::accounts::Config::find_pda().0)
  151. .instruction();
  152. let mut transaction = Transaction::new_unsigned(Message::new(&[ix], Some(&payer.pubkey())));
  153. let blockhash = rpc_client
  154. .get_latest_blockhash()
  155. .await
  156. .map_err(|err| format!("error: unable to get latest blockhash: {}", err))?;
  157. transaction
  158. .try_sign(&[payer], blockhash)
  159. .map_err(|err| format!("error: failed to sign transaction: {}", err))?;
  160. let signature = rpc_client
  161. .send_and_confirm_transaction_with_spinner(&transaction)
  162. .await
  163. .map_err(|err| format!("error: send transaction: {}", err))?;
  164. Ok(signature)
  165. }
  166. async fn process_block_wallet(
  167. rpc_client: &Arc<RpcClient>,
  168. payer: &Arc<dyn Signer>,
  169. wallet_address: &Pubkey,
  170. ) -> Result<Signature, Box<dyn Error>> {
  171. let ix = block_list_client::instructions::BlockWalletBuilder::new()
  172. .authority(payer.pubkey())
  173. .config(block_list_client::accounts::Config::find_pda().0)
  174. .wallet(*wallet_address)
  175. .wallet_block(block_list_client::accounts::WalletBlock::find_pda(wallet_address).0)
  176. .instruction();
  177. let mut transaction = Transaction::new_unsigned(Message::new(&[ix], Some(&payer.pubkey())));
  178. let blockhash = rpc_client
  179. .get_latest_blockhash()
  180. .await
  181. .map_err(|err| format!("error: unable to get latest blockhash: {}", err))?;
  182. transaction
  183. .try_sign(&[payer], blockhash)
  184. .map_err(|err| format!("error: failed to sign transaction: {}", err))?;
  185. let signature = rpc_client
  186. .send_and_confirm_transaction_with_spinner(&transaction)
  187. .await
  188. .map_err(|err| format!("error: send transaction: {}", err))?;
  189. Ok(signature)
  190. }
  191. async fn process_unblock_wallet(
  192. rpc_client: &Arc<RpcClient>,
  193. payer: &Arc<dyn Signer>,
  194. wallet_address: &Pubkey,
  195. ) -> Result<Signature, Box<dyn Error>> {
  196. let ix = block_list_client::instructions::UnblockWalletBuilder::new()
  197. .authority(payer.pubkey())
  198. .config(block_list_client::accounts::Config::find_pda().0)
  199. .wallet_block(block_list_client::accounts::WalletBlock::find_pda(wallet_address).0)
  200. .instruction();
  201. let mut transaction = Transaction::new_unsigned(Message::new(&[ix], Some(&payer.pubkey())));
  202. let blockhash = rpc_client
  203. .get_latest_blockhash()
  204. .await
  205. .map_err(|err| format!("error: unable to get latest blockhash: {}", err))?;
  206. transaction
  207. .try_sign(&[payer], blockhash)
  208. .map_err(|err| format!("error: failed to sign transaction: {}", err))?;
  209. let signature = rpc_client
  210. .send_and_confirm_transaction_with_spinner(&transaction)
  211. .await
  212. .map_err(|err| format!("error: send transaction: {}", err))?;
  213. Ok(signature)
  214. }
  215. #[tokio::main]
  216. async fn main() -> Result<(), Box<dyn Error>> {
  217. let app_matches = Command::new(crate_name!())
  218. .about(crate_description!())
  219. .version(crate_version!())
  220. .subcommand_required(true)
  221. .arg_required_else_help(true)
  222. .arg({
  223. let arg = Arg::new("config_file")
  224. .short('C')
  225. .long("config")
  226. .value_name("PATH")
  227. .takes_value(true)
  228. .global(true)
  229. .help("Configuration file to use");
  230. if let Some(ref config_file) = *solana_cli_config::CONFIG_FILE {
  231. arg.default_value(config_file)
  232. } else {
  233. arg
  234. }
  235. })
  236. .arg(
  237. Arg::new("payer")
  238. .long("payer")
  239. .value_name("KEYPAIR")
  240. .value_parser(SignerSourceParserBuilder::default().allow_all().build())
  241. .takes_value(true)
  242. .global(true)
  243. .help("Filepath or URL to a keypair [default: client keypair]"),
  244. )
  245. .arg(
  246. Arg::new("verbose")
  247. .long("verbose")
  248. .short('v')
  249. .takes_value(false)
  250. .global(true)
  251. .help("Show additional information"),
  252. )
  253. .arg(
  254. Arg::new("json_rpc_url")
  255. .short('u')
  256. .long("url")
  257. .value_name("URL")
  258. .takes_value(true)
  259. .global(true)
  260. .value_parser(parse_url_or_moniker)
  261. .help("JSON RPC URL for the cluster [default: value from configuration file]"),
  262. )
  263. .subcommand(
  264. Command::new("init").about("Initializes the blocklist")
  265. )
  266. .subcommand(
  267. Command::new("block-wallet").about("Blocks a wallet")
  268. .arg(
  269. Arg::new("wallet_address")
  270. .value_name("WALLET_ADDRESS")
  271. .value_parser(SignerSourceParserBuilder::default().allow_pubkey().build())
  272. .takes_value(true)
  273. .index(1)
  274. .help("Specify the wallet address to block"),
  275. )
  276. )
  277. .subcommand(
  278. Command::new("unblock-wallet").about("Unblocks a wallet")
  279. .arg(
  280. Arg::new("wallet_address")
  281. .value_name("WALLET_ADDRESS")
  282. .value_parser(SignerSourceParserBuilder::default().allow_pubkey().build())
  283. .takes_value(true)
  284. .index(1)
  285. .help("Specify the wallet address to unblock"),
  286. )
  287. )
  288. .subcommand(
  289. Command::new("get-extra-metas-account-data").about("Gets the extra metas account data")
  290. )
  291. .subcommand(
  292. Command::new("get-config").about("Gets the config account data")
  293. )
  294. .subcommand(
  295. Command::new("get-extra-metas").about("Gets the extra metas account data")
  296. .arg(
  297. Arg::new("mint_address")
  298. .value_name("MINT_ADDRESS")
  299. .value_parser(SignerSourceParserBuilder::default().allow_pubkey().build())
  300. .takes_value(true)
  301. .index(1)
  302. .help("Specify the mint address"),
  303. )
  304. )
  305. .subcommand(
  306. Command::new("setup-extra-metas").about("Setup the extra metas account")
  307. .arg(
  308. Arg::new("mint_address")
  309. .value_name("MINT_ADDRESS")
  310. .value_parser(SignerSourceParserBuilder::default().allow_pubkey().build())
  311. .takes_value(true)
  312. .index(1)
  313. .help("Specify the mint address"),
  314. )
  315. .arg(
  316. Arg::new("check-both-wallets")
  317. .long("check-both-wallets")
  318. .short('b')
  319. .help("Specify if both wallets should be checked"),
  320. )
  321. )
  322. .get_matches();
  323. let (command, matches) = app_matches.subcommand().unwrap();
  324. let mut wallet_manager: Option<Rc<RemoteWalletManager>> = None;
  325. let config = {
  326. let cli_config = if let Some(config_file) = matches.try_get_one::<String>("config_file")? {
  327. solana_cli_config::Config::load(config_file).unwrap_or_default()
  328. } else {
  329. solana_cli_config::Config::default()
  330. };
  331. let payer = if let Ok(Some((signer, _))) =
  332. SignerSource::try_get_signer(matches, "payer", &mut wallet_manager)
  333. {
  334. Box::new(signer)
  335. } else {
  336. signer_from_path(
  337. matches,
  338. &cli_config.keypair_path,
  339. "payer",
  340. &mut wallet_manager,
  341. )?
  342. };
  343. let json_rpc_url = normalize_to_url_if_moniker(
  344. matches
  345. .get_one::<String>("json_rpc_url")
  346. .unwrap_or(&cli_config.json_rpc_url),
  347. );
  348. Config {
  349. commitment_config: CommitmentConfig::confirmed(),
  350. payer: Arc::from(payer),
  351. json_rpc_url,
  352. verbose: matches.try_contains_id("verbose")?,
  353. }
  354. };
  355. solana_logger::setup_with_default("solana=info");
  356. if config.verbose {
  357. println!("JSON RPC URL: {}", config.json_rpc_url);
  358. }
  359. let rpc_client = Arc::new(RpcClient::new_with_commitment(
  360. config.json_rpc_url.clone(),
  361. config.commitment_config,
  362. ));
  363. match (command, matches) {
  364. ("init", _arg_matches) => {
  365. let response = process_init(
  366. &rpc_client,
  367. &config.payer,
  368. )
  369. .await
  370. .unwrap_or_else(|err| {
  371. eprintln!("error: init: {}", err);
  372. exit(1);
  373. });
  374. println!("{}", response);
  375. }
  376. ("block-wallet", arg_matches) => {
  377. let wallet_address =
  378. SignerSource::try_get_pubkey(arg_matches, "wallet_address", &mut wallet_manager)
  379. .unwrap()
  380. .unwrap();
  381. let response = process_block_wallet(
  382. &rpc_client,
  383. &config.payer,
  384. &wallet_address,
  385. )
  386. .await
  387. .unwrap_or_else(|err| {
  388. eprintln!("error: init: {}", err);
  389. exit(1);
  390. });
  391. println!("{}", response);
  392. }
  393. ("unblock-wallet", arg_matches) => {
  394. let wallet_address =
  395. SignerSource::try_get_pubkey(arg_matches, "wallet_address", &mut wallet_manager)
  396. .unwrap()
  397. .unwrap();
  398. let response = process_unblock_wallet(
  399. &rpc_client,
  400. &config.payer,
  401. &wallet_address,
  402. )
  403. .await
  404. .unwrap_or_else(|err| {
  405. eprintln!("error: init: {}", err);
  406. exit(1);
  407. });
  408. println!("{}", response);
  409. }
  410. ("get-extra-metas-account-data", _arg_matches) => {
  411. get_extra_metas_account_data();
  412. }
  413. ("get-config", _arg_matches) => {
  414. get_config(&rpc_client).await;
  415. }
  416. ("get-extra-metas", arg_matches) => {
  417. let mint_address =
  418. SignerSource::try_get_pubkey(arg_matches, "mint_address", &mut wallet_manager)
  419. .unwrap()
  420. .unwrap();
  421. get_extra_metas(&rpc_client, &mint_address).await;
  422. }
  423. ("setup-extra-metas", arg_matches) => {
  424. let mint_address =
  425. SignerSource::try_get_pubkey(arg_matches, "mint_address", &mut wallet_manager)
  426. .unwrap()
  427. .unwrap();
  428. let check_both_wallets = arg_matches.contains_id("check-both-wallets");
  429. let response = process_setup_extra_metas(
  430. &rpc_client,
  431. &config.payer,
  432. &mint_address,
  433. check_both_wallets,
  434. )
  435. .await
  436. .unwrap_or_else(|err| {
  437. eprintln!("error: setup_extra_metas: {}", err);
  438. exit(1);
  439. });
  440. println!("{}", response);
  441. }
  442. _ => unreachable!(),
  443. };
  444. Ok(())
  445. }