Skip to content
Merged
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ target/
test-ledger
examples/*/Cargo.lock
examples/**/Cargo.lock
*/example/Cargo.lock
tests/*/Cargo.lock
tests/**/Cargo.lock
tests/*/yarn.lock
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ The minor version will be incremented upon a breaking change and the patch versi
## [Unreleased]

### Features
- lang: Add tokio support with `request_threadsafe` to `anchor_client` ([#3057](https://github.com/coral-xyz/anchor/pull/3057])).


Comment thread
cryptopapi997 marked this conversation as resolved.
- ts: Add optional `commitment` parameter to `Program.addEventListener` ([#3052](https://github.com/coral-xyz/anchor/pull/3052)).

Expand Down
61 changes: 49 additions & 12 deletions client/example/src/nonblocking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use composite::instruction as composite_instruction;
use composite::{DummyA, DummyB};
use optional::account::{DataAccount, DataPda};
use std::ops::Deref;
use std::rc::Rc;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::mpsc;
use tokio::time::sleep;
Expand All @@ -43,14 +43,15 @@ pub async fn main() -> Result<()> {
);

// Client.
let payer = Rc::new(payer);
let payer = Arc::new(payer);
let client =
Client::new_with_options(url.clone(), payer.clone(), CommitmentConfig::processed());

println!("\nStarting async test...");
composite(&client, opts.composite_pid).await?;
basic_2(&client, opts.basic_2_pid).await?;
basic_4(&client, opts.basic_4_pid).await?;
test_tokio(client, opts.basic_2_pid).await?;

// Can also use references, since they deref to a signer
let payer: &Keypair = &payer;
Expand All @@ -61,6 +62,42 @@ pub async fn main() -> Result<()> {
Ok(())
}

pub async fn test_tokio(client: Client<Arc<Keypair>>, pid: Pubkey) -> Result<()> {
tokio::spawn(async move {
let program = client.program(pid).unwrap();

// `Create` parameters.
let counter = Arc::new(Keypair::new());
let counter_pubkey = counter.pubkey();
let authority = program.payer();

// Build and send a transaction.
program
.request()
.signer(counter)
.accounts(basic_2_accounts::Create {
counter: counter_pubkey,
user: authority,
system_program: system_program::ID,
})
.args(basic_2_instruction::Create { authority })
.send()
.await
.unwrap();

let counter_account: Counter = program.account(counter_pubkey).await.unwrap();

assert_eq!(counter_account.authority, authority);
assert_eq!(counter_account.count, 0);
})
.await
.unwrap();

println!("Tokio success!");

Ok(())
}

pub async fn composite<C: Deref<Target = impl Signer> + Clone>(
client: &Client<C>,
pid: Pubkey,
Expand All @@ -69,8 +106,8 @@ pub async fn composite<C: Deref<Target = impl Signer> + Clone>(
let program = client.program(pid)?;

// `Initialize` parameters.
let dummy_a = Keypair::new();
let dummy_b = Keypair::new();
let dummy_a = Arc::new(Keypair::new());
let dummy_b = Arc::new(Keypair::new());

// Build and send a transaction.
program
Expand All @@ -95,8 +132,8 @@ pub async fn composite<C: Deref<Target = impl Signer> + Clone>(
500,
&program.id(),
))
.signer(&dummy_a)
.signer(&dummy_b)
.signer(dummy_a.clone())
.signer(dummy_b.clone())
.accounts(Initialize {
dummy_a: dummy_a.pubkey(),
dummy_b: dummy_b.pubkey(),
Expand Down Expand Up @@ -147,13 +184,13 @@ pub async fn basic_2<C: Deref<Target = impl Signer> + Clone>(
let program = client.program(pid)?;

// `Create` parameters.
let counter = Keypair::new();
let counter = Arc::new(Keypair::new());
let authority = program.payer();

// Build and send a transaction.
program
.request()
.signer(&counter)
.signer(counter.clone())
.accounts(basic_2_accounts::Create {
counter: counter.pubkey(),
user: authority,
Expand Down Expand Up @@ -253,13 +290,13 @@ pub async fn optional<C: Deref<Target = impl Signer> + Clone>(
let program = client.program(pid)?;

// `Initialize` parameters.
let data_account_keypair = Keypair::new();
let data_account_keypair = Arc::new(Keypair::new());

let data_account_key = data_account_keypair.pubkey();

let data_pda_seeds = &[DataPda::PREFIX.as_ref(), data_account_key.as_ref()];
let data_pda_key = Pubkey::find_program_address(data_pda_seeds, &pid).0;
let required_keypair = Keypair::new();
let required_keypair = Arc::new(Keypair::new());
let value: u64 = 10;

// Build and send a transaction.
Expand All @@ -276,8 +313,8 @@ pub async fn optional<C: Deref<Target = impl Signer> + Clone>(
DataAccount::LEN as u64,
&program.id(),
))
.signer(&data_account_keypair)
.signer(&required_keypair)
.signer(data_account_keypair.clone())
.signer(required_keypair.clone())
.accounts(OptionalInitialize {
payer: Some(program.payer()),
required: required_keypair.pubkey(),
Expand Down
86 changes: 83 additions & 3 deletions client/src/blocking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ use crate::{
RequestBuilder,
};
use anchor_lang::{prelude::Pubkey, AccountDeserialize, Discriminator};
use solana_client::{rpc_config::RpcSendTransactionConfig, rpc_filter::RpcFilterType};
use solana_client::{
nonblocking::rpc_client::RpcClient as AsyncRpcClient, rpc_config::RpcSendTransactionConfig,
rpc_filter::RpcFilterType,
};
use solana_sdk::{
commitment_config::CommitmentConfig, signature::Signature, signer::Signer,
commitment_config::CommitmentConfig, hash::Hash, signature::Signature, signer::Signer,
transaction::Transaction,
};
use std::{marker::PhantomData, ops::Deref, sync::Arc};
Expand Down Expand Up @@ -33,6 +36,18 @@ impl<C: Deref<Target = impl Signer> + Clone> Program<C> {
})
}

/// Returns a request builder.
pub fn request(&self) -> RequestBuilder<'_, C, Box<dyn Signer + '_>> {
RequestBuilder::from(
self.program_id,
self.cfg.cluster.url(),
self.cfg.payer.clone(),
self.cfg.options,
#[cfg(not(feature = "async"))]
self.rt.handle(),
)
}

/// Returns the account at the given address.
pub fn account<T: AccountDeserialize>(&self, address: Pubkey) -> Result<T, ClientError> {
self.rt.block_on(self.account_internal(address))
Expand Down Expand Up @@ -70,7 +85,7 @@ impl<C: Deref<Target = impl Signer> + Clone> Program<C> {
}
}

impl<'a, C: Deref<Target = impl Signer> + Clone> RequestBuilder<'a, C> {
impl<'a, C: Deref<Target = impl Signer> + Clone> RequestBuilder<'a, C, Box<dyn Signer + 'a>> {
pub fn from(
program_id: Pubkey,
cluster: &str,
Expand All @@ -88,9 +103,74 @@ impl<'a, C: Deref<Target = impl Signer> + Clone> RequestBuilder<'a, C> {
instruction_data: None,
signers: Vec::new(),
handle,
_phantom: PhantomData,
}
}

#[must_use]
pub fn signer<T: Signer + 'a>(mut self, signer: T) -> Self {
self.signers.push(Box::new(signer));
self
}

pub fn signed_transaction_with_blockhash(
&self,
latest_hash: Hash,
) -> Result<Transaction, ClientError> {
let instructions = self.instructions()?;
let signers: Vec<&dyn Signer> = self.signers.iter().map(|s| s.as_ref()).collect();
let mut all_signers = signers;
all_signers.push(&*self.payer);

let tx = Transaction::new_signed_with_payer(
&instructions,
Some(&self.payer.pubkey()),
&all_signers,
latest_hash,
);

Ok(tx)
}

async fn signed_transaction_internal(&self) -> Result<Transaction, ClientError> {
let latest_hash =
AsyncRpcClient::new_with_commitment(self.cluster.to_owned(), self.options)
.get_latest_blockhash()
.await?;
let tx = self.signed_transaction_with_blockhash(latest_hash)?;

Ok(tx)
}

async fn send_internal(&self) -> Result<Signature, ClientError> {
let rpc_client = AsyncRpcClient::new_with_commitment(self.cluster.to_owned(), self.options);
let latest_hash = rpc_client.get_latest_blockhash().await?;
let tx = self.signed_transaction_with_blockhash(latest_hash)?;

rpc_client
.send_and_confirm_transaction(&tx)
.await
.map_err(Into::into)
}

async fn send_with_spinner_and_config_internal(
&self,
config: RpcSendTransactionConfig,
) -> Result<Signature, ClientError> {
let rpc_client = AsyncRpcClient::new_with_commitment(self.cluster.to_owned(), self.options);
let latest_hash = rpc_client.get_latest_blockhash().await?;
let tx = self.signed_transaction_with_blockhash(latest_hash)?;

rpc_client
.send_and_confirm_transaction_with_spinner_and_config(
&tx,
rpc_client.commitment(),
config,
)
.await
.map_err(Into::into)
}
Comment thread
acheroncrypto marked this conversation as resolved.
Outdated

pub fn signed_transaction(&self) -> Result<Transaction, ClientError> {
self.handle.block_on(self.signed_transaction_internal())
}
Expand Down
Loading