// SPDX-License-Identifier: GPL-3.0 // // :+#####%%%%%%%%%%%%%%+ // .-*@@@%+.:+%@@@@@%%#***%@@%= // :=*%@@@#=. :#@@% *@@@%= // .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- // :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. // -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ // =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- // -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: // :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. // %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. // #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ // *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- // -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: // .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- // -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- // -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# // *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- // +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: // =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: // .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. // +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. // -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. // ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. // @@@@@@+. +@@*. .+@@@@@%=. // -@@@@@= =@@%: -#@@@@%+. // +@@@@@. =@@@= .+@@@@@*: // #@@@@#:%@@#. :*@@@@#- // @@@@@%@@@= :#@@@@+. // :@@@@@@@#.:#@@@%- // +@@@@@@-.*@@@*: // #@@@@#.=@@@+. // @@@@+-%@%= // :@@@#%@%= // +@@@@%- // :#%%= // /** * NOTICE * * The T-REX software is licensed under a proprietary license or the GPL v.3. * If you choose to receive it under the GPL v.3 license, the following applies: * T-REX is a suite of smart contracts implementing the ERC-3643 standard and * developed by Tokeny to manage and transfer financial assets on EVM blockchains * * Copyright (C) 2023, Tokeny sàrl. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ pragma solidity 0.8.17; import "./IToken.sol"; import "@onchain-id/solidity/contracts/interface/IIdentity.sol"; import "./TokenStorage.sol"; import "../roles/AgentRoleUpgradeable.sol"; contract Token is IToken, AgentRoleUpgradeable, TokenStorage { /// modifiers /// @dev Modifier to make a function callable only when the contract is not paused. modifier whenNotPaused() { require(!_tokenPaused, "Pausable: paused"); _; } /// @dev Modifier to make a function callable only when the contract is paused. modifier whenPaused() { require(_tokenPaused, "Pausable: not paused"); _; } /** * @dev the constructor initiates the token contract * msg.sender is set automatically as the owner of the smart contract * @param _identityRegistry the address of the Identity registry linked to the token * @param _compliance the address of the compliance contract linked to the token * @param _name the name of the token * @param _symbol the symbol of the token * @param _decimals the decimals of the token * @param _onchainID the address of the onchainID of the token * emits an `UpdatedTokenInformation` event * emits an `IdentityRegistryAdded` event * emits a `ComplianceAdded` event */ function init( address _identityRegistry, address _compliance, string memory _name, string memory _symbol, uint8 _decimals, // _onchainID can be zero address if not set, can be set later by owner address _onchainID ) external initializer { // that require is protecting legacy versions of TokenProxy contracts // as there was a bug with the initializer modifier on these proxies // that check is preventing attackers to call the init functions on those // legacy contracts. require(owner() == address(0), "already initialized"); require( _identityRegistry != address(0) && _compliance != address(0) , "invalid argument - zero address"); require( keccak256(abi.encode(_name)) != keccak256(abi.encode("")) && keccak256(abi.encode(_symbol)) != keccak256(abi.encode("")) , "invalid argument - empty string"); require(0 <= _decimals && _decimals <= 18, "decimals between 0 and 18"); __Ownable_init(); _tokenName = _name; _tokenSymbol = _symbol; _tokenDecimals = _decimals; _tokenOnchainID = _onchainID; _tokenPaused = true; setIdentityRegistry(_identityRegistry); setCompliance(_compliance); emit UpdatedTokenInformation(_tokenName, _tokenSymbol, _tokenDecimals, _TOKEN_VERSION, _tokenOnchainID); } /** * @dev See {IERC20-approve}. */ function approve(address _spender, uint256 _amount) external virtual override returns (bool) { _approve(msg.sender, _spender, _amount); return true; } /** * @dev See {ERC20-increaseAllowance}. */ function increaseAllowance(address _spender, uint256 _addedValue) external virtual returns (bool) { _approve(msg.sender, _spender, _allowances[msg.sender][_spender] + (_addedValue)); return true; } /** * @dev See {ERC20-decreaseAllowance}. */ function decreaseAllowance(address _spender, uint256 _subtractedValue) external virtual returns (bool) { _approve(msg.sender, _spender, _allowances[msg.sender][_spender] - _subtractedValue); return true; } /** * @dev See {IToken-setName}. */ function setName(string calldata _name) external override onlyOwner { require(keccak256(abi.encode(_name)) != keccak256(abi.encode("")), "invalid argument - empty string"); _tokenName = _name; emit UpdatedTokenInformation(_tokenName, _tokenSymbol, _tokenDecimals, _TOKEN_VERSION, _tokenOnchainID); } /** * @dev See {IToken-setSymbol}. */ function setSymbol(string calldata _symbol) external override onlyOwner { require(keccak256(abi.encode(_symbol)) != keccak256(abi.encode("")), "invalid argument - empty string"); _tokenSymbol = _symbol; emit UpdatedTokenInformation(_tokenName, _tokenSymbol, _tokenDecimals, _TOKEN_VERSION, _tokenOnchainID); } /** * @dev See {IToken-setOnchainID}. * if _onchainID is set at zero address it means no ONCHAINID is bound to this token */ function setOnchainID(address _onchainID) external override onlyOwner { _tokenOnchainID = _onchainID; emit UpdatedTokenInformation(_tokenName, _tokenSymbol, _tokenDecimals, _TOKEN_VERSION, _tokenOnchainID); } /** * @dev See {IToken-pause}. */ function pause() external override onlyAgent whenNotPaused { _tokenPaused = true; emit Paused(msg.sender); } /** * @dev See {IToken-unpause}. */ function unpause() external override onlyAgent whenPaused { _tokenPaused = false; emit Unpaused(msg.sender); } /** * @dev See {IToken-batchTransfer}. */ function batchTransfer(address[] calldata _toList, uint256[] calldata _amounts) external override { for (uint256 i = 0; i < _toList.length; i++) { transfer(_toList[i], _amounts[i]); } } /** * @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 _amount The number of tokens to transfer * @return `true` if successful and revert if unsuccessful */ function transferFrom( address _from, address _to, uint256 _amount ) external override whenNotPaused returns (bool) { require(!_frozen[_to] && !_frozen[_from], "wallet is frozen"); require(_amount <= balanceOf(_from) - (_frozenTokens[_from]), "Insufficient Balance"); if (_tokenIdentityRegistry.isVerified(_to) && _tokenCompliance.canTransfer(_from, _to, _amount)) { _approve(_from, msg.sender, _allowances[_from][msg.sender] - (_amount)); _transfer(_from, _to, _amount); _tokenCompliance.transferred(_from, _to, _amount); return true; } revert("Transfer not possible"); } /** * @dev See {IToken-batchForcedTransfer}. */ function batchForcedTransfer( address[] calldata _fromList, address[] calldata _toList, uint256[] calldata _amounts ) external override { for (uint256 i = 0; i < _fromList.length; i++) { forcedTransfer(_fromList[i], _toList[i], _amounts[i]); } } /** * @dev See {IToken-batchMint}. */ function batchMint(address[] calldata _toList, uint256[] calldata _amounts) external override { for (uint256 i = 0; i < _toList.length; i++) { mint(_toList[i], _amounts[i]); } } /** * @dev See {IToken-batchBurn}. */ function batchBurn(address[] calldata _userAddresses, uint256[] calldata _amounts) external override { for (uint256 i = 0; i < _userAddresses.length; i++) { burn(_userAddresses[i], _amounts[i]); } } /** * @dev See {IToken-batchSetAddressFrozen}. */ function batchSetAddressFrozen(address[] calldata _userAddresses, bool[] calldata _freeze) external override { for (uint256 i = 0; i < _userAddresses.length; i++) { setAddressFrozen(_userAddresses[i], _freeze[i]); } } /** * @dev See {IToken-batchFreezePartialTokens}. */ function batchFreezePartialTokens(address[] calldata _userAddresses, uint256[] calldata _amounts) external override { for (uint256 i = 0; i < _userAddresses.length; i++) { freezePartialTokens(_userAddresses[i], _amounts[i]); } } /** * @dev See {IToken-batchUnfreezePartialTokens}. */ function batchUnfreezePartialTokens(address[] calldata _userAddresses, uint256[] calldata _amounts) external override { for (uint256 i = 0; i < _userAddresses.length; i++) { unfreezePartialTokens(_userAddresses[i], _amounts[i]); } } /** * @dev See {IToken-recoveryAddress}. */ function recoveryAddress( address _lostWallet, address _newWallet, address _investorOnchainID ) external override onlyAgent returns (bool) { require(balanceOf(_lostWallet) != 0, "no tokens to recover"); IIdentity _onchainID = IIdentity(_investorOnchainID); bytes32 _key = keccak256(abi.encode(_newWallet)); if (_onchainID.keyHasPurpose(_key, 1)) { uint256 investorTokens = balanceOf(_lostWallet); uint256 frozenTokens = _frozenTokens[_lostWallet]; _tokenIdentityRegistry.registerIdentity(_newWallet, _onchainID, _tokenIdentityRegistry.investorCountry (_lostWallet)); forcedTransfer(_lostWallet, _newWallet, investorTokens); if (frozenTokens > 0) { freezePartialTokens(_newWallet, frozenTokens); } if (_frozen[_lostWallet] == true) { setAddressFrozen(_newWallet, true); } _tokenIdentityRegistry.deleteIdentity(_lostWallet); emit RecoverySuccess(_lostWallet, _newWallet, _investorOnchainID); return true; } revert("Recovery not possible"); } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() external view override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-allowance}. */ function allowance(address _owner, address _spender) external view virtual override returns (uint256) { return _allowances[_owner][_spender]; } /** * @dev See {IToken-identityRegistry}. */ function identityRegistry() external view override returns (IIdentityRegistry) { return _tokenIdentityRegistry; } /** * @dev See {IToken-compliance}. */ function compliance() external view override returns (IModularCompliance) { return _tokenCompliance; } /** * @dev See {IToken-paused}. */ function paused() external view override returns (bool) { return _tokenPaused; } /** * @dev See {IToken-isFrozen}. */ function isFrozen(address _userAddress) external view override returns (bool) { return _frozen[_userAddress]; } /** * @dev See {IToken-getFrozenTokens}. */ function getFrozenTokens(address _userAddress) external view override returns (uint256) { return _frozenTokens[_userAddress]; } /** * @dev See {IToken-decimals}. */ function decimals() external view override returns (uint8) { return _tokenDecimals; } /** * @dev See {IToken-name}. */ function name() external view override returns (string memory) { return _tokenName; } /** * @dev See {IToken-onchainID}. */ function onchainID() external view override returns (address) { return _tokenOnchainID; } /** * @dev See {IToken-symbol}. */ function symbol() external view override returns (string memory) { return _tokenSymbol; } /** * @dev See {IToken-version}. */ function version() external pure override returns (string memory) { return _TOKEN_VERSION; } /** * @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 _amount The number of tokens to transfer * @return `true` if successful and revert if unsuccessful */ function transfer(address _to, uint256 _amount) public override whenNotPaused returns (bool) { require(!_frozen[_to] && !_frozen[msg.sender], "wallet is frozen"); require(_amount <= balanceOf(msg.sender) - (_frozenTokens[msg.sender]), "Insufficient Balance"); if (_tokenIdentityRegistry.isVerified(_to) && _tokenCompliance.canTransfer(msg.sender, _to, _amount)) { _transfer(msg.sender, _to, _amount); _tokenCompliance.transferred(msg.sender, _to, _amount); return true; } revert("Transfer not possible"); } /** * @dev See {IToken-forcedTransfer}. */ function forcedTransfer( address _from, address _to, uint256 _amount ) public override onlyAgent returns (bool) { require(balanceOf(_from) >= _amount, "sender balance too low"); uint256 freeBalance = balanceOf(_from) - (_frozenTokens[_from]); if (_amount > freeBalance) { uint256 tokensToUnfreeze = _amount - (freeBalance); _frozenTokens[_from] = _frozenTokens[_from] - (tokensToUnfreeze); emit TokensUnfrozen(_from, tokensToUnfreeze); } if (_tokenIdentityRegistry.isVerified(_to)) { _transfer(_from, _to, _amount); _tokenCompliance.transferred(_from, _to, _amount); return true; } revert("Transfer not possible"); } /** * @dev See {IToken-mint}. */ function mint(address _to, uint256 _amount) public override onlyAgent { require(_tokenIdentityRegistry.isVerified(_to), "Identity is not verified."); require(_tokenCompliance.canTransfer(address(0), _to, _amount), "Compliance not followed"); _mint(_to, _amount); _tokenCompliance.created(_to, _amount); } /** * @dev See {IToken-burn}. */ function burn(address _userAddress, uint256 _amount) public override onlyAgent { require(balanceOf(_userAddress) >= _amount, "cannot burn more than balance"); uint256 freeBalance = balanceOf(_userAddress) - _frozenTokens[_userAddress]; if (_amount > freeBalance) { uint256 tokensToUnfreeze = _amount - (freeBalance); _frozenTokens[_userAddress] = _frozenTokens[_userAddress] - (tokensToUnfreeze); emit TokensUnfrozen(_userAddress, tokensToUnfreeze); } _burn(_userAddress, _amount); _tokenCompliance.destroyed(_userAddress, _amount); } /** * @dev See {IToken-setAddressFrozen}. */ function setAddressFrozen(address _userAddress, bool _freeze) public override onlyAgent { _frozen[_userAddress] = _freeze; emit AddressFrozen(_userAddress, _freeze, msg.sender); } /** * @dev See {IToken-freezePartialTokens}. */ function freezePartialTokens(address _userAddress, uint256 _amount) public override onlyAgent { uint256 balance = balanceOf(_userAddress); require(balance >= _frozenTokens[_userAddress] + _amount, "Amount exceeds available balance"); _frozenTokens[_userAddress] = _frozenTokens[_userAddress] + (_amount); emit TokensFrozen(_userAddress, _amount); } /** * @dev See {IToken-unfreezePartialTokens}. */ function unfreezePartialTokens(address _userAddress, uint256 _amount) public override onlyAgent { require(_frozenTokens[_userAddress] >= _amount, "Amount should be less than or equal to frozen tokens"); _frozenTokens[_userAddress] = _frozenTokens[_userAddress] - (_amount); emit TokensUnfrozen(_userAddress, _amount); } /** * @dev See {IToken-setIdentityRegistry}. */ function setIdentityRegistry(address _identityRegistry) public override onlyOwner { _tokenIdentityRegistry = IIdentityRegistry(_identityRegistry); emit IdentityRegistryAdded(_identityRegistry); } /** * @dev See {IToken-setCompliance}. */ function setCompliance(address _compliance) public override onlyOwner { if (address(_tokenCompliance) != address(0)) { _tokenCompliance.unbindToken(address(this)); } _tokenCompliance = IModularCompliance(_compliance); _tokenCompliance.bindToken(address(this)); emit ComplianceAdded(_compliance); } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address _userAddress) public view override returns (uint256) { return _balances[_userAddress]; } /** * @dev See {ERC20-_transfer}. */ function _transfer( address _from, address _to, uint256 _amount ) internal virtual { require(_from != address(0), "ERC20: transfer from the zero address"); require(_to != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(_from, _to, _amount); _balances[_from] = _balances[_from] - _amount; _balances[_to] = _balances[_to] + _amount; emit Transfer(_from, _to, _amount); } /** * @dev See {ERC20-_mint}. */ function _mint(address _userAddress, uint256 _amount) internal virtual { require(_userAddress != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), _userAddress, _amount); _totalSupply = _totalSupply + _amount; _balances[_userAddress] = _balances[_userAddress] + _amount; emit Transfer(address(0), _userAddress, _amount); } /** * @dev See {ERC20-_burn}. */ function _burn(address _userAddress, uint256 _amount) internal virtual { require(_userAddress != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(_userAddress, address(0), _amount); _balances[_userAddress] = _balances[_userAddress] - _amount; _totalSupply = _totalSupply - _amount; emit Transfer(_userAddress, address(0), _amount); } /** * @dev See {ERC20-_approve}. */ function _approve( address _owner, address _spender, uint256 _amount ) internal virtual { require(_owner != address(0), "ERC20: approve from the zero address"); require(_spender != address(0), "ERC20: approve to the zero address"); _allowances[_owner][_spender] = _amount; emit Approval(_owner, _spender, _amount); } /** * @dev See {ERC20-_beforeTokenTransfer}. */ // solhint-disable-next-line no-empty-blocks function _beforeTokenTransfer(address _from, address _to, uint256 _amount) internal virtual {} }