/** * Source Code first verified at https://etherscan.io on Wednesday, May 8, 2019 (UTC) */ pragma solidity 0.5.4; /** * @title SafeMath * @dev Unsigned math operations with safety checks that revert on error */ library SafeMath { /** * @dev Multiplies two unsigned integers, reverts on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b); return c; } /** * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a); uint256 c = a - b; return c; } /** * @dev Adds two unsigned integers, reverts on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a); return c; } /** * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), * reverts when dividing by zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0); return a % b; } } /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor () internal { _owner = msg.sender; emit OwnershipTransferred(address(0), _owner); } /** * @return the address of the owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner()); _; } /** * @return true if `msg.sender` is the owner of the contract. */ function isOwner() public view returns (bool) { return msg.sender == _owner; } /** * @dev Allows the current owner to relinquish control of the contract. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. * @notice Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0)); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } /** * @title Roles * @dev Library for managing addresses assigned to a Role. */ library Roles { struct Role { mapping (address => bool) bearer; } /** * @dev give an account access to this role */ function add(Role storage role, address account) internal { require(account != address(0)); require(!has(role, account)); role.bearer[account] = true; } /** * @dev remove an account's access to this role */ function remove(Role storage role, address account) internal { require(account != address(0)); require(has(role, account)); role.bearer[account] = false; } /** * @dev check if an account has this role * @return bool */ function has(Role storage role, address account) internal view returns (bool) { require(account != address(0)); return role.bearer[account]; } } contract MinterRole { using Roles for Roles.Role; event MinterAdded(address indexed account); event MinterRemoved(address indexed account); Roles.Role private _minters; constructor () internal { _addMinter(msg.sender); } modifier onlyMinter() { require(isMinter(msg.sender)); _; } function isMinter(address account) public view returns (bool) { return _minters.has(account); } function addMinter(address account) public onlyMinter { _addMinter(account); } function renounceMinter() public { _removeMinter(msg.sender); } function _addMinter(address account) internal { _minters.add(account); emit MinterAdded(account); } function _removeMinter(address account) internal { _minters.remove(account); emit MinterRemoved(account); } } /** * @title Helps contracts guard against reentrancy attacks. * @author Remco Bloemen <[email protected]π.com>, Eenae <[email protected]> * @dev If you mark a function `nonReentrant`, you should also * mark it `external`. */ contract ReentrancyGuard { /// @dev counter to allow mutex lock with only one SSTORE operation uint256 private _guardCounter; constructor () internal { // The counter starts at one to prevent changing it from zero to a non-zero // value, which is a more expensive operation. _guardCounter = 1; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { _guardCounter += 1; uint256 localCounter = _guardCounter; _; require(localCounter == _guardCounter); } } contract CertificateControllerMock { // Address used by off-chain controller service to sign certificate mapping(address => bool) internal _certificateSigners; // A nonce used to ensure a certificate can be used only once mapping(address => uint256) internal _checkCount; event Checked(address sender); constructor(address _certificateSigner) public { _setCertificateSigner(_certificateSigner, true); } /** * @dev Modifier to protect methods with certificate control */ modifier isValidCertificate(bytes memory data) { require(_certificateSigners[msg.sender] || _checkCertificate(data, 0, 0x00000000), "A3: Transfer Blocked - Sender lockup period not ended"); _checkCount[msg.sender] += 1; // Increment sender check count emit Checked(msg.sender); _; } /** * @dev Get number of transations already sent to this contract by the sender * @param sender Address whom to check the counter of. * @return uint256 Number of transaction already sent to this contract. */ function checkCount(address sender) external view returns (uint256) { return _checkCount[sender]; } /** * @dev Get certificate signer authorization for an operator. * @param operator Address whom to check the certificate signer authorization for. * @return bool 'true' if operator is authorized as certificate signer, 'false' if not. */ function certificateSigners(address operator) external view returns (bool) { return _certificateSigners[operator]; } /** * @dev Set signer authorization for operator. * @param operator Address to add/remove as a certificate signer. * @param authorized 'true' if operator shall be accepted as certificate signer, 'false' if not. */ function _setCertificateSigner(address operator, bool authorized) internal { require(operator != address(0), "Action Blocked - Not a valid address"); _certificateSigners[operator] = authorized; } /** * @dev Checks if a certificate is correct * @param data Certificate to control */ function _checkCertificate(bytes memory data, uint256 /*value*/, bytes4 /*functionID*/) internal pure returns(bool) { // Comments to avoid compilation warnings for unused variables. if(data.length > 0 && (data[0] == hex"10" || data[0] == hex"11" || data[0] == hex"22")) { return true; } else { return false; } } } contract CertificateController is CertificateControllerMock { constructor(address _certificateSigner) public CertificateControllerMock(_certificateSigner) {} } /** * @title IERC777TokensRecipient * @dev ERC777TokensRecipient interface */ interface IERC777TokensRecipient { function canReceive( bytes32 partition, address from, address to, uint value, bytes calldata data, bytes calldata operatorData ) external view returns(bool); function tokensReceived( bytes32 partition, address operator, address from, address to, uint value, bytes calldata data, bytes calldata operatorData ) external; } /** * @title IERC777TokensSender * @dev ERC777TokensSender interface */ interface IERC777TokensSender { function canTransfer( bytes32 partition, address from, address to, uint value, bytes calldata data, bytes calldata operatorData ) external view returns(bool); function tokensToTransfer( bytes32 partition, address operator, address from, address to, uint value, bytes calldata data, bytes calldata operatorData ) external; } /** * @title ERC20 interface * @dev see https://eips.ethereum.org/EIPS/eip-20 */ contract IERC20 { function transfer(address to, uint256 value) external returns (bool); function approve(address spender, uint256 value) external returns (bool); function transferFrom(address from, address to, uint256 value) external returns (bool); function totalSupply() external view returns (uint256); function balanceOf(address who) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } /** * @title IERC1410 partially fungible token standard * @dev ERC1410 interface */ interface IERC1410 { // Token Information function balanceOfByPartition(bytes32 partition, address tokenHolder) external view returns (uint256); // 1/10 function partitionsOf(address tokenHolder) external view returns (bytes32[] memory); // 2/10 // Token Transfers function transferByPartition(bytes32 partition, address to, uint256 value, bytes calldata data) external returns (bytes32); // 3/10 function operatorTransferByPartition(bytes32 partition, address from, address to, uint256 value, bytes calldata data, bytes calldata operatorData) external returns (bytes32); // 4/10 // Default Partition Management function getDefaultPartitions(address tokenHolder) external view returns (bytes32[] memory); // 5/10 function setDefaultPartitions(bytes32[] calldata partitions) external; // 6/10 // Operators function controllersByPartition(bytes32 partition) external view returns (address[] memory); // 7/10 function authorizeOperatorByPartition(bytes32 partition, address operator) external; // 8/10 function revokeOperatorByPartition(bytes32 partition, address operator) external; // 9/10 function isOperatorForPartition(bytes32 partition, address operator, address tokenHolder) external view returns (bool); // 10/10 // Transfer Events event TransferByPartition( bytes32 indexed fromPartition, address operator, address indexed from, address indexed to, uint256 value, bytes data, bytes operatorData ); event ChangedPartition( bytes32 indexed fromPartition, bytes32 indexed toPartition, uint256 value ); // Operator Events event AuthorizedOperatorByPartition(bytes32 indexed partition, address indexed operator, address indexed tokenHolder); event RevokedOperatorByPartition(bytes32 indexed partition, address indexed operator, address indexed tokenHolder); } /** * @title IERC777 token standard * @dev ERC777 interface */ interface IERC777 { function name() external view returns (string memory); // 1/13 function symbol() external view returns (string memory); // 2/13 function totalSupply() external view returns (uint256); // 3/13 function balanceOf(address owner) external view returns (uint256); // 4/13 function granularity() external view returns (uint256); // 5/13 function controllers() external view returns (address[] memory); // 6/13 function authorizeOperator(address operator) external; // 7/13 function revokeOperator(address operator) external; // 8/13 function isOperatorFor(address operator, address tokenHolder) external view returns (bool); // 9/13 function transferWithData(address to, uint256 value, bytes calldata data) external; // 10/13 function transferFromWithData(address from, address to, uint256 value, bytes calldata data, bytes calldata operatorData) external; // 11/13 function redeem(uint256 value, bytes calldata data) external; // 12/13 function redeemFrom(address from, uint256 value, bytes calldata data, bytes calldata operatorData) external; // 13/13 event TransferWithData( address indexed operator, address indexed from, address indexed to, uint256 value, bytes data, bytes operatorData ); event Issued(address indexed operator, address indexed to, uint256 value, bytes data, bytes operatorData); event Redeemed(address indexed operator, address indexed from, uint256 value, bytes data, bytes operatorData); event AuthorizedOperator(address indexed operator, address indexed tokenHolder); event RevokedOperator(address indexed operator, address indexed tokenHolder); } /** * @title ERC1400 security token standard * @dev ERC1400 logic */ interface IERC1400 { // Document Management function getDocument(bytes32 name) external view returns (string memory, bytes32); // 1/9 function setDocument(bytes32 name, string calldata uri, bytes32 documentHash) external; // 2/9 event Document(bytes32 indexed name, string uri, bytes32 documentHash); // Controller Operation function isControllable() external view returns (bool); // 3/9 // Token Issuance function isIssuable() external view returns (bool); // 4/9 function issueByPartition(bytes32 partition, address tokenHolder, uint256 value, bytes calldata data) external; // 5/9 event IssuedByPartition(bytes32 indexed partition, address indexed operator, address indexed to, uint256 value, bytes data, bytes operatorData); // Token Redemption function redeemByPartition(bytes32 partition, uint256 value, bytes calldata data) external; // 6/9 function operatorRedeemByPartition(bytes32 partition, address tokenHolder, uint256 value, bytes calldata data, bytes calldata operatorData) external; // 7/9 event RedeemedByPartition(bytes32 indexed partition, address indexed operator, address indexed from, uint256 value, bytes data, bytes operatorData); // Transfer Validity function canTransferByPartition(bytes32 partition, address to, uint256 value, bytes calldata data) external view returns (byte, bytes32, bytes32); // 8/9 function canOperatorTransferByPartition(bytes32 partition, address from, address to, uint256 value, bytes calldata data, bytes calldata operatorData) external view returns (byte, bytes32, bytes32); // 9/9 } /** * Reason codes - ERC1066 * * To improve the token holder experience, canTransfer MUST return a reason byte code * on success or failure based on the EIP-1066 application-specific status codes specified below. * An implementation can also return arbitrary data as a bytes32 to provide additional * information not captured by the reason code. * * Code Reason * 0xA0 Transfer Verified - Unrestricted * 0xA1 Transfer Verified - On-Chain approval for restricted token * 0xA2 Transfer Verified - Off-Chain approval for restricted token * 0xA3 Transfer Blocked - Sender lockup period not ended * 0xA4 Transfer Blocked - Sender balance insufficient * 0xA5 Transfer Blocked - Sender not eligible * 0xA6 Transfer Blocked - Receiver not eligible * 0xA7 Transfer Blocked - Identity restriction * 0xA8 Transfer Blocked - Token restriction * 0xA9 Transfer Blocked - Token granularity */ contract ERC820Registry { function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external; function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address); function setManager(address _addr, address _newManager) external; function getManager(address _addr) public view returns(address); } /// Base client to interact with the registry. contract ERC820Client { ERC820Registry constant ERC820REGISTRY = ERC820Registry(0x820b586C8C28125366C998641B09DCbE7d4cBF06); function setInterfaceImplementation(string memory _interfaceLabel, address _implementation) internal { bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel)); ERC820REGISTRY.setInterfaceImplementer(address(this), interfaceHash, _implementation); } function interfaceAddr(address addr, string memory _interfaceLabel) internal view returns(address) { bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel)); return ERC820REGISTRY.getInterfaceImplementer(addr, interfaceHash); } function delegateManagement(address _newManager) internal { ERC820REGISTRY.setManager(address(this), _newManager); } } /** * @title ERC777 * @dev ERC777 logic */ contract ERC777 is IERC777, Ownable, ERC820Client, CertificateController, ReentrancyGuard { using SafeMath for uint256; string internal _name; string internal _symbol; uint256 internal _granularity; uint256 internal _totalSupply; // Indicate whether the token can still be controlled by operators or not anymore. bool internal _isControllable; // Mapping from tokenHolder to balance. mapping(address => uint256) internal _balances; /******************** Mappings related to operator **************************/ // Mapping from (operator, tokenHolder) to authorized status. [TOKEN-HOLDER-SPECIFIC] mapping(address => mapping(address => bool)) internal _authorizedOperator; // Array of controllers. [GLOBAL - NOT TOKEN-HOLDER-SPECIFIC] address[] internal _controllers; // Mapping from operator to controller status. [GLOBAL - NOT TOKEN-HOLDER-SPECIFIC] mapping(address => bool) internal _isController; /****************************************************************************/ /** * [ERC777 CONSTRUCTOR] * @dev Initialize ERC777 and CertificateController parameters + register * the contract implementation in ERC820Registry. * @param name Name of the token. * @param symbol Symbol of the token. * @param granularity Granularity of the token. * @param controllers Array of initial controllers. * @param certificateSigner Address of the off-chain service which signs the * conditional ownership certificates required for token transfers, issuance, * redemption (Cf. CertificateController.sol). */ constructor( string memory name, string memory symbol, uint256 granularity, address[] memory controllers, address certificateSigner ) public CertificateController(certificateSigner) { _name = name; _symbol = symbol; _totalSupply = 0; require(granularity >= 1, "Constructor Blocked - Token granularity can not be lower than 1"); _granularity = granularity; _setControllers(controllers); setInterfaceImplementation("ERC777Token", address(this)); } /********************** ERC777 EXTERNAL FUNCTIONS ***************************/ /** * [ERC777 INTERFACE (1/13)] * @dev Get the name of the token, e.g., "MyToken". * @return Name of the token. */ function name() external view returns(string memory) { return _name; } /** * [ERC777 INTERFACE (2/13)] * @dev Get the symbol of the token, e.g., "MYT". * @return Symbol of the token. */ function symbol() external view returns(string memory) { return _symbol; } /** * [ERC777 INTERFACE (3/13)] * @dev Get the total number of issued tokens. * @return Total supply of tokens currently in circulation. */ function totalSupply() external view returns (uint256) { return _totalSupply; } /** * [ERC777 INTERFACE (4/13)] * @dev Get the balance of the account with address 'tokenHolder'. * @param tokenHolder Address for which the balance is returned. * @return Amount of token held by 'tokenHolder' in the token contract. */ function balanceOf(address tokenHolder) external view returns (uint256) { return _balances[tokenHolder]; } /** * [ERC777 INTERFACE (5/13)] * @dev Get the smallest part of the token that’s not divisible. * @return The smallest non-divisible part of the token. */ function granularity() external view returns(uint256) { return _granularity; } /** * [ERC777 INTERFACE (6/13)] * @dev Get the list of controllers as defined by the token contract. * @return List of addresses of all the controllers. */ function controllers() external view returns (address[] memory) { return _controllers; } /** * [ERC777 INTERFACE (7/13)] * @dev Set a third party operator address as an operator of 'msg.sender' to transfer * and redeem tokens on its behalf. * @param operator Address to set as an operator for 'msg.sender'. */ function authorizeOperator(address operator) external { _authorizedOperator[operator][msg.sender] = true; emit AuthorizedOperator(operator, msg.sender); } /** * [ERC777 INTERFACE (8/13)] * @dev Remove the right of the operator address to be an operator for 'msg.sender' * and to transfer and redeem tokens on its behalf. * @param operator Address to rescind as an operator for 'msg.sender'. */ function revokeOperator(address operator) external { _authorizedOperator[operator][msg.sender] = false; emit RevokedOperator(operator, msg.sender); } /** * [ERC777 INTERFACE (9/13)] * @dev Indicate whether the operator address is an operator of the tokenHolder address. * @param operator Address which may be an operator of tokenHolder. * @param tokenHolder Address of a token holder which may have the operator address as an operator. * @return 'true' if operator is an operator of 'tokenHolder' and 'false' otherwise. */ function isOperatorFor(address operator, address tokenHolder) external view returns (bool) { return _isOperatorFor(operator, tokenHolder); } /** * [ERC777 INTERFACE (10/13)] * @dev Transfer the amount of tokens from the address 'msg.sender' to the address 'to'. * @param to Token recipient. * @param value Number of tokens to transfer. * @param data Information attached to the transfer, by the token holder. [CONTAINS THE CONDITIONAL OWNERSHIP CERTIFICATE] */ function transferWithData(address to, uint256 value, bytes calldata data) external isValidCertificate(data) { _transferWithData("", msg.sender, msg.sender, to, value, data, "", true); } /** * [ERC777 INTERFACE (11/13)] * @dev Transfer the amount of tokens on behalf of the address 'from' to the address 'to'. * @param from Token holder (or 'address(0)' to set from to 'msg.sender'). * @param to Token recipient. * @param value Number of tokens to transfer. * @param data Information attached to the transfer, and intended for the token holder ('from'). * @param operatorData Information attached to the transfer by the operator. [CONTAINS THE CONDITIONAL OWNERSHIP CERTIFICATE] */ function transferFromWithData(address from, address to, uint256 value, bytes calldata data, bytes calldata operatorData) external isValidCertificate(operatorData) { address _from = (from == address(0)) ? msg.sender : from; require(_isOperatorFor(msg.sender, _from), "A7: Transfer Blocked - Identity restriction"); _transferWithData("", msg.sender, _from, to, value, data, operatorData, true); } /** * [ERC777 INTERFACE (12/13)] * @dev Redeem the amount of tokens from the address 'msg.sender'. * @param value Number of tokens to redeem. * @param data Information attached to the redemption, by the token holder. [CONTAINS THE CONDITIONAL OWNERSHIP CERTIFICATE] */ function redeem(uint256 value, bytes calldata data) external isValidCertificate(data) { _redeem("", msg.sender, msg.sender, value, data, ""); } /** * [ERC777 INTERFACE (13/13)] * @dev Redeem the amount of tokens on behalf of the address from. * @param from Token holder whose tokens will be redeemed (or address(0) to set from to msg.sender). * @param value Number of tokens to redeem. * @param data Information attached to the redemption. * @param operatorData Information attached to the redemption, by the operator. [CONTAINS THE CONDITIONAL OWNERSHIP CERTIFICATE] */ function redeemFrom(address from, uint256 value, bytes calldata data, bytes calldata operatorData) external isValidCertificate(operatorData) { address _from = (from == address(0)) ? msg.sender : from; require(_isOperatorFor(msg.sender, _from), "A7: Transfer Blocked - Identity restriction"); _redeem("", msg.sender, _from, value, data, operatorData); } /********************** ERC777 INTERNAL FUNCTIONS ***************************/ /** * [INTERNAL] * @dev Check if 'value' is multiple of the granularity. * @param value The quantity that want's to be checked. * @return 'true' if 'value' is a multiple of the granularity. */ function _isMultiple(uint256 value) internal view returns(bool) { return(value.div(_granularity).mul(_granularity) == value); } /** * [INTERNAL] * @dev Check whether an address is a regular address or not. * @param addr Address of the contract that has to be checked. * @return 'true' if 'addr' is a regular address (not a contract). */ function _isRegularAddress(address addr) internal view returns(bool) { if (addr == address(0)) { return false; } uint size; assembly { size := extcodesize(addr) } // solhint-disable-line no-inline-assembly return size == 0; } /** * [INTERNAL] * @dev Indicate whether the operator address is an operator of the tokenHolder address. * @param operator Address which may be an operator of 'tokenHolder'. * @param tokenHolder Address of a token holder which may have the 'operator' address as an operator. * @return 'true' if 'operator' is an operator of 'tokenHolder' and 'false' otherwise. */ function _isOperatorFor(address operator, address tokenHolder) internal view returns (bool) { return (operator == tokenHolder || _authorizedOperator[operator][tokenHolder] || (_isControllable && _isController[operator]) ); } /** * [INTERNAL] * @dev Perform the transfer of tokens. * @param partition Name of the partition (bytes32 to be left empty for ERC777 transfer). * @param operator The address performing the transfer. * @param from Token holder. * @param to Token recipient. * @param value Number of tokens to transfer. * @param data Information attached to the transfer. * @param operatorData Information attached to the transfer by the operator (if any).. * @param preventLocking 'true' if you want this function to throw when tokens are sent to a contract not * implementing 'erc777tokenHolder'. * ERC777 native transfer functions MUST set this parameter to 'true', and backwards compatible ERC20 transfer * functions SHOULD set this parameter to 'false'. */ function _transferWithData( bytes32 partition, address operator, address from, address to, uint256 value, bytes memory data, bytes memory operatorData, bool preventLocking ) internal nonReentrant { require(_isMultiple(value), "A9: Transfer Blocked - Token granularity"); require(to != address(0), "A6: Transfer Blocked - Receiver not eligible"); require(_balances[from] >= value, "A4: Transfer Blocked - Sender balance insufficient"); _callSender(partition, operator, from, to, value, data, operatorData); _balances[from] = _balances[from].sub(value); _balances[to] = _balances[to].add(value); _callRecipient(partition, operator, from, to, value, data, operatorData, preventLocking); emit TransferWithData(operator, from, to, value, data, operatorData); } /** * [INTERNAL] * @dev Perform the token redemption. * @param partition Name of the partition (bytes32 to be left empty for ERC777 transfer). * @param operator The address performing the redemption. * @param from Token holder whose tokens will be redeemed. * @param value Number of tokens to redeem. * @param data Information attached to the redemption. * @param operatorData Information attached to the redemption, by the operator (if any). */ function _redeem(bytes32 partition, address operator, address from, uint256 value, bytes memory data, bytes memory operatorData) internal nonReentrant { require(_isMultiple(value), "A9: Transfer Blocked - Token granularity"); require(from != address(0), "A5: Transfer Blocked - Sender not eligible"); require(_balances[from] >= value, "A4: Transfer Blocked - Sender balance insufficient"); _callSender(partition, operator, from, address(0), value, data, operatorData); _balances[from] = _balances[from].sub(value); _totalSupply = _totalSupply.sub(value); emit Redeemed(operator, from, value, data, operatorData); } /** * [INTERNAL] * @dev Check for 'ERC777TokensSender' hook on the sender and call it. * May throw according to 'preventLocking'. * @param partition Name of the partition (bytes32 to be left empty for ERC777 transfer). * @param operator Address which triggered the balance decrease (through transfer or redemption). * @param from Token holder. * @param to Token recipient for a transfer and 0x for a redemption. * @param value Number of tokens the token holder balance is decreased by. * @param data Extra information. * @param operatorData Extra information, attached by the operator (if any). */ function _callSender( bytes32 partition, address operator, address from, address to, uint256 value, bytes memory data, bytes memory operatorData ) internal { address senderImplementation; senderImplementation = interfaceAddr(from, "ERC777TokensSender"); if (senderImplementation != address(0)) { IERC777TokensSender(senderImplementation).tokensToTransfer(partition, operator, from, to, value, data, operatorData); } } /** * [INTERNAL] * @dev Check for 'ERC777TokensRecipient' hook on the recipient and call it. * May throw according to 'preventLocking'. * @param partition Name of the partition (bytes32 to be left empty for ERC777 transfer). * @param operator Address which triggered the balance increase (through transfer or issuance). * @param from Token holder for a transfer and 0x for an issuance. * @param to Token recipient. * @param value Number of tokens the recipient balance is increased by. * @param data Extra information, intended for the token holder ('from'). * @param operatorData Extra information attached by the operator (if any). * @param preventLocking 'true' if you want this function to throw when tokens are sent to a contract not * implementing 'ERC777TokensRecipient'. * ERC777 native transfer functions MUST set this parameter to 'true', and backwards compatible ERC20 transfer * functions SHOULD set this parameter to 'false'. */ function _callRecipient( bytes32 partition, address operator, address from, address to, uint256 value, bytes memory data, bytes memory operatorData, bool preventLocking ) internal { address recipientImplementation; recipientImplementation = interfaceAddr(to, "ERC777TokensRecipient"); if (recipientImplementation != address(0)) { IERC777TokensRecipient(recipientImplementation).tokensReceived(partition, operator, from, to, value, data, operatorData); } else if (preventLocking) { require(_isRegularAddress(to), "A6: Transfer Blocked - Receiver not eligible"); } } /** * [INTERNAL] * @dev Perform the issuance of tokens. * @param partition Name of the partition (bytes32 to be left empty for ERC777 transfer). * @param operator Address which triggered the issuance. * @param to Token recipient. * @param value Number of tokens issued. * @param data Information attached to the issuance, and intended for the recipient (to). * @param operatorData Information attached to the issuance by the operator (if any). */ function _issue(bytes32 partition, address operator, address to, uint256 value, bytes memory data, bytes memory operatorData) internal nonReentrant { require(_isMultiple(value), "A9: Transfer Blocked - Token granularity"); require(to != address(0), "A6: Transfer Blocked - Receiver not eligible"); _totalSupply = _totalSupply.add(value); _balances[to] = _balances[to].add(value); _callRecipient(partition, operator, address(0), to, value, data, operatorData, true); emit Issued(operator, to, value, data, operatorData); } /********************** ERC777 OPTIONAL FUNCTIONS ***************************/ /** * [NOT MANDATORY FOR ERC777 STANDARD] * @dev Set list of token controllers. * @param operators Controller addresses. */ function _setControllers(address[] memory operators) internal { for (uint i = 0; i<_controllers.length; i++){ _isController[_controllers[i]] = false; } for (uint j = 0; j uint256) internal _totalSupplyByPartition; // Mapping from tokenHolder to their partitions. mapping (address => bytes32[]) internal _partitionsOf; // Mapping from (tokenHolder, partition) to balance of corresponding partition. mapping (address => mapping (bytes32 => uint256)) internal _balanceOfByPartition; // Mapping from tokenHolder to their default partitions (for ERC777 and ERC20 compatibility). mapping (address => bytes32[]) internal _defaultPartitionsOf; // List of token default partitions (for ERC20 compatibility). bytes32[] internal _tokenDefaultPartitions; /****************************************************************************/ /**************** Mappings to find partition operators ************************/ // Mapping from (tokenHolder, partition, operator) to 'approved for partition' status. [TOKEN-HOLDER-SPECIFIC] mapping (address => mapping (bytes32 => mapping (address => bool))) internal _authorizedOperatorByPartition; // Mapping from partition to controllers for the partition. [NOT TOKEN-HOLDER-SPECIFIC] mapping (bytes32 => address[]) internal _controllersByPartition; // Mapping from (partition, operator) to PartitionController status. [NOT TOKEN-HOLDER-SPECIFIC] mapping (bytes32 => mapping (address => bool)) internal _isControllerByPartition; /****************************************************************************/ /** * [ERC1410 CONSTRUCTOR] * @dev Initialize ERC1410 parameters + register * the contract implementation in ERC820Registry. * @param name Name of the token. * @param symbol Symbol of the token. * @param granularity Granularity of the token. * @param controllers Array of initial controllers. * @param certificateSigner Address of the off-chain service which signs the * conditional ownership certificates required for token transfers, issuance, * redemption (Cf. CertificateController.sol). */ constructor( string memory name, string memory symbol, uint256 granularity, address[] memory controllers, address certificateSigner, bytes32[] memory tokenDefaultPartitions ) public ERC777(name, symbol, granularity, controllers, certificateSigner) { _tokenDefaultPartitions = tokenDefaultPartitions; } /********************** ERC1410 EXTERNAL FUNCTIONS **************************/ /** * [ERC1410 INTERFACE (1/10)] * @dev Get balance of a tokenholder for a specific partition. * @param partition Name of the partition. * @param tokenHolder Address for which the balance is returned. * @return Amount of token of partition 'partition' held by 'tokenHolder' in the token contract. */ function balanceOfByPartition(bytes32 partition, address tokenHolder) external view returns (uint256) { return _balanceOfByPartition[tokenHolder][partition]; } /** * [ERC1410 INTERFACE (2/10)] * @dev Get partitions index of a tokenholder. * @param tokenHolder Address for which the partitions index are returned. * @return Array of partitions index of 'tokenHolder'. */ function partitionsOf(address tokenHolder) external view returns (bytes32[] memory) { return _partitionsOf[tokenHolder]; } /** * [ERC1410 INTERFACE (3/10)] * @dev Transfer tokens from a specific partition. * @param partition Name of the partition. * @param to Token recipient. * @param value Number of tokens to transfer. * @param data Information attached to the transfer, by the token holder. [CONTAINS THE CONDITIONAL OWNERSHIP CERTIFICATE] * @return Destination partition. */ function transferByPartition( bytes32 partition, address to, uint256 value, bytes calldata data ) external isValidCertificate(data) returns (bytes32) { return _transferByPartition(partition, msg.sender, msg.sender, to, value, data, ""); } /** * [ERC1410 INTERFACE (4/10)] * @dev Transfer tokens from a specific partition through an operator. * @param partition Name of the partition. * @param from Token holder. * @param to Token recipient. * @param value Number of tokens to transfer. * @param data Information attached to the transfer. [CAN CONTAIN THE DESTINATION PARTITION] * @param operatorData Information attached to the transfer, by the operator. [CONTAINS THE CONDITIONAL OWNERSHIP CERTIFICATE] * @return Destination partition. */ function operatorTransferByPartition( bytes32 partition, address from, address to, uint256 value, bytes calldata data, bytes calldata operatorData ) external isValidCertificate(operatorData) returns (bytes32) { address _from = (from == address(0)) ? msg.sender : from; require(_isOperatorForPartition(partition, msg.sender, _from), "A7: Transfer Blocked - Identity restriction"); return _transferByPartition(partition, msg.sender, _from, to, value, data, operatorData); } /** * [ERC1410 INTERFACE (5/10)] * @dev Get default partitions to transfer from. * Function used for ERC777 and ERC20 backwards compatibility. * For example, a security token may return the bytes32("unrestricted"). * @param tokenHolder Address for which we want to know the default partitions. * @return Array of default partitions. */ function getDefaultPartitions(address tokenHolder) external view returns (bytes32[] memory) { return _defaultPartitionsOf[tokenHolder]; } /** * [ERC1410 INTERFACE (6/10)] * @dev Set default partitions to transfer from. * Function used for ERC777 and ERC20 backwards compatibility. * @param partitions partitions to use by default when not specified. */ function setDefaultPartitions(bytes32[] calldata partitions) external { _defaultPartitionsOf[msg.sender] = partitions; } /** * [ERC1410 INTERFACE (7/10)] * @dev Get controllers for a given partition. * Function used for ERC777 and ERC20 backwards compatibility. * @param partition Name of the partition. * @return Array of controllers for partition. */ function controllersByPartition(bytes32 partition) external view returns (address[] memory) { return _controllersByPartition[partition]; } /** * [ERC1410 INTERFACE (8/10)] * @dev Set 'operator' as an operator for 'msg.sender' for a given partition. * @param partition Name of the partition. * @param operator Address to set as an operator for 'msg.sender'. */ function authorizeOperatorByPartition(bytes32 partition, address operator) external { _authorizedOperatorByPartition[msg.sender][partition][operator] = true; emit AuthorizedOperatorByPartition(partition, operator, msg.sender); } /** * [ERC1410 INTERFACE (9/10)] * @dev Remove the right of the operator address to be an operator on a given * partition for 'msg.sender' and to transfer and redeem tokens on its behalf. * @param partition Name of the partition. * @param operator Address to rescind as an operator on given partition for 'msg.sender'. */ function revokeOperatorByPartition(bytes32 partition, address operator) external { _authorizedOperatorByPartition[msg.sender][partition][operator] = false; emit RevokedOperatorByPartition(partition, operator, msg.sender); } /** * [ERC1410 INTERFACE (10/10)] * @dev Indicate whether the operator address is an operator of the tokenHolder * address for the given partition. * @param partition Name of the partition. * @param operator Address which may be an operator of tokenHolder for the given partition. * @param tokenHolder Address of a token holder which may have the operator address as an operator for the given partition. * @return 'true' if 'operator' is an operator of 'tokenHolder' for partition 'partition' and 'false' otherwise. */ function isOperatorForPartition(bytes32 partition, address operator, address tokenHolder) external view returns (bool) { return _isOperatorForPartition(partition, operator, tokenHolder); } /********************** ERC1410 INTERNAL FUNCTIONS **************************/ /** * [INTERNAL] * @dev Indicate whether the operator address is an operator of the tokenHolder * address for the given partition. * @param partition Name of the partition. * @param operator Address which may be an operator of tokenHolder for the given partition. * @param tokenHolder Address of a token holder which may have the operator address as an operator for the given partition. * @return 'true' if 'operator' is an operator of 'tokenHolder' for partition 'partition' and 'false' otherwise. */ function _isOperatorForPartition(bytes32 partition, address operator, address tokenHolder) internal view returns (bool) { return (_isOperatorFor(operator, tokenHolder) || _authorizedOperatorByPartition[tokenHolder][partition][operator] || (_isControllable && _isControllerByPartition[partition][operator]) ); } /** * [INTERNAL] * @dev Transfer tokens from a specific partition. * @param fromPartition Partition of the tokens to transfer. * @param operator The address performing the transfer. * @param from Token holder. * @param to Token recipient. * @param value Number of tokens to transfer. * @param data Information attached to the transfer. [CAN CONTAIN THE DESTINATION PARTITION] * @param operatorData Information attached to the transfer, by the operator (if any). * @return Destination partition. */ function _transferByPartition( bytes32 fromPartition, address operator, address from, address to, uint256 value, bytes memory data, bytes memory operatorData ) internal returns (bytes32) { require(_balanceOfByPartition[from][fromPartition] >= value, "A4: Transfer Blocked - Sender balance insufficient"); // ensure enough funds bytes32 toPartition = fromPartition; if(operatorData.length != 0 && data.length != 0) { toPartition = _getDestinationPartition(fromPartition, data); } _removeTokenFromPartition(from, fromPartition, value); _transferWithData(fromPartition, operator, from, to, value, data, operatorData, true); _addTokenToPartition(to, toPartition, value); emit TransferByPartition(fromPartition, operator, from, to, value, data, operatorData); if(toPartition != fromPartition) { emit ChangedPartition(fromPartition, toPartition, value); } return toPartition; } /** * [INTERNAL] * @dev Remove a token from a specific partition. * @param from Token holder. * @param partition Name of the partition. * @param value Number of tokens to transfer. */ function _removeTokenFromPartition(address from, bytes32 partition, uint256 value) internal { _balanceOfByPartition[from][partition] = _balanceOfByPartition[from][partition].sub(value); _totalSupplyByPartition[partition] = _totalSupplyByPartition[partition].sub(value); // If the balance of the TokenHolder's partition is zero, finds and deletes the partition. if(_balanceOfByPartition[from][partition] == 0) { for (uint i = 0; i < _partitionsOf[from].length; i++) { if(_partitionsOf[from][i] == partition) { _partitionsOf[from][i] = _partitionsOf[from][_partitionsOf[from].length - 1]; delete _partitionsOf[from][_partitionsOf[from].length - 1]; _partitionsOf[from].length--; break; } } } // If the total supply is zero, finds and deletes the partition. if(_totalSupplyByPartition[partition] == 0) { for (uint i = 0; i < _totalPartitions.length; i++) { if(_totalPartitions[i] == partition) { _totalPartitions[i] = _totalPartitions[_totalPartitions.length - 1]; delete _totalPartitions[_totalPartitions.length - 1]; _totalPartitions.length--; break; } } } } /** * [INTERNAL] * @dev Add a token to a specific partition. * @param to Token recipient. * @param partition Name of the partition. * @param value Number of tokens to transfer. */ function _addTokenToPartition(address to, bytes32 partition, uint256 value) internal { if(value != 0) { if(_balanceOfByPartition[to][partition] == 0) { _partitionsOf[to].push(partition); } _balanceOfByPartition[to][partition] = _balanceOfByPartition[to][partition].add(value); if(_totalSupplyByPartition[partition] == 0) { _totalPartitions.push(partition); } _totalSupplyByPartition[partition] = _totalSupplyByPartition[partition].add(value); } } /** * [INTERNAL] * @dev Retrieve the destination partition from the 'data' field. * By convention, a partition change is requested ONLY when 'data' starts * with the flag: 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff * When the flag is detected, the destination tranche is extracted from the * 32 bytes following the flag. * @param fromPartition Partition of the tokens to transfer. * @param data Information attached to the transfer. [CAN CONTAIN THE DESTINATION PARTITION] * @return Destination partition. */ function _getDestinationPartition(bytes32 fromPartition, bytes memory data) internal pure returns(bytes32 toPartition) { bytes32 changePartitionFlag = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; bytes32 flag; assembly { flag := mload(add(data, 32)) } if(flag == changePartitionFlag) { assembly { toPartition := mload(add(data, 64)) } } else { toPartition = fromPartition; } } /** * [INTERNAL] * @dev Get the sender's default partition if setup, or the global default partition if not. * @param tokenHolder Address for which the default partition is returned. * @return Default partition. */ function _getDefaultPartitions(address tokenHolder) internal view returns(bytes32[] memory) { if(_defaultPartitionsOf[tokenHolder].length != 0) { return _defaultPartitionsOf[tokenHolder]; } else { return _tokenDefaultPartitions; } } /********************* ERC1410 OPTIONAL FUNCTIONS ***************************/ /** * [NOT MANDATORY FOR ERC1410 STANDARD] * @dev Get list of existing partitions. * @return Array of all exisiting partitions. */ function totalPartitions() external view returns (bytes32[] memory) { return _totalPartitions; } /** * [NOT MANDATORY FOR ERC1410 STANDARD][SHALL BE CALLED ONLY FROM ERC1400] * @dev Set list of token partition controllers. * @param partition Name of the partition. * @param operators Controller addresses. */ function _setPartitionControllers(bytes32 partition, address[] memory operators) internal { for (uint i = 0; i<_controllersByPartition[partition].length; i++){ _isControllerByPartition[partition][_controllersByPartition[partition][i]] = false; } for (uint j = 0; j Doc) internal _documents; // Indicate whether the token can still be issued by the issuer or not anymore. bool internal _isIssuable; /** * @dev Modifier to verify if token is issuable. */ modifier issuableToken() { require(_isIssuable, "A8, Transfer Blocked - Token restriction"); _; } /** * [ERC1400 CONSTRUCTOR] * @dev Initialize ERC1400 + register * the contract implementation in ERC820Registry. * @param name Name of the token. * @param symbol Symbol of the token. * @param granularity Granularity of the token. * @param controllers Array of initial controllers. * @param certificateSigner Address of the off-chain service which signs the * conditional ownership certificates required for token transfers, issuance, * redemption (Cf. CertificateController.sol). */ constructor( string memory name, string memory symbol, uint256 granularity, address[] memory controllers, address certificateSigner, bytes32[] memory tokenDefaultPartitions ) public ERC1410(name, symbol, granularity, controllers, certificateSigner, tokenDefaultPartitions) { setInterfaceImplementation("ERC1400Token", address(this)); _isControllable = true; _isIssuable = true; } /********************** ERC1400 EXTERNAL FUNCTIONS **************************/ /** * [ERC1400 INTERFACE (1/9)] * @dev Access a document associated with the token. * @param name Short name (represented as a bytes32) associated to the document. * @return Requested document + document hash. */ function getDocument(bytes32 name) external view returns (string memory, bytes32) { require(bytes(_documents[name].docURI).length != 0, "Action Blocked - Empty document"); return ( _documents[name].docURI, _documents[name].docHash ); } /** * [ERC1400 INTERFACE (2/9)] * @dev Associate a document with the token. * @param name Short name (represented as a bytes32) associated to the document. * @param uri Document content. * @param documentHash Hash of the document [optional parameter]. */ function setDocument(bytes32 name, string calldata uri, bytes32 documentHash) external onlyOwner { _documents[name] = Doc({ docURI: uri, docHash: documentHash }); emit Document(name, uri, documentHash); } /** * [ERC1400 INTERFACE (3/9)] * @dev Know if the token can be controlled by operators. * If a token returns 'false' for 'isControllable()'' then it MUST always return 'false' in the future. * @return bool 'true' if the token can still be controlled by operators, 'false' if it can't anymore. */ function isControllable() external view returns (bool) { return _isControllable; } /** * [ERC1400 INTERFACE (4/9)] * @dev Know if new tokens can be issued in the future. * @return bool 'true' if tokens can still be issued by the issuer, 'false' if they can't anymore. */ function isIssuable() external view returns (bool) { return _isIssuable; } /** * [ERC1400 INTERFACE (5/9)] * @dev Issue tokens from a specific partition. * @param partition Name of the partition. * @param tokenHolder Address for which we want to issue tokens. * @param value Number of tokens issued. * @param data Information attached to the issuance, by the issuer. [CONTAINS THE CONDITIONAL OWNERSHIP CERTIFICATE] */ function issueByPartition(bytes32 partition, address tokenHolder, uint256 value, bytes calldata data) external onlyMinter issuableToken isValidCertificate(data) { _issueByPartition(partition, msg.sender, tokenHolder, value, data, ""); } /** * [ERC1400 INTERFACE (6/9)] * @dev Redeem tokens of a specific partition. * @param partition Name of the partition. * @param value Number of tokens redeemed. * @param data Information attached to the redemption, by the redeemer. [CONTAINS THE CONDITIONAL OWNERSHIP CERTIFICATE] */ function redeemByPartition(bytes32 partition, uint256 value, bytes calldata data) external isValidCertificate(data) { _redeemByPartition(partition, msg.sender, msg.sender, value, data, ""); } /** * [ERC1400 INTERFACE (7/9)] * @dev Redeem tokens of a specific partition. * @param partition Name of the partition. * @param tokenHolder Address for which we want to redeem tokens. * @param value Number of tokens redeemed. * @param data Information attached to the redemption. * @param operatorData Information attached to the redemption, by the operator. [CONTAINS THE CONDITIONAL OWNERSHIP CERTIFICATE] */ function operatorRedeemByPartition(bytes32 partition, address tokenHolder, uint256 value, bytes calldata data, bytes calldata operatorData) external isValidCertificate(operatorData) { address _from = (tokenHolder == address(0)) ? msg.sender : tokenHolder; require(_isOperatorForPartition(partition, msg.sender, _from), "A7: Transfer Blocked - Identity restriction"); _redeemByPartition(partition, msg.sender, _from, value, data, operatorData); } /** * [ERC1400 INTERFACE (8/9)] * @dev Know the reason on success or failure based on the EIP-1066 application-specific status codes. * @param partition Name of the partition. * @param to Token recipient. * @param value Number of tokens to transfer. * @param data Information attached to the transfer, by the token holder. [CONTAINS THE CONDITIONAL OWNERSHIP CERTIFICATE] * @return ESC (Ethereum Status Code) following the EIP-1066 standard. * @return Additional bytes32 parameter that can be used to define * application specific reason codes with additional details (for example the * transfer restriction rule responsible for making the transfer operation invalid). * @return Destination partition. */ function canTransferByPartition(bytes32 partition, address to, uint256 value, bytes calldata data) external view returns (byte, bytes32, bytes32) { if(!_checkCertificate(data, 0, 0xf3d490db)) { // 4 first bytes of keccak256(transferByPartition(bytes32,address,uint256,bytes)) return(hex"A3", "", partition); // Transfer Blocked - Sender lockup period not ended } else { return _canTransfer(partition, msg.sender, msg.sender, to, value, data, ""); } } /** * [ERC1400 INTERFACE (9/9)] * @dev Know the reason on success or failure based on the EIP-1066 application-specific status codes. * @param partition Name of the partition. * @param from Token holder. * @param to Token recipient. * @param value Number of tokens to transfer. * @param data Information attached to the transfer. [CAN CONTAIN THE DESTINATION PARTITION] * @param operatorData Information attached to the transfer, by the operator. [CONTAINS THE CONDITIONAL OWNERSHIP CERTIFICATE] * @return ESC (Ethereum Status Code) following the EIP-1066 standard. * @return Additional bytes32 parameter that can be used to define * application specific reason codes with additional details (for example the * transfer restriction rule responsible for making the transfer operation invalid). * @return Destination partition. */ function canOperatorTransferByPartition(bytes32 partition, address from, address to, uint256 value, bytes calldata data, bytes calldata operatorData) external view returns (byte, bytes32, bytes32) { if(!_checkCertificate(operatorData, 0, 0x8c0dee9c)) { // 4 first bytes of keccak256(operatorTransferByPartition(bytes32,address,address,uint256,bytes,bytes)) return(hex"A3", "", partition); // Transfer Blocked - Sender lockup period not ended } else { address _from = (from == address(0)) ? msg.sender : from; return _canTransfer(partition, msg.sender, _from, to, value, data, operatorData); } } /********************** ERC1400 INTERNAL FUNCTIONS **************************/ /** * [INTERNAL] * @dev Know the reason on success or failure based on the EIP-1066 application-specific status codes. * @param partition Name of the partition. * @param operator The address performing the transfer. * @param from Token holder. * @param to Token recipient. * @param value Number of tokens to transfer. * @param data Information attached to the transfer. [CAN CONTAIN THE DESTINATION PARTITION] * @param operatorData Information attached to the transfer, by the operator (if any). * @return ESC (Ethereum Status Code) following the EIP-1066 standard. * @return Additional bytes32 parameter that can be used to define * application specific reason codes with additional details (for example the * transfer restriction rule responsible for making the transfer operation invalid). * @return Destination partition. */ function _canTransfer(bytes32 partition, address operator, address from, address to, uint256 value, bytes memory data, bytes memory operatorData) internal view returns (byte, bytes32, bytes32) { if(!_isOperatorForPartition(partition, operator, from)) return(hex"A7", "", partition); // "Transfer Blocked - Identity restriction" if((_balances[from] < value) || (_balanceOfByPartition[from][partition] < value)) return(hex"A4", "", partition); // Transfer Blocked - Sender balance insufficient if(to == address(0)) return(hex"A6", "", partition); // Transfer Blocked - Receiver not eligible address senderImplementation; address recipientImplementation; senderImplementation = interfaceAddr(from, "ERC777TokensSender"); recipientImplementation = interfaceAddr(to, "ERC777TokensRecipient"); if((senderImplementation != address(0)) && !IERC777TokensSender(senderImplementation).canTransfer(partition, from, to, value, data, operatorData)) return(hex"A5", "", partition); // Transfer Blocked - Sender not eligible if((recipientImplementation != address(0)) && !IERC777TokensRecipient(recipientImplementation).canReceive(partition, from, to, value, data, operatorData)) return(hex"A6", "", partition); // Transfer Blocked - Receiver not eligible if(!_isMultiple(value)) return(hex"A9", "", partition); // Transfer Blocked - Token granularity return(hex"A2", "", partition); // Transfer Verified - Off-Chain approval for restricted token } /** * [INTERNAL] * @dev Issue tokens from a specific partition. * @param toPartition Name of the partition. * @param operator The address performing the issuance. * @param to Token recipient. * @param value Number of tokens to issue. * @param data Information attached to the issuance. * @param operatorData Information attached to the issuance, by the operator (if any). */ function _issueByPartition( bytes32 toPartition, address operator, address to, uint256 value, bytes memory data, bytes memory operatorData ) internal { _issue(toPartition, operator, to, value, data, operatorData); _addTokenToPartition(to, toPartition, value); emit IssuedByPartition(toPartition, operator, to, value, data, operatorData); } /** * [INTERNAL] * @dev Redeem tokens of a specific partition. * @param fromPartition Name of the partition. * @param operator The address performing the redemption. * @param from Token holder whose tokens will be redeemed. * @param value Number of tokens to redeem. * @param data Information attached to the redemption. * @param operatorData Information attached to the redemption, by the operator (if any). */ function _redeemByPartition( bytes32 fromPartition, address operator, address from, uint256 value, bytes memory data, bytes memory operatorData ) internal { require(_balanceOfByPartition[from][fromPartition] >= value, "A4: Transfer Blocked - Sender balance insufficient"); _removeTokenFromPartition(from, fromPartition, value); _redeem(fromPartition, operator, from, value, data, operatorData); emit RedeemedByPartition(fromPartition, operator, from, value, data, operatorData); } /********************** ERC1400 OPTIONAL FUNCTIONS **************************/ /** * [NOT MANDATORY FOR ERC1400 STANDARD] * @dev Definitely renounce the possibility to control tokens on behalf of tokenHolders. * Once set to false, '_isControllable' can never be set to 'true' again. */ function renounceControl() external onlyOwner { _isControllable = false; } /** * [NOT MANDATORY FOR ERC1400 STANDARD] * @dev Definitely renounce the possibility to issue new tokens. * Once set to false, '_isIssuable' can never be set to 'true' again. */ function renounceIssuance() external onlyOwner { _isIssuable = false; } /** * [NOT MANDATORY FOR ERC1400 STANDARD] * @dev Set list of token controllers. * @param operators Controller addresses. */ function setControllers(address[] calldata operators) external onlyOwner { _setControllers(operators); } /** * [NOT MANDATORY FOR ERC1400 STANDARD] * @dev Set list of token partition controllers. * @param partition Name of the partition. * @param operators Controller addresses. */ function setPartitionControllers(bytes32 partition, address[] calldata operators) external onlyOwner { _setPartitionControllers(partition, operators); } /** * @dev Add a certificate signer for the token. * @param operator Address to set as a certificate signer. * @param authorized 'true' if operator shall be accepted as certificate signer, 'false' if not. */ function setCertificateSigner(address operator, bool authorized) external onlyOwner { _setCertificateSigner(operator, authorized); } /************* ERC1410/ERC777 BACKWARDS RETROCOMPATIBILITY ******************/ /** * [NOT MANDATORY FOR ERC1400 STANDARD] * @dev Get token default partitions to send from. * Function used for ERC777 and ERC20 backwards compatibility. * For example, a security token may return the bytes32("unrestricted"). * @return Default partitions. */ function getTokenDefaultPartitions() external view returns (bytes32[] memory) { return _tokenDefaultPartitions; } /** * [NOT MANDATORY FOR ERC1400 STANDARD] * @dev Set token default partitions to send from. * Function used for ERC777 and ERC20 backwards compatibility. * @param defaultPartitions Partitions to use by default when not specified. */ function setTokenDefaultPartitions(bytes32[] calldata defaultPartitions) external onlyOwner { _tokenDefaultPartitions = defaultPartitions; } /** * [NOT MANDATORY FOR ERC1400 STANDARD][OVERRIDES ERC1410 METHOD] * @dev Redeem the value of tokens from the address 'msg.sender'. * @param value Number of tokens to redeem. * @param data Information attached to the redemption, by the token holder. [CONTAINS THE CONDITIONAL OWNERSHIP CERTIFICATE] */ function redeem(uint256 value, bytes calldata data) external isValidCertificate(data) { _redeemByDefaultPartitions(msg.sender, msg.sender, value, data, ""); } /** * [NOT MANDATORY FOR ERC1400 STANDARD][OVERRIDES ERC1410 METHOD] * @dev Redeem the value of tokens on behalf of the address 'from'. * @param from Token holder whose tokens will be redeemed (or 'address(0)' to set from to 'msg.sender'). * @param value Number of tokens to redeem. * @param data Information attached to the redemption. * @param operatorData Information attached to the redemption, by the operator. [CONTAINS THE CONDITIONAL OWNERSHIP CERTIFICATE] */ function redeemFrom(address from, uint256 value, bytes calldata data, bytes calldata operatorData) external isValidCertificate(operatorData) { address _from = (from == address(0)) ? msg.sender : from; require(_isOperatorFor(msg.sender, _from), "A7: Transfer Blocked - Identity restriction"); _redeemByDefaultPartitions(msg.sender, _from, value, data, operatorData); } /** * [NOT MANDATORY FOR ERC1410 STANDARD] * @dev Redeem tokens from a default partitions. * @param operator The address performing the redeem. * @param from Token holder. * @param value Number of tokens to redeem. * @param data Information attached to the redemption. * @param operatorData Information attached to the redemption, by the operator (if any). */ function _redeemByDefaultPartitions( address operator, address from, uint256 value, bytes memory data, bytes memory operatorData ) internal { bytes32[] memory _partitions = _getDefaultPartitions(from); require(_partitions.length != 0, "A8: Transfer Blocked - Token restriction"); uint256 _remainingValue = value; uint256 _localBalance; for (uint i = 0; i < _partitions.length; i++) { _localBalance = _balanceOfByPartition[from][_partitions[i]]; if(_remainingValue <= _localBalance) { _redeemByPartition(_partitions[i], operator, from, _remainingValue, data, operatorData); _remainingValue = 0; break; } else { _redeemByPartition(_partitions[i], operator, from, _localBalance, data, operatorData); _remainingValue = _remainingValue - _localBalance; } } require(_remainingValue == 0, "A8: Transfer Blocked - Token restriction"); } } /** * @title ERC1400ERC20 * @dev ERC1400 with ERC20 retrocompatibility */ contract ERC1400ERC20 is IERC20, ERC1400 { // Mapping from (tokenHolder, spender) to allowed value. mapping (address => mapping (address => uint256)) internal _allowed; // Mapping from (tokenHolder) to whitelisted status. mapping (address => bool) internal _whitelisted; /** * @dev Modifier to verify if sender and recipient are whitelisted. */ modifier isWhitelisted(address recipient) { require(_whitelisted[recipient], "A3: Transfer Blocked - Sender lockup period not ended"); _; } /** * [ERC1400ERC20 CONSTRUCTOR] * @dev Initialize ERC71400ERC20 and CertificateController parameters + register * the contract implementation in ERC820Registry. * @param name Name of the token. * @param symbol Symbol of the token. * @param granularity Granularity of the token. * @param controllers Array of initial controllers. * @param certificateSigner Address of the off-chain service which signs the * conditional ownership certificates required for token transfers, issuance, * redemption (Cf. CertificateController.sol). */ constructor( string memory name, string memory symbol, uint256 granularity, address[] memory controllers, address certificateSigner, bytes32[] memory tokenDefaultPartitions ) public ERC1400(name, symbol, granularity, controllers, certificateSigner, tokenDefaultPartitions) { setInterfaceImplementation("ERC20Token", address(this)); } /** * [OVERRIDES ERC1400 METHOD] * @dev Perform the transfer of tokens. * @param partition Name of the partition (bytes32 to be left empty for ERC777 transfer). * @param operator The address performing the transfer. * @param from Token holder. * @param to Token recipient. * @param value Number of tokens to transfer. * @param data Information attached to the transfer. * @param operatorData Information attached to the transfer by the operator (if any). * @param preventLocking 'true' if you want this function to throw when tokens are sent to a contract not * implementing 'erc777tokenHolder'. * ERC777 native transfer functions MUST set this parameter to 'true', and backwards compatible ERC20 transfer * functions SHOULD set this parameter to 'false'. */ function _transferWithData( bytes32 partition, address operator, address from, address to, uint256 value, bytes memory data, bytes memory operatorData, bool preventLocking ) internal { ERC777._transferWithData(partition, operator, from, to, value, data, operatorData, preventLocking); emit Transfer(from, to, value); } /** * [OVERRIDES ERC1400 METHOD] * @dev Perform the token redemption. * @param partition Name of the partition (bytes32 to be left empty for ERC777 transfer). * @param operator The address performing the redemption. * @param from Token holder whose tokens will be redeemed. * @param value Number of tokens to redeem. * @param data Information attached to the redemption. * @param operatorData Information attached to the redemption by the operator (if any). */ function _redeem(bytes32 partition, address operator, address from, uint256 value, bytes memory data, bytes memory operatorData) internal { ERC777._redeem(partition, operator, from, value, data, operatorData); emit Transfer(from, address(0), value); // ERC20 backwards compatibility } /** * [OVERRIDES ERC1400 METHOD] * @dev Perform the issuance of tokens. * @param partition Name of the partition (bytes32 to be left empty for ERC777 transfer). * @param operator Address which triggered the issuance. * @param to Token recipient. * @param value Number of tokens issued. * @param data Information attached to the issuance. * @param operatorData Information attached to the issuance by the operator (if any). */ function _issue(bytes32 partition, address operator, address to, uint256 value, bytes memory data, bytes memory operatorData) internal { ERC777._issue(partition, operator, to, value, data, operatorData); emit Transfer(address(0), to, value); // ERC20 backwards compatibility } /** * [OVERRIDES ERC1400 METHOD] * @dev Get the number of decimals of the token. * @return The number of decimals of the token. For Backwards compatibility, decimals are forced to 18 in ERC777. */ function decimals() external pure returns(uint8) { return uint8(18); } /** * [NOT MANDATORY FOR ERC1400 STANDARD] * @dev Check the value of tokens that an owner allowed to a spender. * @param owner address The address which owns the funds. * @param spender address The address which will spend the funds. * @return A uint256 specifying the value of tokens still available for the spender. */ function allowance(address owner, address spender) external view returns (uint256) { return _allowed[owner][spender]; } /** * [NOT MANDATORY FOR ERC1400 STANDARD] * @dev Approve the passed address to spend the specified amount of tokens on behalf of 'msg.sender'. * Beware that changing an allowance with this method brings the risk that someone may use both the old * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @return A boolean that indicates if the operation was successful. */ function approve(address spender, uint256 value) external returns (bool) { require(spender != address(0), "A5: Transfer Blocked - Sender not eligible"); _allowed[msg.sender][spender] = value; emit Approval(msg.sender, spender, value); return true; } /** * [NOT MANDATORY FOR ERC1400 STANDARD] * @dev Transfer token for a specified address. * @param to The address to transfer to. * @param value The value to be transferred. * @return A boolean that indicates if the operation was successful. */ function transfer(address to, uint256 value) external isWhitelisted(to) returns (bool) { _transferByDefaultPartitions(msg.sender, msg.sender, to, value, "", ""); return true; } /** * [NOT MANDATORY FOR ERC1400 STANDARD] * @dev Transfer tokens from one address to another. * @param from The address which you want to transfer tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean that indicates if the operation was successful. */ function transferFrom(address from, address to, uint256 value) external isWhitelisted(to) returns (bool) { address _from = (from == address(0)) ? msg.sender : from; require( _isOperatorFor(msg.sender, _from) || (value <= _allowed[_from][msg.sender]), "A7: Transfer Blocked - Identity restriction"); if(_allowed[_from][msg.sender] >= value) { _allowed[_from][msg.sender] = _allowed[_from][msg.sender].sub(value); } else { _allowed[_from][msg.sender] = 0; } _transferByDefaultPartitions(msg.sender, _from, to, value, "", ""); return true; } /***************** ERC1400ERC20 OPTIONAL FUNCTIONS ***************************/ /** * [NOT MANDATORY FOR ERC1400ERC20 STANDARD] * @dev Get whitelisted status for a tokenHolder. * @param tokenHolder Address whom to check the whitelisted status for. * @return bool 'true' if tokenHolder is whitelisted, 'false' if not. */ function whitelisted(address tokenHolder) external view returns (bool) { return _whitelisted[tokenHolder]; } /** * [NOT MANDATORY FOR ERC1400ERC20 STANDARD] * @dev Set whitelisted status for a tokenHolder. * @param tokenHolder Address to add/remove from whitelist. * @param authorized 'true' if tokenHolder shall be added to whitelist, 'false' if not. */ function setWhitelisted(address tokenHolder, bool authorized) external onlyOwner { _setWhitelisted(tokenHolder, authorized); } /** * [NOT MANDATORY FOR ERC1400ERC20 STANDARD] * @dev Set whitelisted status for a tokenHolder. * @param tokenHolder Address to add/remove from whitelist. * @param authorized 'true' if tokenHolder shall be added to whitelist, 'false' if not. */ function _setWhitelisted(address tokenHolder, bool authorized) internal { require(tokenHolder != address(0), "Action Blocked - Not a valid address"); _whitelisted[tokenHolder] = authorized; } }