Skip to main content
The genesis-framework is a collection of audited, reusable contract primitives that run on the GVM (Grid Virtual Machine). It provides two essential capabilities:
  1. Audited, reusable components that contract developers can install and compose, instead of building protocol logic from scratch.
  2. Reference implementations showcasing best practices for the GVM component-role model, helping new contributors understand real-world patterns.
The fungible-token protocol is used throughout this page as the running example.

How the GVM component architecture works

Core concepts

  • Protocols (#[contract]) define how a contract system operates, structuring all in-system behaviors and declaring entry points to the contract itself.
  • Components are installable units within a protocol that can be installed on entities and interact with the system. Each protocol can have multiple components.
  • System protocol is derived from the implementation and can be reimplemented by other systems to integrate with the same protocol.
  • Entities represent individual accounts that can own and install components.
  • Component capabilities determine what functionality a component installation provides (System = global state, Issuer = minting, Holder = balance management).
For the broader conceptual model, see Components.

Protocol and component lifecycle

  1. Protocol definition. Define how the contract system operates using the #[contract] macro, which structures behaviors and declares entry points.
  2. Component implementation. Create installable units (components) within the protocol using attribute macros (#[entry], #[view], #[private], and so on).
  3. System protocol generation. The framework derives a system protocol interface that other systems can implement for integration.
  4. Installation. Deploy components to entities with specific components using generated installer infrastructure.
  5. Interaction. Components communicate through generated router infrastructure for both local (sync) and remote (async) calls.

What makes Grid different

  • Protocol-first architecture. Contract systems are defined as protocols that structure behaviors and entry points, with components as installable units within those protocols.
  • Sharded and asynchronous. Every EntityId represents its own synchronous unit. Activating multiple components in the same entity can happen synchronously. Cross-entity calls are async and may be reordered by the scheduler.
  • Installation-time configuration. Components receive configuration during installation and can return data for dependent components.
  • Type-safe component communication. Generated client code ensures type-safe calls between components with compile-time and runtime verifications.

Architecture example: fungible token

// Protocol definition with component implementations
use genie::{Result, GvmComponentId, AmountInSubunits};

#[contract]
pub mod fungible_token {
    pub mod root {
        use genie::{deploy, installation_request, view};

        /// Root component with member fields for automatic storage.
        pub struct FungibleTokenSystem {
            metadata: Metadata,
            issuer_installed_flag: IssuerInstalledFlag,
        }

        impl FungibleTokenSystem {
            #[installation_request]
            pub async fn installation_request(
                &mut self,
                component_type: FungibleTokenComponents,
            ) -> Result<Metadata> {
                // Validate and approve component installation based on role
            }

            #[view]
            pub fn get_metadata(&self) -> Result<Metadata> {
                Ok(self.metadata.clone())
            }

            /// Deploy returns Self with initialized fields.
            /// Fields are automatically saved to storage after deployment.
            #[deploy]
            pub async fn deploy(name: String, symbol: String, ...) -> Result<Self> {
                Ok(Self {
                    metadata: Metadata::new(...),
                    issuer_installed_flag: IssuerInstalledFlag::default(),
                })
            }
        }
    }

    pub mod issuer {
        use super::root::Metadata;
        use genie::{install, Result};

        /// Stateless component, no member fields.
        pub struct FungibleTokenIssuer;

        impl FungibleTokenIssuer {
            #[owner]
            pub fn mint(destination: GvmComponentId, amount: AmountInSubunits) -> Result<()> {
                // Delegate minting to root component
            }

            /// Install returns Self. For stateless components, just return Self directly.
            #[install]
            pub async fn install(
                _metadata: Metadata,
            ) -> Result<Self> {
                Ok(Self)
            }
        }
    }

    pub mod holder {
        use super::root::Metadata;
        use genie::{install, Result};

        /// Holder component with member fields for balance tracking.
        pub struct FungibleTokenHolder {
            token_store: TokenStore,
            holder_metadata: HolderMetaData,
        }

        impl FungibleTokenHolder {
            #[owner]
            pub fn transfer_same_entity(&mut self, amount: AmountInSubunits, send_to: GvmComponentId) -> Result<()> {
                // Debit from self, credit to recipient
            }

            #[owner]
            pub fn transfer(&mut self, amount: AmountInSubunits, send_to: GvmComponentId) -> Result<()> {
                // Fire-and-forget cross-entity transfer using spawn
            }

            #[owner]
            pub async fn transfer_with_confirmation(&mut self, amount: AmountInSubunits, send_to: GvmComponentId) -> Result<()> {
                // Async cross-entity transfer with confirmation
            }

            #[view]
            pub fn balance(&self) -> Result<AmountInSubunits> {
                Ok(self.token_store.balance())
            }

            #[private]
            pub fn receive(&mut self, amount: AmountInSubunits) -> Result<()> {
                self.token_store.credit(amount)
            }

            /// Install returns Self with initialized fields.
            /// Fields are automatically saved to storage after installation.
            #[install]
            pub async fn install(
                metadata: Metadata,
            ) -> Result<Self> {
                Ok(Self {
                    token_store: TokenStore::new(),
                    holder_metadata: HolderMetaData::new(metadata),
                })
            }
        }
    }
}
Fully-qualified component call example:
// Generated client provides unified access to all components
let client = ContractClient::new(component_id);

// Access component methods through generated routers
client.holder.local.transfer_same_entity(amount, recipient_component);     // Local call (sync)
client.holder.local.transfer(amount, recipient_component);                 // Fire-and-forget remote call (sync, no await)
client.holder.remote.transfer_with_confirmation(amount, recipient_component).await; // Remote call with confirmation (async)

// Private methods have restricted visibility
client.holder.local.receive(amount); // pub(crate), only accessible within same contract
  • client: the protocol client instance providing unified access.
  • holder: the component being accessed.
  • local/remote: router for sync/async method variants.
  • transfer_same_entity: the method defined in the component implementation.

Protocol index

ProtocolDescriptionComponents
Fungible TokenStandards-compliant token with mint/burn, transfer, and balance trackingRoot (metadata), Issuer (minting), Holder (balances)
More protocols are on the roadmapTBD
Each directory in the framework contains:
  • Component implementations (*_contract.rs): complete component implementations with all methods.
  • Utilities. Shared types, error definitions, and helper functions.
  • Tests. Unit and integration test suites.
  • Generated infrastructure. Automatically generated routers, clients, and installers.

Component development patterns

1. Component-based design

Components provide different capabilities within a protocol. Each component can be installed independently based on the installation logic defined in the root component. Developers can define as many components as needed, but the root component is mandatory and must always be implemented. A single root component is similar to how contracts behave on other chains, where all the functionality sits on a single address.
  • Root component. Handles global state management and coordination logic. Mandatory.
  • User components. Provide entity-specific functionality (Issuer, Holder, and so on) with different method visibility levels:
    • #[public]: public entry points accessible externally.
    • #[view]: read-only methods for querying state.
    • #[owner]: methods accessible only by the component owner.
    • #[private]: internal methods with private permissions, used for contract-internal coordination.

Member storage (automatic field persistence)

Components can define struct fields that are automatically persisted to storage:
pub struct FungibleTokenHolder {
    token_store: TokenStore,       // Automatically loaded/saved
    holder_metadata: HolderMetaData, // Automatically loaded/saved
}
  • On load. Fields are automatically loaded from storage before method execution.
  • On save. After mutable method execution (&mut self), fields are saved automatically.
  • Installation/deployment. Methods annotated with #[deploy] or #[install] return Result<Self>, and fields are saved after successful completion.
This eliminates manual Storage::save() / Storage::load() calls for component state management.

2. Installation flow

Component installation

// Installation method for each component accepts by default 3 parameters,
// unless specified additional parameters in the #[install] method.
// 1. SystemId (The system unique address)
// 2. EntityId (To be installed on)
// 3. GenComponentId (The owning component)
fn install(&self, system_id: GenSystem, installed_on_entity: EntityId, owner: GenComponentId) -> impl Future<Output = Result<GenComponentId, GenieError>>

// Install components using generated installer infrastructure:

// Get the protocol installer
let installer = ContractClient::installer();

// Issuer component installation
let issuer_component = installer.issuer.install(
    system_id, installed_on_entity, owner).await?;

// Holder component installation
let holder_component = installer.holder.install(
    system_id, installed_on_entity, owner).await?;
System-level installation flow is not yet defined; this section will be updated as the surface stabilizes.

3. Cross-component communication

// Generated client provides type-safe component access
let client = ContractClient::new(target_component_id);

// Call specific component implementations

// Same entity (synchronous calls)
client.holder.local.transfer_same_entity(amount, recipient_component)?;
client.holder.local.balance()?;

// Cross-entity (asynchronous calls)
client.holder.remote.transfer(amount, recipient_component).await?;
client.holder.remote.balance().await?;

// Private methods (restricted visibility)
client.holder.local.receive(amount)?; // Only accessible within same contract

See also