Skip to content

Commit 63285cd

Browse files
authored
genericschemeMultipleCalls - fix warnings (#787)
* fix warnings * remove pragma experimental
1 parent 3e5ea1e commit 63285cd

File tree

4 files changed

+165
-42
lines changed

4 files changed

+165
-42
lines changed

contracts/libs/BytesLib.sol

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* @title Solidity Bytes Arrays Utils
3+
* @author Gonçalo Sá <[email protected]>
4+
*
5+
* @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
6+
* The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
7+
*/
8+
9+
pragma solidity 0.5.17;
10+
11+
12+
library BytesLib {
13+
14+
function slice(
15+
bytes memory _bytes,
16+
uint _start,
17+
uint _length
18+
)
19+
internal
20+
pure
21+
returns (bytes memory)
22+
// solhint-disable-next-line function-max-lines
23+
{
24+
require(_bytes.length >= (_start + _length));
25+
26+
bytes memory tempBytes;
27+
// solhint-disable-next-line no-inline-assembly
28+
assembly {
29+
switch iszero(_length)
30+
case 0 {
31+
// Get a location of some free memory and store it in tempBytes as
32+
// Solidity does for memory variables.
33+
tempBytes := mload(0x40)
34+
35+
// The first word of the slice result is potentially a partial
36+
// word read from the original array. To read it, we calculate
37+
// the length of that partial word and start copying that many
38+
// bytes into the array. The first word we copy will start with
39+
// data we don't care about, but the last `lengthmod` bytes will
40+
// land at the beginning of the contents of the new array. When
41+
// we're done copying, we overwrite the full first word with
42+
// the actual length of the slice.
43+
let lengthmod := and(_length, 31)
44+
45+
// The multiplication in the next line is necessary
46+
// because when slicing multiples of 32 bytes (lengthmod == 0)
47+
// the following copy loop was copying the origin's length
48+
// and then ending prematurely not copying everything it should.
49+
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
50+
let end := add(mc, _length)
51+
52+
for {
53+
// The multiplication in the next line has the same exact purpose
54+
// as the one above.
55+
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
56+
} lt(mc, end) {
57+
mc := add(mc, 0x20)
58+
cc := add(cc, 0x20)
59+
} {
60+
mstore(mc, mload(cc))
61+
}
62+
63+
mstore(tempBytes, _length)
64+
65+
//update free-memory pointer
66+
//allocating the array padded to 32 bytes like the compiler does now
67+
mstore(0x40, and(add(mc, 31), not(31)))
68+
}
69+
//if we want a zero-length slice let's just return a zero-length array
70+
default {
71+
tempBytes := mload(0x40)
72+
73+
mstore(0x40, add(tempBytes, 0x20))
74+
}
75+
}
76+
77+
return tempBytes;
78+
}
79+
}

contracts/schemes/GenericSchemeMultiCall.sol

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
pragma solidity 0.5.17;
2-
pragma experimental ABIEncoderV2;
32

43
import "@daostack/infra/contracts/votingMachines/IntVoteInterface.sol";
54
import "@daostack/infra/contracts/votingMachines/ProposalExecuteInterface.sol";
65
import "../votingMachines/VotingMachineCallbacks.sol";
6+
import "../libs/BytesLib.sol";
7+
78

89
/**
910
* @title GenericSchemeMultiCall.
1011
* @dev A scheme for proposing and executing calls to multiple arbitrary function
1112
* on one or multiple contracts on behalf of the organization avatar.
1213
*/
1314
contract GenericSchemeMultiCall is VotingMachineCallbacks, ProposalExecuteInterface {
15+
using BytesLib for bytes;
16+
using SafeMath for uint256;
1417

1518
// Details of a voting proposal:
1619
struct MultiCallProposal {
1720
address[] contractsToCall;
18-
bytes[] callsData;
21+
bytes callsData;
22+
uint256[] callsDataLens;
1923
uint256[] values;
2024
bool exist;
2125
bool passed;
@@ -32,7 +36,7 @@ contract GenericSchemeMultiCall is VotingMachineCallbacks, ProposalExecuteInterf
3236
event NewMultiCallProposal(
3337
address indexed _avatar,
3438
bytes32 indexed _proposalId,
35-
bytes[] _callsData,
39+
bytes _callsData,
3640
uint256[] _values,
3741
string _descriptionHash,
3842
address[] _contractsToCall
@@ -47,6 +51,7 @@ contract GenericSchemeMultiCall is VotingMachineCallbacks, ProposalExecuteInterf
4751
address indexed _avatar,
4852
bytes32 indexed _proposalId,
4953
address _contractToCall,
54+
bytes _callsData,
5055
bool _success,
5156
bytes _callDataReturnValue
5257
);
@@ -65,7 +70,7 @@ contract GenericSchemeMultiCall is VotingMachineCallbacks, ProposalExecuteInterf
6570
* @param _votingMachine the voting machines address to
6671
* @param _voteParams voting machine parameters.
6772
* @param _contractWhitelist the contracts the scheme is allowed to interact with
68-
*
73+
*
6974
*/
7075
function initialize(
7176
Avatar _avatar,
@@ -85,7 +90,7 @@ contract GenericSchemeMultiCall is VotingMachineCallbacks, ProposalExecuteInterf
8590
Controller controller = Controller(_avatar.owner());
8691
whitelistedContracts.push(address(controller));
8792
contractWhitelist[address(controller)] = true;
88-
93+
8994
for (uint i = 0; i < _contractWhitelist.length; i++) {
9095
contractWhitelist[_contractWhitelist[i]] = true;
9196
whitelistedContracts.push(_contractWhitelist[i]);
@@ -130,27 +135,30 @@ contract GenericSchemeMultiCall is VotingMachineCallbacks, ProposalExecuteInterf
130135
bytes memory genericCallReturnValue;
131136
bool success;
132137
Controller controller = Controller(whitelistedContracts[0]);
138+
uint256 startIndex = 0;
133139

134140
for (uint i = 0; i < proposal.contractsToCall.length; i++) {
141+
bytes memory callData = proposal.callsData.slice(startIndex, proposal.callsDataLens[i]);
135142
if (proposal.contractsToCall[i] == address(controller)) {
136143
(IERC20 extToken,
137144
address spender,
138145
uint256 valueToSpend
139146
) =
140147
abi.decode(
141-
proposal.callsData[i],
148+
callData,
142149
(IERC20, address, uint256)
143150
);
144151
(success) = controller.externalTokenApproval(extToken, spender, valueToSpend, avatar);
145152
} else {
146153
(success, genericCallReturnValue) =
147-
controller.genericCall(proposal.contractsToCall[i], proposal.callsData[i], avatar, proposal.values[i]);
154+
controller.genericCall(proposal.contractsToCall[i], callData, avatar, proposal.values[i]);
148155
}
149-
156+
startIndex = startIndex.add(proposal.callsDataLens[i]);
150157
emit ProposalCallExecuted(
151158
address(avatar),
152159
_proposalId,
153160
proposal.contractsToCall[i],
161+
callData,
154162
success,
155163
genericCallReturnValue
156164
);
@@ -164,47 +172,51 @@ contract GenericSchemeMultiCall is VotingMachineCallbacks, ProposalExecuteInterf
164172
/**
165173
* @dev propose to call one or multiple contracts on behalf of the _avatar
166174
* The function trigger NewMultiCallProposal event
167-
* @param _contractsToCall the contracts to be called
175+
* @param _contractsToCall the contracts to be called
168176
* @param _callsData - The abi encode data for the calls
177+
* @param _callsDataLens the length of each callData
169178
* @param _values value(ETH) to transfer with the calls
170179
* @param _descriptionHash proposal description hash
171180
* @return an id which represents the proposal
172181
*/
173182
function proposeCalls(
174183
address[] memory _contractsToCall,
175-
bytes[] memory _callsData,
184+
bytes memory _callsData,
185+
uint256[] memory _callsDataLens,
176186
uint256[] memory _values,
177187
string memory _descriptionHash
178188
)
179189
public
180190
returns(bytes32 proposalId)
181191
{
182192
require(
183-
(_contractsToCall.length == _callsData.length) && (_contractsToCall.length == _values.length),
184-
"Wrong length of _contractsToCall, _callsData or _values arrays"
193+
(_contractsToCall.length == _callsDataLens.length) && (_contractsToCall.length == _values.length),
194+
"Wrong length of _contractsToCall, _callsDataLens or _values arrays"
185195
);
186196
Controller controller = Controller(whitelistedContracts[0]);
197+
uint256 startIndex = 0;
187198
for (uint i = 0; i < _contractsToCall.length; i++) {
188199
require(
189200
contractWhitelist[_contractsToCall[i]], "contractToCall is not whitelisted"
190201
);
191202
if (_contractsToCall[i] == address(controller)) {
192-
(IERC20 extToken,
193-
address spender,
194-
uint256 valueToSpend
195-
) =
203+
204+
bytes memory callData = _callsData.slice(startIndex, _callsDataLens[i]);
205+
(, address spender,) =
196206
abi.decode(
197-
_callsData[i],
207+
callData,
198208
(IERC20, address, uint256)
199209
);
200210
require(contractWhitelist[spender], "spender contract not whitelisted");
201211
}
212+
startIndex = startIndex.add(_callsDataLens[i]);
202213
}
203214
proposalId = votingMachine.propose(2, voteParams, msg.sender, address(avatar));
204215

205216
proposals[proposalId] = MultiCallProposal({
206217
contractsToCall: _contractsToCall,
207218
callsData: _callsData,
219+
callsDataLens: _callsDataLens,
208220
values: _values,
209221
exist: true,
210222
passed: false
@@ -217,4 +229,4 @@ contract GenericSchemeMultiCall is VotingMachineCallbacks, ProposalExecuteInterf
217229
emit NewMultiCallProposal(address(avatar), proposalId, _callsData, _values, _descriptionHash, _contractsToCall);
218230

219231
}
220-
}
232+
}

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.

0 commit comments

Comments
 (0)