Full refactor of the tests to use await method

This commit is contained in:
Jordi Baylina 2017-10-21 09:10:44 +02:00
parent b828579608
commit 5665bd4e9a
No known key found for this signature in database
GPG Key ID: 7480C80C1BE43112
23 changed files with 5321 additions and 3231 deletions

View File

@ -1,46 +1,7 @@
{
"env": {
"browser": true,
"es6": true,
"node": true,
"mocha": true
},
"extends": "airbnb",
"parser": "babel-eslint",
"rules": {
// indentation
"indent": [ 2, 4 ],
// spacing
"template-curly-spacing": [ 2, "always" ],
"array-bracket-spacing": [ 2, "always" ],
"object-curly-spacing": [ 2, "always" ],
"computed-property-spacing": [ 2, "always" ],
"no-multiple-empty-lines": [ 2, { "max": 1, "maxEOF": 0, "maxBOF": 0 } ],
// strings
"quotes": [ 2, "double", "avoid-escape" ],
// code arrangement matter
"no-use-before-define": [ 2, { "functions": false } ],
// make it meaningful
"prefer-const": 1,
// keep it simple
"complexity": [ 1, 5 ],
// Consisten return
"consistent-return": 0,
// Allow concatenations
"prefer-template": 0,
"import/no-extraneous-dependencies": ["error", {"devDependencies": ["**/*.test.js", "**/*.spec.js", "**/compile.js", "**/test/*.js"]}],
// react
"react/prefer-es6-class": 0,
"react/jsx-filename-extension": 0,
"react/jsx-indent": [ 2, 4 ]
"jsx-a11y/href-no-hash": "off",
"jsx-a11y/anchor-is-valid": ["warn", { "aspects": ["invalidHref"] }]
}
}

3
.travis.yml Normal file
View File

@ -0,0 +1,3 @@
language: 'node_js'
node_js:
- '8'

View File

