facebook

ERC-3643 RWA Tokenization | Create KYC-Compliant Permitted Tokens

Posted By : Tushar

Apr 30, 2025

ERC-3643, often known as 'permissioned tokens,' 'RWA tokens,' or T-REX (Token for Regulated EXchanges), is a standard that provides a framework for tokenising real-world assets while complying with regulatory requirements. In accordance with pertinent financial legislation, this protocol permits the depiction of conventional assets on blockchain networks, such as debt instruments, real estate, and equity shares. Unlike other bitcoin tokens, ERC-3643 has built-in compliance features that enforce legal requirements over the token's entire existence. A crucial aspect of this standard is the mandatory identity verification procedure, which requires all token holders to finish Know Your Customer (KYC) and Anti-Money Laundering (AML) procedures before to being able to participate in the ecosystem.

 

ERC-3643 Smart Contract and RWA Tokenization | Technological Components 

 

  • Identity management integration using ONCHAINID technology
  • Storage of verified participant credentials on the chain
  • Rules for programmable compliance that guarantee each transaction is verified
  • Automated implementation of legal obligations

 

Despite these advanced compliance features that facilitate easier integration with wallets, exchanges, and other blockchain platforms, ERC-3643 remains fully compatible with the existing ERC-20 infrastructure. ERC-3643 is an excellent choice for institutional use and security token sales when adherence to the law is essential due to its mix of technological compatibility and legal compliance.

 

This standard marks a significant breakthrough in the integration of blockchain technology with traditional financial institutions by providing digital assets with the programmability and legal certainty necessary for the tokenisation of physical assets.

 

Also, Check | Create DeFi Index Fund with Custom ERC-4626 Tokenized Vaults

 

Bringing ERC-3643 to Life for Regulated Token Transfers with the T-REX Protocol

 

A useful use of the ERC-3643 Ethereum standard, the T-REX protocol was created by Tokeny Solutions to facilitate the compliance issues, transfer, and administration of security tokens. While ERC-3643 lays out the guidelines for token compliance, T-REX uses a collection of Solidity smart contracts to make these guidelines a reality. By combining identity registries and compliance checks, this system guarantees that all token-related activities adhere to legal standards. Based on T-REX's approach, ERC-3643 was officially adopted as an Ethereum standard in December 2023. It is now recognised as a reliable framework for compliant tokenisation by institutions and authorities around the world.

 

Implementation of RWA Tokenization with ERC-3643: 

 

Token.sol file 

 

pragma solidity 0.8.17;

import '@onchain-id/solidity/contracts/interface/IIdentity.sol';
import './TokenStorage.sol';
import '../roles/AgentRoleUpgradeable.sol';

contract Token is AgentRoleUpgradeable, TokenStorage {

    modifier whenNotPaused() {
        require(!_tokenPaused, 'Pausable: paused');
        _;
    }

    modifier whenPaused() {
        require(_tokenPaused, 'Pausable: not paused');
        _;
    }

    function init(
        address _identityRegistry,
        address _compliance,
        string memory _name,
        string memory _symbol,
        uint8 _decimals,
        address _onchainID
    ) external initializer {
        require(owner() == address(0), 'already initialized');
        require(_identityRegistry != address(0) && _compliance != address(0), 'invalid argument');
        require(bytes(_name).length > 0 && bytes(_symbol).length > 0, 'invalid argument');
        require(_decimals <= 18, 'invalid decimals');
        
        __Ownable_init();
        
        _tokenName = _name;
        _tokenSymbol = _symbol;
        _tokenDecimals = _decimals;
        _tokenOnchainID = _onchainID;
        _tokenPaused = true;

        setIdentityRegistry(_identityRegistry);
        setCompliance(_compliance);

        emit UpdatedTokenInformation(_tokenName, _tokenSymbol, _tokenDecimals, _TOKEN_VERSION, _tokenOnchainID);
    }

    function approve(address _spender, uint256 _amount) external override returns (bool) {
        _approve(msg.sender, _spender, _amount);
        return true;
    }

    function transfer(address _to, uint256 _amount) public override whenNotPaused returns (bool) {
        require(!_frozen[_to], 'Wallet is frozen');
        return _transfer(msg.sender, _to, _amount);
    }

    function transferFrom(
        address _from,
        address _to,
        uint256 _amount
    ) external override whenNotPaused returns (bool) {
        require(!_frozen[_to] && !_frozen[_from], 'wallet is frozen');
        _approve(_from, msg.sender, _allowances[_from][msg.sender] - _amount);
        _transfer(_from, _to, _amount);
        return true;
    }

    function setName(string calldata _name) external onlyOwner {
        require(bytes(_name).length > 0, 'invalid argument');
        _tokenName = _name;
        emit UpdatedTokenInformation(_tokenName, _tokenSymbol, _tokenDecimals, _TOKEN_VERSION, _tokenOnchainID);
    }

    function setSymbol(string calldata _symbol) external onlyOwner {
        require(bytes(_symbol).length > 0, 'invalid argument');
        _tokenSymbol = _symbol;
        emit UpdatedTokenInformation(_tokenName, _tokenSymbol, _tokenDecimals, _TOKEN_VERSION, _tokenOnchainID);
    }

    function pause() external onlyAgent whenNotPaused {
        _tokenPaused = true;
        emit Paused(msg.sender);
    }

    function unpause() external onlyAgent whenPaused {
        _tokenPaused = false;
        emit Unpaused(msg.sender);
    }

    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    function _transfer(address sender, address recipient, uint256 amount) internal returns (bool) {
        _balances[sender] -= amount;
        _balances[recipient] += amount;
        emit Transfer(sender, recipient, amount);
        return true;
    }

    function name() public view override returns (string memory) {
        return _tokenName;
    }

    function symbol() public view override returns (string memory) {
        return _tokenSymbol;
    }

    function decimals() public view override returns (uint8) {
        return _tokenDecimals;
    }

    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address account) public view override returns (uint256) {
        return _balances[account];
    }

    function allowance(address owner, address spender) public view override returns (uint256) {
        return _allowances[owner][spender];
    }
}

 

