From 80a10a50740ea6dbd80aa60f50e71f6ac5ec83d9 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Tue, 6 Aug 2024 23:36:40 -0700 Subject: [PATCH 01/31] Create eip-equity_token.md --- ERCS/eip-equity_token.md | 595 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 595 insertions(+) create mode 100644 ERCS/eip-equity_token.md diff --git a/ERCS/eip-equity_token.md b/ERCS/eip-equity_token.md new file mode 100644 index 00000000000..417363a3692 --- /dev/null +++ b/ERCS/eip-equity_token.md @@ -0,0 +1,595 @@ +--- +title: Equity Token +description: Equity token standard for corporations +author: Matt Rosendin +discussions-to: https://ethereum-magicians.org/t/equity-token-standard/20735 +status: Draft +type: Standards +category: ERC +created: 2024-08-06 +requires: EIP-173 +--- + +## Abstract + +An ERC token standard representing shares in a corporation. This new interface standardizes equity management for issuers, investors, transfer agents, and financial intermediaries. + +## Motivation + +Creating a standard for corporation requires an industry effort. That's why this token standard adheres to the same design principles as the [Open Cap Table Coalition](https://www.opencaptablecoalition.com/format) (OCF), an industry-approved data standard for cap tables. Now everyone can adopt the same OCF-compliant token standard for Ethereum and beyond! + +## 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. + +**MUST NOT** use ERC-20 transfer methods, which now **MUST** include the `securityId` parameter. + +**SHOULD** use `balanceOf(address)` to get a holder's total share count, incuding all share classes. + +**SHOULD** use `balanceOfByClass(address,string)` to lookup a holder's balance by the specified share class. + +**MAY** use `balanceOf(address,string)` instead of `balanceOfByClass(address,string)`. + +This standard is not backwards compatible with ERC-20 since it generates a unique security ID for each transfer and mint. + +Every compliant contract must implement this interface: + +```solidity +/// @dev types +library Types { + + /// @dev Stores details about the acquisition of a security + struct Security { + bytes32 id; + address holder; + bytes32 class; + bytes32 balanceSecurityId; + bytes32[] resultingSecurityIds; + uint256 frozenAmount; + uint256 amount; + uint256 issuedAt; // Block timestamp of issuance tx + string uri; // Additional metadata stored in json file at the uri + } + + /// @dev Stores corporate governing documents (e.g. charter, stock plan) + struct Document { + string uri; + bytes32 documentHash; + } + + /// @dev Store details about frozen shares + struct FrozenShares { + bytes32 class; + uint256 amount; + } + +} + +/// @dev interface +interface IToken { + + /// events + + event Approval(address indexed _owner, address indexed _spender, uint256 _amount); + + event Transfer(address indexed from, address indexed to, bytes32 securityId, uint256 amount, bytes32[] newIds); + + event DocumentSet(bytes32 indexed _name, string _uri, bytes32 _documentHash); + + event ShareClassAdded(bytes32 indexed _class, string indexed _uri); + + event ShareClassUpdated(bytes32 indexed _class, string indexed _uri); + + event ShareClassRemoved(bytes32 indexed _class); + + /** + * this event is emitted when the token information is updated. + * the event is emitted by the token init function and by the setTokenInformation function + * `_newName` is the name of the token + * `_newSymbol` is the symbol of the token + * `_newDecimals` is the decimals of the token + * `_newVersion` is the version of the token, current version is 3.0 + * `_newOnchainID` is the address of the onchainID of the token + */ + event UpdatedTokenInformation( + string indexed _newName, + string indexed _newSymbol, + uint8 _newDecimals, + string _newVersion, + address indexed _newOnchainID + ); + + /** + * this event is emitted when the IdentityRegistry has been set for the token + * the event is emitted by the token constructor and by the setIdentityRegistry function + * `_identityRegistry` is the address of the Identity Registry of the token + */ + event IdentityRegistryAdded(address indexed _identityRegistry); + + /** + * this event is emitted when the Compliance has been set for the token + * the event is emitted by the token constructor and by the setCompliance function + * `_compliance` is the address of the Compliance contract of the token + */ + event ComplianceAdded(address indexed _compliance); + + /** + * this event is emitted when an investor successfully recovers his tokens + * the event is emitted by the recoveryAddress function + * `_lostWallet` is the address of the wallet that the investor lost access to + * `_newWallet` is the address of the wallet that the investor provided for the recovery + * `_investorOnchainID` is the address of the onchainID of the investor who asked for a recovery + */ + event RecoverySuccess(address indexed _lostWallet, address indexed _newWallet, address indexed _investorOnchainID); + + /** + * this event is emitted when the wallet of an investor is frozen or unfrozen + * the event is emitted by setAddressFrozen and batchSetAddressFrozen functions + * `_userAddress` is the wallet of the investor that is concerned by the freezing status + * `_isFrozen` is the freezing status of the wallet + * if `_isFrozen` equals `true` the wallet is frozen after emission of the event + * if `_isFrozen` equals `false` the wallet is unfrozen after emission of the event + * `_owner` is the address of the agent who called the function to freeze the wallet + */ + event AddressFrozen(address indexed _userAddress, bool indexed _isFrozen, address indexed _owner); + + /** + * this event is emitted when a certain amount of tokens is frozen on a wallet + * the event is emitted by freezePartialTokens and batchFreezePartialTokens functions + * `_userAddress` is the wallet of the investor that is concerned by the freezing status + * `_amount` is the amount of tokens that are frozen + * `_securityId` is the ID of the security to freeze + */ + event TokensFrozen(address indexed _userAddress, bytes32 _securityId, uint256 _amount); + + /** + * this event is emitted when a certain amount of tokens is unfrozen on a wallet + * the event is emitted by unfreezePartialTokens and batchUnfreezePartialTokens functions + * `_userAddress` is the wallet of the investor that is concerned by the freezing status + * `_amount` is the amount of tokens that are unfrozen + * `_securityId` is the ID of the security to unfreeze + */ + event TokensUnfrozen(address indexed _userAddress, bytes32 _securityId, uint256 _amount); + + /** + * this event is emitted when the token is paused + * the event is emitted by the pause function + * `_userAddress` is the address of the wallet that called the pause function + */ + event Paused(address _userAddress); + + /** + * this event is emitted when the token is unpaused + * the event is emitted by the unpause function + * `_userAddress` is the address of the wallet that called the unpause function + */ + event Unpaused(address _userAddress); + + /// @dev Getter for securities mapping + + function securities(bytes32 securityId) external view returns (Types.Security memory); + + /// @dev Based on ERC1400 and ERC1155 + + /** + * @param _class The name of the share class to query + * @param _holder The holder of the tokens to fetch the balance for + */ + function balanceOfByClass(bytes32 _class, address _holder) external view returns (uint256); + + /** + * @notice Fetches all share type details + */ + function getShareClasses() external view returns (bytes32[] calldata); + + /** + * @param _name The name of the document + */ + function getDocument(bytes32 _name) external view returns (string memory, bytes32); + + /** + * @notice Fetches total supply by share class + */ + function totalSupplyByClass(bytes32 _class) external view returns (uint256); + + /** + * @param _name The name of the document + * @param _uri The URI of the document + * @param _documentHash The document hash + */ + function setDocument(bytes32 _name, string memory _uri, bytes32 _documentHash) external; + + /// @dev Based on ERC20 + + function allowance(address owner, bytes32 shareClass, address spender) external view returns (uint256); + function approve(address spender, bytes32 shareClass, uint256 amount) external returns (bool); + function increaseAllowance(address spender, bytes32 shareClass, uint256 addedValue) external returns (bool); + function decreaseAllowance(address spender, bytes32 shareClass, uint256 subtractedValue) external returns (bool); + function totalSupply() external view returns (uint256); + + /** + * @notice Returns the total balance of all share classes for the user. + * @param account The account to fetch the balance of. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @notice ERC-20 overridden function that include logic to check for trade validity. + * Require that the from and to addresses are not frozen. + * Require that the value should not exceed available balance. + * Require that the to address is a verified address. + * @param _from The address of the sender + * @param _to The address of the receiver + * @param _securityId The ID of the security to transfer + * @param _amount The number of tokens to transfer + * @return `true` if successful and revert if unsuccessful + */ + function transferFrom( + address _from, + address _to, + bytes32 _securityId, + uint256 _amount + ) external returns (bool); + + /** + * @notice ERC-20 overridden function that include logic to check for trade validity. + * Require that the msg.sender and to addresses are not frozen. + * Require that the value should not exceed available balance . + * Require that the to address is a verified address + * @param _to The address of the receiver + * @param _securityId The ID of the security to transfer + * @param _amount The number of tokens to transfer + * @return `true` if successful and revert if unsuccessful + */ + function transfer(address _to, bytes32 _securityId, uint256 _amount) external returns (bool); + + /// @dev Based on ERC3643 + + /** + * @dev sets the token name + * @param _name the name of token to set + * Only the owner of the token smart contract can call this function + * emits a `UpdatedTokenInformation` event + */ + function setName(string calldata _name) external; + + /** + * @dev sets the token symbol + * @param _symbol the token symbol to set + * Only the owner of the token smart contract can call this function + * emits a `UpdatedTokenInformation` event + */ + function setSymbol(string calldata _symbol) external; + + /** + * @dev sets the onchain ID of the token + * @param _onchainID the address of the onchain ID to set + * Only the owner of the token smart contract can call this function + * emits a `UpdatedTokenInformation` event + */ + function setOnchainID(address _onchainID) external; + + /** + * @dev pauses the token contract, when contract is paused investors cannot transfer tokens anymore + * This function can only be called by a wallet set as agent of the token + * emits a `Paused` event + */ + function pause() external; + + /** + * @dev unpauses the token contract, when contract is unpaused investors can transfer tokens + * if their wallet is not blocked & if the amount to transfer is <= to the amount of free tokens + * This function can only be called by a wallet set as agent of the token + * emits an `Unpaused` event + */ + function unpause() external; + + /** + * @dev sets an address frozen status for this token. + * @param _userAddress The address for which to update frozen status + * @param _freeze Frozen status of the address + * This function can only be called by a wallet set as agent of the token + * emits an `AddressFrozen` event + */ + function setAddressFrozen(address _userAddress, bool _freeze) external; + + /** + * @dev freezes token amount specified for given address. + * @param _userAddress The address for which to update frozen tokens + * @param _securityId The security ID to perform partial freeze on + * @param _amount Amount of Tokens to be frozen + * This function can only be called by a wallet set as agent of the token + * emits a `TokensFrozen` event + */ + function freezePartialTokens(address _userAddress, bytes32 _securityId, uint256 _amount) external; + + /** + * @dev unfreezes token amount specified for given address + * @param _userAddress The address for which to update frozen tokens + * @param _securityId The security ID to perform partial freeze on + * @param _amount Amount of Tokens to be unfrozen + * This function can only be called by a wallet set as agent of the token + * emits a `TokensUnfrozen` event + */ + function unfreezePartialTokens(address _userAddress, bytes32 _securityId, uint256 _amount) external; + + /** + * @dev sets the Identity Registry for the token + * @param _identityRegistry the address of the Identity Registry to set + * Only the owner of the token smart contract can call this function + * emits an `IdentityRegistryAdded` event + */ + function setIdentityRegistry(address _identityRegistry) external; + + /** + * @dev sets the compliance contract of the token + * @param _compliance the address of the compliance contract to set + * Only the owner of the token smart contract can call this function + * calls bindToken on the compliance contract + * emits a `ComplianceAdded` event + */ + function setCompliance(address _compliance) external; + + /** + * @dev force a transfer of tokens between 2 whitelisted wallets + * In case the `from` address has not enough free tokens (unfrozen tokens) + * but has a total balance higher or equal to the `amount` + * the amount of frozen tokens is reduced in order to have enough free tokens + * to proceed the transfer, in such a case, the remaining balance on the `from` + * account is 100% composed of frozen tokens post-transfer. + * Require that the `to` address is a verified address, + * @param _from The address of the sender + * @param _to The address of the receiver + * @param _securityId The ID of the security to transfer + * @param _amount The number of tokens to transfer + * @return `true` if successful and revert if unsuccessful + * This function can only be called by a wallet set as agent of the token + * emits a `TokensUnfrozen` event if `_amount` is higher than the free balance of `_from` + * emits a `Transfer` event + */ + function forcedTransfer(address _from, address _to, bytes32 _securityId, uint256 _amount) external returns (bool); + + /** + * @dev mint tokens on a wallet + * Improved version of default mint method. Tokens can be minted + * to an address if only it is a verified address as per the security token. + * @param _to Address to mint the tokens to. + * @param _shareClass The name of the share class (e.g., Common Class A). + * @param _amount The amount of tokens to mint. + * @param _uri The URI containing metadata about the security. + * This function can only be called by a wallet set as agent of the token + * emits a `Transfer` event + */ + function mint(address _to, bytes32 _shareClass, uint256 _amount, string memory _uri) external; + + /** + * @dev burn tokens on a wallet + * In case the `account` address has not enough free tokens (unfrozen tokens) + * but has a total balance higher or equal to the `value` amount + * the amount of frozen tokens is reduced in order to have enough free tokens + * to proceed the burn, in such a case, the remaining balance on the `account` + * is 100% composed of frozen tokens post-transaction. + * @param _userAddress Address to burn the tokens from. + * @param _securityId The ID of the security to burn. + * @param _amount Amount of tokens to burn. + * This function can only be called by a wallet set as agent of the token + * emits a `TokensUnfrozen` event if `_amount` is higher than the free balance of `_userAddress` + * emits a `Transfer` event + */ + function burn(address _userAddress, bytes32 _securityId, uint256 _amount) external; + + /** + * @dev recovery function used to force transfer tokens from a + * lost wallet to a new wallet for an investor. + * @param _lostWallet the wallet that the investor lost + * @param _newWallet the newly provided wallet on which tokens have to be transferred + * @param _investorOnchainID the onchainID of the investor asking for a recovery + * This function can only be called by a wallet set as agent of the token + * emits a `TokensUnfrozen` event if there are frozen tokens on the lost wallet if recovery process successful + * emits a `Transfer` event if the recovery process is successful + * emits a `RecoverySuccess` event if the recovery process is successful + * emits a `RecoveryFails` event if the recovery process fails + */ + function recoveryAddress(address _lostWallet, address _newWallet, address _investorOnchainID) external returns (bool); + + /** + * @dev function allowing to issue transfers in batch + * Require that the msg.sender and `to` addresses are not frozen. + * Require that the total value should not exceed available balance. + * Require that the `to` addresses are all verified addresses, + * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_toList.length` IS TOO HIGH, + * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION + * @param _toList The addresses of the receivers + * @param _securityIds The IDs of the securities to transfer + * @param _amounts The number of tokens to transfer to the corresponding receiver + * emits _toList.length `Transfer` events + */ + function batchTransfer( + address[] calldata _toList, + bytes32[] memory _securityIds, + uint256[] calldata _amounts + ) external; + + /** + * @dev function allowing to issue forced transfers in batch + * Require that `_amounts[i]` should not exceed available balance of `_fromList[i]`. + * Require that the `_toList` addresses are all verified addresses + * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_fromList.length` IS TOO HIGH, + * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION + * @param _fromList The addresses of the senders + * @param _toList The addresses of the receivers + * @param _securityIds The IDs of the securities to transfer + * @param _amounts The number of tokens to transfer to the corresponding receiver + * This function can only be called by a wallet set as agent of the token + * emits `TokensUnfrozen` events if `_amounts[i]` is higher than the free balance of `_fromList[i]` + * emits _fromList.length `Transfer` events + */ + function batchForcedTransfer( + address[] calldata _fromList, + address[] calldata _toList, + bytes32[] calldata _securityIds, + uint256[] calldata _amounts + ) external; + + /** + * @dev Batch mints multiple securities at once. + * Require that the `_toList` addresses are all verified addresses + * IMPORTANT: THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_toList.length` IS TOO HIGH, + * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION + * @param _toList The addresses of the receivers + * @param _shareClasses Array of classes for each security. + * @param _amounts Array of amounts to mint for each security. + * @param _metadataURIs Array of URIs containing metadata about each security. + * This function can only be called by a wallet set as agent of the token + * emits _toList.length `Transfer` events + */ + function batchMint( + address[] memory _toList, + bytes32[] memory _shareClasses, + uint256[] memory _amounts, + string[] memory _metadataURIs + ) external; + + /** + * @dev function allowing to burn tokens in batch + * Require that the `_userAddresses` addresses are all verified addresses + * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH, + * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION + * @param _userAddresses The addresses of the wallets concerned by the burn + * @param _securityIds The IDs of the securities to batch burn + * @param _amounts The number of tokens to burn from the corresponding wallets + * This function can only be called by a wallet set as agent of the token + * emits _userAddresses.length `Transfer` events + */ + function batchBurn(address[] calldata _userAddresses, bytes32[] calldata _securityIds, uint256[] calldata _amounts) external; + + /** + * @dev function allowing to set frozen addresses in batch + * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH, + * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION + * @param _userAddresses The addresses for which to update frozen status + * @param _freeze Frozen status of the corresponding address + * This function can only be called by a wallet set as agent of the token + * emits _userAddresses.length `AddressFrozen` events + */ + function batchSetAddressFrozen(address[] calldata _userAddresses, bool[] calldata _freeze) external; + + /** + * @dev function allowing to freeze tokens partially in batch + * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH, + * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION + * @param _userAddresses The addresses on which tokens need to be frozen + * @param _securityIds The security IDs to operate on + * @param _amounts the amount of tokens to freeze on the corresponding address + * This function can only be called by a wallet set as agent of the token + * emits _userAddresses.length `TokensFrozen` events + */ + function batchFreezePartialTokens( + address[] calldata _userAddresses, + bytes32[] calldata _securityIds, + uint256[] calldata _amounts + ) external; + + /** + * @dev function allowing to unfreeze tokens partially in batch + * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH, + * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION + * @param _userAddresses The addresses on which tokens need to be unfrozen + * @param _securityIds The security IDs to operate on + * @param _amounts the amount of tokens to unfreeze on the corresponding address + * This function can only be called by a wallet set as agent of the token + * emits _userAddresses.length `TokensUnfrozen` events + */ + function batchUnfreezePartialTokens( + address[] calldata _userAddresses, + bytes32[] calldata _securityIds, + uint256[] calldata _amounts + ) external; + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5,05` (`505 / 1 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * balanceOf() and transfer(). + */ + function decimals() external view returns (uint8); + + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the address of the onchainID of the token. + * the onchainID of the token gives all the information available + * about the token and is managed by the token issuer or his agent. + */ + function onchainID() external view returns (address); + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the TREX version of the token. + * current version is 3.0.0 + */ + function version() external view returns (string memory); + + /** + * @dev Returns the Identity Registry linked to the token + */ + function identityRegistry() external view returns (address); + + /** + * @dev Returns the Compliance contract linked to the token + */ + function compliance() external view returns (address); + + /** + * @dev Returns true if the contract is paused, and false otherwise. + */ + function paused() external view returns (bool); + + /** + * @dev Returns the freezing status of a wallet + * if isFrozen returns `true` the wallet is frozen + * if isFrozen returns `false` the wallet is not frozen + * isFrozen returning `true` doesn't mean that the balance is free, tokens could be blocked by + * a partial freeze or the whole token could be blocked by pause + * @param _userAddress the address of the wallet on which isFrozen is called + */ + function isFrozen(address _userAddress) external view returns (bool); + + /** + * @dev Returns the amount of tokens that are partially frozen on a wallet + * the amount of frozen tokens is always <= to the total balance of the wallet + * @param _userAddress the address of the wallet on which getFrozenTokens is called + */ + function getFrozenTokens(address _userAddress) external view returns (Types.FrozenShares[] memory); +} +``` + +## Rationale + +This token is not backwards compatible with ERC-20 transfer methods because the spec requires the generation of a unique security balance ID for every mint and transfer call. We plan to include standards for vesting, equity derivatives, or convertibles in separate ERCs. + +## Backwards Compatibility + +Equity tokens should not be backwards compatible with ERC-20 nor ERC-3643. + +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). From 17b83806beb944eeb02ba3ef3af11dbda5146c5d Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Wed, 7 Aug 2024 08:13:53 -0700 Subject: [PATCH 02/31] Fix reference and rebase to v1 --- ERCS/eip-equity_token.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/eip-equity_token.md b/ERCS/eip-equity_token.md index 417363a3692..aefb8d745d4 100644 --- a/ERCS/eip-equity_token.md +++ b/ERCS/eip-equity_token.md @@ -16,7 +16,7 @@ An ERC token standard representing shares in a corporation. This new interface s ## Motivation -Creating a standard for corporation requires an industry effort. That's why this token standard adheres to the same design principles as the [Open Cap Table Coalition](https://www.opencaptablecoalition.com/format) (OCF), an industry-approved data standard for cap tables. Now everyone can adopt the same OCF-compliant token standard for Ethereum and beyond! +Creating a standard for corporation requires an industry effort. That's why this token standard adheres to the same design principles as the [Open Cap Format](https://www.opencaptablecoalition.com/format) (OCF), an industry-approved data standard for cap tables. Now everyone can adopt the same OCF-compliant token standard for Ethereum and beyond! ## Specification @@ -88,7 +88,7 @@ interface IToken { * `_newName` is the name of the token * `_newSymbol` is the symbol of the token * `_newDecimals` is the decimals of the token - * `_newVersion` is the version of the token, current version is 3.0 + * `_newVersion` is the version of the token, current version is 1.0 * `_newOnchainID` is the address of the onchainID of the token */ event UpdatedTokenInformation( From 06614d540ce42429c0b84c1ea7384cfdf9c72d6e Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Wed, 7 Aug 2024 21:45:50 -0700 Subject: [PATCH 03/31] Update ERCS/eip-equity_token.md add eip number Co-authored-by: Andrew B Coathup <28278242+abcoathup@users.noreply.github.com> --- ERCS/eip-equity_token.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ERCS/eip-equity_token.md b/ERCS/eip-equity_token.md index aefb8d745d4..bb7b0311498 100644 --- a/ERCS/eip-equity_token.md +++ b/ERCS/eip-equity_token.md @@ -1,4 +1,5 @@ --- +eip: 7752 title: Equity Token description: Equity token standard for corporations author: Matt Rosendin From 1e3d53b5ac6a072866f36cf650da15b3f74fed04 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Wed, 7 Aug 2024 21:46:37 -0700 Subject: [PATCH 04/31] Update ERCS/eip-equity_token.md update forum discussion url Co-authored-by: Andrew B Coathup <28278242+abcoathup@users.noreply.github.com> --- ERCS/eip-equity_token.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/eip-equity_token.md b/ERCS/eip-equity_token.md index bb7b0311498..4add1cae2e6 100644 --- a/ERCS/eip-equity_token.md +++ b/ERCS/eip-equity_token.md @@ -3,7 +3,7 @@ eip: 7752 title: Equity Token description: Equity token standard for corporations author: Matt Rosendin -discussions-to: https://ethereum-magicians.org/t/equity-token-standard/20735 +discussions-to: https://ethereum-magicians.org/t/erc-7752-equity-token/20735 status: Draft type: Standards category: ERC From 46f43e8042e9ac25e37b6355ff012718abc18fd5 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Wed, 7 Aug 2024 21:50:27 -0700 Subject: [PATCH 05/31] Rename eip-equity_token.md to eip-7752.md --- ERCS/{eip-equity_token.md => eip-7752.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ERCS/{eip-equity_token.md => eip-7752.md} (100%) diff --git a/ERCS/eip-equity_token.md b/ERCS/eip-7752.md similarity index 100% rename from ERCS/eip-equity_token.md rename to ERCS/eip-7752.md From 68f831846bb35a71bf8fec0fb679865a71bfde60 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Wed, 7 Aug 2024 21:51:36 -0700 Subject: [PATCH 06/31] Rename eip-7752.md to erc-7752.md --- ERCS/{eip-7752.md => erc-7752.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ERCS/{eip-7752.md => erc-7752.md} (100%) diff --git a/ERCS/eip-7752.md b/ERCS/erc-7752.md similarity index 100% rename from ERCS/eip-7752.md rename to ERCS/erc-7752.md From df902f0635b928352570516bbf0f11097bc69fa5 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Wed, 7 Aug 2024 21:54:48 -0700 Subject: [PATCH 07/31] Update erc-7752.md --- ERCS/erc-7752.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index 4add1cae2e6..6ff21629bfa 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -1,11 +1,11 @@ --- eip: 7752 title: Equity Token -description: Equity token standard for corporations -author: Matt Rosendin +description: Equity token for corporations +author: Matt Rosendin (@mrosendin) discussions-to: https://ethereum-magicians.org/t/erc-7752-equity-token/20735 status: Draft -type: Standards +type: Standards Track category: ERC created: 2024-08-06 requires: EIP-173 From b0bbca07fa7a0838015e26d12764e6d96d9880f2 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Wed, 7 Aug 2024 22:03:54 -0700 Subject: [PATCH 08/31] Update erc-7752.md --- ERCS/erc-7752.md | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index 6ff21629bfa..2a014f042eb 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -8,7 +8,7 @@ status: Draft type: Standards Track category: ERC created: 2024-08-06 -requires: EIP-173 +requires: 173 --- ## Abstract @@ -23,15 +23,15 @@ Creating a standard for corporation requires an industry effort. That's why this > 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. -**MUST NOT** use ERC-20 transfer methods, which now **MUST** include the `securityId` parameter. +MUST NOT use [ERC-20](./eip-20.md) transfer methods, which now MUST include the `securityId` parameter. -**SHOULD** use `balanceOf(address)` to get a holder's total share count, incuding all share classes. +SHOULD use `balanceOf(address)` to get a holder's total share count, incuding all share classes. -**SHOULD** use `balanceOfByClass(address,string)` to lookup a holder's balance by the specified share class. +SHOULD use `balanceOfByClass(address,string)` to lookup a holder's balance by the specified share class. -**MAY** use `balanceOf(address,string)` instead of `balanceOfByClass(address,string)`. +MAY use `balanceOf(address,string)` instead of `balanceOfByClass(address,string)`. -This standard is not backwards compatible with ERC-20 since it generates a unique security ID for each transfer and mint. +This standard is not backwards compatible with [ERC-20](./eip-20.md) since it generates a unique security ID for each transfer and mint. Every compliant contract must implement this interface: @@ -214,6 +214,13 @@ interface IToken { */ function balanceOf(address account) external view returns (uint256); + /** + * @notice Returns the balance of the specified share class for the user. + * @param account The account to fetch the balance of. + * @param account The share class to fetch the balance of. + */ + function balanceOf(address account, string shareClass) external view returns (uint256); + /** * @notice ERC-20 overridden function that include logic to check for trade validity. * Require that the from and to addresses are not frozen. @@ -581,11 +588,11 @@ interface IToken { ## Rationale -This token is not backwards compatible with ERC-20 transfer methods because the spec requires the generation of a unique security balance ID for every mint and transfer call. We plan to include standards for vesting, equity derivatives, or convertibles in separate ERCs. +This token is not backwards compatible with [ERC-20](./eip-20.md) transfer methods because the spec requires the generation of a unique security balance ID for every mint and transfer call. We plan to include standards for vesting, equity derivatives, or convertibles in separate ERCs. ## Backwards Compatibility -Equity tokens should not be backwards compatible with ERC-20 nor ERC-3643. +Equity tokens should not be backwards compatible with [ERC-20](./eip-20.md) nor [ERC-3643](./eip-3643.md). ## Security Considerations From 87d5cc15d0247f2645386de7e84b10011a550843 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Wed, 7 Aug 2024 22:04:53 -0700 Subject: [PATCH 09/31] Remove external link --- ERCS/erc-7752.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index 2a014f042eb..0e71835caa5 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -17,7 +17,7 @@ An ERC token standard representing shares in a corporation. This new interface s ## Motivation -Creating a standard for corporation requires an industry effort. That's why this token standard adheres to the same design principles as the [Open Cap Format](https://www.opencaptablecoalition.com/format) (OCF), an industry-approved data standard for cap tables. Now everyone can adopt the same OCF-compliant token standard for Ethereum and beyond! +Creating a standard for corporation requires an industry effort. That's why this token standard adheres to the same design principles as the Open Cap Format (OCF), an industry-approved data standard for cap tables. Now everyone can adopt the same OCF-compliant token standard for Ethereum and beyond! ## Specification From 191f3a8f522de4511c2ca1433ea75b5c44f47fe7 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Thu, 8 Aug 2024 23:02:51 -0700 Subject: [PATCH 10/31] Update erc-7752.md --- ERCS/erc-7752.md | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index 0e71835caa5..0a56b3b047e 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -1,5 +1,5 @@ --- -eip: 7752 +erc: 7752 title: Equity Token description: Equity token for corporations author: Matt Rosendin (@mrosendin) @@ -23,15 +23,25 @@ Creating a standard for corporation requires an industry effort. That's why this > 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. -MUST NOT use [ERC-20](./eip-20.md) transfer methods, which now MUST include the `securityId` parameter. +MUST NOT use [ERC-20](./erc-20.md) methods, which have breaking changes. -SHOULD use `balanceOf(address)` to get a holder's total share count, incuding all share classes. +SHOULD use `addShareClass(bytes32 name, string uri)` to create at least one share class before minting -SHOULD use `balanceOfByClass(address,string)` to lookup a holder's balance by the specified share class. +SHOULD use `balanceOf(address user)` to get a holder's total share count, incuding all share classes. -MAY use `balanceOf(address,string)` instead of `balanceOfByClass(address,string)`. +SHOULD use `balanceOf(address user, string name)` to lookup a holder's balance by the specified share class name. -This standard is not backwards compatible with [ERC-20](./eip-20.md) since it generates a unique security ID for each transfer and mint. +SHOULD use `totalSupply()` to get the total outstanding share count, incuding all share classes. + +SHOULD use `totalSupply(string name)` to lookup outstanding shares by the specified class name. + +SHOULD use `securities(bytes32 securityId)` to fetch individual security details. + +MAY assign a `uri` when creating a share class that points to a JSON file containing relevant metadata. + +MAY set governing documents for all share classes using `setDocument` (e.g., corporate charter or stock plan). + +This standard is not backwards compatible with [ERC-20](./erc-20.md) nor [ERC-3643](./erc-3643) since it generates a unique security ID for each transfer and mint. The security ID provides a unique identifier for every balance resulting from a mint operation and for every residual and/or resulting balances from a transfer operation. Every compliant contract must implement this interface: @@ -588,11 +598,15 @@ interface IToken { ## Rationale -This token is not backwards compatible with [ERC-20](./eip-20.md) transfer methods because the spec requires the generation of a unique security balance ID for every mint and transfer call. We plan to include standards for vesting, equity derivatives, or convertibles in separate ERCs. +Assigning a unique `securityId` to all issuances of equity tokens is the industry-accepted practice for audit and cost-basis purposes. ## Backwards Compatibility -Equity tokens should not be backwards compatible with [ERC-20](./eip-20.md) nor [ERC-3643](./eip-3643.md). +Equity tokens are not backwards compatible with [ERC-20](./erc-20.md) nor [ERC-3643](./erc-3643.md). + +This standard brings breaking changes to [ERC-3643](./erc-3643.md) by requiring an additional `bytes32 securityId` param in `forceTransfer`, `freezePartialTokens`, `unfreezePartialTokens` methods and a `bytes32[] securityIds` param to `batchMint`, `batchBurn`, `batchForcedTransfer`, `batchFreezePartialTokens`, and `batchUnfreezePartialTokens`. + +Finally, [ERC-20](./erc-20.md) breaking changes were made. This change introduces `string shareClass` and `string uri` parameters to the `mint` method and a `bytes32 securityId` parameter to `burn`, `transfer`, and `transferFrom` methods, and a `shareClass` param to the `approve`, `increaseAllowance`, and `decreaseAllowance` methods. The corresponding events also include these changes. ## Security Considerations From 2b21514207b207d450fd1dc506217bfe1423f447 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Thu, 8 Aug 2024 23:06:44 -0700 Subject: [PATCH 11/31] Update erc-7752.md --- ERCS/erc-7752.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index 0a56b3b047e..ffcd1b11d05 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -1,5 +1,5 @@ --- -erc: 7752 +eip: 7752 title: Equity Token description: Equity token for corporations author: Matt Rosendin (@mrosendin) @@ -25,7 +25,7 @@ Creating a standard for corporation requires an industry effort. That's why this MUST NOT use [ERC-20](./erc-20.md) methods, which have breaking changes. -SHOULD use `addShareClass(bytes32 name, string uri)` to create at least one share class before minting +MUST use `addShareClass(bytes32 name, string uri)` to create at least one share class before minting tokens. SHOULD use `balanceOf(address user)` to get a holder's total share count, incuding all share classes. From 59bdc3503df533499fd21f6648afb0332d06df63 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Fri, 9 Aug 2024 01:08:25 -0700 Subject: [PATCH 12/31] Update erc-7752.md --- ERCS/erc-7752.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index ffcd1b11d05..42413cc06c3 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -23,9 +23,9 @@ Creating a standard for corporation requires an industry effort. That's why this > 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. -MUST NOT use [ERC-20](./erc-20.md) methods, which have breaking changes. +MUST NOT use [ERC-20](./eip-20.md) methods, which have breaking changes. -MUST use `addShareClass(bytes32 name, string uri)` to create at least one share class before minting tokens. +SHOULD use `addShareClass(bytes32 name, string uri)` to create at least one share class before minting tokens. SHOULD use `balanceOf(address user)` to get a holder's total share count, incuding all share classes. @@ -41,7 +41,7 @@ MAY assign a `uri` when creating a share class that points to a JSON file contai MAY set governing documents for all share classes using `setDocument` (e.g., corporate charter or stock plan). -This standard is not backwards compatible with [ERC-20](./erc-20.md) nor [ERC-3643](./erc-3643) since it generates a unique security ID for each transfer and mint. The security ID provides a unique identifier for every balance resulting from a mint operation and for every residual and/or resulting balances from a transfer operation. +This standard is not backwards compatible with [ERC-20](./eip-20.md) nor [ERC-3643](./eip-3643.md) since it generates a unique security ID for each transfer and mint. The security ID provides a unique identifier for every balance resulting from a mint operation and for every residual and/or resulting balances from a transfer operation. Every compliant contract must implement this interface: @@ -602,11 +602,11 @@ Assigning a unique `securityId` to all issuances of equity tokens is the industr ## Backwards Compatibility -Equity tokens are not backwards compatible with [ERC-20](./erc-20.md) nor [ERC-3643](./erc-3643.md). +Equity tokens are not backwards compatible with [ERC-20](./eip-20.md) nor [ERC-3643](./eip-3643.md). -This standard brings breaking changes to [ERC-3643](./erc-3643.md) by requiring an additional `bytes32 securityId` param in `forceTransfer`, `freezePartialTokens`, `unfreezePartialTokens` methods and a `bytes32[] securityIds` param to `batchMint`, `batchBurn`, `batchForcedTransfer`, `batchFreezePartialTokens`, and `batchUnfreezePartialTokens`. +This standard brings breaking changes to [ERC-3643](./eip-3643.md) by requiring an additional `bytes32 securityId` param in `forceTransfer`, `freezePartialTokens`, `unfreezePartialTokens` methods and a `bytes32[] securityIds` param to `batchMint`, `batchBurn`, `batchForcedTransfer`, `batchFreezePartialTokens`, and `batchUnfreezePartialTokens`. -Finally, [ERC-20](./erc-20.md) breaking changes were made. This change introduces `string shareClass` and `string uri` parameters to the `mint` method and a `bytes32 securityId` parameter to `burn`, `transfer`, and `transferFrom` methods, and a `shareClass` param to the `approve`, `increaseAllowance`, and `decreaseAllowance` methods. The corresponding events also include these changes. +Finally, [ERC-20](./eip-20.md) breaking changes were made. This change introduces `string shareClass` and `string uri` parameters to the `mint` method and a `bytes32 securityId` parameter to `burn`, `transfer`, and `transferFrom` methods, and a `shareClass` param to the `approve`, `increaseAllowance`, and `decreaseAllowance` methods. The corresponding events also include these changes. ## Security Considerations From 8449d950fb53ddbeb501ac8a4ae6873ba9c56a2e Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Thu, 29 Aug 2024 10:12:33 -0700 Subject: [PATCH 13/31] mint -> issue, burn -> cancel --- ERCS/erc-7752.md | 72 ++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index 42413cc06c3..47ab00f4aaf 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -41,7 +41,7 @@ MAY assign a `uri` when creating a share class that points to a JSON file contai MAY set governing documents for all share classes using `setDocument` (e.g., corporate charter or stock plan). -This standard is not backwards compatible with [ERC-20](./eip-20.md) nor [ERC-3643](./eip-3643.md) since it generates a unique security ID for each transfer and mint. The security ID provides a unique identifier for every balance resulting from a mint operation and for every residual and/or resulting balances from a transfer operation. +This standard is not backwards compatible with [ERC-20](./eip-20.md) nor [ERC-3643](./eip-3643.md) since it generates a unique security ID for each transfer and issuance. Mint and burn methods have been renamed to issue and cancel. The security ID provides a unique identifier for every balance resulting from an issuance operation and for every residual and/or resulting balances from a transfer operation. Every compliant contract must implement this interface: @@ -93,13 +93,15 @@ interface IToken { event ShareClassRemoved(bytes32 indexed _class); + function getMetadataURIs() external view returns (string[] memory); + /** * this event is emitted when the token information is updated. * the event is emitted by the token init function and by the setTokenInformation function * `_newName` is the name of the token * `_newSymbol` is the symbol of the token * `_newDecimals` is the decimals of the token - * `_newVersion` is the version of the token, current version is 1.0 + * `_newVersion` is the version of the token, current version is 3.0 * `_newOnchainID` is the address of the onchainID of the token */ event UpdatedTokenInformation( @@ -188,6 +190,13 @@ interface IToken { */ function balanceOfByClass(bytes32 _class, address _holder) external view returns (uint256); + function addShareClass( + bytes32 _class, + string memory _uri + ) external; + + function removeShareClass(bytes32 _class) external; + /** * @notice Fetches all share type details */ @@ -224,13 +233,6 @@ interface IToken { */ function balanceOf(address account) external view returns (uint256); - /** - * @notice Returns the balance of the specified share class for the user. - * @param account The account to fetch the balance of. - * @param account The share class to fetch the balance of. - */ - function balanceOf(address account, string shareClass) external view returns (uint256); - /** * @notice ERC-20 overridden function that include logic to check for trade validity. * Require that the from and to addresses are not frozen. @@ -368,33 +370,33 @@ interface IToken { function forcedTransfer(address _from, address _to, bytes32 _securityId, uint256 _amount) external returns (bool); /** - * @dev mint tokens on a wallet - * Improved version of default mint method. Tokens can be minted + * @dev issue tokens on a wallet + * Improved version of default issue method. Tokens can be issueed * to an address if only it is a verified address as per the security token. - * @param _to Address to mint the tokens to. - * @param _shareClass The name of the share class (e.g., Common Class A). - * @param _amount The amount of tokens to mint. + * @param _to Address to issue the tokens to. + * @param _shareClass The share type of the security (e.g., common stock, preferred stock series). + * @param _amount The amount of tokens to issue. * @param _uri The URI containing metadata about the security. * This function can only be called by a wallet set as agent of the token * emits a `Transfer` event */ - function mint(address _to, bytes32 _shareClass, uint256 _amount, string memory _uri) external; + function issue(address _to, bytes32 _shareClass, uint256 _amount, string memory _uri) external; /** - * @dev burn tokens on a wallet + * @dev cancel tokens on a wallet * In case the `account` address has not enough free tokens (unfrozen tokens) * but has a total balance higher or equal to the `value` amount * the amount of frozen tokens is reduced in order to have enough free tokens - * to proceed the burn, in such a case, the remaining balance on the `account` + * to proceed the cancel, in such a case, the remaining balance on the `account` * is 100% composed of frozen tokens post-transaction. - * @param _userAddress Address to burn the tokens from. - * @param _securityId The ID of the security to burn. - * @param _amount Amount of tokens to burn. + * @param _userAddress Address to cancel the tokens from. + * @param _securityId The ID of the security to cancel. + * @param _amount Amount of tokens to cancel. * This function can only be called by a wallet set as agent of the token * emits a `TokensUnfrozen` event if `_amount` is higher than the free balance of `_userAddress` * emits a `Transfer` event */ - function burn(address _userAddress, bytes32 _securityId, uint256 _amount) external; + function cancel(address _userAddress, bytes32 _securityId, uint256 _amount) external; /** * @dev recovery function used to force transfer tokens from a @@ -450,36 +452,40 @@ interface IToken { ) external; /** - * @dev Batch mints multiple securities at once. + * @dev Batch issues multiple securities at once. * Require that the `_toList` addresses are all verified addresses * IMPORTANT: THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_toList.length` IS TOO HIGH, * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION * @param _toList The addresses of the receivers - * @param _shareClasses Array of classes for each security. - * @param _amounts Array of amounts to mint for each security. + * @param _shareClasss Array of classes for each security. + * @param _amounts Array of amounts to issue for each security. * @param _metadataURIs Array of URIs containing metadata about each security. * This function can only be called by a wallet set as agent of the token * emits _toList.length `Transfer` events */ - function batchMint( + function batchIssue( address[] memory _toList, - bytes32[] memory _shareClasses, + bytes32[] memory _shareClasss, uint256[] memory _amounts, string[] memory _metadataURIs ) external; /** - * @dev function allowing to burn tokens in batch + * @dev function allowing to cancel tokens in batch * Require that the `_userAddresses` addresses are all verified addresses * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH, * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION - * @param _userAddresses The addresses of the wallets concerned by the burn - * @param _securityIds The IDs of the securities to batch burn - * @param _amounts The number of tokens to burn from the corresponding wallets + * @param _userAddresses The addresses of the wallets concerned by the cancel + * @param _securityIds The IDs of the securities to batch cancel + * @param _amounts The number of tokens to cancel from the corresponding wallets * This function can only be called by a wallet set as agent of the token * emits _userAddresses.length `Transfer` events */ - function batchBurn(address[] calldata _userAddresses, bytes32[] calldata _securityIds, uint256[] calldata _amounts) external; + function batchCancel( + address[] calldata _userAddresses, + bytes32[] calldata _securityIds, + uint256[] calldata _amounts + ) external; /** * @dev function allowing to set frozen addresses in batch @@ -565,12 +571,12 @@ interface IToken { /** * @dev Returns the Identity Registry linked to the token */ - function identityRegistry() external view returns (address); + function identityRegistry() external view returns (IIdentityRegistry); /** * @dev Returns the Compliance contract linked to the token */ - function compliance() external view returns (address); + function compliance() external view returns (IModularCompliance); /** * @dev Returns true if the contract is paused, and false otherwise. From e7078b23dce0f190521cb94dc4231d321c73adaf Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Thu, 29 Aug 2024 14:59:34 -0700 Subject: [PATCH 14/31] Info about vesting and governing docs --- ERCS/erc-7752.md | 160 +++++++++++++++++++++-------------------------- 1 file changed, 72 insertions(+), 88 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index 47ab00f4aaf..15fda816227 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -39,10 +39,12 @@ SHOULD use `securities(bytes32 securityId)` to fetch individual security details MAY assign a `uri` when creating a share class that points to a JSON file containing relevant metadata. -MAY set governing documents for all share classes using `setDocument` (e.g., corporate charter or stock plan). +MAY specify a value of `0` for `startTime`, `cliffTime`, and `duration` parameters in `issue` for stock issuances without a vesting condition. This standard is not backwards compatible with [ERC-20](./eip-20.md) nor [ERC-3643](./eip-3643.md) since it generates a unique security ID for each transfer and issuance. Mint and burn methods have been renamed to issue and cancel. The security ID provides a unique identifier for every balance resulting from an issuance operation and for every residual and/or resulting balances from a transfer operation. +Furthermore, vesting and governing documentation are separate concerns not addressed in this standard, requiring their own companion ERCs. For now the vesting parameters in the `issue` method are temporary until a vesting spec is formalized. A vesting and governing document contract should be linked to the equity token. This approach is being taken to cut down on the equity token contract bytesize. + Every compliant contract must implement this interface: ```solidity @@ -56,22 +58,10 @@ library Types { bytes32 class; bytes32 balanceSecurityId; bytes32[] resultingSecurityIds; - uint256 frozenAmount; uint256 amount; uint256 issuedAt; // Block timestamp of issuance tx - string uri; // Additional metadata stored in json file at the uri - } - - /// @dev Stores corporate governing documents (e.g. charter, stock plan) - struct Document { - string uri; - bytes32 documentHash; - } - - /// @dev Store details about frozen shares - struct FrozenShares { - bytes32 class; - uint256 amount; + string uri; // Additional metadata apart from share type metadata + bool isFrozen; } } @@ -85,8 +75,6 @@ interface IToken { event Transfer(address indexed from, address indexed to, bytes32 securityId, uint256 amount, bytes32[] newIds); - event DocumentSet(bytes32 indexed _name, string _uri, bytes32 _documentHash); - event ShareClassAdded(bytes32 indexed _class, string indexed _uri); event ShareClassUpdated(bytes32 indexed _class, string indexed _uri); @@ -142,27 +130,25 @@ interface IToken { * `_isFrozen` is the freezing status of the wallet * if `_isFrozen` equals `true` the wallet is frozen after emission of the event * if `_isFrozen` equals `false` the wallet is unfrozen after emission of the event - * `_owner` is the address of the agent who called the function to freeze the wallet + * `_owner` is the address of the agent who called the function to freezeSecurity the wallet */ event AddressFrozen(address indexed _userAddress, bool indexed _isFrozen, address indexed _owner); /** - * this event is emitted when a certain amount of tokens is frozen on a wallet - * the event is emitted by freezePartialTokens and batchFreezePartialTokens functions + * this event is emitted when a certain security is frozen on a wallet + * the event is emitted by freezeSecurity and batchFreezePartialTokens functions * `_userAddress` is the wallet of the investor that is concerned by the freezing status - * `_amount` is the amount of tokens that are frozen - * `_securityId` is the ID of the security to freeze + * `_securityId` is the ID of the security to freezeSecurity */ - event TokensFrozen(address indexed _userAddress, bytes32 _securityId, uint256 _amount); + event SecurityFrozen(address indexed _userAddress, bytes32 _securityId); /** - * this event is emitted when a certain amount of tokens is unfrozen on a wallet - * the event is emitted by unfreezePartialTokens and batchUnfreezePartialTokens functions + * this event is emitted when a certain security is unfrozen on a wallet + * the event is emitted by unfreezeSecurity and batchUnfreezeSecurities functions * `_userAddress` is the wallet of the investor that is concerned by the freezing status - * `_amount` is the amount of tokens that are unfrozen - * `_securityId` is the ID of the security to unfreeze + * `_securityId` is the ID of the security to unfreezeSecurity */ - event TokensUnfrozen(address indexed _userAddress, bytes32 _securityId, uint256 _amount); + event SecurityUnfrozen(address indexed _userAddress, bytes32 _securityId); /** * this event is emitted when the token is paused @@ -184,12 +170,6 @@ interface IToken { /// @dev Based on ERC1400 and ERC1155 - /** - * @param _class The name of the share class to query - * @param _holder The holder of the tokens to fetch the balance for - */ - function balanceOfByClass(bytes32 _class, address _holder) external view returns (uint256); - function addShareClass( bytes32 _class, string memory _uri @@ -202,23 +182,11 @@ interface IToken { */ function getShareClasses() external view returns (bytes32[] calldata); - /** - * @param _name The name of the document - */ - function getDocument(bytes32 _name) external view returns (string memory, bytes32); - /** * @notice Fetches total supply by share class */ function totalSupplyByClass(bytes32 _class) external view returns (uint256); - /** - * @param _name The name of the document - * @param _uri The URI of the document - * @param _documentHash The document hash - */ - function setDocument(bytes32 _name, string memory _uri, bytes32 _documentHash) external; - /// @dev Based on ERC20 function allowance(address owner, bytes32 shareClass, address spender) external view returns (uint256); @@ -233,6 +201,12 @@ interface IToken { */ function balanceOf(address account) external view returns (uint256); + /** + * @param account The holder of the tokens to fetch the balance for + * @param shareClass The name of the share class to query + */ + function balanceOf(address account, bytes32 shareClass) external view returns (uint256); + /** * @notice ERC-20 overridden function that include logic to check for trade validity. * Require that the from and to addresses are not frozen. @@ -314,24 +288,22 @@ interface IToken { function setAddressFrozen(address _userAddress, bool _freeze) external; /** - * @dev freezes token amount specified for given address. + * @dev freezes security specified for given address. * @param _userAddress The address for which to update frozen tokens - * @param _securityId The security ID to perform partial freeze on - * @param _amount Amount of Tokens to be frozen + * @param _securityId The security ID to perform partial freezeSecurity on * This function can only be called by a wallet set as agent of the token - * emits a `TokensFrozen` event + * emits a `SecurityFrozen` event */ - function freezePartialTokens(address _userAddress, bytes32 _securityId, uint256 _amount) external; + function freezeSecurity(address _userAddress, bytes32 _securityId) external; /** - * @dev unfreezes token amount specified for given address + * @dev unfreezes security specified for given address * @param _userAddress The address for which to update frozen tokens - * @param _securityId The security ID to perform partial freeze on - * @param _amount Amount of Tokens to be unfrozen + * @param _securityId The security ID to perform partial freezeSecurity on * This function can only be called by a wallet set as agent of the token - * emits a `TokensUnfrozen` event + * emits a `SecurityUnfrozen` event */ - function unfreezePartialTokens(address _userAddress, bytes32 _securityId, uint256 _amount) external; + function unfreezeSecurity(address _userAddress, bytes32 _securityId) external; /** * @dev sets the Identity Registry for the token @@ -364,7 +336,7 @@ interface IToken { * @param _amount The number of tokens to transfer * @return `true` if successful and revert if unsuccessful * This function can only be called by a wallet set as agent of the token - * emits a `TokensUnfrozen` event if `_amount` is higher than the free balance of `_from` + * emits a `SecurityUnfrozen` event if `_amount` is higher than the free balance of `_from` * emits a `Transfer` event */ function forcedTransfer(address _from, address _to, bytes32 _securityId, uint256 _amount) external returns (bool); @@ -377,10 +349,21 @@ interface IToken { * @param _shareClass The share type of the security (e.g., common stock, preferred stock series). * @param _amount The amount of tokens to issue. * @param _uri The URI containing metadata about the security. + * @param _startTime The start time of the vesting period. + * @param _cliffTime The cliff time of the vesting period. + * @param _duration The duration of the vesting period. * This function can only be called by a wallet set as agent of the token * emits a `Transfer` event */ - function issue(address _to, bytes32 _shareClass, uint256 _amount, string memory _uri) external; + function issue( + address _to, + bytes32 _shareClass, + uint256 _amount, + string memory _uri, + uint256 _startTime, + uint256 _cliffTime, + uint256 _duration + ) external; /** * @dev cancel tokens on a wallet @@ -393,7 +376,7 @@ interface IToken { * @param _securityId The ID of the security to cancel. * @param _amount Amount of tokens to cancel. * This function can only be called by a wallet set as agent of the token - * emits a `TokensUnfrozen` event if `_amount` is higher than the free balance of `_userAddress` + * emits a `SecurityUnfrozen` event if `_amount` is higher than the free balance of `_userAddress` * emits a `Transfer` event */ function cancel(address _userAddress, bytes32 _securityId, uint256 _amount) external; @@ -405,7 +388,7 @@ interface IToken { * @param _newWallet the newly provided wallet on which tokens have to be transferred * @param _investorOnchainID the onchainID of the investor asking for a recovery * This function can only be called by a wallet set as agent of the token - * emits a `TokensUnfrozen` event if there are frozen tokens on the lost wallet if recovery process successful + * emits a `SecurityUnfrozen` event if there are frozen tokens on the lost wallet if recovery process successful * emits a `Transfer` event if the recovery process is successful * emits a `RecoverySuccess` event if the recovery process is successful * emits a `RecoveryFails` event if the recovery process fails @@ -441,7 +424,7 @@ interface IToken { * @param _securityIds The IDs of the securities to transfer * @param _amounts The number of tokens to transfer to the corresponding receiver * This function can only be called by a wallet set as agent of the token - * emits `TokensUnfrozen` events if `_amounts[i]` is higher than the free balance of `_fromList[i]` + * emits `SecurityUnfrozen` events if `_amounts[i]` is higher than the free balance of `_fromList[i]` * emits _fromList.length `Transfer` events */ function batchForcedTransfer( @@ -457,17 +440,23 @@ interface IToken { * IMPORTANT: THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_toList.length` IS TOO HIGH, * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION * @param _toList The addresses of the receivers - * @param _shareClasss Array of classes for each security. + * @param _classes Array of classes for each security. * @param _amounts Array of amounts to issue for each security. * @param _metadataURIs Array of URIs containing metadata about each security. + * @param _startTimes Array of start times for vesting period + * @param _cliffTimes Array of cliff times for vesting period + * @param _durations Array of durations of vesting period * This function can only be called by a wallet set as agent of the token * emits _toList.length `Transfer` events */ function batchIssue( - address[] memory _toList, - bytes32[] memory _shareClasss, - uint256[] memory _amounts, - string[] memory _metadataURIs + address[] calldata _toList, + bytes32[] memory _classes, + uint256[] calldata _amounts, + string[] memory _metadataURIs, + uint256[] calldata _startTimes, + uint256[] calldata _cliffTimes, + uint256[] calldata _durations ) external; /** @@ -499,35 +488,31 @@ interface IToken { function batchSetAddressFrozen(address[] calldata _userAddresses, bool[] calldata _freeze) external; /** - * @dev function allowing to freeze tokens partially in batch + * @dev function allowing to freezeSecurity securities in batch * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH, * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION * @param _userAddresses The addresses on which tokens need to be frozen * @param _securityIds The security IDs to operate on - * @param _amounts the amount of tokens to freeze on the corresponding address * This function can only be called by a wallet set as agent of the token - * emits _userAddresses.length `TokensFrozen` events + * emits _userAddresses.length `SecurityFrozen` events */ - function batchFreezePartialTokens( + function batchFreezeSecurities( address[] calldata _userAddresses, - bytes32[] calldata _securityIds, - uint256[] calldata _amounts + bytes32[] calldata _securityIds ) external; /** - * @dev function allowing to unfreeze tokens partially in batch + * @dev function allowing to unfreezeSecurity securities in batch * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH, * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION * @param _userAddresses The addresses on which tokens need to be unfrozen * @param _securityIds The security IDs to operate on - * @param _amounts the amount of tokens to unfreeze on the corresponding address * This function can only be called by a wallet set as agent of the token - * emits _userAddresses.length `TokensUnfrozen` events + * emits _userAddresses.length `SecurityUnfrozen` events */ - function batchUnfreezePartialTokens( + function batchUnfreezeSecurities( address[] calldata _userAddresses, - bytes32[] calldata _securityIds, - uint256[] calldata _amounts + bytes32[] calldata _securityIds ) external; /** @@ -588,17 +573,10 @@ interface IToken { * if isFrozen returns `true` the wallet is frozen * if isFrozen returns `false` the wallet is not frozen * isFrozen returning `true` doesn't mean that the balance is free, tokens could be blocked by - * a partial freeze or the whole token could be blocked by pause + * a partial freezeSecurity or the whole token could be blocked by pause * @param _userAddress the address of the wallet on which isFrozen is called */ function isFrozen(address _userAddress) external view returns (bool); - - /** - * @dev Returns the amount of tokens that are partially frozen on a wallet - * the amount of frozen tokens is always <= to the total balance of the wallet - * @param _userAddress the address of the wallet on which getFrozenTokens is called - */ - function getFrozenTokens(address _userAddress) external view returns (Types.FrozenShares[] memory); } ``` @@ -610,9 +588,15 @@ Assigning a unique `securityId` to all issuances of equity tokens is the industr Equity tokens are not backwards compatible with [ERC-20](./eip-20.md) nor [ERC-3643](./eip-3643.md). -This standard brings breaking changes to [ERC-3643](./eip-3643.md) by requiring an additional `bytes32 securityId` param in `forceTransfer`, `freezePartialTokens`, `unfreezePartialTokens` methods and a `bytes32[] securityIds` param to `batchMint`, `batchBurn`, `batchForcedTransfer`, `batchFreezePartialTokens`, and `batchUnfreezePartialTokens`. +ERC-7752 brings breaking changes to [ERC-3643](./eip-3643.md) by emphasizing each security issuance rather than fungible token balances. Methods like `freezePartialTokens` have been removed in favor of `freezeSecurity`. An additional `securityId` param is required for any method that modifies an account’s equity balance. + +In addition to `mint` and `burn` [ERC-20](./eip-20.md) breaking changes, this standard introduces: -Finally, [ERC-20](./eip-20.md) breaking changes were made. This change introduces `string shareClass` and `string uri` parameters to the `mint` method and a `bytes32 securityId` parameter to `burn`, `transfer`, and `transferFrom` methods, and a `shareClass` param to the `approve`, `increaseAllowance`, and `decreaseAllowance` methods. The corresponding events also include these changes. +- the `shareClass` and `uri` parameters to the `issue` method +- a `securityId` parameter to `cancel`, `transfer`, and `transferFrom`, methods +- a `securityIds` parameter to corresponding batch methods +- a `shareClass` param to the `approve`, `increaseAllowance`, and `decreaseAllowance` methods +- corresponding event signatures. ## Security Considerations From 276d9fbc55f101e62555c592a3f18b232ff78003 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Thu, 29 Aug 2024 15:04:17 -0700 Subject: [PATCH 15/31] update rationale of separate vesting and documentation contracts --- ERCS/erc-7752.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index 15fda816227..c915027bd8e 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -582,7 +582,7 @@ interface IToken { ## Rationale -Assigning a unique `securityId` to all issuances of equity tokens is the industry-accepted practice for audit and cost-basis purposes. +Assigning a unique `securityId` to all issuances of equity tokens is the industry-accepted practice for assignment of unique vesting conditions, audit, and cost-basis tracking purposes. To keep the implementation under the max size limit, vesting conditions and governing documents are to be separate contracts with related data accessed by specifying a `securityId`. ## Backwards Compatibility From 8e59d6d9794b0747a0112ab99494b09f400d5486 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Thu, 29 Aug 2024 15:05:57 -0700 Subject: [PATCH 16/31] fix: eip validation error --- ERCS/erc-7752.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index c915027bd8e..fa37b50c9c8 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -588,7 +588,7 @@ Assigning a unique `securityId` to all issuances of equity tokens is the industr Equity tokens are not backwards compatible with [ERC-20](./eip-20.md) nor [ERC-3643](./eip-3643.md). -ERC-7752 brings breaking changes to [ERC-3643](./eip-3643.md) by emphasizing each security issuance rather than fungible token balances. Methods like `freezePartialTokens` have been removed in favor of `freezeSecurity`. An additional `securityId` param is required for any method that modifies an account’s equity balance. +This standard introduces breaking changes to [ERC-3643](./eip-3643.md) by emphasizing each security issuance rather than fungible token balances. Methods like `freezePartialTokens` have been removed in favor of `freezeSecurity`. An additional `securityId` param is required for any method that modifies an account’s equity balance. In addition to `mint` and `burn` [ERC-20](./eip-20.md) breaking changes, this standard introduces: From de0586ab3944aa2fa93e5303f21fe21b5886be0d Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Fri, 8 Nov 2024 13:26:24 -0800 Subject: [PATCH 17/31] erc-1155 update --- ERCS/erc-7752.md | 1424 ++++++++++++++++++++++++++++------------------ 1 file changed, 878 insertions(+), 546 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index fa37b50c9c8..30bb590d08b 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -1,7 +1,7 @@ --- eip: 7752 -title: Equity Token -description: Equity token for corporations +title: Private Equity Token +description: Token representing private equity asset such as shares or interests author: Matt Rosendin (@mrosendin) discussions-to: https://ethereum-magicians.org/t/erc-7752-equity-token/20735 status: Draft @@ -13,594 +13,926 @@ requires: 173 ## Abstract -An ERC token standard representing shares in a corporation. This new interface standardizes equity management for issuers, investors, transfer agents, and financial intermediaries. +An ERC token standard representing private securities. This new interface standardizes equity management for issuers, investors, transfer agents, and financial intermediaries. ## Motivation -Creating a standard for corporation requires an industry effort. That's why this token standard adheres to the same design principles as the Open Cap Format (OCF), an industry-approved data standard for cap tables. Now everyone can adopt the same OCF-compliant token standard for Ethereum and beyond! +Equity tokens represent ownership shares of a company or asset, encapsulated in a digital token format on the blockchain. While traditional equity mainly refers to stock in a company, there's a growing need to represent ownership in alternative assets like fund interests, real estate, or Special Purpose Vehicle (SPV) interests. Traditional methods of managing and transferring equity and alternative assets are often cumbersome, opaque, and involve significant administrative overhead. By bringing these assets on-chain, we can leverage blockchain technology to enhance transparency, efficiency, and accessibility in equity and alternative asset markets. + +This ERC introduces a standard for representing ownership of equity and alternative assets on-chain, enabling companies and asset managers to issue, manage, and transfer tokens seamlessly. Key use cases for these tokens include: + +- **Simplified Asset Management**: Automate cap table and asset register updates with each token transfer, reducing the need for manual reconciliation and minimizing errors. +- **Enhanced Liquidity**: Facilitate secondary markets for private equity and alternative assets, allowing shareholders and investors to trade their tokens under compliant conditions. +- **Fractional Ownership**: Enable investors to hold fractional interests in assets like funds and SPVs, lowering the barrier to entry and allowing for more diversified investment portfolios. +- **Diverse Asset Representation**: Tokenize a wide range of assets beyond traditional stock, including fund interests, SPV shares, real estate, and more. +- **Automated Compliance**: Enforce regulatory requirements and transfer restrictions programmatically through smart contracts. +- **Streamlined Corporate Actions**: Simplify processes like dividend distribution, profit sharing, voting, and investor communications by utilizing token holder data on the blockchain. + +Implementing tokens representing equity and alternative assets on-chain provides a unified interface for issuers, investors, transfer agents, and financial intermediaries to interact with these assets. By adhering to the same design principles as the Open Cap Format (OCF)—an industry-approved data standard for cap tables and asset registers—this token standard ensures compatibility with existing management systems while leveraging the benefits of blockchain technology. + +This standard bridges the gap between traditional equity and alternative asset markets and decentralized finance, fostering a more efficient, transparent, and inclusive financial ecosystem on Ethereum and beyond. By standardizing tokens for equity and alternative assets, we pave the way for innovative financial products and services that can streamline venture capital, private equity, and asset management operations. ## 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. +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](https://www.rfc-editor.org/rfc/rfc2119) and [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174). -MUST NOT use [ERC-20](./eip-20.md) methods, which have breaking changes. +An ERC-7752 compliant smart contract represents ownership shares of equity and alternative assets, enabling the issuance, management, and transfer of these tokens while ensuring regulatory compliance and efficient asset management. -SHOULD use `addShareClass(bytes32 name, string uri)` to create at least one share class before minting tokens. +### Overview -SHOULD use `balanceOf(address user)` to get a holder's total share count, incuding all share classes. +Every ERC-7752 compliant contract MUST implement the `IERC7752` interface. This standard defines a set of methods and events that enable: -SHOULD use `balanceOf(address user, string name)` to lookup a holder's balance by the specified share class name. +- **Token Initialization** +- **Minting and Burning Tokens** +- **Pause and Unpause Operations** +- **Freezing Addresses and Tokens** +- **Token Transfers** +- **Approvals and Allowances** +- **Batch Operations** +- **Token Information Retrieval** +- **Compliance and Identity Management** +- **Recovery Mechanisms** -SHOULD use `totalSupply()` to get the total outstanding share count, incuding all share classes. +### Token Initialization -SHOULD use `totalSupply(string name)` to lookup outstanding shares by the specified class name. +The contract MUST implement an `init` function to initialize the token with necessary parameters: + +```solidity +function init( + address identityRegistryAddress, + address complianceAddress, + string memory tokenName, + string memory tokenPrefix, + string memory tokenURI, + address tokenIdentity +) external; +``` -SHOULD use `securities(bytes32 securityId)` to fetch individual security details. +- **Parameters**: -MAY assign a `uri` when creating a share class that points to a JSON file containing relevant metadata. + - `identityRegistryAddress`: Address of the Identity Registry contract. + - `complianceAddress`: Address of the Compliance contract. + - `tokenName`: Name of the token. + - `tokenPrefix`: Prefix of the token symbol. + - `tokenURI`: Base URI for the tokens. + - `tokenIdentity`: On-chain identity address of the token issuer. -MAY specify a value of `0` for `startTime`, `cliffTime`, and `duration` parameters in `issue` for stock issuances without a vesting condition. +- **Requirements**: + - MUST be called before any other function. + - MUST emit the `UpdatedTokenInformation` and `IdentityRegistryAdded` events. -This standard is not backwards compatible with [ERC-20](./eip-20.md) nor [ERC-3643](./eip-3643.md) since it generates a unique security ID for each transfer and issuance. Mint and burn methods have been renamed to issue and cancel. The security ID provides a unique identifier for every balance resulting from an issuance operation and for every residual and/or resulting balances from a transfer operation. +### Minting and Burning Tokens -Furthermore, vesting and governing documentation are separate concerns not addressed in this standard, requiring their own companion ERCs. For now the vesting parameters in the `issue` method are temporary until a vesting spec is formalized. A vesting and governing document contract should be linked to the equity token. This approach is being taken to cut down on the equity token contract bytesize. +#### Minting Tokens -Every compliant contract must implement this interface: +The contract MUST implement a `mint` function to issue new tokens: ```solidity -/// @dev types -library Types { +function mint( + address to, + uint256 amount, + string memory uri, + bytes memory data +) external returns (uint256 tokenId); +``` - /// @dev Stores details about the acquisition of a security - struct Security { - bytes32 id; - address holder; - bytes32 class; - bytes32 balanceSecurityId; - bytes32[] resultingSecurityIds; - uint256 amount; - uint256 issuedAt; // Block timestamp of issuance tx - string uri; // Additional metadata apart from share type metadata - bool isFrozen; - } +- **Parameters**: -} + - `to`: Address of the recipient. + - `amount`: Amount of tokens to mint. + - `uri`: URI pointing to the token’s metadata. + - `data`: Additional data for compliance and record-keeping. + +- **Requirements**: + - MUST check compliance rules before minting. + - MUST only be callable by an address with the `AgentRole`. + - MUST emit the `Mint` event upon successful minting. + +#### Burning Tokens + +The contract MUST implement a `burn` function to cancel existing tokens: + +```solidity +function burn( + address from, + uint256 id, + uint256 amount +) external; +``` + +- **Parameters**: + + - `from`: Address holding the tokens. + - `id`: Token ID to burn. + - `amount`: Amount of tokens to burn. + +- **Requirements**: + - MUST verify that the `from` address has sufficient balance. + - MUST only be callable by an address with the `AgentRole`. + - MUST emit the `Burn` event upon successful burning. + +### Pause and Unpause Operations + +The contract MUST support pausing and unpausing of token transfers: + +```solidity +function pause() external; +function unpause() external; +``` + +- **Requirements**: + - MUST only be callable by an address with the `AgentRole`. + - When paused, all token transfers MUST be blocked. + - MUST emit the `Paused` or `Unpaused` events accordingly. + +### Freezing Addresses and Tokens + +#### Freezing Addresses + +The contract MUST allow freezing or unfreezing of specific addresses: + +```solidity +function freezeAddress(address account) external; +function unfreezeAddress(address account) external; +``` + +- **Parameters**: + + - `account`: Address to be frozen or unfrozen. + +- **Requirements**: + - MUST only be callable by an address with the `AgentRole`. + - When an address is frozen, it MUST NOT be able to send or receive tokens. + - MUST emit the `AddressFrozen` event upon changes. + +#### Freezing Tokens + +The contract MUST allow freezing or unfreezing of specific tokens: + +```solidity +function freezeToken(uint256 _id) external; +function unfreezeToken(uint256 _id) external; +``` + +- **Parameters**: + + - `_id`: Token ID to be frozen or unfrozen. + +- **Requirements**: + - MUST only be callable by an address with the `AgentRole`. + - When a token is frozen, it MUST NOT be transferable. + - MUST emit the `TokenFrozen` or `TokenUnfrozen` events accordingly. + +### Token Transfers + +#### Standard Transfers + +The contract MUST implement a `transferFrom` function for transferring tokens: + +```solidity +function transferFrom( + address from, + address to, + uint256 tokenId, + uint256 amount, + bytes memory data +) external returns (uint256 newTokenId); +``` + +- **Parameters**: -/// @dev interface -interface IToken { - - /// events - - event Approval(address indexed _owner, address indexed _spender, uint256 _amount); - - event Transfer(address indexed from, address indexed to, bytes32 securityId, uint256 amount, bytes32[] newIds); - - event ShareClassAdded(bytes32 indexed _class, string indexed _uri); - - event ShareClassUpdated(bytes32 indexed _class, string indexed _uri); - - event ShareClassRemoved(bytes32 indexed _class); - - function getMetadataURIs() external view returns (string[] memory); - - /** - * this event is emitted when the token information is updated. - * the event is emitted by the token init function and by the setTokenInformation function - * `_newName` is the name of the token - * `_newSymbol` is the symbol of the token - * `_newDecimals` is the decimals of the token - * `_newVersion` is the version of the token, current version is 3.0 - * `_newOnchainID` is the address of the onchainID of the token - */ - event UpdatedTokenInformation( - string indexed _newName, - string indexed _newSymbol, - uint8 _newDecimals, - string _newVersion, - address indexed _newOnchainID - ); - - /** - * this event is emitted when the IdentityRegistry has been set for the token - * the event is emitted by the token constructor and by the setIdentityRegistry function - * `_identityRegistry` is the address of the Identity Registry of the token - */ - event IdentityRegistryAdded(address indexed _identityRegistry); - - /** - * this event is emitted when the Compliance has been set for the token - * the event is emitted by the token constructor and by the setCompliance function - * `_compliance` is the address of the Compliance contract of the token - */ - event ComplianceAdded(address indexed _compliance); - - /** - * this event is emitted when an investor successfully recovers his tokens - * the event is emitted by the recoveryAddress function - * `_lostWallet` is the address of the wallet that the investor lost access to - * `_newWallet` is the address of the wallet that the investor provided for the recovery - * `_investorOnchainID` is the address of the onchainID of the investor who asked for a recovery - */ - event RecoverySuccess(address indexed _lostWallet, address indexed _newWallet, address indexed _investorOnchainID); - - /** - * this event is emitted when the wallet of an investor is frozen or unfrozen - * the event is emitted by setAddressFrozen and batchSetAddressFrozen functions - * `_userAddress` is the wallet of the investor that is concerned by the freezing status - * `_isFrozen` is the freezing status of the wallet - * if `_isFrozen` equals `true` the wallet is frozen after emission of the event - * if `_isFrozen` equals `false` the wallet is unfrozen after emission of the event - * `_owner` is the address of the agent who called the function to freezeSecurity the wallet - */ - event AddressFrozen(address indexed _userAddress, bool indexed _isFrozen, address indexed _owner); - - /** - * this event is emitted when a certain security is frozen on a wallet - * the event is emitted by freezeSecurity and batchFreezePartialTokens functions - * `_userAddress` is the wallet of the investor that is concerned by the freezing status - * `_securityId` is the ID of the security to freezeSecurity - */ - event SecurityFrozen(address indexed _userAddress, bytes32 _securityId); - - /** - * this event is emitted when a certain security is unfrozen on a wallet - * the event is emitted by unfreezeSecurity and batchUnfreezeSecurities functions - * `_userAddress` is the wallet of the investor that is concerned by the freezing status - * `_securityId` is the ID of the security to unfreezeSecurity - */ - event SecurityUnfrozen(address indexed _userAddress, bytes32 _securityId); - - /** - * this event is emitted when the token is paused - * the event is emitted by the pause function - * `_userAddress` is the address of the wallet that called the pause function - */ - event Paused(address _userAddress); - - /** - * this event is emitted when the token is unpaused - * the event is emitted by the unpause function - * `_userAddress` is the address of the wallet that called the unpause function - */ - event Unpaused(address _userAddress); - - /// @dev Getter for securities mapping - - function securities(bytes32 securityId) external view returns (Types.Security memory); - - /// @dev Based on ERC1400 and ERC1155 - - function addShareClass( - bytes32 _class, - string memory _uri - ) external; - - function removeShareClass(bytes32 _class) external; - - /** - * @notice Fetches all share type details - */ - function getShareClasses() external view returns (bytes32[] calldata); - - /** - * @notice Fetches total supply by share class - */ - function totalSupplyByClass(bytes32 _class) external view returns (uint256); - - /// @dev Based on ERC20 - - function allowance(address owner, bytes32 shareClass, address spender) external view returns (uint256); - function approve(address spender, bytes32 shareClass, uint256 amount) external returns (bool); - function increaseAllowance(address spender, bytes32 shareClass, uint256 addedValue) external returns (bool); - function decreaseAllowance(address spender, bytes32 shareClass, uint256 subtractedValue) external returns (bool); - function totalSupply() external view returns (uint256); - - /** - * @notice Returns the total balance of all share classes for the user. - * @param account The account to fetch the balance of. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @param account The holder of the tokens to fetch the balance for - * @param shareClass The name of the share class to query - */ - function balanceOf(address account, bytes32 shareClass) external view returns (uint256); - - /** - * @notice ERC-20 overridden function that include logic to check for trade validity. - * Require that the from and to addresses are not frozen. - * Require that the value should not exceed available balance. - * Require that the to address is a verified address. - * @param _from The address of the sender - * @param _to The address of the receiver - * @param _securityId The ID of the security to transfer - * @param _amount The number of tokens to transfer - * @return `true` if successful and revert if unsuccessful - */ - function transferFrom( + - `from`: Address sending the tokens. + - `to`: Address receiving the tokens. + - `tokenId`: ID of the token being transferred. + - `amount`: Amount of tokens to transfer. + - `data`: Additional data for compliance checks. + +- **Requirements**: + - MUST check if transfers are not paused. + - MUST verify that neither the `from` nor the `to` addresses are frozen. + - MUST enforce compliance rules via the Compliance contract. + - MUST emit the `Transfer` and `TransferValue` events upon successful transfer. + +#### Forced Transfers + +The contract SHOULD implement a `forcedTransfer` function to allow transfers without holder consent: + +```solidity +function forcedTransfer( address _from, address _to, - bytes32 _securityId, + uint256 _id, uint256 _amount - ) external returns (bool); - - /** - * @notice ERC-20 overridden function that include logic to check for trade validity. - * Require that the msg.sender and to addresses are not frozen. - * Require that the value should not exceed available balance . - * Require that the to address is a verified address - * @param _to The address of the receiver - * @param _securityId The ID of the security to transfer - * @param _amount The number of tokens to transfer - * @return `true` if successful and revert if unsuccessful - */ - function transfer(address _to, bytes32 _securityId, uint256 _amount) external returns (bool); - - /// @dev Based on ERC3643 - - /** - * @dev sets the token name - * @param _name the name of token to set - * Only the owner of the token smart contract can call this function - * emits a `UpdatedTokenInformation` event - */ - function setName(string calldata _name) external; - - /** - * @dev sets the token symbol - * @param _symbol the token symbol to set - * Only the owner of the token smart contract can call this function - * emits a `UpdatedTokenInformation` event - */ - function setSymbol(string calldata _symbol) external; - - /** - * @dev sets the onchain ID of the token - * @param _onchainID the address of the onchain ID to set - * Only the owner of the token smart contract can call this function - * emits a `UpdatedTokenInformation` event - */ - function setOnchainID(address _onchainID) external; - - /** - * @dev pauses the token contract, when contract is paused investors cannot transfer tokens anymore - * This function can only be called by a wallet set as agent of the token - * emits a `Paused` event - */ - function pause() external; - - /** - * @dev unpauses the token contract, when contract is unpaused investors can transfer tokens - * if their wallet is not blocked & if the amount to transfer is <= to the amount of free tokens - * This function can only be called by a wallet set as agent of the token - * emits an `Unpaused` event - */ - function unpause() external; - - /** - * @dev sets an address frozen status for this token. - * @param _userAddress The address for which to update frozen status - * @param _freeze Frozen status of the address - * This function can only be called by a wallet set as agent of the token - * emits an `AddressFrozen` event - */ - function setAddressFrozen(address _userAddress, bool _freeze) external; - - /** - * @dev freezes security specified for given address. - * @param _userAddress The address for which to update frozen tokens - * @param _securityId The security ID to perform partial freezeSecurity on - * This function can only be called by a wallet set as agent of the token - * emits a `SecurityFrozen` event - */ - function freezeSecurity(address _userAddress, bytes32 _securityId) external; - - /** - * @dev unfreezes security specified for given address - * @param _userAddress The address for which to update frozen tokens - * @param _securityId The security ID to perform partial freezeSecurity on - * This function can only be called by a wallet set as agent of the token - * emits a `SecurityUnfrozen` event - */ - function unfreezeSecurity(address _userAddress, bytes32 _securityId) external; - - /** - * @dev sets the Identity Registry for the token - * @param _identityRegistry the address of the Identity Registry to set - * Only the owner of the token smart contract can call this function - * emits an `IdentityRegistryAdded` event - */ - function setIdentityRegistry(address _identityRegistry) external; - - /** - * @dev sets the compliance contract of the token - * @param _compliance the address of the compliance contract to set - * Only the owner of the token smart contract can call this function - * calls bindToken on the compliance contract - * emits a `ComplianceAdded` event - */ - function setCompliance(address _compliance) external; - - /** - * @dev force a transfer of tokens between 2 whitelisted wallets - * In case the `from` address has not enough free tokens (unfrozen tokens) - * but has a total balance higher or equal to the `amount` - * the amount of frozen tokens is reduced in order to have enough free tokens - * to proceed the transfer, in such a case, the remaining balance on the `from` - * account is 100% composed of frozen tokens post-transfer. - * Require that the `to` address is a verified address, - * @param _from The address of the sender - * @param _to The address of the receiver - * @param _securityId The ID of the security to transfer - * @param _amount The number of tokens to transfer - * @return `true` if successful and revert if unsuccessful - * This function can only be called by a wallet set as agent of the token - * emits a `SecurityUnfrozen` event if `_amount` is higher than the free balance of `_from` - * emits a `Transfer` event - */ - function forcedTransfer(address _from, address _to, bytes32 _securityId, uint256 _amount) external returns (bool); - - /** - * @dev issue tokens on a wallet - * Improved version of default issue method. Tokens can be issueed - * to an address if only it is a verified address as per the security token. - * @param _to Address to issue the tokens to. - * @param _shareClass The share type of the security (e.g., common stock, preferred stock series). - * @param _amount The amount of tokens to issue. - * @param _uri The URI containing metadata about the security. - * @param _startTime The start time of the vesting period. - * @param _cliffTime The cliff time of the vesting period. - * @param _duration The duration of the vesting period. - * This function can only be called by a wallet set as agent of the token - * emits a `Transfer` event - */ - function issue( - address _to, - bytes32 _shareClass, - uint256 _amount, - string memory _uri, - uint256 _startTime, - uint256 _cliffTime, - uint256 _duration - ) external; - - /** - * @dev cancel tokens on a wallet - * In case the `account` address has not enough free tokens (unfrozen tokens) - * but has a total balance higher or equal to the `value` amount - * the amount of frozen tokens is reduced in order to have enough free tokens - * to proceed the cancel, in such a case, the remaining balance on the `account` - * is 100% composed of frozen tokens post-transaction. - * @param _userAddress Address to cancel the tokens from. - * @param _securityId The ID of the security to cancel. - * @param _amount Amount of tokens to cancel. - * This function can only be called by a wallet set as agent of the token - * emits a `SecurityUnfrozen` event if `_amount` is higher than the free balance of `_userAddress` - * emits a `Transfer` event - */ - function cancel(address _userAddress, bytes32 _securityId, uint256 _amount) external; - - /** - * @dev recovery function used to force transfer tokens from a - * lost wallet to a new wallet for an investor. - * @param _lostWallet the wallet that the investor lost - * @param _newWallet the newly provided wallet on which tokens have to be transferred - * @param _investorOnchainID the onchainID of the investor asking for a recovery - * This function can only be called by a wallet set as agent of the token - * emits a `SecurityUnfrozen` event if there are frozen tokens on the lost wallet if recovery process successful - * emits a `Transfer` event if the recovery process is successful - * emits a `RecoverySuccess` event if the recovery process is successful - * emits a `RecoveryFails` event if the recovery process fails - */ - function recoveryAddress(address _lostWallet, address _newWallet, address _investorOnchainID) external returns (bool); - - /** - * @dev function allowing to issue transfers in batch - * Require that the msg.sender and `to` addresses are not frozen. - * Require that the total value should not exceed available balance. - * Require that the `to` addresses are all verified addresses, - * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_toList.length` IS TOO HIGH, - * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION - * @param _toList The addresses of the receivers - * @param _securityIds The IDs of the securities to transfer - * @param _amounts The number of tokens to transfer to the corresponding receiver - * emits _toList.length `Transfer` events - */ - function batchTransfer( +) external returns (uint256 newTokenId); +``` + +- **Requirements**: + - MUST only be callable by an address with the `AgentRole`. + - SHOULD be used in cases of legal enforcement or compliance violations. + - MUST emit the `Transfer` and `TransferValue` events. + +### Approvals and Allowances + +The contract MUST implement approval mechanisms for delegated transfers: + +```solidity +function approve(address to, uint256 tokenId) external; +function setApprovalForAll(address operator, bool approved) external; +function getApproved(uint256 tokenId) external view returns (address); +function isApprovedForAll(address owner, address operator) external view returns (bool); +``` + +- **Requirements**: + - MUST allow token holders to approve other addresses to manage their tokens. + - MUST emit `Approval` and `ApprovalForAll` events as appropriate. + +### Batch Operations + +To improve efficiency, the contract SHOULD implement batch operations: + +#### Batch Safe Transfer + +```solidity +function batchSafeTransfer( + uint256[] calldata _tokenIds, address[] calldata _toList, - bytes32[] memory _securityIds, - uint256[] calldata _amounts - ) external; - - /** - * @dev function allowing to issue forced transfers in batch - * Require that `_amounts[i]` should not exceed available balance of `_fromList[i]`. - * Require that the `_toList` addresses are all verified addresses - * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_fromList.length` IS TOO HIGH, - * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION - * @param _fromList The addresses of the senders - * @param _toList The addresses of the receivers - * @param _securityIds The IDs of the securities to transfer - * @param _amounts The number of tokens to transfer to the corresponding receiver - * This function can only be called by a wallet set as agent of the token - * emits `SecurityUnfrozen` events if `_amounts[i]` is higher than the free balance of `_fromList[i]` - * emits _fromList.length `Transfer` events - */ - function batchForcedTransfer( + uint256[] calldata _amounts, + bytes[] calldata _dataList +) external; +``` + +- **Requirements**: + - MUST perform multiple transfers in a single transaction. + - MUST enforce the same checks as individual transfers. + +#### Batch Freezing Addresses + +```solidity +function batchSetAddressFrozen(address[] calldata _userAddresses, bool[] calldata _freeze) external; +``` + +- **Requirements**: + - MUST allow freezing or unfreezing multiple addresses. + - MUST only be callable by an address with the `AgentRole`. + +#### Batch Freezing Tokens + +```solidity +function batchFreezeTokens(uint256[] calldata _ids) external; +function batchUnfreezeTokens(uint256[] calldata _ids) external; +``` + +- **Requirements**: + - MUST allow freezing or unfreezing multiple tokens. + - MUST only be callable by an address with the `AgentRole`. + +#### Batch Forced Transfers + +```solidity +function batchForcedTransfer( address[] calldata _fromList, address[] calldata _toList, - bytes32[] calldata _securityIds, - uint256[] calldata _amounts - ) external; - - /** - * @dev Batch issues multiple securities at once. - * Require that the `_toList` addresses are all verified addresses - * IMPORTANT: THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_toList.length` IS TOO HIGH, - * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION - * @param _toList The addresses of the receivers - * @param _classes Array of classes for each security. - * @param _amounts Array of amounts to issue for each security. - * @param _metadataURIs Array of URIs containing metadata about each security. - * @param _startTimes Array of start times for vesting period - * @param _cliffTimes Array of cliff times for vesting period - * @param _durations Array of durations of vesting period - * This function can only be called by a wallet set as agent of the token - * emits _toList.length `Transfer` events - */ - function batchIssue( - address[] calldata _toList, - bytes32[] memory _classes, - uint256[] calldata _amounts, - string[] memory _metadataURIs, - uint256[] calldata _startTimes, - uint256[] calldata _cliffTimes, - uint256[] calldata _durations - ) external; - - /** - * @dev function allowing to cancel tokens in batch - * Require that the `_userAddresses` addresses are all verified addresses - * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH, - * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION - * @param _userAddresses The addresses of the wallets concerned by the cancel - * @param _securityIds The IDs of the securities to batch cancel - * @param _amounts The number of tokens to cancel from the corresponding wallets - * This function can only be called by a wallet set as agent of the token - * emits _userAddresses.length `Transfer` events - */ - function batchCancel( - address[] calldata _userAddresses, - bytes32[] calldata _securityIds, + uint256[] calldata _ids, uint256[] calldata _amounts - ) external; - - /** - * @dev function allowing to set frozen addresses in batch - * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH, - * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION - * @param _userAddresses The addresses for which to update frozen status - * @param _freeze Frozen status of the corresponding address - * This function can only be called by a wallet set as agent of the token - * emits _userAddresses.length `AddressFrozen` events - */ - function batchSetAddressFrozen(address[] calldata _userAddresses, bool[] calldata _freeze) external; - - /** - * @dev function allowing to freezeSecurity securities in batch - * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH, - * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION - * @param _userAddresses The addresses on which tokens need to be frozen - * @param _securityIds The security IDs to operate on - * This function can only be called by a wallet set as agent of the token - * emits _userAddresses.length `SecurityFrozen` events - */ - function batchFreezeSecurities( - address[] calldata _userAddresses, - bytes32[] calldata _securityIds - ) external; - - /** - * @dev function allowing to unfreezeSecurity securities in batch - * IMPORTANT : THIS TRANSACTION COULD EXCEED GAS LIMIT IF `_userAddresses.length` IS TOO HIGH, - * USE WITH CARE OR YOU COULD LOSE TX FEES WITH AN "OUT OF GAS" TRANSACTION - * @param _userAddresses The addresses on which tokens need to be unfrozen - * @param _securityIds The security IDs to operate on - * This function can only be called by a wallet set as agent of the token - * emits _userAddresses.length `SecurityUnfrozen` events - */ - function batchUnfreezeSecurities( - address[] calldata _userAddresses, - bytes32[] calldata _securityIds - ) external; - - /** - * @dev Returns the number of decimals used to get its user representation. - * For example, if `decimals` equals `2`, a balance of `505` tokens should - * be displayed to a user as `5,05` (`505 / 1 ** 2`). - * - * Tokens usually opt for a value of 18, imitating the relationship between - * Ether and Wei. - * - * NOTE: This information is only used for _display_ purposes: it in - * no way affects any of the arithmetic of the contract, including - * balanceOf() and transfer(). - */ - function decimals() external view returns (uint8); - - /** - * @dev Returns the name of the token. - */ - function name() external view returns (string memory); - - /** - * @dev Returns the address of the onchainID of the token. - * the onchainID of the token gives all the information available - * about the token and is managed by the token issuer or his agent. - */ - function onchainID() external view returns (address); - - /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. - */ - function symbol() external view returns (string memory); - - /** - * @dev Returns the TREX version of the token. - * current version is 3.0.0 - */ - function version() external view returns (string memory); - - /** - * @dev Returns the Identity Registry linked to the token - */ - function identityRegistry() external view returns (IIdentityRegistry); - - /** - * @dev Returns the Compliance contract linked to the token - */ - function compliance() external view returns (IModularCompliance); - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() external view returns (bool); - - /** - * @dev Returns the freezing status of a wallet - * if isFrozen returns `true` the wallet is frozen - * if isFrozen returns `false` the wallet is not frozen - * isFrozen returning `true` doesn't mean that the balance is free, tokens could be blocked by - * a partial freezeSecurity or the whole token could be blocked by pause - * @param _userAddress the address of the wallet on which isFrozen is called - */ - function isFrozen(address _userAddress) external view returns (bool); +) external; +``` + +- **Requirements**: + - MUST perform multiple forced transfers. + - MUST only be callable by an address with the `AgentRole`. + +### Token Information Retrieval + +The contract MUST provide functions to retrieve token and account information: + +#### Token Metadata + +```solidity +function uri(uint256 tokenId) external view returns (string memory); +``` + +- **Requirements**: + - MUST return the URI pointing to the token's metadata. + +#### Balances and Ownership + +```solidity +function balanceOf(address account) external view returns (uint256); +function balanceOf(uint256 tokenId) external view returns (uint256); +function ownerOf(uint256 tokenId) external view returns (address); +``` + +- **Requirements**: + - `balanceOf(address)` MUST return the total balance of an account across all tokens. + - `balanceOf(uint256)` MUST return the balance associated with a specific token ID. + - `ownerOf` MUST return the owner address of a specific token ID. + +#### Token Status + +```solidity +function isFrozen(address account) external view returns (bool); +function isTokenFrozen(uint256 tokenId) external view returns (bool); +function paused() external view returns (bool); +``` + +- **Requirements**: + - MUST accurately reflect the frozen status of addresses and tokens. + - MUST indicate whether the contract is paused. + +#### Token Details + +```solidity +function getSecurity(uint256 tokenId) external view returns ( + address owner, + uint256 balance, + string memory uri, + bytes memory data, + Types.Status status, + uint256 newTokenId, + uint256 residualTokenId +); +``` + +- **Requirements**: + - MUST return detailed information about a specific token/security issuance. + +#### Contract Metadata + +```solidity +function name() external view returns (string memory); +function prefix() external view returns (string memory); +function onchainID() external view returns (address); +function version() external pure returns (string memory); +``` + +- **Requirements**: + - MUST provide accurate metadata about the token contract. + +### Compliance and Identity Management + +The contract MUST interact with Compliance and Identity Registry contracts: + +#### Identity Registry + +```solidity +function identityRegistry() external view returns (IIdentityRegistry); +function setIdentityRegistry(address identityRegistryAddress) external; +``` + +- **Requirements**: + - MUST enforce identity verification for token holders. + - `setIdentityRegistry` MUST only be callable by the contract owner. + - MUST emit the `IdentityRegistryAdded` event when updated. + +#### Compliance Contract + +```solidity +function compliance() external view returns (IModularCompliance); +function setCompliance(address complianceAddress) external; +``` + +- **Requirements**: + - MUST enforce regulatory compliance for token transfers. + - `setCompliance` MUST only be callable by the contract owner. + - MUST emit the `ComplianceAdded` event when updated. + +### Recovery Mechanisms + +The contract SHOULD provide mechanisms to recover tokens if a wallet is lost: + +```solidity +function recoveryAddress( + address _lostWallet, + address _newWallet, + address _investorOnchainID, + uint256[] calldata _ids +) external returns (bool); +``` + +- **Requirements**: + - MUST only be callable by an address with the `AgentRole`. + - MUST verify the identity of the investor through the Identity Registry. + - MUST emit the `RecoverySuccess` event upon successful recovery. + +### Locking and Unlocking Tokens + +The contract MUST allow token holders to lock and unlock their tokens: + +```solidity +function lockTokens(uint256 tokenId, uint256 amount) external; +function unlockTokens(uint256 tokenId, uint256 amount) external; +``` + +- **Requirements**: + - MUST prevent locked tokens from being transferred. + - MUST emit `TokensLocked` and `TokensUnlocked` events accordingly. + +### Events + +The contract MUST emit events as specified in the interface to enable off-chain monitoring and compliance: + +- `TokensLocked` +- `TokensUnlocked` +- `Approval` +- `ApprovalForAll` +- `Transfer` +- `TransferValue` +- `TokenInvalidated` +- `URI` +- `AgentAddedForToken` +- `AgentRemovedForToken` +- `UpdatedTokenInformation` +- `IdentityRegistryAdded` +- `ComplianceAdded` +- `Paused` +- `Unpaused` +- `Mint` +- `Burn` +- `AddressFrozen` +- `TokenFrozen` +- `TokenUnfrozen` +- `RecoverySuccess` + +### Access Control + +- The contract MUST implement role-based access control to restrict certain functions to authorized addresses (e.g., `AgentRole`). +- Ownership and agent roles SHOULD be managed securely to prevent unauthorized access. + +### Compliance Requirements + +- The contract MUST integrate with compliance modules to enforce KYC/AML regulations. +- Transfers MUST be checked against compliance rules before execution. + +### Error Handling + +- The contract SHOULD use standard OpenZeppelin error messages and revert reasons for consistency. +- MUST revert transactions that violate compliance rules, are paused, or involve frozen addresses/tokens. + +### Backwards Compatibility + +This standard is not backwards compatible with [ERC-20](./eip-20.md) or [ERC-3643](./eip-3643.md) due to: + +- Introduction of unique `tokenId` for each issuance and transfer, differentiating tokens beyond simple fungible balances. +- Replacement of `mint` and `burn` methods with `issue` and `cancel` in earlier drafts, although `mint` and `burn` are used in this interface. +- Emphasis on individual securities rather than fungible token balances. + +### Notes + +- Vesting and governance mechanisms are considered separate concerns and SHOULD be addressed in companion ERCs. +- Contracts implementing this standard SHOULD ensure that they remain within the maximum contract size limits. +- Developers SHOULD consider gas efficiency when implementing batch operations and compliance checks. + +```solidity +library Types { + enum Status { + Outstanding, + Burn, + Transferred, + Converted, + Repurchased, + PartiallyExercised, + FullyExercised, + Forfeited, + Expired + } +} + +interface IERC7752 { + event TokensLocked(address indexed caller, uint256 indexed tokenId, uint256 indexed amount); + event TokensUnlocked(address indexed caller, uint256 indexed tokenId, uint256 indexed amount); + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + event ApprovalForAll(address indexed owner, address indexed operator, bool indexed approved); + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + event TransferValue(uint256 indexed fromTokenId, uint256 indexed toTokenId, uint256 indexed amount); + event TokenInvalidated(uint256 indexed tokenId); + event URI(string uri, uint256 indexed tokenId); + event AgentAddedForToken(uint256 indexed tokenId, address indexed agent); + event AgentRemovedForToken(uint256 indexed tokenId, address indexed agent); + + /// @dev Emitted when the token information is updated. + event UpdatedTokenInformation( + string name, + string prefix, + string version, + address indexed onchainID + ); + + /** + * this event is emitted when the IdentityRegistry has been set for the token + * the event is emitted by the token constructor and by the setIdentityRegistry function + * `_identityRegistry` is the address of the Identity Registry of the token + */ + event IdentityRegistryAdded(address indexed _identityRegistry); + + /** + * this event is emitted when the Compliance has been set for the token + * the event is emitted by the token constructor and by the setCompliance function + * `_compliance` is the address of the Compliance contract of the token + */ + event ComplianceAdded(address indexed _compliance); + + /// @dev Emitted when the contract is paused. + event Paused(address indexed account); + + /// @dev Emitted when the contract is unpaused. + event Unpaused(address indexed account); + + /// @dev Emitted when a security is issued. + event Mint(address indexed to, uint256 indexed id, uint256 amount); + + /// @dev Emitted when a security is canceled (burned). + event Burn(address indexed from, uint256 indexed id, uint256 amount); + + /// @dev Emitted when an address is frozen or unfrozen. + event AddressFrozen(address indexed account, bool isFrozen); + + /// @dev Emitted when tokens are frozen. + event TokenFrozen(uint256 indexed tokenId); + + /// @dev Emitted when tokens are unfrozen. + event TokenUnfrozen(uint256 indexed tokenId); + + /// @dev Emitted when a recovery is successful. + event RecoverySuccess(address indexed lostWallet, address indexed newWallet, address indexed investorOnchainID); + + /** + * @dev Initializes the contract. + * @param identityRegistryAddress Address of the Identity Registry contract. + * @param complianceAddress Address of the Compliance contract. + * @param tokenName Name of the token. + * @param tokenPrefix Prefix of the token. + * @param tokenURI The base URI for the tokens. + * @param tokenIdentity On-chain identity address of the token. + */ + function init( + address identityRegistryAddress, + address complianceAddress, + string memory tokenName, + string memory tokenPrefix, + string memory tokenURI, + address tokenIdentity + ) external; + + function totalSupply() external returns (uint256); + + /** + * @dev Pauses all token transfers. + * + * Requirements: + * - The caller must have the `AgentRole`. + */ + function pause() external; + + /** + * @dev Unpauses all token transfers. + * + * Requirements: + * - The caller must have the `AgentRole`. + */ + function unpause() external; + + /** + * @dev Sets the Identity Registry contract address. + * @param identityRegistryAddress Address of the new Identity Registry. + * + * Requirements: + * - The caller must be the owner. + */ + function setIdentityRegistry(address identityRegistryAddress) external; + + /** + * @dev Sets the Compliance contract address. + * @param complianceAddress Address of the new Compliance contract. + * + * Requirements: + * - The caller must be the owner. + */ + function setCompliance(address complianceAddress) external; + + /** + * @dev Sets the name of the token. + * @param _name New name of the token. + * + * Requirements: + * - The caller must be the owner. + */ + function setName(string calldata _name) external; + + /** + * @dev Sets the prefix of the token. + * @param _prefix New prefix of the token. + * + * Requirements: + * - The caller must be the owner. + */ + function setPrefix(string calldata _prefix) external; + + /** + * @dev Sets the on-chain identity of the token. + * @param _onchainID New on-chain identity address. + * + * Requirements: + * - The caller must be the owner. + */ + function setOnchainID(address _onchainID) external; + + /** + * @dev Mints new tokens (certificates). + * @param to Address of the recipient. + * @param amount Amount of tokens to mint. + * @param uri URI of the certificate metadata. + * @param data Additional data for compliance. + * + * Requirements: + * - The caller must have the `AgentRole`. + */ + function mint( + address to, + uint256 amount, + string memory uri, + bytes memory data + ) external returns (uint256 tokenId); + + /** + * @dev Burns tokens (certificates). + * @param from Address from which to burn tokens. + * @param id Token ID to burn. + * @param amount Amount of tokens to burn. + * + * Requirements: + * - The caller must have the `AgentRole`. + */ + function burn( + address from, + uint256 id, + uint256 amount + ) external; + + /** + * @dev Freezes an address, restricting token transfers. + * @param account Address to freeze. + * + * Requirements: + * - The caller must have the `AgentRole`. + */ + function freezeAddress(address account) external; + + /** + * @dev Unfreezes an address, allowing token transfers. + * @param account Address to unfreeze. + * + * Requirements: + * - The caller must have the `AgentRole`. + */ + function unfreezeAddress(address account) external; + + function batchSafeTransfer( + uint256[] calldata _tokenIds, + address[] calldata _toList, + uint256[] calldata _amounts, + bytes[] calldata _dataList + ) external; + + function batchSetAddressFrozen(address[] calldata _userAddresses, bool[] calldata _freeze) external; + + function batchFreezeTokens(uint256[] calldata _ids) external; + function batchForcedTransfer( + address[] calldata _fromList, + address[] calldata _toList, + uint256[] calldata _ids, + uint256[] calldata _amounts + ) external; + function batchUnfreezeTokens(uint256[] calldata _ids) external; + function recoveryAddress( + address _lostWallet, + address _newWallet, + address _investorOnchainID, + uint256[] calldata _ids + ) external returns (bool); + + + function forcedTransfer( + address _from, + address _to, + uint256 _id, + uint256 _amount + ) external returns (uint256 newTokenId); + + function setAddressFrozen(address _userAddress, bool _freeze) external; + +/** + * @dev Freezes a specific token ID. + * @param _id Token ID to freeze. + * + * Requirements: + * - The caller must have the `AgentRole`. + */ + function freezeToken(uint256 _id) external; + + /** + * @dev Unfreezes a specific token ID. + * @param _id Token ID to unfreeze. + * + * Requirements: + * - The caller must have the `AgentRole`. + */ + function unfreezeToken(uint256 _id) external; + + function approve(address to, uint256 tokenId) external; + + /// @dev Approve or remove an operator for the caller + function setApprovalForAll(address operator, bool approved) external; + + /// @dev Transfers token from one address to another using approval mechanism + function transferFrom( + address from, + address to, + uint256 tokenId, + uint256 amount, + bytes memory data + ) external returns (uint256 newTokenId); + + /// @dev Function to lock tokens owned by the caller + function lockTokens(uint256 tokenId, uint256 amount) external; + + /// @dev Function to unlock tokens owned by the caller + function unlockTokens(uint256 tokenId, uint256 amount) external; + + /** + * @dev Returns the URI for a specific token ID. + * @param tokenId Token ID to query. + */ + function uri(uint256 tokenId) external view returns (string memory); + + /** + * @dev Returns true if the contract is paused. + */ + function paused() external view returns (bool); + + /** + * @dev Returns true if the given address is frozen. + * @param account Address to query. + */ + function isFrozen(address account) external view returns (bool); + + /** + * @dev Returns the Identity Registry address. + */ + function identityRegistry() external view returns (IIdentityRegistry); + + /** + * @dev Returns the Compliance contract address. + */ + function compliance() external view returns (IModularCompliance); + + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the prefix of the token. + */ + function prefix() external view returns (string memory); + + /** + * @dev Returns the on-chain identity of the token. + */ + function onchainID() external view returns (address); + + function isTokenFrozen(uint256 tokenId) external view returns (bool); + + // Function to get token balance + function balanceOf(uint256 tokenId) external view returns (uint256); + + // Function to get the account balance + function balanceOf(address account) external view returns (uint256); + + // Function to get token owner + function ownerOf(uint256 tokenId) external view returns (address); + + /// @dev Returns the approved address for a token ID, or zero if no address set + function getApproved(uint256 tokenId) external view returns (address); + + /// @dev Returns if the operator is allowed to manage all of the assets of owner + function isApprovedForAll(address owner, address operator) external view returns (bool); + + function getSecurity(uint256 tokenId) external view returns ( + address owner, + uint256 balance, + string memory uri, + bytes memory data, + Types.Status status, + uint256 newTokenId, + uint256 residualTokenId + ); + + /** + * @dev Returns the version of the token. + */ + function version() external pure returns (string memory); } ``` ## Rationale -Assigning a unique `securityId` to all issuances of equity tokens is the industry-accepted practice for assignment of unique vesting conditions, audit, and cost-basis tracking purposes. To keep the implementation under the max size limit, vesting conditions and governing documents are to be separate contracts with related data accessed by specifying a `securityId`. +The rationale behind ERC-7752 is to create a comprehensive and standardized framework for tokenizing equity and alternative assets on the Ethereum blockchain. Traditional financial instruments like stocks, fund interests, and SPV (Special Purpose Vehicle) shares are managed through legacy systems that are often inefficient, opaque, and fragmented. By standardizing these assets as tokens on the blockchain, we can address several key challenges in the current financial ecosystem. + +### Bridging Traditional Finance and Blockchain Technology + +One of the main goals of ERC-7752 is to bridge the gap between traditional finance (TradFi) and decentralized finance (DeFi). While blockchain technology has revolutionized many aspects of finance, there remains a significant divide when it comes to regulated securities and equity instruments. ERC-7752 aims to provide a compliant and secure method for representing these assets on-chain, ensuring that regulatory requirements are met while taking advantage of blockchain's benefits. + +### Compliance and Regulatory Considerations + +Compliance with KYC (Know Your Customer), AML (Anti-Money Laundering), and other regulatory requirements is paramount when dealing with equity and alternative assets. Unlike many existing token standards, ERC-7752 is designed with compliance at its core. By integrating with identity registries and modular compliance contracts, the standard ensures that only verified and authorized participants can hold and transfer tokens. This design caters to the needs of issuers and regulators, fostering trust and legitimacy in the tokenized assets. + +### Flexibility for Diverse Asset Classes + +ERC-7752 is deliberately designed to be flexible, accommodating a wide range of asset classes beyond traditional stock. By supporting the tokenization of fund interests, SPV shares, real estate assets, and more, the standard opens up opportunities for fractional ownership and broader investment participation. This inclusiveness is vital for democratizing access to investment opportunities and for fostering innovation in financial products. + +### Enhanced Operational Efficiency + +By automating processes such as cap table management, compliance checks, and corporate actions through smart contracts, ERC-7752 reduces administrative overhead and minimizes the risk of human error. This automation leads to cost savings and faster transaction times, benefiting both issuers and investors. + +### Compatibility with Industry Standards + +Adhering to the design principles of the Open Cap Format (OCF) ensures that ERC-7752 tokens are compatible with existing industry-approved data standards for cap tables and asset registers. This compatibility facilitates easier integration with legacy systems and promotes widespread adoption by aligning with familiar practices in the financial industry. + +### Security and Recovery Mechanisms + +Given the high value and regulatory importance of equity and alternative assets, ERC-7752 includes robust security features such as role-based access control, pausing mechanisms, and the ability to freeze addresses or tokens. Additionally, recovery functions are provided to handle situations like lost wallets, ensuring that asset control can be maintained or restored in compliance with legal requirements. + +### Avoiding Fragmentation of Standards + +While there are existing token standards like ERC-20 for fungible tokens and ERC-721 for non-fungible tokens, they do not adequately address the specific needs of equity and alternative asset tokenization, particularly regarding compliance and regulatory requirements. By introducing ERC-7752, we provide a specialized standard that prevents fragmentation and inconsistency in how these assets are represented on the blockchain. + +### Enabling Future Innovations + +By establishing a solid foundation for tokenized equity and alternative assets, ERC-7752 paves the way for future innovations in the financial ecosystem. Standardization allows developers and financial institutions to build new services and platforms, such as decentralized exchanges for security tokens, automated compliance solutions, and innovative investment products that were not feasible under traditional systems. + +### Conclusion + +ERC-7752 is a comprehensive standard that addresses the unique challenges of tokenizing equity and alternative assets. By focusing on compliance, flexibility, and operational efficiency, it fosters greater trust and participation in blockchain-based financial instruments. The standard aims to catalyze the convergence of traditional finance and blockchain technology, ultimately contributing to a more inclusive and efficient global financial system. ## Backwards Compatibility -Equity tokens are not backwards compatible with [ERC-20](./eip-20.md) nor [ERC-3643](./eip-3643.md). +ERC-7752 introduces a comprehensive framework for tokenizing equity and alternative assets, which significantly extends beyond the capabilities of earlier token standards like [ERC-20](https://eips.ethereum.org/EIPS/eip-20), [ERC-721](https://eips.ethereum.org/EIPS/eip-721), and [ERC-1155](https://eips.ethereum.org/EIPS/eip-1155). While it incorporates some concepts from these standards, ERC-7752 is **not backwards compatible** with them due to several fundamental differences: + +### Unique Token Identification and Granularity + +- **Token IDs**: Unlike ERC-20 tokens, which are purely fungible and indistinct, ERC-7752 assigns a unique `tokenId` to each issuance and transfer. This allows for granular tracking of individual tokens, enabling features like fractional ownership, detailed compliance checks, and specific asset representation. +- **Granular Control**: ERC-7752's emphasis on individual securities and specific token units contrasts with the bulk balance approach of ERC-20, where all tokens are interchangeable and tracked by total balance per address. + +### Compliance and Regulatory Features + +- **Integrated Compliance**: ERC-7752 embeds compliance mechanisms at its core, integrating with Identity Registry and Compliance contracts. This ensures that all token holders meet KYC/AML requirements and that all transfers adhere to regulatory constraints. +- **Compliance Checks on Transfer**: Transfers in ERC-7752 involve verifying compliance rules before execution, which is absent in ERC-20 and ERC-721 standards. +- **Role-Based Access Control**: The standard introduces roles like `AgentRole`, granting specific permissions necessary for regulatory compliance. This model differs from the more permissive access in earlier standards. + +### Modified Function Interfaces + +- **Transfer Functions**: The `transferFrom` function in ERC-7752 includes additional parameters like `data` for compliance purposes and may return a new `tokenId`. This deviates from the simpler `transferFrom` in ERC-20 and ERC-721. +- **Minting and Burning**: While ERC-20 lacks standardized `mint` and `burn` functions, and ERC-721's `mint` and `burn` handle single tokens, ERC-7752's `mint` function can handle batch operations and includes compliance data, aligning more closely with the needs of equity issuance and cancellation. + +### Event Definitions + +- **Extended Events**: ERC-7752 defines additional events such as `Mint`, `Burn`, `TokensLocked`, `TokensUnlocked`, `AddressFrozen`, and `RecoverySuccess`. These events provide critical information for compliance and asset management that are not present in ERC-20 or ERC-721. +- **Event Structure**: The structure and triggers of events in ERC-7752 differ, meaning that applications listening for ERC-20 or ERC-721 events would not capture all necessary events from an ERC-7752 token. + +### Batch Operations and Enhanced Functionality + +- **Batch Transactions**: ERC-7752 supports batch operations for transfers, freezing, and forced transfers, which are not standardized in ERC-20 or ERC-721. +- **Recovery Mechanisms**: The inclusion of token recovery functions addresses scenarios like lost wallets, a feature not found in earlier standards. + +### Token Metadata and Multidimensional Balances + +- **Token Metadata**: ERC-7752's approach to token metadata includes `certificateURI` and handles multiple dimensions of information per token, differing from ERC-721's singular focus on NFTs and ERC-20's lack of individual token metadata. +- **Balances**: The standard allows querying balances both by address and by `tokenId`, providing a multidimensional view of ownership not available in ERC-20 or ERC-721. + +### Freezing and Pausing Functionality + +- **Address and Token Freezing**: ERC-7752 enables freezing specific addresses or tokens, preventing transfers as per compliance needs. ERC-20 and ERC-721 do not natively support this granularity of control. +- **Pausing Mechanism**: While ERC-777 introduces pausing, ERC-7752's pausing is integrated with compliance, affecting how and why transfers can be halted. + +### Non-Fungibility Within Fungibility + +- **Hybrid Nature**: ERC-7752 tokens can represent fungible assets but also track individual units with unique `tokenId`s. This hybrid approach is distinct from ERC-20's fungibility and ERC-721's non-fungibility. +- **Fractional Ownership**: The standard supports fractional ownership of assets, which requires tracking fractions of tokens in ways that ERC-721 cannot accommodate. + +### Contract Interactions and Dependencies + +- **External Contracts**: ERC-7752 interacts with Identity Registry and Compliance contracts, introducing dependencies that are not present in ERC-20 or ERC-721 tokens. +- **Modular Compliance**: The standard's reliance on modular compliance contracts for enforcing rules adds complexity and inter-contract communication that differs from earlier standards. -This standard introduces breaking changes to [ERC-3643](./eip-3643.md) by emphasizing each security issuance rather than fungible token balances. Methods like `freezePartialTokens` have been removed in favor of `freezeSecurity`. An additional `securityId` param is required for any method that modifies an account’s equity balance. +### Conclusion -In addition to `mint` and `burn` [ERC-20](./eip-20.md) breaking changes, this standard introduces: +Due to these substantive differences, ERC-7752 is not backwards compatible with ERC-20, ERC-721, or ERC-1155. Applications, wallets, and exchanges designed exclusively for those standards would not be able to interact fully with ERC-7752 tokens without significant modifications. However, ERC-7752 aims to retain familiar patterns where possible to facilitate understanding and integration. -- the `shareClass` and `uri` parameters to the `issue` method -- a `securityId` parameter to `cancel`, `transfer`, and `transferFrom`, methods -- a `securityIds` parameter to corresponding batch methods -- a `shareClass` param to the `approve`, `increaseAllowance`, and `decreaseAllowance` methods -- corresponding event signatures. +Developers and entities looking to adopt ERC-7752 should consider these differences carefully. While the standard introduces complexities required for regulatory compliance and advanced asset management, it also provides powerful new capabilities for representing and handling equity and alternative assets on-chain. Adoption may require updates to existing systems or the development of new tooling but offers the potential for improved efficiency, compliance, and functionality in the tokenization of traditional financial instruments. ## Security Considerations -Needs discussion. +Needs discussion. ## Copyright From 31127138fbf2bd452cc08d1a8309a12536aa0d81 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Fri, 8 Nov 2024 13:36:51 -0800 Subject: [PATCH 18/31] add links --- ERCS/erc-7752.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index 30bb590d08b..d107b80aee7 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -36,11 +36,11 @@ This standard bridges the gap between traditional equity and alternative asset m 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](https://www.rfc-editor.org/rfc/rfc2119) and [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174). -An ERC-7752 compliant smart contract represents ownership shares of equity and alternative assets, enabling the issuance, management, and transfer of these tokens while ensuring regulatory compliance and efficient asset management. +A compliant smart contract represents ownership shares of equity and alternative assets, enabling the issuance, management, and transfer of these tokens while ensuring regulatory compliance and efficient asset management. ### Overview -Every ERC-7752 compliant contract MUST implement the `IERC7752` interface. This standard defines a set of methods and events that enable: +Every compliant contract MUST implement the `IERC7752` interface. This standard defines a set of methods and events that enable: - **Token Initialization** - **Minting and Burning Tokens** @@ -836,47 +836,47 @@ interface IERC7752 { ## Rationale -The rationale behind ERC-7752 is to create a comprehensive and standardized framework for tokenizing equity and alternative assets on the Ethereum blockchain. Traditional financial instruments like stocks, fund interests, and SPV (Special Purpose Vehicle) shares are managed through legacy systems that are often inefficient, opaque, and fragmented. By standardizing these assets as tokens on the blockchain, we can address several key challenges in the current financial ecosystem. +The rationale behind this standard is to create a comprehensive and standardized framework for tokenizing equity and alternative assets on the Ethereum blockchain. Traditional financial instruments like stocks, fund interests, and SPV (Special Purpose Vehicle) shares are managed through legacy systems that are often inefficient, opaque, and fragmented. By standardizing these assets as tokens on the blockchain, we can address several key challenges in the current financial ecosystem. ### Bridging Traditional Finance and Blockchain Technology -One of the main goals of ERC-7752 is to bridge the gap between traditional finance (TradFi) and decentralized finance (DeFi). While blockchain technology has revolutionized many aspects of finance, there remains a significant divide when it comes to regulated securities and equity instruments. ERC-7752 aims to provide a compliant and secure method for representing these assets on-chain, ensuring that regulatory requirements are met while taking advantage of blockchain's benefits. +One of the main goals of this standard is to bridge the gap between traditional finance (TradFi) and decentralized finance (DeFi). While blockchain technology has revolutionized many aspects of finance, there remains a significant divide when it comes to regulated securities and equity instruments. This standard aims to provide a compliant and secure method for representing these assets on-chain, ensuring that regulatory requirements are met while taking advantage of blockchain's benefits. ### Compliance and Regulatory Considerations -Compliance with KYC (Know Your Customer), AML (Anti-Money Laundering), and other regulatory requirements is paramount when dealing with equity and alternative assets. Unlike many existing token standards, ERC-7752 is designed with compliance at its core. By integrating with identity registries and modular compliance contracts, the standard ensures that only verified and authorized participants can hold and transfer tokens. This design caters to the needs of issuers and regulators, fostering trust and legitimacy in the tokenized assets. +Compliance with KYC (Know Your Customer), AML (Anti-Money Laundering), and other regulatory requirements is paramount when dealing with equity and alternative assets. Unlike many existing token standards, this standard is designed with compliance at its core. By integrating with identity registries and modular compliance contracts, the standard ensures that only verified and authorized participants can hold and transfer tokens. This design caters to the needs of issuers and regulators, fostering trust and legitimacy in the tokenized assets. ### Flexibility for Diverse Asset Classes -ERC-7752 is deliberately designed to be flexible, accommodating a wide range of asset classes beyond traditional stock. By supporting the tokenization of fund interests, SPV shares, real estate assets, and more, the standard opens up opportunities for fractional ownership and broader investment participation. This inclusiveness is vital for democratizing access to investment opportunities and for fostering innovation in financial products. +This standard is deliberately designed to be flexible, accommodating a wide range of asset classes beyond traditional stock. This token standard supports the accounting for these assets in a way that is consistent with the way traditional financial instruments are accounted for. ### Enhanced Operational Efficiency -By automating processes such as cap table management, compliance checks, and corporate actions through smart contracts, ERC-7752 reduces administrative overhead and minimizes the risk of human error. This automation leads to cost savings and faster transaction times, benefiting both issuers and investors. +By automating processes such as cap table management, compliance checks, and corporate actions through smart contracts, this standard reduces administrative overhead and minimizes the risk of human error. This automation leads to cost savings and faster transaction times, benefiting both issuers and investors. ### Compatibility with Industry Standards -Adhering to the design principles of the Open Cap Format (OCF) ensures that ERC-7752 tokens are compatible with existing industry-approved data standards for cap tables and asset registers. This compatibility facilitates easier integration with legacy systems and promotes widespread adoption by aligning with familiar practices in the financial industry. +Adhering to the design principles of the Open Cap Format (OCF) ensures that these tokens are compatible with existing industry-approved data standards for cap tables and asset registers. This compatibility facilitates easier integration with legacy systems and promotes widespread adoption by aligning with familiar practices in the financial industry. ### Security and Recovery Mechanisms -Given the high value and regulatory importance of equity and alternative assets, ERC-7752 includes robust security features such as role-based access control, pausing mechanisms, and the ability to freeze addresses or tokens. Additionally, recovery functions are provided to handle situations like lost wallets, ensuring that asset control can be maintained or restored in compliance with legal requirements. +Given the high value and regulatory importance of equity and alternative assets, this standard includes robust security features such as role-based access control, pausing mechanisms, and the ability to freeze addresses or tokens. Additionally, recovery functions are provided to handle situations like lost wallets, ensuring that asset control can be maintained or restored in compliance with legal requirements. ### Avoiding Fragmentation of Standards -While there are existing token standards like ERC-20 for fungible tokens and ERC-721 for non-fungible tokens, they do not adequately address the specific needs of equity and alternative asset tokenization, particularly regarding compliance and regulatory requirements. By introducing ERC-7752, we provide a specialized standard that prevents fragmentation and inconsistency in how these assets are represented on the blockchain. +While there are existing token standards like ERC-20 for fungible tokens and ERC-721 for non-fungible tokens, they do not adequately address the specific needs of equity and alternative asset tokenization, particularly regarding compliance and regulatory requirements. By introducing this standard, we provide a specialized standard that prevents fragmentation and inconsistency in how these assets are represented on the blockchain. ### Enabling Future Innovations -By establishing a solid foundation for tokenized equity and alternative assets, ERC-7752 paves the way for future innovations in the financial ecosystem. Standardization allows developers and financial institutions to build new services and platforms, such as decentralized exchanges for security tokens, automated compliance solutions, and innovative investment products that were not feasible under traditional systems. +By establishing a solid foundation for tokenized equity and alternative assets, this standard paves the way for future innovations in the financial ecosystem. Standardization allows developers and financial institutions to build new services and platforms, such as decentralized exchanges for security tokens, automated compliance solutions, and innovative investment products that were not feasible under traditional systems. ### Conclusion -ERC-7752 is a comprehensive standard that addresses the unique challenges of tokenizing equity and alternative assets. By focusing on compliance, flexibility, and operational efficiency, it fosters greater trust and participation in blockchain-based financial instruments. The standard aims to catalyze the convergence of traditional finance and blockchain technology, ultimately contributing to a more inclusive and efficient global financial system. +This standard is a comprehensive standard that addresses the unique challenges of tokenizing equity and alternative assets. By focusing on compliance, flexibility, and operational efficiency, it fosters greater trust and participation in blockchain-based financial instruments. The standard aims to catalyze the convergence of traditional finance and blockchain technology, ultimately contributing to a more inclusive and efficient global financial system. ## Backwards Compatibility -ERC-7752 introduces a comprehensive framework for tokenizing equity and alternative assets, which significantly extends beyond the capabilities of earlier token standards like [ERC-20](https://eips.ethereum.org/EIPS/eip-20), [ERC-721](https://eips.ethereum.org/EIPS/eip-721), and [ERC-1155](https://eips.ethereum.org/EIPS/eip-1155). While it incorporates some concepts from these standards, ERC-7752 is **not backwards compatible** with them due to several fundamental differences: +[ERC-7752](./eip-7752.md) introduces a comprehensive framework for tokenizing equity and alternative assets, which significantly extends beyond the capabilities of earlier token standards like [ERC-20](./eip-20.md), [ERC-721](./eip-721.md), and [ERC-1155](./eip-1155.md). While it incorporates some concepts from these standards, ERC-7752 is **not backwards compatible** with them due to several fundamental differences: ### Unique Token Identification and Granularity From 6112ad2369fc864c32bb8b200b0fbdef4ff41106 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Fri, 8 Nov 2024 13:42:05 -0800 Subject: [PATCH 19/31] update links --- ERCS/erc-7752.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index d107b80aee7..3c06087d59d 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -30,7 +30,7 @@ This ERC introduces a standard for representing ownership of equity and alternat Implementing tokens representing equity and alternative assets on-chain provides a unified interface for issuers, investors, transfer agents, and financial intermediaries to interact with these assets. By adhering to the same design principles as the Open Cap Format (OCF)—an industry-approved data standard for cap tables and asset registers—this token standard ensures compatibility with existing management systems while leveraging the benefits of blockchain technology. -This standard bridges the gap between traditional equity and alternative asset markets and decentralized finance, fostering a more efficient, transparent, and inclusive financial ecosystem on Ethereum and beyond. By standardizing tokens for equity and alternative assets, we pave the way for innovative financial products and services that can streamline venture capital, private equity, and asset management operations. +[ERC-7752](./eip-7752.md) bridges the gap between traditional equity and alternative asset markets and decentralized finance, fostering a more efficient, transparent, and inclusive financial ecosystem on Ethereum and beyond. By standardizing tokens for equity and alternative assets, we pave the way for innovative financial products and services that can streamline venture capital, private equity, and asset management operations. ## Specification @@ -40,7 +40,7 @@ A compliant smart contract represents ownership shares of equity and alternative ### Overview -Every compliant contract MUST implement the `IERC7752` interface. This standard defines a set of methods and events that enable: +Every compliant contract MUST implement the `IERC7752` interface. ERC-7752 defines a set of methods and events that enable: - **Token Initialization** - **Minting and Burning Tokens** @@ -470,7 +470,7 @@ The contract MUST emit events as specified in the interface to enable off-chain ### Backwards Compatibility -This standard is not backwards compatible with [ERC-20](./eip-20.md) or [ERC-3643](./eip-3643.md) due to: +ERC-7752 is not backwards compatible with [ERC-20](./eip-20.md) or [ERC-3643](./eip-3643.md) due to: - Introduction of unique `tokenId` for each issuance and transfer, differentiating tokens beyond simple fungible balances. - Replacement of `mint` and `burn` methods with `issue` and `cancel` in earlier drafts, although `mint` and `burn` are used in this interface. @@ -479,7 +479,7 @@ This standard is not backwards compatible with [ERC-20](./eip-20.md) or [ERC-364 ### Notes - Vesting and governance mechanisms are considered separate concerns and SHOULD be addressed in companion ERCs. -- Contracts implementing this standard SHOULD ensure that they remain within the maximum contract size limits. +- Contracts implementing ERC-7752 SHOULD ensure that they remain within the maximum contract size limits. - Developers SHOULD consider gas efficiency when implementing batch operations and compliance checks. ```solidity @@ -836,23 +836,23 @@ interface IERC7752 { ## Rationale -The rationale behind this standard is to create a comprehensive and standardized framework for tokenizing equity and alternative assets on the Ethereum blockchain. Traditional financial instruments like stocks, fund interests, and SPV (Special Purpose Vehicle) shares are managed through legacy systems that are often inefficient, opaque, and fragmented. By standardizing these assets as tokens on the blockchain, we can address several key challenges in the current financial ecosystem. +The rationale behind ERC-7752 is to create a comprehensive and standardized framework for tokenizing equity and alternative assets on the Ethereum blockchain. Traditional financial instruments like stocks, fund interests, and SPV (Special Purpose Vehicle) shares are managed through legacy systems that are often inefficient, opaque, and fragmented. By standardizing these assets as tokens on the blockchain, we can address several key challenges in the current financial ecosystem. ### Bridging Traditional Finance and Blockchain Technology -One of the main goals of this standard is to bridge the gap between traditional finance (TradFi) and decentralized finance (DeFi). While blockchain technology has revolutionized many aspects of finance, there remains a significant divide when it comes to regulated securities and equity instruments. This standard aims to provide a compliant and secure method for representing these assets on-chain, ensuring that regulatory requirements are met while taking advantage of blockchain's benefits. +One of the main goals of ERC-7752 is to bridge the gap between traditional finance (TradFi) and decentralized finance (DeFi). While blockchain technology has revolutionized many aspects of finance, there remains a significant divide when it comes to regulated securities and equity instruments. ERC-7752 aims to provide a compliant and secure method for representing these assets on-chain, ensuring that regulatory requirements are met while taking advantage of blockchain's benefits. ### Compliance and Regulatory Considerations -Compliance with KYC (Know Your Customer), AML (Anti-Money Laundering), and other regulatory requirements is paramount when dealing with equity and alternative assets. Unlike many existing token standards, this standard is designed with compliance at its core. By integrating with identity registries and modular compliance contracts, the standard ensures that only verified and authorized participants can hold and transfer tokens. This design caters to the needs of issuers and regulators, fostering trust and legitimacy in the tokenized assets. +Compliance with KYC (Know Your Customer), AML (Anti-Money Laundering), and other regulatory requirements is paramount when dealing with equity and alternative assets. Unlike many existing token standards, ERC-7752 is designed with compliance at its core. By integrating with identity registries and modular compliance contracts, the standard ensures that only verified and authorized participants can hold and transfer tokens. This design caters to the needs of issuers and regulators, fostering trust and legitimacy in the tokenized assets. ### Flexibility for Diverse Asset Classes -This standard is deliberately designed to be flexible, accommodating a wide range of asset classes beyond traditional stock. This token standard supports the accounting for these assets in a way that is consistent with the way traditional financial instruments are accounted for. +ERC-7752 is deliberately designed to be flexible, accommodating a wide range of asset classes beyond traditional stock. This token standard supports the accounting for these assets in a way that is consistent with the way traditional financial instruments are accounted for. ### Enhanced Operational Efficiency -By automating processes such as cap table management, compliance checks, and corporate actions through smart contracts, this standard reduces administrative overhead and minimizes the risk of human error. This automation leads to cost savings and faster transaction times, benefiting both issuers and investors. +By automating processes such as cap table management, compliance checks, and corporate actions through smart contracts, ERC-7752 reduces administrative overhead and minimizes the risk of human error. This automation leads to cost savings and faster transaction times, benefiting both issuers and investors. ### Compatibility with Industry Standards @@ -860,23 +860,23 @@ Adhering to the design principles of the Open Cap Format (OCF) ensures that thes ### Security and Recovery Mechanisms -Given the high value and regulatory importance of equity and alternative assets, this standard includes robust security features such as role-based access control, pausing mechanisms, and the ability to freeze addresses or tokens. Additionally, recovery functions are provided to handle situations like lost wallets, ensuring that asset control can be maintained or restored in compliance with legal requirements. +Given the high value and regulatory importance of equity and alternative assets, ERC-7752 includes robust security features such as role-based access control, pausing mechanisms, and the ability to freeze addresses or tokens. Additionally, recovery functions are provided to handle situations like lost wallets, ensuring that asset control can be maintained or restored in compliance with legal requirements. ### Avoiding Fragmentation of Standards -While there are existing token standards like ERC-20 for fungible tokens and ERC-721 for non-fungible tokens, they do not adequately address the specific needs of equity and alternative asset tokenization, particularly regarding compliance and regulatory requirements. By introducing this standard, we provide a specialized standard that prevents fragmentation and inconsistency in how these assets are represented on the blockchain. +While there are existing token standards like ERC-20 for fungible tokens and ERC-721 for non-fungible tokens, they do not adequately address the specific needs of equity and alternative asset tokenization, particularly regarding compliance and regulatory requirements. By introducing ERC-7752, we provide a specialized standard that prevents fragmentation and inconsistency in how these assets are represented on the blockchain. ### Enabling Future Innovations -By establishing a solid foundation for tokenized equity and alternative assets, this standard paves the way for future innovations in the financial ecosystem. Standardization allows developers and financial institutions to build new services and platforms, such as decentralized exchanges for security tokens, automated compliance solutions, and innovative investment products that were not feasible under traditional systems. +By establishing a solid foundation for tokenized equity and alternative assets, ERC-7752 paves the way for future innovations in the financial ecosystem. Standardization allows developers and financial institutions to build new services and platforms, such as decentralized exchanges for security tokens, automated compliance solutions, and innovative investment products that were not feasible under traditional systems. ### Conclusion -This standard is a comprehensive standard that addresses the unique challenges of tokenizing equity and alternative assets. By focusing on compliance, flexibility, and operational efficiency, it fosters greater trust and participation in blockchain-based financial instruments. The standard aims to catalyze the convergence of traditional finance and blockchain technology, ultimately contributing to a more inclusive and efficient global financial system. +ERC-7752 is a comprehensive standard that addresses the unique challenges of tokenizing equity and alternative assets. By focusing on compliance, flexibility, and operational efficiency, it fosters greater trust and participation in blockchain-based financial instruments. The standard aims to catalyze the convergence of traditional finance and blockchain technology, ultimately contributing to a more inclusive and efficient global financial system. ## Backwards Compatibility -[ERC-7752](./eip-7752.md) introduces a comprehensive framework for tokenizing equity and alternative assets, which significantly extends beyond the capabilities of earlier token standards like [ERC-20](./eip-20.md), [ERC-721](./eip-721.md), and [ERC-1155](./eip-1155.md). While it incorporates some concepts from these standards, ERC-7752 is **not backwards compatible** with them due to several fundamental differences: +ERC-7752 introduces a comprehensive framework for tokenizing equity and alternative assets, which significantly extends beyond the capabilities of earlier token standards like [ERC-20](./eip-20.md), [ERC-721](./eip-721.md), and [ERC-1155](./eip-1155.md). While it incorporates some concepts from these standards, ERC-7752 is **not backwards compatible** with them due to several fundamental differences: ### Unique Token Identification and Granularity @@ -912,7 +912,7 @@ This standard is a comprehensive standard that addresses the unique challenges o ### Freezing and Pausing Functionality - **Address and Token Freezing**: ERC-7752 enables freezing specific addresses or tokens, preventing transfers as per compliance needs. ERC-20 and ERC-721 do not natively support this granularity of control. -- **Pausing Mechanism**: While ERC-777 introduces pausing, ERC-7752's pausing is integrated with compliance, affecting how and why transfers can be halted. +- **Pausing Mechanism**: While [ERC-777](./erc-777.md) introduces pausing, ERC-7752's pausing is integrated with compliance, affecting how and why transfers can be halted. ### Non-Fungibility Within Fungibility From 649e5dd37fa767b90375bda22267792aa4518ae4 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Fri, 8 Nov 2024 13:44:12 -0800 Subject: [PATCH 20/31] fix markdown-link-first --- ERCS/erc-7752.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index 3c06087d59d..f0b41569909 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -864,7 +864,7 @@ Given the high value and regulatory importance of equity and alternative assets, ### Avoiding Fragmentation of Standards -While there are existing token standards like ERC-20 for fungible tokens and ERC-721 for non-fungible tokens, they do not adequately address the specific needs of equity and alternative asset tokenization, particularly regarding compliance and regulatory requirements. By introducing ERC-7752, we provide a specialized standard that prevents fragmentation and inconsistency in how these assets are represented on the blockchain. +While there are existing token standards like ERC-20 for fungible tokens and ERC-721 for non-fungible tokens, they do not adequately address the specific needs of equity and alternative asset tokenization, particularly regarding compliance and regulatory requirements. By introducing [ERC-7752](./erc-7752.md), we provide a specialized standard that prevents fragmentation and inconsistency in how these assets are represented on the blockchain. ### Enabling Future Innovations From 0cd1e8954838238550c5f4478885c4ac5eaadcf3 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Fri, 8 Nov 2024 13:48:30 -0800 Subject: [PATCH 21/31] fix markdown-first-link --- ERCS/erc-7752.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index f0b41569909..6a058412351 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -864,7 +864,7 @@ Given the high value and regulatory importance of equity and alternative assets, ### Avoiding Fragmentation of Standards -While there are existing token standards like ERC-20 for fungible tokens and ERC-721 for non-fungible tokens, they do not adequately address the specific needs of equity and alternative asset tokenization, particularly regarding compliance and regulatory requirements. By introducing [ERC-7752](./erc-7752.md), we provide a specialized standard that prevents fragmentation and inconsistency in how these assets are represented on the blockchain. +While there are existing token standards like ERC-20 for fungible tokens and [ERC-721](./erc-721.md) for non-fungible tokens, they do not adequately address the specific needs of equity and alternative asset tokenization, particularly regarding compliance and regulatory requirements. By introducing ERC-7752, we provide a specialized standard that prevents fragmentation and inconsistency in how these assets are represented on the blockchain. ### Enabling Future Innovations @@ -876,7 +876,7 @@ ERC-7752 is a comprehensive standard that addresses the unique challenges of tok ## Backwards Compatibility -ERC-7752 introduces a comprehensive framework for tokenizing equity and alternative assets, which significantly extends beyond the capabilities of earlier token standards like [ERC-20](./eip-20.md), [ERC-721](./eip-721.md), and [ERC-1155](./eip-1155.md). While it incorporates some concepts from these standards, ERC-7752 is **not backwards compatible** with them due to several fundamental differences: +ERC-7752 introduces a comprehensive framework for tokenizing equity and alternative assets, which significantly extends beyond the capabilities of earlier token standards like ERC-20, ERC-721, and [ERC-1155](./eip-1155.md). While it incorporates some concepts from these standards, ERC-7752 is **not backwards compatible** with them due to several fundamental differences: ### Unique Token Identification and Granularity From 69a9d8dcb2601e8ec32e6ac2ac2a2cf201b5fb82 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Sat, 14 Dec 2024 19:06:26 -0800 Subject: [PATCH 22/31] Update erc-7752.md --- ERCS/erc-7752.md | 60 +++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index 6a058412351..d25d36a318a 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -1,7 +1,7 @@ --- eip: 7752 -title: Private Equity Token -description: Token representing private equity asset such as shares or interests +title: Restricted Security Token +description: Restricted security token (private equity, debt, and derivative securuties) author: Matt Rosendin (@mrosendin) discussions-to: https://ethereum-magicians.org/t/erc-7752-equity-token/20735 status: Draft @@ -13,34 +13,32 @@ requires: 173 ## Abstract -An ERC token standard representing private securities. This new interface standardizes equity management for issuers, investors, transfer agents, and financial intermediaries. +An ERC token standard representing restricted securities. This new interface standardizes equity and alternative asset management for issuers, investors, transfer agents, and financial intermediaries, including private equity, debt, and derivatives. ## Motivation -Equity tokens represent ownership shares of a company or asset, encapsulated in a digital token format on the blockchain. While traditional equity mainly refers to stock in a company, there's a growing need to represent ownership in alternative assets like fund interests, real estate, or Special Purpose Vehicle (SPV) interests. Traditional methods of managing and transferring equity and alternative assets are often cumbersome, opaque, and involve significant administrative overhead. By bringing these assets on-chain, we can leverage blockchain technology to enhance transparency, efficiency, and accessibility in equity and alternative asset markets. +Restricted security tokens represent alternative assets encapsulated in a digital token format on the blockchain. While public assets are capable of being represented with existing token standards, there's a growing need to represent ownership in private assets like pre-IPO shares and private investment fund interests. Traditional methods of managing and transferring private assets are often cumbersome, opaque, and involve significant administrative overhead. By bringing these assets on-chain, we can leverage blockchain technology to enhance transparency, efficiency, and accessibility in private markets. -This ERC introduces a standard for representing ownership of equity and alternative assets on-chain, enabling companies and asset managers to issue, manage, and transfer tokens seamlessly. Key use cases for these tokens include: +This ERC introduces a standard for representing ownership of restricted securities, enabling companies and asset managers to issue, manage, and transfer restricted security tokens seamlessly. Key use cases for these tokens include: - **Simplified Asset Management**: Automate cap table and asset register updates with each token transfer, reducing the need for manual reconciliation and minimizing errors. -- **Enhanced Liquidity**: Facilitate secondary markets for private equity and alternative assets, allowing shareholders and investors to trade their tokens under compliant conditions. -- **Fractional Ownership**: Enable investors to hold fractional interests in assets like funds and SPVs, lowering the barrier to entry and allowing for more diversified investment portfolios. +- **Enhanced Liquidity**: Facilitate private secondary markets for restricted securities, allowing shareholders and investors to trade their tokens under compliant conditions. +- **Fractional Ownership**: Enable investors to hold onchain fractional interests in assets like private companies and funds in a standardized way. - **Diverse Asset Representation**: Tokenize a wide range of assets beyond traditional stock, including fund interests, SPV shares, real estate, and more. - **Automated Compliance**: Enforce regulatory requirements and transfer restrictions programmatically through smart contracts. - **Streamlined Corporate Actions**: Simplify processes like dividend distribution, profit sharing, voting, and investor communications by utilizing token holder data on the blockchain. -Implementing tokens representing equity and alternative assets on-chain provides a unified interface for issuers, investors, transfer agents, and financial intermediaries to interact with these assets. By adhering to the same design principles as the Open Cap Format (OCF)—an industry-approved data standard for cap tables and asset registers—this token standard ensures compatibility with existing management systems while leveraging the benefits of blockchain technology. - -[ERC-7752](./eip-7752.md) bridges the gap between traditional equity and alternative asset markets and decentralized finance, fostering a more efficient, transparent, and inclusive financial ecosystem on Ethereum and beyond. By standardizing tokens for equity and alternative assets, we pave the way for innovative financial products and services that can streamline venture capital, private equity, and asset management operations. +Implementing restricted security tokens provides a unified interface for issuers, investors, transfer agents, and financial intermediaries, ensuring compatibility with existing management systems while leveraging blockchain technology's benefits. This standard bridges the gap between traditional and decentralized finance, enabling efficient, transparent, and compliant financial ecosystems on Ethereum and beyond. ## 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](https://www.rfc-editor.org/rfc/rfc2119) and [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174). -A compliant smart contract represents ownership shares of equity and alternative assets, enabling the issuance, management, and transfer of these tokens while ensuring regulatory compliance and efficient asset management. +An ERC-7752 compliant smart contract represents ownership of restricted securities, enabling compliant issuance, management, and transfer while supporting advanced features for diverse restricted asset types. ### Overview -Every compliant contract MUST implement the `IERC7752` interface. ERC-7752 defines a set of methods and events that enable: +Every ERC-7752 compliant contract MUST implement the `IERC7752` interface. This standard defines a set of methods and events that enable: - **Token Initialization** - **Minting and Burning Tokens** @@ -470,7 +468,7 @@ The contract MUST emit events as specified in the interface to enable off-chain ### Backwards Compatibility -ERC-7752 is not backwards compatible with [ERC-20](./eip-20.md) or [ERC-3643](./eip-3643.md) due to: +This standard is not backwards compatible with [ERC-20](./eip-20.md) or [ERC-3643](./eip-3643.md) due to: - Introduction of unique `tokenId` for each issuance and transfer, differentiating tokens beyond simple fungible balances. - Replacement of `mint` and `burn` methods with `issue` and `cancel` in earlier drafts, although `mint` and `burn` are used in this interface. @@ -479,7 +477,7 @@ ERC-7752 is not backwards compatible with [ERC-20](./eip-20.md) or [ERC-3643](./ ### Notes - Vesting and governance mechanisms are considered separate concerns and SHOULD be addressed in companion ERCs. -- Contracts implementing ERC-7752 SHOULD ensure that they remain within the maximum contract size limits. +- Contracts implementing this standard SHOULD ensure that they remain within the maximum contract size limits. - Developers SHOULD consider gas efficiency when implementing batch operations and compliance checks. ```solidity @@ -836,51 +834,51 @@ interface IERC7752 { ## Rationale -The rationale behind ERC-7752 is to create a comprehensive and standardized framework for tokenizing equity and alternative assets on the Ethereum blockchain. Traditional financial instruments like stocks, fund interests, and SPV (Special Purpose Vehicle) shares are managed through legacy systems that are often inefficient, opaque, and fragmented. By standardizing these assets as tokens on the blockchain, we can address several key challenges in the current financial ecosystem. +The rationale behind ERC-7752 is to create a comprehensive and standardized framework for tokenizing restricted securities on the Ethereum blockchain. Traditional financial instruments like stocks, bonds, fund interests, and derivatives are often managed through inefficient, opaque, and fragmented legacy systems. By standardizing these assets as tokens, ERC-7752 addresses key challenges in private and restricted securities markets. ### Bridging Traditional Finance and Blockchain Technology -One of the main goals of ERC-7752 is to bridge the gap between traditional finance (TradFi) and decentralized finance (DeFi). While blockchain technology has revolutionized many aspects of finance, there remains a significant divide when it comes to regulated securities and equity instruments. ERC-7752 aims to provide a compliant and secure method for representing these assets on-chain, ensuring that regulatory requirements are met while taking advantage of blockchain's benefits. +ERC-7752 bridges the gap between traditional finance and decentralized finance by introducing a compliant and secure method for representing restricted securities on-chain. This ensures regulatory requirements are met while leveraging blockchain’s efficiency, transparency, and accessibility. ### Compliance and Regulatory Considerations -Compliance with KYC (Know Your Customer), AML (Anti-Money Laundering), and other regulatory requirements is paramount when dealing with equity and alternative assets. Unlike many existing token standards, ERC-7752 is designed with compliance at its core. By integrating with identity registries and modular compliance contracts, the standard ensures that only verified and authorized participants can hold and transfer tokens. This design caters to the needs of issuers and regulators, fostering trust and legitimacy in the tokenized assets. +Compliance with KYC, AML, and other regulations is integral to the design of ERC-7752. The standard integrates identity verification and compliance modules, ensuring only authorized participants can interact with tokens. This fosters trust and legitimacy in tokenized restricted securities. -### Flexibility for Diverse Asset Classes +### Flexibility for Asset Classes -ERC-7752 is deliberately designed to be flexible, accommodating a wide range of asset classes beyond traditional stock. This token standard supports the accounting for these assets in a way that is consistent with the way traditional financial instruments are accounted for. +ERC-7752 is designed to accommodate various asset classes, from equity and debt to derivatives and real estate. This flexibility enables issuers to tokenize and manage a wide range of assets, opening new opportunities for fractional ownership and broader participation. -### Enhanced Operational Efficiency +### Operational Efficiency -By automating processes such as cap table management, compliance checks, and corporate actions through smart contracts, ERC-7752 reduces administrative overhead and minimizes the risk of human error. This automation leads to cost savings and faster transaction times, benefiting both issuers and investors. +By automating cap table management, compliance checks, and corporate actions through smart contracts, ERC-7752 reduces administrative overhead and minimizes errors. This streamlining leads to cost savings and faster transactions. ### Compatibility with Industry Standards -Adhering to the design principles of the Open Cap Format (OCF) ensures that these tokens are compatible with existing industry-approved data standards for cap tables and asset registers. This compatibility facilitates easier integration with legacy systems and promotes widespread adoption by aligning with familiar practices in the financial industry. +ERC-7752 aligns with existing standards like the Open Cap Format (OCF), ensuring compatibility with established systems. This promotes adoption by minimizing disruptions to familiar workflows. -### Security and Recovery Mechanisms +### Enhanced Security -Given the high value and regulatory importance of equity and alternative assets, ERC-7752 includes robust security features such as role-based access control, pausing mechanisms, and the ability to freeze addresses or tokens. Additionally, recovery functions are provided to handle situations like lost wallets, ensuring that asset control can be maintained or restored in compliance with legal requirements. +Security features like freezing addresses and tokens, pausing operations, and recovery mechanisms protect high-value and regulated assets. These safeguards ensure regulatory compliance and asset integrity. ### Avoiding Fragmentation of Standards -While there are existing token standards like ERC-20 for fungible tokens and [ERC-721](./erc-721.md) for non-fungible tokens, they do not adequately address the specific needs of equity and alternative asset tokenization, particularly regarding compliance and regulatory requirements. By introducing ERC-7752, we provide a specialized standard that prevents fragmentation and inconsistency in how these assets are represented on the blockchain. +While there are existing token standards like ERC-20 for fungible tokens and [ERC-721](./eip-721.md) for non-fungible tokens, they do not adequately address the specific needs of equity and alternative asset tokenization, particularly regarding compliance and regulatory requirements. By introducing ERC-7752, which is an NFT extension of ERC-3643, we provide a specialized standard that prevents fragmentation and inconsistency in how these assets are represented on the blockchain while meeting compliance requirements. -### Enabling Future Innovations +### Future Innovations -By establishing a solid foundation for tokenized equity and alternative assets, ERC-7752 paves the way for future innovations in the financial ecosystem. Standardization allows developers and financial institutions to build new services and platforms, such as decentralized exchanges for security tokens, automated compliance solutions, and innovative investment products that were not feasible under traditional systems. +By standardizing restricted securities, ERC-7752 lays the foundation for new financial products and services, such as decentralized secondary markets, automated compliance solutions, and innovative investment platforms. ### Conclusion -ERC-7752 is a comprehensive standard that addresses the unique challenges of tokenizing equity and alternative assets. By focusing on compliance, flexibility, and operational efficiency, it fosters greater trust and participation in blockchain-based financial instruments. The standard aims to catalyze the convergence of traditional finance and blockchain technology, ultimately contributing to a more inclusive and efficient global financial system. +ERC-7752 balances compliance, flexibility, and efficiency to unlock the full potential of tokenized restricted securities. By fostering trust, transparency, and innovation, it drives the convergence of traditional and decentralized finance, creating a more inclusive and efficient financial ecosystem. ## Backwards Compatibility -ERC-7752 introduces a comprehensive framework for tokenizing equity and alternative assets, which significantly extends beyond the capabilities of earlier token standards like ERC-20, ERC-721, and [ERC-1155](./eip-1155.md). While it incorporates some concepts from these standards, ERC-7752 is **not backwards compatible** with them due to several fundamental differences: +ERC-7752 introduces a comprehensive framework for tokenizing restricted securities, which significantly extends beyond the capabilities of earlier token standards like [ERC-20](https://eips.ethereum.org/EIPS/eip-20), [ERC-721](https://eips.ethereum.org/EIPS/eip-721), [ERC-1155](https://eips.ethereum.org/EIPS/eip-1155), and [ERC-3643](https://eips.ethereum.org/EIPS/eip-3643). While it incorporates some concepts from these standards, ERC-7752 is **not backwards compatible** with them due to several fundamental differences: ### Unique Token Identification and Granularity -- **Token IDs**: Unlike ERC-20 tokens, which are purely fungible and indistinct, ERC-7752 assigns a unique `tokenId` to each issuance and transfer. This allows for granular tracking of individual tokens, enabling features like fractional ownership, detailed compliance checks, and specific asset representation. +- **Token IDs**: Unlike ERC-20 tokens, which are purely fungible and indistinct, ERC-7752 assigns a unique `tokenId` to each issuance. Unlike ERC-1155, there can only ever be one owner of any given `tokenId`. This allows for granular tracking of individual tokens, enabling features like fractional ownership, detailed compliance checks, and transparent & accessible audit trails. - **Granular Control**: ERC-7752's emphasis on individual securities and specific token units contrasts with the bulk balance approach of ERC-20, where all tokens are interchangeable and tracked by total balance per address. ### Compliance and Regulatory Features @@ -912,7 +910,7 @@ ERC-7752 introduces a comprehensive framework for tokenizing equity and alternat ### Freezing and Pausing Functionality - **Address and Token Freezing**: ERC-7752 enables freezing specific addresses or tokens, preventing transfers as per compliance needs. ERC-20 and ERC-721 do not natively support this granularity of control. -- **Pausing Mechanism**: While [ERC-777](./erc-777.md) introduces pausing, ERC-7752's pausing is integrated with compliance, affecting how and why transfers can be halted. +- **Pausing Mechanism**: While [ERC-777](./eip-777.md) introduces pausing, ERC-7752's pausing is integrated with compliance, affecting how and why transfers can be halted. ### Non-Fungibility Within Fungibility From 832b472c8d897da50a82d3da2b9f6c845c8d2b18 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Sat, 14 Dec 2024 19:19:06 -0800 Subject: [PATCH 23/31] eip walidator fixes --- ERCS/erc-7752.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index d25d36a318a..afc01d67fa5 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -34,7 +34,7 @@ Implementing restricted security tokens provides a unified interface for issuers 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](https://www.rfc-editor.org/rfc/rfc2119) and [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174). -An ERC-7752 compliant smart contract represents ownership of restricted securities, enabling compliant issuance, management, and transfer while supporting advanced features for diverse restricted asset types. +An [ERC-7752](./eip-7752.md) compliant smart contract represents ownership of restricted securities, enabling compliant issuance, management, and transfer while supporting advanced features for diverse restricted asset types. ### Overview @@ -468,11 +468,11 @@ The contract MUST emit events as specified in the interface to enable off-chain ### Backwards Compatibility -This standard is not backwards compatible with [ERC-20](./eip-20.md) or [ERC-3643](./eip-3643.md) due to: +This standard is not backwards compatible with [ERC-20](./eip-20.md) and [ERC-3643](./eip-3643.md) due to: -- Introduction of unique `tokenId` for each issuance and transfer, differentiating tokens beyond simple fungible balances. +- Introduction of unique `tokenId` for each issuance or transfer, differentiating tokens beyond simple fungible balances. - Replacement of `mint` and `burn` methods with `issue` and `cancel` in earlier drafts, although `mint` and `burn` are used in this interface. -- Emphasis on individual securities rather than fungible token balances. +- Emphasis on individual holdings of securities rather than fungible token balances. ### Notes @@ -874,7 +874,7 @@ ERC-7752 balances compliance, flexibility, and efficiency to unlock the full pot ## Backwards Compatibility -ERC-7752 introduces a comprehensive framework for tokenizing restricted securities, which significantly extends beyond the capabilities of earlier token standards like [ERC-20](https://eips.ethereum.org/EIPS/eip-20), [ERC-721](https://eips.ethereum.org/EIPS/eip-721), [ERC-1155](https://eips.ethereum.org/EIPS/eip-1155), and [ERC-3643](https://eips.ethereum.org/EIPS/eip-3643). While it incorporates some concepts from these standards, ERC-7752 is **not backwards compatible** with them due to several fundamental differences: +ERC-7752 introduces a comprehensive framework for tokenizing restricted securities, which significantly extends beyond the capabilities of earlier token standards like ERC-20, ERC-721, ERC-3643, and [ERC-1155](./eip-1155.md). While it incorporates some concepts from these standards, ERC-7752 is **not backwards compatible** with them due to several fundamental differences: ### Unique Token Identification and Granularity From d2062588ab456928b1f98f28889c48709a408290 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Sun, 8 Jun 2025 22:21:32 -0700 Subject: [PATCH 24/31] Update erc-7752.md --- ERCS/erc-7752.md | 1058 ++++++++-------------------------------------- 1 file changed, 182 insertions(+), 876 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index afc01d67fa5..1b84a8e9b51 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -1,7 +1,7 @@ --- eip: 7752 -title: Restricted Security Token -description: Restricted security token (private equity, debt, and derivative securuties) +title: Lot-based Issued-Asset Token +description: A token standard that treats every acquisition (lot) as its own on-chain record, enabling per-lot cost-basis, lineage, and regulatory controls. author: Matt Rosendin (@mrosendin) discussions-to: https://ethereum-magicians.org/t/erc-7752-equity-token/20735 status: Draft @@ -13,924 +13,230 @@ requires: 173 ## Abstract -An ERC token standard representing restricted securities. This new interface standardizes equity and alternative asset management for issuers, investors, transfer agents, and financial intermediaries, including private equity, debt, and derivatives. +ERC-7752 defines a **lot-based** token model. Each issuance or acquisition event mints a unique **lot** (`lotId`) that carries its own quantity, cost basis, acquisition date, and lineage. The core interface also standardizes pause, freeze, forced-transfer, and other **administrative hooks** mandated for real-world securities and off-chain assets. ## Motivation -Restricted security tokens represent alternative assets encapsulated in a digital token format on the blockchain. While public assets are capable of being represented with existing token standards, there's a growing need to represent ownership in private assets like pre-IPO shares and private investment fund interests. Traditional methods of managing and transferring private assets are often cumbersome, opaque, and involve significant administrative overhead. By bringing these assets on-chain, we can leverage blockchain technology to enhance transparency, efficiency, and accessibility in private markets. +Traditional token standards excel at _fungibility_ (ERC-20) or _single, indivisible items_ (ERC-721). +Real-world assets—equity certificates, debt notes, real-estate fractions—sit between those extremes: -This ERC introduces a standard for representing ownership of restricted securities, enabling companies and asset managers to issue, manage, and transfer restricted security tokens seamlessly. Key use cases for these tokens include: +- One issuer may create **thousands of discrete lots** over time. +- Each lot needs cost-basis, vesting, or lock-up data. +- Regulators demand pause, freeze, and forced-transfer powers. -- **Simplified Asset Management**: Automate cap table and asset register updates with each token transfer, reducing the need for manual reconciliation and minimizing errors. -- **Enhanced Liquidity**: Facilitate private secondary markets for restricted securities, allowing shareholders and investors to trade their tokens under compliant conditions. -- **Fractional Ownership**: Enable investors to hold onchain fractional interests in assets like private companies and funds in a standardized way. -- **Diverse Asset Representation**: Tokenize a wide range of assets beyond traditional stock, including fund interests, SPV shares, real estate, and more. -- **Automated Compliance**: Enforce regulatory requirements and transfer restrictions programmatically through smart contracts. -- **Streamlined Corporate Actions**: Simplify processes like dividend distribution, profit sharing, voting, and investor communications by utilizing token holder data on the blockchain. - -Implementing restricted security tokens provides a unified interface for issuers, investors, transfer agents, and financial intermediaries, ensuring compatibility with existing management systems while leveraging blockchain technology's benefits. This standard bridges the gap between traditional and decentralized finance, enabling efficient, transparent, and compliant financial ecosystems on Ethereum and beyond. +ERC-7752 bakes those requirements into a single, deterministic ABI so wallets, custodians, tax engines, and regulators can interoperate without bespoke dialects. ## 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](https://www.rfc-editor.org/rfc/rfc2119) and [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174). - -An [ERC-7752](./eip-7752.md) compliant smart contract represents ownership of restricted securities, enabling compliant issuance, management, and transfer while supporting advanced features for diverse restricted asset types. - -### Overview - -Every ERC-7752 compliant contract MUST implement the `IERC7752` interface. This standard defines a set of methods and events that enable: - -- **Token Initialization** -- **Minting and Burning Tokens** -- **Pause and Unpause Operations** -- **Freezing Addresses and Tokens** -- **Token Transfers** -- **Approvals and Allowances** -- **Batch Operations** -- **Token Information Retrieval** -- **Compliance and Identity Management** -- **Recovery Mechanisms** - -### Token Initialization - -The contract MUST implement an `init` function to initialize the token with necessary parameters: - -```solidity -function init( - address identityRegistryAddress, - address complianceAddress, - string memory tokenName, - string memory tokenPrefix, - string memory tokenURI, - address tokenIdentity -) external; -``` - -- **Parameters**: - - - `identityRegistryAddress`: Address of the Identity Registry contract. - - `complianceAddress`: Address of the Compliance contract. - - `tokenName`: Name of the token. - - `tokenPrefix`: Prefix of the token symbol. - - `tokenURI`: Base URI for the tokens. - - `tokenIdentity`: On-chain identity address of the token issuer. - -- **Requirements**: - - MUST be called before any other function. - - MUST emit the `UpdatedTokenInformation` and `IdentityRegistryAdded` events. - -### Minting and Burning Tokens - -#### Minting Tokens - -The contract MUST implement a `mint` function to issue new tokens: - -```solidity -function mint( - address to, - uint256 amount, - string memory uri, - bytes memory data -) external returns (uint256 tokenId); -``` - -- **Parameters**: - - - `to`: Address of the recipient. - - `amount`: Amount of tokens to mint. - - `uri`: URI pointing to the token’s metadata. - - `data`: Additional data for compliance and record-keeping. - -- **Requirements**: - - MUST check compliance rules before minting. - - MUST only be callable by an address with the `AgentRole`. - - MUST emit the `Mint` event upon successful minting. - -#### Burning Tokens - -The contract MUST implement a `burn` function to cancel existing tokens: - -```solidity -function burn( - address from, - uint256 id, - uint256 amount -) external; -``` - -- **Parameters**: - - - `from`: Address holding the tokens. - - `id`: Token ID to burn. - - `amount`: Amount of tokens to burn. - -- **Requirements**: - - MUST verify that the `from` address has sufficient balance. - - MUST only be callable by an address with the `AgentRole`. - - MUST emit the `Burn` event upon successful burning. - -### Pause and Unpause Operations - -The contract MUST support pausing and unpausing of token transfers: - -```solidity -function pause() external; -function unpause() external; -``` - -- **Requirements**: - - MUST only be callable by an address with the `AgentRole`. - - When paused, all token transfers MUST be blocked. - - MUST emit the `Paused` or `Unpaused` events accordingly. - -### Freezing Addresses and Tokens - -#### Freezing Addresses - -The contract MUST allow freezing or unfreezing of specific addresses: - -```solidity -function freezeAddress(address account) external; -function unfreezeAddress(address account) external; -``` - -- **Parameters**: - - - `account`: Address to be frozen or unfrozen. - -- **Requirements**: - - MUST only be callable by an address with the `AgentRole`. - - When an address is frozen, it MUST NOT be able to send or receive tokens. - - MUST emit the `AddressFrozen` event upon changes. - -#### Freezing Tokens - -The contract MUST allow freezing or unfreezing of specific tokens: - -```solidity -function freezeToken(uint256 _id) external; -function unfreezeToken(uint256 _id) external; -``` - -- **Parameters**: - - - `_id`: Token ID to be frozen or unfrozen. - -- **Requirements**: - - MUST only be callable by an address with the `AgentRole`. - - When a token is frozen, it MUST NOT be transferable. - - MUST emit the `TokenFrozen` or `TokenUnfrozen` events accordingly. - -### Token Transfers - -#### Standard Transfers - -The contract MUST implement a `transferFrom` function for transferring tokens: - -```solidity -function transferFrom( - address from, - address to, - uint256 tokenId, - uint256 amount, - bytes memory data -) external returns (uint256 newTokenId); -``` - -- **Parameters**: - - - `from`: Address sending the tokens. - - `to`: Address receiving the tokens. - - `tokenId`: ID of the token being transferred. - - `amount`: Amount of tokens to transfer. - - `data`: Additional data for compliance checks. - -- **Requirements**: - - MUST check if transfers are not paused. - - MUST verify that neither the `from` nor the `to` addresses are frozen. - - MUST enforce compliance rules via the Compliance contract. - - MUST emit the `Transfer` and `TransferValue` events upon successful transfer. - -#### Forced Transfers - -The contract SHOULD implement a `forcedTransfer` function to allow transfers without holder consent: - -```solidity -function forcedTransfer( - address _from, - address _to, - uint256 _id, - uint256 _amount -) external returns (uint256 newTokenId); -``` - -- **Requirements**: - - MUST only be callable by an address with the `AgentRole`. - - SHOULD be used in cases of legal enforcement or compliance violations. - - MUST emit the `Transfer` and `TransferValue` events. - -### Approvals and Allowances - -The contract MUST implement approval mechanisms for delegated transfers: - -```solidity -function approve(address to, uint256 tokenId) external; -function setApprovalForAll(address operator, bool approved) external; -function getApproved(uint256 tokenId) external view returns (address); -function isApprovedForAll(address owner, address operator) external view returns (bool); -``` - -- **Requirements**: - - MUST allow token holders to approve other addresses to manage their tokens. - - MUST emit `Approval` and `ApprovalForAll` events as appropriate. - -### Batch Operations - -To improve efficiency, the contract SHOULD implement batch operations: - -#### Batch Safe Transfer - -```solidity -function batchSafeTransfer( - uint256[] calldata _tokenIds, - address[] calldata _toList, - uint256[] calldata _amounts, - bytes[] calldata _dataList -) external; -``` - -- **Requirements**: - - MUST perform multiple transfers in a single transaction. - - MUST enforce the same checks as individual transfers. - -#### Batch Freezing Addresses - -```solidity -function batchSetAddressFrozen(address[] calldata _userAddresses, bool[] calldata _freeze) external; -``` - -- **Requirements**: - - MUST allow freezing or unfreezing multiple addresses. - - MUST only be callable by an address with the `AgentRole`. - -#### Batch Freezing Tokens - -```solidity -function batchFreezeTokens(uint256[] calldata _ids) external; -function batchUnfreezeTokens(uint256[] calldata _ids) external; -``` - -- **Requirements**: - - MUST allow freezing or unfreezing multiple tokens. - - MUST only be callable by an address with the `AgentRole`. - -#### Batch Forced Transfers - -```solidity -function batchForcedTransfer( - address[] calldata _fromList, - address[] calldata _toList, - uint256[] calldata _ids, - uint256[] calldata _amounts -) external; -``` - -- **Requirements**: - - MUST perform multiple forced transfers. - - MUST only be callable by an address with the `AgentRole`. - -### Token Information Retrieval - -The contract MUST provide functions to retrieve token and account information: - -#### Token Metadata - -```solidity -function uri(uint256 tokenId) external view returns (string memory); -``` - -- **Requirements**: - - MUST return the URI pointing to the token's metadata. - -#### Balances and Ownership +Key words **MUST**, **SHOULD**, etc. have the meanings of RFC 2119 / 8174. -```solidity -function balanceOf(address account) external view returns (uint256); -function balanceOf(uint256 tokenId) external view returns (uint256); -function ownerOf(uint256 tokenId) external view returns (address); -``` - -- **Requirements**: - - `balanceOf(address)` MUST return the total balance of an account across all tokens. - - `balanceOf(uint256)` MUST return the balance associated with a specific token ID. - - `ownerOf` MUST return the owner address of a specific token ID. - -#### Token Status - -```solidity -function isFrozen(address account) external view returns (bool); -function isTokenFrozen(uint256 tokenId) external view returns (bool); -function paused() external view returns (bool); -``` - -- **Requirements**: - - MUST accurately reflect the frozen status of addresses and tokens. - - MUST indicate whether the contract is paused. - -#### Token Details - -```solidity -function getSecurity(uint256 tokenId) external view returns ( - address owner, - uint256 balance, - string memory uri, - bytes memory data, - Types.Status status, - uint256 newTokenId, - uint256 residualTokenId -); -``` - -- **Requirements**: - - MUST return detailed information about a specific token/security issuance. - -#### Contract Metadata - -```solidity -function name() external view returns (string memory); -function prefix() external view returns (string memory); -function onchainID() external view returns (address); -function version() external pure returns (string memory); -``` - -- **Requirements**: - - MUST provide accurate metadata about the token contract. - -### Compliance and Identity Management - -The contract MUST interact with Compliance and Identity Registry contracts: - -#### Identity Registry - -```solidity -function identityRegistry() external view returns (IIdentityRegistry); -function setIdentityRegistry(address identityRegistryAddress) external; -``` - -- **Requirements**: - - MUST enforce identity verification for token holders. - - `setIdentityRegistry` MUST only be callable by the contract owner. - - MUST emit the `IdentityRegistryAdded` event when updated. - -#### Compliance Contract +### Core Interface ```solidity -function compliance() external view returns (IModularCompliance); -function setCompliance(address complianceAddress) external; -``` - -- **Requirements**: - - MUST enforce regulatory compliance for token transfers. - - `setCompliance` MUST only be callable by the contract owner. - - MUST emit the `ComplianceAdded` event when updated. - -### Recovery Mechanisms - -The contract SHOULD provide mechanisms to recover tokens if a wallet is lost: - -```solidity -function recoveryAddress( - address _lostWallet, - address _newWallet, - address _investorOnchainID, - uint256[] calldata _ids -) external returns (bool); -``` - -- **Requirements**: - - MUST only be callable by an address with the `AgentRole`. - - MUST verify the identity of the investor through the Identity Registry. - - MUST emit the `RecoverySuccess` event upon successful recovery. - -### Locking and Unlocking Tokens - -The contract MUST allow token holders to lock and unlock their tokens: - -```solidity -function lockTokens(uint256 tokenId, uint256 amount) external; -function unlockTokens(uint256 tokenId, uint256 amount) external; -``` +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; -- **Requirements**: - - MUST prevent locked tokens from being transferred. - - MUST emit `TokensLocked` and `TokensUnlocked` events accordingly. - -### Events - -The contract MUST emit events as specified in the interface to enable off-chain monitoring and compliance: - -- `TokensLocked` -- `TokensUnlocked` -- `Approval` -- `ApprovalForAll` -- `Transfer` -- `TransferValue` -- `TokenInvalidated` -- `URI` -- `AgentAddedForToken` -- `AgentRemovedForToken` -- `UpdatedTokenInformation` -- `IdentityRegistryAdded` -- `ComplianceAdded` -- `Paused` -- `Unpaused` -- `Mint` -- `Burn` -- `AddressFrozen` -- `TokenFrozen` -- `TokenUnfrozen` -- `RecoverySuccess` - -### Access Control - -- The contract MUST implement role-based access control to restrict certain functions to authorized addresses (e.g., `AgentRole`). -- Ownership and agent roles SHOULD be managed securely to prevent unauthorized access. - -### Compliance Requirements - -- The contract MUST integrate with compliance modules to enforce KYC/AML regulations. -- Transfers MUST be checked against compliance rules before execution. - -### Error Handling - -- The contract SHOULD use standard OpenZeppelin error messages and revert reasons for consistency. -- MUST revert transactions that violate compliance rules, are paused, or involve frozen addresses/tokens. - -### Backwards Compatibility - -This standard is not backwards compatible with [ERC-20](./eip-20.md) and [ERC-3643](./eip-3643.md) due to: +/** + * @title ERC-7752 Issued-Asset Core + */ +interface IIssuedAsset { + /*────────────────────────────────── + Data structures + ──────────────────────────────────*/ + enum TransferType { INTERNAL, SALE, GIFT, INHERITANCE, REWARD } + + struct Lot { + bytes32 parentLotId; + uint256 quantity; + address paymentCurrency; + uint256 costBasis; + uint256 acquisitionDate; + uint256 lastUpdate; + bool isValid; + address owner; + TransferType tType; + string uri; + bytes data; + } + + /*────────────────────────────────── + Events (deterministic for audit) + ──────────────────────────────────*/ + + event LotCreated( + address indexed owner, + bytes32 indexed lotId, + bytes32 indexed parentLotId, + uint256 quantity, + address paymentCurrency, + uint256 costBasis, + uint256 acquisitionDate, + uint256 lastUpdate, + string uri, + bytes data, + TransferType tType + ); -- Introduction of unique `tokenId` for each issuance or transfer, differentiating tokens beyond simple fungible balances. -- Replacement of `mint` and `burn` methods with `issue` and `cancel` in earlier drafts, although `mint` and `burn` are used in this interface. -- Emphasis on individual holdings of securities rather than fungible token balances. + event LotTransferred( + bytes32 indexed lotId, + address indexed from, + address indexed to, + uint256 quantity, + string uri, + bytes data, + TransferType tType, + uint256 newCostBasis + ); -### Notes + event LotAdjusted( + bytes32 indexed oldLotId, + bytes32 indexed newLotId, + address operator, + uint256 newQuantity, + uint256 newCostBasis, + address paymentCurrency, + uint256 newAcquisitionDate, + string newUri, + bytes newData, + string reason, + TransferType tType + ); -- Vesting and governance mechanisms are considered separate concerns and SHOULD be addressed in companion ERCs. -- Contracts implementing this standard SHOULD ensure that they remain within the maximum contract size limits. -- Developers SHOULD consider gas efficiency when implementing batch operations and compliance checks. + event LotInvalidated(bytes32 indexed lotId); -```solidity -library Types { - enum Status { - Outstanding, - Burn, - Transferred, - Converted, - Repurchased, - PartiallyExercised, - FullyExercised, - Forfeited, - Expired - } -} + event PausedSet(bool indexed paused); + event AccountFrozen(address indexed account, bool frozen); + event LotFrozen(bytes32 indexed lotId, bool frozen); -interface IERC7752 { - event TokensLocked(address indexed caller, uint256 indexed tokenId, uint256 indexed amount); - event TokensUnlocked(address indexed caller, uint256 indexed tokenId, uint256 indexed amount); - event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); - event ApprovalForAll(address indexed owner, address indexed operator, bool indexed approved); - event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); - event TransferValue(uint256 indexed fromTokenId, uint256 indexed toTokenId, uint256 indexed amount); - event TokenInvalidated(uint256 indexed tokenId); - event URI(string uri, uint256 indexed tokenId); - event AgentAddedForToken(uint256 indexed tokenId, address indexed agent); - event AgentRemovedForToken(uint256 indexed tokenId, address indexed agent); - - /// @dev Emitted when the token information is updated. - event UpdatedTokenInformation( - string name, - string prefix, - string version, - address indexed onchainID + event ForcedTransfer( + bytes32 indexed lotId, + address indexed from, + address indexed to, + uint256 quantity, + string reason ); - /** - * this event is emitted when the IdentityRegistry has been set for the token - * the event is emitted by the token constructor and by the setIdentityRegistry function - * `_identityRegistry` is the address of the Identity Registry of the token - */ - event IdentityRegistryAdded(address indexed _identityRegistry); - - /** - * this event is emitted when the Compliance has been set for the token - * the event is emitted by the token constructor and by the setCompliance function - * `_compliance` is the address of the Compliance contract of the token - */ - event ComplianceAdded(address indexed _compliance); - - /// @dev Emitted when the contract is paused. - event Paused(address indexed account); - - /// @dev Emitted when the contract is unpaused. - event Unpaused(address indexed account); - - /// @dev Emitted when a security is issued. - event Mint(address indexed to, uint256 indexed id, uint256 amount); - - /// @dev Emitted when a security is canceled (burned). - event Burn(address indexed from, uint256 indexed id, uint256 amount); - - /// @dev Emitted when an address is frozen or unfrozen. - event AddressFrozen(address indexed account, bool isFrozen); - - /// @dev Emitted when tokens are frozen. - event TokenFrozen(uint256 indexed tokenId); - - /// @dev Emitted when tokens are unfrozen. - event TokenUnfrozen(uint256 indexed tokenId); - - /// @dev Emitted when a recovery is successful. - event RecoverySuccess(address indexed lostWallet, address indexed newWallet, address indexed investorOnchainID); - - /** - * @dev Initializes the contract. - * @param identityRegistryAddress Address of the Identity Registry contract. - * @param complianceAddress Address of the Compliance contract. - * @param tokenName Name of the token. - * @param tokenPrefix Prefix of the token. - * @param tokenURI The base URI for the tokens. - * @param tokenIdentity On-chain identity address of the token. - */ - function init( - address identityRegistryAddress, - address complianceAddress, - string memory tokenName, - string memory tokenPrefix, - string memory tokenURI, - address tokenIdentity - ) external; - - function totalSupply() external returns (uint256); - - /** - * @dev Pauses all token transfers. - * - * Requirements: - * - The caller must have the `AgentRole`. - */ - function pause() external; - - /** - * @dev Unpauses all token transfers. - * - * Requirements: - * - The caller must have the `AgentRole`. - */ - function unpause() external; - - /** - * @dev Sets the Identity Registry contract address. - * @param identityRegistryAddress Address of the new Identity Registry. - * - * Requirements: - * - The caller must be the owner. - */ - function setIdentityRegistry(address identityRegistryAddress) external; - - /** - * @dev Sets the Compliance contract address. - * @param complianceAddress Address of the new Compliance contract. - * - * Requirements: - * - The caller must be the owner. - */ - function setCompliance(address complianceAddress) external; - - /** - * @dev Sets the name of the token. - * @param _name New name of the token. - * - * Requirements: - * - The caller must be the owner. - */ - function setName(string calldata _name) external; - - /** - * @dev Sets the prefix of the token. - * @param _prefix New prefix of the token. - * - * Requirements: - * - The caller must be the owner. - */ - function setPrefix(string calldata _prefix) external; - - /** - * @dev Sets the on-chain identity of the token. - * @param _onchainID New on-chain identity address. - * - * Requirements: - * - The caller must be the owner. - */ - function setOnchainID(address _onchainID) external; - - /** - * @dev Mints new tokens (certificates). - * @param to Address of the recipient. - * @param amount Amount of tokens to mint. - * @param uri URI of the certificate metadata. - * @param data Additional data for compliance. - * - * Requirements: - * - The caller must have the `AgentRole`. - */ - function mint( + event TransferControllerAdded(address indexed controller); + + /*────────────────────────────────── + Read functions + ──────────────────────────────────*/ + function name() external view returns (string memory); + function symbol() external view returns (string memory); + function getLot(bytes32 id) external view returns (Lot memory); + + /*────────────────────────────────── + Lot CRUD + ──────────────────────────────────*/ + function createLot( + address owner, + uint256 quantity, + address paymentCurrency, + uint256 costBasis, + uint256 acquisitionDate, + string calldata uri, + bytes calldata data, + uint256 customId /* 0 = auto */ + ) external returns (bytes32 lotId, uint256 assignedCustomId); + + function transfer( + bytes32 lotId, address to, - uint256 amount, - string memory uri, - bytes memory data - ) external returns (uint256 tokenId); - - /** - * @dev Burns tokens (certificates). - * @param from Address from which to burn tokens. - * @param id Token ID to burn. - * @param amount Amount of tokens to burn. - * - * Requirements: - * - The caller must have the `AgentRole`. - */ - function burn( + uint256 quantity, + TransferType tType, + uint256 newCostBasis, + string calldata uri, + bytes calldata data + ) external returns (bytes32 newLotId); + + function transferFrom( + bytes32 lotId, address from, - uint256 id, - uint256 amount - ) external; - - /** - * @dev Freezes an address, restricting token transfers. - * @param account Address to freeze. - * - * Requirements: - * - The caller must have the `AgentRole`. - */ - function freezeAddress(address account) external; - - /** - * @dev Unfreezes an address, allowing token transfers. - * @param account Address to unfreeze. - * - * Requirements: - * - The caller must have the `AgentRole`. - */ - function unfreezeAddress(address account) external; - - function batchSafeTransfer( - uint256[] calldata _tokenIds, - address[] calldata _toList, - uint256[] calldata _amounts, - bytes[] calldata _dataList - ) external; - - function batchSetAddressFrozen(address[] calldata _userAddresses, bool[] calldata _freeze) external; - - function batchFreezeTokens(uint256[] calldata _ids) external; - function batchForcedTransfer( - address[] calldata _fromList, - address[] calldata _toList, - uint256[] calldata _ids, - uint256[] calldata _amounts - ) external; - function batchUnfreezeTokens(uint256[] calldata _ids) external; - function recoveryAddress( - address _lostWallet, - address _newWallet, - address _investorOnchainID, - uint256[] calldata _ids - ) external returns (bool); + address to, + uint256 quantity, + TransferType tType, + uint256 newCostBasis, + string calldata uri, + bytes calldata data + ) external returns (bytes32 newLotId); + + function adjustLot( + bytes32 oldLotId, + uint256 newQuantity, + uint256 newCostBasis, + address newPaymentCurrency, + uint256 newAcquisitionDate, + string calldata newUri, + bytes calldata newData, + string calldata reason, + uint256 customId + ) external returns (bytes32 newLotId); + + /*────────────────────────────────── + Administrative hooks (normative) + ──────────────────────────────────*/ + function pause() external; + function unpause() external; + function freezeAccount(address account, bool frozen) external; + function freezeLot(bytes32 lotId, bool frozen) external; function forcedTransfer( - address _from, - address _to, - uint256 _id, - uint256 _amount - ) external returns (uint256 newTokenId); - - function setAddressFrozen(address _userAddress, bool _freeze) external; - -/** - * @dev Freezes a specific token ID. - * @param _id Token ID to freeze. - * - * Requirements: - * - The caller must have the `AgentRole`. - */ - function freezeToken(uint256 _id) external; - - /** - * @dev Unfreezes a specific token ID. - * @param _id Token ID to unfreeze. - * - * Requirements: - * - The caller must have the `AgentRole`. - */ - function unfreezeToken(uint256 _id) external; - - function approve(address to, uint256 tokenId) external; - - /// @dev Approve or remove an operator for the caller - function setApprovalForAll(address operator, bool approved) external; - - /// @dev Transfers token from one address to another using approval mechanism - function transferFrom( + bytes32 lotId, address from, address to, - uint256 tokenId, - uint256 amount, - bytes memory data - ) external returns (uint256 newTokenId); - - /// @dev Function to lock tokens owned by the caller - function lockTokens(uint256 tokenId, uint256 amount) external; - - /// @dev Function to unlock tokens owned by the caller - function unlockTokens(uint256 tokenId, uint256 amount) external; - - /** - * @dev Returns the URI for a specific token ID. - * @param tokenId Token ID to query. - */ - function uri(uint256 tokenId) external view returns (string memory); - - /** - * @dev Returns true if the contract is paused. - */ - function paused() external view returns (bool); - - /** - * @dev Returns true if the given address is frozen. - * @param account Address to query. - */ - function isFrozen(address account) external view returns (bool); - - /** - * @dev Returns the Identity Registry address. - */ - function identityRegistry() external view returns (IIdentityRegistry); - - /** - * @dev Returns the Compliance contract address. - */ - function compliance() external view returns (IModularCompliance); - - /** - * @dev Returns the name of the token. - */ - function name() external view returns (string memory); - - /** - * @dev Returns the prefix of the token. - */ - function prefix() external view returns (string memory); - - /** - * @dev Returns the on-chain identity of the token. - */ - function onchainID() external view returns (address); - - function isTokenFrozen(uint256 tokenId) external view returns (bool); - - // Function to get token balance - function balanceOf(uint256 tokenId) external view returns (uint256); - - // Function to get the account balance - function balanceOf(address account) external view returns (uint256); - - // Function to get token owner - function ownerOf(uint256 tokenId) external view returns (address); - - /// @dev Returns the approved address for a token ID, or zero if no address set - function getApproved(uint256 tokenId) external view returns (address); - - /// @dev Returns if the operator is allowed to manage all of the assets of owner - function isApprovedForAll(address owner, address operator) external view returns (bool); - - function getSecurity(uint256 tokenId) external view returns ( - address owner, - uint256 balance, - string memory uri, - bytes memory data, - Types.Status status, - uint256 newTokenId, - uint256 residualTokenId - ); - - /** - * @dev Returns the version of the token. - */ - function version() external pure returns (string memory); + uint256 quantity, + string calldata reason + ) external returns (bytes32 newLotId); + + /* Transfer-policy plug-in */ + function setTransferController(address controller) external; + function transferController() external view returns (address); + + /* Optional merge / split helpers */ + function mergeLots(bytes32[] calldata sourceLots, string calldata uri, bytes calldata data) + external returns (bytes32 newLotId); + function splitLot(bytes32 lotId, uint256[] calldata quantities, string[] calldata uris) + external returns (bytes32[] memory newLotIds); } ``` -## Rationale - -The rationale behind ERC-7752 is to create a comprehensive and standardized framework for tokenizing restricted securities on the Ethereum blockchain. Traditional financial instruments like stocks, bonds, fund interests, and derivatives are often managed through inefficient, opaque, and fragmented legacy systems. By standardizing these assets as tokens, ERC-7752 addresses key challenges in private and restricted securities markets. - -### Bridging Traditional Finance and Blockchain Technology - -ERC-7752 bridges the gap between traditional finance and decentralized finance by introducing a compliant and secure method for representing restricted securities on-chain. This ensures regulatory requirements are met while leveraging blockchain’s efficiency, transparency, and accessibility. - -### Compliance and Regulatory Considerations - -Compliance with KYC, AML, and other regulations is integral to the design of ERC-7752. The standard integrates identity verification and compliance modules, ensuring only authorized participants can interact with tokens. This fosters trust and legitimacy in tokenized restricted securities. - -### Flexibility for Asset Classes - -ERC-7752 is designed to accommodate various asset classes, from equity and debt to derivatives and real estate. This flexibility enables issuers to tokenize and manage a wide range of assets, opening new opportunities for fractional ownership and broader participation. - -### Operational Efficiency - -By automating cap table management, compliance checks, and corporate actions through smart contracts, ERC-7752 reduces administrative overhead and minimizes errors. This streamlining leads to cost savings and faster transactions. +> Any function marked “external” **MUST** revert if the contract is paused, the caller/lot is frozen, or a transferController vetoes the action. -### Compatibility with Industry Standards +### Tax Appendix (normative) -ERC-7752 aligns with existing standards like the Open Cap Format (OCF), ensuring compatibility with established systems. This promotes adoption by minimizing disruptions to familiar workflows. +| TransferType | U.S. Code reference | Basis rule | Holding-period rule | +| ------------- | ---------------------- | -------------------------------------- | ------------------------------- | +| `INTERNAL` | — | Unchanged | Unchanged | +| `SALE` | IRC § 1012 | Purchase price | Starts day after acquisition | +| `GIFT` | IRC § 1015 | **Carry-over** basis from donor | Tacks to donor’s holding period | +| `INHERITANCE` | IRC § 1014 | **Stepped-up** to FMV at date of death | Resets to date of death | +| `REWARD` | IRC § 61 (ord. income) | FMV at receipt (also income amount) | Starts on receipt date | -### Enhanced Security +> _Parallel principles appear in OECD Model Convention Art. 13 and U.K. TCGA 1992._ -Security features like freezing addresses and tokens, pausing operations, and recovery mechanisms protect high-value and regulated assets. These safeguards ensure regulatory compliance and asset integrity. - -### Avoiding Fragmentation of Standards - -While there are existing token standards like ERC-20 for fungible tokens and [ERC-721](./eip-721.md) for non-fungible tokens, they do not adequately address the specific needs of equity and alternative asset tokenization, particularly regarding compliance and regulatory requirements. By introducing ERC-7752, which is an NFT extension of ERC-3643, we provide a specialized standard that prevents fragmentation and inconsistency in how these assets are represented on the blockchain while meeting compliance requirements. +## Rationale -### Future Innovations +**Administrative hooks (pause, freeze, forcedTransfer, adjustLot)** are embedded in ERC-7752 Core because issued assets **cannot satisfy real-world statutory and fiduciary duties without them.** -By standardizing restricted securities, ERC-7752 lays the foundation for new financial products and services, such as decentralized secondary markets, automated compliance solutions, and innovative investment platforms. +- Securities law obliges issuers and registrars to rectify errors, comply with court orders, and enforce sanctions. +- Transfer-control modules (ERC-7752-Policy) rely on a deterministic ABI to implement jurisdiction-specific rules. +- Standardizing these events enables third-party auditors, tax engines, and regulators to ingest compliance actions uniformly. -### Conclusion - -ERC-7752 balances compliance, flexibility, and efficiency to unlock the full potential of tokenized restricted securities. By fostering trust, transparency, and innovation, it drives the convergence of traditional and decentralized finance, creating a more inclusive and efficient financial ecosystem. +While purely decentralized assets may deem these functions unnecessary, they may be voluntarily disabled by renouncing the `ADMIN_ROLE` or setting `transferController = address(0)`. Omission from the ABI, however, would break composability for the majority of regulated implementations. ## Backwards Compatibility -ERC-7752 introduces a comprehensive framework for tokenizing restricted securities, which significantly extends beyond the capabilities of earlier token standards like ERC-20, ERC-721, ERC-3643, and [ERC-1155](./eip-1155.md). While it incorporates some concepts from these standards, ERC-7752 is **not backwards compatible** with them due to several fundamental differences: - -### Unique Token Identification and Granularity - -- **Token IDs**: Unlike ERC-20 tokens, which are purely fungible and indistinct, ERC-7752 assigns a unique `tokenId` to each issuance. Unlike ERC-1155, there can only ever be one owner of any given `tokenId`. This allows for granular tracking of individual tokens, enabling features like fractional ownership, detailed compliance checks, and transparent & accessible audit trails. -- **Granular Control**: ERC-7752's emphasis on individual securities and specific token units contrasts with the bulk balance approach of ERC-20, where all tokens are interchangeable and tracked by total balance per address. - -### Compliance and Regulatory Features - -- **Integrated Compliance**: ERC-7752 embeds compliance mechanisms at its core, integrating with Identity Registry and Compliance contracts. This ensures that all token holders meet KYC/AML requirements and that all transfers adhere to regulatory constraints. -- **Compliance Checks on Transfer**: Transfers in ERC-7752 involve verifying compliance rules before execution, which is absent in ERC-20 and ERC-721 standards. -- **Role-Based Access Control**: The standard introduces roles like `AgentRole`, granting specific permissions necessary for regulatory compliance. This model differs from the more permissive access in earlier standards. - -### Modified Function Interfaces - -- **Transfer Functions**: The `transferFrom` function in ERC-7752 includes additional parameters like `data` for compliance purposes and may return a new `tokenId`. This deviates from the simpler `transferFrom` in ERC-20 and ERC-721. -- **Minting and Burning**: While ERC-20 lacks standardized `mint` and `burn` functions, and ERC-721's `mint` and `burn` handle single tokens, ERC-7752's `mint` function can handle batch operations and includes compliance data, aligning more closely with the needs of equity issuance and cancellation. - -### Event Definitions - -- **Extended Events**: ERC-7752 defines additional events such as `Mint`, `Burn`, `TokensLocked`, `TokensUnlocked`, `AddressFrozen`, and `RecoverySuccess`. These events provide critical information for compliance and asset management that are not present in ERC-20 or ERC-721. -- **Event Structure**: The structure and triggers of events in ERC-7752 differ, meaning that applications listening for ERC-20 or ERC-721 events would not capture all necessary events from an ERC-7752 token. +ERC-7752 is not backward-compatible with ERC-20/721/1155 because each lotId represents a semi-fungible slice with its own lineage and admin controls. -### Batch Operations and Enhanced Functionality - -- **Batch Transactions**: ERC-7752 supports batch operations for transfers, freezing, and forced transfers, which are not standardized in ERC-20 or ERC-721. -- **Recovery Mechanisms**: The inclusion of token recovery functions addresses scenarios like lost wallets, a feature not found in earlier standards. - -### Token Metadata and Multidimensional Balances - -- **Token Metadata**: ERC-7752's approach to token metadata includes `certificateURI` and handles multiple dimensions of information per token, differing from ERC-721's singular focus on NFTs and ERC-20's lack of individual token metadata. -- **Balances**: The standard allows querying balances both by address and by `tokenId`, providing a multidimensional view of ownership not available in ERC-20 or ERC-721. - -### Freezing and Pausing Functionality - -- **Address and Token Freezing**: ERC-7752 enables freezing specific addresses or tokens, preventing transfers as per compliance needs. ERC-20 and ERC-721 do not natively support this granularity of control. -- **Pausing Mechanism**: While [ERC-777](./eip-777.md) introduces pausing, ERC-7752's pausing is integrated with compliance, affecting how and why transfers can be halted. - -### Non-Fungibility Within Fungibility - -- **Hybrid Nature**: ERC-7752 tokens can represent fungible assets but also track individual units with unique `tokenId`s. This hybrid approach is distinct from ERC-20's fungibility and ERC-721's non-fungibility. -- **Fractional Ownership**: The standard supports fractional ownership of assets, which requires tracking fractions of tokens in ways that ERC-721 cannot accommodate. - -### Contract Interactions and Dependencies +## Security Considerations -- **External Contracts**: ERC-7752 interacts with Identity Registry and Compliance contracts, introducing dependencies that are not present in ERC-20 or ERC-721 tokens. -- **Modular Compliance**: The standard's reliance on modular compliance contracts for enforcing rules adds complexity and inter-contract communication that differs from earlier standards. +- Implementers MUST secure admin roles (e.g., OpenZeppelin `AccessControl` or `AccessManager`). +- A compromised `transferController` could veto or force-transfer lots; multi-sig ownership is **RECOMMENDED**. ### Conclusion -Due to these substantive differences, ERC-7752 is not backwards compatible with ERC-20, ERC-721, or ERC-1155. Applications, wallets, and exchanges designed exclusively for those standards would not be able to interact fully with ERC-7752 tokens without significant modifications. However, ERC-7752 aims to retain familiar patterns where possible to facilitate understanding and integration. - -Developers and entities looking to adopt ERC-7752 should consider these differences carefully. While the standard introduces complexities required for regulatory compliance and advanced asset management, it also provides powerful new capabilities for representing and handling equity and alternative assets on-chain. Adoption may require updates to existing systems or the development of new tooling but offers the potential for improved efficiency, compliance, and functionality in the tokenization of traditional financial instruments. - -## Security Considerations - -Needs discussion. +ERC‑7752 brings granular accounting, compliance, and administrative certainty to on‑chain representations of traditional assets—unlocking the next wave of securitised and regulated token markets. ## Copyright From 1b350455a346a510e8db988fc8d466a9d227a4a3 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Sun, 8 Jun 2025 22:27:04 -0700 Subject: [PATCH 25/31] fix walidator issues --- ERCS/erc-7752.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index 1b84a8e9b51..30974ec0f80 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -1,7 +1,7 @@ --- eip: 7752 title: Lot-based Issued-Asset Token -description: A token standard that treats every acquisition (lot) as its own on-chain record, enabling per-lot cost-basis, lineage, and regulatory controls. +description: A token treating each acquisition (lot) as an on-chain record with cost-basis, lineage, and regulatory controls. author: Matt Rosendin (@mrosendin) discussions-to: https://ethereum-magicians.org/t/erc-7752-equity-token/20735 status: Draft @@ -13,11 +13,11 @@ requires: 173 ## Abstract -ERC-7752 defines a **lot-based** token model. Each issuance or acquisition event mints a unique **lot** (`lotId`) that carries its own quantity, cost basis, acquisition date, and lineage. The core interface also standardizes pause, freeze, forced-transfer, and other **administrative hooks** mandated for real-world securities and off-chain assets. +[ERC-7752](./eip-7752.md) defines a **lot-based** token model. Each issuance or acquisition event mints a unique **lot** (`lotId`) that carries its own quantity, cost basis, acquisition date, and lineage. The core interface also standardizes pause, freeze, forced-transfer, and other **administrative hooks** mandated for real-world securities and off-chain assets. ## Motivation -Traditional token standards excel at _fungibility_ (ERC-20) or _single, indivisible items_ (ERC-721). +Traditional token standards excel at _fungibility_ ([ERC-20](./eip-20.md)) or _single, indivisible items_ ([ERC-721](./eip-721.md)). Real-world assets—equity certificates, debt notes, real-estate fractions—sit between those extremes: - One issuer may create **thousands of discrete lots** over time. @@ -201,7 +201,7 @@ interface IIssuedAsset { } ``` -> Any function marked “external” **MUST** revert if the contract is paused, the caller/lot is frozen, or a transferController vetoes the action. +> Any function marked "external" **MUST** revert if the contract is paused, the caller/lot is frozen, or a transferController vetoes the action. ### Tax Appendix (normative) @@ -209,7 +209,7 @@ interface IIssuedAsset { | ------------- | ---------------------- | -------------------------------------- | ------------------------------- | | `INTERNAL` | — | Unchanged | Unchanged | | `SALE` | IRC § 1012 | Purchase price | Starts day after acquisition | -| `GIFT` | IRC § 1015 | **Carry-over** basis from donor | Tacks to donor’s holding period | +| `GIFT` | IRC § 1015 | **Carry-over** basis from donor | Tacks to donor's holding period | | `INHERITANCE` | IRC § 1014 | **Stepped-up** to FMV at date of death | Resets to date of death | | `REWARD` | IRC § 61 (ord. income) | FMV at receipt (also income amount) | Starts on receipt date | @@ -227,7 +227,7 @@ While purely decentralized assets may deem these functions unnecessary, they may ## Backwards Compatibility -ERC-7752 is not backward-compatible with ERC-20/721/1155 because each lotId represents a semi-fungible slice with its own lineage and admin controls. +ERC-7752 is not backward-compatible with ERC-20/721/[ERC-1155](./eip-1155.md) because each lotId represents a semi-fungible slice with its own lineage and admin controls. ## Security Considerations From 608ef19b3986641ae82e26b7ce5eec9e26dbd96f Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Sun, 8 Jun 2025 22:31:14 -0700 Subject: [PATCH 26/31] Set table for international use --- ERCS/erc-7752.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index 30974ec0f80..6c8e2f59cb3 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -203,17 +203,23 @@ interface IIssuedAsset { > Any function marked "external" **MUST** revert if the contract is paused, the caller/lot is frozen, or a transferController vetoes the action. -### Tax Appendix (normative) +### Tax Considerations (informative) -| TransferType | U.S. Code reference | Basis rule | Holding-period rule | -| ------------- | ---------------------- | -------------------------------------- | ------------------------------- | -| `INTERNAL` | — | Unchanged | Unchanged | -| `SALE` | IRC § 1012 | Purchase price | Starts day after acquisition | -| `GIFT` | IRC § 1015 | **Carry-over** basis from donor | Tacks to donor's holding period | -| `INHERITANCE` | IRC § 1014 | **Stepped-up** to FMV at date of death | Resets to date of death | -| `REWARD` | IRC § 61 (ord. income) | FMV at receipt (also income amount) | Starts on receipt date | +The `TransferType` enum enables implementations to capture transfer context for jurisdiction-specific tax compliance: -> _Parallel principles appear in OECD Model Convention Art. 13 and U.K. TCGA 1992._ +| TransferType | Purpose | Tax implications vary by jurisdiction | +| ------------- | ---------------------------------------- | ------------------------------------- | +| `INTERNAL` | Administrative transfers, splits, merges | Typically no taxable event | +| `SALE` | Market transactions for consideration | Usually triggers capital gains | +| `GIFT` | Gratuitous transfers | Donor/recipient tax treatment varies | +| `INHERITANCE` | Estate/probate transfers | Step-up rules vary by jurisdiction | +| `REWARD` | Compensation, airdrops, staking rewards | Often treated as ordinary income | + +**Example U.S. Treatment**: `GIFT` uses carry-over basis (IRC § 1015), `INHERITANCE` gets stepped-up basis (IRC § 1014). +**Example UK Treatment**: Different rules under TCGA 1992. +**Example Singapore**: No capital gains tax. + +Implementations **SHOULD** consult local tax authorities and **MAY** implement jurisdiction-specific tax calculation modules. ## Rationale From 20507ae68899aa06100653f0f3dfb3e81774738d Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Sun, 8 Jun 2025 22:41:10 -0700 Subject: [PATCH 27/31] Update title --- ERCS/erc-7752.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index 6c8e2f59cb3..88bcf11b8b3 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -1,6 +1,6 @@ --- eip: 7752 -title: Lot-based Issued-Asset Token +title: Lot Token description: A token treating each acquisition (lot) as an on-chain record with cost-basis, lineage, and regulatory controls. author: Matt Rosendin (@mrosendin) discussions-to: https://ethereum-magicians.org/t/erc-7752-equity-token/20735 From c2190144674bae1b6e0bce73c5a7a3ee99b3633f Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Tue, 10 Jun 2025 11:10:23 -0700 Subject: [PATCH 28/31] Update erc-7752.md --- ERCS/erc-7752.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index 88bcf11b8b3..5ed48f62586 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -37,9 +37,9 @@ Key words **MUST**, **SHOULD**, etc. have the meanings of RFC 2119 / 8174. pragma solidity ^0.8.20; /** - * @title ERC-7752 Issued-Asset Core + * @title ERC-7752 Lot Token (Core) */ -interface IIssuedAsset { +interface LotToken { /*────────────────────────────────── Data structures ──────────────────────────────────*/ From beaabad3507268ea6f141a526c53444290a90936 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Thu, 17 Jul 2025 21:50:44 -0700 Subject: [PATCH 29/31] Update erc-7752.md --- ERCS/erc-7752.md | 400 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 271 insertions(+), 129 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index 5ed48f62586..f18708f1c88 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -1,19 +1,18 @@ --- eip: 7752 title: Lot Token -description: A token treating each acquisition (lot) as an on-chain record with cost-basis, lineage, and regulatory controls. +description: A token treating each acquisition (lot) as an on-chain record with cost-basis and lineage. author: Matt Rosendin (@mrosendin) discussions-to: https://ethereum-magicians.org/t/erc-7752-equity-token/20735 status: Draft type: Standards Track category: ERC created: 2024-08-06 -requires: 173 --- ## Abstract -[ERC-7752](./eip-7752.md) defines a **lot-based** token model. Each issuance or acquisition event mints a unique **lot** (`lotId`) that carries its own quantity, cost basis, acquisition date, and lineage. The core interface also standardizes pause, freeze, forced-transfer, and other **administrative hooks** mandated for real-world securities and off-chain assets. +[ERC-7752](./eip-7752.md) defines a **lot-based** token model where each issuance or acquisition event creates a unique **lot** (`lotId`) with its own quantity, basis, acquisition date, and lineage. This standard provides core lot management functions while enabling modular extensions for transfer controls, compliance, and administrative features. ## Motivation @@ -28,221 +27,364 @@ ERC-7752 bakes those requirements into a single, deterministic ABI so wallets, c ## Specification -Key words **MUST**, **SHOULD**, etc. have the meanings of RFC 2119 / 8174. +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. ### Core Interface ```solidity -// SPDX-License-Identifier: BUSL-1.1 +// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; + /** - * @title ERC-7752 Lot Token (Core) + * @title ERC-7752 Lot Token (Core Interface) + * @notice Core lot management */ -interface LotToken { +interface IERC7752 { /*────────────────────────────────── - Data structures + Data Structures ──────────────────────────────────*/ - enum TransferType { INTERNAL, SALE, GIFT, INHERITANCE, REWARD } + + enum TransferType { + INTERNAL, // Administrative transfers, splits, merges + SALE, // Market transactions for consideration + GIFT, // Gratuitous transfers + INHERITANCE, // Estate/probate transfers + REWARD // Compensation, airdrops, staking rewards + } struct Lot { - bytes32 parentLotId; - uint256 quantity; - address paymentCurrency; - uint256 costBasis; - uint256 acquisitionDate; - uint256 lastUpdate; - bool isValid; - address owner; - TransferType tType; - string uri; - bytes data; + bytes32 parentLotId; // Parent lot for lineage tracking + uint256 quantity; // Amount in this lot + address currency; // Currency used for acquisition + uint256 basis; // Total cost basis (in currency) + uint256 acquisitionDate; // Unix timestamp of acquisition + uint256 lastUpdate; // Unix timestamp of last modification + address owner; // Current owner address + TransferType transferType; // How this lot was acquired + string uri; // Metadata URI + bytes data; // Additional lot-specific data } /*────────────────────────────────── - Events (deterministic for audit) + Events ──────────────────────────────────*/ + /** + * @notice Emitted when a new lot is created + */ event LotCreated( address indexed owner, bytes32 indexed lotId, bytes32 indexed parentLotId, uint256 quantity, - address paymentCurrency, - uint256 costBasis, + address currency, + uint256 basis, uint256 acquisitionDate, - uint256 lastUpdate, + TransferType transferType, string uri, - bytes data, - TransferType tType + bytes data ); + /** + * @notice Emitted when a lot is transferred (full or partial) + */ event LotTransferred( bytes32 indexed lotId, + bytes32 indexed newLotId, address indexed from, address indexed to, uint256 quantity, + uint256 newBasis, + TransferType transferType, string uri, - bytes data, - TransferType tType, - uint256 newCostBasis + bytes data ); - event LotAdjusted( - bytes32 indexed oldLotId, + /** + * @notice Emitted when lots are merged into a new lot + */ + event LotsMerged( + bytes32[] sourceLotIds, bytes32 indexed newLotId, - address operator, - uint256 newQuantity, - uint256 newCostBasis, - address paymentCurrency, - uint256 newAcquisitionDate, - string newUri, - bytes newData, - string reason, - TransferType tType + address indexed owner, + uint256 totalQuantity, + string uri, + bytes data ); - event LotInvalidated(bytes32 indexed lotId); - - event PausedSet(bool indexed paused); - event AccountFrozen(address indexed account, bool frozen); - event LotFrozen(bytes32 indexed lotId, bool frozen); - - event ForcedTransfer( - bytes32 indexed lotId, - address indexed from, - address indexed to, - uint256 quantity, - string reason + /** + * @notice Emitted when a lot is split into multiple lots + */ + event LotSplit( + bytes32 indexed sourceLotId, + bytes32[] newLotIds, + uint256[] quantities, + address indexed owner ); - event TransferControllerAdded(address indexed controller); - /*────────────────────────────────── - Read functions + Core Read Functions ──────────────────────────────────*/ - function name() external view returns (string memory); - function symbol() external view returns (string memory); - function getLot(bytes32 id) external view returns (Lot memory); + + /** + * @notice Returns the token name + */ + function name() external view returns (string memory); + + /** + * @notice Returns the token symbol + */ + function symbol() external view returns (string memory); + + /** + * @notice Returns lot details for a given lot ID + * @param lotId The lot identifier + * @return lot The lot data structure + */ + function getLot(bytes32 lotId) external view returns (Lot memory lot); + + /** + * @notice Returns all lot IDs owned by an address + * @param owner The owner address + * @return lotIds Array of lot identifiers + */ + function getLotsOf(address owner) external view returns (bytes32[] memory lotIds); + + /** + * @notice Returns total quantity across all lots for an owner + * @param owner The owner address + * @return totalQuantity Sum of quantities across all lots + */ + function balanceOf(address owner) external view returns (uint256 totalQuantity); + + /** + * @notice Checks if a lot ID exists and is valid + * @param lotId The lot identifier + * @return exists True if lot exists + */ + function lotExists(bytes32 lotId) external view returns (bool exists); /*────────────────────────────────── - Lot CRUD + Core Lot Operations ──────────────────────────────────*/ + + /** + * @notice Creates a new lot + * @param owner Initial owner of the lot + * @param quantity Amount in the lot + * @param currency Currency used for acquisition (address(0) for native) + * @param basis Total cost basis in currency + * @param acquisitionDate Unix timestamp of acquisition + * @param transferType How this lot was acquired + * @param uri Metadata URI for the lot + * @param data Additional lot-specific data + * @return lotId The created lot identifier + */ function createLot( address owner, uint256 quantity, - address paymentCurrency, - uint256 costBasis, + address currency, + uint256 basis, uint256 acquisitionDate, - string calldata uri, - bytes calldata data, - uint256 customId /* 0 = auto */ - ) external returns (bytes32 lotId, uint256 assignedCustomId); - + TransferType transferType, + string calldata uri, + bytes calldata data + ) external returns (bytes32 lotId); + + /** + * @notice Transfers full or partial lot to another address + * @param lotId Source lot identifier + * @param to Recipient address + * @param quantity Amount to transfer (must be ≤ lot quantity) + * @param transferType Type of transfer for the new lot + * @param newBasis Cost basis for the transferred portion + * @param uri Metadata URI for the new lot + * @param data Additional data for the new lot + * @return newLotId Identifier of the newly created lot for recipient + */ function transfer( bytes32 lotId, address to, uint256 quantity, - TransferType tType, - uint256 newCostBasis, + TransferType transferType, + uint256 newBasis, string calldata uri, - bytes calldata data + bytes calldata data ) external returns (bytes32 newLotId); + /** + * @notice Transfers lot on behalf of owner (requires approval) + * @param lotId Source lot identifier + * @param from Owner address + * @param to Recipient address + * @param quantity Amount to transfer + * @param transferType Type of transfer + * @param newBasis Cost basis for transferred portion + * @param uri Metadata URI for new lot + * @param data Additional data for new lot + * @return newLotId Identifier of newly created lot + */ function transferFrom( bytes32 lotId, address from, address to, uint256 quantity, - TransferType tType, - uint256 newCostBasis, + TransferType transferType, + uint256 newBasis, string calldata uri, - bytes calldata data + bytes calldata data ) external returns (bytes32 newLotId); - function adjustLot( - bytes32 oldLotId, - uint256 newQuantity, - uint256 newCostBasis, - address newPaymentCurrency, - uint256 newAcquisitionDate, - string calldata newUri, - bytes calldata newData, - string calldata reason, - uint256 customId + /** + * @notice Merges multiple lots into a single new lot + * @param sourceLotIds Array of lot identifiers to merge (must have same owner) + * @param uri Metadata URI for merged lot + * @param data Additional data for merged lot + * @return newLotId Identifier of the merged lot + */ + function mergeLots( + bytes32[] calldata sourceLotIds, + string calldata uri, + bytes calldata data ) external returns (bytes32 newLotId); + /** + * @notice Splits a lot into multiple new lots + * @param lotId Source lot identifier + * @param quantities Array of quantities for new lots (must sum to original) + * @param uris Array of metadata URIs for new lots + * @param dataArray Array of additional data for new lots + * @return newLotIds Array of identifiers for new lots + */ + function splitLot( + bytes32 lotId, + uint256[] calldata quantities, + string[] calldata uris, + bytes[] calldata dataArray + ) external returns (bytes32[] memory newLotIds); + /*────────────────────────────────── - Administrative hooks (normative) + Approval System ──────────────────────────────────*/ - function pause() external; - function unpause() external; - function freezeAccount(address account, bool frozen) external; - function freezeLot(bytes32 lotId, bool frozen) external; + /** + * @notice Approve an operator for a specific lot + * @param lotId The lot identifier + * @param operator Address to approve + * @param approved True to approve, false to revoke + */ + function approveLot(bytes32 lotId, address operator, bool approved) external; + + /** + * @notice Approve an operator for all lots owned by caller + * @param operator Address to approve + * @param approved True to approve, false to revoke + */ + function setApprovalForAll(address operator, bool approved) external; + + /** + * @notice Check if operator is approved for a specific lot + * @param lotId The lot identifier + * @param operator Address to check + * @return approved True if approved + */ + function isApprovedForLot(bytes32 lotId, address operator) external view returns (bool approved); + + /** + * @notice Check if operator is approved for all lots of an owner + * @param owner The owner address + * @param operator Address to check + * @return approved True if approved for all + */ + function isApprovedForAll(address owner, address operator) external view returns (bool approved); +} +``` - function forcedTransfer( - bytes32 lotId, - address from, - address to, - uint256 quantity, - string calldata reason - ) external returns (bytes32 newLotId); +### Transfer Type Usage - /* Transfer-policy plug-in */ - function setTransferController(address controller) external; - function transferController() external view returns (address); +The `TransferType` enum enables implementations to capture transfer context for compliance and tax purposes: - /* Optional merge / split helpers */ - function mergeLots(bytes32[] calldata sourceLots, string calldata uri, bytes calldata data) - external returns (bytes32 newLotId); - function splitLot(bytes32 lotId, uint256[] calldata quantities, string[] calldata uris) - external returns (bytes32[] memory newLotIds); -} -``` +- `INTERNAL`: Administrative transfers, splits, merges (typically no taxable event) +- `SALE`: Market transactions for consideration (usually triggers capital gains) +- `GIFT`: Gratuitous transfers (carry-over basis treatment varies by jurisdiction) +- `INHERITANCE`: Estate transfers (step-up basis rules vary by jurisdiction) +- `REWARD`: Compensation, airdrops, staking rewards (often ordinary income) -> Any function marked "external" **MUST** revert if the contract is paused, the caller/lot is frozen, or a transferController vetoes the action. +Implementations MAY use this information for automated compliance reporting but SHOULD consult applicable regulations. -### Tax Considerations (informative) +## Rationale -The `TransferType` enum enables implementations to capture transfer context for jurisdiction-specific tax compliance: +### Why Lot-Based Architecture? -| TransferType | Purpose | Tax implications vary by jurisdiction | -| ------------- | ---------------------------------------- | ------------------------------------- | -| `INTERNAL` | Administrative transfers, splits, merges | Typically no taxable event | -| `SALE` | Market transactions for consideration | Usually triggers capital gains | -| `GIFT` | Gratuitous transfers | Donor/recipient tax treatment varies | -| `INHERITANCE` | Estate/probate transfers | Step-up rules vary by jurisdiction | -| `REWARD` | Compensation, airdrops, staking rewards | Often treated as ordinary income | +Traditional fungible tokens aggregate all holdings into a single balance, losing crucial information needed for: -**Example U.S. Treatment**: `GIFT` uses carry-over basis (IRC § 1015), `INHERITANCE` gets stepped-up basis (IRC § 1014). -**Example UK Treatment**: Different rules under TCGA 1992. -**Example Singapore**: No capital gains tax. +- **Tax compliance**: Basis tracking for capital gains calculations +- **Regulatory reporting**: Audit trails for compliance officers +- **Corporate actions**: Proper handling of stock splits, dividends, vesting +- **Estate planning**: Clear lineage for inheritance and probate -Implementations **SHOULD** consult local tax authorities and **MAY** implement jurisdiction-specific tax calculation modules. +### Core vs Extensions -## Rationale +ERC-7752 Core focuses on essential lot operations while enabling modular extensions: + +**Core Standard (ERC-7752):** -**Administrative hooks (pause, freeze, forcedTransfer, adjustLot)** are embedded in ERC-7752 Core because issued assets **cannot satisfy real-world statutory and fiduciary duties without them.** +- Lot creation, transfer, merge, split operations +- Basic approval system +- Essential events for tracking +- No administrative controls or transfer restrictions -- Securities law obliges issuers and registrars to rectify errors, comply with court orders, and enforce sanctions. -- Transfer-control modules (ERC-7752-Policy) rely on a deterministic ABI to implement jurisdiction-specific rules. -- Standardizing these events enables third-party auditors, tax engines, and regulators to ingest compliance actions uniformly. +**Potential Extensions:** -While purely decentralized assets may deem these functions unnecessary, they may be voluntarily disabled by renouncing the `ADMIN_ROLE` or setting `transferController = address(0)`. Omission from the ABI, however, would break composability for the majority of regulated implementations. +- Transfer controls and compliance checks +- Pause/freeze functionality +- Forced transfers and administrative overrides +- Multi-signature requirements +- Time-based restrictions + +This separation allows: + +- Simple implementations for basic use cases +- Complex compliance layers when needed +- Clear upgrade paths +- Regulatory flexibility + +### Event Design + +Events are designed for deterministic compliance reporting: + +- All lot state changes emit structured events +- Transfer types enable automated tax categorization +- Lineage tracking supports audit requirements +- Consistent event signatures across implementations ## Backwards Compatibility -ERC-7752 is not backward-compatible with ERC-20/721/[ERC-1155](./eip-1155.md) because each lotId represents a semi-fungible slice with its own lineage and admin controls. +ERC-7752 is not backward-compatible with ERC-20/ERC-721/[ERC-1155](./eip-1155.md) because: + +- Each `lotId` represents a semi-fungible slice with unique properties +- Transfer operations create new lots rather than moving balances +- Approval system operates on lot-level rather than token-level + +Implementations MAY provide compatibility layers but this is not required by the standard. ## Security Considerations -- Implementers MUST secure admin roles (e.g., OpenZeppelin `AccessControl` or `AccessManager`). -- A compromised `transferController` could veto or force-transfer lots; multi-sig ownership is **RECOMMENDED**. +### Lot ID Uniqueness -### Conclusion +Implementations MUST generate lot IDs deterministically to ensure uniqueness and prevent collisions: -ERC‑7752 brings granular accounting, compliance, and administrative certainty to on‑chain representations of traditional assets—unlocking the next wave of securitised and regulated token markets. +```solidity +function _generateLotId( + address owner, + uint256 quantity, + uint256 blockNumber, + uint256 nonce +) internal pure returns (bytes32) { + return keccak256(abi.encodePacked(owner, quantity, blockNumber, nonce)); +} +``` ## Copyright From 5577912c2800f9051e079a6675b9b41c6e12ac82 Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Mon, 28 Jul 2025 14:49:23 -0700 Subject: [PATCH 30/31] Update erc-7752.md --- ERCS/erc-7752.md | 98 ++++++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index f18708f1c88..6c384ee6469 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -1,6 +1,6 @@ --- eip: 7752 -title: Lot Token +title: Lot-based Token description: A token treating each acquisition (lot) as an on-chain record with cost-basis and lineage. author: Matt Rosendin (@mrosendin) discussions-to: https://ethereum-magicians.org/t/erc-7752-equity-token/20735 @@ -16,14 +16,14 @@ created: 2024-08-06 ## Motivation -Traditional token standards excel at _fungibility_ ([ERC-20](./eip-20.md)) or _single, indivisible items_ ([ERC-721](./eip-721.md)). -Real-world assets—equity certificates, debt notes, real-estate fractions—sit between those extremes: +Traditional token standards excel at _fungibility_ ([ERC-20](./eip-20.md)) or _single, indivisible items_ ([ERC-721](./eip-721.md)). Real-world assets—equity certificates, debt notes, real-estate fractions—require granular tracking: -- One issuer may create **thousands of discrete lots** over time. -- Each lot needs cost-basis, vesting, or lock-up data. -- Regulators demand pause, freeze, and forced-transfer powers. +- **Distinct lots** with individual basis and acquisition dates +- **Lineage tracking** for audit trails and compliance +- **Modular architecture** supporting jurisdiction-specific extensions +- **Deterministic events** for tax engines and regulatory reporting -ERC-7752 bakes those requirements into a single, deterministic ABI so wallets, custodians, tax engines, and regulators can interoperate without bespoke dialects. +ERC-7752 provides the foundational interface for lot-based tokens while enabling specialized implementations through modular extensions. ## Specification @@ -35,7 +35,6 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; - /** * @title ERC-7752 Lot Token (Core Interface) * @notice Core lot management @@ -50,7 +49,7 @@ interface IERC7752 { SALE, // Market transactions for consideration GIFT, // Gratuitous transfers INHERITANCE, // Estate/probate transfers - REWARD // Compensation, airdrops, staking rewards + INCOME // Compensation, dividends, staking rewards, airdrops } struct Lot { @@ -58,7 +57,7 @@ interface IERC7752 { uint256 quantity; // Amount in this lot address currency; // Currency used for acquisition uint256 basis; // Total cost basis (in currency) - uint256 acquisitionDate; // Unix timestamp of acquisition + uint256 acquisitionDate; // Unix timestamp of original acquisition uint256 lastUpdate; // Unix timestamp of last modification address owner; // Current owner address TransferType transferType; // How this lot was acquired @@ -113,16 +112,6 @@ interface IERC7752 { bytes data ); - /** - * @notice Emitted when a lot is split into multiple lots - */ - event LotSplit( - bytes32 indexed sourceLotId, - bytes32[] newLotIds, - uint256[] quantities, - address indexed owner - ); - /*────────────────────────────────── Core Read Functions ──────────────────────────────────*/ @@ -249,21 +238,6 @@ interface IERC7752 { bytes calldata data ) external returns (bytes32 newLotId); - /** - * @notice Splits a lot into multiple new lots - * @param lotId Source lot identifier - * @param quantities Array of quantities for new lots (must sum to original) - * @param uris Array of metadata URIs for new lots - * @param dataArray Array of additional data for new lots - * @return newLotIds Array of identifiers for new lots - */ - function splitLot( - bytes32 lotId, - uint256[] calldata quantities, - string[] calldata uris, - bytes[] calldata dataArray - ) external returns (bytes32[] memory newLotIds); - /*────────────────────────────────── Approval System ──────────────────────────────────*/ @@ -301,6 +275,21 @@ interface IERC7752 { } ``` +### Lot ID Generation + +Implementations MUST generate lot IDs deterministically to ensure uniqueness and prevent collisions: + +```solidity +function _generateLotId( + address owner, + uint256 quantity, + uint256 blockNumber, + uint256 nonce +) internal pure returns (bytes32) { + return keccak256(abi.encodePacked(owner, quantity, blockNumber, nonce)); +} +``` + ### Transfer Type Usage The `TransferType` enum enables implementations to capture transfer context for compliance and tax purposes: @@ -309,7 +298,7 @@ The `TransferType` enum enables implementations to capture transfer context for - `SALE`: Market transactions for consideration (usually triggers capital gains) - `GIFT`: Gratuitous transfers (carry-over basis treatment varies by jurisdiction) - `INHERITANCE`: Estate transfers (step-up basis rules vary by jurisdiction) -- `REWARD`: Compensation, airdrops, staking rewards (often ordinary income) +- `INCOME`: Compensation, dividends, staking rewards, airdrops (usually ordinary income) Implementations MAY use this information for automated compliance reporting but SHOULD consult applicable regulations. @@ -330,18 +319,20 @@ ERC-7752 Core focuses on essential lot operations while enabling modular extensi **Core Standard (ERC-7752):** -- Lot creation, transfer, merge, split operations +- Lot creation, transfer, merge operations - Basic approval system - Essential events for tracking - No administrative controls or transfer restrictions **Potential Extensions:** +- Lot splitting functionality - Transfer controls and compliance checks - Pause/freeze functionality - Forced transfers and administrative overrides - Multi-signature requirements - Time-based restrictions +- Custom lot ID functionality (for legacy systems) This separation allows: @@ -361,7 +352,7 @@ Events are designed for deterministic compliance reporting: ## Backwards Compatibility -ERC-7752 is not backward-compatible with ERC-20/ERC-721/[ERC-1155](./eip-1155.md) because: +ERC-7752 is not backward-compatible with [ERC-20](./eip-20.md), [ERC-721](./eip-721.md), or [ERC-1155](./eip-1155.md) because: - Each `lotId` represents a semi-fungible slice with unique properties - Transfer operations create new lots rather than moving balances @@ -373,18 +364,27 @@ Implementations MAY provide compatibility layers but this is not required by the ### Lot ID Uniqueness -Implementations MUST generate lot IDs deterministically to ensure uniqueness and prevent collisions: +Implementations MUST ensure lot IDs are unique across the entire token contract to prevent: -```solidity -function _generateLotId( - address owner, - uint256 quantity, - uint256 blockNumber, - uint256 nonce -) internal pure returns (bytes32) { - return keccak256(abi.encodePacked(owner, quantity, blockNumber, nonce)); -} -``` +- Accidental overwrites of existing lots +- Unauthorized access to lot data +- Inconsistent state between lot records + +### Approval Management + +The two-tier approval system (lot-specific and owner-wide) requires careful implementation: + +- Lot-specific approvals SHOULD take precedence over global approvals +- Approval checks MUST occur before any transfer operation +- Approvals SHOULD be revoked when lots are transferred or modified + +### Arithmetic Safety + +Lot operations involving quantities and basis calculations MUST: + +- Check for integer overflow/underflow +- Validate that split quantities sum to original lot quantity +- Ensure basis calculations maintain precision ## Copyright From 0051018414210591dd35fcd09ff05dae3db4b1fc Mon Sep 17 00:00:00 2001 From: Matthew Rosendin Date: Tue, 16 Sep 2025 15:01:41 -0700 Subject: [PATCH 31/31] Update erc-7752.md --- ERCS/erc-7752.md | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/ERCS/erc-7752.md b/ERCS/erc-7752.md index 6c384ee6469..366af843f8c 100644 --- a/ERCS/erc-7752.md +++ b/ERCS/erc-7752.md @@ -32,7 +32,7 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S ### Core Interface ```solidity -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.20; /** @@ -277,19 +277,29 @@ interface IERC7752 { ### Lot ID Generation -Implementations MUST generate lot IDs deterministically to ensure uniqueness and prevent collisions: +Lots are identified with a `bytes32` ID. Implementations MUST ensure lot IDs are: +- **Deterministic**: Same inputs produce same lot ID within the contract +- **Collision-resistant**: Unique within the contract scope +- **Contract-scoped**: Include contract identifier to prevent cross-contract collisions +#### Recommended Approach: ```solidity function _generateLotId( address owner, uint256 quantity, - uint256 blockNumber, - uint256 nonce -) internal pure returns (bytes32) { - return keccak256(abi.encodePacked(owner, quantity, blockNumber, nonce)); + uint256 timestamp +) internal view returns (bytes32) { + return keccak256(abi.encodePacked( + owner, + quantity, + timestamp, + address(this) // Ensures contract-scoped uniqueness + )); } ``` +**Note**: This is a recommended implementation. Alternative approaches are permitted provided they meet the uniqueness and collision-resistance requirements above. + ### Transfer Type Usage The `TransferType` enum enables implementations to capture transfer context for compliance and tax purposes: