Skip to content

Commit

Permalink
Only join (#780)
Browse files Browse the repository at this point in the history
* joinandquit fix

* opt

* remove ragequit and  refund

* tests

* tests

* spelling

* bump version to   "version": "0.1.2-rc.5",
  • Loading branch information
orenyodfat authored Aug 8, 2020
1 parent 572d842 commit 780bbed
Show file tree
Hide file tree
Showing 8 changed files with 648 additions and 946 deletions.
114 changes: 15 additions & 99 deletions contracts/schemes/JoinAndQuit.sol → contracts/schemes/Join.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,17 @@ import "./CommonInterface.sol";
/**
* @title A scheme for join in a dao.
* - A member can be proposed to join in by sending a min amount of fee.
* - A member can ask to quite (RageQuit) a dao on any time.
* - A member can donate to a dao.
*/
contract JoinAndQuit is
contract Join is
VotingMachineCallbacks,
ProposalExecuteInterface,
CommonInterface {
using SafeMath for uint;
using SafeERC20 for IERC20;
using StringUtil for string;

enum MemeberState { None, Candidate, Accepted, Rejected, ReputationRedeemed }
enum MemberState { None, Candidate, Accepted, Rejected, ReputationRedeemed }

event JoinInProposal(
address indexed _avatar,
Expand All @@ -34,18 +33,6 @@ contract JoinAndQuit is
address indexed _avatar
);

event RageQuit(
address indexed _avatar,
address indexed _rageQuitter,
uint256 indexed _refund
);

event Refund(
address indexed _avatar,
address indexed _beneficiary,
uint256 indexed _refund
);

event RedeemReputation(
address indexed _avatar,
bytes32 indexed _proposalId,
Expand All @@ -59,22 +46,15 @@ contract JoinAndQuit is
uint256 funding;
}

struct MemberFund {
MemeberState state;
bool rageQuit;
uint256 funding;
}

mapping(bytes32=>Proposal) public proposals;
mapping(address=>MemberFund) public fundings;
mapping(address=>MemberState) public membersState;

IERC20 public fundingToken;
uint256 public minFeeToJoin;
uint256 public memberReputation;
uint256 public fundingGoal;
uint256 public fundingGoalDeadline;
uint256 public totalDonation;
bool public rageQuitEnable;

/**
* @dev initialize
Expand All @@ -89,7 +69,6 @@ contract JoinAndQuit is
if this param is zero so the repution will be allocated proportional to the fee paid
* @param _fundingGoal the funding goal
* @param _fundingGoalDeadline the funding goal deadline
* @param _rageQuitEnable rageQuit enabling flag
*/
function initialize(
Avatar _avatar,
Expand All @@ -101,8 +80,7 @@ contract JoinAndQuit is
uint256 _minFeeToJoin,
uint256 _memberReputation,
uint256 _fundingGoal,
uint256 _fundingGoalDeadline,
bool _rageQuitEnable
uint256 _fundingGoalDeadline
)
external
{
Expand All @@ -112,7 +90,6 @@ contract JoinAndQuit is
memberReputation = _memberReputation;
fundingGoal = _fundingGoal;
fundingGoalDeadline = _fundingGoalDeadline;
rageQuitEnable = _rageQuitEnable;
}

/**
Expand All @@ -127,13 +104,12 @@ contract JoinAndQuit is
returns(bool) {
Proposal memory proposal = proposals[_proposalId];
require(proposal.proposedMember != address(0), "not a valid proposal");
require(fundings[proposal.proposedMember].state == MemeberState.Candidate, "proposal already been executed");
require(membersState[proposal.proposedMember] == MemberState.Candidate, "member is not a cadidate");

bool success;
// Check if vote was successful:
if ((_decision == 1) && (avatar.nativeReputation().balanceOf(proposal.proposedMember) == 0)) {
fundings[proposal.proposedMember].state = MemeberState.Accepted;
fundings[proposal.proposedMember].funding = proposal.funding;
membersState[proposal.proposedMember] = MemberState.Accepted;
totalDonation = totalDonation.add(proposal.funding);
if (fundingToken == IERC20(0)) {
// solhint-disable-next-line
Expand All @@ -145,7 +121,7 @@ contract JoinAndQuit is
//this should be called/check after the transfer to the avatar.
setFundingGoalReachedFlag();
} else {
fundings[proposal.proposedMember].state = MemeberState.Rejected;
membersState[proposal.proposedMember] = MemberState.Rejected;
if (fundingToken == IERC20(0)) {
// solhint-disable-next-line
(success, ) = proposal.proposedMember.call{value:proposal.funding}("");
Expand Down Expand Up @@ -174,11 +150,11 @@ contract JoinAndQuit is
returns(bytes32)
{
address proposer = msg.sender;
require(fundings[proposer].state != MemeberState.Candidate, "already a candidate");
require(fundings[proposer].state != MemeberState.Accepted, "accepted and not redeemed yet");
require(avatar.nativeReputation().balanceOf(proposer) == 0, "already a member");
require(_feeAmount >= minFeeToJoin, "_feeAmount should be >= then the minFeeToJoin");
fundings[proposer].state = MemeberState.Candidate;
require(membersState[proposer] != MemberState.Candidate, "proposer is already a candidate");
require(membersState[proposer] != MemberState.Accepted, "proposer is accepted and not redeemed yet");
require(avatar.nativeReputation().balanceOf(proposer) == 0, "proposer is already a member");
require(_feeAmount >= minFeeToJoin, "_feeAmount should be >= than the minFeeToJoin");
membersState[proposer] = MemberState.Candidate;
if (fundingToken == IERC20(0)) {
require(_feeAmount == msg.value, "ETH received should match the _feeAmount");
} else {
Expand Down Expand Up @@ -212,11 +188,11 @@ contract JoinAndQuit is
function redeemReputation(bytes32 _proposalId) public returns(uint256 reputation) {
Proposal memory proposal = proposals[_proposalId];
require(proposal.proposedMember != address(0), "no member to redeem");
require(!fundings[proposal.proposedMember].rageQuit, "member already rageQuit");
require(fundings[proposal.proposedMember].state == MemeberState.Accepted, "member not accepeted");
require(membersState[proposal.proposedMember] == MemberState.Accepted, "member not accepted");
//set proposal proposedMember to zero to prevent reentrancy attack.
proposals[_proposalId].proposedMember = address(0);
fundings[proposal.proposedMember].state = MemeberState.ReputationRedeemed;
proposals[_proposalId].proposedMember = address(0);
membersState[proposal.proposedMember] = MemberState.ReputationRedeemed;
if (memberReputation == 0) {
reputation = proposal.funding;
} else {
Expand All @@ -228,49 +204,6 @@ contract JoinAndQuit is
emit RedeemReputation(address(avatar), _proposalId, proposal.proposedMember, reputation);
}

/**
* @dev refund refund donator if the the funding goal did not reached till the funding goal deadline.
* @return refundAmount the refund amount
*/
function refund() public returns(uint256 refundAmount) {
// solhint-disable-next-line not-rely-on-time
require(now > fundingGoalDeadline, "can refund only after fundingGoalDeadline");
require(
(avatar.db(FUNDED_BEFORE_DEADLINE_KEY).hashCompareWithLengthCheck(FUNDED_BEFORE_DEADLINE_VALUE) == false),
"can refund only if funding goal not reached");
require(fundings[msg.sender].funding > 0, "no funds to refund");
refundAmount = fundings[msg.sender].funding;
fundings[msg.sender].funding = 0;
sendToBeneficiary(refundAmount, msg.sender);
emit Refund(address(avatar), msg.sender, refundAmount);
}

/**
* @dev rageQuit quit from the dao.
* can be done on any time
* REFUND = USER_DONATION * CURRENT_DAO_BALANCE / TOTAL_DONATIONS
* @return refundAmount the refund amount
*/
function rageQuit() public returns(uint256 refundAmount) {
require(rageQuitEnable, "RageQuit disabled");
require(fundings[msg.sender].funding > 0, "no fund to RageQuit");
uint256 userDonation = fundings[msg.sender].funding;
fundings[msg.sender].funding = 0;
fundings[msg.sender].rageQuit = true;
if (fundingToken == IERC20(0)) {
refundAmount = userDonation.mul(address(avatar.vault()).balance).div(totalDonation);
} else {
refundAmount = userDonation.mul(fundingToken.balanceOf(address(avatar))).div(totalDonation);
}
totalDonation = totalDonation.sub(userDonation);
uint256 msgSenderReputation = avatar.nativeReputation().balanceOf(msg.sender);
require(
Controller(
avatar.owner()).burnReputation(msgSenderReputation, msg.sender));
sendToBeneficiary(refundAmount, msg.sender);
emit RageQuit(address(avatar), msg.sender, refundAmount);
}

/**
* @dev setFundingGoalReachedFlag check if funding goal reached.
*/
Expand All @@ -294,21 +227,4 @@ contract JoinAndQuit is
}
}

