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 Token-Curated Registry for Content Approvers

A technical guide to building a decentralized list of vetted content moderators using token-based governance. This tutorial covers contract architecture, application/challenge periods, and staking mechanics.
Chainscore © 2026
introduction
A TECHNICAL GUIDE

Setting Up a Token-Curated Registry for Content Approvers

A Token-Curated Registry (TCR) is a decentralized list maintained by token holders who are economically incentivized to curate quality entries. This guide explains how to implement a TCR for managing a list of trusted content approvers or moderators.

A Token-Curated Registry (TCR) is a smart contract pattern where a list's membership is governed by token-based voting. To propose a new member (e.g., a content approver), a user must deposit tokens as a stake. Existing token holders then vote to accept or reject the proposal, with their voting power proportional to their stake. This creates a cryptoeconomic system where the cost of a bad actor being listed (their lost stake) outweighs the potential benefit, aligning incentives for quality curation. Popularized by projects like AdChain and FOAM, TCRs are ideal for creating trustless, community-moderated whitelists.

The core TCR lifecycle involves three main phases: Application, Challenge, and Resolution. First, an applicant stakes tokens to submit their address for inclusion. The application enters a challenge period, during which any token holder can challenge it by matching the stake, triggering a vote. All token holders then vote, with the option to commit their vote secretly and later reveal it to prevent last-minute manipulation. The side that loses the vote forfeits its stake to the winner, creating a powerful financial disincentive for malicious proposals or frivolous challenges. The winning outcome determines if the applicant is added to or kept off the registry.

To implement a basic TCR, you can start with a Solidity smart contract. Key state variables include the registry mapping for listed addresses, a stakeAmount for applications, and structs to manage Proposals and Challenges. The contract must handle the commit-reveal voting scheme, which requires hashing a voter's choice with a secret salt during the commit phase. After the voting period ends, voters reveal their salt and vote to tally the results. You can extend the OpenZeppelin library for secure ownership and pausable patterns. A minimal frontend using ethers.js or web3.js is needed for users to interact with these functions.

For a content moderation TCR, you would list Ethereum addresses representing approved moderators. The stake amount should be set high enough to deter spam but low enough to allow legitimate participants. Governance parameters like the challenge period duration, vote commit period, and stake decay rate must be carefully calibrated. You can use a parameterizer contract to allow token holders to vote on changing these values over time. This setup ensures the list of approvers remains relevant and resistant to capture, as the economic cost of corrupting the registry scales with the total value staked by honest participants.

In practice, running a TCR requires active community participation and liquidity for the native token. Consider using a bonding curve for initial token distribution or integrating with a decentralized autonomous organization (DAO) framework like Aragon for advanced governance. Monitoring tools are essential to track proposal status, stake amounts, and voting activity. While TCRs provide robust Sybil resistance, they are not set-and-forget systems; they require ongoing engagement and potentially a fallback emergency multisig for critical upgrades or bug fixes in the smart contract code.

prerequisites
PREREQUISITES AND SETUP

Setting Up a Token-Curated Registry for Content Approvers

A step-by-step guide to preparing your development environment and understanding the core components required to build a token-curated registry (TCR) for decentralized content moderation.

A token-curated registry (TCR) is a decentralized application where a list is curated by stakeholders who deposit a token as a bond. For a content approval system, this list could be a registry of trusted reviewers, approved content sources, or whitelisted publishers. The core mechanism relies on economic incentives: participants stake tokens to add or challenge entries, with disputes resolved through a voting process. This guide will walk through the prerequisites for building such a system on Ethereum using Solidity and common Web3 tooling.

Before writing any code, you must set up your development environment. You will need Node.js (v18 or later) and a package manager like npm or yarn. Essential tools include Hardhat or Foundry for smart contract development and testing, and a wallet such as MetaMask for interacting with contracts. For initial testing, configure Hardhat to use its built-in local network. Install the necessary packages: npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox @openzeppelin/contracts to get started with a secure development foundation.

The smart contract architecture for a basic TCR involves several key contracts. You will typically inherit from OpenZeppelin's audited libraries for security. The main registry contract manages the list of entries and the stake required for submission. A separate Voting contract, possibly implementing a fork of ERC20Votes for snapshot-based governance, handles challenge resolutions. You'll also need a token contract, often an ERC-20 standard, which stakeholders will use for staking. Planning this separation of concerns is crucial for maintainability and security.

