Skip to content

Braga has been retired. The next public testnet is coming in September 2026. Read the announcement →

createPublicClient

createPublicClient<transport, chain, accountOrAddress, rpcSchema>(parameters): object

Defined in: src/clients/createPublicClient.ts:45

Creates a Public Client with a given Transport configured for a Chain.

A Public Client is an interface to “public” Ethereum JSON-RPC API, Arkiv JSON-RPC API, and Cheesecake JSON-RPC API methods such as retrieving block numbers, transactions, reading from smart contracts, etc through Public Actions.

transport extends Transport

chain extends Chain | undefined = undefined

accountOrAddress extends `0x${string}` | Account | undefined = undefined

rpcSchema extends RpcSchema | undefined = ArkivRpcSchema

Configuration object for the public client (chain, transport, etc.)

A Arkiv Public Client. PublicArkivClient

getBlockTiming: () => Promise<{ blockDuration: number; currentBlock: bigint; currentBlockTime: number; }>

Returns the current block timing.

Promise<{ blockDuration: number; currentBlock: bigint; currentBlockTime: number; }>

The current block timing. GetBlockTimingReturnType

import { createPublicClient } from "@arkiv-network/sdk"
import { cheesecake } from "@arkiv-network/sdk/chains"
import { http } from "viem"
const client = createPublicClient({
chain: cheesecake,
transport: http(),
})
const blockTiming = await client.getBlockTiming()
// {
// currentBlock: 10n, // block number
// currentBlockTime: 1234567890, // block timestamp
// blockDuration: 2, // in seconds
// }

getEntity: (key) => Promise<FullEntity>

Returns the entity with the given key.

`0x${string}`

The entity key (hex string)

Promise<FullEntity>

The entity with the given key, with every field populated. FullEntity

import { createPublicClient } from "@arkiv-network/sdk"
import { cheesecake } from "@arkiv-network/sdk/chains"
import { http } from "viem"
const client = createPublicClient({
chain: cheesecake,
transport: http(),
})
const entity = await client.getEntity(entityKey)
// Entity {
// key: "0x9f2c…",
// owner: "0xabc…",
// contentType: "application/json",
// payload: Uint8Array, // entity.toJson() / entity.toText() decode it
// attributes: { category: { type: "str", value: "docs" } },
// expiresAt: 1_297_000n,
// }

getEntityCount: () => Promise<number>

Returns the total number of entities on the chain.

Promise<number>

The number of entities currently stored

import { createPublicClient } from "@arkiv-network/sdk"
import { cheesecake } from "@arkiv-network/sdk/chains"
import { http } from "viem"
const client = createPublicClient({
chain: cheesecake,
transport: http(),
})
const entityCount = await client.getEntityCount()
// entityCount = 0

getEntityNonce: (owner) => Promise<bigint>

Returns how many entities an account has created — its entity-minting nonce.

Not the account’s transaction nonce: the engine keeps its own counter per creator and mixes it into every key it mints, which is why two identical creates from one account never collide.

`0x${string}`

The account whose nonce to read.

Promise<bigint>

The nonce the account’s next create will use.

import { createPublicClient } from "@arkiv-network/sdk"
import { cheesecake } from "@arkiv-network/sdk/chains"
import { http } from "viem"
const client = createPublicClient({
chain: cheesecake,
transport: http(),
})
const nonce = await client.getEntityNonce("0xabc…")
// 7n — the next entity this account creates is its eighth

predictEntityKeys: <TCount, TSalts>(parameters) => Promise<PredictEntityKeysReturnType<TCount, TSalts>>

Works out the keys an account’s next creates will be given, before sending them.

The engine derives a key from the chain, the registry, the owner, the owner’s nonce and a salt — everything but the nonce is known to the caller, and the nonce is read here. This is what lets a batch reference an entity it is about to mint.

Each key arrives paired with the salt that mints it, because a create defaults to a random salt: a predicted key only holds if the create carries the salt it was predicted with. And the prediction holds only while nothing else from this owner is in flight — for a key you can rely on unconditionally, read it back from the create instead.

A literal count comes back as a fixed-length tuple, so the pairs destructure into names.

TCount extends number = number

TSalts extends readonly bigint[] | undefined = undefined

PredictEntityKeysParameters<TCount, TSalts>

Owner, and either a count or the salts. PredictEntityKeysParameters

Promise<PredictEntityKeysReturnType<TCount, TSalts>>

One { key, salt } pair per create, in batch order. PredictEntityKeysReturnType

A batch whose second entity points at its first.
import { key } from "@arkiv-network/sdk/attr"
const [parent, child] = await client.predictEntityKeys({ owner: account.address, count: 2 })
await wallet.mutateEntities({
creates: [
{ payload, contentType, expires, salt: parent.salt },
{
payload,
contentType,
expires,
salt: child.salt,
attributes: { parent: key(parent.key) },
},
],
})

query: (query, queryOptions?) => Promise<QueryReturnType>

Runs one query and returns one page, with no builder in between.

