flashloan-rs 0.2.1

Minimal Multicall3 Flashloan Module
Documentation
/// @title ERC1967 Upgrade
/// @notice SPDX-License-Identifier: MIT
/// @author asnared <https://github.com/abigger87>
/// @author Maddiaa <https://github.com/cheethas>
/// @dev This abstract contract provides getters and event emitting update functions for https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
/// @notice Adapted from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/ERC1967/ERC1967Upgrade.sol)

#include "../utils/CommonErrors.huff"

// Function Definitions

#define function implementation() view returns (address)

/// @notice Foreign implementation function
#define function proxiableUUID() nonpayable returns (bytes32)

// EIP 1967 Constants

/// @notice This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
#define constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143

/// @notice Storage slot with the address of the current implementation
/// @notice This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1
/// @notice Validated in the constructor
#define constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc

/// @notice Storage slot with the admin of the contract
/// @notice This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1
/// @notice Validated in the constructor
#define constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103

/// @notice The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy
/// @notice This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1))
/// @notice Validated in the constructor
#define constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50

// Events

/// @notice Emitted when the implementation is upgraded.
#define event Upgraded(address indexed implementation)

/// @notice Emitted when the admin account has changed
#define event AdminChanged(address previousAdmin, address newAdmin)

/// @notice Emitted when the beacon is upgraded
#define event BeaconUpgraded(address indexed beacon)


// Implementation Macros

/// @notice Returns the current implementation
#define macro GET_IMPLEMENTATION() = takes (0) returns (1) {
    [_IMPLEMENTATION_SLOT] sload    // [address]
}

/// @notice Sets the implementation address of the proxy
#define macro SET_IMPLEMENTATION() = takes (1) returns (0) {
    // Input Stack: [address]

    // Check that the address is a contract
    dup1 extcodesize iszero iszero              // [isContract(address), address]
    __Is_Contract_JUMP jumpi                    // [address]
        NON_CONTRACT(0x00)

    // Set the implementation slot
    __Is_Contract_JUMP:
    [_IMPLEMENTATION_SLOT] sstore               // []
}

/// @notice Perform implementation upgrade
#define macro UPGRADE_TO() = takes (1) returns (0) {
    // Input Stack: [address]

    // Set the implementation
    dup1 SET_IMPLEMENTATION()                   // [address]

    // Emit the Upgraded event
    __EVENT_HASH(Upgraded) 0x00 0x00 log2       // []
}


/// @notice Perform implementation upgrade with additional setup call.
#define macro UPGRADE_TO_AND_CALL() = takes (3) returns (0) {
    // Input Stack: [address, data, forceCall]

    // Set the implementation
    dup1 UPGRADE_TO()                           // [address, data, forceCall]

    // Copy the data from calldata to memory
    swap2 not                                   // [!forceCall, data, address]

    dup2 calldataload dup1                      // [dataSize, dataSize, forceCall, data, address]
    swap2 swap1 iszero and                      // [(!forceCall && dataSize == 0), dataSize, data, address]
    __IgnoreCall_And_Clean_Stack jumpi

    dup1 swap2 0x20 add                         // [&data[0], dataSize, dataSize, address]
    0x00                                        // [destOffset, calldataOffset, dataSize, dataSize, address]
    calldatacopy                                // [dataSize, address]

    // Call the implementation with the given data
    0x00                                        // [retOffset, dataSize, address]
    swap2                                       // [argSize, retOffset, address]
    0x00                                        // [argOffset, argSize, retOffset, address]
    0x00                                        // [retSize, argOffset, argSize, retOffset, address]
    swap4                                       // [address, argOffset, argSize, retOffset, retSize]
    gas                                         // [gas, address, argOffset, argSize, retOffset, retSize]
    delegatecall                                // [success]
    iszero iszero __DelegateCall_Success jumpi  // []
    0x00 dup1 revert

    __IgnoreCall_And_Clean_Stack:
    pop pop pop                                 // []

    __DelegateCall_Success:
}


/// @notice Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
#define macro UPGRADE_TO_AND_CALL_UUPS() = takes (3) returns (0) {
    // Upgrades from old implementations will perform a rollback test. This test requires the new
    // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
    // this special case will break upgrade paths from old UUPS implementation to new ones.

    // Input Stack: [newImplementation, data, forceCall]

    // Check if there is a value in the rollback slot, if so continue
    // if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
    [_ROLLBACK_SLOT] sload              // [rollbackslot.value]
    iszero else jumpi                   // [!rollbackslot.value]

        SET_IMPLEMENTATION()
        continue jump
    else:
        // Branch Input Stack : [newImplementation, data, forceCall] 
        // call the new implementation

        // store function selector in mem
        0x00                            // [0, newImplementation, data, forceCall]
        __FUNC_SIG(proxiableUUID)       // [funcSig(proxiableUUID), 0, newImplementation, data, forceCall]
        dup2                            // [0, funcSig(proxiableUUID), 0, newImplementation, data, forceCall]
        mstore                          // [0, newImplementation, data, forceCall]

        // Call
        0x20                            // [retSize, 0, newImplementation, data, forceCall]
        dup2                            // [retOffset, retSize, 0, newImplementation, data, forceCall]
        0x04                            // [argSize, retOffset, retSize, 0, newImplementation, data, forceCall]
        0x1c                            // [argOffset, argSize, retOffset ,retSize, 0, newImplementation, data, forceCall]
        dup5                            // [value(0), argOffset, argSize, retOffset ,retSize, 0, newImplementation, data, forceCall]
        dup7                            // [address, value(0), argOffset, argSize, retOffset ,retSize, 0, newImplementation, data, forceCall]
        gas                             // [gas, address, value(0), argOffset, argSize, retOffset ,retSize, 0, newImplementation, data, forceCall]
        call                            // [success, 0, newImplementation, data, forceCall]

        // check call success
        iszero not_supported_check_revert jumpi 
            // Try success block 

            // The return implementation slot is now stored between 00:32 in the current call frame's memory
            mload                           // [slot, newImplementation, data, forceCall]
            [_IMPLEMENTATION_SLOT] eq        // [(slot == IMPLEMENTATION_SLOT), newImplementation, data, forceCall]
            iszero not_uups_revert jumpi // [newImplementation, data, forceCall]

            UPGRADE_TO_AND_CALL()   

            // Continue past revert logic
            continue jump

        // Branch Input stack: [0, funcSig(proxiableUUID), newImplementation, data, forceCall]
        // This branch will terminate execution 
        not_supported_check_revert:
            // Emit error message that the proxy call failed
            REVERT_UNSUPPORTED_PROXIABLE_UUID(0x00)
        not_uups_revert:
            REVERT_UPGRADE_IMPLEMENTATION_NOT_UUPS(0x00)

    continue:
    // NOTE: Execution will continue after this macro, make sure you terminate execution in the calling block
}

// Admin Macros

/// @notice Returns the current admin
#define macro GET_ADMIN() = takes (0) returns (1) {
    [_ADMIN_SLOT] sload    // [address]
}

/// @notice Sets the admin address of the proxy
#define macro SET_ADMIN() = takes (1) returns (0) {
    // Input Stack: [address]

    // Check that the address != address(0)
    dup1 iszero iszero                          // [address != address(0), address]
    __Is_Not_Zero_addr_JUMP jumpi                    // [address]
        ZERO_ADDRESS(0x00)

    // Set the admin slot
    __Is_Not_Zero_addr_JUMP:
    [_ADMIN_SLOT] sstore               // []
}

/// @notice Change the admin
#define macro CHANGE_ADMIN() = takes (1) returns (0) {
    // Input Stack: [address]

    // Get the previous admin
    GET_ADMIN()                         // [previousAdmin, address]

    // Set the admin
    dup2 SET_ADMIN()                    // [previousAdmin, address]

    // Emit the Upgraded event
    __EVENT_HASH(AdminChanged) 0x00 0x00 log3       // []
}

// Beacon Macros

/// @notice Returns the current beacon
#define macro GET_BEACON() = takes (0) returns (1) {
    [_BEACON_SLOT] sload        // [bytes32]
}

/// @notice Sets the proxy beacon
#define macro SET_BEACON() = takes (1) returns (0) {
    // Input Stack: [address]

    // Check that the address is a contract
    dup1 extcodesize iszero iszero              // [isContract(address), address]
    __Is_Contract_JUMP jumpi                    // [address]
        NON_CONTRACT(0x00)

    // Set the beacon slot
    __Is_Contract_JUMP:

    // Staticcall the implementation on address
    __FUNC_SIG(implementation) 0x00 mstore      // [address]
    0x20                                        // [retSize, address]
    0x00                                        // [retOffset, retSize, address]
    0x04                                        // [argSize, retOffset, retSize, address]
    0x1c                                        // [argOffset, argSize, retOffset, retSize, address]
    dup5                                        // [address, argOffset, argSize, retOffset, retSize, address]
    gas                                         // [gas, address, argOffset, argSize, retOffset, retSize, address]
    staticcall                                  // [success, address]
    iszero iszero __StaticCall_Success jumpi    // [address]
        0x00 dup1 revert
    __StaticCall_Success:

    // Load the implementation address from the staticcal
    0x00 mload                                  // [implementation, address]
    extcodesize iszero iszero                   // [isContract(address), address]
    __Is_Contract_JUMP jumpi                    // [address]
        NON_CONTRACT(0x00)

    // The implementation is a contract, so we can now set the beacon slot
    __Is_Contract_JUMP:
    [_BEACON_SLOT] sstore               // []
}


#define macro UPGRADE_TO_BEACON_AND_CALL() = takes (0) returns (0) {
    // Input Stack:                                [newBeacon, data, forceCall]

    // Set the new beacon
    dup1 SET_BEACON()                           // [newBeacon, data, forceCall]

    // Emit the BeaconUpgraded event
    dup1 __EVENT_HASH(BeaconUpgraded)           // [sig, newBeacon, newBeacon, data, forceCall]
    0x00 0x00 log2                              // [newBeacon, data, forceCall]

    // Copy the calldata to memory
    dup2 calldataload dup1                      // [dataSize, dataSize, newBeacon, data, forceCall]
    swap4 not swap1 iszero and                  // [(!forceCall && dataSize == 0), newBeacon, data, dataSize]
    __IgnoreCall_And_Clean_Stack jumpi

    // Get the implementation address from the newBeacon
    __FUNC_SIG(implementation) 0x00 mstore      // [newBeacon, data, dataSize]
    0x20                                        // [retSize, newBeacon, data, dataSize]
    0x00                                        // [retOffset, retSize, newBeacon, data, dataSize]
    0x04                                        // [argSize, retOffset, retSize, newBeacon, data, dataSize]
    0x1c                                        // [argOffset, argSize, retOffset, retSize, newBeacon, data, dataSize]
    dup5                                        // [newBeacon, argOffset, argSize, retOffset, retSize, newBeacon, data, dataSize]
    gas                                         // [gas, newBeacon, argOffset, argSize, retOffset, retSize, newBeacon, data, dataSize]
    staticcall                                  // [success, newBeacon, data, dataSize]
    iszero iszero __ImplCall_Success jumpi      // [newBeacon, data, dataSize]
    0x00 dup1 revert
    __ImplCall_Success:

    // Delegate call to the implementation address
    0x00 mload                                  // [implementation, newBeacon, data, dataSize]
    swap3 swap1 swap2                           // [data, dataSize, newBeacon, implementation]
    dup2 dup2 0x20 add                          // [&data[0], dataSize, data, dataSize, newBeacon, implementation]
    0x00                                        // [destOffset, calldataOffset, dataSize, data, dataSize, newBeacon, implementation]
    calldatacopy                                // [data, dataSize, newBeacon, implementation]
    swap1                                       // [dataSize, data, newBeacon, implementation]

    // Call the implementation with the given data
    0x00                                        // [retOffset, dataSize, data, newBeacon, implementation]
    0x00                                        // [retSize, retOffset, dataSize, data, newBeacon, implementation]
    swap2                                       // [argSize, retOffset, retSize, data, newBeacon, implementation]
    0x00                                        // [argOffset, argSize, retOffset, retSize, data, newBeacon, implementation]
    dup7                                        // [newBeacon, argOffset, argSize, retOffset, retSize, data, newBeacon, implementation]
    gas                                         // [gas, address, argOffset, argSize, retOffset, retSize, data, newBeacon, implementation]
    delegatecall                                // [success, data, newBeacon, implementation]
    iszero iszero __DelegateCall_Success jumpi  // [data, newBeacon, implementation]
    0x00 dup1 revert

    __IgnoreCall_And_Clean_Stack:
    pop pop pop                                 // []

    __DelegateCall_Success:
}