// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.20;
import "src/weiroll/VM.sol";
import "solady/utils/SafeTransferLib.sol";
/// @title AllMight
/// @notice Weiroll implementation that allow to do literally anything.
/// @author Stake DAO
/// @custom:contact contact@stakedao.org
contract AllMight is VM {
using SafeTransferLib for address;
/// @notice Address of the governance contract.
address public governance;
/// @notice Address of the future governance contract.
address public futureGovernance;
/// @notice Address authorized to call the execute function.
mapping(address => bool) public isAllowed;
////////////////////////////////////////////////////////////////
/// --- EVENTS & ERRORS
///////////////////////////////////////////////////////////////
/// @notice Event emitted when a new governance is proposed.
event GovernanceProposed(address indexed newGovernance);
/// @notice Event emitted when the governance is changed.
event GovernanceChanged(address indexed newGovernance);
/// @notice Throws if caller is not the governance.
error GOVERNANCE();
/// @notice Throws if caller is not allowed.
error NOT_ALLOWED();
/// @notice Throws if the length of the tokens and amounts arrays are not equal.
error WRONG_LENGTH();
////////////////////////////////////////////////////////////////
/// --- MODIFIERS
///////////////////////////////////////////////////////////////
modifier onlyGovernance() {
if (msg.sender != governance) revert GOVERNANCE();
_;
}
modifier onlyAllowed() {
if (!isAllowed[msg.sender]) revert NOT_ALLOWED();
_;
}
constructor(address _governance) {
governance = _governance;
}
////////////////////////////////////////////////////////////////
/// --- ADMIN FUNCTIONS
///////////////////////////////////////////////////////////////
/// @notice Allow an address to call the execute function.
/// @param _address Address to allow.
function allowAddress(address _address) external onlyGovernance {
isAllowed[_address] = true;
}
/// @notice Disallow an address to call the execute function.
/// @param _address Address to disallow.
function disallowAddress(address _address) external onlyGovernance {
isAllowed[_address] = false;
}
/// @notice Transfer the governance to a new address.
/// @param _governance Address of the new governance.
function transferGovernance(address _governance) external onlyGovernance {
emit GovernanceProposed(futureGovernance = _governance);
}
/// @notice Accept the governance transfer.
function acceptGovernance() external {
if (msg.sender != futureGovernance) revert GOVERNANCE();
emit GovernanceChanged(governance = msg.sender);
}
/// @notice Withdraw ETH or ERC20 tokens from the contract.
function withdraw(address[] calldata _tokens, uint256[] calldata _amount, address _recipient)
external
onlyGovernance
{
if (_tokens.length != _amount.length) revert WRONG_LENGTH();
for (uint256 i = 0; i < _tokens.length; i++) {
if (_tokens[i] == address(0)) {
SafeTransferLib.safeTransferETH(_recipient, _amount[i]);
} else {
SafeTransferLib.safeTransfer(_tokens[i], _recipient, _amount[i]);
}
}
}
////////////////////////////////////////////////////////////////
/// --- EXECUTE FUNCTION
///////////////////////////////////////////////////////////////
/// @notice Execute a list of commands.
/// @param commands List of commands to execute.
/// @param state State to execute the commands on.
function execute(bytes32[] calldata commands, bytes[] memory state) external onlyAllowed returns (bytes[] memory) {
return _execute(commands, state);
}
receive() external payable {}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;
library CommandBuilder {
uint256 constant IDX_VARIABLE_LENGTH = 0x80;
uint256 constant IDX_VALUE_MASK = 0x7f;
uint256 constant IDX_END_OF_ARGS = 0xff;
uint256 constant IDX_USE_STATE = 0xfe;
function buildInputs(bytes[] memory state, bytes4 selector, bytes32 indices)
internal
view
returns (bytes memory ret)
{
uint256 free; // Pointer to first free byte in tail part of message
uint256 idx;
// Determine the length of the encoded data
for (uint256 i; i < 32;) {
idx = uint8(indices[i]);
if (idx == IDX_END_OF_ARGS) break;
unchecked {
free += 32;
}
unchecked {
++i;
}
}
// Encode it
uint256 bytesWritten;
assembly {
ret := mload(0x40)
bytesWritten := add(bytesWritten, 4)
mstore(0x40, add(ret, and(add(add(bytesWritten, 0x20), 0x1f), not(0x1f))))
mstore(add(ret, 32), selector)
}
uint256 count = 0;
bytes memory stateData; // Optionally encode the current state if the call requires it
for (uint256 i; i < 32;) {
idx = uint8(indices[i]);
if (idx == IDX_END_OF_ARGS) break;
if (idx & IDX_VARIABLE_LENGTH != 0) {
if (idx == IDX_USE_STATE) {
assembly {
bytesWritten := add(bytesWritten, 32)
mstore(0x40, add(ret, and(add(add(bytesWritten, 0x20), 0x1f), not(0x1f))))
mstore(add(add(ret, 36), count), free)
}
if (stateData.length == 0) {
stateData = abi.encode(state);
}
assembly {
bytesWritten := add(bytesWritten, mload(stateData))
mstore(0x40, add(ret, and(add(add(bytesWritten, 0x20), 0x1f), not(0x1f))))
}
memcpy(stateData, 32, ret, free + 4, stateData.length - 32);
free += stateData.length - 32;
} else {
bytes memory stateVar = state[idx & IDX_VALUE_MASK];
uint256 arglen = stateVar.length;
// Variable length data; put a pointer in the slot and write the data at the end
assembly {
bytesWritten := add(bytesWritten, 32)
mstore(0x40, add(ret, and(add(add(bytesWritten, 0x20), 0x1f), not(0x1f))))
mstore(add(add(ret, 36), count), free)
}
assembly {
bytesWritten := add(bytesWritten, arglen)
mstore(0x40, add(ret, and(add(add(bytesWritten, 0x20), 0x1f), not(0x1f))))
}
memcpy(stateVar, 0, ret, free + 4, arglen);
free += arglen;
}
} else {
// Fixed length data; write it directly
bytes memory stateVar = state[idx & IDX_VALUE_MASK];
assembly {
bytesWritten := add(bytesWritten, mload(stateVar))
mstore(0x40, add(ret, and(add(add(bytesWritten, 0x20), 0x1f), not(0x1f))))
mstore(add(add(ret, 36), count), mload(add(stateVar, 32)))
}
}
unchecked {
count += 32;
}
unchecked {
++i;
}
}
assembly {
mstore(ret, bytesWritten)
}
}
function writeOutputs(bytes[] memory state, bytes1 index, bytes memory output)
internal
pure
returns (bytes[] memory)
{
uint256 idx = uint8(index);
if (idx == IDX_END_OF_ARGS) return state;
if (idx & IDX_VARIABLE_LENGTH != 0) {
if (idx == IDX_USE_STATE) {
state = abi.decode(output, (bytes[]));
} else {
// Check the first field is 0x20 (because we have only a single return value)
uint256 argptr;
assembly {
argptr := mload(add(output, 32))
}
require(argptr == 32, "Only one return value permitted (variable)");
assembly {
// Overwrite the first word of the return data with the length - 32
mstore(add(output, 32), sub(mload(output), 32))
// Insert a pointer to the return data, starting at the second word, into state
mstore(add(add(state, 32), mul(and(idx, IDX_VALUE_MASK), 32)), add(output, 32))
}
}
} else {
// Single word
require(output.length == 32, "Only one return value permitted (static)");
state[idx & IDX_VALUE_MASK] = output;
}
return state;
}
function writeTuple(bytes[] memory state, bytes1 index, bytes memory output) internal view {
uint256 idx = uint256(uint8(index));
if (idx == IDX_END_OF_ARGS) return;
bytes memory entry = state[idx & IDX_VALUE_MASK] = new bytes(output.length + 32);
memcpy(output, 0, entry, 32, output.length);
assembly {
let l := mload(output)
mstore(add(entry, 32), l)
}
}
function memcpy(bytes memory src, uint256 srcidx, bytes memory dest, uint256 destidx, uint256 len) internal view {
assembly {
pop(staticcall(gas(), 4, add(add(src, 32), srcidx), len, add(add(dest, 32), destidx), len))
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for gas griefing protection.
/// - For ERC20s, this implementation won't check that a token has code,
/// responsibility is delegated to the caller.
library SafeTransferLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ETH transfer has failed.
error ETHTransferFailed();
/// @dev The ERC20 `transferFrom` has failed.
error TransferFromFailed();
/// @dev The ERC20 `transfer` has failed.
error TransferFailed();
/// @dev The ERC20 `approve` has failed.
error ApproveFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;
/// @dev Suggested gas stipend for contract receiving ETH to perform a few
/// storage reads and writes, but low enough to prevent griefing.
uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ETH OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// If gas griefing protection is needed, please use the force variants.
//
// The regular variants:
// - Forwards all remaining gas to the target.
// - Reverts if the target reverts.
// - Reverts if the current contract has insufficient balance.
//
// The force variants:
// - Forwards with an optional gas stipend
// (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
// - If the target reverts, or if the gas stipend is exhausted,
// creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
// Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
// - Reverts if the current contract has insufficient balance.
//
// The try variants:
// - Forwards with a mandatory gas stipend.
// - Instead of reverting, returns whether the transfer succeeded.
/// @dev Sends `amount` (in wei) ETH to `to`.
function safeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gas(), to, amount, gas(), 0x00, gas(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Sends all the ETH in the current contract to `to`.
function safeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// Transfer all the ETH and check if it succeeded or not.
if iszero(call(gas(), to, selfbalance(), gas(), 0x00, gas(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(gasStipend, to, amount, gas(), 0x00, gas(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) {
returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation.
}
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gasStipend, to, selfbalance(), gas(), 0x00, gas(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) {
returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation.
}
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, gas(), 0x00, gas(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) {
returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation.
}
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), gas(), 0x00, gas(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) {
returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation.
}
}
}
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, amount, gas(), 0x00, gas(), 0x00)
}
}
/// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
function trySafeTransferAllETH(address to, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, selfbalance(), gas(), 0x00, gas(), 0x00)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends all of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have their entire balance approved for
/// the current contract to manage.
function safeTransferAllFrom(address token, address from, address to)
internal
returns (uint256 amount)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransfer(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sends all of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransferAll(address token, address to) internal returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
mstore(0x20, address()) // Store the address of the current contract.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x14, to) // Store the `to` argument.
amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// Reverts upon failure.
function safeApprove(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
/// then retries the approval again (some tokens, e.g. USDT, requires this).
/// Reverts upon failure.
function safeApproveWithRetry(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, retrying upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x34, 0) // Store 0 for the `amount`.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
pop(call(gas(), token, 0, 0x10, 0x44, 0x00, 0x00)) // Reset the approval.
mstore(0x34, amount) // Store back the original `amount`.
// Retry the approval, reverting upon failure.
if iszero(
and(
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Returns the amount of ERC20 `token` owned by `account`.
/// Returns zero if the `token` does not exist.
function balanceOf(address token, address account) internal view returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, account) // Store the `account` argument.
mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
amount :=
mul(
mload(0x20),
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;
import "src/weiroll/CommandBuilder.sol";
abstract contract VM {
using CommandBuilder for bytes[];
uint256 constant FLAG_CT_DELEGATECALL = 0x00;
uint256 constant FLAG_CT_CALL = 0x01;
uint256 constant FLAG_CT_STATICCALL = 0x02;
uint256 constant FLAG_CT_VALUECALL = 0x03;
uint256 constant FLAG_CT_MASK = 0x03;
uint256 constant FLAG_EXTENDED_COMMAND = 0x40;
uint256 constant FLAG_TUPLE_RETURN = 0x80;
uint256 constant SHORT_COMMAND_FILL = 0x000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
address immutable self;
error ExecutionFailed(uint256 command_index, address target, string message);
constructor() {
self = address(this);
}
function _execute(bytes32[] calldata commands, bytes[] memory state) internal returns (bytes[] memory) {
bytes32 command;
uint256 flags;
bytes32 indices;
bool success;
bytes memory outdata;
uint256 commandsLength = commands.length;
for (uint256 i; i < commandsLength;) {
command = commands[i];
flags = uint256(command >> 216) & 0xFF; // more efficient
// flags = uint256(uint8(bytes1(command << 32))); // more readable
if (flags & FLAG_EXTENDED_COMMAND != 0) {
indices = commands[++i];
} else {
indices = bytes32(uint256(command << 40) | SHORT_COMMAND_FILL);
}
if (flags & FLAG_CT_MASK == FLAG_CT_DELEGATECALL) {
(success, outdata) = address(uint160(uint256(command))).delegatecall( // target
// inputs
state.buildInputs(
//selector
bytes4(command),
indices
)
);
} else if (flags & FLAG_CT_MASK == FLAG_CT_CALL) {
(success, outdata) = address(uint160(uint256(command))).call( // target
// inputs
state.buildInputs(
//selector
bytes4(command),
indices
)
);
} else if (flags & FLAG_CT_MASK == FLAG_CT_STATICCALL) {
(success, outdata) = address(uint160(uint256(command))).staticcall( // target
// inputs
state.buildInputs(
//selector
bytes4(command),
indices
)
);
} else if (flags & FLAG_CT_MASK == FLAG_CT_VALUECALL) {
uint256 callEth;
bytes memory v = state[uint8(bytes1(indices))];
require(v.length == 32, "_execute: value call has no value indicated.");
assembly {
callEth := mload(add(v, 0x20))
}
(success, outdata) = address(uint160(uint256(command))).call{ // target
value: callEth
}(
// inputs
state.buildInputs(
//selector
bytes4(command),
bytes32(uint256(indices << 8) | CommandBuilder.IDX_END_OF_ARGS)
)
);
} else {
revert("Invalid calltype");
}
if (!success) {
if (outdata.length > 0) {
assembly {
outdata := add(outdata, 68)
}
}
revert ExecutionFailed({
command_index: 0,
target: address(uint160(uint256(command))),
message: outdata.length > 0 ? string(outdata) : "Unknown"
});
}
if (flags & FLAG_TUPLE_RETURN != 0) {
state.writeTuple(bytes1(command << 88), outdata);
} else {
state = state.writeOutputs(bytes1(command << 88), outdata);
}
unchecked {
++i;
}
}
return state;
}
}
{
"compilationTarget": {
"src/AllMight.sol": "AllMight"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
":AddressBook/=lib/AddressBook/src/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
":solady/=lib/solady/src/",
":solmate/=lib/solmate/src/"
]
}
[{"inputs":[{"internalType":"address","name":"_governance","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"command_index","type":"uint256"},{"internalType":"address","name":"target","type":"address"},{"internalType":"string","name":"message","type":"string"}],"name":"ExecutionFailed","type":"error"},{"inputs":[],"name":"GOVERNANCE","type":"error"},{"inputs":[],"name":"NOT_ALLOWED","type":"error"},{"inputs":[],"name":"WRONG_LENGTH","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newGovernance","type":"address"}],"name":"GovernanceChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newGovernance","type":"address"}],"name":"GovernanceProposed","type":"event"},{"inputs":[],"name":"acceptGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"allowAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"disallowAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"commands","type":"bytes32[]"},{"internalType":"bytes[]","name":"state","type":"bytes[]"}],"name":"execute","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"futureGovernance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_governance","type":"address"}],"name":"transferGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"uint256[]","name":"_amount","type":"uint256[]"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]