@ -1,5 +1,7 @@
![MiniMe Token](readme-header.png)
[![Build Status](https://travis-ci.org/Giveth/minime.svg?branch=master)](https://travis-ci.org/Giveth/minime)
The MiniMeToken contract is a standard ERC20 token with extra functionality:
### The token is easy to clone!

7
build/Controlled.sol.js Normal file
View File

@ -0,0 +1,7 @@
/* This is an autogenerated file. DO NOT EDIT MANUALLY */
exports.ControlledAbi = [{"constant":false,"inputs":[{"name":"_newController","type":"address"}],"name":"changeController","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"controller","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
exports.ControlledByteCode = "0x6060604052341561000f57600080fd5b60008054600160a060020a033316600160a060020a03199091161790556101668061003b6000396000f30060606040526004361061004b5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416633cebb8238114610050578063f77c47911461007e575b600080fd5b341561005b57600080fd5b61007c73ffffffffffffffffffffffffffffffffffffffff600435166100ba565b005b341561008957600080fd5b61009161011e565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b6000543373ffffffffffffffffffffffffffffffffffffffff9081169116146100e257600080fd5b6000805473ffffffffffffffffffffffffffffffffffffffff191673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60005473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a72305820348e33213b8644e828ed3b4249fa8ae33458cd699d8de08bf42e589b2b9fe1eb0029"
exports.ControlledRuntimeByteCode = "0x60606040526004361061004b5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416633cebb8238114610050578063f77c47911461007e575b600080fd5b341561005b57600080fd5b61007c73ffffffffffffffffffffffffffffffffffffffff600435166100ba565b005b341561008957600080fd5b61009161011e565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b6000543373ffffffffffffffffffffffffffffffffffffffff9081169116146100e257600080fd5b6000805473ffffffffffffffffffffffffffffffffffffffff191673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60005473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a72305820348e33213b8644e828ed3b4249fa8ae33458cd699d8de08bf42e589b2b9fe1eb0029"
exports._solcVersion = "0.4.18+commit.9cf6e910.Emscripten.clang"
exports._sha256 = "0xcd25f1d20c06711ec5147fce5dc8518b5374f61f2313d4035c3650bd229e9de0"

19
build/Controlled_all.sol Normal file
View File

@ -0,0 +1,19 @@
//File: ./contracts/Controlled.sol
pragma solidity ^0.4.18;
contract Controlled {
/// @notice The address of the controller is the only address that can call
/// a function with this modifier
modifier onlyController { require(msg.sender == controller); _; }
address public controller;
function Controlled() public { controller = msg.sender;}
/// @notice Changes the controller of the contract
/// @param _newController The new controller of the contract
function changeController(address _newController) public onlyController {
controller = _newController;
}
}

19
build/MiniMeToken.sol.js Normal file

File diff suppressed because one or more lines are too long

612
build/MiniMeToken_all.sol Normal file
View File

@ -0,0 +1,612 @@
//File: contracts/Controlled.sol
pragma solidity ^0.4.18;
contract Controlled {
/// @notice The address of the controller is the only address that can call
/// a function with this modifier
modifier onlyController { require(msg.sender == controller); _; }
address public controller;
function Controlled() public { controller = msg.sender;}
/// @notice Changes the controller of the contract
/// @param _newController The new controller of the contract
function changeController(address _newController) public onlyController {
controller = _newController;
}
}
//File: contracts/TokenController.sol
pragma solidity ^0.4.18;
/// @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) public 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) public 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) public
returns(bool);
}
//File: ./contracts/MiniMeToken.sol
pragma solidity ^0.4.18;
/*
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 <http://www.gnu.org/licenses/>.
*/
/// @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.
contract ApproveAndCallFallBack {
function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public;
}
/// @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.2'; //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
) public {
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) public 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
) 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) {
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) public 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) 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
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
) public 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
) public 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() public 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) 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
// 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) 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
// 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
) public 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
) 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);
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 public 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) public 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) pure 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 () public 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) public 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
) public returns (MiniMeToken) {
MiniMeToken newToken = new MiniMeToken(
this,
_parentToken,
_snapshotBlock,
_tokenName,
_decimalUnits,
_tokenSymbol,
_transfersEnabled
);
newToken.changeController(msg.sender);
return newToken;
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,799 @@
//File: contracts/Controlled.sol
pragma solidity ^0.4.18;
contract Controlled {
/// @notice The address of the controller is the only address that can call
/// a function with this modifier
modifier onlyController { require(msg.sender == controller); _; }
address public controller;
function Controlled() public { controller = msg.sender;}
/// @notice Changes the controller of the contract
/// @param _newController The new controller of the contract
function changeController(address _newController) public onlyController {
controller = _newController;
}
}
//File: contracts/TokenController.sol
pragma solidity ^0.4.18;
/// @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) public 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) public 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) public
returns(bool);
}
//File: contracts/MiniMeToken.sol
pragma solidity ^0.4.18;
/*
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 <http://www.gnu.org/licenses/>.
*/
/// @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.
contract ApproveAndCallFallBack {
function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public;
}
/// @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.2'; //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
) public {
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) public 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
) 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) {
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) public 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) 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
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
) public 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
) public 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() public 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) 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
// 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) 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
// 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
) public 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
) 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);
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 public 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) public 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) pure 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 () public 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) public 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
) public returns (MiniMeToken) {
MiniMeToken newToken = new MiniMeToken(
this,
_parentToken,
_snapshotBlock,
_tokenName,
_decimalUnits,
_tokenSymbol,
_transfersEnabled
);
newToken.changeController(msg.sender);
return newToken;
}
}
//File: ./contracts/SampleCampaign-TokenController.sol
pragma solidity ^0.4.6;
/*
Copyright 2017, 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 <http://www.gnu.org/licenses/>.
*/
/// @title MilestoneTracker Contract
/// @author Jordi Baylina
/// @dev This contract controls the issuance of tokens for the MiniMe Token
/// Contract. This version specifically acts as a Campaign manager for raising
/// funds for non-profit causes, but it can be customized for any variety of
/// purposes.
/// @dev `Owned` is a base level contract that assigns an `owner` that can be
/// later changed
contract Owned {
/// @dev `owner` is the only address that can call a function with this
/// modifier
modifier onlyOwner { require (msg.sender == owner); _; }
address public owner;
/// @notice The Constructor assigns the message sender to be `owner`
function Owned() { owner = msg.sender;}
/// @notice `owner` can step down and assign some other address to this role
/// @param _newOwner The address of the new owner. 0x0 can be used to create
/// an unowned neutral vault, however that cannot be undone
function changeOwner(address _newOwner) onlyOwner {
owner = _newOwner;
}
}
/// @dev This is designed to control the issuance of a MiniMe Token for a
/// non-profit Campaign. This contract effectively dictates the terms of the
/// funding round.
contract Campaign is TokenController, Owned {
uint public startFundingTime; // In UNIX Time Format
uint public endFundingTime; // In UNIX Time Format
uint public maximumFunding; // In wei
uint public totalCollected; // In wei
MiniMeToken public tokenContract; // The new token for this Campaign
address public vaultAddress; // The address to hold the funds donated
/// @notice 'Campaign()' initiates the Campaign by setting its funding
/// parameters
/// @dev There are several checks to make sure the parameters are acceptable
/// @param _startFundingTime The UNIX time that the Campaign will be able to
/// start receiving funds
/// @param _endFundingTime The UNIX time that the Campaign will stop being able
/// to receive funds
/// @param _maximumFunding In wei, the Maximum amount that the Campaign can
/// receive (currently the max is set at 10,000 ETH for the beta)
/// @param _vaultAddress The address that will store the donated funds
/// @param _tokenAddress Address of the token contract this contract controls
function Campaign(
uint _startFundingTime,
uint _endFundingTime,
uint _maximumFunding,
address _vaultAddress,
address _tokenAddress
) {
require ((_endFundingTime >= now) && // Cannot end in the past
(_endFundingTime > _startFundingTime) &&
(_maximumFunding <= 10000 ether) && // The Beta is limited
(_vaultAddress != 0)); // To prevent burning ETH
startFundingTime = _startFundingTime;
endFundingTime = _endFundingTime;
maximumFunding = _maximumFunding;
tokenContract = MiniMeToken(_tokenAddress);// The Deployed Token Contract
vaultAddress = _vaultAddress;
}
/// @dev The fallback function is called when ether is sent to the contract, it
/// simply calls `doPayment()` with the address that sent the ether as the
/// `_owner`. Payable is a required solidity modifier for functions to receive
/// ether, without this modifier functions will throw if ether is sent to them
function () payable {
doPayment(msg.sender);
}
/////////////////
// TokenController interface
/////////////////
/// @notice `proxyPayment()` allows the caller to send ether to the Campaign and
/// have the tokens created in an address of their choosing
/// @param _owner The address that will hold the newly created tokens
function proxyPayment(address _owner) payable returns(bool) {
doPayment(_owner);
return true;
}
/// @notice Notifies the controller about a transfer, for this Campaign all
/// transfers are allowed by default and no extra notifications are needed
/// @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) {
return true;
}
/// @notice Notifies the controller about an approval, for this Campaign all
/// approvals are allowed by default and no extra notifications are needed
/// @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)
{
return true;
}
/// @dev `doPayment()` is an internal function that sends the ether that this
/// contract receives to the `vault` and creates tokens in the address of the
/// `_owner` assuming the Campaign is still accepting funds
/// @param _owner The address that will hold the newly created tokens
function doPayment(address _owner) internal {
// First check that the Campaign is allowed to receive this donation
require ((now >= startFundingTime) &&
(now <= endFundingTime) &&
(tokenContract.controller() != 0) && // Extra check
(msg.value != 0) &&
(totalCollected + msg.value <= maximumFunding));
//Track how much the Campaign has collected
totalCollected += msg.value;
//Send the ether to the vault
require (vaultAddress.send(msg.value));
// Creates an equal amount of tokens as ether sent. The new tokens are created
// in the `_owner` address
require (tokenContract.generateTokens(_owner, msg.value));
return;
}
/// @notice `finalizeFunding()` ends the Campaign by calling setting the
/// controller to 0, thereby ending the issuance of new tokens and stopping the
/// Campaign from receiving more ether
/// @dev `finalizeFunding()` can only be called after the end of the funding period.
function finalizeFunding() {
require(now >= endFundingTime);
tokenContract.changeController(0);
}
/// @notice `onlyOwner` changes the location that ether is sent
/// @param _newVaultAddress The address that will receive the ether sent to this
/// Campaign
function setVault(address _newVaultAddress) onlyOwner {
vaultAddress = _newVaultAddress;
}
}

View File

@ -0,0 +1,7 @@
/* This is an autogenerated file. DO NOT EDIT MANUALLY */
exports.TokenControllerAbi = [{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"onTransfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"},{"name":"_amount","type":"uint256"}],"name":"onApprove","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"}],"name":"proxyPayment","outputs":[{"name":"","type":"bool"}],"payable":true,"stateMutability":"payable","type":"function"}]
exports.TokenControllerByteCode = "0x"
exports.TokenControllerRuntimeByteCode = "0x"
exports._solcVersion = "0.4.18+commit.9cf6e910.Emscripten.clang"
exports._sha256 = "0x4ab21dd789d6619432629f4e930c7f6cd05cd149e14a8127f99c7c83f2c4377f"

View File

@ -0,0 +1,28 @@
//File: ./contracts/TokenController.sol
pragma solidity ^0.4.18;
/// @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) public 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) public 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) public
returns(bool);
}

17
contracts/Controlled.sol Normal file
View File

