Skip to content

Commit 5bce4a0

Browse files
authored
Redeemer for GSMC (#809)
* Redeemer for GSMC * cov * Check proposal passed before executing * Update Redeemer.sol * Genericscheme redeemer * bump fee
1 parent 1b15ff2 commit 5bce4a0

File tree

5 files changed

+205
-2
lines changed

5 files changed

+205
-2
lines changed

contracts/utils/Redeemer.sol

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ pragma solidity 0.5.17;
22

33
import "../universalSchemes/ContributionReward.sol";
44
import "../schemes/ContributionRewardExt.sol";
5+
import "../schemes/GenericSchemeMultiCall.sol";
6+
import "../schemes/GenericScheme.sol";
57
import "@daostack/infra/contracts/votingMachines/GenesisProtocol.sol";
68
import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
79

@@ -121,6 +123,98 @@ contract Redeemer {
121123
}
122124
}
123125

126+
/**
127+
* @dev helper to redeem proposal and execute the multi-call
128+
* It calls execute on the proposal if it is not yet executed.
129+
* It tries to redeem reputation and stake from the GenesisProtocol.
130+
* It tries to execute multi call to contracts from the GenericSchemeMultiCall
131+
* This function does not emit events.
132+
* A client should listen to GenesisProtocol and GenericSchemeMultiCall events
133+
* to monitor redemption and execution operations.
134+
* @param _genericSchemeMultiCall GenericSchemeMultiCall scheme
135+
* @param _genesisProtocol genesisProtocol
136+
* @param _proposalId the ID of the voting in the voting machine
137+
* @param _beneficiary beneficiary
138+
* @return gpRewards array
139+
* gpRewards[0] - stakerTokenAmount
140+
* gpRewards[1] - voterReputationAmount
141+
* gpRewards[2] - proposerReputationAmount
142+
* @return gpDaoBountyReward array
143+
* gpDaoBountyReward[0] - staker dao bounty reward -
144+
* will be zero for the case there is not enough tokens in avatar for the reward.
145+
* gpDaoBountyReward[1] - staker potential dao bounty reward.
146+
* @return executed bool true or false
147+
* @return winningVote
148+
* 1 - executed or closed and the winning vote is YES
149+
* 2 - executed or closed and the winning vote is NO
150+
*/
151+
function redeemGenericSchemeMultiCall(GenericSchemeMultiCall _genericSchemeMultiCall,
152+
GenesisProtocol _genesisProtocol,
153+
bytes32 _proposalId,
154+
address _beneficiary)
155+
external
156+
returns(uint[3] memory gpRewards,
157+
uint[2] memory gpDaoBountyReward,
158+
bool executed,
159+
uint256 winningVote
160+
)
161+
{
162+
bool callGenericSchemeMultiCall;
163+
(gpRewards, gpDaoBountyReward, executed, winningVote, callGenericSchemeMultiCall) =
164+
genesisProtocolRedeem(_genesisProtocol, _proposalId, _beneficiary);
165+
bool passedAndNotExecuted;
166+
(,passedAndNotExecuted) = _genericSchemeMultiCall.proposals(_proposalId);
167+
if (callGenericSchemeMultiCall && passedAndNotExecuted) {
168+
_genericSchemeMultiCall.execute(_proposalId);
169+
}
170+
}
171+
172+
/**
173+
* @dev helper to redeem proposal and execute the call
174+
* It calls execute on the proposal if it is not yet executed.
175+
* It tries to redeem reputation and stake from the GenesisProtocol.
176+
* It tries to execute call to contract from the GenericScheme
177+
* This function does not emit events.
178+
* A client should listen to GenesisProtocol and GenericScheme events
179+
* to monitor redemption and execution operations.
180+
* @param _genericScheme GenericScheme scheme
181+
* @param _genesisProtocol genesisProtocol
182+
* @param _proposalId the ID of the voting in the voting machine
183+
* @param _beneficiary beneficiary
184+
* @return gpRewards array
185+
* gpRewards[0] - stakerTokenAmount
186+
* gpRewards[1] - voterReputationAmount
187+
* gpRewards[2] - proposerReputationAmount
188+
* @return gpDaoBountyReward array
189+
* gpDaoBountyReward[0] - staker dao bounty reward -
190+
* will be zero for the case there is not enough tokens in avatar for the reward.
191+
* gpDaoBountyReward[1] - staker potential dao bounty reward.
192+
* @return executed bool true or false
193+
* @return winningVote
194+
* 1 - executed or closed and the winning vote is YES
195+
* 2 - executed or closed and the winning vote is NO
196+
*/
197+
function redeemGenericScheme(GenericScheme _genericScheme,
198+
GenesisProtocol _genesisProtocol,
199+
bytes32 _proposalId,
200+
address _beneficiary)
201+
external
202+
returns(uint[3] memory gpRewards,
203+
uint[2] memory gpDaoBountyReward,
204+
bool executed,
205+
uint256 winningVote
206+
)
207+
{
208+
bool callGenericScheme;
209+
(gpRewards, gpDaoBountyReward, executed, winningVote, callGenericScheme) =
210+
genesisProtocolRedeem(_genesisProtocol, _proposalId, _beneficiary);
211+
bool passedAndNotExecuted;
212+
(,,,passedAndNotExecuted) = _genericScheme.organizationProposals(_proposalId);
213+
if (callGenericScheme && passedAndNotExecuted) {
214+
_genericScheme.execute(_proposalId);
215+
}
216+
}
217+
124218
function genesisProtocolRedeem(GenesisProtocol _genesisProtocol,
125219
bytes32 _proposalId,
126220
address _beneficiary)

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@daostack/arc",
3-
"version": "0.0.1-rc.51",
3+
"version": "0.0.1-rc.52",
44
"description": "A platform for building DAOs",
55
"files": [
66
"contracts/",

test/genericscheme.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const DAOTracker = artifacts.require("./DAOTracker.sol");
77
const ERC20Mock = artifacts.require("./ERC20Mock.sol");
88
const ActionMock = artifacts.require("./ActionMock.sol");
99
const Wallet = artifacts.require("./Wallet.sol");
10+
const Redeemer = artifacts.require("./Redeemer.sol");
1011

1112
class GenericSchemeParams {
1213
constructor() {
@@ -162,6 +163,43 @@ contract('GenericScheme', function(accounts) {
162163
});
163164

164165

166+
it("execute proposeVote -positive decision - destination reverts and then active and executed with redeemer", async function() {
167+
var actionMock =await ActionMock.new();
168+
var standardTokenMock = await ERC20Mock.new(accounts[0],1000);
169+
var testSetup = await setup(accounts,actionMock.address,0,true,standardTokenMock.address);
170+
var activationTime = (await web3.eth.getBlock("latest")).timestamp + 1000;
171+
await actionMock.setActivationTime(activationTime);
172+
var callData = await new web3.eth.Contract(actionMock.abi).methods.test3().encodeABI();
173+
var tx = await testSetup.genericScheme.proposeCall(callData,0,helpers.NULL_HASH);
174+
var proposalId = await helpers.getValueFromLogs(tx, '_proposalId');
175+
176+
await testSetup.genericSchemeParams.votingMachine.genesisProtocol.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]});
177+
//actionMock revert because msg.sender is not the _addr param at actionMock thpugh the generic scheme not .
178+
var organizationProposal = await testSetup.genericScheme.organizationProposals(proposalId);
179+
assert.equal(organizationProposal.exist,true);//new contract address
180+
assert.equal(organizationProposal.passed,true);//new contract address
181+
//can call execute
182+
await helpers.increaseTime(1001);
183+
var redeemer = await Redeemer.new();
184+
var redeemRewards = await redeemer.redeemGenericScheme.call(
185+
testSetup.genericScheme.address,
186+
testSetup.genericSchemeParams.votingMachine.genesisProtocol.address,
187+
proposalId,
188+
accounts[0]);
189+
assert.equal(redeemRewards[0][1],0); //redeemRewards[0] gpRewards
190+
assert.equal(redeemRewards[0][2],60);
191+
assert.equal(redeemRewards.executed,false); // GP already executed by vote
192+
assert.equal(redeemRewards.winningVote,1);
193+
tx = await redeemer.redeemGenericScheme(
194+
testSetup.genericScheme.address,
195+
testSetup.genericSchemeParams.votingMachine.genesisProtocol.address,
196+
proposalId,
197+
accounts[0]);
198+
organizationProposal = await testSetup.genericScheme.organizationProposals(proposalId);
199+
assert.equal(organizationProposal.exist,false);//new contract address
200+
assert.equal(organizationProposal.passed,false);//new contract address
201+
});
202+
165203

