TokenF Core
Last updated
Last updated
The central part of the framework is the TokenF smart contract which represents one particular tokenized asset. TokenF is an ERC-20 compatible contract built on a Diamond architecture, which allows for a seamless extension of standard TokenF functionality. One of the key benefits is that Diamond architecture enables contracts to bypass the maximum bytecode size restriction, making complex rules and checks feasible. Another crucial aspect of TokenF architecture is the modules. Every TokenF-based token may be additionally configured with a custom set of modules that work as standalone token plugins. Modules are composed of rules and checks that users have to abide by to use the token. These modules, in turn, integrate with 3rd-party KYC providers, monitoring services, etc.
The diagram depicts the basic TokenF structure. The token itself is a Diamond proxy contract that extends the ERC-20 and AccessControl
smart contracts while containing KYCCompliance
and RegulatoryCompliance
as standard Diamond facets.
Custom modules may be plugged into the corresponding KYCCompliance
and RegulatoryCompliance
facets, giving you flexibility in following any business logic through custom integrations.
TokenF contract extends AccessControl
(from Openzeppelin) functionality, which gives you convenience in managing existing roles in the tokenization project.
The role list may be configured according to the given tokenization logic and may include:
“DAO” role with access to mint
, burn
, recover
, and forced transfer
functions.
“Investor” role for transfer
and transfer from
functions.
“Compliance officer” role for setting up and updating compliance rules.
Having custom roles set does not limit you from checking that users follow regulatory compliance rules. Instead, access control allows for an additional layer of security and customization.
As mentioned above, TokenF is a Diamond contract with two facets included in the base implementation:
Compliance Facet (RegulatoryCompliance
)
KYC Facet (KYCCompliance
)
These facets enable module management functionality for compliance contracts, as well as functions for validating rules from the corresponding compliance interfaces.
The compliance control is achieved via hooks that are called every time the user interacts with the TokenF token. These hooks initialize the assembly of “context” that will be forwarded to the plugged-in modules:
_canTransfer
- Read-only hook that is used by RegulatoryCompliance
modules to check if the token transfer (amount) satisfies assigned rules.
_isKYCed
- Read-only hook that is used by KYCCompliance
modules to check that users follow the set of KYC rules.
_transferred
- Read / Write hook that is used by RegulatoryCompliance
modules to update some custom state of the accounting system.
Let’s take a look at the TokenF transfer
function execution flow. By KYC compliance rules we assume that the token sender and receiver have to be KYCed by possessing an SBT token from the trusted KYC provider. Additionally, we assume that not less than 1 token could be sent. In the TokenF terms, we treat this rule as a compliance rule.
As soon as the sender executes transfer
transaction, the _canTransfer
hook is called and the minimal transferred amount is checked. If the amount is less than 1 token, the transaction will be reverted. Then, the _isKYCed
hook gets called. It checks that both the token sender and receiver possess SBT tokens from the KYC provider. If that check fails, the transaction will be reverted.
Having all checks pass, _transferred
hook gets called after the successful token transfer (balance update). The hook can be used, for example, to update the transaction count for statistics, analytics, or other purposes.
When users interact with the TokenF contract, a certain context is forwarded to the Compliance and KYC modules. Depending on that context, regulatory checks are configured. The Context
contains the following information:
Function called (selector), for example, transfer
, transfer from
, or other function;
Address from which tokens are transferred;
Address to which tokens are transferred;
Amount of tokens transferred;
Address of an operator;
Additional bytes data for custom compliance checks;
Simply put, context-based flow allows us to implement diverse regulatory checks. For example, for transfer function, the minimum transaction amount as 1 token can be set. However, transfer from function may be configured with a minimum amount of 10 tokens.
Moreover, the explicitness of the context makes it possible to implement different rules for different users (sender
/ recipient
/ operator
). You may specify the sender to hold a certain amount of tokens before they are allowed to spend them.
All these checks are explicitly configured via handlers.
Handler is an internal function of a module-contract with a specific signature (specified below) that internally implements the KYC / regulatory checks required for the business logic.
These handler functions are associated with special “claim topics” (claim topic => handler function) that in turn describe what these handlers do. The handler function may integrate with some external service to authenticate the user / perform some accounting system state checks.
In a nutshell, a claim topic is an identifier of the handler function that will perform some compliance checks. Since there may be more than one check to execute, the framework has to support the set of claim topics to be configured.
Indeed, the configuration is performed through the specification of “claim topic key” that points to the array of “claim topics”. During the transaction execution, the TokenF module is passed an “execution context” (section 2.4.) which eventually becomes the source of “claim topic key” construction. The associated “claim topics” are extracted from this “claim topic key”, the corresponding “handler functions” are obtained, and, at last, called.
In the basic implementation, the claim topic key is formed as keccak256(function selector)
.
Suppose we want to add a rule to the token contract that no more than 10 ether tokens can be transferred per transaction. In this case, we can define a bytes32 public constant MIN_TRANSFER_LIMIT_TOPIC = keccak256("MIN_TRANSFER_LIMIT")
claim topic, for which we will set up a handler:
We will associate the MIN_TRANSFER_LIMIT_TOPIC
with the “claim topic key” keccak256(transfer.selector)
. During the transaction execution this “claim topic key” will be reconstructed from the given “execution context” (TokenF will take the context selector) and perform the compliance flow.