diff --git a/future-apps/fundraising/.gitignore b/future-apps/fundraising/.gitignore deleted file mode 100644 index e3fbd98336..0000000000 --- a/future-apps/fundraising/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -build -node_modules diff --git a/future-apps/fundraising/.solcover.js b/future-apps/fundraising/.solcover.js deleted file mode 100644 index 2169f75895..0000000000 --- a/future-apps/fundraising/.solcover.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - norpc: true, - // rsync is needed so symlinks are resolved on copy of lerna packages - testCommand: 'rsync --copy-links -r ../node_modules/@aragon node_modules && node --max-old-space-size=4096 ../node_modules/.bin/truffle test --network coverage', - copyNodeModules: true, -} diff --git a/future-apps/fundraising/.soliumignore b/future-apps/fundraising/.soliumignore deleted file mode 100644 index 8b13789179..0000000000 --- a/future-apps/fundraising/.soliumignore +++ /dev/null @@ -1 +0,0 @@ - diff --git a/future-apps/fundraising/.soliumrc.json b/future-apps/fundraising/.soliumrc.json deleted file mode 100644 index f69c6709a6..0000000000 --- a/future-apps/fundraising/.soliumrc.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "extends": "solium:all", - "rules": { - "imports-on-top": ["error"], - "variable-declarations": ["error"], - "array-declarations": ["error"], - "operator-whitespace": ["error"], - "lbrace": ["error"], - "mixedcase": 0, - "camelcase": ["error"], - "uppercase": 0, - "no-empty-blocks": ["error"], - "no-unused-vars": ["error"], - "quotes": ["error"], - "indentation": 0, - "whitespace": ["error"], - "deprecated-suicide": ["error"], - "arg-overflow": ["error", 8], - "pragma-on-top": ["error"], - "security/enforce-explicit-visibility": ["error"] - } -} diff --git a/future-apps/fundraising/app/index.html b/future-apps/fundraising/app/index.html deleted file mode 100644 index b04d74d430..0000000000 --- a/future-apps/fundraising/app/index.html +++ /dev/null @@ -1 +0,0 @@ -

Fundraising