166204
it("execute proposeVote without return value-positive decision - check action", async function() {
167205
var actionMock =await ActionMock.new();

test/genericschememulticall.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const ERC20Mock = artifacts.require("./ERC20Mock.sol");
88
const ActionMock = artifacts.require("./ActionMock.sol");
99
const DxDaoSchemeConstraints = artifacts.require("./DxDaoSchemeConstraints.sol");
1010
const SimpleSchemeConstraints = artifacts.require("./SimpleSchemeConstraints.sol");
11+
const Redeemer = artifacts.require("./Redeemer.sol");
1112

1213
class GenericSchemeParams {
1314
constructor() {
@@ -291,6 +292,76 @@ contract('GenericSchemeMultiCall', function(accounts) {
291292
});
292293
});
293294

295+
it("redeemer should fail if not executed from votingMachine", async function() {
296+
var actionMock =await ActionMock.new();
297+
var standardTokenMock = await ERC20Mock.new(accounts[0],1000);
298+
var testSetup = await setup(accounts,[actionMock.address],0,true,standardTokenMock.address);
299+
const encodeABI = await new web3.eth.Contract(actionMock.abi).methods.withoutReturnValue(testSetup.org.avatar.address).encodeABI();
300+
var tx = await testSetup.genericSchemeMultiCall.proposeCalls([actionMock.address],[encodeABI],[0],helpers.NULL_HASH);
301+
var proposalId = await helpers.getValueFromLogs(tx, '_proposalId');
302+
var redeemer = await Redeemer.new();
303+
var redeemRewards = await redeemer.redeemGenericSchemeMultiCall.call(
304+
testSetup.genericSchemeMultiCall.address,
305+
testSetup.genericSchemeParams.votingMachine.genesisProtocol.address,
306+
proposalId,
307+
accounts[0]);
308+
assert.equal(redeemRewards[0][1],0); //redeemRewards[0] gpRewards
309+
assert.equal(redeemRewards[0][2],0);
310+
assert.equal(redeemRewards.executed,false);
311+
assert.equal(redeemRewards.winningVote,0); // Cannot redeem, so will not get the winning vote
312+
tx = await redeemer.redeemGenericSchemeMultiCall(
313+
testSetup.genericSchemeMultiCall.address,
314+
testSetup.genericSchemeParams.votingMachine.genesisProtocol.address,
315+
proposalId,
316+
accounts[0]);
317+
await testSetup.genericSchemeMultiCall.getPastEvents('ProposalExecuted', {
318+
fromBlock: tx.blockNumber,
319+
toBlock: 'latest'
320+
})
321+
.then(function(events){
322+
assert.equal(events.length,0);
323+
});
324+
});
325+
326+
it("execute proposeVote -positive decision - execute with redeemer", async function() {
327+
var actionMock =await ActionMock.new();
328+
var standardTokenMock = await ERC20Mock.new(accounts[0],1000);
329+
var testSetup = await setup(accounts,[actionMock.address],0,true,standardTokenMock.address);
330+
var value = 50000;
331+
var callData = await createCallToActionMock(testSetup.org.avatar.address,actionMock);
332+
var tx = await testSetup.genericSchemeMultiCall.proposeCalls([actionMock.address,actionMock.address],[callData,callData],[value,value],helpers.NULL_HASH);
333+
var proposalId = await helpers.getValueFromLogs(tx, '_proposalId');
334+
//transfer some eth to avatar
335+
await web3.eth.sendTransaction({from:accounts[0],to:testSetup.org.avatar.address, value: web3.utils.toWei('1', "ether")});
336+
assert.equal(await web3.eth.getBalance(actionMock.address),0);
337+
await testSetup.genericSchemeParams.votingMachine.genesisProtocol.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]});
338+
var redeemer = await Redeemer.new();
339+
var redeemRewards = await redeemer.redeemGenericSchemeMultiCall.call(
340+
testSetup.genericSchemeMultiCall.address,
341+
testSetup.genericSchemeParams.votingMachine.genesisProtocol.address,
342+
proposalId,
343+
accounts[0]);
344+
assert.equal(redeemRewards[0][1],0); //redeemRewards[0] gpRewards
345+
assert.equal(redeemRewards[0][2],60);
346+
assert.equal(redeemRewards.executed,false); // GP already executed by vote
347+
assert.equal(redeemRewards.winningVote,1);
348+
tx = await redeemer.redeemGenericSchemeMultiCall(
349+
testSetup.genericSchemeMultiCall.address,
350+
testSetup.genericSchemeParams.votingMachine.genesisProtocol.address,
351+
proposalId,
352+
accounts[0]);
353+
await testSetup.genericSchemeMultiCall.getPastEvents('ProposalExecuted', {
354+
fromBlock: tx.blockNumber,
355+
toBlock: 'latest'
356+
})
357+
.then(function(events){
358+
assert.equal(events[0].event,"ProposalExecuted");
359+
assert.equal(events[0].args._proposalId,proposalId);
360+
});
361+
assert.equal(await web3.eth.getBalance(actionMock.address),value*2);
362+
});
363+
364+
294365
it("schemeconstrains eth value exceed limit", async function() {
295366
var actionMock =await ActionMock.new();
296367
var standardTokenMock = await ERC20Mock.new(accounts[0],1000);

0 commit comments

Comments
 (0)