facebook

Creating a Token Curated Registry (TCR) on Ethereum

Posted By : Kapil

Dec 24, 2024

What is a TCR (Token Curated Registry)

 

A Token Curated Registry (TCR) is an incentivized voting system that helps create and maintain trusted lists, managed by the users themselves. Utilizing the "Wisdom of the Crowds" concept, participants vote with tokens to determine which submissions are valid and should be included on the list.

 

In blockchain app development, TCRs play a vital role in curating and managing lists of information via blockchain technology. These registries are powered by community-driven efforts, ensuring the quality and reliability of data. TCRs replace traditional centralized systems with transparent, trustless alternatives, aligned with the goals of Web3 consulting services, which focus on decentralized solutions for list management.

 

A Token Curated Registry (TCR) operates on three key components:


1. Token Economy: The native token serves as a stake for participants, incentivizing accurate curation through voting and staking.
2. Governance Structure: Smart contracts enforce transparent and automated rules for voting, entry evaluation, and dispute resolution, ensuring fairness and reducing bias.
3. Curation Process: Community-driven proposals, voting, and maintenance ensure high-quality entries, leveraging the token economy and governance.


These components create a decentralized, efficient, and robust system for managing information, aligning with Web3 solutions.

 

 

Registration Period
 

A new restaurant, "Tommy's Taco's" - thinks they're worthy of being included; so they submit a deposit using the TCR's token. This begins, the "registration period." If the community agrees to include Tommy's Tacos into the registry, everyone simply waits for a registration period to expire and the submission is added.

 

Challenge Period
 

If the community believes a submission should not be included, a 'challenge period' is triggered. A challenge begins when a user matches the submission deposit, prompting a vote.

 

All token holders can then vote to either include or exclude 'Tommy's Tacos' from the list.

 

  • If the vote favors exclusion, 'Tommy's Tacos' loses its deposit, which is redistributed to the challenger and those who voted for exclusion.
  • If the vote favors inclusion, the challenger's deposit is forfeited and redistributed to those who voted for inclusion.

     


// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/access/Ownable.sol';

contract MyERC20Token is ERC20, Ownable {
   constructor(string memory name, string memory symbol, uint256 initialSupply, address initialOwner) 
       ERC20(name, symbol) 
       Ownable(initialOwner) 
   {
       _mint(initialOwner, initialSupply);
   }

   function mint(address to, uint256 amount) external onlyOwner {
       _mint(to, amount);
   }

   function burn(address from, uint256 amount) external onlyOwner {
       _burn(from, amount);
   }
}

contract TokenCuratedRegistry {
   struct Listing {
       address proposer;
       uint256 deposit;
       bool approved;
       uint256 challengeEnd;
       uint256 voteCount;
       mapping(address => bool) voters;
   }

   MyERC20Token public token;

   mapping(bytes32 => Listing) public listings;
   uint256 public challengePeriod;
   uint256 public minDeposit;

   event ListingProposed(bytes32 indexed listingId, address proposer, uint256 deposit);
   event ListingChallenged(bytes32 indexed listingId, address challenger);
   event ListingApproved(bytes32 indexed listingId);
   event ListingRejected(bytes32 indexed listingId);

   constructor(address _token, uint256 _minDeposit, uint256 _challengePeriod) {
       require(_token != address(0), 'Invalid token address');
       token = MyERC20Token(_token);
       minDeposit = _minDeposit;
       challengePeriod = _challengePeriod;
   }

   function proposeListing(bytes32 listingId) external {
       require(listings[listingId].proposer == address(0), 'Listing already exists');
       require(token.transferFrom(msg.sender, address(this), minDeposit), 'Token transfer failed');

       listings[listingId].proposer = msg.sender;
       listings[listingId].deposit = minDeposit;
       listings[listingId].approved = false;
       listings[listingId].challengeEnd = block.timestamp + challengePeriod;
       listings[listingId].voteCount = 0;

       emit ListingProposed(listingId, msg.sender, minDeposit);
   }

   function challengeListing(bytes32 listingId) external {
       require(listings[listingId].proposer != address(0), 'Listing does not exist');
       require(block.timestamp <= listings[listingId].challengeEnd, 'Challenge period over');

       emit ListingChallenged(listingId, msg.sender);
   }

   function vote(bytes32 listingId, bool approve) external {
       require(listings[listingId].proposer != address(0), 'Listing does not exist');
       require(!listings[listingId].voters[msg.sender], 'Already voted');

       listings[listingId].voters[msg.sender] = true;
       if (approve) {
           listings[listingId].voteCount++;
       } else {
           listings[listingId].voteCount--;
       }
   }

   function finalize(bytes32 listingId) external {
       require(listings[listingId].proposer != address(0), 'Listing does not exist');
       require(block.timestamp > listings[listingId].challengeEnd, 'Challenge period not over');

       if (listings[listingId].voteCount > 0) {
           listings[listingId].approved = true;
           emit ListingApproved(listingId);
       } else {
           token.transfer(listings[listingId].proposer, listings[listingId].deposit);
           delete listings[listingId];
           emit ListingRejected(listingId);
       }
   }

   function withdrawDeposit(bytes32 listingId) external {
       require(listings[listingId].approved, 'Listing not approved');
       require(listings[listingId].proposer == msg.sender, 'Not proposer');

       token.transfer(listings[listingId].proposer, listings[listingId].deposit);
       delete listings[listingId];
   }
}

 

