Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x9E092cb431e5F1aa70e47e052773711d2Ba4917E
Balance 0 ETH
Nonce 1
Code Size 6162 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

6162 bytes
0x608060405234801561001057600080fd5b50600436106100725760003560e01c8063d9d98ce411610050578063d9d98ce4146100d4578063e563037e146100e7578063f04f27071461013357600080fd5b806340a08f1014610077578063613255ab146100a0578063af2511c0146100c1575b600080fd5b61008a610085366004610da8565b610148565b6040516100979190610eae565b60405180910390f35b6100b36100ae366004610ec1565b6101f6565b604051908152602001610097565b61008a6100cf366004610ede565b610207565b6100b36100e2366004610f9c565b610297565b61010e7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c881565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610097565b61014661014136600461116c565b610354565b005b6060600060405180608001604052808a73ffffffffffffffffffffffffffffffffffffffff1681526020013373ffffffffffffffffffffffffffffffffffffffff168152602001858563ffffffff169060201b1760401b815260200187878080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250505091525090506101e988888361046a565b9998505050505050505050565b60006102018261053f565b92915050565b6040805160808101825273ffffffffffffffffffffffffffffffffffffffff891681523360208201526060916000919081018560e086901c63ffffffff169060201b1760401b815260200187878080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250505091525090506101e988888361046a565b6000806102a38461053f565b905060008111610314576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f556e737570706f727465642063757272656e637900000000000000000000000060448201526064015b60405180910390fd5b8083101561032a57610325836105f3565b61034c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b949350505050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c816146103c3576040517f698bba4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001548151602083012014610404576040517f3f4d605300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600090556104648460008151811061042057610420611277565b60200260200101518460008151811061043b5761043b611277565b60200260200101518460008151811061045657610456611277565b6020026020010151846106fc565b50505050565b606061049684848460405160200161048291906112a6565b60405160208183030381529060405261081c565b600080546104a39061131b565b80601f01602080910402602001604051908101604052809291908181526020018280546104cf9061131b565b801561051c5780601f106104f15761010080835404028352916020019161051c565b820191906000526020600020905b8154815290600101906020018083116104ff57829003601f168201915b5050505050905060008151111561053857610538600080610cb9565b9392505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c881166004830152600091908316906370a0823190602401602060405180830381865afa1580156105cf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610201919061136e565b60006102017f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c873ffffffffffffffffffffffffffffffffffffffff1663d2946c2b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610663573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106879190611387565b73ffffffffffffffffffffffffffffffffffffffff1663d877845c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f5919061136e565b83906108dd565b60008180602001905181019061071291906113f4565b9050610723858583600001516108f2565b600081604001518060601c9060401c63ffffffff1683602001516107647f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c890565b89898988606001516040518763ffffffff1660e01b815260040161078d969594939291906114b0565b6000604051808303816000875af11580156107ac573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526107f29190810190611509565b90506107ff868686610918565b8051156108145760006108128282611584565b505b505050505050565b80805190602001206001819055507f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c873ffffffffffffffffffffffffffffffffffffffff16635c38449e306108868673ffffffffffffffffffffffffffffffffffffffff16610a10565b61088f86610a85565b856040518563ffffffff1660e01b81526004016108af949392919061169e565b600060405180830381600087803b1580156108c957600080fd5b505af1158015610812573d6000803e3d6000fd5b60006105388383670de0b6b3a7640000610acc565b61091373ffffffffffffffffffffffffffffffffffffffff84168284610b10565b505050565b307f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c873ffffffffffffffffffffffffffffffffffffffff16036109135773ffffffffffffffffffffffffffffffffffffffff831663095ea7b33361097c8486611751565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044016020604051808303816000875af11580156109ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610464919061178b565b604080516001808252818301909252606091602080830190803683370190505090508181600081518110610a4657610a46611277565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050919050565b604080516001808252818301909252606091602080830190803683370190505090508181600081518110610abb57610abb611277565b602002602001018181525050919050565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0484118302158202610b0157600080fd5b50910281810615159190040190565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001790529151600092839290871691610ba791906117ad565b6000604051808303816000865af19150503d8060008114610be4576040519150601f19603f3d011682016040523d82523d6000602084013e610be9565b606091505b5091509150818015610c13575080511580610c13575080806020019051810190610c13919061178b565b610c5357610c2081610c5a565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161030b9190610eae565b5050505050565b6060604482511015610c9f57505060408051808201909152601d81527f5472616e73616374696f6e2072657665727465642073696c656e746c79000000602082015290565b6004820191508180602001905181019061020191906117c9565b508054610cc59061131b565b6000825580601f10610cd5575050565b601f016020900490600052602060002090810190610cf39190610cf6565b50565b5b80821115610d0b5760008155600101610cf7565b5090565b73ffffffffffffffffffffffffffffffffffffffff81168114610cf357600080fd5b60008083601f840112610d4357600080fd5b50813567ffffffffffffffff811115610d5b57600080fd5b602083019150836020828501011115610d7357600080fd5b9250929050565b7fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000081168114610cf357600080fd5b600080600080600080600060a0888a031215610dc357600080fd5b8735610dce81610d0f565b96506020880135610dde81610d0f565b955060408801359450606088013567ffffffffffffffff811115610e0157600080fd5b610e0d8a828b01610d31565b9095509350506080880135610e2181610d7a565b8060601c925063ffffffff8160401c1691505092959891949750929550565b60005b83811015610e5b578181015183820152602001610e43565b50506000910152565b60008151808452610e7c816020860160208601610e40565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006105386020830184610e64565b600060208284031215610ed357600080fd5b813561053881610d0f565b600080600080600080600060c0888a031215610ef957600080fd5b8735610f0481610d0f565b96506020880135610f1481610d0f565b955060408801359450606088013567ffffffffffffffff811115610f3757600080fd5b610f438a828b01610d31565b9095509350506080880135610f5781610d0f565b915060a08801357fffffffff0000000000000000000000000000000000000000000000000000000081168114610f8c57600080fd5b8091505092959891949750929550565b60008060408385031215610faf57600080fd5b8235610fba81610d0f565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561103e5761103e610fc8565b604052919050565b600067ffffffffffffffff82111561106057611060610fc8565b5060051b60200190565b600082601f83011261107b57600080fd5b8135602061109061108b83611046565b610ff7565b82815260059290921b840181019181810190868411156110af57600080fd5b8286015b848110156110ca57803583529183019183016110b3565b509695505050505050565b600067ffffffffffffffff8211156110ef576110ef610fc8565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f83011261112c57600080fd5b813561113a61108b826110d5565b81815284602083860101111561114f57600080fd5b816020850160208301376000918101602001919091529392505050565b6000806000806080858703121561118257600080fd5b843567ffffffffffffffff8082111561119a57600080fd5b818701915087601f8301126111ae57600080fd5b813560206111be61108b83611046565b82815260059290921b8401810191818101908b8411156111dd57600080fd5b948201945b838610156112045785356111f581610d0f565b825294820194908201906111e2565b9850508801359250508082111561121a57600080fd5b6112268883890161106a565b9450604087013591508082111561123c57600080fd5b6112488883890161106a565b9350606087013591508082111561125e57600080fd5b5061126b8782880161111b565b91505092959194509250565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60208152600073ffffffffffffffffffffffffffffffffffffffff808451166020840152806020850151166040840152507fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006040840151166060830152606083015160808084015261034c60a0840182610e64565b600181811c9082168061132f57607f821691505b602082108103611368577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b60006020828403121561138057600080fd5b5051919050565b60006020828403121561139957600080fd5b815161053881610d0f565b60006113b261108b846110d5565b90508281528383830111156113c657600080fd5b610538836020830184610e40565b600082601f8301126113e557600080fd5b610538838351602085016113a4565b60006020828403121561140657600080fd5b815167ffffffffffffffff8082111561141e57600080fd5b908301906080828603121561143257600080fd5b60405160808101818110838211171561144d5761144d610fc8565b604052825161145b81610d0f565b8152602083015161146b81610d0f565b6020820152604083015161147e81610d7a565b604082015260608301518281111561149557600080fd5b6114a1878286016113d4565b60608301525095945050505050565b600073ffffffffffffffffffffffffffffffffffffffff8089168352808816602084015280871660408401525084606083015283608083015260c060a08301526114fd60c0830184610e64565b98975050505050505050565b60006020828403121561151b57600080fd5b815167ffffffffffffffff81111561153257600080fd5b61034c848285016113d4565b601f82111561091357600081815260208120601f850160051c810160208610156115655750805b601f850160051c820191505b8181101561081457828155600101611571565b815167ffffffffffffffff81111561159e5761159e610fc8565b6115b2816115ac845461131b565b8461153e565b602080601f83116001811461160557600084156115cf5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555610814565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561165257888601518255948401946001909101908401611633565b508582101561168e57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60006080820173ffffffffffffffffffffffffffffffffffffffff8088168452602060808186015282885180855260a087019150828a01945060005b818110156116f85785518516835294830194918301916001016116da565b5050858103604087015287518082529082019350915080870160005b8381101561173057815185529382019390820190600101611714565b5050505082810360608401526117468185610e64565b979650505050505050565b80820180821115610201577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006020828403121561179d57600080fd5b8151801515811461053857600080fd5b600082516117bf818460208701610e40565b9190910192915050565b6000602082840312156117db57600080fd5b815167ffffffffffffffff8111156117f257600080fd5b8201601f8101841361180357600080fd5b61034c848251602084016113a456

Verified Source Code Partial Match

Compiler: v0.8.19+commit.7dd6d404 EVM: london Optimization: Yes (10000 runs)
BaseWrapper.sol 134 lines
// SPDX-License-Identifier: MIT
// Thanks to ultrasecr.eth
pragma solidity ^0.8.19;

import "erc7399/IERC7399.sol";

import { TransferHelper, ERC20 } from "./utils/TransferHelper.sol";
import { FunctionCodec } from "./utils/FunctionCodec.sol";

/// @dev All ERC7399 flash loan wrappers have the same general structure.
/// - The ERC7399 `flash` function is the entry point for the flash loan.
/// - The wrapper calls the underlying lender flash lender on their non-ERC7399 flash lending call to borrow the funds.
/// -     The lender sends the funds to the wrapper.
/// -         The wrapper receives the callback from the lender.
/// -         The wrapper sends the funds to the loan receiver.
/// -         The wrapper calls the callback supplied by the original borrower.
/// -             The callback from the original borrower executes.
/// -         Depending on the lender, the wrapper may have to approve it to pull the repayment.
/// -         If there is any data to return, it is kept in a storage variable.
/// -         The wrapper exits the callback.
/// -     The lender verifies or pulls the repayment.
/// - The wrapper returns to the original borrower the stored result of its callback.
abstract contract BaseWrapper is IERC7399 {
    using TransferHelper for address;

    struct Data {
        address loanReceiver;
        address initiator;
        function(address, address, address, uint256, uint256, bytes memory) external returns (bytes memory) callback;
        bytes initiatorData;
    }

    bytes internal _callbackResult;

    /// @inheritdoc IERC7399
    /// @dev The entry point for the ERC7399 flash loan. Packs data to convert the legacy flash loan into an ERC7399
    /// flash loan. Then it calls the legacy flash loan. Once the flash loan is done, checks if there is any return
    /// data and returns it.
    function flash(
        address loanReceiver,
        address asset,
        uint256 amount,
        bytes calldata initiatorData,
        function(address, address, address, uint256, uint256, bytes memory) external returns (bytes memory) callback
    )
        external
        returns (bytes memory result)
    {
        Data memory data = Data({
            loanReceiver: loanReceiver,
            initiator: msg.sender,
            callback: callback,
            initiatorData: initiatorData
        });

        return _flash(asset, amount, data);
    }

    /// @dev Alternative entry point for the ERC7399 flash loan, without function pointers. Packs data to convert the
    /// legacy flash loan into an ERC7399 flash loan. Then it calls the legacy flash loan. Once the flash loan is done,
    /// checks if there is any return data and returns it.
    function flash(
        address loanReceiver,
        address asset,
        uint256 amount,
        bytes calldata initiatorData,
        address callbackTarget,
        bytes4 callbackSelector
    )
        external
        returns (bytes memory result)
    {
        Data memory data = Data({
            loanReceiver: loanReceiver,
            initiator: msg.sender,
            callback: FunctionCodec.decodeFunction(callbackTarget, callbackSelector),
            initiatorData: initiatorData
        });

        return _flash(asset, amount, data);
    }

    function _flash(address asset, uint256 amount, Data memory data) internal virtual returns (bytes memory result) {
        _flashLoan(asset, amount, abi.encode(data));

        result = _callbackResult;
        // Avoid storage write if not needed
        if (result.length > 0) {
            delete _callbackResult;
        }
        return result;
    }

    /// @dev Call the legacy flashloan function in the child contract. This is where we borrow from Aave, Uniswap, etc.
    function _flashLoan(address asset, uint256 amount, bytes memory data) internal virtual;

    /// @dev Handle the common parts of bridging the callback from legacy to ERC7399. Transfer the funds to the loan
    /// receiver. Call the callback supplied by the original borrower. Approve the repayment if necessary. If there is
    /// any result, it is kept in a storage variable to be retrieved on `flash` after the legacy flash loan is finished.
    function _bridgeToCallback(address asset, uint256 amount, uint256 fee, bytes memory params) internal {
        Data memory data = abi.decode(params, (Data));
        _transferAssets(asset, amount, data.loanReceiver);

        // call the callback and tell the callback receiver to repay the loan to this contract
        bytes memory result = data.callback(data.initiator, _repayTo(), address(asset), amount, fee, data.initiatorData);

        _approveRepayment(asset, amount, fee);

        if (result.length > 0) {
            // if there's any result, it is kept in a storage variable to be retrieved later in this tx
            _callbackResult = result;
        }
    }

    /// @dev Transfer the assets to the loan receiver.
    /// Override it if the provider can send the funds directly
    function _transferAssets(address asset, uint256 amount, address loanReceiver) internal virtual {
        asset.safeTransfer(loanReceiver, amount);
    }

    /// @dev Approve the repayment of the loan to the provider if needed.
    /// Override it if the provider can receive the funds directly and you want to avoid the if condition
    function _approveRepayment(address asset, uint256 amount, uint256 fee) internal virtual {
        if (_repayTo() == address(this)) {
            ERC20(asset).approve(msg.sender, amount + fee);
        }
    }

    /// @dev Where should the end client send the funds to repay the loan
    /// Override it if the provider can receive the funds directly
    function _repayTo() internal view virtual returns (address) {
        return address(this);
    }
}
Arrays.sol 15 lines
// SPDX-License-Identifier: MIT
// Thanks to ultrasecr.eth
pragma solidity ^0.8.19;

library Arrays {
    function toArray(uint256 n) internal pure returns (uint256[] memory arr) {
        arr = new uint[](1);
        arr[0] = n;
    }

    function toArray(address a) internal pure returns (address[] memory arr) {
        arr = new address[](1);
        arr[0] = a;
    }
}
FunctionCodec.sol 48 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

library FunctionCodec {
    function encodeParams(address contractAddr, bytes4 selector) internal pure returns (bytes24) {
        return bytes24(bytes20(contractAddr)) | bytes24(selector) >> 160;
    }

    function decodeParams(bytes24 encoded) internal pure returns (address contractAddr, bytes4 selector) {
        contractAddr = address(bytes20(encoded));
        selector = bytes4(encoded << 160);
    }

    function encodeFunction(
        function(address, address, address, uint256, uint256, bytes memory) external returns (bytes memory) f
    )
        internal
        pure
        returns (bytes24)
    {
        return encodeParams(f.address, f.selector);
    }

    function decodeFunction(
        address contractAddr,
        bytes4 selector
    )
        internal
        pure
        returns (function(address, address, address, uint256, uint256, bytes memory) external returns (bytes memory) f)
    {
        uint32 s = uint32(selector);
        // solhint-disable-next-line no-inline-assembly
        assembly {
            f.address := contractAddr
            f.selector := s
        }
    }

    function decodeFunction(bytes24 encoded)
        internal
        pure
        returns (function(address, address, address, uint256, uint256, bytes memory) external returns (bytes memory) f)
    {
        (address contractAddr, bytes4 selector) = decodeParams(encoded);
        return decodeFunction(contractAddr, selector);
    }
}
IERC7399.sol 42 lines
// SPDX-License-Identifier: CC0
pragma solidity >=0.6.4;

/// @dev Specification for flash lenders compatible with ERC-7399
interface IERC7399 {
    /// @dev The amount of currency available to be lent.
    /// @param asset The loan currency.
    /// @return The amount of `asset` that can be borrowed.
    function maxFlashLoan(address asset) external view returns (uint256);

    /// @dev The fee to be charged for a given loan.
    /// @param asset The loan currency.
    /// @param amount The amount of assets lent.
    /// @return The amount of `asset` to be charged for the loan, on top of the returned principal.
    function flashFee(address asset, uint256 amount) external view returns (uint256);

    /// @dev Initiate a flash loan.
    /// @param loanReceiver The address receiving the flash loan
    /// @param asset The asset to be loaned
    /// @param amount The amount to loaned
    /// @param data The ABI encoded user data
    /// @param callback The address and signature of the callback function
    /// @return result ABI encoded result of the callback
    function flash(
        address loanReceiver,
        address asset,
        uint256 amount,
        bytes calldata data,
        /// @dev callback. This is a combination of the callback receiver address, and the signature of callback
        /// function. It is encoded packed as 20 bytes + 4 bytes.
        /// @dev the return of the callback function is not encoded in the parameter, but must be `returns (bytes
        /// memory)` for compliance with the standard.
        /// @param initiator The address that called this function
        /// @param paymentReceiver The address that needs to receive the amount plus fee at the end of the callback
        /// @param asset The asset to be loaned
        /// @param amount The amount to loaned
        /// @param fee The fee to be paid
        /// @param data The ABI encoded data to be passed to the callback
        /// @return result ABI encoded result of the callback
        function(address, address, address, uint256, uint256, bytes memory) external returns (bytes memory) callback
    ) external returns (bytes memory);
}
TransferHelper.sol 23 lines
// SPDX-License-Identifier: MIT
// Taken from https://github.com/Uniswap/uniswap-lib/blob/master/src/libraries/TransferHelper.sol
pragma solidity ^0.8.19;

import { ERC20 } from "solmate/tokens/ERC20.sol";
import { RevertMsgExtractor } from "./RevertMsgExtractor.sol";

// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
// USDT is a well known token that returns nothing for its transfer, transferFrom, and approve functions
// and part of the reason this library exists
library TransferHelper {
    /// @notice Transfers assets from msg.sender to a recipient
    /// @dev Errors with the underlying revert message if transfer fails
    /// @param asset The contract address of the asset which will be transferred
    /// @param to The recipient of the transfer
    /// @param value The value of the transfer
    function safeTransfer(address asset, address to, uint256 value) internal {
        (bool success, bytes memory data) =
        // solhint-disable-next-line avoid-low-level-calls
         address(asset).call(abi.encodeWithSelector(ERC20.transfer.selector, to, value));
        if (!(success && (data.length == 0 || abi.decode(data, (bool))))) revert(RevertMsgExtractor.getRevertMsg(data));
    }
}
ERC20.sol 206 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}
BalancerWrapper.sol 77 lines
// SPDX-License-Identifier: MIT
// Thanks to ultrasecr.eth
pragma solidity ^0.8.19;

import { IFlashLoanRecipient } from "./interfaces/IFlashLoanRecipient.sol";
import { IFlashLoaner } from "./interfaces/IFlashLoaner.sol";

import { Arrays } from "../utils/Arrays.sol";

import { FixedPointMathLib } from "lib/solmate/src/utils/FixedPointMathLib.sol";

import { BaseWrapper, IERC7399, ERC20 } from "../BaseWrapper.sol";

/// @dev Balancer Flash Lender that uses Balancer Pools as source of liquidity.
/// Balancer allows pushing repayments, so we override `_repayTo`.
contract BalancerWrapper is BaseWrapper, IFlashLoanRecipient {
    using Arrays for uint256;
    using Arrays for address;
    using FixedPointMathLib for uint256;

    error NotBalancer();
    error HashMismatch();

    IFlashLoaner public immutable balancer;

    bytes32 private flashLoanDataHash;

    constructor(IFlashLoaner _balancer) {
        balancer = _balancer;
    }

    /// @inheritdoc IERC7399
    function maxFlashLoan(address asset) external view returns (uint256) {
        return _maxFlashLoan(asset);
    }

    /// @inheritdoc IERC7399
    function flashFee(address asset, uint256 amount) external view returns (uint256) {
        uint256 max = _maxFlashLoan(asset);
        require(max > 0, "Unsupported currency");
        return amount >= max ? type(uint256).max : _flashFee(amount);
    }

    /// @inheritdoc IFlashLoanRecipient
    function receiveFlashLoan(
        address[] memory assets,
        uint256[] memory amounts,
        uint256[] memory fees,
        bytes memory params
    )
        external
        override
    {
        if (msg.sender != address(balancer)) revert NotBalancer();
        if (keccak256(params) != flashLoanDataHash) revert HashMismatch();
        delete flashLoanDataHash;

        _bridgeToCallback(assets[0], amounts[0], fees[0], params);
    }

    function _flashLoan(address asset, uint256 amount, bytes memory data) internal override {
        flashLoanDataHash = keccak256(data);
        balancer.flashLoan(this, asset.toArray(), amount.toArray(), data);
    }

    function _repayTo() internal view override returns (address) {
        return address(balancer);
    }

    function _flashFee(uint256 amount) internal view returns (uint256) {
        return amount.mulWadUp(balancer.getProtocolFeesCollector().getFlashLoanFeePercentage());
    }

    function _maxFlashLoan(address asset) internal view returns (uint256) {
        return ERC20(asset).balanceOf(address(balancer));
    }
}
RevertMsgExtractor.sol 21 lines
// SPDX-License-Identifier: MIT
// Taken from
// https://github.com/sushiswap/BoringSolidity/blob/441e51c0544cf2451e6116fe00515e71d7c42e2c/src/BoringBatchable.sol

pragma solidity ^0.8.19;

library RevertMsgExtractor {
    /// @dev Helper function to extract a useful revert message from a failed call.
    /// If the returned data is malformed or not correctly abi encoded then this call can fail itself.
    function getRevertMsg(bytes memory returnData) internal pure returns (string memory) {
        // If the _res length is less than 68, then the transaction failed silently (without a revert message)
        if (returnData.length < 68) return "Transaction reverted silently";

        // solhint-disable-next-line no-inline-assembly
        assembly {
            // Slice the sighash.
            returnData := add(returnData, 0x04)
        }
        return abi.decode(returnData, (string)); // All that remains is the revert string
    }
}
IFlashLoaner.sol 17 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;

import { IFlashLoanRecipient } from "./IFlashLoanRecipient.sol";
import { IProtocolFeesCollector } from "./IProtocolFeesCollector.sol";

interface IFlashLoaner {
    function flashLoan(
        IFlashLoanRecipient recipient,
        address[] memory tokens,
        uint256[] memory amounts,
        bytes memory userData
    )
        external;

    function getProtocolFeesCollector() external view returns (IProtocolFeesCollector);
}
FixedPointMathLib.sol 255 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant MAX_UINT256 = 2**256 - 1;

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // Divide x * y by the denominator.
            z := div(mul(x, y), denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // If x * y modulo the denominator is strictly greater than 0,
            // 1 is added to round up the division of x * y by the denominator.
            z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, z)
            }

            // Goal was to get z*z*y within a small factor of x. More iterations could
            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
            // We ensured y >= 256 so that the relative difference between y and y+1 is small.
            // That's not possible if x < 256 but we can just verify those cases exhaustively.

            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.

            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.

            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.

            // There is no overflow risk here since y < 2^136 after the first branch above.
            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If x+1 is a perfect square, the Babylonian method cycles between
            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Mod x by y. Note this will return
            // 0 instead of reverting if y is zero.
            z := mod(x, y)
        }
    }

    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Divide x by y. Note this will return
            // 0 instead of reverting if y is zero.
            r := div(x, y)
        }
    }

    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Add 1 to x * y if x % y > 0. Note this will
            // return 0 instead of reverting if y is zero.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}
IFlashLoanRecipient.sol 21 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;

interface IFlashLoanRecipient {
    /**
     * @dev When `flashLoan` is called on the Vault, it invokes the `receiveFlashLoan` hook on the recipient.
     *
     * At the time of the call, the Vault will have transferred `amounts` for `tokens` to the recipient. Before this
     * call returns, the recipient must have transferred `amounts` plus `feeAmounts` for each token back to the
     * Vault, or else the entire flash loan will revert.
     *
     * `userData` is the same value passed in the `IVault.flashLoan` call.
     */
    function receiveFlashLoan(
        address[] memory tokens,
        uint256[] memory amounts,
        uint256[] memory feeAmounts,
        bytes memory userData
    )
        external;
}
IProtocolFeesCollector.sol 6 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;

interface IProtocolFeesCollector {
    function getFlashLoanFeePercentage() external view returns (uint256);
}

Read Contract

balancer 0xe563037e → address
flashFee 0xd9d98ce4 → uint256
maxFlashLoan 0x613255ab → uint256

Write Contract 3 functions

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

flash 0x40a08f10
address loanReceiver
address asset
uint256 amount
bytes initiatorData
function callback
returns: bytes
flash 0xaf2511c0
address loanReceiver
address asset
uint256 amount
bytes initiatorData
address callbackTarget
bytes4 callbackSelector
returns: bytes
receiveFlashLoan 0xf04f2707
address[] assets
uint256[] amounts
uint256[] fees
bytes params

Recent Transactions

No transactions found for this address