From ff4c320869e31a0953f3515d093e82a4c4177816 Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Tue, 28 Nov 2017 01:33:25 -0200 Subject: [PATCH 1/8] base structure of upgradable republic --- contracts/token/MiniMeToken.sol | 560 ++++++++++++++++++++++++++++ contracts/token/TokenController.sol | 26 ++ 2 files changed, 586 insertions(+) create mode 100644 contracts/token/MiniMeToken.sol create mode 100644 contracts/token/TokenController.sol diff --git a/contracts/token/MiniMeToken.sol b/contracts/token/MiniMeToken.sol new file mode 100644 index 0000000..de9ecef --- /dev/null +++ b/contracts/token/MiniMeToken.sol @@ -0,0 +1,560 @@ +pragma solidity ^0.4.6; + +/* + Copyright 2016, Jordi Baylina + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/// @title MiniMeToken Contract +/// @author Jordi Baylina +/// @dev This token contract's goal is to make it easy for anyone to clone this +/// token using the token distribution at a given block, this will allow DAO's +/// and DApps to upgrade their features in a decentralized manner without +/// affecting the original token +/// @dev It is ERC20 compliant, but still needs to under go further testing. + +import "../common/Controlled.sol"; +import "./TokenController.sol"; +import "./ApproveAndCallFallBack.sol"; + +/// @dev The actual token contract, the default controller is the msg.sender +/// that deploys the contract, so usually this token will be deployed by a +/// token controller contract, which Giveth will call a "Campaign" +contract MiniMeToken is Controlled { + + string public name; //The Token's name: e.g. DigixDAO Tokens + uint8 public decimals; //Number of decimals of the smallest unit + string public symbol; //An identifier: e.g. REP + string public version = 'MMT_0.1'; //An arbitrary versioning scheme + + + /// @dev `Checkpoint` is the structure that attaches a block number to a + /// given value, the block number attached is the one that last changed the + /// value + struct Checkpoint { + + // `fromBlock` is the block number that the value was generated from + uint128 fromBlock; + + // `value` is the amount of tokens at a specific block number + uint128 value; + } + + // `parentToken` is the Token address that was cloned to produce this token; + // it will be 0x0 for a token that was not cloned + MiniMeToken public parentToken; + + // `parentSnapShotBlock` is the block number from the Parent Token that was + // used to determine the initial distribution of the Clone Token + uint public parentSnapShotBlock; + + // `creationBlock` is the block number that the Clone Token was created + uint public creationBlock; + + // `balances` is the map that tracks the balance of each address, in this + // contract when the balance changes the block number that the change + // occurred is also included in the map + mapping (address => Checkpoint[]) balances; + + // `allowed` tracks any extra transfer rights as in all ERC20 tokens + mapping (address => mapping (address => uint256)) allowed; + + // Tracks the history of the `totalSupply` of the token + Checkpoint[] totalSupplyHistory; + + // Flag that determines if the token is transferable or not. + bool public transfersEnabled; + + // The factory used to create new clone tokens + MiniMeTokenFactory public tokenFactory; + +//////////////// +// Constructor +//////////////// + + /// @notice Constructor to create a MiniMeToken + /// @param _tokenFactory The address of the MiniMeTokenFactory contract that + /// will create the Clone token contracts, the token factory needs to be + /// deployed first + /// @param _parentToken Address of the parent token, set to 0x0 if it is a + /// new token + /// @param _parentSnapShotBlock Block of the parent token that will + /// determine the initial distribution of the clone token, set to 0 if it + /// is a new token + /// @param _tokenName Name of the new token + /// @param _decimalUnits Number of decimals of the new token + /// @param _tokenSymbol Token Symbol for the new token + /// @param _transfersEnabled If true, tokens will be able to be transferred + function MiniMeToken( + address _tokenFactory, + address _parentToken, + uint _parentSnapShotBlock, + string _tokenName, + uint8 _decimalUnits, + string _tokenSymbol, + bool _transfersEnabled + ) { + tokenFactory = MiniMeTokenFactory(_tokenFactory); + name = _tokenName; // Set the name + decimals = _decimalUnits; // Set the decimals + symbol = _tokenSymbol; // Set the symbol + parentToken = MiniMeToken(_parentToken); + parentSnapShotBlock = _parentSnapShotBlock; + transfersEnabled = _transfersEnabled; + creationBlock = block.number; + } + + +/////////////////// +// ERC20 Methods +/////////////////// + + /// @notice Send `_amount` tokens to `_to` from `msg.sender` + /// @param _to The address of the recipient + /// @param _amount The amount of tokens to be transferred + /// @return Whether the transfer was successful or not + function transfer(address _to, uint256 _amount) returns (bool success) { + require(transfersEnabled); + return doTransfer(msg.sender, _to, _amount); + } + + /// @notice Send `_amount` tokens to `_to` from `_from` on the condition it + /// is approved by `_from` + /// @param _from The address holding the tokens being transferred + /// @param _to The address of the recipient + /// @param _amount The amount of tokens to be transferred + /// @return True if the transfer was successful + function transferFrom(address _from, address _to, uint256 _amount + ) returns (bool success) { + + // The controller of this contract can move tokens around at will, + // this is important to recognize! Confirm that you trust the + // controller of this contract, which in most situations should be + // another open source smart contract or 0x0 + if (msg.sender != controller) { + require(transfersEnabled); + + // The standard ERC 20 transferFrom functionality + if (allowed[_from][msg.sender] < _amount) return false; + allowed[_from][msg.sender] -= _amount; + } + return doTransfer(_from, _to, _amount); + } + + /// @dev This is the actual transfer function in the token contract, it can + /// only be called by other functions in this contract. + /// @param _from The address holding the tokens being transferred + /// @param _to The address of the recipient + /// @param _amount The amount of tokens to be transferred + /// @return True if the transfer was successful + function doTransfer(address _from, address _to, uint _amount + ) internal returns(bool) { + + if (_amount == 0) { + return true; + } + + require(parentSnapShotBlock < block.number); + + // Do not allow transfer to 0x0 or the token contract itself + require((_to != 0) && (_to != address(this))); + + // If the amount being transfered is more than the balance of the + // account the transfer returns false + var previousBalanceFrom = balanceOfAt(_from, block.number); + if (previousBalanceFrom < _amount) { + return false; + } + + // Alerts the token controller of the transfer + if (isContract(controller)) { + require(TokenController(controller).onTransfer(_from, _to, _amount)); + } + + // First update the balance array with the new value for the address + // sending the tokens + updateValueAtNow(balances[_from], previousBalanceFrom - _amount); + + // Then update the balance array with the new value for the address + // receiving the tokens + var previousBalanceTo = balanceOfAt(_to, block.number); + require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow + updateValueAtNow(balances[_to], previousBalanceTo + _amount); + + // An event to make the transfer easy to find on the blockchain + Transfer(_from, _to, _amount); + + return true; + } + + /// @param _owner The address that's balance is being requested + /// @return The balance of `_owner` at the current block + function balanceOf(address _owner) constant returns (uint256 balance) { + return balanceOfAt(_owner, block.number); + } + + /// @notice `msg.sender` approves `_spender` to spend `_amount` tokens on + /// its behalf. This is a modified version of the ERC20 approve function + /// to be a little bit safer + /// @param _spender The address of the account able to transfer the tokens + /// @param _amount The amount of tokens to be approved for transfer + /// @return True if the approval was successful + function approve(address _spender, uint256 _amount) returns (bool success) { + require(transfersEnabled); + + // To change the approve amount you first have to reduce the addresses` + // allowance to zero by calling `approve(_spender,0)` if it is not + // already 0 to mitigate the race condition described here: + // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + require((_amount == 0) || (allowed[msg.sender][_spender] == 0)); + + // Alerts the token controller of the approve function call + if (isContract(controller)) { + require(TokenController(controller).onApprove(msg.sender, _spender, _amount)); + } + + allowed[msg.sender][_spender] = _amount; + Approval(msg.sender, _spender, _amount); + return true; + } + + /// @dev This function makes it easy to read the `allowed[]` map + /// @param _owner The address of the account that owns the token + /// @param _spender The address of the account able to transfer the tokens + /// @return Amount of remaining tokens of _owner that _spender is allowed + /// to spend + function allowance(address _owner, address _spender + ) constant returns (uint256 remaining) { + return allowed[_owner][_spender]; + } + + /// @notice `msg.sender` approves `_spender` to send `_amount` tokens on + /// its behalf, and then a function is triggered in the contract that is + /// being approved, `_spender`. This allows users to use their tokens to + /// interact with contracts in one function call instead of two + /// @param _spender The address of the contract able to transfer the tokens + /// @param _amount The amount of tokens to be approved for transfer + /// @return True if the function call was successful + function approveAndCall(address _spender, uint256 _amount, bytes _extraData + ) returns (bool success) { + require(approve(_spender, _amount)); + + ApproveAndCallFallBack(_spender).receiveApproval( + msg.sender, + _amount, + this, + _extraData + ); + + return true; + } + + /// @dev This function makes it easy to get the total number of tokens + /// @return The total number of tokens + function totalSupply() constant returns (uint) { + return totalSupplyAt(block.number); + } + + +//////////////// +// Query balance and totalSupply in History +//////////////// + + /// @dev Queries the balance of `_owner` at a specific `_blockNumber` + /// @param _owner The address from which the balance will be retrieved + /// @param _blockNumber The block number when the balance is queried + /// @return The balance at `_blockNumber` + function balanceOfAt(address _owner, uint _blockNumber) constant + returns (uint) { + + // These next few lines are used when the balance of the token is + // requested before a check point was ever created for this token, it + // requires that the `parentToken.balanceOfAt` be queried at the + // genesis block for that token as this contains initial balance of + // this token + if ((balances[_owner].length == 0) + || (balances[_owner][0].fromBlock > _blockNumber)) { + if (address(parentToken) != 0) { + return parentToken.balanceOfAt(_owner, min(_blockNumber, parentSnapShotBlock)); + } else { + // Has no parent + return 0; + } + + // This will return the expected balance during normal situations + } else { + return getValueAt(balances[_owner], _blockNumber); + } + } + + /// @notice Total amount of tokens at a specific `_blockNumber`. + /// @param _blockNumber The block number when the totalSupply is queried + /// @return The total amount of tokens at `_blockNumber` + function totalSupplyAt(uint _blockNumber) constant returns(uint) { + + // These next few lines are used when the totalSupply of the token is + // requested before a check point was ever created for this token, it + // requires that the `parentToken.totalSupplyAt` be queried at the + // genesis block for this token as that contains totalSupply of this + // token at this block number. + if ((totalSupplyHistory.length == 0) + || (totalSupplyHistory[0].fromBlock > _blockNumber)) { + if (address(parentToken) != 0) { + return parentToken.totalSupplyAt(min(_blockNumber, parentSnapShotBlock)); + } else { + return 0; + } + + // This will return the expected totalSupply during normal situations + } else { + return getValueAt(totalSupplyHistory, _blockNumber); + } + } + +//////////////// +// Clone Token Method +//////////////// + + /// @notice Creates a new clone token with the initial distribution being + /// this token at `_snapshotBlock` + /// @param _cloneTokenName Name of the clone token + /// @param _cloneDecimalUnits Number of decimals of the smallest unit + /// @param _cloneTokenSymbol Symbol of the clone token + /// @param _snapshotBlock Block when the distribution of the parent token is + /// copied to set the initial distribution of the new clone token; + /// if the block is zero than the actual block, the current block is used + /// @param _transfersEnabled True if transfers are allowed in the clone + /// @return The address of the new MiniMeToken Contract + function createCloneToken( + string _cloneTokenName, + uint8 _cloneDecimalUnits, + string _cloneTokenSymbol, + uint _snapshotBlock, + bool _transfersEnabled + ) returns(address) { + if (_snapshotBlock == 0) _snapshotBlock = block.number; + MiniMeToken cloneToken = tokenFactory.createCloneToken( + this, + _snapshotBlock, + _cloneTokenName, + _cloneDecimalUnits, + _cloneTokenSymbol, + _transfersEnabled + ); + + cloneToken.changeController(msg.sender); + + // An event to make the token easy to find on the blockchain + NewCloneToken(address(cloneToken), _snapshotBlock); + return address(cloneToken); + } + +//////////////// +// Generate and destroy tokens +//////////////// + + /// @notice Generates `_amount` tokens that are assigned to `_owner` + /// @param _owner The address that will be assigned the new tokens + /// @param _amount The quantity of tokens generated + /// @return True if the tokens are generated correctly + function generateTokens(address _owner, uint _amount + ) onlyController returns (bool) { + uint curTotalSupply = totalSupply(); + require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow + uint previousBalanceTo = balanceOf(_owner); + require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow + updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount); + updateValueAtNow(balances[_owner], previousBalanceTo + _amount); + Transfer(0, _owner, _amount); + return true; + } + + + /// @notice Burns `_amount` tokens from `_owner` + /// @param _owner The address that will lose the tokens + /// @param _amount The quantity of tokens to burn + /// @return True if the tokens are burned correctly + function destroyTokens(address _owner, uint _amount + ) onlyController returns (bool) { + uint curTotalSupply = totalSupply(); + require(curTotalSupply >= _amount); + uint previousBalanceFrom = balanceOf(_owner); + require(previousBalanceFrom >= _amount); + updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount); + updateValueAtNow(balances[_owner], previousBalanceFrom - _amount); + Transfer(_owner, 0, _amount); + return true; + } + +//////////////// +// Enable tokens transfers +//////////////// + + + /// @notice Enables token holders to transfer their tokens freely if true + /// @param _transfersEnabled True if transfers are allowed in the clone + function enableTransfers(bool _transfersEnabled) onlyController { + transfersEnabled = _transfersEnabled; + } + +//////////////// +// Internal helper functions to query and set a value in a snapshot array +//////////////// + + /// @dev `getValueAt` retrieves the number of tokens at a given block number + /// @param checkpoints The history of values being queried + /// @param _block The block number to retrieve the value at + /// @return The number of tokens being queried + function getValueAt(Checkpoint[] storage checkpoints, uint _block + ) constant internal returns (uint) { + if (checkpoints.length == 0) return 0; + + // Shortcut for the actual value + if (_block >= checkpoints[checkpoints.length-1].fromBlock) + return checkpoints[checkpoints.length-1].value; + if (_block < checkpoints[0].fromBlock) return 0; + + // Binary search of the value in the array + uint min = 0; + uint max = checkpoints.length-1; + while (max > min) { + uint mid = (max + min + 1)/ 2; + if (checkpoints[mid].fromBlock<=_block) { + min = mid; + } else { + max = mid-1; + } + } + return checkpoints[min].value; + } + + /// @dev `updateValueAtNow` used to update the `balances` map and the + /// `totalSupplyHistory` + /// @param checkpoints The history of data being updated + /// @param _value The new number of tokens + function updateValueAtNow(Checkpoint[] storage checkpoints, uint _value + ) internal { + if ((checkpoints.length == 0) + || (checkpoints[checkpoints.length -1].fromBlock < block.number)) { + Checkpoint storage newCheckPoint = checkpoints[ checkpoints.length++ ]; + newCheckPoint.fromBlock = uint128(block.number); + newCheckPoint.value = uint128(_value); + } else { + Checkpoint storage oldCheckPoint = checkpoints[checkpoints.length-1]; + oldCheckPoint.value = uint128(_value); + } + } + + /// @dev Internal function to determine if an address is a contract + /// @param _addr The address being queried + /// @return True if `_addr` is a contract + function isContract(address _addr) constant internal returns(bool) { + uint size; + if (_addr == 0) return false; + assembly { + size := extcodesize(_addr) + } + return size>0; + } + + /// @dev Helper function to return a min betwen the two uints + function min(uint a, uint b) internal returns (uint) { + return a < b ? a : b; + } + + /// @notice The fallback function: If the contract's controller has not been + /// set to 0, then the `proxyPayment` method is called which relays the + /// ether and creates tokens as described in the token controller contract + function () payable { + require(isContract(controller)); + require(TokenController(controller).proxyPayment.value(msg.value)(msg.sender)); + } + +////////// +// Safety Methods +////////// + + /// @notice This method can be used by the controller to extract mistakenly + /// sent tokens to this contract. + /// @param _token The address of the token contract that you want to recover + /// set to 0 in case you want to extract ether. + function claimTokens(address _token) onlyController { + if (_token == 0x0) { + controller.transfer(this.balance); + return; + } + + MiniMeToken token = MiniMeToken(_token); + uint balance = token.balanceOf(this); + token.transfer(controller, balance); + ClaimedTokens(_token, controller, balance); + } + +//////////////// +// Events +//////////////// + event ClaimedTokens(address indexed _token, address indexed _controller, uint _amount); + event Transfer(address indexed _from, address indexed _to, uint256 _amount); + event NewCloneToken(address indexed _cloneToken, uint _snapshotBlock); + event Approval( + address indexed _owner, + address indexed _spender, + uint256 _amount + ); + +} + + +//////////////// +// MiniMeTokenFactory +//////////////// + +/// @dev This contract is used to generate clone contracts from a contract. +/// In solidity this is the way to create a contract from a contract of the +/// same class +contract MiniMeTokenFactory { + + /// @notice Update the DApp by creating a new token with new functionalities + /// the msg.sender becomes the controller of this clone token + /// @param _parentToken Address of the token being cloned + /// @param _snapshotBlock Block of the parent token that will + /// determine the initial distribution of the clone token + /// @param _tokenName Name of the new token + /// @param _decimalUnits Number of decimals of the new token + /// @param _tokenSymbol Token Symbol for the new token + /// @param _transfersEnabled If true, tokens will be able to be transferred + /// @return The address of the new token contract + function createCloneToken( + address _parentToken, + uint _snapshotBlock, + string _tokenName, + uint8 _decimalUnits, + string _tokenSymbol, + bool _transfersEnabled + ) returns (MiniMeToken) { + MiniMeToken newToken = new MiniMeToken( + this, + _parentToken, + _snapshotBlock, + _tokenName, + _decimalUnits, + _tokenSymbol, + _transfersEnabled + ); + + newToken.changeController(msg.sender); + return newToken; + } +} diff --git a/contracts/token/TokenController.sol b/contracts/token/TokenController.sol new file mode 100644 index 0000000..0dff23c --- /dev/null +++ b/contracts/token/TokenController.sol @@ -0,0 +1,26 @@ +pragma solidity ^0.4.14; + +/// @dev The token controller contract must implement these functions +contract TokenController { + /// @notice Called when `_owner` sends ether to the MiniMe Token contract + /// @param _owner The address that sent the ether to create tokens + /// @return True if the ether is accepted, false if it throws + function proxyPayment(address _owner) payable returns(bool); + + /// @notice Notifies the controller about a token transfer allowing the + /// controller to react if desired + /// @param _from The origin of the transfer + /// @param _to The destination of the transfer + /// @param _amount The amount of the transfer + /// @return False if the controller does not authorize the transfer + function onTransfer(address _from, address _to, uint _amount) returns(bool); + + /// @notice Notifies the controller about an approval allowing the + /// controller to react if desired + /// @param _owner The address that calls `approve()` + /// @param _spender The spender in the `approve()` call + /// @param _amount The amount in the `approve()` call + /// @return False if the controller does not authorize the approval + function onApprove(address _owner, address _spender, uint _amount) + returns(bool); +} \ No newline at end of file From 6766c5d7b15bb370f89f140ca3dc579583d54332 Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Wed, 29 Nov 2017 12:42:10 -0200 Subject: [PATCH 2/8] missing file --- contracts/token/ApproveAndCallFallBack.sol | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 contracts/token/ApproveAndCallFallBack.sol diff --git a/contracts/token/ApproveAndCallFallBack.sol b/contracts/token/ApproveAndCallFallBack.sol new file mode 100644 index 0000000..d2086f3 --- /dev/null +++ b/contracts/token/ApproveAndCallFallBack.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.4.14; + +contract ApproveAndCallFallBack { + function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public; +} \ No newline at end of file From de977246879eda1ad5d6d7f622b85071644cdffa Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Tue, 19 Dec 2017 23:06:19 -0200 Subject: [PATCH 3/8] proposal manager executes to its controller --- contracts/token/ApproveAndCallFallBack.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/token/ApproveAndCallFallBack.sol b/contracts/token/ApproveAndCallFallBack.sol index d2086f3..b0b4b4e 100644 --- a/contracts/token/ApproveAndCallFallBack.sol +++ b/contracts/token/ApproveAndCallFallBack.sol @@ -1,5 +1,5 @@ pragma solidity ^0.4.14; contract ApproveAndCallFallBack { - function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public; + function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public ; } \ No newline at end of file From f51ae08fa904cf1073a91613dfff49bee95710a8 Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Wed, 24 Jan 2018 02:09:50 +0000 Subject: [PATCH 4/8] current maineth contracts --- contracts/token/ApproveAndCallFallBack.sol | 5 + contracts/token/MiniMeToken.sol | 533 +++++++++++++++++++++ contracts/token/MiniMeTokenFactory.sol | 45 ++ contracts/token/TokenController.sol | 26 + 4 files changed, 609 insertions(+) create mode 100644 contracts/token/ApproveAndCallFallBack.sol create mode 100644 contracts/token/MiniMeToken.sol create mode 100644 contracts/token/MiniMeTokenFactory.sol create mode 100644 contracts/token/TokenController.sol diff --git a/contracts/token/ApproveAndCallFallBack.sol b/contracts/token/ApproveAndCallFallBack.sol new file mode 100644 index 0000000..10b9c8e --- /dev/null +++ b/contracts/token/ApproveAndCallFallBack.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.4.11; + +contract ApproveAndCallFallBack { + function receiveApproval(address from, uint256 _amount, address _token, bytes _data); +} diff --git a/contracts/token/MiniMeToken.sol b/contracts/token/MiniMeToken.sol new file mode 100644 index 0000000..3855bf5 --- /dev/null +++ b/contracts/token/MiniMeToken.sol @@ -0,0 +1,533 @@ +pragma solidity ^0.4.11; + +import "../common/Controlled.sol"; +import "./TokenController.sol"; +import "./ApproveAndCallFallBack.sol"; + +/* + Copyright 2016, Jordi Baylina + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/// @title MiniMeToken Contract +/// @author Jordi Baylina +/// @dev This token contract's goal is to make it easy for anyone to clone this +/// token using the token distribution at a given block, this will allow DAO's +/// and DApps to upgrade their features in a decentralized manner without +/// affecting the original token +/// @dev It is ERC20 compliant, but still needs to under go further testing. +/// @dev The actual token contract, the default controller is the msg.sender +/// that deploys the contract, so usually this token will be deployed by a +/// token controller contract, which Giveth will call a "Campaign" +contract MiniMeToken is Controlled { + + string public name; //The Token's name: e.g. DigixDAO Tokens + uint8 public decimals; //Number of decimals of the smallest unit + string public symbol; //An identifier: e.g. REP + string public version = 'MMT_0.1'; //An arbitrary versioning scheme + + + /// @dev `Checkpoint` is the structure that attaches a block number to a + /// given value, the block number attached is the one that last changed the + /// value + struct Checkpoint { + + // `fromBlock` is the block number that the value was generated from + uint128 fromBlock; + + // `value` is the amount of tokens at a specific block number + uint128 value; + } + + // `parentToken` is the Token address that was cloned to produce this token; + // it will be 0x0 for a token that was not cloned + MiniMeToken public parentToken; + + // `parentSnapShotBlock` is the block number from the Parent Token that was + // used to determine the initial distribution of the Clone Token + uint public parentSnapShotBlock; + + // `creationBlock` is the block number that the Clone Token was created + uint public creationBlock; + + // `balances` is the map that tracks the balance of each address, in this + // contract when the balance changes the block number that the change + // occurred is also included in the map + mapping (address => Checkpoint[]) balances; + + // `allowed` tracks any extra transfer rights as in all ERC20 tokens + mapping (address => mapping (address => uint256)) allowed; + + // Tracks the history of the `totalSupply` of the token + Checkpoint[] totalSupplyHistory; + + // Flag that determines if the token is transferable or not. + bool public transfersEnabled; + + // The factory used to create new clone tokens + MiniMeTokenFactory public tokenFactory; + +//////////////// +// Constructor +//////////////// + + /// @notice Constructor to create a MiniMeToken + /// @param _tokenFactory The address of the MiniMeTokenFactory contract that + /// will create the Clone token contracts, the token factory needs to be + /// deployed first + /// @param _parentToken Address of the parent token, set to 0x0 if it is a + /// new token + /// @param _parentSnapShotBlock Block of the parent token that will + /// determine the initial distribution of the clone token, set to 0 if it + /// is a new token + /// @param _tokenName Name of the new token + /// @param _decimalUnits Number of decimals of the new token + /// @param _tokenSymbol Token Symbol for the new token + /// @param _transfersEnabled If true, tokens will be able to be transferred + function MiniMeToken( + address _tokenFactory, + address _parentToken, + uint _parentSnapShotBlock, + string _tokenName, + uint8 _decimalUnits, + string _tokenSymbol, + bool _transfersEnabled + ) { + tokenFactory = MiniMeTokenFactory(_tokenFactory); + name = _tokenName; // Set the name + decimals = _decimalUnits; // Set the decimals + symbol = _tokenSymbol; // Set the symbol + parentToken = MiniMeToken(_parentToken); + parentSnapShotBlock = _parentSnapShotBlock; + transfersEnabled = _transfersEnabled; + creationBlock = getBlockNumber(); + } + + +/////////////////// +// ERC20 Methods +/////////////////// + + /// @notice Send `_amount` tokens to `_to` from `msg.sender` + /// @param _to The address of the recipient + /// @param _amount The amount of tokens to be transferred + /// @return Whether the transfer was successful or not + function transfer(address _to, uint256 _amount) returns (bool success) { + if (!transfersEnabled) throw; + return doTransfer(msg.sender, _to, _amount); + } + + /// @notice Send `_amount` tokens to `_to` from `_from` on the condition it + /// is approved by `_from` + /// @param _from The address holding the tokens being transferred + /// @param _to The address of the recipient + /// @param _amount The amount of tokens to be transferred + /// @return True if the transfer was successful + function transferFrom(address _from, address _to, uint256 _amount + ) returns (bool success) { + + // The controller of this contract can move tokens around at will, + // this is important to recognize! Confirm that you trust the + // controller of this contract, which in most situations should be + // another open source smart contract or 0x0 + if (msg.sender != controller) { + if (!transfersEnabled) throw; + + // The standard ERC 20 transferFrom functionality + if (allowed[_from][msg.sender] < _amount) return false; + allowed[_from][msg.sender] -= _amount; + } + return doTransfer(_from, _to, _amount); + } + + /// @dev This is the actual transfer function in the token contract, it can + /// only be called by other functions in this contract. + /// @param _from The address holding the tokens being transferred + /// @param _to The address of the recipient + /// @param _amount The amount of tokens to be transferred + /// @return True if the transfer was successful + function doTransfer(address _from, address _to, uint _amount + ) internal returns(bool) { + + if (_amount == 0) { + return true; + } + + if (parentSnapShotBlock >= getBlockNumber()) throw; + + // Do not allow transfer to 0x0 or the token contract itself + if ((_to == 0) || (_to == address(this))) throw; + + // If the amount being transfered is more than the balance of the + // account the transfer returns false + var previousBalanceFrom = balanceOfAt(_from, getBlockNumber()); + if (previousBalanceFrom < _amount) { + return false; + } + + // Alerts the token controller of the transfer + if (isContract(controller)) { + if (!TokenController(controller).onTransfer(_from, _to, _amount)) + throw; + } + + // First update the balance array with the new value for the address + // sending the tokens + updateValueAtNow(balances[_from], previousBalanceFrom - _amount); + + // Then update the balance array with the new value for the address + // receiving the tokens + var previousBalanceTo = balanceOfAt(_to, getBlockNumber()); + if (previousBalanceTo + _amount < previousBalanceTo) throw; // Check for overflow + updateValueAtNow(balances[_to], previousBalanceTo + _amount); + + // An event to make the transfer easy to find on the blockchain + Transfer(_from, _to, _amount); + + return true; + } + + /// @param _owner The address that's balance is being requested + /// @return The balance of `_owner` at the current block + function balanceOf(address _owner) constant returns (uint256 balance) { + return balanceOfAt(_owner, getBlockNumber()); + } + + /// @notice `msg.sender` approves `_spender` to spend `_amount` tokens on + /// its behalf. This is a modified version of the ERC20 approve function + /// to be a little bit safer + /// @param _spender The address of the account able to transfer the tokens + /// @param _amount The amount of tokens to be approved for transfer + /// @return True if the approval was successful + function approve(address _spender, uint256 _amount) returns (bool success) { + if (!transfersEnabled) throw; + + // To change the approve amount you first have to reduce the addresses` + // allowance to zero by calling `approve(_spender,0)` if it is not + // already 0 to mitigate the race condition described here: + // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + if ((_amount!=0) && (allowed[msg.sender][_spender] !=0)) throw; + + // Alerts the token controller of the approve function call + if (isContract(controller)) { + if (!TokenController(controller).onApprove(msg.sender, _spender, _amount)) + throw; + } + + allowed[msg.sender][_spender] = _amount; + Approval(msg.sender, _spender, _amount); + return true; + } + + /// @dev This function makes it easy to read the `allowed[]` map + /// @param _owner The address of the account that owns the token + /// @param _spender The address of the account able to transfer the tokens + /// @return Amount of remaining tokens of _owner that _spender is allowed + /// to spend + function allowance(address _owner, address _spender + ) constant returns (uint256 remaining) { + return allowed[_owner][_spender]; + } + + /// @notice `msg.sender` approves `_spender` to send `_amount` tokens on + /// its behalf, and then a function is triggered in the contract that is + /// being approved, `_spender`. This allows users to use their tokens to + /// interact with contracts in one function call instead of two + /// @param _spender The address of the contract able to transfer the tokens + /// @param _amount The amount of tokens to be approved for transfer + /// @return True if the function call was successful + function approveAndCall(address _spender, uint256 _amount, bytes _extraData + ) returns (bool success) { + if (!approve(_spender, _amount)) throw; + + ApproveAndCallFallBack(_spender).receiveApproval( + msg.sender, + _amount, + this, + _extraData + ); + + return true; + } + + /// @dev This function makes it easy to get the total number of tokens + /// @return The total number of tokens + function totalSupply() constant returns (uint) { + return totalSupplyAt(getBlockNumber()); + } + + +//////////////// +// Query balance and totalSupply in History +//////////////// + + /// @dev Queries the balance of `_owner` at a specific `_blockNumber` + /// @param _owner The address from which the balance will be retrieved + /// @param _blockNumber The block number when the balance is queried + /// @return The balance at `_blockNumber` + function balanceOfAt(address _owner, uint _blockNumber) constant + returns (uint) { + + // These next few lines are used when the balance of the token is + // requested before a check point was ever created for this token, it + // requires that the `parentToken.balanceOfAt` be queried at the + // genesis block for that token as this contains initial balance of + // this token + if ((balances[_owner].length == 0) + || (balances[_owner][0].fromBlock > _blockNumber)) { + if (address(parentToken) != 0) { + return parentToken.balanceOfAt(_owner, min(_blockNumber, parentSnapShotBlock)); + } else { + // Has no parent + return 0; + } + + // This will return the expected balance during normal situations + } else { + return getValueAt(balances[_owner], _blockNumber); + } + } + + /// @notice Total amount of tokens at a specific `_blockNumber`. + /// @param _blockNumber The block number when the totalSupply is queried + /// @return The total amount of tokens at `_blockNumber` + function totalSupplyAt(uint _blockNumber) constant returns(uint) { + + // These next few lines are used when the totalSupply of the token is + // requested before a check point was ever created for this token, it + // requires that the `parentToken.totalSupplyAt` be queried at the + // genesis block for this token as that contains totalSupply of this + // token at this block number. + if ((totalSupplyHistory.length == 0) + || (totalSupplyHistory[0].fromBlock > _blockNumber)) { + if (address(parentToken) != 0) { + return parentToken.totalSupplyAt(min(_blockNumber, parentSnapShotBlock)); + } else { + return 0; + } + + // This will return the expected totalSupply during normal situations + } else { + return getValueAt(totalSupplyHistory, _blockNumber); + } + } + +//////////////// +// Clone Token Method +//////////////// + + /// @notice Creates a new clone token with the initial distribution being + /// this token at `_snapshotBlock` + /// @param _cloneTokenName Name of the clone token + /// @param _cloneDecimalUnits Number of decimals of the smallest unit + /// @param _cloneTokenSymbol Symbol of the clone token + /// @param _snapshotBlock Block when the distribution of the parent token is + /// copied to set the initial distribution of the new clone token; + /// if the block is zero than the actual block, the current block is used + /// @param _transfersEnabled True if transfers are allowed in the clone + /// @return The address of the new MiniMeToken Contract + function createCloneToken( + string _cloneTokenName, + uint8 _cloneDecimalUnits, + string _cloneTokenSymbol, + uint _snapshotBlock, + bool _transfersEnabled + ) returns(address) { + if (_snapshotBlock == 0) _snapshotBlock = getBlockNumber(); + MiniMeToken cloneToken = tokenFactory.createCloneToken( + this, + _snapshotBlock, + _cloneTokenName, + _cloneDecimalUnits, + _cloneTokenSymbol, + _transfersEnabled + ); + + cloneToken.changeController(msg.sender); + + // An event to make the token easy to find on the blockchain + NewCloneToken(address(cloneToken), _snapshotBlock); + return address(cloneToken); + } + +//////////////// +// Generate and destroy tokens +//////////////// + + /// @notice Generates `_amount` tokens that are assigned to `_owner` + /// @param _owner The address that will be assigned the new tokens + /// @param _amount The quantity of tokens generated + /// @return True if the tokens are generated correctly + function generateTokens(address _owner, uint _amount + ) onlyController returns (bool) { + uint curTotalSupply = getValueAt(totalSupplyHistory, getBlockNumber()); + if (curTotalSupply + _amount < curTotalSupply) throw; // Check for overflow + updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount); + var previousBalanceTo = balanceOf(_owner); + if (previousBalanceTo + _amount < previousBalanceTo) throw; // Check for overflow + updateValueAtNow(balances[_owner], previousBalanceTo + _amount); + Transfer(0, _owner, _amount); + return true; + } + + + /// @notice Burns `_amount` tokens from `_owner` + /// @param _owner The address that will lose the tokens + /// @param _amount The quantity of tokens to burn + /// @return True if the tokens are burned correctly + function destroyTokens(address _owner, uint _amount + ) onlyController returns (bool) { + uint curTotalSupply = getValueAt(totalSupplyHistory, getBlockNumber()); + if (curTotalSupply < _amount) throw; + updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount); + var previousBalanceFrom = balanceOf(_owner); + if (previousBalanceFrom < _amount) throw; + updateValueAtNow(balances[_owner], previousBalanceFrom - _amount); + Transfer(_owner, 0, _amount); + return true; + } + +//////////////// +// Enable tokens transfers +//////////////// + + + /// @notice Enables token holders to transfer their tokens freely if true + /// @param _transfersEnabled True if transfers are allowed in the clone + function enableTransfers(bool _transfersEnabled) onlyController { + transfersEnabled = _transfersEnabled; + } + +//////////////// +// Internal helper functions to query and set a value in a snapshot array +//////////////// + + /// @dev `getValueAt` retrieves the number of tokens at a given block number + /// @param checkpoints The history of values being queried + /// @param _block The block number to retrieve the value at + /// @return The number of tokens being queried + function getValueAt(Checkpoint[] storage checkpoints, uint _block + ) constant internal returns (uint) { + if (checkpoints.length == 0) return 0; + + // Shortcut for the actual value + if (_block >= checkpoints[checkpoints.length-1].fromBlock) + return checkpoints[checkpoints.length-1].value; + if (_block < checkpoints[0].fromBlock) return 0; + + // Binary search of the value in the array + uint min = 0; + uint max = checkpoints.length-1; + while (max > min) { + uint mid = (max + min + 1)/ 2; + if (checkpoints[mid].fromBlock<=_block) { + min = mid; + } else { + max = mid-1; + } + } + return checkpoints[min].value; + } + + /// @dev `updateValueAtNow` used to update the `balances` map and the + /// `totalSupplyHistory` + /// @param checkpoints The history of data being updated + /// @param _value The new number of tokens + function updateValueAtNow(Checkpoint[] storage checkpoints, uint _value + ) internal { + if ((checkpoints.length == 0) + || (checkpoints[checkpoints.length -1].fromBlock < getBlockNumber())) { + Checkpoint newCheckPoint = checkpoints[ checkpoints.length++ ]; + newCheckPoint.fromBlock = uint128(getBlockNumber()); + newCheckPoint.value = uint128(_value); + } else { + Checkpoint oldCheckPoint = checkpoints[checkpoints.length-1]; + oldCheckPoint.value = uint128(_value); + } + } + + /// @dev Internal function to determine if an address is a contract + /// @param _addr The address being queried + /// @return True if `_addr` is a contract + function isContract(address _addr) constant internal returns(bool) { + uint size; + if (_addr == 0) return false; + assembly { + size := extcodesize(_addr) + } + return size>0; + } + + /// @dev Helper function to return a min betwen the two uints + function min(uint a, uint b) internal returns (uint) { + return a < b ? a : b; + } + + /// @notice The fallback function: If the contract's controller has not been + /// set to 0, then the `proxyPayment` method is called which relays the + /// ether and creates tokens as described in the token controller contract + function () payable { + if (isContract(controller)) { + if (! TokenController(controller).proxyPayment.value(msg.value)(msg.sender)) + throw; + } else { + throw; + } + } + + +////////// +// Testing specific methods +////////// + + /// @notice This function is overridden by the test Mocks. + function getBlockNumber() internal constant returns (uint256) { + return block.number; + } + +////////// +// Safety Methods +////////// + + /// @notice This method can be used by the controller to extract mistakenly + /// sent tokens to this contract. + /// @param _token The address of the token contract that you want to recover + /// set to 0 in case you want to extract ether. + function claimTokens(address _token) onlyController { + if (_token == 0x0) { + controller.transfer(this.balance); + return; + } + + ERC20Token token = ERC20Token(_token); + uint balance = token.balanceOf(this); + token.transfer(controller, balance); + ClaimedTokens(_token, controller, balance); + } + +//////////////// +// Events +//////////////// + + event ClaimedTokens(address indexed _token, address indexed _controller, uint _amount); + event Transfer(address indexed _from, address indexed _to, uint256 _amount); + event NewCloneToken(address indexed _cloneToken, uint _snapshotBlock); + event Approval( + address indexed _owner, + address indexed _spender, + uint256 _amount + ); + +} \ No newline at end of file diff --git a/contracts/token/MiniMeTokenFactory.sol b/contracts/token/MiniMeTokenFactory.sol new file mode 100644 index 0000000..4e79ead --- /dev/null +++ b/contracts/token/MiniMeTokenFactory.sol @@ -0,0 +1,45 @@ +pragma solidity ^0.4.11; + +import "./MiniMeToken.sol"; + +//////////////// +// MiniMeTokenFactory +//////////////// + +/// @dev This contract is used to generate clone contracts from a contract. +/// In solidity this is the way to create a contract from a contract of the +/// same class +contract MiniMeTokenFactory { + + /// @notice Update the DApp by creating a new token with new functionalities + /// the msg.sender becomes the controller of this clone token + /// @param _parentToken Address of the token being cloned + /// @param _snapshotBlock Block of the parent token that will + /// determine the initial distribution of the clone token + /// @param _tokenName Name of the new token + /// @param _decimalUnits Number of decimals of the new token + /// @param _tokenSymbol Token Symbol for the new token + /// @param _transfersEnabled If true, tokens will be able to be transferred + /// @return The address of the new token contract + function createCloneToken( + address _parentToken, + uint _snapshotBlock, + string _tokenName, + uint8 _decimalUnits, + string _tokenSymbol, + bool _transfersEnabled + ) returns (MiniMeToken) { + MiniMeToken newToken = new MiniMeToken( + this, + _parentToken, + _snapshotBlock, + _tokenName, + _decimalUnits, + _tokenSymbol, + _transfersEnabled + ); + + newToken.changeController(msg.sender); + return newToken; + } +} \ No newline at end of file diff --git a/contracts/token/TokenController.sol b/contracts/token/TokenController.sol new file mode 100644 index 0000000..70fad1b --- /dev/null +++ b/contracts/token/TokenController.sol @@ -0,0 +1,26 @@ +pragma solidity ^0.4.11; + +/// @dev The token controller contract must implement these functions +contract TokenController { + /// @notice Called when `_owner` sends ether to the MiniMe Token contract + /// @param _owner The address that sent the ether to create tokens + /// @return True if the ether is accepted, false if it throws + function proxyPayment(address _owner) payable returns(bool); + + /// @notice Notifies the controller about a token transfer allowing the + /// controller to react if desired + /// @param _from The origin of the transfer + /// @param _to The destination of the transfer + /// @param _amount The amount of the transfer + /// @return False if the controller does not authorize the transfer + function onTransfer(address _from, address _to, uint _amount) returns(bool); + + /// @notice Notifies the controller about an approval allowing the + /// controller to react if desired + /// @param _owner The address that calls `approve()` + /// @param _spender The spender in the `approve()` call + /// @param _amount The amount in the `approve()` call + /// @return False if the controller does not authorize the approval + function onApprove(address _owner, address _spender, uint _amount) + returns(bool); +} From 8bae559ec9d9d91760ae0caf409b6a943b6acaad Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Wed, 24 Jan 2018 02:25:40 +0000 Subject: [PATCH 5/8] update to new solc + lint --- contracts/token/MiniMeToken.sol | 217 +++++++++++++++++++------------- 1 file changed, 132 insertions(+), 85 deletions(-) diff --git a/contracts/token/MiniMeToken.sol b/contracts/token/MiniMeToken.sol index 3855bf5..9d586ff 100644 --- a/contracts/token/MiniMeToken.sol +++ b/contracts/token/MiniMeToken.sol @@ -1,6 +1,7 @@ -pragma solidity ^0.4.11; +pragma solidity ^0.4.17; import "../common/Controlled.sol"; +import "./MiniMeTokenFactory.sol"; import "./TokenController.sol"; import "./ApproveAndCallFallBack.sol"; @@ -36,13 +37,13 @@ contract MiniMeToken is Controlled { string public name; //The Token's name: e.g. DigixDAO Tokens uint8 public decimals; //Number of decimals of the smallest unit string public symbol; //An identifier: e.g. REP - string public version = 'MMT_0.1'; //An arbitrary versioning scheme + string public version = "MMT_0.1"; //An arbitrary versioning scheme /// @dev `Checkpoint` is the structure that attaches a block number to a /// given value, the block number attached is the one that last changed the /// value - struct Checkpoint { + struct Checkpoint { // `fromBlock` is the block number that the value was generated from uint128 fromBlock; @@ -104,7 +105,9 @@ contract MiniMeToken is Controlled { uint8 _decimalUnits, string _tokenSymbol, bool _transfersEnabled - ) { + ) + public + { tokenFactory = MiniMeTokenFactory(_tokenFactory); name = _tokenName; // Set the name decimals = _decimalUnits; // Set the decimals @@ -112,7 +115,7 @@ contract MiniMeToken is Controlled { parentToken = MiniMeToken(_parentToken); parentSnapShotBlock = _parentSnapShotBlock; transfersEnabled = _transfersEnabled; - creationBlock = getBlockNumber(); + creationBlock = block.number; } @@ -124,8 +127,8 @@ contract MiniMeToken is Controlled { /// @param _to The address of the recipient /// @param _amount The amount of tokens to be transferred /// @return Whether the transfer was successful or not - function transfer(address _to, uint256 _amount) returns (bool success) { - if (!transfersEnabled) throw; + function transfer(address _to, uint256 _amount) public returns (bool success) { + require(transfersEnabled); return doTransfer(msg.sender, _to, _amount); } @@ -135,18 +138,26 @@ contract MiniMeToken is Controlled { /// @param _to The address of the recipient /// @param _amount The amount of tokens to be transferred /// @return True if the transfer was successful - function transferFrom(address _from, address _to, uint256 _amount - ) returns (bool success) { + function transferFrom( + address _from, + address _to, + uint256 _amount + ) + public + returns (bool success) + { // The controller of this contract can move tokens around at will, // this is important to recognize! Confirm that you trust the // controller of this contract, which in most situations should be // another open source smart contract or 0x0 if (msg.sender != controller) { - if (!transfersEnabled) throw; + require(transfersEnabled); // The standard ERC 20 transferFrom functionality - if (allowed[_from][msg.sender] < _amount) return false; + if (allowed[_from][msg.sender] < _amount) { + return false; + } allowed[_from][msg.sender] -= _amount; } return doTransfer(_from, _to, _amount); @@ -158,29 +169,34 @@ contract MiniMeToken is Controlled { /// @param _to The address of the recipient /// @param _amount The amount of tokens to be transferred /// @return True if the transfer was successful - function doTransfer(address _from, address _to, uint _amount - ) internal returns(bool) { + function doTransfer( + address _from, + address _to, + uint _amount + ) + internal + returns(bool) + { if (_amount == 0) { return true; } - if (parentSnapShotBlock >= getBlockNumber()) throw; + require(parentSnapShotBlock < block.number); // Do not allow transfer to 0x0 or the token contract itself - if ((_to == 0) || (_to == address(this))) throw; + require((_to != 0) && (_to != address(this))); // If the amount being transfered is more than the balance of the // account the transfer returns false - var previousBalanceFrom = balanceOfAt(_from, getBlockNumber()); + var previousBalanceFrom = balanceOfAt(_from, block.number); if (previousBalanceFrom < _amount) { return false; } // Alerts the token controller of the transfer if (isContract(controller)) { - if (!TokenController(controller).onTransfer(_from, _to, _amount)) - throw; + require(TokenController(controller).onTransfer(_from, _to, _amount)); } // First update the balance array with the new value for the address @@ -189,8 +205,8 @@ contract MiniMeToken is Controlled { // Then update the balance array with the new value for the address // receiving the tokens - var previousBalanceTo = balanceOfAt(_to, getBlockNumber()); - if (previousBalanceTo + _amount < previousBalanceTo) throw; // Check for overflow + var previousBalanceTo = balanceOfAt(_to, block.number); + require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow updateValueAtNow(balances[_to], previousBalanceTo + _amount); // An event to make the transfer easy to find on the blockchain @@ -201,8 +217,8 @@ contract MiniMeToken is Controlled { /// @param _owner The address that's balance is being requested /// @return The balance of `_owner` at the current block - function balanceOf(address _owner) constant returns (uint256 balance) { - return balanceOfAt(_owner, getBlockNumber()); + function balanceOf(address _owner) public constant returns (uint256 balance) { + return balanceOfAt(_owner, block.number); } /// @notice `msg.sender` approves `_spender` to spend `_amount` tokens on @@ -211,19 +227,18 @@ contract MiniMeToken is Controlled { /// @param _spender The address of the account able to transfer the tokens /// @param _amount The amount of tokens to be approved for transfer /// @return True if the approval was successful - function approve(address _spender, uint256 _amount) returns (bool success) { - if (!transfersEnabled) throw; + function approve(address _spender, uint256 _amount) public returns (bool success) { + require(transfersEnabled); // To change the approve amount you first have to reduce the addresses` // allowance to zero by calling `approve(_spender,0)` if it is not // already 0 to mitigate the race condition described here: // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - if ((_amount!=0) && (allowed[msg.sender][_spender] !=0)) throw; + require((_amount == 0) || (allowed[msg.sender][_spender] == 0)); // Alerts the token controller of the approve function call if (isContract(controller)) { - if (!TokenController(controller).onApprove(msg.sender, _spender, _amount)) - throw; + require(TokenController(controller).onApprove(msg.sender, _spender, _amount)); } allowed[msg.sender][_spender] = _amount; @@ -236,8 +251,14 @@ contract MiniMeToken is Controlled { /// @param _spender The address of the account able to transfer the tokens /// @return Amount of remaining tokens of _owner that _spender is allowed /// to spend - function allowance(address _owner, address _spender - ) constant returns (uint256 remaining) { + function allowance( + address _owner, + address _spender + ) + public + constant + returns (uint256 remaining) + { return allowed[_owner][_spender]; } @@ -248,9 +269,15 @@ contract MiniMeToken is Controlled { /// @param _spender The address of the contract able to transfer the tokens /// @param _amount The amount of tokens to be approved for transfer /// @return True if the function call was successful - function approveAndCall(address _spender, uint256 _amount, bytes _extraData - ) returns (bool success) { - if (!approve(_spender, _amount)) throw; + function approveAndCall( + address _spender, + uint256 _amount, + bytes _extraData + ) + public + returns (bool success) + { + require(approve(_spender, _amount)); ApproveAndCallFallBack(_spender).receiveApproval( msg.sender, @@ -264,8 +291,8 @@ contract MiniMeToken is Controlled { /// @dev This function makes it easy to get the total number of tokens /// @return The total number of tokens - function totalSupply() constant returns (uint) { - return totalSupplyAt(getBlockNumber()); + function totalSupply() public constant returns (uint) { + return totalSupplyAt(block.number); } @@ -277,8 +304,14 @@ contract MiniMeToken is Controlled { /// @param _owner The address from which the balance will be retrieved /// @param _blockNumber The block number when the balance is queried /// @return The balance at `_blockNumber` - function balanceOfAt(address _owner, uint _blockNumber) constant - returns (uint) { + function balanceOfAt( + address _owner, + uint _blockNumber + ) + public + constant + returns (uint) + { // These next few lines are used when the balance of the token is // requested before a check point was ever created for this token, it @@ -303,7 +336,7 @@ contract MiniMeToken is Controlled { /// @notice Total amount of tokens at a specific `_blockNumber`. /// @param _blockNumber The block number when the totalSupply is queried /// @return The total amount of tokens at `_blockNumber` - function totalSupplyAt(uint _blockNumber) constant returns(uint) { + function totalSupplyAt(uint _blockNumber) public constant returns(uint) { // These next few lines are used when the totalSupply of the token is // requested before a check point was ever created for this token, it @@ -344,8 +377,13 @@ contract MiniMeToken is Controlled { string _cloneTokenSymbol, uint _snapshotBlock, bool _transfersEnabled - ) returns(address) { - if (_snapshotBlock == 0) _snapshotBlock = getBlockNumber(); + ) + public + returns(address) + { + if (_snapshotBlock == 0) { + _snapshotBlock = block.number; + } MiniMeToken cloneToken = tokenFactory.createCloneToken( this, _snapshotBlock, @@ -370,13 +408,19 @@ contract MiniMeToken is Controlled { /// @param _owner The address that will be assigned the new tokens /// @param _amount The quantity of tokens generated /// @return True if the tokens are generated correctly - function generateTokens(address _owner, uint _amount - ) onlyController returns (bool) { - uint curTotalSupply = getValueAt(totalSupplyHistory, getBlockNumber()); - if (curTotalSupply + _amount < curTotalSupply) throw; // Check for overflow + function generateTokens( + address _owner, + uint _amount + ) + public + onlyController + returns (bool) + { + uint curTotalSupply = totalSupply(); + require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow + uint previousBalanceTo = balanceOf(_owner); + require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount); - var previousBalanceTo = balanceOf(_owner); - if (previousBalanceTo + _amount < previousBalanceTo) throw; // Check for overflow updateValueAtNow(balances[_owner], previousBalanceTo + _amount); Transfer(0, _owner, _amount); return true; @@ -387,13 +431,19 @@ contract MiniMeToken is Controlled { /// @param _owner The address that will lose the tokens /// @param _amount The quantity of tokens to burn /// @return True if the tokens are burned correctly - function destroyTokens(address _owner, uint _amount - ) onlyController returns (bool) { - uint curTotalSupply = getValueAt(totalSupplyHistory, getBlockNumber()); - if (curTotalSupply < _amount) throw; + function destroyTokens( + address _owner, + uint _amount + ) + public + onlyController + returns (bool) + { + uint curTotalSupply = totalSupply(); + require(curTotalSupply >= _amount); + uint previousBalanceFrom = balanceOf(_owner); + require(previousBalanceFrom >= _amount); updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount); - var previousBalanceFrom = balanceOf(_owner); - if (previousBalanceFrom < _amount) throw; updateValueAtNow(balances[_owner], previousBalanceFrom - _amount); Transfer(_owner, 0, _amount); return true; @@ -406,7 +456,7 @@ contract MiniMeToken is Controlled { /// @notice Enables token holders to transfer their tokens freely if true /// @param _transfersEnabled True if transfers are allowed in the clone - function enableTransfers(bool _transfersEnabled) onlyController { + function enableTransfers(bool _transfersEnabled) public onlyController { transfersEnabled = _transfersEnabled; } @@ -418,20 +468,31 @@ contract MiniMeToken is Controlled { /// @param checkpoints The history of values being queried /// @param _block The block number to retrieve the value at /// @return The number of tokens being queried - function getValueAt(Checkpoint[] storage checkpoints, uint _block - ) constant internal returns (uint) { - if (checkpoints.length == 0) return 0; + function getValueAt( + Checkpoint[] storage checkpoints, + uint _block + ) + constant + internal + returns (uint) + { + if (checkpoints.length == 0) { + return 0; + } // Shortcut for the actual value - if (_block >= checkpoints[checkpoints.length-1].fromBlock) + if (_block >= checkpoints[checkpoints.length-1].fromBlock) { return checkpoints[checkpoints.length-1].value; - if (_block < checkpoints[0].fromBlock) return 0; + } + if (_block < checkpoints[0].fromBlock) { + return 0; + } // Binary search of the value in the array uint min = 0; uint max = checkpoints.length-1; while (max > min) { - uint mid = (max + min + 1)/ 2; + uint mid = (max + min + 1) / 2; if (checkpoints[mid].fromBlock<=_block) { min = mid; } else { @@ -445,15 +506,14 @@ contract MiniMeToken is Controlled { /// `totalSupplyHistory` /// @param checkpoints The history of data being updated /// @param _value The new number of tokens - function updateValueAtNow(Checkpoint[] storage checkpoints, uint _value - ) internal { + function updateValueAtNow(Checkpoint[] storage checkpoints, uint _value) internal { if ((checkpoints.length == 0) - || (checkpoints[checkpoints.length -1].fromBlock < getBlockNumber())) { - Checkpoint newCheckPoint = checkpoints[ checkpoints.length++ ]; - newCheckPoint.fromBlock = uint128(getBlockNumber()); + || (checkpoints[checkpoints.length -1].fromBlock < block.number)) { + Checkpoint storage newCheckPoint = checkpoints[ checkpoints.length++ ]; + newCheckPoint.fromBlock = uint128(block.number); newCheckPoint.value = uint128(_value); } else { - Checkpoint oldCheckPoint = checkpoints[checkpoints.length-1]; + Checkpoint storage oldCheckPoint = checkpoints[checkpoints.length-1]; oldCheckPoint.value = uint128(_value); } } @@ -463,7 +523,9 @@ contract MiniMeToken is Controlled { /// @return True if `_addr` is a contract function isContract(address _addr) constant internal returns(bool) { uint size; - if (_addr == 0) return false; + if (_addr == 0){ + return false; + } assembly { size := extcodesize(_addr) } @@ -478,23 +540,9 @@ contract MiniMeToken is Controlled { /// @notice The fallback function: If the contract's controller has not been /// set to 0, then the `proxyPayment` method is called which relays the /// ether and creates tokens as described in the token controller contract - function () payable { - if (isContract(controller)) { - if (! TokenController(controller).proxyPayment.value(msg.value)(msg.sender)) - throw; - } else { - throw; - } - } - - -////////// -// Testing specific methods -////////// - - /// @notice This function is overridden by the test Mocks. - function getBlockNumber() internal constant returns (uint256) { - return block.number; + function () public payable { + require(isContract(controller)); + require(TokenController(controller).proxyPayment.value(msg.value)(msg.sender)); } ////////// @@ -505,13 +553,13 @@ contract MiniMeToken is Controlled { /// sent tokens to this contract. /// @param _token The address of the token contract that you want to recover /// set to 0 in case you want to extract ether. - function claimTokens(address _token) onlyController { + function claimTokens(address _token) public onlyController { if (_token == 0x0) { controller.transfer(this.balance); return; } - ERC20Token token = ERC20Token(_token); + MiniMeToken token = MiniMeToken(_token); uint balance = token.balanceOf(this); token.transfer(controller, balance); ClaimedTokens(_token, controller, balance); @@ -520,7 +568,6 @@ contract MiniMeToken is Controlled { //////////////// // Events //////////////// - event ClaimedTokens(address indexed _token, address indexed _controller, uint _amount); event Transfer(address indexed _from, address indexed _to, uint256 _amount); event NewCloneToken(address indexed _cloneToken, uint _snapshotBlock); From c2d3928ebec9366053643f0a6d9c8006d8b5d078 Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Sat, 17 Mar 2018 11:41:08 -0300 Subject: [PATCH 6/8] reduce warnings --- contracts/token/MiniMeToken.sol | 45 +------------------------- contracts/token/MiniMeTokenFactory.sol | 2 +- 2 files changed, 2 insertions(+), 45 deletions(-) diff --git a/contracts/token/MiniMeToken.sol b/contracts/token/MiniMeToken.sol index 2ade3f4..da9c815 100644 --- a/contracts/token/MiniMeToken.sol +++ b/contracts/token/MiniMeToken.sol @@ -577,47 +577,4 @@ contract MiniMeToken is Controlled { uint256 _amount ); -} - - -//////////////// -// MiniMeTokenFactory -//////////////// - -/// @dev This contract is used to generate clone contracts from a contract. -/// In solidity this is the way to create a contract from a contract of the -/// same class -contract MiniMeTokenFactory { - - /// @notice Update the DApp by creating a new token with new functionalities - /// the msg.sender becomes the controller of this clone token - /// @param _parentToken Address of the token being cloned - /// @param _snapshotBlock Block of the parent token that will - /// determine the initial distribution of the clone token - /// @param _tokenName Name of the new token - /// @param _decimalUnits Number of decimals of the new token - /// @param _tokenSymbol Token Symbol for the new token - /// @param _transfersEnabled If true, tokens will be able to be transferred - /// @return The address of the new token contract - function createCloneToken( - address _parentToken, - uint _snapshotBlock, - string _tokenName, - uint8 _decimalUnits, - string _tokenSymbol, - bool _transfersEnabled - ) returns (MiniMeToken) { - MiniMeToken newToken = new MiniMeToken( - this, - _parentToken, - _snapshotBlock, - _tokenName, - _decimalUnits, - _tokenSymbol, - _transfersEnabled - ); - - newToken.changeController(msg.sender); - return newToken; - } -} +} \ No newline at end of file diff --git a/contracts/token/MiniMeTokenFactory.sol b/contracts/token/MiniMeTokenFactory.sol index 4e79ead..a57eea4 100644 --- a/contracts/token/MiniMeTokenFactory.sol +++ b/contracts/token/MiniMeTokenFactory.sol @@ -28,7 +28,7 @@ contract MiniMeTokenFactory { uint8 _decimalUnits, string _tokenSymbol, bool _transfersEnabled - ) returns (MiniMeToken) { + ) public returns (MiniMeToken) { MiniMeToken newToken = new MiniMeToken( this, _parentToken, From 1386334b57dc149b15682ceb413b5258a0f84ed2 Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Sat, 17 Mar 2018 12:55:25 -0300 Subject: [PATCH 7/8] lint and interface abstraction --- contracts/token/MiniMeToken.sol | 6 +- contracts/token/MiniMeTokenInterface.sol | 97 ++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 contracts/token/MiniMeTokenInterface.sol diff --git a/contracts/token/MiniMeToken.sol b/contracts/token/MiniMeToken.sol index da9c815..bf4378b 100644 --- a/contracts/token/MiniMeToken.sol +++ b/contracts/token/MiniMeToken.sol @@ -28,11 +28,13 @@ pragma solidity ^0.4.6; import "../common/Controlled.sol"; import "./TokenController.sol"; import "./ApproveAndCallFallBack.sol"; +import "./MiniMeTokenInterface.sol"; +import "./MiniMeTokenFactory.sol"; /// @dev The actual token contract, the default controller is the msg.sender /// that deploys the contract, so usually this token will be deployed by a /// token controller contract, which Giveth will call a "Campaign" -contract MiniMeToken is Controlled { +contract MiniMeToken is MiniMeTokenInterface, Controlled { string public name; //The Token's name: e.g. DigixDAO Tokens uint8 public decimals; //Number of decimals of the smallest unit @@ -43,7 +45,7 @@ contract MiniMeToken is Controlled { /// @dev `Checkpoint` is the structure that attaches a block number to a /// given value, the block number attached is the one that last changed the /// value - struct Checkpoint { + struct Checkpoint { // `fromBlock` is the block number that the value was generated from uint128 fromBlock; diff --git a/contracts/token/MiniMeTokenInterface.sol b/contracts/token/MiniMeTokenInterface.sol new file mode 100644 index 0000000..4e143e0 --- /dev/null +++ b/contracts/token/MiniMeTokenInterface.sol @@ -0,0 +1,97 @@ +pragma solidity ^0.4.17; + +import "./ERC20Token.sol"; + +contract MiniMeTokenInterface is ERC20Token { + + + /// @notice `msg.sender` approves `_spender` to send `_amount` tokens on + /// its behalf, and then a function is triggered in the contract that is + /// being approved, `_spender`. This allows users to use their tokens to + /// interact with contracts in one function call instead of two + /// @param _spender The address of the contract able to transfer the tokens + /// @param _amount The amount of tokens to be approved for transfer + /// @return True if the function call was successful + function approveAndCall( + address _spender, + uint256 _amount, + bytes _extraData + ) + public + returns (bool success); + + + /// @notice Creates a new clone token with the initial distribution being + /// this token at `_snapshotBlock` + /// @param _cloneTokenName Name of the clone token + /// @param _cloneDecimalUnits Number of decimals of the smallest unit + /// @param _cloneTokenSymbol Symbol of the clone token + /// @param _snapshotBlock Block when the distribution of the parent token is + /// copied to set the initial distribution of the new clone token; + /// if the block is zero than the actual block, the current block is used + /// @param _transfersEnabled True if transfers are allowed in the clone + /// @return The address of the new MiniMeToken Contract + function createCloneToken( + string _cloneTokenName, + uint8 _cloneDecimalUnits, + string _cloneTokenSymbol, + uint _snapshotBlock, + bool _transfersEnabled + ) + public + returns(address); + + /// @notice Generates `_amount` tokens that are assigned to `_owner` + /// @param _owner The address that will be assigned the new tokens + /// @param _amount The quantity of tokens generated + /// @return True if the tokens are generated correctly + function generateTokens( + address _owner, + uint _amount + ) + public + returns (bool); + + /// @notice Burns `_amount` tokens from `_owner` + /// @param _owner The address that will lose the tokens + /// @param _amount The quantity of tokens to burn + /// @return True if the tokens are burned correctly + function destroyTokens( + address _owner, + uint _amount + ) + public + returns (bool); + + + /// @notice Enables token holders to transfer their tokens freely if true + /// @param _transfersEnabled True if transfers are allowed in the clone + function enableTransfers(bool _transfersEnabled) public; + + + /// @notice This method can be used by the controller to extract mistakenly + /// sent tokens to this contract. + /// @param _token The address of the token contract that you want to recover + /// set to 0 in case you want to extract ether. + function claimTokens(address _token) public; + + /// @dev Queries the balance of `_owner` at a specific `_blockNumber` + /// @param _owner The address from which the balance will be retrieved + /// @param _blockNumber The block number when the balance is queried + /// @return The balance at `_blockNumber` + function balanceOfAt( + address _owner, + uint _blockNumber + ) + public + constant + returns (uint); + + /// @notice Total amount of tokens at a specific `_blockNumber`. + /// @param _blockNumber The block number when the totalSupply is queried + /// @return The total amount of tokens at `_blockNumber` + function totalSupplyAt(uint _blockNumber) public constant returns(uint); + + + +} \ No newline at end of file From e8edd3d53c2f322d30eeab211f681ebdbb03b89b Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Sat, 17 Mar 2018 13:50:33 -0300 Subject: [PATCH 8/8] fix compile errors --- contracts/token/MiniMeTokenInterface.sol | 36 ++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/contracts/token/MiniMeTokenInterface.sol b/contracts/token/MiniMeTokenInterface.sol index 4e143e0..e315956 100644 --- a/contracts/token/MiniMeTokenInterface.sol +++ b/contracts/token/MiniMeTokenInterface.sol @@ -1,9 +1,41 @@ pragma solidity ^0.4.17; -import "./ERC20Token.sol"; +contract MiniMeTokenInterface { -contract MiniMeTokenInterface is ERC20Token { + event Transfer(address indexed _from, address indexed _to, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _value); + + /// @dev This function makes it easy to get the total number of tokens + /// @return The total number of tokens + function totalSupply() public constant returns (uint); + /// @param _owner The address from which the balance will be retrieved + /// @return The balance + function balanceOf(address _owner) public constant returns (uint256 balance); + + /// @notice send `_value` token to `_to` from `msg.sender` + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + /// @return Whether the transfer was successful or not + function transfer(address _to, uint256 _value) public returns (bool success); + + /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from` + /// @param _from The address of the sender + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + /// @return Whether the transfer was successful or not + function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); + + /// @notice `msg.sender` approves `_spender` to spend `_value` tokens + /// @param _spender The address of the account able to transfer the tokens + /// @param _value The amount of tokens to be approved for transfer + /// @return Whether the approval was successful or not + function approve(address _spender, uint256 _value) public returns (bool success); + + /// @param _owner The address of the account owning tokens + /// @param _spender The address of the account able to transfer the tokens + /// @return Amount of remaining tokens allowed to spent + function allowance(address _owner, address _spender) public constant returns (uint256 remaining); /// @notice `msg.sender` approves `_spender` to send `_amount` tokens on /// its behalf, and then a function is triggered in the contract that is