From cef3ca405bc9c378fca7578172c6213a60a540fa Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 11 Apr 2018 23:58:41 +0000 Subject: [PATCH 1/4] add truffle tests for DummyValidator --- migrations/2_jurisdiction.js | 13 ------------- package.json | 4 +++- test/contracts/DummyValidator.test.js | 18 ++++++++++++++++++ test/{ => react}/setup.js | 0 test/{ => react}/test.js | 2 +- 5 files changed, 22 insertions(+), 15 deletions(-) delete mode 100644 migrations/2_jurisdiction.js create mode 100644 test/contracts/DummyValidator.test.js rename test/{ => react}/setup.js (100%) rename test/{ => react}/test.js (88%) diff --git a/migrations/2_jurisdiction.js b/migrations/2_jurisdiction.js deleted file mode 100644 index cd0ac6a..0000000 --- a/migrations/2_jurisdiction.js +++ /dev/null @@ -1,13 +0,0 @@ -const Jurisdiction = artifacts.require("Jurisdiction"); -const DummyValidator = artifacts.require("DummyValidator"); - -module.exports = function (deployer) { - deployer.deploy(Jurisdiction).then(function () { - deployer.deploy(DummyValidator, Jurisdiction.address).then(function () { - const jur = new Jurisdiction(Jurisdiction.address); - return jur.addValidator(DummyValidator.address).then(function () { - console.log("Validator added"); - }).catch(console.log); - }); - }); -}; diff --git a/package.json b/package.json index abb98b2..81b34fd 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,9 @@ "scripts": { "start": "react-scripts start", "build": "react-scripts build", - "test": "mocha --require babel-core/register --require test/setup.js", + "test": "npm run test:react && npm run test:contracts", + "test:contracts": "truffle test test/contracts/*", + "test:react": "mocha test/react --require babel-core/register --require test/react/setup.js", "eject": "react-scripts eject" } } diff --git a/test/contracts/DummyValidator.test.js b/test/contracts/DummyValidator.test.js new file mode 100644 index 0000000..5730657 --- /dev/null +++ b/test/contracts/DummyValidator.test.js @@ -0,0 +1,18 @@ +const Jurisdiction = artifacts.require('Jurisdiction'); +const DummyValidator = artifacts.require('DummyValidator'); + +var should = require('chai').should(); + +contract('DummyValidator', function ([investor]) { + + beforeEach(async function () { + this.jurisdiction = await Jurisdiction.new(); + this.dummyValidator = await DummyValidator.new(this.jurisdiction.address); + await this.jurisdiction.addValidator(this.dummyValidator.address); + }); + + it('should validate', async function () { + await this.dummyValidator.validate({from: investor}); + (await this.jurisdiction.hasAttribute(investor, 'VALID')).should.be.true; + }); +}); diff --git a/test/setup.js b/test/react/setup.js similarity index 100% rename from test/setup.js rename to test/react/setup.js diff --git a/test/test.js b/test/react/test.js similarity index 88% rename from test/test.js rename to test/react/test.js index d29277b..e487e39 100644 --- a/test/test.js +++ b/test/react/test.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { shallow } from 'enzyme'; import { spy } from 'sinon'; import { createMockStore } from 'redux-test-utils'; -import ValidatorsList from '../src/components/ValidatorsList.react'; +import ValidatorsList from '../../src/components/ValidatorsList.react'; spy(ValidatorsList.prototype, 'componentDidMount'); From 92804100db852743b677f7b795efc904644f65c7 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 12 Apr 2018 10:21:04 +0000 Subject: [PATCH 2/4] add contracts for token and crowdsale, with tests --- contracts/SampleCrowdsale.sol | 17 ++++++++ contracts/SampleToken.sol | 33 ++++++++++++++ package-lock.json | 15 +++++++ package.json | 4 +- test/contracts/SampleCrowdsale.test.js | 59 ++++++++++++++++++++++++++ 5 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 contracts/SampleCrowdsale.sol create mode 100644 contracts/SampleToken.sol create mode 100644 test/contracts/SampleCrowdsale.test.js diff --git a/contracts/SampleCrowdsale.sol b/contracts/SampleCrowdsale.sol new file mode 100644 index 0000000..9b297d1 --- /dev/null +++ b/contracts/SampleCrowdsale.sol @@ -0,0 +1,17 @@ +pragma solidity ^0.4.18; + +import "zeppelin-solidity/contracts/crowdsale/Crowdsale.sol"; +import "zeppelin-solidity/contracts/crowdsale/emission/MintedCrowdsale.sol"; +import "zeppelin-solidity/contracts/token/ERC20/MintableToken.sol"; + +contract SampleCrowdsale is MintedCrowdsale { + function SampleCrowdsale( + uint256 _rate, + address _wallet, + MintableToken _token + ) + public + Crowdsale(_rate, _wallet, _token) + { + } +} diff --git a/contracts/SampleToken.sol b/contracts/SampleToken.sol new file mode 100644 index 0000000..1d9005f --- /dev/null +++ b/contracts/SampleToken.sol @@ -0,0 +1,33 @@ +pragma solidity ^0.4.18; + +import "zeppelin-solidity/contracts/token/ERC20/MintableToken.sol"; +import { Jurisdiction } from "./tpl-contracts/Jurisdiction.sol"; + +/** + * @title SampleToken + * @dev Mintable ERC20 Token. + */ +contract SampleToken is MintableToken { + + Jurisdiction jurisdiction; + + string public constant name = "SampleToken"; + string public constant symbol = "TPL"; + uint8 public constant decimals = 18; + + function SampleToken(Jurisdiction _jurisdiction) public { + jurisdiction = _jurisdiction; + totalSupply_ = 0; + } + + function mint(address _to, uint256 _amount) + onlyOwner + canMint + public + returns (bool) + { + require(jurisdiction.hasAttribute(_to, 'VALID')); + return super.mint(_to, _amount); + } + +} diff --git a/package-lock.json b/package-lock.json index a6ca061..039bad7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1532,6 +1532,21 @@ "type-detect": "4.0.8" } }, + "chai-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "dev": true, + "requires": { + "check-error": "1.0.2" + } + }, + "chai-bignumber": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/chai-bignumber/-/chai-bignumber-2.0.2.tgz", + "integrity": "sha512-BIdRNjRaoRj4bMsZLKbIZPMNKqmwnzNiyxqBYDSs6dFOCs9w8OHPuUE8e1bH60i1IhOzT0NjLtCD+lKEWB1KTQ==", + "dev": true + }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", diff --git a/package.json b/package.json index 81b34fd..4f81939 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,8 @@ }, "devDependencies": { "chai": "^4.1.2", + "chai-as-promised": "^7.1.1", + "chai-bignumber": "^2.0.2", "enzyme": "^3.3.0", "enzyme-adapter-react-16": "^1.1.1", "jsdom": "^11.7.0", @@ -32,7 +34,7 @@ "start": "react-scripts start", "build": "react-scripts build", "test": "npm run test:react && npm run test:contracts", - "test:contracts": "truffle test test/contracts/*", + "test:contracts": "truffle test test/contracts/* --network unexisting", "test:react": "mocha test/react --require babel-core/register --require test/react/setup.js", "eject": "react-scripts eject" } diff --git a/test/contracts/SampleCrowdsale.test.js b/test/contracts/SampleCrowdsale.test.js new file mode 100644 index 0000000..bc2891e --- /dev/null +++ b/test/contracts/SampleCrowdsale.test.js @@ -0,0 +1,59 @@ +const Jurisdiction = artifacts.require('Jurisdiction'); +const SampleToken = artifacts.require('SampleToken'); +const SampleCrowdsale = artifacts.require('SampleCrowdsale'); + +const BigNumber = web3.BigNumber; + +var should = require('chai') + .use(require('chai-as-promised')) + .use(require('chai-bignumber')(BigNumber)) + .should(); + +contract('SampleCrowdsale', function ([owner, wallet, investor]) { + + const RATE = new BigNumber(1); + + beforeEach(async function () { + this.jurisdiction = await Jurisdiction.new(); + this.jurisdiction.addValidator(owner); + + this.token = await SampleToken.new(this.jurisdiction.address); + this.crowdsale = await SampleCrowdsale.new( + RATE, wallet, this.token.address); + await this.token.transferOwnership(this.crowdsale.address); + }); + + it('should create crowdsale with correct parameters', async function () { + this.crowdsale.should.exist; + this.token.should.exist; + + (await this.crowdsale.rate()).should.be.bignumber.equal(RATE); + (await this.crowdsale.wallet()).should.be.equal(wallet); + }); + + it('should not accept payment not validated', async function () { + const investmentAmount = new BigNumber(web3.toWei(1, 'ether')); + await this.crowdsale.buyTokens( + investor, + { value: investmentAmount, from: investor } + ).should.be.rejectedWith('revert'); + (await this.token.balanceOf(investor)) + .should.be.bignumber.equal(0); + (await this.token.totalSupply()) + .should.be.bignumber.equal(0); + }); + + it('should accept payment validated', async function () { + await this.jurisdiction.addAttribute(investor, 'VALID', 1); + + const investmentAmount = new BigNumber(web3.toWei(1, 'ether')); + await this.crowdsale.buyTokens( + investor, + { value: investmentAmount, from: investor } + ).should.be.fulfilled; + (await this.token.balanceOf(investor)) + .should.be.bignumber.equal(investmentAmount); + (await this.token.totalSupply()) + .should.be.bignumber.equal(investmentAmount); + }); +}); From b52f61c7a6661e2de479ea078dc49943842523f9 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 18 Apr 2018 19:04:17 +0000 Subject: [PATCH 3/4] add validation on transfer and transferFrom --- contracts/SampleToken.sol | 9 ++ test/contracts/SampleToken.test.js | 129 +++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 test/contracts/SampleToken.test.js diff --git a/contracts/SampleToken.sol b/contracts/SampleToken.sol index 1d9005f..b399af4 100644 --- a/contracts/SampleToken.sol +++ b/contracts/SampleToken.sol @@ -30,4 +30,13 @@ contract SampleToken is MintableToken { return super.mint(_to, _amount); } + function transfer(address _to, uint256 _value) public returns (bool) { + require(jurisdiction.hasAttribute(_to, 'VALID')); + return super.transfer(_to, _value); + } + + function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { + require(jurisdiction.hasAttribute(_to, 'VALID')); + return super.transferFrom(_from, _to, _value); + } } diff --git a/test/contracts/SampleToken.test.js b/test/contracts/SampleToken.test.js new file mode 100644 index 0000000..2bccaf3 --- /dev/null +++ b/test/contracts/SampleToken.test.js @@ -0,0 +1,129 @@ +const Jurisdiction = artifacts.require('Jurisdiction'); +const SampleToken = artifacts.require('SampleToken'); + +const BigNumber = web3.BigNumber; + +var should = require('chai') + .use(require('chai-as-promised')) + .use(require('chai-bignumber')(BigNumber)) + .should(); + +contract('SampleToken', function ([owner, investor1, investor2]) { + + beforeEach(async function () { + this.jurisdiction = await Jurisdiction.new(); + this.jurisdiction.addValidator(owner); + + this.token = await SampleToken.new(this.jurisdiction.address); + await this.token.transferOwnership(owner); + }); + + it('should not accept not validated minting', async function () { + const investmentAmount = new BigNumber(web3.toWei(1, 'ether')); + await this.token.mint( + investor1, investmentAmount, + { from: investor1 } + ).should.be.rejectedWith('revert'); + (await this.token.balanceOf(investor1)) + .should.be.bignumber.equal(0); + (await this.token.totalSupply()) + .should.be.bignumber.equal(0); + }); + + it('should accept validated minting', async function () { + await this.jurisdiction.addAttribute(investor1, 'VALID', 1); + + const investmentAmount = new BigNumber(web3.toWei(1, 'ether')); + await this.token.mint( + investor1, investmentAmount, + { from: owner } + ).should.be.fulfilled; + (await this.token.balanceOf(investor1)) + .should.be.bignumber.equal(investmentAmount); + (await this.token.totalSupply()) + .should.be.bignumber.equal(investmentAmount); + }); + + it('should not accept not validated transfer', async function () { + await this.jurisdiction.addAttribute(investor1, 'VALID', 1); + const investmentAmount = new BigNumber(web3.toWei(1, 'ether')); + await this.token.mint( + investor1, investmentAmount, + { from: owner } + ).should.be.fulfilled; + + await this.token.transfer( + investor2, investmentAmount, + { from: investor1 } + ).should.be.rejectedWith('revert'); + (await this.token.balanceOf(investor1)) + .should.be.bignumber.equal(investmentAmount); + (await this.token.balanceOf(investor2)) + .should.be.bignumber.equal(0); + }); + + it('should accept validated transfer', async function () { + await this.jurisdiction.addAttribute(investor1, 'VALID', 1); + const investmentAmount = new BigNumber(web3.toWei(1, 'ether')); + await this.token.mint( + investor1, investmentAmount, + { from: owner } + ).should.be.fulfilled; + + await this.jurisdiction.addAttribute(investor2, 'VALID', 1); + await this.token.transfer( + investor2, investmentAmount, + { from: investor1 } + ).should.be.fulfilled; + (await this.token.balanceOf(investor1)) + .should.be.bignumber.equal(0); + (await this.token.balanceOf(investor2)) + .should.be.bignumber.equal(investmentAmount); + }); + + it('should not accept not validated transferFrom', async function () { + await this.jurisdiction.addAttribute(investor1, 'VALID', 1); + const investmentAmount = new BigNumber(web3.toWei(1, 'ether')); + await this.token.mint( + investor1, investmentAmount, + { from: owner } + ).should.be.fulfilled; + await this.token.approve( + owner, investmentAmount, + { from: investor1 } + ).should.be.fulfilled; + + await this.token.transferFrom( + investor1, investor2, investmentAmount, + { from: owner } + ).should.be.rejectedWith('revert'); + (await this.token.balanceOf(investor1)) + .should.be.bignumber.equal(investmentAmount); + (await this.token.balanceOf(investor2)) + .should.be.bignumber.equal(0); + }); + + it('should accept validated transferFrom', async function () { + await this.jurisdiction.addAttribute(investor1, 'VALID', 1); + const investmentAmount = new BigNumber(web3.toWei(1, 'ether')); + await this.token.mint( + investor1, investmentAmount, + { from: owner } + ).should.be.fulfilled; + await this.token.approve( + owner, investmentAmount, + { from: investor1 } + ).should.be.fulfilled; + + await this.jurisdiction.addAttribute(investor2, 'VALID', 1); + await this.token.transferFrom( + investor1, investor2, investmentAmount, + { from: owner } + ).should.be.fulfilled; + (await this.token.balanceOf(investor1)) + .should.be.bignumber.equal(0); + (await this.token.balanceOf(investor2)) + .should.be.bignumber.equal(investmentAmount); + }); + +}); From 863afb5e90dde761ab4715bb1774b048ec0bafa4 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 18 Apr 2018 19:05:44 +0000 Subject: [PATCH 4/4] applied the suggestions by frangio --- test/contracts/SampleCrowdsale.test.js | 5 +---- test/contracts/SampleToken.test.js | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/test/contracts/SampleCrowdsale.test.js b/test/contracts/SampleCrowdsale.test.js index bc2891e..825d63f 100644 --- a/test/contracts/SampleCrowdsale.test.js +++ b/test/contracts/SampleCrowdsale.test.js @@ -15,7 +15,7 @@ contract('SampleCrowdsale', function ([owner, wallet, investor]) { beforeEach(async function () { this.jurisdiction = await Jurisdiction.new(); - this.jurisdiction.addValidator(owner); + await this.jurisdiction.addValidator(owner); this.token = await SampleToken.new(this.jurisdiction.address); this.crowdsale = await SampleCrowdsale.new( @@ -24,9 +24,6 @@ contract('SampleCrowdsale', function ([owner, wallet, investor]) { }); it('should create crowdsale with correct parameters', async function () { - this.crowdsale.should.exist; - this.token.should.exist; - (await this.crowdsale.rate()).should.be.bignumber.equal(RATE); (await this.crowdsale.wallet()).should.be.equal(wallet); }); diff --git a/test/contracts/SampleToken.test.js b/test/contracts/SampleToken.test.js index 2bccaf3..af45d96 100644 --- a/test/contracts/SampleToken.test.js +++ b/test/contracts/SampleToken.test.js @@ -12,7 +12,7 @@ contract('SampleToken', function ([owner, investor1, investor2]) { beforeEach(async function () { this.jurisdiction = await Jurisdiction.new(); - this.jurisdiction.addValidator(owner); + await this.jurisdiction.addValidator(owner); this.token = await SampleToken.new(this.jurisdiction.address); await this.token.transferOwnership(owner);