Understanding the TCR lifecycle is critical for implementation. The process follows these steps: 1) A proposer stakes tokens to submit a new entry (e.g., a reviewer's address). 2) A challenge period begins where other token holders can dispute the entry by matching the stake. 3) If challenged, token holders vote to resolve the dispute, using their staked tokens as voting weight. 4) The winner of the vote reclaims their stake plus a portion of the loser's stake as a reward. This creates a robust, incentive-aligned system for curation.

Finally, you'll need to plan the frontend and off-chain components. Your application will require a way to query the blockchain for registry state and voting events. Use a provider library like ethers.js or viem to connect your dApp interface. For efficient data retrieval, consider indexing contract events with The Graph or a similar service. Ensure you have test ETH for your chosen testnet (like Sepolia or Holesky) and a clear plan for the user journey: connecting a wallet, viewing the registry, and participating in proposals and challenges.

key-concepts-text
TUTORIAL

Setting Up a Token-Curated Registry for Content Approvers

A Token-Curated Registry (TCR) is a decentralized list where token holders vote to curate entries. This guide explains the core concepts and steps for building a TCR to manage content approvers.

A Token-Curated Registry (TCR) is a self-governing list maintained by a decentralized community. In a TCR for content approvers, the list itself contains the addresses of users authorized to moderate or approve content. The core mechanism is simple: anyone can propose adding or removing an address by depositing tokens. Other token holders then vote to accept or challenge the proposal. This creates a cryptoeconomic system where the cost of proposing spam (the deposit) and the reward for good curation (earned tokens) align incentives toward maintaining a high-quality list. The original TCR model was popularized by projects like AdChain for curating non-fraudulent advertising publishers.

The lifecycle of a TCR application involves three main phases: Application, Voting, and Resolution. First, an applicant stakes tokens to propose adding their address to the registry. This stake is held as a bond. The application then enters a challenge period, during which any token holder can dispute it by matching the applicant's stake. If challenged, a vote is triggered where token holders commit their tokens to support or oppose the application. The side with fewer votes forfeits its stake to the winning side, creating a powerful financial disincentive for poor submissions or malicious challenges. This process ensures only addresses deemed valuable by the token-weighted consensus are listed.

Implementing a basic TCR requires defining key parameters in a smart contract. These include the application stake amount, challenge period duration, and the commit/reveal voting mechanism to prevent last-minute manipulation. A common reference is the PLCRVoting (Partial-Lock Commit-Reveal Voting) system. Developers often use existing frameworks like OpenZeppelin for secure contract patterns and The Graph for indexing and querying application events. The contract must manage the token deposits, track application statuses (like Pending, Challenged, Accepted), and distribute rewards and slashed stakes correctly. Setting these parameters requires careful consideration of the token's economic value to balance accessibility with security.

For a content moderation TCR, the listed addresses could represent oracles, delegates, or algorithmic agents responsible for flagging or approving user-generated content. The TCR's quality directly impacts the platform's integrity. A well-curated list prevents Sybil attacks by making it expensive for a single entity to control multiple approver slots. Furthermore, the staking mechanism ensures approvers have skin in the game, aligning their financial interest with honest moderation. Over time, a successful TCR creates a trusted, decentralized reputation system, moving away from centralized, opaque appointment of moderators. This model is applicable to DAO governance, credential verification, and any system requiring a curated set of actors.

FRAMEWORK COMPARISON

TCR Implementation Frameworks: AdChain vs. Kleros

A technical comparison of two leading smart contract frameworks for building a token-curated registry.

Feature / MetricAdChain RegistryKleros TCR

Core Architecture

Single, upgradable registry contract

Modular, court-based arbitration system

Challenge Mechanism

Stake-based voting by token holders

Decentralized dispute resolution via jurors

Arbitration Finality

7-day voting period, majority wins

Multiple appeal rounds, Kleros court final

Developer Integration

Direct contract interaction via SDK

Requires integration with Kleros arbitrator

Base Deposit (approx.)

0.1 ETH + application stake

Governed by Kleros court parameters

Time to Resolution

Minimum 7 days for a challenge

Variable, depends on court rounds (days-weeks)

Token Utility

ADT token for voting and staking

PNK token for juror staking and governance

Suitable For

Brand safety, curated lists

Crowdsourced truth, subjective criteria

contract-architecture
SMART CONTRACT PATTERNS

Token-Curated Registry Architecture

A Token-Curated Registry (TCR) is a decentralized application that uses a token-based voting mechanism to curate a high-quality list. This guide details the core smart contract architecture and state machine logic for building a TCR for content approvers.

A Token-Curated Registry (TCR) is governed by a state machine that manages the lifecycle of each list entry. For a content approver registry, the primary states are: Absent, Pending, Accepted, and Removed. A prospective approver submits an application by depositing tokens, moving the entry to Pending. This initiates a challenge period, during which any token holder can stake tokens to dispute the applicant's suitability. The contract's core logic defines the rules for transitions between these states, enforced by on-chain voting.

The architecture typically involves three main contracts: a Registry contract storing the list and state, a Voting contract (often a fork of ERC20Votes or Governor) to manage polls, and a Token contract (ERC20 or ERC721) representing governance rights. The Registry contract emits events for state changes, which off-chain indexers use to update UIs. Key functions include apply(bytes32 data, uint deposit), challenge(uint listingID, uint stake), and resolveChallenge(uint listingID). Security considerations like commit-reveal schemes for voting and partial lock of challenge stakes are critical to prevent gaming.

Implementing the state machine requires careful handling of timelocks and economic incentives. For example, the apply function should lock the proposer's deposit for a minimum period, even if unchallenged, to prevent spam. The resolveChallenge function must calculate rewards and slashing: the winning party receives a portion of the loser's stake, with the remainder burned or sent to a treasury to maintain token scarcity. Using OpenZeppelin's SafeCast and ReentrancyGuard libraries is essential for secure arithmetic and protection against reentrancy attacks in these financial functions.

A practical implementation for an approver TCR might store struct Listing { address applicant; uint deposit; uint applicationExpiry; uint challengeID; Status status; }. The Status is an enum mapping to the state machine. The challenge period is enforced by checking block.timestamp against applicationExpiry. For voting integration, the contract can call an external Voting contract's createProposal function, passing the listing details. The minimal viable TCR avoids complex delegation by using a simple token-weighted vote, but for production, integrating with a governance framework like OpenZeppelin Governor provides better security and upgradeability.

Testing the state machine is paramount. Use a framework like Foundry or Hardhat to simulate full lifecycle scenarios: a successful application, a failed challenge, and a successful challenge. Write tests that verify state transitions, event emissions, and token balance changes. For instance, test that an applicant's deposit is slashed and distributed correctly when a challenge succeeds. Always conduct invariant testing to ensure that the total token supply or treasury balance behaves as expected across all operations, preventing economic exploits in your TCR implementation.

staking-mechanism
TOKEN-CURATED REGISTRIES

Implementing Staking and Slashing Logic

A Token-Curated Registry (TCR) uses economic incentives to curate a list of high-quality entries. This guide details the core smart contract logic for staking tokens to add or challenge entries and the slashing mechanism that penalizes bad actors.

A Token-Curated Registry (TCR) is a decentralized application where the list of approved entries is governed by token holders. Participants must stake the registry's native token to propose a new entry (like a content approver's address) or to challenge an existing one. This staking mechanism aligns incentives: only those who believe in the quality of an entry will risk their capital to support it. The staked tokens are locked in a smart contract during a challenge period, creating economic skin in the game. This model, popularized by projects like AdChain, ensures the list's integrity is maintained by its stakeholders rather than a central authority.

