⚠️ This documentation covers the Legacy, Closed-Beta of NVNM Chain L2, powered by MANTRA Chain. For the *NEW* NVNM Chain documentation, please visit https://docs.nvnmchain.io.

Custom Modules

Anchoring Module

Native cosmos module, extended with EVM-compatible precompile: Anchoring module is used to manage registries, records, and role-based access.

The Anchoring module is a custom Cosmos SDK module exposed to EVM through a precompiled contract. It allows both Cosmos and EVM callers to create registries, anchor off-chain artifacts (documents, files, data) on-chain via checksummed records, and manage access through a role-based permission system.

Core Concepts

  • Registry — A named container for records. Any user can create one and automatically becomes its admin.
  • Record — An anchored reference to off-chain data, identified by a checksum. Records are versioned: submitting the same checksum to the same registry increments the version while preserving the recordId.
  • Roles — Two roles govern access: admin (full control, can grant/revoke roles) and editor (can add records and update status).

Info

Precompile Address: 0x0000000000000000000000000000000000000A00

Functions

Transactions

FunctionDescription
addRegistry(string name, string description)Creates a new registry. Returns the registryId. Caller becomes admin.
addRecord(Record record)Adds a record to a registry. If the checksum already exists, a new version is created.
updateRecordStatus(uint64 registryId, uint64 recordId, string checksum, uint64 index, string status)Updates the status of a specific record version.
grantRole(uint64 registryId, string checksum, address account, string role)Grants a role to an address. Pass an empty checksum for registry-level scope.
revokeRole(uint64 registryId, string checksum, address account, string role)Revokes a role from an address.

Queries

FunctionDescription
records(string registry, string checksum, uint64 recordId, uint64 index, PageRequest pagination)Fetches records. All filter parameters are optional — pass empty/zero to ignore.
registries(uint64 registryId, string name, PageRequest pagination)Fetches registries by ID or name.

Note

Queries are read-only and do not require any permissions.

Data Structures

Record

struct Record {
    string registry;      // Registry name
    string uri;           // Link to off-chain data (e.g. IPFS URI)
    string checksum;      // Cryptographic hash of the data
    string checksumAlgo;  // Algorithm used (e.g. "sha256")
    string metadata;      // JSON metadata string
    string timestamp;     // Set by the chain — ignored on input
    string status;        // User-defined (e.g. "active", "removed")
    uint64 recordId;      // Set by the chain — use 0 on input
    uint64 index;         // Version index — set by the chain
    bool   isLatest;      // Latest version flag — set by the chain
}

Registry

struct Registry {
    uint64 id;
    string name;
    string description;
    string creator;       // Bech32 address of the creator
    string createdAt;
}

PageRequest / PageResponse

struct PageRequest {
    bytes  key;
    uint64 offset;
    uint64 limit;
    bool   countTotal;
    bool   reverse;
}

struct PageResponse {
    bytes  nextKey;
    uint64 total;
}

Permission Model

The module uses hierarchical role-based access control. When a permission check runs, it evaluates roles in order — Record → Registry → Global — and permits the action at the first match.

OperationRequired Role
addRegistryAny user
addRecordadmin or editor
updateRecordStatusadmin or editor
grantRoleadmin only
revokeRoleadmin only
QueriesNone

Role Scoping

  • Record-level — Applies to a single checksum. Grant with a specific checksum value.
  • Registry-level — Applies to all records in a registry. Grant with an empty checksum.
  • Global — Applies across all registries (assigned at the chain level).

Record-level roles take precedence over registry-level roles. A user with editor at the registry level but admin on a specific record will have admin permissions for that record only.

Usage Examples (EVM)

Creating a Registry

DOCUMENT_PRECOMPILE = ContractAsync.from_abi(DOCUMENT_PRECOMPILE_ABI)
DOCUMENT_ADDRESS = "0x0000000000000000000000000000000000000A00"