diff --git a/future-apps/fundraising/contracts/Fundraising.sol b/future-apps/fundraising/contracts/Fundraising.sol deleted file mode 100644 index 5e9d8fe364..0000000000 --- a/future-apps/fundraising/contracts/Fundraising.sol +++ /dev/null @@ -1,353 +0,0 @@ -pragma solidity 0.4.18; - -import "@aragon/os/contracts/apps/AragonApp.sol"; -import "@aragon/os/contracts/common/Initializable.sol"; - -import "@aragon/apps-token-manager/contracts/TokenManager.sol"; - -import "@aragon/os/contracts/lib/zeppelin/token/ERC20.sol"; -import "@aragon/os/contracts/lib/zeppelin/math/SafeMath.sol"; -import "@aragon/os/contracts/lib/zeppelin/math/Math.sol"; - -import "@aragon/os/contracts/lib/misc/Migrations.sol"; - - -contract Fundraising is AragonApp { - using SafeMath for uint256; - - uint256 constant MAX_PERIODS = 50; - uint64 constant MAX_UINT64 = uint64(-1); - - bytes32 constant public CREATE_SALES_ROLE = bytes32(1); - bytes32 constant public CLOSE_SALES_ROLE = bytes32(2); - - struct SalePeriod { - uint64 periodEnds; - uint256 initialPrice; - uint256 finalPrice; - } - - struct Sale { - bool closed; - - address investor; // if set to 0 is public sale - ERC20 raisedToken; - - uint256 maxRaised; - uint256 maxSold; - uint256 minBuy; - bool isInversePrice; - - uint64 saleStartTime; - uint64 periodStartTime; - SalePeriod[] periods; - uint256 currentPeriod; - - uint256 raisedAmount; - uint256 soldAmount; - } - - TokenManager public tokenManager; - address public vault; - - Sale[] sales; - - event NewSale(uint256 indexed saleId); - event CloseSale(uint256 indexed saleId); - event BuyTokens(uint256 indexed saleId, address indexed buyer, uint256 tokensPayed, uint256 tokensBought); - - /** - * @param _tokenManager Reference to the token manager that will mint tokens on sales (Requires mint permission!) - * @param _vault Address that will receive funds raised in sale - */ - function initialize(TokenManager _tokenManager, address _vault) onlyInit external { - initialized(); - - tokenManager = _tokenManager; - vault = _vault; - } - - /** - * @notice Create token sale (TODO: Figure out how to explain better) - * @param _investor Address allowed to buy in the sale. If set to 0x00.., anyone can buy - * @param _raisedToken Address of the token being raised in the sale - * @param _maxRaised Maximum amount of tokens raised - * @param _maxSold Maximum amount of tokens that can be sold - * @param _minBuy Minimum amount of raisedTokens that can have to be payed in a sale - * @param _isInversePrice How pricing works. If inverse is set to true price will be used as a multiplicator, if set to false as a divisor - * @param _periodStartTime Initial timestamp when sale starts - * @param _periodEnds Array of timestamps when each period of the sale ends - * @param _prices Array of prices for sale. For each period two prices are provided (initial and finalPrice). If different, price is linearly interpolated. - */ - function newSale( - address _investor, - ERC20 _raisedToken, - uint256 _maxRaised, - uint256 _maxSold, - uint256 _minBuy, - bool _isInversePrice, - uint64 _periodStartTime, - uint64[] _periodEnds, - uint256[] _prices - ) auth(CREATE_SALES_ROLE) external returns (uint256 saleId) - { - // Dont allow token multiplication sales - require(address(_raisedToken) != 0 && _raisedToken != ERC20(tokenManager.token())); - - saleId = sales.length++; - Sale storage sale = sales[saleId]; - - sale.investor = _investor; - sale.raisedToken = _raisedToken; - sale.maxRaised = _maxRaised; - sale.maxSold = _maxSold; - sale.minBuy = _minBuy; - sale.isInversePrice = _isInversePrice; - - sale.saleStartTime = _periodStartTime; - sale.periodStartTime = _periodStartTime; - - require(_periodEnds.length > 0 && _periodEnds.length <= MAX_PERIODS); - require(_periodEnds.length * 2 == _prices.length); - - for (uint i = 0; i < _periodEnds.length; i++) { - uint256 periodStarted = i == 0 ? sale.saleStartTime : sale.periods[i - 1].periodEnds; - require(_periodEnds[i] > periodStarted); // periods must last at least 1 second - require(_prices[2 * i] > 0 && _prices[2 * i + 1] > 0); // price being 0 could break future calculations - - sale.periods.push(SalePeriod(_periodEnds[i], _prices[2 * i], _prices[2 * i + 1])); - } - - NewSale(saleId); - } - - /** - * @dev ERC20 approve and then call buy in support - * @notice Buy in sale with id `_saleId` with `_payedTokens` tokens - * @param _saleId Sale numeric identifier - * @param _payedTokens Amount of tokens payed (must have a preauthorized allowance) - */ - function transferAndBuy(uint256 _saleId, uint256 _payedTokens) external { - ERC20 raisedToken = sales[_saleId].raisedToken; - - // Buying is attempted before transfering tokens, but if transfer fails it will revert the entire tx - uint256 returnTokens = _buy(_saleId, msg.sender, _payedTokens); - - // No need to return tokens as we never take them from sender's balance - require(raisedToken.transferFrom(msg.sender, vault, _payedTokens.sub(returnTokens))); - } - - /** - * @dev ERC677 buy in support. Data must be equivalent to a buy(uint256) call - */ - /* TODO: adapt to ERC777 - function tokenFallback(address _sender, uint256 _value, bytes _data) external returns (bool ok) { - var (sig, saleId) = parseBuyData(_data); - require(sig == bytes4(sha3("buyWithToken(uint256)"))); // TODO: Replace for .request with solc 0.4.17 - - ERC20 raisedToken = sales[saleId].raisedToken; - require(msg.sender == address(raisedToken)); - - uint256 returnTokens = _buy(saleId, _sender, _value); - - require(raisedToken.transfer(vault, _value.sub(returnTokens))); - if (returnTokens > 0) - require(raisedToken.transfer(_sender, returnTokens)); - - return true; - } - */ - - /** - * @dev Dummy function for ERC677 entrypoint. Call is handled on token fallback but must have this function's format - * @notice Buy in sale with id `_saleId` - * @param _saleId Sale numeric identifier - */ - /* TODO: adapt to ERC777 - function buyWithToken(uint256 _saleId) external { - _saleId; - revert(); - } - */ - - /** - * @notice Force the close of sale with id `_saleId` (It will always succeed if sale is open) - * @param _saleId Sale numeric identifier - */ - function forceCloseSale(uint256 _saleId) auth(CLOSE_SALES_ROLE) external { - _closeSale(_saleId); - } - - /** - * @notice Close finished sale - * @param _saleId Sale numeric identifier - */ - function closeSale(uint256 _saleId) external { - Sale storage sale = sales[_saleId]; - transitionSalePeriodIfNeeded(sale); - - require(sale.periodStartTime == MAX_UINT64); - _closeSale(_saleId); - } - - function getSale(uint256 _saleId) public view returns (bool closed, address investor, address raisedToken, uint256 maxRaised, uint256 maxSold, uint256 minBuy, bool isInversePrice, uint64 saleStartTime, uint256 periodsCount, uint256 currentPeriod, uint256 raisedAmount, uint256 soldAmount) { - Sale storage sale = sales[_saleId]; - - closed = sale.closed; - investor = sale.investor; - raisedToken = sale.raisedToken; - maxRaised = sale.maxRaised; - maxSold = sale.maxSold; - minBuy = sale.minBuy; - saleStartTime = sale.saleStartTime; - isInversePrice = sale.isInversePrice; - periodsCount = sale.periods.length; - currentPeriod = sale.currentPeriod; - raisedAmount = sale.raisedAmount; - soldAmount = sale.soldAmount; - } - - function getPeriod(uint256 _saleId, uint256 _salePeriod) public view returns (uint64 periodStarts, uint64 periodEnds, uint256 initialPrice, uint256 finalPrice) { - Sale storage sale = sales[_saleId]; - SalePeriod storage period = sale.periods[_salePeriod]; - - periodStarts = _salePeriod == 0 ? sale.saleStartTime : sale.periods[_salePeriod - 1].periodEnds; - periodEnds = period.periodEnds; - initialPrice = period.initialPrice; - finalPrice = period.finalPrice; - } - - /** - * @param _saleId Sale numeric identifier - * @return price Current price - * @return isInversePrice Whether price affects with multiplication or division - * @return pricePrecision Factor by which price has been multiplied for precision - */ - function getCurrentPrice(uint256 _saleId) public view returns (uint256 price, bool isInversePrice, uint256 pricePrecision) { - transitionSalePeriodIfNeeded(sales[_saleId]); // if done with 'sendTransaction' this function can modify state - return calculatePrice(_saleId); - } - - function _buy(uint256 _saleId, address _buyer, uint256 _payedTokens) internal returns (uint256 returnTokens) { - Sale storage sale = sales[_saleId]; - - transitionSalePeriodIfNeeded(sale); - - require(sale.investor == 0 || sale.investor == _buyer); - require(_payedTokens >= sale.minBuy); - require(getTimestamp() >= sale.periodStartTime); - require(!sale.closed); - - // Only allow until max raised cap is hit - uint256 allowedAmount = Math.min256(_payedTokens, sale.maxRaised.sub(sale.raisedAmount)); - - var (price,,pricePrecision) = calculatePrice(_saleId); - - uint256 boughtTokens; - if (sale.isInversePrice) { - boughtTokens = allowedAmount.mul(price).div(pricePrecision); - } else { - boughtTokens = allowedAmount.mul(pricePrecision).div(price); - } - - // Only allow until max sold cap is hit - uint256 allowedBuy = Math.min256(boughtTokens, sale.maxSold.sub(sale.soldAmount)); - - // Calculate how much we need to refund for the tokens that weren't sold - uint256 nonBoughtTokens = boughtTokens.sub(allowedBuy); - - uint256 returnAmount; - if (!sale.isInversePrice) { - returnAmount = nonBoughtTokens.mul(price).div(pricePrecision); - } else { - returnAmount = nonBoughtTokens.mul(pricePrecision).div(price); - } - - uint256 finalAllowedAmount = allowedAmount.sub(returnAmount); - - sale.soldAmount = sale.soldAmount.add(allowedBuy); - sale.raisedAmount = sale.raisedAmount.add(finalAllowedAmount); - - if (sale.soldAmount == sale.maxSold || sale.raisedAmount == sale.maxRaised) - _closeSale(_saleId); - - tokenManager.mint(_buyer, allowedBuy); // Do actual minting of tokens for buyer - - BuyTokens( - _saleId, - _buyer, - finalAllowedAmount, - allowedBuy - ); - - return _payedTokens.sub(finalAllowedAmount); // how many tokens must be returned to buyer as they weren't allowed in - } - - function _closeSale(uint256 _saleId) internal { - require(!sales[_saleId].closed); - sales[_saleId].closed = true; - CloseSale(_saleId); - } - - function calculatePrice(uint256 _saleId) internal view returns (uint256 price, bool isInversePrice, uint256 pricePrecision) { - Sale storage sale = sales[_saleId]; - - SalePeriod storage period = sale.periods[sale.currentPeriod]; - - // Make sure calculatePrice is executed running with the correct period set - // All entry points to this function should have performed the transition - assert(getTimestamp() < period.periodEnds); - - pricePrecision = 10 ** 8; // given that exchangeRate is a uint, we need more precision for interpolating - isInversePrice = sale.isInversePrice; - price = period.initialPrice.mul(pricePrecision); - - if (period.initialPrice != period.finalPrice) { // interpolate price by period - uint256 periodDelta = uint256(period.periodEnds).sub(uint256(sale.periodStartTime)); - uint256 periodState = getTimestamp().sub(uint256(sale.periodStartTime)); - if (period.finalPrice > period.initialPrice) { - uint256 p1 = period.finalPrice.sub(period.initialPrice); - uint256 inc = pricePrecision.mul(p1).mul(periodState).div(periodDelta); - price = price.add(inc); - } else { - uint256 p2 = period.initialPrice.sub(period.finalPrice); - uint256 dec = pricePrecision.mul(p2).mul(periodState).div(periodDelta); - price = price.sub(dec); - } - } - } - - function transitionSalePeriodIfNeeded(Sale storage sale) internal { - uint64 newStartTime = sale.periodStartTime; - uint256 newCurrentPeriod = sale.currentPeriod; - while (getTimestamp() >= sale.periods[newCurrentPeriod].periodEnds) { - // In all transitions but last - if (sale.periods.length > newCurrentPeriod + 1) { - newStartTime = sale.periods[newCurrentPeriod].periodEnds; - newCurrentPeriod += 1; - } else { - newStartTime = MAX_UINT64; // last period ended - break; - } - } - - if (sale.periodStartTime != newStartTime) - sale.periodStartTime = newStartTime; - - if (sale.currentPeriod != newCurrentPeriod) - sale.currentPeriod = newCurrentPeriod; - } - - /* TODO: adapt to ERC777 - function parseBuyData(bytes data) internal pure returns (bytes4 sig, uint256 saleId) { - assembly { - sig := mload(add(data, 0x20)) - saleId := mload(add(data, 0x24)) // read first parameter of buy function call - } - } - */ - - function getTimestamp() internal view returns (uint256) { - return now; - } -} diff --git a/future-apps/fundraising/manifest.json b/future-apps/fundraising/manifest.json deleted file mode 100644 index f89cdfa670..0000000000 --- a/future-apps/fundraising/manifest.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "Fundraising", - "description": "Fundraise for your organization by selling tokens", - "icons": [{ - "src": "images/icon.png", - "sizes": "192x192" - }] -} diff --git a/future-apps/fundraising/migrations/1_initial_migration.js b/future-apps/fundraising/migrations/1_initial_migration.js deleted file mode 100644 index 1eb6f9daf6..0000000000 --- a/future-apps/fundraising/migrations/1_initial_migration.js +++ /dev/null @@ -1,5 +0,0 @@ -var Migrations = artifacts.require('./Migrations.sol') - -module.exports = function(deployer) { - deployer.deploy(Migrations) -} diff --git a/future-apps/fundraising/module.json b/future-apps/fundraising/module.json deleted file mode 100644 index aec9c82913..0000000000 --- a/future-apps/fundraising/module.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "appName": "fundraising.aragonpm.test", - "version": "1.0.0", - "roles": [ - { "name": "Create fundraising", "id": "CREATOR_ROLE" }, - { "name": "Close fundraising", "id": "CLOSER_ROLE" } - ], - "path": "contracts/FundraisingApp.sol" -} diff --git a/future-apps/fundraising/package.json b/future-apps/fundraising/package.json deleted file mode 100644 index 7d8ed2caef..0000000000 --- a/future-apps/fundraising/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "@aragon/future-apps-fundraising", - "version": "1.0.0", - "main": "index.js", - "scripts": { - "test": "TRUFFLE_TEST=true npm run ganache-cli:test", - "test:gas": "GAS_REPORTER=true npm test", - "lint": "solium --dir ./contracts", - "coverage": "SOLIDITY_COVERAGE=true npm run ganache-cli:test", - "ganache-cli:test": "./node_modules/@aragon/test-helpers/ganache-cli.sh" - }, - "keywords": [], - "author": "Aragon Institution MTU ", - "contributors": [ - "Jorge Izquierdo ", - "Pierre Bertet ", - "Oliver Nordbjerg " - ], - "license": "GPL-3.0", - "description": "", - "devDependencies": { - "@aragon/apps-token-manager": "^1.0.0", - "@aragon/test-helpers": "^1.0.0", - "babel-helpers": "^6.24.1", - "babel-polyfill": "^6.26.0", - "babel-preset-es2015": "^6.18.0", - "babel-preset-stage-2": "^6.24.1", - "babel-preset-stage-3": "^6.17.0", - "babel-register": "^6.26.0", - "ganache-cli": "^6.1.0", - "solidity-coverage": "0.3.5", - "solium": "^1.0.4", - "truffle": "4.0.5", - "truffle-hdwallet-provider": "0.0.3", - "webpack": "^3.7.1" - }, - "dependencies": { - "@aragon/os": "^3.0.5" - } -} diff --git a/future-apps/fundraising/test/TestFundraising.sol b/future-apps/fundraising/test/TestFundraising.sol deleted file mode 100644 index 9b213cf990..0000000000 --- a/future-apps/fundraising/test/TestFundraising.sol +++ /dev/null @@ -1,19 +0,0 @@ -pragma solidity 0.4.18; - -import "@aragon/os/contracts/lib/minime/MiniMeToken.sol"; - -// You might think this file is a bit odd, but let me explain. -// We only use the MiniMeToken contract in our tests, which -// means Truffle will not compile it for us, because it is -// from an external dependency. -// -// We are now left with three options: -// - Copy/paste the MiniMeToken contract -// - Run the tests with `truffle compile --all` on -// - Or trick Truffle by claiming we use it in a Solidity test -// -// You know which one I went for. - -contract TestFundraising { - // ... -} diff --git a/future-apps/fundraising/test/fundraising.js b/future-apps/fundraising/test/fundraising.js deleted file mode 100644 index 5a66da8721..0000000000 --- a/future-apps/fundraising/test/fundraising.js +++ /dev/null @@ -1,303 +0,0 @@ -const { assertRevert } = require('@aragon/test-helpers/assertThrow') - -const TokenManager = artifacts.require('TokenManager') -const MiniMeToken = artifacts.require('MiniMeToken') -const Fundraising = artifacts.require('FundraisingMock') - -const zeroAddress = '0x0000000000000000000000000000000000000000' - -contract('Fundraising', accounts => { - let tokenManager, token, raisedToken, fundraising = {} - - const vault = accounts[8] - const holder1000 = accounts[1] - const holder10 = accounts[2] - - beforeEach(async () => { - raisedToken = await MiniMeToken.new(zeroAddress, zeroAddress, 0, 'n', 0, 'n', true) - await raisedToken.generateTokens(holder1000, 1000) - await raisedToken.generateTokens(holder10, 10) - - token = await MiniMeToken.new(zeroAddress, zeroAddress, 0, 'n', 0, 'n', true) - tokenManager = await TokenManager.new() - await token.changeController(tokenManager.address) - await tokenManager.initialize(token.address, true, 0, false) - - fundraising = await Fundraising.new() - await fundraising.initialize(tokenManager.address, vault) - await fundraising.mock_setTimestamp(5) - }) - - it('fails on reinitialization', async () => { - return assertRevert(async () => { - await fundraising.initialize(tokenManager.address, vault) - }) - }) - - it('fails if any price is 0', async () => { - return assertRevert(async () => { - await fundraising.newSale(zeroAddress, raisedToken.address, 100, 150, 0, true, 1, [11], [0, 2]) - }) - }) - - it('fails if period times overlap', async () => { - return assertRevert(async () => { - await fundraising.newSale(zeroAddress, raisedToken.address, 100, 150, 0, true, 11, [11], [2, 2]) - }) - }) - - it('fails if any raised token is sold token', async () => { - return assertRevert(async () => { - await fundraising.newSale(zeroAddress, token.address, 100, 150, 0, true, 1, [11], [2, 2]) - }) - }) - - it('fails if no periods are provided', async () => { - return assertRevert(async () => { - await fundraising.newSale(zeroAddress, token.address, 100, 150, 0, true, 1, [], []) - }) - }) - - it('fails if too many periods are provided', async () => { - const maxPeriods = 51 - const periodsEndTime = [...Array(maxPeriods).keys()].map(x => 11 + x) - const periodPrices = [...Array(maxPeriods * 2).keys()] - return assertRevert(async () => { - await fundraising.newSale(zeroAddress, token.address, 100, 150, 0, true, 1, periodsEndTime, periodPrices) - }) - }) - - it('fails if on periods/prices argument number mismatch', async () => { - return assertRevert(async () => { - await fundraising.newSale(zeroAddress, token.address, 100, 150, 0, true, 1, [11], [10]) - }) - }) - - it('max raised and max sold are both hit', async () => { - const maxRaised = 100 - const maxSold = 150 - - await fundraising.newSale(zeroAddress, raisedToken.address, maxRaised, maxSold, 0, true, 1, [11], [2, 2]) - await raisedToken.approve(fundraising.address, 130, { from: holder1000 }) - - await fundraising.transferAndBuy(0, 40, { from: holder1000 }) - await fundraising.transferAndBuy(0, 34, { from: holder1000 }) - await fundraising.transferAndBuy(0, 56, { from: holder1000 }) - - assert.equal(await token.balanceOf(holder1000), 150, 'should have gotten max sold tokens') - assert.equal(await raisedToken.balanceOf(holder1000), 925, 'should have non-spent tokens') - - const [closed] = await fundraising.getSale(0) - assert.isTrue(closed, 'sale should be closed') - }) - - it('fails if buying less than min buy', async () => { - const minBuy = 2 - - await fundraising.newSale(zeroAddress, raisedToken.address, 100, 150, minBuy, true, 1, [11], [1, 1]) - await raisedToken.approve(fundraising.address, 1, { from: holder1000 }) - - return assertRevert(async () => { - await fundraising.transferAndBuy(0, 1, { from: holder1000 }) - }) - }) - - it('only allow investor to invest in private sale', async () => { - await fundraising.newSale(holder10, raisedToken.address, 10000, 10000, 0, true, 1, [11], [2, 2]) - await raisedToken.approve(fundraising.address, 1, { from: holder1000 }) - await raisedToken.approve(fundraising.address, 1, { from: holder10 }) - - await fundraising.transferAndBuy(0, 1, { from: holder10 }) - - assert.equal(await token.balanceOf(holder10), 2, 'investor should have received tokens') - - return assertRevert(async () => { - await fundraising.transferAndBuy(0, 1, { from: holder1000 }) - }) - }) - - it('supports increasing price sales', async () => { - await fundraising.newSale(zeroAddress, raisedToken.address, 10000, 10000, 0, false, 1, [11], [2, 4]) - await fundraising.mock_setTimestamp(6) - const [price, inverse, precision] = await fundraising.getCurrentPrice(0) - - assert.equal(price, 3 * precision, 'price and precision should be correct') - assert.isFalse(inverse, 'price should be inverse') - }) - - /* TODO: adapt to ERC777 - context('ERC677 sales', () => { - let etherToken, buyData = {} - - beforeEach(async () => { - etherToken = await EtherToken.new() - await fundraising.newSale(zeroAddress, etherToken.address, 10000, 10000, 0, true, 4, [10], [2, 2]) - - const saleId = 0 - buyData = fundraising.contract.buyWithToken.getData(saleId) - }) - - it('fails when calling buy with token externally', async () => { - return assertRevert(async () => { - console.log(await fundraising.buyWithToken(0)) - }) - }) - - it('buys tokens correctly', async () => { - await etherToken.wrapAndCall(fundraising.address, buyData, { value: 100 }) // performs transferAndCall under the hood - - assert.equal(await token.balanceOf(accounts[0]), 200, 'should have received tokens') - assert.equal(await etherToken.balanceOf(vault), 100) - }) - - it('returns tokens if cap hit', async () => { - await etherToken.wrapAndCall(fundraising.address, buyData, { value: 15000 }) // performs transferAndCall under the hood - // only 5000 accepted (hits maxSold), returns 10000 - - assert.equal(await token.balanceOf(accounts[0]), 10000, 'should have received tokens') - assert.equal(await etherToken.balanceOf(vault), 5000) - assert.equal(await etherToken.balanceOf(accounts[0]), 10000) - }) - - it('fails if data is not correctly specified', async () => { - return assertRevert(async () => { - await etherToken.wrapAndCall(fundraising.address, '0x00', { value: 15000 }) - }) - }) - - it('fails if buying in non-existent sale', async () => { - return assertRevert(async () => { - await etherToken.wrapAndCall(fundraising.address, fundraising.contract.buyWithToken.getData(100), { value: 15000 }) - }) - }) - - it('fails if not buying through token', async () => { - return assertRevert(async () => { - await fundraising.tokenFallback(accounts[1], 100, buyData) - }) - }) - }) - */ - - context('creating normal rate sale', () => { - beforeEach(async () => { - // sale with decreasing price. 1 token per 5 raised tokens first 10 seconds, 1 token per 2 raised last 10 seconds - await fundraising.newSale(zeroAddress, raisedToken.address, 100, 30, 0, false, 11, [21, 31], [5, 5, 2, 2]) - await raisedToken.approve(fundraising.address, 1000, { from: holder1000 }) - }) - - it('returns correct sale info', async () => { - const info = await fundraising.getSale(0) - - const [closed, inv, rt, mr, ms, mb, inp, st, pc, cp] = info - - assert.equal(closed, false, 'sale should not be closed') - assert.equal(inv, zeroAddress, 'investor should be correct') - assert.equal(rt, raisedToken.address, 'raisedToken should be correct') - assert.equal(mr, 100, 'max raised should be correct') - assert.equal(ms, 30, 'max sold should be correct') - assert.equal(mb, 0, 'min buy should be correct') - assert.equal(st, 11, 'start time should be correct') - assert.equal(inp, false, 'inverse price should be correct') - assert.equal(pc, 2, 'periods count should be correct') - assert.equal(cp, 0, 'current periods should be correct') - }) - - it('returns correct period info', async () => { - await fundraising.mock_setTimestamp(35) - - const [start1, end1, initial1, final1] = await fundraising.getPeriod(0, 0) - const [start2, end2, initial2, final2] = await fundraising.getPeriod(0, 1) - - assert.equal(start1, 11, 'start time should be correct') - assert.equal(end1, 21, 'end time should be correct') - assert.equal(initial1, 5, 'price should be correct') - assert.equal(final1, 5, 'price should be correct') - - assert.equal(start2, 21, 'start time should be correct') - assert.equal(end2, 31, 'end time should be correct') - assert.equal(initial2, 2, 'price should be correct') - assert.equal(final2, 2, 'price should be correct') - }) - - it('fails if buying before sale starts', async () => { - await fundraising.mock_setTimestamp(10) - return assertRevert(async () => { - await fundraising.transferAndBuy(0, 30, { from: holder1000 }) - }) - }) - - it('fails if buying with more tokens than owned', async () => { - await fundraising.mock_setTimestamp(11) - - // transfers all its tokens away before buying - await raisedToken.transfer(accounts[7], 1000, { from: holder1000 }) - - return assertRevert(async () => { - await fundraising.transferAndBuy(0, 110, { from: holder1000 }) - }) - }) - - it('can only buy up-to max raised', async () => { - await fundraising.mock_setTimestamp(11) - - await fundraising.transferAndBuy(0, 110, { from: holder1000 }) - - assert.equal(await raisedToken.balanceOf(vault), 100, 'should have only received max raised') - assert.equal(await raisedToken.balanceOf(holder1000), 900, 'should have only debited max raised') - - assert.equal(await token.balanceOf(holder1000), 20, 'should received tokens') - const [closed] = await fundraising.getSale(0) - assert.isTrue(closed, 'sale should be closed') - }) - - it('can only buy up-to max sold', async () => { - await fundraising.mock_setTimestamp(22) - - await fundraising.transferAndBuy(0, 90, { from: holder1000 }) - - assert.equal(await raisedToken.balanceOf(vault), 60, 'should have only received max raised') - assert.equal(await raisedToken.balanceOf(holder1000), 940, 'should have only debited max raised') - - assert.equal(await token.balanceOf(holder1000), 30, 'should received tokens') - const [closed] = await fundraising.getSale(0) - assert.isTrue(closed, 'sale should be closed') - }) - - it('can force closing sale if authorized', async () => { - await fundraising.forceCloseSale(0) - const [closed] = await fundraising.getSale(0) - assert.isTrue(closed, 'sale should be closed') - }) - - it('fails if buying in closed sale', async () => { - await fundraising.forceCloseSale(0) - - return assertRevert(async () => { - await fundraising.transferAndBuy(0, 90, { from: holder1000 }) - }) - }) - - it('fails if closing closed sale', async () => { - await fundraising.forceCloseSale(0) - return assertRevert(async () => { - await fundraising.forceCloseSale(0) - }) - }) - - it('can close sale after all periods ended', async () => { - await fundraising.mock_setTimestamp(32) - await fundraising.closeSale(0) - - const [closed] = await fundraising.getSale(0) - assert.isTrue(closed, 'sale should be closed') - }) - - it('fails when closing ongoing sale', async () => { - await fundraising.mock_setTimestamp(21) - return assertRevert(async () => { - await fundraising.closeSale(0) - }) - }) - }) -}) diff --git a/future-apps/fundraising/test/mocks/FundraisingMock.sol b/future-apps/fundraising/test/mocks/FundraisingMock.sol deleted file mode 100644 index dd7864f2e0..0000000000 --- a/future-apps/fundraising/test/mocks/FundraisingMock.sol +++ /dev/null @@ -1,10 +0,0 @@ -pragma solidity 0.4.18; - -import "../../contracts/Fundraising.sol"; - -contract FundraisingMock is Fundraising { - uint _mockTime = now; - - function getTimestamp() internal view returns (uint256) { return _mockTime; } - function mock_setTimestamp(uint i) { _mockTime = i; } -} diff --git a/future-apps/fundraising/truffle.js b/future-apps/fundraising/truffle.js deleted file mode 100644 index 466a16a350..0000000000 --- a/future-apps/fundraising/truffle.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require("@aragon/os/truffle-config")