@ -0,0 +1,17 @@
pragma solidity ^0.4.18;
contract Controlled {
/// @notice The address of the controller is the only address that can call
/// a function with this modifier
modifier onlyController { require(msg.sender == controller); _; }
address public controller;
function Controlled() public { controller = msg.sender;}
/// @notice Changes the controller of the contract
/// @param _newController The new controller of the contract
function changeController(address _newController) public onlyController {
controller = _newController;
}
}

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.6;
pragma solidity ^0.4.18;
/*
Copyright 2016, Jordi Baylina
@ -25,50 +25,11 @@ pragma solidity ^0.4.6;
/// affecting the original token
/// @dev It is ERC20 compliant, but still needs to under go further testing.
/// @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);
}
contract Controlled {
/// @notice The address of the controller is the only address that can call
/// a function with this modifier
modifier onlyController { require(msg.sender == controller); _; }
address public controller;
function Controlled() { controller = msg.sender;}
/// @notice Changes the controller of the contract
/// @param _newController The new controller of the contract
function changeController(address _newController) onlyController {
controller = _newController;
}
}
import "./Controlled.sol";
import "./TokenController.sol";
contract ApproveAndCallFallBack {
function receiveApproval(address from, uint256 _amount, address _token, bytes _data);
function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public;
}
/// @dev The actual token contract, the default controller is the msg.sender
@ -147,7 +108,7 @@ contract MiniMeToken is Controlled {
uint8 _decimalUnits,
string _tokenSymbol,
bool _transfersEnabled
) {
) public {
tokenFactory = MiniMeTokenFactory(_tokenFactory);
name = _tokenName; // Set the name
decimals = _decimalUnits; // Set the decimals
@ -167,7 +128,7 @@ 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) {
function transfer(address _to, uint256 _amount) public returns (bool success) {
require(transfersEnabled);
return doTransfer(msg.sender, _to, _amount);
}
@ -179,7 +140,7 @@ contract MiniMeToken is Controlled {
/// @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) {
) 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
@ -243,7 +204,7 @@ 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) {
function balanceOf(address _owner) public constant returns (uint256 balance) {
return balanceOfAt(_owner, block.number);
}
@ -253,7 +214,7 @@ 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) {
function approve(address _spender, uint256 _amount) public returns (bool success) {
require(transfersEnabled);
// To change the approve amount you first have to reduce the addresses`
@ -278,7 +239,7 @@ contract MiniMeToken is Controlled {
/// @return Amount of remaining tokens of _owner that _spender is allowed
/// to spend
function allowance(address _owner, address _spender
) constant returns (uint256 remaining) {
) public constant returns (uint256 remaining) {
return allowed[_owner][_spender];
}
@ -290,7 +251,7 @@ contract MiniMeToken is Controlled {
/// @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) {
) public returns (bool success) {
require(approve(_spender, _amount));
ApproveAndCallFallBack(_spender).receiveApproval(
@ -305,7 +266,7 @@ 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) {
function totalSupply() public constant returns (uint) {
return totalSupplyAt(block.number);
}
@ -318,7 +279,7 @@ 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
function balanceOfAt(address _owner, uint _blockNumber) public constant
returns (uint) {
// These next few lines are used when the balance of the token is
@ -344,7 +305,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
@ -385,7 +346,7 @@ contract MiniMeToken is Controlled {
string _cloneTokenSymbol,
uint _snapshotBlock,
bool _transfersEnabled
) returns(address) {
) public returns(address) {
if (_snapshotBlock == 0) _snapshotBlock = block.number;
MiniMeToken cloneToken = tokenFactory.createCloneToken(
this,
@ -412,7 +373,7 @@ contract MiniMeToken is Controlled {
/// @param _amount The quantity of tokens generated
/// @return True if the tokens are generated correctly
function generateTokens(address _owner, uint _amount
) onlyController returns (bool) {
) public onlyController returns (bool) {
uint curTotalSupply = totalSupply();
require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow
uint previousBalanceTo = balanceOf(_owner);
@ -429,7 +390,7 @@ contract MiniMeToken is Controlled {
/// @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) {
) onlyController public returns (bool) {
uint curTotalSupply = totalSupply();
require(curTotalSupply >= _amount);
uint previousBalanceFrom = balanceOf(_owner);
@ -447,7 +408,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;
}
@ -512,14 +473,14 @@ contract MiniMeToken is Controlled {
}
/// @dev Helper function to return a min betwen the two uints
function min(uint a, uint b) internal returns (uint) {
function min(uint a, uint b) pure 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 {
function () public payable {
require(isContract(controller));
require(TokenController(controller).proxyPayment.value(msg.value)(msg.sender));
}
@ -532,7 +493,7 @@ 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;
@ -585,7 +546,7 @@ contract MiniMeTokenFactory {
uint8 _decimalUnits,
string _tokenSymbol,
bool _transfersEnabled
) returns (MiniMeToken) {
) public returns (MiniMeToken) {
MiniMeToken newToken = new MiniMeToken(
this,
_parentToken,

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,26 @@
pragma solidity ^0.4.18;
/// @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) public 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) public 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) public
returns(bool);
}

265
dist/minimetoken.js vendored
View File

@ -1,265 +0,0 @@
"use strict";
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var async = require("async");
var BigNumber = require("bignumber.js");
var _require = require("runethtx"),
_deploy = _require.deploy,
sendContractTx = _require.sendContractTx,
asyncfunc = _require.asyncfunc;
var _require2 = require("../contracts/MiniMeToken.sol.js"),
MiniMeTokenAbi = _require2.MiniMeTokenAbi,
MiniMeTokenByteCode = _require2.MiniMeTokenByteCode,
MiniMeTokenFactoryAbi = _require2.MiniMeTokenFactoryAbi,
MiniMeTokenFactoryByteCode = _require2.MiniMeTokenFactoryByteCode;
module.exports = function () {
function MiniMeToken(web3, address) {
_classCallCheck(this, MiniMeToken);
this.web3 = web3;
this.contract = this.web3.eth.contract(MiniMeTokenAbi).at(address);
}
_createClass(MiniMeToken, [{
key: "getState",
value: function getState(_cb) {
var _this = this;
return asyncfunc(function (cb) {
var st = {
balances: {}
};
var accounts = void 0;
async.series([function (cb1) {
_this.contract.name(function (err, _name) {
if (err) {
cb1(err);return;
}
st.name = _name;
cb1();
});
}, function (cb1) {
_this.contract.decimals(function (err, _decimals) {
if (err) {
cb1(err);return;
}
st.decimals = _decimals;
cb1();
});
}, function (cb1) {
_this.contract.controller(function (err, _controller) {
if (err) {
cb1(err);return;
}
st.controller = _controller;
cb1();
});
}, function (cb1) {
_this.web3.eth.getAccounts(function (err, _accounts) {
if (err) {
cb1(err);return;
}
accounts = _accounts;
cb1();
});
}, function (cb1) {
_this.contract.totalSupply(function (err, _totalSupply) {
if (err) {
cb1(err);return;
}
st.totalSupply = _totalSupply.div(new BigNumber(10).pow(st.decimals)).toNumber();
cb1();
});
}, function (cb1) {
_this.contract.parentToken(function (err, _parentToken) {
if (err) {
cb1(err);return;
}
st.parentToken = _parentToken;
cb1();
});
}, function (cb1) {
_this.contract.parentSnapShotBlock(function (err, _parentSnapShotBlock) {
if (err) {
cb1(err);return;
}
st.parentSnapShotBlock = _parentSnapShotBlock;
cb1();
});
}, function (cb1) {
async.eachSeries(accounts, function (account, cb2) {
_this.contract.balanceOf(account, function (err, res) {
if (err) {
cb2(err);return;
}
st.balances[account] = res.div(new BigNumber(10).pow(st.decimals)).toNumber();
cb2();
});
}, cb1);
}], function (err2) {
cb(err2, st);
});
}, _cb);
}
}, {
key: "createCloneToken",
value: function createCloneToken(opts, _cb) {
var _this2 = this;
return asyncfunc(function (cb) {
sendContractTx(_this2.web3, _this2.contract, "createCloneToken", opts, function (err2, txHash) {
if (err2) {
cb(err2);
return;
}
var firstSend = new Date().getTime();
var getTransactionReceiptCB = function getTransactionReceiptCB(err3, res) {
if (err3) {
cb(err3);
return;
}
if (!res) {
var now = new Date().getTime();
if (now - firstSend > 900000) {
cb(new Error("Timeout mining transaction"));
return;
}
setTimeout(function () {
_this2.web3.eth.getTransactionReceipt(txHash, getTransactionReceiptCB);
}, 200);
return;
}
var cloneTokenAddr = _this2.web3.toBigNumber(res.logs[0].topics[1]).toString(16);
while (cloneTokenAddr.length < 40) {
cloneTokenAddr = "0" + cloneTokenAddr;
}cloneTokenAddr = "0x" + cloneTokenAddr;
var miniMeTokenClone = new MiniMeToken(_this2.web3, cloneTokenAddr);
cb(null, miniMeTokenClone);
};
_this2.web3.eth.getTransactionReceipt(txHash, getTransactionReceiptCB);
});
}, _cb);
}
}, {
key: "convertAmountAndSend",
value: function convertAmountAndSend(method, opts, _cb) {
var _this3 = this;
return asyncfunc(function (cb) {
_this3.contract.decimals(function (err, _decimals) {
if (err) {
cb(err);
return;
}
var params = Object.assign({}, opts);
params.amount = new BigNumber(10).pow(_decimals).mul(params.amount);
sendContractTx(_this3.web3, _this3.contract, method, params, function (err2, txHash) {
if (err2) {
cb(err2);
return;
}
cb(null, txHash);
});
});
}, _cb);
}
}, {
key: "transfer",
value: function transfer(opts, cb) {
return this.convertAmountAndSend("transfer", opts, cb);
}
}, {
key: "generateTokens",
value: function generateTokens(opts, cb) {
return this.convertAmountAndSend("generateTokens", opts, cb);
}
}, {
key: "destroyTokens",
value: function destroyTokens(opts, cb) {
var params = Object.assign({}, opts);
params.extraGas = 50000;
return this.convertAmountAndSend("destroyTokens", params, cb);
}
}, {
key: "approve",
value: function approve(opts, cb) {
return this.convertAmountAndSend("approve", opts, cb);
}
}, {
key: "allowance",
value: function allowance(opts, _cb) {
var _this4 = this;
return asyncfunc(function (cb) {
var decimals = void 0;
var allowance = void 0;
async.series([function (cb1) {
_this4.contract.decimals(function (err, _decimals) {
if (err) {
cb1(err);
return;
}
decimals = _decimals;
cb1();
});
}, function (cb1) {
_this4.contract.allowance(opts.owner, opts.spender, function (err, res) {
if (err) {
cb1(err);
return;
}
allowance = res.div(new BigNumber(10).pow(decimals)).toNumber();
cb1();
});
}], function (err2) {
if (err2) {
cb(err2);
} else {
cb(null, allowance);
}
});
}, _cb);
}
}], [{
key: "deploy",
value: function deploy(web3, opts, _cb) {
return asyncfunc(function (cb) {
var params = Object.assign({}, opts);
params.parentToken = params.parentToken || 0;
params.parentSnapShotBlock = params.parentSnapShotBlock || 0;
params.transfersEnabled = typeof params.transfersEnabled === "undefined" ? true : params.transfersEnabled;
async.series([function (cb1) {
params.abi = MiniMeTokenFactoryAbi;
params.byteCode = MiniMeTokenFactoryByteCode;
_deploy(web3, params, function (err, _tokenFactory) {
if (err) {
cb1(err);
return;
}
params.tokenFactory = _tokenFactory.address;
cb1();
});
}, function (cb1) {
params.abi = MiniMeTokenAbi;
params.byteCode = MiniMeTokenByteCode;
_deploy(web3, params, cb1);
}], function (err, res) {
if (err) {
cb(err);
return;
}
var minime = new MiniMeToken(web3, res[1].address);
cb(null, minime);
});
}, _cb);
}
}]);
return MiniMeToken;
}();

3
index.js Normal file
View File

@ -0,0 +1,3 @@
exports.MiniMeToken = require('./js/minimetoken');
exports.MiniMeTokenFactory = require('./js/minimetokenfactory');
exports.MiniMeTokenState = require('./js/minimetokenstate');

View File

@ -1,247 +1,5 @@
const async = require("async");
const BigNumber = require("bignumber.js");
const { deploy, sendContractTx, asyncfunc } = require("runethtx");
const {
MiniMeTokenAbi,
MiniMeTokenByteCode,
MiniMeTokenFactoryAbi,
MiniMeTokenFactoryByteCode,
} = require("../contracts/MiniMeToken.sol.js");
const MiniMeTokenAbi = require('../build/MiniMeToken.sol').MiniMeTokenAbi;
const MiniMeTokenByteCode = require('../build/MiniMeToken.sol').MiniMeTokenByteCode;
const generateClass = require('eth-contract-class').default;
module.exports = class MiniMeToken {
constructor(web3, address) {
this.web3 = web3;
this.contract = this.web3.eth.contract(MiniMeTokenAbi).at(address);
}
getState(_cb) {
return asyncfunc((cb) => {
const st = {
balances: {},
};
let accounts;
async.series([
(cb1) => {
this.contract.name((err, _name) => {
if (err) { cb1(err); return; }
st.name = _name;
cb1();
});
},
(cb1) => {
this.contract.decimals((err, _decimals) => {
if (err) { cb1(err); return; }
st.decimals = _decimals;
cb1();
});
},
(cb1) => {
this.contract.controller((err, _controller) => {
if (err) { cb1(err); return; }
st.controller = _controller;
cb1();
});
},
(cb1) => {
this.web3.eth.getAccounts((err, _accounts) => {
if (err) { cb1(err); return; }
accounts = _accounts;
cb1();
});
},
(cb1) => {
this.contract.totalSupply((err, _totalSupply) => {
if (err) { cb1(err); return; }
st.totalSupply =
_totalSupply.div(new BigNumber(10).pow(st.decimals)).toNumber();
cb1();
});
},
(cb1) => {
this.contract.parentToken((err, _parentToken) => {
if (err) { cb1(err); return; }
st.parentToken = _parentToken;
cb1();
});
},
(cb1) => {
this.contract.parentSnapShotBlock((err, _parentSnapShotBlock) => {
if (err) { cb1(err); return; }
st.parentSnapShotBlock = _parentSnapShotBlock;
cb1();
});
},
(cb1) => {
async.eachSeries(accounts, (account, cb2) => {
this.contract.balanceOf(account, (err, res) => {
if (err) { cb2(err); return; }
st.balances[ account ] =
res.div(new BigNumber(10).pow(st.decimals)).toNumber();
cb2();
});
}, cb1);
},
], (err2) => {
cb(err2, st);
});
}, _cb);
}
static deploy(web3, opts, _cb) {
return asyncfunc((cb) => {
const params = Object.assign({}, opts);
params.parentToken = params.parentToken || 0;
params.parentSnapShotBlock = params.parentSnapShotBlock || 0;
params.transfersEnabled = (typeof params.transfersEnabled === "undefined") ? true : params.transfersEnabled;
async.series([
(cb1) => {
params.abi = MiniMeTokenFactoryAbi;
params.byteCode = MiniMeTokenFactoryByteCode;
deploy(web3, params, (err, _tokenFactory) => {
if (err) {
cb1(err);
return;
}
params.tokenFactory = _tokenFactory.address;
cb1();
});
},
(cb1) => {
params.abi = MiniMeTokenAbi;
params.byteCode = MiniMeTokenByteCode;
deploy(web3, params, cb1);
},
],
(err, res) => {
if (err) {
cb(err);
return;
}
const minime = new MiniMeToken(web3, res[ 1 ].address);
cb(null, minime);
});
}, _cb);
}
createCloneToken(opts, _cb) {
return asyncfunc((cb) => {
sendContractTx(
this.web3,
this.contract,
"createCloneToken",
opts,
(err2, txHash) => {
if (err2) {
cb(err2);
return;
}
const firstSend = new Date().getTime();
const getTransactionReceiptCB = (err3, res) => {
if (err3) {
cb(err3);
return;
}
if (!res) {
const now = new Date().getTime();
if (now - firstSend > 900000) {
cb(new Error("Timeout mining transaction"));
return;
}
setTimeout(() => {
this.web3.eth.getTransactionReceipt(
txHash,
getTransactionReceiptCB);
}, 200);
return;
}
let cloneTokenAddr =
this.web3.toBigNumber(res.logs[ 0 ].topics[ 1 ]).toString(16);
while (cloneTokenAddr.length < 40) cloneTokenAddr = "0" + cloneTokenAddr;
cloneTokenAddr = "0x" + cloneTokenAddr;
const miniMeTokenClone = new MiniMeToken(this.web3, cloneTokenAddr);
cb(null, miniMeTokenClone);
};
this.web3.eth.getTransactionReceipt(txHash, getTransactionReceiptCB);
});
}, _cb);
}
convertAmountAndSend(method, opts, _cb) {
return asyncfunc((cb) => {
this.contract.decimals((err, _decimals) => {
if (err) {
cb(err);
return;
}
const params = Object.assign({}, opts);
params.amount = new BigNumber(10).pow(_decimals).mul(params.amount);
sendContractTx(
this.web3,
this.contract,
method,
params,
(err2, txHash) => {
if (err2) {
cb(err2);
return;
}
cb(null, txHash);
});
});
}, _cb);
}
transfer(opts, cb) {
return this.convertAmountAndSend("transfer", opts, cb);
}
generateTokens(opts, cb) {
return this.convertAmountAndSend("generateTokens", opts, cb);
}
destroyTokens(opts, cb) {
const params = Object.assign({}, opts);
params.extraGas = 50000;
return this.convertAmountAndSend("destroyTokens", params, cb);
}
approve(opts, cb) {
return this.convertAmountAndSend("approve", opts, cb);
}
allowance(opts, _cb) {
return asyncfunc((cb) => {
let decimals;
let allowance;
async.series([
(cb1) => {
this.contract.decimals((err, _decimals) => {
if (err) {
cb1(err);
return;
}
decimals = _decimals;
cb1();
});
},
(cb1) => {
this.contract.allowance(opts.owner, opts.spender, (err, res) => {
if (err) {
cb1(err);
return;
}
allowance = res.div(new BigNumber(10).pow(decimals)).toNumber();
cb1();
});
},
], (err2) => {
if (err2) {
cb(err2);
} else {
cb(null, allowance);
}
});
}, _cb);
}
};
module.exports = generateClass(MiniMeTokenAbi, MiniMeTokenByteCode);

5
js/minimetokenfactory.js Normal file
View File

@ -0,0 +1,5 @@
const MiniMeTokenFactoryAbi = require('../build/MiniMeToken.sol').MiniMeTokenFactoryAbi;
const MiniMeTokenFactoryByteCode = require('../build/MiniMeToken.sol').MiniMeTokenFactoryByteCode;
const generateClass = require('eth-contract-class').default;
module.exports = generateClass(MiniMeTokenFactoryAbi, MiniMeTokenFactoryByteCode);

44
js/minimetokenstate.js Normal file
View File

@ -0,0 +1,44 @@
class MiniMeTokenState {
constructor(minimeToken) {
this.$token = minimeToken;
}
async getState() {
const st = {
balances: {},
};
const res = await Promise.all([
this.$token.name(),
this.$token.decimals(),
this.$token.controller(),
this.$token.totalSupply(),
this.$token.parentToken(),
this.$token.controller(),
this.$token.parentSnapShotBlock(),
this.$token.$web3.eth.getAccounts(),
]);
st.name = res[0];
st.decimals = res[1];
st.controller = res[2];
st.totalSupply = res[3];
st.parentToken = res[4];
st.controller = res[5];
st.parentSnapShotBlock = res[6];
const accounts = res[7];
const calls = accounts.map(account => this.$token.balanceOf(account));
const balances = await Promise.all(calls);
for (let i = 0; i < accounts.length; i += 1) {
st.balances[accounts[i]] = balances[i];
}
return st;
}
}
module.exports = MiniMeTokenState;

5627
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,14 @@
{
"name": "minimetoken",
"version": "0.1.7",
"version": "0.2.0",
"description": "MiniMe contract",
"main": "dist/minimetoken.js",
"directories": {
"test": "test"
},
"scripts": {
"test": "mocha --compilers js:babel-core/register",
"build": "rm -rf dist/* && babel-node js/compile.js && babel js/minimetoken.js -o dist/minimetoken.js"
"build": "solcpiler",
"test": "solcpiler; mocha --harmony"
},
"repository": {
"type": "git",
@ -32,16 +32,20 @@
},
"homepage": "https://github.com/Giveth/minime",
"dependencies": {
"async": "^2.1.4",
"bignumber.js": "^4.0.0",
"ethconnector": "0.0.24",
"lodash": "^4.17.4",
"runethtx": "0.0.7"
"eth-contract-class": "0.0.6"
},
"devDependencies": {
"babel-cli": "^6.22.2",
"babel-plugin-add-module-exports": "^0.2.1",
"babel-preset-es2015": "^6.22.0",
"babel-preset-stage-2": "^6.22.0"
"babel-eslint": "^7.2.3",
"eslint-config-airbnb": "^15.0.1",
"eslint-plugin-import": "^2.6.0",
"eslint-plugin-jsx-a11y": "^6.0.2",
"eslint-plugin-react": "^7.1.0",
"ethereumjs-testrpc": "git://github.com/perissology/testrpc.git#81216dbc",
"lerna": "^2.2.0",
"random-bytes": "^1.0.0",
"mocha": "^3.5.0",
"solcpiler": "0.0.6",
"web3": "git://github.com/perissology/web3.js.git#all_fixes",
"chai": "^4.1.0"
}
}

View File

@ -1,11 +1,24 @@
const ethConnector = require("ethconnector");
const assert = require("assert"); // node.js core module
const async = require("async");
/* eslint-env mocha */
/* eslint-disable no-await-in-loop */
const TestRPC = require('ethereumjs-testrpc');
const Web3 = require('web3');
const chai = require('chai');
const MiniMeToken = require("../js/minimetoken");
const MiniMeToken = require('../index.js').MiniMeToken;
const MiniMeTokenFactory = require('../index.js').MiniMeTokenFactory;
const MiniMeTokenState = require('../index.js').MiniMeTokenState;
const assert = chai.assert;
const { utils } = Web3;
const verbose = false;
const log = (S) => {
if (verbose) {
console.log(S);
}
};
// b[0] -> 0, 0, 0, 0
// b[1] -> 0,10, 0, 0
// b[2] -> 0, 8, 2, 0
@ -15,430 +28,191 @@ const verbose = false;
// b[5] -> 0, 6, 1, 0
// b[6] -> 0, 2, 5. 0
describe("MiniMeToken test", () => {
let miniMeToken;
let miniMeTokenClone;
const b = [];
describe('MiniMeToken test', () => {
let testrpc;
let web3;
let accounts;
let miniMeToken;
let miniMeTokenState;
let miniMeTokenClone;
let miniMeTokenCloneState;
const b = [];
before((done) => {
ethConnector.init("testrpc", { gasLimit: 4000000 }, done);
});
it("should deploy all the contracts", (done) => {
MiniMeToken.deploy(ethConnector.web3, {
tokenName: "MiniMe Test Token",
decimalUnits: 18,
tokenSymbol: "MMT",
}, (err, _miniMeToken) => {
assert.ifError(err);
assert.ok(_miniMeToken.contract.address);
miniMeToken = _miniMeToken;
done();
});
}).timeout(20000);
it("Should generate tokens for address 1", (done) => {
async.series([
(cb) => {
ethConnector.web3.eth.getBlockNumber((err, _blockNumber) => {
assert.ifError(err);
b[ 0 ] = _blockNumber;
log("b[0]->" + b[ 0 ]);
cb();
});
},
(cb) => {
miniMeToken.generateTokens({
owner: ethConnector.accounts[ 1 ],
amount: 10,
from: ethConnector.accounts[ 0 ],
}, cb);
},
(cb) => {
miniMeToken.getState((err, _st) => {
assert.ifError(err);
assert.equal(_st.totalSupply, 10);
assert.equal(_st.balances[ ethConnector.accounts[ 1 ] ], 10);
cb();
});
},
(cb) => {
ethConnector.web3.eth.getBlockNumber((err, _blockNumber) => {
assert.ifError(err);
b[ 1 ] = _blockNumber;
log("b[1]->" + b[ 1 ]);
cb();
});
},
], done);
}).timeout(6000);
it("Should transfer tokens from address 1 to address 2", (done) => {
async.series([
(cb) => {
miniMeToken.transfer({
to: ethConnector.accounts[ 2 ],
from: ethConnector.accounts[ 1 ],
amount: 2,
extraGas: 30000,
}, cb);
},
(cb) => {
ethConnector.web3.eth.getBlockNumber((err, _blockNumber) => {
assert.ifError(err);
b[ 2 ] = _blockNumber;
log("b[2]->" + b[ 2 ]);
cb();
});
},
(cb) => {
miniMeToken.getState((err, _st) => {
assert.ifError(err);
assert.equal(_st.totalSupply, 10);
assert.equal(_st.balances[ ethConnector.accounts[ 1 ] ], 8);
assert.equal(_st.balances[ ethConnector.accounts[ 2 ] ], 2);
cb();
});
},
(cb) => {
miniMeToken.contract.balanceOfAt(
ethConnector.accounts[ 1 ],
b[ 1 ],
(err, _balance) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_balance).toNumber(), 10);
cb();
});
},
], done);
}).timeout(6000);
it("Should allow and transfer tokens from address 2 to address 1 allowed to 3", (done) => {
async.series([
(cb) => {
miniMeToken.approve({
spender: ethConnector.accounts[ 3 ],
amount: 2,
from: ethConnector.accounts[ 2 ],
extraGas: 20000,
}, cb);
},
(cb) => {
miniMeToken.allowance({
owner: ethConnector.accounts[ 2 ],
spender: ethConnector.accounts[ 3 ],
}, (err, _allowed) => {
assert.ifError(err);
assert.equal(_allowed, 2);
cb();
});
},
(cb) => {
miniMeToken.contract.transferFrom(
ethConnector.accounts[ 2 ],
ethConnector.accounts[ 1 ],
ethConnector.web3.toWei(1),
{
from: ethConnector.accounts[ 3 ],
gas: 200000,
}, (err) => {
assert.ifError(err);
cb();
});
},
(cb) => {
miniMeToken.allowance({
owner: ethConnector.accounts[ 2 ],
spender: ethConnector.accounts[ 3 ],
}, (err, _allowed) => {
assert.ifError(err);
assert.equal(_allowed, 1);
cb();
});
},
(cb) => {
ethConnector.web3.eth.getBlockNumber((err, _blockNumber) => {
assert.ifError(err);
b[ 3 ] = _blockNumber;
log("b[3]->" + b[ 3 ]);
cb();
});
},
(cb) => {
miniMeToken.getState((err, _st) => {
assert.ifError(err);
assert.equal(_st.totalSupply, 10);
assert.equal(_st.balances[ ethConnector.accounts[ 1 ] ], 9);
assert.equal(_st.balances[ ethConnector.accounts[ 2 ] ], 1);
cb();
});
},
(cb) => {
miniMeToken.contract.balanceOfAt(ethConnector.accounts[ 1 ], b[ 2 ],
(err, _balance) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_balance), 8);
cb();
});
},
(cb) => {
miniMeToken.contract.balanceOfAt(ethConnector.accounts[ 2 ], b[ 2 ],
(err, _balance) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_balance), 2);
cb();
});
},
(cb) => {
miniMeToken.contract.balanceOfAt(ethConnector.accounts[ 1 ], b[ 1 ],
(err, _balance) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_balance), 10);
cb();
});
},
(cb) => {
miniMeToken.contract.balanceOfAt(ethConnector.accounts[ 2 ], b[ 1 ],
(err, _balance) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_balance), 0);
cb();
});
},
(cb) => {
miniMeToken.contract.balanceOfAt(ethConnector.accounts[ 1 ], b[ 0 ],
(err, _balance) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_balance), 0);
cb();
});
},
(cb) => {
miniMeToken.contract.balanceOfAt(ethConnector.accounts[ 2 ], b[ 0 ],
(err, _balance) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_balance), 0);
cb();
});
},
(cb) => {
miniMeToken.contract.balanceOfAt(ethConnector.accounts[ 1 ], 0,
(err, _balance) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_balance), 0);
cb();
});
},
(cb) => {
miniMeToken.contract.balanceOfAt(ethConnector.accounts[ 2 ], 0,
(err, _balance) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_balance), 0);
cb();
});
},
], done);
});
it("Should Destroy 3 tokens from 1 and 1 from 2", (done) => {
async.series([
(cb) => {
miniMeToken.destroyTokens({
owner: ethConnector.accounts[ 1 ],
amount: 3,
from: ethConnector.accounts[ 0 ],
}, cb);
},
(cb) => {
ethConnector.web3.eth.getBlockNumber((err, _blockNumber) => {
assert.ifError(err);
b[ 4 ] = _blockNumber;
log("b[4]->" + b[ 4 ]);
cb();
});
},
(cb) => {
miniMeToken.getState((err, _st) => {
assert.ifError(err);
assert.equal(_st.totalSupply, 7);
assert.equal(_st.balances[ ethConnector.accounts[ 1 ] ], 6);
cb();
});
},
], done);
});
it("Should Create the clone token", (done) => {
async.series([
(cb) => {
miniMeToken.createCloneToken({
cloneTokenName: "Clone Token 1",
cloneDecimalUnits: 18,
cloneTokenSymbol: "MMTc",
snapshotBlock: 0,
transfersEnabled: true,
}, (err, _miniMeTokenClone) => {
assert.ifError(err);
miniMeTokenClone = _miniMeTokenClone;
cb();
});
},
(cb) => {
ethConnector.web3.eth.getBlockNumber((err, _blockNumber) => {
assert.ifError(err);
b[ 5 ] = _blockNumber;
log("b[5]->" + b[ 5 ]);
cb();
});
},
(cb) => {
miniMeTokenClone.getState((err, _st) => {
assert.ifError(err);
assert.equal(_st.parentToken, miniMeToken.contract.address);
assert.equal(_st.parentSnapShotBlock, b[ 5 ]);
assert.equal(_st.totalSupply, 7);
assert.equal(_st.balances[ ethConnector.accounts[ 1 ] ], 6);
cb();
});
},
(cb) => {
miniMeTokenClone.contract.totalSupplyAt(b[ 4 ], (err, _balance) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_balance), 7);
cb();
});
},
(cb) => {
miniMeTokenClone.contract.balanceOfAt(ethConnector.accounts[ 2 ], b[ 4 ],
(err, _balance) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_balance), 1);
cb();
});
},
], done);
}).timeout(6000000);
it("Should mine one block to take effect clone", (done) => {
miniMeToken.transfer({
to: ethConnector.accounts[ 1 ],
from: ethConnector.accounts[ 1 ],
amount: 1,
extraGas: 30000,
}, done);
});
it("Should move tokens in the clone token from 2 to 3", (done) => {
async.series([
(cb) => {
miniMeTokenClone.transfer({
to: ethConnector.accounts[ 2 ],
amount: 4,
from: ethConnector.accounts[ 1 ],
extraGas: 200000,
}, cb);
},
(cb) => {
ethConnector.web3.eth.getBlockNumber((err, _blockNumber) => {
assert.ifError(err);
b[ 6 ] = _blockNumber;
log("b[6]->" + b[ 6 ]);
cb();
});
},
(cb) => {
miniMeTokenClone.getState((err, _st) => {
assert.ifError(err);
assert.equal(_st.totalSupply, 7);
assert.equal(_st.balances[ ethConnector.accounts[ 1 ] ], 2);
assert.equal(_st.balances[ ethConnector.accounts[ 2 ] ], 5);
cb();
});
},
(cb) => {
miniMeToken.contract.balanceOfAt(ethConnector.accounts[ 1 ], b[ 5 ],
(err, _balance) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_balance), 6);
cb();
});
},
(cb) => {
miniMeToken.contract.balanceOfAt(ethConnector.accounts[ 2 ], b[ 5 ],
(err, _balance) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_balance), 1);
cb();
});
},
(cb) => {
miniMeTokenClone.contract.balanceOfAt(ethConnector.accounts[ 1 ], b[ 5 ],
(err, _balance) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_balance), 6);
cb();
});
},
(cb) => {
miniMeTokenClone.contract.balanceOfAt(ethConnector.accounts[ 2 ], b[ 5 ],
(err, _balance) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_balance), 1);
cb();
});
},
(cb) => {
miniMeTokenClone.contract.balanceOfAt(ethConnector.accounts[ 1 ], b[ 4 ],
(err, _balance) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_balance), 6);
cb();
});
},
(cb) => {
miniMeTokenClone.contract.balanceOfAt(ethConnector.accounts[ 2 ], b[ 4 ],
(err, _balance) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_balance), 1);
cb();
});
},
(cb) => {
miniMeTokenClone.contract.totalSupplyAt(b[ 5 ],
(err, _totalSupply) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_totalSupply), 7);
cb();
});
},
(cb) => {
miniMeTokenClone.contract.totalSupplyAt(b[ 4 ],
(err, _totalSupply) => {
assert.ifError(err);
assert.equal(ethConnector.web3.fromWei(_totalSupply), 7);
cb();
});
},
], done);
}).timeout(6000000);
it("Should create tokens in the child token", (done) => {
async.series([
(cb) => {
miniMeTokenClone.generateTokens({
owner: ethConnector.accounts[ 1 ],
amount: 10,
from: ethConnector.accounts[ 0 ],
extraGas: 300000,
}, cb);
},
(cb) => {
miniMeTokenClone.getState((err, _st) => {
assert.ifError(err);
assert.equal(_st.totalSupply, 17);
assert.equal(_st.balances[ ethConnector.accounts[ 1 ] ], 12);
assert.equal(_st.balances[ ethConnector.accounts[ 2 ] ], 5);
cb();
});
},
], done);
before(async () => {
testrpc = TestRPC.server({
ws: true,
gasLimit: 5800000,
total_accounts: 10,
});
function log(S) {
if (verbose) {
console.log(S);
}
}
testrpc.listen(8546, '127.0.0.1');
web3 = new Web3('ws://localhost:8546');
accounts = await web3.eth.getAccounts();
});
after((done) => {
testrpc.close();
done();
});
it('should deploy all the contracts', async () => {
const tokenFactory = await MiniMeTokenFactory.new(web3);
miniMeToken = await MiniMeToken.new(web3,
tokenFactory.$address,
0,
0,
'MiniMe Test Token',
18,
'MMT',
true);
assert.ok(miniMeToken.$address);
miniMeTokenState = new MiniMeTokenState(miniMeToken);
}).timeout(20000);
it('Should generate tokens for address 1', async () => {
b[0] = await web3.eth.getBlockNumber();
log(`b[0]-> ${b[0]}`);
await miniMeToken.generateTokens(accounts[1], 10);
const st = await miniMeTokenState.getState();
assert.equal(st.totalSupply, 10);
assert.equal(st.balances[accounts[1]], 10);
b[1] = await web3.eth.getBlockNumber();
}).timeout(6000);
it('Should transfer tokens from address 1 to address 2', async () => {
await miniMeToken.transfer(accounts[2], 2, { from: accounts[1], gas: 200000 });
b[2] = await web3.eth.getBlockNumber();
log(`b[2]-> ${b[3]}`);
const st = await miniMeTokenState.getState();
assert.equal(st.totalSupply, 10);
assert.equal(st.balances[accounts[1]], 8);
assert.equal(st.balances[accounts[2]], 2);
const balance = await miniMeToken.balanceOfAt(accounts[1], b[1]);
assert.equal(balance, 10);
}).timeout(6000);
it('Should allow and transfer tokens from address 2 to address 1 allowed to 3', async () => {
await miniMeToken.approve(accounts[3], 2, { from: accounts[2] });
const allowed = await miniMeToken.allowance(accounts[2], accounts[3]);
assert.equal(allowed, 2);
await miniMeToken.transferFrom(accounts[2], accounts[1], 1, { from: accounts[3] });
const allowed2 = await miniMeToken.allowance(accounts[2], accounts[3]);
assert.equal(allowed2, 1);
b[3] = await web3.eth.getBlockNumber();
log(`b[3]-> ${b[3]}`);
const st = await miniMeTokenState.getState();
assert.equal(st.totalSupply, 10);
assert.equal(st.balances[accounts[1]], 9);
assert.equal(st.balances[accounts[2]], 1);
let balance;
balance = await miniMeToken.balanceOfAt(accounts[1], b[2]);
assert.equal(balance, 8);
balance = await miniMeToken.balanceOfAt(accounts[2], b[2]);
assert.equal(balance, 2);
balance = await miniMeToken.balanceOfAt(accounts[1], b[1]);
assert.equal(balance, 10);
balance = await miniMeToken.balanceOfAt(accounts[2], b[1]);
assert.equal(balance, 0);
balance = await miniMeToken.balanceOfAt(accounts[1], b[0]);
assert.equal(balance, 0);
balance = await miniMeToken.balanceOfAt(accounts[2], b[0]);
assert.equal(balance, 0);
balance = await miniMeToken.balanceOfAt(accounts[1], 0);
assert.equal(balance, 0);
balance = await miniMeToken.balanceOfAt(accounts[2], 0);
assert.equal(balance, 0);
});
it('Should Destroy 3 tokens from 1 and 1 from 2', async () => {
await miniMeToken.destroyTokens(accounts[1], 3, { from: accounts[0], gas: 200000 });
b[4] = await web3.eth.getBlockNumber();
log(`b[4]-> ${b[4]}`);
const st = await miniMeTokenState.getState();
assert.equal(st.totalSupply, 7);
assert.equal(st.balances[accounts[1]], 6);
});
it('Should Create the clone token', async () => {
const miniMeTokenCloneTx = await miniMeToken.createCloneToken(
'Clone Token 1',
18,
'MMTc',
0,
true);
let addr = miniMeTokenCloneTx.events.NewCloneToken.raw.topics[1];
addr = `0x${addr.slice(26)}`;
addr = utils.toChecksumAddress(addr);
miniMeTokenClone = new MiniMeToken(web3, addr);
miniMeTokenCloneState = new MiniMeTokenState(miniMeTokenClone);
b[5] = await web3.eth.getBlockNumber();
log(`b[5]-> ${b[5]}`);
const st = await miniMeTokenCloneState.getState();
assert.equal(st.parentToken, miniMeToken.$address);
assert.equal(st.parentSnapShotBlock, b[5]);
assert.equal(st.totalSupply, 7);
assert.equal(st.balances[accounts[1]], 6);
const totalSupply = await miniMeTokenClone.totalSupplyAt(b[4]);
assert.equal(totalSupply, 7);
const balance = await miniMeTokenClone.balanceOfAt(accounts[2], b[4]);
assert.equal(balance, 1);
}).timeout(6000);
it('Should mine one block to take effect clone', async () => {
await miniMeToken.transfer(accounts[1], 1, { from: accounts[1] });
});
it('Should move tokens in the clone token from 2 to 3', async () => {
await miniMeTokenClone.transfer(accounts[2], 4, { from: accounts[1] });
b[6] = await web3.eth.getBlockNumber();
log(`b[6]-> ${b[6]}`);
const st = await miniMeTokenCloneState.getState();
assert.equal(st.totalSupply, 7);
assert.equal(st.balances[accounts[1]], 2);
assert.equal(st.balances[accounts[2]], 5);
let balance;
balance = await miniMeToken.balanceOfAt(accounts[1], b[5]);
assert.equal(balance, 6);
balance = await miniMeToken.balanceOfAt(accounts[2], b[5]);
assert.equal(balance, 1);
balance = await miniMeTokenClone.balanceOfAt(accounts[1], b[5]);
assert.equal(balance, 6);
balance = await miniMeTokenClone.balanceOfAt(accounts[2], b[5]);
assert.equal(balance, 1);
balance = await miniMeTokenClone.balanceOfAt(accounts[1], b[4]);
assert.equal(balance, 6);
balance = await miniMeTokenClone.balanceOfAt(accounts[2], b[4]);
assert.equal(balance, 1);
let totalSupply;
totalSupply = await miniMeTokenClone.totalSupplyAt(b[5]);
assert.equal(totalSupply, 7);
totalSupply = await miniMeTokenClone.totalSupplyAt(b[4]);
assert.equal(totalSupply, 7);
}).timeout(6000);
it('Should create tokens in the child token', async () => {
await miniMeTokenClone.generateTokens(accounts[1], 10, { from: accounts[0], gas: 300000 });
const st = await miniMeTokenCloneState.getState();
assert.equal(st.totalSupply, 17);
assert.equal(st.balances[accounts[1]], 12);
assert.equal(st.balances[accounts[2]], 5);
});
});