Ethereum and dApp development workshop
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

broodfonds.sol 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. pragma solidity ^0.4.0;
  2. // TODO: There currently is no incentive to call in better. A
  3. // participant that calls in sick could be punished for the time being
  4. // sick, e.g. by burning credits each month. This would be an
  5. // incentive to callInBetter before one month has passed.
  6. // This is currently the biggest problem with the contract, because it
  7. // is a fundamental issue.
  8. contract BroodFonds {
  9. // A participant is a member that can deposit and withdraw.
  10. struct Participant {
  11. uint256 calledInSick;
  12. uint256 credit;
  13. uint256 lastAccessed;
  14. bool exists;
  15. mapping(address => int8) votes;
  16. }
  17. // The chairperson is the creator. This makes the contract less
  18. // democratit and centralised. But it allows for some initial tweaking
  19. // and simple secutiry measures.
  20. address public chairperson;
  21. // TODO: this helper should better be a simple summing method, since
  22. // that is (probably) cheaper than storing it in the State.
  23. uint public totalBalance;
  24. // Contains the list of participants in the state.
  25. // Note that there currently is no way to remove participants, nor to
  26. // alter an existing participant.
  27. // TODO: implement full CRUD on participants, but in such a way that
  28. // it is simple, clear and secure.
  29. mapping(address => Participant) public participants;
  30. // TODO: this helper should better be a simple summing method, since
  31. // that is (probably) cheaper than storing it in the State.
  32. Participant private participant;
  33. uint private _numParticipantsMax = 50;
  34. uint private _numParticipants = 0;
  35. // Set the chairperson on init.
  36. // Note that the chairman is not added as participant. This is by
  37. // design, so that a chairman can be an external entity managing this
  38. // contract.
  39. function BroodFonds() public {
  40. chairperson = msg.sender;
  41. }
  42. // Event emitted when a member calls in sick.
  43. // TODO: Add events for deposits, participant additions, withdrawals,
  44. // acknowledgements, denials etc.
  45. event CalledInSick(address caller);
  46. // Checks if the called is whitelisted as participant.
  47. modifier ifParticipant() {
  48. require(participants[msg.sender].exists);
  49. _;
  50. }
  51. // Checks if the caller is the chairperson.
  52. // TODO: an improvement that makes the contract less centralised, is
  53. // to allow any participant to modify the contract, not just an
  54. // allocated member. This, however, opens up many security
  55. // considerations and should probably be handled by an external
  56. // management contract. For example one that allows voting on changes.
  57. modifier ifChairperson() {
  58. require(msg.sender == chairperson);
  59. _;
  60. }
  61. // Checks if the deposit is of the right amount.
  62. // TODO: should check if this month's deposit has already been made
  63. // and deny more deposits.
  64. // TODO: implement the full table of possible deposit-amounts.
  65. modifier ifDepositCorrect() {
  66. require(msg.value == 100);
  67. _;
  68. }
  69. // Checks if the amount requested matches the amount that I am allowed
  70. // to get.
  71. // TODO: should use the full table of deposit amounts instead of
  72. // hardcoding 100 wei.
  73. modifier ifWithdrawalCorrect() {
  74. require(msg.value == 100 * 22);
  75. _;
  76. }
  77. // Chairperson may add new participants once.
  78. // Up to a maximum amount of participants.
  79. function addParticipant(address _participant) public {
  80. require((msg.sender == chairperson) &&
  81. !participants[_participant].exists &&
  82. _numParticipants <= _numParticipantsMax
  83. );
  84. participants[_participant] = Participant({
  85. calledInSick: 0,
  86. credit: 0,
  87. lastAccessed: 0,
  88. exists: true
  89. });
  90. _numParticipants = _numParticipants + 1;
  91. }
  92. // Any participant can deposit 100 wei, once a month.
  93. // If a participant deposits, her credit will be increased.
  94. function deposit() public ifDepositCorrect ifParticipant payable {
  95. participant = participants[msg.sender];
  96. // Only allow one deposit per month;
  97. require(block.timestamp - participant.lastAccessed >= 2629800);
  98. participant.lastAccessed = block.timestamp;
  99. // Reset the participant's credit to the max credit;
  100. participant.credit = msg.value * 22;
  101. totalBalance = totalBalance + msg.value;
  102. }
  103. // A participant can call in sick. This adds a timestamp to the state
  104. // which starts counting down, so that one month later, the
  105. // participant can start withdrawing funds.
  106. // TODO: cast a vote on yourself when calling in sick.
  107. function callInSick() ifParticipant public {
  108. participants[msg.sender].calledInSick = block.timestamp;
  109. CalledInSick(msg.sender);
  110. }
  111. // A particpant that called in sick, can be voted on.
  112. // In order to withdraw funds, at least half of the (other) participants
  113. // need to acknowledge your sick-call.
  114. function acknowledgeSick(address _participant) ifParticipant public {
  115. require(participants[_participant].exists);
  116. participants[_participant].votes[msg.sender] = 1;
  117. }
  118. // A particpant that called in sick, can be voted on.
  119. // In order to withdraw funds, at least half of the (other) participants
  120. // need to acknowledge your sick-call.
  121. // A participant can either acknowledge, abstain (no vote) or deny. A
  122. // denial will substract from the votes.
  123. function denySick(address _participant) ifParticipant public {
  124. require(participants[_participant].exists);
  125. participants[_participant].votes[msg.sender] = -1;
  126. }
  127. // Once a participant is better, she can call in as being better.
  128. // All counters and votes will be reset.
  129. function callInBetter() ifParticipant public {
  130. participant = participants[msg.sender];
  131. participant.calledInSick = 0;
  132. // TODO: reset the votes mapping.
  133. // This is a bug!
  134. }
  135. // Allow a member to withdraw.
  136. // TODO: the voting is not reset in callInBetter, so we don't use it
  137. // here, yet: the entire voting is ignored until it is properly
  138. // implemented!
  139. function withdraw(uint amount) ifWithdrawalCorrect ifParticipant public {
  140. participant = participants[msg.sender];
  141. require((participant.calledInSick > 0) &&
  142. (participant.calledInSick <= block.timestamp - 2629800) &&
  143. (participant.credit >= amount) &&
  144. (totalBalance >= amount));
  145. participant.credit = participant.credit - amount;
  146. msg.sender.transfer(amount);
  147. }
  148. // Kill-switch to remove all funds.
  149. // TODO: instead of selfdestruct which will wire all the funds to the
  150. // manager, an additional layer could be added which wires all
  151. // remaining funds to all participants based on their personal credit
  152. // ratio. This, however is complex and a potential for bugs, so a
  153. // catch-all, simple kill-switch should remain in place.
  154. function kill() public ifChairperson {
  155. selfdestruct(chairperson);
  156. }
  157. }