|
@@ -4,6 +4,8 @@
|
|
|
use anchor_lang::solana_program::instruction::{AccountMeta, Instruction};
|
|
|
use anchor_lang::solana_program::program_error::ProgramError;
|
|
|
use anchor_lang::solana_program::pubkey::Pubkey;
|
|
|
+use anchor_lang::solana_program::system_program;
|
|
|
+use anchor_lang::solana_program::sysvar::rent;
|
|
|
use anchor_lang::{AccountDeserialize, InstructionData, ToAccountMetas};
|
|
|
use regex::Regex;
|
|
|
use solana_client::client_error::ClientError as SolanaClientError;
|
|
@@ -87,11 +89,23 @@ impl Program {
|
|
|
|
|
|
/// Returns a request builder.
|
|
|
pub fn request(&self) -> RequestBuilder {
|
|
|
- RequestBuilder::new(
|
|
|
+ RequestBuilder::from(
|
|
|
self.program_id,
|
|
|
&self.cfg.cluster.url(),
|
|
|
Keypair::from_bytes(&self.cfg.payer.to_bytes()).unwrap(),
|
|
|
self.cfg.options,
|
|
|
+ RequestNamespace::Global,
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns a request builder for program state.
|
|
|
+ pub fn state_request(&self) -> RequestBuilder {
|
|
|
+ RequestBuilder::from(
|
|
|
+ self.program_id,
|
|
|
+ &self.cfg.cluster.url(),
|
|
|
+ Keypair::from_bytes(&self.cfg.payer.to_bytes()).unwrap(),
|
|
|
+ self.cfg.options,
|
|
|
+ RequestNamespace::State { new: false },
|
|
|
)
|
|
|
}
|
|
|
|
|
@@ -109,6 +123,10 @@ impl Program {
|
|
|
T::try_deserialize(&mut data).map_err(Into::into)
|
|
|
}
|
|
|
|
|
|
+ pub fn state<T: AccountDeserialize>(&self) -> Result<T, ClientError> {
|
|
|
+ self.account(anchor_lang::__private::state::address(&self.program_id))
|
|
|
+ }
|
|
|
+
|
|
|
pub fn rpc(&self) -> RpcClient {
|
|
|
RpcClient::new_with_commitment(
|
|
|
self.cfg.cluster.url().to_string(),
|
|
@@ -304,14 +322,27 @@ pub struct RequestBuilder<'a> {
|
|
|
// Serialized instruction data for the target RPC.
|
|
|
instruction_data: Option<Vec<u8>>,
|
|
|
signers: Vec<&'a dyn Signer>,
|
|
|
+ // True if the user is sending a state instruction.
|
|
|
+ namespace: RequestNamespace,
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(PartialEq)]
|
|
|
+pub enum RequestNamespace {
|
|
|
+ Global,
|
|
|
+ State {
|
|
|
+ // True if the request is to the state's new ctor.
|
|
|
+ new: bool,
|
|
|
+ },
|
|
|
+ Interface,
|
|
|
}
|
|
|
|
|
|
impl<'a> RequestBuilder<'a> {
|
|
|
- pub fn new(
|
|
|
+ pub fn from(
|
|
|
program_id: Pubkey,
|
|
|
cluster: &str,
|
|
|
payer: Keypair,
|
|
|
options: Option<CommitmentConfig>,
|
|
|
+ namespace: RequestNamespace,
|
|
|
) -> Self {
|
|
|
Self {
|
|
|
program_id,
|
|
@@ -322,6 +353,7 @@ impl<'a> RequestBuilder<'a> {
|
|
|
instructions: Vec::new(),
|
|
|
instruction_data: None,
|
|
|
signers: Vec::new(),
|
|
|
+ namespace,
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -361,18 +393,53 @@ impl<'a> RequestBuilder<'a> {
|
|
|
self
|
|
|
}
|
|
|
|
|
|
+ /// Invokes the `#[state]`'s `new` constructor.
|
|
|
+ pub fn new(mut self, args: impl InstructionData) -> Self {
|
|
|
+ assert!(self.namespace == RequestNamespace::State { new: false });
|
|
|
+ self.namespace = RequestNamespace::State { new: true };
|
|
|
+ self.instruction_data = Some(args.data());
|
|
|
+ self
|
|
|
+ }
|
|
|
+
|
|
|
pub fn signer(mut self, signer: &'a dyn Signer) -> Self {
|
|
|
self.signers.push(signer);
|
|
|
self
|
|
|
}
|
|
|
|
|
|
pub fn send(self) -> Result<Signature, ClientError> {
|
|
|
+ let accounts = match self.namespace {
|
|
|
+ RequestNamespace::State { new } => {
|
|
|
+ let mut accounts = match new {
|
|
|
+ false => vec![AccountMeta::new(
|
|
|
+ anchor_lang::__private::state::address(&self.program_id),
|
|
|
+ false,
|
|
|
+ )],
|
|
|
+ true => vec![
|
|
|
+ AccountMeta::new_readonly(self.payer.pubkey(), true),
|
|
|
+ AccountMeta::new(
|
|
|
+ anchor_lang::__private::state::address(&self.program_id),
|
|
|
+ false,
|
|
|
+ ),
|
|
|
+ AccountMeta::new_readonly(
|
|
|
+ Pubkey::find_program_address(&[], &self.program_id).0,
|
|
|
+ false,
|
|
|
+ ),
|
|
|
+ AccountMeta::new_readonly(system_program::ID, false),
|
|
|
+ AccountMeta::new_readonly(self.program_id, false),
|
|
|
+ AccountMeta::new_readonly(rent::ID, false),
|
|
|
+ ],
|
|
|
+ };
|
|
|
+ accounts.extend_from_slice(&self.accounts);
|
|
|
+ accounts
|
|
|
+ }
|
|
|
+ _ => self.accounts,
|
|
|
+ };
|
|
|
let mut instructions = self.instructions;
|
|
|
if let Some(ix_data) = self.instruction_data {
|
|
|
instructions.push(Instruction {
|
|
|
program_id: self.program_id,
|
|
|
data: ix_data,
|
|
|
- accounts: self.accounts,
|
|
|
+ accounts,
|
|
|
});
|
|
|
}
|
|
|
|