substrate_api_client/api/rpc_api/
author.rsuse crate::{
api::{rpc_api::events::FetchEvents, Error, Result},
rpc::{HandleSubscription, Request, Subscribe},
Api, ExtrinsicReport, TransactionStatus, XtStatus,
};
use ac_compose_macros::rpc_params;
use ac_primitives::{config::Config, UncheckedExtrinsicV4};
#[cfg(not(feature = "sync-api"))]
use alloc::boxed::Box;
use codec::{Decode, Encode};
use log::*;
use serde::de::DeserializeOwned;
use sp_core::Bytes;
use sp_runtime::traits::Hash as HashTrait;
pub type TransactionSubscriptionFor<Client, Hash> =
<Client as Subscribe>::Subscription<TransactionStatus<Hash, Hash>>;
#[maybe_async::maybe_async(?Send)]
pub trait SubmitExtrinsic {
type Hash;
async fn submit_extrinsic<Address, Call, Signature, SignedExtra>(
&self,
extrinsic: UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>,
) -> Result<Self::Hash>
where
Address: Encode,
Call: Encode,
Signature: Encode,
SignedExtra: Encode;
async fn submit_opaque_extrinsic(&self, encoded_extrinsic: &Bytes) -> Result<Self::Hash>;
}
#[maybe_async::maybe_async(?Send)]
impl<T, Client> SubmitExtrinsic for Api<T, Client>
where
T: Config,
Client: Request,
{
type Hash = T::Hash;
async fn submit_extrinsic<Address, Call, Signature, SignedExtra>(
&self,
extrinsic: UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>,
) -> Result<Self::Hash>
where
Address: Encode,
Call: Encode,
Signature: Encode,
SignedExtra: Encode,
{
self.submit_opaque_extrinsic(&extrinsic.encode().into()).await
}
async fn submit_opaque_extrinsic(&self, encoded_extrinsic: &Bytes) -> Result<Self::Hash> {
let hex_encoded_xt = rpc_params![encoded_extrinsic];
debug!("sending extrinsic: {:?}", hex_encoded_xt);
let xt_hash = self.client().request("author_submitExtrinsic", hex_encoded_xt).await?;
Ok(xt_hash)
}
}
#[maybe_async::maybe_async(?Send)]
pub trait SubmitAndWatch {
type Client: Subscribe;
type Hash: DeserializeOwned + Decode + Encode;
async fn submit_and_watch_extrinsic<Address, Call, Signature, SignedExtra>(
&self,
extrinsic: UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>,
) -> Result<TransactionSubscriptionFor<Self::Client, Self::Hash>>
where
Address: Encode,
Call: Encode,
Signature: Encode,
SignedExtra: Encode;
async fn submit_and_watch_opaque_extrinsic(
&self,
encoded_extrinsic: &Bytes,
) -> Result<TransactionSubscriptionFor<Self::Client, Self::Hash>>;
async fn submit_and_watch_extrinsic_until<Address, Call, Signature, SignedExtra>(
&self,
extrinsic: UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>,
watch_until: XtStatus,
) -> Result<ExtrinsicReport<Self::Hash>>
where
Address: Encode,
Call: Encode,
Signature: Encode,
SignedExtra: Encode;
async fn submit_and_watch_opaque_extrinsic_until(
&self,
encoded_extrinsic: &Bytes,
watch_until: XtStatus,
) -> Result<ExtrinsicReport<Self::Hash>>;
async fn submit_and_watch_extrinsic_until_without_events<
Address,
Call,
Signature,
SignedExtra,
>(
&self,
extrinsic: UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>,
watch_until: XtStatus,
) -> Result<ExtrinsicReport<Self::Hash>>
where
Address: Encode,
Call: Encode,
Signature: Encode,
SignedExtra: Encode;
async fn submit_and_watch_opaque_extrinsic_until_without_events(
&self,
encoded_extrinsic: &Bytes,
watch_until: XtStatus,
) -> Result<ExtrinsicReport<Self::Hash>>;
async fn populate_events(&self, report: &mut ExtrinsicReport<Self::Hash>) -> Result<()>;
}
#[maybe_async::maybe_async(?Send)]
impl<T, Client> SubmitAndWatch for Api<T, Client>
where
T: Config,
Client: Subscribe + Request,
{
type Client = Client;
type Hash = T::Hash;
async fn submit_and_watch_extrinsic<Address, Call, Signature, SignedExtra>(
&self,
extrinsic: UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>,
) -> Result<TransactionSubscriptionFor<Self::Client, Self::Hash>>
where
Address: Encode,
Call: Encode,
Signature: Encode,
SignedExtra: Encode,
{
self.submit_and_watch_opaque_extrinsic(&extrinsic.encode().into()).await
}
async fn submit_and_watch_opaque_extrinsic(
&self,
encoded_extrinsic: &Bytes,
) -> Result<TransactionSubscriptionFor<Self::Client, Self::Hash>> {
self.client()
.subscribe(
"author_submitAndWatchExtrinsic",
rpc_params![encoded_extrinsic],
"author_unsubmitAndWatchExtrinsic",
)
.await
.map_err(|e| e.into())
}
async fn submit_and_watch_extrinsic_until<Address, Call, Signature, SignedExtra>(
&self,
extrinsic: UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>,
watch_until: XtStatus,
) -> Result<ExtrinsicReport<Self::Hash>>
where
Address: Encode,
Call: Encode,
Signature: Encode,
SignedExtra: Encode,
{
self.submit_and_watch_opaque_extrinsic_until(&extrinsic.encode().into(), watch_until)
.await
}
async fn submit_and_watch_opaque_extrinsic_until(
&self,
encoded_extrinsic: &Bytes,
watch_until: XtStatus,
) -> Result<ExtrinsicReport<Self::Hash>> {
let mut report = self
.submit_and_watch_opaque_extrinsic_until_without_events(encoded_extrinsic, watch_until)
.await?;
if watch_until < XtStatus::InBlock {
return Ok(report)
}
self.populate_events(&mut report).await?;
report.check_events_for_dispatch_error(self.metadata())?;
return Ok(report);
}
async fn populate_events(&self, report: &mut ExtrinsicReport<Self::Hash>) -> Result<()> {
if report.events.is_some() {
return Err(Error::EventsAlreadyPresent)
}
let block_hash = report.block_hash.ok_or(Error::BlockHashNotFound)?;
let extrinsic_events =
self.fetch_events_for_extrinsic(block_hash, report.extrinsic_hash).await?;
report.add_events(extrinsic_events);
Ok(())
}
async fn submit_and_watch_extrinsic_until_without_events<
Address,
Call,
Signature,
SignedExtra,
>(
&self,
extrinsic: UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>,
watch_until: XtStatus,
) -> Result<ExtrinsicReport<Self::Hash>>
where
Address: Encode,
Call: Encode,
Signature: Encode,
SignedExtra: Encode,
{
self.submit_and_watch_opaque_extrinsic_until_without_events(
&extrinsic.encode().into(),
watch_until,
)
.await
}
async fn submit_and_watch_opaque_extrinsic_until_without_events(
&self,
encoded_extrinsic: &Bytes,
watch_until: XtStatus,
) -> Result<ExtrinsicReport<Self::Hash>> {
let tx_hash = T::Hasher::hash(encoded_extrinsic);
let mut subscription: TransactionSubscriptionFor<Self::Client, Self::Hash> =
self.submit_and_watch_opaque_extrinsic(encoded_extrinsic).await?;
while let Some(transaction_status) = subscription.next().await {
let transaction_status = transaction_status?;
match transaction_status.is_expected() {
Ok(_) =>
if transaction_status.reached_status(watch_until) {
subscription.unsubscribe().await?;
let block_hash = transaction_status.get_maybe_block_hash();
return Ok(ExtrinsicReport::new(
tx_hash,
block_hash.copied(),
transaction_status,
None,
))
},
Err(e) => {
subscription.unsubscribe().await?;
return Err(e)
},
}
}
Err(Error::NoStream)
}
}