Skip to content

Commit 732d620

Browse files
authored
add refund (#748)
* add refund * add refund with eth test * tests
1 parent da0696a commit 732d620

File tree

2 files changed

+132
-11
lines changed

2 files changed

+132
-11
lines changed

contracts/schemes/JoinAndQuit.sol

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ contract JoinAndQuit is
3737
uint256 indexed _refund
3838
);
3939

40+
event Refund(
41+
address indexed _avatar,
42+
address indexed _beneficiary,
43+
uint256 indexed _refund
44+
);
45+
4046
event RedeemReputation(
4147
address indexed _avatar,
4248
bytes32 indexed _proposalId,
@@ -216,35 +222,47 @@ contract JoinAndQuit is
216222
emit RedeemReputation(address(avatar), _proposalId, _proposal.proposedMember, reputation);
217223
}
218224

225+
/**
226+
* @dev refund refund donator if the the funding goal did not reached till the funding goal deadline.
227+
* @return refundAmount the refund amount
228+
*/
229+
function refund() public returns(uint256 refundAmount) {
230+
// solhint-disable-next-line not-rely-on-time
231+
require(now > fundingGoalDeadline, "can refund only after fundingGoalDeadline");
232+
require(
233+
(avatar.db(FUNDED_BEFORE_DEADLINE_KEY).hashCompareWithLengthCheck(FUNDED_BEFORE_DEADLINE_VALUE) == false),
234+
"can refund only if funding goal not reached");
235+
require(fundings[msg.sender].funding > 0, "no funds to refund");
236+
refundAmount = fundings[msg.sender].funding;
237+
fundings[msg.sender].funding = 0;
238+
sendToBeneficiary(refundAmount, msg.sender);
239+
emit Refund(address(avatar), msg.sender, refundAmount);
240+
}
241+
219242
/**
220243
* @dev rageQuit quit from the dao.
221244
* can be done on any time
222245
* REFUND = USER_DONATION * CURRENT_DAO_BALANCE / TOTAL_DONATIONS
223-
* @return refund the refund amount
246+
* @return refundAmount the refund amount
224247
*/
225-
function rageQuit() public returns(uint256 refund) {
248+
function rageQuit() public returns(uint256 refundAmount) {
226249
require(rageQuitEnable, "RageQuit disabled");
227250
require(fundings[msg.sender].funding > 0, "no fund to RageQuit");
228251
uint256 userDonation = fundings[msg.sender].funding;
229252
fundings[msg.sender].funding = 0;
230253
fundings[msg.sender].rageQuit = true;
231254
if (fundingToken == IERC20(0)) {
232-
refund = userDonation.mul(address(avatar.vault()).balance).div(totalDonation);
233-
require(
234-
Controller(
235-
avatar.owner()).sendEther(refund, msg.sender), "send ether failed");
255+
refundAmount = userDonation.mul(address(avatar.vault()).balance).div(totalDonation);
236256
} else {
237-
refund = userDonation.mul(fundingToken.balanceOf(address(avatar))).div(totalDonation);
238-
require(
239-
Controller(
240-
avatar.owner()).externalTokenTransfer(fundingToken, msg.sender, refund), "send token failed");
257+
refundAmount = userDonation.mul(fundingToken.balanceOf(address(avatar))).div(totalDonation);
241258
}
259+
sendToBeneficiary(refundAmount, msg.sender);
242260
uint256 msgSenderReputation = avatar.nativeReputation().balanceOf(msg.sender);
243261
require(
244262
Controller(
245263
avatar.owner()).burnReputation(msgSenderReputation, msg.sender));
246264
totalDonation = totalDonation.sub(userDonation);
247-
emit RageQuit(address(avatar), msg.sender, refund);
265+
emit RageQuit(address(avatar), msg.sender, refundAmount);
248266
}
249267

250268
/**
@@ -270,4 +288,21 @@ contract JoinAndQuit is
270288
}
271289
}
272290

291+
/**
292+
* @dev sendToBeneficiary send amount of eth or token to beneficiary
293+
* @param _amount the amount to send
294+
* @param _beneficiary the beneficiary
295+
*/
296+
function sendToBeneficiary(uint256 _amount, address payable _beneficiary) private {
297+
if (fundingToken == IERC20(0)) {
298+
require(
299+
Controller(
300+
avatar.owner()).sendEther(_amount, _beneficiary), "send ether failed");
301+
} else {
302+
require(
303+
Controller(
304+
avatar.owner()).externalTokenTransfer(fundingToken, _beneficiary, _amount), "send token failed");
305+
}
306+
}
307+
273308
}

