Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
LABS
Guides

Setting Up a Decentralized Attestation Registry

A step-by-step technical guide for developers to deploy, manage, and query an on-chain attestation registry for decentralized reputation and credentials.
Chainscore © 2026
introduction
GUIDE

Setting Up a Decentralized Attestation Registry

A practical guide to deploying and managing a smart contract-based registry for on-chain attestations.

An on-chain attestation registry is a foundational smart contract that serves as a public, immutable ledger for claims or statements. Unlike traditional databases, these registries leverage blockchain properties—decentralization, censorship resistance, and cryptographic verifiability—to create a trusted source of truth. Developers use them to build systems for verifiable credentials, reputation scores, or proof-of-humanity. Popular standards like the Ethereum Attestation Service (EAS) and the ERC-721/1155 token standards for Soulbound Tokens (SBTs) provide frameworks, but a custom registry offers full control over schema design, issuance logic, and revocation mechanisms.

The core of any registry is its attestation schema, which defines the structure of the data being recorded. A schema specifies the fields (e.g., recipient, issuer, expirationTime, data) and their types. You define this schema on-chain, which generates a unique schemaUID. For example, a KYC attestation schema might include a uint64 for a user's verification level and a bytes32 for a hashed document reference. Once deployed, this schema acts as a template; all attestations issued against it must conform to its structure, ensuring data consistency and enabling efficient querying by off-chain indexers.

