pragma solidity ^0.4.24; /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting '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; } c = a * b; assert(c / a == b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return a / b; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256 c) { c = a + b; assert(c >= a); return c; } } /** * @title ERC20Basic * @dev Simpler version of ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/179 */ contract ERC20Basic { function totalSupply() public view returns (uint256); function balanceOf(address who) public view returns (uint256); function transfer(address to, uint256 value) public returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); } /** * @title ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/20 */ contract ERC20 is ERC20Basic { function allowance(address owner, address spender) public view returns (uint256); function transferFrom(address from, address to, uint256 value) public returns (bool); function approve(address spender, uint256 value) public returns (bool); event Approval( address indexed owner, address indexed spender, uint256 value ); } /** * @title Interface for the polymath ticker registry contract */ contract ITickerRegistry { /** * @notice Check the validity of the symbol * @param _symbol token symbol * @param _owner address of the owner * @param _tokenName Name of the token * @return bool */ function checkValidity(string _symbol, address _owner, string _tokenName) public returns(bool); /** * @notice Returns the owner and timestamp for a given symbol * @param _symbol symbol */ function getDetails(string _symbol) public view returns (address, uint256, string, bytes32, bool); /** * @notice Check the symbol is reserved or not * @param _symbol Symbol of the token * @return bool */ function isReserved(string _symbol, address _owner, string _tokenName, bytes32 _swarmHash) public returns(bool); } /** * @title Utility contract for reusable code */ contract Util { /** * @notice changes a string to upper case * @param _base string to change */ function upper(string _base) internal pure returns (string) { bytes memory _baseBytes = bytes(_base); for (uint i = 0; i < _baseBytes.length; i++) { bytes1 b1 = _baseBytes[i]; if (b1 >= 0x61 && b1 <= 0x7A) { b1 = bytes1(uint8(b1)-32); } _baseBytes[i] = b1; } return string(_baseBytes); } } /** * @title Utility contract to allow pausing and unpausing of certain functions */ contract Pausable { event Pause(uint256 _timestammp); event Unpause(uint256 _timestamp); bool public paused = false; /** * @notice Modifier to make a function callable only when the contract is not paused. */ modifier whenNotPaused() { require(!paused); _; } /** * @notice Modifier to make a function callable only when the contract is paused. */ modifier whenPaused() { require(paused); _; } /** * @notice called by the owner to pause, triggers stopped state */ function _pause() internal { require(!paused); paused = true; emit Pause(now); } /** * @notice called by the owner to unpause, returns to normal state */ function _unpause() internal { require(paused); paused = false; emit Unpause(now); } } /** * @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 public owner; event OwnershipRenounced(address indexed previousOwner); event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to relinquish control of the contract. */ function renounceOwnership() public onlyOwner { emit OwnershipRenounced(owner); 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 Utility contract to allow owner to retreive any ERC20 sent to the contract */ contract ReclaimTokens is Ownable { /** * @notice Reclaim all ERC20Basic compatible tokens * @param _tokenContract The address of the token contract */ function reclaimERC20(address _tokenContract) external onlyOwner { require(_tokenContract != address(0)); ERC20Basic token = ERC20Basic(_tokenContract); uint256 balance = token.balanceOf(address(this)); require(token.transfer(owner, balance)); } } /** * @title Core functionality for registry upgradability */ contract PolymathRegistry is ReclaimTokens { mapping (bytes32 => address) public storedAddresses; event LogChangeAddress(string _nameKey, address indexed _oldAddress, address indexed _newAddress); /** * @notice Get the contract address * @param _nameKey is the key for the contract address mapping * @return address */ function getAddress(string _nameKey) view public returns(address) { bytes32 key = keccak256(bytes(_nameKey)); require(storedAddresses[key] != address(0), "Invalid address key"); return storedAddresses[key]; } /** * @notice change the contract address * @param _nameKey is the key for the contract address mapping * @param _newAddress is the new contract address */ function changeAddress(string _nameKey, address _newAddress) public onlyOwner { bytes32 key = keccak256(bytes(_nameKey)); emit LogChangeAddress(_nameKey, storedAddresses[key], _newAddress); storedAddresses[key] = _newAddress; } } contract RegistryUpdater is Ownable { address public polymathRegistry; address public moduleRegistry; address public securityTokenRegistry; address public tickerRegistry; address public polyToken; constructor (address _polymathRegistry) public { require(_polymathRegistry != address(0)); polymathRegistry = _polymathRegistry; } function updateFromRegistry() onlyOwner public { moduleRegistry = PolymathRegistry(polymathRegistry).getAddress("ModuleRegistry"); securityTokenRegistry = PolymathRegistry(polymathRegistry).getAddress("SecurityTokenRegistry"); tickerRegistry = PolymathRegistry(polymathRegistry).getAddress("TickerRegistry"); polyToken = PolymathRegistry(polymathRegistry).getAddress("PolyToken"); } } /** * @title Registry contract for issuers to reserve their security token symbols * @notice Allows issuers to reserve their token symbols ahead of actually generating their security token. * @dev SecurityTokenRegistry would reference this contract and ensure that a token symbol exists here and only its owner can deploy the token with that symbol. */ contract TickerRegistry is ITickerRegistry, Util, Pausable, RegistryUpdater, ReclaimTokens { using SafeMath for uint256; // constant variable to check the validity to use the symbol // For now it's value is 15 days; uint256 public expiryLimit = 15 * 1 days; // Details of the symbol that get registered with the polymath platform struct SymbolDetails { address owner; uint256 timestamp; string tokenName; bytes32 swarmHash; bool status; } // Storage of symbols correspond to their details. mapping(string => SymbolDetails) registeredSymbols; // Emit after the symbol registration event LogRegisterTicker(address indexed _owner, string _symbol, string _name, bytes32 _swarmHash, uint256 indexed _timestamp); // Emit when the token symbol expiry get changed event LogChangeExpiryLimit(uint256 _oldExpiry, uint256 _newExpiry); // Registration fee in POLY base 18 decimals uint256 public registrationFee; // Emit when changePolyRegisterationFee is called event LogChangePolyRegisterationFee(uint256 _oldFee, uint256 _newFee); constructor (address _polymathRegistry, uint256 _registrationFee) public RegistryUpdater(_polymathRegistry) { registrationFee = _registrationFee; } /** * @notice Register the token symbol for its particular owner * @notice Once the token symbol is registered to its owner then no other issuer can claim * @notice its ownership. If the symbol expires and its issuer hasn't used it, then someone else can take it. * @param _symbol token symbol * @param _tokenName Name of the token * @param _owner Address of the owner of the token * @param _swarmHash Off-chain details of the issuer and token */ function registerTicker(address _owner, string _symbol, string _tokenName, bytes32 _swarmHash) public whenNotPaused { require(_owner != address(0), "Owner should not be 0x"); require(bytes(_symbol).length > 0 && bytes(_symbol).length <= 10, "Ticker length should always between 0 & 10"); if(registrationFee > 0) require(ERC20(polyToken).transferFrom(msg.sender, this, registrationFee), "Failed transferFrom because of sufficent Allowance is not provided"); string memory symbol = upper(_symbol); require(expiryCheck(symbol), "Ticker is already reserved"); registeredSymbols[symbol] = SymbolDetails(_owner, now, _tokenName, _swarmHash, false); emit LogRegisterTicker (_owner, symbol, _tokenName, _swarmHash, now); } /** * @notice Change the expiry time for the token symbol * @param _newExpiry new time period for token symbol expiry */ function changeExpiryLimit(uint256 _newExpiry) public onlyOwner { require(_newExpiry >= 1 days, "Expiry should greater than or equal to 1 day"); uint256 _oldExpiry = expiryLimit; expiryLimit = _newExpiry; emit LogChangeExpiryLimit(_oldExpiry, _newExpiry); } /** * @notice Check the validity of the symbol * @param _symbol token symbol * @param _owner address of the owner * @param _tokenName Name of the token * @return bool */ function checkValidity(string _symbol, address _owner, string _tokenName) public returns(bool) { string memory symbol = upper(_symbol); require(msg.sender == securityTokenRegistry, "msg.sender should be SecurityTokenRegistry contract"); require(registeredSymbols[symbol].status != true, "Symbol status should not equal to true"); require(registeredSymbols[symbol].owner == _owner, "Owner of the symbol should matched with the requested issuer address"); require(registeredSymbols[symbol].timestamp.add(expiryLimit) >= now, "Ticker should not be expired"); registeredSymbols[symbol].tokenName = _tokenName; registeredSymbols[symbol].status = true; return true; } /** * @notice Check the symbol is reserved or not * @param _symbol Symbol of the token * @param _owner Owner of the token * @param _tokenName Name of the token * @param _swarmHash off-chain hash * @return bool */ function isReserved(string _symbol, address _owner, string _tokenName, bytes32 _swarmHash) public returns(bool) { string memory symbol = upper(_symbol); require(msg.sender == securityTokenRegistry, "msg.sender should be SecurityTokenRegistry contract"); if (registeredSymbols[symbol].owner == _owner && !expiryCheck(_symbol)) { registeredSymbols[symbol].status = true; return false; } else if (registeredSymbols[symbol].owner == address(0) || expiryCheck(symbol)) { registeredSymbols[symbol] = SymbolDetails(_owner, now, _tokenName, _swarmHash, true); emit LogRegisterTicker (_owner, symbol, _tokenName, _swarmHash, now); return false; } else return true; } /** * @notice Returns the owner and timestamp for a given symbol * @param _symbol symbol * @return address * @return uint256 * @return string * @return bytes32 * @return bool */ function getDetails(string _symbol) public view returns (address, uint256, string, bytes32, bool) { string memory symbol = upper(_symbol); if (registeredSymbols[symbol].status == true||registeredSymbols[symbol].timestamp.add(expiryLimit) > now) { return ( registeredSymbols[symbol].owner, registeredSymbols[symbol].timestamp, registeredSymbols[symbol].tokenName, registeredSymbols[symbol].swarmHash, registeredSymbols[symbol].status ); }else return (address(0), uint256(0), "", bytes32(0), false); } /** * @notice To re-initialize the token symbol details if symbol validity expires * @param _symbol token symbol * @return bool */ function expiryCheck(string _symbol) internal returns(bool) { if (registeredSymbols[_symbol].owner != address(0)) { if (now > registeredSymbols[_symbol].timestamp.add(expiryLimit) && registeredSymbols[_symbol].status != true) { registeredSymbols[_symbol] = SymbolDetails(address(0), uint256(0), "", bytes32(0), false); return true; }else return false; } return true; } /** * @notice set the ticker registration fee in POLY tokens * @param _registrationFee registration fee in POLY tokens (base 18 decimals) */ function changePolyRegisterationFee(uint256 _registrationFee) public onlyOwner { require(registrationFee != _registrationFee); emit LogChangePolyRegisterationFee(registrationFee, _registrationFee); registrationFee = _registrationFee; } /** * @notice pause registration function */ function unpause() public onlyOwner { _unpause(); } /** * @notice unpause registration function */ function pause() public onlyOwner { _pause(); } }