Skip to content

Commit

Permalink
genericschemeMultipleCalls - fix warnings (#787)
Browse files Browse the repository at this point in the history
* fix warnings

* remove pragma experimental
  • Loading branch information
orenyodfat authored Sep 10, 2020
1 parent 3e5ea1e commit 63285cd
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 42 deletions.
79 changes: 79 additions & 0 deletions contracts/libs/BytesLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* @title Solidity Bytes Arrays Utils
* @author Gonçalo Sá <[email protected]>
*
* @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
* The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
*/

pragma solidity 0.5.17;


library BytesLib {

function slice(
bytes memory _bytes,
uint _start,
uint _length
)
internal
pure
returns (bytes memory)
// solhint-disable-next-line function-max-lines
{
require(_bytes.length >= (_start + _length));

bytes memory tempBytes;
// solhint-disable-next-line no-inline-assembly
assembly {
switch iszero(_length)
case 0 {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)

// The first word of the slice result is potentially a partial
// word read from the original array. To read it, we calculate
// the length of that partial word and start copying that many
// bytes into the array. The first word we copy will start with
// data we don't care about, but the last `lengthmod` bytes will
// land at the beginning of the contents of the new array. When
// we're done copying, we overwrite the full first word with
// the actual length of the slice.
let lengthmod := and(_length, 31)

// The multiplication in the next line is necessary
// because when slicing multiples of 32 bytes (lengthmod == 0)
// the following copy loop was copying the origin's length
// and then ending prematurely not copying everything it should.
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
let end := add(mc, _length)

for {
// The multiplication in the next line has the same exact purpose
// as the one above.
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}

mstore(tempBytes, _length)

//update free-memory pointer
//allocating the array padded to 32 bytes like the compiler does now
mstore(0x40, and(add(mc, 31), not(31)))
}
//if we want a zero-length slice let's just return a zero-length array
default {
tempBytes := mload(0x40)

mstore(0x40, add(tempBytes, 0x20))
}
}

return tempBytes;
}
}
48 changes: 30 additions & 18 deletions contracts/schemes/GenericSchemeMultiCall.sol
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
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";
import "../libs/BytesLib.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 {
using BytesLib for bytes;
using SafeMath for uint256;

// Details of a voting proposal:
struct MultiCallProposal {
address[] contractsToCall;
bytes[] callsData;
bytes callsData;
uint256[] callsDataLens;
uint256[] values;
bool exist;
bool passed;
Expand All @@ -32,7 +36,7 @@ contract GenericSchemeMultiCall is VotingMachineCallbacks, ProposalExecuteInterf
event NewMultiCallProposal(
address indexed _avatar,
bytes32 indexed _proposalId,
bytes[] _callsData,
bytes _callsData,
uint256[] _values,
string _descriptionHash,
address[] _contractsToCall
Expand All @@ -47,6 +51,7 @@ contract GenericSchemeMultiCall is VotingMachineCallbacks, ProposalExecuteInterf
address indexed _avatar,
bytes32 indexed _proposalId,
address _contractToCall,
bytes _callsData,
bool _success,
bytes _callDataReturnValue
);
Expand All @@ -65,7 +70,7 @@ contract GenericSchemeMultiCall is VotingMachineCallbacks, ProposalExecuteInterf
* @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,
Expand All @@ -85,7 +90,7 @@ contract GenericSchemeMultiCall is VotingMachineCallbacks, ProposalExecuteInterf
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]);
Expand Down Expand Up @@ -130,27 +135,30 @@ contract GenericSchemeMultiCall is VotingMachineCallbacks, ProposalExecuteInterf
bytes memory genericCallReturnValue;
bool success;
Controller controller = Controller(whitelistedContracts[0]);
uint256 startIndex = 0;

for (uint i = 0; i < proposal.contractsToCall.length; i++) {
bytes memory callData = proposal.callsData.slice(startIndex, proposal.callsDataLens[i]);
if (proposal.contractsToCall[i] == address(controller)) {
(IERC20 extToken,
address spender,
uint256 valueToSpend
) =
abi.decode(
proposal.callsData[i],
callData,
(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]);
controller.genericCall(proposal.contractsToCall[i], callData, avatar, proposal.values[i]);
}

startIndex = startIndex.add(proposal.callsDataLens[i]);
emit ProposalCallExecuted(
address(avatar),
_proposalId,
proposal.contractsToCall[i],
callData,
success,
genericCallReturnValue
);
Expand All @@ -164,47 +172,51 @@ contract GenericSchemeMultiCall is VotingMachineCallbacks, ProposalExecuteInterf
/**
* @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 _contractsToCall the contracts to be called
* @param _callsData - The abi encode data for the calls
* @param _callsDataLens the length of each callData
* @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,
bytes memory _callsData,
uint256[] memory _callsDataLens,
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"
(_contractsToCall.length == _callsDataLens.length) && (_contractsToCall.length == _values.length),
"Wrong length of _contractsToCall, _callsDataLens or _values arrays"
);
Controller controller = Controller(whitelistedContracts[0]);
uint256 startIndex = 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
) =

bytes memory callData = _callsData.slice(startIndex, _callsDataLens[i]);
(, address spender,) =
abi.decode(
_callsData[i],
callData,
(IERC20, address, uint256)
);
require(contractWhitelist[spender], "spender contract not whitelisted");
}
startIndex = startIndex.add(_callsDataLens[i]);
}
proposalId = votingMachine.propose(2, voteParams, msg.sender, address(avatar));

proposals[proposalId] = MultiCallProposal({
contractsToCall: _contractsToCall,
callsData: _callsData,
callsDataLens: _callsDataLens,
values: _values,
exist: true,
passed: false
Expand All @@ -217,4 +229,4 @@ contract GenericSchemeMultiCall is VotingMachineCallbacks, ProposalExecuteInterf
emit NewMultiCallProposal(address(avatar), proposalId, _callsData, _values, _descriptionHash, _contractsToCall);

}
}
}
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 63285cd

Please sign in to comment.