Deploying the registry involves writing and testing the smart contract logic. A basic Solidity contract needs functions to: create a new schema, issue an attestation (storing the schemaUID, attester, recipient, and data), and optionally revoke one. Critical considerations include gas optimization for batch operations, implementing access control (e.g., using OpenZeppelin's Ownable or role-based permissions), and deciding on a revocation model (immutable, revocable by issuer, or community-challenged). Testing with frameworks like Foundry or Hardhat is essential to audit security and logic before mainnet deployment.

After deployment, the off-chain infrastructure becomes key for usability. This includes a graph indexer (using The Graph or Subsquid) to make attestations queryable via a GraphQL API, and a front-end SDK for applications to easily request, create, and verify attestations. Verification involves checking the on-chain proof of the attestation's existence and validity. For developers, integrating with existing attestation networks like EAS can accelerate development, while a custom registry is chosen for niche use cases requiring specific business logic or data privacy models not supported by generalized protocols.

prerequisites
PREREQUISITES AND SETUP

Setting Up a Decentralized Attestation Registry

A step-by-step guide to preparing your development environment for building on-chain attestation systems using EAS and other protocols.

A decentralized attestation registry is a public, on-chain system for making and verifying claims. The most prominent protocol is the Ethereum Attestation Service (EAS), which provides a schema-based framework for creating attestations—tamper-proof records signed by an issuer. Before interacting with any registry, you need a foundational setup. This includes a Web3 wallet (like MetaMask), a basic understanding of smart contracts, and access to a blockchain node via an RPC provider such as Alchemy or Infura. You'll also need test ETH or the native token for the network you're deploying on, like Sepolia ETH for testing.

The core component is the EAS smart contract. You must connect to its address, which varies by network. For Ethereum mainnet, the contract is at 0xA1207F3BBa224E2c9c3c6D5aF63D0eb1582Ce587. For Sepolia testnet, use 0xC2679fBD37d54388Ce493F1DB75320D236e1815e. You can interact with it directly using ethers.js or viem. First, install the necessary packages: npm install ethers or npm install viem. Then, initialize a provider and contract instance. This setup allows you to call functions like attest() to create records or getAttestation() to verify them.

Attestations require a schema, which defines the structure of the data being attested. Schemas are registered on-chain with a unique UID. For example, a simple 'KYC Verified' schema could have a string field for userId. You can register a new schema using the EAS contract's register() function, or use an existing one. Always test schema registration on a testnet first. The schema UID is a critical piece of data you'll reference every time you create an attestation, ensuring data consistency and interoperability across applications.

For local development and testing, you can fork a mainnet or use a local blockchain like Hardhat or Anvil. This lets you deploy the EAS contracts locally and experiment without spending gas. Clone the official EAS repository and run npm run test to see examples of attestation lifecycle tests. These tests demonstrate key operations: funding accounts, registering schemas, making attestations, and revoking them. Studying this code is the best way to understand the contract's capabilities and edge cases.

Beyond EAS, other attestation frameworks exist, like Verax on Linea or Coinbase's Base Attestation Service. Their setup is similar but requires connecting to their specific contract addresses and networks. The principle remains: connect a wallet, fund it, instantiate the contract ABI, and manage schemas. Your choice of registry depends on the target chain and desired properties like cost, throughput, and ecosystem support. Always consult the latest official documentation for contract addresses and network details before proceeding to production.

key-concepts-text
DECENTRALIZED ATTESTATION REGISTRY

Core Concepts: Schemas, Attestations, and Revocations

A decentralized attestation registry is a public, immutable ledger for creating and verifying structured statements on-chain. This guide explains its core components and how to set one up using the Ethereum Attestation Service (EAS).

A decentralized attestation registry provides a foundational layer for trust and reputation in Web3. Unlike traditional, centralized databases, it allows anyone to create, store, and verify structured statements—called attestations—on a public blockchain. This creates a tamper-proof and universally accessible system for proving facts, credentials, or relationships. The registry is governed by a schema, which defines the structure and rules for the data being attested. Think of a schema as a template; it specifies the fields (like recipient, score, expiryDate) and their data types, ensuring all attestations created from it are consistent and machine-readable.

The core action in this system is making an attestation. An attestation is a signed piece of data that conforms to a predefined schema and is recorded on-chain. For example, a DAO could attest that a wallet address completed a governance course, or a protocol could attest to a user's credit score. Each attestation contains key metadata: the schema it uses, the attester (creator), the recipient (subject), and a revocable flag. Because it's on-chain, anyone can independently verify its authenticity and check who made the claim without relying on a central authority.

Revocation is a critical feature for maintaining the registry's integrity. When an attestation is created as revocable, the original attester (or a designated revoker) can later invalidate it. This is essential for managing outdated information (like an expired certification) or correcting errors. Revocation does not delete the attestation; instead, it publishes a separate revocation transaction that links to the original, signaling to verifiers that the claim is no longer valid. This creates a complete, auditable history. The ability to revoke adds a necessary layer of control and trust, ensuring the registry reflects current, accurate information.

To set up a registry, you first need to choose or create a schema. Using the EAS, you can register a new schema on-chain via its SchemaRegistry contract. This is a one-time, on-chain transaction that defines your data structure. Here's a simplified example of registering a schema for a KYCStatus attestation using Ethers.js:

javascript
const schema = "bytes32 userId, bool kycApproved, uint64 expiryTimestamp";
const resolverAddress = "0x0"; // Use zero address for no custom logic
const revocable = true;

const tx = await schemaRegistry.register(schema, resolverAddress, revocable);

After registration, you receive a unique schemaUID (a bytes32 identifier) that you will use when creating attestations.

Once your schema is registered, you can begin making attestations using the EAS Attestation contract. You will need the schemaUID, the recipient's address, and the data values that match your schema's fields. The contract will emit an Attested event and return an attestationUID. Here is a basic code snippet:

javascript
const attestationData = {
  recipient: "0xRecipientAddress",
  expirationTime: 0n, // 0 for no expiration
  revocable: true,
  data: ethers.AbiCoder.defaultAbiCoder().encode(
    ["bytes32", "bool", "uint64"],
    [userId, true, expiryTimestamp]
  )
};

const tx = await eas.attest(attestationData);

This on-chain transaction creates the permanent, verifiable record. Tools like the EAS Scan explorer allow anyone to look up attestations and schemas by their UID.

Managing the lifecycle of attestations is crucial. Always consider gas optimization by batching attestations where possible and setting appropriate expiration times. For production systems, implement off-chain signing with EAS's off-chain attestations to reduce costs, then optionally bring them on-chain later. Security is paramount: protect your attester's private key, use multisig wallets for important schemas, and thoroughly test revocation logic. A well-designed registry becomes a reusable, public utility for your application, enabling features like verified credentials, trustless reputation systems, and compliant on-chain records without building the infrastructure from scratch.

ARCHITECTURE COMPARISON

Attestation Registry Implementation Options

A technical comparison of on-chain, off-chain, and hybrid approaches for building a decentralized attestation registry.

Feature / MetricOn-Chain Registry (e.g., Ethereum L1)Off-Chain Registry (e.g., Ceramic, IPFS)Hybrid Registry (e.g., EAS on Optimism)

Data Storage Location

Entire attestation on-chain

Content-addressed pointers (CIDs) on-chain, data off-chain

Core schema & attestation on-chain, large payloads off-chain

Data Immutability

Censorship Resistance

Gas Cost per Attestation

$10-50 (L1)

< $0.01

$0.10-1.00 (L2)

Query Performance

Slow (block time)

Fast (p2p/HTTP)

Fast (L2 block time)

Schema Evolution Support

Native Revocation Support

Implementation Complexity

High

Medium

Medium-High

deploy-eas-registry
FOUNDATION

Step 1: Deploying an EAS Registry

Deploying a custom registry is the foundational step for any project using the Ethereum Attestation Service (EAS). This guide walks through the process using the official EAS SDK.

The Ethereum Attestation Service (EAS) uses a registry-contract model. The global, canonical EAS contract acts as the main entry point, but all attestation data is stored in a separate Schema Registry contract. When you deploy your own registry, you are creating a dedicated, project-specific home for your attestation schemas. This provides isolation from other projects and allows for custom governance rules, like who can register new schemas. You can interact with the canonical registry at 0xA1207F3BBa224E2c9c3c6D5aF63D0eb1582Ce587 on Ethereum Mainnet, but for most applications, a custom deployment is recommended.

To deploy, you'll need the EAS SDK (@ethereum-attestation-service/eas-sdk). First, install it in your project using npm or yarn. The core deployment script requires an Ethers.js Signer connected to your target network (e.g., Sepolia, Optimism, Arbitrum). The key function is deploySchemaRegistry. This deploys a new SchemaRegistry contract and returns its address. It's crucial to save this address, as it will be used to initialize your EAS contract instance and for all subsequent schema registrations.

Here is a basic deployment script example using Hardhat and Ethers v6:

javascript
import { SchemaRegistry } from "@ethereum-attestation-service/eas-sdk";
import { ethers } from "hardhat";

async function deployRegistry() {
  const [deployer] = await ethers.getSigners();
  const schemaRegistry = new SchemaRegistry("0xYourSchemaRegistryAddress"); // Use placeholder for linking
  
  const tx = await schemaRegistry.deploy({ signer: deployer });
  const registryAddress = await tx.getAddress();
  
  console.log(`SchemaRegistry deployed to: ${registryAddress}`);
  return registryAddress;
}

After deployment, you must initialize the main EAS contract with your new registry's address using the initialize function. This links the EAS contract to your specific data store.

Consider your upgradeability strategy at this stage. The reference implementations are non-upgradeable by default for maximum decentralization and security. However, for projects requiring future fixes or feature additions, you can deploy the registry and EAS contracts behind a Transparent or UUPS Proxy. This is a critical architectural decision; a non-upgradeable contract is simpler and more trust-minimized, while a proxy allows for evolution but adds complexity. Always verify your contracts on a block explorer like Etherscan after deployment.

Finally, configure your application's frontend and backend to use the new contract addresses. Your EAS SDK instance should be initialized with the deployed EAS contract address and the connected signer/provider. All subsequent operations—creating schemas, making attestations, and revoking them—will now flow through your custom deployment. This setup forms the complete, self-contained attestation infrastructure for your application, enabling features like verifiable credentials, on-chain reputation, and attestation-based governance.

register-schemas
SCHEMA DEFINITION

Step 2: Registering Attestation Schemas

Define the data structure for your attestations by creating and registering a schema on-chain.

An attestation schema is a blueprint that defines the structure of the data you will be attesting to. It specifies the field names, data types, and whether each field is required. For example, a schema for a KYC attestation might include fields like fullName (string), dateOfBirth (string), and countryCode (string). Schemas are registered on a decentralized registry, such as the Ethereum Attestation Service (EAS) on Ethereum mainnet or Optimism, creating a permanent, public reference for your data format.

To register a schema, you first need to encode its definition. Using the EAS SDK, you create a schema string. This string lists each field with its type and a flag indicating if it's required (true) or optional (false). For instance, a simple credential schema could be: bytes32 credentialId,string issuerName,bool isRevoked. You then call the register function on the EAS contract's schema registry, passing the schema string and an optional revocable flag. This transaction returns a unique schema UID, a cryptographic hash of your schema and resolver address.

The schema UID is critical. It is used to link every subsequent attestation to its defining structure, ensuring data consistency and verifiability. Anyone can query the registry with this UID to retrieve the original schema string. When building your application, you should store this UID securely, as it is required for making attestations that conform to this schema. Registration is typically a one-time, on-chain transaction per unique data structure.

Consider your schema design carefully, as modifying a live schema is not possible. You must register a new schema if your data requirements change. For complex use cases, you can reference other attestations within a schema by including a field of type bytes32 to store the UID of the related attestation. This enables the creation of interconnected, graph-like data structures on-chain, which is powerful for representing credentials, reviews, or delegated authorities.

Here is a practical example using the EAS SDK in a Node.js environment to register a schema:

javascript
import { EAS, SchemaRegistry } from "@ethereum-attestation-service/eas-sdk";
const schemaRegistryContractAddress = "0xYourSchemaRegistryAddress";
const schemaRegistry = new SchemaRegistry(schemaRegistryContractAddress);
schemaRegistry.connect(yourSigner);
const schema = "bytes32 credentialId,string issuerName,uint256 issuedAt,bool isRevoked";
const tx = await schemaRegistry.register({
  schema,
  revocable: true,
});
const receipt = await tx.wait();
// The SchemaRegistered event contains your new schemaUID
const newSchemaUID = receipt.events[0].args.schemaUID;
console.log("New Schema UID:", newSchemaUID);

After registration, you can use this newSchemaUID to create attestations in the next step.

issue-revoke-attestations
ON-CHAIN OPERATIONS

Step 3: Issuing and Revoking Attestations

Learn how to programmatically create and manage attestations on your deployed registry using the Ethereum Attestation Service (EAS) SDK.

With your Schema Registry and EAS contracts deployed, you can now issue on-chain attestations. An attestation is a signed data record that links a subject (an Ethereum address, a hash, etc.) to a claim defined by your schema. You'll need the EAS SDK (@ethereum-attestation-service/eas-sdk) to interact with your contracts. First, connect to the SDK using your contract address and provider (like Ethers.js). For example, on Sepolia: const eas = new EAS('0xC2679fBD37d54388Ce493F1DB75320D236e1815e'); eas.connect(provider);.

To issue an attestation, you must specify the schema UID you registered, the recipient (the subject of the attestation), an optional expiration time, and whether the attestation is revocable. The data must be encoded to match your schema's types. For a schema with a string field for rating, you would encode it using the SDK's encodeData function. The transaction is signed by the attester's wallet, creating an immutable record on-chain. You can attach ETH as a fee or incentive for the recipient.

Revocation is a critical feature for maintaining registry integrity. If an attestation was created as revocable, the original attester (or a designated revoker) can call revoke on the EAS contract, providing the attestation UID. This permanently invalidates the attestation without deleting it, providing a transparent audit trail. Off-chain attestations can also be created using the getOffchainUID function for gas-free signatures, which can later be timestamped on-chain via a multiAttest transaction for batch efficiency.

Always verify attestations before relying on them. Use eas.getAttestation(uid) to fetch the full on-chain record and check its validity. The returned object includes the revocable status, revocationTime, expirationTime, and the decoded data. For complex queries, you can index attestation events or use the EAS Scan explorer to view all attestations for a given schema or recipient, which is essential for building verifier logic in your dApp.

Best practices include: - Setting sensible expiration times for time-bound credentials. - Using refUID to link attestations together, creating a graph of verifiable claims. - Implementing optimistic revocation patterns in your UI to immediately reflect changes. - Considering ZKP attestations (like those from the Verax registry on Linea) for privacy-sensitive data where the claim can be proven without revealing the underlying information on-chain.

indexing-querying-data
SETTING UP A DECENTRALIZED ATTESTATION REGISTRY

Step 4: Indexing and Querying Attestation Data

This guide explains how to build a queryable index for on-chain attestations, enabling applications to efficiently discover and verify credentials.

After attestations are created on-chain (e.g., via EAS or Verax), the raw data is stored as immutable logs. To make this data usable, you must index it into a structured database and expose a GraphQL or REST API for queries. This process transforms scattered blockchain events into a searchable registry. Popular indexing solutions include The Graph for a decentralized approach or self-hosted services like Subsquid. The indexer listens for Attested and Revoked events from your registry contract, decodes the data, and stores it in a format optimized for fast retrieval.

A well-designed schema is critical for query performance. Your index should store core attestation fields: uid, schemaId, attester, recipient, revocationTime, and the data bytes. For EAS, you must also decode the data field according to the associated schema's schema string to make individual data points queryable. For example, an attestation for a "KYCStatus" schema with a bool verified field should have that boolean exposed as a top-level queryable field in your GraphQL API, not just as raw bytes.

Here is a simplified example of a GraphQL schema definition for an attestation index, showing how to make decoded data accessible:

graphql
type Attestation @entity {
  id: ID! # The attestation UID
  schemaId: String!
  attester: Bytes!
  recipient: Bytes!
  revoked: Boolean!
  # Decoded data fields based on schema
  verified: Boolean
  expiryDate: BigInt
  countryCode: String
}

Your mapping handler (e.g., in AssemblyScript for The Graph) would decode the attestation data based on the schemaId and populate these typed fields.

With the API live, applications can query for attestations in real-time. Common queries include fetching all valid (non-revoked) attestations for a specific recipient address, finding attestations by a trusted attester, or filtering by decoded data values like verified: true. This enables use cases like token-gating a website based on credential ownership, building a reputation dashboard, or verifying user claims within a dApp without needing to directly query the blockchain for each check.

For production systems, consider indexing performance and decentralization. Using The Graph delegates hosting and syncing to a decentralized network. If self-hosting, you are responsible for maintaining the indexer node, database, and API uptime. Always implement pagination for list queries and cache frequently accessed data. The completed index turns your attestation registry from a static ledger into a dynamic, programmable data layer for your application.

building-query-interface
DECENTRALIZED ATTESTATION REGISTRY

Step 5: Building a Frontend Query Interface

This guide explains how to build a frontend interface to query and display attestations from a decentralized registry like Ethereum Attestation Service (EAS) or Verax.

A frontend query interface transforms raw on-chain attestation data into a user-friendly application. Your primary task is to connect to a GraphQL endpoint provided by an indexing service like The Graph or a hosted subgraph. This endpoint allows you to efficiently query attestations using filters for schemaId, attester, recipient, or revocation status. For example, to fetch all attestations for a specific credential schema, you would query the attestations entity with a where clause like { schemaId: "0x123..." }. This is far more efficient than directly querying the blockchain for events.

The core of your interface will be a component that executes these GraphQL queries. Using a library like Apollo Client or urql in a React application is standard practice. You'll define your queries using GraphQL's query language. A basic query to fetch attestations with their core fields might look like:

graphql
query GetAttestations($schemaId: String!) {
  attestations(where: { schemaId: $schemaId }) {
    id
    attester
    recipient
    refUID
    revocable
    revoked
    data
  }
}

The data field contains the encoded attestation payload, which you must decode according to the schema's structure using a library like ethers.js.

After fetching the data, you must decode the attestation payload. The data field is a bytes string encoded according to the schema's schema string. If your schema is bytes32 userId, uint256 score, you need to decode the data using the Ethereum ABI coder. In ethers.js, you would use ethers.AbiCoder.defaultAbiCoder().decode(['bytes32', 'uint256'], data). This converts the hex data into usable JavaScript values like strings and numbers, which you can then display in your UI components.

For a polished interface, implement filtering and search functionality. Allow users to filter attestations by:

  • Attester: Show only credentials issued by a specific trusted entity.
  • Recipient: Look up all credentials held by a specific Ethereum address.
  • Schema: Switch between different credential types (e.g., KYC, skill badges).
  • Status: Toggle to show only active or revoked attestations. These filters translate directly into variables passed to your GraphQL query's where argument, making the index do the heavy lifting.

Finally, consider caching and state management. Attestation data is relatively static until revoked. Use your GraphQL client's built-in cache to avoid redundant network requests. For real-time updates, you can subscribe to new attestation events if your subgraph supports GraphQL subscriptions. This ensures your UI automatically updates when a new credential is issued or an existing one is revoked, providing a dynamic, app-like experience directly powered by decentralized protocol data.

custom-contract-considerations
ADVANCED IMPLEMENTATION

Alternative: Building a Custom Registry Contract

For applications requiring specialized logic, building a custom attestation registry on Ethereum offers maximum flexibility and control over your attestation schema and lifecycle.

A custom registry is a smart contract you deploy that implements the core IEAS interface from the Ethereum Attestation Service (EAS). This interface defines the essential functions for creating, revoking, and timestamping attestations. By building your own contract, you can embed custom validation rules directly into the attestation process. For example, you could require specific signer permissions, enforce complex business logic before an attestation is issued, or integrate attestations directly with your protocol's tokenomics. This approach moves beyond the standard, permissionless EAS.sol contract to create a purpose-built attestation system.

The primary technical step is to write a contract that inherits from and implements IEAS. Your contract's attest and multiAttest functions will contain your custom logic. Common enhancements include adding access controls with OpenZeppelin's Ownable or role-based libraries, validating attestation data against an on-chain allowlist, or charging a fee in a native or ERC-20 token. After deployment, you must register your new contract's address with the global EAS schema registry using the register function. This links your custom resolver logic to specific schema UIDs, ensuring attestations for those schemas are processed through your contract.

Consider a DeFi protocol that wants to attest to a user's creditworthiness. Instead of a simple off-chain attestation, they could build a registry that only allows attestations after the user's wallet holds a minimum stake of the protocol's governance token. The attest function would check the user's balance and revert if the condition isn't met, making the attestation a verifiable, on-chain credential with built-in economic security. This creates attestations that are inherently more valuable and trust-minimized because the issuance rules are transparent and enforced by the blockchain.

While powerful, custom registries introduce significant development overhead and security considerations. You are responsible for auditing the contract logic, managing upgrades (often via proxy patterns), and covering the gas costs for all users. They are best suited for applications where attestations are a core, value-bearing component of the system. For most use cases—like issuing simple credentials, reviews, or approvals—using the standard EAS contract with an off-chain resolver is simpler and more cost-effective. Reserve custom contracts for when you need programmable trust that cannot be achieved with the base layer.

TROUBLESHOOTING

Frequently Asked Questions (FAQ)

Common questions and solutions for developers building with or interacting with decentralized attestation registries like Ethereum Attestation Service (EAS) or Verax.

Your attestation may not be queryable on a frontend or subgraph for several common reasons. First, verify the attestation was successfully included in a block by checking the transaction hash on a block explorer like Etherscan. If confirmed, the issue is likely with the resolver contract or indexing.

Common causes:

  • Incorrect Schema UID: The resolver's isPayable or attest function logic may filter attestations based on schema. Double-check the UID used in your attestation call.
  • Resolver Revert: Your resolver contract's attest function may have reverted due to failed custom logic (e.g., payment checks, data validation). Check the resolver's event logs for errors.
  • Indexing Delay: Subgraphs (The Graph) can lag behind the blockchain by several blocks. Wait a few minutes and refresh.

Debugging steps:

  1. Call EAS.getAttestation(attestationUID) to confirm on-chain data.
  2. Review your resolver contract's logic for the specific schema.
  3. Check the subgraph's sync status or use a direct RPC call to the EAS contract.
How to Set Up a Decentralized Attestation Registry | ChainScore Guides