Skip to main content
Create your own

Integrating External Identity Registries

Hello! Welcome to the next lesson in our journey to build a compliant security token.

In our last session, we designed the high-level architecture for our token's compliance mechanism. We established that our SecurityToken contract would consult an external InvestorRegistry before allowing a transfer, using the _beforeTokenTransfer hook. We even sketched out a simple "interface" to define this communication.

Today, we will formalize that concept. The goal of this lesson is to implement an interface for an external identity registry contract. This is a crucial step in professional smart contract development. For someone with your experience in designing data systems, this is analogous to defining a formal API specification, like OpenAPI/Swagger, before building the microservice that implements it. It's about creating a clear, unambiguous "contract" that governs how different components of a system will communicate, ensuring they can be developed, tested, and even upgraded independently.

What is a Solidity Interface and Why is it Essential?

Imagine our SecurityToken contract is deployed on the Ethereum mainnet. The InvestorRegistry it needs to talk to is also deployed, but at a different address. How can our token contract make a call to the registry without having its full source code? More importantly, how can it trust that the registry has the exact isVerified function it expects to call?

This is where Solidity interface contracts come in. An interface is a collection of function definitions without any implementation. It defines a contract's external-facing API, specifying function names, parameters, and return types, but contains no logic.

To get a quick and clear overview of this concept, please watch the following video.

Interface | Solidity 0.8

This video from Smart Contract Programmer provides an excellent, concise explanation of what interfaces are, why they are needed, and how to use them.

Please watch the entire video. Pay close attention to these key moments: The motivation for using an interface: to call contracts without their source code. The syntax for defining an interface, including the I naming convention and the use of semicolons instead of function bodies. The implementation, which shows how to use the interface with a contract's address to make a call.

As the video explains, using an interface provides three main benefits:

  1. Decoupling: It allows the SecurityToken and InvestorRegistry to be developed and deployed completely independently. As long as the registry implements the interface correctly, the token can interact with it.
  2. Interoperability: Interfaces are the foundation of standards like ERC-20 or ERC-721. They ensure that any token implementing the standard can be used by any wallet or application that understands the standard's interface.
  3. Code Size and Gas: By using an interface, you avoid including the full source code of the contract you are calling, which keeps your own contract smaller and cheaper to deploy.

The image below shows an example in the Remix IDE. The Interaction contract uses the ICounter interface to define how it will call the separate Counter contract. This is exactly the pattern we will follow.

This screenshot shows an `Interaction` contract in Solidity, which uses an `ICounter` interface to interact with a separate `Counter` contract. Notice how the `Interaction` contract holds the address of the `Counter` contract and uses the interface to call its `getCount` function. This cleanly separates the two contracts.

Defining Our Formal IInvestorRegistry Interface

In the previous lesson, we conceptualized a simple isVerified function. Now, let's create the formal interface in its own Solidity file. Standard practice is to name the file and the interface with a capital I prefix, so we will create IInvestorRegistry.sol.

This interface will declare all the functions that our system's InvestorRegistry must provide. For now, we will stick to the core functions we've discussed: checking if an investor is verified and retrieving their compliance tier.

Here is the complete code for our IInvestorRegistry.sol file:

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

/**
 * @title IInvestorRegistry
 * @dev Interface for a contract that manages investor identity and verification status.
 * This defines the standard functions that a Security Token can use to query
 * compliance information about an investor.
 */
interface IInvestorRegistry {
    /**
     * @notice Checks if an investor's address is on the allowlist and verified.
     * @param investor The address of the investor to check.
     * @return bool True if the investor is verified, false otherwise.
     */
    function isVerified(address investor) external view returns (bool);

    /**
     * @notice Retrieves the compliance tier of a given investor.
     * @param investor The address of the investor.
     * @return uint8 The numerical tier (e.g., 0 for None, 1 for Tier A, etc.).
     */
    function getInvestorTier(address investor) external view returns (uint8);
}

Notice that the functions are declared with external visibility and end with a semicolon. They have no function body ({...}). This is the "blueprint" our actual registry contract will have to follow.

From Our Blueprint to an Industry Standard: ERC-3643

Our simple interface is a great start, but it's valuable to see what a production-grade, standardized interface looks like. The ERC-3643 (T-REX) standard is designed for exactly this purpose: providing a comprehensive framework for tokenized securities.

A key part of its modular architecture is the IdentityRegistry. The diagram below illustrates its role: the token contract initiates checks with an external validator (the Identity Registry) before completing a transfer.

This diagram shows a peer initiating a transaction. The T-REX token then calls an external validator to check identities. Only after receiving a positive status does the token execute the transfer. The validator in this flow is a contract that implements the `IIdentityRegistry` interface.

To see what this professional interface looks like, we'll examine the official T-REX smart contracts, as documented in a security audit report.

[PDF] Tokeny Smart Contract Code Review and Security Analysis

This document contains a security audit of the T-REX contracts. It includes the full source code for the interfaces, which is exactly what we need.

Find the file IIdentityRegistry.sol within the PDF. You can search for the text IIdentityRegistry.sol to locate it. Please read through the function declarations in this interface. You don't need to memorize them all, but focus on identifying functions like registerIdentity, deleteIdentity, and, most importantly, isVerified.

You'll notice that the T-REX IIdentityRegistry is far more comprehensive than our simple version. It includes functions for registration, batch operations, and managing claims. However, the fundamental principle is identical. It defines a clear, public API for an identity management contract, and its isVerified function serves the same purpose as ours. Our simplified design is a subset of this robust, industry-tested standard.

Conclusion

In this lesson, we formalized the concept of contract-to-contract communication by implementing a Solidity interface. This is not just a theoretical exercise; it's a fundamental pattern for building secure, modular, and interoperable dApps, especially in the context of tokenized assets where components like identity, compliance, and the token itself must work together seamlessly.

Key Takeaways:

  • Interfaces Define a Contract's API: They specify what functions can be called (the signature) but not how they are implemented.
  • Decoupling is Key: Using interfaces allows you to build systems from independent, upgradeable components. The token doesn't need to know the internal logic of the registry, only that it adheres to the IInvestorRegistry interface.
  • The Syntax: An interface is defined with the interface keyword, typically prefixed with an I, and contains only function signatures ending in semicolons.
  • Industry Standards: Real-world standards like ERC-3643 rely heavily on interfaces (IIdentityRegistry, ICompliance, etc.) to create their modular architecture.

We have now defined the "what"—the API blueprint for our identity system. In our next lesson, we will focus on the "how": we will implement a basic, on-chain allowlist that serves as our first InvestorRegistry, bringing the interface we designed today to life.

Can't find a good explanation? Sign up and we'll make it for you

Sign up