use std::future::{Future, IntoFuture};
use std::marker::PhantomData;
use std::pin::Pin;
use std::sync::Arc;
use serde::de::DeserializeOwned;
use crate::error::Error;
use crate::types::{
AccessKeyListView, AccountBalance, AccountId, AccountView, BlockReference, CryptoHash, Finality,
};
use super::rpc::RpcClient;
pub struct BalanceQuery {
rpc: Arc<RpcClient>,
account_id: AccountId,
block_ref: BlockReference,
}
impl BalanceQuery {
pub(crate) fn new(rpc: Arc<RpcClient>, account_id: AccountId) -> Self {
Self {
rpc,
account_id,
block_ref: BlockReference::default(),
}
}
pub fn at_block(mut self, height: u64) -> Self {
self.block_ref = BlockReference::Height(height);
self
}
pub fn at_block_hash(mut self, hash: CryptoHash) -> Self {
self.block_ref = BlockReference::Hash(hash);
self
}
pub fn finality(mut self, finality: Finality) -> Self {
self.block_ref = BlockReference::Finality(finality);
self
}
}
impl IntoFuture for BalanceQuery {
type Output = Result<AccountBalance, Error>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
let view = self
.rpc
.view_account(&self.account_id, self.block_ref)
.await?;
Ok(AccountBalance::from(view))
})
}
}
pub struct AccountQuery {
rpc: Arc<RpcClient>,
account_id: AccountId,
block_ref: BlockReference,
}
impl AccountQuery {
pub(crate) fn new(rpc: Arc<RpcClient>, account_id: AccountId) -> Self {
Self {
rpc,
account_id,
block_ref: BlockReference::default(),
}
}
pub fn at_block(mut self, height: u64) -> Self {
self.block_ref = BlockReference::Height(height);
self
}
pub fn at_block_hash(mut self, hash: CryptoHash) -> Self {
self.block_ref = BlockReference::Hash(hash);
self
}
pub fn finality(mut self, finality: Finality) -> Self {
self.block_ref = BlockReference::Finality(finality);
self
}
}
impl IntoFuture for AccountQuery {
type Output = Result<AccountView, Error>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
let view = self
.rpc
.view_account(&self.account_id, self.block_ref)
.await?;
Ok(view)
})
}
}
pub struct AccountExistsQuery {
rpc: Arc<RpcClient>,
account_id: AccountId,
block_ref: BlockReference,
}
impl AccountExistsQuery {
pub(crate) fn new(rpc: Arc<RpcClient>, account_id: AccountId) -> Self {
Self {
rpc,
account_id,
block_ref: BlockReference::default(),
}
}
pub fn at_block(mut self, height: u64) -> Self {
self.block_ref = BlockReference::Height(height);
self
}
pub fn at_block_hash(mut self, hash: CryptoHash) -> Self {
self.block_ref = BlockReference::Hash(hash);
self
}
pub fn finality(mut self, finality: Finality) -> Self {
self.block_ref = BlockReference::Finality(finality);
self
}
}
impl IntoFuture for AccountExistsQuery {
type Output = Result<bool, Error>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
match self
.rpc
.view_account(&self.account_id, self.block_ref)
.await
{
Ok(_) => Ok(true),
Err(crate::error::RpcError::AccountNotFound(_)) => Ok(false),
Err(e) => Err(e.into()),
}
})
}
}
pub struct AccessKeysQuery {
rpc: Arc<RpcClient>,
account_id: AccountId,
block_ref: BlockReference,
}
impl AccessKeysQuery {
pub(crate) fn new(rpc: Arc<RpcClient>, account_id: AccountId) -> Self {
Self {
rpc,
account_id,
block_ref: BlockReference::default(),
}
}
pub fn at_block(mut self, height: u64) -> Self {
self.block_ref = BlockReference::Height(height);
self
}
pub fn at_block_hash(mut self, hash: CryptoHash) -> Self {
self.block_ref = BlockReference::Hash(hash);
self
}
pub fn finality(mut self, finality: Finality) -> Self {
self.block_ref = BlockReference::Finality(finality);
self
}
}
impl IntoFuture for AccessKeysQuery {
type Output = Result<AccessKeyListView, Error>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
let list = self
.rpc
.view_access_key_list(&self.account_id, self.block_ref)
.await?;
Ok(list)
})
}
}
pub struct ViewCall<T> {
rpc: Arc<RpcClient>,
contract_id: AccountId,
method: String,
args: Vec<u8>,
block_ref: BlockReference,
_phantom: PhantomData<T>,
}
impl<T> ViewCall<T> {
pub(crate) fn new(rpc: Arc<RpcClient>, contract_id: AccountId, method: String) -> Self {
Self {
rpc,
contract_id,
method,
args: vec![],
block_ref: BlockReference::default(),
_phantom: PhantomData,
}
}
pub fn args<A: serde::Serialize>(mut self, args: A) -> Self {
self.args = serde_json::to_vec(&args).unwrap_or_default();
self
}
pub fn args_raw(mut self, args: Vec<u8>) -> Self {
self.args = args;
self
}
pub fn args_borsh<A: borsh::BorshSerialize>(mut self, args: A) -> Self {
self.args = borsh::to_vec(&args).unwrap_or_default();
self
}
pub fn at_block(mut self, height: u64) -> Self {
self.block_ref = BlockReference::Height(height);
self
}
pub fn at_block_hash(mut self, hash: CryptoHash) -> Self {
self.block_ref = BlockReference::Hash(hash);
self
}
pub fn finality(mut self, finality: Finality) -> Self {
self.block_ref = BlockReference::Finality(finality);
self
}
pub fn borsh(self) -> ViewCallBorsh<T> {
ViewCallBorsh {
rpc: self.rpc,
contract_id: self.contract_id,
method: self.method,
args: self.args,
block_ref: self.block_ref,
_phantom: PhantomData,
}
}
}
impl<T: DeserializeOwned + Send + 'static> IntoFuture for ViewCall<T> {
type Output = Result<T, Error>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
let result = self
.rpc
.view_function(&self.contract_id, &self.method, &self.args, self.block_ref)
.await?;
Ok(result.json()?)
})
}
}
pub struct ViewCallBorsh<T> {
rpc: Arc<RpcClient>,
contract_id: AccountId,
method: String,
args: Vec<u8>,
block_ref: BlockReference,
_phantom: PhantomData<T>,
}
impl<T: borsh::BorshDeserialize + Send + 'static> IntoFuture for ViewCallBorsh<T> {
type Output = Result<T, Error>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
let result = self
.rpc
.view_function(&self.contract_id, &self.method, &self.args, self.block_ref)
.await?;
result.borsh().map_err(|e| Error::Borsh(e.to_string()))
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_balance_query_builder() {
let rpc = Arc::new(RpcClient::new("http://localhost:3030"));
let account_id: AccountId = "alice.testnet".parse().unwrap();
let query = BalanceQuery::new(rpc.clone(), account_id.clone());
assert_eq!(query.block_ref, BlockReference::default());
let query = BalanceQuery::new(rpc.clone(), account_id.clone()).at_block(12345);
assert_eq!(query.block_ref, BlockReference::Height(12345));
let query = BalanceQuery::new(rpc.clone(), account_id).finality(Finality::Optimistic);
assert_eq!(
query.block_ref,
BlockReference::Finality(Finality::Optimistic)
);
}
}