mirror of
https://github.com/vacp2p/minime.git
synced 2026-01-08 22:57:57 -05:00
Copied token from future recopistory
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
276
MiniMeToken.sol
Normal file
276
MiniMeToken.sol
Normal file
@@ -0,0 +1,276 @@
|
||||
pragma solidity ^0.4.4;
|
||||
|
||||
contract Owned {
|
||||
/// Allows only the owner to call a function
|
||||
modifier onlyOwner { if (msg.sender != owner) throw; _; }
|
||||
|
||||
address public owner;
|
||||
|
||||
function Owned() { owner = msg.sender;}
|
||||
|
||||
|
||||
|
||||
function changeOwner(address _newOwner) onlyOwner {
|
||||
owner = _newOwner;
|
||||
}
|
||||
}
|
||||
|
||||
contract MiniMeToken is Owned {
|
||||
|
||||
string public name; //fancy name: eg Simon Bucks
|
||||
uint8 public decimals; //How many decimals to show. ie. There could 1000 base units with 3 decimals. Meaning 0.980 SBX = 980 base units. It's like comparing 1 wei to 1 ether.
|
||||
string public symbol; //An identifier: eg SBX
|
||||
string public version = 'H0.1'; //human 0.1 standard. Just an arbitrary versioning scheme.
|
||||
|
||||
struct Checkpoint {
|
||||
// snapshot when starts to take effect this assignation
|
||||
uint fromBlock;
|
||||
// balance assigned to token holder from this snapshot
|
||||
uint value;
|
||||
}
|
||||
|
||||
MiniMeToken public parentToken;
|
||||
uint public parentSnapShotBlock;
|
||||
uint public creationBlock;
|
||||
mapping (address => Checkpoint[]) balances;
|
||||
mapping (address => mapping (address => uint256)) allowed;
|
||||
Checkpoint[] totalSupplyHistory;
|
||||
bool public isConstant;
|
||||
|
||||
MiniMeTokenFactory public tokenFactory;
|
||||
|
||||
////////////////
|
||||
// Constructor
|
||||
////////////////
|
||||
|
||||
function MiniMeToken(
|
||||
address _tokenFactory,
|
||||
address _parentToken,
|
||||
uint _parentSnapShotBlock,
|
||||
string _tokenName,
|
||||
uint8 _decimalUnits,
|
||||
string _tokenSymbol,
|
||||
bool _isConstant
|
||||
) {
|
||||
tokenFactory = MiniMeTokenFactory(_tokenFactory);
|
||||
name = _tokenName; // Set the name for display purposes
|
||||
decimals = _decimalUnits; // Amount of decimals for display purposes
|
||||
symbol = _tokenSymbol; // Set the symbol for display purposes
|
||||
parentToken = MiniMeToken(_parentToken);
|
||||
parentSnapShotBlock = _parentSnapShotBlock;
|
||||
isConstant = _isConstant;
|
||||
creationBlock = block.number;
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
// ERC20 Interface
|
||||
////////////////
|
||||
|
||||
function transfer(address _to, uint256 _value) returns (bool success) {
|
||||
|
||||
return doTransfer(msg.sender, _to, _value);
|
||||
}
|
||||
|
||||
function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
|
||||
//same as above. Replace this line with the following if you want to protect against wrapping uints.
|
||||
//if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]) {
|
||||
|
||||
if (isConstant) throw;
|
||||
if (msg.sender != owner) {
|
||||
if (allowed[_from][msg.sender] < _value) return false;
|
||||
allowed[_from][msg.sender] -= _value;
|
||||
}
|
||||
return doTransfer(_from, _to, _value);
|
||||
}
|
||||
|
||||
function doTransfer(address _from, address _to, uint _value) internal returns(bool) {
|
||||
|
||||
if (_value == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do not allow transfer to this
|
||||
if ((_to == 0) || (_to == address(this))) throw;
|
||||
|
||||
// Remove _from votes
|
||||
var previousBalanceFrom = balanceOfAt(_from, block.number);
|
||||
if (previousBalanceFrom < _value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
updateValueAtNow(balances[_from], previousBalanceFrom - _value);
|
||||
|
||||
var previousBalanceTo = balanceOfAt(_to, block.number);
|
||||
updateValueAtNow(balances[_to], previousBalanceTo + _value);
|
||||
|
||||
Transfer(_from, _to, _value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function balanceOf(address _owner) constant returns (uint256 balance) {
|
||||
return balanceOfAt(_owner, block.number);
|
||||
}
|
||||
|
||||
function approve(address _spender, uint256 _value) returns (bool success) {
|
||||
if (isConstant) throw;
|
||||
allowed[msg.sender][_spender] = _value;
|
||||
Approval(msg.sender, _spender, _value);
|
||||
return true;
|
||||
}
|
||||
|
||||
function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
|
||||
return allowed[_owner][_spender];
|
||||
}
|
||||
|
||||
/* Approves and then calls the receiving contract */
|
||||
function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) {
|
||||
if (isConstant) throw;
|
||||
allowed[msg.sender][_spender] = _value;
|
||||
Approval(msg.sender, _spender, _value);
|
||||
|
||||
//call the receiveApproval function on the contract you want to be notified. This crafts the function signature manually so one doesn't have to include a contract in here just for this.
|
||||
//receiveApproval(address _from, uint256 _value, address _tokenContract, bytes _extraData)
|
||||
//it is assumed that when does this that the call *should* succeed, otherwise one would use vanilla approve instead.
|
||||
if(!_spender.call(bytes4(bytes32(sha3("receiveApproval(address,uint256,address,bytes)"))), msg.sender, _value, this, _extraData)) { throw; }
|
||||
return true;
|
||||
}
|
||||
|
||||
function totalSupply() constant returns (uint) {
|
||||
return totalSupplyAt(block.number);
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
// Query balance and totalSupply in History
|
||||
////////////////
|
||||
|
||||
function balanceOfAt(address _holder, uint _blockNumber) constant returns (uint) {
|
||||
|
||||
if (_blockNumber < creationBlock) {
|
||||
return 0;
|
||||
} else if ((balances[_holder].length == 0) || (balances[_holder][0].fromBlock > _blockNumber)) {
|
||||
if (address(parentToken) != 0) {
|
||||
return parentToken.balanceOfAt(_holder, parentSnapShotBlock);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return getValueAt( balances[_holder], _blockNumber);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function totalSupplyAt(uint _blockNumber) constant returns(uint) {
|
||||
if (_blockNumber < creationBlock) {
|
||||
return 0;
|
||||
} else if ((totalSupplyHistory.length == 0) || (totalSupplyHistory[0].fromBlock > _blockNumber)) {
|
||||
if (address(parentToken) != 0) {
|
||||
return parentToken.totalSupplyAt(parentSnapShotBlock);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return getValueAt( totalSupplyHistory, _blockNumber);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////
|
||||
// Create a child token from an snapshot of this token at a given block
|
||||
////////////////
|
||||
|
||||
function createChildToken(string _childTokenName, uint8 _childDecimalUnits, string _childTokenSymbol, uint _snapshotBlock, bool _isConstant) returns(address) {
|
||||
if (_snapshotBlock > block.number) _snapshotBlock = block.number;
|
||||
MiniMeToken childToken = tokenFactory.createChildToken(this, _snapshotBlock, _childTokenName, _childDecimalUnits, _childTokenSymbol, _isConstant);
|
||||
NewChildToken(address(childToken), _snapshotBlock);
|
||||
return address(childToken);
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
// Generate and destroy tokens
|
||||
////////////////
|
||||
|
||||
function generateTokens(address _holder, uint _value) onlyOwner {
|
||||
if (isConstant) throw;
|
||||
uint curTotalSupply = getValueAt(totalSupplyHistory, block.number);
|
||||
updateValueAtNow(totalSupplyHistory, curTotalSupply + _value);
|
||||
var previousBalanceTo = balanceOf(_holder);
|
||||
updateValueAtNow(balances[_holder], previousBalanceTo + _value);
|
||||
Transfer(0, _holder, _value);
|
||||
}
|
||||
|
||||
function destroyTokens(address _holder, uint _value) onlyOwner {
|
||||
if (isConstant) throw;
|
||||
uint curTotalSupply = getValueAt(totalSupplyHistory, block.number);
|
||||
if (curTotalSupply < _value) throw;
|
||||
updateValueAtNow(totalSupplyHistory, curTotalSupply - _value);
|
||||
var previousBalanceFrom = balanceOf(_holder);
|
||||
if (previousBalanceFrom < _value) throw;
|
||||
updateValueAtNow(balances[_holder], previousBalanceFrom - _value);
|
||||
Transfer(_holder, 0, _value);
|
||||
}
|
||||
|
||||
////////////////
|
||||
// Constant tokens
|
||||
////////////////
|
||||
|
||||
function setConstant(bool _isConstant) onlyOwner {
|
||||
isConstant = _isConstant;
|
||||
}
|
||||
|
||||
////////////////
|
||||
// Internal helper functions to query and set a value in a snapshot array
|
||||
////////////////
|
||||
|
||||
function getValueAt(Checkpoint[] storage checkpoints, uint _block) constant internal returns (uint) {
|
||||
if (checkpoints.length == 0) return 0;
|
||||
//Shorcut for the actual value
|
||||
if (_block >= checkpoints[checkpoints.length-1].fromBlock) return checkpoints[checkpoints.length-1].value;
|
||||
if (_block < checkpoints[0].fromBlock) return 0;
|
||||
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;
|
||||
}
|
||||
|
||||
function updateValueAtNow(Checkpoint[] storage checkpoints, uint _value) internal {
|
||||
if ((checkpoints.length == 0) || (checkpoints[checkpoints.length -1].fromBlock < block.number)) {
|
||||
Checkpoint newCheckPoint = checkpoints[ checkpoints.length++ ];
|
||||
newCheckPoint.fromBlock = block.number;
|
||||
newCheckPoint.value = _value;
|
||||
} else {
|
||||
Checkpoint oldCheckPoint = checkpoints[checkpoints.length-1];
|
||||
oldCheckPoint.value = _value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
event Transfer(address indexed _from, address indexed _to, uint256 _value);
|
||||
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
|
||||
event NewChildToken(address indexed _childToken, uint _snapshotBlock);
|
||||
|
||||
}
|
||||
|
||||
contract MiniMeTokenFactory {
|
||||
function createChildToken(
|
||||
address _parentToken,
|
||||
uint _snapshotBlock,
|
||||
string _tokenName,
|
||||
uint8 _decimalUnits,
|
||||
string _tokenSymbol,
|
||||
bool _isConstant
|
||||
) returns (MiniMeToken) {
|
||||
MiniMeToken newToken = new MiniMeToken(this, _parentToken, _snapshotBlock, _tokenName, _decimalUnits, _tokenSymbol, _isConstant);
|
||||
return newToken;
|
||||
}
|
||||
}
|
||||
58
README.md
Normal file
58
README.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# MiniMeToken
|
||||
|
||||
A MiniMeToken is a standard ERC20 token with some extra functionality:
|
||||
|
||||
### The token is clonable
|
||||
|
||||
Any body can create a new token with an initial distribution of cloned tokens identical to the original token. At some specific block.
|
||||
|
||||
To create a child token, the next function is defined:
|
||||
|
||||
function createChildToken(
|
||||
string _childTokenName,
|
||||
uint8 _childDecimalUnits,
|
||||
string _childTokenSymbol
|
||||
uint _snapshotBlock, // if block is not already mined, it will teke the current block
|
||||
bool _isConstant
|
||||
) returns(address)
|
||||
|
||||
Once the child token is created, it acts as a completely independent token.
|
||||
|
||||
### Balances history is registered and available
|
||||
|
||||
The contract maintains a history of all the distribution changes of the token. Two calls are introduced to know the totalSupply and the balance of any address at any block in the past.
|
||||
|
||||
function totalSupplyAt(uint _blockNumber) constant returns(uint)
|
||||
|
||||
function balanceOfAt(address _holder, uint _blockNumber) constant returns (uint)
|
||||
|
||||
### Optional token owner
|
||||
|
||||
The owner of the contract can generate/destroy/transfer tokens at its own discretion. Of course, the owner can be a regular account, another contract that rules the contract or just the address 0x0 if this functionality is not wanted.
|
||||
|
||||
As an example, a Token Creation contract can be the owner of the Token Contract and at the end of the token creation period, the ownership can be transfered to the 0x0 address.
|
||||
|
||||
To create and destroy tokens, this two functions are introduced:
|
||||
|
||||
function generateTokens(address _holder, uint _value) onlyOwner
|
||||
|
||||
function destroyTokens(address _holder, uint _value) onlyOwner
|
||||
|
||||
### Owner of the token can freeze the transfers.
|
||||
|
||||
Tokens can be created with the constant flag set on, and the owner can also toggle this flag. When a token is flagged with this flag, no transfers, generations and destroys are allowed.
|
||||
|
||||
function setConstant(bool _isConstant) onlyOwner
|
||||
|
||||
|
||||
## Applications
|
||||
|
||||
Some of the applications that child tokens can be used for are:
|
||||
|
||||
1. a ballot that is burned when you vote.
|
||||
2. a discount ticked that is redeemed when you use it.
|
||||
3. a token of a "spinoff" DAO.
|
||||
4. a token that can be used to give explicit support to an action or a campaign.
|
||||
5. lots of other applications.
|
||||
|
||||
And all that maintaining always the original token.
|
||||
77
js/minimetoken_helper.js
Normal file
77
js/minimetoken_helper.js
Normal file
@@ -0,0 +1,77 @@
|
||||
/*jslint node: true */
|
||||
"use strict";
|
||||
|
||||
var async = require('async');
|
||||
var ethConnector = require('ethconnector');
|
||||
var path = require('path');
|
||||
var _ = require('lodash');
|
||||
|
||||
|
||||
var miniMeTokenAbi;
|
||||
var minimeToken;
|
||||
var miniMeTokenFactoryAbi;
|
||||
var miniMeTokenFactory;
|
||||
|
||||
var src;
|
||||
|
||||
exports.deploy = function(opts, cb) {
|
||||
var compilationResult;
|
||||
return async.series([
|
||||
function(cb) {
|
||||
ethConnector.loadSol(path.join(__dirname, "../MiniMeToken.sol"), function(err, _src) {
|
||||
if (err) return cb(err);
|
||||
src = _src;
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
ethConnector.applyConstants(src, opts, function(err, _src) {
|
||||
if (err) return cb(err);
|
||||
src = _src;
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
ethConnector.compile(src, function(err, result) {
|
||||
if (err) return cb(err);
|
||||
compilationResult = result;
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeTokenFactoryAbi = JSON.parse(compilationResult.MiniMeTokenFactory.interface);
|
||||
ethConnector.deploy(compilationResult.MiniMeTokenFactory.interface,
|
||||
compilationResult.MiniMeTokenFactory.bytecode,
|
||||
0,
|
||||
0,
|
||||
function(err, _miniMeTokenFactory) {
|
||||
if (err) return cb(err);
|
||||
miniMeTokenFactory = _miniMeTokenFactory;
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeTokenAbi = JSON.parse(compilationResult.MiniMeToken.interface);
|
||||
exports.miniMeTokenAbi = miniMeTokenAbi;
|
||||
ethConnector.deploy(compilationResult.MiniMeToken.interface,
|
||||
compilationResult.MiniMeToken.bytecode,
|
||||
0,
|
||||
0,
|
||||
miniMeTokenFactory.address,
|
||||
0,
|
||||
0,
|
||||
opts.tokenName,
|
||||
opts.decimalUnits,
|
||||
opts.tokenSymbol,
|
||||
opts.isConstant || false,
|
||||
function(err, _minimeToken) {
|
||||
if (err) return cb(err);
|
||||
minimeToken = _minimeToken;
|
||||
cb();
|
||||
});
|
||||
}
|
||||
], function(err) {
|
||||
if (err) return cb(err);
|
||||
cb(null,minimeToken, compilationResult);
|
||||
});
|
||||
};
|
||||
34
package.json
Normal file
34
package.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "minimi",
|
||||
"version": "0.0.1",
|
||||
"description": "Minimi contract",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/CharityDAO/minimi.git"
|
||||
},
|
||||
"keywords": [
|
||||
"dao",
|
||||
"solidity",
|
||||
"token",
|
||||
"charity",
|
||||
"smart",
|
||||
"contract",
|
||||
"minime"
|
||||
],
|
||||
"author": "Jordi Baylina",
|
||||
"license": "GPL-3.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/CharityDAO/minimi/issues"
|
||||
},
|
||||
"homepage": "https://github.com/CharityDAO/future",
|
||||
"dependencies": {
|
||||
"ethconnector": "0.0.13"
|
||||
}
|
||||
}
|
||||
525
test/minimetoken_normal.js
Normal file
525
test/minimetoken_normal.js
Normal file
@@ -0,0 +1,525 @@
|
||||
/*jslint node: true */
|
||||
/*global describe, it, before, beforeEach, after, afterEach */
|
||||
"use strict";
|
||||
|
||||
|
||||
|
||||
var miniMeTokenHelper = require('../js/minimetoken_helper.js');
|
||||
var ethConnector = require('ethconnector');
|
||||
var BigNumber = require('bignumber.js');
|
||||
|
||||
|
||||
var assert = require("assert"); // node.js core module
|
||||
var async = require('async');
|
||||
var _ = require('lodash');
|
||||
|
||||
var verbose = false;
|
||||
|
||||
|
||||
// b[0] -> 0, 0, 0, 0
|
||||
// b[1] -> 0,10, 0, 0
|
||||
// b[2] -> 0, 8, 2, 0
|
||||
// b[3] -> 0, 9, 1, 0
|
||||
// b[4] -> 0, 6, 1, 0
|
||||
// Child token
|
||||
// b[5] -> 0, 6, 1, 0
|
||||
// b[6] -> 0, 2, 5. 0
|
||||
|
||||
|
||||
|
||||
describe('MiniMeToken test', function(){
|
||||
var miniMeToken;
|
||||
var miniMeTokenChild;
|
||||
var b = [];
|
||||
|
||||
before(function(done) {
|
||||
ethConnector.init('testrpc' ,done);
|
||||
// ethConnector.init('rpc', done);
|
||||
});
|
||||
it('should deploy all the contracts ', function(done){
|
||||
this.timeout(200000000);
|
||||
var now = Math.floor(new Date().getTime() /1000);
|
||||
|
||||
miniMeTokenHelper.deploy({
|
||||
tokenName: "MiniMe Test Token",
|
||||
decimalUnits: 18,
|
||||
tokenSymbol: "MMT",
|
||||
}, function(err, _miniMeToken) {
|
||||
assert.ifError(err);
|
||||
assert.ok(_miniMeToken.address);
|
||||
miniMeToken = _miniMeToken;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('Should generate tokens for address 1', function(done) {
|
||||
this.timeout(2000);
|
||||
async.series([
|
||||
function(cb) {
|
||||
ethConnector.web3.eth.getBlockNumber(function (err, _blockNumber) {
|
||||
assert.ifError(err);
|
||||
b[0] = _blockNumber;
|
||||
log("b[0]->"+b[0]);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.generateTokens(ethConnector.accounts[1], ethConnector.web3.toWei(10), {
|
||||
from: ethConnector.accounts[0],
|
||||
gas: 200000},
|
||||
function(err) {
|
||||
assert.ifError(err);
|
||||
cb();
|
||||
}
|
||||
);
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.totalSupply(function(err, _totalSupply) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_totalSupply), 10);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.balanceOf(ethConnector.accounts[1], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 10);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
ethConnector.web3.eth.getBlockNumber(function (err, _blockNumber) {
|
||||
assert.ifError(err);
|
||||
b[1] = _blockNumber;
|
||||
log("b[1]->"+b[1]);
|
||||
cb();
|
||||
});
|
||||
}
|
||||
],function(err) {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('Should transfer tokens from address 1 to address 2', function(done) {
|
||||
this.timeout(2000);
|
||||
async.series([
|
||||
|
||||
function(cb) {
|
||||
miniMeToken.transfer(ethConnector.accounts[2], ethConnector.web3.toWei(2), {
|
||||
from: ethConnector.accounts[1],
|
||||
gas: 200000},
|
||||
function(err) {
|
||||
assert.ifError(err);
|
||||
cb();
|
||||
}
|
||||
);
|
||||
},
|
||||
function(cb) {
|
||||
ethConnector.web3.eth.getBlockNumber(function (err, _blockNumber) {
|
||||
assert.ifError(err);
|
||||
b[2] = _blockNumber;
|
||||
log("b[2]->"+b[2]);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.totalSupply(function(err, _totalSupply) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_totalSupply), 10);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.balanceOf(ethConnector.accounts[1], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 8);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.balanceOf(ethConnector.accounts[2], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 2);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.balanceOfAt(ethConnector.accounts[1], b[1], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance).toNumber(), 10);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
],function(err) {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('Should allow and transfer tokens from address 2 to address 1 allowed to 3', function(done) {
|
||||
async.series([
|
||||
function(cb) {
|
||||
miniMeToken.approve(ethConnector.accounts[3], ethConnector.web3.toWei(2), {
|
||||
from: ethConnector.accounts[2],
|
||||
gas: 200000},
|
||||
function(err) {
|
||||
assert.ifError(err);
|
||||
cb();
|
||||
}
|
||||
);
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.allowance(ethConnector.accounts[2], ethConnector.accounts[3], function(err, _allowed) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_allowed), 2);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.transferFrom(ethConnector.accounts[2], ethConnector.accounts[1], ethConnector.web3.toWei(1), {
|
||||
from: ethConnector.accounts[3],
|
||||
gas: 200000},
|
||||
function(err) {
|
||||
assert.ifError(err);
|
||||
cb();
|
||||
}
|
||||
);
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.allowance(ethConnector.accounts[2], ethConnector.accounts[3], function(err, _allowed) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_allowed), 1);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
ethConnector.web3.eth.getBlockNumber(function (err, _blockNumber) {
|
||||
assert.ifError(err);
|
||||
b[3] = _blockNumber;
|
||||
log("b[3]->"+b[3]);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.balanceOf(ethConnector.accounts[1], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 9);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.balanceOf(ethConnector.accounts[2], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 1);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.balanceOfAt(ethConnector.accounts[1], b[2], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 8);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.balanceOfAt(ethConnector.accounts[2], b[2], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 2);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.balanceOfAt(ethConnector.accounts[1], b[1], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 10);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.balanceOfAt(ethConnector.accounts[2], b[1], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 0);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.balanceOfAt(ethConnector.accounts[1], b[0], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 0);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.balanceOfAt(ethConnector.accounts[2], b[0], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 0);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.balanceOfAt(ethConnector.accounts[1], 0, function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 0);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.balanceOfAt(ethConnector.accounts[2], 0, function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 0);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
],function(err) {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Should Destroy 3 tokens from 1 and 1 from 2', function(done) {
|
||||
async.series([
|
||||
function(cb) {
|
||||
miniMeToken.destroyTokens(ethConnector.accounts[1], ethConnector.web3.toWei(3), {
|
||||
from: ethConnector.accounts[0],
|
||||
gas: 200000},
|
||||
function(err) {
|
||||
assert.ifError(err);
|
||||
cb();
|
||||
}
|
||||
);
|
||||
},
|
||||
function(cb) {
|
||||
ethConnector.web3.eth.getBlockNumber(function (err, _blockNumber) {
|
||||
assert.ifError(err);
|
||||
b[4] = _blockNumber;
|
||||
log("b[4]->"+b[4]);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.totalSupply(function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 7);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.balanceOf(ethConnector.accounts[1], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 6);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
],function(err) {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Should Create the child token', function(done) {
|
||||
this.timeout(200000000);
|
||||
async.series([
|
||||
function(cb) {
|
||||
miniMeToken.createChildToken(
|
||||
"Child Token 1",
|
||||
18,
|
||||
"MMTc",
|
||||
Number.MAX_SAFE_INTEGER,
|
||||
false,
|
||||
{
|
||||
from: ethConnector.accounts[3],
|
||||
gas: 4700000
|
||||
},
|
||||
function(err, txHash) {
|
||||
assert.ifError(err);
|
||||
ethConnector.web3.eth.getTransactionReceipt(txHash, function(err, res) {
|
||||
var childTokenAddr = ethConnector.web3.toBigNumber(res.logs[0].topics[1]).toString(16);
|
||||
while (childTokenAddr.length < 40) childTokenAddr = '0' + childTokenAddr;
|
||||
childTokenAddr = '0x' + childTokenAddr;
|
||||
miniMeTokenChild = ethConnector.web3.eth.contract( miniMeTokenHelper.miniMeTokenAbi).at(childTokenAddr);
|
||||
cb();
|
||||
});
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
ethConnector.web3.eth.getBlockNumber(function (err, _blockNumber) {
|
||||
assert.ifError(err);
|
||||
b[5] = _blockNumber;
|
||||
log("b[5]->"+b[5]);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeTokenChild.parentToken(function(err, _parentAddress) {
|
||||
assert.ifError(err);
|
||||
assert.equal(_parentAddress, miniMeToken.address);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeTokenChild.parentSnapShotBlock(function(err, _parentSnapshotBlock) {
|
||||
assert.ifError(err);
|
||||
assert.equal(_parentSnapshotBlock, b[5]);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeTokenChild.totalSupply(function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 7);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeTokenChild.balanceOf(ethConnector.accounts[1], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 6);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeTokenChild.totalSupplyAt(b[4], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 0);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeTokenChild.balanceOfAt(ethConnector.accounts[2], b[4], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 0);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
],function(err) {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('Should move tokens in the child token from 2 to 3', function(done) {
|
||||
async.series([
|
||||
function(cb) {
|
||||
miniMeTokenChild.transfer(ethConnector.accounts[2], ethConnector.web3.toWei(4), {
|
||||
from: ethConnector.accounts[1],
|
||||
gas: 200000},
|
||||
function(err) {
|
||||
assert.ifError(err);
|
||||
cb();
|
||||
}
|
||||
);
|
||||
},
|
||||
function(cb) {
|
||||
ethConnector.web3.eth.getBlockNumber(function (err, _blockNumber) {
|
||||
assert.ifError(err);
|
||||
b[6] = _blockNumber;
|
||||
log("b[6]->"+b[6]);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeTokenChild.balanceOf(ethConnector.accounts[1], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 2);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeTokenChild.balanceOf(ethConnector.accounts[2], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 5);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.balanceOfAt(ethConnector.accounts[1], b[5], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 6);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeToken.balanceOfAt(ethConnector.accounts[2], b[5], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 1);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeTokenChild.balanceOfAt(ethConnector.accounts[1], b[5], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 6);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeTokenChild.balanceOfAt(ethConnector.accounts[2], b[5], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 1);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeTokenChild.balanceOfAt(ethConnector.accounts[1], b[4], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 0);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeTokenChild.balanceOfAt(ethConnector.accounts[2], b[4], function(err, _balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_balance), 0);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeTokenChild.totalSupply(function(err, _totalSupply) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_totalSupply), 7);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeTokenChild.totalSupplyAt(b[5], function(err, _totalSupply) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_totalSupply), 7);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
function(cb) {
|
||||
miniMeTokenChild.totalSupplyAt(b[4], function(err, _totalSupply) {
|
||||
assert.ifError(err);
|
||||
assert.equal(ethConnector.web3.fromWei(_totalSupply), 0);
|
||||
cb();
|
||||
});
|
||||
}
|
||||
],function(err) {
|
||||
done();
|
||||
});
|
||||
});
|
||||
function bcDelay(secs, cb) {
|
||||
send("evm_increaseTime", [secs], function(err, result) {
|
||||
if (err) return cb(err);
|
||||
|
||||
// Mine a block so new time is recorded.
|
||||
send("evm_mine", function(err, result) {
|
||||
if (err) return cb(err);
|
||||
cb();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function log(S) {
|
||||
if (verbose) {
|
||||
console.log(S);
|
||||
}
|
||||
}
|
||||
|
||||
// CALL a low level rpc
|
||||
function send(method, params, callback) {
|
||||
if (typeof params == "function") {
|
||||
callback = params;
|
||||
params = [];
|
||||
}
|
||||
|
||||
ethConnector.web3.currentProvider.sendAsync({
|
||||
jsonrpc: "2.0",
|
||||
method: method,
|
||||
params: params || [],
|
||||
id: new Date().getTime()
|
||||
}, callback);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user