-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* added GenericSchemeMultiCall * Cleanup GenericSchemeMultiCall * Cleanup GenericSchemeMultiCall * Cleanup GenericSchemeMultiCall * Added contract whitelist & tokenApproval * GenericSchemeMultiCall tests * . * Added GenericSchemeMultiCall tests * Added GenericSchemeMultiCall tests * added approval spender restriction * cleanup * cleanup * cleanup * cleanup * cleanup * cleanup * GenericSchemeMultiCall v.0.0.1 * GenericSchemeMultiCall * updated .gitignore * Cleanup * cleanup * Update package.json * Update .gitignore * Update package-lock.json * Update 2_deploy_organization.js * Change version to 0.0.1-rc.45 Co-authored-by: Nico Elzer <>
- Loading branch information
Showing
3 changed files
with
598 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,220 @@ | ||
pragma solidity 0.5.17; | ||
pragma experimental ABIEncoderV2; | ||
|
||
import "@daostack/infra/contracts/votingMachines/IntVoteInterface.sol"; | ||
import "@daostack/infra/contracts/votingMachines/ProposalExecuteInterface.sol"; | ||
import "../votingMachines/VotingMachineCallbacks.sol"; | ||
|
||
/** | ||
* @title GenericSchemeMultiCall. | ||
* @dev A scheme for proposing and executing calls to multiple arbitrary function | ||
* on one or multiple contracts on behalf of the organization avatar. | ||
*/ | ||
contract GenericSchemeMultiCall is VotingMachineCallbacks, ProposalExecuteInterface { | ||
|
||
// Details of a voting proposal: | ||
struct MultiCallProposal { | ||
address[] contractsToCall; | ||
bytes[] callsData; | ||
uint256[] values; | ||
bool exist; | ||
bool passed; | ||
} | ||
|
||
mapping(bytes32=>MultiCallProposal) public proposals; | ||
|
||
IntVoteInterface public votingMachine; | ||
bytes32 public voteParams; | ||
mapping(address=>bool) internal contractWhitelist; | ||
address[] public whitelistedContracts; | ||
Avatar public avatar; | ||
|
||
event NewMultiCallProposal( | ||
address indexed _avatar, | ||
bytes32 indexed _proposalId, | ||
bytes[] _callsData, | ||
uint256[] _values, | ||
string _descriptionHash, | ||
address[] _contractsToCall | ||
); | ||
|
||
event ProposalExecuted( | ||
address indexed _avatar, | ||
bytes32 indexed _proposalId | ||
); | ||
|
||
event ProposalCallExecuted( | ||
address indexed _avatar, | ||
bytes32 indexed _proposalId, | ||
address _contractToCall, | ||
bool _success, | ||
bytes _callDataReturnValue | ||
); | ||
|
||
event ProposalExecutedByVotingMachine( | ||
address indexed _avatar, | ||
bytes32 indexed _proposalId, | ||
int256 _param | ||
); | ||
|
||
event ProposalDeleted(address indexed _avatar, bytes32 indexed _proposalId); | ||
|
||
/** | ||
* @dev initialize | ||
* @param _avatar the avatar to mint reputation from | ||
* @param _votingMachine the voting machines address to | ||
* @param _voteParams voting machine parameters. | ||
* @param _contractWhitelist the contracts the scheme is allowed to interact with | ||
* | ||
*/ | ||
function initialize( | ||
Avatar _avatar, | ||
IntVoteInterface _votingMachine, | ||
bytes32 _voteParams, | ||
address[] calldata _contractWhitelist | ||
) | ||
external | ||
{ | ||
require(avatar == Avatar(0), "can be called only one time"); | ||
require(_avatar != Avatar(0), "avatar cannot be zero"); | ||
require(_contractWhitelist.length > 0, "contractWhitelist cannot be empty"); | ||
avatar = _avatar; | ||
votingMachine = _votingMachine; | ||
voteParams = _voteParams; | ||
/* Whitelist controller by default*/ | ||
Controller controller = Controller(_avatar.owner()); | ||
whitelistedContracts.push(address(controller)); | ||
contractWhitelist[address(controller)] = true; | ||
|
||
for (uint i = 0; i < _contractWhitelist.length; i++) { | ||
contractWhitelist[_contractWhitelist[i]] = true; | ||
whitelistedContracts.push(_contractWhitelist[i]); | ||
} | ||
} | ||
|
||
/** | ||
* @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) | ||
returns(bool) { | ||
MultiCallProposal storage proposal = proposals[_proposalId]; | ||
require(proposal.exist, "must be a live proposal"); | ||
require(proposal.passed == false, "cannot execute twice"); | ||
|
||
if (_decision == 1) { | ||
proposal.passed = true; | ||
execute(_proposalId); | ||
} else { | ||
delete proposals[_proposalId]; | ||
emit ProposalDeleted(address(avatar), _proposalId); | ||
} | ||
|
||
emit ProposalExecutedByVotingMachine(address(avatar), _proposalId, _decision); | ||
return true; | ||
} | ||
|
||
/** | ||
* @dev execution of proposals after it has been decided by the voting machine | ||
* @param _proposalId the ID of the voting in the voting machine | ||
*/ | ||
function execute(bytes32 _proposalId) public { | ||
MultiCallProposal storage proposal = proposals[_proposalId]; | ||
require(proposal.exist, "must be a live proposal"); | ||
require(proposal.passed, "proposal must passed by voting machine"); | ||
proposal.exist = false; | ||
bytes memory genericCallReturnValue; | ||
bool success; | ||
Controller controller = Controller(whitelistedContracts[0]); | ||
|
||
for (uint i = 0; i < proposal.contractsToCall.length; i++) { | ||
if (proposal.contractsToCall[i] == address(controller)) { | ||
(IERC20 extToken, | ||
address spender, | ||
uint256 valueToSpend | ||
) = | ||
abi.decode( | ||
proposal.callsData[i], | ||
(IERC20, address, uint256) | ||
); | ||
(success) = controller.externalTokenApproval(extToken, spender, valueToSpend, avatar); | ||
} else { | ||
(success, genericCallReturnValue) = | ||
controller.genericCall(proposal.contractsToCall[i], proposal.callsData[i], avatar, proposal.values[i]); | ||
} | ||
|
||
emit ProposalCallExecuted( | ||
address(avatar), | ||
_proposalId, | ||
proposal.contractsToCall[i], | ||
success, | ||
genericCallReturnValue | ||
); | ||
} | ||
|
||
delete proposals[_proposalId]; | ||
emit ProposalDeleted(address(avatar), _proposalId); | ||
emit ProposalExecuted(address(avatar), _proposalId); | ||
} | ||
|
||
/** | ||
* @dev propose to call one or multiple contracts on behalf of the _avatar | ||
* The function trigger NewMultiCallProposal event | ||
* @param _contractsToCall the contracts to be called | ||
* @param _callsData - The abi encode data for the calls | ||
* @param _values value(ETH) to transfer with the calls | ||
* @param _descriptionHash proposal description hash | ||
* @return an id which represents the proposal | ||
*/ | ||
function proposeCalls( | ||
address[] memory _contractsToCall, | ||
bytes[] memory _callsData, | ||
uint256[] memory _values, | ||
string memory _descriptionHash | ||
) | ||
public | ||
returns(bytes32 proposalId) | ||
{ | ||
require( | ||
(_contractsToCall.length == _callsData.length) && (_contractsToCall.length == _values.length), | ||
"Wrong length of _contractsToCall, _callsData or _values arrays" | ||
); | ||
Controller controller = Controller(whitelistedContracts[0]); | ||
for (uint i = 0; i < _contractsToCall.length; i++) { | ||
require( | ||
contractWhitelist[_contractsToCall[i]], "contractToCall is not whitelisted" | ||
); | ||
if (_contractsToCall[i] == address(controller)) { | ||
(IERC20 extToken, | ||
address spender, | ||
uint256 valueToSpend | ||
) = | ||
abi.decode( | ||
_callsData[i], | ||
(IERC20, address, uint256) | ||
); | ||
require(contractWhitelist[spender], "spender contract not whitelisted"); | ||
} | ||
} | ||
proposalId = votingMachine.propose(2, voteParams, msg.sender, address(avatar)); | ||
|
||
proposals[proposalId] = MultiCallProposal({ | ||
contractsToCall: _contractsToCall, | ||
callsData: _callsData, | ||
values: _values, | ||
exist: true, | ||
passed: false | ||
}); | ||
proposalsInfo[address(votingMachine)][proposalId] = ProposalInfo({ | ||
blockNumber:block.number, | ||
avatar:avatar | ||
}); | ||
|
||
emit NewMultiCallProposal(address(avatar), proposalId, _callsData, _values, _descriptionHash, _contractsToCall); | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.