mirror of
https://github.com/vacp2p/minime.git
synced 2026-01-06 22:03:51 -05:00
Full refactor of the tests to use await method
This commit is contained in:
43
.eslintrc
43
.eslintrc
@@ -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
3
.travis.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
language: 'node_js'
|
||||
node_js:
|
||||
- '8'
|
||||
@@ -1,5 +1,7 @@
|
||||

|
||||
|
||||
[](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
7
build/Controlled.sol.js
Normal 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
19
build/Controlled_all.sol
Normal 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
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
612
build/MiniMeToken_all.sol
Normal 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;
|
||||
}
|
||||
}
|
||||
25
build/SampleCampaign-TokenController.sol.js
Normal file
25
build/SampleCampaign-TokenController.sol.js
Normal file
File diff suppressed because one or more lines are too long
799
build/SampleCampaign-TokenController_all.sol
Normal file
799
build/SampleCampaign-TokenController_all.sol
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
7
build/TokenController.sol.js
Normal file
7
build/TokenController.sol.js
Normal 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"
|
||||
28
build/TokenController_all.sol
Normal file
28
build/TokenController_all.sol
Normal 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
17
contracts/Controlled.sol
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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
26
contracts/TokenController.sol
Normal file
26
contracts/TokenController.sol
Normal 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
265
dist/minimetoken.js
vendored
@@ -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
3
index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
exports.MiniMeToken = require('./js/minimetoken');
|
||||
exports.MiniMeTokenFactory = require('./js/minimetokenfactory');
|
||||
exports.MiniMeTokenState = require('./js/minimetokenstate');
|
||||
@@ -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
5
js/minimetokenfactory.js
Normal 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
44
js/minimetokenstate.js
Normal 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
5627
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
28
package.json
28
package.json
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user