diff --git a/contracts/Project.sol b/contracts/Project.sol index 9fda19a..01c0af6 100644 --- a/contracts/Project.sol +++ b/contracts/Project.sol @@ -2,31 +2,48 @@ pragma solidity ^0.4.2; contract Project { - /* public properties */ + /* Public properties */ address public owner; uint public target; + uint public deadline; uint public totalAmountRaised; address[] public contributors; mapping (address => uint) public contributions; - /* events */ + /* Internal properties */ + mapping (address => uint) internal balances; + bool hasOwnerWithdrawnFunds = false; + + /* Events */ event ContributionMade(address contributor, uint amount); + event GoalReached(uint amount, address beneficiary); - function Project(address _owner, uint _target) public { + /* Constructor */ + function Project(address _owner, uint _target, uint _durationInHours) public { owner = _owner; target = _target; + deadline = now + _durationInHours * 1 hours; } - function contribute(address _contributor, uint _amount) public returns (bool success) { + /* Modifiers */ + modifier afterDeadline() { if (now >= deadline) _; } + modifier projectOpen() { if (deadline < now) _; } + + /* Publicly-exposed interface */ + + function contribute(address _contributor, uint _amount) public projectOpen returns (bool success) { + require(_amount > 0); + 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) { @@ -37,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; + } + } + } 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 () => {}); });