From 76538fce1688f00574ff305add28bd81e2c54e79 Mon Sep 17 00:00:00 2001 From: Kayvon Tehranian Date: Sat, 20 Jan 2018 16:21:28 -0800 Subject: [PATCH 01/12] Remove concept of duration from tests after rebase --- test/Project.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/Project.js b/test/Project.js index 26c1d03..8465abc 100644 --- a/test/Project.js +++ b/test/Project.js @@ -4,8 +4,7 @@ contract("Project", accounts => { it("is initialized with an owner and target", async () => { const target = 200; const owner = accounts[0]; - const duration = 12; - const contract = await Project.new(owner, target, duration); + const contract = await Project.new(owner, target); const contractOwner = await contract.owner(); assert.equal(contractOwner, owner); const contractTarget = await contract.target(); @@ -34,7 +33,7 @@ contract("Project", accounts => { it("should be able to determine if its target has been reached", async () => { let funded = false; - const contract = await Project.new(accounts[0], 200, 12); + const contract = await Project.new(accounts[0], 200); await contract.contribute(accounts[1], 100); funded = await contract.isFullyFunded(); // Project is not yet fully funded. @@ -46,7 +45,7 @@ contract("Project", accounts => { }); it("should return the total number of contributions", async () => { - const contract = await Project.new(accounts[0], 200, 12); + const contract = await Project.new(accounts[0], 200); await contract.contribute(accounts[1], 100); await contract.contribute(accounts[2], 200); await contract.contribute(accounts[3], 300); From 24967f0e97742c3061b80144c8b4b26ba3356742 Mon Sep 17 00:00:00 2001 From: Kayvon Tehranian Date: Fri, 19 Jan 2018 18:01:27 -0800 Subject: [PATCH 02/12] Add event for goal being reached --- contracts/Project.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/Project.sol b/contracts/Project.sol index 9fda19a..33b7df1 100644 --- a/contracts/Project.sol +++ b/contracts/Project.sol @@ -11,6 +11,7 @@ contract Project { /* events */ event ContributionMade(address contributor, uint amount); + event GoalReached(uint amount, address beneficiary); function Project(address _owner, uint _target) public { owner = _owner; From 99f7dc6a6f01c35dce9d27e19b2a5e27950588ac Mon Sep 17 00:00:00 2001 From: Kayvon Tehranian Date: Fri, 19 Jan 2018 17:43:06 -0800 Subject: [PATCH 03/12] Add modifier to check for deadline being passed --- contracts/Project.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/Project.sol b/contracts/Project.sol index 33b7df1..bb570b9 100644 --- a/contracts/Project.sol +++ b/contracts/Project.sol @@ -18,6 +18,10 @@ contract Project { target = _target; } + /* This modifier ensures that the function it modifies is only run if the + deadline of the project has passed. */ + modifier afterDeadline() { if (now >= deadline) _; } + function contribute(address _contributor, uint _amount) public returns (bool success) { contributions[_contributor] += _amount; totalAmountRaised += _amount; From 9a2a02a9a4f94544a8e16e346291bfb244a3db53 Mon Sep 17 00:00:00 2001 From: Kayvon Tehranian Date: Fri, 19 Jan 2018 17:20:21 -0800 Subject: [PATCH 04/12] Add concept of a deadline to project --- contracts/Project.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contracts/Project.sol b/contracts/Project.sol index bb570b9..9d26fda 100644 --- a/contracts/Project.sol +++ b/contracts/Project.sol @@ -5,6 +5,7 @@ contract Project { /* public properties */ address public owner; uint public target; + uint public deadline; uint public totalAmountRaised; address[] public contributors; mapping (address => uint) public contributions; @@ -13,9 +14,10 @@ contract Project { event ContributionMade(address contributor, uint amount); event GoalReached(uint amount, address beneficiary); - function Project(address _owner, uint _target) public { + function Project(address _owner, uint _target, uint _durationInHours) public { owner = _owner; target = _target; + deadline = now + _durationInHours * 1 hours; } /* This modifier ensures that the function it modifies is only run if the From bded60ed7d60f31a6089a624a8ed4cae8acbd19b Mon Sep 17 00:00:00 2001 From: Kayvon Tehranian Date: Sat, 20 Jan 2018 16:26:03 -0800 Subject: [PATCH 05/12] Revert "Remove concept of duration from tests after rebase" This reverts commit d36fb5f6012e1f8f7b57bec2370866ee26a4bd1c. --- test/Project.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/Project.js b/test/Project.js index 8465abc..26c1d03 100644 --- a/test/Project.js +++ b/test/Project.js @@ -4,7 +4,8 @@ contract("Project", accounts => { it("is initialized with an owner and target", async () => { const target = 200; const owner = accounts[0]; - const contract = await Project.new(owner, target); + const duration = 12; + const contract = await Project.new(owner, target, duration); const contractOwner = await contract.owner(); assert.equal(contractOwner, owner); const contractTarget = await contract.target(); @@ -33,7 +34,7 @@ contract("Project", accounts => { it("should be able to determine if its target has been reached", async () => { let funded = false; - const contract = await Project.new(accounts[0], 200); + const contract = await Project.new(accounts[0], 200, 12); await contract.contribute(accounts[1], 100); funded = await contract.isFullyFunded(); // Project is not yet fully funded. @@ -45,7 +46,7 @@ contract("Project", accounts => { }); it("should return the total number of contributions", async () => { - const contract = await Project.new(accounts[0], 200); + const contract = await Project.new(accounts[0], 200, 12); await contract.contribute(accounts[1], 100); await contract.contribute(accounts[2], 200); await contract.contribute(accounts[3], 300); From a7c0b526d0d23ba6c8a2f9562b599ae5b51a63d0 Mon Sep 17 00:00:00 2001 From: Kayvon Tehranian Date: Sat, 20 Jan 2018 16:04:51 -0800 Subject: [PATCH 06/12] Add test spec for 0.0.0 --- test/Project.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/Project.js b/test/Project.js index 26c1d03..17ecd37 100644 --- a/test/Project.js +++ b/test/Project.js @@ -53,4 +53,20 @@ contract("Project", accounts => { const numberOfContributions = await contract.numberOfContributions(); assert.equal(numberOfContributions, 3, "There are 3 contributions."); }); + + it("should indicate whether a project is open or closed to contribution", async () => {}); + + it("should not allow an owner to withdraw funds if project is still open", async () => {}); + + it("should allow an owner to withdraw funds if project is closed and successful", async () => {}); + + it("should not allow an owner to withdraw funds if project is closed and unsuccessful", async () => {}); + + it("should allow a contributor to withdraw funds if project is closed and unsuccessful", async () => {}); + + it("should not allow a contributor to withdraw funds if project is closed and successful", async () => {}); + + xit("should not allow a contributor to withdraw funds if project is still open", async () => {}); + + it("should only allow contributors or owners to withdraw funds", async () => {}); }); From 3db17407b992c9d5d8dff0e32603a5c7fec99d21 Mon Sep 17 00:00:00 2001 From: Kayvon Tehranian Date: Mon, 22 Jan 2018 15:13:33 -0800 Subject: [PATCH 07/12] Document contract sections --- contracts/Project.sol | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/contracts/Project.sol b/contracts/Project.sol index 9d26fda..b985382 100644 --- a/contracts/Project.sol +++ b/contracts/Project.sol @@ -2,7 +2,7 @@ pragma solidity ^0.4.2; contract Project { - /* public properties */ + /* Public properties */ address public owner; uint public target; uint public deadline; @@ -10,20 +10,22 @@ contract Project { address[] public contributors; mapping (address => uint) public contributions; - /* events */ + /* Events */ event ContributionMade(address contributor, uint amount); event GoalReached(uint amount, address beneficiary); + /* Constructor */ function Project(address _owner, uint _target, uint _durationInHours) public { owner = _owner; target = _target; deadline = now + _durationInHours * 1 hours; } - /* This modifier ensures that the function it modifies is only run if the - deadline of the project has passed. */ + /* Modifiers */ modifier afterDeadline() { if (now >= deadline) _; } + /* Publicly-exposed interface */ + function contribute(address _contributor, uint _amount) public returns (bool success) { contributions[_contributor] += _amount; totalAmountRaised += _amount; From 225adcbb457eb7e6b76cbb14d8e2c6278b24bf9b Mon Sep 17 00:00:00 2001 From: Kayvon Tehranian Date: Mon, 22 Jan 2018 15:13:43 -0800 Subject: [PATCH 08/12] Add modifier to ensure project is open --- contracts/Project.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/Project.sol b/contracts/Project.sol index b985382..268d21b 100644 --- a/contracts/Project.sol +++ b/contracts/Project.sol @@ -23,6 +23,7 @@ contract Project { /* Modifiers */ modifier afterDeadline() { if (now >= deadline) _; } + modifier projectOpen() { if (deadline < now) _; } /* Publicly-exposed interface */ From 347cd1ff3ca968ee36915e9c7269e87cfefa5dac Mon Sep 17 00:00:00 2001 From: Kayvon Tehranian Date: Mon, 22 Jan 2018 15:14:03 -0800 Subject: [PATCH 09/12] Contributions are only allowed if project is open --- contracts/Project.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/Project.sol b/contracts/Project.sol index 268d21b..032dd7c 100644 --- a/contracts/Project.sol +++ b/contracts/Project.sol @@ -27,7 +27,7 @@ contract Project { /* Publicly-exposed interface */ - function contribute(address _contributor, uint _amount) public returns (bool success) { + function contribute(address _contributor, uint _amount) public projectOpen returns (bool success) { contributions[_contributor] += _amount; totalAmountRaised += _amount; contributors.push(_contributor); From 837262fe59f4c8fba26522e80cb69c29a7c193b3 Mon Sep 17 00:00:00 2001 From: Kayvon Tehranian Date: Mon, 22 Jan 2018 15:14:23 -0800 Subject: [PATCH 10/12] Contributions need to be non-zero. --- contracts/Project.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contracts/Project.sol b/contracts/Project.sol index 032dd7c..495bcc3 100644 --- a/contracts/Project.sol +++ b/contracts/Project.sol @@ -28,6 +28,8 @@ contract Project { /* Publicly-exposed interface */ function contribute(address _contributor, uint _amount) public projectOpen returns (bool success) { + require(_amount > 0); + contributions[_contributor] += _amount; totalAmountRaised += _amount; contributors.push(_contributor); From ed9b6de84d2ba463363ee9045021d844d86c3a2e Mon Sep 17 00:00:00 2001 From: Kayvon Tehranian Date: Mon, 22 Jan 2018 15:14:48 -0800 Subject: [PATCH 11/12] Add internal property to track balances of contributors --- contracts/Project.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/contracts/Project.sol b/contracts/Project.sol index 495bcc3..f84ad21 100644 --- a/contracts/Project.sol +++ b/contracts/Project.sol @@ -10,6 +10,8 @@ contract Project { address[] public contributors; mapping (address => uint) public contributions; + /* Internal properties */ + mapping (address => uint) internal balances; /* Events */ event ContributionMade(address contributor, uint amount); event GoalReached(uint amount, address beneficiary); @@ -32,13 +34,14 @@ contract Project { contributions[_contributor] += _amount; totalAmountRaised += _amount; + balances[_contributor] += _amount; contributors.push(_contributor); ContributionMade(_contributor, _amount); return true; } function balanceOf(address _contributor) public view returns (uint balance) { - return contributions[_contributor]; + return balances[_contributor]; } function isFullyFunded() public view returns (bool funded) { From ef6ce307881afc21237cd4a1d9af71cd33097105 Mon Sep 17 00:00:00 2001 From: Kayvon Tehranian Date: Mon, 22 Jan 2018 15:15:02 -0800 Subject: [PATCH 12/12] Implement `withdraw --- contracts/Project.sol | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/contracts/Project.sol b/contracts/Project.sol index f84ad21..01c0af6 100644 --- a/contracts/Project.sol +++ b/contracts/Project.sol @@ -12,6 +12,8 @@ contract Project { /* Internal properties */ mapping (address => uint) internal balances; + bool hasOwnerWithdrawnFunds = false; + /* Events */ event ContributionMade(address contributor, uint amount); event GoalReached(uint amount, address beneficiary); @@ -52,4 +54,28 @@ contract Project { return contributors.length; } + function withdraw() public afterDeadline { + if (isFullyFunded()) { + withdrawForOwner(); + } else { + withdrawForContributor(); + } + } + + /* Internal functions */ + + function withdrawForContributor() internal { + if (balances[msg.sender] > 0) { + msg.sender.transfer(balances[msg.sender]); + balances[msg.sender] = 0; + } + } + + function withdrawForOwner() internal { + if (!hasOwnerWithdrawnFunds) { + owner.transfer(totalAmountRaised); + hasOwnerWithdrawnFunds = true; + } + } + }