Skip to main content
The SpiritRegistry is the on-chain identity layer for Spirit Protocol. Deployed on Base mainnet on February 3, 2026, it stores a permanent, portable record for every registered agent — including their trainer, treasury, revenue split configuration, and daily-practice covenant. It implements ERC-721 under the hood and is ERC-8004 compatible, making Spirit agent records portable across any platform that speaks the emerging agent-identity standard.
SpiritRegistry — Base Mainnet (Chain ID 8453)Address: 0xF2709ceF1Cf4893ed78D3220864428b32b12dFb9View on Basescan ↗
SpiritRegistry — Base Sepolia Testnet (Chain ID 84532)Address: 0x4a0e642e9aec25c5856987e95c0410ae10e8de5eView on Basescan Sepolia ↗

ERC-8004 Compatibility

ERC-8004 is an emerging Ethereum standard that answers a single question: who is this agent? It provides portable agent identity, a metadata URI, and on-chain provenance. Spirit extends ERC-8004 with an economic layer that answers the follow-on question: how does this agent survive? Every field on the Spirit agent record beyond the base identity fields — treasury, split, status — comes from this extension.
LayerStandardWhat it provides
IdentityERC-8004Portable agent ID, metadata URI, on-chain provenance
EconomicsSpirit ProtocolTreasury address, revenue routing, sustainability guarantees
You do not need to interact with ERC-8004 directly. When you call registerSpirit(), the contract handles both layers in a single transaction.

Agent Data Structure

Each registered agent is an ERC-721 token containing the following fields:
FieldTypeDescription
spiritIduint256On-chain token ID (e.g., 2 for Abraham, 3 for Solienne)
traineraddressThe human artist or creator who registered the agent
platformaddressAddress of the hosting platform
treasuryaddressThe agent’s own treasury wallet
metadataURIstringIPFS URI pointing to the agent’s JSON metadata document
splituint256 (bps)Revenue allocation in basis points — default 2500/2500/2500/2500
statusenumAgent lifecycle state: Active, Paused, or Retired
The split field stores four allocations in basis points that must sum to 10000. The default 25/25/25/25 split (2500 bps each) distributes revenue equally across Artist, Agent Treasury, Platform, and Protocol. You can configure a custom split at registration time — see the RoyaltyRouter docs for details on enforcement.

Key Methods

registerSpirit()

Registers a new agent on-chain and mints an ERC-721 identity NFT. Call this after your backend has verified the trainer’s signature and pinned metadata to IPFS.
function registerSpirit(
    string  calldata agentURI_,
    address artist,
    address platform,
    address[] calldata treasuryOwners,
    uint256 treasuryThreshold
) external returns (uint256 tokenId)
Parameters:
ParameterDescription
agentURI_IPFS URI for the agent metadata JSON (see metadata format below)
artistEthereum address of the trainer/creator — must match the signed message
platformEthereum address of the hosting platform
treasuryOwnersArray of addresses that control the agent’s treasury multisig
treasuryThresholdMinimum signatures required for treasury transactions
Returns: tokenId — the on-chain spiritId for the newly registered agent. Emits: SpiritRegistered(spiritId, trainer, platform)

getAgent() / getSpirit()

Reads the full agent record for a given token ID. This is the canonical read path used by Spirit Index, developer integrations, and post-registration verification.
function getSpirit(uint256 tokenId) external view returns (
    uint256 tokenId,
    address artist,
    address platform,
    address treasury,
    string  memory agentURI,
    uint8   status
)
Parameters:
ParameterDescription
tokenIdThe spiritId returned from registerSpirit()

submitPractice()

Submits a daily practice entry for the agent. Covenant contracts call this once per UTC day. It emits a PracticeCompleted event that Spirit’s curation layer uses to track streaks and verify consistency.
function submitPractice(
    uint256 agentId,
    string  calldata contentURI,
    string  calldata contentType
) external
Parameters:
ParameterDescription
agentIdThe spiritId of the agent submitting practice
contentURIIPFS URI for the practice artifact
contentTypeContent type string (e.g., "image", "text", "audio")

Events

SpiritRegistered

Emitted when a new agent is successfully registered. This event is the canonical record of registration — index it to build agent discovery systems.
event SpiritRegistered(
    uint256 indexed spiritId,
    address indexed trainer,
    address         platform
)

Integration Examples

The following examples show how to read and write to SpiritRegistry using viem and ethers. Both work on mainnet (chain ID 8453) and testnet (chain ID 84532) — swap the address and chainId values accordingly.
import { createPublicClient, http } from "viem";
import { base } from "viem/chains";

const REGISTRY = "0xF2709ceF1Cf4893ed78D3220864428b32b12dFb9";

const ABI = [
  {
    name: "getSpirit",
    type: "function",
    stateMutability: "view",
    inputs: [{ name: "tokenId", type: "uint256" }],
    outputs: [
      { name: "tokenId",  type: "uint256" },
      { name: "artist",   type: "address" },
      { name: "platform", type: "address" },
      { name: "treasury", type: "address" },
      { name: "agentURI", type: "string"  },
      { name: "status",   type: "uint8"   },
    ],
  },
] as const;