This code implements two Ethereum smart contracts:
 

MyERC20Token: A standard ERC20 token contract with added minting and burning functionality.
TokenCuratedRegistry: A Token Curated Registry (TCR) system that uses MyERC20Token for staking and manages a registry of items.


1. MyERC20Token Contract
 

This contract inherits from OpenZeppelin's ERC20 and Ownable contracts. It provides a secure, extensible implementation for creating ERC20 tokens.

 

Key Features:


Constructor:

 

  • Accepts token name, symbol, initial supply, and the owner's address.
  • Initializes ERC20 with the name and symbol.
  • Mints the initial supply of tokens to the owner.
     

Mint Function:

 

  • Allows the owner to mint new tokens.
  • Uses onlyOwner modifier to restrict access.
     

Burn Function:

 

  • Allows the owner to burn tokens from a specific address.
  • Uses onlyOwner modifier for access control.

 

Also, Check | Ethereum Distributed Validator Technology | DVT for Staking


2. TokenCuratedRegistry Contract


This contract allows users to propose, challenge, and vote on items in a registry. It leverages the MyERC20Token for deposits and voting power.

 

Key Components:


Struct: 

 

Listing:

 

Represents a registry entry with the following fields:


proposer: The address of the proposer.
deposit: Amount of tokens staked for the listing.
approved: Indicates whether the listing is approved.
challengeEnd: Timestamp when the challenge period ends.
voteCount: Tally of votes.
voters: Tracks addresses that have voted.
 

Variables:

token: Reference to the MyERC20Token used for staking.
listings: Mapping of listing IDs to their respective Listing structs.
challengePeriod: Time allowed for challenges.
minDeposit: Minimum token deposit required for a proposal.
 

Functions:

 

Constructor:

  • Accepts token contract address, minimum deposit, and challenge period.
  • Initializes the contract with these values.
     

Propose Listing:

 

  • Users propose a listing by staking tokens.
  • Tokens are transferred to the contract, and a new Listing struct is created.
  • Emits ListingProposed.


    Challenge Listing:

     

  • Allows users to challenge a listing within the challenge period.
  • Emits ListingChallenged.


    Vote:

 

  • Users can vote on a listing to approve or reject it.
  • Prevents double voting using a mapping.
  • Adjusts the voteCount based on the vote.
     

    Finalize:

     

  • Can be called after the challenge period ends.
  • If the vote count is positive, the listing is approved.
  • If negative, the listing is rejected, and the staked tokens are refunded.
  • Emits ListingApproved or ListingRejected.


    Withdraw Deposit:

     

  • Allows the proposer to withdraw their deposit if the listing is approved.
  • Deletes the listing entry.

 

You may also like | How to Create a Multi-Signature Wallet on Solana using Rust


Explanation of Workflow
 

Proposing a Listing:

 

  • A user calls proposeListing with a unique listingId.
  • The user deposits tokens into the contract.
  • A Listing struct is created, and the challenge period begins.


Challenging a Listing:

 

  • During the challenge period, any user can challenge the listing.
  • This initiates the voting phase.