The core logic revolves around a challenge-and-vote process. When an entry is proposed with a stake, a challenge period begins. Other token holders can dispute the entry by matching the stake, triggering a vote. All token holders then vote to either keep or remove the entry. The voting outcome determines which party loses their stake—a process known as slashing. The loser's stake is typically distributed between the winner and a reward pool for voters. This creates a powerful disincentive against listing spam or malicious entries, as incorrect stakers face direct financial loss.

Implementing slashing requires careful state management. Your smart contract must track several key parameters: the challengeStake amount, the challengePeriodDuration, and the commitPeriodDuration for vote hiding. A typical slashing function, invoked after a vote resolves, might look like this Solidity snippet:

solidity
function _slashAndResolve(address _loser, address _winner) internal {
    uint256 loserStake = stakes[_loser];
    stakes[_loser] = 0;
    // Reward the winner with a portion of the slashed stake
    uint256 winnerReward = loserStake * REWARD_PERCENT / 100;
    token.transfer(_winner, winnerReward);
    // Send the remainder to a reward pool for voters
    token.transfer(rewardPool, loserStake - winnerReward);
}

This function zeroes out the loser's stake and distributes it, enforcing the economic penalty.

To set up a TCR for content approvers, you must define what constitutes a valid entry. This could be an Ethereum address, a DID (Decentralized Identifier), or an NFT representing identity. The contract's apply function should require a stake and emit an event to signal the start of the application period. It's critical to use a secure oracle or a decentralized voting system like Kleros for resolving challenges if your logic extends beyond simple token-weighted votes. The contract should also include a withdraw function allowing users to retrieve their stake after an entry is firmly accepted and all challenge periods have expired.