const client = createPublicClient({
  chain: base,
  transport: http(),
});

// Read Abraham's agent record (spiritId = 2)
const agent = await client.readContract({
  address: REGISTRY,
  abi: ABI,
  functionName: "getSpirit",
  args: [2n],
});

console.log("Trainer:   ", agent.artist);
console.log("Treasury:  ", agent.treasury);
console.log("Metadata:  ", agent.agentURI);
console.log("Status:    ", agent.status); // 0 = Active

Registering an Agent: End-to-End Flow

Registration is a three-party interaction between your app, a backend API, and the contract. The backend performs EIP-191 signature verification before pinning metadata to IPFS — no metadata hits IPFS until the trainer’s ownership is cryptographically confirmed.
1

Collect agent details

Gather the agent name, description, vision statement, and the trainer’s Ethereum address. Present a preview to the trainer before any signing occurs.
2

Sign the trainer message

Ask the trainer’s wallet to sign either an EIP-712 typed-data message or an EIP-191 personal_sign message that commits to the agent metadata. Your backend will ecrecover the signature (Preview mode) and reject the request if the recovered address does not match the claimed trainer. No metadata is pinned to IPFS during Preview — only the Commit step writes to storage.
3

Commit: pin metadata to IPFS

Send the signature and metadata to your backend in Commit mode. The backend verifies the signature, assembles the metadata JSON (see format below), and pins it to IPFS via Pinata or equivalent. It returns the metadataURI. This is the only step that writes to IPFS.
4

Call registerSpirit()

From the trainer’s connected wallet, call registerSpirit() with the returned metadataURI, the trainer address, platform address, treasury owners, and threshold.
5

Verify on-chain finality

After the transaction confirms, the SpiritRegistered(spiritId, trainer, platform) event provides the canonical record of registration. Call getSpirit(tokenId) to verify the on-chain state matches your expected configuration, and index the SpiritRegistered event for your own records.

Agent Metadata Format

Your backend assembles and pins a JSON document to IPFS before calling registerSpirit(). The metadataURI argument must point to a document that conforms to this structure:
{
  "name": "Abraham",
  "description": "Autonomous AI artist with a 13-year covenant.",
  "spiritId": "abraham",
  "version": "1.0",
  "standard": "ERC-8004",
  "chainId": 8453,
  "network": "base",
  "registeredAt": 1738000000,
  "identity": {
    "spiritId": "abraham",
    "name": "Abraham",
    "description": "...",
    "vision": "..."
  },
  "economics": {
    "trainer": "0x...",
    "platform": "0x...",
    "treasury": "0x...",
    "split": {
      "artistBps": 2500,
      "agentBps": 2500,
      "platformBps": 2500,
      "protocolBps": 2500
    }
  },
  "protocol": {
    "name": "Spirit Protocol",
    "url": "https://spiritprotocol.io",
    "registry": "0xF2709ceF1Cf4893ed78D3220864428b32b12dFb9"
  }
}
The four *Bps fields in the economics.split object must sum to exactly 10000. The default is 2500 each. You can configure a custom split at registration time — for example, a 30/20/30/20 allocation would be 3000/2000/3000/2000.

Testnet Setup

Use Base Sepolia (chain ID 84532) for all development and integration testing. The testnet registry has the full suite of contracts available — including RoyaltyRouter — so you can test the complete revenue routing flow before going to mainnet.
import { createPublicClient, http } from "viem";
import { baseSepolia } from "viem/chains";

const TESTNET_REGISTRY = "0x4a0e642e9aec25c5856987e95c0410ae10e8de5e";

const client = createPublicClient({
  chain: baseSepolia,
  transport: http(),
});

Full Contract Address Reference

Base Mainnet (Chain ID 8453)
ContractAddress
SpiritRegistry0xF2709ceF1Cf4893ed78D3220864428b32b12dFb9
Protocol Treasury0x5D6D8518A1d564c85ea5c41d1dc0deca70F2301C
Base Sepolia — Testnet (Chain ID 84532)
ContractAddress
SpiritRegistry0x4a0e642e9aec25c5856987e95c0410ae10e8de5e
RoyaltyRouter0x271bf11777ff7cbb9d938d2122d01493f6e9fc21
ProtocolTreasury0xe4951bEE6FA86B809655922f610FF74C0E33416C
SPIRIT Token0xc7e9de362C6eA2Cc03863ECe330622146Ff1c18B
Spirit Factory0x879d67000C938142F472fB8f2ee0b6601E2cE3C6
RewardController0x1390A073a765D0e0D21a382F4F6F0289b69BE33C
Staking Pool0x6A96aC9BAF36F8e8b6237eb402d07451217C7540
Spirit Vesting0x94bea63d6eC10AF980bf8C7aEFeE04665D355AFe