From e9052b8c8376f93e5fa9a5f44751d7c9c1da3124 Mon Sep 17 00:00:00 2001 From: Oren Sokolowsky Date: Sun, 2 Aug 2020 22:25:12 +0300 Subject: [PATCH 1/7] joinandquit fix --- contracts/schemes/JoinAndQuit.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/schemes/JoinAndQuit.sol b/contracts/schemes/JoinAndQuit.sol index 0ce2aa17..9bf265dd 100644 --- a/contracts/schemes/JoinAndQuit.sol +++ b/contracts/schemes/JoinAndQuit.sol @@ -215,7 +215,11 @@ contract JoinAndQuit is require(!fundings[proposal.proposedMember].rageQuit, "member already rageQuit"); require(fundings[proposal.proposedMember].state == MemeberState.Accepted, "member not accepeted"); //set proposal proposedMember to zero to prevent reentrancy attack. +<<<<<<< HEAD proposals[_proposalId].proposedMember = address(0); +======= + proposal.proposedMember = address(0); +>>>>>>> joinandquit fix fundings[proposal.proposedMember].state = MemeberState.ReputationRedeemed; if (memberReputation == 0) { reputation = proposal.funding; From ec9125df0779d18c49202d8e139c1b81352d9b92 Mon Sep 17 00:00:00 2001 From: Oren Sokolowsky Date: Sun, 2 Aug 2020 22:44:11 +0300 Subject: [PATCH 2/7] opt --- contracts/schemes/JoinAndQuit.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/schemes/JoinAndQuit.sol b/contracts/schemes/JoinAndQuit.sol index 9bf265dd..fd96995e 100644 --- a/contracts/schemes/JoinAndQuit.sol +++ b/contracts/schemes/JoinAndQuit.sol @@ -215,11 +215,15 @@ contract JoinAndQuit is require(!fundings[proposal.proposedMember].rageQuit, "member already rageQuit"); require(fundings[proposal.proposedMember].state == MemeberState.Accepted, "member not accepeted"); //set proposal proposedMember to zero to prevent reentrancy attack. +<<<<<<< HEAD <<<<<<< HEAD proposals[_proposalId].proposedMember = address(0); ======= proposal.proposedMember = address(0); >>>>>>> joinandquit fix +======= + proposals[_proposalId].proposedMember = address(0); +>>>>>>> opt fundings[proposal.proposedMember].state = MemeberState.ReputationRedeemed; if (memberReputation == 0) { reputation = proposal.funding; From 0056cf019bd91decd6d35530c0cba3bfc0aa4281 Mon Sep 17 00:00:00 2001 From: Oren Sokolowsky Date: Sun, 2 Aug 2020 23:31:19 +0300 Subject: [PATCH 3/7] remove ragequit and refund --- .../schemes/{JoinAndQuit.sol => Join.sol} | 109 +--- contracts/utils/Redeemer.sol | 22 +- test/helpers.js | 6 +- test/join.js | 543 ++++++++++++++++++ 4 files changed, 571 insertions(+), 109 deletions(-) rename contracts/schemes/{JoinAndQuit.sol => Join.sol} (66%) create mode 100644 test/join.js diff --git a/contracts/schemes/JoinAndQuit.sol b/contracts/schemes/Join.sol similarity index 66% rename from contracts/schemes/JoinAndQuit.sol rename to contracts/schemes/Join.sol index fd96995e..272a6a2f 100644 --- a/contracts/schemes/JoinAndQuit.sol +++ b/contracts/schemes/Join.sol @@ -9,10 +9,9 @@ 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 { @@ -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, @@ -59,14 +46,8 @@ 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=>MemeberState) public membersState; IERC20 public fundingToken; uint256 public minFeeToJoin; @@ -74,7 +55,6 @@ contract JoinAndQuit is uint256 public fundingGoal; uint256 public fundingGoalDeadline; uint256 public totalDonation; - bool public rageQuitEnable; /** * @dev initialize @@ -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, @@ -101,8 +80,7 @@ contract JoinAndQuit is uint256 _minFeeToJoin, uint256 _memberReputation, uint256 _fundingGoal, - uint256 _fundingGoalDeadline, - bool _rageQuitEnable + uint256 _fundingGoalDeadline ) external { @@ -112,7 +90,6 @@ contract JoinAndQuit is memberReputation = _memberReputation; fundingGoal = _fundingGoal; fundingGoalDeadline = _fundingGoalDeadline; - rageQuitEnable = _rageQuitEnable; } /** @@ -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] == MemeberState.Candidate, "proposal already been executed"); 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] = MemeberState.Accepted; totalDonation = totalDonation.add(proposal.funding); if (fundingToken == IERC20(0)) { // solhint-disable-next-line @@ -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] = MemeberState.Rejected; if (fundingToken == IERC20(0)) { // solhint-disable-next-line (success, ) = proposal.proposedMember.call{value:proposal.funding}(""); @@ -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(membersState[proposer] != MemeberState.Candidate, "already a candidate"); + require(membersState[proposer] != 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; + membersState[proposer] = MemeberState.Candidate; if (fundingToken == IERC20(0)) { require(_feeAmount == msg.value, "ETH received should match the _feeAmount"); } else { @@ -212,12 +188,12 @@ 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] == MemeberState.Accepted, "member not accepeted"); //set proposal proposedMember to zero to prevent reentrancy attack. <<<<<<< HEAD <<<<<<< HEAD proposals[_proposalId].proposedMember = address(0); +<<<<<<< HEAD:contracts/schemes/JoinAndQuit.sol ======= proposal.proposedMember = address(0); >>>>>>> joinandquit fix @@ -225,6 +201,9 @@ contract JoinAndQuit is proposals[_proposalId].proposedMember = address(0); >>>>>>> opt fundings[proposal.proposedMember].state = MemeberState.ReputationRedeemed; +======= + membersState[proposal.proposedMember] = MemeberState.ReputationRedeemed; +>>>>>>> remove ragequit and refund:contracts/schemes/Join.sol if (memberReputation == 0) { reputation = proposal.funding; } else { @@ -236,49 +215,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. */ @@ -302,21 +238,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"); - } - } - } diff --git a/contracts/utils/Redeemer.sol b/contracts/utils/Redeemer.sol index 0ee9109d..5f59b535 100644 --- a/contracts/utils/Redeemer.sol +++ b/contracts/utils/Redeemer.sol @@ -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"; @@ -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 @@ -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) @@ -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); } } diff --git a/test/helpers.js b/test/helpers.js index 581e9ca4..3ae6f5ee 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -34,7 +34,7 @@ const SignalScheme = artifacts.require("./SignalScheme.sol"); const ReputationFromToken = artifacts.require("./ReputationFromToken.sol"); const VoteInOrganization = artifacts.require("./VoteInOrganizationScheme.sol"); const ARCVotingMachineCallbacksMock = artifacts.require("./ARCVotingMachineCallbacksMock.sol"); -const JoinAndQuit = artifacts.require("./JoinAndQuit.sol"); +const Join = artifacts.require("./Join.sol"); const FundingRequest = artifacts.require("./FundingRequest.sol"); const Dictator = artifacts.require("./Dictator.sol"); const ReputationAdmin = artifacts.require("./ReputationAdmin.sol"); @@ -156,7 +156,7 @@ const SOME_ADDRESS = '0x1000000000000000000000000000000000000000'; registration.reputationFromToken = await ReputationFromToken.new(); registration.voteInOrganization = await VoteInOrganization.new(); registration.arcVotingMachineCallbacksMock = await ARCVotingMachineCallbacksMock.new(); - registration.joinAndQuit = await JoinAndQuit.new(); + registration.join = await Join.new(); registration.fundingRequest = await FundingRequest.new(); registration.rewarderMock = await RewarderMock.new(); registration.dictator = await Dictator.new(); @@ -190,7 +190,7 @@ const SOME_ADDRESS = '0x1000000000000000000000000000000000000000'; await implementationDirectory.setImplementation("ReputationFromToken",registration.reputationFromToken.address); await implementationDirectory.setImplementation("VoteInOrganization",registration.voteInOrganization.address); await implementationDirectory.setImplementation("ARCVotingMachineCallbacksMock",registration.arcVotingMachineCallbacksMock.address); - await implementationDirectory.setImplementation("JoinAndQuit",registration.joinAndQuit.address); + await implementationDirectory.setImplementation("Join",registration.join.address); await implementationDirectory.setImplementation("FundingRequest",registration.fundingRequest.address); await implementationDirectory.setImplementation("Dictator",registration.dictator.address); await implementationDirectory.setImplementation("ReputationAdmin",registration.reputationAdmin.address); diff --git a/test/join.js b/test/join.js new file mode 100644 index 00000000..2d843a34 --- /dev/null +++ b/test/join.js @@ -0,0 +1,543 @@ +const helpers = require("./helpers"); +const Join = artifacts.require("./Join.sol"); +const ERC20Mock = artifacts.require('./test/ERC20Mock.sol'); +const Avatar = artifacts.require("./Avatar.sol"); +const Redeemer = artifacts.require("./Redeemer.sol"); + +class JoinParams { + constructor() { + } +} + +const addMember = async function(accounts,_testSetup,_fee,_from) { + var tx = await _testSetup.join.proposeToJoin( + "description-hash", + _fee, + {value:_fee,from:_from}); + + //Vote with reputation to trigger execution + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + await _testSetup.joinParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + return tx; +}; + +const avatarBalance = async function(_testSetup) { + let avatar = await Avatar.at(_testSetup.org.avatar.address); + var vault = await avatar.vault(); + return await web3.eth.getBalance(vault); +}; + +const setupJoin = async function( + accounts, + genesisProtocol, + token, + _fundingToken, + _minFeeToJoin, + _memberReputation, + _fundingGoal, + _fundingGoalDeadline, + ) { + var joinParams = new JoinParams(); + + if (genesisProtocol === true) { + joinParams.votingMachine = await helpers.setupGenesisProtocol(accounts,token,helpers.NULL_ADDRESS); + joinParams.initdata = await new web3.eth.Contract(registration.join.abi) + .methods + .initialize(helpers.NULL_ADDRESS, + joinParams.votingMachine.genesisProtocol.address, + joinParams.votingMachine.uintArray, + joinParams.votingMachine.voteOnBehalf, + helpers.NULL_HASH, + _fundingToken, + _minFeeToJoin, + _memberReputation, + _fundingGoal, + _fundingGoalDeadline) + .encodeABI(); + } else { + joinParams.votingMachine = await helpers.setupAbsoluteVote(helpers.NULL_ADDRESS,50); + joinParams.initdata = await new web3.eth.Contract(registration.join.abi) + .methods + .initialize(helpers.NULL_ADDRESS, + joinParams.votingMachine.absoluteVote.address, + [0,0,0,0,0,0,0,0,0,0,0], + helpers.NULL_ADDRESS, + joinParams.votingMachine.params, + _fundingToken, + _minFeeToJoin, + _memberReputation, + _fundingGoal, + _fundingGoalDeadline) + .encodeABI(); + } + return joinParams; +}; +var registration; +const setup = async function (accounts, + ethFunding = false, + genesisProtocol = false, + tokenAddress=helpers.NULL_ADDRESS, + minFeeToJoin = 100, + memberReputation = 100, + fundingGoal = 1000, + fundingGoalDeadline = 3000) { + var testSetup = new helpers.TestSetup(); + testSetup.standardTokenMock = await ERC20Mock.new(accounts[0],100000); + registration = await helpers.registerImplementation(); + testSetup.reputationArray = [7000]; + testSetup.proxyAdmin = accounts[5]; + testSetup.fundingGoalDeadline = (await web3.eth.getBlock("latest")).timestamp + fundingGoalDeadline; + testSetup.minFeeToJoin = minFeeToJoin; + testSetup.memberReputation = memberReputation; + testSetup.fundingGoal = fundingGoal; + + var fundPath = testSetup.standardTokenMock.address; + if (ethFunding === true) { + fundPath = helpers.NULL_ADDRESS; + } + + testSetup.joinParams= await setupJoin( + accounts, + genesisProtocol, + tokenAddress, + fundPath, + minFeeToJoin, + memberReputation, + fundingGoal, + testSetup.fundingGoalDeadline); + + var permissions = "0x00000000"; + [testSetup.org,tx] = await helpers.setupOrganizationWithArraysDAOFactory(testSetup.proxyAdmin, + accounts, + registration, + [accounts[2]], + [0], + testSetup.reputationArray, + 0, + [web3.utils.fromAscii("Join")], + testSetup.joinParams.initdata, + [helpers.getBytesLength(testSetup.joinParams.initdata)], + [permissions], + "metaData"); + + testSetup.join = await Join.at(await helpers.getSchemeAddress(registration.daoFactory.address,tx)); + + await testSetup.standardTokenMock.transfer(accounts[3],10000); + + return testSetup; +}; +contract('Join', accounts => { + + it("initialize", async function() { + var testSetup = await setup(accounts); + assert.equal(await testSetup.join.votingMachine(),testSetup.joinParams.votingMachine.absoluteVote.address); + assert.equal(await testSetup.join.fundingGoalDeadline(),testSetup.fundingGoalDeadline); + }); + + it("propose log", async function() { + var testSetup = await setup(accounts); + await testSetup.standardTokenMock.approve(testSetup.join.address,testSetup.minFeeToJoin,{from:accounts[3]}); + + var tx = await testSetup.join.proposeToJoin( + "description-hash", + testSetup.minFeeToJoin, + {from:accounts[3]}); + + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.join.address),testSetup.minFeeToJoin); + assert.equal(tx.logs.length, 1); + assert.equal(tx.logs[0].event, "JoinInProposal"); + assert.equal(tx.logs[0].args._avatar, testSetup.org.avatar.address); + assert.equal(tx.logs[0].args._descriptionHash, "description-hash"); + assert.equal(tx.logs[0].args._proposedMember, accounts[3]); + assert.equal(tx.logs[0].args._feeAmount, testSetup.minFeeToJoin); + }); + + it("propose log with eth", async function() { + var testSetup = await setup(accounts,true); + + var tx = await testSetup.join.proposeToJoin( + "description-hash", + testSetup.minFeeToJoin, + + {value:testSetup.minFeeToJoin,from:accounts[3]}); + + assert.equal(await web3.eth.getBalance(testSetup.join.address),testSetup.minFeeToJoin); + assert.equal(tx.logs.length, 1); + assert.equal(tx.logs[0].event, "JoinInProposal"); + assert.equal(tx.logs[0].args._avatar, testSetup.org.avatar.address); + assert.equal(tx.logs[0].args._descriptionHash, "description-hash"); + assert.equal(tx.logs[0].args._proposedMember, accounts[3]); + assert.equal(tx.logs[0].args._feeAmount, testSetup.minFeeToJoin); + }); + + it("propose with eth should be equal to _feeAmount", async function() { + var testSetup = await setup(accounts,true); + + try { + await testSetup.join.proposeToJoin( + "description-hash", + testSetup.minFeeToJoin, + + {value:testSetup.minFeeToJoin+1,from:accounts[3]}); + assert(false, 'should be equal to _feeAmount'); + } catch (ex) { + helpers.assertVMException(ex); + } + }); + + + it("propose cannot add a member if already is a candidate", async function() { + var testSetup = await setup(accounts); + + await testSetup.standardTokenMock.approve(testSetup.join.address,testSetup.minFeeToJoin*2); + await testSetup.join.proposeToJoin( + "description-hash", + testSetup.minFeeToJoin); + + try { + await testSetup.join.proposeToJoin( + "description-hash", + testSetup.minFeeToJoin); + assert(false, 'proposer already is a candidate'); + } catch (ex) { + helpers.assertVMException(ex); + } + }); + + it("propose cannot add a member if member allready has reputation", async function() { + var testSetup = await setup(accounts); + var candidate = accounts[3]; + await testSetup.standardTokenMock.approve(testSetup.join.address, + testSetup.minFeeToJoin*2, + {from:candidate}); + var tx = await testSetup.join.proposeToJoin( + "description-hash", + testSetup.minFeeToJoin, + {from:candidate}); + //Vote with reputation to trigger execution + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + await testSetup.joinParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + + await testSetup.join.redeemReputation(proposalId); + + try { + await testSetup.join.proposeToJoin( + "description-hash", + testSetup.minFeeToJoin, + {from:candidate}); + assert(false, 'proposer already have reputation'); + } catch (ex) { + helpers.assertVMException(ex); + } + }); + + + it("proposeJoin check minFeeToJoin", async() => { + var testSetup = await setup(accounts); + await testSetup.standardTokenMock.approve(testSetup.join.address,testSetup.minFeeToJoin); + try { + await testSetup.join.proposeToJoin( + "description-hash", + testSetup.minFeeToJoin-1); + assert(false, 'minFeeToJoin'); + } catch (ex) { + helpers.assertVMException(ex); + } + }); + + it("proposeJoin check minFeeToJoin with eth", async() => { + var testSetup = await setup(accounts,true); + try { + await testSetup.join.proposeToJoin( + "description-hash", + testSetup.minFeeToJoin-1, + {value:testSetup.minFeeToJoin-1}); + assert(false, 'minFeeToJoin'); + } catch (ex) { + helpers.assertVMException(ex); + } + }); + + it("execute proposeJoin yes ", async function() { + var testSetup = await setup(accounts); + await testSetup.standardTokenMock.approve(testSetup.join.address,testSetup.minFeeToJoin,{from:accounts[3]}); + var tx = await testSetup.join.proposeToJoin( + "description-hash", + testSetup.minFeeToJoin, + {from:accounts[3]}); + + //Vote with reputation to trigger execution + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + await testSetup.joinParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + var state = await testSetup.join.membersState(accounts[3]); + assert.equal(state,2); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.org.avatar.address),testSetup.minFeeToJoin); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.join.address),0); + }); + + it("execute proposeJoin yes with eth", async function() { + var testSetup = await setup(accounts,true); + var tx = await testSetup.join.proposeToJoin( + "description-hash", + testSetup.minFeeToJoin, + {value:testSetup.minFeeToJoin,from:accounts[3]}); + + //Vote with reputation to trigger execution + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + await testSetup.joinParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + var state = await testSetup.join.membersState(accounts[3]); + assert.equal(state,2); + assert.equal(await avatarBalance(testSetup),testSetup.minFeeToJoin); + assert.equal(await web3.eth.getBalance(testSetup.join.address),0); + }); + + it("execute proposeJoin no", async function() { + var testSetup = await setup(accounts); + await testSetup.standardTokenMock.approve(testSetup.join.address,testSetup.minFeeToJoin,{from:accounts[3]}); + var tx = await testSetup.join.proposeToJoin( + "description-hash", + testSetup.minFeeToJoin, + {from:accounts[3]}); + + //Vote with reputation to trigger execution + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + await testSetup.joinParams.votingMachine.absoluteVote.vote(proposalId,2,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + var state = await testSetup.join.membersState(accounts[3]); + assert.equal(state,3); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.org.avatar.address),0); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.join.address),0); + assert.equal(await testSetup.standardTokenMock.balanceOf(accounts[3]),10000); + }); + + + it("execute proposeJoin no with eth", async function() { + var testSetup = await setup(accounts,true); + var tx = await testSetup.join.proposeToJoin( + "description-hash", + testSetup.minFeeToJoin, + {value:testSetup.minFeeToJoin,from:accounts[3]}); + + //Vote with reputation to trigger execution + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + var balanceBefore = await web3.eth.getBalance(accounts[3]); + await testSetup.joinParams.votingMachine.absoluteVote.vote(proposalId,2,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + var state = await testSetup.join.membersState(accounts[3]); + assert.equal(state,3); + assert.equal(await avatarBalance(testSetup),0); + assert.equal(await web3.eth.getBalance(testSetup.join.address),0); + var BN = web3.utils.BN; + var a = new BN(balanceBefore); + var b = new BN(testSetup.minFeeToJoin); + var expectedBalance = a.add(b); + assert.equal(await web3.eth.getBalance(accounts[3]),expectedBalance); + }); + + it("reputation redeem ", async function() { + var testSetup = await setup(accounts); + await testSetup.standardTokenMock.approve(testSetup.join.address,testSetup.minFeeToJoin,{from:accounts[3]}); + var tx = await testSetup.join.proposeToJoin( + "description-hash", + testSetup.minFeeToJoin, + {from:accounts[3]}); + + //Vote with reputation to trigger execution + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + await testSetup.joinParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + tx = await testSetup.join.redeemReputation(proposalId); + assert.equal(tx.logs[0].event, "RedeemReputation"); + assert.equal(tx.logs[0].args._amount, testSetup.memberReputation); + assert.equal(await testSetup.org.reputation.balanceOf(accounts[3]),testSetup.memberReputation); + try { + await testSetup.join.redeemReputation(proposalId); + assert(false, 'cannot redeem twice'); + } catch (ex) { + helpers.assertVMException(ex); + } + }); + + it("reputation redeem memberReputation 0", async function() { + var testSetup = await setup(accounts, false, false, helpers.NULL_ADDRESS, 100, 0); + await testSetup.standardTokenMock.approve(testSetup.join.address,testSetup.minFeeToJoin,{from:accounts[3]}); + var tx = await testSetup.join.proposeToJoin( + "description-hash", + testSetup.minFeeToJoin, + {from:accounts[3]}); + + //Vote with reputation to trigger execution + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + await testSetup.joinParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + tx = await testSetup.join.redeemReputation(proposalId); + assert.equal(tx.logs[0].event, "RedeemReputation"); + assert.equal(tx.logs[0].args._amount, testSetup.minFeeToJoin); + assert.equal(await testSetup.org.reputation.balanceOf(accounts[3]),testSetup.minFeeToJoin); + }); + + it("reputation redeem + genesisProtocol", async function() { + var testSetup = await setup(accounts,false,true); + await testSetup.standardTokenMock.approve(testSetup.join.address,testSetup.minFeeToJoin,{from:accounts[3]}); + var tx = await testSetup.join.proposeToJoin( + "description-hash", + testSetup.minFeeToJoin, + {from:accounts[3]}); + + //Vote with reputation to trigger execution + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + var arcUtils = await Redeemer.new(); + tx = await arcUtils.redeemJoin(testSetup.join.address, + testSetup.joinParams.votingMachine.genesisProtocol.address, + proposalId, + accounts[2]); + + await testSetup.join.getPastEvents('RedeemReputation', { + fromBlock: tx.blockNumber, + toBlock: 'latest' + }) + .then(function(events){ + assert.equal(events.length,0); + }); + await testSetup.joinParams.votingMachine.genesisProtocol.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + tx = await arcUtils.redeemJoin(testSetup.join.address, + testSetup.joinParams.votingMachine.genesisProtocol.address, + proposalId, + accounts[2]); + + await testSetup.join.getPastEvents('RedeemReputation', { + fromBlock: tx.blockNumber, + toBlock: 'latest' + }) + .then(function(events){ + assert.equal(events[0].event,"RedeemReputation"); + assert.equal(events[0].args._amount, testSetup.memberReputation); + + }); + assert.equal(await testSetup.org.reputation.balanceOf(accounts[3]),testSetup.memberReputation); + try { + await testSetup.join.redeemReputation(proposalId); + assert(false, 'cannot redeem twice'); + } catch (ex) { + helpers.assertVMException(ex); + } + }); + + it("reputation cannot redeemed ", async function() { + var testSetup = await setup(accounts); + await testSetup.standardTokenMock.approve(testSetup.join.address,testSetup.minFeeToJoin,{from:accounts[3]}); + var tx = await testSetup.join.proposeToJoin( + "description-hash", + testSetup.minFeeToJoin, + {from:accounts[3]}); + + //Vote with reputation to trigger execution + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + await testSetup.joinParams.votingMachine.absoluteVote.vote(proposalId,2,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + try { + await testSetup.join.redeemReputation(proposalId); + assert(false, 'reputation cannot redeemed'); + } catch (ex) { + helpers.assertVMException(ex); + } + }); + + it("checkFundedBeforeDeadLine ", async function() { + var testSetup = await setup(accounts); + await testSetup.standardTokenMock.approve(testSetup.join.address,testSetup.fundingGoal,{from:accounts[3]}); + let avatar = await Avatar.at(testSetup.org.avatar.address); + let key = await testSetup.join.FUNDED_BEFORE_DEADLINE_KEY(); + let value = await testSetup.join.FUNDED_BEFORE_DEADLINE_VALUE(); + assert.equal(await avatar.db(key),""); + + var tx = await testSetup.join.proposeToJoin( + "description-hash", + testSetup.fundingGoal, + {from:accounts[3]}); + + //Vote with reputation to trigger execution + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + tx = await testSetup.joinParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + await testSetup.join.getPastEvents('FundedBeforeDeadline', { + fromBlock: tx.blockNumber, + toBlock: 'latest' + }) + .then(function(events){ + assert.equal(events[0].event,"FundedBeforeDeadline"); + }); + assert.equal(await avatar.db(key),value); + }); + + + it("checkFundedBeforeDeadLine with eth", async function() { + var testSetup = await setup(accounts,true); + let avatar = await Avatar.at(testSetup.org.avatar.address); + let key = "FUNDED_BEFORE_DEADLINE"; + let value = "TRUE"; + assert.equal(await avatar.db(key),""); + + var tx = await testSetup.join.proposeToJoin( + "description-hash", + testSetup.fundingGoal, + {value:testSetup.fundingGoal,from:accounts[3]}); + + //Vote with reputation to trigger execution + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); + tx = await testSetup.joinParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + await testSetup.join.getPastEvents('FundedBeforeDeadline', { + fromBlock: tx.blockNumber, + toBlock: 'latest' + }) + .then(function(events){ + assert.equal(events[0].event,"FundedBeforeDeadline"); + }); + assert.equal(await avatar.db(key),value); + }); + + it("checkFundedBeforeDeadLine after deadline", async function() { + var testSetup = await setup(accounts); + await testSetup.standardTokenMock.approve(testSetup.join.address,testSetup.fundingGoal,{from:accounts[3]}); + let avatar = await Avatar.at(testSetup.org.avatar.address); + let key = "FUNDED_BEFORE_DEADLINE"; + assert.equal(await avatar.db(key),""); + await helpers.increaseTime(testSetup.fundingGoalDeadline); + await addMember(accounts,testSetup,testSetup.fundingGoal,accounts[3]); + assert.equal(await avatar.db(key),""); + }); + + it("checkFundedBeforeDeadLine after deadline with eth", async function() { + var testSetup = await setup(accounts,true); + let avatar = await Avatar.at(testSetup.org.avatar.address); + let key = "FUNDED_BEFORE_DEADLINE"; + assert.equal(await avatar.db(key),""); + await helpers.increaseTime(testSetup.fundingGoalDeadline); + await addMember(accounts,testSetup,testSetup.fundingGoal,accounts[3]); + assert.equal(await avatar.db(key),""); + }); + + + it("can fund the dao directly and set the goal", async function() { + var testSetup = await setup(accounts); + await testSetup.standardTokenMock.approve(testSetup.join.address,testSetup.fundingGoal,{from:accounts[3]}); + let avatar = await Avatar.at(testSetup.org.avatar.address); + let key = await testSetup.join.FUNDED_BEFORE_DEADLINE_KEY(); + await testSetup.join.FUNDED_BEFORE_DEADLINE_VALUE(); + assert.equal(await avatar.db(key),""); + await testSetup.standardTokenMock.transfer(testSetup.org.avatar.address,testSetup.fundingGoal-1); + var tx = await testSetup.join.setFundingGoalReachedFlag(); + await testSetup.join.getPastEvents('FundedBeforeDeadline', { + fromBlock: tx.blockNumber, + toBlock: 'latest' + }) + .then(function(events){ + assert.equal(events.length,0); + }); + //now fill up the funding goal.. + await testSetup.standardTokenMock.transfer(testSetup.org.avatar.address,1); + tx = await testSetup.join.setFundingGoalReachedFlag(); + await testSetup.join.getPastEvents('FundedBeforeDeadline', { + fromBlock: tx.blockNumber, + toBlock: 'latest' + }) + .then(function(events){ + assert.equal(events[0].event,"FundedBeforeDeadline"); + }); + }); + +}); From 303884da2969e34977a3d1eff9de10f0ce33e9ab Mon Sep 17 00:00:00 2001 From: Oren Sokolowsky Date: Mon, 3 Aug 2020 00:38:57 +0300 Subject: [PATCH 4/7] tests --- test/fundingrequest.js | 52 ++++++++++++++++++++---------------------- test/join.js | 10 ++++++++ 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/test/fundingrequest.js b/test/fundingrequest.js index df6bcacc..0a841ff6 100644 --- a/test/fundingrequest.js +++ b/test/fundingrequest.js @@ -1,10 +1,10 @@ const helpers = require("./helpers"); -const JoinAndQuit = artifacts.require("./JoinAndQuit.sol"); +const Join = artifacts.require("./Join.sol"); const FundingRequest = artifacts.require("./FundingRequest.sol"); const ERC20Mock = artifacts.require('./test/ERC20Mock.sol'); const Redeemer = artifacts.require("./Redeemer.sol"); -class JoinAndQuitParams { +class JoinParams { constructor() { } } @@ -14,7 +14,7 @@ class FundingRequestParams { } } -const setupJoinAndQuit = async function( +const setupJoin = async function( accounts, genesisProtocol, token, @@ -24,42 +24,40 @@ const setupJoinAndQuit = async function( _fundingGoal, _fundingGoalDeadline ) { - var joinAndQuitParams = new JoinAndQuitParams(); + var joinParams = new JoinParams(); if (genesisProtocol === true) { - joinAndQuitParams.votingMachine = await helpers.setupGenesisProtocol(accounts,token,helpers.NULL_ADDRESS); - joinAndQuitParams.initdata = await new web3.eth.Contract(registration.joinAndQuit.abi) + joinParams.votingMachine = await helpers.setupGenesisProtocol(accounts,token,helpers.NULL_ADDRESS); + joinParams.initdata = await new web3.eth.Contract(registration.join.abi) .methods .initialize(helpers.NULL_ADDRESS, - joinAndQuitParams.votingMachine.genesisProtocol.address, - joinAndQuitParams.votingMachine.uintArray, - joinAndQuitParams.votingMachine.voteOnBehalf, + joinParams.votingMachine.genesisProtocol.address, + joinParams.votingMachine.uintArray, + joinParams.votingMachine.voteOnBehalf, helpers.NULL_HASH, _fundingToken, _minFeeToJoin, _memberReputation, _fundingGoal, - _fundingGoalDeadline, - false) + _fundingGoalDeadline) .encodeABI(); } else { - joinAndQuitParams.votingMachine = await helpers.setupAbsoluteVote(helpers.NULL_ADDRESS,50); - joinAndQuitParams.initdata = await new web3.eth.Contract(registration.joinAndQuit.abi) + joinParams.votingMachine = await helpers.setupAbsoluteVote(helpers.NULL_ADDRESS,50); + joinParams.initdata = await new web3.eth.Contract(registration.join.abi) .methods .initialize(helpers.NULL_ADDRESS, - joinAndQuitParams.votingMachine.absoluteVote.address, + joinParams.votingMachine.absoluteVote.address, [1,1,1,1,1,1,1,1,1,1,1], helpers.NULL_ADDRESS, - joinAndQuitParams.votingMachine.params, + joinParams.votingMachine.params, _fundingToken, _minFeeToJoin, _memberReputation, _fundingGoal, - _fundingGoalDeadline, - false) + _fundingGoalDeadline) .encodeABI(); } - return joinAndQuitParams; + return joinParams; }; @@ -125,7 +123,7 @@ const setup = async function (accounts, fundPath = helpers.NULL_ADDRESS; } - testSetup.joinAndQuitParams= await setupJoinAndQuit( + testSetup.joinParams= await setupJoin( accounts, genesisProtocol, tokenAddress, @@ -151,32 +149,32 @@ const setup = async function (accounts, [1000,0,0], testSetup.reputationArray, 0, - [web3.utils.fromAscii("JoinAndQuit"), web3.utils.fromAscii("FundingRequest")], - helpers.concatBytes(testSetup.joinAndQuitParams.initdata, testSetup.fundingRequestParams.initdata), - [helpers.getBytesLength(testSetup.joinAndQuitParams.initdata), helpers.getBytesLength(testSetup.fundingRequestParams.initdata)], + [web3.utils.fromAscii("Join"), web3.utils.fromAscii("FundingRequest")], + helpers.concatBytes(testSetup.joinParams.initdata, testSetup.fundingRequestParams.initdata), + [helpers.getBytesLength(testSetup.joinParams.initdata), helpers.getBytesLength(testSetup.fundingRequestParams.initdata)], [permissions, permissions], "metaData"); - testSetup.joinAndQuit = await JoinAndQuit.at(tx.logs[6].args._scheme); + testSetup.join = await Join.at(tx.logs[6].args._scheme); testSetup.fundingRequest = await FundingRequest.at(tx.logs[8].args._scheme); if(setupJAQProposal) { await testSetup.standardTokenMock.transfer(accounts[3],10000); - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.fundingGoal,{from:accounts[3]}); + await testSetup.standardTokenMock.approve(testSetup.join.address,testSetup.fundingGoal,{from:accounts[3]}); let value = 0; if (ethFunding) { value = testSetup.fundingGoal; } - tx = await testSetup.joinAndQuit.proposeToJoin( + tx = await testSetup.join.proposeToJoin( "description-hash", testSetup.fundingGoal, {value, from:accounts[3]}); var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); if (genesisProtocol === false) { - await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + await testSetup.joinParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); } else { - await testSetup.joinAndQuitParams.votingMachine.genesisProtocol.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + await testSetup.joinParams.votingMachine.genesisProtocol.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); } } return testSetup; diff --git a/test/join.js b/test/join.js index 2d843a34..cd6a6268 100644 --- a/test/join.js +++ b/test/join.js @@ -343,6 +343,16 @@ contract('Join', accounts => { //Vote with reputation to trigger execution var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); await testSetup.joinParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + try { + await testSetup.join.proposeToJoin( + "description-hash", + testSetup.minFeeToJoin, + {from:accounts[3]}); + assert(false, 'accepted candidate which not redeemed yet cannt be proposed again'); + } catch (ex) { + helpers.assertVMException(ex); + } + tx = await testSetup.join.redeemReputation(proposalId); assert.equal(tx.logs[0].event, "RedeemReputation"); assert.equal(tx.logs[0].args._amount, testSetup.memberReputation); From d45934ff9916f83b1af3e03664efd7526e48b408 Mon Sep 17 00:00:00 2001 From: Oren Sokolowsky Date: Tue, 4 Aug 2020 10:30:11 +0300 Subject: [PATCH 5/7] tests --- contracts/schemes/Join.sol | 11 - test/joinandquit.js | 765 ------------------------------------- 2 files changed, 776 deletions(-) delete mode 100644 test/joinandquit.js diff --git a/contracts/schemes/Join.sol b/contracts/schemes/Join.sol index 272a6a2f..65412c0b 100644 --- a/contracts/schemes/Join.sol +++ b/contracts/schemes/Join.sol @@ -190,20 +190,9 @@ contract Join is require(proposal.proposedMember != address(0), "no member to redeem"); require(membersState[proposal.proposedMember] == MemeberState.Accepted, "member not accepeted"); //set proposal proposedMember to zero to prevent reentrancy attack. -<<<<<<< HEAD -<<<<<<< HEAD proposals[_proposalId].proposedMember = address(0); -<<<<<<< HEAD:contracts/schemes/JoinAndQuit.sol -======= - proposal.proposedMember = address(0); ->>>>>>> joinandquit fix -======= proposals[_proposalId].proposedMember = address(0); ->>>>>>> opt - fundings[proposal.proposedMember].state = MemeberState.ReputationRedeemed; -======= membersState[proposal.proposedMember] = MemeberState.ReputationRedeemed; ->>>>>>> remove ragequit and refund:contracts/schemes/Join.sol if (memberReputation == 0) { reputation = proposal.funding; } else { diff --git a/test/joinandquit.js b/test/joinandquit.js deleted file mode 100644 index 098066a3..00000000 --- a/test/joinandquit.js +++ /dev/null @@ -1,765 +0,0 @@ -const helpers = require("./helpers"); -const JoinAndQuit = artifacts.require("./JoinAndQuit.sol"); -const ERC20Mock = artifacts.require('./test/ERC20Mock.sol'); -const Avatar = artifacts.require("./Avatar.sol"); -const Redeemer = artifacts.require("./Redeemer.sol"); - -class JoinAndQuitParams { - constructor() { - } -} - -const addMember = async function(accounts,_testSetup,_fee,_from) { - var tx = await _testSetup.joinAndQuit.proposeToJoin( - "description-hash", - _fee, - {value:_fee,from:_from}); - - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - await _testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - return tx; -}; - -const avatarBalance = async function(_testSetup) { - let avatar = await Avatar.at(_testSetup.org.avatar.address); - var vault = await avatar.vault(); - return await web3.eth.getBalance(vault); -}; - -const setupJoinAndQuit = async function( - accounts, - genesisProtocol, - token, - _fundingToken, - _minFeeToJoin, - _memberReputation, - _fundingGoal, - _fundingGoalDeadline, - _rageQuitEnable = true, - ) { - var joinAndQuitParams = new JoinAndQuitParams(); - - if (genesisProtocol === true) { - joinAndQuitParams.votingMachine = await helpers.setupGenesisProtocol(accounts,token,helpers.NULL_ADDRESS); - joinAndQuitParams.initdata = await new web3.eth.Contract(registration.joinAndQuit.abi) - .methods - .initialize(helpers.NULL_ADDRESS, - joinAndQuitParams.votingMachine.genesisProtocol.address, - joinAndQuitParams.votingMachine.uintArray, - joinAndQuitParams.votingMachine.voteOnBehalf, - helpers.NULL_HASH, - _fundingToken, - _minFeeToJoin, - _memberReputation, - _fundingGoal, - _fundingGoalDeadline, - _rageQuitEnable) - .encodeABI(); - } else { - joinAndQuitParams.votingMachine = await helpers.setupAbsoluteVote(helpers.NULL_ADDRESS,50); - joinAndQuitParams.initdata = await new web3.eth.Contract(registration.joinAndQuit.abi) - .methods - .initialize(helpers.NULL_ADDRESS, - joinAndQuitParams.votingMachine.absoluteVote.address, - [0,0,0,0,0,0,0,0,0,0,0], - helpers.NULL_ADDRESS, - joinAndQuitParams.votingMachine.params, - _fundingToken, - _minFeeToJoin, - _memberReputation, - _fundingGoal, - _fundingGoalDeadline, - _rageQuitEnable) - .encodeABI(); - } - return joinAndQuitParams; -}; -var registration; -const setup = async function (accounts, - ethFunding = false, - genesisProtocol = false, - tokenAddress=helpers.NULL_ADDRESS, - minFeeToJoin = 100, - memberReputation = 100, - fundingGoal = 1000, - fundingGoalDeadline = 3000, - rageQuitEnable = true) { - var testSetup = new helpers.TestSetup(); - testSetup.standardTokenMock = await ERC20Mock.new(accounts[0],100000); - registration = await helpers.registerImplementation(); - testSetup.reputationArray = [7000]; - testSetup.proxyAdmin = accounts[5]; - testSetup.fundingGoalDeadline = (await web3.eth.getBlock("latest")).timestamp + fundingGoalDeadline; - testSetup.minFeeToJoin = minFeeToJoin; - testSetup.memberReputation = memberReputation; - testSetup.fundingGoal = fundingGoal; - - var fundPath = testSetup.standardTokenMock.address; - if (ethFunding === true) { - fundPath = helpers.NULL_ADDRESS; - } - - testSetup.joinAndQuitParams= await setupJoinAndQuit( - accounts, - genesisProtocol, - tokenAddress, - fundPath, - minFeeToJoin, - memberReputation, - fundingGoal, - testSetup.fundingGoalDeadline, - rageQuitEnable); - - var permissions = "0x00000000"; - [testSetup.org,tx] = await helpers.setupOrganizationWithArraysDAOFactory(testSetup.proxyAdmin, - accounts, - registration, - [accounts[2]], - [0], - testSetup.reputationArray, - 0, - [web3.utils.fromAscii("JoinAndQuit")], - testSetup.joinAndQuitParams.initdata, - [helpers.getBytesLength(testSetup.joinAndQuitParams.initdata)], - [permissions], - "metaData"); - - testSetup.joinAndQuit = await JoinAndQuit.at(await helpers.getSchemeAddress(registration.daoFactory.address,tx)); - - await testSetup.standardTokenMock.transfer(accounts[3],10000); - - return testSetup; -}; -contract('JoinAndQuit', accounts => { - - it("initialize", async function() { - var testSetup = await setup(accounts); - assert.equal(await testSetup.joinAndQuit.votingMachine(),testSetup.joinAndQuitParams.votingMachine.absoluteVote.address); - assert.equal(await testSetup.joinAndQuit.fundingGoalDeadline(),testSetup.fundingGoalDeadline); - }); - - it("propose log", async function() { - var testSetup = await setup(accounts); - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.minFeeToJoin,{from:accounts[3]}); - - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - {from:accounts[3]}); - - assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.joinAndQuit.address),testSetup.minFeeToJoin); - assert.equal(tx.logs.length, 1); - assert.equal(tx.logs[0].event, "JoinInProposal"); - assert.equal(tx.logs[0].args._avatar, testSetup.org.avatar.address); - assert.equal(tx.logs[0].args._descriptionHash, "description-hash"); - assert.equal(tx.logs[0].args._proposedMember, accounts[3]); - assert.equal(tx.logs[0].args._feeAmount, testSetup.minFeeToJoin); - }); - - it("propose log with eth", async function() { - var testSetup = await setup(accounts,true); - - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - - {value:testSetup.minFeeToJoin,from:accounts[3]}); - - assert.equal(await web3.eth.getBalance(testSetup.joinAndQuit.address),testSetup.minFeeToJoin); - assert.equal(tx.logs.length, 1); - assert.equal(tx.logs[0].event, "JoinInProposal"); - assert.equal(tx.logs[0].args._avatar, testSetup.org.avatar.address); - assert.equal(tx.logs[0].args._descriptionHash, "description-hash"); - assert.equal(tx.logs[0].args._proposedMember, accounts[3]); - assert.equal(tx.logs[0].args._feeAmount, testSetup.minFeeToJoin); - }); - - it("propose with eth should be equal to _feeAmount", async function() { - var testSetup = await setup(accounts,true); - - try { - await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - - {value:testSetup.minFeeToJoin+1,from:accounts[3]}); - assert(false, 'should be equal to _feeAmount'); - } catch (ex) { - helpers.assertVMException(ex); - } - }); - - - it("propose cannot add a member if already is a candidate", async function() { - var testSetup = await setup(accounts); - - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.minFeeToJoin*2); - await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin); - - try { - await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin); - assert(false, 'proposer already is a candidate'); - } catch (ex) { - helpers.assertVMException(ex); - } - }); - - it("propose cannot add a member if member allready has reputation", async function() { - var testSetup = await setup(accounts); - var candidate = accounts[3]; - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address, - testSetup.minFeeToJoin*2, - {from:candidate}); - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - {from:candidate}); - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - - await testSetup.joinAndQuit.redeemReputation(proposalId); - - try { - await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - {from:candidate}); - assert(false, 'proposer already have reputation'); - } catch (ex) { - helpers.assertVMException(ex); - } - }); - - - it("proposeJoinAndQuit check minFeeToJoin", async() => { - var testSetup = await setup(accounts); - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.minFeeToJoin); - try { - await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin-1); - assert(false, 'minFeeToJoin'); - } catch (ex) { - helpers.assertVMException(ex); - } - }); - - it("proposeJoinAndQuit check minFeeToJoin with eth", async() => { - var testSetup = await setup(accounts,true); - try { - await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin-1, - {value:testSetup.minFeeToJoin-1}); - assert(false, 'minFeeToJoin'); - } catch (ex) { - helpers.assertVMException(ex); - } - }); - - it("execute proposeJoinAndQuit yes ", async function() { - var testSetup = await setup(accounts); - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.minFeeToJoin,{from:accounts[3]}); - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - {from:accounts[3]}); - - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - var funding = await testSetup.joinAndQuit.fundings(accounts[3]); - assert.equal(funding.state,2); - assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.org.avatar.address),testSetup.minFeeToJoin); - assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.joinAndQuit.address),0); - assert.equal((await testSetup.joinAndQuit.fundings(accounts[3])).funding,testSetup.minFeeToJoin); - }); - - it("execute proposeJoinAndQuit yes with eth", async function() { - var testSetup = await setup(accounts,true); - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - {value:testSetup.minFeeToJoin,from:accounts[3]}); - - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - var funding = await testSetup.joinAndQuit.fundings(accounts[3]); - assert.equal(funding.state,2); - assert.equal(await avatarBalance(testSetup),testSetup.minFeeToJoin); - assert.equal(await web3.eth.getBalance(testSetup.joinAndQuit.address),0); - assert.equal((await testSetup.joinAndQuit.fundings(accounts[3])).funding,testSetup.minFeeToJoin); - }); - - it("execute proposeJoinAndQuit no", async function() { - var testSetup = await setup(accounts); - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.minFeeToJoin,{from:accounts[3]}); - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - {from:accounts[3]}); - - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,2,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - var funding = await testSetup.joinAndQuit.fundings(accounts[3]); - assert.equal(funding.state,3); - assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.org.avatar.address),0); - assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.joinAndQuit.address),0); - assert.equal((await testSetup.joinAndQuit.fundings(accounts[3])).funding,0); - assert.equal(await testSetup.standardTokenMock.balanceOf(accounts[3]),10000); - }); - - - it("execute proposeJoinAndQuit no with eth", async function() { - var testSetup = await setup(accounts,true); - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - {value:testSetup.minFeeToJoin,from:accounts[3]}); - - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - var balanceBefore = await web3.eth.getBalance(accounts[3]); - await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,2,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - var funding = await testSetup.joinAndQuit.fundings(accounts[3]); - assert.equal(funding.state,3); - assert.equal(await avatarBalance(testSetup),0); - assert.equal(await web3.eth.getBalance(testSetup.joinAndQuit.address),0); - assert.equal((await testSetup.joinAndQuit.fundings(accounts[3])).funding,0); - var BN = web3.utils.BN; - var a = new BN(balanceBefore); - var b = new BN(testSetup.minFeeToJoin); - var expectedBalance = a.add(b); - assert.equal(await web3.eth.getBalance(accounts[3]),expectedBalance); - }); - - it("reputation redeem ", async function() { - var testSetup = await setup(accounts); - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.minFeeToJoin,{from:accounts[3]}); - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - {from:accounts[3]}); - - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - try { - await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - {from:accounts[3]}); - assert(false, 'accepted candidate which not redeemed yet cannt be proposed again'); - } catch (ex) { - helpers.assertVMException(ex); - } - tx = await testSetup.joinAndQuit.redeemReputation(proposalId); - assert.equal(tx.logs[0].event, "RedeemReputation"); - assert.equal(tx.logs[0].args._amount, testSetup.memberReputation); - assert.equal(await testSetup.org.reputation.balanceOf(accounts[3]),testSetup.memberReputation); - try { - await testSetup.joinAndQuit.redeemReputation(proposalId); - assert(false, 'cannot redeem twice'); - } catch (ex) { - helpers.assertVMException(ex); - } - }); - - it("reputation redeem memberReputation 0", async function() { - var testSetup = await setup(accounts, false, false, helpers.NULL_ADDRESS, 100, 0); - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.minFeeToJoin,{from:accounts[3]}); - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - {from:accounts[3]}); - - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - tx = await testSetup.joinAndQuit.redeemReputation(proposalId); - assert.equal(tx.logs[0].event, "RedeemReputation"); - assert.equal(tx.logs[0].args._amount, testSetup.minFeeToJoin); - assert.equal(await testSetup.org.reputation.balanceOf(accounts[3]),testSetup.minFeeToJoin); - }); - - it("reputation redeem + genesisProtocol", async function() { - var testSetup = await setup(accounts,false,true); - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.minFeeToJoin,{from:accounts[3]}); - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - {from:accounts[3]}); - - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - var arcUtils = await Redeemer.new(); - tx = await arcUtils.redeemJoinAndQuit(testSetup.joinAndQuit.address, - testSetup.joinAndQuitParams.votingMachine.genesisProtocol.address, - proposalId, - accounts[2]); - - await testSetup.joinAndQuit.getPastEvents('RedeemReputation', { - fromBlock: tx.blockNumber, - toBlock: 'latest' - }) - .then(function(events){ - assert.equal(events.length,0); - }); - await testSetup.joinAndQuitParams.votingMachine.genesisProtocol.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - tx = await arcUtils.redeemJoinAndQuit(testSetup.joinAndQuit.address, - testSetup.joinAndQuitParams.votingMachine.genesisProtocol.address, - proposalId, - accounts[2]); - - await testSetup.joinAndQuit.getPastEvents('RedeemReputation', { - fromBlock: tx.blockNumber, - toBlock: 'latest' - }) - .then(function(events){ - assert.equal(events[0].event,"RedeemReputation"); - assert.equal(events[0].args._amount, testSetup.memberReputation); - - }); - assert.equal(await testSetup.org.reputation.balanceOf(accounts[3]),testSetup.memberReputation); - try { - await testSetup.joinAndQuit.redeemReputation(proposalId); - assert(false, 'cannot redeem twice'); - } catch (ex) { - helpers.assertVMException(ex); - } - }); - - it("reputation cannot redeemed ", async function() { - var testSetup = await setup(accounts); - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.minFeeToJoin,{from:accounts[3]}); - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - {from:accounts[3]}); - - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,2,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - try { - await testSetup.joinAndQuit.redeemReputation(proposalId); - assert(false, 'reputation cannot redeemed'); - } catch (ex) { - helpers.assertVMException(ex); - } - }); - - it("rageQuit and redeem", async function() { - var testSetup = await setup(accounts); - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.minFeeToJoin,{from:accounts[3]}); - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - {from:accounts[3]}); - - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.org.avatar.address),testSetup.minFeeToJoin); - assert.equal((await testSetup.joinAndQuit.fundings(accounts[3])).funding,testSetup.minFeeToJoin); - await testSetup.joinAndQuit.rageQuit({from:accounts[3]}); - assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.joinAndQuit.address),0); - assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.org.avatar.address),0); - assert.equal((await testSetup.joinAndQuit.fundings(accounts[3])).funding,0); - try { - await testSetup.joinAndQuit.rageQuit({from:accounts[3]}); - assert(false, 'cannot rage quite twice without refunding'); - } catch (ex) { - helpers.assertVMException(ex); - } - await testSetup.standardTokenMock.transfer(accounts[0],1000); - await testSetup.standardTokenMock.transfer(accounts[1],1000); - await testSetup.standardTokenMock.transfer(accounts[4],1000); - - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,1000,{from:accounts[0]}); - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,1000,{from:accounts[1]}); - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,1000,{from:accounts[4]}); - await addMember(accounts,testSetup,300,accounts[0]); - await addMember(accounts,testSetup,100,accounts[1]); - await addMember(accounts,testSetup,500,accounts[4]); - - assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.org.avatar.address),900); - assert.equal((await testSetup.joinAndQuit.fundings(accounts[0])).funding,300); - assert.equal(await testSetup.joinAndQuit.totalDonation(),900); - tx = await testSetup.joinAndQuit.rageQuit({from:accounts[0]}); - assert.equal(tx.logs[0].event, "RageQuit"); - assert.equal(tx.logs[0].args._refund, 300); - tx = await testSetup.joinAndQuit.rageQuit({from:accounts[1]}); - assert.equal(tx.logs[0].args._refund, 100); - await testSetup.standardTokenMock.transfer(testSetup.org.avatar.address,100); - tx = await testSetup.joinAndQuit.rageQuit({from:accounts[4]}); - assert.equal(tx.logs[0].args._refund, 500+100); - - try { - await testSetup.joinAndQuit.redeemReputation(proposalId); - assert(false, 'reputation cannot redeemed after rageQuit'); - } catch (ex) { - helpers.assertVMException(ex); - } - }); - - - it("rageQuit with eth", async function() { - var testSetup = await setup(accounts,true); - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - {from:accounts[3],value:testSetup.minFeeToJoin}); - - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - assert.equal(await avatarBalance(testSetup),testSetup.minFeeToJoin); - assert.equal((await testSetup.joinAndQuit.fundings(accounts[3])).funding,testSetup.minFeeToJoin); - await testSetup.joinAndQuit.rageQuit({from:accounts[3]}); - assert.equal(await web3.eth.getBalance(testSetup.joinAndQuit.address),0); - assert.equal(await avatarBalance(testSetup),0); - assert.equal((await testSetup.joinAndQuit.fundings(accounts[3])).funding,0); - - try { - await testSetup.joinAndQuit.rageQuit({from:accounts[3]}); - assert(false, 'cannot rage quite twice without refunding'); - } catch (ex) { - helpers.assertVMException(ex); - } - - await addMember(accounts,testSetup,300,accounts[0]); - await addMember(accounts,testSetup,100,accounts[1]); - await addMember(accounts,testSetup,500,accounts[4]); - - assert.equal(await avatarBalance(testSetup),900); - assert.equal((await testSetup.joinAndQuit.fundings(accounts[0])).funding,300); - assert.equal(await testSetup.joinAndQuit.totalDonation(),900); - tx = await testSetup.joinAndQuit.rageQuit({from:accounts[0]}); - assert.equal(tx.logs[0].event, "RageQuit"); - assert.equal(tx.logs[0].args._refund, 300); - tx = await testSetup.joinAndQuit.rageQuit({from:accounts[1]}); - assert.equal(tx.logs[0].args._refund, 100); - await web3.eth.sendTransaction({to:testSetup.org.avatar.address, from:accounts[0], value:100}); - tx = await testSetup.joinAndQuit.rageQuit({from:accounts[4]}); - assert.equal(tx.logs[0].args._refund, 500+100); - }); - - it("checkFundedBeforeDeadLine ", async function() { - var testSetup = await setup(accounts); - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.fundingGoal,{from:accounts[3]}); - let avatar = await Avatar.at(testSetup.org.avatar.address); - let key = await testSetup.joinAndQuit.FUNDED_BEFORE_DEADLINE_KEY(); - let value = await testSetup.joinAndQuit.FUNDED_BEFORE_DEADLINE_VALUE(); - assert.equal(await avatar.db(key),""); - - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.fundingGoal, - {from:accounts[3]}); - - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - tx = await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - await testSetup.joinAndQuit.getPastEvents('FundedBeforeDeadline', { - fromBlock: tx.blockNumber, - toBlock: 'latest' - }) - .then(function(events){ - assert.equal(events[0].event,"FundedBeforeDeadline"); - }); - assert.equal(await avatar.db(key),value); - }); - - - it("checkFundedBeforeDeadLine with eth", async function() { - var testSetup = await setup(accounts,true); - let avatar = await Avatar.at(testSetup.org.avatar.address); - let key = "FUNDED_BEFORE_DEADLINE"; - let value = "TRUE"; - assert.equal(await avatar.db(key),""); - - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.fundingGoal, - {value:testSetup.fundingGoal,from:accounts[3]}); - - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - tx = await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - await testSetup.joinAndQuit.getPastEvents('FundedBeforeDeadline', { - fromBlock: tx.blockNumber, - toBlock: 'latest' - }) - .then(function(events){ - assert.equal(events[0].event,"FundedBeforeDeadline"); - }); - assert.equal(await avatar.db(key),value); - }); - - it("checkFundedBeforeDeadLine after deadline", async function() { - var testSetup = await setup(accounts); - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.fundingGoal,{from:accounts[3]}); - let avatar = await Avatar.at(testSetup.org.avatar.address); - let key = "FUNDED_BEFORE_DEADLINE"; - assert.equal(await avatar.db(key),""); - await helpers.increaseTime(testSetup.fundingGoalDeadline); - await addMember(accounts,testSetup,testSetup.fundingGoal,accounts[3]); - assert.equal(await avatar.db(key),""); - }); - - it("checkFundedBeforeDeadLine after deadline with eth", async function() { - var testSetup = await setup(accounts,true); - let avatar = await Avatar.at(testSetup.org.avatar.address); - let key = "FUNDED_BEFORE_DEADLINE"; - assert.equal(await avatar.db(key),""); - await helpers.increaseTime(testSetup.fundingGoalDeadline); - await addMember(accounts,testSetup,testSetup.fundingGoal,accounts[3]); - assert.equal(await avatar.db(key),""); - }); - - - it("can fund the dao directly and set the goal", async function() { - var testSetup = await setup(accounts); - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.fundingGoal,{from:accounts[3]}); - let avatar = await Avatar.at(testSetup.org.avatar.address); - let key = await testSetup.joinAndQuit.FUNDED_BEFORE_DEADLINE_KEY(); - await testSetup.joinAndQuit.FUNDED_BEFORE_DEADLINE_VALUE(); - assert.equal(await avatar.db(key),""); - await testSetup.standardTokenMock.transfer(testSetup.org.avatar.address,testSetup.fundingGoal-1); - var tx = await testSetup.joinAndQuit.setFundingGoalReachedFlag(); - await testSetup.joinAndQuit.getPastEvents('FundedBeforeDeadline', { - fromBlock: tx.blockNumber, - toBlock: 'latest' - }) - .then(function(events){ - assert.equal(events.length,0); - }); - //now fill up the funding goal.. - await testSetup.standardTokenMock.transfer(testSetup.org.avatar.address,1); - tx = await testSetup.joinAndQuit.setFundingGoalReachedFlag(); - await testSetup.joinAndQuit.getPastEvents('FundedBeforeDeadline', { - fromBlock: tx.blockNumber, - toBlock: 'latest' - }) - .then(function(events){ - assert.equal(events[0].event,"FundedBeforeDeadline"); - }); - }); - - - it("rageQuit not enable", async function() { - var testSetup = await setup(accounts, false, false, helpers.NULL_ADDRESS, 100, 0,1000,3000,false); - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.minFeeToJoin,{from:accounts[3]}); - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - {from:accounts[3]}); - - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.org.avatar.address),testSetup.minFeeToJoin); - assert.equal((await testSetup.joinAndQuit.fundings(accounts[3])).funding,testSetup.minFeeToJoin); - try { - await testSetup.joinAndQuit.rageQuit({from:accounts[3]}); - assert(false, 'rageQuitEnable is false'); - } catch (ex) { - helpers.assertVMException(ex); - } - }); - - it("refund", async function() { - var testSetup = await setup(accounts); - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.minFeeToJoin,{from:accounts[3]}); - var donatorBalance = await testSetup.standardTokenMock.balanceOf(accounts[3]); - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - {from:accounts[3]}); - - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.org.avatar.address),testSetup.minFeeToJoin); - assert.equal((await testSetup.joinAndQuit.fundings(accounts[3])).funding,testSetup.minFeeToJoin); - try { - await testSetup.joinAndQuit.refund({from:accounts[3]}); - assert(false, 'cannot refund before deadline'); - } catch (ex) { - helpers.assertVMException(ex); - } - await helpers.increaseTime(testSetup.fundingGoalDeadline); - tx = await testSetup.joinAndQuit.refund({from:accounts[3]}); - assert.equal(tx.logs.length, 1); - assert.equal(tx.logs[0].event, "Refund"); - assert.equal(tx.logs[0].args._avatar, testSetup.org.avatar.address); - assert.equal(tx.logs[0].args._beneficiary, accounts[3]); - assert.equal(tx.logs[0].args._refund, testSetup.minFeeToJoin); - assert.equal((await testSetup.standardTokenMock.balanceOf(accounts[3])).toString(),donatorBalance.toString()); - }); - - it("refund - cannot if funding goal reached.", async function() { - var testSetup = await setup(accounts); - await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.fundingGoal+1,{from:accounts[3]}); - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.fundingGoal+1, - {from:accounts[3]}); - - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - - await helpers.increaseTime(testSetup.fundingGoalDeadline); - try { - await testSetup.joinAndQuit.refund({from:accounts[3]}); - assert(false, 'cannot if funding goal reached'); - } catch (ex) { - helpers.assertVMException(ex); - } - }); - - it("refund with eth", async function() { - var testSetup = await setup(accounts,true); - var tx = await testSetup.joinAndQuit.proposeToJoin( - "description-hash", - testSetup.minFeeToJoin, - {from:accounts[3],value:testSetup.minFeeToJoin}); - - //Vote with reputation to trigger execution - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); - await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - assert.equal((await testSetup.joinAndQuit.fundings(accounts[3])).funding,testSetup.minFeeToJoin); - try { - await testSetup.joinAndQuit.refund({from:accounts[3]}); - assert(false, 'cannot refund before deadline'); - } catch (ex) { - helpers.assertVMException(ex); - } - await helpers.increaseTime(testSetup.fundingGoalDeadline); - var balanceBefore = await avatarBalance(testSetup); - tx = await testSetup.joinAndQuit.refund({from:accounts[3]}); - assert.equal(tx.logs.length, 1); - assert.equal(tx.logs[0].event, "Refund"); - assert.equal(tx.logs[0].args._avatar, testSetup.org.avatar.address); - assert.equal(tx.logs[0].args._beneficiary, accounts[3]); - assert.equal(tx.logs[0].args._refund, testSetup.minFeeToJoin); - assert.equal(await avatarBalance(testSetup),balanceBefore - testSetup.minFeeToJoin); - try { - await testSetup.joinAndQuit.refund({from:accounts[3]}); - assert(false, 'cannot refund twice'); - } catch (ex) { - helpers.assertVMException(ex); - } - }); - - -}); From cd988de10261a68a145508bfba9da37e590f33b5 Mon Sep 17 00:00:00 2001 From: Oren Sokolowsky Date: Tue, 4 Aug 2020 11:16:39 +0300 Subject: [PATCH 6/7] spelling --- contracts/schemes/Join.sol | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/contracts/schemes/Join.sol b/contracts/schemes/Join.sol index 65412c0b..106e99d5 100644 --- a/contracts/schemes/Join.sol +++ b/contracts/schemes/Join.sol @@ -19,7 +19,7 @@ contract Join is 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, @@ -47,7 +47,7 @@ contract Join is } mapping(bytes32=>Proposal) public proposals; - mapping(address=>MemeberState) public membersState; + mapping(address=>MemberState) public membersState; IERC20 public fundingToken; uint256 public minFeeToJoin; @@ -104,12 +104,12 @@ contract Join is returns(bool) { Proposal memory proposal = proposals[_proposalId]; require(proposal.proposedMember != address(0), "not a valid proposal"); - require(membersState[proposal.proposedMember] == 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)) { - membersState[proposal.proposedMember] = MemeberState.Accepted; + membersState[proposal.proposedMember] = MemberState.Accepted; totalDonation = totalDonation.add(proposal.funding); if (fundingToken == IERC20(0)) { // solhint-disable-next-line @@ -121,7 +121,7 @@ contract Join is //this should be called/check after the transfer to the avatar. setFundingGoalReachedFlag(); } else { - membersState[proposal.proposedMember] = MemeberState.Rejected; + membersState[proposal.proposedMember] = MemberState.Rejected; if (fundingToken == IERC20(0)) { // solhint-disable-next-line (success, ) = proposal.proposedMember.call{value:proposal.funding}(""); @@ -150,11 +150,11 @@ contract Join is returns(bytes32) { address proposer = msg.sender; - require(membersState[proposer] != MemeberState.Candidate, "already a candidate"); - require(membersState[proposer] != 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"); - membersState[proposer] = 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 { @@ -188,11 +188,11 @@ contract Join is function redeemReputation(bytes32 _proposalId) public returns(uint256 reputation) { Proposal memory proposal = proposals[_proposalId]; require(proposal.proposedMember != address(0), "no member to redeem"); - require(membersState[proposal.proposedMember] == 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); proposals[_proposalId].proposedMember = address(0); - membersState[proposal.proposedMember] = MemeberState.ReputationRedeemed; + membersState[proposal.proposedMember] = MemberState.ReputationRedeemed; if (memberReputation == 0) { reputation = proposal.funding; } else { From 35f6e2ad068c729f43c19fb966bb48faa10aaed5 Mon Sep 17 00:00:00 2001 From: Oren Sokolowsky Date: Wed, 5 Aug 2020 11:57:13 +0300 Subject: [PATCH 7/7] bump version to "version": "0.1.2-rc.5", --- package-lock.json | 80 +++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1e2e00df..e681a4a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@daostack/arc-experimental", - "version": "0.1.2-rc.4", + "version": "0.1.2-rc.5", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -55,12 +55,12 @@ } }, "@babel/generator": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.5.tgz", - "integrity": "sha512-3vXxr3FEW7E7lJZiWQ3bM4+v/Vyr9C+hpolQ8BGFr9Y8Ri2tFLWTixmwKBafDujO1WVah4fhZBeU1bieKdghig==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz", + "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==", "dev": true, "requires": { - "@babel/types": "^7.10.5", + "@babel/types": "^7.11.0", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -86,12 +86,12 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz", - "integrity": "sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", "dev": true, "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.11.0" } }, "@babel/helper-validator-identifier": { @@ -112,9 +112,9 @@ } }, "@babel/parser": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.5.tgz", - "integrity": "sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ==", + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.1.tgz", + "integrity": "sha512-u9QMIRdKVF7hfEkb3nu2LgZDIzCQPv+yHD9Eg6ruoJLjkrQ9fFz4IBSlF/9XwoNri9+2F1IY+dYuOfZrXq8t3w==", "dev": true }, "@babel/template": { @@ -129,17 +129,17 @@ } }, "@babel/traverse": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.5.tgz", - "integrity": "sha512-yc/fyv2gUjPqzTz0WHeRJH2pv7jA9kA7mBX2tXl/x5iOE81uaVPuGPtaYk7wmkx4b67mQ7NqI8rmT2pF47KYKQ==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz", + "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.10.5", + "@babel/generator": "^7.11.0", "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4", - "@babel/parser": "^7.10.5", - "@babel/types": "^7.10.5", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.11.0", + "@babel/types": "^7.11.0", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" @@ -163,9 +163,9 @@ } }, "@babel/types": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz", - "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", + "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -3991,9 +3991,9 @@ "dev": true }, "dayjs": { - "version": "1.8.31", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.31.tgz", - "integrity": "sha512-mPh1mslned+5PuIuiUfbw4CikHk6AEAf2Baxih+wP5fssv+wmlVhvgZ7mq+BhLt7Sr/Hc8leWDiwe6YnrpNt3g==", + "version": "1.8.32", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.32.tgz", + "integrity": "sha512-V91aTRu5btP+uzGHaaOfodckEfBWhmi9foRP7cauAO1PTB8+tZ9o0Jec7q6TIIRY1N4q1IfiKsZunkB/AEWqMQ==", "dev": true }, "death": { @@ -4778,9 +4778,9 @@ }, "dependencies": { "acorn": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", - "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", + "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", "dev": true }, "ansi-regex": { @@ -5392,9 +5392,9 @@ } }, "ethereumjs-util": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.0.3.tgz", - "integrity": "sha512-uLQsGPOwsRxe50WV1Dybh5N8zXDz4ev7wP49LKX9kr28I5TmcDILPgpKK/BFe5zYSfRGEeo+hPT7W3tjghYLuA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.0.4.tgz", + "integrity": "sha512-isldtbCn9fdnhBPxedMNbFkNWVZ8ZdQvKRDSrdflame/AycAPKMer+vEpndpBxYIB3qxN6bd3Gh1YCQW9LDkCQ==", "dev": true, "requires": { "@types/bn.js": "^4.11.3", @@ -7422,9 +7422,9 @@ "dev": true }, "uglify-js": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.0.tgz", - "integrity": "sha512-Esj5HG5WAyrLIdYU74Z3JdG2PxdIusvj6IWHMtlyESxc7kcDz7zYlYjpnSokn1UbpV0d/QX9fan7gkCNd/9BQA==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.1.tgz", + "integrity": "sha512-RjxApKkrPJB6kjJxQS3iZlf///REXWYxYJxO/MpmlQzVkDWVI3PSnCBWezMecmTU/TRkNxrl8bmsfFQCp+LO+Q==", "dev": true, "optional": true }, @@ -8081,9 +8081,9 @@ "dev": true }, "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "dev": true, "requires": { "has-symbols": "^1.0.1" @@ -11048,9 +11048,9 @@ "dev": true }, "rxjs": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.0.tgz", - "integrity": "sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==", + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz", + "integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==", "dev": true, "requires": { "tslib": "^1.9.0" diff --git a/package.json b/package.json index 62c5765c..13b02112 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@daostack/arc-experimental", - "version": "0.1.2-rc.4", + "version": "0.1.2-rc.5", "description": "A platform for building DAOs", "files": [ "contracts/",