Skip to main content
FIRE is a development framework that helps you harness The Grid’s computational model to design scalable, bottleneck-free systems. Genie is the Rust SDK for The Grid. It helps you structure contract logic using FIRE principles — one #[contract] codebase, multiple installable component roles — and handles deployment, installation, access control, and type-safe calls within and across entities.
Genie is an early preview. The API surface will evolve.
Prerequisite. This reference assumes you are familiar with how The Grid structures execution and state. If entities, components, and activations are new, read these first:
  • Entities and accounts — what an entity is and how it holds components
  • Components — installable logic and storage, same-entity vs cross-entity interaction
  • Activations — how work is submitted, queued, and routed to components

Contracts and the entity model

The entity and component model describes how code and state are structured on The Grid.
  • Entity — an addressable container and the unit of sequential execution. Activations arrive at an entity, enter its queue, and are processed one at a time; each is routed to a component inside the entity. See Entities and accounts.
  • Component — immutable logic and independent state installed inside an entity. Each component owns its storage; many components can coexist on one entity. See Components.
  • Logic immutability — Installing a component into an entity does not let the entity owner tamper with it. The instance runs only its verified on-chain code and responds through its published ABI; every activation is handled according to that logic and its access rules, not at the installer’s discretion.
  • Contract — one verified codebase that defines one or more component types (for example root, issuer, holder). Deploy creates the root instance; install creates other component types on an entity.
  • Co-location boundary — components on the same entity can call each other synchronously within a single activation on that entity’s queue. Components on different entities do not share storage; they interact only by sending activations between entities.
In Genie, #[deploy] instantiates the root component on an entity; #[install] adds non-root types. The manifest’s component_type_index identifies each type when you push and install from the CLI.

Actor model

In FIRE, the actor model is the foundation for distributing execution and state. On The Grid, the entity is the actor: it owns the activation queue and processes work sequentially. Components are the immutable logic and independent state modules that live inside an entity and run when an activation reaches them.

Core principles

  • Message passing and sequential handling — Entities communicate asynchronously by sending activations to one another. Each entity maintains its own queue and processes incoming activations one at a time, in order, dispatching each to the targeted component inside it.
  • Independent execution units — Each entity runs on its own queue. Work on one entity does not block unrelated entities elsewhere in the system.
  • Immediate state commit — When an activation completes on an entity, the affected components’ state changes commit immediately. Local state stays consistent without waiting for cross-entity consensus.
  • State isolation — Each component maintains its own storage. During an activation, a component may update its own state and trigger new activations to other entities; it never directly mutates another component’s storage.

Operational considerations

Distributing state and execution trades synchronous, all-or-nothing transactions for parallel throughput. The async consequences are familiar from other actor and message-passing systems. Two show up most often:
  • No transactional revert — Each activation is its own commit boundary. A failure downstream does not roll back work already committed upstream; coordinate across steps explicitly instead of assuming one global transaction.
  • Stale reads and interleaving — As in ordinary async code, state can change between sending a request and handling the response. Design multi-step flows with continuations and idempotent handlers where retries are possible.

Key FIRE concepts

FIRE is how you scale safely on The Grid. Scale requires asynchrony — throughput comes from distributed execution across entities, not from serializing the whole network. Mitigate async complexity with developer-defined guarantees and explicit design patterns rather than assuming one global transaction. Unlock parallel scalability by structuring work so independent entities and components run concurrently without a single hot spot. The principles below spell that out and map each one to Genie: Used together, these principles are how applications built with FIRE are planned for scalability: independent entities and components handle work in parallel — cross-entity remote calls and async continuations where work should be distributed, flow control and error handling shaped by component relations, and entity-level atomicity through co-location when a step must stay local.

Feature snapshot

Common types

Quick start

Define a contract

Contract crates target RISC-V and use #![no_std] instead of the full standard library. If the contract uses heap types (String, Vec, and so on), also add extern crate alloc at the top of src/lib.rs:

Use the component

Macro reference

Genie uses procedural macros to define component boundaries, roles, and communication patterns. Entry-point attributes (#[view], #[owner], #[public], #[private]) also generate async variants for remote client calls.

Contract definition

Component lifecycle

Access control

Read-only methods

Attribute usage patterns

Read-only versus mutable methods

Storage API

StorageKey — a byte key for keyed storage (StorageKey::from(...)). Keyed methods namespace values by type plus key, so you can store multiple values of the same type. StorageGuard<T> — a load–modify–save wrapper returned by load_guarded. Mutate through it like a reference; changes persist when it is dropped. Do not hold it across .await.

Member storage (automatic field persistence)

Components can define struct fields that are automatically persisted to storage. When a component struct has fields:
  1. On load. The entire struct is loaded from storage using Storage::load_self<T>().
  2. On save. After mutable method execution, the entire struct is saved using Storage::save_self<T>(&object).
This whole-struct storage approach is more efficient than field-by-field storage, reducing the number of storage operations and ensuring atomic state updates. It is also convenient: state lives on the component struct and is used naturally through &self, &mut self, and field access, like ordinary Rust.

Runtime and communication API

Remote activation patterns

Remote calls from generated clients return a ComponentCallFuture. Three ways to run them:

Await

Wait for the result in the same method. The execution yields until the remote call completes.
The calling method must be async.

Spawn

Queue the call with genie::spawn and continue local work. All queued spawns are sent as activations when execution yields.

Spawn with callbacks

Attach a #[private] handler. The runtime invokes it in a later activation when the call completes or fails.
Use .on_success or .on_result for other callback shapes.

Component communication patterns

Local versus remote calls

Generated clients expose .local and .remote routers on each component role.

Typed role references

Method parameters can use generated refs instead of raw GvmComponentId. Import from super::self_refs:
When you have a GvmComponentId and need a typed ref (or the reverse), use .try_into()?.

Calling root from a non-root component

Non-root components call root methods via Self::root_client():

Error handling

Fallible component methods return genie::Result<T> (alias for Result<T, GenieError>). Returning Err reports failure to the caller; it does not revert storage already written in the same activation.

Validation

Use ensure! for early returns — with a message string or a typed error that implements Into<GenieError>.

Component errors

Define one error enum per component with #[error_type]. Return typed errors via .into().

Rollback on failed remote calls

Remote calls can fail after local state has already changed. Update local state first, then revert manually if the remote leg fails:

Cross-contract errors

Use try_as() to match a callee’s typed error after a failed remote call.

Cross-contract composition

A contract can take GvmContract handles to already-deployed contracts, install their components, and call them through generated clients.

Two-token pool example

A pool that trades token A for token B needs one holder per token contract — two holders because there are two different tokens. Root installs pool holders on its own entity at #[deploy] (the liquidity reserves):
Client installs user holders on the installing entity at #[install]. Root passes the token contracts and its pool holder IDs through InstallData:
Calling the other contract — on root, wrap a stored pool holder ID in a generated client:

Generated infrastructure

The #[contract] macro automatically generates:
  1. Contract client{Contract}Client with a router per component role (.local, .remote, .callbacks):
  2. Installers{Contract}Installer for installing non-root components:
  3. Components enum — one variant per non-root role (for example SimpleCounterContractComponents::Controller). Passed to the root’s #[installation_request] handler so it knows which component is being installed:
  4. Callback router — typed refs for #[private] methods, wired via Self::callbacks() in spawn handlers:
  5. Role refssuper::self_refs::{Holder, Issuer, ...} for type-safe same-contract method parameters.
  6. Self::root_client() — on non-root components, call root methods without constructing a client manually: