This proposal is a security token standard that extends ERC-1155 to provide a flexible framework for managing compliant real-asset security tokens. It introduces the concept of partitions, where each tokenId represents a distinct partition with its own set of rights and privileges. This makes it suitable for various use cases, particularly semi-fungible asset management. The standard also includes features like token locking, forced transfers for recovery, address freezing, payouts, and dynamic compliance management using off-chain vouchers.
Motivation
The growing demand for tokenized real-world assets necessitates a token standard that can accommodate the unique requirements of security tokens. Existing standards, while powerful, do not fully address the need for flexible partitioning and comprehensive compliance management.
Build upon of ERC-1155 to introduce partitions, allowing for the creation of semi-fungible tokens representing fractional ownership, different share classes, or other distinct units within a single token contract. This flexibility is crucial for tokenizing complex real-world assets like real estate or funds.
Furthermore, it includes features essential for security tokens, such as token locking for vesting or holding periods, forced transfers for recovery in case of lost keys, address freezing for regulatory compliance, efficient payout mechanisms, and dynamic compliance management using off-chain vouchers.
By providing a standardized interface for these features, this proposal aims to facilitate the development of interoperable and compliant security token ecosystems.
Specification
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119 and RFC 8174.
Interface
pragmasolidity^0.8.0;interfaceIERC7518isIERC1155,IERC165{eventTokensLocked(addressindexedaccount,uintindexedid,uint256amount,uint256releaseTime);eventTokenUnlocked(addressindexedaccount,uintindexedid);eventTokensForceTransferred(addressindexedfrom,addressindexedto,uintindexedid,uint256amount);eventAddressFrozen(addressindexedaccount,bytesdata);eventAddressUnfrozen(addressindexedaccount,bytesdata);// Emitted when the transferability of tokens with a specific ID is restricted.
eventTransferRestricted(uintindexedid);// Emitted when the transferability restriction of tokens with a specific ID is removed.
eventTransferRestrictionRemoved(uintindexedid);eventPayoutDelivered(addressindexedfrom,addressindexedto,uint256amount);/**
* @dev Retrieves the transferable balance of tokens for the specified account and ID.
* @param account The address of the account.
* @param id The token ID.
* @return The transferable balance of tokens.
*/functiontransferableBalance(addressaccount,uintid)externalviewreturns(uint);/**
* @dev Retrieves the locked balance of tokens for the specified account and ID.
* @param account The address of the account.
* @param id The token ID.
* @return The locked balance of tokens.
*/functionlockedBalanceOf(addressaccount,uint256id)externalviewreturns(uint256);/**
* @dev Restricts the transferability of tokens with the specified ID.
* @param id The token ID.
* @return A boolean value indicating whether the operation was successful.
*/functionrestrictTransfer(uintid)externalreturns(bool);/**
* @dev Removes the transferability restriction of tokens with the specified ID.
* @param id The token ID.
* @return A boolean value indicating whether the operation was successful.
*/functionremoveRestriction(uintid)externalreturns(bool);/**
* @notice Transfers `_value` amount of an `_id` from the `_from` address to the `_to` address specified (with safety call).
* @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard).
* After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
* @param _from Source address
* @param _to Target address
* @param _id ID of the token type
* @param _value Transfer amount
* @param _data Additional data with no specified format, MUST be sent unaltered in call to `onERC1155Received` on `_to`
*/functionsafeTransferFrom(address_from,address_to,uint256_id,uint256_value,bytescalldata_data)overrideexternal;/**
* @dev Checks if a transfer is allowed.
* @param from The address to transfer tokens from.
* @param to The address to transfer tokens to.
* @param id The token ID.
* @param amount The amount of tokens to transfer.
* @param data Additional data related to the transfer.
* @return status A boolean value indicating whether the transfer is allowed.
*/functioncanTransfer(addressfrom,addressto,uintid,uintamount,bytescalldatadata)externalviewreturns(boolstatus);/**
* @dev lock token till a particular block time.
* @param account The address of the account for which tokens will be locked.
* @param id The token ID.
* @param amount The amount of tokens to be locked for the account.
* @param releaseTime The timestamp indicating when the locked tokens can be released.
* @return bool Returns true if the tokens are successfully locked, otherwise false.
*/functionlockTokens(addressaccount,uintid,uint256amount,uint256releaseTime)externalreturns(bool);/**
* @dev Unlocks tokens that have crossed the release time for a specific account and id.
* @param account The address of the account to unlock tokens for.
* @param id The token ID.
*/functionunlockToken(addressaccount,uint256id)external;/**
* @dev Force transfer in cases like recovery of tokens.
* @param from The address to transfer tokens from.
* @param to The address to transfer tokens to.
* @param id The token ID.
* @param amount The amount of tokens to transfer.
* @param data Additional data related to the transfer.
* @return A boolean value indicating whether the operation was successful.
*/functionforceTransfer(addressfrom,addressto,uint256id,uint256amount,bytesmemorydata)externalreturns(bool);/**
* @dev Freezes specified address.
* @param account The address to be frozen.
* @param data Additional data related to the freeze operation.
* @return A boolean value indicating whether the operation was successful.
*/functionfreezeAddress(addressaccount,bytescalldatadata)externalreturns(bool);/**
* @dev Unfreezes specified address.
* @param account The address to be unfrozen.
* @param data Additional data related to the unfreeze operation.
* @return A boolean value indicating whether the operation was successful.
*/functionunFreeze(addressaccount,bytesmemorydata)externalreturns(bool);/**
* @dev Sends payout to single address with corresponding amounts.
* @param to address to send the payouts to.
* @param amount amount representing the payouts to be sent.
* @return A boolean indicating whether the batch payouts were successful.
*/*functionpayout(addresscalldatato,uint256calldataamount)publicreturns(bool);/**
* @dev Sends batch payouts to multiple addresses with corresponding amounts.
* @param to An array of addresses to send the payouts to.
* @param amount An array of amounts representing the payouts to be sent.
* @return A boolean indicating whether the batch payouts were successful.
*/functionbatchPayout(address[]calldatato,uint256[]calldataamount)publicreturns(bool);}
Methods for token
transferableBalance
Retrieves the transferable balance of tokens for the specified account and ID.
MUST calculate and return the transferable balance of tokens for the specified account and ID ie current balanceOf(account, id) - lockedBalanceOf(account, id).
lockedBalanceOf
Retrieves the locked balance of tokens for the specified account and ID.
MUST bypass normal transfer restrictions and authorization checks.
MUST revert if the from address is not Frozen.
MUST revert if to address is Frozen.
MUST ensure that only authorized entities have the capability to call this function.
Additional data related to the freeze operation.
SHOULD emit TokensForceTransferred.
freeze
Freezes specified address. The Freeze function takes in the account address to be frozen and additional data, and returns a boolean value indicating whether the operation was successful.
SHOULD implement appropriate access control measures to ensure that only authorized addresses can be unfrozen.
SHOULD emit AddressFrozen.
unFreeze
The Unfreeze function takes in the account address to be unfrozen and additional data, and returns a boolean value indicating whether the operation was successful.
SHOULD have sufficient balance to transfer token from issuer address.
SHOULD emit PayoutDelivered.
batchPayout
Send payouts to multiple addresses at once, with each address receiving a specific amount of tokens. It can be used for various purposes such as distributing rewards, dividends, or interest payment.
SHOULD have sufficient balance to transfer token from issuer address.
SHOULD emit PayoutDelivered.
Interoperability
This proposal facilitates interoperability with ERC-3643 tokens through a token wrapping method. The process involves two key components: the ERC-3643 token contracts representing the original and the proposed token contract for the wrapped version. Users seeking to wrap their tokens interact with the wrapping contract, which securely locks their original tokens and mints an equivalent amount of the proposed tokens to their address. Conversely, unwrapping is achieved by calling the contract’s withdraw function, resulting in the burning of the proposed tokens and the release of the corresponding original tokens. Events are emitted for transparency, and robust security measures are implemented to safeguard user assets and address any potential vulnerabilities in the contract code. With this design, this proposal ensures the seamless conversion and compatibility with ERC-3643 tokens, promoting greater utility and usability across the Ethereum ecosystem.
Interface for Interoperability
interfaceIERC1155WrapperisIERC7518{/**
@dev Emitted when a new wrapped token address is added to the set.
@param wrappedTokenAddress The address of the wrapped token that was added.
*/eventWrappedTokenAddressSet(addresswrappedTokenAddress);/**
@dev Emitted when tokens are wrapped.
@param The ERC1155 token ID of the wrapped tokens.
@param amount The amount of tokens that were wrapped.
*/eventTokensWrapped(uintindexedid,uint256amount);/**
@dev Emitted when tokens are unwrapped.
@param wrappedTokenId Is the ERC1155 token ID of the wrapped tokens.
@param amount The amount of tokens that were unwrapped.
*/eventTokensUnwrapped(uintindexedwrappedTokenId,uint256amount);/**
* @dev Sets the wrapped token address and logic for deciding partitions.
* @param wrappedTokenAddress The address of the wrapped token contract.
* @return A boolean value indicating whether the operation was successful.
*/functionsetWrappedToken(addresstoken)externalreturns(bool);/**
* @dev Wraps the specified amount of tokens by depositing the original tokens and receiving new standard tokens.
* @param amount The amount of tokens to wrap.
* @param data Additional data for partition.
* @return A boolean value indicating whether the operation was successful.
*/functionwrapToken(uint256amount,bytescalldatadata)externalreturns(bool);/**
* @notice Wraps a specified amount of tokens from a given partition into the main balance.
* @dev This function allows users to convert tokens from a specific partition back to the main balance,making them fungible with tokens from other partitions.
* @param partitionId The unique identifier of the partition from which tokens will be wrapped.
* @param id The unique identifier of the token.
* @param amount The amount of tokens to be wrapped from the specified partition.
* @param data Additional data that may be used to handle the wrap process (optional).
* @return success A boolean indicating whether the wrapping operation was successful or not.
*/functionwrapTokenFromPartition(bytes32partitionId,uint256id,uint256amount,bytescalldatadata)externalreturns(bool);/**
* @dev Unwraps the specified amount of wrapped tokens by depositing the current tokens and receiving the original tokens.
* @param wrappedTokenId internal partition id.
* @param amount The amount of wrapped tokens to unwrap.
* @param data Additional data for partition.
* @return A boolean value indicating whether the operation was successful.
*/functionunwrapToken(uint256wrappedTokenId,uint256amount,bytescalldatadata)externalreturns(bool);/**
* @dev Retrieves the balance of wrapped tokens for the specified account and ID.
* @param account The address of the account.
* @param id The token ID.
* @param data Additional data for partition.
* @return The balance of wrapped tokens.
*/functionwrappedBalanceOf(addressaccount,uint256id,bytescalldatadata)externalviewreturns(uint256);/**
* @dev Retrieves the balance of original tokens for the specified account and ID.
* @param account The address of the account.
* @param id The token ID.
* @param data Additional data for partition.
* @return The balance of original tokens.
*/functionoriginalBalanceOf(addressaccount,uint256id,bytescalldatadata)externalviewreturns(uint256);}
MUST burn the proposed token and release the original token.
MUST verify that the token is not subject to any of the proposal’s locking functionality.
Partition Management
The proposal leverages the tokenId feature of ERC-1155 to represent distinct partitions within a token contract. Each tokenId corresponds to a unique partition with its own set of rights, privileges, and compliance rules. This enables the creation of semi-fungible tokens representing fractional ownership, different share classes, or other granular units.
The partition paradigm offers significant flexibility and power in managing security tokens:
Dynamic Allocation : Partitions allow for dynamic allocation of tokens between different classes or categories. For example, in a real estate tokenization scenario, an issuer can initially allocate tokens to a Reg D partition for accredited U.S. investors and a “Reg S” partition for non-U.S. investors. As the offering progresses and demand shifts, the issuer can dynamically mint tokens into the appropriate partition based on the investor’s eligibility, ensuring optimal distribution and compliance.
Temporary Non-Fungibility : Partitions enable temporary non-fungibility of tokens. In some cases, securities may need to be treated as non-fungible for a certain period, such as tokens of the same underlying asset sold at different offerings. By assigning tokens to specific partitions, issuers can enforce these restrictions and maintain the necessary segregation between them, but merge them at a later point to prevent liquidity fragmentation. Merger occurs by creating a new joint partition, a deploying a merger contract where users can deposit old partitioned tokens to receive new joint partition token.
Granular Compliance : Each partition can have its own set of compliance rules and transfer restrictions. This allows for more granular control over token transfers based on the specific characteristics of each partition. For instance, a partition representing a particular share class may have different transfer restrictions or payout rights compared to other partitions.
Efficient Asset Management : Partitions streamline the management of complex asset structures. Instead of deploying separate contracts for each share class or asset category, issuers can manage multiple partitions within a single proposed contract, reducing deployment costs and simplifying overall asset management.
Compliance Management
This proposal includes functions for managing token transfers in accordance with regulatory requirements and issuer-defined rules. The canTransfer function checks whether a transfer is allowed based on factors such as token restrictions, frozen addresses, transferable balances, and token locking.
To facilitate dynamic compliance management, it introduces the concept of off-chain vouchers. These vouchers are signed messages generated by an authorized entity (e.g., the issuer or a designated compliance service) that attest to the compliance of a specific transfer. The canTransfer function can verify these vouchers to determine the eligibility of a transfer.
Here’s an example of how off-chain vouchers can be used with the proposal:
The token issuer defines a set of compliance rules and requirements for token transfers.
When a user initiates a transfer, they submit a request to a designated compliance service with the necessary details (sender, recipient, amount, etc.).
The compliance service evaluates the transfer request against the predefined rules and requirements, considering factors such as investor eligibility, transfer restrictions, and regulatory compliance.
If the transfer is deemed compliant, the compliance service generates a signed voucher containing the relevant details and returns it to the user.
The user includes the signed voucher as an additional parameter when calling the safeTransferFrom function on the proposed contract.
The canTransfer function verifies the authenticity and validity of the voucher by checking the signature and ensuring that the voucher details match the transfer parameters.
If the voucher is valid and the transfer meets all other requirements, the transfer is allowed to proceed.
By leveraging off-chain vouchers, the proposal enables dynamic compliance management, allowing issuers to enforce complex and evolving compliance rules without the need to update the token contract itself. This approach provides flexibility and adaptability in the face of changing regulatory requirements.
Token Recovery
In case of lost or compromised wallets, the proposal includes a forceTransfer function that allows authorized entities (e.g., the issuer or a designated recovery agent) to transfer tokens from one address to another. This function bypasses the usual transfer restrictions and can be used as a recovery mechanism.
Payout Management
Provides functions for efficient payout distribution to token holders. The payout function allows sending payouts to a single address, while batchPayout enables sending payouts to multiple addresses in a single transaction. These functions streamline the process of distributing dividends, interest, or other payments to token holders.
Real World Example
Use Case 1: Tokenization of Commercial Real Estate
In this use case, a commercial real estate property with 100 floors is being tokenized using this proposal. Each floor is represented as a unique non-fungible token (NFT) partition, allowing for fractional ownership and separate management of individual floors.
Property Representation: The entire commercial property is tokenized using the proposed contract, with each floor being assigned a unique tokenId representing an NFT partition.
Fractional Ownership: Each floor’s NFT partition can be divided into multiple fungible tokens, enabling fractional ownership. For instance, if a floor is divided into 100 tokens, multiple investors can own portions of that floor.
Dynamic Pricing: Since each floor is a separate partition, the pricing of tokens within a partition can be adjusted dynamically based on factors such as floor level, amenities, or market demand. This flexibility allows for accurate representation of the varying values of different floors.
Transfer of Ownership: The ownership of each floor’s NFT partition can be transferred seamlessly to token holders using the safeTransferFrom function. This enables the seamless transfer of ownership rights for specific floors.
Compliance Management: Different compliance rules and transfer restrictions can be applied to each partition (floor) based on regulatory requirements or issuer-defined rules. The canTransfer function can be used to enforce these rules before allowing transfers.
Payouts: The payout and batchPayout functions can be used to distribute rental income, dividends, or other payouts to token holders of specific floor partitions efficiently.
By leveraging proposal, this use case demonstrates the ability to tokenize complex real estate assets while maintaining granular control over ownership, pricing, compliance, and payouts for individual units within the property.
Use Case 2: Tokenization of Securities with Reg S and Reg D Partitions
In this use case, a company is tokenizing its securities and wants to comply with different regulations for U.S. accredited investors (Reg D) and non-U.S. investors (Reg S).
Initial Partitions: The company deploys an proposed standard and creates two partitions: one for Reg D investors (accredited U.S. investors) and another for Reg S investors (non-U.S. investors).
Dynamic Allocation: As the offering progresses, the company can dynamically mint tokens into the appropriate partition based on investor eligibility. For example, if a U.S. accredited investor wants to participate, tokens can be minted in the Reg D partition, while tokens for non-U.S. investors are minted in the Reg S partition.
Compliance Management: Each partition can have its own set of compliance rules and transfer restrictions. The canTransfer function can be integrated with off-chain compliance services to verify the eligibility of a transfer based on the specific rules for each partition.
Temporary Non-Fungibility: During the initial offering period, tokens in the Reg D and Reg S partitions may need to be treated as non-fungible due to different regulatory requirements. However, after the holding period, the company can create a new joint partition and allow token holders to deposit their old partitioned tokens to receive the new joint partition tokens, merging the two classes.
Payouts: The payout and batchPayout functions can be used to distribute dividends, interest payments, or other payouts to token holders in each partition based on their respective rights and privileges.
By utilizing the proposal, this use case demonstrates the ability to tokenize securities while maintaining compliance with different regulatory regimes, dynamically allocating tokens based on investor eligibility, and efficiently managing payouts and potential mergers of different share classes.
Use Case 3: Force Transfer for AML/KYC/Compliance Violations
In the world of tokenized securities, maintaining compliance with regulatory requirements is of utmost importance. This proposal provides a robust mechanism to handle situations where an investor’s tokens need to be forcibly transferred due to violations of Anti-Money Laundering (AML), Know Your Customer (KYC), or other compliance-related regulations.
Let’s consider the scenario of Alice, an investor who holds tokens in the proposed token compliant security token contract. During the regular compliance checks conducted by the token issuer or a designated compliance service, it is discovered that Alice’s wallet address is associated with suspicious activities related to money laundering or other financial crimes.
In such a situation, the regulatory authorities or the contract administrators may decide to freeze Alice’s account and initiate a forced transfer of her tokens to a designated address controlled by the issuer or a recovery agent. The forceTransfer function in this proposal enables this process.
Rationale
Enhancing Compliance Management
The canTransfer function facilitates compliance checks during token transfers, offering adaptability through diverse implementation methods such as on-chain storage, oracle utilization, or any off-chain methodologies. This versatility ensures seamless integration with existing compliance frameworks, particularly in enforcing regulatory standards like KYC/AML. Additionally, functionalities like freezeAddress, restrictTransfer, lockToken and forceTransfer empower entities to regulate token movements based on specified conditions or regulatory requirements. Complementing these, the unlockToken function enhances transparency and accountability by facilitating the release of tokens post-compliance actions.
Interoperability with other standard
The functions wrapToken and wrapTokenFromPartition are essential for simplifying conversions within the token system. wrapToken is specifically designed for wrapping ERC-20-like tokens to this protocol, on the other hand, wrapTokenFromPartition is used when we want to convert tokens from non-fungible tokens or any multi-standard token into proposed protocol. It allows for more specialized conversions, ensuring tokens from different standards can work together smoothly.
The unwrapToken function is used to reverse the process of wrapping tokens. When tokens are wrapped, they’re usually locked or held in a special way to ensure they’re used correctly. users can unlock or release these tokens, returning them to their original standard, essentially, frees up tokens that were previously locked, giving users more control over their assets in the ecosystem.
Payment distribution
The payout function enables direct payments to individual token holders for one-off or event-triggered distributions, facilitating targeted disbursements. Meanwhile, the batchPayout function processes multiple payments in a single transaction, optimizing efficiency for larger-scale or regular payouts on the blockchain
Backwards Compatibility
The proposal is fully compatible with ERC-1155 , and any ERC-1155 compliant wallet or marketplace can interact with the proposal’s tokens. The additional functions introduced by this proposal do not conflict with the ERC-1155 interface, ensuring seamless integration with existing ecosystem tools and infrastructure.
Security Considerations
Access Control: The proposal includes functions that can significantly impact token transfers and balances, such as forceTransfer, freezeAddress, and lockTokens. It is crucial to implement proper access control mechanisms, such as role-based permissions, to ensure that only authorized entities can execute these functions.
Parameter Validation: Functions like safeTransferFrom, lockTokens, and forceTransfer should validate input parameters to prevent unauthorized or unintended actions. This includes checking for valid addresses, sufficient balances, and appropriate permissions.
Reentrancy Protection: The contract should implement reentrancy guards to prevent potential vulnerabilities arising from external calls, especially in functions that transfer tokens or update balances.
Overflow/Underflow Protection: The contract should use safe math libraries or built-in overflow protection to prevent integer overflow and underflow vulnerabilities.
Payout Security: The payout and batchPayout functions should ensure that only authorized entities can initiate payouts and that the total payout amount does not exceed the available balance. Proper access control and input validation are essential to prevent unauthorized or fraudulent payouts.
Off-Chain Voucher Security: When using off-chain vouchers for dynamic compliance management, it is crucial to ensure the security and integrity of the voucher generation process. The compliance service responsible for generating vouchers should have robust security measures in place to prevent unauthorized voucher creation or tampering. Additionally, the proposed contract should thoroughly validate the authenticity and validity of vouchers before allowing transfers to proceed.