From cd29ed4b9988c88b79cabbe4326b8cba01af6ab6 Mon Sep 17 00:00:00 2001 From: Dmitry Kurinskiy Date: Fri, 1 Dec 2017 21:49:09 +0300 Subject: [PATCH] Fpt release (#1) --- contracts/FakeCertifier.sol | 13 ++ contracts/FluencePreRelease.sol | 111 +++++++++++++++ contracts/FluenceToken.sol | 17 +++ contracts/Haltable.sol | 37 +++++ contracts/MintTokenProxy.sol | 16 +++ contracts/Mintable.sol | 55 ++++++++ contracts/RenameableSymbol.sol | 21 +++ migrations/3_minting.js | 7 + migrations/4_releasing.js | 12 ++ package-lock.json | 239 ++++++++++++++++++-------------- package.json | 10 +- test/mintable.js | 146 +++++++++++++++++++ test/{fluence.js => preSale.js} | 0 test/token.js | 32 +++++ truffle.js | 5 + 15 files changed, 611 insertions(+), 110 deletions(-) create mode 100644 contracts/FakeCertifier.sol create mode 100644 contracts/FluencePreRelease.sol create mode 100644 contracts/FluenceToken.sol create mode 100644 contracts/Haltable.sol create mode 100644 contracts/MintTokenProxy.sol create mode 100644 contracts/Mintable.sol create mode 100644 contracts/RenameableSymbol.sol create mode 100644 migrations/3_minting.js create mode 100644 migrations/4_releasing.js create mode 100644 test/mintable.js rename test/{fluence.js => preSale.js} (100%) create mode 100644 test/token.js diff --git a/contracts/FakeCertifier.sol b/contracts/FakeCertifier.sol new file mode 100644 index 0000000..eeb72b1 --- /dev/null +++ b/contracts/FakeCertifier.sol @@ -0,0 +1,13 @@ +pragma solidity ^0.4.18; + + +contract FakeCertifier { + function FakeCertifier(){ + + } + + function certified(address _who) constant returns (bool) { + return true; + } + +} diff --git a/contracts/FluencePreRelease.sol b/contracts/FluencePreRelease.sol new file mode 100644 index 0000000..e07ad03 --- /dev/null +++ b/contracts/FluencePreRelease.sol @@ -0,0 +1,111 @@ +pragma solidity ^0.4.18; + + +import 'zeppelin-solidity/contracts/math/SafeMath.sol'; +import 'zeppelin-solidity/contracts/token/ERC20Basic.sol'; +import 'zeppelin-solidity/contracts/token/StandardToken.sol'; +import 'zeppelin-solidity/contracts/ownership/Ownable.sol'; + + +interface Certifier { + function certified(address _who) constant returns (bool); +} + + +contract FluencePreRelease is Ownable { + event Released(address indexed caller, address indexed _to, uint256 amount); + + using SafeMath for uint256; + + mapping (address => uint256) public released; + + address public certifier; + + address public preSale; + + address public token; + + bool public launched; + + address public spender; + + modifier beforeLaunch { + require(!launched); + _; + } + + modifier afterLaunch { + require(launched); + _; + } + + function FluencePreRelease(address _certifier, address _preSale, address _token, address _spender) { + require(_certifier != address(0x0)); + require(_preSale != address(0x0)); + require(_token != address(0x0)); + + certifier = _certifier; + preSale = _preSale; + token = _token; + + if (_spender == address(0x0)) { + spender = msg.sender; + } + else { + spender = _spender; + } + } + + // Can preset only before releasing is launched + function presetReleased(address _to, uint256 amount) onlyOwner beforeLaunch public { + released[_to] = amount; + } + + // After launch, owner can't do anything with the contract + function launch() onlyOwner beforeLaunch public { + launched = true; + } + + function release(address _holder) public afterLaunch returns (uint256 amount) { + address beneficiary = _holder; + if (beneficiary == address(0x0)) beneficiary = msg.sender; + // check if verified + require(Certifier(certifier).certified(beneficiary)); + + address source = msg.sender; + // check fpt balance + // subtract $released + amount = ERC20Basic(preSale).balanceOf(source).sub(released[source]); + require(amount > 0); + + // release tokens + released[source] = released[source].add(amount); + assert(released[source] == ERC20Basic(preSale).balanceOf(source)); + + assert(StandardToken(token).transferFrom(spender, beneficiary, amount)); + Released(source, beneficiary, amount); + } + + function bytesToAddress(bytes _address) internal returns (address) { + uint160 m = 0; + uint160 b = 0; + + for (uint8 i = 0; i < 20; i++) { + m *= 256; + b = uint160(_address[i]); + m += (b); + } + + return address(m); + } + + function() public { + if (msg.data.length == 20) { + release(bytesToAddress(msg.data)); + } + else { + release(msg.sender); + } + } + +} \ No newline at end of file diff --git a/contracts/FluenceToken.sol b/contracts/FluenceToken.sol new file mode 100644 index 0000000..1e902ff --- /dev/null +++ b/contracts/FluenceToken.sol @@ -0,0 +1,17 @@ +pragma solidity ^0.4.18; + + +import './RenameableSymbol.sol'; +import 'zeppelin-solidity/contracts/token/BurnableToken.sol'; + + +contract FluenceToken is BurnableToken, RenameableSymbol("FPT(U)", "Fluence Presale Token (Unlocked)") { + + function FluenceToken(uint256 allocation) { + totalSupply = allocation; + balances[owner] = allocation; + } + + uint public constant decimals = 18; + +} diff --git a/contracts/Haltable.sol b/contracts/Haltable.sol new file mode 100644 index 0000000..40fb3fb --- /dev/null +++ b/contracts/Haltable.sol @@ -0,0 +1,37 @@ +pragma solidity ^0.4.18; + +import 'zeppelin-solidity/contracts/ownership/Ownable.sol'; + +/* + * Haltable + * + * Abstract contract that allows children to implement an + * emergency stop mechanism. Differs from Pausable by causing a throw when in halt mode. + * + * + * Originally envisioned in FirstBlood ICO contract. + */ +contract Haltable is Ownable { + bool public halted; + + modifier stopInEmergency { + require(!halted); + _; + } + + modifier onlyInEmergency { + require(halted); + _; + } + + // called by the owner on emergency, triggers stopped state + function halt() external onlyOwner { + halted = true; + } + + // called by the owner on end of emergency, returns to normal state + function unhalt() external onlyOwner onlyInEmergency { + halted = false; + } + +} \ No newline at end of file diff --git a/contracts/MintTokenProxy.sol b/contracts/MintTokenProxy.sol new file mode 100644 index 0000000..db9fd2f --- /dev/null +++ b/contracts/MintTokenProxy.sol @@ -0,0 +1,16 @@ +pragma solidity ^0.4.18; + +import './Mintable.sol'; + +// Used only for testing +contract MintTokenProxy { + Mintable token; + + function setToken(address _token) public { + token = Mintable(_token); + } + + function mint(address _to, uint256 _amount) public returns(bool) { + return token.mint(_to, _amount); + } +} diff --git a/contracts/Mintable.sol b/contracts/Mintable.sol new file mode 100644 index 0000000..f78d8ff --- /dev/null +++ b/contracts/Mintable.sol @@ -0,0 +1,55 @@ +pragma solidity ^0.4.18; + +import 'zeppelin-solidity/contracts/token/StandardToken.sol'; +import 'zeppelin-solidity/contracts/ownership/Ownable.sol'; + +contract Mintable is StandardToken, Ownable { + + event Mint(address indexed to, uint256 amount); + event MintFinished(); + + bool public mintingFinished = false; + mapping (address => bool) private minters; + + modifier canMint() { + require(!mintingFinished); + _; + } + + modifier onlyMinter() { + require(minters[msg.sender]); + _; + } + + function allowMinting(address _to) onlyOwner canMint public { + minters[_to] = true; + } + + function denyMinting(address _to) onlyOwner public { + delete minters[_to]; + } + + /** + * @dev Function to mint tokens + * @param _to The address that will receive the minted tokens. + * @param _amount The amount of tokens to mint. + * @return A boolean that indicates if the operation was successful. + */ + function mint(address _to, uint256 _amount) onlyMinter canMint public returns (bool) { + totalSupply = totalSupply.add(_amount); + balances[_to] = balances[_to].add(_amount); + Mint(_to, _amount); + Transfer(0x0, _to, _amount); + return true; + } + + /** + * @dev Function to stop minting new tokens. + * @return True if the operation was successful. + */ + function finishMinting() onlyOwner canMint public returns (bool) { + mintingFinished = true; + MintFinished(); + return true; + } +} diff --git a/contracts/RenameableSymbol.sol b/contracts/RenameableSymbol.sol new file mode 100644 index 0000000..d867744 --- /dev/null +++ b/contracts/RenameableSymbol.sol @@ -0,0 +1,21 @@ +pragma solidity ^0.4.18; + + +import 'zeppelin-solidity/contracts/ownership/Ownable.sol'; + + +contract RenameableSymbol is Ownable { + string public name; + + string public symbol; + + function RenameableSymbol(string _symbol, string _name) public { + name = _name; + symbol = _symbol; + } + + function rename(string _symbol, string _name) onlyOwner public { + name = _name; + symbol = _symbol; + } +} diff --git a/migrations/3_minting.js b/migrations/3_minting.js new file mode 100644 index 0000000..9b9a3a2 --- /dev/null +++ b/migrations/3_minting.js @@ -0,0 +1,7 @@ +var Mintable = artifacts.require("./Mintable.sol"); +var MintTokenProxy = artifacts.require("./MintTokenProxy.sol"); + +module.exports = function(deployer) { + deployer.deploy(Mintable); + deployer.deploy(MintTokenProxy); +}; diff --git a/migrations/4_releasing.js b/migrations/4_releasing.js new file mode 100644 index 0000000..63cd701 --- /dev/null +++ b/migrations/4_releasing.js @@ -0,0 +1,12 @@ +var FluenceToken = artifacts.require("./FluenceToken.sol"); +var FluencePreSale = artifacts.require("./FluencePreSale.sol"); +var FluencePreRelease = artifacts.require("./FluencePreRelease.sol"); +var FakeCertifier = artifacts.require("./FakeCertifier.sol"); + +module.exports = function (deployer) { + deployer.deploy(FluenceToken, 2*10**(18+7)).then(() => { + deployer.deploy(FakeCertifier).then(() => { + deployer.deploy(FluencePreRelease, FakeCertifier.address, FluencePreSale.address, FluenceToken.address); + }) + }); +}; diff --git a/package-lock.json b/package-lock.json index 60cf46d..0d90502 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,9 +16,9 @@ "dev": true }, "babel-code-frame": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", - "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ=", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { "chalk": "1.1.3", @@ -27,45 +27,45 @@ } }, "babel-core": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.25.0.tgz", - "integrity": "sha1-fdQrBGPHQunVKW3rPsZ6kyLa1yk=", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", + "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", "dev": true, "requires": { - "babel-code-frame": "6.22.0", - "babel-generator": "6.25.0", + "babel-code-frame": "6.26.0", + "babel-generator": "6.26.0", "babel-helpers": "6.24.1", "babel-messages": "6.23.0", - "babel-register": "6.24.1", - "babel-runtime": "6.25.0", - "babel-template": "6.25.0", - "babel-traverse": "6.25.0", - "babel-types": "6.25.0", - "babylon": "6.17.4", + "babel-register": "6.26.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", "convert-source-map": "1.5.0", - "debug": "2.6.8", + "debug": "2.6.9", "json5": "0.5.1", "lodash": "4.17.4", "minimatch": "3.0.4", "path-is-absolute": "1.0.1", "private": "0.1.7", "slash": "1.0.0", - "source-map": "0.5.6" + "source-map": "0.5.7" } }, "babel-generator": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.25.0.tgz", - "integrity": "sha1-M6GvcNXyiQrrRlpKd5PB32qeqfw=", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz", + "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=", "dev": true, "requires": { "babel-messages": "6.23.0", - "babel-runtime": "6.25.0", - "babel-types": "6.25.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", "detect-indent": "4.0.0", "jsesc": "1.3.0", "lodash": "4.17.4", - "source-map": "0.5.6", + "source-map": "0.5.7", "trim-right": "1.0.1" } }, @@ -75,8 +75,8 @@ "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", "dev": true, "requires": { - "babel-runtime": "6.25.0", - "babel-template": "6.25.0" + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" } }, "babel-messages": { @@ -85,89 +85,96 @@ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, "requires": { - "babel-runtime": "6.25.0" + "babel-runtime": "6.26.0" } }, "babel-polyfill": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.23.0.tgz", - "integrity": "sha1-g2TKYt+Or7gwSZ9pkXdGbDsDSZ0=", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", + "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", "requires": { - "babel-runtime": "6.25.0", - "core-js": "2.5.0", + "babel-runtime": "6.26.0", + "core-js": "2.5.1", "regenerator-runtime": "0.10.5" } }, "babel-register": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.24.1.tgz", - "integrity": "sha1-fhDhOi9xBlvfrVoXh7pFvKbe118=", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", "dev": true, "requires": { - "babel-core": "6.25.0", - "babel-runtime": "6.25.0", - "core-js": "2.5.0", + "babel-core": "6.26.0", + "babel-runtime": "6.26.0", + "core-js": "2.5.1", "home-or-tmp": "2.0.0", "lodash": "4.17.4", "mkdirp": "0.5.1", - "source-map-support": "0.4.15" + "source-map-support": "0.4.18" } }, "babel-runtime": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.25.0.tgz", - "integrity": "sha1-M7mOql1IK7AajRqmtDetKwGuxBw=", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "requires": { - "core-js": "2.5.0", - "regenerator-runtime": "0.10.5" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", + "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==" + } } }, "babel-template": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.25.0.tgz", - "integrity": "sha1-ZlJBFmt8KqTGGdceGSlpVSsQwHE=", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "dev": true, "requires": { - "babel-runtime": "6.25.0", - "babel-traverse": "6.25.0", - "babel-types": "6.25.0", - "babylon": "6.17.4", + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", "lodash": "4.17.4" } }, "babel-traverse": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.25.0.tgz", - "integrity": "sha1-IldJfi/NGbie3BPEyROB+VEklvE=", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "dev": true, "requires": { - "babel-code-frame": "6.22.0", + "babel-code-frame": "6.26.0", "babel-messages": "6.23.0", - "babel-runtime": "6.25.0", - "babel-types": "6.25.0", - "babylon": "6.17.4", - "debug": "2.6.8", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "debug": "2.6.9", "globals": "9.18.0", "invariant": "2.2.2", "lodash": "4.17.4" } }, "babel-types": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.25.0.tgz", - "integrity": "sha1-cK+ySNVmDl0Y+BHZHIMDtUE0oY4=", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, "requires": { - "babel-runtime": "6.25.0", + "babel-runtime": "6.26.0", "esutils": "2.0.2", "lodash": "4.17.4", "to-fast-properties": "1.0.3" } }, "babylon": { - "version": "6.17.4", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz", - "integrity": "sha512-kChlV+0SXkjE0vUn9OZ7pBMWRFd8uq3mZe8x1K6jhuNcAFAtEnjchFAqB+dYEXKyd+JpT6eppRR78QAr5gTsUw==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", "dev": true }, "balanced-match": { @@ -210,14 +217,6 @@ "has-ansi": "2.0.0", "strip-ansi": "3.0.1", "supports-color": "2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } } }, "cliui": { @@ -255,14 +254,15 @@ "dev": true }, "core-js": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.0.tgz", - "integrity": "sha1-VpwFCRi+ZIazg3VSAorgRmtxcIY=" + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", + "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=" }, "debug": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, "requires": { "ms": "2.0.0" } @@ -323,7 +323,7 @@ "jsonfile": "2.4.0", "klaw": "1.3.1", "path-is-absolute": "1.0.1", - "rimraf": "2.6.1" + "rimraf": "2.6.2" } }, "fs.realpath": { @@ -384,6 +384,11 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" + }, "home-or-tmp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", @@ -627,9 +632,9 @@ } }, "mocha": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.0.tgz", - "integrity": "sha512-pIU2PJjrPYvYRqVpjXzj76qltO9uBYI7woYAMoxbSefsa+vqAfptjoeevd6bUgwD0mPIO+hv9f7ltvsNreL2PA==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz", + "integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==", "requires": { "browser-stdout": "1.3.0", "commander": "2.9.0", @@ -638,10 +643,29 @@ "escape-string-regexp": "1.0.5", "glob": "7.1.1", "growl": "1.9.2", + "he": "1.1.1", "json3": "3.3.2", "lodash.create": "3.1.1", "mkdirp": "0.5.1", "supports-color": "3.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } + }, + "supports-color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", + "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", + "requires": { + "has-flag": "1.0.0" + } + } } }, "ms": { @@ -802,9 +826,9 @@ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" }, "rimraf": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", - "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "requires": { "glob": "7.1.1" } @@ -826,9 +850,9 @@ "dev": true }, "solc": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/solc/-/solc-0.4.13.tgz", - "integrity": "sha1-qly9zOPmrjwZDSD1/fi8iAcC7HU=", + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.4.18.tgz", + "integrity": "sha512-Kq+O3PNF9Pfq7fB+lDYAuoqRdghLmZyfngsg0h1Hj38NKAeVHeGPOGeZasn5KqdPeCzbMFvaGyTySxzGv6aXCg==", "requires": { "fs-extra": "0.30.0", "memorystream": "0.3.1", @@ -838,18 +862,18 @@ } }, "source-map": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", - "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, "source-map-support": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz", - "integrity": "sha1-AyAt9lwG0r2MfsI2KhkwVv7407E=", + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "dev": true, "requires": { - "source-map": "0.5.6" + "source-map": "0.5.7" } }, "spdx-correct": { @@ -897,12 +921,10 @@ } }, "supports-color": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", - "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", - "requires": { - "has-flag": "1.0.0" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true }, "to-fast-properties": { "version": "1.0.3", @@ -917,13 +939,13 @@ "dev": true }, "truffle": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/truffle/-/truffle-3.4.7.tgz", - "integrity": "sha512-sAc1dixGDpt38Qq9gqEEIFpXXIIaNKViWlojD9Xv4NBqK/03lxb43M7gcILM4gMpgMdwtTAunG4ZQe7z+HYvJQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/truffle/-/truffle-4.0.1.tgz", + "integrity": "sha512-PybO+GMq3AvsfCWfEx4sbuaJlDL19iR8Ff20cO0TtP599N5JbMLlhwlffvVInPgFjP+F11vjSOYj3hT8fONs5A==", "requires": { - "mocha": "3.5.0", + "mocha": "3.5.3", "original-require": "1.0.1", - "solc": "0.4.13" + "solc": "0.4.18" } }, "validate-npm-package-license": { @@ -993,6 +1015,11 @@ "camelcase": "3.0.0", "lodash.assign": "4.2.0" } + }, + "zeppelin-solidity": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/zeppelin-solidity/-/zeppelin-solidity-1.3.0.tgz", + "integrity": "sha512-q+WyqvQjE5gpKgXrhD8LBh0Njg7GI/zs4lKX14y0j7ISJIxe68hihSjaMU05V0M0qAbxgRC43AFTt7CxNsO8nQ==" } } } diff --git a/package.json b/package.json index cd301ad..04bbaa8 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,15 @@ "version": "0.0.0", "private": true, "scripts": { - "run": "truffle build" + "build": "truffle build", + "dev": "truffle develop" }, "dependencies": { - "babel-polyfill": "^6.23.0", - "truffle": "^3.4.7" + "babel-polyfill": "^6.26.0", + "truffle": "^4.0.1", + "zeppelin-solidity": "^1.3.0" }, "devDependencies": { - "babel-register": "^6.24.1" + "babel-register": "^6.26.0" } } diff --git a/test/mintable.js b/test/mintable.js new file mode 100644 index 0000000..83a511c --- /dev/null +++ b/test/mintable.js @@ -0,0 +1,146 @@ +const MintTokenProxy = artifacts.require("./MintTokenProxy.sol"); +const Mintable = artifacts.require("./Mintable.sol"); + +contract('FluenceToken', function (accounts) { + + + it("should allow/deny minting to owner", async function() { + const token = await Mintable.deployed(); + + // Initially, there's no supply + const s = await token.totalSupply() + assert.equal(s, 0) + + // Try to mint by owner + try { + await token.mint(accounts[1], 1000) + } catch(e) { + } + // Try to allow minting by wrong account + try { + await token.allowMinting(accounts[3], {from: accounts[1]}) + } catch(e) { + } + // Try to mint by another account + try { + await token.mint(accounts[1], 1000, {from: accounts[3]}) + } catch(e) { + } + + // Total supply and balance should be intact + const s1 = await token.totalSupply() + assert.equal(s1, 0) + + const b0 = await token.balanceOf(accounts[1]) + assert.equal(b0, 0) + + // Allow minting for one account + await token.allowMinting(accounts[3]) + + // Try mint from wrong account + try { + await token.mint(accounts[1], 1000) + } catch(e) { + } + + // Balances should be intact + const s2 = await token.totalSupply() + assert.equal(s2, 0) + + const b1 = await token.balanceOf(accounts[1]) + assert.equal(b1, 0) + + // Try to mint from wrong (non-owner) account + try { + await token.mint(accounts[1], 1000, {from: accounts[1]}) + } catch(e) { + } + + // Balances should be intact + const s3 = await token.totalSupply() + assert.equal(s3.valueOf(), 0) + + const b2 = await token.balanceOf(accounts[1]) + assert.equal(b2.valueOf(), 0) + + // Try to mint from correct account + await token.mint(accounts[1], 1000, {from: accounts[3]}) + + // Supply should be changed + const s4 = await token.totalSupply() + assert.equal(s4.valueOf(), 1000) + + const b3 = await token.balanceOf(accounts[1]) + assert.equal(b3.valueOf(), 1000) + + // Deny minting by wrong account + try { + await token.denyMinting(acounts[3], {from: accounts[3]}) + } catch(e){} + + await token.mint(accounts[1], 1000, {from: accounts[3]}) + const s5 = await token.totalSupply() + assert.equal(s5.valueOf(), 2000) + + // Deny minting + await token.denyMinting(accounts[3]) + try { + await token.mint(accounts[1], 1000, {from: accounts[3]}) + } catch(e){} + const s6 = await token.totalSupply() + assert.equal(s6.valueOf(), 2000) + }) + + it("should mint correctly via proxy", async function(){ + const token = await Mintable.deployed(); + const proxy = await MintTokenProxy.deployed(); + await proxy.setToken(token.address) + + await token.allowMinting(proxy.address) + + const s0 = await token.totalSupply() + assert.equal(s0.valueOf(), 2000) + + try { + await token.mint(accounts[0], 1000) + } catch(e){} + + const s1 = await token.totalSupply() + assert.equal(s1.valueOf(), 2000) + + await proxy.mint(accounts[4], 1000) + + const s2 = await token.totalSupply() + assert.equal(s2.valueOf(), 3000) + + const b0 = await token.balanceOf(accounts[4]) + assert.equal(b0.valueOf(), 1000) + + await token.denyMinting(proxy.address) + try { + await proxy.mint(accounts[4], 1000) + } catch(e){} + + const s3 = await token.totalSupply() + assert.equal(s3.valueOf(), 3000) + + }) + + it("should deny minting after all", async function(){ + const token = await Mintable.deployed(); + + const s3 = await token.totalSupply() + assert.equal(s3.valueOf(), 3000) + + // Allow minting again + await token.allowMinting(accounts[1]) + // Finish minting + await token.finishMinting() + // Can't mint anymore + try { + await token.mint(accounts[1], 1000, {from: accounts[3]}) + } catch(e){} + const s7 = await token.totalSupply() + assert.equal(s7.valueOf(), 3000) + }) +}); \ No newline at end of file diff --git a/test/fluence.js b/test/preSale.js similarity index 100% rename from test/fluence.js rename to test/preSale.js diff --git a/test/token.js b/test/token.js new file mode 100644 index 0000000..0f16e6e --- /dev/null +++ b/test/token.js @@ -0,0 +1,32 @@ +const FluenceToken = artifacts.require("./FluenceToken.sol"); + +contract('FluenceToken', function (accounts) { + it("should be renameable", async function () { + const token = await FluenceToken.deployed(); + + const s0 = await token.symbol() + const n0 = await token.name() + + assert.equal(s0, "FPT(U)") + assert.equal(n0, "Fluence Presale Token (Unlocked)") + + await token.rename("newSymbol", "newName") + + const s1 = await token.symbol() + const n1 = await token.name() + + assert.equal(s1, "newSymbol") + assert.equal(n1, "newName") + + try { + await token.rename("newsNam22e", "newnSymb22ol", {from: accounts[1]}) + } catch (e) { + } + + const s2 = await token.symbol() + const n2 = await token.name() + + assert.equal(s2, "newSymbol") + assert.equal(n2, "newName") + }) +}); \ No newline at end of file diff --git a/truffle.js b/truffle.js index b38292c..2909e15 100644 --- a/truffle.js +++ b/truffle.js @@ -7,6 +7,11 @@ module.exports = { host: "localhost", port: 8545, network_id: "*" // Match any network id + }, + kovan: { + host: 'localhost', + port: 8547, + network_id: '42' } } };