diff --git a/contracts/BrandedToken.sol b/contracts/BrandedToken.sol index ba2ad157..d5659c22 100644 --- a/contracts/BrandedToken.sol +++ b/contracts/BrandedToken.sol @@ -1,3 +1,4 @@ +/* solhint-disable-next-line compiler-fixed */ pragma solidity ^0.4.17; // Copyright 2017 OpenST Ltd. @@ -27,6 +28,7 @@ import "./SafeMath.sol"; import "./EIP20Token.sol"; import "./UtilityTokenAbstract.sol"; + /// @dev Branded Token is an EIP20 token minted by staking Simple Token /// on Ethereum mainnet. Branded tokens are designed to be used /// within a (decentralised) application and support: @@ -38,59 +40,65 @@ import "./UtilityTokenAbstract.sol"; /// their equivalent part of the Simple Token stake /// on Ethereum (before v1.0) contract BrandedToken is EIP20Token, UtilityTokenAbstract { - using SafeMath for uint256; - - /* - * Public functions - */ - function BrandedToken( - address _openSTProtocol, - bytes32 _uuid, - string _symbol, - string _name, - uint8 _decimals) - EIP20Token(_symbol, _name, _decimals) - UtilityTokenAbstract(_openSTProtocol, _uuid) - public - { + using SafeMath for uint256; - } + /* + * Public functions + */ + function BrandedToken( + bytes32 _uuid, + string _symbol, + string _name, + uint8 _decimals, + uint256 _chainIdValue, + uint256 _chainIdUtility, + uint256 _conversionRate) + public + EIP20Token(_symbol, _name, _decimals) + UtilityTokenAbstract( + _uuid, + _symbol, + _name, + _chainIdValue, + _chainIdUtility, + _conversionRate) + { } - function claim( - address _beneficiary) - public - returns (bool /* success */) - { - uint256 amount = claimInternal(_beneficiary); + function claim( + address _beneficiary) + public + returns (bool /* success */) + { + uint256 amount = claimInternal(_beneficiary); - return claimEIP20(_beneficiary, amount); - } + return claimEIP20(_beneficiary, amount); + } - function mint( - address _beneficiary, - uint256 _amount) - public - onlyProtocol - returns (bool /* success */) - { - mintEIP20(_amount); + function mint( + address _beneficiary, + uint256 _amount) + public + onlyProtocol + returns (bool /* success */) + { + mintEIP20(_amount); - return mintInternal(_beneficiary, _amount); - } + return mintInternal(_beneficiary, _amount); + } - function burn( - address _burner, - uint256 _amount) - public - onlyProtocol - payable - returns (bool /* success */) - { - // force non-payable, as only ST' handles in base tokens - require(msg.value == 0); + function burn( + address _burner, + uint256 _amount) + public + onlyProtocol + payable + returns (bool /* success */) + { + // force non-payable, as only ST' handles in base tokens + require(msg.value == 0); - burnEIP20(_amount); + burnEIP20(_amount); - return burnInternal(_burner, _amount); - } + return burnInternal(_burner, _amount); + } } \ No newline at end of file diff --git a/contracts/OpenSTUtility.sol b/contracts/OpenSTUtility.sol index 8d02d1d7..de38c0ce 100644 --- a/contracts/OpenSTUtility.sol +++ b/contracts/OpenSTUtility.sol @@ -1,3 +1,4 @@ +/* solhint-disable-next-line compiler-fixed */ pragma solidity ^0.4.17; // Copyright 2017 OpenST Ltd. @@ -36,396 +37,262 @@ import "./ProtocolVersioned.sol"; /// @title OpenST Utility contract OpenSTUtility is Hasher, OpsManaged { - using SafeMath for uint256; - - /* - * Structures - */ - struct RegisteredToken { - UtilityTokenInterface token; - address registrar; - } - - - struct Mint { - bytes32 uuid; - address staker; - address beneficiary; - uint256 amount; - uint256 expirationHeight; - } - - struct Redemption { - bytes32 uuid; - address redeemer; - uint256 amountUT; - uint256 unlockHeight; - } - /* - * Events - */ - event ProposedBrandedToken(address indexed _requester, address indexed _token, - bytes32 _uuid, string _symbol, string _name, uint256 _conversionRate); - event RegisteredBrandedToken(address indexed _registrar, address indexed _token, - bytes32 _uuid, string _symbol, string _name, uint256 _conversionRate, address _requester); + using SafeMath for uint256; + + /* + * Structures + */ + struct RegisteredToken { + UtilityTokenInterface token; + address registrar; + } + + struct Mint { + bytes32 uuid; + address staker; + address beneficiary; + uint256 amount; + uint256 expirationHeight; + } + + struct Redemption { + bytes32 uuid; + address redeemer; + uint256 amountUT; + uint256 unlockHeight; + } + + /* + * Events + */ + event ProposedBrandedToken(address indexed _requester, address indexed _token, + bytes32 _uuid, string _symbol, string _name, uint256 _conversionRate); + + event RegisteredBrandedToken(address indexed _registrar, address indexed _token, + bytes32 _uuid, string _symbol, string _name, uint256 _conversionRate, address _requester); + event StakingIntentConfirmed(bytes32 indexed _uuid, bytes32 indexed _stakingIntentHash, - address _staker, address _beneficiary, uint256 _amountST, uint256 _amountUT, uint256 _expirationHeight); + address _staker, address _beneficiary, uint256 _amountST, uint256 _amountUT, uint256 _expirationHeight); + event ProcessedMint(bytes32 indexed _uuid, bytes32 indexed _stakingIntentHash, address _token, - address _staker, address _beneficiary, uint256 _amount); - event RevertedMint(bytes32 indexed _uuid, bytes32 indexed _stakingIntentHash, address _staker, - address _beneficiary, uint256 _amountUT); - event RedemptionIntentDeclared(bytes32 indexed _uuid, bytes32 indexed _redemptionIntentHash, - address _token, address _redeemer, uint256 _nonce, uint256 _amount, uint256 _unlockHeight, - uint256 _chainIdValue); - event ProcessedRedemption(bytes32 indexed _uuid, bytes32 indexed _redemptionIntentHash, address _token, - address _redeemer, uint256 _amount); - event RevertedRedemption(bytes32 indexed _uuid, bytes32 indexed _redemptionIntentHash, - address _redeemer, uint256 _amountUT); - - /* - * Constants - */ - string public constant STPRIME_SYMBOL = "STP"; + address _staker, address _beneficiary, uint256 _amount); + + event RevertedMint(bytes32 indexed _uuid, bytes32 indexed _stakingIntentHash, address _staker, + address _beneficiary, uint256 _amountUT); + + event RedemptionIntentDeclared(bytes32 indexed _uuid, bytes32 indexed _redemptionIntentHash, + address _token, address _redeemer, uint256 _nonce, uint256 _amount, uint256 _unlockHeight, + uint256 _chainIdValue); + + event ProcessedRedemption(bytes32 indexed _uuid, bytes32 indexed _redemptionIntentHash, address _token, + address _redeemer, uint256 _amount); + + event RevertedRedemption(bytes32 indexed _uuid, bytes32 indexed _redemptionIntentHash, + address _redeemer, uint256 _amountUT); + + /* + * Constants + */ + string public constant STPRIME_SYMBOL = "STP"; string public constant STPRIME_NAME = "SimpleTokenPrime"; uint256 public constant STPRIME_CONVERSION_RATE = 1; uint8 public constant TOKEN_DECIMALS = 18; uint256 public constant DECIMALSFACTOR = 10**uint256(TOKEN_DECIMALS); - // ~2 weeks, assuming ~15s per block - uint256 public constant BLOCKS_TO_WAIT_LONG = 80667; - // ~1hour, assuming ~15s per block - uint256 public constant BLOCKS_TO_WAIT_SHORT = 240; - - /* - * Storage - */ - /// store address of Simple Token Prime - address public simpleTokenPrime; - bytes32 public uuidSTPrime; - /// restrict (for now) to a single value chain - uint256 public chainIdValue; - /// chainId of the current utility chain - uint256 public chainIdUtility; - address public registrar; - /// registered branded tokens - mapping(bytes32 /* uuid */ => RegisteredToken) registeredTokens; - /// name reservation is first come, first serve - mapping(bytes32 /* hashName */ => address /* requester */) public nameReservation; - /// symbol reserved for unique API routes - /// and resolves to address - mapping(bytes32 /* hashSymbol */ => address /* UtilityToken */) public symbolRoute; - /// nonce makes the staking process atomic across the two-phased process - /// and protects against replay attack on (un)staking proofs during the process. - /// On the value chain nonces need to strictly increase by one; on the utility - /// chain the nonce need to strictly increase (as one value chain can have multiple - /// utility chains) - mapping(address /* (un)staker */ => uint256) nonces; - /// store the ongoing mints and redemptions - mapping(bytes32 /* stakingIntentHash */ => Mint) mints; - mapping(bytes32 /* redemptionIntentHash*/ => Redemption) redemptions; - - /* - * Modifiers - */ - modifier onlyRegistrar() { - // for now keep unique registrar - require(msg.sender == registrar); - _; - } - - /* - * Public functions - */ - function OpenSTUtility( - uint256 _chainIdValue, - uint256 _chainIdUtility, - address _registrar) - public - OpsManaged() - { - require(_chainIdValue != 0); - require(_chainIdUtility != 0); - require(_registrar != address(0)); - - chainIdValue = _chainIdValue; - chainIdUtility = _chainIdUtility; - registrar = _registrar; - - uuidSTPrime = hashUuid( - STPRIME_SYMBOL, - STPRIME_NAME, - _chainIdValue, - _chainIdUtility, - address(this), - STPRIME_CONVERSION_RATE); - simpleTokenPrime = new STPrime( - address(this), - uuidSTPrime); - - registeredTokens[uuidSTPrime] = RegisteredToken({ - token: UtilityTokenInterface(simpleTokenPrime), - registrar: registrar - }); - - // lock name and symbol route for ST' - bytes32 hashName = keccak256(STPRIME_NAME); - nameReservation[hashName] = registrar; - bytes32 hashSymbol = keccak256(STPRIME_SYMBOL); - symbolRoute[hashSymbol] = simpleTokenPrime; - - // @dev read STPrime address and uuid from contract - } - - /// @dev Congratulations on looking under the hood and obtaining ST' to call proposeBrandedToken; - /// however, OpenSTFoundation is not yet actively listening to these events - /// because to automate it we will build a web UI where you can authenticate - /// with your msg.sender = _requester key; - /// until that time please drop us a line on partners(at)simpletoken.org and we can - /// work with you to register for your branded token - function proposeBrandedToken( - string _symbol, - string _name, - uint256 _conversionRate) - public - returns (bytes32) - { - require(bytes(_symbol).length > 0); - require(bytes(_name).length > 0); - require(_conversionRate > 0); - - bytes32 hashSymbol = keccak256(_symbol); - bytes32 hashName = keccak256(_name); - require(checkAvailability(hashSymbol, hashName, msg.sender)); - - bytes32 btUuid = hashUuid( - _symbol, - _name, - chainIdValue, - chainIdUtility, - address(this), - _conversionRate); - - BrandedToken proposedBT = new BrandedToken( - address(this), - btUuid, - _symbol, - _name, - TOKEN_DECIMALS); - // reserve name for sender under opt-in discretion of - // registrar - nameReservation[hashName] = msg.sender; - - ProposedBrandedToken(msg.sender, address(proposedBT), btUuid, - _symbol, _name, _conversionRate); - - return btUuid; - } - - - function checkAvailability( - bytes32 _hashSymbol, - bytes32 _hashName, - address _requester) - public - view - returns (bool /* success */) - { - // a reserved symbol means the route is already chosen - address token = symbolRoute[_hashSymbol]; - if (token != address(0)) return false; - - // a name can have been reserved during the Simple Token sale - // in which case must come from same address - // otherwise proposals are first come, first serve - // under opt-in discretion of registrar - address requester = nameReservation[_hashName]; - if ((requester == address(0) || - requester == _requester)) { - return true; - } - return false; - } - - /* - * Registrar functions - */ - /// @dev for v0.9.1 tracking Ethereum mainnet on the utility chain - /// is not a required feature yet, so the core is simplified - /// to uint256 valueChainId as storage on construction - // function addCore( - // CoreInterface _core) - // public - // onlyRegistrar - // returns (bool /* success */) - // { - // require(address(_core) != address(0)); - // // core constructed with same registrar - // require(registrar == _core.registrar()); - // // on utility chain core only tracks a remote value chain - // uint256 coreChainIdValue = _core.chainIdRemote(); - // require(chainIdUtility != 0); - // // cannot overwrite core for given chainId - // require(cores[coreChainIdValue] == address(0)); - - // cores[coreChainIdValue] = _core; - - // return true; - // } - - function registerBrandedToken( - string _symbol, - string _name, - uint256 _conversionRate, - address _requester, - UtilityTokenInterface _brandedToken, - bytes32 _checkUuid) - public - onlyRegistrar - returns (bytes32 registeredUuid) - { - require(bytes(_symbol).length > 0); - require(bytes(_name).length > 0); - require(_conversionRate > 0); - - bytes32 hashSymbol = keccak256(_symbol); - bytes32 hashName = keccak256(_name); - require(checkAvailability(hashSymbol, hashName, _requester)); - - registeredUuid = hashUuid( - _symbol, - _name, - chainIdValue, - chainIdUtility, - address(this), - _conversionRate); - - require(registeredUuid == _checkUuid); - require(_brandedToken.uuid() == _checkUuid); - - assert(address(registeredTokens[registeredUuid].token) == address(0)); - - registeredTokens[registeredUuid] = RegisteredToken({ - token: _brandedToken, - registrar: registrar - }); - - // register name to registrar - nameReservation[hashName] = registrar; - // register symbol - symbolRoute[hashSymbol] = _brandedToken; - - RegisteredBrandedToken(registrar, _brandedToken, registeredUuid, _symbol, _name, - _conversionRate, _requester); - - return registeredUuid; - } - - function confirmStakingIntent( - bytes32 _uuid, - address _staker, - uint256 _stakerNonce, - address _beneficiary, - uint256 _amountST, - uint256 _amountUT, - uint256 _stakingUnlockHeight, - bytes32 _stakingIntentHash) - external - onlyRegistrar - returns (uint256 expirationHeight) - { - require(address(registeredTokens[_uuid].token) != address(0)); - - require(nonces[_staker] < _stakerNonce); - require(_amountST > 0); - require(_amountUT > 0); - // stakingUnlockheight needs to be checked against the core that tracks the value chain - require(_stakingUnlockHeight > 0); - require(_stakingIntentHash != ""); - - expirationHeight = block.number + blocksToWaitShort(); - nonces[_staker] = _stakerNonce; - - bytes32 stakingIntentHash = hashStakingIntent( - _uuid, - _staker, - _stakerNonce, - _beneficiary, - _amountST, - _amountUT, - _stakingUnlockHeight - ); - - require(stakingIntentHash == _stakingIntentHash); - - mints[stakingIntentHash] = Mint({ - uuid: _uuid, - staker: _staker, - beneficiary: _beneficiary, - amount: _amountUT, - expirationHeight: expirationHeight - }); - - StakingIntentConfirmed(_uuid, stakingIntentHash, _staker, _beneficiary, _amountST, - _amountUT, expirationHeight); - - return expirationHeight; + // ~2 weeks, assuming ~15s per block + uint256 public constant BLOCKS_TO_WAIT_LONG = 80667; + // ~1hour, assuming ~15s per block + uint256 public constant BLOCKS_TO_WAIT_SHORT = 240; + + /* + * Storage + */ + /// store address of Simple Token Prime + address public simpleTokenPrime; + bytes32 public uuidSTPrime; + /// restrict (for now) to a single value chain + uint256 public chainIdValue; + /// chainId of the current utility chain + uint256 public chainIdUtility; + address public registrar; + /// registered branded tokens + mapping(bytes32 /* uuid */ => RegisteredToken) internal registeredTokens; + /// name reservation is first come, first serve + mapping(bytes32 /* hashName */ => address /* requester */) public nameReservation; + /// symbol reserved for unique API routes + /// and resolves to address + mapping(bytes32 /* hashSymbol */ => address /* UtilityToken */) public symbolRoute; + /// nonce makes the staking process atomic across the two-phased process + /// and protects against replay attack on (un)staking proofs during the process. + /// On the value chain nonces need to strictly increase by one; on the utility + /// chain the nonce need to strictly increase (as one value chain can have multiple + /// utility chains) + mapping(address /* (un)staker */ => uint256) internal nonces; + /// store the ongoing mints and redemptions + mapping(bytes32 /* stakingIntentHash */ => Mint) internal mints; + mapping(bytes32 /* redemptionIntentHash*/ => Redemption) internal redemptions; + + /* + * Modifiers + */ + modifier onlyRegistrar() { + // for now keep unique registrar + require(msg.sender == registrar); + _; + } + + function OpenSTUtility( + uint256 _chainIdValue, + uint256 _chainIdUtility, + address _registrar) + public + OpsManaged() + { + require(_chainIdValue != 0); + require(_chainIdUtility != 0); + require(_registrar != address(0)); + + chainIdValue = _chainIdValue; + chainIdUtility = _chainIdUtility; + registrar = _registrar; + + uuidSTPrime = hashUuid( + STPRIME_SYMBOL, + STPRIME_NAME, + _chainIdValue, + _chainIdUtility, + address(this), + STPRIME_CONVERSION_RATE); + + simpleTokenPrime = new STPrime( + uuidSTPrime, + _chainIdValue, + _chainIdUtility, + STPRIME_CONVERSION_RATE); + + registeredTokens[uuidSTPrime] = RegisteredToken({ + token: UtilityTokenInterface(simpleTokenPrime), + registrar: registrar + }); + + // lock name and symbol route for ST' + bytes32 hashName = keccak256(STPRIME_NAME); + nameReservation[hashName] = registrar; + bytes32 hashSymbol = keccak256(STPRIME_SYMBOL); + symbolRoute[hashSymbol] = simpleTokenPrime; + + // @dev read STPrime address and uuid from contract + } + + /* + * External functions + */ + function confirmStakingIntent( + bytes32 _uuid, + address _staker, + uint256 _stakerNonce, + address _beneficiary, + uint256 _amountST, + uint256 _amountUT, + uint256 _stakingUnlockHeight, + bytes32 _stakingIntentHash) + external + onlyRegistrar + returns (uint256 expirationHeight) + { + require(address(registeredTokens[_uuid].token) != address(0)); + + require(nonces[_staker] < _stakerNonce); + require(_amountST > 0); + require(_amountUT > 0); + // stakingUnlockheight needs to be checked against the core that tracks the value chain + require(_stakingUnlockHeight > 0); + require(_stakingIntentHash != ""); + + expirationHeight = block.number + blocksToWaitShort(); + nonces[_staker] = _stakerNonce; + + bytes32 stakingIntentHash = hashStakingIntent( + _uuid, + _staker, + _stakerNonce, + _beneficiary, + _amountST, + _amountUT, + _stakingUnlockHeight + ); + + require(stakingIntentHash == _stakingIntentHash); + + mints[stakingIntentHash] = Mint({ + uuid: _uuid, + staker: _staker, + beneficiary: _beneficiary, + amount: _amountUT, + expirationHeight: expirationHeight + }); + + StakingIntentConfirmed(_uuid, stakingIntentHash, _staker, _beneficiary, _amountST, + _amountUT, expirationHeight); + + return expirationHeight; } function processMinting( - bytes32 _stakingIntentHash) - external - returns (address tokenAddress) + bytes32 _stakingIntentHash) + external + returns (address tokenAddress) { - require(_stakingIntentHash != ""); + require(_stakingIntentHash != ""); - Mint storage mint = mints[_stakingIntentHash]; - require(mint.staker == msg.sender); + Mint storage mint = mints[_stakingIntentHash]; + require(mint.staker == msg.sender); - // as process minting results in a gain it needs to expire well before - // the escrow on the cost unlocks in OpenSTValue.processStake - require(mint.expirationHeight > block.number); + // as process minting results in a gain it needs to expire well before + // the escrow on the cost unlocks in OpenSTValue.processStake + require(mint.expirationHeight > block.number); - UtilityTokenInterface token = registeredTokens[mint.uuid].token; - tokenAddress = address(token); - require(tokenAddress != address(0)); + UtilityTokenInterface token = registeredTokens[mint.uuid].token; + tokenAddress = address(token); + require(tokenAddress != address(0)); - require(token.mint(mint.beneficiary, mint.amount)); + require(token.mint(mint.beneficiary, mint.amount)); - ProcessedMint(mint.uuid, _stakingIntentHash, tokenAddress, mint.staker, - mint.beneficiary, mint.amount); + ProcessedMint(mint.uuid, _stakingIntentHash, tokenAddress, mint.staker, + mint.beneficiary, mint.amount); - delete mints[_stakingIntentHash]; + delete mints[_stakingIntentHash]; - return tokenAddress; + return tokenAddress; } function revertMinting( - bytes32 _stakingIntentHash) - external - returns ( - bytes32 uuid, - address staker, - address beneficiary, - uint256 amount) + bytes32 _stakingIntentHash) + external + returns ( + bytes32 uuid, + address staker, + address beneficiary, + uint256 amount) { - require(_stakingIntentHash != ""); + require(_stakingIntentHash != ""); - Mint storage mint = mints[_stakingIntentHash]; + Mint storage mint = mints[_stakingIntentHash]; - // require that the mint has expired and that the staker has not - // processed the minting, ie mint has not been deleted - require(mint.expirationHeight > 0); - require(mint.expirationHeight <= block.number); + // require that the mint has expired and that the staker has not + // processed the minting, ie mint has not been deleted + require(mint.expirationHeight > 0); + require(mint.expirationHeight <= block.number); - uuid = mint.uuid; - amount = mint.amount; - staker = mint.staker; - beneficiary = mint.beneficiary; + uuid = mint.uuid; + amount = mint.amount; + staker = mint.staker; + beneficiary = mint.beneficiary; - delete mints[_stakingIntentHash]; + delete mints[_stakingIntentHash]; - RevertedMint(uuid, _stakingIntentHash, staker, beneficiary, amount); + RevertedMint(uuid, _stakingIntentHash, staker, beneficiary, amount); - return (uuid, staker, beneficiary, amount); + return (uuid, staker, beneficiary, amount); } /// @dev redeemer must set an allowance for the branded token with OpenSTUtility @@ -433,189 +300,338 @@ contract OpenSTUtility is Hasher, OpsManaged { /// note: for STPrime, call OpenSTUtility.redeemSTPrime as a payable function /// note: nonce must be queried from OpenSTValue contract function redeem( - bytes32 _uuid, - uint256 _amountBT, - uint256 _nonce) - external - returns ( - uint256 unlockHeight, - bytes32 redemptionIntentHash) + bytes32 _uuid, + uint256 _amountBT, + uint256 _nonce) + external + returns ( + uint256 unlockHeight, + bytes32 redemptionIntentHash) { - require(_uuid != ""); - require(_amountBT > 0); - // on redemption allow the nonce to be re-used to cover for an unsuccessful - // previous redemption previously; as the nonce is strictly increasing plus - // one on the value chain; there is no gain on redeeming with the same nonce, - // only self-inflicted cost. - require(_nonce >= nonces[msg.sender]); - nonces[msg.sender] = _nonce; - - // to redeem ST' one needs to send value to payable - // function redeemSTPrime - require(_uuid != uuidSTPrime); - - BrandedToken token = BrandedToken(registeredTokens[_uuid].token); - - require(token.allowance(msg.sender, address(this)) >= _amountBT); - require(token.transferFrom(msg.sender, address(this), _amountBT)); - - unlockHeight = block.number + blocksToWaitLong(); - - redemptionIntentHash = hashRedemptionIntent( - _uuid, - msg.sender, - _nonce, - _amountBT, - unlockHeight - ); - - redemptions[redemptionIntentHash] = Redemption({ - uuid: _uuid, - redeemer: msg.sender, - amountUT: _amountBT, - unlockHeight: unlockHeight - }); - - RedemptionIntentDeclared(_uuid, redemptionIntentHash, address(token), - msg.sender, _nonce, _amountBT, unlockHeight, chainIdValue); - - return (unlockHeight, redemptionIntentHash); + require(_uuid != ""); + require(_amountBT > 0); + // on redemption allow the nonce to be re-used to cover for an unsuccessful + // previous redemption previously; as the nonce is strictly increasing plus + // one on the value chain; there is no gain on redeeming with the same nonce, + // only self-inflicted cost. + require(_nonce >= nonces[msg.sender]); + nonces[msg.sender] = _nonce; + + // to redeem ST' one needs to send value to payable + // function redeemSTPrime + require(_uuid != uuidSTPrime); + + BrandedToken token = BrandedToken(registeredTokens[_uuid].token); + + require(token.allowance(msg.sender, address(this)) >= _amountBT); + require(token.transferFrom(msg.sender, address(this), _amountBT)); + + unlockHeight = block.number + blocksToWaitLong(); + + redemptionIntentHash = hashRedemptionIntent( + _uuid, + msg.sender, + _nonce, + _amountBT, + unlockHeight + ); + + redemptions[redemptionIntentHash] = Redemption({ + uuid: _uuid, + redeemer: msg.sender, + amountUT: _amountBT, + unlockHeight: unlockHeight + }); + + RedemptionIntentDeclared(_uuid, redemptionIntentHash, address(token), + msg.sender, _nonce, _amountBT, unlockHeight, chainIdValue); + + return (unlockHeight, redemptionIntentHash); } /// @dev redeemer must send as value the amount STP to redeem /// note: nonce must be queried from OpenSTValue contract function redeemSTPrime( - uint256 _nonce) - external - payable - returns ( - uint256 amountSTP, - uint256 unlockHeight, - bytes32 redemptionIntentHash) + uint256 _nonce) + external + payable + returns ( + uint256 amountSTP, + uint256 unlockHeight, + bytes32 redemptionIntentHash) { - require(msg.value > 0); - // on redemption allow the nonce to be re-used to cover for an unsuccessful - // previous redemption previously; as the nonce is strictly increasing plus - // one on the value chain; there is no gain on redeeming with the same nonce, - // only self-inflicted cost. - require(_nonce >= nonces[msg.sender]); - nonces[msg.sender] = _nonce; - - amountSTP = msg.value; - unlockHeight = block.number + blocksToWaitLong(); - - redemptionIntentHash = hashRedemptionIntent( - uuidSTPrime, - msg.sender, - _nonce, - amountSTP, - unlockHeight - ); - - redemptions[redemptionIntentHash] = Redemption({ - uuid: uuidSTPrime, - redeemer: msg.sender, - amountUT: amountSTP, - unlockHeight: unlockHeight - }); - - RedemptionIntentDeclared(uuidSTPrime, redemptionIntentHash, simpleTokenPrime, - msg.sender, _nonce, amountSTP, unlockHeight, chainIdValue); - - return (amountSTP, unlockHeight, redemptionIntentHash); + require(msg.value > 0); + // on redemption allow the nonce to be re-used to cover for an unsuccessful + // previous redemption previously; as the nonce is strictly increasing plus + // one on the value chain; there is no gain on redeeming with the same nonce, + // only self-inflicted cost. + require(_nonce >= nonces[msg.sender]); + nonces[msg.sender] = _nonce; + + amountSTP = msg.value; + unlockHeight = block.number + blocksToWaitLong(); + + redemptionIntentHash = hashRedemptionIntent( + uuidSTPrime, + msg.sender, + _nonce, + amountSTP, + unlockHeight + ); + + redemptions[redemptionIntentHash] = Redemption({ + uuid: uuidSTPrime, + redeemer: msg.sender, + amountUT: amountSTP, + unlockHeight: unlockHeight + }); + + RedemptionIntentDeclared(uuidSTPrime, redemptionIntentHash, simpleTokenPrime, + msg.sender, _nonce, amountSTP, unlockHeight, chainIdValue); + + return (amountSTP, unlockHeight, redemptionIntentHash); } function processRedeeming( - bytes32 _redemptionIntentHash) - external - returns ( - address tokenAddress) + bytes32 _redemptionIntentHash) + external + returns ( + address tokenAddress) { - require(_redemptionIntentHash != ""); + require(_redemptionIntentHash != ""); - Redemption storage redemption = redemptions[_redemptionIntentHash]; + Redemption storage redemption = redemptions[_redemptionIntentHash]; - // note: as processRedemption incurs a cost for the redeemer, we provide a fallback - // in v0.9 for registrar to process the redemption on behalf of the redeemer, - // as the redeemer could fail to process the redemption and avoid the cost of redeeming; - // this will be replaced with a signature carry-over implementation instead, where - // the signature of the intent hash suffices on value and utility chain, decoupling - // it from the transaction to processRedemption and processUnstaking - require(redemption.redeemer == msg.sender || registrar == msg.sender); + // note: as processRedemption incurs a cost for the redeemer, we provide a fallback + // in v0.9 for registrar to process the redemption on behalf of the redeemer, + // as the redeemer could fail to process the redemption and avoid the cost of redeeming; + // this will be replaced with a signature carry-over implementation instead, where + // the signature of the intent hash suffices on value and utility chain, decoupling + // it from the transaction to processRedemption and processUnstaking + require(redemption.redeemer == msg.sender || registrar == msg.sender); - // as process redemption bears the cost there is no need to require - // the unlockHeight is not past, the same way as we do require for - // the expiration height on the unstake to not have expired yet. + // as process redemption bears the cost there is no need to require + // the unlockHeight is not past, the same way as we do require for + // the expiration height on the unstake to not have expired yet. - UtilityTokenInterface token = registeredTokens[redemption.uuid].token; - tokenAddress = address(token); - require(tokenAddress != address(0)); + UtilityTokenInterface token = registeredTokens[redemption.uuid].token; + tokenAddress = address(token); + require(tokenAddress != address(0)); - uint256 value = 0; - if (redemption.uuid == uuidSTPrime) value = redemption.amountUT; + uint256 value = 0; + if (redemption.uuid == uuidSTPrime) value = redemption.amountUT; - require(token.burn.value(value)(redemption.redeemer, redemption.amountUT)); + require(token.burn.value(value)(redemption.redeemer, redemption.amountUT)); - ProcessedRedemption(redemption.uuid, _redemptionIntentHash, token, - redemption.redeemer, redemption.amountUT); + ProcessedRedemption(redemption.uuid, _redemptionIntentHash, token, + redemption.redeemer, redemption.amountUT); - delete redemptions[_redemptionIntentHash]; + delete redemptions[_redemptionIntentHash]; - return tokenAddress; - } + return tokenAddress; + } - function revertRedemption( - bytes32 _redemptionIntentHash) - external - returns ( - bytes32 uuid, - address redeemer, - uint256 amountUT) - { - require(_redemptionIntentHash != ""); + function revertRedemption( + bytes32 _redemptionIntentHash) + external + returns ( + bytes32 uuid, + address redeemer, + uint256 amountUT) + { + require(_redemptionIntentHash != ""); - Redemption storage redemption = redemptions[_redemptionIntentHash]; + Redemption storage redemption = redemptions[_redemptionIntentHash]; - // require that the redemption is unlocked and exists - require(redemption.unlockHeight > 0); - require(redemption.unlockHeight <= block.number); + // require that the redemption is unlocked and exists + require(redemption.unlockHeight > 0); + require(redemption.unlockHeight <= block.number); - uuid = redemption.uuid; - amountUT = redemption.amountUT; - redeemer = redemption.redeemer; + uuid = redemption.uuid; + amountUT = redemption.amountUT; + redeemer = redemption.redeemer; - if (redemption.uuid == uuidSTPrime) { - // transfer throws if insufficient funds - redeemer.transfer(amountUT); - } else { - EIP20Interface token = EIP20Interface(registeredTokens[redemption.uuid].token); + if (redemption.uuid == uuidSTPrime) { + // transfer throws if insufficient funds + redeemer.transfer(amountUT); + } else { + EIP20Interface token = EIP20Interface(registeredTokens[redemption.uuid].token); - require(token.transfer(redemption.redeemer, redemption.amountUT)); - } + require(token.transfer(redemption.redeemer, redemption.amountUT)); + } - delete redemptions[_redemptionIntentHash]; + delete redemptions[_redemptionIntentHash]; - // fire event - RevertedRedemption(uuid, _redemptionIntentHash, redeemer, amountUT); + // fire event + RevertedRedemption(uuid, _redemptionIntentHash, redeemer, amountUT); - return (uuid, redeemer, amountUT); - } + return (uuid, redeemer, amountUT); + } - /* - * Public view functions - */ + /* + * External view functions + */ function registeredTokenProperties( - bytes32 _uuid) - external - view - returns ( - address /* token */, - address /* registrar */) + bytes32 _uuid) + external + view + returns ( + address, /* token */ + address /* registrar */) { - RegisteredToken storage registeredToken = registeredTokens[_uuid]; - return ( - address(registeredToken.token), - registeredToken.registrar); + RegisteredToken storage registeredToken = registeredTokens[_uuid]; + return ( + address(registeredToken.token), + registeredToken.registrar); + } + + /* + * Public functions + */ + /// @dev Congratulations on looking under the hood and obtaining ST' to call proposeBrandedToken; + /// however, OpenSTFoundation is not yet actively listening to these events + /// because to automate it we will build a web UI where you can authenticate + /// with your msg.sender = _requester key; + /// until that time please drop us a line on partners(at)simpletoken.org and we can + /// work with you to register for your branded token + function proposeBrandedToken( + string _symbol, + string _name, + uint256 _conversionRate) + public + returns (bytes32) + { + require(bytes(_symbol).length > 0); + require(bytes(_name).length > 0); + require(_conversionRate > 0); + + bytes32 hashSymbol = keccak256(_symbol); + bytes32 hashName = keccak256(_name); + require(checkAvailability(hashSymbol, hashName, msg.sender)); + + bytes32 btUuid = hashUuid( + _symbol, + _name, + chainIdValue, + chainIdUtility, + address(this), + _conversionRate); + + BrandedToken proposedBT = new BrandedToken( + btUuid, + _symbol, + _name, + TOKEN_DECIMALS, + chainIdValue, + chainIdUtility, + _conversionRate); + // reserve name for sender under opt-in discretion of + // registrar + nameReservation[hashName] = msg.sender; + + ProposedBrandedToken(msg.sender, address(proposedBT), btUuid, + _symbol, _name, _conversionRate); + + return btUuid; + } + + function checkAvailability( + bytes32 _hashSymbol, + bytes32 _hashName, + address _requester) + public + view + returns (bool /* success */) + { + // a reserved symbol means the route is already chosen + address token = symbolRoute[_hashSymbol]; + if (token != address(0)) return false; + + // a name can have been reserved during the Simple Token sale + // in which case must come from same address + // otherwise proposals are first come, first serve + // under opt-in discretion of registrar + address requester = nameReservation[_hashName]; + if ((requester == address(0) || + requester == _requester)) { + return true; + } + return false; + } + + /* + * Registrar functions + */ + /// @dev for v0.9.1 tracking Ethereum mainnet on the utility chain + /// is not a required feature yet, so the core is simplified + /// to uint256 valueChainId as storage on construction + // function addCore( + // CoreInterface _core) + // public + // onlyRegistrar + // returns (bool /* success */) + // { + // require(address(_core) != address(0)); + // // core constructed with same registrar + // require(registrar == _core.registrar()); + // // on utility chain core only tracks a remote value chain + // uint256 coreChainIdValue = _core.chainIdRemote(); + // require(chainIdUtility != 0); + // // cannot overwrite core for given chainId + // require(cores[coreChainIdValue] == address(0)); + + // cores[coreChainIdValue] = _core; + + // return true; + // } + + /* solhint-disable-next-line separate-by-one-line-in-contract */ + function registerBrandedToken( + string _symbol, + string _name, + uint256 _conversionRate, + address _requester, + UtilityTokenInterface _brandedToken, + bytes32 _checkUuid) + public + onlyRegistrar + returns (bytes32 registeredUuid) + { + require(bytes(_symbol).length > 0); + require(bytes(_name).length > 0); + require(_conversionRate > 0); + + bytes32 hashSymbol = keccak256(_symbol); + bytes32 hashName = keccak256(_name); + require(checkAvailability(hashSymbol, hashName, _requester)); + + registeredUuid = hashUuid( + _symbol, + _name, + chainIdValue, + chainIdUtility, + address(this), + _conversionRate); + + require(registeredUuid == _checkUuid); + require(_brandedToken.uuid() == _checkUuid); + + assert(address(registeredTokens[registeredUuid].token) == address(0)); + + registeredTokens[registeredUuid] = RegisteredToken({ + token: _brandedToken, + registrar: registrar + }); + + // register name to registrar + nameReservation[hashName] = registrar; + // register symbol + symbolRoute[hashSymbol] = _brandedToken; + + RegisteredBrandedToken(registrar, _brandedToken, registeredUuid, _symbol, _name, + _conversionRate, _requester); + + return registeredUuid; } /* @@ -636,6 +652,7 @@ contract OpenSTUtility is Hasher, OpsManaged { // on the very first released version v0.9.1 there is no need // to completeProtocolTransfer from a previous version + /* solhint-disable-next-line separate-by-one-line-in-contract */ function revokeProtocolTransfer( ProtocolVersioned _token) public @@ -647,11 +664,11 @@ contract OpenSTUtility is Hasher, OpsManaged { return true; } - function blocksToWaitLong() public pure returns (uint256) { - return BLOCKS_TO_WAIT_LONG; - } + function blocksToWaitLong() public pure returns (uint256) { + return BLOCKS_TO_WAIT_LONG; + } - function blocksToWaitShort() public pure returns (uint256) { - return BLOCKS_TO_WAIT_SHORT; - } + function blocksToWaitShort() public pure returns (uint256) { + return BLOCKS_TO_WAIT_SHORT; + } } \ No newline at end of file diff --git a/contracts/OpenSTUtilityInterface.sol b/contracts/OpenSTUtilityInterface.sol index ea48eac4..b688d52d 100644 --- a/contracts/OpenSTUtilityInterface.sol +++ b/contracts/OpenSTUtilityInterface.sol @@ -1,3 +1,4 @@ +/* solhint-disable-next-line compiler-fixed */ pragma solidity ^0.4.17; // Copyright 2017 OpenST Ltd. @@ -24,34 +25,35 @@ pragma solidity ^0.4.17; // import "./CoreInterface.sol"; import "./UtilityTokenInterface.sol"; -contract OpenSTUtilityInterface { - function registerBrandedToken( - string _symbol, - string _name, - uint256 _conversionRate, - address _requester, - UtilityTokenInterface _brandedToken, - bytes32 _checkUuid) - public - returns ( - bytes32 registeredUuid); - function confirmStakingIntent( - bytes32 _uuid, - address _staker, - uint256 _stakerNonce, - address _beneficiary, - uint256 _amountST, - uint256 _amountUT, - uint256 _stakingUnlockHeight, - bytes32 _stakingIntentHash) - external - returns ( - uint256 expirationHeight); +contract OpenSTUtilityInterface { + function confirmStakingIntent( + bytes32 _uuid, + address _staker, + uint256 _stakerNonce, + address _beneficiary, + uint256 _amountST, + uint256 _amountUT, + uint256 _stakingUnlockHeight, + bytes32 _stakingIntentHash) + external + returns ( + uint256 expirationHeight); function processRedeeming( - bytes32 _redemptionIntentHash) - external - returns ( - address tokenAddress); + bytes32 _redemptionIntentHash) + external + returns ( + address tokenAddress); + + function registerBrandedToken( + string _symbol, + string _name, + uint256 _conversionRate, + address _requester, + UtilityTokenInterface _brandedToken, + bytes32 _checkUuid) + public + returns ( + bytes32 registeredUuid); } \ No newline at end of file diff --git a/contracts/STPrime.sol b/contracts/STPrime.sol index bca9c6fc..0f1d2cca 100644 --- a/contracts/STPrime.sol +++ b/contracts/STPrime.sol @@ -1,3 +1,4 @@ +/* solhint-disable-next-line compiler-fixed */ pragma solidity ^0.4.17; // Copyright 2017 OpenST Ltd. @@ -32,6 +33,7 @@ import "./SafeMath.sol"; import "./UtilityTokenAbstract.sol"; import "./STPrimeConfig.sol"; + /* * @title STPrime * @notice a freely tradable equivalent representation of Simple Token [ST] @@ -41,7 +43,7 @@ import "./STPrimeConfig.sol"; * of the finite amount of base tokens on the utility chain */ contract STPrime is UtilityTokenAbstract, STPrimeConfig { - using SafeMath for uint256; + using SafeMath for uint256; /* @@ -63,13 +65,19 @@ contract STPrime is UtilityTokenAbstract, STPrimeConfig { * Public functions */ function STPrime( - address _openSTProtocol, - bytes32 _uuid) - UtilityTokenAbstract(_openSTProtocol, _uuid) - public - { - - } + bytes32 _uuid, + uint256 _chainIdValue, + uint256 _chainIdUtility, + uint256 _conversionRate) + public + UtilityTokenAbstract( + _uuid, + TOKEN_SYMBOL, + TOKEN_NAME, + _chainIdValue, + _chainIdUtility, + _conversionRate) + { } /// On setup of the utility chain the base tokens need to be transfered /// in full to STPrime for the base tokens to be minted as ST' @@ -84,7 +92,7 @@ contract STPrime is UtilityTokenAbstract, STPrimeConfig { initialized = true; } - /// @dev transfer full claim to beneficiary + /// @dev transfer full claim to beneficiary /// claim can be called publicly as the beneficiary /// and amount are set, and this allows for reduced /// steps on the user experience to complete the claim @@ -93,13 +101,13 @@ contract STPrime is UtilityTokenAbstract, STPrimeConfig { /// has to be zero to deploy the contracts and accept the very /// first staking of ST for ST' and its protocol executions. function claim( - address _beneficiary) - public + address _beneficiary) + public onlyInitialized - returns (bool /* success */) + returns (bool /* success */) { - uint256 amount = claimInternal(_beneficiary); - assert(this.balance >= amount); + uint256 amount = claimInternal(_beneficiary); + assert(this.balance >= amount); // transfer throws if insufficient funds _beneficiary.transfer(amount); @@ -113,32 +121,32 @@ contract STPrime is UtilityTokenAbstract, STPrimeConfig { /// the protocol completion does not continue into /// foreign contracts at _beneficiary. function mint( - address _beneficiary, - uint256 _amount) - public - onlyProtocol + address _beneficiary, + uint256 _amount) + public + onlyProtocol onlyInitialized - returns (bool /* success */) - { - // add the minted amount to the beneficiary's claim - return mintInternal(_beneficiary, _amount); + returns (bool /* success */) + { + // add the minted amount to the beneficiary's claim + return mintInternal(_beneficiary, _amount); } /// @dev Burn utility tokens after having redeemed them /// through the protocol for the staked Simple Token function burn( - address _burner, - uint256 _amount) - public - onlyProtocol + address _burner, + uint256 _amount) + public + onlyProtocol onlyInitialized - payable - returns (bool /* success */) - { - // only accept the exact amount of base tokens to be returned - // to the ST' minting contract - require(msg.value == _amount); - - return burnInternal(_burner, _amount); - } + payable + returns (bool /* success */) + { + // only accept the exact amount of base tokens to be returned + // to the ST' minting contract + require(msg.value == _amount); + + return burnInternal(_burner, _amount); + } } \ No newline at end of file diff --git a/contracts/UtilityTokenAbstract.sol b/contracts/UtilityTokenAbstract.sol index f713a742..2388ee42 100644 --- a/contracts/UtilityTokenAbstract.sol +++ b/contracts/UtilityTokenAbstract.sol @@ -1,3 +1,4 @@ +/* solhint-disable-next-line compiler-fixed */ pragma solidity ^0.4.17; // Copyright 2017 OpenST Ltd. @@ -22,57 +23,88 @@ pragma solidity ^0.4.17; // ---------------------------------------------------------------------------- import "./SafeMath.sol"; +import "./Hasher.sol"; import "./ProtocolVersioned.sol"; // utility chain contracts import "./UtilityTokenInterface.sol"; + /// @title UtilityToken abstract -contract UtilityTokenAbstract is ProtocolVersioned, UtilityTokenInterface { - using SafeMath for uint256; - - /* - * Events - */ - /// Minted raised when new utility tokens are minted for a beneficiary - /// Minted utility tokens still need to be claimed by anyone to transfer - /// them to the beneficiary. +contract UtilityTokenAbstract is Hasher, ProtocolVersioned, UtilityTokenInterface { + using SafeMath for uint256; + + /* + * Events + */ + /// Minted raised when new utility tokens are minted for a beneficiary + /// Minted utility tokens still need to be claimed by anyone to transfer + /// them to the beneficiary. event Minted(bytes32 indexed _uuid, address indexed _beneficiary, - uint256 _amount, uint256 _unclaimed, uint256 _totalSupply); + uint256 _amount, uint256 _unclaimed, uint256 _totalSupply); + event Burnt(bytes32 indexed _uuid, address indexed _account, - uint256 _amount, uint256 _totalSupply); - + uint256 _amount, uint256 _totalSupply); + /* * Storage */ - /// UUID for the utility token - bytes32 private tokenUuid; - /// totalSupply holds the total supply of utility tokens - uint256 private totalTokenSupply; - /// claims is follows EIP20 allowance pattern but - /// for a staker to stake the utility token for a beneficiary - mapping(address => uint256) private claims; + /// UUID for the utility token + bytes32 private tokenUuid; + /// totalSupply holds the total supply of utility tokens + uint256 private totalTokenSupply; + /// conversion rate for the utility token + uint256 private tokenConversionRate; + /// tokenChainIdValue is an invariant in the tokenUuid calculation + uint256 private tokenChainIdValue; + /// tokenChainIdUtility is an invariant in the tokenUuid calculation + uint256 private tokenChainIdUtility; + /// tokenOpenSTUtility is an invariant in the tokenUuid calculation + address private tokenOpenSTUtility; + /// claims is follows EIP20 allowance pattern but + /// for a staker to stake the utility token for a beneficiary + mapping(address => uint256) private claims; /* * Public functions */ - function UtilityTokenAbstract(address _protocol, bytes32 _uuid) - public - ProtocolVersioned(_protocol) + function UtilityTokenAbstract( + bytes32 _uuid, + string _symbol, + string _name, + uint256 _chainIdValue, + uint256 _chainIdUtility, + uint256 _conversionRate) + public + ProtocolVersioned(msg.sender) + { + tokenUuid = hashUuid( + _symbol, + _name, + _chainIdValue, + _chainIdUtility, + msg.sender, + _conversionRate); + + require(tokenUuid == _uuid); + + totalTokenSupply = 0; + tokenConversionRate = _conversionRate; + tokenChainIdValue = _chainIdValue; + tokenChainIdUtility = _chainIdUtility; + tokenOpenSTUtility = msg.sender; + } + + /// @dev Get totalTokenSupply as view so that child cannot edit + function totalSupply() + public + view + returns (uint256 /* supply */) { - tokenUuid = _uuid; - totalTokenSupply = 0; + return totalTokenSupply; } - - /// @dev Get totalTokenSupply as view so that child cannot edit - function totalSupply() - public - view - returns (uint256 /* supply */) - { - return totalTokenSupply; - } + /// @dev Get tokenUuid as view so that child cannot edit function uuid() public view @@ -81,60 +113,95 @@ contract UtilityTokenAbstract is ProtocolVersioned, UtilityTokenInterface { return tokenUuid; } + /// @dev Get tokenConversionRate as view so that child cannot edit + function conversionRate() + public + view + returns (uint256 /* rate */) + { + return tokenConversionRate; + } + + /// @dev Get tokenChainIdValue as view so that child cannot edit + function genesisChainIdValue() + public + view + returns (uint256 /* tokenChainIdValue */) + { + return tokenChainIdValue; + } + + /// @dev Get tokenChainIdUtility as view so that child cannot edit + function genesisChainIdUtility() + public + view + returns (uint256 /* tokenChainIdUtility */) + { + return tokenChainIdUtility; + } + + /// @dev Get tokenOpenSTUtility as view so that child cannot edit + function genesisOpenSTUtility() + public + view + returns (address /* tokenOpenSTUtility */) + { + return tokenOpenSTUtility; + } /// @dev returns unclaimed amount for beneficiary - function unclaimed( - address _beneficiary) - public - view - returns (uint256 /* amount */) - { - return claims[_beneficiary]; - } + function unclaimed( + address _beneficiary) + public + view + returns (uint256 /* amount */) + { + return claims[_beneficiary]; + } /* * Internal functions */ - /// @dev claim transfers all utility tokens to _beneficiary - function claimInternal( - address _beneficiary) - internal - returns (uint256 amount) + /// @dev claim transfers all utility tokens to _beneficiary + function claimInternal( + address _beneficiary) + internal + returns (uint256 amount) { - amount = claims[_beneficiary]; - claims[_beneficiary] = 0; + amount = claims[_beneficiary]; + claims[_beneficiary] = 0; - return amount; + return amount; } /// @dev Mint new utility token by adding a claim /// for the beneficiary function mintInternal( - address _beneficiary, - uint256 _amount) - internal - returns (bool /* success */) + address _beneficiary, + uint256 _amount) + internal + returns (bool /* success */) { - totalTokenSupply = totalTokenSupply.add(_amount); + totalTokenSupply = totalTokenSupply.add(_amount); claims[_beneficiary] = claims[_beneficiary].add(_amount); - Minted(tokenUuid, _beneficiary, _amount, claims[_beneficiary], totalTokenSupply); + Minted(tokenUuid, _beneficiary, _amount, claims[_beneficiary], totalTokenSupply); - return true; + return true; } /// @dev Burn utility tokens after having redeemed them /// through the protocol for the staked Simple Token function burnInternal( - address _burner, - uint256 _amount) - internal - returns (bool /* success */) - { - totalTokenSupply = totalTokenSupply.sub(_amount); + address _burner, + uint256 _amount) + internal + returns (bool /* success */) + { + totalTokenSupply = totalTokenSupply.sub(_amount); - Burnt(tokenUuid, _burner, _amount, totalTokenSupply); + Burnt(tokenUuid, _burner, _amount, totalTokenSupply); - return true; - } + return true; + } } \ No newline at end of file diff --git a/contracts/UtilityTokenAbstractMock.sol b/contracts/UtilityTokenAbstractMock.sol index 986a0ec2..55c6235a 100644 --- a/contracts/UtilityTokenAbstractMock.sol +++ b/contracts/UtilityTokenAbstractMock.sol @@ -1,3 +1,4 @@ +/* solhint-disable-next-line compiler-fixed */ pragma solidity ^0.4.17; // Copyright 2017 OpenST Ltd. @@ -23,58 +24,75 @@ pragma solidity ^0.4.17; import "./UtilityTokenAbstract.sol"; + /// @title UtilityTokenAbstractMock -/// @dev Implements mock claim, mint, and burn functions and wraps internal functions to enable testing UtilityTokenAbstract +/// @dev Implements mock claim, mint, and burn functions +/// and wraps internal functions to enable testing UtilityTokenAbstract contract UtilityTokenAbstractMock is UtilityTokenAbstract { - function UtilityTokenAbstractMock(address _protocol, bytes32 _uuid) - UtilityTokenAbstract(_protocol, _uuid) - ProtocolVersioned(_protocol) - public { } + function UtilityTokenAbstractMock( + bytes32 _uuid, + string _symbol, + string _name, + uint256 _chainIdValue, + uint256 _chainIdUtility, + uint256 _conversionRate) + public + UtilityTokenAbstract( + _uuid, + _symbol, + _name, + _chainIdValue, + _chainIdUtility, + _conversionRate) + ProtocolVersioned(msg.sender) + { } - /// @dev Mock claim function + /// @dev Mock claim function function claim(address _beneficiary) public returns (bool success) { _beneficiary; success = true; } + /// @dev Mock mint function function mint(address _beneficiary, uint256 _amount) public returns (bool success) { _beneficiary; _amount; success = true; } + /// @dev Mock burn function function burn(address _redeemer, uint256 _amount) public payable returns (bool success) { _redeemer; _amount; success = true; } - - /// @dev Public wrapper for claimInternal - function claimInternalPublic( - address _beneficiary) - public - returns (uint256 amount) + + /// @dev Public wrapper for claimInternal + function claimInternalPublic( + address _beneficiary) + public + returns (uint256 amount) { - amount = claimInternal(_beneficiary); + amount = claimInternal(_beneficiary); } /// @dev Public wrapper for mintInternal function mintInternalPublic( - address _beneficiary, - uint256 _amount) - public - returns (bool /* success */) + address _beneficiary, + uint256 _amount) + public + returns (bool /* success */) { - return mintInternal(_beneficiary, _amount); + return mintInternal(_beneficiary, _amount); } /// @dev Public wrapper for burnInternal function burnInternalPublic( - address _redeemer, - uint256 _amount) - public - returns (bool /* success */) - { - return burnInternal(_redeemer, _amount); - } + address _redeemer, + uint256 _amount) + public + returns (bool /* success */) + { + return burnInternal(_redeemer, _amount); + } } \ No newline at end of file diff --git a/contracts/UtilityTokenInterface.sol b/contracts/UtilityTokenInterface.sol index 6a890389..41517673 100644 --- a/contracts/UtilityTokenInterface.sol +++ b/contracts/UtilityTokenInterface.sol @@ -1,3 +1,4 @@ +/* solhint-disable-next-line compiler-fixed */ pragma solidity ^0.4.17; // Copyright 2017 OpenST Ltd. @@ -23,16 +24,18 @@ pragma solidity ^0.4.17; contract UtilityTokenInterface { - /// @dev transfer full claim to beneficiary + /// @dev transfer full claim to beneficiary function claim(address _beneficiary) public returns (bool success); /// @dev Mint new utility token into claim for beneficiary function mint(address _beneficiary, uint256 _amount) public returns (bool success); /// @dev Burn utility tokens after having redeemed them /// through the protocol for the staked Simple Token function burn(address _burner, uint256 _amount) public payable returns (bool success); - - /// @dev Get totalTokenSupply as view so that child cannot edit - function totalSupply() public view returns (uint256 supply); - /// get unique universal identifier for utility token - function uuid() public view returns (bytes32 getUuid); + + /// @dev Get totalTokenSupply as view so that child cannot edit + function totalSupply() public view returns (uint256 supply); + /// @dev Get unique universal identifier for utility token + function uuid() public view returns (bytes32 getUuid); + /// @dev Get conversion rate for utility token + function conversionRate() public view returns (uint256 rate); } \ No newline at end of file diff --git a/contracts/compile.sh b/contracts/compile.sh new file mode 100644 index 00000000..10891e24 --- /dev/null +++ b/contracts/compile.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +CONTRACTDIR=./contracts/*.sol +ABIDIRUTILITY=./contracts/abi +BINDIRVALUE=./contracts/bin + +mkdir -p "$ABIDIRUTILITY" +mkdir -p "$BINDIRVALUE" + +for filename in $CONTRACTDIR; do + echo "" + echo "Compiling ${filename}" + echo "" + solc --abi --optimize --optimize-runs 200 --overwrite ${filename} -o $ABIDIRUTILITY + solc --bin --optimize --optimize-runs 200 --overwrite ${filename} -o $BINDIRVALUE +done \ No newline at end of file diff --git a/test/BrandedToken_utils.js b/test/BrandedToken_utils.js index 53cd4ef0..e511e742 100644 --- a/test/BrandedToken_utils.js +++ b/test/BrandedToken_utils.js @@ -19,19 +19,23 @@ // // ---------------------------------------------------------------------------- -const Assert = require('assert'); +const Assert = require('assert'); const BigNumber = require('bignumber.js'); +var Hasher = artifacts.require("./Hasher.sol"); var BrandedToken = artifacts.require("./BrandedToken.sol"); /// @dev Deploy module.exports.deployBrandedToken = async (artifacts, accounts) => { - /// mock unique identifier for utility token - const UUID = "0xbce8a3809c9356cf0e5178a2aef207f50df7d32b388c8fceb8e363df00efce31"; - /// mock OpenST protocol contract address with an external account - const openSTProtocol = accounts[4]; + const hasher = await Hasher.new(); + /// mock OpenST protocol contract address with an external account + const openSTProtocol = accounts[4]; + const conversionRate = 10; + const genesisChainIdValue = 3; + const genesisChainIdUtility = 1410; + const UUID = await hasher.hashUuid.call("symbol", "name", genesisChainIdValue, genesisChainIdUtility, openSTProtocol, conversionRate); - const token = await BrandedToken.new(openSTProtocol, UUID, "SYMBOL", "Name", 18, { from: accounts[0], gas: 3500000 }); + const token = await BrandedToken.new(UUID, "symbol", "name", 18, genesisChainIdValue, genesisChainIdUtility, conversionRate, { from: openSTProtocol }); return { token : token diff --git a/test/STPrime_utils.js b/test/STPrime_utils.js index 511752e5..26cc08b9 100644 --- a/test/STPrime_utils.js +++ b/test/STPrime_utils.js @@ -19,21 +19,29 @@ // // ---------------------------------------------------------------------------- -const Assert = require('assert'); +const Assert = require('assert'); const BigNumber = require('bignumber.js'); -var STPrime = artifacts.require("./STPrime.sol"); +var Hasher = artifacts.require("./Hasher.sol"); +var STPrimeConfig = artifacts.require("./STPrimeConfig.sol"); +var STPrime = artifacts.require("./STPrime.sol"); /// @dev Deploy module.exports.deploySTPrime = async (artifacts, accounts) => { - /// mock unique identifier for utility token - const UUID = "0xbce8a3809c9356cf0e5178a2aef207f50df7d32b388c8fceb8e363df00efce31"; - /// mock OpenST protocol contract address with an external account - const openSTProtocol = accounts[4]; + const hasher = await Hasher.new(); + const stPrimeConfig = await STPrimeConfig.new(); + /// mock OpenST protocol contract address with an external account + const openSTProtocol = accounts[4]; + const conversionRate = 10; + const genesisChainIdValue = 3; + const genesisChainIdUtility = 1410; + const stPrimeSymbol = await stPrimeConfig.TOKEN_SYMBOL.call(); + const stPrimeName = await stPrimeConfig.TOKEN_NAME.call(); + const UUID = await hasher.hashUuid.call(stPrimeSymbol, stPrimeName, genesisChainIdValue, genesisChainIdUtility, openSTProtocol, conversionRate); - const stPrime = await STPrime.new(openSTProtocol, UUID); + const stPrime = await STPrime.new(UUID, genesisChainIdValue, genesisChainIdUtility, conversionRate, { from: openSTProtocol }); - return { - stPrime : stPrime - } + return { + stPrime : stPrime + } } diff --git a/test/UtilityTokenAbstract.js b/test/UtilityTokenAbstract.js index 6cd33f8d..7ce53065 100644 --- a/test/UtilityTokenAbstract.js +++ b/test/UtilityTokenAbstract.js @@ -23,11 +23,20 @@ const BigNumber = require('bignumber.js'); const Utils = require('./lib/utils.js'); const UtilityTokenAbstract_utils = require('./UtilityTokenAbstract_utils.js'); +const UtilityTokenAbstract = artifacts.require("./UtilityTokenAbstractMock.sol"); + /// /// Test stories /// +/// Construction +/// fails to deploy if UUID is bad +/// /// Properties /// has uuid +/// has conversionRate +/// has genesisChainIdValue +/// has genesisChainIdUtility +/// has genesisOpenSTUtility /// /// MintInternal, ClaimInternal, and BurnInternal /// successfully mints @@ -36,28 +45,58 @@ const UtilityTokenAbstract_utils = require('./UtilityTokenAbstract_utils.js'); /// contract('UtilityTokenAbstract', function(accounts) { - const UUID = "0xbce8a3809c9356cf0e5178a2aef207f50df7d32b388c8fceb8e363df00efce31"; - const openSTProtocol = accounts[4]; - const beneficiary1 = '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; - const beneficiary2 = '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'; - const ST1 = new BigNumber(web3.toWei(1, "ether")); - const ST2 = new BigNumber(web3.toWei(2, "ether")); - const ST3 = new BigNumber(web3.toWei(3, "ether")); - const ST4 = new BigNumber(web3.toWei(4, "ether")); - - var totalSupply = null; - var unclaimed = null; - var result = null; - var amount = null; + const openSTProtocol = accounts[4]; + const conversionRate = 10; + const genesisChainIdValue = 3; + const genesisChainIdUtility = 1410; + const beneficiary1 = '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; + const beneficiary2 = '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'; + const ST1 = new BigNumber(web3.toWei(1, "ether")); + const ST2 = new BigNumber(web3.toWei(2, "ether")); + const ST3 = new BigNumber(web3.toWei(3, "ether")); + const ST4 = new BigNumber(web3.toWei(4, "ether")); + + var contracts = null; + var hasher = null; + var utilityTokenAbstract = null; + var uuid = null; + var totalSupply = null; + var unclaimed = null; + var result = null; + var amount = null; + + describe ('Construction', async () => { + it('fails to deploy if UUID is bad', async () => { + await Utils.expectThrow(UtilityTokenAbstract.new("bad uuid", "symbol", "name", genesisChainIdValue, genesisChainIdUtility, conversionRate, { from: openSTProtocol })); + }) + }) describe ('Properties', async () => { before(async () => { - contracts = await UtilityTokenAbstract_utils.deployUtilityTokenAbstract(artifacts, accounts); + contracts = await UtilityTokenAbstract_utils.deployUtilityTokenAbstract(artifacts, accounts); + hasher = contracts.hasher; utilityTokenAbstract = contracts.utilityTokenAbstract; }) it('has uuid', async () => { - assert.equal(await utilityTokenAbstract.uuid.call(), UUID); + uuid = await hasher.hashUuid.call("symbol", "name", genesisChainIdValue, genesisChainIdUtility, openSTProtocol, conversionRate); + assert.equal(await utilityTokenAbstract.uuid.call(), uuid); + }) + + it ('has conversionRate', async () => { + assert.equal(await utilityTokenAbstract.conversionRate.call(), conversionRate); + }) + + it ('has genesisChainIdValue', async () => { + assert.equal(await utilityTokenAbstract.genesisChainIdValue.call(), genesisChainIdValue); + }) + + it ('has genesisChainIdUtility', async () => { + assert.equal(await utilityTokenAbstract.genesisChainIdUtility.call(), genesisChainIdUtility); + }) + + it ('has genesisOpenSTUtility', async () => { + assert.equal(await utilityTokenAbstract.genesisOpenSTUtility.call(), openSTProtocol); }) }) @@ -81,7 +120,7 @@ contract('UtilityTokenAbstract', function(accounts) { unclaimed = await utilityTokenAbstract.unclaimed.call(beneficiary1); assert.equal(totalSupply.toNumber(), ST2.toNumber()); assert.equal(unclaimed.toNumber(), ST2.toNumber()); - UtilityTokenAbstract_utils.checkMintedEvent(result.logs[0], UUID, beneficiary1, ST2, ST2, ST2); + UtilityTokenAbstract_utils.checkMintedEvent(result.logs[0], uuid, beneficiary1, ST2, ST2, ST2); // Mint again for beneficiary1 result = await utilityTokenAbstract.mintInternalPublic(beneficiary1, ST1); @@ -90,7 +129,7 @@ contract('UtilityTokenAbstract', function(accounts) { unclaimed = await utilityTokenAbstract.unclaimed.call(beneficiary1); assert.equal(totalSupply.toNumber(), ST3.toNumber()); assert.equal(unclaimed.toNumber(), ST3.toNumber()); - UtilityTokenAbstract_utils.checkMintedEvent(result.logs[0], UUID, beneficiary1, ST1, ST3, ST3); + UtilityTokenAbstract_utils.checkMintedEvent(result.logs[0], uuid, beneficiary1, ST1, ST3, ST3); unclaimed = await utilityTokenAbstract.unclaimed.call(beneficiary2); assert.equal(unclaimed.toNumber(), 0); @@ -103,7 +142,7 @@ contract('UtilityTokenAbstract', function(accounts) { unclaimed = await utilityTokenAbstract.unclaimed.call(beneficiary2); assert.equal(totalSupply.toNumber(), ST4.toNumber()); assert.equal(unclaimed.toNumber(), ST1.toNumber()); - UtilityTokenAbstract_utils.checkMintedEvent(result.logs[0], UUID, beneficiary2, ST1, ST1, ST4); + UtilityTokenAbstract_utils.checkMintedEvent(result.logs[0], uuid, beneficiary2, ST1, ST1, ST4); }) it('successfully claims', async () => { @@ -141,7 +180,7 @@ contract('UtilityTokenAbstract', function(accounts) { var postBurnTotalSupply = await utilityTokenAbstract.totalSupply.call(); assert.equal(postBurnTotalSupply.toNumber(), totalSupply.minus(amount).toNumber()); - UtilityTokenAbstract_utils.checkBurntEvent(result.logs[0], UUID, beneficiary1, amount, postBurnTotalSupply); + UtilityTokenAbstract_utils.checkBurntEvent(result.logs[0], uuid, beneficiary1, amount, postBurnTotalSupply); // Burn 7 for 0x0 to reflect that this function // is only concerned with reducing totalTokenSupply @@ -152,8 +191,7 @@ contract('UtilityTokenAbstract', function(accounts) { postBurnTotalSupply = await utilityTokenAbstract.totalSupply.call(); assert.equal(postBurnTotalSupply.toNumber(), totalSupply.minus(amount).toNumber()); - UtilityTokenAbstract_utils.checkBurntEvent(result.logs[0], UUID, 0, amount, postBurnTotalSupply); + UtilityTokenAbstract_utils.checkBurntEvent(result.logs[0], uuid, 0, amount, postBurnTotalSupply); }) - }) }) diff --git a/test/UtilityTokenAbstract_utils.js b/test/UtilityTokenAbstract_utils.js index b0f29242..5be73099 100644 --- a/test/UtilityTokenAbstract_utils.js +++ b/test/UtilityTokenAbstract_utils.js @@ -19,18 +19,22 @@ // // ---------------------------------------------------------------------------- +var Hasher = artifacts.require("./Hasher.sol"); var UtilityTokenAbstract = artifacts.require("./UtilityTokenAbstractMock.sol"); /// @dev Deploy module.exports.deployUtilityTokenAbstract = async (artifacts, accounts) => { - /// mock unique identifier for utility token - const UUID = "0xbce8a3809c9356cf0e5178a2aef207f50df7d32b388c8fceb8e363df00efce31"; + const hasher = await Hasher.new(); /// mock OpenST protocol contract address with an external account - const openSTProtocol = accounts[4]; - - const utilityTokenAbstract = await UtilityTokenAbstract.new(openSTProtocol, UUID, { from: accounts[0] }); + const openSTProtocol = accounts[4]; + const conversionRate = 10; + const genesisChainIdValue = 3; + const genesisChainIdUtility = 1410; + const UUID = await hasher.hashUuid.call("symbol", "name", genesisChainIdValue, genesisChainIdUtility, openSTProtocol, conversionRate); + const utilityTokenAbstract = await UtilityTokenAbstract.new(UUID, "symbol", "name", genesisChainIdValue, genesisChainIdUtility, conversionRate, { from: openSTProtocol }); return { + hasher : hasher, utilityTokenAbstract : utilityTokenAbstract }; };