Querying Data

When building frontend, we need to display all users who have issued keys, all holders of a given key, all holdings of a given user. In order to do so, we need to maintain registries for these data. There are 2 options: on-chain and off-chain.

In this example, we use the on-chain approach. It's slow but easy to implement. For all fields below with comment only required for on-chain registry, they are added for the on-chain registry. Then we can use view function to read them.


module friend_tech_addr::friend_tech {
struct Holding has key {
// only required for on-chain registry
issuer: address,
// only required for on-chain registry
holder: address,
shares: u64,
}
struct User has key {
// only required for on-chain registry
holdings: vector<object::Object<Holding>>,
}
struct Issuer has key {
addr: address,
social_media_handle: string::String,
total_issued_shares: u64,
// only required for on-chain registry
holder_holdings: vector<object::Object<Holding>>,
}
struct IssuerRegistry has key {
// only required for on-chain registry
issuers: vector<object::Object<Issuer>>
}
#[view]
public fun get_issuer_registry(
): vector<object::Object<Issuer>> acquires IssuerRegistry {
let registry = borrow_global<IssuerRegistry>(@friend_tech_addr);
registry.issuers
}
#[view]
public fun get_issuer(
issuer_obj: object::Object<Issuer>
): (
address,
string::String,
u64,
vector<object::Object<Holding>>
) acquires Issuer {
let issuer = borrow_global<Issuer>(object::object_address(&issuer_obj));
(
issuer.addr,
issuer.social_media_handle,
issuer.total_issued_shares,
issuer.holder_holdings
)
}
#[view]
public fun get_user_holdings(
user_obj: object::Object<User>
): vector<object::Object<Holding>> acquires User {
let user = borrow_global<User>(object::object_address(&user_obj));
user.holdings
}
#[view]
public fun get_holding(
holding_obj: object::Object<Holding>
): (address, address, u64) acquires Holding {
let holding = borrow_global<Holding>(object::object_address(&holding_obj));
(holding.issuer, holding.holder, holding.shares)
}
}

If we are using the off-chain approach, we can remove those fields and add these events instead. Then listen to these events in a custom indexer that writes to a database like Postgres. You can see these events include the same information, we can use them to build issuers table, key_holders table and user_holdings table. Then we can query data from the database efficiently.


module friend_tech_addr::friend_tech {
#[event]
struct IssueKeyEvent has store, drop {
issuer_addr: address,
issuer_obj: object::Object<Issuer>,
username: string::String,
}
#[event]
struct BuyKeyEvent has store, drop {
issuer_addr: address,
issuer_obj: object::Object<Issuer>,
buyer_addr: address,
buyer_user_obj: object::Object<User>,
amount: u64,
key_cost: u64,
issuer_fee: u64,
protocol_fee: u64,
total_cost: u64,
}
#[event]
struct SellKeyEvent has store, drop {
issuer_addr: address,
issuer_obj: object::Object<Issuer>,
seller_addr: address,
seller_user_obj: object::Object<User>,
amount: u64,
key_cost: u64,
issuer_fee: u64,
protocol_fee: u64,
total_cost: u64,
}
}