Skip to content

Commit

Permalink
Create VentureEth.sol, add tests, add README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
vsfalliance committed Jan 25, 2020
1 parent cb01bd5 commit 41fdafb
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 1 deletion.
12 changes: 11 additions & 1 deletion contracts/drafts/issuance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,14 @@ setMinInvestment(uint256 _minInvestment)
```
Setter for _minInvestment. Can only be called during `SETUP`.

---
---

## VentureEth

A hybrid mating of IssuanceEth and ERC20DividendableEth.

### Description

0. Initialize or inherit the contract. There are no parameters for the constructor.
1. Use it as a regular `IssuanceEth`, whose `issuanceToken` is its own address. The dividendable qualities are disabled in this phase.
2. After issuance, you can use the dividendable qualities for any ether sent to `increasePool`.
70 changes: 70 additions & 0 deletions contracts/drafts/issuance/VentureEth.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
pragma solidity ^0.5.10;

import "@openzeppelin/contracts/token/ERC20/ERC20Mintable.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../../issuance/IssuanceEth.sol";
import "../../token/ERC20DividendableEth.sol";


/**
* @title VentureEth
* @notice Implements a venture
*
* 1. Use `setIssuePrice` to determine how many ether (in wei) do investors
* have to pay for each issued token.
* 2. Use `openIssuance` to allow investors to invest.
* 3. Investors can `invest` their ether at will.
* 4. Investors can also `cancelInvestment` and get their ether back.
* 5. The contract owner can `cancelAllInvestments` to close the investment phase.
* In this case `invest` is not available, but `cancelInvestment` is.
* 6. Use `startDistribution` to close the investment phase.
* 7. Investors can only `withdraw` their issued tokens now.
* 8. Owner can use `transferFunds` to send collected ether to a wallet.
* 9.
* 10.
*/
contract VentureEth is ERC20Mintable, ERC20DividendableEth, IssuanceEth {

constructor() public ERC20Mintable()
ERC20DividendableEth() IssuanceEth(address(this)) {
_createState("ENDED");
_createTransition("LIVE", "ENDED");
_createTransition("FAILED", "ENDED");
addMinter(address(this));
}

modifier afterIssuance {
require(
currentState == "ENDED",
"Issuance must have ended."
);
_;
}

/**
* @notice Send ether to this function in orther to disburse dividends. Venture must be live.
*/
function increasePool() external payable afterIssuance {
totalDividends = totalDividends.add(msg.value);
totalDividendPoints = totalDividends
.mul(pointMultiplier).div(this.totalSupply());
}

/**
* @notice Function to transfer all collected tokens to the wallet of the owner.
*/
function transferFunds(address payable _wallet) public onlyOwner {
super.transferFunds(_wallet);
_transition("ENDED");
}

/**
* @dev Function to update an account
* @param account The account to update
* @notice Will revert if account need not be updated, or venture is not live.
*/
function updateAccount(address payable account) public afterIssuance {
super.updateAccount(account);
}

}
1 change: 1 addition & 0 deletions contracts/token/ERC20DividendableEth.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,5 @@ contract ERC20DividendableEth is ERC20 {
return this.balanceOf(account)
.mul(newDividendPoints).div(pointMultiplier);
}

}
107 changes: 107 additions & 0 deletions test/drafts/issuance/VentureEth.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { should } from 'chai';

// tslint:disable-next-line:no-var-requires
const { balance, BN, constants, ether, expectEvent, expectRevert, send } = require('@openzeppelin/test-helpers');

import { VentureEthInstance } from '../../../types/truffle-contracts';

const VentureEth = artifacts.require(
'VentureEth.sol',
) as Truffle.Contract<VentureEthInstance>;


should();

contract('VentureEth - before issuance', (accounts) => {

const [user1, investor1, investor2] = accounts;

let ventureEth: VentureEthInstance;

beforeEach(async () => {
ventureEth = await VentureEth.new();
});

/**
* @test {VentureEth#increasePool}
*/
it('cannot increase pool when state is not "LIVE"', async () => {
await expectRevert(
ventureEth.increasePool({ from: user1, value: ether('6').toString()}),
'Issuance must have ended.',
);
});

/**
* @test {VentureEth#updateAccount}
*/
it('cannot update account when state is not "LIVE"', async () => {
await expectRevert(
ventureEth.updateAccount(investor1),
'Issuance must have ended.',
);
});
});


contract('VentureEth - after issuance', (accounts) => {

const [wallet, user1, investor1, investor2] = accounts;

let ventureEth: VentureEthInstance;

beforeEach(async () => {
ventureEth = await VentureEth.new();
await ventureEth.setIssuePrice(5);
await ventureEth.openIssuance();
await ventureEth.invest({ from: investor1, value: ether('0.5').toString() });
await ventureEth.invest({ from: investor2, value: ether('0.1').toString() });
await ventureEth.startDistribution();
(bytes32ToString(await ventureEth.currentState())).should.be.equal('LIVE');
await ventureEth.withdraw({ from: investor1 });
await ventureEth.withdraw({ from: investor2 });
BN(await ventureEth.balanceOf(investor1)).should.be.bignumber.equal(ether('0.1'));
BN(await ventureEth.balanceOf(investor2)).should.be.bignumber.equal(ether('0.02'));
const tracker = await balance.tracker(wallet, 'wei');
await tracker.get();
await ventureEth.transferFunds(wallet);
BN(await tracker.delta()).should.be.bignumber.gte(ether('0.5')).and.bignumber.lte(ether('0.6'));
});

/**
* @test {VentureEth#updateAccount} and {VentureEth#increasePool}
*/
it('updateAccount can succesfully update an account', async () => {
const tracker1 = await balance.tracker(investor1, 'ether');
const tracker2 = await balance.tracker(investor2, 'ether');
await tracker1.get();
await tracker2.get();
await ventureEth.increasePool({ from: user1, value: ether('6').toString()});
await ventureEth.updateAccount(investor1);
await ventureEth.updateAccount(investor2);
(await tracker1.delta()).should.be.bignumber.equal('5');
(await tracker2.delta()).should.be.bignumber.equal('1');
});

/**
* @test {VentureEth#updateAccount} and {VentureEth#increasePool}
*/
it('more updateAccount usage, including a revert', async () => {
const tracker1 = await balance.tracker(investor1, 'ether');
const tracker2 = await balance.tracker(investor2, 'ether');
await tracker1.get();
await tracker2.get();
await ventureEth.increasePool({ from: user1, value: ether('6').toString()});
await ventureEth.updateAccount(investor1);
(await tracker1.delta()).should.be.bignumber.equal('5');
await expectRevert(ventureEth.updateAccount(investor1), 'Account need not be updated now.');
await ventureEth.increasePool({ from: user1, value: ether('6').toString()});
await ventureEth.updateAccount(investor2);
(await tracker2.delta()).should.be.bignumber.equal('2');
});
});

function bytes32ToString(text: string) {
return web3.utils.toAscii(text).replace(/\0/g, '');
}

0 comments on commit 41fdafb

Please sign in to comment.