ERC-7518 Dynamic compliance security token (Dynamic Compliant Interop Security Token)

Review · Tokens · created 2023-09-14

A standard that extends ERC-1155 to manage security tokens within a single contract where rights and regulatory rules differ by partition. It provides token locking, address freezing, forced transfer, dividend payment, and signed voucher based compliance checks.

Why it is needed

Tokenizing a real world asset such as real estate or a fund requires complying with securities regulations. Features are needed to block transfers based on investor eligibility, lock tokens for a holding period, recover tokens from an investor who lost their key, or freeze accounts that violate regulations. However, existing token standards do not standardize these features, so each issuer must implement them differently, making it hard for wallets or trading services to integrate in a common way. Also, even for the same asset, tokens must be managed separately by investor group under different regulations, such as accredited investors in the US versus overseas investors, and deploying a separate contract for each group increases cost and management complexity. This standard solves this by dividing a single contract into partitions by tokenId, applying different regulatory rules per partition, and providing compliance features through a common interface.

What to implement

In depth

Using tokenId as the partition structure

Each individual tokenId of ERC-1155 is used as a partition with its own rights and rules. Tokens with the same tokenId are fungible with each other and not fungible with other tokenIds. Issuers distinguish stock classes, regulatory jurisdictions, issuance rounds, and so on by issuing them under different tokenIds. As an example from the source, you can separate partitions for US accredited investors (Reg D) and non-US investors (Reg S), issuing tokens into the relevant partition based on investor eligibility, and once the holding period ends, merge them by creating a new consolidated partition where holders deposit their existing partition tokens and receive the consolidated token. For commercial real estate, each floor could have its own partition, with each partition split into multiple tokens to represent fractional ownership.

Locking and transferable balance

lockTokens must lock a set amount of an account's tokens until releaseTime. It must revert if an attempt is made to lock more than the held balance, and only an authorized party should be able to call it. transferableBalance must return the transferable balance calculated as balanceOf(account, id) - lockedBalanceOf(account, id). unlockToken must release all tokens whose release time is at or before block.timestamp, and if there are no tokens to release, it is recommended to revert to save gas. This is used to implement vesting or mandatory holding periods.

canTransfer check rules

canTransfer is a view function that determines whether a transfer is allowed. It must check that from and to are not frozen addresses, that the given id is not restricted, and that amount is within the transferable balance, and may call an external contract to verify this if needed. It must not change state or produce side effects. safeTransferFrom must call canTransfer before proceeding with the transfer, and must revert if the recipient address is the zero address or the balance is insufficient. Implementations that use vouchers also verify the signature, expiration, and binding to from, to, id, and amount of the signed voucher received in the data argument.

Forced transfer and address freezing

forceTransfer moves tokens bypassing normal transfer restrictions and approval checks. It must revert if the from address is not frozen, and must also revert if the to address is frozen. In other words, the order is to first freeze the problem account with freezeAddress and then force the transfer. The source gives the scenario of freezing the account of an investor confirmed to be involved in money laundering or similar and moving tokens to the issuer's or a recovery agent's address, as well as use for recovery in case of key loss. freezeAddress must block transfers and receipt of payments for the account, and unFreeze lifts the freeze. Both functions require access control so that only an authorized party can use them.

Compliance approval extension (IERC7518Authorization)

As an optional extension, it defines an EIP-712 formatted voucher that authorizes a single transfer. The ComplianceAuthorization struct consists of the fields token, authority, operationHash, policyId, contextHash, nonce, validAfter, and validBefore, where token must equal address(this) and the validity period is from validAfter (inclusive) to validBefore (exclusive). operationHash is computed by binding the caller, the parties, the id, the amount, and application data, so it cannot be reused for a different transfer. The voucher is carried in the ERC-1155 data argument inside a standard envelope beginning with the magic value bytes4(0x7518a001). On transfer, after the voucher is validated, it must be marked Consumed within the same transaction and before the external recipient hook is called, to prevent reuse. revokeAuthorization changes an unused voucher to Revoked, and both Consumed and Revoked are kept as final states. If authority is a contract, its signature must be verified via ERC-1271, and the EIP-712 domain must be disclosed via ERC-5267.

Asset profile / role / action record extensions

There are three further optional extensions. IERC7518AssetProfile is for asset lookups: assetKey is the keccak256 hash of the abi.encode of chainid, the token contract, tokenId, and partitionId, and assetProfile returns an assetId that identifies the same economic asset even across contract upgrades, the current policy policyId, and profileHash, the keccak256 hash of the profileURI document. identifier looks up values from identification schemes such as ISIN, DTI, or CUSIP. IERC7518Roles defines four role keys, REGISTRAR, TRANSFER_AGENT, PAYING_AGENT, and COMPLIANCE_AUTHORITY, and must emit RoleUpdated on every role change. IERC7518ActionRecord records asset actions such as splits, tender offers, and voting; status ranges from 0 Unknown to 4 Cancelled, and a completed or cancelled action cannot be reverted to a previous state. When extensions are implemented together, combined rules apply. For example, if the authorization extension and the roles extension are used together, authority must hold the COMPLIANCE_AUTHORITY role at the time the voucher is consumed.

Interoperability with other standards (wrapping)

Defines IERC1155Wrapper, a wrapping interface for wrapping tokens of other standards into this standard. wrapToken locks the original token in an on-chain vault-style contract and mints an equal amount of this standard's token, while unwrapToken burns this standard's token and returns the original. When unwrapping, it must be confirmed that the token is not subject to this standard's lock function. For ERC-20 issuances, the entire supply can be mapped to a single partition, and it is recommended to use tokenId = 0 as the default. ERC-721 adapters map one partition per original tokenId, and ERC-1155 adapters map identical tokenId partitions when supply and rights are the same.

Interface identifiers and proposal status

Implementations must implement ERC-1155 and ERC-165, and supportsInterface must return true for the ERC-1155 identifier 0xd9b67a26 and the IERC7518 identifier 0x5a264a5d. Optional extensions each have their own separate identifiers. IERC7518AssetProfile is 0xe8aa3d31, IERC7518Roles is 0x779da628, IERC7518Authorization is 0x5b880cc8, and IERC7518ActionRecord is 0x9118aff3. Support for one extension does not imply support for another. Note that this proposal is currently in Review status, so its contents may still change.

Things to watch out for

Original ERC · ← ERCs