#blockchain #query #faucet-client

sui-graphql-client

Sui GraphQL RPC Client for the Sui Blockchain

2 releases

0.0.3 Mar 20, 2025
0.0.0 Sep 25, 2024

#371 in #query

Download history 3/week @ 2024-12-04 6/week @ 2024-12-11 1/week @ 2025-02-05 20/week @ 2025-02-12 4/week @ 2025-02-26 135/week @ 2025-03-19

139 downloads per month

Apache-2.0

180KB
4K SLoC

sui-graphql-client

sui-graphql-client on crates.io Documentation (latest release) Documentation (master)

The Sui GraphQL client is a client for interacting with the Sui blockchain via GraphQL. It provides a set of APIs for querying the blockchain for information such as chain identifier, reference gas price, protocol configuration, service configuration, checkpoint, epoch, executing transactions and more.

Design Principles

  1. Type Safety: The client uses the cynic library to generate types from the schema. This ensures that the queries are type-safe.
  2. Convenience: The client provides a set of APIs for common queries such as chain identifier, reference gas price, protocol configuration, service configuration, checkpoint, epoch, executing transactions and more.
  3. Custom Queries: The client provides a way to run custom queries using the cynic library.

Usage

Connecting to a GraphQL server

Instantiate a client with [Client::new(server: &str)] or use one of the predefined functions for different networks Client.

use sui_graphql_client::Client;
use anyhow::Result;

#[tokio::main]
async fn main() -> Result<()> {

   // Connect to the mainnet GraphQL server
   let client = Client::new_mainnet();
   let chain_id = client.chain_id().await?;
   println!("{:?}", chain_id);

   Ok(())
}

Requesting gas from the faucet

The client provides an API to request gas from the faucet. The request_and_wait function sends a request to the faucet and waits until the transaction is confirmed. The function returns the transaction details if the request is successful.

Example for standard devnet/testnet/local networks.

use sui_graphql_client::faucet::FaucetClient;
use sui_types::Address;

use anyhow::Result;
use std::str::FromStr;

#[tokio::main]
async fn main() -> Result<()> {
    let address = Address::from_str("SUI_ADDRESS_HERE")?;
    // Request gas from the faucet and wait until a coin is received
    // As the client is set to devnet, faucet will use the devnet faucet.
    let faucet = FaucetClient::devnet().request_and_wait(address).await?;
    if let Some(resp) = faucet {
        let coins = resp.sent;
        for coin in coins {
            println!("coin: {:?}", coin);
        }
    }

    // Request gas from the testnet faucet by explicitly setting the faucet to testnet
    let faucet_testnet = FaucetClient::testnet().request_and_wait(address).await?;
    Ok(())
}

Example for custom faucet service.

