Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xEEe3fdCc5b9D7821570294b26070B2f45cFd8aEc
Balance 0 ETH
Nonce 1
Code Size 5808 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

5808 bytes
0x60806040526004361015610049575b3615610018575f80fd5b32331461002157005b7f1b10b0f9000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f5f3560e01c80633c376bec1461096557806346904840146109135780635c975abb146108d357806369fe0e2d146107ff578063715018a6146107605780638da5cb5b1461070c578063ac77ec2a146102b5578063ddca3f4314610279578063e74b981b146101d25763f2fde38b146100c2575061000e565b346101cf5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cf576100f9610f31565b61010161162b565b73ffffffffffffffffffffffffffffffffffffffff81169081156101a35773ffffffffffffffffffffffffffffffffffffffff9074ffffffffffffffffffffffffffffffffffffffff0084549160081b167fffffffffffffffffffffff0000000000000000000000000000000000000000ff821617845560081c167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b6024837f1e4fbdf700000000000000000000000000000000000000000000000000000000815280600452fd5b80fd5b50346101cf5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cf5773ffffffffffffffffffffffffffffffffffffffff61021f610f31565b61022761162b565b16807fffffffffffffffffffffffff000000000000000000000000000000000000000060025416176002557f7a7b5a0a132f9e0581eb8527f66eae9ee89c2a3e79d4ac7e41a1f1f4d48a7fc28280a280f35b50346101cf57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cf576020600154604051908152f35b506101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cf576102e9610f31565b6101007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc360112610708576101243567ffffffffffffffff811161070457610335903690600401610f54565b9061033e6111ef565b4260e435106106dc5761034f610f82565b90610358610fa5565b9061036283611222565b946101043594600286161561069e5786156106985760a4355b341115610670575b60a435966127106103966001548a611148565b043491155f146105e657916020939173ffffffffffffffffffffffffffffffffffffffff610408856104028c61043f9f9a988f908e6103e49160048960025416951615159485923390611349565b8d866103f8866103f2611074565b946110ba565b9216903390611349565b8c6110ba565b946040519c8d96879586937fab5898e8000000000000000000000000000000000000000000000000000000008552600485016110f4565b0393165af19485156105d95781956105a0575b604096509384906001161561058a5761046b308561143e565b60018111610547575b5080866104a76104876104ad9483611148565b926104a28960c4359561049a8288611148565b111595611148565b61115b565b91611111565b73ffffffffffffffffffffffffffffffffffffffff6104ca611097565b1615159050610536576104df84335b836114e1565b73ffffffffffffffffffffffffffffffffffffffff80865192858452866020850152169216907f764f0dc063c06f32d89a3f3af80c0db4be8a090901f589a478b447e0a51f09f1863392a482519182526020820152f35b6104df84610542611097565b6104d9565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0194506104ad9061058461057c87836110ba565b9633876114e1565b90610474565b5061059b60c4358681811015611111565b6104ad565b94506020863d6020116105d1575b816105bb60209383610fc8565b810103126105cd576040955194610452565b5f80fd5b3d91506105ae565b50604051903d90823e3d90fd5b9050888080808473ffffffffffffffffffffffffffffffffffffffff600254165af1610610611192565b5015610648579161043f9893918373ffffffffffffffffffffffffffffffffffffffff610408610642602097346110ba565b92610402565b6004897f4033e4e3000000000000000000000000000000000000000000000000000000008152fd5b6004887f1841b4e1000000000000000000000000000000000000000000000000000000008152fd5b8761037b565b86156106d65760a4355b3414610383576004887f1841b4e1000000000000000000000000000000000000000000000000000000008152fd5b876106a8565b6004847f769d11e4000000000000000000000000000000000000000000000000000000008152fd5b8280fd5b5080fd5b50346101cf57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cf5773ffffffffffffffffffffffffffffffffffffffff6020915460081c16604051908152f35b50346101cf57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cf5761079761162b565b8073ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffff0000000000000000000000000000000000000000ff8116835560081c167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50346101cf5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cf5760043561083a61162b565b6127108111610875576020817f8c4d35e54a3f2ef1134138fd8ea3daee6a3c89e10d2665996babdf70261e2c7692600155604051908152a180f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f496e76616c6964466565000000000000000000000000000000000000000000006044820152fd5b50346101cf57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cf5760ff60209154166040519015158152f35b50346101cf57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cf57602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b506102207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105cd57610999610f31565b906101007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601126105cd5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedc3601126105cd576101e43567ffffffffffffffff81116105cd57610a10903690600401610f54565b6102049391933567ffffffffffffffff81116105cd57610a34903690600401610f54565b92610a3d6111ef565b4260e43510610f095760a43593612710610a5960015487611148565b0496610a6b610a66610f82565b611222565b9485610ee15761010435956002871615610ea65715610ea057865b341115610e78575b6e22d473030f116ddee9f6b43ac78ba33b156105cd57604051917f2b67b5700000000000000000000000000000000000000000000000000000000083523360048401526101243573ffffffffffffffffffffffffffffffffffffffff81168091036105cd5760248401526101443573ffffffffffffffffffffffffffffffffffffffff81168091036105cd5760448401526101643565ffffffffffff81168091036105cd5760648401526101843565ffffffffffff81168091036105cd5760848401526101a4359173ffffffffffffffffffffffffffffffffffffffff83168093036105cd57610b9e849283925f9560a48501526101c43560c485015261010060e4850152610104840191611036565b0381836e22d473030f116ddee9f6b43ac78ba35af18015610e6d57610e4a575b509060209291610c3c88610bf9610c73999a610bd8610f82565b73ffffffffffffffffffffffffffffffffffffffff60025416903390611260565b610c36610c04610f82565b610c0c611074565b9073ffffffffffffffffffffffffffffffffffffffff610c2c858d6110ba565b9216903390611260565b876110ba565b92604051978894859384937fab5898e8000000000000000000000000000000000000000000000000000000008552600485016110f4565b039173ffffffffffffffffffffffffffffffffffffffff3491165af1928315610e3f578493610e08575b50604093829184919060011615610ded57610cc0610cb9610f82565b309061143e565b60018111610d9d575b50610ce084836104a7610487610d16979883611148565b610ce8610fa5565b9073ffffffffffffffffffffffffffffffffffffffff610d06611097565b1615159050610d8f5733906114e1565b73ffffffffffffffffffffffffffffffffffffffff610d33610f82565b1673ffffffffffffffffffffffffffffffffffffffff610d51610fa5565b169084518381528460208201527f764f0dc063c06f32d89a3f3af80c0db4be8a090901f589a478b447e0a51f09f1863392a482519182526020820152f35b610d97611097565b906114e1565b610d1693507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610ce0910194610de5610dd687836110ba565b9633610de0610f82565b6114e1565b949350610cc9565b610d16929350610e0360c4358381811015611111565b610ce0565b9392506020843d602011610e37575b81610e2460209383610fc8565b810103126105cd57604093519293610c9d565b3d9150610e17565b6040513d86823e3d90fd5b610c73965090610e5e5f6020959493610fc8565b610c3c5f975050909192610bbe565b6040513d5f823e3d90fd5b7f1841b4e1000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f610a86565b15610edb57865b3414610a8e577f1841b4e1000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f610ead565b7f50e3c4c2000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f769d11e4000000000000000000000000000000000000000000000000000000005f5260045ffd5b6004359073ffffffffffffffffffffffffffffffffffffffff821682036105cd57565b9181601f840112156105cd5782359167ffffffffffffffff83116105cd57602083818601950101116105cd57565b60243573ffffffffffffffffffffffffffffffffffffffff811681036105cd5790565b60443573ffffffffffffffffffffffffffffffffffffffff811681036105cd5790565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761100957604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093818652868601375f8582860101520116010190565b60643573ffffffffffffffffffffffffffffffffffffffff811681036105cd5790565b60843573ffffffffffffffffffffffffffffffffffffffff811681036105cd5790565b919082039182116110c757565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b93929160209161110c91604087526040870191611036565b930152565b1561111a575050565b7f064a4ec6000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b818102929181159184041417156110c757565b8115611165570490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b3d156111ea573d9067ffffffffffffffff821161100957604051916111df60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610fc8565b82523d5f602084013e565b606090565b60ff5f54166111fa57565b7fd93c0665000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff168015908115611245575090565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee91501490565b92919073ffffffffffffffffffffffffffffffffffffffff8311611321575f938493608493604051937f36c785160000000000000000000000000000000000000000000000000000000085526004850152602484015260448301526064820152826e22d473030f116ddee9f6b43ac78ba35af180611308575b156112e057565b7ff4059071000000000000000000000000000000000000000000000000000000005f5260045ffd5b506e22d473030f116ddee9f6b43ac78ba33b15156112d9565b7f8112e119000000000000000000000000000000000000000000000000000000005f5260045ffd5b93156113cb5773ffffffffffffffffffffffffffffffffffffffff8311611321575f938493608493604051937f36c785160000000000000000000000000000000000000000000000000000000085526004850152602484015260448301526064820152826e22d473030f116ddee9f6b43ac78ba35af18061130857156112e057565b6064906020935f93604051927f23b872dd00000000000000000000000000000000000000000000000000000000845260048401526024830152604482015282855af1908161141c575b50156112e057565b90503d15611436575060015f5114601f3d11165b5f611414565b3b1515611430565b61144781611222565b1561145157503190565b9073ffffffffffffffffffffffffffffffffffffffff60246020928260405195869485937f70a08231000000000000000000000000000000000000000000000000000000008552166004840152165afa908115610e6d575f916114b2575090565b90506020813d6020116114d9575b816114cd60209383610fc8565b810103126105cd575190565b3d91506114c0565b826114eb57505050565b6114f481611222565b15611582575081471061155a575f80809373ffffffffffffffffffffffffffffffffffffffff829416611388f1611529611192565b5015611532575b565b7fb12d13eb000000000000000000000000000000000000000000000000000000005f5260045ffd5b7ff4d678b8000000000000000000000000000000000000000000000000000000005f5260045ffd5b9160446020925f9273ffffffffffffffffffffffffffffffffffffffff604051927fa9059cbb000000000000000000000000000000000000000000000000000000008452166004830152602482015282855af19081611609575b50611530577ffb7f5079000000000000000000000000000000000000000000000000000000005f5260045ffd5b90503d15611623575060015f5114601f3d11165b5f6115dc565b3b151561161d565b73ffffffffffffffffffffffffffffffffffffffff5f5460081c16330361164e57565b7f118cdaa7000000000000000000000000000000000000000000000000000000005f523360045260245ffdfea2646970667358221220c7a17ae34b3261597992a251f30d8c953f02626eefe458d471457bb1f365ca6264736f6c634300081d0033