With features like role-based access control, token information updating, and transfer pausing, this Solidity contract implements a token. To manage roles and keep track of token information, it extends AgentRoleUpgradeable and TokenStorage. Functions for transferring, authorizing, and managing allowances are included in the contract, along with provisions for frozen wallets and paused status. To change the token's name or symbol, only the owner can do it. Transfers can be paused or resumed by agents, and the contract tracks state changes by emitting events. Enhanced access control is integrated with ERC-20 capabilities.

 

Also, Discover | ERC 4337 : Account Abstraction for Ethereum Smart Contract Wallets



Token Storage file:

 

pragma solidity 0.8.17;
import '../compliance/modular/IModularCompliance.sol';
import '../registry/interface/IdentityRegistry.sol';

contract TokenStorage {

    mapping(address => uint256) internal _balances;
    mapping(address => mapping(address => uint256)) internal _allowances;
    uint256 internal _totalSupply;
    string internal _tokenName;
    string internal _tokenSymbol;
    uint8 internal _tokenDecimals;
    address internal _tokenOnchainID;
    string internal constant _TOKEN_VERSION = '4.1.3';

    
    mapping(address => bool) internal _frozen;
    mapping(address => uint256) internal _frozenTokens;

    bool internal _tokenPaused = false;

    
    IIdentityRegistry internal _tokenIdentityRegistry;

   
    IModularCompliance internal _tokenCompliance;

 
    uint256[49] private __gap;
}

 

Token properties such as name, symbol, and decimals, as well as balances, allowances, and the total supply, are all stored in the Token Storage contract. In addition to tracking paused and suspended accounts, it connects with the identity registry and external compliance contracts. For upcoming upgrades, a gap array is also included in the contract.

 

pragma solidity 0.8.17;

import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';

import './Roles.sol';

contract AgentRoleUpgradeable is OwnableUpgradeable {
    using Roles for Roles.Role;

    Roles.Role private _agents;

    event AgentAdded(address indexed _agent);
    event AgentRemoved(address indexed _agent);

    modifier onlyAgent() {
        require(isAgent(msg.sender), 'AgentRole: caller does not have the Agent role');
        _;
    }

    function addAgent(address _agent) public onlyOwner {
        require(_agent != address(0), 'invalid argument - zero address');
        _agents.add(_agent);
        emit AgentAdded(_agent);
    }

    function removeAgent(address _agent) public onlyOwner {
        require(_agent != address(0), 'invalid argument - zero address');
        _agents.remove(_agent);
        emit AgentRemoved(_agent);
    }

    function isAgent(address _agent) public view returns (bool) {
        return _agents.has(_agent);
    }
}

 

Balances, allowances, the total supply, and token properties like name, symbol, and decimals are all stored in the Token Storage contract. It interfaces with identity registry and external compliance contracts, and it keeps track of stopped and suspended accounts. Additionally, a gap array for upcoming updates is included in the contract.

 

Roles.sol File

 

pragma solidity 0.8.17;
library Roles {
    struct Role {
        mapping(address => bool) bearer;
    }


    function add(Role storage role, address account) internal {
        require(!has(role, account), 'Roles: account already has role');
        role.bearer[account] = true;
    }


    function remove(Role storage role, address account) internal {
        require(has(role, account), 'Roles: account does not have role');
        role.bearer[account] = false;
    }


    function has(Role storage role, address account) internal view returns (bool) {
        require(account != address(0), 'Roles: account is the zero address');
        return role.bearer[account];
    }
}

 

The Roles library provides a reusable structure to manage role-based access control in Solidity. It defines a Role struct that maps addresses to a boolean indicating role ownership. The library includes functions to add and remove roles, ensuring no duplicates or removals of non-existent roles, and a check function to verify if an address holds a role.

 

You may also like | A Guide to Implementing NFT Royalties on ERC-721 & ERC-1155

 

Conclusion


More than just a foundation for traditional financial assets, ERC 3643 has developed into much more. It establishes the framework for managing and digitising a variety of asset categories that were previously thought to be difficult or prohibitive to tokenise. ERC3643 stands out as a crucial solution in the developing field of asset tokenization, ensuring safe and legal ownership, access, transferability, and usage of digital assets, thanks to its integrated regulatory compliance measures and flexible, scalable design. In case you are looking to explore RWA tokenization, connect with our skilled blockchain developers

Leave a

Comment

Name is required

Invalid Name

Comment is required

Recaptcha is required.

blog-detail

June 26, 2025 at 12:53 pm

Your comment is awaiting moderation.

bg bg

What's Trending in Tech

bg

Our Offices

India

INDIA

DG-18-009, Tower B,
Emaar Digital Greens, Sector 61,
Gurugram, Haryana
122011.
Unit- 117-120, First Floor,
Welldone Tech Park,
Sector 48, Sohna road,
Gurugram, Haryana
122018.
USA

USA

30N, Gloud St STR E, Sheridan, Wyoming (USA) - 82801
Singapore

SINGAPORE

10 Anson Road, #13-09, International Plaza Singapore 079903.

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.