test/joinandquit.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,4 +642,90 @@ contract('JoinAndQuit', accounts => {
642642
}
643643
});
644644

645+
it("refund", async function() {
646+
var testSetup = await setup(accounts);
647+
await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.minFeeToJoin,{from:accounts[3]});
648+
var donatorBalance = await testSetup.standardTokenMock.balanceOf(accounts[3]);
649+
var tx = await testSetup.joinAndQuit.proposeToJoin(
650+
"description-hash",
651+
testSetup.minFeeToJoin,
652+
{from:accounts[3]});
653+
654+
//Vote with reputation to trigger execution
655+
var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1);
656+
await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]});
657+
assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.org.avatar.address),testSetup.minFeeToJoin);
658+
assert.equal((await testSetup.joinAndQuit.fundings(accounts[3])).funding,testSetup.minFeeToJoin);
659+
try {
660+
await testSetup.joinAndQuit.refund({from:accounts[3]});
661+
assert(false, 'cannot refund before deadline');
662+
} catch (ex) {
663+
helpers.assertVMException(ex);
664+
}
665+
await helpers.increaseTime(testSetup.fundingGoalDeadline);
666+
tx = await testSetup.joinAndQuit.refund({from:accounts[3]});
667+
assert.equal(tx.logs.length, 1);
668+
assert.equal(tx.logs[0].event, "Refund");
669+
assert.equal(tx.logs[0].args._avatar, testSetup.org.avatar.address);
670+
assert.equal(tx.logs[0].args._beneficiary, accounts[3]);
671+
assert.equal(tx.logs[0].args._refund, testSetup.minFeeToJoin);
672+
assert.equal((await testSetup.standardTokenMock.balanceOf(accounts[3])).toString(),donatorBalance.toString());
673+
});
674+
675+
it("refund - cannot if funding goal reached.", async function() {
676+
var testSetup = await setup(accounts);
677+
await testSetup.standardTokenMock.approve(testSetup.joinAndQuit.address,testSetup.fundingGoal+1,{from:accounts[3]});
678+
var tx = await testSetup.joinAndQuit.proposeToJoin(
679+
"description-hash",
680+
testSetup.fundingGoal+1,
681+
{from:accounts[3]});
682+
683+
//Vote with reputation to trigger execution
684+
var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1);
685+
await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]});
686+
687+
await helpers.increaseTime(testSetup.fundingGoalDeadline);
688+
try {
689+
await testSetup.joinAndQuit.refund({from:accounts[3]});
690+
assert(false, 'cannot if funding goal reached');
691+
} catch (ex) {
692+
helpers.assertVMException(ex);
693+
}
694+
});
695+
696+
it("refund with eth", async function() {
697+
var testSetup = await setup(accounts,true);
698+
var tx = await testSetup.joinAndQuit.proposeToJoin(
699+
"description-hash",
700+
testSetup.minFeeToJoin,
701+
{from:accounts[3],value:testSetup.minFeeToJoin});
702+
703+
//Vote with reputation to trigger execution
704+
var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1);
705+
await testSetup.joinAndQuitParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]});
706+
assert.equal((await testSetup.joinAndQuit.fundings(accounts[3])).funding,testSetup.minFeeToJoin);
707+
try {
708+
await testSetup.joinAndQuit.refund({from:accounts[3]});
709+
assert(false, 'cannot refund before deadline');
710+
} catch (ex) {
711+
helpers.assertVMException(ex);
712+
}
713+
await helpers.increaseTime(testSetup.fundingGoalDeadline);
714+
var balanceBefore = await avatarBalance(testSetup);
715+
tx = await testSetup.joinAndQuit.refund({from:accounts[3]});
716+
assert.equal(tx.logs.length, 1);
717+
assert.equal(tx.logs[0].event, "Refund");
718+
assert.equal(tx.logs[0].args._avatar, testSetup.org.avatar.address);
719+
assert.equal(tx.logs[0].args._beneficiary, accounts[3]);
720+
assert.equal(tx.logs[0].args._refund, testSetup.minFeeToJoin);
721+
assert.equal(await avatarBalance(testSetup),balanceBefore - testSetup.minFeeToJoin);
722+
try {
723+
await testSetup.joinAndQuit.refund({from:accounts[3]});
724+
assert(false, 'cannot refund twice');
725+
} catch (ex) {
726+
helpers.assertVMException(ex);
727+
}
728+
});
729+
730+
645731
});

0 commit comments

Comments
 (0)