Verified Source Code Full Match

Compiler: v0.8.29+commit.ab55807c EVM: cancun Optimization: Yes (100000 runs)
Aggregator.sol 188 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.29;

import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";
import {EthReceiver} from "./utils/EthReceiver.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {UniERC20} from "./libraries/UniERC20.sol";
import {SafeERC20} from "./libraries/SafeERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IAggregationExecutor} from "./interfaces/IAggregationExecutor.sol";
import {IPermit2} from "./interfaces/IPermit2.sol";
import {IAggregator} from "./interfaces/IAggregator.sol";

/**
 * @title Aggregator
 * @notice Router that allows to use `IAggregationExecutor` for swaps.
 */
contract Aggregator is IAggregator, Pausable, EthReceiver, Ownable {
    using UniERC20 for IERC20;
    using SafeERC20 for IERC20;

    uint256 private constant _PARTIAL_FILL = 1 << 0;
    uint256 private constant _REQUIRES_EXTRA_ETH = 1 << 1;
    uint256 private constant _USE_PERMIT2 = 1 << 2;
    uint256 private constant FEE_DENOMINATOR = 10000;
    // Fee in basis points (e.g., 100 = 1%)
    uint256 public fee;

    // Uniswap Permit2 address
    address private constant _PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

    address public feeRecipient;

    /// @notice Emitted when fee is updated
    event FeeUpdated(uint256 newFeeBps);

    constructor(address _owner, address _feeRecipient, uint256 _fee) Ownable(_owner) {
        feeRecipient = _feeRecipient;
        // Set initial fee in basis points
        require(_fee <= FEE_DENOMINATOR, "InvalidFee");
        fee = _fee;
    }

    // @inheritdoc IAggregator
    function swap(IAggregationExecutor executor, SwapDescription calldata desc, bytes calldata data)
        external
        payable
        whenNotPaused
        returns (uint256 returnAmount, uint256 spentAmount)
    {
        require(desc.deadline >= block.timestamp, InvalidDeadline());
        IERC20 srcToken = desc.srcToken;
        IERC20 dstToken = desc.dstToken;

        bool srcETH = srcToken.isETH();
        if (desc.flags & _REQUIRES_EXTRA_ETH != 0) {
            require(msg.value > (srcETH ? desc.amount : 0), InvalidMsgValue());
        } else {
            require(msg.value == (srcETH ? desc.amount : 0), InvalidMsgValue());
        }

        uint256 protocolFee = _getFee(desc.amount);
        uint256 sendValue = msg.value;
        if (!srcETH) {
            srcToken.safeTransferFromUniversal(
                msg.sender, address(feeRecipient), protocolFee, desc.flags & _USE_PERMIT2 != 0
            );
            srcToken.safeTransferFromUniversal(
                msg.sender, desc.srcReceiver, desc.amount - protocolFee, desc.flags & _USE_PERMIT2 != 0
            );
        } else {
            (bool success,) = feeRecipient.call{value: protocolFee}("");
            require(success, FeeTransferFailed());
            sendValue = msg.value - protocolFee;
        }

        returnAmount = IAggregationExecutor(executor).execute{value: sendValue}(data, desc.amount - protocolFee);
        spentAmount = desc.amount;

        if (desc.flags & _PARTIAL_FILL != 0) {
            uint256 unspentAmount = srcToken.uniBalanceOf(address(this));
            if (unspentAmount > 1) {
                // we leave 1 wei on the router for gas optimisations reasons
                unchecked {
                    unspentAmount--;
                }
                spentAmount -= unspentAmount;
                srcToken.uniTransfer(payable(msg.sender), unspentAmount);
            }
            require(
                returnAmount * desc.amount >= desc.minReturnAmount * spentAmount,
                ReturnAmountIsNotEnough(returnAmount, desc.minReturnAmount * spentAmount / desc.amount)
            );
        } else {
            require(returnAmount >= desc.minReturnAmount, ReturnAmountIsNotEnough(returnAmount, desc.minReturnAmount));
        }

        address payable dstReceiver = (desc.dstReceiver == address(0)) ? payable(msg.sender) : desc.dstReceiver;
        dstToken.uniTransfer(dstReceiver, returnAmount);

        // Emit swap event
        emit SwapExecuted(msg.sender, address(srcToken), address(dstToken), spentAmount, returnAmount);
    }

    // @inheritdoc IAggregator
    function swapWithPermit2(
        IAggregationExecutor executor,
        SwapDescription calldata desc,
        IPermit2.PermitSingle calldata permit,
        bytes calldata signature,
        bytes calldata data
    ) external payable whenNotPaused returns (uint256 returnAmount, uint256 spentAmount) {
        require(desc.deadline >= block.timestamp, InvalidDeadline());

        uint256 protocolFee = _getFee(desc.amount);
        uint256 sendValue = msg.value;
        {
            bool srcETH = desc.srcToken.isETH();
            require(!srcETH, SrcTokenIsETH());
            if (desc.flags & _REQUIRES_EXTRA_ETH != 0) {
                require(msg.value > (srcETH ? desc.amount : 0), InvalidMsgValue());
            } else {
                require(msg.value == (srcETH ? desc.amount : 0), InvalidMsgValue());
            }

            IPermit2(_PERMIT2).permit(msg.sender, permit, signature);

            desc.srcToken.safeTransferFromUniversal(msg.sender, address(feeRecipient), protocolFee, true);
            desc.srcToken.safeTransferFromUniversal(msg.sender, desc.srcReceiver, desc.amount - protocolFee, true);
        }

        returnAmount = IAggregationExecutor(executor).execute{value: sendValue}(data, desc.amount - protocolFee);
        spentAmount = desc.amount;

        if (desc.flags & _PARTIAL_FILL != 0) {
            uint256 unspentAmount = desc.srcToken.uniBalanceOf(address(this));
            if (unspentAmount > 1) {
                // we leave 1 wei on the router for gas optimisations reasons
                unchecked {
                    unspentAmount--;
                }
                spentAmount -= unspentAmount;
                desc.srcToken.uniTransfer(payable(msg.sender), unspentAmount);
            }
            require(
                returnAmount * desc.amount >= desc.minReturnAmount * spentAmount,
                ReturnAmountIsNotEnough(returnAmount, desc.minReturnAmount * spentAmount / desc.amount)
            );
        } else {
            require(returnAmount >= desc.minReturnAmount, ReturnAmountIsNotEnough(returnAmount, desc.minReturnAmount));
        }

        desc.dstToken.uniTransfer(
            (desc.dstReceiver == address(0)) ? payable(msg.sender) : desc.dstReceiver, returnAmount
        );

        // Emit swap event
        emit SwapExecuted(msg.sender, address(desc.srcToken), address(desc.dstToken), spentAmount, returnAmount);
    }

    // @inheritdoc IAggregator
    function setFeeRecipient(address _feeRecipient) external onlyOwner {
        feeRecipient = _feeRecipient;
        emit FeeRecipientUpdated(_feeRecipient);
    }

    /**
     * @notice Updates protocol fee in basis points
     * @param _fee New fee value in basis points (max 10000)
     */
    function setFee(uint256 _fee) external onlyOwner {
        require(_fee <= FEE_DENOMINATOR, "InvalidFee");
        fee = _fee;
        emit FeeUpdated(_fee);
    }

    function _unpause() internal override onlyOwner {
        super._unpause();
    }

    function _pause() internal override onlyOwner {
        super._pause();
    }

    function _getFee(uint256 amount) internal view returns (uint256) {
        return amount * fee / FEE_DENOMINATOR;
    }
}
Pausable.sol 112 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/Pausable.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    bool private _paused;

    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    /**
     * @dev The operation failed because the contract is paused.
     */
    error EnforcedPause();

    /**
     * @dev The operation failed because the contract is not paused.
     */
    error ExpectedPause();

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}
EthReceiver.sol 15 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

abstract contract EthReceiver {
    error EthDepositRejected();

    receive() external payable {
        _receive();
    }

    function _receive() internal virtual {
        // solhint-disable-next-line avoid-tx-origin
        require(msg.sender != tx.origin, EthDepositRejected());
    }
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)

pragma solidity >=0.4.16;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
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 value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value) external returns (bool);
}
UniERC20.sol 73 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.29;

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

/// @title Library, which allows usage of ETH as ERC20 and ERC20 itself. Uses SafeERC20 library for ERC20 interface.
library UniERC20 {
    using SafeERC20 for IERC20;

    error InsufficientBalance();
    error ApproveCalledOnETH();
    error NotEnoughValue();
    error FromIsNotSender();
    error ToIsNotThis();
    error ETHTransferFailed();

    uint256 private constant _RAW_CALL_GAS_LIMIT = 5000;
    IERC20 private constant _ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
    IERC20 private constant _ZERO_ADDRESS = IERC20(address(0));

    /// @dev Returns true if `token` is ETH.
    function isETH(IERC20 token) internal pure returns (bool) {
        return (token == _ZERO_ADDRESS || token == _ETH_ADDRESS);
    }

    /// @dev Returns `account` ERC20 `token` balance.
    function uniBalanceOf(IERC20 token, address account) internal view returns (uint256) {
        if (isETH(token)) {
            return account.balance;
        } else {
            return token.balanceOf(account);
        }
    }

    /// @dev `token` transfer `to` `amount`.
    /// Note that this function does nothing in case of zero amount.
    function uniTransfer(IERC20 token, address payable to, uint256 amount) internal {
        if (amount > 0) {
            if (isETH(token)) {
                if (address(this).balance < amount) revert InsufficientBalance();
                // solhint-disable-next-line avoid-low-level-calls
                (bool success,) = to.call{value: amount, gas: _RAW_CALL_GAS_LIMIT}("");
                if (!success) revert ETHTransferFailed();
            } else {
                token.safeTransfer(to, amount);
            }
        }
    }

    /// @dev `token` transfer `from` `to` `amount`.
    /// Note that this function does nothing in case of zero amount.
    function uniTransferFrom(IERC20 token, address payable from, address to, uint256 amount) internal {
        if (amount > 0) {
            if (isETH(token)) {
                if (msg.value < amount) revert NotEnoughValue();
                if (from != msg.sender) revert FromIsNotSender();
                if (to != address(this)) revert ToIsNotThis();
                if (msg.value > amount) {
                    // Return remainder if exist
                    unchecked {
                        // solhint-disable-next-line avoid-low-level-calls
                        (bool success,) = from.call{value: msg.value - amount, gas: _RAW_CALL_GAS_LIMIT}("");
                        if (!success) revert ETHTransferFailed();
                    }
                }
            } else {
                token.safeTransferFrom(from, to, amount);
            }
        }
    }
}
SafeERC20.sol 454 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.29;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import {IDaiLikePermit} from "../interfaces/DaiLikePermit.sol";
import {IPermit2} from "../interfaces/IPermit2.sol";
import {IWETH} from "../interfaces/IWETH.sol";
/**
 * @title Implements efficient safe methods for ERC20 interface.
 * @notice Compared to the standard ERC20, this implementation offers several enhancements:
 * 1. more gas-efficient, providing significant savings in transaction costs.
 * 2. support for different permit implementations
 * 3. forceApprove functionality
 * 4. support for WETH deposit and withdraw
 */

library SafeERC20 {
    error SafeTransferFailed();
    error SafeTransferFromFailed();
    error ForceApproveFailed();
    error SafeIncreaseAllowanceFailed();
    error SafeDecreaseAllowanceFailed();
    error SafePermitBadLength();
    error Permit2TransferAmountTooHigh();
    error PermitFailed();

    // Uniswap Permit2 address
    address private constant _PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
    bytes4 private constant _PERMIT_LENGTH_ERROR = 0x68275857; // SafePermitBadLength.selector
    uint256 private constant _RAW_CALL_GAS_LIMIT = 5000;

    /**
     * @notice Fetches the balance of a specific ERC20 token held by an account.
     * Consumes less gas then regular `ERC20.balanceOf`.
     * @dev Note that the implementation does not perform dirty bits cleaning, so it is the
     * responsibility of the caller to make sure that the higher 96 bits of the `account` parameter are clean.
     * @param token The IERC20 token contract for which the balance will be fetched.
     * @param account The address of the account whose token balance will be fetched.
     * @return tokenBalance The balance of the specified ERC20 token held by the account.
     */
    function safeBalanceOf(IERC20 token, address account) internal view returns (uint256 tokenBalance) {
        bytes4 selector = IERC20.balanceOf.selector;
        assembly ("memory-safe") {
            // solhint-disable-line no-inline-assembly
            mstore(0x00, selector)
            mstore(0x04, account)
            let success := staticcall(gas(), token, 0x00, 0x24, 0x00, 0x20)
            tokenBalance := mload(0)

            if or(iszero(success), lt(returndatasize(), 0x20)) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
        }
    }

    /**
     * @notice Attempts to safely transfer tokens from one address to another.
     * @dev If permit2 is true, uses the Permit2 standard; otherwise uses the standard ERC20 transferFrom.
     * Either requires `true` in return data, or requires target to be smart-contract and empty return data.
     * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
     * the caller to make sure that the higher 96 bits of the `from` and `to` parameters are clean.
     * @param token The IERC20 token contract from which the tokens will be transferred.
     * @param from The address from which the tokens will be transferred.
     * @param to The address to which the tokens will be transferred.
     * @param amount The amount of tokens to transfer.
     * @param permit2 If true, uses the Permit2 standard for the transfer; otherwise uses the standard ERC20 transferFrom.
     */
    function safeTransferFromUniversal(IERC20 token, address from, address to, uint256 amount, bool permit2) internal {
        if (permit2) {
            safeTransferFromPermit2(token, from, to, amount);
        } else {
            safeTransferFrom(token, from, to, amount);
        }
    }

    /**
     * @notice Attempts to safely transfer tokens from one address to another using the ERC20 standard.
     * @dev Either requires `true` in return data, or requires target to be smart-contract and empty return data.
     * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
     * the caller to make sure that the higher 96 bits of the `from` and `to` parameters are clean.
     * @param token The IERC20 token contract from which the tokens will be transferred.
     * @param from The address from which the tokens will be transferred.
     * @param to The address to which the tokens will be transferred.
     * @param amount The amount of tokens to transfer.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 amount) internal {
        bytes4 selector = token.transferFrom.selector;
        bool success;
        assembly ("memory-safe") {
            // solhint-disable-line no-inline-assembly
            let data := mload(0x40)

            mstore(data, selector)
            mstore(add(data, 0x04), from)
            mstore(add(data, 0x24), to)
            mstore(add(data, 0x44), amount)
            success := call(gas(), token, 0, data, 100, 0x0, 0x20)
            if success {
                switch returndatasize()
                case 0 { success := gt(extcodesize(token), 0) }
                default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) }
            }
        }
        if (!success) revert SafeTransferFromFailed();
    }

    /**
     * @notice Attempts to safely transfer tokens from one address to another using the Permit2 standard.
     * @dev Either requires `true` in return data, or requires target to be smart-contract and empty return data.
     * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
     * the caller to make sure that the higher 96 bits of the `from` and `to` parameters are clean.
     * @param token The IERC20 token contract from which the tokens will be transferred.
     * @param from The address from which the tokens will be transferred.
     * @param to The address to which the tokens will be transferred.
     * @param amount The amount of tokens to transfer.
     */
    function safeTransferFromPermit2(IERC20 token, address from, address to, uint256 amount) internal {
        if (amount > type(uint160).max) revert Permit2TransferAmountTooHigh();
        bytes4 selector = IPermit2.transferFrom.selector;
        bool success;
        assembly ("memory-safe") {
            // solhint-disable-line no-inline-assembly
            let data := mload(0x40)

            mstore(data, selector)
            mstore(add(data, 0x04), from)
            mstore(add(data, 0x24), to)
            mstore(add(data, 0x44), amount)
            mstore(add(data, 0x64), token)
            success := call(gas(), _PERMIT2, 0, data, 0x84, 0x0, 0x0)
            if success { success := gt(extcodesize(_PERMIT2), 0) }
        }
        if (!success) revert SafeTransferFromFailed();
    }

    /**
     * @notice Attempts to safely transfer tokens to another address.
     * @dev Either requires `true` in return data, or requires target to be smart-contract and empty return data.
     * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
     * the caller to make sure that the higher 96 bits of the `to` parameter are clean.
     * @param token The IERC20 token contract from which the tokens will be transferred.
     * @param to The address to which the tokens will be transferred.
     * @param value The amount of tokens to transfer.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        if (!_makeCall(token, token.transfer.selector, to, value)) {
            revert SafeTransferFailed();
        }
    }

    /**
     * @notice Attempts to approve a spender to spend a certain amount of tokens.
     * @dev If `approve(from, to, amount)` fails, it tries to set the allowance to zero, and retries the `approve` call.
     * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
     * the caller to make sure that the higher 96 bits of the `spender` parameter are clean.
     * @param token The IERC20 token contract on which the call will be made.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        if (!_makeCall(token, token.approve.selector, spender, value)) {
            if (
                !_makeCall(token, token.approve.selector, spender, 0)
                    || !_makeCall(token, token.approve.selector, spender, value)
            ) {
                revert ForceApproveFailed();
            }
        }
    }

    /**
     * @notice Safely increases the allowance of a spender.
     * @dev Increases with safe math check. Checks if the increased allowance will overflow, if yes, then it reverts the transaction.
     * Then uses `forceApprove` to increase the allowance.
     * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
     * the caller to make sure that the higher 96 bits of the `spender` parameter are clean.
     * @param token The IERC20 token contract on which the call will be made.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to increase the allowance by.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 allowance = token.allowance(address(this), spender);
        if (value > type(uint256).max - allowance) revert SafeIncreaseAllowanceFailed();
        forceApprove(token, spender, allowance + value);
    }

    /**
     * @notice Safely decreases the allowance of a spender.
     * @dev Decreases with safe math check. Checks if the decreased allowance will underflow, if yes, then it reverts the transaction.
     * Then uses `forceApprove` to increase the allowance.
     * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
     * the caller to make sure that the higher 96 bits of the `spender` parameter are clean.
     * @param token The IERC20 token contract on which the call will be made.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to decrease the allowance by.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 allowance = token.allowance(address(this), spender);
        if (value > allowance) revert SafeDecreaseAllowanceFailed();
        forceApprove(token, spender, allowance - value);
    }

    /**
     * @notice Attempts to execute the `permit` function on the provided token with the sender and contract as parameters.
     * Permit type is determined automatically based on permit calldata (IERC20Permit, IDaiLikePermit, and IPermit2).
     * @dev Wraps `tryPermit` function and forwards revert reason if permit fails.
     * @param token The IERC20 token to execute the permit function on.
     * @param permit The permit data to be used in the function call.
     */
    function safePermit(IERC20 token, bytes calldata permit) internal {
        require(!tryPermit(token, msg.sender, address(this), permit), PermitFailed());
    }

    /**
     * @notice Attempts to execute the `permit` function on the provided token with custom owner and spender parameters.
     * Permit type is determined automatically based on permit calldata (IERC20Permit, IDaiLikePermit, and IPermit2).
     * @dev Wraps `tryPermit` function and forwards revert reason if permit fails.
     * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
     * the caller to make sure that the higher 96 bits of the `owner` and `spender` parameters are clean.
     * @param token The IERC20 token to execute the permit function on.
     * @param owner The owner of the tokens for which the permit is made.
     * @param spender The spender allowed to spend the tokens by the permit.
     * @param permit The permit data to be used in the function call.
     */
    function safePermit(IERC20 token, address owner, address spender, bytes calldata permit) internal {
        require(!tryPermit(token, owner, spender, permit), PermitFailed());
    }

    /**
     * @notice Attempts to execute the `permit` function on the provided token with the sender and contract as parameters.
     * @dev Invokes `tryPermit` with sender as owner and contract as spender.
     * @param token The IERC20 token to execute the permit function on.
     * @param permit The permit data to be used in the function call.
     * @return success Returns true if the permit function was successfully executed, false otherwise.
     */
    function tryPermit(IERC20 token, bytes calldata permit) internal returns (bool success) {
        return tryPermit(token, msg.sender, address(this), permit);
    }

    /**
     * @notice The function attempts to call the permit function on a given ERC20 token.
     * @dev The function is designed to support a variety of permit functions, namely: IERC20Permit, IDaiLikePermit, and IPermit2.
     * It accommodates both Compact and Full formats of these permit types.
     * Please note, it is expected that the `expiration` parameter for the compact Permit2 and the `deadline` parameter
     * for the compact Permit are to be incremented by one before invoking this function. This approach is motivated by
     * gas efficiency considerations; as the unlimited expiration period is likely to be the most common scenario, and
     * zeros are cheaper to pass in terms of gas cost. Thus, callers should increment the expiration or deadline by one
     * before invocation for optimized performance.
     * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
     * the caller to make sure that the higher 96 bits of the `owner` and `spender` parameters are clean.
     * @param token The address of the ERC20 token on which to call the permit function.
     * @param owner The owner of the tokens. This address should have signed the off-chain permit.
     * @param spender The address which will be approved for transfer of tokens.
     * @param permit The off-chain permit data, containing different fields depending on the type of permit function.
     * @return success A boolean indicating whether the permit call was successful.
     */
    function tryPermit(IERC20 token, address owner, address spender, bytes calldata permit)
        internal
        returns (bool success)
    {
        // load function selectors for different permit standards
        bytes4 permitSelector = IERC20Permit.permit.selector;
        bytes4 daiPermitSelector = IDaiLikePermit.permit.selector;
        bytes4 permit2Selector = IPermit2.permit.selector;
        assembly ("memory-safe") {
            // solhint-disable-line no-inline-assembly
            let ptr := mload(0x40)

            // Switch case for different permit lengths, indicating different permit standards
            switch permit.length
            // Compact IERC20Permit
            case 100 {
                mstore(ptr, permitSelector) // store selector
                mstore(add(ptr, 0x04), owner) // store owner
                mstore(add(ptr, 0x24), spender) // store spender

                // Compact IERC20Permit.permit(uint256 value, uint32 deadline, uint256 r, uint256 vs)
                {
                    // stack too deep
                    let deadline := shr(224, calldataload(add(permit.offset, 0x20))) // loads permit.offset 0x20..0x23
                    let vs := calldataload(add(permit.offset, 0x44)) // loads permit.offset 0x44..0x63

                    calldatacopy(add(ptr, 0x44), permit.offset, 0x20) // store value     = copy permit.offset 0x00..0x19
                    mstore(add(ptr, 0x64), sub(deadline, 1)) // store deadline  = deadline - 1
                    mstore(add(ptr, 0x84), add(27, shr(255, vs))) // store v         = most significant bit of vs + 27 (27 or 28)
                    calldatacopy(add(ptr, 0xa4), add(permit.offset, 0x24), 0x20) // store r         = copy permit.offset 0x24..0x43
                    mstore(add(ptr, 0xc4), shr(1, shl(1, vs))) // store s         = vs without most significant bit
                }
                // IERC20Permit.permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
                success := call(gas(), token, 0, ptr, 0xe4, 0, 0)
            }
            // Compact IDaiLikePermit
            case 72 {
                mstore(ptr, daiPermitSelector) // store selector
                mstore(add(ptr, 0x04), owner) // store owner
                mstore(add(ptr, 0x24), spender) // store spender

                // Compact IDaiLikePermit.permit(uint32 nonce, uint32 expiry, uint256 r, uint256 vs)
                {
                    // stack too deep
                    let expiry := shr(224, calldataload(add(permit.offset, 0x04))) // loads permit.offset 0x04..0x07
                    let vs := calldataload(add(permit.offset, 0x28)) // loads permit.offset 0x28..0x47

                    mstore(add(ptr, 0x44), shr(224, calldataload(permit.offset))) // store nonce   = copy permit.offset 0x00..0x03
                    mstore(add(ptr, 0x64), sub(expiry, 1)) // store expiry  = expiry - 1
                    mstore(add(ptr, 0x84), true) // store allowed = true
                    mstore(add(ptr, 0xa4), add(27, shr(255, vs))) // store v       = most significant bit of vs + 27 (27 or 28)
                    calldatacopy(add(ptr, 0xc4), add(permit.offset, 0x08), 0x20) // store r       = copy permit.offset 0x08..0x27
                    mstore(add(ptr, 0xe4), shr(1, shl(1, vs))) // store s       = vs without most significant bit
                }
                // IDaiLikePermit.permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s)
                success := call(gas(), token, 0, ptr, 0x104, 0, 0)
            }
            // IERC20Permit
            case 224 {
                mstore(ptr, permitSelector)
                calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata
                // IERC20Permit.permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
                success := call(gas(), token, 0, ptr, 0xe4, 0, 0)
            }
            // IDaiLikePermit
            case 256 {
                mstore(ptr, daiPermitSelector)
                calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata
                // IDaiLikePermit.permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s)
                success := call(gas(), token, 0, ptr, 0x104, 0, 0)
            }
            // Compact IPermit2
            case 96 {
                // Compact IPermit2.permit(uint160 amount, uint32 expiration, uint32 nonce, uint32 sigDeadline, uint256 r, uint256 vs)
                mstore(ptr, permit2Selector) // store selector
                mstore(add(ptr, 0x04), owner) // store owner
                mstore(add(ptr, 0x24), token) // store token

                calldatacopy(add(ptr, 0x50), permit.offset, 0x14) // store amount = copy permit.offset 0x00..0x13
                // and(0xffffffffffff, ...) - conversion to uint48
                mstore(add(ptr, 0x64), and(0xffffffffffff, sub(shr(224, calldataload(add(permit.offset, 0x14))), 1))) // store expiration = ((permit.offset 0x14..0x17 - 1) & 0xffffffffffff)
                mstore(add(ptr, 0x84), shr(224, calldataload(add(permit.offset, 0x18)))) // store nonce = copy permit.offset 0x18..0x1b
                mstore(add(ptr, 0xa4), spender) // store spender
                // and(0xffffffffffff, ...) - conversion to uint48
                mstore(add(ptr, 0xc4), and(0xffffffffffff, sub(shr(224, calldataload(add(permit.offset, 0x1c))), 1))) // store sigDeadline = ((permit.offset 0x1c..0x1f - 1) & 0xffffffffffff)
                mstore(add(ptr, 0xe4), 0x100) // store offset = 256
                mstore(add(ptr, 0x104), 0x40) // store length = 64
                calldatacopy(add(ptr, 0x124), add(permit.offset, 0x20), 0x20) // store r      = copy permit.offset 0x20..0x3f
                calldatacopy(add(ptr, 0x144), add(permit.offset, 0x40), 0x20) // store vs     = copy permit.offset 0x40..0x5f
                // IPermit2.permit(address owner, PermitSingle calldata permitSingle, bytes calldata signature)
                success := call(gas(), _PERMIT2, 0, ptr, 0x164, 0, 0)
            }
            // IPermit2
            case 352 {
                mstore(ptr, permit2Selector)
                calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata
                // IPermit2.permit(address owner, PermitSingle calldata permitSingle, bytes calldata signature)
                success := call(gas(), _PERMIT2, 0, ptr, 0x164, 0, 0)
            }
            // Unknown
            default {
                mstore(ptr, _PERMIT_LENGTH_ERROR)
                revert(ptr, 4)
            }
        }
    }

    /**
     * @dev Executes a low level call to a token contract, making it resistant to reversion and erroneous boolean returns.
     * @param token The IERC20 token contract on which the call will be made.
     * @param selector The function signature that is to be called on the token contract.
     * @param to The address to which the token amount will be transferred.
     * @param amount The token amount to be transferred.
     * @return success A boolean indicating if the call was successful. Returns 'true' on success and 'false' on failure.
     * In case of success but no returned data, validates that the contract code exists.
     * In case of returned data, ensures that it's a boolean `true`.
     */
    function _makeCall(IERC20 token, bytes4 selector, address to, uint256 amount) private returns (bool success) {
        assembly ("memory-safe") {
            // solhint-disable-line no-inline-assembly
            let data := mload(0x40)

            mstore(data, selector)
            mstore(add(data, 0x04), to)
            mstore(add(data, 0x24), amount)
            success := call(gas(), token, 0, data, 0x44, 0x0, 0x20)
            if success {
                switch returndatasize()
                case 0 { success := gt(extcodesize(token), 0) }
                default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) }
            }
        }
    }

    /**
     * @notice Safely deposits a specified amount of Ether into the IWETH contract. Consumes less gas then regular `IWETH.deposit`.
     * @param weth The IWETH token contract.
     * @param amount The amount of Ether to deposit into the IWETH contract.
     */
    function safeDeposit(IWETH weth, uint256 amount) internal {
        if (amount > 0) {
            bytes4 selector = IWETH.deposit.selector;
            assembly ("memory-safe") {
                // solhint-disable-line no-inline-assembly
                mstore(0, selector)
                if iszero(call(gas(), weth, amount, 0, 4, 0, 0)) {
                    let ptr := mload(0x40)
                    returndatacopy(ptr, 0, returndatasize())
                    revert(ptr, returndatasize())
                }
            }
        }
    }

    /**
     * @notice Safely withdraws a specified amount of wrapped Ether from the IWETH contract. Consumes less gas then regular `IWETH.withdraw`.
     * @dev Uses inline assembly to interact with the IWETH contract.
     * @param weth The IWETH token contract.
     * @param amount The amount of wrapped Ether to withdraw from the IWETH contract.
     */
    function safeWithdraw(IWETH weth, uint256 amount) internal {
        bytes4 selector = IWETH.withdraw.selector;
        assembly ("memory-safe") {
            // solhint-disable-line no-inline-assembly
            mstore(0, selector)
            mstore(4, amount)
            if iszero(call(gas(), weth, 0, 0, 0x24, 0, 0)) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
        }
    }

    /**
     * @notice Safely withdraws a specified amount of wrapped Ether from the IWETH contract to a specified recipient.
     * Consumes less gas then regular `IWETH.withdraw`.
     * @param weth The IWETH token contract.
     * @param amount The amount of wrapped Ether to withdraw from the IWETH contract.
     * @param to The recipient of the withdrawn Ether.
     */
    function safeWithdrawTo(IWETH weth, uint256 amount, address to) internal {
        safeWithdraw(weth, amount);
        if (to != address(this)) {
            assembly ("memory-safe") {
                // solhint-disable-line no-inline-assembly
                if iszero(call(_RAW_CALL_GAS_LIMIT, to, amount, 0, 0, 0, 0)) {
                    let ptr := mload(0x40)
                    returndatacopy(ptr, 0, returndatasize())
                    revert(ptr, returndatasize())
                }
            }
        }
    }
}
Ownable.sol 100 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
IAggregationExecutor.sol 6 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.29;

interface IAggregationExecutor {
    function execute(bytes memory data, uint256 amountIn) external payable returns (uint256);
}
IPermit2.sol 41 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.29;

interface IPermit2 {
    struct PermitDetails {
        // ERC20 token address
        address token;
        // the maximum amount allowed to spend
        uint160 amount;
        // timestamp at which a spender's token allowances become invalid
        uint48 expiration;
        // an incrementing value indexed per owner,token,and spender for each signature
        uint48 nonce;
    }
    /// @notice The permit message signed for a single token allownce

    struct PermitSingle {
        // the permit data for a single token alownce
        PermitDetails details;
        // address permissioned on the allowed tokens
        address spender;
        // deadline on the permit signature
        uint256 sigDeadline;
    }
    /// @notice Packed allowance

    struct PackedAllowance {
        // amount allowed
        uint160 amount;
        // permission expiry
        uint48 expiration;
        // an incrementing value indexed per owner,token,and spender for each signature
        uint48 nonce;
    }

    function transferFrom(address user, address spender, uint160 amount, address token) external;

    function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;

    function allowance(address user, address token, address spender) external view returns (PackedAllowance memory);
}
IAggregator.sol 84 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.29;

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

/**
 * @title IAggregator
 * @notice Interface for GenericRouter that allows to use `IAggregationExecutor` for swaps.
 */
interface IAggregator {
    // Custom errors
    error InvalidMsgValue();
    error ReturnAmountIsNotEnough(uint256 returnAmount, uint256 minReturnAmount);
    error InvalidDeadline();
    error FeeTransferFailed();
    error SrcTokenIsETH();

    // Events
    event FeeRecipientUpdated(address indexed newFeeRecipient);
    event SwapExecuted(
        address indexed user, address indexed srcToken, address indexed dstToken, uint256 srcAmount, uint256 dstAmount
    );

    // Structures
    struct SwapDescription {
        IERC20 srcToken;
        IERC20 dstToken;
        address payable srcReceiver;
        address payable dstReceiver;
        uint256 amount;
        uint256 minReturnAmount;
        uint256 deadline;
        uint256 flags;
    }

    /**
     * @notice Performs a swap, delegating all calls encoded in `data` to `executor`. See tests for usage examples.
     * @dev Router keeps 1 wei of every token on the contract balance for gas optimisations reasons.
     *      This affects first swap of every token by leaving 1 wei on the contract.
     * @param executor Aggregation executor that executes calls described in `data`.
     * @param desc Swap description.
     * @param data Encoded calls that `caller` should execute in between of swaps.
     * @return returnAmount Resulting token amount.
     * @return spentAmount Source token amount.
     */
    function swap(IAggregationExecutor executor, SwapDescription calldata desc, bytes calldata data)
        external
        payable
        returns (uint256 returnAmount, uint256 spentAmount);

    /**
     * @notice Performs a swap using Permit2 for token approval.
     * @dev This function requires explicit permit2 structure and signature.
     * @param executor Aggregation executor that executes calls described in `data`.
     * @param desc Swap description for permit2.
     * @param permit The permit2 data structure.
     * @param signature The signature for the permit.
     * @param data Encoded calls that `executor` should execute.
     * @return returnAmount Resulting token amount.
     * @return spentAmount Source token amount.
     */
    function swapWithPermit2(
        IAggregationExecutor executor,
        SwapDescription calldata desc,
        IPermit2.PermitSingle calldata permit,
        bytes calldata signature,
        bytes calldata data
    ) external payable returns (uint256 returnAmount, uint256 spentAmount);

    /**
     * @notice Sets the fee recipient address.
     * @dev Only owner can call this function.
     * @param _feeRecipient New fee recipient address.
     */
    function setFeeRecipient(address _feeRecipient) external;

    /**
     * @notice Returns the current fee recipient address.
     * @return The address of the current fee recipient.
     */
    function feeRecipient() external view returns (address);
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
IERC20Metadata.sol 26 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity >=0.6.2;

import {IERC20} from "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC-20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
IERC20Permit.sol 90 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity >=0.4.16;

/**
 * @dev Interface of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[ERC-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC-20 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.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
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].
     *
     * CAUTION: See Security Considerations above.
     */
    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);
}
DaiLikePermit.sol 15 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.29;

interface IDaiLikePermit {
    function permit(
        address holder,
        address spender,
        uint256 nonce,
        uint256 expiry,
        bool allowed,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}
IWETH.sol 14 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.29;

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

interface IWETH is IERC20 {
    event Deposit(address indexed dst, uint256 wad);

    event Withdrawal(address indexed src, uint256 wad);

    function deposit() external payable;

    function withdraw(uint256 amount) external;
}

Read Contract

fee 0xddca3f43 → uint256
feeRecipient 0x46904840 → address
owner 0x8da5cb5b → address
paused 0x5c975abb → bool

Write Contract 6 functions

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

renounceOwnership 0x715018a6
No parameters
setFee 0x69fe0e2d
uint256 _fee
setFeeRecipient 0xe74b981b
address _feeRecipient
swap 0x6b1ef56f
address executor
tuple desc
bytes data
returns: uint256, uint256
swapWithPermit2 0x982ee81b
address executor
tuple desc
tuple permit
bytes signature
bytes data
returns: uint256, uint256
transferOwnership 0xf2fde38b
address newOwner

Recent Transactions

No transactions found for this address