Note that this FaucetClient is explicitly designed to work with two endpoints: v1/gas, and v1/status. When passing in the custom faucet URL, skip the final endpoint and only pass in the top-level url (e.g., https://faucet.devnet.sui.io).

use sui_graphql_client::faucet::FaucetClient;
use sui_types::Address;

use anyhow::Result;
use std::str::FromStr;

#[tokio::main]
async fn main() -> Result<()> {
    let address = Address::from_str("SUI_ADDRESS_HERE")?;
    // Request gas from the faucet and wait until a coin is received
    // As the client is set to devnet, faucet will use the devnet faucet.
    let faucet = FaucetClient::new("https://myfaucet_testnet.com").request_and_wait(address).await?;
    if let Some(resp) = faucet {
        let coins = resp.sent;
        for coin in coins {
            println!("coin: {:?}", coin);
        }
    }
    Ok(())
}

Custom Queries

There are several options for running custom queries.

  1. Use a GraphQL client library of your choosing.
  2. Use the cynic's web generator that accepts as input the schema and generates the query types.
  3. Use the cynic's CLI and use the cynic querygen command to generate the query types.

Below is an example that uses the cynic querygen CLI to generate the query types from the schema and the following query:

cynic querygen --schema rpc.graphql --query custom_query.graphql

where custom_query.graphql contains the following query:

query CustomQuery($id: UInt53) {
  epoch(id: $id) {
    referenceGasPrice
    totalGasFees
    totalCheckpoints
    totalTransactions
  }
}

When using cynic and sui-graphql-client, you will need to register the schema by calling sui-graphql-client-build::register_schema in a build.rs file. See sui-graphql-client-build for more information.

The generated query types are defined below. Note that the id variable is optional (to make it mandatory change the schema to $id: Uint53! -- note the ! character which indicates a mandatory field). That means that if the id variable is not provided, the query will return the data for the last known epoch. Note that instead of using Uint53, the scalar is mapped to u64 in the library using impl_scalar(u64, schema::Uint53), thus all references to Uint53 in the schema are replaced with u64 in the code below.

#[derive(cynic::QueryVariables, Debug)]
pub struct CustomQueryVariables {
    pub id: Option<u64>,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(schema = "SCHEMA_NAME_HERE", graphql_type = "Query", variables = "CustomQueryVariables")]
pub struct CustomQuery {
    #[arguments(id: $id)]
    pub epoch: Option<Epoch>,
}

#[derive(cynic::QueryFragment, Debug)]
pub struct Epoch {
    pub epoch_id: u64,
    pub reference_gas_price: Option<BigInt>,
    pub total_gas_fees: Option<BigInt>,
    pub total_checkpoints: Option<u64>,
    pub total_transactions: Option<u64>,
}

#[derive(cynic::Scalar, Debug, Clone)]
pub struct BigInt(pub String);

The complete example is shown below:

use anyhow::Result;
use cynic::QueryBuilder;

use sui_graphql_client::{
    query_types::{schema, BigInt},
    Client,
};
use sui_types::Address;

// The data returned by the custom query.
#[derive(cynic::QueryFragment, Debug)]
#[cynic(schema = "SCHEMA_NAME_HERE", graphql_type = "Epoch")]
pub struct EpochData {
    pub epoch_id: u64,
    pub reference_gas_price: Option<BigInt>,
    pub total_gas_fees: Option<BigInt>,
    pub total_checkpoints: Option<u64>,
    pub total_transactions: Option<u64>,
}

// The variables to pass to the custom query.
// If an epoch id is passed, then the query will return the data for that epoch.
// Otherwise, the query will return the data for the last known epoch.
#[derive(cynic::QueryVariables, Debug)]
pub struct CustomVariables {
    pub id: Option<u64>,
}

// The custom query. Note that the variables need to be explicitly declared.
#[derive(cynic::QueryFragment, Debug)]
#[cynic(schema = "SCHEMA_NAME_HERE", graphql_type = "Query", variables = "CustomVariables")]
pub struct CustomQuery {
    #[arguments(id: $id)]
    pub epoch: Option<EpochData>,
}

// Custom query with no variables.
#[derive(cynic::QueryFragment, Debug)]
#[cynic(schema = "SCHEMA_NAME_HERE", graphql_type = "Query")]
pub struct ChainIdQuery {
    chain_identifier: String,
}

#[tokio::main]
async fn main() -> Result<()> {
    let mut client = Client::new_devnet();

    // Query the data for the last known epoch. Note that id variable is None, so last epoch data
    // will be returned.
    let operation = CustomQuery::build(CustomVariables { id: None });
    let response = client
        .run_query::<CustomQuery, CustomVariables>(&operation)
        .await;
    println!("{:?}", response);

    // Query the data for epoch 1.
    let epoch_id = 1;
    let operation = CustomQuery::build(CustomVariables { id: Some(epoch_id) });
    let response = client
        .run_query::<CustomQuery, CustomVariables>(&operation)
        .await;
    println!("{:?}", response);

    // When the query has no variables, just pass () as the type argument
    let operation = ChainIdQuery::build(());
    let response = client.run_query::<ChainIdQuery, ()>(&operation).await?;
    if let Some(chain_id) = response.data {
        println!("Chain ID: {}", chain_id.chain_identifier);
    }

    Ok(())
}

Dependencies

~16–28MB
~412K SLoC