From 554985356af0d55bea819e598543e13eab69dd10 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Wed, 15 Nov 2017 22:54:03 +0530 Subject: [PATCH 01/17] test: setup local unit testframework --- contracts/OpenSTInterface.sol | 23 + contracts/OpenSTUtility.sol | 35 ++ contracts/OpenSTValue.sol | 35 ++ contracts/SimpleStake.sol | 150 +++++++ contracts/SimpleToken/ERC20Interface.sol | 34 ++ contracts/SimpleToken/ERC20Token.sol | 111 +++++ contracts/SimpleToken/OpsManaged.sol | 112 +++++ contracts/SimpleToken/Owned.sol | 62 +++ contracts/SimpleToken/README.md | 3 + contracts/SimpleToken/SafeMath.sol | 52 +++ contracts/SimpleToken/SimpleToken.sol | 107 +++++ contracts/SimpleToken/SimpleTokenConfig.sol | 21 + migrations/1_initial_migration.js | 5 + migrations/2_deploy_contracts.js | 8 + test/OpenST.js | 21 + test/SimpleStake.js | 24 ++ test/SimpleToken.js | 449 ++++++++++++++++++++ test/SimpleToken_utils.js | 44 ++ test/lib/utils.js | 191 +++++++++ tools/runTestRpc.sh | 2 + truffle.js | 9 + truffle/contracts/ConvertLib.sol | 8 + truffle/contracts/MetaCoin.sol | 34 ++ truffle/contracts/Migrations.sol | 23 + truffle/test/TestMetacoin.sol | 25 ++ truffle/test/metacoin.js | 63 +++ 26 files changed, 1651 insertions(+) create mode 100644 contracts/OpenSTInterface.sol create mode 100644 contracts/OpenSTUtility.sol create mode 100644 contracts/OpenSTValue.sol create mode 100644 contracts/SimpleStake.sol create mode 100644 contracts/SimpleToken/ERC20Interface.sol create mode 100644 contracts/SimpleToken/ERC20Token.sol create mode 100644 contracts/SimpleToken/OpsManaged.sol create mode 100644 contracts/SimpleToken/Owned.sol create mode 100644 contracts/SimpleToken/README.md create mode 100644 contracts/SimpleToken/SafeMath.sol create mode 100644 contracts/SimpleToken/SimpleToken.sol create mode 100644 contracts/SimpleToken/SimpleTokenConfig.sol create mode 100644 migrations/1_initial_migration.js create mode 100644 migrations/2_deploy_contracts.js create mode 100644 test/OpenST.js create mode 100644 test/SimpleStake.js create mode 100644 test/SimpleToken.js create mode 100644 test/SimpleToken_utils.js create mode 100644 test/lib/utils.js create mode 100755 tools/runTestRpc.sh create mode 100644 truffle.js create mode 100644 truffle/contracts/ConvertLib.sol create mode 100644 truffle/contracts/MetaCoin.sol create mode 100644 truffle/contracts/Migrations.sol create mode 100644 truffle/test/TestMetacoin.sol create mode 100644 truffle/test/metacoin.js diff --git a/contracts/OpenSTInterface.sol b/contracts/OpenSTInterface.sol new file mode 100644 index 00000000..19fae9fc --- /dev/null +++ b/contracts/OpenSTInterface.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// OpenST protocol interface +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + diff --git a/contracts/OpenSTUtility.sol b/contracts/OpenSTUtility.sol new file mode 100644 index 00000000..4da9bfa4 --- /dev/null +++ b/contracts/OpenSTUtility.sol @@ -0,0 +1,35 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// OpenST - Value staking contract for OpenST Platform v0.9 on value chain +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +import "./SafeMath.sol"; + +/// @title OpenST Utility +/// @notice +contract OpenSTUtility { + using SafeMath for uint256; + + /* + * Storage + */ + // mapping() +} \ No newline at end of file diff --git a/contracts/OpenSTValue.sol b/contracts/OpenSTValue.sol new file mode 100644 index 00000000..f3bee8e4 --- /dev/null +++ b/contracts/OpenSTValue.sol @@ -0,0 +1,35 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// OpenST - Value staking contract for OpenST Platform v0.9 on value chain +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +import "./SafeMath.sol"; + +/// @title OpenSTValue - value staking contract for OpenST +/// @notice +contract OpenSTValue { + using SafeMath for uint256; + + /* + * Storage + */ + // mapping() +} \ No newline at end of file diff --git a/contracts/SimpleStake.sol b/contracts/SimpleStake.sol new file mode 100644 index 00000000..91ce3cac --- /dev/null +++ b/contracts/SimpleStake.sol @@ -0,0 +1,150 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// SimpleStake - holds the value for a utility token on the OpenST platform +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +import "./EIP20Interface.sol"; +import "./SafeMath.sol"; + +/// @title SimpleStake - holds the value of an EIP20 token +/// for a utility token on the OpenST platform +/// @author OpenST Ltd. +contract SimpleStake { + using SafeMath for uint256; + + /* + * Events + */ + event ReleasedStake(address indexed _protocol, address indexed _to, uint256 _amount); + event ProtocolTransferInitiated(address indexed _existingProtocol, address indexed _proposedProtocol, uint256 _activationHeight); + event ProtocolTransferRevoked(address indexed _existingProtocol, address indexed _revokedProtocol); + event ProtocolTransferCompleted(address indexed _newProtocol); + + /* + * Constants + */ + /// Blocks to wait before the protocol transfer can be completed + /// This allows anyone with a stake to unstake under the existing + /// protocol if they disagree with the new proposed protocol + /// @dev from OpenST ^v1.0 this constant will be set + uint256 constant public PROTOCOL_TRANSFER_BLOCKS_TO_WAIT = 1; + + /* + * Storage + */ + /// EIP20 token contract that can be staked + EIP20Interface public eip20Token; + /// UUID for the utility token + bytes32 public uuid; + + /// OpenST protocol contract + address public openSTProtocol; + /// proposed OpenST protocol + address public proposedProtocol; + /// earliest protocol transfer height + uint256 public earliestTransferHeight; + + /* + * Modifiers + */ + modifier onlyProtocol() { + require(msg.sender == openSTProtocol); + _; + } + + modifier notNull(address _address) { + if (_address == 0) + revert(); + _; + } + + /* + * Public functions + */ + /// @dev Contract constructor sets initial owner and EIP20 token that + /// @param _eip20Token EIP20 token contract that will be staked + /// @param _openSTProtocol OpenSTProtocol contract that governs staking + /// @param _uuid Unique Universal Identifier of the registered utility token + function SimpleStake( + EIP20Interface _eip20Token, + address _openSTProtocol, + bytes32 _uuid) + public + { + eip20Token = _eip20Token; + openSTProtocol = _openSTProtocol; + uuid = _uuid; + } + + /// @dev Allows the protocol to release the staked amount + /// into provided address + /// @param _to Beneficiary of the amount of the stake + /// @param _amount Amount of stake to release to beneficiary + function releaseTo(address _to, uint256 _amount) + public + onlyProtocol + returns (bool) + { + require(eip20Token.transfer(_to, _amount)); + + ReleasedStake(msg.sender, _to, _amount); + + return true; + } + + function initiateProtocolTransfer( + address _proposedProtocol) + public + onlyProtocol + returns (bool) + { + earliestTransferHeight = block.number + PROTOCOL_TRANSFER_BLOCKS_TO_WAIT; + proposedProtocol = _proposedProtocol; + + ProtocolTransferInitiated(openSTProtocol, _proposedProtocol, earliestTransferHeight); + + return true; + } + + + function completeProtocolTransfer() public returns (bool) { + require(msg.sender == proposedProtocol); + + openSTProtocol = proposedProtocol; + proposedProtocol = address(0); + + ProtocolTransferCompleted(openSTProtocol); + + return true; + } + + /* + * Web3 call functions + */ + /// @dev + function getTotalStake() + public + constant + returns (uint256) + { + return eip20Token.balanceOf(this); + } +} \ No newline at end of file diff --git a/contracts/SimpleToken/ERC20Interface.sol b/contracts/SimpleToken/ERC20Interface.sol new file mode 100644 index 00000000..4238ee23 --- /dev/null +++ b/contracts/SimpleToken/ERC20Interface.sol @@ -0,0 +1,34 @@ +pragma solidity ^0.4.17; + +// ---------------------------------------------------------------------------- +// Standard ERC20 Interface +// +// Copyright (c) 2017 OpenST Ltd. +// https://simpletoken.org/ +// +// The MIT Licence. +// ---------------------------------------------------------------------------- + + +// ---------------------------------------------------------------------------- +// Based on the 'final' ERC20 token standard as specified at: +// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md +// ---------------------------------------------------------------------------- + +contract ERC20Interface { + + event Transfer(address indexed _from, address indexed _to, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _value); + + function name() public view returns (string); + function symbol() public view returns (string); + function decimals() public view returns (uint8); + function totalSupply() public view returns (uint256); + + function balanceOf(address _owner) public view returns (uint256 balance); + function allowance(address _owner, address _spender) public view returns (uint256 remaining); + + function transfer(address _to, uint256 _value) public returns (bool success); + function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); + function approve(address _spender, uint256 _value) public returns (bool success); +} diff --git a/contracts/SimpleToken/ERC20Token.sol b/contracts/SimpleToken/ERC20Token.sol new file mode 100644 index 00000000..bb915a54 --- /dev/null +++ b/contracts/SimpleToken/ERC20Token.sol @@ -0,0 +1,111 @@ +pragma solidity ^0.4.17; + +// ---------------------------------------------------------------------------- +// Standard ERC20 Token Implementation +// +// Copyright (c) 2017 OpenST Ltd. +// https://simpletoken.org/ +// +// The MIT Licence. +// ---------------------------------------------------------------------------- + +import "./ERC20Interface.sol"; +import "./Owned.sol"; +import "./SafeMath.sol"; + + +// +// Standard ERC20 implementation, with ownership. +// +contract ERC20Token is ERC20Interface, Owned { + + using SafeMath for uint256; + + string private tokenName; + string private tokenSymbol; + uint8 private tokenDecimals; + uint256 internal tokenTotalSupply; + + mapping(address => uint256) balances; + mapping(address => mapping (address => uint256)) allowed; + + + function ERC20Token(string _symbol, string _name, uint8 _decimals, uint256 _totalSupply) public + Owned() + { + tokenSymbol = _symbol; + tokenName = _name; + tokenDecimals = _decimals; + tokenTotalSupply = _totalSupply; + balances[owner] = _totalSupply; + + // According to the ERC20 standard, a token contract which creates new tokens should trigger + // a Transfer event and transfers of 0 values must also fire the event. + Transfer(0x0, owner, _totalSupply); + } + + + function name() public view returns (string) { + return tokenName; + } + + + function symbol() public view returns (string) { + return tokenSymbol; + } + + + function decimals() public view returns (uint8) { + return tokenDecimals; + } + + + function totalSupply() public view returns (uint256) { + return tokenTotalSupply; + } + + + function balanceOf(address _owner) public view returns (uint256) { + return balances[_owner]; + } + + + function allowance(address _owner, address _spender) public view returns (uint256 remaining) { + return allowed[_owner][_spender]; + } + + + function transfer(address _to, uint256 _value) public returns (bool success) { + // According to the EIP20 spec, "transfers of 0 values MUST be treated as normal + // transfers and fire the Transfer event". + // Also, should throw if not enough balance. This is taken care of by SafeMath. + balances[msg.sender] = balances[msg.sender].sub(_value); + balances[_to] = balances[_to].add(_value); + + Transfer(msg.sender, _to, _value); + + return true; + } + + + function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { + balances[_from] = balances[_from].sub(_value); + allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); + balances[_to] = balances[_to].add(_value); + + Transfer(_from, _to, _value); + + return true; + } + + + function approve(address _spender, uint256 _value) public returns (bool success) { + + allowed[msg.sender][_spender] = _value; + + Approval(msg.sender, _spender, _value); + + return true; + } + +} diff --git a/contracts/SimpleToken/OpsManaged.sol b/contracts/SimpleToken/OpsManaged.sol new file mode 100644 index 00000000..5bbc84c5 --- /dev/null +++ b/contracts/SimpleToken/OpsManaged.sol @@ -0,0 +1,112 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// Admin / Ops Permission Model +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +import "./Owned.sol"; + +/** + @title OpsManaged + @notice Implements OpenST ownership and permission model +*/ +contract OpsManaged is Owned { + + address public opsAddress; + address public adminAddress; + + event AdminAddressChanged(address indexed _newAddress); + event OpsAddressChanged(address indexed _newAddress); + + + function OpsManaged() public + Owned() + { + } + + + modifier onlyAdmin() { + require(isAdmin(msg.sender)); + _; + } + + + modifier onlyAdminOrOps() { + require(isAdmin(msg.sender) || isOps(msg.sender)); + _; + } + + + modifier onlyOwnerOrAdmin() { + require(isOwner(msg.sender) || isAdmin(msg.sender)); + _; + } + + + modifier onlyOps() { + require(isOps(msg.sender)); + _; + } + + + function isAdmin(address _address) internal view returns (bool) { + return (adminAddress != address(0) && _address == adminAddress); + } + + + function isOps(address _address) internal view returns (bool) { + return (opsAddress != address(0) && _address == opsAddress); + } + + + function isOwnerOrOps(address _address) internal view returns (bool) { + return (isOwner(_address) || isOps(_address)); + } + + + // Owner and Admin can change the admin address. Address can also be set to 0 to 'disable' it. + function setAdminAddress(address _adminAddress) external onlyOwnerOrAdmin returns (bool) { + require(_adminAddress != owner); + require(_adminAddress != address(this)); + require(!isOps(_adminAddress)); + + adminAddress = _adminAddress; + + AdminAddressChanged(_adminAddress); + + return true; + } + + + // Owner and Admin can change the operations address. Address can also be set to 0 to 'disable' it. + function setOpsAddress(address _opsAddress) external onlyOwnerOrAdmin returns (bool) { + require(_opsAddress != owner); + require(_opsAddress != address(this)); + require(!isAdmin(_opsAddress)); + + opsAddress = _opsAddress; + + OpsAddressChanged(_opsAddress); + + return true; + } +} + + diff --git a/contracts/SimpleToken/Owned.sol b/contracts/SimpleToken/Owned.sol new file mode 100644 index 00000000..6696c6ee --- /dev/null +++ b/contracts/SimpleToken/Owned.sol @@ -0,0 +1,62 @@ +pragma solidity ^0.4.17; + +// ---------------------------------------------------------------------------- +// Basic Ownership Implementation +// +// Copyright (c) 2017 OpenST Ltd. +// https://simpletoken.org/ +// +// The MIT Licence. +// ---------------------------------------------------------------------------- + + +// +// Implements basic ownership with 2-step transfers. +// +contract Owned { + + address public owner; + address public proposedOwner; + + event OwnershipTransferInitiated(address indexed _proposedOwner); + event OwnershipTransferCompleted(address indexed _newOwner); + + + function Owned() public { + owner = msg.sender; + } + + + modifier onlyOwner() { + require(isOwner(msg.sender)); + _; + } + + + function isOwner(address _address) internal view returns (bool) { + return (_address == owner); + } + + + function initiateOwnershipTransfer(address _proposedOwner) public onlyOwner returns (bool) { + proposedOwner = _proposedOwner; + + OwnershipTransferInitiated(_proposedOwner); + + return true; + } + + + function completeOwnershipTransfer() public returns (bool) { + require(msg.sender == proposedOwner); + + owner = proposedOwner; + proposedOwner = address(0); + + OwnershipTransferCompleted(owner); + + return true; + } +} + + diff --git a/contracts/SimpleToken/README.md b/contracts/SimpleToken/README.md new file mode 100644 index 00000000..a8748fa0 --- /dev/null +++ b/contracts/SimpleToken/README.md @@ -0,0 +1,3 @@ +# Simple Token EIP20 + +Include the contract code of the deployed Simple Token EIP20 contracts here for development ease. The official Simple Token repository is at [github.com/OpenSTFoundation/SimpleTokenSale](github.com/OpenSTFoundation/SimpleTokenSale). Simple Token has been deployed and went live on Ethereum mainnet on 14 November 2017 13 UTC at [0x2C4e8f2D746113d0696cE89B35F0d8bF88E0AEcA](https://etherscan.io/address/0x2c4e8f2d746113d0696ce89b35f0d8bf88e0aeca) \ No newline at end of file diff --git a/contracts/SimpleToken/SafeMath.sol b/contracts/SimpleToken/SafeMath.sol new file mode 100644 index 00000000..c3c635a7 --- /dev/null +++ b/contracts/SimpleToken/SafeMath.sol @@ -0,0 +1,52 @@ +pragma solidity ^0.4.17; + +// ---------------------------------------------------------------------------- +// SafeMath Library Implementation +// +// Copyright (c) 2017 OpenST Ltd. +// https://simpletoken.org/ +// +// The MIT Licence. +// +// Based on the SafeMath library by the OpenZeppelin team. +// Copyright (c) 2016 Smart Contract Solutions, Inc. +// https://github.com/OpenZeppelin/zeppelin-solidity +// The MIT License. +// ---------------------------------------------------------------------------- + + +library SafeMath { + + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a * b; + + assert(a == 0 || c / a == b); + + return c; + } + + + function div(uint256 a, uint256 b) internal pure returns (uint256) { + // 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 c; + } + + + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + assert(b <= a); + + return a - b; + } + + + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + + assert(c >= a); + + return c; + } +} diff --git a/contracts/SimpleToken/SimpleToken.sol b/contracts/SimpleToken/SimpleToken.sol new file mode 100644 index 00000000..ae793094 --- /dev/null +++ b/contracts/SimpleToken/SimpleToken.sol @@ -0,0 +1,107 @@ +pragma solidity ^0.4.17; + +// ---------------------------------------------------------------------------- +// Simple Token Contract +// +// Copyright (c) 2017 OpenST Ltd. +// https://simpletoken.org/ +// +// The MIT Licence. +// ---------------------------------------------------------------------------- + + +import "./ERC20Token.sol"; +import "./SimpleTokenConfig.sol"; +import "./OpsManaged.sol"; + + +// +// SimpleToken is a standard ERC20 token with some additional functionality: +// - It has a concept of finalize +// - Before finalize, nobody can transfer tokens except: +// - Owner and operations can transfer tokens +// - Anybody can send back tokens to owner +// - After finalize, no restrictions on token transfers +// + +// +// Permissions, according to the ST key management specification. +// +// Owner Admin Ops +// transfer (before finalize) x x +// transferForm (before finalize) x x +// finalize x +// + +contract SimpleToken is ERC20Token, OpsManaged, SimpleTokenConfig { + + bool public finalized; + + + // Events + event Burnt(address indexed _from, uint256 _amount); + event Finalized(); + + + function SimpleToken() public + ERC20Token(TOKEN_SYMBOL, TOKEN_NAME, TOKEN_DECIMALS, TOKENS_MAX) + OpsManaged() + { + finalized = false; + } + + + // Implementation of the standard transfer method that takes into account the finalize flag. + function transfer(address _to, uint256 _value) public returns (bool success) { + checkTransferAllowed(msg.sender, _to); + + return super.transfer(_to, _value); + } + + + // Implementation of the standard transferFrom method that takes into account the finalize flag. + function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { + checkTransferAllowed(msg.sender, _to); + + return super.transferFrom(_from, _to, _value); + } + + + function checkTransferAllowed(address _sender, address _to) private view { + if (finalized) { + // Everybody should be ok to transfer once the token is finalized. + return; + } + + // Owner and Ops are allowed to transfer tokens before the sale is finalized. + // This allows the tokens to move from the TokenSale contract to a beneficiary. + // We also allow someone to send tokens back to the owner. This is useful among other + // cases, for the Trustee to transfer unlocked tokens back to the owner (reclaimTokens). + require(isOwnerOrOps(_sender) || _to == owner); + } + + // Implement a burn function to permit msg.sender to reduce its balance + // which also reduces tokenTotalSupply + function burn(uint256 _value) public returns (bool success) { + require(_value <= balances[msg.sender]); + + balances[msg.sender] = balances[msg.sender].sub(_value); + tokenTotalSupply = tokenTotalSupply.sub(_value); + + Burnt(msg.sender, _value); + + return true; + } + + + // Finalize method marks the point where token transfers are finally allowed for everybody. + function finalize() external onlyAdmin returns (bool success) { + require(!finalized); + + finalized = true; + + Finalized(); + + return true; + } +} diff --git a/contracts/SimpleToken/SimpleTokenConfig.sol b/contracts/SimpleToken/SimpleTokenConfig.sol new file mode 100644 index 00000000..300faede --- /dev/null +++ b/contracts/SimpleToken/SimpleTokenConfig.sol @@ -0,0 +1,21 @@ +pragma solidity ^0.4.17; + +// ---------------------------------------------------------------------------- +// Token Configuration +// +// Copyright (c) 2017 OpenST Ltd. +// https://simpletoken.org/ +// +// The MIT Licence. +// ---------------------------------------------------------------------------- + + +contract SimpleTokenConfig { + + string public constant TOKEN_SYMBOL = "ST"; + string public constant TOKEN_NAME = "Simple Token"; + uint8 public constant TOKEN_DECIMALS = 18; + + uint256 public constant DECIMALSFACTOR = 10**uint256(TOKEN_DECIMALS); + uint256 public constant TOKENS_MAX = 800000000 * DECIMALSFACTOR; +} diff --git a/migrations/1_initial_migration.js b/migrations/1_initial_migration.js new file mode 100644 index 00000000..4d5f3f9b --- /dev/null +++ b/migrations/1_initial_migration.js @@ -0,0 +1,5 @@ +var Migrations = artifacts.require("./Migrations.sol"); + +module.exports = function(deployer) { + deployer.deploy(Migrations); +}; diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js new file mode 100644 index 00000000..b3dc3e90 --- /dev/null +++ b/migrations/2_deploy_contracts.js @@ -0,0 +1,8 @@ +var ConvertLib = artifacts.require("./ConvertLib.sol"); +var MetaCoin = artifacts.require("./MetaCoin.sol"); + +module.exports = function(deployer) { + deployer.deploy(ConvertLib); + deployer.link(ConvertLib, MetaCoin); + deployer.deploy(MetaCoin); +}; diff --git a/test/OpenST.js b/test/OpenST.js new file mode 100644 index 00000000..e8f697ef --- /dev/null +++ b/test/OpenST.js @@ -0,0 +1,21 @@ +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// test/OpenST.js +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + diff --git a/test/SimpleStake.js b/test/SimpleStake.js new file mode 100644 index 00000000..e95c8884 --- /dev/null +++ b/test/SimpleStake.js @@ -0,0 +1,24 @@ +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// test/SimpleStake.js +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +const Utils = require('./lib/utils.js'); + +const BigNumber = require('bignumber.js'); diff --git a/test/SimpleToken.js b/test/SimpleToken.js new file mode 100644 index 00000000..8ce7d5da --- /dev/null +++ b/test/SimpleToken.js @@ -0,0 +1,449 @@ +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// test/SimpleToken.js +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +const Assert = require('assert'); +const Utils = require('./lib/utils.js') + +const Moment = require('moment') +const BigNumber = require('bignumber.js') + +const SimpleToken = artifacts.require("./SimpleToken.sol") + + +// +// Basic properties +// name +// symbol +// decimals +// totalSupply +// balances is private +// Constructor raised transfer event +// +// transfer before finalize +// transfer from owner to other +// transfer 0 tokens +// transfer > balance +// transfer = balance +// transfer 1 token +// transfer 10000 tokens +// +// transfer after finalize +// transfer 0 tokens +// transfer > balance +// transfer = balance +// transfer 1 token +// transfer 10000 tokens +// +// transferFrom +// transfer 0 from account 0 -> 1 with 0 allowance +// transfer 1000 from account 0 -> 1 without allowance +// transfer 1000 from account 0 -> 1 with 10 allowance +// transfer 1000 from account 0 -> 1 with 1000 allowance +// transfer 50+50 from account 0 -> 1 with 100 allowance +// transfer 1000 from account 0 -> 1 with 999 allowance +// transfer 1 from account 0 -> 1 with 0 allowance +// +// transferFrom after finalize +// transfer 0 from account 0 -> 1 with 0 allowance +// transfer 1000 from account 0 -> 1 without allowance +// transfer 1000 from account 0 -> 1 with 10 allowance +// transfer 1000 from account 0 -> 1 with 1000 allowance +// transfer 50+50 from account 0 -> 1 with 100 allowance +// transfer 1000 from account 0 -> 1 with 999 allowance +// transfer 1 from account 0 -> 1 with 0 allowance +// +// approve +// balanceOf +// allowance +// * covered indirectly by testing the other functions +// +// burn +// burn greater than balance +// burn less than or equal to balance +// +// balances +// check if balances is exposed publicly +// +// owner and operations +// - owner is set +// - admin is 0 +// - operations is 0 +// - set operations key +// - set operations key +// - finalize (owner + ops) +// +// finalize +// - check properties before and after finalize +// - try to finalize a 2nd time +// * other cases covered indirectly by testing other functions +// + +contract('SimpleToken', (accounts) => { + + const DECIMALSFACTOR = new BigNumber('10').pow('18') + + const SYMBOL = "ST" + const NAME = "Simple Token" + const DECIMALS = 18 + const TOTAL_SUPPLY = new BigNumber('800000000').mul(DECIMALSFACTOR) + + const owner = accounts[0] + const admin = accounts[1] + const ops = accounts[2] + + async function createToken() { + return await SimpleToken.new() + } + + + describe('Basic properties', async () => { + + var token = null + + before(async () => { + token = await createToken() + }) + + + it("name", async () => { + assert.equal(await token.name.call(), NAME) + }) + + it("symbol", async () => { + assert.equal(await token.symbol.call(), SYMBOL) + }) + + it("decimals", async () => { + assert.equal(await token.decimals.call(), DECIMALS) + }) + + it("totalSupply", async () => { + assert.equal((await token.totalSupply.call()).toNumber(), TOTAL_SUPPLY.toNumber()) + }) + + it("balances is private", async () => { + assert.isTrue(typeof(token.balances) == 'undefined') + }) + + it('Constructor raised transfer event', async () => { + const receipt = await web3.eth.getTransactionReceipt(token.transactionHash) + assert.equal(receipt.logs.length, 1) + const logs = Utils.decodeLogs(token.abi, [ receipt.logs[0] ]) + Utils.checkTransferEvent(logs[0], 0, accounts[0], TOTAL_SUPPLY) + }) + }) + + + describe('transfer function after finalize', async () => { + + var token = null + + before(async () => { + token = await createToken() + + await token.setOpsAddress(ops) + await token.setAdminAddress(admin) + + await token.finalize({ from: admin }) + }) + + it("transfer tokens from owner to other", async () => { + Utils.checkTransferEventGroup(await token.transfer(accounts[1], 1000), accounts[0], accounts[1], 1000) + }) + + it("transfer 0 tokens", async () => { + assert.equal(await token.transfer.call(accounts[2], 0, { from: accounts[1] }), true) + Utils.checkTransferEventGroup(await token.transfer(accounts[2], 0, { from: accounts[1] }), accounts[1], accounts[2], 0) + }) + + it("transfer > balance", async () => { + const balance = await token.balanceOf.call(accounts[1]) + await Utils.expectThrow(token.transfer.call(accounts[2], balance.add(1), { from: accounts[1] })) + }) + + it("transfer = balance", async () => { + const balance1Before = await token.balanceOf.call(accounts[1]) + const balance2Before = await token.balanceOf.call(accounts[2]) + + assert.equal(await token.transfer.call(accounts[2], balance1Before, { from: accounts[1] }), true) + await token.transfer(accounts[2], balance1Before, { from: accounts[1] }) + + const balance1After = await token.balanceOf.call(accounts[1]) + const balance2After = await token.balanceOf.call(accounts[2]) + + assert.equal(balance1After.toNumber(), 0) + assert.equal(balance2After.sub(balance2Before).toNumber(), balance1Before.sub(balance1After).toNumber(), balance1Before.toNumber()) + }) + + it("transfer 1 token", async () => { + const balance1Before = await token.balanceOf.call(accounts[1]) + const balance2Before = await token.balanceOf.call(accounts[2]) + + assert.equal(await token.transfer.call(accounts[1], 1, { from: accounts[2] }), true) + await token.transfer(accounts[1], 1, { from: accounts[2] }) + + const balance1After = await token.balanceOf.call(accounts[1]) + const balance2After = await token.balanceOf.call(accounts[2]) + + assert.equal(balance1After.toNumber(), 1) + assert.equal(balance2After.toNumber(), balance2Before.sub(1).toNumber()) + }) + }) + + + describe('transferFrom function before finalize', async () => { + + var token = null + + before(async () => { + token = await createToken() + + await token.setOpsAddress(ops) + await token.setAdminAddress(admin) + + await token.transfer(accounts[4], 10000) + }) + + + it("transfer 0 from account 1 -> 2 with 0 allowance", async () => { + assert.equal(await token.approve.call(accounts[2], 0, { from: accounts[4] }), true) + assert.equal(await token.allowance.call(accounts[4], accounts[2]), 0) + await Utils.expectThrow(token.transferFrom.call(accounts[4], accounts[2], 10, { from: accounts[2] })) + }) + + it("transfer 1000 from account 1 -> 2 without allowance", async () => { + await Utils.expectThrow(token.transferFrom.call(accounts[4], accounts[2], 1000, { from: accounts[4] })) + await Utils.expectThrow(token.transferFrom.call(accounts[4], accounts[2], 1000, { from: accounts[2] })) + }) + + it("transfer 1000 from account 1 -> 2 with 10 allowance", async () => { + assert.equal(await token.approve.call(accounts[2], 10, { from: accounts[4] }), true) + Utils.checkApprovalEventGroup(await token.approve(accounts[2], 10, { from: accounts[4] }), accounts[4], accounts[2], 10) + + assert.equal((await token.allowance.call(accounts[4], accounts[2], { from: accounts[4] })).toNumber(), 10) + + await Utils.expectThrow(token.transferFrom.call(accounts[4], accounts[2], 1000, { from: accounts[4] })) + await Utils.expectThrow(token.transferFrom.call(accounts[4], accounts[2], 1000, { from: accounts[2] })) + }) + + it("transfer 1000 from account 1 -> 2 with 1000 allowance (as ops)", async () => { + // We first need to bring approval to 0 + assert.equal(await token.approve.call(ops, 0, { from: accounts[4] }), true) + Utils.checkApprovalEventGroup(await token.approve(ops, 0, { from: accounts[4] }), accounts[4], ops, 0) + + assert.equal(await token.allowance.call(accounts[4], ops, { from: accounts[4] }), 0) + + assert.equal(await token.approve.call(ops, 1000, { from: accounts[4] }), true) + Utils.checkApprovalEventGroup(await token.approve(ops, 1000, { from: accounts[4] }), accounts[4], ops, 1000) + + assert.equal(await token.allowance.call(accounts[4], ops), 1000, { from: accounts[4] }) + + await Utils.expectThrow(token.transferFrom.call(accounts[4], ops, 1000, { from: accounts[4] })) + assert.equal(await token.transferFrom.call(accounts[4], ops, 1000, { from: ops }), true) + await token.transferFrom(accounts[4], ops, 1000, { from: ops }) + + assert.equal((await token.balanceOf.call(accounts[4])).toNumber(), 9000) + assert.equal((await token.balanceOf.call(ops)).toNumber(), 1000) + }) + + it("transfer 1000 from account 1 -> 2 with 1000 allowance (as admin)", async () => { + // We first need to bring approval to 0 + assert.equal(await token.approve.call(admin, 0, { from: accounts[4] }), true) + Utils.checkApprovalEventGroup(await token.approve(admin, 0, { from: accounts[4] }), accounts[4], admin, 0) + + assert.equal(await token.allowance.call(accounts[4], admin, { from: accounts[4] }), 0) + + assert.equal(await token.approve.call(admin, 1000, { from: accounts[4] }), true) + Utils.checkApprovalEventGroup(await token.approve(admin, 1000, { from: accounts[4] }), accounts[4], admin, 1000) + + assert.equal(await token.allowance.call(accounts[4], admin), 1000, { from: accounts[4] }) + + await Utils.expectThrow(token.transferFrom.call(accounts[4], admin, 1000, { from: accounts[4] })) + await Utils.expectThrow(token.transferFrom.call(accounts[4], admin, 1000, { from: admin })) + }) + }) + + + describe('transferFrom function after finalize', async () => { + + var token = null + + before(async () => { + token = await createToken() + + await token.setOpsAddress(ops) + await token.setAdminAddress(admin) + + await token.transfer(accounts[1], 10000) + + token.finalize({ from: admin }) + }) + + + it("transfer 0 from account 1 -> 2 with 0 allowance", async () => { + assert.equal(await token.approve.call(accounts[2], 0, { from: accounts[1] }), true) + assert.equal(await token.allowance.call(accounts[1], accounts[2]), 0) + assert.equal(await token.transferFrom.call(accounts[1], accounts[2], 0, { from: accounts[1] }), true) + assert.equal(await token.transferFrom.call(accounts[1], accounts[2], 0, { from: accounts[2] }), true) + }) + + it("transfer 1000 from account 1 -> 2 without allowance", async () => { + await Utils.expectThrow(token.transferFrom.call(accounts[1], accounts[2], 1000, { from: accounts[1] })) + await Utils.expectThrow(token.transferFrom.call(accounts[1], accounts[2], 1000, { from: accounts[2] })) + }) + + it("transfer 1000 from account 1 -> 2 with 10 allowance", async () => { + assert.equal(await token.approve.call(accounts[2], 10, { from: accounts[1] }), true) + await token.approve(accounts[2], 10, { from: accounts[1] }) + assert.equal(await token.allowance.call(accounts[1], accounts[2]), 10) + await Utils.expectThrow(token.transferFrom.call(accounts[1], accounts[2], 1000, { from: accounts[1] })) + await Utils.expectThrow(token.transferFrom.call(accounts[1], accounts[2], 1000, { from: accounts[2] })) + }) + + it("transfer 1000 from account 1 -> 2 with 1000 allowance", async () => { + // We first need to bring approval to 0 + assert.equal(await token.approve.call(accounts[2], 0, { from: accounts[1] }), true) + await token.approve(accounts[2], 0, { from: accounts[1] }) + assert.equal(await token.allowance.call(accounts[1], accounts[2]), 0) + + assert.equal(await token.approve.call(accounts[2], 1000, { from: accounts[1] }), true) + await token.approve(accounts[2], 1000, { from: accounts[1] }) + assert.equal(await token.allowance.call(accounts[1], accounts[2]), 1000) + + await Utils.expectThrow(token.transferFrom.call(accounts[1], accounts[2], 1000, { from: accounts[1] })) + assert.equal(await token.transferFrom.call(accounts[1], accounts[2], 1000, { from: accounts[2] }), true) + await token.transferFrom(accounts[1], accounts[2], 1000, { from: accounts[2] }) + + assert.equal((await token.balanceOf.call(accounts[1])).toNumber(), 9000) + assert.equal((await token.balanceOf.call(accounts[2])).toNumber(), 1000) + }) + }) + + + describe('owner and operations', async () => { + + var token = null + + + before(async () => { + token = await createToken() + + await token.setOpsAddress(ops) + await token.setAdminAddress(admin) + + await token.transfer(accounts[1], 10000) + await token.transfer(accounts[2], 1000) + }) + + + it("check initial owner", async () => { + assert.equal(await token.owner.call(), accounts[0]) + }) + + it("check initial admin", async () => { + assert.equal(await token.adminAddress.call(), accounts[1]) + }) + + it("check initial ops", async () => { + assert.equal(await token.opsAddress.call(), accounts[2]) + }) + + it("change ops address to some account", async () => { + assert.equal(await token.setOpsAddress.call(accounts[5]), true) + await token.setOpsAddress(accounts[5]) + }) + + it("change ops address to 0", async () => { + assert.equal(await token.setOpsAddress.call(0), true) + await token.setOpsAddress(0) + }) + + it("change ops address to account 3", async () => { + assert.equal(await token.setOpsAddress.call(accounts[3]), true) + await token.setOpsAddress(accounts[3]) + }) + + it("finalize as normal", async () => { + await Utils.expectThrow(token.finalize.call({ from: accounts[4] })) + }) + + it("finalize as ops", async () => { + await Utils.expectThrow(token.finalize.call({ from: ops })) + }) + + it("finalize as admin", async () => { + assert.equal(await token.finalize.call({ from: admin }), true) + }) + }) + + + describe('finalize function', async () => { + + var token = null + + before(async () => { + token = await createToken() + + await token.setOpsAddress(ops) + await token.setAdminAddress(admin) + }) + + + it("check properties before and after finalize", async () => { + assert.equal(await token.finalized.call(), false) + Utils.checkFinalizedEventGroup(await token.finalize({ from: admin })) + assert.equal(await token.finalized.call(), true) + }) + + it("try to finalize a 2nd time", async () => { + await Utils.expectThrow(token.finalize.call({ from: admin })) + }) + }) + + + describe('burn function', async () => { + + var token = null + + before(async () => { + token = await createToken() + + await token.setOpsAddress(ops) + await token.setAdminAddress(admin) + }) + + it("burn greater than balance", async () => { + await Utils.expectThrow(token.burn.call((TOTAL_SUPPLY.plus(1)), { from: accounts[0] })) + }) + + it("burn less than or equal to balance", async () => { + const balanceBefore = await token.balanceOf(accounts[0]) + + Utils.checkBurntEventGroup(await token.burn(1000, { from: accounts[0] })) + + const currentBalance = await token.balanceOf(accounts[0]) + const currentSupply = await token.totalSupply.call() + + assert.equal(balanceBefore.minus(currentBalance).toNumber(), 1000) + assert.equal(TOTAL_SUPPLY.minus(currentSupply).toNumber(), 1000) + }) + }) +}) diff --git a/test/SimpleToken_utils.js b/test/SimpleToken_utils.js new file mode 100644 index 00000000..f6248a11 --- /dev/null +++ b/test/SimpleToken_utils.js @@ -0,0 +1,44 @@ +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// test/SimpleToken_utils.js +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +const Assert = require('assert'); +const BigNumer = require('bignumber.js'); + +/// @dev Assert on Transfer event +module.exports.checkTransferEventGroup = (result, _from, _to, _value) => { + assert.equal(result.logs.length, 1); + + const event = result.logs[0]; + + module.exports.checkTransferEvent(event, _from, _to, _value); +} + + +module.exports.checkTransferEvent = (event, _from, _to, _value) => { + if (Number.isInteger(_value)) { + _value = new BigNumber(_value); + } + + assert.equal(event.event, "Transfer"); + assert.equal(event.args._from, _from); + assert.equal(event.args._to, _to); + assert.equal(event.args._value.toNumber(), _value.toNumber()); +} diff --git a/test/lib/utils.js b/test/lib/utils.js new file mode 100644 index 00000000..27703298 --- /dev/null +++ b/test/lib/utils.js @@ -0,0 +1,191 @@ +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// test/lib/utils.js +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +const Assert = require('assert'); +// const BigNumer = require('bignumber.js'); +// const SolidityEvent = require("web3/lib/web3/event.js"); + +var SimpleToken = artifacts.require("./SimpleToken/SimpleToken.sol") +// var SimpleStake = artifacts.require("./SimpleStake.sol"); + +/// @dev Deploy SimpleToken and finalize it +module.exports.deployContracts = async (artifacts, accounts) => { + + const token = await SimpleToken.new({ from: accounts[0]}, gas: 3500000 }); + + // // Set Simple Token admin to account[1] + // await token.setAdminAddress(accounts[1]); + // // and finalize Simple Token + // Assert.ok(await token.finalize({ from: accounts[1] })); + + return { + token : token + } +} + + +/* + * Basic Ethereum checks + */ + +/// @dev Expect failure from invalid opcode or out of gas, +/// but returns error instead +module.exports.expectThrow = async (promise) => { + try { + await promise; + } catch (error) { + const invalidOpcode = error.message.search('invalid opcode') > -1; + + const outOfGas = error.message.search('out of gas') > -1; + + assert(invalidOpcode || outOfGas, `Expected throw, but got ${error} instead`); + + return; + } + + assert(false, "Did not throw as expected"); +}; + +/// @dev Get account balance +module.exports.getBalance = function (address) { + return new Promise (function (resolve, reject) { + web3.eth.getBalance(address, function (error, result) { + if (error) { + reject(error); + } else { + resolve(result); + } + }) + }) +} + +/// @dev Get gas price +module.exports.getGasPrice = function () { + return new Promise (function (resolve, reject) { + web3.eth.getGasPrice(function (error, result) { + if (error) { + reject(error); + } else { + resolve(result); + } + }) + }) +} + +/// @dev Decode logs with ABI +module.exports.decodeLogs = (abi, logs) => { + return decodeLogs(abi, logs) +} + + +function decodeLogs(abi, logs) { + var decodedLogs = null + try { + decodedLogs = decodeLogsInternal(abi, logs) + } catch(error) { + throw new 'Could not decode receipt log for transaction ' + txID + ', message: ' + error + } + + return decodedLogs +} + + +function decodeLogsInternal(abi, logs) { + + // Find events in the ABI + var abiEvents = abi.filter(json => { + return json.type === 'event' + }) + + if (abiEvents.length === 0) { + return + } + + // Build SolidityEvent objects + var solidityEvents = [] + for (i = 0; i < abiEvents.length; i++) { + solidityEvents.push(new SolidityEvent(null, abiEvents[i], null)) + } + + // Decode each log entry + var decodedLogs = [] + for (i = 0; i < logs.length; i++) { + + var event = null + for (j = 0; j < solidityEvents.length; j++) { + if (solidityEvents[j].signature() == logs[i].topics[0].replace("0x", "")) { + event = solidityEvents[j] + break + } + } + + var decodedLog = null + + if (event != null) { + decodedLog = event.decode(logs[i]) + } else { + // We could not find the right event to decode this log entry, just keep as is. + decodedLog = logs[i] + } + + // Convert bytes32 parameters to ascii + for (j = 0; j < abiEvents.length; j++) { + const abiEvent = abiEvents[j] + + if (!abiEvent.inputs) { + continue + } + + if (abiEvent.name != decodedLog.name) { + continue + } + + for (k = 0; k < abiEvent.inputs; k++) { + if (abiEvent.inputs[k].type == 'bytes32') { + decodedLog.args[abiEvent.inputs[k].name] = hexToAscii(decodedLog.args[abiEvent.inputs[k]]); + } + } + } + + decodedLogs.push(decodedLog) + } + + return decodedLogs +} + + +function hexToAscii(hexStr) { + var asciiStr = '' + + var start = (hex.substring(0, 2) === '0x') ? 2 : 0 + + for (i = start; i < hexStr.length; i += 2) { + var charCode = parseInt(hex.substr(i, 2), 16) + + if (code === 0) { + continue + } + + asciiStr += String.fromCharCode(code); + } + + return asciiStr; +} \ No newline at end of file diff --git a/tools/runTestRpc.sh b/tools/runTestRpc.sh new file mode 100755 index 00000000..685e1c5d --- /dev/null +++ b/tools/runTestRpc.sh @@ -0,0 +1,2 @@ +#!/bin/bash +testrpc --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea0,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea1,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea2,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea3,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea4,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea5,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea6,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea7,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea8,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5ea9,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb0,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb1,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb2,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb3,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb4,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb5,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb6,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb7,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb8,1000000000000000000000000000" --account="0x8fbbbaceff30d4eea3e2ffa2dfedc3c053f78c1f53103e4ddc31309e6b1d5eb9,1000000000000000000000000000" diff --git a/truffle.js b/truffle.js new file mode 100644 index 00000000..2491c7c8 --- /dev/null +++ b/truffle.js @@ -0,0 +1,9 @@ +module.exports = { + networks: { + development: { + host: "localhost", + port: 8545, + network_id: "*" // Match any network id + } + } +}; diff --git a/truffle/contracts/ConvertLib.sol b/truffle/contracts/ConvertLib.sol new file mode 100644 index 00000000..b97c88d7 --- /dev/null +++ b/truffle/contracts/ConvertLib.sol @@ -0,0 +1,8 @@ +pragma solidity ^0.4.4; + +library ConvertLib{ + function convert(uint amount,uint conversionRate) returns (uint convertedAmount) + { + return amount * conversionRate; + } +} diff --git a/truffle/contracts/MetaCoin.sol b/truffle/contracts/MetaCoin.sol new file mode 100644 index 00000000..fa570e96 --- /dev/null +++ b/truffle/contracts/MetaCoin.sol @@ -0,0 +1,34 @@ +pragma solidity ^0.4.4; + +import "./ConvertLib.sol"; + +// This is just a simple example of a coin-like contract. +// It is not standards compatible and cannot be expected to talk to other +// coin/token contracts. If you want to create a standards-compliant +// token, see: https://github.com/ConsenSys/Tokens. Cheers! + +contract MetaCoin { + mapping (address => uint) balances; + + event Transfer(address indexed _from, address indexed _to, uint256 _value); + + function MetaCoin() { + balances[tx.origin] = 10000; + } + + function sendCoin(address receiver, uint amount) returns(bool sufficient) { + if (balances[msg.sender] < amount) return false; + balances[msg.sender] -= amount; + balances[receiver] += amount; + Transfer(msg.sender, receiver, amount); + return true; + } + + function getBalanceInEth(address addr) returns(uint){ + return ConvertLib.convert(getBalance(addr),2); + } + + function getBalance(address addr) returns(uint) { + return balances[addr]; + } +} diff --git a/truffle/contracts/Migrations.sol b/truffle/contracts/Migrations.sol new file mode 100644 index 00000000..7e7fe8d4 --- /dev/null +++ b/truffle/contracts/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.4.4; + +contract Migrations { + address public owner; + uint public last_completed_migration; + + modifier restricted() { + if (msg.sender == owner) _; + } + + function Migrations() { + owner = msg.sender; + } + + function setCompleted(uint completed) restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/truffle/test/TestMetacoin.sol b/truffle/test/TestMetacoin.sol new file mode 100644 index 00000000..96ccc1e2 --- /dev/null +++ b/truffle/test/TestMetacoin.sol @@ -0,0 +1,25 @@ +pragma solidity ^0.4.2; + +import "truffle/Assert.sol"; +import "truffle/DeployedAddresses.sol"; +import "../contracts/MetaCoin.sol"; + +contract TestMetacoin { + + function testInitialBalanceUsingDeployedContract() { + MetaCoin meta = MetaCoin(DeployedAddresses.MetaCoin()); + + uint expected = 10000; + + Assert.equal(meta.getBalance(tx.origin), expected, "Owner should have 10000 MetaCoin initially"); + } + + function testInitialBalanceWithNewMetaCoin() { + MetaCoin meta = new MetaCoin(); + + uint expected = 10000; + + Assert.equal(meta.getBalance(tx.origin), expected, "Owner should have 10000 MetaCoin initially"); + } + +} diff --git a/truffle/test/metacoin.js b/truffle/test/metacoin.js new file mode 100644 index 00000000..c61c0937 --- /dev/null +++ b/truffle/test/metacoin.js @@ -0,0 +1,63 @@ +var MetaCoin = artifacts.require("./MetaCoin.sol"); + +contract('MetaCoin', function(accounts) { + it("should put 10000 MetaCoin in the first account", function() { + return MetaCoin.deployed().then(function(instance) { + return instance.getBalance.call(accounts[0]); + }).then(function(balance) { + assert.equal(balance.valueOf(), 10000, "10000 wasn't in the first account"); + }); + }); + it("should call a function that depends on a linked library", function() { + var meta; + var metaCoinBalance; + var metaCoinEthBalance; + + return MetaCoin.deployed().then(function(instance) { + meta = instance; + return meta.getBalance.call(accounts[0]); + }).then(function(outCoinBalance) { + metaCoinBalance = outCoinBalance.toNumber(); + return meta.getBalanceInEth.call(accounts[0]); + }).then(function(outCoinBalanceEth) { + metaCoinEthBalance = outCoinBalanceEth.toNumber(); + }).then(function() { + assert.equal(metaCoinEthBalance, 2 * metaCoinBalance, "Library function returned unexpected function, linkage may be broken"); + }); + }); + it("should send coin correctly", function() { + var meta; + + // Get initial balances of first and second account. + var account_one = accounts[0]; + var account_two = accounts[1]; + + var account_one_starting_balance; + var account_two_starting_balance; + var account_one_ending_balance; + var account_two_ending_balance; + + var amount = 10; + + return MetaCoin.deployed().then(function(instance) { + meta = instance; + return meta.getBalance.call(account_one); + }).then(function(balance) { + account_one_starting_balance = balance.toNumber(); + return meta.getBalance.call(account_two); + }).then(function(balance) { + account_two_starting_balance = balance.toNumber(); + return meta.sendCoin(account_two, amount, {from: account_one}); + }).then(function() { + return meta.getBalance.call(account_one); + }).then(function(balance) { + account_one_ending_balance = balance.toNumber(); + return meta.getBalance.call(account_two); + }).then(function(balance) { + account_two_ending_balance = balance.toNumber(); + + assert.equal(account_one_ending_balance, account_one_starting_balance - amount, "Amount wasn't correctly taken from the sender"); + assert.equal(account_two_ending_balance, account_two_starting_balance + amount, "Amount wasn't correctly sent to the receiver"); + }); + }); +}); From aafe290959fef73308a48edeeed7938be07bf125 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Fri, 17 Nov 2017 04:06:10 +0530 Subject: [PATCH 02/17] test: revert back to web3-0.20; address later --- .gitignore | 3 ++ contracts/Registrar.sol | 38 +++++++++++++++ contracts/truffle/Migrations.sol | 23 +++++++++ migrations/2_deploy_contracts.js | 8 --- migrations/_2_deploy_contracts.js | 5 ++ package-lock.json | 81 +++++++++++++++++++++++++++++++ package.json | 15 ++++++ test/SimpleStake.js | 9 ++++ test/SimpleToken.js | 53 ++++++++++++++------ test/SimpleToken_utils.js | 46 +++++++++++++++--- test/lib/utils.js | 6 +-- 11 files changed, 255 insertions(+), 32 deletions(-) create mode 100644 contracts/Registrar.sol create mode 100644 contracts/truffle/Migrations.sol delete mode 100644 migrations/2_deploy_contracts.js create mode 100644 migrations/_2_deploy_contracts.js create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore index 800aaaed..834a19d4 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ ubuntu-xenial-16.04-cloudimg-console.log # we do commit the contract binaries in platform contracts/bin/ contracts/abi/ + +# don't commit node_modules +node_modules diff --git a/contracts/Registrar.sol b/contracts/Registrar.sol new file mode 100644 index 00000000..7d0b5044 --- /dev/null +++ b/contracts/Registrar.sol @@ -0,0 +1,38 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// Registrar +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +import "./OpsManaged.sol"; + +/// @title Registrar - registers for a utility token +contract Registrar is OpsManaged { + + /* + * Public functions + */ + function Registrar() + public + OpsManaged() + { + + } +} \ No newline at end of file diff --git a/contracts/truffle/Migrations.sol b/contracts/truffle/Migrations.sol new file mode 100644 index 00000000..03ea8c3e --- /dev/null +++ b/contracts/truffle/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.4.4; + +contract Migrations { + address public owner; + uint public last_completed_migration; + + modifier restricted() { + if (msg.sender == owner) _; + } + + function Migrations() public { + owner = msg.sender; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js deleted file mode 100644 index b3dc3e90..00000000 --- a/migrations/2_deploy_contracts.js +++ /dev/null @@ -1,8 +0,0 @@ -var ConvertLib = artifacts.require("./ConvertLib.sol"); -var MetaCoin = artifacts.require("./MetaCoin.sol"); - -module.exports = function(deployer) { - deployer.deploy(ConvertLib); - deployer.link(ConvertLib, MetaCoin); - deployer.deploy(MetaCoin); -}; diff --git a/migrations/_2_deploy_contracts.js b/migrations/_2_deploy_contracts.js new file mode 100644 index 00000000..7df52ca8 --- /dev/null +++ b/migrations/_2_deploy_contracts.js @@ -0,0 +1,5 @@ +module.exports = function(deployer) { + // deployer.deploy(ConvertLib); + // deployer.link(ConvertLib, MetaCoin); + // deployer.deploy(MetaCoin); +}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..705f4391 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,81 @@ +{ + "name": "openst-protocol", + "version": "0.9.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "requires": { + "util": "0.10.3" + } + }, + "bignumber": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bignumber/-/bignumber-1.1.0.tgz", + "integrity": "sha1-5qsKdD2l8+oBjlwXWX0SH3howVk=" + }, + "bignumber.js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.1.0.tgz", + "integrity": "sha512-eJzYkFYy9L4JzXsbymsFn3p54D+llV27oTQ+ziJG7WFRheJcNZilgVXMG0LoZtlQSKBsJdWtLFqOD0u+U0jZKA==" + }, + "crypto-js": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.8.tgz", + "integrity": "sha1-cV8HC/YBTyrpkqmLOSkli3E/CNU=" + }, + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "moment": { + "version": "2.19.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.19.2.tgz", + "integrity": "sha512-Rf6jiHPEfxp9+dlzxPTmRHbvoFXsh2L/U8hOupUMpnuecHQmI6cF6lUbJl3QqKPko1u6ujO+FxtcajLVfLpAtA==" + }, + "utf8": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz", + "integrity": "sha1-H6DZJw6b6FDZsFAn9jUZv0ZFfZY=" + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + } + }, + "web3": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/web3/-/web3-0.20.2.tgz", + "integrity": "sha1-xU2sX8DjdzmcBMGm7LsS5FEyeNY=", + "requires": { + "bignumber.js": "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934", + "crypto-js": "3.1.8", + "utf8": "2.1.2", + "xhr2": "0.1.4", + "xmlhttprequest": "1.8.0" + }, + "dependencies": { + "bignumber.js": { + "version": "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934" + } + } + }, + "xhr2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.1.4.tgz", + "integrity": "sha1-f4dliEdxbbUCYyOBL4GMras4el8=" + }, + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..2d0dd561 --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "openst-protocol", + "version": "0.9.1", + "description": "", + "dependencies": { + "assert": "^1.4.1", + "bignumber": "^1.1.0", + "bignumber.js": "^4.1.0", + "moment": "^2.19.2", + "web3": "^0.20.2" + }, + "devDependencies": {}, + "author": "OpenST Foundation Ltd.", + "license": "Apache v2.0" +} diff --git a/test/SimpleStake.js b/test/SimpleStake.js index e95c8884..8875878c 100644 --- a/test/SimpleStake.js +++ b/test/SimpleStake.js @@ -22,3 +22,12 @@ const Utils = require('./lib/utils.js'); const BigNumber = require('bignumber.js'); + +/// +/// Test stories +/// +/// + +contract('SimpleStake', function(accounts) { + +}); \ No newline at end of file diff --git a/test/SimpleToken.js b/test/SimpleToken.js index 8ce7d5da..a509f790 100644 --- a/test/SimpleToken.js +++ b/test/SimpleToken.js @@ -20,12 +20,15 @@ // ---------------------------------------------------------------------------- const Assert = require('assert'); -const Utils = require('./lib/utils.js') +// const Web3 = require('web3'); +const Moment = require('moment'); +const BigNumber = require('bignumber.js'); -const Moment = require('moment') -const BigNumber = require('bignumber.js') +const Utils = require('./lib/utils.js'); +const SimpleTokenUtils = require('./SimpleToken_utils.js') -const SimpleToken = artifacts.require("./SimpleToken.sol") + +const SimpleToken = artifacts.require("./SimpleToken/SimpleToken.sol") // @@ -147,8 +150,27 @@ contract('SimpleToken', (accounts) => { const receipt = await web3.eth.getTransactionReceipt(token.transactionHash) assert.equal(receipt.logs.length, 1) const logs = Utils.decodeLogs(token.abi, [ receipt.logs[0] ]) - Utils.checkTransferEvent(logs[0], 0, accounts[0], TOTAL_SUPPLY) - }) + SimpleTokenUtils.checkTransferEvent(logs[0], 0, accounts[0], TOTAL_SUPPLY) + }) + // it('Constructor raised transfer event', async () => { + // const receipt = await web3.eth.getTransactionReceipt(token.transactionHash) + // assert.equal(receipt.logs.length, 1) + // console.log("Simple Token -------- token.abi"); + // console.log(token.abi.length) + // console.log(JSON.stringify(token.abi[32], null, 4)); + // console.log("Simple Token -------- receipt.logs[0]"); + // console.log(receipt.logs[0]) + // console.log("Simple Token -------- receipt.logs[0].topics"); + // console.log(receipt.logs[0].topics) + // // @dev SimpleToken abi[32] is 'Transfer' event; + // // solve this better for moving to web3 v1.0.0 + // // var inputs = token.abi[32].inputs; + // // var data = receipt.logs[0].data; + // // var topics = receipt.logs[0].topics; + // // const logs = Web3.eth.abi.decodeLog(inputs, data, topics) + // // console.log(JSON.stringify(logs, null, 4)); + // // Utils.checkTransferEvent(logs, 0, accounts[0], TOTAL_SUPPLY) + // }) }) @@ -166,12 +188,13 @@ contract('SimpleToken', (accounts) => { }) it("transfer tokens from owner to other", async () => { - Utils.checkTransferEventGroup(await token.transfer(accounts[1], 1000), accounts[0], accounts[1], 1000) + var res = await token.transfer(accounts[1], 1000); + SimpleTokenUtils.checkTransferEventGroup(await token.transfer(accounts[1], 1000), accounts[0], accounts[1], 1000) }) it("transfer 0 tokens", async () => { assert.equal(await token.transfer.call(accounts[2], 0, { from: accounts[1] }), true) - Utils.checkTransferEventGroup(await token.transfer(accounts[2], 0, { from: accounts[1] }), accounts[1], accounts[2], 0) + SimpleTokenUtils.checkTransferEventGroup(await token.transfer(accounts[2], 0, { from: accounts[1] }), accounts[1], accounts[2], 0) }) it("transfer > balance", async () => { @@ -236,7 +259,7 @@ contract('SimpleToken', (accounts) => { it("transfer 1000 from account 1 -> 2 with 10 allowance", async () => { assert.equal(await token.approve.call(accounts[2], 10, { from: accounts[4] }), true) - Utils.checkApprovalEventGroup(await token.approve(accounts[2], 10, { from: accounts[4] }), accounts[4], accounts[2], 10) + SimpleTokenUtils.checkApprovalEventGroup(await token.approve(accounts[2], 10, { from: accounts[4] }), accounts[4], accounts[2], 10) assert.equal((await token.allowance.call(accounts[4], accounts[2], { from: accounts[4] })).toNumber(), 10) @@ -247,12 +270,12 @@ contract('SimpleToken', (accounts) => { it("transfer 1000 from account 1 -> 2 with 1000 allowance (as ops)", async () => { // We first need to bring approval to 0 assert.equal(await token.approve.call(ops, 0, { from: accounts[4] }), true) - Utils.checkApprovalEventGroup(await token.approve(ops, 0, { from: accounts[4] }), accounts[4], ops, 0) + SimpleTokenUtils.checkApprovalEventGroup(await token.approve(ops, 0, { from: accounts[4] }), accounts[4], ops, 0) assert.equal(await token.allowance.call(accounts[4], ops, { from: accounts[4] }), 0) assert.equal(await token.approve.call(ops, 1000, { from: accounts[4] }), true) - Utils.checkApprovalEventGroup(await token.approve(ops, 1000, { from: accounts[4] }), accounts[4], ops, 1000) + SimpleTokenUtils.checkApprovalEventGroup(await token.approve(ops, 1000, { from: accounts[4] }), accounts[4], ops, 1000) assert.equal(await token.allowance.call(accounts[4], ops), 1000, { from: accounts[4] }) @@ -267,12 +290,12 @@ contract('SimpleToken', (accounts) => { it("transfer 1000 from account 1 -> 2 with 1000 allowance (as admin)", async () => { // We first need to bring approval to 0 assert.equal(await token.approve.call(admin, 0, { from: accounts[4] }), true) - Utils.checkApprovalEventGroup(await token.approve(admin, 0, { from: accounts[4] }), accounts[4], admin, 0) + SimpleTokenUtils.checkApprovalEventGroup(await token.approve(admin, 0, { from: accounts[4] }), accounts[4], admin, 0) assert.equal(await token.allowance.call(accounts[4], admin, { from: accounts[4] }), 0) assert.equal(await token.approve.call(admin, 1000, { from: accounts[4] }), true) - Utils.checkApprovalEventGroup(await token.approve(admin, 1000, { from: accounts[4] }), accounts[4], admin, 1000) + SimpleTokenUtils.checkApprovalEventGroup(await token.approve(admin, 1000, { from: accounts[4] }), accounts[4], admin, 1000) assert.equal(await token.allowance.call(accounts[4], admin), 1000, { from: accounts[4] }) @@ -409,7 +432,7 @@ contract('SimpleToken', (accounts) => { it("check properties before and after finalize", async () => { assert.equal(await token.finalized.call(), false) - Utils.checkFinalizedEventGroup(await token.finalize({ from: admin })) + SimpleTokenUtils.checkFinalizedEventGroup(await token.finalize({ from: admin })) assert.equal(await token.finalized.call(), true) }) @@ -437,7 +460,7 @@ contract('SimpleToken', (accounts) => { it("burn less than or equal to balance", async () => { const balanceBefore = await token.balanceOf(accounts[0]) - Utils.checkBurntEventGroup(await token.burn(1000, { from: accounts[0] })) + SimpleTokenUtils.checkBurntEventGroup(await token.burn(1000, { from: accounts[0] })) const currentBalance = await token.balanceOf(accounts[0]) const currentSupply = await token.totalSupply.call() diff --git a/test/SimpleToken_utils.js b/test/SimpleToken_utils.js index f6248a11..c258438d 100644 --- a/test/SimpleToken_utils.js +++ b/test/SimpleToken_utils.js @@ -20,11 +20,11 @@ // ---------------------------------------------------------------------------- const Assert = require('assert'); -const BigNumer = require('bignumber.js'); +const BigNumber = require('bignumber.js'); /// @dev Assert on Transfer event module.exports.checkTransferEventGroup = (result, _from, _to, _value) => { - assert.equal(result.logs.length, 1); + Assert.equal(result.logs.length, 1); const event = result.logs[0]; @@ -36,9 +36,43 @@ module.exports.checkTransferEvent = (event, _from, _to, _value) => { if (Number.isInteger(_value)) { _value = new BigNumber(_value); } + Assert.equal(event.event, "Transfer"); + Assert.equal(event.args._from, _from); + Assert.equal(event.args._to, _to); + Assert.equal(event.args._value.toNumber(), _value.toNumber()); +} + + +module.exports.checkApprovalEventGroup = (result, _owner, _spender, _value) => { + assert.equal(result.logs.length, 1) + + const event = result.logs[0] - assert.equal(event.event, "Transfer"); - assert.equal(event.args._from, _from); - assert.equal(event.args._to, _to); - assert.equal(event.args._value.toNumber(), _value.toNumber()); + if (Number.isInteger(_value)) { + _value = new BigNumber(_value) + } + + assert.equal(event.event, "Approval") + assert.equal(event.args._owner, _owner) + assert.equal(event.args._spender, _spender) + assert.equal(event.args._value.toNumber(), _value.toNumber()) } + +module.exports.checkFinalizedEventGroup = (result) => { + assert.equal(result.logs.length, 1) + + const event = result.logs[0] + + assert.equal(event.event, "Finalized") +} + + +module.exports.checkBurntEventGroup = (result, _from, _value) => { + assert.equal(result.logs.length, 1) + + const event = result.logs[0] + + assert.equal(event.event, "Burnt") + assert.equal(event._from, _from) + assert.equal(event._value, _value) +} \ No newline at end of file diff --git a/test/lib/utils.js b/test/lib/utils.js index 27703298..1841ca63 100644 --- a/test/lib/utils.js +++ b/test/lib/utils.js @@ -20,8 +20,8 @@ // ---------------------------------------------------------------------------- const Assert = require('assert'); -// const BigNumer = require('bignumber.js'); -// const SolidityEvent = require("web3/lib/web3/event.js"); +// const BigNumber = require('bignumber.js'); +const SolidityEvent = require("web3/lib/web3/event.js"); var SimpleToken = artifacts.require("./SimpleToken/SimpleToken.sol") // var SimpleStake = artifacts.require("./SimpleStake.sol"); @@ -29,7 +29,7 @@ var SimpleToken = artifacts.require("./SimpleToken/SimpleToken.sol") /// @dev Deploy SimpleToken and finalize it module.exports.deployContracts = async (artifacts, accounts) => { - const token = await SimpleToken.new({ from: accounts[0]}, gas: 3500000 }); + const token = await SimpleToken.new({ from: accounts[0], gas: 3500000 }); // // Set Simple Token admin to account[1] // await token.setAdminAddress(accounts[1]); From e77188ed44893c0bd34add3c941542ed6f658212 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Fri, 17 Nov 2017 06:19:06 +0530 Subject: [PATCH 03/17] test: first test SimpleStake in isolation, small steps --- contracts/SimpleStake.sol | 38 ++++++++++-- test/SimpleStake_basic.js | 61 +++++++++++++++++++ test/{SimpleStake.js => SimpleStake_utils.js} | 29 ++++++--- test/SimpleToken_utils.js | 13 ++++ test/lib/utils.js | 10 ++- 5 files changed, 130 insertions(+), 21 deletions(-) create mode 100644 test/SimpleStake_basic.js rename test/{SimpleStake.js => SimpleStake_utils.js} (52%) diff --git a/contracts/SimpleStake.sol b/contracts/SimpleStake.sol index 91ce3cac..50b67d5b 100644 --- a/contracts/SimpleStake.sol +++ b/contracts/SimpleStake.sol @@ -70,6 +70,12 @@ contract SimpleStake { _; } + modifier onlyProposedProtocolAfterWait() { + require(msg.sender == proposedProtocol); + require(earliestTransferHeight <= block.number); + _; + } + modifier notNull(address _address) { if (_address == 0) revert(); @@ -113,9 +119,12 @@ contract SimpleStake { function initiateProtocolTransfer( address _proposedProtocol) public - onlyProtocol + onlyProtocol + notNull(_proposedProtocol) returns (bool) { + require(_proposedProtocol != openSTProtocol); + earliestTransferHeight = block.number + PROTOCOL_TRANSFER_BLOCKS_TO_WAIT; proposedProtocol = _proposedProtocol; @@ -125,9 +134,11 @@ contract SimpleStake { } - function completeProtocolTransfer() public returns (bool) { - require(msg.sender == proposedProtocol); - + function completeProtocolTransfer() + public + onlyProposedProtocolAfterWait + returns (bool) + { openSTProtocol = proposedProtocol; proposedProtocol = address(0); @@ -136,10 +147,27 @@ contract SimpleStake { return true; } + function revokeProtocolTransfer() + public + onlyProtocol + returns (bool) + { + require(proposedProtocol != address(0)); + + proposedProtocol = address(0); + earliestTransferHeight = 0; + + return true; + } + /* * Web3 call functions */ - /// @dev + /// @dev total stake is the balance of the staking contract + /// accidental transfers directly to SimpleStake bypassing + /// the OpenST protocol will not mint new utility tokens, + /// but will add to the total stake. + /// (accidental) donations can not be prevented function getTotalStake() public constant diff --git a/test/SimpleStake_basic.js b/test/SimpleStake_basic.js new file mode 100644 index 00000000..79b51ae0 --- /dev/null +++ b/test/SimpleStake_basic.js @@ -0,0 +1,61 @@ +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// test/SimpleStake_basic.js +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +const assert = require('assert'); +const Utils = require('./lib/utils.js'); +const SimpleStakeUtils = require('./SimpleStake_utils.js') + +/// +/// Test stories +/// +/// Test SimpleStake in isolation from OpenST protocol +/// - + +contract('SimpleStake', function(accounts) { + + /// mock unique identifier for utility token + const UUID = "0xbce8a3809c9356cf0e5178a2aef207f50df7d32b388c8fceb8e363df00efce31"; + /// mock OpenST protocol contract address + const openSTProtocol = "0x6654f8a92c4521413fe997d11865730160ec1649"; + /// mock upgraded OpenST protocol contract address + const upgradedOpenSTProtocol = "0x18ae4cd1d5a5fae4cf70e31bff16827150acb535"; + + describe('Basic properties', async () => { + + var token = null; + var simpleStake = null; + + before(async () => { + var contracts = + await SimpleStakeUtils.deploySingleSimpleStake( + artifacts, accounts, openSTProtocol, UUID); + + token = contracts.token; + simpleStake = contracts.simpleStake; + }); + + it("UUID", async() => { + assert.equal(await simpleStake.uuid.call(), UUID); + }); + + + }); +}); \ No newline at end of file diff --git a/test/SimpleStake.js b/test/SimpleStake_utils.js similarity index 52% rename from test/SimpleStake.js rename to test/SimpleStake_utils.js index 8875878c..cceb0277 100644 --- a/test/SimpleStake.js +++ b/test/SimpleStake_utils.js @@ -13,21 +13,30 @@ // limitations under the License. // // ---------------------------------------------------------------------------- -// test/SimpleStake.js +// test/SimpleStake_utils.js // // http://www.simpletoken.org/ // // ---------------------------------------------------------------------------- -const Utils = require('./lib/utils.js'); +const Assert = require('assert'); -const BigNumber = require('bignumber.js'); +var SimpleToken = artifacts.require("./SimpleToken/SimpleToken.sol"); +var SimpleStake = artifacts.require("./SimpleStake.sol"); -/// -/// Test stories -/// -/// +/// @dev Deploy +module.exports.deploySingleSimpleStake = async (artifacts, accounts, protocol, UUID) => { -contract('SimpleStake', function(accounts) { - -}); \ No newline at end of file + const token = await SimpleToken.new({ from: accounts[0], gas: 3500000 }); + // Set Simple Token admin to account[1] + await token.setAdminAddress(accounts[1]); + // and finalize Simple Token + Assert.ok(await token.finalize({ from: accounts[1] })); + + const simpleStake = await SimpleStake.new(token.address, protocol, UUID, { from: accounts[0] }); + + return { + token : token, + simpleStake : simpleStake + }; +}; \ No newline at end of file diff --git a/test/SimpleToken_utils.js b/test/SimpleToken_utils.js index c258438d..e9a5adb0 100644 --- a/test/SimpleToken_utils.js +++ b/test/SimpleToken_utils.js @@ -22,6 +22,19 @@ const Assert = require('assert'); const BigNumber = require('bignumber.js'); +var SimpleToken = artifacts.require("./SimpleToken/SimpleToken.sol"); + +/// @dev currently unused as tests for SimpleToken.js don't +/// follow this paradigm - to align +module.exports.deploySimpleToken = async (artifacts, accounts) => { + + const token = await SimpleToken.new({ from: accounts[0], gas: 3500000 }); + + return { + token : token + } +} + /// @dev Assert on Transfer event module.exports.checkTransferEventGroup = (result, _from, _to, _value) => { Assert.equal(result.logs.length, 1); diff --git a/test/lib/utils.js b/test/lib/utils.js index 1841ca63..dcc763bf 100644 --- a/test/lib/utils.js +++ b/test/lib/utils.js @@ -26,16 +26,14 @@ const SolidityEvent = require("web3/lib/web3/event.js"); var SimpleToken = artifacts.require("./SimpleToken/SimpleToken.sol") // var SimpleStake = artifacts.require("./SimpleStake.sol"); -/// @dev Deploy SimpleToken and finalize it +/// @dev Deploy SimpleToken and other contracts +/// to test full protocol module.exports.deployContracts = async (artifacts, accounts) => { const token = await SimpleToken.new({ from: accounts[0], gas: 3500000 }); - // // Set Simple Token admin to account[1] - // await token.setAdminAddress(accounts[1]); - // // and finalize Simple Token - // Assert.ok(await token.finalize({ from: accounts[1] })); - + // to be extended + return { token : token } From d9085ed1c1edb06f1522f6d6d61997456901e587 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Fri, 17 Nov 2017 09:18:28 +0530 Subject: [PATCH 04/17] test: isolated basic staking and unstaking test --- contracts/SimpleStake.sol | 4 +- test/SimpleStake_basic.js | 80 +++++++++++++++++++++++++++++++++------ test/SimpleStake_utils.js | 12 +++++- test/lib/utils.js | 10 ++++- 4 files changed, 91 insertions(+), 15 deletions(-) diff --git a/contracts/SimpleStake.sol b/contracts/SimpleStake.sol index 50b67d5b..72357339 100644 --- a/contracts/SimpleStake.sol +++ b/contracts/SimpleStake.sol @@ -44,8 +44,8 @@ contract SimpleStake { /// Blocks to wait before the protocol transfer can be completed /// This allows anyone with a stake to unstake under the existing /// protocol if they disagree with the new proposed protocol - /// @dev from OpenST ^v1.0 this constant will be set - uint256 constant public PROTOCOL_TRANSFER_BLOCKS_TO_WAIT = 1; + /// @dev from OpenST ^v1.0 this constant will be set to a significant value + uint256 constant public PROTOCOL_TRANSFER_BLOCKS_TO_WAIT = 5; /* * Storage diff --git a/test/SimpleStake_basic.js b/test/SimpleStake_basic.js index 79b51ae0..52179c96 100644 --- a/test/SimpleStake_basic.js +++ b/test/SimpleStake_basic.js @@ -19,7 +19,8 @@ // // ---------------------------------------------------------------------------- -const assert = require('assert'); +const Assert = require('assert'); +const BigNumber = require('bignumber.js'); const Utils = require('./lib/utils.js'); const SimpleStakeUtils = require('./SimpleStake_utils.js') @@ -33,29 +34,86 @@ contract('SimpleStake', function(accounts) { /// mock unique identifier for utility token const UUID = "0xbce8a3809c9356cf0e5178a2aef207f50df7d32b388c8fceb8e363df00efce31"; - /// mock OpenST protocol contract address - const openSTProtocol = "0x6654f8a92c4521413fe997d11865730160ec1649"; - /// mock upgraded OpenST protocol contract address - const upgradedOpenSTProtocol = "0x18ae4cd1d5a5fae4cf70e31bff16827150acb535"; + /// mock OpenST protocol contract address with an external account + const openSTProtocol = accounts[4]; + /// mock upgraded OpenST protocol contract address with an external account + const upgradedOpenSTProtocol = accounts[5]; + /// constant protocol wait time for protocol transfer in blocks + const PROTOCOL_TRANSFER_BLOCKS_TO_WAIT = 5; - describe('Basic properties', async () => { + describe('in isolation', async () => { var token = null; var simpleStake = null; - + var totalSupply = new BigNumber(0); + + const ST0 = new BigNumber(web3.toWei(0, "ether")); + 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 ST5 = new BigNumber(web3.toWei(5, "ether")); + const STMAX = new BigNumber(web3.toWei(11, "ether")); + before(async () => { var contracts = await SimpleStakeUtils.deploySingleSimpleStake( - artifacts, accounts, openSTProtocol, UUID); + artifacts, accounts, openSTProtocol, UUID); token = contracts.token; simpleStake = contracts.simpleStake; + + totalSupply = new BigNumber(await token.balanceOf.call(accounts[0])); + Assert.ok(totalSupply.toNumber() >= STMAX); }); - it("UUID", async() => { - assert.equal(await simpleStake.uuid.call(), UUID); + context("on construction", async () => { + + it("should store a UUID", async() => { + Assert.equal(await simpleStake.uuid.call(), UUID); + }); + + it("should have an EIP20Token contract", async() => { + Assert.equal(await simpleStake.eip20Token.call(), token.address); + }); + + it("should have a well-defined OpenSTProtocol", async() => { + // assert protocol contract address is set + Assert.equal(await simpleStake.openSTProtocol.call(), openSTProtocol); + // assert no new protocol contract address is proposed + Assert.ok(Utils.isNullAddress(await simpleStake.proposedProtocol.call())); + // assert transfer height is zero + Assert.equal(await simpleStake.earliestTransferHeight.call(), 0); + }); + + it("should hold a zero balance", async() => { + Assert.equal(await simpleStake.getTotalStake.call(), 0); + }); + + it("Protocol transfer wait blocktime", async() => { + Assert.equal(await simpleStake.PROTOCOL_TRANSFER_BLOCKS_TO_WAIT.call(), + PROTOCOL_TRANSFER_BLOCKS_TO_WAIT); + }); }); + context('before protocol transfer', async () => { + + it("stake 5ST", async () => { + Assert.ok(await token.transfer(simpleStake.address, ST5, { from: accounts[0] })); + Assert.equal((await simpleStake.getTotalStake()).toNumber(), ST5.toNumber()); + Assert.equal((await token.balanceOf(accounts[0])).toNumber(), totalSupply.sub(ST5).toNumber()); + }); - }); + it("release 3ST", async () => { + Assert.ok(await simpleStake.releaseTo(accounts[0], ST3, { from: openSTProtocol })); + Assert.equal((await simpleStake.getTotalStake.call()).toNumber(), ST2.toNumber()); + Assert.equal((await token.balanceOf(accounts[0])).toNumber(), totalSupply.sub(ST2).toNumber()); + }); + + it("fail to release 3ST", async () => { + await Utils.expectThrow(simpleStake.releaseTo(accounts[0], ST3, { from: openSTProtocol })); + Assert.equal((await simpleStake.getTotalStake.call()).toNumber(), ST2.toNumber()); + Assert.equal((await token.balanceOf(accounts[0])).toNumber(), totalSupply.sub(ST2).toNumber()); + }); + }); + }) }); \ No newline at end of file diff --git a/test/SimpleStake_utils.js b/test/SimpleStake_utils.js index cceb0277..d90c0ae5 100644 --- a/test/SimpleStake_utils.js +++ b/test/SimpleStake_utils.js @@ -39,4 +39,14 @@ module.exports.deploySingleSimpleStake = async (artifacts, accounts, protocol, U token : token, simpleStake : simpleStake }; -}; \ No newline at end of file +}; + +module.exports.checkTransferEvent = (event, _from, _to, _value) => { + if (Number.isInteger(_value)) { + _value = new BigNumber(_value); + } + Assert.equal(event.event, "Transfer"); + Assert.equal(event.args._from, _from); + Assert.equal(event.args._to, _to); + Assert.equal(event.args._value.toNumber(), _value.toNumber()); +} \ No newline at end of file diff --git a/test/lib/utils.js b/test/lib/utils.js index dcc763bf..90f4928e 100644 --- a/test/lib/utils.js +++ b/test/lib/utils.js @@ -26,6 +26,8 @@ const SolidityEvent = require("web3/lib/web3/event.js"); var SimpleToken = artifacts.require("./SimpleToken/SimpleToken.sol") // var SimpleStake = artifacts.require("./SimpleStake.sol"); +const NullAddress = "0x0000000000000000000000000000000000000000"; + /// @dev Deploy SimpleToken and other contracts /// to test full protocol module.exports.deployContracts = async (artifacts, accounts) => { @@ -33,7 +35,7 @@ module.exports.deployContracts = async (artifacts, accounts) => { const token = await SimpleToken.new({ from: accounts[0], gas: 3500000 }); // to be extended - + return { token : token } @@ -44,6 +46,12 @@ module.exports.deployContracts = async (artifacts, accounts) => { * Basic Ethereum checks */ +/// @dev Compare to null address +module.exports.isNullAddress = function (address) { + Assert.strictEqual(typeof address, 'string', `address must be of type 'string'`); + return (address == NullAddress); +} + /// @dev Expect failure from invalid opcode or out of gas, /// but returns error instead module.exports.expectThrow = async (promise) => { From b1da800e469acdd0b566c5d7c09fe5d3d750ebaa Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Fri, 17 Nov 2017 20:45:15 +0530 Subject: [PATCH 05/17] test: checkTotalStaked always verifies token.balanceOf(stake) --- test/SimpleStake_basic.js | 25 +++++++++++++++---------- test/SimpleStake_utils.js | 7 +++++++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/test/SimpleStake_basic.js b/test/SimpleStake_basic.js index 52179c96..d3d07796 100644 --- a/test/SimpleStake_basic.js +++ b/test/SimpleStake_basic.js @@ -48,11 +48,9 @@ contract('SimpleStake', function(accounts) { var totalSupply = new BigNumber(0); const ST0 = new BigNumber(web3.toWei(0, "ether")); - 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 ST5 = new BigNumber(web3.toWei(5, "ether")); - const STMAX = new BigNumber(web3.toWei(11, "ether")); before(async () => { var contracts = @@ -63,7 +61,7 @@ contract('SimpleStake', function(accounts) { simpleStake = contracts.simpleStake; totalSupply = new BigNumber(await token.balanceOf.call(accounts[0])); - Assert.ok(totalSupply.toNumber() >= STMAX); + Assert.ok(totalSupply.toNumber() >= ST5.toNumber()); }); context("on construction", async () => { @@ -97,23 +95,30 @@ contract('SimpleStake', function(accounts) { context('before protocol transfer', async () => { - it("stake 5ST", async () => { + it("can stake 5ST", async () => { Assert.ok(await token.transfer(simpleStake.address, ST5, { from: accounts[0] })); - Assert.equal((await simpleStake.getTotalStake()).toNumber(), ST5.toNumber()); - Assert.equal((await token.balanceOf(accounts[0])).toNumber(), totalSupply.sub(ST5).toNumber()); + await SimpleStakeUtils.checkTotalStaked(simpleStake, token, ST5); + Assert.equal((await token.balanceOf.call(accounts[0])).toNumber(), totalSupply.sub(ST5).toNumber()); }); - it("release 3ST", async () => { + it("can release 3ST", async () => { Assert.ok(await simpleStake.releaseTo(accounts[0], ST3, { from: openSTProtocol })); - Assert.equal((await simpleStake.getTotalStake.call()).toNumber(), ST2.toNumber()); + await SimpleStakeUtils.checkTotalStaked(simpleStake, token, ST2); Assert.equal((await token.balanceOf(accounts[0])).toNumber(), totalSupply.sub(ST2).toNumber()); }); - it("fail to release 3ST", async () => { + it("must fail to release 3ST", async () => { await Utils.expectThrow(simpleStake.releaseTo(accounts[0], ST3, { from: openSTProtocol })); - Assert.equal((await simpleStake.getTotalStake.call()).toNumber(), ST2.toNumber()); + await SimpleStakeUtils.checkTotalStaked(simpleStake, token, ST2); Assert.equal((await token.balanceOf(accounts[0])).toNumber(), totalSupply.sub(ST2).toNumber()); }); + + it("can release all remaining stake", async () => { + var remainingStake = await simpleStake.getTotalStake.call(); + Assert.ok(await simpleStake.releaseTo(accounts[0], remainingStake, { from: openSTProtocol })); + await SimpleStakeUtils.checkTotalStaked(simpleStake, token, ST0); + Assert.equal((await token.balanceOf(accounts[0])).toNumber(), totalSupply.toNumber()); + }); }); }) }); \ No newline at end of file diff --git a/test/SimpleStake_utils.js b/test/SimpleStake_utils.js index d90c0ae5..c3c50cb0 100644 --- a/test/SimpleStake_utils.js +++ b/test/SimpleStake_utils.js @@ -41,6 +41,13 @@ module.exports.deploySingleSimpleStake = async (artifacts, accounts, protocol, U }; }; +/// @dev Check staked balance +module.exports.checkTotalStaked = async (stake, token, amount) => { + Assert.equal((await stake.getTotalStake.call()).toNumber(), amount.toNumber()); + Assert.equal((await token.balanceOf.call(stake.address)).toNumber(), amount.toNumber()); +}; + + module.exports.checkTransferEvent = (event, _from, _to, _value) => { if (Number.isInteger(_value)) { _value = new BigNumber(_value); From db7426d9275f22bf6580ee4fc70ce5ecf9f7f120 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Sat, 18 Nov 2017 01:02:17 +0530 Subject: [PATCH 06/17] contracts: SimpleStake v0.9.1 --- contracts/SimpleStake.sol | 8 +++++++- test/SimpleStake_basic.js | 16 +++++++++++++++- test/SimpleStake_utils.js | 18 ++++++++++++++++++ test/lib/utils.js | 6 ++++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/contracts/SimpleStake.sol b/contracts/SimpleStake.sol index 72357339..c9deb34e 100644 --- a/contracts/SimpleStake.sol +++ b/contracts/SimpleStake.sol @@ -76,6 +76,9 @@ contract SimpleStake { _; } + // TODO: [ben] change notNull to hasCode so that for + // a significant wait time the code at the proposed new + // protocol can be reviewed modifier notNull(address _address) { if (_address == 0) revert(); @@ -101,7 +104,10 @@ contract SimpleStake { } /// @dev Allows the protocol to release the staked amount - /// into provided address + /// into provided address. + /// The protocol MUST be a contract that sets the rules + /// on how the stake can be released and to who. + /// The protocol takes the role of an "owner" of the stake. /// @param _to Beneficiary of the amount of the stake /// @param _amount Amount of stake to release to beneficiary function releaseTo(address _to, uint256 _amount) diff --git a/test/SimpleStake_basic.js b/test/SimpleStake_basic.js index d3d07796..9d4773f3 100644 --- a/test/SimpleStake_basic.js +++ b/test/SimpleStake_basic.js @@ -102,9 +102,10 @@ contract('SimpleStake', function(accounts) { }); it("can release 3ST", async () => { - Assert.ok(await simpleStake.releaseTo(accounts[0], ST3, { from: openSTProtocol })); + const result = await simpleStake.releaseTo(accounts[0], ST3, { from: openSTProtocol }); await SimpleStakeUtils.checkTotalStaked(simpleStake, token, ST2); Assert.equal((await token.balanceOf(accounts[0])).toNumber(), totalSupply.sub(ST2).toNumber()); + SimpleStakeUtils.checkReleasedEventGroup(result, openSTProtocol, accounts[0], ST3); }); it("must fail to release 3ST", async () => { @@ -120,5 +121,18 @@ contract('SimpleStake', function(accounts) { Assert.equal((await token.balanceOf(accounts[0])).toNumber(), totalSupply.toNumber()); }); }); + + // TODO: [ben] + // - test protocol transfers + // - test staking and releasing during protocol transfers + // - test the time to wait by calling repeatedly complete and expect it to fail if + // blockheight is not yet reached - find better way to test this + + // context('during protocol transfer', async () => { + + // it('can initiate protocol transfer', async () => { + // Assert.ok(await ); + // }); + // }); }) }); \ No newline at end of file diff --git a/test/SimpleStake_utils.js b/test/SimpleStake_utils.js index c3c50cb0..776b7b43 100644 --- a/test/SimpleStake_utils.js +++ b/test/SimpleStake_utils.js @@ -47,6 +47,24 @@ module.exports.checkTotalStaked = async (stake, token, amount) => { Assert.equal((await token.balanceOf.call(stake.address)).toNumber(), amount.toNumber()); }; +/* + * Event checks + */ + +/// @dev Check stake release events +module.exports.checkReleasedEventGroup = (result, _protocol, _to, _amount) => { + if (Number.isInteger(_amount)) { + _amount = new BigNumber(_amount); + }; + // TODO: [ben] parse result.receipt.logs for EIP20.Transfer event too + Assert.equal(result.logs.length, 1); + + const releaseEvent = result.logs[0]; + Assert.equal(releaseEvent.event, "ReleasedStake"); + Assert.equal(releaseEvent.args._protocol, _protocol); + Assert.equal(releaseEvent.args._to, _to); + Assert.equal(releaseEvent.args._amount.toNumber(), _amount.toNumber()); +}; module.exports.checkTransferEvent = (event, _from, _to, _value) => { if (Number.isInteger(_value)) { diff --git a/test/lib/utils.js b/test/lib/utils.js index 90f4928e..7d950b3f 100644 --- a/test/lib/utils.js +++ b/test/lib/utils.js @@ -41,6 +41,12 @@ module.exports.deployContracts = async (artifacts, accounts) => { } } +/* + * General event checks + */ +module.exports.expectNoEvents = (result) => { + Assert.equal(result.receipt.logs.length, 0, "expected empty array of logs") +} /* * Basic Ethereum checks From e9b729ce29d43a19ab6c9e7901c92a7c5ebb7806 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Sat, 18 Nov 2017 01:46:21 +0530 Subject: [PATCH 07/17] contracts: move v0.9.0 contracts out of the way for v0.9.1 --- contracts/{Staking.sol => v0.9.0_Staking.so_} | 0 contracts/{StakingData.sol => v0.9.0_StakingData.so_} | 0 contracts/{UtilityToken.sol => v0.9.0_UtilityToken.so_} | 0 contracts/{UtilityTokenData.sol => v0.9.0_UtilityTokenData.so_} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename contracts/{Staking.sol => v0.9.0_Staking.so_} (100%) rename contracts/{StakingData.sol => v0.9.0_StakingData.so_} (100%) rename contracts/{UtilityToken.sol => v0.9.0_UtilityToken.so_} (100%) rename contracts/{UtilityTokenData.sol => v0.9.0_UtilityTokenData.so_} (100%) diff --git a/contracts/Staking.sol b/contracts/v0.9.0_Staking.so_ similarity index 100% rename from contracts/Staking.sol rename to contracts/v0.9.0_Staking.so_ diff --git a/contracts/StakingData.sol b/contracts/v0.9.0_StakingData.so_ similarity index 100% rename from contracts/StakingData.sol rename to contracts/v0.9.0_StakingData.so_ diff --git a/contracts/UtilityToken.sol b/contracts/v0.9.0_UtilityToken.so_ similarity index 100% rename from contracts/UtilityToken.sol rename to contracts/v0.9.0_UtilityToken.so_ diff --git a/contracts/UtilityTokenData.sol b/contracts/v0.9.0_UtilityTokenData.so_ similarity index 100% rename from contracts/UtilityTokenData.sol rename to contracts/v0.9.0_UtilityTokenData.so_ From 3a3626710956b54bad6d420a603e49744b94bd04 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Mon, 20 Nov 2017 14:40:36 +0530 Subject: [PATCH 08/17] contracts: ProtocolVersioned, STPrime is UtilityTokenAbstract --- contracts/BrandedToken.sol | 40 ++++++++ contracts/EIP20Token.sol | 9 +- contracts/ProtocolVersioned.sol | 141 +++++++++++++++++++++++++++++ contracts/STPrime.sol | 109 ++++++++++++++++++++++ contracts/STPrimeConfig.sol | 37 ++++++++ contracts/SimpleStake.sol | 97 ++------------------ contracts/UtilityTokenAbstract.sol | 103 +++++++++++++++++++++ 7 files changed, 438 insertions(+), 98 deletions(-) create mode 100644 contracts/BrandedToken.sol create mode 100644 contracts/ProtocolVersioned.sol create mode 100644 contracts/STPrime.sol create mode 100644 contracts/STPrimeConfig.sol create mode 100644 contracts/UtilityTokenAbstract.sol diff --git a/contracts/BrandedToken.sol b/contracts/BrandedToken.sol new file mode 100644 index 00000000..b2a749ee --- /dev/null +++ b/contracts/BrandedToken.sol @@ -0,0 +1,40 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// contracts/BrandedToken.sol +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +import "./SafeMath.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: +/// - smart contract controlled password reset for users who don't +/// yet (hard-spoon FTW) manage their own private keys (+v0.9.2) +/// - soft-exit for a user to redeem their equivalent part of the +/// Simple Token stake on Ethereum mainnet +/// - hard-exit for all users if the utility chain halts to reclaim +/// their equivalent part of the Simple Token stake +/// on Ethereum (before v1.0) +// contract BrandedToken is EIP20Token, UtilityTokenAbstract { +// using SafeMath for uint256; + + +// } \ No newline at end of file diff --git a/contracts/EIP20Token.sol b/contracts/EIP20Token.sol index 848d3e4e..8656e0d1 100644 --- a/contracts/EIP20Token.sol +++ b/contracts/EIP20Token.sol @@ -22,14 +22,13 @@ pragma solidity ^0.4.17; // ---------------------------------------------------------------------------- import "./EIP20Interface.sol"; -import "./Owned.sol"; import "./SafeMath.sol"; /** @title EIP20Token @notice Implements EIP20 token */ -contract EIP20Token is Owned, EIP20Interface { +contract EIP20Token is EIP20Interface { using SafeMath for uint256; string internal tokenName; @@ -42,15 +41,11 @@ contract EIP20Token is Owned, EIP20Interface { function EIP20Token(string _symbol, string _name, uint8 _decimals) public - Owned() { tokenSymbol = _symbol; tokenName = _name; tokenDecimals = _decimals; - - // According to the EIP20 standard, a token contract which creates new tokens should trigger - // a Transfer event and transfers of 0 values must also fire the event. - Transfer(0x0, owner, tokenTotalSupply); + tokenTotalSupply = 0; } diff --git a/contracts/ProtocolVersioned.sol b/contracts/ProtocolVersioned.sol new file mode 100644 index 00000000..474319e9 --- /dev/null +++ b/contracts/ProtocolVersioned.sol @@ -0,0 +1,141 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// contracts/ProtocolVersioned.sol +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +contract ProtocolVersioned { + + /* + * Events + */ + event ProtocolTransferInitiated(address indexed _existingProtocol, address indexed _proposedProtocol, uint256 _activationHeight); + event ProtocolTransferRevoked(address indexed _existingProtocol, address indexed _revokedProtocol); + event ProtocolTransferCompleted(address indexed _newProtocol); + + /* + * Constants + */ + /// Blocks to wait before the protocol transfer can be completed + /// This allows anyone with a stake to unstake under the existing + /// protocol if they disagree with the new proposed protocol + /// @dev from OpenST ^v1.0 this constant will be set to a significant value + uint256 constant public PROTOCOL_TRANSFER_BLOCKS_TO_WAIT = 5; + + /* + * Storage + */ + /// OpenST protocol contract + address public openSTProtocol; + /// proposed OpenST protocol + address public proposedProtocol; + /// earliest protocol transfer height + uint256 public earliestTransferHeight; + + /* + * Modifiers + */ + modifier onlyProtocol() { + require(msg.sender == openSTProtocol); + _; + } + + modifier onlyProposedProtocol() { + require(msg.sender == proposedProtocol); + _; + } + + modifier afterWait() { + require(earliestTransferHeight <= block.number); + _; + } + + modifier notNull(address _address) { + if (_address == 0) + revert(); + _; + } + + // TODO: [ben] add hasCode modifier so that for + // a significant wait time the code at the proposed new + // protocol can be reviewed + + /* + * Public functions + */ + /// @dev Constructor set the OpenST Protocol + function ProtocolVersioned(address _protocol) + public + notNull(_protocol) + { + openSTProtocol = _protocol; + } + + /// @dev initiate protocol transfer + function initiateProtocolTransfer( + address _proposedProtocol) + public + onlyProtocol + notNull(_proposedProtocol) + returns (bool) + { + require(_proposedProtocol != openSTProtocol); + + earliestTransferHeight = block.number + PROTOCOL_TRANSFER_BLOCKS_TO_WAIT; + proposedProtocol = _proposedProtocol; + + ProtocolTransferInitiated(openSTProtocol, _proposedProtocol, earliestTransferHeight); + + return true; + } + + /// @dev only after the waiting period, can + /// proposed protocol complete the transfer + function completeProtocolTransfer() + public + onlyProposedProtocol + afterWait + returns (bool) + { + openSTProtocol = proposedProtocol; + proposedProtocol = address(0); + earliestTransferHeight = 0; + + ProtocolTransferCompleted(openSTProtocol); + + return true; + } + + /// @dev protocol can revoke initiated protocol + /// transfer + function revokeProtocolTransfer() + public + onlyProtocol + returns (bool) + { + require(proposedProtocol != address(0)); + + proposedProtocol = address(0); + earliestTransferHeight = 0; + + return true; + } + +} \ No newline at end of file diff --git a/contracts/STPrime.sol b/contracts/STPrime.sol new file mode 100644 index 00000000..91dae8eb --- /dev/null +++ b/contracts/STPrime.sol @@ -0,0 +1,109 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// contracts/STPrime.sol +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +/// Simple Token Prime [ST'] is equivalently staked for with Simple Token +/// on the value chain and is the base token that pays for gas on the utility chain. +/// The gasprice on utility chains is set in [ST'-Wei/gas] (like Ether pays for gas +/// on Ethereum mainnet) when sending a transaction on the open utility chain. + +import "./SafeMath.sol"; +import "./UtilityTokenAbstract.sol"; +import "./STPrimeConfig.sol"; + +/* + * @title STPrime - is a freely tradable equivalent representation of Simple Token [ST] + * on Ethereum mainnet on the utility chain + * @dev STPrime functions as the base token to pay for gas consumption on the utility chain + * It is not an ERC20 token, but functions as the genesis guardian + * of the finite amount of base tokens on the utility chain + */ +contract STPrime is UtilityTokenAbstract, STPrimeConfig { + using SafeMath for uint256; + + /* + * Storage + */ + + + /* + * Public functions + */ + function STPrime( + address _openSTProtocol, + bytes32 _uuid) + UtilityTokenAbstract(_openSTProtocol, _uuid) + public + { + + } + + /// @dev returns total token supply + function totalSupply() public view returns (uint256) { + return totalTokenSupply; + } + + function claim( + address _beneficiary) + public + returns (bool success) + { + uint256 amount = claims[_beneficiary]; + require(this.balance >= amount); + claims[_beneficiary] = 0; + + // transfer throws if insufficient funds + _beneficiary.transfer(amount); + + return true; + } + + /// @dev Mint new utility token into + function mint( + address _beneficiary, + uint256 _amount) + public + onlyProtocol + returns (bool success) + { + // can't mint more ST' than there are available base tokens + assert(this.balance >= totalTokenSupply + _amount); + + // 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 _redeemer, uint256 _amount) + public + onlyProtocol + 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(_redeemer, _amount); + } +} \ No newline at end of file diff --git a/contracts/STPrimeConfig.sol b/contracts/STPrimeConfig.sol new file mode 100644 index 00000000..8fb32447 --- /dev/null +++ b/contracts/STPrimeConfig.sol @@ -0,0 +1,37 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// SafeMath Library Implementation +// +// http://www.simpletoken.org/ +// +// Based on the SafeMath library by the OpenZeppelin team. +// Copyright (c) 2016 Smart Contract Solutions, Inc. +// https://github.com/OpenZeppelin/zeppelin-solidity +// The MIT License. +// ---------------------------------------------------------------------------- + + +contract STPrimeConfig { + + string public constant TOKEN_SYMBOL = "STP"; + string public constant TOKEN_NAME = "SimpleTokenPrime"; + uint8 public constant TOKEN_DECIMALS = 18; + + uint256 public constant DECIMALSFACTOR = 10**uint256(TOKEN_DECIMALS); + uint256 public constant TOKENS_MAX = 800000000 * DECIMALSFACTOR; +} diff --git a/contracts/SimpleStake.sol b/contracts/SimpleStake.sol index c9deb34e..1e3cba70 100644 --- a/contracts/SimpleStake.sol +++ b/contracts/SimpleStake.sol @@ -15,38 +15,27 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// SimpleStake - holds the value for a utility token on the OpenST platform -// +// contracts/SimpleStake +// // http://www.simpletoken.org/ // // ---------------------------------------------------------------------------- import "./EIP20Interface.sol"; import "./SafeMath.sol"; +import "./ProtocolVersioned.sol"; -/// @title SimpleStake - holds the value of an EIP20 token +/// @title SimpleStake - stakes the value of an EIP20 token on Ethereum /// for a utility token on the OpenST platform /// @author OpenST Ltd. -contract SimpleStake { +contract SimpleStake is ProtocolVersioned { using SafeMath for uint256; /* * Events */ event ReleasedStake(address indexed _protocol, address indexed _to, uint256 _amount); - event ProtocolTransferInitiated(address indexed _existingProtocol, address indexed _proposedProtocol, uint256 _activationHeight); - event ProtocolTransferRevoked(address indexed _existingProtocol, address indexed _revokedProtocol); - event ProtocolTransferCompleted(address indexed _newProtocol); - /* - * Constants - */ - /// Blocks to wait before the protocol transfer can be completed - /// This allows anyone with a stake to unstake under the existing - /// protocol if they disagree with the new proposed protocol - /// @dev from OpenST ^v1.0 this constant will be set to a significant value - uint256 constant public PROTOCOL_TRANSFER_BLOCKS_TO_WAIT = 5; - /* * Storage */ @@ -55,36 +44,6 @@ contract SimpleStake { /// UUID for the utility token bytes32 public uuid; - /// OpenST protocol contract - address public openSTProtocol; - /// proposed OpenST protocol - address public proposedProtocol; - /// earliest protocol transfer height - uint256 public earliestTransferHeight; - - /* - * Modifiers - */ - modifier onlyProtocol() { - require(msg.sender == openSTProtocol); - _; - } - - modifier onlyProposedProtocolAfterWait() { - require(msg.sender == proposedProtocol); - require(earliestTransferHeight <= block.number); - _; - } - - // TODO: [ben] change notNull to hasCode so that for - // a significant wait time the code at the proposed new - // protocol can be reviewed - modifier notNull(address _address) { - if (_address == 0) - revert(); - _; - } - /* * Public functions */ @@ -96,10 +55,10 @@ contract SimpleStake { EIP20Interface _eip20Token, address _openSTProtocol, bytes32 _uuid) + ProtocolVersioned(_openSTProtocol) public { eip20Token = _eip20Token; - openSTProtocol = _openSTProtocol; uuid = _uuid; } @@ -122,50 +81,6 @@ contract SimpleStake { return true; } - function initiateProtocolTransfer( - address _proposedProtocol) - public - onlyProtocol - notNull(_proposedProtocol) - returns (bool) - { - require(_proposedProtocol != openSTProtocol); - - earliestTransferHeight = block.number + PROTOCOL_TRANSFER_BLOCKS_TO_WAIT; - proposedProtocol = _proposedProtocol; - - ProtocolTransferInitiated(openSTProtocol, _proposedProtocol, earliestTransferHeight); - - return true; - } - - - function completeProtocolTransfer() - public - onlyProposedProtocolAfterWait - returns (bool) - { - openSTProtocol = proposedProtocol; - proposedProtocol = address(0); - - ProtocolTransferCompleted(openSTProtocol); - - return true; - } - - function revokeProtocolTransfer() - public - onlyProtocol - returns (bool) - { - require(proposedProtocol != address(0)); - - proposedProtocol = address(0); - earliestTransferHeight = 0; - - return true; - } - /* * Web3 call functions */ diff --git a/contracts/UtilityTokenAbstract.sol b/contracts/UtilityTokenAbstract.sol new file mode 100644 index 00000000..bd913e27 --- /dev/null +++ b/contracts/UtilityTokenAbstract.sol @@ -0,0 +1,103 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// contracts/UtilityToken.sol +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +import "./SafeMath.sol"; +import "./ProtocolVersioned.sol"; + +/// @title UtilityToken abstract +contract UtilityTokenAbstract is ProtocolVersioned { + 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 _openClaim, uint256 _totalSupply); + event Burnt(bytes32 indexed _uuid, address indexed _account, + uint256 _amount, uint256 _totalSupply); + + /* + * Storage + */ + /// UUID for the utility token + bytes32 public uuid; + /// totalSupply holds the total supply of utility tokens + uint256 internal totalTokenSupply; + /// claims is follows EIP20 allowance pattern but + /// for a staker to stake the utility token for a beneficiary + mapping(address => uint256) public claims; + + /* + * Public functions + */ + function UtilityTokenAbstract(address _protocol, bytes32 _uuid) + public + ProtocolVersioned(_protocol) + { + uuid = _uuid; + totalTokenSupply = 0; + } + + /// @dev claim transfers all utility tokens to _beneficiary + function claim( + address _beneficiary) + public + returns (bool success); + + /* + * Internal functions + */ + /// @dev Mint new utility token by adding a claim + /// for the beneficiary + function mintInternal( + address _beneficiary, + uint256 _amount) + internal + returns (bool success) + { + totalTokenSupply = totalTokenSupply.add(_amount); + claims[_beneficiary] = claims[_beneficiary].add(_amount); + + Minted(uuid, _beneficiary, _amount, claims[_beneficiary], totalTokenSupply); + + return true; + } + + /// @dev Burn utility tokens after having redeemed them + /// through the protocol for the staked Simple Token + function burnInternal( + address _redeemer, + uint256 _amount) + internal + returns (bool success) + { + totalTokenSupply = totalTokenSupply.sub(_amount); + + Burnt(uuid, _redeemer, _amount, totalTokenSupply); + + return true; + } +} \ No newline at end of file From 17344b03a933c2ef48ca7f4daacca9ae6f2ae304 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Mon, 20 Nov 2017 15:12:21 +0530 Subject: [PATCH 09/17] contracts: restrict visibility of totalTokenSupply --- contracts/BrandedToken.sol | 23 ++++++++++++++++++++--- contracts/STPrime.sol | 20 +++++++++----------- contracts/UtilityTokenAbstract.sol | 11 ++++++++++- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/contracts/BrandedToken.sol b/contracts/BrandedToken.sol index b2a749ee..805143fb 100644 --- a/contracts/BrandedToken.sol +++ b/contracts/BrandedToken.sol @@ -22,6 +22,8 @@ pragma solidity ^0.4.17; // ---------------------------------------------------------------------------- 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 @@ -33,8 +35,23 @@ import "./SafeMath.sol"; /// - hard-exit for all users if the utility chain halts to reclaim /// their equivalent part of the Simple Token stake /// on Ethereum (before v1.0) -// contract BrandedToken is EIP20Token, UtilityTokenAbstract { -// using SafeMath for uint256; +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 + { + + } -// } \ No newline at end of file +} \ No newline at end of file diff --git a/contracts/STPrime.sol b/contracts/STPrime.sol index 91dae8eb..42442173 100644 --- a/contracts/STPrime.sol +++ b/contracts/STPrime.sol @@ -40,11 +40,6 @@ import "./STPrimeConfig.sol"; contract STPrime is UtilityTokenAbstract, STPrimeConfig { using SafeMath for uint256; - /* - * Storage - */ - - /* * Public functions */ @@ -56,11 +51,6 @@ contract STPrime is UtilityTokenAbstract, STPrimeConfig { { } - - /// @dev returns total token supply - function totalSupply() public view returns (uint256) { - return totalTokenSupply; - } function claim( address _beneficiary) @@ -86,7 +76,7 @@ contract STPrime is UtilityTokenAbstract, STPrimeConfig { returns (bool success) { // can't mint more ST' than there are available base tokens - assert(this.balance >= totalTokenSupply + _amount); + assert(this.balance >= totalSupplyInternal() + _amount); // add the minted amount to the beneficiary's claim return mintInternal(_beneficiary, _amount); @@ -106,4 +96,12 @@ contract STPrime is UtilityTokenAbstract, STPrimeConfig { return burnInternal(_redeemer, _amount); } + + /* + * Web3 call functions + */ + /// @dev returns total token supply + function totalSupply() public view returns (uint256) { + return totalSupplyInternal(); + } } \ No newline at end of file diff --git a/contracts/UtilityTokenAbstract.sol b/contracts/UtilityTokenAbstract.sol index bd913e27..839abc60 100644 --- a/contracts/UtilityTokenAbstract.sol +++ b/contracts/UtilityTokenAbstract.sol @@ -45,7 +45,7 @@ contract UtilityTokenAbstract is ProtocolVersioned { /// UUID for the utility token bytes32 public uuid; /// totalSupply holds the total supply of utility tokens - uint256 internal totalTokenSupply; + uint256 private totalTokenSupply; /// claims is follows EIP20 allowance pattern but /// for a staker to stake the utility token for a beneficiary mapping(address => uint256) public claims; @@ -100,4 +100,13 @@ contract UtilityTokenAbstract is ProtocolVersioned { return true; } + + /// @dev Get totalTokenSupply as view so that child cannot edit + function totalSupplyInternal() + internal + view + returns (uint256) + { + return totalTokenSupply; + } } \ No newline at end of file From 51f845cd8ed1f5032c257bb68d01975be7450d1d Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Mon, 20 Nov 2017 17:32:01 +0530 Subject: [PATCH 10/17] contracts: branded token --- contracts/BrandedToken.sol | 41 +++++++++++++++++++- contracts/EIP20Token.sol | 32 +++++++++++----- contracts/STPrime.sol | 28 ++++++-------- contracts/UtilityTokenAbstract.sol | 60 +++++++++++++++++++++--------- 4 files changed, 115 insertions(+), 46 deletions(-) diff --git a/contracts/BrandedToken.sol b/contracts/BrandedToken.sol index 805143fb..982c9f41 100644 --- a/contracts/BrandedToken.sol +++ b/contracts/BrandedToken.sol @@ -53,5 +53,44 @@ contract BrandedToken is EIP20Token, UtilityTokenAbstract { { } - + + function claim( + address _beneficiary) + public + returns (bool /* success */) + { + uint256 amount = claimInternal(_beneficiary); + + return claimEIP20(_beneficiary, amount); + } + + function mint( + address _beneficiary, + uint256 _amount) + public + onlyProtocol + returns (bool /* success */) + { + mintEIP20(_amount); + assert(balanceOf(address(this)) >= totalSupply() + _amount); + + return mintInternal(_beneficiary, _amount); + } + + function burn( + address _redeemer, + uint256 _amount) + public + onlyProtocol + payable + returns (bool /* success */) + { + // force non-payable, as only ST' handles in base tokens + require(msg.value == 0); + require(_amount <= balanceOf(msg.sender)); + + balances[msg.sender] = balances[msg.sender].sub(_amount); + + return burnInternal(_redeemer, _amount); + } } \ No newline at end of file diff --git a/contracts/EIP20Token.sol b/contracts/EIP20Token.sol index 8656e0d1..91a78077 100644 --- a/contracts/EIP20Token.sol +++ b/contracts/EIP20Token.sol @@ -31,10 +31,9 @@ import "./SafeMath.sol"; contract EIP20Token is EIP20Interface { using SafeMath for uint256; - string internal tokenName; - string internal tokenSymbol; - uint8 internal tokenDecimals; - uint256 internal tokenTotalSupply; + string private tokenName; + string private tokenSymbol; + uint8 private tokenDecimals; mapping(address => uint256) balances; mapping(address => mapping (address => uint256)) allowed; @@ -45,7 +44,6 @@ contract EIP20Token is EIP20Interface { tokenSymbol = _symbol; tokenName = _name; tokenDecimals = _decimals; - tokenTotalSupply = 0; } @@ -64,11 +62,6 @@ contract EIP20Token is EIP20Interface { } - function totalSupply() public view returns (uint256) { - return tokenTotalSupply; - } - - function balanceOf(address _owner) public view returns (uint256) { return balances[_owner]; } @@ -111,4 +104,23 @@ contract EIP20Token is EIP20Interface { return true; } + + + function claimEIP20(address _beneficiary, uint256 _amount) internal returns (bool success) { + // claimable tokens are minted in the contract address to be pulled on claim + balances[address(this)] = balances[address(this)].sub(_amount); + balances[_beneficiary] = balances[_beneficiary].add(_amount); + + Transfer(address(this), _beneficiary, _amount); + + return true; + } + + + function mintEIP20(uint256 _amount) internal returns (bool /* success */) { + // mint EIP20 tokens in contract address for them to be claimed + balances[address(this)] = balances[address(this)].add(_amount); + + return true; + } } diff --git a/contracts/STPrime.sol b/contracts/STPrime.sol index 42442173..a44e5915 100644 --- a/contracts/STPrime.sol +++ b/contracts/STPrime.sol @@ -52,14 +52,14 @@ contract STPrime is UtilityTokenAbstract, STPrimeConfig { } + /// @dev transfer full claim to beneficiary function claim( address _beneficiary) public - returns (bool success) + returns (bool /* success */) { - uint256 amount = claims[_beneficiary]; - require(this.balance >= amount); - claims[_beneficiary] = 0; + uint256 amount = claimInternal(_beneficiary); + assert(this.balance >= amount); // transfer throws if insufficient funds _beneficiary.transfer(amount); @@ -73,10 +73,10 @@ contract STPrime is UtilityTokenAbstract, STPrimeConfig { uint256 _amount) public onlyProtocol - returns (bool success) + returns (bool /* success */) { // can't mint more ST' than there are available base tokens - assert(this.balance >= totalSupplyInternal() + _amount); + assert(this.balance >= totalSupply() + _amount); // add the minted amount to the beneficiary's claim return mintInternal(_beneficiary, _amount); @@ -84,24 +84,18 @@ contract STPrime is UtilityTokenAbstract, STPrimeConfig { /// @dev Burn utility tokens after having redeemed them /// through the protocol for the staked Simple Token - function burn(address _redeemer, uint256 _amount) + function burn( + address _redeemer, + uint256 _amount) public onlyProtocol payable - returns (bool success) + 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(_redeemer, _amount); - } - - /* - * Web3 call functions - */ - /// @dev returns total token supply - function totalSupply() public view returns (uint256) { - return totalSupplyInternal(); - } + } } \ No newline at end of file diff --git a/contracts/UtilityTokenAbstract.sol b/contracts/UtilityTokenAbstract.sol index 839abc60..a2fec504 100644 --- a/contracts/UtilityTokenAbstract.sol +++ b/contracts/UtilityTokenAbstract.sol @@ -48,7 +48,7 @@ contract UtilityTokenAbstract is ProtocolVersioned { uint256 private totalTokenSupply; /// claims is follows EIP20 allowance pattern but /// for a staker to stake the utility token for a beneficiary - mapping(address => uint256) public claims; + mapping(address => uint256) private claims; /* * Public functions @@ -60,23 +60,56 @@ contract UtilityTokenAbstract is ProtocolVersioned { uuid = _uuid; totalTokenSupply = 0; } - - /// @dev claim transfers all utility tokens to _beneficiary - function claim( - address _beneficiary) - public - returns (bool success); + + /// @dev transfer full claim to beneficiary + function claim(address _beneficiary) public returns (bool success); + /// @dev Mint new utility token into + 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 _redeemer, uint256 _amount) public payable returns (bool success); + + /// @dev Get totalTokenSupply as view so that child cannot edit + function totalSupply() + public + view + returns (uint256 /* supply */) + { + return totalTokenSupply; + } + + /// @dev returns unclaimed amount for 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) + { + amount = claims[_beneficiary]; + claims[_beneficiary] = 0; + + return amount; + } + /// @dev Mint new utility token by adding a claim /// for the beneficiary function mintInternal( address _beneficiary, uint256 _amount) internal - returns (bool success) + returns (bool /* success */) { totalTokenSupply = totalTokenSupply.add(_amount); claims[_beneficiary] = claims[_beneficiary].add(_amount); @@ -92,7 +125,7 @@ contract UtilityTokenAbstract is ProtocolVersioned { address _redeemer, uint256 _amount) internal - returns (bool success) + returns (bool /* success */) { totalTokenSupply = totalTokenSupply.sub(_amount); @@ -100,13 +133,4 @@ contract UtilityTokenAbstract is ProtocolVersioned { return true; } - - /// @dev Get totalTokenSupply as view so that child cannot edit - function totalSupplyInternal() - internal - view - returns (uint256) - { - return totalTokenSupply; - } } \ No newline at end of file From 10a5774453c9a99415b4c3fd3cd933f16032c95c Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Mon, 20 Nov 2017 17:40:10 +0530 Subject: [PATCH 11/17] contracts: split source explicitly over utility and value chain --- contracts/{ => utilityChain}/BrandedToken.sol | 2 +- contracts/{ => utilityChain}/EIP20Token.sol | 4 ++-- contracts/{ => utilityChain}/OpenSTUtility.sol | 2 +- contracts/{ => utilityChain}/STPrime.sol | 0 contracts/{ => utilityChain}/STPrimeConfig.sol | 0 contracts/{ => utilityChain}/UtilityTokenAbstract.sol | 4 ++-- contracts/{ => valueChain}/OpenSTValue.sol | 2 +- contracts/{ => valueChain}/SimpleStake.sol | 6 +++--- 8 files changed, 10 insertions(+), 10 deletions(-) rename contracts/{ => utilityChain}/BrandedToken.sol (99%) rename contracts/{ => utilityChain}/EIP20Token.sol (98%) rename contracts/{ => utilityChain}/OpenSTUtility.sol (97%) rename contracts/{ => utilityChain}/STPrime.sol (100%) rename contracts/{ => utilityChain}/STPrimeConfig.sol (100%) rename contracts/{ => utilityChain}/UtilityTokenAbstract.sol (98%) rename contracts/{ => valueChain}/OpenSTValue.sol (97%) rename contracts/{ => valueChain}/SimpleStake.sol (96%) diff --git a/contracts/BrandedToken.sol b/contracts/utilityChain/BrandedToken.sol similarity index 99% rename from contracts/BrandedToken.sol rename to contracts/utilityChain/BrandedToken.sol index 982c9f41..2f9ab5cf 100644 --- a/contracts/BrandedToken.sol +++ b/contracts/utilityChain/BrandedToken.sol @@ -21,7 +21,7 @@ pragma solidity ^0.4.17; // // ---------------------------------------------------------------------------- -import "./SafeMath.sol"; +import "../SafeMath.sol"; import "./EIP20Token.sol"; import "./UtilityTokenAbstract.sol"; diff --git a/contracts/EIP20Token.sol b/contracts/utilityChain/EIP20Token.sol similarity index 98% rename from contracts/EIP20Token.sol rename to contracts/utilityChain/EIP20Token.sol index 91a78077..6f8eb463 100644 --- a/contracts/EIP20Token.sol +++ b/contracts/utilityChain/EIP20Token.sol @@ -21,8 +21,8 @@ pragma solidity ^0.4.17; // // ---------------------------------------------------------------------------- -import "./EIP20Interface.sol"; -import "./SafeMath.sol"; +import "../EIP20Interface.sol"; +import "../SafeMath.sol"; /** @title EIP20Token diff --git a/contracts/OpenSTUtility.sol b/contracts/utilityChain/OpenSTUtility.sol similarity index 97% rename from contracts/OpenSTUtility.sol rename to contracts/utilityChain/OpenSTUtility.sol index 4da9bfa4..60e5a8bc 100644 --- a/contracts/OpenSTUtility.sol +++ b/contracts/utilityChain/OpenSTUtility.sol @@ -21,7 +21,7 @@ pragma solidity ^0.4.17; // // ---------------------------------------------------------------------------- -import "./SafeMath.sol"; +import "../SafeMath.sol"; /// @title OpenST Utility /// @notice diff --git a/contracts/STPrime.sol b/contracts/utilityChain/STPrime.sol similarity index 100% rename from contracts/STPrime.sol rename to contracts/utilityChain/STPrime.sol diff --git a/contracts/STPrimeConfig.sol b/contracts/utilityChain/STPrimeConfig.sol similarity index 100% rename from contracts/STPrimeConfig.sol rename to contracts/utilityChain/STPrimeConfig.sol diff --git a/contracts/UtilityTokenAbstract.sol b/contracts/utilityChain/UtilityTokenAbstract.sol similarity index 98% rename from contracts/UtilityTokenAbstract.sol rename to contracts/utilityChain/UtilityTokenAbstract.sol index a2fec504..75c8d53e 100644 --- a/contracts/UtilityTokenAbstract.sol +++ b/contracts/utilityChain/UtilityTokenAbstract.sol @@ -21,8 +21,8 @@ pragma solidity ^0.4.17; // // ---------------------------------------------------------------------------- -import "./SafeMath.sol"; -import "./ProtocolVersioned.sol"; +import "../SafeMath.sol"; +import "../ProtocolVersioned.sol"; /// @title UtilityToken abstract contract UtilityTokenAbstract is ProtocolVersioned { diff --git a/contracts/OpenSTValue.sol b/contracts/valueChain/OpenSTValue.sol similarity index 97% rename from contracts/OpenSTValue.sol rename to contracts/valueChain/OpenSTValue.sol index f3bee8e4..64c03907 100644 --- a/contracts/OpenSTValue.sol +++ b/contracts/valueChain/OpenSTValue.sol @@ -21,7 +21,7 @@ pragma solidity ^0.4.17; // // ---------------------------------------------------------------------------- -import "./SafeMath.sol"; +import "../SafeMath.sol"; /// @title OpenSTValue - value staking contract for OpenST /// @notice diff --git a/contracts/SimpleStake.sol b/contracts/valueChain/SimpleStake.sol similarity index 96% rename from contracts/SimpleStake.sol rename to contracts/valueChain/SimpleStake.sol index 1e3cba70..1f526353 100644 --- a/contracts/SimpleStake.sol +++ b/contracts/valueChain/SimpleStake.sol @@ -21,9 +21,9 @@ pragma solidity ^0.4.17; // // ---------------------------------------------------------------------------- -import "./EIP20Interface.sol"; -import "./SafeMath.sol"; -import "./ProtocolVersioned.sol"; +import "../EIP20Interface.sol"; +import "../SafeMath.sol"; +import "../ProtocolVersioned.sol"; /// @title SimpleStake - stakes the value of an EIP20 token on Ethereum /// for a utility token on the OpenST platform From df7ff620539549d908f5063f075486638c48f01b Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Tue, 21 Nov 2017 20:06:42 +0530 Subject: [PATCH 12/17] contracts: review comments by Jason Banks --- contracts/Registrar.sol | 2 +- contracts/utilityChain/BrandedToken.sol | 8 +++--- contracts/utilityChain/EIP20Token.sol | 6 +++++ contracts/utilityChain/STPrime.sol | 27 ++++++++++++------- contracts/utilityChain/STPrimeConfig.sol | 6 +---- .../utilityChain/UtilityTokenAbstract.sol | 2 +- 6 files changed, 30 insertions(+), 21 deletions(-) diff --git a/contracts/Registrar.sol b/contracts/Registrar.sol index 7d0b5044..1e1bd501 100644 --- a/contracts/Registrar.sol +++ b/contracts/Registrar.sol @@ -23,7 +23,7 @@ pragma solidity ^0.4.17; import "./OpsManaged.sol"; -/// @title Registrar - registers for a utility token +/// @title Registrar - registers for utility tokens contract Registrar is OpsManaged { /* diff --git a/contracts/utilityChain/BrandedToken.sol b/contracts/utilityChain/BrandedToken.sol index 2f9ab5cf..3efa09d4 100644 --- a/contracts/utilityChain/BrandedToken.sol +++ b/contracts/utilityChain/BrandedToken.sol @@ -72,13 +72,12 @@ contract BrandedToken is EIP20Token, UtilityTokenAbstract { returns (bool /* success */) { mintEIP20(_amount); - assert(balanceOf(address(this)) >= totalSupply() + _amount); return mintInternal(_beneficiary, _amount); } function burn( - address _redeemer, + address _burner, uint256 _amount) public onlyProtocol @@ -87,10 +86,9 @@ contract BrandedToken is EIP20Token, UtilityTokenAbstract { { // force non-payable, as only ST' handles in base tokens require(msg.value == 0); - require(_amount <= balanceOf(msg.sender)); - balances[msg.sender] = balances[msg.sender].sub(_amount); + burnEIP20(_burner, _amount); - return burnInternal(_redeemer, _amount); + return burnInternal(_burner, _amount); } } \ No newline at end of file diff --git a/contracts/utilityChain/EIP20Token.sol b/contracts/utilityChain/EIP20Token.sol index 6f8eb463..bad3518f 100644 --- a/contracts/utilityChain/EIP20Token.sol +++ b/contracts/utilityChain/EIP20Token.sol @@ -123,4 +123,10 @@ contract EIP20Token is EIP20Interface { return true; } + + function burnEIP20(address _burner, uint256 _amount) internal returns (bool /* success */) { + balances[msg.sender] = balances[msg.sender].sub(_amount); + + return true; + } } diff --git a/contracts/utilityChain/STPrime.sol b/contracts/utilityChain/STPrime.sol index a44e5915..7ee0b690 100644 --- a/contracts/utilityChain/STPrime.sol +++ b/contracts/utilityChain/STPrime.sol @@ -26,15 +26,16 @@ pragma solidity ^0.4.17; /// The gasprice on utility chains is set in [ST'-Wei/gas] (like Ether pays for gas /// on Ethereum mainnet) when sending a transaction on the open utility chain. -import "./SafeMath.sol"; +import "../SafeMath.sol"; import "./UtilityTokenAbstract.sol"; import "./STPrimeConfig.sol"; /* - * @title STPrime - is a freely tradable equivalent representation of Simple Token [ST] - * on Ethereum mainnet on the utility chain + * @title STPrime + * @notice a freely tradable equivalent representation of Simple Token [ST] + * on Ethereum mainnet on the utility chain * @dev STPrime functions as the base token to pay for gas consumption on the utility chain - * It is not an ERC20 token, but functions as the genesis guardian + * It is not an EIP20 token, but functions as the genesis guardian * of the finite amount of base tokens on the utility chain */ contract STPrime is UtilityTokenAbstract, STPrimeConfig { @@ -53,6 +54,13 @@ contract STPrime is UtilityTokenAbstract, STPrimeConfig { } /// @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 + /// automatically. + /// @notice for first stake of ST' the gas price by one validator + /// 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 @@ -67,17 +75,18 @@ contract STPrime is UtilityTokenAbstract, STPrimeConfig { return true; } - /// @dev Mint new utility token into + /// @dev Mint new Simple Token Prime into circulation + /// and increase total supply accordingly. + /// Tokens are minted into a claim to ensure that + /// the protocol completion does not continue into + /// foreign contracts at _beneficiary. function mint( address _beneficiary, uint256 _amount) public onlyProtocol returns (bool /* success */) - { - // can't mint more ST' than there are available base tokens - assert(this.balance >= totalSupply() + _amount); - + { // add the minted amount to the beneficiary's claim return mintInternal(_beneficiary, _amount); } diff --git a/contracts/utilityChain/STPrimeConfig.sol b/contracts/utilityChain/STPrimeConfig.sol index 8fb32447..c1c94634 100644 --- a/contracts/utilityChain/STPrimeConfig.sol +++ b/contracts/utilityChain/STPrimeConfig.sol @@ -15,14 +15,10 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// SafeMath Library Implementation +// contracts/utilityChain/STPrimeConfig.sol // // http://www.simpletoken.org/ // -// Based on the SafeMath library by the OpenZeppelin team. -// Copyright (c) 2016 Smart Contract Solutions, Inc. -// https://github.com/OpenZeppelin/zeppelin-solidity -// The MIT License. // ---------------------------------------------------------------------------- diff --git a/contracts/utilityChain/UtilityTokenAbstract.sol b/contracts/utilityChain/UtilityTokenAbstract.sol index 75c8d53e..6099afeb 100644 --- a/contracts/utilityChain/UtilityTokenAbstract.sol +++ b/contracts/utilityChain/UtilityTokenAbstract.sol @@ -35,7 +35,7 @@ contract UtilityTokenAbstract is ProtocolVersioned { /// 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 _openClaim, uint256 _totalSupply); + uint256 _amount, uint256 _unclaimed, uint256 _totalSupply); event Burnt(bytes32 indexed _uuid, address indexed _account, uint256 _amount, uint256 _totalSupply); From 374f0bab0347fdbbe90b65a69f7bb9273743edc0 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Wed, 22 Nov 2017 13:55:35 +0530 Subject: [PATCH 13/17] contracts: Hasher and registry of BT in OpenSTUtility; STPrime on construction --- contracts/Hasher.sol | 84 ++++++++ contracts/utilityChain/BrandedToken.sol | 2 +- contracts/utilityChain/EIP20Token.sol | 2 +- contracts/utilityChain/OpenSTUtility.sol | 188 +++++++++++++++++- contracts/utilityChain/STPrime.sol | 4 +- .../utilityChain/UtilityTokenAbstract.sol | 4 +- 6 files changed, 275 insertions(+), 9 deletions(-) create mode 100644 contracts/Hasher.sol diff --git a/contracts/Hasher.sol b/contracts/Hasher.sol new file mode 100644 index 00000000..17571aa2 --- /dev/null +++ b/contracts/Hasher.sol @@ -0,0 +1,84 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// contracts/Hasher.sol +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +contract Hasher { + + /* + * Public pure functions + */ + function hashUuid( + string _name, + uint256 _chainIdValue, + uint256 _chainIdUtility, + address _openSTUtility, + uint256 _conversionRate) + public + pure + returns (bytes32) + { + return keccak256( + _name, + _chainIdValue, + _chainIdUtility, + _openSTUtility, + _conversionRate); + } + + function hashMintingIntent( + bytes32 _uuid, + address _account, + uint256 _accountNonce, + uint256 _amountST, + uint256 _amountUT, + uint256 _escrowUnlockHeight) + public + pure + returns (bytes32) + { + return keccak256( + _uuid, + _account, + _accountNonce, + _amountST, + _amountUT, + _escrowUnlockHeight); + } + + function hashUnstakingIntent( + bytes32 _uuid, + address _account, + uint256 _accountNonce, + uint256 _amountUT, + uint256 _escrowUnlockHeight) + public + pure + returns (bytes32) + { + return keccak256( + _uuid, + _account, + _accountNonce, + _amountUT, + _escrowUnlockHeight); + } +} \ No newline at end of file diff --git a/contracts/utilityChain/BrandedToken.sol b/contracts/utilityChain/BrandedToken.sol index 3efa09d4..c8125943 100644 --- a/contracts/utilityChain/BrandedToken.sol +++ b/contracts/utilityChain/BrandedToken.sol @@ -87,7 +87,7 @@ contract BrandedToken is EIP20Token, UtilityTokenAbstract { // force non-payable, as only ST' handles in base tokens require(msg.value == 0); - burnEIP20(_burner, _amount); + burnEIP20(_amount); return burnInternal(_burner, _amount); } diff --git a/contracts/utilityChain/EIP20Token.sol b/contracts/utilityChain/EIP20Token.sol index bad3518f..dde2e519 100644 --- a/contracts/utilityChain/EIP20Token.sol +++ b/contracts/utilityChain/EIP20Token.sol @@ -124,7 +124,7 @@ contract EIP20Token is EIP20Interface { return true; } - function burnEIP20(address _burner, uint256 _amount) internal returns (bool /* success */) { + function burnEIP20(uint256 _amount) internal returns (bool /* success */) { balances[msg.sender] = balances[msg.sender].sub(_amount); return true; diff --git a/contracts/utilityChain/OpenSTUtility.sol b/contracts/utilityChain/OpenSTUtility.sol index 60e5a8bc..ae92bf37 100644 --- a/contracts/utilityChain/OpenSTUtility.sol +++ b/contracts/utilityChain/OpenSTUtility.sol @@ -15,21 +15,203 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// OpenST - Value staking contract for OpenST Platform v0.9 on value chain +// contracts/utilityChain/OpenSTUtility.sol // // http://www.simpletoken.org/ // // ---------------------------------------------------------------------------- import "../SafeMath.sol"; +import "../Hasher.sol"; +import "../OpsManaged.sol"; +import "./STPrime.sol"; +import "./STPrimeConfig.sol"; +import "./BrandedToken.sol"; /// @title OpenST Utility -/// @notice -contract OpenSTUtility { +contract OpenSTUtility is Hasher, OpsManaged { using SafeMath for uint256; + /* + * Structures + */ + struct RegisteredBrandedToken { + address tokenAddress; + address registrar; + } + + /* + * Events + */ + event RequestedBrandedToken(address indexed _requester, address indexed _token, + bytes32 _uuid, string _symbol, string _name, uint256 _conversionRate); + + /* + * Constants + */ + string public constant STPRIME_NAME = "SimpleTokenPrime"; + uint8 public constant TOKEN_DECIMALS = 18; + uint256 public constant DECIMALSFACTOR = 10**uint256(TOKEN_DECIMALS); + /* * Storage */ // mapping() + /// 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; + /// registered branded tokens + mapping(bytes32 /* uuid */ => RegisteredBrandedToken) public registeredBrandedTokens; + /// 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 /* BrandedToken */) public symbolRoute; + + /* + * Public functions + */ + function OpenSTUtility( + uint256 _chainIdValue, + uint256 _chainIdUtility) + public + { + chainIdValue = _chainIdValue; + chainIdUtility = _chainIdUtility; + uuidSTPrime = hashUuid( + STPRIME_NAME, + _chainIdValue, + _chainIdUtility, + address(this), + 1); + simpleTokenPrime = new STPrime( + address(this), + uuidSTPrime); + + } + + function proposeBrandedToken( + string _symbol, + string _name, + uint256 _conversionRate) + public + returns (bytes32) + { + bytes32 hashSymbol = keccak256(_symbol); + bytes32 hashName = keccak256(_name); + require(checkAvailability(hashSymbol, hashName)); + + bytes32 btUuid = hashUuid( + _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; + + RequestedBrandedToken(msg.sender, address(proposedBT), btUuid, + _symbol, _name, _conversionRate); + + return btUuid; + } + + function checkAvailability( + bytes32 _hashSymbol, + bytes32 _hashName) + 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 == msg.sender) { + return true; + } + return false; + } + + /* + * Internal functions + */ + + /* + * Operation functions + */ + /// @dev TODO: add events to trigger for each action + + function addNameReservation( + bytes32 _hashName, + address _requester) + public + onlyAdminOrOps + returns (bool /* success */) + { + address requester = nameReservation[_hashName]; + if (requester == _requester) return true; + if (requester == address(0)) { + nameReservation[_hashName] = _requester; + return true; + } + return false; + } + + function setSymbolRoute( + bytes32 _hashSymbol, + address _token) + public + onlyAdminOrOps + returns (bool /* success */) + { + address token = symbolRoute[_hashSymbol]; + if (token == _token) return true; + if (token == address(0)) { + symbolRoute[_hashSymbol] = _token; + return true; + } + return false; + } + + function removeNameReservation( + bytes32 _hashName) + public + onlyAdminOrOps + returns (bool /* success */) + { + require(nameReservation[_hashName] != address(0)); + + delete nameReservation[_hashName]; + return true; + } + + function removeSymbolRoute( + bytes32 _hashSymbol) + public + onlyAdminOrOps + returns (bool /* success */) + { + require(symbolRoute[_hashSymbol] != address(0)); + + delete symbolRoute[_hashSymbol]; + return true; + } } \ No newline at end of file diff --git a/contracts/utilityChain/STPrime.sol b/contracts/utilityChain/STPrime.sol index 7ee0b690..6b85c092 100644 --- a/contracts/utilityChain/STPrime.sol +++ b/contracts/utilityChain/STPrime.sol @@ -94,7 +94,7 @@ contract STPrime is UtilityTokenAbstract, STPrimeConfig { /// @dev Burn utility tokens after having redeemed them /// through the protocol for the staked Simple Token function burn( - address _redeemer, + address _burner, uint256 _amount) public onlyProtocol @@ -105,6 +105,6 @@ contract STPrime is UtilityTokenAbstract, STPrimeConfig { // to the ST' minting contract require(msg.value == _amount); - return burnInternal(_redeemer, _amount); + return burnInternal(_burner, _amount); } } \ No newline at end of file diff --git a/contracts/utilityChain/UtilityTokenAbstract.sol b/contracts/utilityChain/UtilityTokenAbstract.sol index 6099afeb..8d03bbe6 100644 --- a/contracts/utilityChain/UtilityTokenAbstract.sol +++ b/contracts/utilityChain/UtilityTokenAbstract.sol @@ -122,14 +122,14 @@ contract UtilityTokenAbstract is ProtocolVersioned { /// @dev Burn utility tokens after having redeemed them /// through the protocol for the staked Simple Token function burnInternal( - address _redeemer, + address _burner, uint256 _amount) internal returns (bool /* success */) { totalTokenSupply = totalTokenSupply.sub(_amount); - Burnt(uuid, _redeemer, _amount, totalTokenSupply); + Burnt(uuid, _burner, _amount, totalTokenSupply); return true; } From b97dd57ee41ec3d09f342c1fe57a4f311e7e1ef0 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Thu, 23 Nov 2017 03:25:59 +0530 Subject: [PATCH 14/17] contracts: propose BT on utility, then register on VC and UC --- contracts/Core.sol | 99 +++++++++++ contracts/CoreInterface.sol | 30 ++++ contracts/Hasher.sol | 2 + contracts/OpenSTInterface.sol | 1 - contracts/Registrar.sol | 23 ++- contracts/utilityChain/OpenSTUtility.sol | 101 ++++++++++- .../utilityChain/OpenSTUtilityInterface.sol | 22 +++ .../utilityChain/UtilityTokenAbstract.sol | 2 +- contracts/valueChain/OpenSTValue.sol | 165 +++++++++++++++++- contracts/valueChain/OpenSTValueInterface.sol | 26 +++ contracts/valueChain/SimpleStake.sol | 4 +- 11 files changed, 454 insertions(+), 21 deletions(-) create mode 100644 contracts/Core.sol create mode 100644 contracts/CoreInterface.sol create mode 100644 contracts/utilityChain/OpenSTUtilityInterface.sol create mode 100644 contracts/valueChain/OpenSTValueInterface.sol diff --git a/contracts/Core.sol b/contracts/Core.sol new file mode 100644 index 00000000..c99ec3c9 --- /dev/null +++ b/contracts/Core.sol @@ -0,0 +1,99 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// contracts/Core.sol +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +import "./CoreInterface.sol"; + +contract Core is CoreInterface { + + /* + * Structures + */ + // struct stakeTokenTuple { + // address stake; + // address token; + // } + + + /* + * Storage + */ + /// registrar registers for the two chains + address private coreRegistrar; + /// chainIdOrigin stores the chainId this chain + uint256 private coreChainIdOrigin; + /// chainIdRemote stores the chainId of the remote chain + uint256 private coreChainIdRemote; + /// OpenST remote is the address of the OpenST contract + /// on the remote chain + address private coreOpenSTRemote; + // /// + // mapping(bytes32 => address) stakeTokenTuple; + + + /* + * Public functions + */ + function Core( + address _registrar, + uint256 _chainIdOrigin, + uint256 _chainIdRemote, + address _openSTRemote) + public + { + require(_registrar != address(0)); + require(_chainIdOrigin != 0); + require(_chainIdRemote != 0); + require(_openSTRemote != 0); + coreRegistrar = _registrar; + coreChainIdOrigin = _chainIdOrigin; + coreChainIdRemote = _chainIdRemote; + coreOpenSTRemote = _openSTRemote; + } + + /* + * Public view functions + */ + function registrar() + public + view + returns (address /* registrar */) + { + return coreRegistrar; + } + + function chainIdRemote() + public + view + returns (uint256 /* chainIdRemote */) + { + return coreChainIdRemote; + } + + function openSTRemote() + public + view + returns (address /* OpenSTRemote */) + { + return coreOpenSTRemote; + } +} \ No newline at end of file diff --git a/contracts/CoreInterface.sol b/contracts/CoreInterface.sol new file mode 100644 index 00000000..892b4aff --- /dev/null +++ b/contracts/CoreInterface.sol @@ -0,0 +1,30 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// contracts/CoreInterface.sol +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +contract CoreInterface { + + function registrar() public view returns (address /* registrar */); + + function chainIdRemote() public view returns (uint256 /* chainIdRemote */); + function openSTRemote() public view returns (address /* OpenSTRemote */); +} \ No newline at end of file diff --git a/contracts/Hasher.sol b/contracts/Hasher.sol index 17571aa2..4134d428 100644 --- a/contracts/Hasher.sol +++ b/contracts/Hasher.sol @@ -27,6 +27,7 @@ contract Hasher { * Public pure functions */ function hashUuid( + string _symbol, string _name, uint256 _chainIdValue, uint256 _chainIdUtility, @@ -37,6 +38,7 @@ contract Hasher { returns (bytes32) { return keccak256( + _symbol, _name, _chainIdValue, _chainIdUtility, diff --git a/contracts/OpenSTInterface.sol b/contracts/OpenSTInterface.sol index 19fae9fc..f7b51c26 100644 --- a/contracts/OpenSTInterface.sol +++ b/contracts/OpenSTInterface.sol @@ -20,4 +20,3 @@ pragma solidity ^0.4.17; // http://www.simpletoken.org/ // // ---------------------------------------------------------------------------- - diff --git a/contracts/Registrar.sol b/contracts/Registrar.sol index 1e1bd501..dbd2f38d 100644 --- a/contracts/Registrar.sol +++ b/contracts/Registrar.sol @@ -22,17 +22,22 @@ pragma solidity ^0.4.17; // ---------------------------------------------------------------------------- import "./OpsManaged.sol"; +import "./valueChain/OpenSTValueInterface.sol"; +import "./utilityChain/OpenSTUtilityInterface.sol"; /// @title Registrar - registers for utility tokens contract Registrar is OpsManaged { - /* - * Public functions - */ - function Registrar() - public - OpsManaged() - { - - } + /* + * Public functions + */ + function Registrar() public + OpsManaged() + { + } + + + + + } \ No newline at end of file diff --git a/contracts/utilityChain/OpenSTUtility.sol b/contracts/utilityChain/OpenSTUtility.sol index ae92bf37..49a38a5f 100644 --- a/contracts/utilityChain/OpenSTUtility.sol +++ b/contracts/utilityChain/OpenSTUtility.sol @@ -24,6 +24,7 @@ pragma solidity ^0.4.17; import "../SafeMath.sol"; import "../Hasher.sol"; import "../OpsManaged.sol"; +import "../CoreInterface.sol"; import "./STPrime.sol"; import "./STPrimeConfig.sol"; import "./BrandedToken.sol"; @@ -49,9 +50,14 @@ contract OpenSTUtility is Hasher, OpsManaged { /* * Constants */ + string public constant STPRIME_SYMBOL = "STP"; string public constant STPRIME_NAME = "SimpleTokenPrime"; 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 @@ -64,6 +70,7 @@ contract OpenSTUtility is Hasher, OpsManaged { uint256 public chainIdValue; /// chainId of the current utility chain uint256 public chainIdUtility; + address public registrar; /// registered branded tokens mapping(bytes32 /* uuid */ => RegisteredBrandedToken) public registeredBrandedTokens; /// name reservation is first come, first serve @@ -72,17 +79,35 @@ contract OpenSTUtility is Hasher, OpsManaged { /// and resolves to address mapping(bytes32 /* hashSymbol */ => address /* BrandedToken */) public symbolRoute; + /* + * Modifiers + */ + modifier onlyRegistrar() { + // for now keep unique registrar + require(msg.sender == registrar); + _; + } + /* * Public functions */ function OpenSTUtility( uint256 _chainIdValue, - uint256 _chainIdUtility) + 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, @@ -92,6 +117,7 @@ contract OpenSTUtility is Hasher, OpsManaged { address(this), uuidSTPrime); + // TODO } function proposeBrandedToken( @@ -101,16 +127,22 @@ contract OpenSTUtility is Hasher, OpsManaged { 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)); + require(checkAvailability(hashSymbol, hashName, msg.sender)); bytes32 btUuid = hashUuid( + _symbol, _name, chainIdValue, chainIdUtility, address(this), _conversionRate); + BrandedToken proposedBT = new BrandedToken( address(this), btUuid, @@ -127,9 +159,11 @@ contract OpenSTUtility is Hasher, OpsManaged { return btUuid; } + function checkAvailability( bytes32 _hashSymbol, - bytes32 _hashName) + bytes32 _hashName, + address _requester) public view returns (bool /* success */) @@ -143,16 +177,71 @@ contract OpenSTUtility is Hasher, OpsManaged { // otherwise proposals are first come, first serve // under opt-in discretion of registrar address requester = nameReservation[_hashName]; - if (requester == address(0) || - requester == msg.sender) { + if ((requester == address(0) || + requester == _requester)) { return true; } return false; } /* - * Internal functions + * 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, + bytes32 _checkUuid) + public + onlyRegistrar + returns (bytes32 uuid) + { + 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)); + + uuid = hashUuid( + _symbol, + _name, + chainIdValue, + chainIdUtility, + address(this), + _conversionRate); + + require(uuid == _checkUuid); + + // ... + + return uuid; + } /* * Operation functions diff --git a/contracts/utilityChain/OpenSTUtilityInterface.sol b/contracts/utilityChain/OpenSTUtilityInterface.sol new file mode 100644 index 00000000..a149a9d5 --- /dev/null +++ b/contracts/utilityChain/OpenSTUtilityInterface.sol @@ -0,0 +1,22 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// OpenST protocol interface - Utility chain +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- diff --git a/contracts/utilityChain/UtilityTokenAbstract.sol b/contracts/utilityChain/UtilityTokenAbstract.sol index 8d03bbe6..7c026e17 100644 --- a/contracts/utilityChain/UtilityTokenAbstract.sol +++ b/contracts/utilityChain/UtilityTokenAbstract.sol @@ -67,7 +67,7 @@ contract UtilityTokenAbstract is ProtocolVersioned { 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 _redeemer, uint256 _amount) public payable returns (bool success); + function burn(address _burner, uint256 _amount) public payable returns (bool success); /// @dev Get totalTokenSupply as view so that child cannot edit function totalSupply() diff --git a/contracts/valueChain/OpenSTValue.sol b/contracts/valueChain/OpenSTValue.sol index 64c03907..0dc1c22b 100644 --- a/contracts/valueChain/OpenSTValue.sol +++ b/contracts/valueChain/OpenSTValue.sol @@ -22,14 +22,175 @@ pragma solidity ^0.4.17; // ---------------------------------------------------------------------------- import "../SafeMath.sol"; +import "../Hasher.sol"; +import "../OpsManaged.sol"; +import "../EIP20Interface.sol"; +import "../CoreInterface.sol"; +import "./SimpleStake.sol"; /// @title OpenSTValue - value staking contract for OpenST /// @notice -contract OpenSTValue { +contract OpenSTValue is OpsManaged, Hasher { using SafeMath for uint256; + /* + * Events + */ + event UtilityTokenRegistered(bytes32 indexed _uuid, address indexed stake, + string _symbol, string _name, uint8 _decimals, uint256 _conversionRate, + uint256 _chainIdUtility, address indexed _stakingAccount); + + /* + * Constants + */ + 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; + + /* + * Structures + */ + struct UtilityToken { + string symbol; + string name; + uint256 conversionRate; + uint8 decimals; + uint256 chainIdUtility; + address simpleStake; + address stakingAccount; + mapping(bytes32 /* hashStakingIntent */ => Stake) stakes; + mapping(bytes32 /* hashRedemptionIntent */ => Unstake) unstakes; + } + + struct Stake { + address staker; + address beneficiary; + uint256 amountST; + uint256 amountUT; + uint256 escrowUnlockHeight; + } + + struct Unstake { + address unstaker; + uint256 amount; + } + /* * Storage */ - // mapping() + uint256 public chainIdValue; + EIP20Interface public valueToken; + address public registrar; + mapping(uint256 /* chainIdUtility */ => CoreInterface) cores; + mapping(bytes32 /* uuid */ => UtilityToken) utilityTokens; + + /* + * Modifiers + */ + modifier onlyRegistrar() { + // for now keep unique registrar + require(msg.sender == registrar); + _; + } + + /* + * Public functions + */ + function OpenSTValue( + uint256 _chainIdValue, + EIP20Interface _eip20token, + address _registrar) + public + OpsManaged() + { + require(_chainIdValue != 0); + require(_eip20token != address(0)); + require(_registrar != address(0)); + + chainIdValue = _chainIdValue; + valueToken = _eip20token; + // registrar cannot be reset + // TODO: require it to be a contract + registrar = _registrar; + } + + /// @dev In order to stake the tx.origin needs to set an allowance + /// for the OpenSTValue contract to transfer to itself to hold + /// during the staking process. + // function stake( + // ) + + + /* + * Registrar functions + */ + function addCore( + CoreInterface _core) + public + onlyRegistrar + returns (bool /* success */) + { + require(address(_core) != address(0)); + // core constructed with same registrar + require(registrar == _core.registrar()); + // on value chain core only tracks a remote utility chain + uint256 chainIdUtility = _core.chainIdRemote(); + require(chainIdUtility != 0); + // cannot overwrite core for given chainId + require(cores[chainIdUtility] == address(0)); + + cores[chainIdUtility] = _core; + + return true; + } + + function registerUtilityToken( + string _symbol, + string _name, + uint256 _conversionRate, + uint256 _chainIdUtility, + address _stakingAccount, + bytes32 _checkUuid) + public + onlyRegistrar + returns (bytes32 uuid) + { + require(bytes(_name).length > 0); + require(bytes(_symbol).length > 0); + require(_conversionRate > 0); + + address openSTRemote = cores[_chainIdUtility].openSTRemote(); + require(openSTRemote != address(0)); + + uuid = hashUuid( + _symbol, + _name, + chainIdValue, + _chainIdUtility, + openSTRemote, + _conversionRate); + + require(uuid == _checkUuid); + + SimpleStake simpleStake = new SimpleStake( + valueToken, address(this), uuid); + + utilityTokens[uuid] = UtilityToken({ + symbol: _symbol, + name: _name, + conversionRate: _conversionRate, + decimals: TOKEN_DECIMALS, + chainIdUtility: _chainIdUtility, + simpleStake: address(simpleStake), + stakingAccount: _stakingAccount + }); + + UtilityTokenRegistered(uuid, address(simpleStake), _symbol, _name, + TOKEN_DECIMALS, _conversionRate, _chainIdUtility, _stakingAccount); + + return uuid; + } } \ No newline at end of file diff --git a/contracts/valueChain/OpenSTValueInterface.sol b/contracts/valueChain/OpenSTValueInterface.sol new file mode 100644 index 00000000..0d33c3f9 --- /dev/null +++ b/contracts/valueChain/OpenSTValueInterface.sol @@ -0,0 +1,26 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// OpenST protocol interface - Value chain +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +contract OpenSTValueInterface { + +} \ No newline at end of file diff --git a/contracts/valueChain/SimpleStake.sol b/contracts/valueChain/SimpleStake.sol index 1f526353..33c5fa07 100644 --- a/contracts/valueChain/SimpleStake.sol +++ b/contracts/valueChain/SimpleStake.sol @@ -47,8 +47,8 @@ contract SimpleStake is ProtocolVersioned { /* * Public functions */ - /// @dev Contract constructor sets initial owner and EIP20 token that - /// @param _eip20Token EIP20 token contract that will be staked + /// @dev Contract constructor sets the protocol and the EIP20 token to stake + /// @param _eip20Token EIP20 token that will be staked /// @param _openSTProtocol OpenSTProtocol contract that governs staking /// @param _uuid Unique Universal Identifier of the registered utility token function SimpleStake( From 449f1dee6fbd3dea2c50bd022e01a7e70ec45c48 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Thu, 23 Nov 2017 07:06:35 +0530 Subject: [PATCH 15/17] contracts: register on OpenSTUtility --- contracts/utilityChain/OpenSTUtility.sol | 43 +++++++++++++++++++----- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/contracts/utilityChain/OpenSTUtility.sol b/contracts/utilityChain/OpenSTUtility.sol index 49a38a5f..d7d967d0 100644 --- a/contracts/utilityChain/OpenSTUtility.sol +++ b/contracts/utilityChain/OpenSTUtility.sol @@ -36,7 +36,7 @@ contract OpenSTUtility is Hasher, OpsManaged { /* * Structures */ - struct RegisteredBrandedToken { + struct RegisteredToken { address tokenAddress; address registrar; } @@ -46,7 +46,8 @@ contract OpenSTUtility is Hasher, OpsManaged { */ event RequestedBrandedToken(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); /* * Constants */ @@ -72,12 +73,12 @@ contract OpenSTUtility is Hasher, OpsManaged { uint256 public chainIdUtility; address public registrar; /// registered branded tokens - mapping(bytes32 /* uuid */ => RegisteredBrandedToken) public registeredBrandedTokens; + mapping(bytes32 /* uuid */ => RegisteredToken) public 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 /* BrandedToken */) public symbolRoute; + mapping(bytes32 /* hashSymbol */ => address /* UtilityToken */) public symbolRoute; /* * Modifiers @@ -117,7 +118,18 @@ contract OpenSTUtility is Hasher, OpsManaged { address(this), uuidSTPrime); - // TODO + registeredTokens[uuidSTPrime] = RegisteredToken({ + tokenAddress: 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 } function proposeBrandedToken( @@ -215,6 +227,7 @@ contract OpenSTUtility is Hasher, OpsManaged { string _name, uint256 _conversionRate, address _requester, + BrandedToken _brandedToken, bytes32 _checkUuid) public onlyRegistrar @@ -237,9 +250,23 @@ contract OpenSTUtility is Hasher, OpsManaged { _conversionRate); require(uuid == _checkUuid); - - // ... - + require(_brandedToken.uuid() == _checkUuid); + + assert(registeredTokens[uuid].tokenAddress == address(0)); + + registeredTokens[uuid] = RegisteredToken({ + tokenAddress: _brandedToken, + registrar: registrar + }); + + // register name to registrar + nameReservation[hashName] = registrar; + // register symbol + symbolRoute[hashSymbol] = _brandedToken; + + RegisteredBrandedToken(registrar, _brandedToken, uuid, _symbol, _name, + _conversionRate, _requester); + return uuid; } From d7b8b677cf8a92f501f2435845d54fdf9a8a2c59 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Fri, 24 Nov 2017 15:06:02 +0530 Subject: [PATCH 16/17] contracts: stake and confirmStakingIntent --- contracts/Hasher.sol | 4 +- contracts/Registrar.sol | 3 - contracts/utilityChain/OpenSTUtility.sol | 123 ++++++++++++++++-- .../utilityChain/UtilityTokenAbstract.sol | 17 +-- .../utilityChain/UtilityTokenInterface.sol | 36 +++++ contracts/valueChain/OpenSTValue.sol | 83 +++++++++++- 6 files changed, 240 insertions(+), 26 deletions(-) create mode 100644 contracts/utilityChain/UtilityTokenInterface.sol diff --git a/contracts/Hasher.sol b/contracts/Hasher.sol index 4134d428..3b575605 100644 --- a/contracts/Hasher.sol +++ b/contracts/Hasher.sol @@ -46,10 +46,11 @@ contract Hasher { _conversionRate); } - function hashMintingIntent( + function hashStakingIntent( bytes32 _uuid, address _account, uint256 _accountNonce, + address _beneficiary, uint256 _amountST, uint256 _amountUT, uint256 _escrowUnlockHeight) @@ -61,6 +62,7 @@ contract Hasher { _uuid, _account, _accountNonce, + _beneficiary, _amountST, _amountUT, _escrowUnlockHeight); diff --git a/contracts/Registrar.sol b/contracts/Registrar.sol index dbd2f38d..2948ba47 100644 --- a/contracts/Registrar.sol +++ b/contracts/Registrar.sol @@ -36,8 +36,5 @@ contract Registrar is OpsManaged { { } - - - } \ No newline at end of file diff --git a/contracts/utilityChain/OpenSTUtility.sol b/contracts/utilityChain/OpenSTUtility.sol index d7d967d0..fa96fa97 100644 --- a/contracts/utilityChain/OpenSTUtility.sol +++ b/contracts/utilityChain/OpenSTUtility.sol @@ -37,10 +37,25 @@ contract OpenSTUtility is Hasher, OpsManaged { * Structures */ struct RegisteredToken { - address tokenAddress; + UtilityTokenInterface token; address registrar; } + + struct Mint { + bytes32 uuid; + address staker; + address beneficiary; + uint256 amount; + uint256 unlockHeight; + } + + struct Redemption { + bytes32 uuid; + address redeemer; + uint256 amountUT; + uint256 escrowUnlockHeight; + } /* * Events */ @@ -48,10 +63,15 @@ contract OpenSTUtility is Hasher, OpsManaged { 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 unlockHeight); + event ProcessedMint(bytes32 indexed _uuid, bytes32 indexed _stakingIntentHash, address _staker, + address _beneficiary, uint256 _amount); + /* * Constants */ - string public constant STPRIME_SYMBOL = "STP"; + string public constant STPRIME_SYMBOL = "STP"; string public constant STPRIME_NAME = "SimpleTokenPrime"; uint8 public constant TOKEN_DECIMALS = 18; uint256 public constant DECIMALSFACTOR = 10**uint256(TOKEN_DECIMALS); @@ -63,7 +83,6 @@ contract OpenSTUtility is Hasher, OpsManaged { /* * Storage */ - // mapping() /// store address of Simple Token Prime address public simpleTokenPrime; bytes32 public uuidSTPrime; @@ -79,7 +98,16 @@ contract OpenSTUtility is Hasher, OpsManaged { /// 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 */ @@ -119,8 +147,8 @@ contract OpenSTUtility is Hasher, OpsManaged { uuidSTPrime); registeredTokens[uuidSTPrime] = RegisteredToken({ - tokenAddress: simpleTokenPrime, - registrar: registrar + token: UtilityTokenInterface(simpleTokenPrime), + registrar: registrar }); // lock name and symbol route for ST' @@ -252,11 +280,11 @@ contract OpenSTUtility is Hasher, OpsManaged { require(uuid == _checkUuid); require(_brandedToken.uuid() == _checkUuid); - assert(registeredTokens[uuid].tokenAddress == address(0)); + assert(address(registeredTokens[uuid].token) == address(0)); registeredTokens[uuid] = RegisteredToken({ - tokenAddress: _brandedToken, - registrar: registrar + token: _brandedToken, + registrar: registrar }); // register name to registrar @@ -270,11 +298,86 @@ contract OpenSTUtility is Hasher, OpsManaged { return uuid; } + function confirmStakingIntent( + bytes32 _uuid, + address _staker, + uint256 _stakerNonce, + address _beneficiary, + uint256 _amountST, + uint256 _amountUT, + uint256 _stakingUnlockHeight, + bytes32 _stakingIntentHash) + external + onlyRegistrar + returns (uint256 unlockHeight) + { + require(address(registeredTokens[_uuid].token) != address(0)); + + require(nonces[_staker] < _stakerNonce); + require(_amountST > 0); + require(_amountUT > 0); + // escrowunlockheight needs to be checked against the core that tracks the value chain + require(_stakingUnlockHeight > 0); + require(_stakingIntentHash != ""); + + unlockHeight = block.number + BLOCKS_TO_WAIT_SHORT; + 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, + unlockHeight: unlockHeight + }); + + StakingIntentConfirmed(_uuid, _stakingIntentHash, _staker, _beneficiary, _amountST, + _amountUT, unlockHeight); + + return unlockHeight; + } + + function processMinting( + bytes32 _stakingIntentHash) + external + returns (address tokenAddress) + { + require(_stakingIntentHash != ""); + + Mint storage mint = mints[_stakingIntentHash]; + require(mint.staker == msg.sender); + require(mint.unlockHeight > block.number); + + UtilityTokenInterface token = registeredTokens[mint.uuid].token; + tokenAddress = address(token); + require(tokenAddress != address(0)); + + require(token.mint(mint.beneficiary, mint.amount)); + + ProcessedMint(mint.uuid, _stakingIntentHash, mint.staker, mint.beneficiary, mint.amount); + + delete mints[_stakingIntentHash]; + + return tokenAddress; + } + + /* * Operation functions */ /// @dev TODO: add events to trigger for each action - function addNameReservation( bytes32 _hashName, address _requester) diff --git a/contracts/utilityChain/UtilityTokenAbstract.sol b/contracts/utilityChain/UtilityTokenAbstract.sol index 7c026e17..ad089e05 100644 --- a/contracts/utilityChain/UtilityTokenAbstract.sol +++ b/contracts/utilityChain/UtilityTokenAbstract.sol @@ -23,9 +23,10 @@ pragma solidity ^0.4.17; import "../SafeMath.sol"; import "../ProtocolVersioned.sol"; +import "./UtilityTokenInterface.sol"; /// @title UtilityToken abstract -contract UtilityTokenAbstract is ProtocolVersioned { +contract UtilityTokenAbstract is ProtocolVersioned, UtilityTokenInterface { using SafeMath for uint256; /* @@ -61,13 +62,13 @@ contract UtilityTokenAbstract is ProtocolVersioned { totalTokenSupply = 0; } - /// @dev transfer full claim to beneficiary - function claim(address _beneficiary) public returns (bool success); - /// @dev Mint new utility token into - 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 transfer full claim to beneficiary + // function claim(address _beneficiary) public returns (bool success); + // /// @dev Mint new utility token into + // 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() diff --git a/contracts/utilityChain/UtilityTokenInterface.sol b/contracts/utilityChain/UtilityTokenInterface.sol new file mode 100644 index 00000000..7bc2ea87 --- /dev/null +++ b/contracts/utilityChain/UtilityTokenInterface.sol @@ -0,0 +1,36 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// contracts/utilityChain/UtilityTokenInterface.sol +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +contract UtilityTokenInterface { + + /// @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); +} \ No newline at end of file diff --git a/contracts/valueChain/OpenSTValue.sol b/contracts/valueChain/OpenSTValue.sol index 0dc1c22b..b840029d 100644 --- a/contracts/valueChain/OpenSTValue.sol +++ b/contracts/valueChain/OpenSTValue.sol @@ -39,6 +39,9 @@ contract OpenSTValue is OpsManaged, Hasher { event UtilityTokenRegistered(bytes32 indexed _uuid, address indexed stake, string _symbol, string _name, uint8 _decimals, uint256 _conversionRate, uint256 _chainIdUtility, address indexed _stakingAccount); + event StakingIntentDeclared(bytes32 indexed _uuid, address indexed _staker, + uint256 _stakerNonce, address _beneficiary, uint256 _amountST, + uint256 _amountUT, uint256 _escrowUnlockHeight, bytes32 _stakingIntentHash); /* * Constants @@ -61,19 +64,20 @@ contract OpenSTValue is OpsManaged, Hasher { uint256 chainIdUtility; address simpleStake; address stakingAccount; - mapping(bytes32 /* hashStakingIntent */ => Stake) stakes; - mapping(bytes32 /* hashRedemptionIntent */ => Unstake) unstakes; } struct Stake { + bytes32 uuid; address staker; address beneficiary; + uint256 nonce; uint256 amountST; uint256 amountUT; uint256 escrowUnlockHeight; } struct Unstake { + bytes32 uuid; address unstaker; uint256 amount; } @@ -86,6 +90,16 @@ contract OpenSTValue is OpsManaged, Hasher { address public registrar; mapping(uint256 /* chainIdUtility */ => CoreInterface) cores; mapping(bytes32 /* uuid */ => UtilityToken) utilityTokens; + /// 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; + /// register the active stakes and unstakes + mapping(bytes32 /* hashStakingIntent */ => Stake) stakes; + mapping(bytes32 /* hashRedemptionIntent */ => Unstake) unstakes; + /* * Modifiers @@ -120,8 +134,69 @@ contract OpenSTValue is OpsManaged, Hasher { /// @dev In order to stake the tx.origin needs to set an allowance /// for the OpenSTValue contract to transfer to itself to hold /// during the staking process. - // function stake( - // ) + function stake( + bytes32 _uuid, + uint256 _amountST, + address _beneficiary) + external + returns ( + uint256 amountUT, + uint256 nonce, + uint256 unlockHeight, + bytes32 stakingIntentHash) + { + // check the staking contract has been approved to spend the amount to stake + // OpenSTValue needs to be able to transfer the stake into its balance for + // keeping until the two-phase process is completed on both chains. + require(_amountST > 0); + // Consider the security risk of using tx.origin; at the same time an allowance + // needs to be set before calling stake over a potentially malicious contract at stakingAccount. + // The second protection is that the staker needs to check the intent hash before + // signing off on completing the two-phased process. + require(valueToken.allowance(tx.origin, address(this)) >= _amountST); + + require(utilityTokens[_uuid].simpleStake != address(0)); + require(_beneficiary != address(0)); + + UtilityToken storage utilityToken = utilityTokens[_uuid]; + + // if the staking account is set to a non-zero address, + // then all transactions have come (from/over) the staking account, + // whether this is an EOA or a contract; tx.origin is putting forward the funds + if (utilityToken.stakingAccount != address(0)) require(msg.sender == utilityToken.stakingAccount); + require(valueToken.transferFrom(tx.origin, address(this), _amountST)); + + amountUT = _amountST.mul(utilityToken.conversionRate); + unlockHeight = block.number + BLOCKS_TO_WAIT_LONG; + + nonces[tx.origin]++; + nonce = nonces[tx.origin]; + + stakingIntentHash = hashStakingIntent( + _uuid, + tx.origin, + nonce, + _beneficiary, + _amountST, + amountUT, + unlockHeight + ); + + stakes[stakingIntentHash] = Stake({ + uuid: _uuid, + staker: tx.origin, + beneficiary: _beneficiary, + nonce: nonce, + amountST: _amountST, + amountUT: amountUT, + escrowUnlockHeight: unlockHeight + }); + + StakingIntentDeclared(_uuid, tx.origin, nonce, _beneficiary, + _amountST, amountUT, unlockHeight, stakingIntentHash); + + return (amountUT, nonce, unlockHeight, stakingIntentHash); + } /* From 087e91a52871e79f2192858540b2d6f02a61a34b Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Fri, 24 Nov 2017 19:18:16 +0530 Subject: [PATCH 17/17] contracts: reorganise contracts and update headers to indicate which chain they belong on - fixes #2 --- contracts/{utilityChain => }/BrandedToken.sol | 6 +- contracts/Core.sol | 2 +- contracts/CoreInterface.sol | 2 +- contracts/EIP20Interface.sol | 2 +- contracts/{utilityChain => }/EIP20Token.sol | 6 +- contracts/Hasher.sol | 2 +- contracts/OpenSTInterface.sol | 22 -- .../{utilityChain => }/OpenSTUtility.sol | 17 +- .../OpenSTUtilityInterface.sol | 2 +- contracts/{valueChain => }/OpenSTValue.sol | 14 +- .../{valueChain => }/OpenSTValueInterface.sol | 2 +- contracts/OpsManaged.sol | 2 +- contracts/Owned.sol | 2 +- contracts/ProtocolVersioned.sol | 2 +- contracts/Registrar.sol | 6 +- contracts/{utilityChain => }/STPrime.sol | 6 +- .../{utilityChain => }/STPrimeConfig.sol | 2 +- contracts/SafeMath.sol | 2 +- contracts/{valueChain => }/SimpleStake.sol | 8 +- .../UtilityTokenAbstract.sol | 8 +- .../UtilityTokenInterface.sol | 2 +- contracts/v0.9.0_Staking.so_ | 264 ------------------ contracts/v0.9.0_StakingData.so_ | 72 ----- contracts/v0.9.0_UtilityToken.so_ | 178 ------------ contracts/v0.9.0_UtilityTokenData.so_ | 55 ---- 25 files changed, 53 insertions(+), 633 deletions(-) rename contracts/{utilityChain => }/BrandedToken.sol (96%) rename contracts/{utilityChain => }/EIP20Token.sol (97%) delete mode 100644 contracts/OpenSTInterface.sol rename contracts/{utilityChain => }/OpenSTUtility.sol (97%) rename contracts/{utilityChain => }/OpenSTUtilityInterface.sol (94%) rename contracts/{valueChain => }/OpenSTValue.sol (97%) rename contracts/{valueChain => }/OpenSTValueInterface.sol (95%) rename contracts/{utilityChain => }/STPrime.sol (97%) rename contracts/{utilityChain => }/STPrimeConfig.sol (96%) rename contracts/{valueChain => }/SimpleStake.sol (96%) rename contracts/{utilityChain => }/UtilityTokenAbstract.sol (96%) rename contracts/{utilityChain => }/UtilityTokenInterface.sol (96%) delete mode 100644 contracts/v0.9.0_Staking.so_ delete mode 100644 contracts/v0.9.0_StakingData.so_ delete mode 100644 contracts/v0.9.0_UtilityToken.so_ delete mode 100644 contracts/v0.9.0_UtilityTokenData.so_ diff --git a/contracts/utilityChain/BrandedToken.sol b/contracts/BrandedToken.sol similarity index 96% rename from contracts/utilityChain/BrandedToken.sol rename to contracts/BrandedToken.sol index c8125943..ba2ad157 100644 --- a/contracts/utilityChain/BrandedToken.sol +++ b/contracts/BrandedToken.sol @@ -15,13 +15,15 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// contracts/BrandedToken.sol +// Utility chain: BrandedToken // // http://www.simpletoken.org/ // // ---------------------------------------------------------------------------- -import "../SafeMath.sol"; +import "./SafeMath.sol"; + +// utility chain contracts import "./EIP20Token.sol"; import "./UtilityTokenAbstract.sol"; diff --git a/contracts/Core.sol b/contracts/Core.sol index c99ec3c9..678e11bd 100644 --- a/contracts/Core.sol +++ b/contracts/Core.sol @@ -15,7 +15,7 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// contracts/Core.sol +// Common: Core // // http://www.simpletoken.org/ // diff --git a/contracts/CoreInterface.sol b/contracts/CoreInterface.sol index 892b4aff..a6e94847 100644 --- a/contracts/CoreInterface.sol +++ b/contracts/CoreInterface.sol @@ -15,7 +15,7 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// contracts/CoreInterface.sol +// Common: CoreInterface // // http://www.simpletoken.org/ // diff --git a/contracts/EIP20Interface.sol b/contracts/EIP20Interface.sol index 67042047..350793da 100644 --- a/contracts/EIP20Interface.sol +++ b/contracts/EIP20Interface.sol @@ -15,7 +15,7 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// Standard EIP20 Interface +// Common: Standard EIP20 Interface // // http://www.simpletoken.org/ // diff --git a/contracts/utilityChain/EIP20Token.sol b/contracts/EIP20Token.sol similarity index 97% rename from contracts/utilityChain/EIP20Token.sol rename to contracts/EIP20Token.sol index dde2e519..8f68d2fe 100644 --- a/contracts/utilityChain/EIP20Token.sol +++ b/contracts/EIP20Token.sol @@ -15,14 +15,14 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// EIP20 Token Implementation +// Utility chain: EIP20 Token Implementation // // http://www.simpletoken.org/ // // ---------------------------------------------------------------------------- -import "../EIP20Interface.sol"; -import "../SafeMath.sol"; +import "./EIP20Interface.sol"; +import "./SafeMath.sol"; /** @title EIP20Token diff --git a/contracts/Hasher.sol b/contracts/Hasher.sol index 3b575605..52321920 100644 --- a/contracts/Hasher.sol +++ b/contracts/Hasher.sol @@ -15,7 +15,7 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// contracts/Hasher.sol +// Common: Hasher // // http://www.simpletoken.org/ // diff --git a/contracts/OpenSTInterface.sol b/contracts/OpenSTInterface.sol deleted file mode 100644 index f7b51c26..00000000 --- a/contracts/OpenSTInterface.sol +++ /dev/null @@ -1,22 +0,0 @@ -pragma solidity ^0.4.17; - -// Copyright 2017 OpenST Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// ---------------------------------------------------------------------------- -// OpenST protocol interface -// -// http://www.simpletoken.org/ -// -// ---------------------------------------------------------------------------- diff --git a/contracts/utilityChain/OpenSTUtility.sol b/contracts/OpenSTUtility.sol similarity index 97% rename from contracts/utilityChain/OpenSTUtility.sol rename to contracts/OpenSTUtility.sol index fa96fa97..b6cb7dce 100644 --- a/contracts/utilityChain/OpenSTUtility.sol +++ b/contracts/OpenSTUtility.sol @@ -15,16 +15,18 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// contracts/utilityChain/OpenSTUtility.sol +// Utility chain: OpenSTUtility // // http://www.simpletoken.org/ // // ---------------------------------------------------------------------------- -import "../SafeMath.sol"; -import "../Hasher.sol"; -import "../OpsManaged.sol"; -import "../CoreInterface.sol"; +import "./SafeMath.sol"; +import "./Hasher.sol"; +import "./OpsManaged.sol"; +import "./CoreInterface.sol"; + +// utility chain contracts import "./STPrime.sol"; import "./STPrimeConfig.sol"; import "./BrandedToken.sol"; @@ -73,6 +75,7 @@ contract OpenSTUtility is Hasher, OpsManaged { */ 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 @@ -141,7 +144,7 @@ contract OpenSTUtility is Hasher, OpsManaged { _chainIdValue, _chainIdUtility, address(this), - 1); + STPRIME_CONVERSION_RATE); simpleTokenPrime = new STPrime( address(this), uuidSTPrime); @@ -343,7 +346,7 @@ contract OpenSTUtility is Hasher, OpsManaged { unlockHeight: unlockHeight }); - StakingIntentConfirmed(_uuid, _stakingIntentHash, _staker, _beneficiary, _amountST, + StakingIntentConfirmed(_uuid, stakingIntentHash, _staker, _beneficiary, _amountST, _amountUT, unlockHeight); return unlockHeight; diff --git a/contracts/utilityChain/OpenSTUtilityInterface.sol b/contracts/OpenSTUtilityInterface.sol similarity index 94% rename from contracts/utilityChain/OpenSTUtilityInterface.sol rename to contracts/OpenSTUtilityInterface.sol index a149a9d5..3a202de9 100644 --- a/contracts/utilityChain/OpenSTUtilityInterface.sol +++ b/contracts/OpenSTUtilityInterface.sol @@ -15,7 +15,7 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// OpenST protocol interface - Utility chain +// Utility chain: OpenSTUtilityInterface // // http://www.simpletoken.org/ // diff --git a/contracts/valueChain/OpenSTValue.sol b/contracts/OpenSTValue.sol similarity index 97% rename from contracts/valueChain/OpenSTValue.sol rename to contracts/OpenSTValue.sol index b840029d..7b43757c 100644 --- a/contracts/valueChain/OpenSTValue.sol +++ b/contracts/OpenSTValue.sol @@ -15,17 +15,19 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// OpenST - Value staking contract for OpenST Platform v0.9 on value chain +// Value chain: OpenSTValue // // http://www.simpletoken.org/ // // ---------------------------------------------------------------------------- -import "../SafeMath.sol"; -import "../Hasher.sol"; -import "../OpsManaged.sol"; -import "../EIP20Interface.sol"; -import "../CoreInterface.sol"; +import "./SafeMath.sol"; +import "./Hasher.sol"; +import "./OpsManaged.sol"; +import "./EIP20Interface.sol"; +import "./CoreInterface.sol"; + +// value chain contracts import "./SimpleStake.sol"; /// @title OpenSTValue - value staking contract for OpenST diff --git a/contracts/valueChain/OpenSTValueInterface.sol b/contracts/OpenSTValueInterface.sol similarity index 95% rename from contracts/valueChain/OpenSTValueInterface.sol rename to contracts/OpenSTValueInterface.sol index 0d33c3f9..b7af0bec 100644 --- a/contracts/valueChain/OpenSTValueInterface.sol +++ b/contracts/OpenSTValueInterface.sol @@ -15,7 +15,7 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// OpenST protocol interface - Value chain +// Value chain: OpenST protocol interface // // http://www.simpletoken.org/ // diff --git a/contracts/OpsManaged.sol b/contracts/OpsManaged.sol index 5bbc84c5..34037873 100644 --- a/contracts/OpsManaged.sol +++ b/contracts/OpsManaged.sol @@ -15,7 +15,7 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// Admin / Ops Permission Model +// Common: Admin / Ops Permission Model // // http://www.simpletoken.org/ // diff --git a/contracts/Owned.sol b/contracts/Owned.sol index 2a1c1ec4..d748cf7a 100644 --- a/contracts/Owned.sol +++ b/contracts/Owned.sol @@ -15,7 +15,7 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// Basic Ownership Implementation +// Common: Basic Ownership Implementation // // http://www.simpletoken.org/ // diff --git a/contracts/ProtocolVersioned.sol b/contracts/ProtocolVersioned.sol index 474319e9..2ab7a462 100644 --- a/contracts/ProtocolVersioned.sol +++ b/contracts/ProtocolVersioned.sol @@ -15,7 +15,7 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// contracts/ProtocolVersioned.sol +// Common: ProtocolVersioned.sol // // http://www.simpletoken.org/ // diff --git a/contracts/Registrar.sol b/contracts/Registrar.sol index 2948ba47..caf79ec0 100644 --- a/contracts/Registrar.sol +++ b/contracts/Registrar.sol @@ -15,15 +15,15 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// Registrar +// Common: Registrar // // http://www.simpletoken.org/ // // ---------------------------------------------------------------------------- import "./OpsManaged.sol"; -import "./valueChain/OpenSTValueInterface.sol"; -import "./utilityChain/OpenSTUtilityInterface.sol"; +import "./OpenSTValueInterface.sol"; +import "./OpenSTUtilityInterface.sol"; /// @title Registrar - registers for utility tokens contract Registrar is OpsManaged { diff --git a/contracts/utilityChain/STPrime.sol b/contracts/STPrime.sol similarity index 97% rename from contracts/utilityChain/STPrime.sol rename to contracts/STPrime.sol index 6b85c092..afd7ea54 100644 --- a/contracts/utilityChain/STPrime.sol +++ b/contracts/STPrime.sol @@ -15,7 +15,7 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// contracts/STPrime.sol +// Utility chain: STPrime // // http://www.simpletoken.org/ // @@ -26,7 +26,9 @@ pragma solidity ^0.4.17; /// The gasprice on utility chains is set in [ST'-Wei/gas] (like Ether pays for gas /// on Ethereum mainnet) when sending a transaction on the open utility chain. -import "../SafeMath.sol"; +import "./SafeMath.sol"; + +// utility chain contracts import "./UtilityTokenAbstract.sol"; import "./STPrimeConfig.sol"; diff --git a/contracts/utilityChain/STPrimeConfig.sol b/contracts/STPrimeConfig.sol similarity index 96% rename from contracts/utilityChain/STPrimeConfig.sol rename to contracts/STPrimeConfig.sol index c1c94634..96b1da2a 100644 --- a/contracts/utilityChain/STPrimeConfig.sol +++ b/contracts/STPrimeConfig.sol @@ -15,7 +15,7 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// contracts/utilityChain/STPrimeConfig.sol +// Utility chain: STPrimeConfig // // http://www.simpletoken.org/ // diff --git a/contracts/SafeMath.sol b/contracts/SafeMath.sol index 780788ed..379f7be2 100644 --- a/contracts/SafeMath.sol +++ b/contracts/SafeMath.sol @@ -15,7 +15,7 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// SafeMath Library Implementation +// Common: SafeMath Library Implementation // // http://www.simpletoken.org/ // diff --git a/contracts/valueChain/SimpleStake.sol b/contracts/SimpleStake.sol similarity index 96% rename from contracts/valueChain/SimpleStake.sol rename to contracts/SimpleStake.sol index 33c5fa07..42445349 100644 --- a/contracts/valueChain/SimpleStake.sol +++ b/contracts/SimpleStake.sol @@ -15,15 +15,15 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// contracts/SimpleStake +// ValueChain: SimpleStake // // http://www.simpletoken.org/ // // ---------------------------------------------------------------------------- -import "../EIP20Interface.sol"; -import "../SafeMath.sol"; -import "../ProtocolVersioned.sol"; +import "./EIP20Interface.sol"; +import "./SafeMath.sol"; +import "./ProtocolVersioned.sol"; /// @title SimpleStake - stakes the value of an EIP20 token on Ethereum /// for a utility token on the OpenST platform diff --git a/contracts/utilityChain/UtilityTokenAbstract.sol b/contracts/UtilityTokenAbstract.sol similarity index 96% rename from contracts/utilityChain/UtilityTokenAbstract.sol rename to contracts/UtilityTokenAbstract.sol index ad089e05..f32f317e 100644 --- a/contracts/utilityChain/UtilityTokenAbstract.sol +++ b/contracts/UtilityTokenAbstract.sol @@ -15,14 +15,16 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// contracts/UtilityToken.sol +// Utility chain: UtilityTokenAbstract // // http://www.simpletoken.org/ // // ---------------------------------------------------------------------------- -import "../SafeMath.sol"; -import "../ProtocolVersioned.sol"; +import "./SafeMath.sol"; +import "./ProtocolVersioned.sol"; + +// utility chain contracts import "./UtilityTokenInterface.sol"; /// @title UtilityToken abstract diff --git a/contracts/utilityChain/UtilityTokenInterface.sol b/contracts/UtilityTokenInterface.sol similarity index 96% rename from contracts/utilityChain/UtilityTokenInterface.sol rename to contracts/UtilityTokenInterface.sol index 7bc2ea87..da9086ab 100644 --- a/contracts/utilityChain/UtilityTokenInterface.sol +++ b/contracts/UtilityTokenInterface.sol @@ -15,7 +15,7 @@ pragma solidity ^0.4.17; // limitations under the License. // // ---------------------------------------------------------------------------- -// contracts/utilityChain/UtilityTokenInterface.sol +// Utility chain: UtilityTokenInterface // // http://www.simpletoken.org/ // diff --git a/contracts/v0.9.0_Staking.so_ b/contracts/v0.9.0_Staking.so_ deleted file mode 100644 index eebfc268..00000000 --- a/contracts/v0.9.0_Staking.so_ +++ /dev/null @@ -1,264 +0,0 @@ -pragma solidity ^0.4.17; - -// Copyright 2017 OpenST Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// ---------------------------------------------------------------------------- -// Staking -// -// http://www.simpletoken.org/ -// -// ---------------------------------------------------------------------------- - -import "./SafeMath.sol"; -import "./OpsManaged.sol"; -import "./StakingData.sol"; - -/** - @title Staking - @notice Enables staking tokens on a value chain to make use of utility tokens on a utility chain -*/ -contract Staking is OpsManaged, StakingData { - using SafeMath for uint256; - - event UtilityTokenRegistered(bytes32 indexed _uuid, string _symbol, string _name, uint8 _decimals, uint _conversionRate, bytes32 _chainId, address indexed _stakingAccount); - event UtilityTokenStakingAccountSet(bytes32 indexed _uuid, address _newStakingAccount); - event MintingIntentDeclared(bytes32 indexed _uuid, address indexed _staker, uint256 _stakerNonce, uint256 _amountST, uint256 _amountUT, uint256 _escrowUnlockHeight, bytes32 _mintingIntentHash /* redundant for abundance of clarity for MVU */); - event Staked(bytes32 indexed _uuid, address indexed _staker, uint256 _amountST, uint256 _amountUT); - event UnstakingIntentConfirmed(bytes32 indexed _uuid, bytes32 _unstakingIntentHash); - event Unstaked(bytes32 indexed _uuid, address indexed _unstaker, uint256 _amount); - - function Staking(EIP20Interface _eip20Token) OpsManaged() public { - eip20Token = _eip20Token; - } - - function hashMintingIntent( - bytes32 _uuid, - address _account, - uint256 _accountNonce, - uint256 _amountST, - uint256 _amountUT, - uint256 _escrowUnlockHeight - ) - public pure returns (bytes32) { - return keccak256(_uuid, _account, _accountNonce, _amountST, _amountUT, _escrowUnlockHeight); - } - - function hashUnstakingIntent( - bytes32 _uuid, - address _account, - uint256 _accountNonce, - uint256 _amountUT, - uint256 _escrowUnlockHeight - ) - public pure returns (bytes32) { - return keccak256(_uuid, _account, _accountNonce, _amountUT, _escrowUnlockHeight); - } - - // @dev `_chainId` should be blank for v0.9 - // @dev `_stakingAccount` is optional - function registerUtilityToken( - string _symbol, - string _name, - uint8 _decimals, - uint _conversionRate, - bytes32 _chainId, - address _stakingAccount - ) - public onlyAdmin returns(bytes32) { - require(bytes(_name).length > 0); - require(bytes(_symbol).length > 0); - require(_decimals > 0); - require(_conversionRate > 0); - - bytes32 uuid = keccak256(_name, _chainId); - - // Confirm that UUID has not already been registered - require(utilityTokens[uuid].conversionRate == 0); - - utilityTokens[uuid] = UtilityToken({ - symbol: _symbol, - name: _name, - decimals: _decimals, - conversionRate: _conversionRate, - chainId: _chainId, - stakedST: 0, - stakingAccount: _stakingAccount - }); - - UtilityTokenRegistered(uuid, _symbol, _name, _decimals, _conversionRate, _chainId, _stakingAccount); - - return uuid; - } - - // TODO: 2 step change - // Require that if this is 0, it cannot be set to non-0 - // function setUtilityTokenStakingAccount(bytes32 _uuid, address _newStakingAccount) public returns (bool) { - // require(_uuid != ""); - // require(utilityTokens[_uuid].conversionRate > 0); - // require(_newStakingAccount != address(0)); - - // // TODO; - // } - - function stake( - bytes32 _uuid, - uint256 _amountST - ) - external - returns ( - uint256 _nonce, - uint256 _unlockHeight - ) { - require(_uuid != ""); - require(utilityTokens[_uuid].conversionRate > 0); - require(_amountST > 0); - - UtilityToken storage utilityToken = utilityTokens[_uuid]; - - if (utilityToken.stakingAccount != address(0)) require(msg.sender == utilityToken.stakingAccount); - require(eip20Token.transferFrom(msg.sender, address(this), _amountST)); - - uint256 amountUT = _amountST.mul(utilityToken.conversionRate); - uint256 escrowUnlockHeight = block.number + BLOCKS_TO_WAIT_LONG; - - nonces[msg.sender]++; - - uint256 usedNonce = nonces[msg.sender]; - - bytes32 mintingIntentHash = hashMintingIntent( - _uuid, - msg.sender, - usedNonce, - _amountST, - amountUT, - escrowUnlockHeight - ); - - utilityToken.stakes[mintingIntentHash] = Stake({ - staker: msg.sender, - amountST: _amountST, - amountUT: amountUT, - escrowUnlockHeight: escrowUnlockHeight, - granter: address(0) - }); - - MintingIntentDeclared( - _uuid, - utilityToken.stakes[mintingIntentHash].staker, - usedNonce, - utilityToken.stakes[mintingIntentHash].amountST, - utilityToken.stakes[mintingIntentHash].amountUT, - escrowUnlockHeight, - mintingIntentHash - ); - - return (usedNonce, escrowUnlockHeight); - } - - // when calling hashMintingIntent, make sure the beneficiary is passed in as _staker - // if _uuid == a branded token, require(msg.sender == adminAddress) && require(_beneficiary == utilityTokens[_uuid].stakingAccount) - // requires(eip20Token.transferFrom(_beneficiary, this, _amount)) - // Calls hashMintingIntent - // Adds Stake to stakes - // Emits event: MintingIntentDeclared - // function stakeFor(address _beneficiary, bytes32 _uuid, uint256 _amountST) external returns (bool, uint256) { - // require(_beneficiary != address(0)); - // require(_uuid != ""); - // require(utilityTokens[_uuid].conversionRate > 0); - // require(_amountST > 0); - - // // TODO; - // } - - // @dev Checks msg.sender for purposes of MVU - function processStaking( - bytes32 _uuid, - bytes32 _mintingIntentHash - ) external returns (bool) { - require(_uuid != ""); - require(utilityTokens[_uuid].conversionRate > 0); - require(_mintingIntentHash != ""); - require(utilityTokens[_uuid].stakes[_mintingIntentHash].staker == msg.sender); - - Stake storage stake = utilityTokens[_uuid].stakes[_mintingIntentHash]; - utilityTokens[_uuid].stakedST = utilityTokens[_uuid].stakedST.add(stake.amountST); - - Staked(_uuid, stake.staker, stake.amountST, stake.amountUT); - - delete utilityTokens[_uuid].stakes[_mintingIntentHash]; - - return true; - } - - function unstake( - bytes32 _uuid, - address _unstaker, - uint256 _unstakerNonce, - uint256 _amountUT, - uint256 _escrowUnlockHeight, - bytes32 _unstakingIntentHash - ) external onlyAdmin returns (bool) { - require(_uuid != ""); - require(utilityTokens[_uuid].conversionRate > 0); - require(nonces[_unstaker] + 1 == _unstakerNonce); - require(_amountUT > 0); - require(_escrowUnlockHeight > 0); - require(_unstakingIntentHash != ""); - - nonces[_unstaker]++; - - bytes32 unstakingIntentHash = hashUnstakingIntent( - _uuid, - _unstaker, - nonces[_unstaker], - _amountUT, - _escrowUnlockHeight - ); - - require(_unstakingIntentHash == unstakingIntentHash); - - UtilityToken storage utilityToken = utilityTokens[_uuid]; - uint256 amountST = _amountUT.div(utilityToken.conversionRate); - - utilityTokens[_uuid].unstakes[unstakingIntentHash] = Unstake({ - unstaker: _unstaker, - amount: amountST - }); - - UnstakingIntentConfirmed(_uuid, unstakingIntentHash); - - return true; - } - - function processUnstaking( - bytes32 _uuid, - bytes32 _unstakingIntentHash - ) public returns (bool) { - require(_uuid != ""); - require(utilityTokens[_uuid].conversionRate > 0); - require(_unstakingIntentHash != ""); - - Unstake storage unstake = utilityTokens[_uuid].unstakes[_unstakingIntentHash]; - utilityTokens[_uuid].stakedST = utilityTokens[_uuid].stakedST.sub(unstake.amount); - - Unstaked(_uuid, unstake.unstaker, unstake.amount); - - require(eip20Token.transfer(unstake.unstaker, unstake.amount)); - - delete utilityTokens[_uuid].unstakes[_unstakingIntentHash]; - - return true; - } -} diff --git a/contracts/v0.9.0_StakingData.so_ b/contracts/v0.9.0_StakingData.so_ deleted file mode 100644 index b308e018..00000000 --- a/contracts/v0.9.0_StakingData.so_ +++ /dev/null @@ -1,72 +0,0 @@ -pragma solidity ^0.4.17; - -// Copyright 2017 OpenST Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// ---------------------------------------------------------------------------- -// Staking Data -// -// http://www.simpletoken.org/ -// -// ---------------------------------------------------------------------------- - -import "./EIP20Interface.sol"; - -/** - @title StakingData - @notice Data structures for a Staking contract -*/ -contract StakingData { - EIP20Interface public eip20Token; - - // ~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; - - // [uuid == H(utility token name, chainId)] - mapping(bytes32 => UtilityToken) public utilityTokens; - - // [staker address] - mapping(address => uint) nonces; - - struct UtilityToken { - string symbol; - string name; - uint8 decimals; - uint conversionRate; - bytes32 chainId; - uint256 stakedST; - address stakingAccount; - // [stakingHash] - mapping(bytes32 => Stake) stakes; - // [unstakingHash] - mapping(bytes32 => Unstake) unstakes; - } - - struct Stake { - address staker; - uint256 amountST; - uint256 amountUT; - uint256 escrowUnlockHeight; - // TODO: determine unstaking logic w/r/t granters/grantees - address granter; - } - - struct Unstake { - // TODO: determine unstaking logic w/r/t granters/grantees - address unstaker; - uint256 amount; - } -} diff --git a/contracts/v0.9.0_UtilityToken.so_ b/contracts/v0.9.0_UtilityToken.so_ deleted file mode 100644 index 9f0b9ec8..00000000 --- a/contracts/v0.9.0_UtilityToken.so_ +++ /dev/null @@ -1,178 +0,0 @@ -pragma solidity ^0.4.17; - -// Copyright 2017 OpenST Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// ---------------------------------------------------------------------------- -// Utility Token -// -// http://www.simpletoken.org/ -// -// ---------------------------------------------------------------------------- - -import "./SafeMath.sol"; -import "./EIP20Token.sol"; -import "./UtilityTokenData.sol"; - -/** - @title UtilityToken - @notice Represents utility tokens on a utility chain that are backed by crypto-assets staked on a value chain -*/ -contract UtilityToken is EIP20Token, UtilityTokenData { - using SafeMath for uint256; - - event UnstakingIntentDeclared(bytes32 indexed _uuid, address indexed _unstaker, uint256 _unstakerNonce, uint256 _amountUT, uint256 _escrowUnlockHeight, bytes32 _unstakingIntentHash /* redundant for abundance of clarity for MVU */); - event Redeemed(bytes32 indexed _uuid, address indexed _account, uint256 _amount, uint256 _balance, uint256 _totalSupply); - event MintingIntentConfirmed(bytes32 indexed _uuid, bytes32 _mintingIntentHash); - event Minted(bytes32 indexed _uuid, address indexed _account, uint256 _amount, uint256 _balance, uint256 _totalSupply); - - // TODO: ERC20Token contract that does not take a total supply constructor param - function UtilityToken(string _symbol, string _name, uint8 _decimals, bytes32 _chainId) - EIP20Token(_symbol, _name, _decimals) - public { - require(bytes(_symbol).length > 0); - require(bytes(_name).length > 0); - require(_decimals > 0); - - uuid = keccak256(_name, _chainId); - } - - function hashMintingIntent( - bytes32 _uuid, - address _account, - uint256 _accountNonce, - uint256 _amountST, - uint256 _amountUT, - uint256 _escrowUnlockHeight - ) - public pure returns (bytes32) { - return keccak256(_uuid, _account, _accountNonce, _amountST, _amountUT, _escrowUnlockHeight); - } - - function hashUnstakingIntent( - bytes32 _uuid, - address _account, - uint256 _accountNonce, - uint256 _amountUT, - uint256 _escrowUnlockHeight - ) - public pure returns (bytes32) { - return keccak256(_uuid, _account, _accountNonce, _amountUT, _escrowUnlockHeight); - } - - // @dev There's no need for msg.sender to approve this contract to transfer - // because calling `transfer` from `redeem` has the same msg.sender - // TODO redeem needs user nonce from staking contract as parameter - // and must be strictly greater than stored nonce in UtilityToken - function redeem(uint256 _amountUT) public returns (bool) { - require(_amountUT > 0); - require(transfer(address(this), _amountUT)); - - uint256 escrowUnlockHeight = block.number + BLOCKS_TO_WAIT_LONG; - bytes32 unstakingIntentHash = hashUnstakingIntent( - uuid, - msg.sender, - nonces[msg.sender], - _amountUT, - escrowUnlockHeight - ); - - nonces[msg.sender]++; - - redemptions[unstakingIntentHash] = Redemption({ - redeemer: msg.sender, - amountUT: _amountUT, - escrowUnlockHeight: escrowUnlockHeight - }); - - UnstakingIntentDeclared( - uuid, - msg.sender, - nonces[msg.sender], - _amountUT, - escrowUnlockHeight, - unstakingIntentHash - ); - - return (true); - } - - function processRedemption(bytes32 _unstakingIntentHash) public returns (bool) { - require(_unstakingIntentHash != ""); - require(redemptions[_unstakingIntentHash].redeemer == msg.sender); - - Redemption storage redemption = redemptions[_unstakingIntentHash]; - tokenTotalSupply = tokenTotalSupply.sub(redemption.amountUT); - balances[address(this)] = balances[address(this)].sub(redemption.amountUT); - - Redeemed(uuid, redemption.redeemer, redemption.amountUT, balances[redemption.redeemer], tokenTotalSupply); - - delete redemptions[_unstakingIntentHash]; - - return true; - } - - function mint( - bytes32 _uuid, - address _minter, - uint256 _minterNonce, - uint256 _amountST, - uint256 _amountUT, - uint256 _escrowUnlockHeight, - bytes32 _mintingIntentHash - ) external returns (bool) { - require(_uuid == uuid); - require(nonces[_minter] < _minterNonce); - require(_amountST > 0); - require(_amountUT > 0); - require(_escrowUnlockHeight > 0); - require(_mintingIntentHash != ""); - - nonces[_minter] = _minterNonce; - - bytes32 mintingIntentHash = hashMintingIntent( - _uuid, - _minter, - _minterNonce, - _amountST, - _amountUT, - _escrowUnlockHeight - ); - - require(_mintingIntentHash == mintingIntentHash); - - mints[mintingIntentHash] = Mint({ - minter: _minter, - amount: _amountUT - }); - - MintingIntentConfirmed(_uuid, mintingIntentHash); - - return true; - } - - function processMinting(bytes32 _mintingIntentHash) external returns (bool) { - require(_mintingIntentHash != ""); - - Mint storage mint = mints[_mintingIntentHash]; - balances[mint.minter] = balances[mint.minter].add(mint.amount); - tokenTotalSupply = tokenTotalSupply.add(mint.amount); - - Minted(uuid, mint.minter, mint.amount, balances[mint.minter], tokenTotalSupply); - - delete mints[_mintingIntentHash]; - - return true; - } -} diff --git a/contracts/v0.9.0_UtilityTokenData.so_ b/contracts/v0.9.0_UtilityTokenData.so_ deleted file mode 100644 index cd94b03a..00000000 --- a/contracts/v0.9.0_UtilityTokenData.so_ +++ /dev/null @@ -1,55 +0,0 @@ -pragma solidity ^0.4.17; - -// Copyright 2017 OpenST Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// ---------------------------------------------------------------------------- -// Utility Token Data -// -// http://www.simpletoken.org/ -// -// ---------------------------------------------------------------------------- - -/** - @title UtilityTokenData - @notice Data structures for a UtilityToken contract -*/ -contract UtilityTokenData { - bytes32 public uuid; - - // ~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; - - // [staker address] - mapping(address => uint) nonces; - - // [stakingHash] - mapping(bytes32 => Mint) mints; - - // [unstakingHash] - mapping(bytes32 => Redemption) redemptions; - - struct Mint { - address minter; - uint256 amount; - } - - struct Redemption { - address redeemer; - uint256 amountUT; - uint256 escrowUnlockHeight; - } -}