/**
* @dev sendToBeneficiary send amount of eth or token to beneficiary
* @param _amount the amount to send
* @param _beneficiary the beneficiary
*/
function sendToBeneficiary(uint256 _amount, address payable _beneficiary) private {
if (fundingToken == IERC20(0)) {
require(
Controller(
avatar.owner()).sendEther(_amount, _beneficiary), "send ether failed");
} else {
require(
Controller(
avatar.owner()).externalTokenTransfer(fundingToken, _beneficiary, _amount), "send token failed");
}
}

}
22 changes: 11 additions & 11 deletions contracts/utils/Redeemer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.6.12;
import "../schemes/ContributionReward.sol";
import "../schemes/ContributionRewardExt.sol";
import "../schemes/FundingRequest.sol";
import "../schemes/JoinAndQuit.sol";
import "../schemes/Join.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/IERC20.sol";


Expand Down Expand Up @@ -125,11 +125,11 @@ contract Redeemer {
* @dev helper to redeem rewards for a proposal
* It calls execute on the proposal if it is not yet executed.
* It tries to redeem reputation and stake from the GenesisProtocol.
* It tries to redeem proposal rewards from the JoinAndQuit scheme.
* It tries to redeem proposal rewards from the Join scheme.
* This function does not emit events.
* A client should listen to GenesisProtocol and JoinAndQuit redemption events
* A client should listen to GenesisProtocol and Join redemption events
* to monitor redemption operations.
* @param _joinAndQuit joinAndQuit scheme
* @param _join join scheme
* @param _genesisProtocol genesisProtocol
* @param _proposalId the ID of the voting in the voting machine
* @param _beneficiary beneficiary
Expand All @@ -145,9 +145,9 @@ contract Redeemer {
* @return winningVote
* 1 - executed or closed and the winning vote is YES
* 2 - executed or closed and the winning vote is NO
* @return joinAndQuitReputationReward Reputation - from JoinAndQuit reputation reward
* @return joinReputationReward Reputation - from Join reputation reward
*/
function redeemJoinAndQuit(JoinAndQuit _joinAndQuit,
function redeemJoin(Join _join,
GenesisProtocol _genesisProtocol,
bytes32 _proposalId,
address _beneficiary)
Expand All @@ -156,14 +156,14 @@ contract Redeemer {
uint[2] memory gpDaoBountyReward,
bool executed,
uint256 winningVote,
uint256 joinAndQuitReputationReward
uint256 joinReputationReward
)
{
bool callJoinAndQuit;
(gpRewards, gpDaoBountyReward, executed, winningVote, callJoinAndQuit) =
bool callJoin;
(gpRewards, gpDaoBountyReward, executed, winningVote, callJoin) =
genesisProtocolRedeem(_genesisProtocol, _proposalId, _beneficiary);
if (callJoinAndQuit) {
joinAndQuitReputationReward = _joinAndQuit.redeemReputation(_proposalId);
if (callJoin) {
joinReputationReward = _join.redeemReputation(_proposalId);
}
}

Expand Down
Loading

0 comments on commit 780bbed

Please sign in to comment.