lib.rs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  1. #![cfg_attr(docsrs, feature(doc_auto_cfg))]
  2. //! An RPC client to interact with Solana programs written in [`anchor_lang`].
  3. //!
  4. //! # Examples
  5. //!
  6. //! A simple example that creates a client, sends a transaction and fetches an account:
  7. //!
  8. //! ```ignore
  9. //! use std::rc::Rc;
  10. //!
  11. //! use anchor_client::{
  12. //! solana_sdk::{
  13. //! signature::{read_keypair_file, Keypair},
  14. //! signer::Signer,
  15. //! system_program,
  16. //! },
  17. //! Client, Cluster,
  18. //! };
  19. //! use my_program::{accounts, instruction, MyAccount};
  20. //!
  21. //! fn main() -> Result<(), Box<dyn std::error::Error>> {
  22. //! // Create client
  23. //! let payer = read_keypair_file("keypair.json")?;
  24. //! let client = Client::new(Cluster::Localnet, Rc::new(payer));
  25. //!
  26. //! // Create program
  27. //! let program = client.program(my_program::ID)?;
  28. //!
  29. //! // Send a transaction
  30. //! let my_account_kp = Keypair::new();
  31. //! program
  32. //! .request()
  33. //! .accounts(accounts::Initialize {
  34. //! my_account: my_account_kp.pubkey(),
  35. //! payer: program.payer(),
  36. //! system_program: system_program::ID,
  37. //! })
  38. //! .args(instruction::Initialize { field: 42 })
  39. //! .signer(&my_account_kp)
  40. //! .send()?;
  41. //!
  42. //! // Fetch an account
  43. //! let my_account: MyAccount = program.account(my_account_kp.pubkey())?;
  44. //! assert_eq!(my_account.field, 42);
  45. //!
  46. //! Ok(())
  47. //! }
  48. //! ```
  49. //!
  50. //! More examples can be found in [here].
  51. //!
  52. //! [here]: https://github.com/coral-xyz/anchor/tree/v0.29.0/client/example/src
  53. //!
  54. //! # Features
  55. //!
  56. //! The client is blocking by default. To enable asynchronous client, add `async` feature:
  57. //!
  58. //! ```toml
  59. //! anchor-client = { version = "0.29.0 ", features = ["async"] }
  60. //! ````
  61. use anchor_lang::solana_program::hash::Hash;
  62. use anchor_lang::solana_program::instruction::{AccountMeta, Instruction};
  63. use anchor_lang::solana_program::program_error::ProgramError;
  64. use anchor_lang::solana_program::pubkey::Pubkey;
  65. use anchor_lang::{AccountDeserialize, Discriminator, InstructionData, ToAccountMetas};
  66. use futures::{Future, StreamExt};
  67. use regex::Regex;
  68. use solana_account_decoder::UiAccountEncoding;
  69. use solana_client::rpc_config::{
  70. RpcAccountInfoConfig, RpcProgramAccountsConfig, RpcSendTransactionConfig,
  71. RpcTransactionLogsConfig, RpcTransactionLogsFilter,
  72. };
  73. use solana_client::rpc_filter::{Memcmp, RpcFilterType};
  74. use solana_client::{
  75. client_error::ClientError as SolanaClientError,
  76. nonblocking::{
  77. pubsub_client::{PubsubClient, PubsubClientError},
  78. rpc_client::RpcClient as AsyncRpcClient,
  79. },
  80. rpc_client::RpcClient,
  81. rpc_response::{Response as RpcResponse, RpcLogsResponse},
  82. };
  83. use solana_sdk::account::Account;
  84. use solana_sdk::commitment_config::CommitmentConfig;
  85. use solana_sdk::signature::{Signature, Signer};
  86. use solana_sdk::transaction::Transaction;
  87. use std::iter::Map;
  88. use std::marker::PhantomData;
  89. use std::ops::Deref;
  90. use std::pin::Pin;
  91. use std::sync::Arc;
  92. use std::vec::IntoIter;
  93. use thiserror::Error;
  94. use tokio::{
  95. runtime::Handle,
  96. sync::{
  97. mpsc::{unbounded_channel, UnboundedReceiver},
  98. RwLock,
  99. },
  100. task::JoinHandle,
  101. };
  102. pub use anchor_lang;
  103. pub use cluster::Cluster;
  104. pub use solana_client;
  105. pub use solana_sdk;
  106. mod cluster;
  107. #[cfg(not(feature = "async"))]
  108. mod blocking;
  109. #[cfg(feature = "async")]
  110. mod nonblocking;
  111. const PROGRAM_LOG: &str = "Program log: ";
  112. const PROGRAM_DATA: &str = "Program data: ";
  113. type UnsubscribeFn = Box<dyn FnOnce() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send>;
  114. /// Client defines the base configuration for building RPC clients to
  115. /// communicate with Anchor programs running on a Solana cluster. It's
  116. /// primary use is to build a `Program` client via the `program` method.
  117. pub struct Client<C> {
  118. cfg: Config<C>,
  119. }
  120. impl<C: Clone + Deref<Target = impl Signer>> Client<C> {
  121. pub fn new(cluster: Cluster, payer: C) -> Self {
  122. Self {
  123. cfg: Config {
  124. cluster,
  125. payer,
  126. options: None,
  127. },
  128. }
  129. }
  130. pub fn new_with_options(cluster: Cluster, payer: C, options: CommitmentConfig) -> Self {
  131. Self {
  132. cfg: Config {
  133. cluster,
  134. payer,
  135. options: Some(options),
  136. },
  137. }
  138. }
  139. pub fn program(&self, program_id: Pubkey) -> Result<Program<C>, ClientError> {
  140. let cfg = Config {
  141. cluster: self.cfg.cluster.clone(),
  142. options: self.cfg.options,
  143. payer: self.cfg.payer.clone(),
  144. };
  145. Program::new(program_id, cfg)
  146. }
  147. }
  148. /// Auxiliary data structure to align the types of the Solana CLI utils with Anchor client.
  149. /// Client<C> implementation requires <C: Clone + Deref<Target = impl Signer>> which does not comply with Box<dyn Signer>
  150. /// that's used when loaded Signer from keypair file. This struct is used to wrap the usage.
  151. pub struct DynSigner(pub Arc<dyn Signer>);
  152. impl Signer for DynSigner {
  153. fn pubkey(&self) -> Pubkey {
  154. self.0.pubkey()
  155. }
  156. fn try_pubkey(&self) -> Result<Pubkey, solana_sdk::signer::SignerError> {
  157. self.0.try_pubkey()
  158. }
  159. fn sign_message(&self, message: &[u8]) -> solana_sdk::signature::Signature {
  160. self.0.sign_message(message)
  161. }
  162. fn try_sign_message(
  163. &self,
  164. message: &[u8],
  165. ) -> Result<solana_sdk::signature::Signature, solana_sdk::signer::SignerError> {
  166. self.0.try_sign_message(message)
  167. }
  168. fn is_interactive(&self) -> bool {
  169. self.0.is_interactive()
  170. }
  171. }
  172. // Internal configuration for a client.
  173. #[derive(Debug)]
  174. pub struct Config<C> {
  175. cluster: Cluster,
  176. payer: C,
  177. options: Option<CommitmentConfig>,
  178. }
  179. pub struct EventUnsubscriber<'a> {
  180. handle: JoinHandle<Result<(), ClientError>>,
  181. rx: UnboundedReceiver<UnsubscribeFn>,
  182. #[cfg(not(feature = "async"))]
  183. runtime_handle: &'a Handle,
  184. _lifetime_marker: PhantomData<&'a Handle>,
  185. }
  186. impl<'a> EventUnsubscriber<'a> {
  187. async fn unsubscribe_internal(mut self) {
  188. if let Some(unsubscribe) = self.rx.recv().await {
  189. unsubscribe().await;
  190. }
  191. let _ = self.handle.await;
  192. }
  193. }
  194. /// Program is the primary client handle to be used to build and send requests.
  195. pub struct Program<C> {
  196. program_id: Pubkey,
  197. cfg: Config<C>,
  198. sub_client: Arc<RwLock<Option<PubsubClient>>>,
  199. #[cfg(not(feature = "async"))]
  200. rt: tokio::runtime::Runtime,
  201. }
  202. impl<C: Deref<Target = impl Signer> + Clone> Program<C> {
  203. pub fn payer(&self) -> Pubkey {
  204. self.cfg.payer.pubkey()
  205. }
  206. /// Returns a request builder.
  207. pub fn request(&self) -> RequestBuilder<C> {
  208. RequestBuilder::from(
  209. self.program_id,
  210. self.cfg.cluster.url(),
  211. self.cfg.payer.clone(),
  212. self.cfg.options,
  213. #[cfg(not(feature = "async"))]
  214. self.rt.handle(),
  215. )
  216. }
  217. pub fn id(&self) -> Pubkey {
  218. self.program_id
  219. }
  220. pub fn rpc(&self) -> RpcClient {
  221. RpcClient::new_with_commitment(
  222. self.cfg.cluster.url().to_string(),
  223. self.cfg.options.unwrap_or_default(),
  224. )
  225. }
  226. pub fn async_rpc(&self) -> AsyncRpcClient {
  227. AsyncRpcClient::new_with_commitment(
  228. self.cfg.cluster.url().to_string(),
  229. self.cfg.options.unwrap_or_default(),
  230. )
  231. }
  232. async fn account_internal<T: AccountDeserialize>(
  233. &self,
  234. address: Pubkey,
  235. ) -> Result<T, ClientError> {
  236. let rpc_client = AsyncRpcClient::new_with_commitment(
  237. self.cfg.cluster.url().to_string(),
  238. self.cfg.options.unwrap_or_default(),
  239. );
  240. let account = rpc_client
  241. .get_account_with_commitment(&address, CommitmentConfig::processed())
  242. .await?
  243. .value
  244. .ok_or(ClientError::AccountNotFound)?;
  245. let mut data: &[u8] = &account.data;
  246. T::try_deserialize(&mut data).map_err(Into::into)
  247. }
  248. async fn accounts_lazy_internal<T: AccountDeserialize + Discriminator>(
  249. &self,
  250. filters: Vec<RpcFilterType>,
  251. ) -> Result<ProgramAccountsIterator<T>, ClientError> {
  252. let account_type_filter =
  253. RpcFilterType::Memcmp(Memcmp::new_base58_encoded(0, &T::discriminator()));
  254. let config = RpcProgramAccountsConfig {
  255. filters: Some([vec![account_type_filter], filters].concat()),
  256. account_config: RpcAccountInfoConfig {
  257. encoding: Some(UiAccountEncoding::Base64),
  258. ..RpcAccountInfoConfig::default()
  259. },
  260. ..RpcProgramAccountsConfig::default()
  261. };
  262. Ok(ProgramAccountsIterator {
  263. inner: self
  264. .async_rpc()
  265. .get_program_accounts_with_config(&self.id(), config)
  266. .await?
  267. .into_iter()
  268. .map(|(key, account)| {
  269. Ok((key, T::try_deserialize(&mut (&account.data as &[u8]))?))
  270. }),
  271. })
  272. }
  273. async fn init_sub_client_if_needed(&self) -> Result<(), ClientError> {
  274. let lock = &self.sub_client;
  275. let mut client = lock.write().await;
  276. if client.is_none() {
  277. let sub_client = PubsubClient::new(self.cfg.cluster.ws_url()).await?;
  278. *client = Some(sub_client);
  279. }
  280. Ok(())
  281. }
  282. async fn on_internal<T: anchor_lang::Event + anchor_lang::AnchorDeserialize>(
  283. &self,
  284. f: impl Fn(&EventContext, T) + Send + 'static,
  285. ) -> Result<
  286. (
  287. JoinHandle<Result<(), ClientError>>,
  288. UnboundedReceiver<UnsubscribeFn>,
  289. ),
  290. ClientError,
  291. > {
  292. self.init_sub_client_if_needed().await?;
  293. let (tx, rx) = unbounded_channel::<_>();
  294. let config = RpcTransactionLogsConfig {
  295. commitment: self.cfg.options,
  296. };
  297. let program_id_str = self.program_id.to_string();
  298. let filter = RpcTransactionLogsFilter::Mentions(vec![program_id_str.clone()]);
  299. let lock = Arc::clone(&self.sub_client);
  300. let handle = tokio::spawn(async move {
  301. if let Some(ref client) = *lock.read().await {
  302. let (mut notifications, unsubscribe) =
  303. client.logs_subscribe(filter, config).await?;
  304. tx.send(unsubscribe).map_err(|e| {
  305. ClientError::SolanaClientPubsubError(PubsubClientError::RequestFailed {
  306. message: "Unsubscribe failed".to_string(),
  307. reason: e.to_string(),
  308. })
  309. })?;
  310. while let Some(logs) = notifications.next().await {
  311. let ctx = EventContext {
  312. signature: logs.value.signature.parse().unwrap(),
  313. slot: logs.context.slot,
  314. };
  315. let events = parse_logs_response(logs, &program_id_str);
  316. for e in events {
  317. f(&ctx, e);
  318. }
  319. }
  320. }
  321. Ok::<(), ClientError>(())
  322. });
  323. Ok((handle, rx))
  324. }
  325. }
  326. /// Iterator with items of type (Pubkey, T). Used to lazily deserialize account structs.
  327. /// Wrapper type hides the inner type from usages so the implementation can be changed.
  328. pub struct ProgramAccountsIterator<T> {
  329. inner: Map<IntoIter<(Pubkey, Account)>, AccountConverterFunction<T>>,
  330. }
  331. /// Function type that accepts solana accounts and returns deserialized anchor accounts
  332. type AccountConverterFunction<T> = fn((Pubkey, Account)) -> Result<(Pubkey, T), ClientError>;
  333. impl<T> Iterator for ProgramAccountsIterator<T> {
  334. type Item = Result<(Pubkey, T), ClientError>;
  335. fn next(&mut self) -> Option<Self::Item> {
  336. self.inner.next()
  337. }
  338. }
  339. pub fn handle_program_log<T: anchor_lang::Event + anchor_lang::AnchorDeserialize>(
  340. self_program_str: &str,
  341. l: &str,
  342. ) -> Result<(Option<T>, Option<String>, bool), ClientError> {
  343. use anchor_lang::__private::base64;
  344. use base64::engine::general_purpose::STANDARD;
  345. use base64::Engine;
  346. // Log emitted from the current program.
  347. if let Some(log) = l
  348. .strip_prefix(PROGRAM_LOG)
  349. .or_else(|| l.strip_prefix(PROGRAM_DATA))
  350. {
  351. let borsh_bytes = match STANDARD.decode(log) {
  352. Ok(borsh_bytes) => borsh_bytes,
  353. _ => {
  354. #[cfg(feature = "debug")]
  355. println!("Could not base64 decode log: {}", log);
  356. return Ok((None, None, false));
  357. }
  358. };
  359. let mut slice: &[u8] = &borsh_bytes[..];
  360. let disc: [u8; 8] = {
  361. let mut disc = [0; 8];
  362. disc.copy_from_slice(&borsh_bytes[..8]);
  363. slice = &slice[8..];
  364. disc
  365. };
  366. let mut event = None;
  367. if disc == T::discriminator() {
  368. let e: T = anchor_lang::AnchorDeserialize::deserialize(&mut slice)
  369. .map_err(|e| ClientError::LogParseError(e.to_string()))?;
  370. event = Some(e);
  371. }
  372. Ok((event, None, false))
  373. }
  374. // System log.
  375. else {
  376. let (program, did_pop) = handle_system_log(self_program_str, l);
  377. Ok((None, program, did_pop))
  378. }
  379. }
  380. pub fn handle_system_log(this_program_str: &str, log: &str) -> (Option<String>, bool) {
  381. if log.starts_with(&format!("Program {this_program_str} log:")) {
  382. (Some(this_program_str.to_string()), false)
  383. } else if log.contains("invoke") {
  384. (Some("cpi".to_string()), false) // Any string will do.
  385. } else {
  386. let re = Regex::new(r"^Program (.*) success*$").unwrap();
  387. if re.is_match(log) {
  388. (None, true)
  389. } else {
  390. (None, false)
  391. }
  392. }
  393. }
  394. pub struct Execution {
  395. stack: Vec<String>,
  396. }
  397. impl Execution {
  398. pub fn new(logs: &mut &[String]) -> Result<Self, ClientError> {
  399. let l = &logs[0];
  400. *logs = &logs[1..];
  401. let re = Regex::new(r"^Program (.*) invoke.*$").unwrap();
  402. let c = re
  403. .captures(l)
  404. .ok_or_else(|| ClientError::LogParseError(l.to_string()))?;
  405. let program = c
  406. .get(1)
  407. .ok_or_else(|| ClientError::LogParseError(l.to_string()))?
  408. .as_str()
  409. .to_string();
  410. Ok(Self {
  411. stack: vec![program],
  412. })
  413. }
  414. pub fn program(&self) -> String {
  415. assert!(!self.stack.is_empty());
  416. self.stack[self.stack.len() - 1].clone()
  417. }
  418. pub fn push(&mut self, new_program: String) {
  419. self.stack.push(new_program);
  420. }
  421. pub fn pop(&mut self) {
  422. assert!(!self.stack.is_empty());
  423. self.stack.pop().unwrap();
  424. }
  425. }
  426. #[derive(Debug)]
  427. pub struct EventContext {
  428. pub signature: Signature,
  429. pub slot: u64,
  430. }
  431. #[derive(Debug, Error)]
  432. pub enum ClientError {
  433. #[error("Account not found")]
  434. AccountNotFound,
  435. #[error("{0}")]
  436. AnchorError(#[from] anchor_lang::error::Error),
  437. #[error("{0}")]
  438. ProgramError(#[from] ProgramError),
  439. #[error("{0}")]
  440. SolanaClientError(#[from] SolanaClientError),
  441. #[error("{0}")]
  442. SolanaClientPubsubError(#[from] PubsubClientError),
  443. #[error("Unable to parse log: {0}")]
  444. LogParseError(String),
  445. #[error(transparent)]
  446. IOError(#[from] std::io::Error),
  447. }
  448. /// `RequestBuilder` provides a builder interface to create and send
  449. /// transactions to a cluster.
  450. pub struct RequestBuilder<'a, C> {
  451. cluster: String,
  452. program_id: Pubkey,
  453. accounts: Vec<AccountMeta>,
  454. options: CommitmentConfig,
  455. instructions: Vec<Instruction>,
  456. payer: C,
  457. // Serialized instruction data for the target RPC.
  458. instruction_data: Option<Vec<u8>>,
  459. signers: Vec<&'a dyn Signer>,
  460. #[cfg(not(feature = "async"))]
  461. handle: &'a Handle,
  462. }
  463. impl<'a, C: Deref<Target = impl Signer> + Clone> RequestBuilder<'a, C> {
  464. #[must_use]
  465. pub fn payer(mut self, payer: C) -> Self {
  466. self.payer = payer;
  467. self
  468. }
  469. #[must_use]
  470. pub fn cluster(mut self, url: &str) -> Self {
  471. self.cluster = url.to_string();
  472. self
  473. }
  474. #[must_use]
  475. pub fn instruction(mut self, ix: Instruction) -> Self {
  476. self.instructions.push(ix);
  477. self
  478. }
  479. #[must_use]
  480. pub fn program(mut self, program_id: Pubkey) -> Self {
  481. self.program_id = program_id;
  482. self
  483. }
  484. /// Set the accounts to pass to the instruction.
  485. ///
  486. /// `accounts` argument can be:
  487. ///
  488. /// - Any type that implements [`ToAccountMetas`] trait
  489. /// - A vector of [`AccountMeta`]s (for remaining accounts)
  490. ///
  491. /// Note that the given accounts are appended to the previous list of accounts instead of
  492. /// overriding the existing ones (if any).
  493. ///
  494. /// # Example
  495. ///
  496. /// ```ignore
  497. /// program
  498. /// .request()
  499. /// // Regular accounts
  500. /// .accounts(accounts::Initialize {
  501. /// my_account: my_account_kp.pubkey(),
  502. /// payer: program.payer(),
  503. /// system_program: system_program::ID,
  504. /// })
  505. /// // Remaining accounts
  506. /// .accounts(vec![AccountMeta {
  507. /// pubkey: remaining,
  508. /// is_signer: true,
  509. /// is_writable: true,
  510. /// }])
  511. /// .args(instruction::Initialize { field: 42 })
  512. /// .send()?;
  513. /// ```
  514. #[must_use]
  515. pub fn accounts(mut self, accounts: impl ToAccountMetas) -> Self {
  516. let mut metas = accounts.to_account_metas(None);
  517. self.accounts.append(&mut metas);
  518. self
  519. }
  520. #[must_use]
  521. pub fn options(mut self, options: CommitmentConfig) -> Self {
  522. self.options = options;
  523. self
  524. }
  525. #[must_use]
  526. pub fn args(mut self, args: impl InstructionData) -> Self {
  527. self.instruction_data = Some(args.data());
  528. self
  529. }
  530. #[must_use]
  531. pub fn signer(mut self, signer: &'a dyn Signer) -> Self {
  532. self.signers.push(signer);
  533. self
  534. }
  535. pub fn instructions(&self) -> Result<Vec<Instruction>, ClientError> {
  536. let mut instructions = self.instructions.clone();
  537. if let Some(ix_data) = &self.instruction_data {
  538. instructions.push(Instruction {
  539. program_id: self.program_id,
  540. data: ix_data.clone(),
  541. accounts: self.accounts.clone(),
  542. });
  543. }
  544. Ok(instructions)
  545. }
  546. fn signed_transaction_with_blockhash(
  547. &self,
  548. latest_hash: Hash,
  549. ) -> Result<Transaction, ClientError> {
  550. let instructions = self.instructions()?;
  551. let mut signers = self.signers.clone();
  552. signers.push(&*self.payer);
  553. let tx = Transaction::new_signed_with_payer(
  554. &instructions,
  555. Some(&self.payer.pubkey()),
  556. &signers,
  557. latest_hash,
  558. );
  559. Ok(tx)
  560. }
  561. pub fn transaction(&self) -> Result<Transaction, ClientError> {
  562. let instructions = &self.instructions;
  563. let tx = Transaction::new_with_payer(instructions, Some(&self.payer.pubkey()));
  564. Ok(tx)
  565. }
  566. async fn signed_transaction_internal(&self) -> Result<Transaction, ClientError> {
  567. let latest_hash =
  568. AsyncRpcClient::new_with_commitment(self.cluster.to_owned(), self.options)
  569. .get_latest_blockhash()
  570. .await?;
  571. let tx = self.signed_transaction_with_blockhash(latest_hash)?;
  572. Ok(tx)
  573. }
  574. async fn send_internal(&self) -> Result<Signature, ClientError> {
  575. let rpc_client = AsyncRpcClient::new_with_commitment(self.cluster.to_owned(), self.options);
  576. let latest_hash = rpc_client.get_latest_blockhash().await?;
  577. let tx = self.signed_transaction_with_blockhash(latest_hash)?;
  578. rpc_client
  579. .send_and_confirm_transaction(&tx)
  580. .await
  581. .map_err(Into::into)
  582. }
  583. async fn send_with_spinner_and_config_internal(
  584. &self,
  585. config: RpcSendTransactionConfig,
  586. ) -> Result<Signature, ClientError> {
  587. let rpc_client = AsyncRpcClient::new_with_commitment(self.cluster.to_owned(), self.options);
  588. let latest_hash = rpc_client.get_latest_blockhash().await?;
  589. let tx = self.signed_transaction_with_blockhash(latest_hash)?;
  590. rpc_client
  591. .send_and_confirm_transaction_with_spinner_and_config(
  592. &tx,
  593. rpc_client.commitment(),
  594. config,
  595. )
  596. .await
  597. .map_err(Into::into)
  598. }
  599. }
  600. fn parse_logs_response<T: anchor_lang::Event + anchor_lang::AnchorDeserialize>(
  601. logs: RpcResponse<RpcLogsResponse>,
  602. program_id_str: &str,
  603. ) -> Vec<T> {
  604. let mut logs = &logs.value.logs[..];
  605. let mut events: Vec<T> = Vec::new();
  606. if !logs.is_empty() {
  607. if let Ok(mut execution) = Execution::new(&mut logs) {
  608. for l in logs {
  609. // Parse the log.
  610. let (event, new_program, did_pop) = {
  611. if program_id_str == execution.program() {
  612. handle_program_log(program_id_str, l).unwrap_or_else(|e| {
  613. println!("Unable to parse log: {e}");
  614. std::process::exit(1);
  615. })
  616. } else {
  617. let (program, did_pop) = handle_system_log(program_id_str, l);
  618. (None, program, did_pop)
  619. }
  620. };
  621. // Emit the event.
  622. if let Some(e) = event {
  623. events.push(e);
  624. }
  625. // Switch program context on CPI.
  626. if let Some(new_program) = new_program {
  627. execution.push(new_program);
  628. }
  629. // Program returned.
  630. if did_pop {
  631. execution.pop();
  632. }
  633. }
  634. }
  635. }
  636. events
  637. }
  638. #[cfg(test)]
  639. mod tests {
  640. use super::*;
  641. #[test]
  642. fn new_execution() {
  643. let mut logs: &[String] =
  644. &["Program 7Y8VDzehoewALqJfyxZYMgYCnMTCDhWuGfJKUvjYWATw invoke [1]".to_string()];
  645. let exe = Execution::new(&mut logs).unwrap();
  646. assert_eq!(
  647. exe.stack[0],
  648. "7Y8VDzehoewALqJfyxZYMgYCnMTCDhWuGfJKUvjYWATw".to_string()
  649. );
  650. }
  651. #[test]
  652. fn handle_system_log_pop() {
  653. let log = "Program 7Y8VDzehoewALqJfyxZYMgYCnMTCDhWuGfJKUvjYWATw success";
  654. let (program, did_pop) = handle_system_log("asdf", log);
  655. assert_eq!(program, None);
  656. assert!(did_pop);
  657. }
  658. #[test]
  659. fn handle_system_log_no_pop() {
  660. let log = "Program 7swsTUiQ6KUK4uFYquQKg4epFRsBnvbrTf2fZQCa2sTJ qwer";
  661. let (program, did_pop) = handle_system_log("asdf", log);
  662. assert_eq!(program, None);
  663. assert!(!did_pop);
  664. }
  665. }