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-rpcwsfeature; enable it in Cargo and useEventsMethods::subscribe_parsed_eventsfor streaming.