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..b399af4 --- /dev/null +++ b/contracts/SampleToken.sol @@ -0,0 +1,42 @@ +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); + } + + 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/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-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 abb98b2..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", @@ -31,7 +33,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/* --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/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/contracts/SampleCrowdsale.test.js b/test/contracts/SampleCrowdsale.test.js new file mode 100644 index 0000000..825d63f --- /dev/null +++ b/test/contracts/SampleCrowdsale.test.js @@ -0,0 +1,56 @@ +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(); + await 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 () { + (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); + }); +}); diff --git a/test/contracts/SampleToken.test.js b/test/contracts/SampleToken.test.js new file mode 100644 index 0000000..af45d96 --- /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(); + await 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); + }); + +}); 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');