From fd3c5cfa398d30e393ed4efd131f610da1a78845 Mon Sep 17 00:00:00 2001 From: benk10 Date: Thu, 23 Jul 2020 10:37:05 +0300 Subject: [PATCH 1/5] Add reputation token trade scheme --- contracts/schemes/ReputationTokenTrade.sol | 158 ++++++++++ test/helpers.js | 3 + test/reputationtokentrade.js | 330 +++++++++++++++++++++ 3 files changed, 491 insertions(+) create mode 100644 contracts/schemes/ReputationTokenTrade.sol create mode 100644 test/reputationtokentrade.js diff --git a/contracts/schemes/ReputationTokenTrade.sol b/contracts/schemes/ReputationTokenTrade.sol new file mode 100644 index 00000000..823161bb --- /dev/null +++ b/contracts/schemes/ReputationTokenTrade.sol @@ -0,0 +1,158 @@ +pragma solidity ^0.6.10; +// SPDX-License-Identifier: GPL-3.0 + +import "../votingMachines/VotingMachineCallbacks.sol"; + + +/** + * @title A scheme for trading ERC20 tokens for reputation with the DAO + */ +contract ReputationTokenTrade is VotingMachineCallbacks, ProposalExecuteInterface { + using SafeMath for uint256; + using SafeERC20 for IERC20; + + event TokenTradeProposed( + address indexed _avatar, + bytes32 indexed _proposalId, + string _descriptionHash, + address indexed _beneficiary, + IERC20 _sendToken, + uint256 _sendTokenAmount, + uint256 _reputationAmount + ); + + event TokenTradeProposalExecuted( + address indexed _avatar, + bytes32 indexed _proposalId, + address indexed _beneficiary, + IERC20 _sendToken, + uint256 _sendTokenAmount, + uint256 _reputationAmount + ); + + event ProposalExecuted(address indexed _avatar, bytes32 indexed _proposalId, int256 _decision); + + struct Proposal { + address beneficiary; + IERC20 sendToken; + uint256 sendTokenAmount; + uint256 reputationAmount; + bool passed; + bool decided; + } + + mapping(bytes32=>Proposal) public proposals; + + /** + * @dev initialize + * @param _avatar the avatar this scheme referring to. + * @param _votingMachine the voting machines address to + * @param _votingParams genesisProtocol parameters - valid only if _voteParamsHash is zero + * @param _voteOnBehalf genesisProtocol parameter - valid only if _voteParamsHash is zero + * @param _voteParamsHash voting machine parameters. + */ + function initialize( + Avatar _avatar, + IntVoteInterface _votingMachine, + uint256[11] calldata _votingParams, + address _voteOnBehalf, + bytes32 _voteParamsHash + ) + external + { + super._initializeGovernance(_avatar, _votingMachine, _voteParamsHash, _votingParams, _voteOnBehalf); + } + + /** + * @dev execution of proposals, can only be called by the voting machine in which the vote is held. + * @param _proposalId the ID of the voting in the voting machine + * @param _decision a parameter of the voting result, 1 yes and 2 is no. + * @return bool success + */ + function executeProposal(bytes32 _proposalId, int256 _decision) + external + onlyVotingMachine(_proposalId) + override + returns(bool) { + Proposal memory proposal = proposals[_proposalId]; + if (_decision == 1) { + proposals[_proposalId].passed = true; + } + proposals[_proposalId].decided = true; + + emit ProposalExecuted(address(avatar), _proposalId, _decision); + return true; + } + + + function execute(bytes32 _proposalId) public { + Proposal storage proposal = proposals[_proposalId]; + require(proposal.decided, "must be a decided proposal"); + if (proposal.passed) { + proposal.sendToken.safeTransfer(address(avatar), proposal.sendTokenAmount); + require( + Controller(avatar.owner()).mintReputation(proposal.reputationAmount, proposal.beneficiary), + "mint reputation should succeed" + ); + + emit TokenTradeProposalExecuted( + address(avatar), + _proposalId, + proposal.beneficiary, + proposal.sendToken, + proposal.sendTokenAmount, + proposal.reputationAmount + ); + delete proposals[_proposalId]; + } else { + Proposal memory _proposal = proposals[_proposalId]; + delete proposals[_proposalId]; + _proposal.sendToken.safeTransfer(address(_proposal.beneficiary), _proposal.sendTokenAmount); + } + } + + /** + * @dev propose to trade tokens with the DAO + * @param _sendToken token the proposer suggests to send to the DAO + * @param _sendTokenAmount token amount the proposer suggests to send to the DAO + * @param _reputationAmount reputation amount the proposer asks to receive from the DAO + * @param _descriptionHash proposal description hash + * @return proposalId an id which represents the proposal + */ + function proposeTokenTrade( + IERC20 _sendToken, + uint256 _sendTokenAmount, + uint256 _reputationAmount, + string memory _descriptionHash + ) + public + returns(bytes32 proposalId) + { + require(address(_sendToken) != address(0), "Token address must not be null"); + require(_sendTokenAmount > 0 && _reputationAmount > 0, "Token and reputation amount must be greater than 0"); + + _sendToken.safeTransferFrom(msg.sender, address(this), _sendTokenAmount); + proposalId = votingMachine.propose(2, voteParamsHash, msg.sender, address(avatar)); + + proposals[proposalId] = Proposal({ + beneficiary: msg.sender, + sendToken: _sendToken, + sendTokenAmount: _sendTokenAmount, + reputationAmount: _reputationAmount, + passed: false, + decided: false + }); + + proposalsBlockNumber[proposalId] = block.number; + + emit TokenTradeProposed( + address(avatar), + proposalId, + _descriptionHash, + msg.sender, + _sendToken, + _sendTokenAmount, + _reputationAmount + ); + } +} diff --git a/test/helpers.js b/test/helpers.js index 09e8d7c6..581e9ca4 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -39,6 +39,7 @@ const FundingRequest = artifacts.require("./FundingRequest.sol"); const Dictator = artifacts.require("./Dictator.sol"); const ReputationAdmin = artifacts.require("./ReputationAdmin.sol"); const TokenTrade = artifacts.require("./TokenTrade.sol"); +const ReputationTokenTrade = artifacts.require("./ReputationTokenTrade.sol"); const MAX_UINT_256 = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; @@ -161,6 +162,7 @@ const SOME_ADDRESS = '0x1000000000000000000000000000000000000000'; registration.dictator = await Dictator.new(); registration.reputationAdmin = await ReputationAdmin.new(); registration.tokenTrade = await TokenTrade.new(); + registration.reputationTokenTrade = await ReputationTokenTrade.new(); await implementationDirectory.setImplementation("DAOToken",registration.daoToken.address); await implementationDirectory.setImplementation("Reputation",registration.reputation.address); @@ -193,6 +195,7 @@ const SOME_ADDRESS = '0x1000000000000000000000000000000000000000'; await implementationDirectory.setImplementation("Dictator",registration.dictator.address); await implementationDirectory.setImplementation("ReputationAdmin",registration.reputationAdmin.address); await implementationDirectory.setImplementation("TokenTrade",registration.tokenTrade.address); + await implementationDirectory.setImplementation("ReputationTokenTrade",registration.reputationTokenTrade.address); registration.implementationDirectory = implementationDirectory; diff --git a/test/reputationtokentrade.js b/test/reputationtokentrade.js new file mode 100644 index 00000000..eb3b9fe0 --- /dev/null +++ b/test/reputationtokentrade.js @@ -0,0 +1,330 @@ +const helpers = require("./helpers"); +const { NULL_ADDRESS } = require("./helpers"); +const ReputationTokenTrade = artifacts.require('./ReputationTokenTrade.sol'); +const ERC20Mock = artifacts.require("./ERC20Mock.sol"); + + +class ReputationTokenTradeParams { + constructor() { + } +} + +var registration; +const setupReputationTokenTradeParams = async function( + accounts, + genesisProtocol, + token + ) { + var reputationTokenTradeParams = new ReputationTokenTradeParams(); + + if (genesisProtocol === true) { + reputationTokenTradeParams.votingMachine = await helpers.setupGenesisProtocol(accounts,token,helpers.NULL_ADDRESS); + reputationTokenTradeParams.initdata = await new web3.eth.Contract(registration.reputationTokenTrade.abi) + .methods + .initialize( + helpers.NULL_ADDRESS, + reputationTokenTradeParams.votingMachine.genesisProtocol.address, + reputationTokenTradeParams.votingMachine.uintArray, + reputationTokenTradeParams.votingMachine.voteOnBehalf, + helpers.NULL_HASH + ).encodeABI(); + } else { + reputationTokenTradeParams.votingMachine = await helpers.setupAbsoluteVote(helpers.NULL_ADDRESS,50); + reputationTokenTradeParams.initdata = await new web3.eth.Contract(registration.reputationTokenTrade.abi) + .methods + .initialize( + helpers.NULL_ADDRESS, + reputationTokenTradeParams.votingMachine.absoluteVote.address, + [0,0,0,0,0,0,0,0,0,0,0], + helpers.NULL_ADDRESS, + reputationTokenTradeParams.votingMachine.params + ).encodeABI(); + } + return reputationTokenTradeParams; +}; + +const setup = async function (accounts,reputationAccount=0,genesisProtocol = false,tokenAddress=0) { + var testSetup = new helpers.TestSetup(); + registration = await helpers.registerImplementation(); + testSetup.reputationArray = [10,70]; + var account2; + if (reputationAccount === 0) { + account2 = accounts[2]; + } else { + account2 = reputationAccount; + } + testSetup.proxyAdmin = accounts[5]; + testSetup.reputationTokenTradeParams= await setupReputationTokenTradeParams( + accounts, + genesisProtocol, + tokenAddress + ); + + var permissions = "0x0000001f"; + [testSetup.org,tx] = await helpers.setupOrganizationWithArraysDAOFactory(testSetup.proxyAdmin, + accounts, + registration, + [accounts[1], + account2], + [1000,0], + testSetup.reputationArray, + 0, + [web3.utils.fromAscii("ReputationTokenTrade")], + testSetup.reputationTokenTradeParams.initdata, + [helpers.getBytesLength(testSetup.reputationTokenTradeParams.initdata)], + [permissions], + "metaData" + ); + + testSetup.reputationTokenTrade = await ReputationTokenTrade.at(await helpers.getSchemeAddress(registration.daoFactory.address,tx)); + testSetup.standardTokenMock = await ERC20Mock.new(accounts[0],10000); + return testSetup; +}; + +contract('ReputationTokenTrade', function(accounts) { + before(function() { + helpers.etherForEveryone(accounts); + }); + + it("proposeTokenTrade log", async function() { + var testSetup = await setup(accounts); + assert.equal(await testSetup.standardTokenMock.balanceOf(accounts[0]), 10000); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.reputationTokenTrade.address), 0); + assert.equal(await testSetup.org.reputation.balanceOf(accounts[0]), 0); + await testSetup.standardTokenMock.approve(testSetup.reputationTokenTrade.address, 100); + + var tx = await testSetup.reputationTokenTrade.proposeTokenTrade( + testSetup.standardTokenMock.address, + 100, + 100, + helpers.NULL_HASH + ); + assert.equal(tx.logs.length, 1); + assert.equal(tx.logs[0].event, "TokenTradeProposed"); + assert.equal(tx.logs[0].args._beneficiary, accounts[0]); + assert.equal(tx.logs[0].args._descriptionHash, helpers.NULL_HASH); + assert.equal(tx.logs[0].args._sendToken, testSetup.standardTokenMock.address); + assert.equal(tx.logs[0].args._sendTokenAmount, 100); + assert.equal(tx.logs[0].args._reputationAmount, 100); + assert.equal(await testSetup.standardTokenMock.balanceOf(accounts[0]), 9900); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.reputationTokenTrade.address), 100); + }); + + it("proposeTokenTrade should fail if tokens aren't transferred", async function() { + var testSetup = await setup(accounts); + + try { + await testSetup.reputationTokenTrade.proposeTokenTrade( + testSetup.standardTokenMock.address, + 100, + 100, + helpers.NULL_HASH + ); + assert(false, "proposing should fail if token transfer fails"); + } catch(error) { + helpers.assertVMException(error); + } + }); + + it("proposeTokenTrade should fail if token not specified or amount is 0", async function() { + var testSetup = await setup(accounts); + await testSetup.standardTokenMock.approve(testSetup.reputationTokenTrade.address, 100); + + try { + await testSetup.reputationTokenTrade.proposeTokenTrade( + NULL_ADDRESS, + 100, + 100, + helpers.NULL_HASH + ); + assert(false, "proposing should fail if send token is null"); + } catch(error) { + helpers.assertVMException(error); + } + + try { + await testSetup.reputationTokenTrade.proposeTokenTrade( + testSetup.standardTokenMock.address, + 0, + 100, + helpers.NULL_HASH + ); + assert(false, "proposing should fail if send token amount is 0"); + } catch(error) { + helpers.assertVMException(error); + } + + try { + await testSetup.reputationTokenTrade.proposeTokenTrade( + testSetup.standardTokenMock.address, + 100, + 0, + helpers.NULL_HASH + ); + assert(false, "proposing should fail if reputation amount is 0"); + } catch(error) { + helpers.assertVMException(error); + } + }); + + it("execute proposal - fail - proposal should be deleted and funds returned", async function() { + var testSetup = await setup(accounts); + await testSetup.standardTokenMock.transfer(testSetup.org.avatar.address, 5000); + await testSetup.standardTokenMock.approve(testSetup.reputationTokenTrade.address, 100); + assert.equal(await testSetup.standardTokenMock.balanceOf(accounts[0]), 5000); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.reputationTokenTrade.address), 0); + assert.equal(await testSetup.org.reputation.balanceOf(accounts[0]), 0); + + var tx = await testSetup.reputationTokenTrade.proposeTokenTrade( + testSetup.standardTokenMock.address, + 100, + 100, + helpers.NULL_HASH + ); + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId'); + + assert.equal(await testSetup.standardTokenMock.balanceOf(accounts[0]), 4900); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.reputationTokenTrade.address), 100); + var proposal = await testSetup.reputationTokenTrade.proposals(proposalId); + assert.equal(proposal.sendToken, testSetup.standardTokenMock.address); + + await testSetup.reputationTokenTradeParams.votingMachine.absoluteVote.vote(proposalId,0,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + tx = await testSetup.reputationTokenTrade.execute(proposalId); + proposal = await testSetup.reputationTokenTrade.proposals(proposalId); + assert.equal(proposal.sendToken, NULL_ADDRESS); + assert.equal(await testSetup.standardTokenMock.balanceOf(accounts[0]), 5000); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.reputationTokenTrade.address), 0); + assert.equal(await testSetup.org.reputation.balanceOf(accounts[0]), 0); + await testSetup.reputationTokenTrade.getPastEvents("TokenTradeProposalExecuted", { + fromBlock: tx.blockNumber, + toBlock: 'latest' + }).then(function(events){ + assert.equal(events.length, 0); + }); + }); + + it("execute proposeVote - pass - proposal executed and deleted", async function() { + var testSetup = await setup(accounts); + await testSetup.standardTokenMock.transfer(testSetup.org.avatar.address, 5000); + await testSetup.standardTokenMock.approve(testSetup.reputationTokenTrade.address, 100); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.org.avatar.address), 5000); + assert.equal(await testSetup.standardTokenMock.balanceOf(accounts[0]), 5000); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.reputationTokenTrade.address), 0); + assert.equal(await testSetup.org.reputation.balanceOf(accounts[0]), 0); + + var tx = await testSetup.reputationTokenTrade.proposeTokenTrade( + testSetup.standardTokenMock.address, + 100, + 100, + helpers.NULL_HASH + ); + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId'); + + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.org.avatar.address), 5000); + assert.equal(await testSetup.standardTokenMock.balanceOf(accounts[0]), 4900); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.reputationTokenTrade.address), 100); + var proposal = await testSetup.reputationTokenTrade.proposals(proposalId); + assert.equal(proposal.sendToken, testSetup.standardTokenMock.address); + + await testSetup.reputationTokenTradeParams.votingMachine.absoluteVote.vote(proposalId, 1, 0, helpers.NULL_ADDRESS, {from:accounts[2]}); + tx = await testSetup.reputationTokenTrade.execute(proposalId); + proposal = await testSetup.reputationTokenTrade.proposals(proposalId); + assert.equal(proposal.sendToken, NULL_ADDRESS); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.org.avatar.address), 5100); + assert.equal(await testSetup.standardTokenMock.balanceOf(accounts[0]), 4900); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.reputationTokenTrade.address), 0); + assert.equal(await testSetup.org.reputation.balanceOf(accounts[0]), 100); + + await testSetup.reputationTokenTrade.getPastEvents("TokenTradeProposalExecuted", { + fromBlock: tx.blockNumber, + toBlock: 'latest' + }).then(function(events){ + assert.equal(events[0].event, "TokenTradeProposalExecuted"); + assert.equal(events[0].args._avatar, testSetup.org.avatar.address); + assert.equal(events[0].args._proposalId, proposalId); + assert.equal(events[0].args._beneficiary, accounts[0]); + assert.equal(events[0].args._sendToken, testSetup.standardTokenMock.address); + assert.equal(events[0].args._sendTokenAmount, 100); + assert.equal(events[0].args._reputationAmount, 100); + }); + }); + + it("execute proposal - pass - proposal cannot execute before passed/ twice", async function() { + var testSetup = await setup(accounts); + await testSetup.standardTokenMock.transfer(testSetup.org.avatar.address, 5000); + await testSetup.standardTokenMock.approve(testSetup.reputationTokenTrade.address, 100); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.org.avatar.address), 5000); + assert.equal(await testSetup.standardTokenMock.balanceOf(accounts[0]), 5000); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.reputationTokenTrade.address), 0); + + var tx = await testSetup.reputationTokenTrade.proposeTokenTrade( + testSetup.standardTokenMock.address, + 100, + 100, + helpers.NULL_HASH + ); + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId'); + + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.org.avatar.address), 5000); + assert.equal(await testSetup.standardTokenMock.balanceOf(accounts[0]), 4900); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.reputationTokenTrade.address), 100); + assert.equal(await testSetup.org.reputation.balanceOf(accounts[0]), 0); + var proposal = await testSetup.reputationTokenTrade.proposals(proposalId); + assert.equal(proposal.sendToken, testSetup.standardTokenMock.address); + + try { + await testSetup.reputationTokenTrade.execute(proposalId); + assert(false, "cannot execute before passed"); + } catch(error) { + helpers.assertVMException(error); + } + + await testSetup.reputationTokenTradeParams.votingMachine.absoluteVote.vote(proposalId, 1, 0, helpers.NULL_ADDRESS, {from:accounts[2]}); + tx = await testSetup.reputationTokenTrade.execute(proposalId); + + proposal = await testSetup.reputationTokenTrade.proposals(proposalId); + assert.equal(proposal.sendToken, NULL_ADDRESS); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.org.avatar.address), 5100); + assert.equal(await testSetup.standardTokenMock.balanceOf(accounts[0]), 4900); + assert.equal(await testSetup.standardTokenMock.balanceOf(testSetup.reputationTokenTrade.address), 0); + assert.equal(await testSetup.org.reputation.balanceOf(accounts[0]), 100); + await testSetup.reputationTokenTrade.getPastEvents("TokenTradeProposalExecuted", { + fromBlock: tx.blockNumber, + toBlock: 'latest' + }).then(function(events){ + assert.equal(events[0].event, "TokenTradeProposalExecuted"); + assert.equal(events[0].args._avatar, testSetup.org.avatar.address); + assert.equal(events[0].args._proposalId, proposalId); + assert.equal(events[0].args._beneficiary, accounts[0]); + assert.equal(events[0].args._sendToken, testSetup.standardTokenMock.address); + assert.equal(events[0].args._sendTokenAmount, 100); + assert.equal(events[0].args._reputationAmount, 100); + }); + + try { + await testSetup.reputationTokenTrade.execute(proposalId); + assert(false, "cannot execute twice"); + } catch(error) { + helpers.assertVMException(error); + } + }); + + it("cannot init twice", async function() { + var testSetup = await setup(accounts); + + try { + await testSetup.reputationTokenTrade.initialize( + testSetup.org.avatar.address, + accounts[0], + [0,0,0,0,0,0,0,0,0,0,0], + helpers.NULL_ADDRESS, + helpers.SOME_HASH + ); + assert(false, "cannot init twice"); + } catch(error) { + helpers.assertVMException(error); + } + + }); + +}); From e1a165004dea362b092d73f5d28a07fc749a8e90 Mon Sep 17 00:00:00 2001 From: benk10 Date: Thu, 23 Jul 2020 12:30:19 +0300 Subject: [PATCH 2/5] single delete --- contracts/schemes/ReputationTokenTrade.sol | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/contracts/schemes/ReputationTokenTrade.sol b/contracts/schemes/ReputationTokenTrade.sol index 823161bb..480d8f9c 100644 --- a/contracts/schemes/ReputationTokenTrade.sol +++ b/contracts/schemes/ReputationTokenTrade.sol @@ -103,12 +103,10 @@ contract ReputationTokenTrade is VotingMachineCallbacks, ProposalExecuteInterfac proposal.sendTokenAmount, proposal.reputationAmount ); - delete proposals[_proposalId]; } else { - Proposal memory _proposal = proposals[_proposalId]; - delete proposals[_proposalId]; - _proposal.sendToken.safeTransfer(address(_proposal.beneficiary), _proposal.sendTokenAmount); + proposal.sendToken.safeTransfer(address(proposal.beneficiary), proposal.sendTokenAmount); } + delete proposals[_proposalId]; } /** From a7576a7ac0905a9352d4e944c4920b922d610f6c Mon Sep 17 00:00:00 2001 From: benk10 Date: Mon, 27 Jul 2020 18:35:29 +0300 Subject: [PATCH 3/5] Remove unused var --- contracts/schemes/ReputationTokenTrade.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/schemes/ReputationTokenTrade.sol b/contracts/schemes/ReputationTokenTrade.sol index 480d8f9c..f09b81a8 100644 --- a/contracts/schemes/ReputationTokenTrade.sol +++ b/contracts/schemes/ReputationTokenTrade.sol @@ -74,7 +74,6 @@ contract ReputationTokenTrade is VotingMachineCallbacks, ProposalExecuteInterfac onlyVotingMachine(_proposalId) override returns(bool) { - Proposal memory proposal = proposals[_proposalId]; if (_decision == 1) { proposals[_proposalId].passed = true; } From 6c986f078028ceb7705a53a13fafefe7971e26ed Mon Sep 17 00:00:00 2001 From: benk10 Date: Thu, 30 Jul 2020 15:38:28 +0300 Subject: [PATCH 4/5] Update ReputationTokenTrade.sol --- contracts/schemes/ReputationTokenTrade.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/schemes/ReputationTokenTrade.sol b/contracts/schemes/ReputationTokenTrade.sol index f09b81a8..cd96a350 100644 --- a/contracts/schemes/ReputationTokenTrade.sol +++ b/contracts/schemes/ReputationTokenTrade.sol @@ -85,7 +85,7 @@ contract ReputationTokenTrade is VotingMachineCallbacks, ProposalExecuteInterfac function execute(bytes32 _proposalId) public { - Proposal storage proposal = proposals[_proposalId]; + Proposal memory proposal = proposals[_proposalId]; require(proposal.decided, "must be a decided proposal"); if (proposal.passed) { proposal.sendToken.safeTransfer(address(avatar), proposal.sendTokenAmount); From acc1874d527a7aada8cdd38569fed9ef07906db8 Mon Sep 17 00:00:00 2001 From: benk10 Date: Thu, 30 Jul 2020 16:29:25 +0300 Subject: [PATCH 5/5] lint --- contracts/schemes/ReputationTokenTrade.sol | 1 - contracts/schemes/TokenTrade.sol | 2 -- 2 files changed, 3 deletions(-) diff --git a/contracts/schemes/ReputationTokenTrade.sol b/contracts/schemes/ReputationTokenTrade.sol index cd96a350..a7d71491 100644 --- a/contracts/schemes/ReputationTokenTrade.sol +++ b/contracts/schemes/ReputationTokenTrade.sol @@ -83,7 +83,6 @@ contract ReputationTokenTrade is VotingMachineCallbacks, ProposalExecuteInterfac return true; } - function execute(bytes32 _proposalId) public { Proposal memory proposal = proposals[_proposalId]; require(proposal.decided, "must be a decided proposal"); diff --git a/contracts/schemes/TokenTrade.sol b/contracts/schemes/TokenTrade.sol index 7284b2a4..91ccc21c 100644 --- a/contracts/schemes/TokenTrade.sol +++ b/contracts/schemes/TokenTrade.sol @@ -77,7 +77,6 @@ contract TokenTrade is VotingMachineCallbacks, ProposalExecuteInterface { onlyVotingMachine(_proposalId) override returns(bool) { - Proposal memory proposal = proposals[_proposalId]; if (_decision == 1) { proposals[_proposalId].passed = true; } @@ -87,7 +86,6 @@ contract TokenTrade is VotingMachineCallbacks, ProposalExecuteInterface { return true; } - function execute(bytes32 _proposalId) public { Proposal storage proposal = proposals[_proposalId]; require(proposal.decided, "must be a decided proposal");