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";
    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) {
            address contractAddress = _script.addressAt(location);
            // Check address being called is not blacklist
            for (uint 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), contractAddress, 0, calldataStart, calldataLength, 0, 0)
            }

            require(success, ERROR_CALL_REVERTED);
        }
    }

    function executorType() external pure returns (bytes32) {
        return EXECUTOR_TYPE;
    }
}
// Inspired by https://github.com/reverendus/tx-manager
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) {
        address contractAddress = _script.addressAt(location);
        // Check address being called is not blacklist
        for (uint 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), contractAddress, 0, calldataStart, calldataLength, 0, 0)
        }

        require(success, ERROR_CALL_REVERTED);
    }
}

/**
 * @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
 */