// 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;
}
}
{
"compilationTarget": {
"CallsScript.sol": "CallsScript"
},
"evmVersion": "byzantium",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 10000
},
"remappings": []
}
[{"constant":true,"inputs":[],"name":"hasInitialized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_script","type":"bytes"},{"name":"","type":"bytes"},{"name":"_blacklist","type":"address[]"}],"name":"execScript","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"executorType","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"getInitializationBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isPetrified","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"dst","type":"address"}],"name":"LogScriptCall","type":"event"}]