ARC stands for "Action Registry Core" and is a standard to represent on chain data using the Entity-Component pattern found in traditional game development.
Modules: - Core Proposed Framework Program (Code Complete) - Rust SDK (+WASM) for CoreDS (TODO) - Admin Registry (Code Complete) - Rust SDK (+WASM) for Admin Registry (TODO) - TSAB (Token Standard Action Bundle) - Rust SDK (+WASM) for TSAB (TODO) - xNFT to view ARC NFTs (TODO)
The primary goal for the ARC Proposed Framework is to provide a unified interface for on chain games on Solana.
ARC stands for “Action, Registry, Core”, and uses Entity-Component-System (ECS) style architecture often found in video game development under the hood. The key here is to separate data from its execution, while creating dynamic data structures that boost interoperability and composability.
The Core DS program is in charge of maintaining data buckets called entities. It’s a relatively small code surface, and could be frozen if needed, without worrying about schema upgrades (as we’ll get into in a little bit). The goal for the Core DS program is to provide a single program where all the Entity accounts can be queried from.
The Registry programs sit on top of the Core DS program and usually are unique per community. These are governance programs whose goal is to validate if actions taken to make changes to Entities were done in accordance to the community’s governance policy.
Finally, most of the logic for using ARC DS lies in the Action Bundles, that can exist either on chain or off chain. These action bundles are groups of change functions that modify entity data buckets.
The Core DS program attempts to be as simple as possible to as to be frozen after audits. It’s goal is to define and track three types of accounts: Entities, ARCNFTs, and RegistryInstances.
For every registry, or community, there might exist multiple instances. For example, a given game might have a single registry, but have each of it’s servers as different instances. For simple communities, like the pfp-nft community you might only have the one instance per registry, with a different registry per collection. More on registries down below.
seeds = [
b"registry",
registry.key().to_bytes().as_ref(),
instance.to_be_bytes().as_ref()
]
#[account]
pub struct RegistryInstance {
pub registry: Pubkey,
pub instance: u64,
pub entities: u64,
}
Registry instances keep track of the Registry Program (registry) that they belong to, their instance_id (u64), and how many entities have been created.
An entity is the magical data bucket that keeps all sort of state through a BTreeMap that maps a Pubkey to a SerializedComponent. It also contains info about the registry and instance it’s correlated to, but we’ll cover what those are later.
seeds = [
b"entity",
entity_id.to_be_bytes().as_ref(),
registry_instance.key().as_ref()
]
#[account]
pub struct Entity {
pub entity_id: u64,
pub instance: u64,
pub registry: Pubkey,
pub components: BTreeMap<Pubkey, SerializedComponent>,
}
ARC NFTs are Entities entangled with a sol mint. This allows them to be transferred and traded just like SPL Tokens, while tying Entity data to that SPL token.
seeds = [
b"arcnft",
mint.key().as_ref(),
entity.key().as_ref()
]
#[account]
pub struct ARCNFT {
pub entity: Pubkey,
pub mint: Pubkey,
}
A Registry is a community or set of communities that interact with the same set of components in roughly the same way. There isn’t a good formal definition I can give for registries, but it might make sense when thinking of them through examples.
→ For games, Registries can encompass a full game, where the instances of the registry encompass each server of that game. Some games might have lots of instances; you might create a new instance every time you play the game with a friend, or maybe only a few instances, as official persistent servers.
→ For PFP-Style/Traditional NFTs, there might exist a registry per IP with an instance per collection drop. This way, the governance of that IP is handled by the same registry for all the different collections they drop. Or they might have just one registry per collection, and isolate their governance per drop.
One benefit is by isolating governance by community, each community can setup their own system, and don’t need to rely on one over arching system, while still enjoying the benefits of one over arching data storage contract. This significantly boosts interoperability with wallets and explorers by standardizing data formats while still having a very flexible system for on chain data.
A registry is in charge primarily of hosting two types of accounts; ComponentSchema and ActionBundleRegistration.
seeds = [
schema_url.as_bytes()
]
#[account]
pub struct ComponentSchema{
pub url: String,
}
The ComponentSchema account simply registers a schema url for a component and issues it a pubkey that identifies it within the Registry. When the registry is adding/modifying/removing components from entities, it uses this pubkey, just as it uses it when deciding which ActionBundles to give what kind of access.
seeds = [
b"action_bundle_registration",
registry_instance.key().as_ref(),
action_bundle.key().as_ref()
]
#[account]
pub struct ActionBundleRegistration{
pub action_bundle: Pubkey,
pub instance: BTreeSet<u64>,
pub can_mint: bool,
pub components: BTreeSet<Pubkey>, //PDA of the Component Schema
}
ActionBundleRegistration keeps track of what Pubkeys can make changes to what components. It also specifies in which instances that action bundle can make changes. It also tracks if that action bundle has the ability to mint ARC NFTs and what specific components it can edit.
Action Bundles are where all serialization and deserialization logic takes place for SerializedComponents. Action Bundles validate the state change based on community rules, Registries validate that the Action Bundle approving the change was approved by the community, and finally Core DS handles the data storage itself. You can think of the Action Bundle as a Bank Client, the Registry as a Banker, and the Core DS program as the Bank Vault in how their responsibilities tie together.
$ claude mcp add sol-arc \
-- python -m otcore.mcp_server <graph>