Skip to content

Commit

Permalink
Update tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Ricardo Silva committed Feb 6, 2018
1 parent 8b200dc commit d86be09
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 64 deletions.
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["es2015", "stage-2", "stage-3"]
}
75 changes: 46 additions & 29 deletions contracts/Fund.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ contract Fund is OpenFund, Ownable {
uint256 public sellFeePP;
uint256 public feeDenominator;

uint256 public reserveBalance;
address public manager;

mapping(address => uint256) pendingPurchases;
mapping(address => uint256) pendingSells;
address investmentWallet;

function Fund(address _investmentWallet, string _name, string _tokenSymbol, uint256 _purchaseFeePP, uint256 _sellFeePP, uint _feeDenominator) public {
investmentWallet = _investmentWallet;
function Fund(address _manager, string _name, string _tokenSymbol, uint256 _purchaseFeePP, uint256 _sellFeePP, uint _feeDenominator) public {
manager = _manager;
name = _name;
tokenSymbol = _tokenSymbol;
purchaseFeePP = _purchaseFeePP;
Expand All @@ -31,27 +33,17 @@ contract Fund is OpenFund, Ownable {
return new OpenFundToken(name, tokenSymbol, 8);
}

function () external payable {
purchase();
modifier onlyManager() {
require(msg.sender == manager);
_;
}

function purchase() public payable {
//todo what happens if amount is less than NAV?
uint256 weiAmount = msg.value;
address buyer = msg.sender;

pendingPurchases[buyer] = pendingPurchases[buyer].add(weiAmount); // add amount to the pending requests to be processed
owner.transfer(weiAmount); // add amount to the wallet

TokenPurchaseRequest(buyer, weiAmount);
function getTokenAmount(uint256 weiAmount, uint256 nav) internal pure returns(uint256) {
return weiAmount.div(nav);
}

function sell(uint256 tokenAmount) public {
require(token.balanceOf(msg.sender) >= tokenAmount);

pendingSells[msg.sender] = pendingSells[msg.sender].add(tokenAmount);

TokenSellRequest(msg.sender, tokenAmount);
function getWeiAmount(uint256 tokenAmount, uint256 nav) internal pure returns(uint256) {
return tokenAmount.mul(nav);
}

function getPendingAmount() public view returns (uint256) {
Expand All @@ -62,41 +54,66 @@ contract Fund is OpenFund, Ownable {
return pendingPurchases[buyer];
}

function processPurchase(uint256 nav, address buyer) public onlyOwner payable { // nav: net asset value. is the price per share of the fund
function purchase() public payable {
require((pendingPurchases[msg.sender] + msg.value) > pendingPurchases[msg.sender]);

//TODO what happens if amount is less than NAV?

pendingPurchases[msg.sender] = pendingPurchases[msg.sender].add(msg.value); // add amount to the pending requests to be processed

TokenPurchaseRequest(msg.sender, msg.value);
}

function sell(uint256 tokenAmount) public {
require(token.balanceOf(msg.sender) >= tokenAmount);
require((pendingSells[msg.sender] + tokenAmount) > pendingSells[msg.sender]);

pendingSells[msg.sender] = pendingSells[msg.sender].add(tokenAmount);

TokenSellRequest(msg.sender, tokenAmount);
}

function processPurchase(uint256 nav, address buyer) public onlyOwner payable {
uint256 weiAmount = pendingPurchases[buyer];
uint256 fee = weiAmount.mul(purchaseFeePP).div(feeDenominator);
uint256 tokens = getTokenAmount(weiAmount - fee, nav);
uint256 weiNetAmount = weiAmount - fee;
uint256 tokens = getTokenAmount(weiNetAmount, nav);

token.mint(buyer, tokens);
manager.transfer(weiNetAmount);

delete pendingPurchases[buyer];

TokenPurchased(buyer, tokens, nav, fee);
}

function processSell(uint256 nav, address buyer) public onlyOwner payable {
uint256 tokenAmount = pendingSells[buyer];
uint256 weiAmount = getWeiAmount(tokenAmount, nav);

require(reserveBalance >= weiAmount);

uint256 fee = weiAmount.mul(sellFeePP).div(feeDenominator);

reserveBalance = reserveBalance.sub(weiAmount);
token.burn(buyer, tokenAmount);
buyer.transfer(weiAmount - fee);

delete pendingSells[buyer];

TokenSold(buyer, tokenAmount, nav, fee);
}
}//"test": "concurrently --kill-others \"npm run blockchain\" \"npm start\""

function getTokenAmount(uint256 weiAmount, uint256 nav) internal pure returns(uint256) {
return weiAmount.div(nav);
}
function deposit() public onlyManager payable {
reserveBalance = reserveBalance.add(msg.value);

function getWeiAmount(uint256 tokenAmount, uint256 nav) internal pure returns(uint256) {
return tokenAmount.mul(nav);
FundManagerDeposit(msg.value);
}

event TokenPurchaseRequest(address indexed purchaser, uint256 amount);
event TokenPurchased(address indexed purchaser, uint256 tokenAmount, uint256 nav, uint256 fee);
event TokenSellRequest(address indexed purchaser, uint256 amount);
event TokenSold(address indexed purchaser, uint256 tokenAmount, uint256 nav, uint256 fee);

event FundManagerDeposit(uint256 weiAmount);
}
4 changes: 2 additions & 2 deletions contracts/OpenFundToken.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pragma solidity ^0.4.18;

import "../node_modules/zeppelin-solidity/contracts/math/SafeMath.sol";
import '../node_modules/zeppelin-solidity/contracts/token/MintableToken.sol';
import '../node_modules/zeppelin-solidity/contracts/token/ERC20/MintableToken.sol';

contract OpenFundToken is MintableToken {
using SafeMath for uint256;
Expand Down Expand Up @@ -35,7 +35,7 @@ contract OpenFundToken is MintableToken {
// sender's balance is greater than the totalSupply, which *should* be an assertion failure

balances[burner] = balances[burner].sub(_value);
totalSupply = totalSupply.sub(_value);
totalSupply_ = totalSupply_.sub(_value);
Burn(burner, _value);
}
}
20 changes: 15 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
{
"name": "ethercontract",
"version": "1.0.0",
"name": "cf-contracts",
"version": "0.0.1",
"description": "",
"main": "truffle-config.js",
"directories": {
"test": "test"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"pipeline": "truffle develop --log & truffle deploy --reset --network trufflenetwork && truffle test --network trufflenetwork"
},
"author": "",
"license": "ISC",
"devDependencies": {
"babel-polyfill": "^6.23.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-stage-2": "^6.18.0",
"babel-preset-stage-3": "^6.17.0",
"babel-register": "^6.23.0",
"chai": "^4.1.2",
"concurrently": "^3.5.1",
"truffle": "^4.0.6"
},
"dependencies": {
"package.json": "^2.0.1",
"zeppelin-solidity": "^1.5.0"
"zeppelin-solidity": "^1.6.0"
}
}
}
111 changes: 83 additions & 28 deletions test/Fund.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ const web3 = global.web3;
const Fund = artifacts.require('Fund');
const OpenFundToken = artifacts.require('OpenFundToken');

import expectThrow from 'zeppelin-solidity/test/helpers/expectThrow';
import expectEvent from 'zeppelin-solidity/test/helpers/expectEvent';

const BigNumber = web3.BigNumber;

contract('Fund', function([ownerWallet, investmentWallet, wallet, purchaser]) {
contract('Fund', function ([ownerWallet, investmentWallet, wallet, purchaser]) {

const initialParams = {
name: "teste",
Expand All @@ -15,57 +18,109 @@ contract('Fund', function([ownerWallet, investmentWallet, wallet, purchaser]) {
let fund;
let token;

beforeEach(async function() {
fund = await Fund.new(investmentWallet, initialParams.name, initialParams.tokenSymbol);
beforeEach(async function () {
fund = await Fund.new(investmentWallet, initialParams.name, initialParams.tokenSymbol, 0, 0, 100);// 0% fee
token = OpenFundToken.at(await fund.token());
});

it("should set initial attributes", async function() {
assert.equal(await fund.getPendingAmount({from: purchaser}), 0);
it("should set initial attributes", async function () {
assert.equal(await fund.getPendingAmount({ from: purchaser }), 0);
assert.equal(await fund.name(), initialParams.name);
assert.equal(await token.name(), initialParams.name);
});

it("should add requested amount in tokens", async function() {
it("should purchase amount bigger than zero", async function () {
await expectThrow(fund.purchase({ from: purchaser, value: 0 }));
});

it("should sell amount bigger than zero", async function () {
await expectThrow(fund.sell(0));
});

it("should have reserves for selling", async function () {
//TODO
});

it("should trigger event when calling purchase", async function () {
await expectEvent.inTransaction(
fund.purchase({ from: purchaser, value: 100 }),
'TokenPurchaseRequest'
);
});

it("should sum purchase orders", async function () {
await fund.purchase({ from: purchaser, value: 100 })
await fund.purchase({ from: purchaser, value: 100 })
let pendingAmount = await fund.getPendingAmount({ from: purchaser })
assert.equal(pendingAmount.toNumber(), 200);
});

it("should increase deposit amount", async function () {
let depositValue = 1;

await fund.deposit({ from: investmentWallet, value: depositValue });

let reserve = await fund.reserveBalance()
assert.equal(reserve.toNumber(), depositValue);
});

it("should decrease reserve amount", async function () {
let nav = 1;
let sellTokens = 100;
let depositValue = 200;

await mockPurchase(1000, nav)

await fund.sell(sellTokens, { from: purchaser });
await fund.deposit({ from: investmentWallet, value: depositValue });
await fund.processSell(nav, purchaser);

let reserve = await fund.reserveBalance()
assert.equal(reserve.toNumber(), depositValue - sellTokens);
});

it("should add requested amount in tokens", async function () {
let nav = 10;
let tokenValue = 200;
let weiValue = tokenValue * nav;

await fund.purchase({from: purchaser, value: weiValue})
let requestedAmount = await fund.getPendingAmount({from: purchaser});
await fund.purchase({ from: purchaser, value: weiValue })
let requestedAmount = await fund.getPendingAmount({ from: purchaser });
assert.equal(requestedAmount.toNumber(), weiValue);
await fund.processPurchase(nav, purchaser, {value: requestedAmount});

await fund.processPurchase(nav, purchaser, { value: requestedAmount });

let balance = await token.balanceOf(purchaser);
let totalSupply = await token.totalSupply();

assert.equal(balance.toNumber(), tokenValue);
assert.equal(totalSupply.toNumber(), tokenValue);
});

it("should sell tokens", async function() {
// // logs?
// var event = fund.Log();
// event.watch(function(error, result){
// if (!error)
// console.log(result.args.amount.toNumber());
// });

it("should sell tokens", async function () {
let nav = 3;
let tokenValue = 1000;
let weiValue = tokenValue * nav;
let tokenAmount = 1000;
let weiValue = tokenAmount * nav;

await fund.purchase({from: purchaser, value: weiValue})
await fund.processPurchase(nav, purchaser, {value: weiValue});
//await mockPurchase(tokenAmount, nav)
await fund.purchase({ from: purchaser, value: weiValue })
await fund.processPurchase(nav, purchaser, { value: weiValue });

let sellTokens = 100;

await fund.sell(sellTokens, {from: purchaser});
await fund.processSell(nav, purchaser, {value: weiValue})

await fund.sell(sellTokens, { from: purchaser });
await fund.deposit({ from: investmentWallet, value: sellTokens * nav });
await fund.processSell(nav, purchaser)

let balance = await token.balanceOf(purchaser);
assert.equal(balance.toNumber(), tokenValue - sellTokens);
assert.equal(balance.toNumber(), tokenAmount - sellTokens);
});

});
async function mockPurchase(tokenAmount, nav) {
let weiValue = tokenAmount * nav;

await fund.purchase({ from: purchaser, value: weiValue })
await fund.processPurchase(nav, purchaser, { value: weiValue });
}
});

35 changes: 35 additions & 0 deletions test/TestExample.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
pragma solidity ^0.4.18;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";

contract TestExample {

SomeContract internal someContract;

function TestExample() public {
someContract = new SomeContract();
}

function testZero() public {
Assert.isTrue(execute("method()", 0), "Should not allow zero amount");
}

function testNonZero() public {
Assert.isTrue(execute("method()", 100), "Should allow non-zero amount");
}

function execute(string signature, uint256 value) internal returns (bool) {
bytes4 sig = bytes4(keccak256(signature));
return someContract.call.value(value)(sig);
}
}

contract SomeContract {

function method() public {
ExampleEvent(msg.sender, msg.value);
}

event ExampleEvent(address indexed add, uint256 amount);
}
Loading

0 comments on commit d86be09

Please sign in to comment.