/** * Source Code first verified at https://etherscan.io on Tuesday, April 16, 2019 (UTC) */ // File: contracts/evmscript/ScriptHelpers.sol /* * SPDX-License-Identitifer: MIT */ pragma solidity ^0.4.24; library ScriptHelpers { function getSpecId(bytes _script) internal pure returns (uint32) { return uint32At(_script, 0); } function uint256At(bytes _data, uint256 _location) internal pure returns (uint256 result) { assembly { result := mload(add(_data, add(0x20, _location))) } } function addressAt(bytes _data, uint256 _location) internal pure returns (address result) { uint256 word = uint256At(_data, _location); assembly { result := div(and(word, 0xffffffffffffffffffffffffffffffffffffffff000000000000000000000000), 0x1000000000000000000000000) } } function uint32At(bytes _data, uint256 _location) internal pure returns (uint32 result) { uint256 word = uint256At(_data, _location); assembly { result := div(and(word, 0xffffffff00000000000000000000000000000000000000000000000000000000), 0x100000000000000000000000000000000000000000000000000000000) } } function locationOf(bytes _data, uint256 _location) internal pure returns (uint256 result) { assembly { result := add(_data, add(0x20, _location)) } } function toBytes(bytes4 _sig) internal pure returns (bytes) { bytes memory payload = new bytes(4); assembly { mstore(add(payload, 0x20), _sig) } return payload; } } // File: contracts/common/Uint256Helpers.sol pragma solidity ^0.4.24; library Uint256Helpers { uint256 private constant MAX_UINT64 = uint64(-1); string private constant ERROR_NUMBER_TOO_BIG = "UINT64_NUMBER_TOO_BIG"; function toUint64(uint256 a) internal pure returns (uint64) { require(a <= MAX_UINT64, ERROR_NUMBER_TOO_BIG); return uint64(a); } } // File: contracts/common/TimeHelpers.sol /* * SPDX-License-Identitifer: MIT */ pragma solidity ^0.4.24; contract TimeHelpers { using Uint256Helpers for uint256; /** * @dev Returns the current block number. * Using a function rather than `block.number` allows us to easily mock the block number in * tests. */ function getBlockNumber() internal view returns (uint256) { return block.number; } /** * @dev Returns the current block number, converted to uint64. * Using a function rather than `block.number` allows us to easily mock the block number in * tests. */ function getBlockNumber64() internal view returns (uint64) { return getBlockNumber().toUint64(); } /** * @dev Returns the current timestamp. * Using a function rather than `block.timestamp` allows us to easily mock it in * tests. */ function getTimestamp() internal view returns (uint256) { return block.timestamp; // solium-disable-line security/no-block-members } /** * @dev Returns the current timestamp, converted to uint64. * Using a function rather than `block.timestamp` allows us to easily mock it in * tests. */ function getTimestamp64() internal view returns (uint64) { return getTimestamp().toUint64(); } } // File: contracts/common/UnstructuredStorage.sol /* * SPDX-License-Identitifer: MIT */ pragma solidity ^0.4.24; library UnstructuredStorage { function getStorageBool(bytes32 position) internal view returns (bool data) { assembly { data := sload(position) } } function getStorageAddress(bytes32 position) internal view returns (address data) { assembly { data := sload(position) } } function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) { assembly { data := sload(position) } } function getStorageUint256(bytes32 position) internal view returns (uint256 data) { assembly { data := sload(position) } } function setStorageBool(bytes32 position, bool data) internal { assembly { sstore(position, data) } } function setStorageAddress(bytes32 position, address data) internal { assembly { sstore(position, data) } } function setStorageBytes32(bytes32 position, bytes32 data) internal { assembly { sstore(position, data) } } function setStorageUint256(bytes32 position, uint256 data) internal { assembly { sstore(position, data) } } } // File: contracts/common/Initializable.sol /* * SPDX-License-Identitifer: MIT */ pragma solidity ^0.4.24; contract Initializable is TimeHelpers { using UnstructuredStorage for bytes32; // keccak256("aragonOS.initializable.initializationBlock") bytes32 internal constant INITIALIZATION_BLOCK_POSITION = 0xebb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e; string private constant ERROR_ALREADY_INITIALIZED = "INIT_ALREADY_INITIALIZED"; string private constant ERROR_NOT_INITIALIZED = "INIT_NOT_INITIALIZED"; modifier onlyInit { require(getInitializationBlock() == 0, ERROR_ALREADY_INITIALIZED); _; } modifier isInitialized { require(hasInitialized(), ERROR_NOT_INITIALIZED); _; } /** * @return Block number in which the contract was initialized */ function getInitializationBlock() public view returns (uint256) { return INITIALIZATION_BLOCK_POSITION.getStorageUint256(); } /** * @return Whether the contract has been initialized by the time of the current block */ function hasInitialized() public view returns (bool) { uint256 initializationBlock = getInitializationBlock(); return initializationBlock != 0 && getBlockNumber() >= initializationBlock; } /** * @dev Function to be called by top level contract after initialization has finished. */ function initialized() internal onlyInit { INITIALIZATION_BLOCK_POSITION.setStorageUint256(getBlockNumber()); } /** * @dev Function to be called by top level contract after initialization to enable the contract * at a future block number rather than immediately. */ function initializedAt(uint256 _blockNumber) internal onlyInit { INITIALIZATION_BLOCK_POSITION.setStorageUint256(_blockNumber); } } // File: contracts/common/Petrifiable.sol /* * SPDX-License-Identitifer: MIT */ pragma solidity ^0.4.24; contract Petrifiable is Initializable { // Use block UINT256_MAX (which should be never) as the initializable date uint256 internal constant PETRIFIED_BLOCK = uint256(-1); function isPetrified() public view returns (bool) { return getInitializationBlock() == PETRIFIED_BLOCK; } /** * @dev Function to be called by top level contract to prevent being initialized. * Useful for freezing base contracts when they're used behind proxies. */ function petrify() internal onlyInit { initializedAt(PETRIFIED_BLOCK); } } // File: contracts/common/Autopetrified.sol /* * SPDX-License-Identitifer: MIT */ pragma solidity ^0.4.24; contract Autopetrified is Petrifiable { constructor() public { // Immediately petrify base (non-proxy) instances of inherited contracts on deploy. // This renders them uninitializable (and unusable without a proxy). petrify(); } } // File: contracts/evmscript/IEVMScriptExecutor.sol /* * SPDX-License-Identitifer: MIT */ pragma solidity ^0.4.24; interface IEVMScriptExecutor { function execScript(bytes script, bytes input, address[] blacklist) external returns (bytes); function executorType() external pure returns (bytes32); } // File: contracts/evmscript/executors/BaseEVMScriptExecutor.sol /* * SPDX-License-Identitifer: MIT */ pragma solidity ^0.4.24; contract BaseEVMScriptExecutor is IEVMScriptExecutor, Autopetrified { uint256 internal constant SCRIPT_START_LOCATION = 4; } // File: contracts/evmscript/executors/CallsScript.sol pragma solidity 0.4.24; // Inspired by https://github.com/reverendus/tx-manager contract CallsScript is BaseEVMScriptExecutor { using ScriptHelpers for bytes; /* Hardcoded constants to save gas bytes32 internal constant EXECUTOR_TYPE = keccak256("CALLS_SCRIPT"); */ bytes32 internal constant EXECUTOR_TYPE = 0x2dc858a00f3e417be1394b87c07158e989ec681ce8cc68a9093680ac1a870302; string private constant ERROR_BLACKLISTED_CALL = "EVMCALLS_BLACKLISTED_CALL"; string private constant ERROR_INVALID_LENGTH = "EVMCALLS_INVALID_LENGTH"; /* This is manually crafted in assembly string private constant ERROR_CALL_REVERTED = "EVMCALLS_CALL_REVERTED"; */ event LogScriptCall(address indexed sender, address indexed src, address indexed dst); /** * @notice Executes a number of call scripts * @param _script [ specId (uint32) ] many calls with this structure -> * [ to (address: 20 bytes) ] [ calldataLength (uint32: 4 bytes) ] [ calldata (calldataLength bytes) ] * @param _blacklist Addresses the script cannot call to, or will revert. * @return Always returns empty byte array */ function execScript(bytes _script, bytes, address[] _blacklist) external isInitialized returns (bytes) { uint256 location = SCRIPT_START_LOCATION; // first 32 bits are spec id while (location < _script.length) { // Check there's at least address + calldataLength available require(_script.length - location >= 0x18, ERROR_INVALID_LENGTH); address contractAddress = _script.addressAt(location); // Check address being called is not blacklist for (uint256 i = 0; i < _blacklist.length; i++) { require(contractAddress != _blacklist[i], ERROR_BLACKLISTED_CALL); } // logged before execution to ensure event ordering in receipt // if failed entire execution is reverted regardless emit LogScriptCall(msg.sender, address(this), contractAddress); uint256 calldataLength = uint256(_script.uint32At(location + 0x14)); uint256 startOffset = location + 0x14 + 0x04; uint256 calldataStart = _script.locationOf(startOffset); // compute end of script / next location location = startOffset + calldataLength; require(location <= _script.length, ERROR_INVALID_LENGTH); bool success; assembly { success := call( sub(gas, 5000), // forward gas left - 5000 contractAddress, // address 0, // no value calldataStart, // calldata start calldataLength, // calldata length 0, // don't write output 0 // don't write output ) switch success case 0 { let ptr := mload(0x40) switch returndatasize case 0 { // No error data was returned, revert with "EVMCALLS_CALL_REVERTED" // See remix: doing a `revert("EVMCALLS_CALL_REVERTED")` always results in // this memory layout mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000) // error identifier mstore(add(ptr, 0x04), 0x0000000000000000000000000000000000000000000000000000000000000020) // starting offset mstore(add(ptr, 0x24), 0x0000000000000000000000000000000000000000000000000000000000000016) // reason length mstore(add(ptr, 0x44), 0x45564d43414c4c535f43414c4c5f524556455254454400000000000000000000) // reason revert(ptr, 100) // 100 = 4 + 3 * 32 (error identifier + 3 words for the ABI encoded error) } default { // Forward the full error data returndatacopy(ptr, 0, returndatasize) revert(ptr, returndatasize) } } default { } } } // No need to allocate empty bytes for the return as this can only be called via an delegatecall // (due to the isInitialized modifier) } function executorType() external pure returns (bytes32) { return EXECUTOR_TYPE; } }