Key security considerations include preventing reentrancy attacks when transferring slashed tokens and ensuring vote dilution attacks are mitigated. Use the Checks-Effects-Interactions pattern and consider implementing a commit-reveal voting scheme to prevent last-minute manipulation. The challengeStake amount should be dynamically adjustable via governance to adapt to the token's market value. By correctly implementing this staking and slashing logic, you create a self-sustaining, community-moderated registry where the economic cost of dishonesty outweighs the potential benefit, ensuring a high-quality, trusted list of content approvers.

voting-integration
TUTORIAL

Integrating a Voting Mechanism

A step-by-step guide to implementing a Token-Curated Registry (TCR) for managing a decentralized list of content approvers using smart contracts.

A Token-Curated Registry (TCR) is a decentralized list where entry and removal are governed by token-holder votes. In this guide, we'll build a TCR for a list of trusted content approvers. The core mechanism involves a challenge period: any listed member can be challenged by staking tokens, triggering a community vote to determine if they should remain. This creates a self-policing system where the economic stake of token holders aligns with maintaining a high-quality registry. We'll implement this using Solidity on Ethereum-compatible chains.

The smart contract structure requires several key state variables: a mapping of address to Member structs, a challenge struct to track active disputes, and the token contract interface for the native governance token (e.g., an ERC-20). The Member struct should store the member's applicationTimestamp, unstakedDeposit, and challengeId. The primary functions are apply(), challenge(), vote(), and resolveChallenge(). Applicants must stake a deposit to apply, which is locked until they are removed or choose to exit.

Here is a simplified code snippet for the challenge and voting logic. When a member is challenged, a voting period begins. We use a commit-reveal scheme or a simple snapshot to prevent vote manipulation.

solidity
function challengeMember(address _member) external {
    require(members[_member].exists, "Not a member");
    require(activeChallenge[_member] == 0, "Already challenged");
    
    uint challengeId = ++challengeCounter;
    challenges[challengeId] = Challenge({
        challenger: msg.sender,
        member: _member,
        votesFor: 0,
        votesAgainst: 0,
        resolved: false
    });
    activeChallenge[_member] = challengeId;
    // Transfer challenge stake from msg.sender
}

Voting requires token holders to stake their tokens, which are locked for the duration of the challenge. The outcome is determined by which side accumulates more vote weight (token stake). If the challenge succeeds, the member is removed and their deposit is split between the challenger and voters. If it fails, the challenger's stake is forfeited. This skin-in-the-game design discourages frivolous challenges. After deploying, you must integrate a front-end to display the registry, active challenges, and facilitate voting, connecting via libraries like ethers.js or web3.js.

Key security considerations include: preventing reentrancy in reward distribution, ensuring accurate token accounting, and setting appropriate parameters for applyFee, challengePeriodDuration (e.g., 7 days), and commitPeriodDuration. Tools like OpenZeppelin's SafeERC20 and ReentrancyGuard are recommended. For production, consider using a governance framework like Compound's Governor or a TCR-specific library to reduce audit surface. Always test thoroughly on a testnet (like Sepolia) before mainnet deployment.

This TCR pattern is extensible. You can modify it to curate not just people, but also datasets, smart contract addresses, or API endpoints. The economic guarantees ensure the list's quality scales with the value of the underlying token. For further reading, review the original TCR whitepaper by Mike Goldin and implementations like AdChain or Kleros TCR.

frontend-integration
TUTORIAL

Building a Frontend Interface

A step-by-step guide to creating a React-based UI for a token-curated registry (TCR) that manages content approvers.

A token-curated registry (TCR) is a decentralized list where inclusion is governed by token holders. For a content moderation system, this list contains the addresses of trusted content approvers. The frontend's primary job is to let users view the registry, propose new approvers, challenge existing ones, and participate in governance votes. We'll build this using React, ethers.js for blockchain interaction, and a simple smart contract with functions like apply(), challenge(), and vote(). The UI must connect to a user's wallet (e.g., MetaMask) to sign transactions.

