lib.rs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. //! `anchor_client` provides an RPC client to send transactions and fetch
  2. //! deserialized accounts from Solana programs written in `anchor_lang`.
  3. use anchor_lang::solana_program::instruction::{AccountMeta, Instruction};
  4. use anchor_lang::solana_program::program_error::ProgramError;
  5. use anchor_lang::solana_program::pubkey::Pubkey;
  6. use anchor_lang::{AccountDeserialize, AnchorSerialize, ToAccountMetas};
  7. use solana_client::client_error::ClientError as SolanaClientError;
  8. use solana_client::rpc_client::RpcClient;
  9. use solana_sdk::commitment_config::CommitmentConfig;
  10. use solana_sdk::signature::{Keypair, Signature, Signer};
  11. use solana_sdk::transaction::Transaction;
  12. use std::convert::Into;
  13. use thiserror::Error;
  14. pub use anchor_lang;
  15. pub use solana_client;
  16. pub use solana_sdk;
  17. /// Client defines the base configuration for building RPC clients to
  18. /// communitcate with Anchor programs running on a Solana cluster. It's
  19. /// primary use is to build a `Program` client via the `program` method.
  20. pub struct Client {
  21. cfg: Config,
  22. }
  23. impl Client {
  24. pub fn new(cluster: &str, payer: Keypair) -> Self {
  25. Self {
  26. cfg: Config {
  27. cluster: cluster.to_string(),
  28. payer,
  29. options: None,
  30. },
  31. }
  32. }
  33. pub fn new_with_options(cluster: &str, payer: Keypair, options: CommitmentConfig) -> Self {
  34. Self {
  35. cfg: Config {
  36. cluster: cluster.to_string(),
  37. payer,
  38. options: Some(options),
  39. },
  40. }
  41. }
  42. pub fn program(&self, program_id: Pubkey) -> Program {
  43. Program {
  44. program_id,
  45. cfg: Config {
  46. cluster: self.cfg.cluster.clone(),
  47. options: self.cfg.options.clone(),
  48. payer: Keypair::from_bytes(&self.cfg.payer.to_bytes()).unwrap(),
  49. },
  50. }
  51. }
  52. }
  53. // Internal configuration for a client.
  54. struct Config {
  55. cluster: String,
  56. payer: Keypair,
  57. options: Option<CommitmentConfig>,
  58. }
  59. /// Program is the primary client handle to be used to build and send requests.
  60. pub struct Program {
  61. program_id: Pubkey,
  62. cfg: Config,
  63. }
  64. impl Program {
  65. pub fn payer(&self) -> Pubkey {
  66. self.cfg.payer.pubkey()
  67. }
  68. /// Returns a request builder.
  69. pub fn request(&self) -> RequestBuilder {
  70. RequestBuilder::new(
  71. self.program_id,
  72. &self.cfg.cluster,
  73. Keypair::from_bytes(&self.cfg.payer.to_bytes()).unwrap(),
  74. self.cfg.options.clone(),
  75. )
  76. }
  77. /// Returns the account at the given address.
  78. pub fn account<T: AccountDeserialize>(&self, address: Pubkey) -> Result<T, ClientError> {
  79. let rpc_client = RpcClient::new_with_commitment(
  80. self.cfg.cluster.clone(),
  81. self.cfg.options.unwrap_or(Default::default()),
  82. );
  83. let account = rpc_client
  84. .get_account_with_commitment(&address, CommitmentConfig::recent())?
  85. .value
  86. .ok_or(ClientError::AccountNotFound)?;
  87. let mut data: &[u8] = &account.data;
  88. T::try_deserialize(&mut data).map_err(Into::into)
  89. }
  90. pub fn rpc(&self) -> RpcClient {
  91. RpcClient::new_with_commitment(
  92. self.cfg.cluster.clone(),
  93. self.cfg.options.unwrap_or(Default::default()),
  94. )
  95. }
  96. pub fn id(&self) -> Pubkey {
  97. self.program_id
  98. }
  99. }
  100. #[derive(Debug, Error)]
  101. pub enum ClientError {
  102. #[error("Account not found")]
  103. AccountNotFound,
  104. #[error("{0}")]
  105. ProgramError(#[from] ProgramError),
  106. #[error("{0}")]
  107. SolanaClientError(#[from] SolanaClientError),
  108. }
  109. /// `RequestBuilder` provides a builder interface to create and send
  110. /// transactions to a cluster.
  111. pub struct RequestBuilder<'a> {
  112. cluster: String,
  113. program_id: Pubkey,
  114. accounts: Vec<AccountMeta>,
  115. options: CommitmentConfig,
  116. instructions: Vec<Instruction>,
  117. payer: Keypair,
  118. // Serialized instruction data for the target RPC.
  119. instruction_data: Option<Vec<u8>>,
  120. signers: Vec<&'a dyn Signer>,
  121. }
  122. impl<'a> RequestBuilder<'a> {
  123. pub fn new(
  124. program_id: Pubkey,
  125. cluster: &str,
  126. payer: Keypair,
  127. options: Option<CommitmentConfig>,
  128. ) -> Self {
  129. Self {
  130. program_id,
  131. payer,
  132. cluster: cluster.to_string(),
  133. accounts: Vec::new(),
  134. options: options.unwrap_or(Default::default()),
  135. instructions: Vec::new(),
  136. instruction_data: None,
  137. signers: Vec::new(),
  138. }
  139. }
  140. pub fn payer(mut self, payer: Keypair) -> Self {
  141. self.payer = payer;
  142. self
  143. }
  144. pub fn cluster(mut self, url: &str) -> Self {
  145. self.cluster = url.to_string();
  146. self
  147. }
  148. pub fn instruction(mut self, ix: Instruction) -> Self {
  149. self.instructions.push(ix);
  150. self
  151. }
  152. pub fn program(mut self, program_id: Pubkey) -> Self {
  153. self.program_id = program_id;
  154. self
  155. }
  156. pub fn accounts(mut self, accounts: impl ToAccountMetas) -> Self {
  157. let mut metas = accounts.to_account_metas(None);
  158. self.accounts.append(&mut metas);
  159. self
  160. }
  161. pub fn options(mut self, options: CommitmentConfig) -> Self {
  162. self.options = options;
  163. self
  164. }
  165. pub fn args(mut self, args: impl AnchorSerialize) -> Self {
  166. let data = args.try_to_vec().expect("Should always serialize");
  167. self.instruction_data = Some(data);
  168. self
  169. }
  170. pub fn signer(mut self, signer: &'a dyn Signer) -> Self {
  171. self.signers.push(signer);
  172. self
  173. }
  174. pub fn send(self) -> Result<Signature, ClientError> {
  175. let mut instructions = self.instructions;
  176. if let Some(ix_data) = self.instruction_data {
  177. instructions.push(Instruction {
  178. program_id: self.program_id,
  179. data: ix_data,
  180. accounts: self.accounts,
  181. });
  182. }
  183. let mut signers = self.signers;
  184. signers.push(&self.payer);
  185. let rpc_client = RpcClient::new_with_commitment(self.cluster, self.options);
  186. let tx = {
  187. let (recent_hash, _fee_calc) = rpc_client.get_recent_blockhash()?;
  188. Transaction::new_signed_with_payer(
  189. &instructions,
  190. Some(&self.payer.pubkey()),
  191. &signers,
  192. recent_hash,
  193. )
  194. };
  195. rpc_client
  196. .send_and_confirm_transaction(&tx)
  197. .map_err(Into::into)
  198. }
  199. }