Skip to content

Query Circles Data

Read-only queries using the Rust crates. The examples here avoid writes; runner-dependent flows will be added later.

Fetch profiles via circles_query

use circles_rpc::{PagedQuery, QueryMethods, RpcClient};
use circles_types::{
    Address, FilterPredicate, OrderBy, PagedQueryParams, QueryParams, SortOrder,
};
use serde::Deserialize;
use reqwest::Url;
 
#[derive(Debug, Clone, Deserialize)]
struct ProfileRow {
    avatar: Address,
    name: Option<String>,
    description: Option<String>,
}
 
#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = RpcClient::http(Url::parse("https://rpc.aboutcircles.com/")?);
    let queries = QueryMethods::new(client.clone());
 
    // Direct circles_query
    let params = QueryParams {
        namespace: "circles".into(),
        table: "profiles".into(),
        columns: vec![
            "avatar".into(),
            "name".into(),
            "description".into(),
        ],
        filter: vec![FilterPredicate::equals(
            "avatar".into(),
            "0x1234567890123456789012345678901234567890",
        )
        .into()],
        order: vec![OrderBy::desc("blockNumber".into())],
        limit: Some(5),
    };
 
    let rows: Vec<ProfileRow> = queries.circles_query(params).await?;
    for row in rows {
        println!("{row:?}");
    }
 
    // Paged query convenience wrapper
    let paged = PagedQueryParams {
        namespace: "circles".into(),
        table: "profiles".into(),
        sort_order: SortOrder::DESC,
        columns: vec!["avatar".into(), "name".into(), "description".into()],
        filter: None,
        limit: 10,
    };
    let page: circles_types::PagedResult<ProfileRow> =
        queries.paged_query(paged).await?;
    println!("returned {} rows, has_more={}", page.size, page.has_more);
 
    Ok(())
}

Get total balance (read-only)

use circles_rpc::{BalanceMethods, RpcClient};
use circles_types::Address;
use reqwest::Url;
 
#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = RpcClient::http(Url::parse("https://rpc.aboutcircles.com/")?);
    let balances = BalanceMethods::new(client);
 
    let addr: Address = "0x1234567890123456789012345678901234567890".parse()?;
    let balance = balances
        .get_total_balance(addr, /* as_time_circles */ false, /* use_v2 */ true)
        .await?;
 
    println!("Balance: {balance:?}");
    Ok(())
}

Token balances table (profiles + tokens)

use circles_rpc::{QueryMethods, RpcClient};
use circles_types::{
    Address, Filter, FilterPredicate, OrderBy, QueryParams, SortOrder, TokenBalanceRow,
};
use reqwest::Url;
 
#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = RpcClient::http(Url::parse("https://rpc.aboutcircles.com/")?);
    let queries = QueryMethods::new(client);
 
    let params = QueryParams {
        namespace: "circles".into(),
        table: "tokenBalances".into(),
        columns: vec![
            "owner".into(),
            "token".into(),
            "balance".into(),
            "timestamp".into(),
        ],
        filter: vec![Filter::from(FilterPredicate::equals(
            "owner".into(),
            "0x1234567890123456789012345678901234567890",
        ))],
        order: vec![OrderBy::desc("timestamp".into())],
        limit: Some(20),
    };
 
    let rows: Vec<TokenBalanceRow> = queries.circles_query(params).await?;
    for row in rows.iter().take(5) {
        println!("token {} balance {}", row.token, row.balance);
    }
 
    Ok(())
}

Trust relations

use circles_rpc::{QueryMethods, RpcClient};
use circles_types::{FilterPredicate, OrderBy, QueryParams, TrustRelationRow};
use reqwest::Url;
 
#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = RpcClient::http(Url::parse("https://rpc.aboutcircles.com/")?);
    let queries = QueryMethods::new(client);
 
    let params = QueryParams {
        namespace: "circles".into(),
        table: "trustRelations".into(),
        columns: vec![
            "truster".into(),
            "trustee".into(),
            "limit".into(),
            "timestamp".into(),
        ],
        filter: vec![FilterPredicate::equals(
            "truster".into(),
            "0x1234567890123456789012345678901234567890",
        )
        .into()],
        order: vec![OrderBy::desc("timestamp".into())],
        limit: Some(20),
    };
 
    let rows: Vec<TrustRelationRow> = queries.circles_query(params).await?;
    for row in rows.iter().take(5) {
        println!("truster {} -> trustee {}", row.truster, row.trustee);
    }
 
    Ok(())
}

Groups and memberships

use circles_rpc::{QueryMethods, RpcClient};
use circles_types::{FilterPredicate, GroupMembershipRow, OrderBy, QueryParams};
use reqwest::Url;
 
#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = RpcClient::http(Url::parse("https://rpc.aboutcircles.com/")?);
    let queries = QueryMethods::new(client);
 
    // Group lookup
    let groups_params = QueryParams {
        namespace: "circles".into(),
        table: "groups".into(),
        columns: vec!["group".into(), "name".into(), "symbol".into()],
        filter: vec![],
        order: vec![OrderBy::asc("group".into())],
        limit: Some(10),
    };
    let groups: Vec<circles_types::GroupRow> = queries.circles_query(groups_params).await?;
    println!("groups: {}", groups.len());
 
    // Memberships for a given avatar
    let memberships_params = QueryParams {
        namespace: "circles".into(),
        table: "groupMemberships".into(),
        columns: vec!["group".into(), "member".into(), "timestamp".into()],
        filter: vec![FilterPredicate::equals(
            "member".into(),
            "0x1234567890123456789012345678901234567890",
        )
        .into()],
        order: vec![OrderBy::desc("timestamp".into())],
        limit: Some(20),
    };
    let memberships: Vec<GroupMembershipRow> =
        queries.circles_query(memberships_params).await?;
    println!("memberships: {}", memberships.len());
 
    Ok(())
}

Event fetch and decode (HTTP)

use circles_rpc::{EventsMethods, RpcClient};
use reqwest::Url;
 
#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = RpcClient::http(Url::parse("https://rpc.aboutcircles.com/")?);
    let events = EventsMethods::new(client);
 
    // Fetch and parse Circles events from block 0 to latest
    let parsed = events
        .circles_events(None, /* from_block */ 0, /* to_block */ None, None)
        .await?;
 
    for evt in parsed.iter().take(5) {
        println!("event: {:?}", evt.event_type);
    }
 
    Ok(())
}

Websocket subscriptions are supported behind the circles-rpc ws feature; enable it in Cargo and use EventsMethods::subscribe_parsed_events for streaming.