client: Add `tokio` support to `RequestBuilder` with `async` feature (#3057)
Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com>
This commit is contained in:
parent
fd6174a88d
commit
f677742a97
|
@ -10,6 +10,7 @@ target/
|
||||||
test-ledger
|
test-ledger
|
||||||
examples/*/Cargo.lock
|
examples/*/Cargo.lock
|
||||||
examples/**/Cargo.lock
|
examples/**/Cargo.lock
|
||||||
|
*/example/Cargo.lock
|
||||||
tests/*/Cargo.lock
|
tests/*/Cargo.lock
|
||||||
tests/**/Cargo.lock
|
tests/**/Cargo.lock
|
||||||
tests/*/yarn.lock
|
tests/*/yarn.lock
|
||||||
|
|
|
@ -28,6 +28,7 @@ The minor version will be incremented upon a breaking change and the patch versi
|
||||||
### Breaking
|
### Breaking
|
||||||
|
|
||||||
- syn: Remove `bpf` target support in `hash` feature ([#3078](https://github.com/coral-xyz/anchor/pull/3078)).
|
- syn: Remove `bpf` target support in `hash` feature ([#3078](https://github.com/coral-xyz/anchor/pull/3078)).
|
||||||
|
- client: Add `tokio` support to `RequestBuilder` with `async` feature ([#3057](https://github.com/coral-xyz/anchor/pull/3057])).
|
||||||
|
|
||||||
## [0.30.1] - 2024-06-20
|
## [0.30.1] - 2024-06-20
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ use composite::instruction as composite_instruction;
|
||||||
use composite::{DummyA, DummyB};
|
use composite::{DummyA, DummyB};
|
||||||
use optional::account::{DataAccount, DataPda};
|
use optional::account::{DataAccount, DataPda};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::rc::Rc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
@ -43,7 +43,7 @@ pub async fn main() -> Result<()> {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Client.
|
// Client.
|
||||||
let payer = Rc::new(payer);
|
let payer = Arc::new(payer);
|
||||||
let client =
|
let client =
|
||||||
Client::new_with_options(url.clone(), payer.clone(), CommitmentConfig::processed());
|
Client::new_with_options(url.clone(), payer.clone(), CommitmentConfig::processed());
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ pub async fn main() -> Result<()> {
|
||||||
composite(&client, opts.composite_pid).await?;
|
composite(&client, opts.composite_pid).await?;
|
||||||
basic_2(&client, opts.basic_2_pid).await?;
|
basic_2(&client, opts.basic_2_pid).await?;
|
||||||
basic_4(&client, opts.basic_4_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
|
// Can also use references, since they deref to a signer
|
||||||
let payer: &Keypair = &payer;
|
let payer: &Keypair = &payer;
|
||||||
|
@ -61,6 +62,42 @@ pub async fn main() -> Result<()> {
|
||||||
Ok(())
|
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>(
|
pub async fn composite<C: Deref<Target = impl Signer> + Clone>(
|
||||||
client: &Client<C>,
|
client: &Client<C>,
|
||||||
pid: Pubkey,
|
pid: Pubkey,
|
||||||
|
@ -69,8 +106,8 @@ pub async fn composite<C: Deref<Target = impl Signer> + Clone>(
|
||||||
let program = client.program(pid)?;
|
let program = client.program(pid)?;
|
||||||
|
|
||||||
// `Initialize` parameters.
|
// `Initialize` parameters.
|
||||||
let dummy_a = Keypair::new();
|
let dummy_a = Arc::new(Keypair::new());
|
||||||
let dummy_b = Keypair::new();
|
let dummy_b = Arc::new(Keypair::new());
|
||||||
|
|
||||||
// Build and send a transaction.
|
// Build and send a transaction.
|
||||||
program
|
program
|
||||||
|
@ -95,8 +132,8 @@ pub async fn composite<C: Deref<Target = impl Signer> + Clone>(
|
||||||
500,
|
500,
|
||||||
&program.id(),
|
&program.id(),
|
||||||
))
|
))
|
||||||
.signer(&dummy_a)
|
.signer(dummy_a.clone())
|
||||||
.signer(&dummy_b)
|
.signer(dummy_b.clone())
|
||||||
.accounts(Initialize {
|
.accounts(Initialize {
|
||||||
dummy_a: dummy_a.pubkey(),
|
dummy_a: dummy_a.pubkey(),
|
||||||
dummy_b: dummy_b.pubkey(),
|
dummy_b: dummy_b.pubkey(),
|
||||||
|
@ -147,13 +184,13 @@ pub async fn basic_2<C: Deref<Target = impl Signer> + Clone>(
|
||||||
let program = client.program(pid)?;
|
let program = client.program(pid)?;
|
||||||
|
|
||||||
// `Create` parameters.
|
// `Create` parameters.
|
||||||
let counter = Keypair::new();
|
let counter = Arc::new(Keypair::new());
|
||||||
let authority = program.payer();
|
let authority = program.payer();
|
||||||
|
|
||||||
// Build and send a transaction.
|
// Build and send a transaction.
|
||||||
program
|
program
|
||||||
.request()
|
.request()
|
||||||
.signer(&counter)
|
.signer(counter.clone())
|
||||||
.accounts(basic_2_accounts::Create {
|
.accounts(basic_2_accounts::Create {
|
||||||
counter: counter.pubkey(),
|
counter: counter.pubkey(),
|
||||||
user: authority,
|
user: authority,
|
||||||
|
@ -253,13 +290,13 @@ pub async fn optional<C: Deref<Target = impl Signer> + Clone>(
|
||||||
let program = client.program(pid)?;
|
let program = client.program(pid)?;
|
||||||
|
|
||||||
// `Initialize` parameters.
|
// `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_account_key = data_account_keypair.pubkey();
|
||||||
|
|
||||||
let data_pda_seeds = &[DataPda::PREFIX.as_ref(), data_account_key.as_ref()];
|
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 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;
|
let value: u64 = 10;
|
||||||
|
|
||||||
// Build and send a transaction.
|
// Build and send a transaction.
|
||||||
|
@ -276,8 +313,8 @@ pub async fn optional<C: Deref<Target = impl Signer> + Clone>(
|
||||||
DataAccount::LEN as u64,
|
DataAccount::LEN as u64,
|
||||||
&program.id(),
|
&program.id(),
|
||||||
))
|
))
|
||||||
.signer(&data_account_keypair)
|
.signer(data_account_keypair.clone())
|
||||||
.signer(&required_keypair)
|
.signer(required_keypair.clone())
|
||||||
.accounts(OptionalInitialize {
|
.accounts(OptionalInitialize {
|
||||||
payer: Some(program.payer()),
|
payer: Some(program.payer()),
|
||||||
required: required_keypair.pubkey(),
|
required: required_keypair.pubkey(),
|
||||||
|
|
|
@ -33,6 +33,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.
|
/// Returns the account at the given address.
|
||||||
pub fn account<T: AccountDeserialize>(&self, address: Pubkey) -> Result<T, ClientError> {
|
pub fn account<T: AccountDeserialize>(&self, address: Pubkey) -> Result<T, ClientError> {
|
||||||
self.rt.block_on(self.account_internal(address))
|
self.rt.block_on(self.account_internal(address))
|
||||||
|
@ -70,7 +82,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(
|
pub fn from(
|
||||||
program_id: Pubkey,
|
program_id: Pubkey,
|
||||||
cluster: &str,
|
cluster: &str,
|
||||||
|
@ -88,9 +100,16 @@ impl<'a, C: Deref<Target = impl Signer> + Clone> RequestBuilder<'a, C> {
|
||||||
instruction_data: None,
|
instruction_data: None,
|
||||||
signers: Vec::new(),
|
signers: Vec::new(),
|
||||||
handle,
|
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(&self) -> Result<Transaction, ClientError> {
|
pub fn signed_transaction(&self) -> Result<Transaction, ClientError> {
|
||||||
self.handle.block_on(self.signed_transaction_internal())
|
self.handle.block_on(self.signed_transaction_internal())
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,8 +60,6 @@
|
||||||
//! anchor-client = { version = "0.30.1 ", features = ["async"] }
|
//! anchor-client = { version = "0.30.1 ", features = ["async"] }
|
||||||
//! ````
|
//! ````
|
||||||
|
|
||||||
use anchor_lang::solana_program::hash::Hash;
|
|
||||||
use anchor_lang::solana_program::instruction::{AccountMeta, Instruction};
|
|
||||||
use anchor_lang::solana_program::program_error::ProgramError;
|
use anchor_lang::solana_program::program_error::ProgramError;
|
||||||
use anchor_lang::solana_program::pubkey::Pubkey;
|
use anchor_lang::solana_program::pubkey::Pubkey;
|
||||||
use anchor_lang::{AccountDeserialize, Discriminator, InstructionData, ToAccountMetas};
|
use anchor_lang::{AccountDeserialize, Discriminator, InstructionData, ToAccountMetas};
|
||||||
|
@ -84,6 +82,8 @@ use solana_client::{
|
||||||
};
|
};
|
||||||
use solana_sdk::account::Account;
|
use solana_sdk::account::Account;
|
||||||
use solana_sdk::commitment_config::CommitmentConfig;
|
use solana_sdk::commitment_config::CommitmentConfig;
|
||||||
|
use solana_sdk::hash::Hash;
|
||||||
|
use solana_sdk::instruction::{AccountMeta, Instruction};
|
||||||
use solana_sdk::signature::{Signature, Signer};
|
use solana_sdk::signature::{Signature, Signer};
|
||||||
use solana_sdk::transaction::Transaction;
|
use solana_sdk::transaction::Transaction;
|
||||||
use std::iter::Map;
|
use std::iter::Map;
|
||||||
|
@ -227,18 +227,6 @@ impl<C: Deref<Target = impl Signer> + Clone> Program<C> {
|
||||||
self.cfg.payer.pubkey()
|
self.cfg.payer.pubkey()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a request builder.
|
|
||||||
pub fn request(&self) -> RequestBuilder<C> {
|
|
||||||
RequestBuilder::from(
|
|
||||||
self.program_id,
|
|
||||||
self.cfg.cluster.url(),
|
|
||||||
self.cfg.payer.clone(),
|
|
||||||
self.cfg.options,
|
|
||||||
#[cfg(not(feature = "async"))]
|
|
||||||
self.rt.handle(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn id(&self) -> Pubkey {
|
pub fn id(&self) -> Pubkey {
|
||||||
self.program_id
|
self.program_id
|
||||||
}
|
}
|
||||||
|
@ -503,23 +491,34 @@ pub enum ClientError {
|
||||||
IOError(#[from] std::io::Error),
|
IOError(#[from] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait AsSigner {
|
||||||
|
fn as_signer(&self) -> &dyn Signer;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AsSigner for Box<dyn Signer + 'a> {
|
||||||
|
fn as_signer(&self) -> &dyn Signer {
|
||||||
|
self.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// `RequestBuilder` provides a builder interface to create and send
|
/// `RequestBuilder` provides a builder interface to create and send
|
||||||
/// transactions to a cluster.
|
/// transactions to a cluster.
|
||||||
pub struct RequestBuilder<'a, C> {
|
pub struct RequestBuilder<'a, C, S: 'a> {
|
||||||
cluster: String,
|
cluster: String,
|
||||||
program_id: Pubkey,
|
program_id: Pubkey,
|
||||||
accounts: Vec<AccountMeta>,
|
accounts: Vec<AccountMeta>,
|
||||||
options: CommitmentConfig,
|
options: CommitmentConfig,
|
||||||
instructions: Vec<Instruction>,
|
instructions: Vec<Instruction>,
|
||||||
payer: C,
|
payer: C,
|
||||||
// Serialized instruction data for the target RPC.
|
|
||||||
instruction_data: Option<Vec<u8>>,
|
instruction_data: Option<Vec<u8>>,
|
||||||
signers: Vec<&'a dyn Signer>,
|
signers: Vec<S>,
|
||||||
#[cfg(not(feature = "async"))]
|
#[cfg(not(feature = "async"))]
|
||||||
handle: &'a Handle,
|
handle: &'a Handle,
|
||||||
|
_phantom: PhantomData<&'a ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: Deref<Target = impl Signer> + Clone> RequestBuilder<'a, C> {
|
// Shared implementation for all RequestBuilders
|
||||||
|
impl<'a, C: Deref<Target = impl Signer> + Clone, S: AsSigner> RequestBuilder<'a, C, S> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn payer(mut self, payer: C) -> Self {
|
pub fn payer(mut self, payer: C) -> Self {
|
||||||
self.payer = payer;
|
self.payer = payer;
|
||||||
|
@ -593,12 +592,6 @@ impl<'a, C: Deref<Target = impl Signer> + Clone> RequestBuilder<'a, C> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn signer(mut self, signer: &'a dyn Signer) -> Self {
|
|
||||||
self.signers.push(signer);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn instructions(&self) -> Result<Vec<Instruction>, ClientError> {
|
pub fn instructions(&self) -> Result<Vec<Instruction>, ClientError> {
|
||||||
let mut instructions = self.instructions.clone();
|
let mut instructions = self.instructions.clone();
|
||||||
if let Some(ix_data) = &self.instruction_data {
|
if let Some(ix_data) = &self.instruction_data {
|
||||||
|
@ -617,13 +610,14 @@ impl<'a, C: Deref<Target = impl Signer> + Clone> RequestBuilder<'a, C> {
|
||||||
latest_hash: Hash,
|
latest_hash: Hash,
|
||||||
) -> Result<Transaction, ClientError> {
|
) -> Result<Transaction, ClientError> {
|
||||||
let instructions = self.instructions()?;
|
let instructions = self.instructions()?;
|
||||||
let mut signers = self.signers.clone();
|
let signers: Vec<&dyn Signer> = self.signers.iter().map(|s| s.as_signer()).collect();
|
||||||
signers.push(&*self.payer);
|
let mut all_signers = signers;
|
||||||
|
all_signers.push(&*self.payer);
|
||||||
|
|
||||||
let tx = Transaction::new_signed_with_payer(
|
let tx = Transaction::new_signed_with_payer(
|
||||||
&instructions,
|
&instructions,
|
||||||
Some(&self.payer.pubkey()),
|
Some(&self.payer.pubkey()),
|
||||||
&signers,
|
&all_signers,
|
||||||
latest_hash,
|
latest_hash,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
ClientError, Config, EventContext, EventUnsubscriber, Program, ProgramAccountsIterator,
|
AsSigner, ClientError, Config, EventContext, EventUnsubscriber, Program,
|
||||||
RequestBuilder,
|
ProgramAccountsIterator, RequestBuilder,
|
||||||
};
|
};
|
||||||
use anchor_lang::{prelude::Pubkey, AccountDeserialize, Discriminator};
|
use anchor_lang::{prelude::Pubkey, AccountDeserialize, Discriminator};
|
||||||
use solana_client::{rpc_config::RpcSendTransactionConfig, rpc_filter::RpcFilterType};
|
use solana_client::{rpc_config::RpcSendTransactionConfig, rpc_filter::RpcFilterType};
|
||||||
|
@ -18,6 +18,22 @@ impl<'a> EventUnsubscriber<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ThreadSafeSigner: Signer + Send + Sync + 'static {
|
||||||
|
fn to_signer(&self) -> &dyn Signer;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Signer + Send + Sync + 'static> ThreadSafeSigner for T {
|
||||||
|
fn to_signer(&self) -> &dyn Signer {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsSigner for Arc<dyn ThreadSafeSigner> {
|
||||||
|
fn as_signer(&self) -> &dyn Signer {
|
||||||
|
self.to_signer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<C: Deref<Target = impl Signer> + Clone> Program<C> {
|
impl<C: Deref<Target = impl Signer> + Clone> Program<C> {
|
||||||
pub fn new(program_id: Pubkey, cfg: Config<C>) -> Result<Self, ClientError> {
|
pub fn new(program_id: Pubkey, cfg: Config<C>) -> Result<Self, ClientError> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
@ -27,6 +43,16 @@ impl<C: Deref<Target = impl Signer> + Clone> Program<C> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a threadsafe request builder
|
||||||
|
pub fn request(&self) -> RequestBuilder<'_, C, Arc<dyn ThreadSafeSigner>> {
|
||||||
|
RequestBuilder::from(
|
||||||
|
self.program_id,
|
||||||
|
self.cfg.cluster.url(),
|
||||||
|
self.cfg.payer.clone(),
|
||||||
|
self.cfg.options,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the account at the given address.
|
/// Returns the account at the given address.
|
||||||
pub async fn account<T: AccountDeserialize>(&self, address: Pubkey) -> Result<T, ClientError> {
|
pub async fn account<T: AccountDeserialize>(&self, address: Pubkey) -> Result<T, ClientError> {
|
||||||
self.account_internal(address).await
|
self.account_internal(address).await
|
||||||
|
@ -66,7 +92,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, Arc<dyn ThreadSafeSigner>> {
|
||||||
pub fn from(
|
pub fn from(
|
||||||
program_id: Pubkey,
|
program_id: Pubkey,
|
||||||
cluster: &str,
|
cluster: &str,
|
||||||
|
@ -82,9 +108,16 @@ impl<'a, C: Deref<Target = impl Signer> + Clone> RequestBuilder<'a, C> {
|
||||||
instructions: Vec::new(),
|
instructions: Vec::new(),
|
||||||
instruction_data: None,
|
instruction_data: None,
|
||||||
signers: Vec::new(),
|
signers: Vec::new(),
|
||||||
|
_phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn signer<T: ThreadSafeSigner>(mut self, signer: T) -> Self {
|
||||||
|
self.signers.push(Arc::new(signer));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn signed_transaction(&self) -> Result<Transaction, ClientError> {
|
pub async fn signed_transaction(&self) -> Result<Transaction, ClientError> {
|
||||||
self.signed_transaction_internal().await
|
self.signed_transaction_internal().await
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue