Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xc2d0D6C49979703C524Be2D21776C4874C92180e
Balance 0 ETH
Nonce 1
Code Size 8601 bytes
Last Active
Indexed Transactions 6 (24,321,14924,460,781)
Value (indexed) ↓ 0.180000 ETH
Gas Used (indexed) 11,733,245
External Etherscan · Sourcify

Contract Bytecode

8601 bytes
0x60806040526004361061005b575f3560e01c80639861329b116100415780639861329b146100ae578063d7ca9d72146100bc578063f3f85dcb146100ae575f5ffd5b806313990539146100665780633a3b18e01461009b575f5ffd5b3661006257005b5f5ffd5b348015610071575f5ffd5b50610085610080366004610e8c565b6100cf565b6040516100929190610ec4565b60405180910390f35b6100856100a9366004610f44565b610112565b610085610080366004610e8c565b6100856100ca366004610f76565b610189565b6100f16040518060600160405280606081526020015f81526020015f81525090565b6100f961036f565b610102826103e0565b905061010d60015f55565b919050565b6101346040518060600160405280606081526020015f81526020015f81525090565b61013c61036f565b5f5a905061017d61014c846112c5565b610177610160610100870160e088016113d3565b610172610120880161010089016113d3565b610599565b8361066b565b91505061010d60015f55565b6101ab6040518060600160405280606081526020015f81526020015f81525090565b6101b361036f565b5f5a90506101f56101ca60408701602088016113d3565b86604001357f000000000000000000000000726e3244e2a73405ce13d82862d81e1c5dd0004e61099f565b6040517f741308e30000000000000000000000000000000000000000000000000000000081525f9073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000726e3244e2a73405ce13d82862d81e1c5dd0004e169063741308e39061026d90899089908990600401611b64565b5f604051808303815f875af1158015610288573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526102cd9190810190611d6c565b6020808201519085018190528151855290915060c087013510610351576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f494e53554646494349454e545f4f55540000000000000000000000000000000060448201526064015b60405180910390fd5b5a61035c9083611e02565b6040840152505060015f555b9392505050565b60025f54036103da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610348565b60025f55565b6104026040518060600160405280606081526020015f81526020015f81525090565b5f5a6040805161012081019091529091505f90806104208680611e3a565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050509082525060209081019061046c90604088019088016113d3565b73ffffffffffffffffffffffffffffffffffffffff16815260408087013560208301520161049d6060870187611e9b565b808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152505050908252506020016104e06080870187611e9b565b6104e991611eff565b81526020016104fb60a0870187611e9b565b808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050509082525060c0860135602082015260400161054c610100870160e088016113d3565b73ffffffffffffffffffffffffffffffffffffffff1681523360209091015290506105918161058b610585610100880160e089016113d3565b33610599565b8461066b565b949350505050565b5f73ffffffffffffffffffffffffffffffffffffffff83161561064b576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301528416906370a0823190602401602060405180830381865afa158015610620573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106449190611f0b565b9050610665565b5073ffffffffffffffffffffffffffffffffffffffff8116315b92915050565b61068d6040518060600160405280606081526020015f81526020015f81525090565b83604001515f036106fa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f494e56414c49445f494e5055545f414d4f554e540000000000000000000000006044820152606401610348565b8360c001515f03610767576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f494e56414c49445f4f55545055545f414d4f554e5400000000000000000000006044820152606401610348565b61079a846020015185604001517f000000000000000000000000726e3244e2a73405ce13d82862d81e1c5dd0004e61099f565b83516060850151608086015160a08701516040517f662110ef00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000726e3244e2a73405ce13d82862d81e1c5dd0004e169463662110ef9461081f9491939092600401611fb4565b5f604051808303815f875af115801561083a573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261087f9190810190612096565b51815260e08401516101008501515f9161089891610599565b9050838111610903576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f494e56414c49445f4e45575f42414c414e4345000000000000000000000000006044820152606401610348565b5f61090e8583611e02565b90508560c0015181101561097e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f494e53554646494349454e545f4f5554000000000000000000000000000000006044820152606401610348565b602083018190525a6109909085611e02565b60408401525090949350505050565b73ffffffffffffffffffffffffffffffffffffffff8316156109cc576109c783338385610ade565b505050565b81341015610a36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f494e53554646494349454e545f455448000000000000000000000000000000006044820152606401610348565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1663d0e30db0836040518263ffffffff1660e01b81526004015f604051808303818588803b158015610a9c575f5ffd5b505af1158015610aae573d5f5f3e3d5ffd5b50505050506109c77f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28284610bc0565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052610bba9085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152610c16565b50505050565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526109c79084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401610b38565b5f610c77826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16610d219092919063ffffffff16565b8051909150156109c75780806020019051810190610c959190612120565b6109c7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610348565b606061059184845f85855f5f8673ffffffffffffffffffffffffffffffffffffffff168587604051610d53919061213b565b5f6040518083038185875af1925050503d805f8114610d8d576040519150601f19603f3d011682016040523d82523d5f602084013e610d92565b606091505b5091509150610da387838387610dae565b979650505050505050565b60608315610e435782515f03610e3c5773ffffffffffffffffffffffffffffffffffffffff85163b610e3c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610348565b5081610591565b6105918383815115610e585781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103489190612151565b5f60208284031215610e9c575f5ffd5b813567ffffffffffffffff811115610eb2575f5ffd5b82016101008185031215610368575f5ffd5b602080825282516060838301528051608084018190525f929190910190829060a08501905b80831015610f0c5783518252602082019150602084019350600183019250610ee9565b50602086015160408601526040860151606086015280935050505092915050565b5f6101208284031215610f3e575f5ffd5b50919050565b5f60208284031215610f54575f5ffd5b813567ffffffffffffffff811115610f6a575f5ffd5b61059184828501610f2d565b5f5f5f60608486031215610f88575f5ffd5b833567ffffffffffffffff811115610f9e575f5ffd5b610faa86828701610f2d565b935050602084013567ffffffffffffffff811115610fc6575f5ffd5b84016101808187031215610fd8575f5ffd5b929592945050506040919091013590565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051610120810167ffffffffffffffff8111828210171561103a5761103a610fe9565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561108757611087610fe9565b604052919050565b5f82601f83011261109e575f5ffd5b813567ffffffffffffffff8111156110b8576110b8610fe9565b6110e960207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601611040565b8181528460208386010111156110fd575f5ffd5b816020850160208301375f918101602001919091529392505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461113a575f5ffd5b50565b803561010d81611119565b5f67ffffffffffffffff82111561116157611161610fe9565b5060051b60200190565b5f82601f83011261117a575f5ffd5b813561118d61118882611148565b611040565b8082825260208201915060208360051b8601019250858311156111ae575f5ffd5b602085015b838110156111cb5780358352602092830192016111b3565b5095945050505050565b5f6111e261118884611148565b838152905060208101600584901b8301858111156111fe575f5ffd5b835b8181101561123957803567ffffffffffffffff81111561121e575f5ffd5b61122a8882880161108f565b84525060209283019201611200565b5050509392505050565b5f82601f830112611252575f5ffd5b610368838335602085016111d5565b5f82601f830112611270575f5ffd5b813561127e61118882611148565b8082825260208201915060208360051b86010192508583111561129f575f5ffd5b602085015b838110156111cb5780356112b781611119565b8352602092830192016112a4565b5f61012082360312156112d6575f5ffd5b6112de611016565b823567ffffffffffffffff8111156112f4575f5ffd5b6113003682860161108f565b82525061130f6020840161113d565b602082015260408381013590820152606083013567ffffffffffffffff811115611337575f5ffd5b6113433682860161116b565b606083015250608083013567ffffffffffffffff811115611362575f5ffd5b61136e36828601611243565b60808301525060a083013567ffffffffffffffff81111561138d575f5ffd5b61139936828601611261565b60a08301525060c083810135908201526113b560e0840161113d565b60e08201526113c7610100840161113d565b61010082015292915050565b5f602082840312156113e3575f5ffd5b813561036881611119565b5f5f83357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112611421575f5ffd5b830160208101925035905067ffffffffffffffff811115611440575f5ffd5b80360382131561144e575f5ffd5b9250929050565b81835281816020850137505f602082840101525f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b5f5f83357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126114cf575f5ffd5b830160208101925035905067ffffffffffffffff8111156114ee575f5ffd5b8060051b360382131561144e575f5ffd5b8183525f7f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561152f575f5ffd5b8260051b80836020870137939093016020019392505050565b5f8383855260208501945060208460051b820101835f5b868110156115bb577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084840301885261159882876113ee565b6115a3858284611455565b60209a8b019a9095509390930192505060010161155f565b50909695505050505050565b8183526020830192505f815f5b848110156116125781356115e781611119565b73ffffffffffffffffffffffffffffffffffffffff16865260209586019591909101906001016115d4565b5093949350505050565b5f82357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6183360301811261164e575f5ffd5b90910192915050565b8183526020830192505f815f5b8481101561161257813561167781611119565b73ffffffffffffffffffffffffffffffffffffffff1686526020958601959190910190600101611664565b5f6116ad82836113ee565b60a085526116bf60a086018284611455565b9150506116cf60208401846113ee565b85830360208701526116e2838284611455565b925050506116f3604084018461149c565b8583036040870152611706838284611657565b92505050611717606084018461149c565b858303606087015261172a8382846114ff565b608095860135969095019590955250919392505050565b8183526020830192505f815f5b8481101561161257813561176181611119565b73ffffffffffffffffffffffffffffffffffffffff16865260208201356bffffffffffffffffffffffff8116808214611798575f5ffd5b602088015250604095860195919091019060010161174e565b803582525f6020820135368390037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe10181126117eb575f5ffd5b820160208101903567ffffffffffffffff811115611807575f5ffd5b8060061b3603821315611818575f5ffd5b60a0602086015261182d60a086018284611741565b60408581013590870152606080860135908701529150611852905060808401846113ee565b8583036080870152611865838284611455565b9695505050505050565b801515811461113a575f5ffd5b803561010d8161186f565b80356118928161186f565b1515825260208101356118a48161186f565b151560208301526040810135600381108015906118bf575f5ffd5b506040929092019190915250565b5f82357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa183360301811261164e575f5ffd5b5f61190a828361149c565b6060855261191c606086018284611657565b91505061192c602084018461149c565b858303602087015261193f838284611657565b92505050611950604084018461149c565b8583036040870152611865838284611657565b5f82357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4183360301811261164e575f5ffd5b5f813565ffffffffffff81168082146119ac575f5ffd5b845250602082013563ffffffff811681146119c5575f5ffd5b63ffffffff1660208401526040828101359084015260608083013590840152608080830135908401526119fb60a083018361149c565b60c060a0860152611a1060c086018284611657565b95945050505050565b611a4082611a268361113d565b73ffffffffffffffffffffffffffffffffffffffff169052565b5f611a4e602083018361161c565b6101806020850152611a646101808501826116a2565b9050611a73604084018461161c565b8482036040860152611a8582826117b1565b915050611a986060850160608501611887565b611aa560c08401846118cd565b84820360c0860152611ab782826118ff565b915050611ac660e0840161187c565b151560e0850152611ada610100840161113d565b73ffffffffffffffffffffffffffffffffffffffff16610100850152611b03610120840161113d565b73ffffffffffffffffffffffffffffffffffffffff16610120850152611b2d610140840184611963565b848203610140860152611b408282611995565b915050611b51610160840184611963565b848203610160860152611a108282611995565b606081525f611b7385866113ee565b6101206060850152611b8a61018085018284611455565b915050611b996020870161113d565b73ffffffffffffffffffffffffffffffffffffffff166080840152604086013560a0840152611bcb606087018761149c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08584030160c0860152611c008382846114ff565b92505050611c11608087018761149c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08584030160e0860152611c46838284611548565b92505050611c5760a087018761149c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa085840301610100860152611c8d8382846115c7565b60c08901356101208701529250611ca991505060e0870161113d565b73ffffffffffffffffffffffffffffffffffffffff16610140840152611cd2610100870161113d565b73ffffffffffffffffffffffffffffffffffffffff166101608401528281036020840152611d008186611a19565b915050826040830152949350505050565b5f82601f830112611d20575f5ffd5b8151611d2e61118882611148565b8082825260208201915060208360051b860101925085831115611d4f575f5ffd5b602085015b838110156111cb578051835260209283019201611d54565b5f60208284031215611d7c575f5ffd5b815167ffffffffffffffff811115611d92575f5ffd5b820160408185031215611da3575f5ffd5b6040805190810167ffffffffffffffff81118282101715611dc657611dc6610fe9565b604052815167ffffffffffffffff811115611ddf575f5ffd5b611deb86828501611d11565b825250602091820151918101919091529392505050565b81810381811115610665577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f5f83357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112611e6d575f5ffd5b83018035915067ffffffffffffffff821115611e87575f5ffd5b60200191503681900382131561144e575f5ffd5b5f5f83357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112611ece575f5ffd5b83018035915067ffffffffffffffff821115611ee8575f5ffd5b6020019150600581901b360382131561144e575f5ffd5b5f6103683684846111d5565b5f60208284031215611f1b575f5ffd5b5051919050565b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b5f8151808452602084019350602083015f5b8281101561161257815173ffffffffffffffffffffffffffffffffffffffff16865260209586019590910190600101611f80565b608081525f611fc66080830187611f22565b82810360208401528086518083526020830191506020880192505f5b81811015612000578351835260209384019390920191600101611fe2565b50508381036040850152855180825260208083019350600582901b830181019088015f5b83811015612074577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe085840301865261205e838351611f22565b6020968701969093509190910190600101612024565b505085810360608701526120888188611f6e565b9a9950505050505050505050565b5f602082840312156120a6575f5ffd5b815167ffffffffffffffff8111156120bc575f5ffd5b8201602081850312156120cd575f5ffd5b6040516020810167ffffffffffffffff811182821017156120f0576120f0610fe9565b604052815167ffffffffffffffff811115612109575f5ffd5b61211586828501611d11565b825250949350505050565b5f60208284031215612130575f5ffd5b81516103688161186f565b5f82518060208501845e5f920191825250919050565b602081525f6103686020830184611f2256fea26469706673582212209ef1dc5ba68fb69b77d16c0c475d8e599361ab524236e28f9d7eaa0e1f9339bc64736f6c634300081e0033

Verified Source Code Full Match

Compiler: v0.8.30+commit.73712a01 EVM: cancun Optimization: Yes (50000 runs)
IVotes.sol 61 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (governance/utils/IVotes.sol)
pragma solidity ^0.8.0;

/**
 * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.
 *
 * _Available since v4.5._
 */
interface IVotes {
    /**
     * @dev Emitted when an account changes their delegate.
     */
    event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);

    /**
     * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.
     */
    event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);

    /**
     * @dev Returns the current amount of votes that `account` has.
     */
    function getVotes(address account) external view returns (uint256);

    /**
     * @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`).
     */
    function getPastVotes(address account, uint256 blockNumber) external view returns (uint256);

    /**
     * @dev Returns the total supply of votes available at the end of a past block (`blockNumber`).
     *
     * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
     * Votes that have not been delegated are still part of total supply, even though they would not participate in a
     * vote.
     */
    function getPastTotalSupply(uint256 blockNumber) external view returns (uint256);

    /**
     * @dev Returns the delegate that `account` has chosen.
     */
    function delegates(address account) external view returns (address);

    /**
     * @dev Delegates votes from the sender to `delegatee`.
     */
    function delegate(address delegatee) external;

    /**
     * @dev Delegates votes from signer to `delegatee`.
     */
    function delegateBySig(
        address delegatee,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}
ERC2771Context.sol 43 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (metatx/ERC2771Context.sol)

pragma solidity ^0.8.9;

import "../utils/Context.sol";

/**
 * @dev Context variant with ERC2771 support.
 */
abstract contract ERC2771Context is Context {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable _trustedForwarder;

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor(address trustedForwarder) {
        _trustedForwarder = trustedForwarder;
    }

    function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
        return forwarder == _trustedForwarder;
    }

    function _msgSender() internal view virtual override returns (address sender) {
        if (isTrustedForwarder(msg.sender)) {
            // The assembly code is more direct than the Solidity version using `abi.decode`.
            /// @solidity memory-safe-assembly
            assembly {
                sender := shr(96, calldataload(sub(calldatasize(), 20)))
            }
        } else {
            return super._msgSender();
        }
    }

    function _msgData() internal view virtual override returns (bytes calldata) {
        if (isTrustedForwarder(msg.sender)) {
            return msg.data[:msg.data.length - 20];
        } else {
            return super._msgData();
        }
    }
}
ReentrancyGuard.sol 69 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}
draft-IERC20Permit.sol 60 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
IERC20.sol 82 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}
SafeERC20.sol 116 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}
Address.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}
Context.sol 24 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}
Math.sol 345 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}
IFolio_400.sol 292 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IVotes } from "@openzeppelin/contracts/governance/utils/IVotes.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";

interface IFolio_400 is IERC20 {
    // === Events ===

    event AuctionOpened(
        uint256 indexed rebalanceNonce,
        uint256 indexed auctionId,
        address[] tokens,
        WeightRange[] weights,
        PriceRange[] prices,
        RebalanceLimits limits,
        uint256 startTime,
        uint256 endTime
    );
    event AuctionBid(
        uint256 indexed auctionId,
        address indexed sellToken,
        address indexed buyToken,
        uint256 sellAmount,
        uint256 buyAmount
    );
    event AuctionClosed(uint256 indexed auctionId);
    event AuctionTrustedFillCreated(uint256 indexed auctionId, address filler);

    event FolioFeePaid(address indexed recipient, uint256 amount);
    event ProtocolFeePaid(address indexed recipient, uint256 amount);

    event BasketTokenAdded(address indexed token);
    event BasketTokenRemoved(address indexed token);
    event TVLFeeSet(uint256 newFee, uint256 feeAnnually);
    event MintFeeSet(uint256 newFee);
    event FeeRecipientsSet(FeeRecipient[] recipients);
    event AuctionDelaySet(uint256 newAuctionDelay);
    event AuctionLengthSet(uint256 newAuctionLength);
    event DustAmountSet(address token, uint256 newDustAmount);
    event MandateSet(string newMandate);
    event TrustedFillerRegistrySet(address trustedFillerRegistry, bool isEnabled);
    event FolioDeprecated();

    event RebalanceControlSet(RebalanceControl newControl);
    event RebalanceStarted(
        uint256 nonce,
        PriceControl priceControl,
        address[] tokens,
        WeightRange[] weights,
        PriceRange[] prices,
        RebalanceLimits limits,
        uint256 restrictedUntil,
        uint256 availableUntil
    );
    event RebalanceEnded(uint256 nonce);
    // === Errors ===

    error Folio__FolioDeprecated();
    error Folio__Unauthorized();

    error Folio__EmptyAssets();
    error Folio__BasketModificationFailed();
    error Folio__BalanceNotRemovable();

    error Folio__FeeRecipientInvalidAddress();
    error Folio__FeeRecipientInvalidFeeShare();
    error Folio__BadFeeTotal();
    error Folio__TVLFeeTooHigh();
    error Folio__TVLFeeTooLow();
    error Folio__MintFeeTooHigh();
    error Folio__ZeroInitialShares();

    error Folio__InvalidAsset();
    error Folio__DuplicateAsset();
    error Folio__InvalidAssetAmount(address asset);

    error Folio__InvalidAuctionLength();
    error Folio__InvalidLimits();
    error Folio__InvalidWeights();
    error Folio__AuctionCannotBeOpenedWithoutRestriction();
    error Folio__AuctionNotOngoing();
    error Folio__InvalidPrices();
    error Folio__SlippageExceeded();
    error Folio__InsufficientSellAvailable();
    error Folio__InsufficientBuyAvailable();
    error Folio__InsufficientBid();
    error Folio__InsufficientSharesOut();
    error Folio__TooManyFeeRecipients();
    error Folio__InvalidArrayLengths();
    error Folio__InvalidTransferToSelf();

    error Folio__InvalidRegistry();
    error Folio__TrustedFillerRegistryNotEnabled();
    error Folio__TrustedFillerRegistryAlreadySet();
    error Folio__InvalidTTL();
    error Folio__NotRebalancing();
    error Folio__EmptyAuction();
    error Folio__MixedAtomicSwaps();

    // === Structures ===

    /// Price control AUCTION_LAUNCHER has on rebalancing
    enum PriceControl {
        NONE, // cannot change prices
        PARTIAL, // can set auction prices within bounds of initial prices
        ATOMIC_SWAP // PARTIAL + atomic swaps
    }

    struct FolioBasicDetails {
        string name;
        string symbol;
        address[] assets;
        uint256[] amounts; // {tok}
        uint256 initialShares; // {share}
    }

    struct FolioAdditionalDetails {
        uint256 auctionLength; // {s}
        FeeRecipient[] feeRecipients;
        uint256 tvlFee; // D18{1/s}
        uint256 mintFee; // D18{1}
        string mandate;
    }

    struct FolioRegistryIndex {
        address daoFeeRegistry;
        address trustedFillerRegistry;
    }

    struct FolioFlags {
        bool trustedFillerEnabled;
        RebalanceControl rebalanceControl;
    }

    struct FeeRecipient {
        address recipient;
        uint96 portion; // D18{1}
    }

    /// AUCTION_LAUNCHER control over rebalancing
    struct RebalanceControl {
        bool weightControl; // if AUCTION_LAUNCHER can move weights
        PriceControl priceControl; // if AUCTION_LAUNCHER can narrow prices
    }

    /// Basket limits for rebalancing
    struct RebalanceLimits {
        uint256 low; // D18{BU/share} (0, 1e36] to buy assets up to
        uint256 spot; // D18{BU/share} (0, 1e36] point estimate to be used only in the event of unrestricted caller
        uint256 high; // D18{BU/share} (0, 1e36] to sell assets down to
    }

    /// Range of basket weights for BU definition
    struct WeightRange {
        uint256 low; // D27{tok/BU} [0, 1e54] to buy assets up to
        uint256 spot; // D27{tok/BU} [0, 1e54] point estimate to be used in the event of unrestricted caller
        uint256 high; // D27{tok/BU} [0, 1e54] to sell assets down to
    }

    /// Individual token price ranges
    /// @dev Unit of Account (UoA) can be anything as long as it's consistent; USD is most common
    struct PriceRange {
        uint256 low; // D27{UoA/tok} (0, 1e54]
        uint256 high; // D27{UoA/tok} (0, 1e54]
    }

    /// Rebalance details for a token
    struct RebalanceDetails {
        bool inRebalance;
        WeightRange weights; // D27{tok/BU} [0, 1e54]
        PriceRange initialPrices; // D27{UoA/tok} (0, 1e54]
    }

    /// Singleton rebalance state
    // struct Rebalance {
    //     uint256 nonce;
    //     mapping(address token => RebalanceDetails) details;
    //     RebalanceLimits limits; // D18{BU/share} (0, 1e36]
    //     uint256 startedAt; // {s} timestamp rebalancing started, inclusive
    //     uint256 restrictedUntil; // {s} timestamp rebalancing is unrestricted to everyone, exclusive
    //     uint256 availableUntil; // {s} timestamp rebalancing ends overall, exclusive
    //     PriceControl priceControl; // AUCTION_LAUNCHER control over auction pricing
    // }

    /// 1 running auction at a time; N per rebalance overall
    /// Auction states:
    ///   - APPROVED: startTime == 0 && endTime == 0
    ///   - PENDING: block.timestamp < startTime
    ///   - OPEN: block.timestamp >= startTime && block.timestamp <= endTime
    ///   - CLOSED: block.timestamp > endTime
    // struct Auction {
    //     uint256 rebalanceNonce;
    //     mapping(address token => PriceRange) prices; // D27{UoA/tok} (0, 1e54]
    //     uint256 startTime; // {s} inclusive
    //     uint256 endTime; // {s} inclusive
    // }

    /// Used to mark old storage slots now deprecated
    struct DeprecatedStruct {
        bytes32 EMPTY;
    }

    function distributeFees() external;

    function toAssets(uint256 shares, Math.Rounding rounding) external view returns (address[] memory _assets, uint256[] memory _amounts);

    function mint(uint256 shares, address receiver, uint256 minSharesOut) external returns (address[] memory _assets, uint256[] memory _amounts);
    function redeem(
        uint256 shares,
        address receiver,
        address[] calldata assets,
        uint256[] calldata minAmountsOut
    ) external returns (uint256[] memory _amounts);

    function mandate() external returns (string memory);
    function tvlFee() external returns (uint256);
    function mintFee() external returns (uint256);
    function lastPoke() external returns (uint256);
    function isDeprecated() external returns (bool);
    function nextAuctionId() external returns (uint256);

    function version() external returns (string memory);
}


interface IGovernanceDeployer_400 {
    struct GovParams {
        // Basic Parameters
        uint48 votingDelay; // {s}
        uint32 votingPeriod; // {s}
        uint256 proposalThreshold; // D18{1}
        uint256 quorumThreshold; // D18{1}
        uint256 timelockDelay; // {s}
        // Roles
        address[] guardians; // Canceller Role
    }

    struct GovRoles {
        address[] existingBasketManagers;
        address[] auctionLaunchers;
        address[] brandManagers;
    }

    function deployGovernanceWithTimelock(
        IGovernanceDeployer_400.GovParams calldata govParams,
        IVotes stToken,
        bytes32 deploymentNonce
    ) external returns (address governor, address timelock);
}


interface IFolioDeployer_400 {
    error FolioDeployer__LengthMismatch();

    event FolioDeployed(address indexed folioOwner, address indexed folio, address folioAdmin);
    event GovernedFolioDeployed(
        address indexed stToken,
        address indexed folio,
        address ownerGovernor,
        address ownerTimelock,
        address tradingGovernor,
        address tradingTimelock
    );

    function folioImplementation() external view returns (address);

    function deployFolio(
        IFolio_400.FolioBasicDetails calldata basicDetails,
        IFolio_400.FolioAdditionalDetails calldata additionalDetails,
        IFolio_400.FolioFlags calldata folioFlags,
        address owner,
        address[] memory basketManagers,
        address[] memory auctionLaunchers,
        address[] memory brandManagers,
        bytes32 deploymentNonce
    ) external returns (address folio, address proxyAdmin);

    function deployGovernedFolio(
        IVotes stToken,
        IFolio_400.FolioBasicDetails calldata basicDetails,
        IFolio_400.FolioAdditionalDetails calldata additionalDetails,
        IFolio_400.FolioFlags calldata folioFlags,
        IGovernanceDeployer_400.GovParams calldata ownerGovParams,
        IGovernanceDeployer_400.GovParams calldata tradingGovParams,
        IGovernanceDeployer_400.GovRoles calldata govRoles,
        bytes32 deploymentNonce
    ) external returns (address folio, address proxyAdmin);

    function version() external returns (string memory);
}
IFolio.sol 212 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IVotes } from "@openzeppelin/contracts/governance/utils/IVotes.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";

interface IFolio is IERC20 {
    // === Events ===

    event AuctionApproved(uint256 indexed auctionId, address indexed from, address indexed to, Auction auction);
    event AuctionOpened(uint256 indexed auctionId, Auction auction);
    event AuctionBid(uint256 indexed auctionId, uint256 sellAmount, uint256 buyAmount);
    event AuctionClosed(uint256 indexed auctionId);

    event FolioFeePaid(address indexed recipient, uint256 amount);
    event ProtocolFeePaid(address indexed recipient, uint256 amount);

    event BasketTokenAdded(address indexed token);
    event BasketTokenRemoved(address indexed token);
    event TVLFeeSet(uint256 newFee, uint256 feeAnnually);
    event MintFeeSet(uint256 newFee);
    event FeeRecipientSet(address indexed recipient, uint96 portion);
    event AuctionDelaySet(uint256 newAuctionDelay);
    event AuctionLengthSet(uint256 newAuctionLength);
    event MandateSet(string newMandate);
    event FolioKilled();

    // === Errors ===

    error Folio__FolioKilled();
    error Folio__Unauthorized();

    error Folio__EmptyAssets();
    error Folio__BasketModificationFailed();

    error Folio__FeeRecipientInvalidAddress();
    error Folio__FeeRecipientInvalidFeeShare();
    error Folio__BadFeeTotal();
    error Folio__TVLFeeTooHigh();
    error Folio__TVLFeeTooLow();
    error Folio__MintFeeTooHigh();
    error Folio__ZeroInitialShares();

    error Folio__InvalidAsset();
    error Folio__InvalidAssetAmount(address asset);

    error Folio__InvalidAuctionLength();
    error Folio__InvalidSellLimit();
    error Folio__InvalidBuyLimit();
    error Folio__AuctionCannotBeOpened();
    error Folio__AuctionCannotBeOpenedPermissionlesslyYet();
    error Folio__AuctionNotOngoing();
    error Folio__AuctionCollision();
    error Folio__InvalidPrices();
    error Folio__AuctionTimeout();
    error Folio__SlippageExceeded();
    error Folio__InsufficientBalance();
    error Folio__InsufficientBid();
    error Folio__ExcessiveBid();
    error Folio__InvalidAuctionTokens();
    error Folio__InvalidAuctionDelay();
    error Folio__InvalidAuctionTTL();
    error Folio__TooManyFeeRecipients();
    error Folio__InvalidArrayLengths();

    // === Structures ===

    struct FolioBasicDetails {
        string name;
        string symbol;
        address[] assets;
        uint256[] amounts; // {tok}
        uint256 initialShares; // {share}
    }

    struct FolioAdditionalDetails {
        uint256 auctionDelay; // {s}
        uint256 auctionLength; // {s}
        FeeRecipient[] feeRecipients;
        uint256 tvlFee; // D18{1/s}
        uint256 mintFee; // D18{1}
        string mandate;
    }

    struct FeeRecipient {
        address recipient;
        uint96 portion; // D18{1}
    }

    struct BasketRange {
        uint256 spot; // D27{buyTok/share}
        uint256 low; // D27{buyTok/share} inclusive
        uint256 high; // D27{buyTok/share} inclusive
    }

    struct Prices {
        uint256 start; // D27{buyTok/sellTok}
        uint256 end; // D27{buyTok/sellTok}
    }

    /// Auction states:
    ///   - APPROVED: start == 0 && end == 0
    ///   - OPEN: block.timestamp >= start && block.timestamp <= end
    ///   - CLOSED: block.timestamp > end
    struct Auction {
        uint256 id;
        IERC20 sell;
        IERC20 buy;
        BasketRange sellLimit; // D27{sellTok/share} min ratio of sell token in the basket, inclusive
        BasketRange buyLimit; // D27{buyTok/share} max ratio of buy token in the basket, exclusive
        Prices prices; // D27{buyTok/sellTok}
        uint256 availableAt; // {s} inclusive
        uint256 launchTimeout; // {s} inclusive
        uint256 start; // {s} inclusive
        uint256 end; // {s} inclusive
        // === Gas optimization ===
        uint256 k; // D18{1} price = startPrice * e ^ -kt
    }

    function distributeFees() external;

    function folio() external view returns (address[] memory _assets, uint256[] memory _amounts);
    function toAssets(uint256 shares, Math.Rounding rounding) external view returns (address[] memory _assets, uint256[] memory _amounts);
    function AUCTION_APPROVER() external view returns (bytes32);
    function AUCTION_LAUNCHER() external view returns (bytes32);
    function BRAND_MANAGER() external view returns (bytes32);
    function mintFee() external view returns (uint256);

    function mint(uint256 shares, address receiver) external returns (address[] memory _assets, uint256[] memory _amounts);
    function redeem(
        uint256 shares,
        address receiver,
        address[] calldata assets,
        uint256[] calldata minAmountsOut
    ) external returns (uint256[] memory _amounts);

    function version() external returns (string memory);
}


interface IGovernanceDeployer {
    struct GovParams {
        // Basic Parameters
        uint48 votingDelay; // {s}
        uint32 votingPeriod; // {s}
        uint256 proposalThreshold; // D18{1}
        uint256 quorumPercent; // in percent, e.g 4 for 4%
        uint256 timelockDelay; // {s}
        // Roles
        address[] guardians; // Canceller Role
    }

    function deployGovernanceWithTimelock(
        IGovernanceDeployer.GovParams calldata govParams,
        IVotes stToken
    ) external returns (address governor, address timelock);
}

struct GovRoles {
  address[] existingTradeProposers;
  address[] tradeLaunchers;
  address[] vibesOfficers;
}


interface IFolioDeployer {
  error FolioDeployer__LengthMismatch();

  event FolioDeployed(address indexed folioOwner, address indexed folio, address folioAdmin);
  event GovernedFolioDeployed(
      address indexed stToken,
      address indexed folio,
      address ownerGovernor,
      address ownerTimelock,
      address tradingGovernor,
      address tradingTimelock
  );

  function folioImplementation() external view returns (address);

  function deployFolio(
    IFolio.FolioBasicDetails calldata basicDetails,
    IFolio.FolioAdditionalDetails calldata additionalDetails,
    address owner,
    address[] memory auctionApprovers,
    address[] memory auctionLaunchers,
    address[] memory brandManagers,
    bytes32 deploymentNonce
  ) external returns (address folio, address proxyAdmin);

  function deployGovernedFolio(
    IVotes stToken,
    IFolio.FolioBasicDetails calldata basicDetails,
    IFolio.FolioAdditionalDetails calldata additionalDetails,
    IGovernanceDeployer.GovParams calldata ownerGovParams,
    IGovernanceDeployer.GovParams calldata tradingGovParams,
    GovRoles calldata govRoles,
    bytes32 deploymentNonce
  )
    external
    returns (
        address folio,
        address proxyAdmin,
        address ownerGovernor,
        address ownerTimelock,
        address tradingGovernor,
        address tradingTimelock
    );

    function version() external returns (string memory);
}
IRTokenZapper.sol 51 lines
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.30;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

struct Call {
    address to;
    bytes data;
    uint256 value;
}

struct ZapERC20Params {
    bytes program;
    // Token to zap
    IERC20 tokenIn;
    // Total amount to zap / pull from user
    uint256 amountIn;
    
    // Weiroll code to execute to produce 'amountOut' of 'tokenOut'
    bytes32[] commands;
    bytes[] state;
    IERC20[] tokens;

    // RTokens the user requested
    uint256 amountOut;
    // RToken to issue
    IERC20 tokenOut;
}


struct ZapParams {
    // OpCodes to execute to produce 'amountOut' of 'tokenOut'
    bytes program;

    // Token to zap
    address tokenIn;
    // Total amount to zap / pull from user
    uint256 amountIn;
    
    // Weiroll code to execute to produce 'amountOut' of 'tokenOut'
    bytes32[] commands;
    bytes[] state;
    IERC20[] tokens;

    // RTokens the user requested
    uint256 amountOut;
    // RToken to issue
    address tokenOut;

    address recipient;
}
IWrappedNative.sol 9 lines
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.30;

interface IWrappedNative {
    function deposit() external payable;
    function withdraw(uint256 amount) external;
    function balanceOf(address account) external view returns (uint256);
}

PreventTampering.sol 40 lines
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.30;

abstract contract PreventTampering {
    modifier revertOnCodeHashChange() {
        bytes32 hashBefore;
        assembly {
            hashBefore := extcodehash(address())
        }
        _;
        bytes32 hashPostExecution;
        assembly {
            hashPostExecution := extcodehash(address())
        }
        require(hashPostExecution == hashBefore, "PreventTampering: Code has changed");
    }
}


contract SelfDestruct {
    function destroy() external {
        selfdestruct(payable(msg.sender));
    }
    function doNothing() external {}
}

contract TestPreventTampering is PreventTampering {
    function shouldNotRevert() external {
        SelfDestruct selfDestruct = new SelfDestruct();
        address(selfDestruct).delegatecall(abi.encodeWithSelector(selfDestruct.destroy.selector));
    }
    function shouldRevert() revertOnCodeHashChange() external {
        SelfDestruct selfDestruct = new SelfDestruct();
        address(selfDestruct).delegatecall(abi.encodeWithSelector(selfDestruct.destroy.selector));
    }
    function markedRevertOnCodeHashChangeDontRevert() revertOnCodeHashChange() external {
        SelfDestruct selfDestruct = new SelfDestruct();
        address(selfDestruct).delegatecall(abi.encodeWithSelector(selfDestruct.doNothing.selector));
    }
}
Command.sol 457 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;

import "./Constants.sol" as constants;
import "./Indices.sol" as indices;
import {Indices, Index, IndicesImpl} from "./Indices.sol";

type Command is bytes32;

type Flags is bytes32;
// Utility for manipulating the byte 5 of a command

/** Command format:
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
┌───────┬─┬─┬─────────────────────────────────────────┬──┬──┬──┐
│  sel  │f│ │  10 args                                |S │ o│  |
└───────┴─┴─┴─────────────────────────────────────────┴──┴──┴──┘
byte 4 flags => 32, 216
byte 6-26: args => 48, 208
byte 26-27: size of args
byte 28-29: output
byte 30-31: unused

S: The number of words in the fixed part of the arguments

flags 
  0   1   2   3   4   5   6   7
┌───┬───┬───────────────┬────────┐
│tup│ext│   reserved    │calltype│
└───┴───┴───────────────┴────────┘

calltype:
  ┌──────┬───────────────────┐
  │ 0x00 │  DELEGATECALL     │
  ├──────┼───────────────────┤
  │ 0x01 │  CALL             │
  ├──────┼───────────────────┤
  │ 0x02 │  STATICCALL       │
  ├──────┼───────────────────┤
  │ 0x03 │  CALL with value  │
  └──────┴───────────────────┘

args, out and T are all an Index, which is a 2 byte value containing a 15 bit offset, and a 1 bit flag

  0   1   2   3   4   5   6   7   8   9   10   11   12   13   14   15
┌─────────────────────────────────────────────────────────────────┬──┐
│           offset                                                |f |
└─────────────────────────────────────────────────────────────────┴──┘

 */

library CommandImpl {
    uint256 constant FLAG_MASK =
        0x00000000ff000000000000000000000000000000000000000000000000000000;
    uint256 constant INV_FLAG_MASK =
        0xffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffff;
    uint256 constant TEN_FIRST_ARGS_MASK =
        0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff;

    uint256 constant UPDATE_OUTPUT_MASK =
        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffff;
    uint256 constant UPDATE_ARGS_MASK =
        0xffffffffffff0000000000000000000000000000000000000000ffffffffffff;

    using IndicesImpl for Indices;
    /**
     * @notice Checks if the command returns a tuple (multiple values)
     * @param self The Flags to check
     * @return out True if the tuple return flag (bit 0) is set
     */
    function isTupleReturn(Flags self) internal pure returns (bool out) {
        // Check if bit 33 is set
        assembly ("memory-safe") {
            out := eq(
                and(
                    self,
                    0x80000000000000000000000000000000000000000000000000000000
                ),
                0x80000000000000000000000000000000000000000000000000000000
            )
        }
    }

    /**
     * @notice Checks if the command returns a tuple (multiple values)
     * @param self The Command to check
     * @return out True if the tuple return flag (bit 0) is set
     */
    function isTupleReturn(Command self) internal pure returns (bool out) {
        // Check if bit 33 is set
        assembly ("memory-safe") {
            out := eq(
                and(
                    self,
                    0x80000000000000000000000000000000000000000000000000000000
                ),
                0x80000000000000000000000000000000000000000000000000000000
            )
        }
    }
    /**
     * @notice Checks if this is an extended command
     * @dev Extended commands support additional features for future extensibility
     * @param self The Flags to check
     * @return out True if the extended command flag (bit 1) is set
     */
    function isExtendedCommand(Flags self) internal pure returns (bool out) {
        // Check if bit 34 is set
        assembly ("memory-safe") {
            out := eq(
                and(
                    self,
                    0x40000000000000000000000000000000000000000000000000000000
                ),
                0x40000000000000000000000000000000000000000000000000000000
            )
        }
    }
    /**
     * @notice Checks if this is an extended command
     * @dev Extended commands support additional features for future extensibility
     * @param self The Flags to check
     * @return out True if the extended command flag (bit 1) is set
     */
    function isExtendedCommand(Command self) internal pure returns (bool out) {
        // Check if bit 34 is set
        assembly ("memory-safe") {
            out := eq(
                and(
                    self,
                    0x40000000000000000000000000000000000000000000000000000000
                ),
                0x40000000000000000000000000000000000000000000000000000000
            )
        }
    }
    /**
     * @notice Extracts the call type from the flags
     * @param self The Flags to extract from
     * @return out The call type (0=DELEGATECALL, 1=CALL, 2=STATICCALL, 3=VALUECALL)
     */
    function callType(Flags self) internal pure returns (uint256 out) {
        assembly ("memory-safe") {
            out := and(shr(216, self), 3)
        }
    }
    /**
     * @notice Extracts the call type from the flags
     * @param self The Command to extract from
     * @return out The call type (0=DELEGATECALL, 1=CALL, 2=STATICCALL, 3=VALUECALL)
     */
    function callType(Command self) internal pure returns (uint256 out) {
        assembly ("memory-safe") {
            out := and(shr(216, self), 3)
        }
    }
    /**
     * @notice Checks if the command uses DELEGATECALL
     * @param self The Command to check
     * @return out True if call type is DELEGATECALL
     */
    function isDelegateCall(Command self) internal pure returns (bool out) {
        return callType(self) == constants.FLAG_CT_DELEGATECALL;
    }
    /**
     * @notice Checks if the command uses CALL
     * @param self The Command to check
     * @return out True if call type is CALL
     */
    function isCall(Command self) internal pure returns (bool out) {
        return callType(self) == constants.FLAG_CT_CALL;
    }
    /**
     * @notice Checks if the command uses STATICCALL
     * @param self The Command to check
     * @return out True if call type is STATICCALL
     */
    function isStaticCall(Command self) internal pure returns (bool out) {
        return callType(self) == constants.FLAG_CT_STATICCALL;
    }
    /**
     * @notice Checks if the command uses CALL with value transfer
     * @param self The Command to check
     * @return out True if call type is VALUECALL
     */
    function isValueCall(Command self) internal pure returns (bool out) {
        return callType(self) == constants.FLAG_CT_VALUECALL;
    }

    /**
     * @notice Checks if the command uses DELEGATECALL
     * @param self The Flags to check
     * @return out True if call type is DELEGATECALL
     */
    function isDelegateCall(Flags self) internal pure returns (bool out) {
        return callType(self) == constants.FLAG_CT_DELEGATECALL;
    }
    /**
     * @notice Checks if the command uses CALL
     * @param self The Flags to check
     * @return out True if call type is CALL
     */
    function isCall(Flags self) internal pure returns (bool out) {
        return callType(self) == constants.FLAG_CT_CALL;
    }
    /**
     * @notice Checks if the command uses STATICCALL
     * @param self The Flags to check
     * @return out True if call type is STATICCALL
     */
    function isStaticCall(Flags self) internal pure returns (bool out) {
        return callType(self) == constants.FLAG_CT_STATICCALL;
    }
    /**
     * @notice Checks if the command uses CALL with value transfer
     * @param self The Flags to check
     * @return out True if call type is VALUECALL
     */
    function isValueCall(Flags self) internal pure returns (bool out) {
        return callType(self) == constants.FLAG_CT_VALUECALL;
    }
    /**
     * @notice Extracts the flags from a command
     * @dev Returns the command itself as Flags are embedded in the command
     * @param self The Command to extract flags from
     * @return out The Flags embedded in the command
     */
    function flags(Command self) internal pure returns (Flags out) {
        assembly ("memory-safe") {
            out := self
        }
    }

    /**
     * @notice Extracts the function selector from a command
     * @dev Returns the first 4 bytes of the command
     * @param self The Command to extract from
     * @return out The function selector as bytes32 (only first 4 bytes are significant)
     */
    function selector(Command self) internal pure returns (bytes32 out) {
        assembly ("memory-safe") {
            out := and(
                self,
                0xffffffff00000000000000000000000000000000000000000000000000000000
            )
        }
    }

    /**
     * @notice Extracts the argument indices from a command
     * @dev Returns bytes 6-25 containing up to 10 Index values
     * @param self The Command to extract from
     * @return out The Indices collection containing argument references
     */
    function args(Command self) internal pure returns (Indices out) {
        assembly ("memory-safe") {
            out := or(
                shr(48, self),
                0xffffffffffffffffffffffff0000000000000000000000000000000000000000
            )
        }
    }
    /**
     * @dev abi calls are recursively encoded as |head|tail|. Given the arguments (uint256, (uint256, uint256), string) the head will
     * contain 3 elements, first element is 1 word, second element is 2 words, the their element is also 32 bytes, and will store an
     * and offset to the variable length data in the tail.
     */
    function fixedArgsWords(Command self) internal pure returns (uint256 out) {
        assembly ("memory-safe") {
            out := and(shr(32, self), 0xFFFF)
        }
    }
    /**
     * @notice Checks if the command has no return value
     * @param self The Command to check
     * @return out True if output index is 0xFFFF (NO_RETURN)
     */
    function noReturn(Command self) internal pure returns (bool out) {
        assembly ("memory-safe") {
            out := eq(and(self, 0xffff0000), 0xffff0000)
        }
    }
    /**
     * @notice Extracts the output index from a command
     * @dev Returns bytes 28-29 containing the output storage location
     * @param self The Command to extract from
     * @return out The Index where the function return value should be stored
     */
    function output(Command self) internal pure returns (Index out) {
        assembly ("memory-safe") {
            out := and(shr(16, self), 0xffff)
        }
    }

    /**
     * @notice Creates a new Command with specified selector, arguments, and output
     * @param sel The function selector (4 bytes)
     * @param _args The Indices containing argument references
     * @param out The Index where the return value should be stored
     * @return cmd The constructed Command
     */
    function makeCommand(
        uint256 sel,
        Indices _args,
        Index out
    ) internal pure returns (Command cmd) {
        uint256 argsCount = _args.argCount();
        assembly ("memory-safe") {
            // Shift selector to the left
            cmd := or(
                or(
                    or(shl(224, sel), shl(48, and(_args, TEN_FIRST_ARGS_MASK))),
                    shl(32, and(argsCount, 0xFFFF))
                ),
                shl(16, and(out, 0xFFFF))
            )
        }
    }

    /**
     * @notice Creates a new Command with no return value
     * @param sel The function selector (4 bytes)
     * @param _args The Indices containing argument references
     * @return cmd The constructed Command with NO_RETURN output
     */
    function makeCommand(
        uint256 sel,
        Indices _args
    ) internal pure returns (Command cmd) {
        return makeCommand(sel, _args, IndicesImpl.NO_RETURN);
    }

    /**
     * @notice Creates a new Command with no arguments and no return value
     * @param sel The function selector (4 bytes)
     * @return cmd The constructed Command with empty args and NO_RETURN output
     */
    function makeCommand(uint256 sel) internal pure returns (Command cmd) {
        return makeCommand(sel, IndicesImpl.initEmpty(), IndicesImpl.NO_RETURN);
    }

    /**
     * @notice Creates a new Command with no arguments and no return value
     * @param sel The function selector (4 bytes)
     * @return cmd The constructed Command with empty args and NO_RETURN output
     */
    function makeCommand(
        uint256 sel,
        Index output
    ) internal pure returns (Command cmd) {
        return makeCommand(sel, IndicesImpl.initEmpty(), output);
    }

    /**
     * @notice Updates the output index in a command
     * @param self The Command to update
     * @param idx The Index where the return value should be stored
     * @return out The updated Command with new output index
     */
    function withOutput(
        Command self,
        Index idx
    ) internal pure returns (Command out) {
        assembly ("memory-safe") {
            out := or(and(self, UPDATE_OUTPUT_MASK), shl(16, and(idx, 0xFFFF)))
        }
    }
    /**
     * @notice Updates the arguments in a command
     * @param self The Command to update
     * @param _args The new Indices containing argument references
     * @param argsCount The number of arguments (for fixed args word count)
     * @return out The updated Command with new arguments
     */
    function withArgs(
        Command self,
        Indices _args,
        uint256 argsCount
    ) internal pure returns (Command out) {
        assembly ("memory-safe") {
            out := or(
                or(
                    and(self, UPDATE_ARGS_MASK),
                    shl(48, and(_args, TEN_FIRST_ARGS_MASK))
                ),
                shl(32, and(argsCount, 0xFFFF))
            )
        }
    }
    /**
     * @notice Updates the flags in a command
     * @param self The Command to update
     * @param _flags The new Flags to set
     * @return out The updated Command with new flags
     */
    function withFlags(
        Command self,
        Flags _flags
    ) internal pure returns (Command out) {
        assembly ("memory-safe") {
            out := or(and(self, INV_FLAG_MASK), and(_flags, FLAG_MASK))
        }
    }

    /**
     * @notice Creates Flags with specified attributes
     * @param tupleReturn Whether the function returns multiple values
     * @param extendedCommand Whether this is an extended command
     * @param ct The call type (0=DELEGATECALL, 1=CALL, 2=STATICCALL, 3=VALUECALL)
     * @return out The constructed Flags value
     */
    function makeFlags(
        bool tupleReturn,
        bool extendedCommand,
        uint256 ct
    ) internal pure returns (Flags out) {
        assembly ("memory-safe") {
            // Validate callType is within valid range (0-3)
            if gt(ct, 3) {
                revert(0, 0)
            }

            // Set flags to match the bit positions used by the reader functions
            // Tuple return: bit position to match isTupleReturn check
            if tupleReturn {
                // Set bit 8
                out := or(out, 128)
            }

            // Extended command: bit position to match isExtendedCommand check
            if extendedCommand {
                out := or(out, 64)
            }

            out := or(out, ct)

            out := shl(216, out)
        }
    }

    /**
     * @notice Unwraps Flags to its raw bytes32 value
     * @param self The Flags to unwrap
     * @return out The underlying bytes32 value
     */
    function unwrap(Flags self) internal pure returns (bytes32 out) {
        assembly ("memory-safe") {
            out := self
        }
    }
    function unwrap(Command self) internal pure returns (bytes32 out) {
        assembly ("memory-safe") {
            out := self
        }
    }
}
CommandBuilder.sol 243 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.30;
import "./Constants.sol";

import {Index, Indices, IndicesImpl, SHIFT_ARGS_FILL, INDEX_MASK, INDEX_VARIABLE_LENGTH_FLAG, INDEX_NUM_BITS, OFFSET_MASK} from "./Indices.sol";
import {Command, CommandImpl} from "./Command.sol";

library CommandBuilder {
    using IndicesImpl for Index;
    using CommandImpl for Command;
    using IndicesImpl for Indices;

    function buildInputs(
        bytes[] memory state,
        Command command,
        Indices indices
    ) internal view returns (bytes memory argsBuffer) {
        assembly ("memory-safe") {
            state := add(state, 0x20)
            let headPtr := mload(0x40)
            argsBuffer := headPtr
            headPtr := add(headPtr, 0x20)

            mstore(headPtr, command)
            headPtr := add(headPtr, 0x4)

            let tailOffset := mul(and(shr(32, command), INDEX_MASK), 0x20)
            for {

            } 1 {

            } {
                if eq(and(indices, INDEX_MASK), INDEX_MASK) {
                    break
                }
                let slot := mload(
                    add(state, mul(and(indices, OFFSET_MASK), 0x20))
                )
                switch and(INDEX_VARIABLE_LENGTH_FLAG, indices)
                case 0 {
                    mstore(headPtr, mload(add(slot, 0x20)))
                }
                default {
                    // Variable length
                    let argLen := mload(slot)
                    mstore(headPtr, tailOffset)
                    pop(
                        staticcall(
                            gas(),
                            4,
                            add(slot, 0x20),
                            argLen,
                            add(argsBuffer, add(tailOffset, 36)),
                            argLen
                        )
                    )
                    tailOffset := add(tailOffset, argLen)
                }
                headPtr := add(headPtr, 0x20)
                indices := or(shr(INDEX_NUM_BITS, indices), SHIFT_ARGS_FILL)
            }
            mstore(argsBuffer, add(tailOffset, 4))
            mstore(0x40, add(argsBuffer, add(0x20, mload(argsBuffer))))
        }
    }

    // function buildInputs(
    //     bytes[] memory state,
    //     Command command,
    //     Indices indices
    // ) internal view returns (bytes memory) {
    //     unchecked {
    //         uint256 headPtr;
    //         bytes memory argsBuffer;
    //         // We're doing something slightly unsafe here, as we're using the stack-space above the free memory pointer as a buffer
    //         // to construct the calldata. But we're doing it within an isolated function body, so it's likely fine. Just do not add any calls, or forge/hardhat
    //         // within this body as it will trash the buffer we're constructing.

    //         assembly ("memory-safe") {
    //             headPtr := mload(0x40)
    //             argsBuffer := headPtr
    //             headPtr := add(headPtr, 32)
    //         }

    //         // ==== MEMORY UNSAFE CODE STARTS HERE ====
    //         {
    //             // Write the selector
    //             bytes32 sel = command.selector();
    //             assembly ("memory-safe") {
    //                 mstore(headPtr, sel)
    //                 headPtr := add(headPtr, 4)
    //             }
    //         }

    //         uint256 tailOffset = command.fixedArgsWords() * 32;

    //         for (uint256 i; i < 32; i++) {
    //             Index idx = indices.peek();
    //             if (idx.isEndOfArgs()) {
    //                 break;
    //             }

    //             uint256 offset = idx.slotIndex();

    //             if (idx.isVariableLength()) {
    //                 uint256 arglen = state[offset].length;

    //                 // Write the pointer to the variable length data
    //                 assembly ("memory-safe") {
    //                     mstore(headPtr, tailOffset)
    //                     headPtr := add(headPtr, 32)
    //                 }

    //                 // Copy the data to the tail buffer
    //                 memcpy(
    //                     state[offset],
    //                     32,
    //                     argsBuffer,
    //                     tailOffset + 36,
    //                     arglen
    //                 );

    //                 assembly ("memory-safe") {
    //                     tailOffset := add(tailOffset, arglen)
    //                 }
    //             } else {
    //                 assembly ("memory-safe") {
    //                     let stateVariablePtr := mload(
    //                         add(add(state, 32), mul(offset, 32))
    //                     )
    //                     mstore(headPtr, mload(add(stateVariablePtr, 32)))
    //                     headPtr := add(headPtr, 32)
    //                 }
    //             }
    //             indices = indices.shift();
    //         }

    //         assembly ("memory-safe") {
    //             mstore(argsBuffer, add(tailOffset, 4))

    //             mstore(0x40, add(argsBuffer, add(32, mload(argsBuffer))))
    //         }

    //         // ==== MEMORY UNSAFE CODE ENDS HERE ====
    //         return argsBuffer;
    //     }
    // }

    function writeOutputs(
        bytes[] memory state,
        Index outputIndex,
        bytes memory output
    ) internal view {
        assembly ("memory-safe") {
            state := add(add(state, 32), mul(and(outputIndex, 0x7FFF), 0x20))
            switch and(0x8000, outputIndex)
            case 0 {
                mstore(state, output)
            }
            default {
                // 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(state, add(output, 32))
            }
        }
    }

    // function writeOutputs(
    //     bytes[] memory state,
    //     Index outputIndex,
    //     bytes memory output
    // ) internal view {
    //     uint256 offset = outputIndex.slotIndex();
    //     if (outputIndex.isVariableLength()) {
    //         // Check the first field is 0x20 (because we have only a single return value)
    //         uint256 argptr;
    //         assembly ("memory-safe") {
    //             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(offset, 32)), add(output, 32))
    //         }
    //     } else {
    //         require(output.length >= 32, "Return at least 32 bytes");

    //         // There are rare instances of contracts whoes ABI indicate a single word return returning more than 1 word
    //         // returndata buffers containing a single word of data.
    //         if (output.length > 32) {
    //             // Truncate returndata to proper size
    //             bytes memory newOutput = new bytes(32);
    //             memcpy(output, 0, newOutput, 0, output.length);
    //             output = newOutput;
    //         }

    //         state[offset] = output;
    //     }
    // }

    // Wraps the tuple in a buffer
    function writeTuple(
        bytes[] memory state,
        Index outputIndex,
        bytes memory output
    ) internal view {
        unchecked {
            uint256 offset = outputIndex.slotIndex();
            bytes memory entry = state[offset] = new bytes(output.length + 32);
            memcpy(output, 32, entry, 64, output.length);
            assembly ("memory-safe") {
                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 ("memory-safe") {
            pop(
                staticcall(
                    gas(),
                    4,
                    add(src, srcidx),
                    len,
                    add(dest, destidx),
                    len
                )
            )
        }
    }
}
Constants.sol 12 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.30;

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 = 0x80;
uint256 constant FLAG_TUPLE_RETURN = 0x40;
uint256 constant SHORT_COMMAND_FILL = 0x000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
Indices.sol 214 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.30;

import "./Constants.sol" as constants;

uint256 constant INDEX_MASK = 0xFFFF;
uint256 constant INDEX_NUM_BITS = 16;
uint256 constant INDEX_VARIABLE_LENGTH_FLAG = 0x8000;
uint256 constant OFFSET_MASK = 0x7FFF;
uint256 constant SHIFT_ARGS_FILL = 0xFFFF000000000000000000000000000000000000000000000000000000000000;

type Indices is bytes32;
type Index is uint256;
// Structure of indices:
// Each Index is 2 bytes
// First bit is the variable length flag
// last 15 bits is the offset

library IndicesImpl {
    Index constant END_OF_ARGS = Index.wrap(0xFFFF);
    Index constant NO_RETURN = Index.wrap(0xFFFF);

    /**
     * @notice Creates an Index with the specified offset and variable length flag
     * @param offset The position in the state array (must be < 32768)
     * @param variableLen Whether this index points to variable-length data
     * @return out The constructed Index value
     */
    function makeIndex(
        uint256 offset,
        bool variableLen
    ) internal pure returns (Index out) {
        assembly ("memory-safe") {
            out := and(offset, OFFSET_MASK)
            if variableLen {
                out := or(out, INDEX_VARIABLE_LENGTH_FLAG)
            }
        }
    }

    function writeWord(
        Index self,
        bytes[] memory state,
        uint256 value
    ) internal pure returns (bytes memory out) {
        assembly ("memory-safe") {
            let slotPtr := mload(
                add(state, add(mul(and(self, 0x7FFF), 0x20), 0x20))
            )
            mstore(add(slotPtr, 32), value)
        }
    }
    function getSlot(
        Index self,
        bytes[] memory state
    ) internal pure returns (bytes memory out) {
        assembly ("memory-safe") {
            out := mload(
                add(state, add(mul(and(self, OFFSET_MASK), 0x20), 0x20))
            )
        }
    }
    function getSlotWord(
        Index self,
        bytes[] memory state
    ) internal pure returns (uint256 out) {
        assembly ("memory-safe") {
            out := mload(
                add(
                    mload(
                        add(state, add(mul(and(self, OFFSET_MASK), 0x20), 0x20))
                    ),
                    0x20
                )
            )
        }
    }
    /**
     * @notice Counts the number of arguments in an Indices collection
     * @dev Iterates through indices until END_OF_ARGS (0xFFFF) is encountered
     * @param self The Indices to count
     * @return out The number of arguments (excluding the terminator)
     */
    function argCount(Indices self) internal pure returns (uint256 out) {
        while (!isEndOfArgs(peek(self))) {
            out++;
            self = shift(self);
        }
    }
    /**
     * @notice Initializes an empty Indices collection
     * @dev Returns a bytes32 filled with 0xFF bytes (all END_OF_ARGS markers)
     * @return out An empty Indices ready to accept push operations
     */
    function initEmpty() internal pure returns (Indices out) {
        assembly ("memory-safe") {
            out := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
        }
    }
    /**
     * @notice Adds an Index to the Indices collection
     * @dev Shifts existing indices left by 16 bits and adds the new index at the rightmost position
     * @param self The current Indices collection
     * @param index The Index to add
     * @return out The updated Indices with the new index added
     */
    function push(
        Indices self,
        Index index
    ) internal pure returns (Indices out) {
        assembly ("memory-safe") {
            out := or(shl(INDEX_NUM_BITS, self), and(index, INDEX_MASK))
        }
    }
    /**
     * @notice Checks if an Index represents the end of arguments marker
     * @param self The Index to check
     * @return out True if the index equals 0xFFFF (END_OF_ARGS)
     */
    function isEndOfArgs(Index self) internal pure returns (bool out) {
        assembly ("memory-safe") {
            out := eq(self, INDEX_MASK)
        }
    }
    /**
     * @notice Checks if an Index represents variable-length data
     * @dev Checks the least significant bit (bit 0) of the index
     * @param self The Index to check
     * @return out True if the variable length flag is set
     */
    function isVariableLength(Index self) internal pure returns (bool out) {
        assembly ("memory-safe") {
            out := eq(
                and(self, INDEX_VARIABLE_LENGTH_FLAG),
                INDEX_VARIABLE_LENGTH_FLAG
            )
        }
    }
    /**
     * @notice Extracts the state array offset from an Index
     * @dev Masks out the variable length flag to get the 15-bit offset
     * @param self The Index to extract from
     * @return out The offset value (0-32767)
     */
    function slotIndex(Index self) internal pure returns (uint256 out) {
        assembly ("memory-safe") {
            out := and(self, OFFSET_MASK)
        }
    }
    /**
     * @notice Returns the rightmost (most recently pushed) Index without removing it
     * @param self The Indices to peek at
     * @return inner The rightmost Index in the collection
     */
    function peek(Indices self) internal pure returns (Index inner) {
        assembly ("memory-safe") {
            inner := and(self, INDEX_MASK)
        }
    }

    /**
     * @notice Returns the rightmost (most recently pushed) Index without removing it
     * @param self The Indices to peek at
     * @param state The state array
     * @return out The rightmost Index in the collection
     */
    function peekWord(
        Indices self,
        bytes[] memory state
    ) internal pure returns (uint256 out) {
        assembly ("memory-safe") {
            out := mload(
                add(
                    mload(
                        add(state, add(mul(and(self, OFFSET_MASK), 0x20), 0x20))
                    ),
                    0x20
                )
            )
        }
    }
    /**
     * @notice Unwraps an Index to its raw uint256 value
     * @param self The Index to unwrap
     * @return out The underlying uint256 value
     */
    function unwrap(Index self) internal pure returns (uint256 out) {
        assembly ("memory-safe") {
            out := self
        }
    }
    /**
     * @notice Unwraps an Indices to its raw bytes32 value
     * @param self The Indices to unwrap
     * @return out The underlying bytes32 value
     */
    function unwrap(Indices self) internal pure returns (bytes32 out) {
        assembly ("memory-safe") {
            out := self
        }
    }
    /**
     * @notice Removes the rightmost Index and shifts remaining indices right
     * @dev Shifts right by 16 bits and fills left with 0xFF bytes
     * @param self The Indices to shift
     * @return out The shifted Indices with the rightmost element removed
     */
    function shift(Indices self) internal pure returns (Indices out) {
        assembly ("memory-safe") {
            out := or(shr(INDEX_NUM_BITS, self), SHIFT_ARGS_FILL)
        }
    }
}
OpCodes.sol 580 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.30;
import "./Constants.sol" as constants;
import {Index, Indices, IndicesImpl, INDEX_MASK, INDEX_NUM_BITS, INDEX_VARIABLE_LENGTH_FLAG, OFFSET_MASK, SHIFT_ARGS_FILL} from "./Indices.sol";
import {Command, Flags, CommandImpl} from "./Command.sol";
import {CommandBuilder} from "./CommandBuilder.sol";

type OpCode is bytes32;

type OpType is uint256;
type OpBinOp is uint256;
type OpUnOp is uint256;

// |byte cmdIndex|Index target| => executes cmd at index cmdIndex on target
OpType constant OP_CMD = OpType.wrap(0);

// |byte op|Index a|Index b|Index out| => slot[out] = op(slot[a], slot[b])
OpType constant OP_BIN_OP = OpType.wrap(1);

// |byte op|Index a|Index out| => slot[out] = op slot[a]
OpType constant OP_UN_OP = OpType.wrap(2);
OpType constant OP_HALT = OpType.wrap(3);

// |Index slot|1byte (offset in words)|1byte (size in words)|Index out| => slot[out] = slot[offset:offset+size]
OpType constant OP_TUPLE_READ = OpType.wrap(4);

// |Index slot|1byte (offset in words)|Index data| => slot[offset] = data
// (Only supports word sized fields)
OpType constant OP_TUPLE_WRITE = OpType.wrap(5);

// |1byte (size in bytes)|Index out|1..29 bytes data => slot[out] = data
// data is shifted to the right
OpType constant OP_WRITE_CONSTANT = OpType.wrap(6);

OpBinOp constant OP_BIN_OP_ADD = OpBinOp.wrap(0);
OpBinOp constant OP_BIN_OP_SUB = OpBinOp.wrap(1);
OpBinOp constant OP_BIN_OP_MUL = OpBinOp.wrap(2);
OpBinOp constant OP_BIN_OP_DIV = OpBinOp.wrap(3);
OpBinOp constant OP_BIN_OP_EQ = OpBinOp.wrap(4);
OpBinOp constant OP_BIN_OP_NEQ = OpBinOp.wrap(5);
OpBinOp constant OP_BIN_OP_LT = OpBinOp.wrap(6);
OpBinOp constant OP_BIN_OP_LTE = OpBinOp.wrap(7);
OpBinOp constant OP_BIN_OP_GT = OpBinOp.wrap(8);
OpBinOp constant OP_BIN_OP_GTE = OpBinOp.wrap(9);

OpBinOp constant OP_BIN_OP_MUL_X18 = OpBinOp.wrap(10);
OpBinOp constant OP_BIN_OP_DIV_X18 = OpBinOp.wrap(11);

OpUnOp constant OP_UN_OP_NOT = OpUnOp.wrap(0);
OpUnOp constant OP_UN_OP_NEG = OpUnOp.wrap(1);
OpUnOp constant OP_UN_OP_ABS = OpUnOp.wrap(2);
OpUnOp constant OP_UN_OP_INC = OpUnOp.wrap(3);
OpUnOp constant OP_UN_OP_DEC = OpUnOp.wrap(4);

/**
 * OpCodes is the program that is executed by the VM.
 *
 * Each OpCode is stored packed in a byte array, and the OpCodes library provides a way to decode them.
 *
 * The decode functionality will read the next 32 bytes from the program on each call, the bytes are shifted such that the MSB contains the OpCode, and params
 * be stored in the next 31 bytes somewhere.
 */

library OpCodeImpl {
    function unwrap(OpCode op) internal pure returns (uint256 out) {
        assembly ("memory-safe") {
            out := op
        }
    }
    function unwrap(OpType op) internal pure returns (uint256 out) {
        assembly ("memory-safe") {
            out := op
        }
    }

    function consumeByte(OpCode op) internal pure returns (OpCode out) {
        assembly ("memory-safe") {
            out := or(shl(8, op), 0xff)
        }
    }
    function consume2Bytes(OpCode op) internal pure returns (OpCode out) {
        assembly ("memory-safe") {
            out := or(shl(16, op), 0xffff)
        }
    }
    function readOpType(OpCode op) internal pure returns (OpType out) {
        assembly ("memory-safe") {
            out := shr(248, op)
        }
    }
    function readByte(OpCode op) internal pure returns (uint256 out) {
        assembly ("memory-safe") {
            out := shr(248, op)
        }
    }

    function readIndex(OpCode op) internal pure returns (Index out) {
        assembly ("memory-safe") {
            out := and(shr(240, op), 0xffff)
        }
    }

    /**
     * @dev offset and size are converted into byte sizes
     **/
    function unpackOpTupleRead(
        OpCode op
    )
        internal
        pure
        returns (Index slot, uint256 offset, uint256 size, Index out)
    {
        slot = readIndex(op);
        op = consume2Bytes(op);

        offset = readByte(op) * 32;
        op = consumeByte(op);

        size = readByte(op) * 32;
        op = consumeByte(op);

        out = readIndex(op);
    }

    function unpackOpTupleWrite(
        OpCode op
    ) internal pure returns (Index slot, uint256 offset, Index data) {
        slot = readIndex(op);
        op = consume2Bytes(op);

        offset = readByte(op) * 32;
        op = consumeByte(op);

        data = readIndex(op);
    }

    function unpackOpWriteConstant(
        OpCode op
    ) internal pure returns (Index slot, bytes32 data) {
        uint256 size = readByte(op);
        op = consumeByte(op);

        slot = readIndex(op);
        op = consume2Bytes(op);

        assembly ("memory-safe") {
            data := shr(sub(256, mul(size, 8)), op)
        }
    }

    function isHalt(OpType opType) internal pure returns (bool out) {
        assembly ("memory-safe") {
            out := eq(opType, 3)
        }
    }
    function eq(OpType a, OpType b) internal pure returns (bool out) {
        unchecked {
            assembly ("memory-safe") {
                out := eq(a, b)
            }
        }
    }
    function isOpCmd(OpType opType) internal pure returns (bool out) {
        assembly ("memory-safe") {
            out := eq(opType, 0)
        }
    }
    function isOpBinOp(OpType opType) internal pure returns (bool out) {
        assembly ("memory-safe") {
            out := eq(opType, 1)
        }
    }
    function isOpUnOp(OpType opType) internal pure returns (bool out) {
        assembly ("memory-safe") {
            out := eq(opType, 2)
        }
    }
    function isOpTupleRead(OpType opType) internal pure returns (bool out) {
        assembly ("memory-safe") {
            out := eq(opType, 4)
        }
    }
    function isOpTupleWrite(OpType opType) internal pure returns (bool out) {
        assembly ("memory-safe") {
            out := eq(opType, 5)
        }
    }
    function isOpWriteConstant(OpType opType) internal pure returns (bool out) {
        assembly ("memory-safe") {
            out := eq(opType, 6)
        }
    }
}

interface VMOpCodes {
    function cmd(uint256 cmdIndex) external view returns (uint256);
    function add(uint256 a, uint256 b) external view returns (uint256);
    function sub(uint256 a, uint256 b) external view returns (uint256);
    function mul(uint256 a, uint256 b) external view returns (uint256);
    function div(uint256 a, uint256 b) external view returns (uint256);
    function eq(uint256 a, uint256 b) external view returns (uint256);
    function neq(uint256 a, uint256 b) external view returns (uint256);
    function lt(uint256 a, uint256 b) external view returns (uint256);
    function lte(uint256 a, uint256 b) external view returns (uint256);
    function gt(uint256 a, uint256 b) external view returns (uint256);
    function gte(uint256 a, uint256 b) external view returns (uint256);

    function mulx18(uint256 a, uint256 b) external view returns (uint256);
    function divx18(uint256 a, uint256 b) external view returns (uint256);

    function not(uint256 a) external view returns (uint256);
    function neg(uint256 a) external view returns (uint256);
    function abs(uint256 a) external view returns (uint256);
    function inc(uint256 a) external view returns (uint256);
    function dec(uint256 a) external view returns (uint256);

    function tupleRead(
        bytes memory tuple,
        uint256 offset,
        uint256 size
    ) external pure returns (uint256);
    function tupleWrite(
        bytes memory tuple,
        uint256 offset,
        uint256 data
    ) external;

    function writeConstant(
        uint256 size,
        uint256 data
    ) external pure returns (uint256);
}

library BinOpImpl {
    uint256 constant CODE = 1;
    uint256 constant OP_BIN_LENGTH = 8;
    function evalOpBin(OpCode opCodeArgs, bytes[] memory state) internal pure {
        assembly ("memory-safe") {
            state := add(state, 32)
            let a := mload(
                add(
                    mload(
                        add(
                            state,
                            mul(and(shr(232, opCodeArgs), INDEX_MASK), 0x20)
                        )
                    ),
                    0x20
                )
            )
            let b := mload(
                add(
                    mload(
                        add(
                            state,
                            mul(and(shr(216, opCodeArgs), INDEX_MASK), 0x20)
                        )
                    ),
                    0x20
                )
            )
            let out := 0
            switch shr(248, opCodeArgs)
            case 0 {
                out := add(a, b)
            }
            case 1 {
                out := sub(a, b)
            }
            case 2 {
                out := mul(a, b)
            }
            case 3 {
                out := div(a, b)
            }
            case 4 {
                out := eq(a, b)
            }
            case 5 {
                out := iszero(eq(a, b))
            }
            case 6 {
                out := lt(a, b)
            }
            case 7 {
                out := or(eq(a, b), lt(a, b))
            }
            case 8 {
                out := gt(a, b)
            }
            case 9 {
                out := or(eq(a, b), gt(a, b))
            }
            case 10 {
                out := div(mul(a, b), 1000000000000000000)
            }
            case 11 {
                out := div(mul(a, 1000000000000000000), b)
            }
            default {
                revert(0, 0)
            }
            mstore(
                add(
                    mload(
                        add(
                            state,
                            mul(and(shr(200, opCodeArgs), INDEX_MASK), 0x20)
                        )
                    ),
                    0x20
                ),
                out
            )
        }
    }
}

library UnOpImpl {
    uint256 constant CODE = 2;
    uint256 constant OP_UN_LENGTH = 6;
    function evalOpUn(OpCode opCodeArgs, bytes[] memory state) internal pure {
        assembly ("memory-safe") {
            state := add(state, 32)
            let a := mload(
                add(
                    mload(
                        add(
                            state,
                            mul(and(shr(232, opCodeArgs), INDEX_MASK), 0x20)
                        )
                    ),
                    0x20
                )
            )
            let out := 0
            switch shr(248, opCodeArgs)
            case 0 {
                out := not(a)
            }
            case 1 {
                out := sub(0, a)
            }
            case 2 {
                out := a
                if slt(a, 0) {
                    out := sub(0, a)
                }
            }
            case 3 {
                out := add(a, 1)
            }
            case 4 {
                out := sub(a, 1)
            }
            default {
                revert(0, 0)
            }
            mstore(
                add(
                    mload(
                        add(
                            state,
                            mul(and(shr(216, opCodeArgs), INDEX_MASK), 0x20)
                        )
                    ),
                    0x20
                ),
                out
            )
        }
    }
}

library CmdOpImpl {
    uint256 constant CODE = 0;
    uint256 constant OP_CMD_LENGTH = 4;
    using OpCodeImpl for OpCode;
    using CommandImpl for Flags;
    using CommandImpl for Command;
    using CommandImpl for Command;
    using IndicesImpl for Indices;
    using CommandBuilder for bytes[];
    using IndicesImpl for Index;

    error ExecutionFailed(uint256 commandIndex, address target, string message);

    uint256 constant CMD_INDICES_MASK =
        0xffffffffffffffffffffffff0000000000000000000000000000000000000000;
    uint256 constant EXT_FLAG =
        0x40000000000000000000000000000000000000000000000000000000;
    function evalOpCmd(
        OpCode opCodeArgs,
        uint256 instructionIndex,
        bytes32[] calldata commands,
        bytes[] memory state
    ) internal {
        address target;
        Command command;
        Indices indices;
        assembly ("memory-safe") {
            command := calldataload(
                add(commands.offset, mul(shr(248, opCodeArgs), 0x20))
            )
            target := mload(
                add(
                    mload(
                        add(
                            state,
                            add(
                                mul(
                                    and(shr(232, opCodeArgs), OFFSET_MASK),
                                    0x20
                                ),
                                0x20
                            )
                        )
                    ),
                    0x20
                )
            )
            indices := or(shr(48, command), CMD_INDICES_MASK)
            if and(command, EXT_FLAG) {
                indices := calldataload(
                    add(
                        commands.offset,
                        add(mul(shr(248, opCodeArgs), 0x20), 0x20)
                    )
                )
            }
        }

        bool success;
        bytes memory outdata;
        uint256 callType = command.callType();
        if (callType == constants.FLAG_CT_CALL) {
            (success, outdata) = target.call(
                state.buildInputs(command, indices)
            );
        } else if (callType == constants.FLAG_CT_DELEGATECALL) {
            (success, outdata) = target.delegatecall(
                state.buildInputs(command, indices)
            );
        } else if (callType == constants.FLAG_CT_STATICCALL) {
            (success, outdata) = target.staticcall(
                state.buildInputs(command, indices)
            );
        } else if (callType == constants.FLAG_CT_VALUECALL) {
            (success, outdata) = target.call{value: indices.peekWord(state)}(
                state.buildInputs(command, indices.shift())
            );
        } else {
            revert("Invalid calltype");
        }
        if (success) {
            if (command.noReturn()) {
                return;
            }
            if (command.isTupleReturn()) {
                state.writeTuple(command.output(), outdata);
            } else {
                state.writeOutputs(command.output(), outdata);
            }
        } else {
            assembly {
                if gt(mload(outdata), 0) {
                    outdata := add(outdata, 68)
                }
            }
            revert ExecutionFailed({
                commandIndex: instructionIndex,
                target: target,
                message: string(outdata)
            });
        }
    }
}

library TupleReadImpl {
    uint256 constant CODE = 4;
    uint256 constant OP_TUPLE_READ_LENGTH = 7;
    using OpCodeImpl for OpCode;
    using IndicesImpl for Indices;
    using IndicesImpl for Index;
    using CommandBuilder for bytes[];
    error TupleReadOutOfBounds(
        Index slot,
        uint256 offset,
        uint256 size,
        uint256 tupleLength
    );

    function evalOpTupleRead(
        OpCode opCodeArgs,
        bytes[] memory state
    ) internal view {
        (Index slot, uint256 offset, uint256 size, Index out) = opCodeArgs
            .unpackOpTupleRead();
        bytes memory tuple = slot.getSlot(state);
        if (tuple.length < offset + size) {
            revert TupleReadOutOfBounds(slot, offset, size, tuple.length);
        }

        if (size == 32) {
            uint256 value;
            assembly ("memory-safe") {
                value := mload(add(tuple, offset))
            }
            out.writeWord(state, value);
        } else {
            bytes memory outData = new bytes(size);

            assembly ("memory-safe") {
                let buff := add(outData, 32)
                tuple := add(tuple, 32)
                for {
                    let i := offset
                } lt(i, add(offset, size)) {
                    i := add(i, 32)
                } {
                    mstore(buff, mload(add(tuple, i)))
                    buff := add(buff, 32)
                }
            }
            state.writeTuple(out, outData);
        }
    }
}

library TupleWriteImpl {
    uint256 constant CODE = 5;
    uint256 constant OP_TUPLE_WRITE_LENGTH = 6;
    using OpCodeImpl for OpCode;
    using IndicesImpl for Indices;
    using IndicesImpl for Index;
    using CommandBuilder for bytes[];
    error TupleWriteOutOfBounds(
        Index slot,
        uint256 offset,
        uint256 size,
        uint256 tupleLength
    );

    function evalOpTupleWrite(
        OpCode opCodeArgs,
        bytes[] memory state
    ) internal pure {
        (Index slot, uint256 offset, Index dataIdx) = opCodeArgs
            .unpackOpTupleWrite();

        uint256 dataVal = dataIdx.getSlotWord(state);
        bytes memory tupleData = slot.getSlot(state);
        if (tupleData.length < offset + 32) {
            revert TupleWriteOutOfBounds(slot, offset, 32, tupleData.length);
        }
        assembly ("memory-safe") {
            mstore(add(tupleData, offset), dataVal)
        }
    }
}

library WriteConstantImpl {
    uint256 constant CODE = 6;
    using OpCodeImpl for OpCode;
    using IndicesImpl for Indices;
    using IndicesImpl for Index;
    using CommandBuilder for bytes[];

    function evalOpWriteConstant(
        OpCode opCodeArgs,
        bytes[] memory state
    ) internal pure returns (uint256 out) {
        assembly ("memory-safe") {
            out := add(4, shr(248, opCodeArgs))
        }
        (Index slot, bytes32 data) = opCodeArgs.unpackOpWriteConstant();
        slot.writeWord(state, uint256(data));
    }
}
VM.sol 74 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.30;
import "./CommandBuilder.sol";
import "./Constants.sol";
import {Index, Indices, IndicesImpl} from "./Indices.sol";
import {Command, Flags, CommandImpl} from "./Command.sol";
import {UnOpImpl, CmdOpImpl, OpType, BinOpImpl, OpCodeImpl, OpCode, WriteConstantImpl, TupleReadImpl, TupleWriteImpl} from "./OpCodes.sol";
import "./OpCodes.sol" as ops;

abstract contract VM {
    using BinOpImpl for OpCode;
    using UnOpImpl for OpCode;
    using OpCodeImpl for OpCode;
    using OpCodeImpl for OpType;
    using CommandImpl for Command;
    using CommandImpl for Flags;
    using IndicesImpl for Indices;
    using IndicesImpl for Index;
    using CmdOpImpl for OpCode;
    using CommandBuilder for bytes[];
    using WriteConstantImpl for OpCode;
    using TupleReadImpl for OpCode;
    using TupleWriteImpl for OpCode;

    error InvalidOpCode(OpType opCode);

    function _execute(
        bytes calldata program,
        bytes32[] calldata commands,
        bytes[] memory state
    ) internal returns (bytes[] memory) {
        unchecked {
            uint256 ip = 0;
            while (true) {
                OpCode opCodeArgs;
                OpType opCode;
                assembly ("memory-safe") {
                    opCodeArgs := calldataload(add(program.offset, ip))
                    opCode := shr(248, opCodeArgs)
                    opCodeArgs := shl(8, opCodeArgs)
                }

                if (opCode.isOpCmd()) {
                    opCodeArgs.evalOpCmd(ip, commands, state);
                    ip += CmdOpImpl.OP_CMD_LENGTH;
                } else if (opCode.isOpBinOp()) {
                    opCodeArgs.evalOpBin(state);
                    ip += BinOpImpl.OP_BIN_LENGTH;
                } else if (opCode.isOpUnOp()) {
                    opCodeArgs.evalOpUn(state);
                    ip += UnOpImpl.OP_UN_LENGTH;
                } else if (opCode.isOpTupleRead()) {
                    opCodeArgs.evalOpTupleRead(state);
                    ip += TupleReadImpl.OP_TUPLE_READ_LENGTH;
                } else if (opCode.isOpTupleWrite()) {
                    opCodeArgs.evalOpTupleWrite(state);
                    ip += TupleWriteImpl.OP_TUPLE_WRITE_LENGTH;
                } else if (opCode.isOpWriteConstant()) {
                    ip += opCodeArgs.evalOpWriteConstant(state);
                } else if (opCode.isHalt()) {
                    break;
                } else {
                    revert InvalidOpCode(opCode);
                }
                if (ip >= program.length) {
                    break;
                }
            }

            return state;
        }
    }
}
Zapper2.sol 178 lines
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.30;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";

import {IWrappedNative} from "./IWrappedNative.sol";
import {VM} from "./weiroll/VM.sol";
import {PreventTampering} from "./PreventTampering.sol";

import {ZapParams, ZapERC20Params} from "./IRTokenZapper.sol";
import {ZapperExecutor, DeployFolioConfig, ExecuteDeployOutput} from "./ZapperExecutor.sol";

struct ZapperOutput {
    uint256[] dust;
    uint256 amountOut;
    uint256 gasUsed;
}

contract Zapper2 is ReentrancyGuard {
    IWrappedNative internal immutable wrappedNative;
    ZapperExecutor internal immutable zapperExecutor;

    constructor(IWrappedNative wrappedNative_, ZapperExecutor executor_) {
        wrappedNative = wrappedNative_;
        zapperExecutor = executor_;
    }

    receive() external payable {}

    function zap(
        ZapParams calldata params
    ) external payable nonReentrant returns (ZapperOutput memory) {
        uint256 startGas = gasleft();
        return
            zapInner(
                params,
                balanceOf(params.tokenOut, params.recipient),
                startGas
            );
    }

    function zapDeploy(
        ZapParams calldata params,
        DeployFolioConfig calldata config,
        bytes32 nonce
    ) external payable nonReentrant returns (ZapperOutput memory out) {
        uint256 startGas = gasleft();
        pullFundsFromSender(
            params.tokenIn,
            params.amountIn,
            address(zapperExecutor)
        );
        // STEP 1: Execute
        ExecuteDeployOutput memory deployOutput = zapperExecutor.executeDeploy(
            params,
            config,
            nonce
        );
        out.amountOut = deployOutput.amountOut;
        out.dust = deployOutput.dust;

        require(out.amountOut > params.amountOut, "INSUFFICIENT_OUT");

        out.gasUsed = startGas - gasleft();
    }

    function validateTokenOut(address tokenOut) private {
        uint256 codeSizeTokenOut = 0;
        assembly {
            codeSizeTokenOut := extcodesize(tokenOut)
        }
        require(codeSizeTokenOut == 0, "RETRY");
    }

    function zapInner(
        ZapParams memory params,
        uint256 initialBalance,
        uint256 startGas
    ) private returns (ZapperOutput memory out) {
        require(params.amountIn != 0, "INVALID_INPUT_AMOUNT");
        require(params.amountOut != 0, "INVALID_OUTPUT_AMOUNT");

        pullFundsFromSender(
            params.tokenIn,
            params.amountIn,
            address(zapperExecutor)
        );
        // STEP 1: Execute
        out.dust = zapperExecutor
            .execute(
                params.program,
                params.commands,
                params.state,
                params.tokens
            )
            .dust;

        // STEP 2: Verify that the user has gotten the tokens they requested
        uint256 newBalance = balanceOf(params.tokenOut, params.recipient);
        require(newBalance > initialBalance, "INVALID_NEW_BALANCE");
        uint256 difference = newBalance - initialBalance;
        require(difference >= params.amountOut, "INSUFFICIENT_OUT");

        out.amountOut = difference;
        out.gasUsed = startGas - gasleft();
    }

    function pullFundsFromSender(
        address token,
        uint256 amount,
        address to
    ) private {
        if (token != address(0)) {
            SafeERC20.safeTransferFrom(IERC20(token), msg.sender, to, amount);
        } else {
            require(msg.value >= amount, "INSUFFICIENT_ETH");
            wrappedNative.deposit{value: amount}();
            SafeERC20.safeTransfer(IERC20(address(wrappedNative)), to, amount);
        }
    }

    function balanceOf(
        address token,
        address account
    ) private view returns (uint256) {
        if (token != address(0)) {
            // Check if token address contains bytecode
            return IERC20(token).balanceOf(account);
        } else {
            return account.balance;
        }
    }

    /** Stubs for old interface  */
    function translateOldStyleZap(
        ZapERC20Params calldata params
    ) private returns (ZapperOutput memory) {
        uint256 startGas = gasleft();
        ZapParams memory zapParams = ZapParams({
            program: params.program,
            tokenIn: address(params.tokenIn),
            amountIn: params.amountIn,
            commands: params.commands,
            state: params.state,
            tokens: params.tokens,
            amountOut: params.amountOut,
            tokenOut: address(params.tokenOut),
            recipient: msg.sender
        });

        return
            zapInner(
                zapParams,
                balanceOf(address(params.tokenOut), msg.sender),
                startGas
            );
    }

    function zapERC20(
        ZapERC20Params calldata params
    ) external nonReentrant returns (ZapperOutput memory) {
        return translateOldStyleZap(params);
    }
    function zapETH(
        ZapERC20Params calldata params
    ) external payable nonReentrant returns (ZapperOutput memory) {
        return translateOldStyleZap(params);
    }
    function zapToETH(
        ZapERC20Params calldata params
    ) external payable nonReentrant returns (ZapperOutput memory) {
        return translateOldStyleZap(params);
    }
}
ZapperExecutor.sol 153 lines
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.30;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {VM} from "./weiroll/VM.sol";
import {PreventTampering} from "./PreventTampering.sol";

import {IVotes} from "./IFolio.sol";
import {IFolio_400, IFolioDeployer_400, IGovernanceDeployer_400} from "./IFolio_400.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ZapParams} from "./IRTokenZapper.sol";

struct DeployFolioConfig {
    address deployer;
    IFolio_400.FolioBasicDetails basicDetails;
    IFolio_400.FolioAdditionalDetails additionalDetails;
    IFolio_400.FolioFlags folioFlags;
    IGovernanceDeployer_400.GovRoles govRoles;
    bool isGoverned;
    IVotes stToken;
    address owner;
    IGovernanceDeployer_400.GovParams ownerGovParams;
    IGovernanceDeployer_400.GovParams tradingGovParams;
}

struct ExecuteOutput {
    uint256[] dust;
}
struct ExecuteDeployOutput {
    uint256[] dust;
    uint256 amountOut;
}
contract ZapperExecutor is VM, PreventTampering {
    receive() external payable {}

    /** @dev Main endpoint to call
     * @param commands - Weiroll code to execute
     * @param state - Intiaial Weiroll state to use
     * @param tokens - All tokens used by the Zap in order to calculate dust
     */
    function execute(
        bytes calldata program,
        bytes32[] calldata commands,
        bytes[] memory state,
        IERC20[] memory tokens
    ) public payable revertOnCodeHashChange returns (ExecuteOutput memory out) {
        _execute(program, commands, state);
        out.dust = new uint256[](tokens.length);
        for (uint256 i; i < tokens.length; i++) {
            out.dust[i] = tokens[i].balanceOf(address(this));
        }
    }

    function _prepareFolio(
        address deployer,
        address[] memory assets,
        uint256[] memory amounts
    ) internal returns (uint256 initialShares) {
        initialShares = type(uint256).max;
        for (uint256 i = 0; i < assets.length; i++) {
            uint256 balance = IERC20(assets[i]).balanceOf(address(this));
            if (balance == 0) {
                revert("ZERO BALANCE");
            }
            uint256 quantityPrShare = amounts[i];
            if (quantityPrShare == 0) {
                revert("ZERO QUANTITY");
            }
            uint256 shares = (balance * 1e18) / quantityPrShare;

            if (shares < initialShares) {
                initialShares = shares;
            }
            SafeERC20.safeApprove(IERC20(assets[i]), deployer, 0);
            SafeERC20.safeApprove(
                IERC20(assets[i]),
                deployer,
                type(uint256).max
            );
        }
        if (initialShares == type(uint256).max) {
            revert("NO SHARES");
        }
    }

    function _transferDust(
        IERC20[] memory tokens,
        address recipient
    ) internal returns (uint256[] memory dust) {
        dust = new uint256[](tokens.length);
        for (uint256 i; i < tokens.length; i++) {
            dust[i] = tokens[i].balanceOf(address(this));
            SafeERC20.safeTransfer(tokens[i], recipient, dust[i]);
        }
    }

    function executeDeploy(
        ZapParams calldata params,
        DeployFolioConfig memory config,
        bytes32 nonce
    )
        public
        payable
        revertOnCodeHashChange
        returns (ExecuteDeployOutput memory out)
    {
        _execute(params.program, params.commands, params.state);
        // STEP 2: Deploy folio
        uint256 initialShares = _prepareFolio(
            config.deployer,
            config.basicDetails.assets,
            config.basicDetails.amounts
        );

        for (uint256 i = 0; i < config.basicDetails.assets.length; i++) {
            config.basicDetails.amounts[i] =
                (initialShares * config.basicDetails.amounts[i]) /
                1e18;
        }

        config.basicDetails.initialShares = initialShares;

        address folio;
        if (config.isGoverned) {
            (folio, ) = IFolioDeployer_400(config.deployer).deployGovernedFolio(
                config.stToken,
                config.basicDetails,
                config.additionalDetails,
                config.folioFlags,
                config.ownerGovParams,
                config.tradingGovParams,
                config.govRoles,
                nonce
            );
        } else {
            (folio, ) = IFolioDeployer_400(config.deployer).deployFolio(
                config.basicDetails,
                config.additionalDetails,
                config.folioFlags,
                config.owner,
                config.govRoles.existingBasketManagers,
                config.govRoles.auctionLaunchers,
                config.govRoles.brandManagers,
                nonce
            );
        }

        out.amountOut = IERC20(folio).balanceOf(address(this));
        SafeERC20.safeTransfer(IERC20(folio), params.recipient, out.amountOut);

        out.dust = _transferDust(params.tokens, params.recipient);
    }
}

Write Contract 5 functions

These functions modify contract state and require a wallet transaction to execute.

zap 0x0d69de01
tuple params
returns: tuple
zapDeploy 0x7b928330
tuple params
tuple config
bytes32 nonce
returns: tuple
zapERC20 0x52abf338
tuple params
returns: tuple
zapETH 0xda673b16
tuple params
returns: tuple
zapToETH 0xbe355c46
tuple params
returns: tuple

Top Interactions

Recent Transactions

CSV
|
Hash Method Block Age From/To Value Txn Fee Type
0x252c591d...74cbda 0x3a3b18e0 24,460,781 IN 0x3A6fb4fA...eaDE 0 ETH 0.000071090677 ETH EIP-1559
0xb06412fc...37b916 0x3a3b18e0 24,460,512 IN 0x9eCE7936...3d04 0 ETH 0.00147425 ETH EIP-1559
0xf045466a...b1a4d0 0x3a3b18e0 24,460,082 IN 0x3A6fb4fA...eaDE 0 ETH 0.000079216587 ETH EIP-1559
0x244c3a33...c91a87 0x3a3b18e0 24,322,925 IN 0x304F3500...b5ac 0 ETH 0.000114473357 ETH EIP-1559
0x3c5c4852...378b3c 0x3a3b18e0 24,321,904 IN 0xA3b43a51...af79 0.180000 ETH 0.00373511 ETH EIP-1559
0xd42f2ffb...8cf72d 0x3a3b18e0 24,321,149 IN 0x03d03A02...da99 0 ETH 0.000234677005 ETH EIP-1559