Use select for anything typed — this returns full Entity objects whatever the selection, and takes a raw string as an escape hatch for a query built elsewhere. A raw string goes to the node exactly as written, with none of the name, type or operator checks the expression combinators apply.

An Expression, or the raw query string.

string | Expression

QueryOptions

Selection, page size, cursor and block. QueryOptions

Promise<QueryReturnType>

One page of entities. QueryReturnType

import { createPublicClient } from "@arkiv-network/sdk"
import { cheesecake } from "@arkiv-network/sdk/chains"
import { and, eq, gte } from "@arkiv-network/sdk/query"
import { i32 } from "@arkiv-network/sdk/attr"
import { http } from "viem"
const client = createPublicClient({
chain: cheesecake,
transport: http(),
})
const page = await client.query(and(eq("category", "docs"), gte("level", i32(10))), {
select: { key: true, attributes: true },
limit: 100,
})
// { entities: [Entity], cursor: "b64:…", blockNumber: 32223n }
// The raw form, unchecked:
await client.query("category = str('docs') AND level >= i32(10)")

select: {(selection?): SelectQueryBuilder<FullEntity>; <S>(selection): SelectQueryBuilder<ProjectedEntity<S>>; (selection): SelectQueryBuilder<FullEntity>; }

Returns a SelectQueryBuilder for building and executing queries — the recommended way to read entities. You declare up front which parts of an entity you want returned, so results always contain exactly the data you asked for.

(selection?): SelectQueryBuilder<FullEntity>

Select every field. Pass nothing or "*"; the returned entities contain all fields.

"*"

SelectQueryBuilder<FullEntity>

<S>(selection): SelectQueryBuilder<ProjectedEntity<S>>

Pick the entity fields to return. Set the ones you want to true (at least one is required); the result is typed to exactly those fields, so reading anything else is a compile error.

Available fields: key, owner, creator, createdAt, updatedAt, expiresAt, creationFlags, contentType, payload, attributeSchema and attributes.

attributes also takes a map of names, to fetch only those: select({ key: true, attributes: { projectId: true } }).

Pass the selection inline so its fields stay literal true. A selection stored in a let/ const variable widens to boolean and the result type can no longer be narrowed — annotate it as const (e.g. const sel = { owner: true } as const) in that case.

S extends EntitySelection

S

SelectQueryBuilder<ProjectedEntity<S>>

client.select({ owner: true, attributes: true }) // entities typed { owner, attributes }
client.select({ key: true, payload: true }) // includes payload → toText()/toJson() too
client.select({ key: true, attributeSchema: true }) // what shape is the data?

(selection): SelectQueryBuilder<FullEntity>

Dynamic selection: accepts a value typed SelectArg (e.g. built at runtime). The result cannot be narrowed in this case, so the entities are typed as the full entity.

SelectArg

SelectQueryBuilder<FullEntity>

What to include in the results. Omit it (or pass "*") to select everything, or pass an object to select specific parts (at least one field is required). Every part is opt-in, including the key. The selection is flat — each field maps to an entity field. SelectArg

A SelectQueryBuilder instance for building and executing queries. SelectQueryBuilder

import { createPublicClient } from "@arkiv-network/sdk"
import { cheesecake } from "@arkiv-network/sdk/chains"
import { eq } from "@arkiv-network/sdk/query"
import { http } from "viem"
const client = createPublicClient({
chain: cheesecake,
transport: http(),
})
// select everything
await client.select().where(eq("category", "docs")).fetch()
await client.select("*").where(eq("category", "docs")).fetch()
// only the key
await client.select({ key: true }).where(eq("category", "docs")).fetch()
// select specific fields — result typed { owner: Hex; attributes: Attributes }
await client.select({ owner: true, attributes: true }).fetch()
// a single field — result typed { owner: Hex }
await client.select({ owner: true }).fetch()

watchEntityEvents: (parameters) => () => void

Watches entity events, calling the handlers you pass as they arrive.

All five are decoded from logs — onEntityCreated, onEntityPatched, onExpiryExtended, onOwnershipTransferred, onEntityDeleted — and each carries the block, transaction and log index it came from, which is the order the operations were applied in. onEvent receives all of them, whatever their type.

WatchEntityEventsParameters

Handlers and options, all optional. WatchEntityEventsParameters

A function that stops the watcher.

(): void

void

import { createPublicClient } from "@arkiv-network/sdk"
import { cheesecake } from "@arkiv-network/sdk/chains"
import { http } from "viem"
const client = createPublicClient({
chain: cheesecake,
transport: http(),
})
const unwatch = client.watchEntityEvents({
onEntityCreated: ({ entityKey, expiresAt }) => console.log(entityKey, "until", expiresAt),
onError: (error) => console.error("watchEntityEvents error", error),
})
unwatch() // stop watching
import { createPublicClient } from "@arkiv-network/sdk"
import { cheesecake } from "@arkiv-network/sdk/chains"
import { http } from "viem"
const client = createPublicClient({
chain: cheesecake,
transport: http(),
})