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")