Voting:

 

  • Users vote to approve or reject the listing by calling vote.
  • Each user can vote only once for a listing.
     

Finalizing:

 

  • After the challenge period, the finalize function is called.
  • If approved, the listing remains in the registry.
  • If rejected, the staked tokens are refunded to the proposer.
     

Withdrawing Deposits:

 

  • If a listing is approved, the proposer can withdraw their staked tokens using withdrawDeposit.

 

Security and Design Considerations
 

Reentrancy Protection:

 

  • The code assumes that token transfers are safe and non-reentrant.
  • For additional security, you may consider adding the ReentrancyGuard modifier.

 

Discover more | How to Deploy a Distributed Validator Node for Ethereum 2.0


Double Voting Prevention

 

The voter mapping ensures that users cannot vote multiple times.


Extensibility:

 

The MyERC20Token contract allows minting and burning, making it flexible for use in various scenarios.


Ownership:

 

The Ownable contract restricts certain functions like minting and burning to the contract owner.
 

Usage Example
 

Deploy MyERC20Token:


Provide the name, symbol, initial supply, and owner's address.

 

Deploy TokenCuratedRegistry:


Provide the address of the deployed MyERC20Token, the minimum deposit, and the challenge period.
 

Interact:

 

Users can propose, challenge, vote, finalize, and withdraw deposits using the respective functions.

 

Also, Read | Creating a Token Vesting Contract on Solana Blockchain

 

Importance of a Token Curated Registry (TCR)
 

Token Curated Registries (TCRs) play a vital role in the decentralized Web3 ecosystem due to their innovative approach to managing information. Here's why TCRs are important:

 

Decentralized Data Curation:

 

TCRs enable communities to collaboratively manage and curate high-quality lists without relying on centralized authorities. This fosters trust and transparency in decision-making.


Incentivized Participation:

 

The token economy ensures active engagement by rewarding honest behavior and penalizing malicious actions. Participants are motivated to contribute accurate and valuable information.
 

Quality Assurance:

 

The community-driven voting process ensures that only trustworthy and high-quality entries are included in the registry. It promotes accountability and discourages low-quality submissions.
 

Transparency and Trust:

 

Governance rules encoded in smart contracts ensure that the curation process is fair, transparent, and tamper-proof. Anyone can audit the on-chain activity.
 

Automation:

 

Smart contracts automate critical processes such as voting, staking, and dispute resolution, reducing overhead and human error. This creates an efficient system that operates independently.
 

Applications in Web3:

 

Reputation Systems: Curate lists of trusted participants or products in decentralized marketplaces.
Content Curation: Manage lists of valuable articles, assets, or media on decentralized platforms.
Token Listings: Curate quality tokens for decentralized exchanges or fundraising platforms.
 

Alignment with Web3 Principles:

 

TCRs embody the core values of Web3: decentralization, community empowerment, and censorship resistance. They provide a scalable solution for decentralized governance and information management.
 

Dispute Resolution:

 

TCRs offer built-in mechanisms for resolving disputes via challenges and community voting, ensuring that errors or biases are corrected. In summary, TCRs are essential for creating trustless, decentralized, and efficient systems for data and information management. They empower communities to curate valuable information while maintaining alignment with the principles of Web3 development.

 

Also, Discover | Integrate Raydium Swap Functionality on a Solana Program

 

Conclusion

 

In conclusion, Token Curated Registries (TCRs) offer a decentralized and efficient way to manage trusted lists in the Web3 ecosystem. By leveraging token-based incentives and community-driven governance, TCRs ensure transparency, quality, and accountability in data curation. This approach aligns with the core principles of Web3, empowering users to curate valuable information while eliminating the need for centralized authorities. If you are looking for blockchain development services, consider connecting with our blockchain developers to get started. 

Leave a

Comment

Name is required

Invalid Name

Comment is required

Recaptcha is required.

blog-detail

January 14, 2025 at 04:09 pm

Your comment is awaiting moderation.

By using this site, you allow our use of cookies. For more information on the cookies we use and how to delete or block them, please read our cookie notice.

Chat with Us
Telegram Button
Youtube Button

Contact Us

Oodles | Blockchain Development Company

Name is required

Please enter a valid Name

Please enter a valid Phone Number

Please remove URL from text