Skip to content

Commit

Permalink
feat: Client primitives (FuelLabs#1144)
Browse files Browse the repository at this point in the history
Related issues:
- FuelLabs#1121 

This PR introduces a module of Rust types used by the `Client` in the
`fuel-core-client` crate. These types are referred to as client types
and they are used to compose the `Client` interface. All function
signatures in the Client interface now use only client types and Rust
primitives. All references to GraphQL schema types are removed from the
interface's function signatures.

Specifically, this PR introduces the following client types:
- `Balance`
- `Block` (as well as its constituent parts `Header`, `Consensus`,
`Genesis`, and `PoAConsensus`)
- `ChainInfo`
- `Coin`, `MessageCoin`, and `CoinType`
- `ConsensusParameters`
- `Contract` and `ContractBalance`
- `MerkleProof`
- `Message` and `MessageProof`
- `NodeInfo` 

While GraphQL types are used inside the client methods, all types
returned by the GraphQL server are transformed back into client types so
that the `Client` returns only these client types. This is enabled by
defining a number of `From` traits, that transform the GraphQL schema
type into the client type.

---------

Co-authored-by: green <xgreenx9999@gmail.com>
  • Loading branch information
Brandon Vrooman and xgreenx authored May 22, 2023
1 parent 1ab29d9 commit 7be707f
Show file tree
Hide file tree
Showing 35 changed files with 883 additions and 303 deletions.
33 changes: 17 additions & 16 deletions bin/e2e-test-client/src/test_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ use anyhow::{
};
use fuel_core_chain_config::ContractConfig;
use fuel_core_client::client::{
schema::coins::CoinType,
types::TransactionStatus,
pagination::{
PageDirection,
PaginationRequest,
},
types::{
CoinType,
TransactionStatus,
},
FuelClient,
PageDirection,
PaginationRequest,
};
use fuel_core_types::{
fuel_crypto::PublicKey,
Expand Down Expand Up @@ -123,10 +127,7 @@ impl Wallet {
.await?
.results;
// check if page has the utxos we're looking for
if results
.iter()
.any(|coin| UtxoId::from(coin.utxo_id.clone()) == utxo_id)
{
if results.iter().any(|coin| coin.utxo_id == utxo_id) {
return Ok(true)
}
}
Expand Down Expand Up @@ -164,11 +165,11 @@ impl Wallet {
if let CoinType::Coin(coin) = coin {
tx.add_unsigned_coin_input(
self.secret,
coin.utxo_id.clone().into(),
coin.amount.clone().into(),
coin.asset_id.clone().into(),
coin.utxo_id,
coin.amount,
coin.asset_id,
Default::default(),
coin.maturity.clone().into(),
coin.maturity.into(),
);
}
}
Expand Down Expand Up @@ -236,11 +237,11 @@ impl Wallet {
if let CoinType::Coin(coin) = coin {
tx.add_unsigned_coin_input(
self.secret,
coin.utxo_id.clone().into(),
coin.amount.clone().into(),
coin.asset_id.clone().into(),
coin.utxo_id,
coin.amount,
coin.asset_id,
Default::default(),
coin.maturity.clone().into(),
coin.maturity.into(),
);
}
}
Expand Down
95 changes: 48 additions & 47 deletions crates/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,17 @@ use fuel_core_types::{
#[cfg(feature = "subscriptions")]
use futures::StreamExt;
use itertools::Itertools;
use pagination::{
PageDirection,
PaginatedResult,
PaginationRequest,
};
use reqwest::cookie::CookieStore;
use schema::{
balance::BalanceArgs,
block::BlockByIdArgs,
coins::{
Coin,
CoinByIdArgs,
},
contract::{
Contract,
ContractByIdArgs,
},
coins::CoinByIdArgs,
contract::ContractByIdArgs,
tx::{
TxArg,
TxIdArgs,
Expand All @@ -69,14 +68,8 @@ use schema::{
SetSingleSteppingArgs,
StartTx,
StartTxArgs,
TransactionId,
U64,
};
pub use schema::{
PageDirection,
PaginatedResult,
PaginationRequest,
};
#[cfg(feature = "subscriptions")]
use std::future;
use std::{
Expand Down Expand Up @@ -105,6 +98,7 @@ use self::schema::{
message::MessageProofArgs,
};

pub mod pagination;
pub mod schema;
pub mod types;

Expand Down Expand Up @@ -311,14 +305,14 @@ impl FuelClient {
self.query(query).await.map(|r| r.health)
}

pub async fn node_info(&self) -> io::Result<schema::node_info::NodeInfo> {
pub async fn node_info(&self) -> io::Result<types::NodeInfo> {
let query = schema::node_info::QueryNodeInfo::build(());
self.query(query).await.map(|r| r.node_info)
self.query(query).await.map(|r| r.node_info.into())
}

pub async fn chain_info(&self) -> io::Result<schema::chain::ChainInfo> {
pub async fn chain_info(&self) -> io::Result<types::ChainInfo> {
let query = schema::chain::ChainQuery::build(());
self.query(query).await.map(|r| r.chain)
self.query(query).await.map(|r| r.chain.into())
}

/// Default dry run, matching the exact configuration as the node
Expand All @@ -345,13 +339,16 @@ impl FuelClient {
.collect()
}

pub async fn submit(&self, tx: &Transaction) -> io::Result<TransactionId> {
pub async fn submit(
&self,
tx: &Transaction,
) -> io::Result<types::scalars::TransactionId> {
let tx = tx.clone().to_bytes();
let query = schema::tx::Submit::build(TxArg {
tx: HexString(Bytes(tx)),
});

let id = self.query(query).await.map(|r| r.submit)?.id;
let id = self.query(query).await.map(|r| r.submit)?.id.into();
Ok(id)
}

Expand Down Expand Up @@ -554,7 +551,7 @@ impl FuelClient {
} else {
Err(io::Error::new(
io::ErrorKind::Other,
format!("Failed to get status for transaction {:?}", status_result),
format!("Failed to get status for transaction {status_result:?}"),
))
}
}
Expand Down Expand Up @@ -596,9 +593,9 @@ impl FuelClient {
vec.into_iter().map(TryInto::<Receipt>::try_into).collect();
vec
})
.transpose();
.transpose()?;

Ok(receipts?)
Ok(receipts)
}

pub async fn produce_blocks(
Expand All @@ -617,25 +614,22 @@ impl FuelClient {
Ok(new_height.into())
}

pub async fn block(&self, id: &str) -> io::Result<Option<schema::block::Block>> {
pub async fn block(&self, id: &str) -> io::Result<Option<types::Block>> {
let query = schema::block::BlockByIdQuery::build(BlockByIdArgs {
id: Some(id.parse()?),
});

let block = self.query(query).await?.block;
let block = self.query(query).await?.block.map(Into::into);

Ok(block)
}

pub async fn block_by_height(
&self,
height: u64,
) -> io::Result<Option<schema::block::Block>> {
pub async fn block_by_height(&self, height: u64) -> io::Result<Option<types::Block>> {
let query = schema::block::BlockByHeightQuery::build(BlockByHeightArgs {
height: Some(U64(height)),
});

let block = self.query(query).await?.block;
let block = self.query(query).await?.block.map(Into::into);

Ok(block)
}
Expand All @@ -644,19 +638,19 @@ impl FuelClient {
pub async fn blocks(
&self,
request: PaginationRequest<String>,
) -> io::Result<PaginatedResult<schema::block::Block, String>> {
) -> io::Result<PaginatedResult<types::Block, String>> {
let query = schema::block::BlocksQuery::build(request.into());

let blocks = self.query(query).await?.blocks.into();

Ok(blocks)
}

pub async fn coin(&self, id: &str) -> io::Result<Option<Coin>> {
pub async fn coin(&self, id: &str) -> io::Result<Option<types::Coin>> {
let query = schema::coins::CoinByIdQuery::build(CoinByIdArgs {
utxo_id: id.parse()?,
});
let coin = self.query(query).await?.coin;
let coin = self.query(query).await?.coin.map(Into::into);
Ok(coin)
}

Expand All @@ -666,7 +660,7 @@ impl FuelClient {
owner: &str,
asset_id: Option<&str>,
request: PaginationRequest<String>,
) -> io::Result<PaginatedResult<schema::coins::Coin, String>> {
) -> io::Result<PaginatedResult<types::Coin, String>> {
let owner: schema::Address = owner.parse()?;
let asset_id: schema::AssetId = match asset_id {
Some(asset_id) => asset_id.parse()?,
Expand All @@ -685,7 +679,7 @@ impl FuelClient {
spend_query: Vec<(&str, u64, Option<u64>)>,
// (Utxos, Messages Nonce)
excluded_ids: Option<(Vec<&str>, Vec<&str>)>,
) -> io::Result<Vec<Vec<schema::coins::CoinType>>> {
) -> io::Result<Vec<Vec<types::CoinType>>> {
let owner: schema::Address = owner.parse()?;
let spend_query: Vec<SpendQueryElementInput> = spend_query
.iter()
Expand All @@ -703,15 +697,21 @@ impl FuelClient {
(owner, spend_query, excluded_ids).into(),
);

let coins_per_asset = self.query(query).await?.coins_to_spend;
let coins_per_asset = self
.query(query)
.await?
.coins_to_spend
.into_iter()
.map(|v| v.into_iter().map(Into::into).collect::<Vec<_>>())
.collect::<Vec<_>>();
Ok(coins_per_asset)
}

pub async fn contract(&self, id: &str) -> io::Result<Option<Contract>> {
pub async fn contract(&self, id: &str) -> io::Result<Option<types::Contract>> {
let query = schema::contract::ContractByIdQuery::build(ContractByIdArgs {
id: id.parse()?,
});
let contract = self.query(query).await?.contract;
let contract = self.query(query).await?.contract.map(Into::into);
Ok(contract)
}

Expand All @@ -731,8 +731,9 @@ impl FuelClient {
asset: asset_id,
});

let balance = self.query(query).await.unwrap().contract_balance.amount;
Ok(balance.into())
let balance: types::ContractBalance =
self.query(query).await?.contract_balance.into();
Ok(balance.amount)
}

pub async fn balance(&self, owner: &str, asset_id: Option<&str>) -> io::Result<u64> {
Expand All @@ -742,16 +743,16 @@ impl FuelClient {
None => schema::AssetId::default(),
};
let query = schema::balance::BalanceQuery::build(BalanceArgs { owner, asset_id });
let balance = self.query(query).await?.balance;
Ok(balance.amount.into())
let balance: types::Balance = self.query(query).await?.balance.into();
Ok(balance.amount)
}

// Retrieve a page of balances by their owner
pub async fn balances(
&self,
owner: &str,
request: PaginationRequest<String>,
) -> io::Result<PaginatedResult<schema::balance::Balance, String>> {
) -> io::Result<PaginatedResult<types::Balance, String>> {
let owner: schema::Address = owner.parse()?;
let query = schema::balance::BalancesQuery::build((owner, request).into());

Expand All @@ -763,7 +764,7 @@ impl FuelClient {
&self,
contract: &str,
request: PaginationRequest<String>,
) -> io::Result<PaginatedResult<schema::contract::ContractBalance, String>> {
) -> io::Result<PaginatedResult<types::ContractBalance, String>> {
let contract_id: schema::ContractId = contract.parse()?;
let query =
schema::contract::ContractBalancesQuery::build((contract_id, request).into());
Expand All @@ -777,7 +778,7 @@ impl FuelClient {
&self,
owner: Option<&str>,
request: PaginationRequest<String>,
) -> io::Result<PaginatedResult<schema::message::Message, String>> {
) -> io::Result<PaginatedResult<types::Message, String>> {
let owner: Option<schema::Address> =
owner.map(|owner| owner.parse()).transpose()?;
let query = schema::message::OwnedMessageQuery::build((owner, request).into());
Expand All @@ -794,7 +795,7 @@ impl FuelClient {
message_id: &str,
commit_block_id: Option<&str>,
commit_block_height: Option<BlockHeight>,
) -> io::Result<Option<schema::message::MessageProof>> {
) -> io::Result<Option<types::MessageProof>> {
let transaction_id: schema::TransactionId = transaction_id.parse()?;
let message_id: schema::MessageId = message_id.parse()?;
let commit_block_id: Option<schema::BlockId> = commit_block_id
Expand All @@ -808,7 +809,7 @@ impl FuelClient {
commit_block_height,
});

let proof = self.query(query).await?.message_proof;
let proof = self.query(query).await?.message_proof.map(Into::into);

Ok(proof)
}
Expand Down
24 changes: 24 additions & 0 deletions crates/client/src/client/pagination.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/// Specifies the direction of a paginated query
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum PageDirection {
Forward,
Backward,
}

/// Used to parameterize paginated queries
#[derive(Clone, Debug)]
pub struct PaginationRequest<T> {
/// The cursor returned from a previous query to indicate an offset
pub cursor: Option<T>,
/// The number of results to take
pub results: usize,
/// The direction of the query (e.g. asc, desc order).
pub direction: PageDirection,
}

pub struct PaginatedResult<T, C> {
pub cursor: Option<C>,
pub results: Vec<T>,
pub has_next_page: bool,
pub has_previous_page: bool,
}
Loading

0 comments on commit 7be707f

Please sign in to comment.