receipt = await DOCUMENT_PRECOMPILE.fns.addRegistry(
    "my-registry",
    "A registry for document anchoring"
).transact(w3, admin_account, to=DOCUMENT_ADDRESS, gas=100_000)

Adding a Record

record = (
    "my-registry",          # registry
    "ipfs://QmABC123...",   # uri
    "abc123def456",         # checksum
    "sha256",               # checksumAlgo
    '{"document": "..."}',  # metadata (JSON string)
    "",                     # timestamp (chain sets this)
    "active",               # status
    0,                      # recordId (chain sets this)
    0,                      # index (chain sets this)
    False                   # isLatest (chain sets this)
)

receipt = await DOCUMENT_PRECOMPILE.fns.addRecord(record).transact(
    w3, admin_account, to=DOCUMENT_ADDRESS, gas=100_000
)

Querying Records

pagination = (b"", 0, 100, False, False)

records, page_response = await DOCUMENT_PRECOMPILE.fns.records(
    "my-registry",  # filter by registry (empty string = all)
    "",             # filter by checksum (empty string = all)
    0,              # recordId (0 = ignore)
    0,              # index (0 = ignore)
    pagination
).call(w3, to=DOCUMENT_ADDRESS)

Granting a Role

# Grant registry-level editor role (empty checksum = registry scope)
await DOCUMENT_PRECOMPILE.fns.grantRole(
    registry_id,
    "",              # empty = registry-level
    user_address,    # EVM address
    "editor"
).transact(w3, admin_account, to=DOCUMENT_ADDRESS)

# Grant record-level admin role (specific checksum)
await DOCUMENT_PRECOMPILE.fns.grantRole(
    registry_id,
    "abc123def456",  # specific document checksum
    user_address,
    "admin"
).transact(w3, admin_account, to=DOCUMENT_ADDRESS)

Usage Example (COSMOS TX)

{
    "body": {
        "messages": [
            {
                "@type": "/nvnmchain.anchoring.v1.MsgAddRegistry",
                "sender": "<WALLET_ADDRESS>",
                "name": "<NAME OF REGISTRY>",
                "description": "<DESCRIPTION>",
                "metadata": "{\"owner\":\"NVNM Foundation\", \"purpose\":\"Protocol Documentation\", \"website\":\"https://nvnmchain.io\"}" // Customisable metadata json structure
            },
            {
                "@type": "/nvnmchain.anchoring.v1.MsgAddRecord",
                "sender": "<WALLET_ADDRESS>",
                "record": {
                    "registry": "<NAME OF REGISTRY>",
                    "uri": "<IPFS or other URL RECORD>",
                    "checksum": "<SHA256SUM RECOMMENDED>",
                    "checksum_algo": "sha256",
                    "metadata": "{\"version\":\"1.0\",\"title\":\"NVNM Chain Whitepaper\",\"author\":\"Albert Berdellans\",\"organization\":\"NVNM Labs\",\"date\":\"May 2026\",\"summary\":\"Accountability infrastructure for agentic activity\"}",
                    "status": "active"
                }
            }
        ],
        "memo": "Anchoring the initial May 2026 NVNM Chain Whitepaper authored by Inveniam.",
        "timeout_height": "0",
        "unordered": false,
        "timeout_timestamp": null,
        "extension_options": [],
        "non_critical_extension_options": []
    },
    "auth_info": {
        "signer_infos": [],
        "fee": {
            "amount": [
                {
                    "denom": "ibc/93575DF9D4BC5B2C85D967DE020E5DEA1863FDD108AD509A36DA6C83E3398227",
                    "amount": "0"
                }
            ],
            "gas_limit": "3000000",
            "payer": "",
            "granter": ""
        },
        "tip": null
    },
    "signatures": []
}

Error Handling

ScenarioResult
Non-admin calls grantRole or revokeRoleTransaction reverts
addRecord with an invalid registry nameTransaction reverts
Query for a non-existent registry or checksumReturns empty result
Granting an already-held roleSucceeds (idempotent)