Start by initializing a React project with create-react-app and install the necessary dependencies: ethers, @web3-react/core, and @web3-react/injected-connector. Set up a Web3ReactProvider to manage the wallet connection state globally. Create a connection button that triggers MetaMask. Once connected, your app can read the TCR contract's state. Use the useEffect hook to call view functions like getApprovers() to fetch and display the current list of approved addresses and their staked token balances.

The core interactive components are the Proposal and Challenge forms. A proposal form submits a transaction to the apply(address applicant, uint stake) function, locking the proposer's tokens as a deposit. A challenge form calls challenge(uint index) on a listed approver, initiating a dispute period. For both actions, you must display the transaction status (pending, confirmed, failed) using the transaction receipt from ethers. It's critical to estimate gas and handle user rejection errors gracefully to avoid a poor UX.

During a challenge's vote period, token holders can commit their votes. Implement a voting interface that calls vote(uint challengeId, bool supports). To ensure transparency, fetch and display the vote tally and time remaining until the challenge resolves. Use a library like date-fns to format the deadline. After the vote ends, any user can trigger the resolveChallenge() function to execute the outcome, which will either remove the approver (and slash their stake) or reaffirm them.

For a polished interface, add real-time updates using event listeners. Your smart contract should emit events like ApplicantApplied and ChallengeCreated. Use the contract.on method in ethers to listen for these events and update the UI state accordingly, so users see new proposals or challenges without refreshing the page. This creates a dynamic, application-like experience. Always include a block explorer link for every transaction to allow for easy verification.

Finally, consider security and testing. Use environment variables to store your contract address and ABI. Write integration tests with Jest and ethers that simulate the full flow: connecting a wallet, proposing an approver, and challenging an entry. For production, audit your frontend for common Web3 pitfalls like injection attacks and ensure all contract calls validate user input. The complete code for this tutorial is available in the Chainscore TCR Frontend Repository.

TOKEN-CURATED REGISTRY

Frequently Asked Questions

Common questions and troubleshooting for developers implementing a TCR for content moderation or approval systems.

A Token-Curated Registry (TCR) is a decentralized list where token holders govern the inclusion and removal of items. For content approvers, the registry lists vetted entities (e.g., reviewers, moderators, publications). The core mechanism involves:

  • Staking: An applicant stakes tokens to propose their addition to the registry.
  • Challenges: Any token holder can challenge an entry by matching the stake, triggering a vote.
  • Voting: Token holders vote to accept or reject the application/challenge. Voters are rewarded from the loser's stake.
  • Slashing: An applicant who loses a challenge forfeits their stake.

This creates a cryptoeconomic incentive for the crowd to curate a high-quality list, as token value is tied to the registry's usefulness. Popular implementations include the AdChain Registry for advertising and Kleros TCR for various lists.

conclusion
IMPLEMENTATION SUMMARY

Conclusion and Next Steps

You have successfully configured a Token-Curated Registry (TCR) to manage content approvers. This guide covered the core components: token staking, challenge mechanisms, and voting logic.

Your TCR is now a functional, on-chain governance layer for your content platform. Key parameters like the challengePeriodDuration, deposit, and commitStageLength are set, defining the economic security and dispute timeline. The contract enforces that only approved addresses—those who have staked tokens and survived a challenge—can perform privileged actions like publishing or curating content. This creates a Sybil-resistant system where reputation is tied to financial stake.

For production deployment, several critical steps remain. First, thoroughly audit the smart contract code, focusing on the challenge and voting logic. Consider using a service like OpenZeppelin Defender for admin operations and automated monitoring. You must also design a front-end dApp that interfaces with your TCR contract, allowing users to easily apply, stake tokens, and participate in challenges. The wagmi or ethers.js libraries are excellent choices for this integration.

To enhance your TCR, consider implementing advanced features. A delegated voting system can improve participation by allowing token holders to delegate their voting power. Partial unlocking of staked tokens over time can reduce liquidity pressure on participants. For content-specific use, you could integrate an off-chain data attestation service like EAS (Ethereum Attestation Service) to link on-chain registry status with detailed reviewer credentials or performance metrics stored on IPFS or Ceramic.

The next phase involves growing and managing the registry community. Develop clear documentation and onboarding materials for potential approvers. Monitor key metrics: application rate, challenge success/failure ratios, and the average stake amount. Be prepared to use the TCR's own governance to propose and vote on parameter adjustments (e.g., changing the required stake) as the platform scales, ensuring the system remains secure and accessible.

How to Build a Token-Curated Registry for Content Moderators | ChainScore Guides