use crate::{
AsGear, ProgramStateChanges, Result, UserMessageSentFilter, UserMessageSentSubscription,
config::GearConfig, gear::Event,
};
use core::ops::{Deref, DerefMut};
use futures::{Stream, prelude::*};
use jsonrpsee::{client_transport::ws::Url, ws_client::WsClientBuilder};
use sp_core::H256;
use std::{borrow::Cow, time::Duration};
use subxt::{
OnlineClient,
backend::rpc::RpcClient,
blocks::Block,
ext::subxt_rpcs::{LegacyRpcMethods, rpc_params},
};
const ONE_HUNDRED_MEGABYTES: u32 = 100 * 1024 * 1024;
#[derive(Debug, Clone)]
pub struct Api {
rpc: RpcClient,
legacy_methods: LegacyRpcMethods<GearConfig>,
client: OnlineClient<GearConfig>,
}
#[derive(Debug, Clone, Default)]
pub struct ApiBuilder<'a> {
uri: Option<Cow<'a, str>>,
timeout: Option<Duration>,
}
impl Api {
pub const VARA_ENDPOINT: &str = "wss://rpc.vara.network:443";
pub const VARA_TESTNET_ENDPOINT: &str = "wss://testnet.vara.network:443";
pub const DEV_ENDPOINT: &str = "ws://127.0.0.1:9944";
pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60);
pub const fn builder<'a>() -> ApiBuilder<'a> {
ApiBuilder {
uri: None,
timeout: None,
}
}
}
impl<'a> ApiBuilder<'a> {
pub fn uri(mut self, uri: impl Into<Cow<'a, str>>) -> Self {
self.uri = Some(uri.into());
self
}
pub fn dev(self) -> Self {
self.uri(Api::DEV_ENDPOINT)
}
pub fn testnet(self) -> Self {
self.uri(Api::VARA_TESTNET_ENDPOINT)
}
pub const fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
self
}
pub async fn build(self) -> Result<Api> {
Api::from_rpc_client(self.rpc_client().await?).await
}
async fn rpc_client(self) -> Result<RpcClient> {
let uri = self.uri.as_ref().map_or(Api::VARA_ENDPOINT, Cow::as_ref);
let uri = Url::parse(uri)?;
let timeout = self.timeout.unwrap_or(Api::DEFAULT_TIMEOUT);
let client = WsClientBuilder::new()
.max_request_size(ONE_HUNDRED_MEGABYTES)
.connection_timeout(timeout)
.request_timeout(timeout)
.build(uri)
.await?;
Ok(RpcClient::new(client))
}
}
impl Api {
pub async fn new(uri: &str) -> Result<Self> {
Self::builder().uri(uri).build().await
}
pub async fn from_rpc_client(rpc: RpcClient) -> Result<Self> {
let legacy_methods = LegacyRpcMethods::new(rpc.clone());
let client = OnlineClient::from_rpc_client(rpc.clone()).await?;
Ok(Self {
rpc,
client,
legacy_methods,
})
}
fn blocks_to_events(
blocks: impl Stream<
Item = Result<Block<GearConfig, subxt::OnlineClient<GearConfig>>, subxt::Error>,
>,
) -> impl Stream<Item = Result<Event>> {
blocks
.then(|block| async move {
Ok::<_, subxt::Error>(stream::iter(
block?.events().await?.iter().collect::<Vec<_>>(),
))
})
.try_flatten()
.map(|event| event?.as_gear())
}
pub async fn subscribe_all_events(&self) -> Result<impl Stream<Item = Result<Event>>> {
Ok(Self::blocks_to_events(self.blocks().subscribe_all().await?))
}
pub async fn subscribe_finalized_events(&self) -> Result<impl Stream<Item = Result<Event>>> {
Ok(Self::blocks_to_events(
self.blocks().subscribe_finalized().await?,
))
}
pub async fn subscribe_program_state_changes(
&self,
program_ids: Option<Vec<H256>>,
) -> Result<ProgramStateChanges> {
let subscription = self
.rpc()
.subscribe(
"gear_subscribeProgramStateChanges",
rpc_params![program_ids],
"gear_unsubscribeProgramStateChanges",
)
.await?;
Ok(ProgramStateChanges::new(subscription))
}
pub async fn subscribe_user_message_sent(
&self,
filter: UserMessageSentFilter,
) -> Result<UserMessageSentSubscription> {
let subscription = self
.rpc()
.subscribe(
"gear_subscribeUserMessageSent",
rpc_params![filter],
"gear_unsubscribeUserMessageSent",
)
.await?;
Ok(UserMessageSentSubscription::new(subscription))
}
pub fn rpc(&self) -> &RpcClient {
&self.rpc
}
pub fn legacy(&self) -> &LegacyRpcMethods<GearConfig> {
&self.legacy_methods
}
}
impl Deref for Api {
type Target = OnlineClient<GearConfig>;
fn deref(&self) -> &Self::Target {
&self.client
}
}
impl DerefMut for Api {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.client
}
}