ETH Price: $1,949.06 (-1.85%)

Transaction Decoder

Block:
24510611 at Feb-22-2026 06:27:11 AM +UTC
Transaction Fee:
0.000001677620510832 ETH $0.00327
Gas Used:
52,176 Gas / 0.032153107 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x23F433BF...ab3E4bC5D
0.000095372675630841 Eth
Nonce: 64
0.000093695055120009 Eth
Nonce: 65
0.000001677620510832
(quasarbuilder)
21.12373360857030938 Eth21.123733669042919492 Eth0.000000060472610112

Execution Trace

RainbowRouter.fillQuoteTokenToEth( sellTokenAddress=0x1Ff22Cb1c7804b3529A005DdcF9bDE6E7c9D6d90, target=0x0000000000001fF3684f28c67538d4D072C22734, swapCallData=0x2213BC0B000000000000000000000000F48A3F7C0575C85CF4529AA220CAF3C055773F1C0000000000000000000000001FF22CB1C7804B3529A005DDCF9BDE6E7C9D6D900000000000000000000000000000000000000000000000000014340A3653EC00000000000000000000000000F48A3F7C0575C85CF4529AA220CAF3C055773F1C00000000000000000000000000000000000000000000000000000000000000A000000000000000000000000000000000000000000000000000000000000005641FFF991F00000000000000000000000000000000009726632680FB29D3F7A9734E3010E2000000000000000000000000EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE0000000000000000000000000000000000000000000000000000090105FB43FC00000000000000000000000000000000000000000000000000000000000000A0FCA6B9F55B130F937A69740900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001A000000000000000000000000000000000000000000000000000000000000002A000000000000000000000000000000000000000000000000000000000000003E000000000000000000000000000000000000000000000000000000000000000E4C1FB425E000000000000000000000000D9A59DD0EB0F3A0F163C1AD05D3BC5CA47260E4E0000000000000000000000001FF22CB1C7804B3529A005DDCF9BDE6E7C9D6D900000000000000000000000000000000000000000000000000014340A3653EC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000699AA2DE00000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C4103B48BE000000000000000000000000F48A3F7C0575C85CF4529AA220CAF3C055773F1C0000000000000000000000001FF22CB1C7804B3529A005DDCF9BDE6E7C9D6D900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000D9A59DD0EB0F3A0F163C1AD05D3BC5CA47260E4E0000000000000000000000000000000000000000000000000000000000001E01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010438C9C147000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC20000000000000000000000000000000000000000000000000000000000002710000000000000000000000000C02AAA39B223FE8D0A0E5C4F27EAD9083C756CC2000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000A000000000000000000000000000000000000000000000000000000000000000242E1A7D4D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008434EE90CA000000000000000000000000F5C4F3DC02C3FB9279495A8FEF7B0741DA956157000000000000000000000000EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE00000000000000000000000000000000000000000000000000000971982E348000000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, sellAmount=5686718000000000, feePercentageBasisPoints=8500000000000000 )
  • 0x1ff22cb1c7804b3529a005ddcf9bde6e7c9d6d90.23b872dd( )
    fillQuoteTokenToEth[BaseAggregator (ln:538)]
    /*
                                                                ▐██▒           ███
                                                ,╓, ▄▄  ,▄▄▄,   .▄▄. ,╓, ▄▄▄   ██▌ ▄▄▄     ,▄▄▄,  ,╓╓   ╓╓   ,╓
                                                ██████ ███▀▀██⌐ ▐██⌐ ███▀▀███⌐ ████▀▀███ ╓██▀▀▀██▄`██▌ ▐██▌ ▐██
                                                ███    ▄▄█████▌ ▐██⌐ ██▌  ▐██▌ ██▌   ║██⌐███   ▓██ ╙██▄█▌██▄██⌐
                                                ██▌   ▐██▄▄███▌,▐██⌐ ██▌  ▐██▌ ███▓▄▄███ ╙██▄▄▄██▀  ║███¬╙███▌
                                                ╙╙└    ╙╙▀╙─╙▀▀└"╙╙` ╙╙└   ╙╙" ╙╙`╙╙▀▀└    ╙╙▀▀╙`    ╙╙└  ╙╙╙
                                                
            
                                                _," _   _"""ⁿ=-,  _
                                                ⌠            _    __"=.__
                                                ▐░......   _  _          "=._
                                                ▐░░░░░░░░░░░░░░░░.           "= _
                                                ╚╩╩╩╩╩╩δφφφφ░░░░░░░░░░░          >__
                                                ▐░░░░░░░░__ _ ╙╙╚╩φφ░░░░░░░        ^=_
                                                ▐░░░░░░░░░░░░░░░,░  `╙╠φ░░░░░░░       ⁿ
                                                ▐░░░░░░░░░░░░░░░░░░░░._ `╚Åφ░░░░░       " _
                                                ╚╠╠╠╠╠╠╠╬╬╬▒▒φ░░░░░░░░░░░░  ╙╠░░░░░       "
                                                ╚╝╝╝╝╝╝╝╬╬╬╠╢╬╠╬╠╬▒░░░░░░░░░░ "╚φ░░░░       ½_
                                                ▐░░░░░░░░░░;░╙╙╝╬╠╬╠╠▒▒░░░░░░░░_ ╚φ░░░░      "_
                                                ╚▒φφφ░░░░░░░░░░░░-╙╚╬╠╠╠╬▒░░░░░░░ `╠▒░░░░      ,
                                                ╞╬╬╬╠╠╠╬╬╬╬▒φ▒░░░░░░░╙╚╬╬╠╬▒▒░░░░░ .╙╠░░░░      ≥
                                                _▒░░░ΓΓ╙╙╙╚╩╬╠╬╠▒▒░░░░░░╙╬╬╠╬▒▒░░░░░' ╠▒░░░░     ≥
                                                `╙ⁿⁿ≈≈σ╓░ '╙╙╚╬╠╬▒░░░░░░╙╬╬╠╬▒░░░░░  ╠▒░░░░     [
                                                            _╙Θ░ ░╙╠╠╬╬▒░░░░░╬╠╠╠▒▒░░░░  ╠▒░░░░    '_
                                                            _╙φ░'╙╠╠╬▒░░░░░╟╠╠╠▒░░░░░ _╠▒░░░     ░_
                                                                _`φ ░╚╬╠╠▒░░░░║╠╠╠▒░░░░░.`╠░░░░     [
                                                                _╚░⌡╚╠╬╬▒░░░░╠╠╠╬▒░░░░░ ╠▒░░░░    ░
                                                                    _╙░⌡╚╠╠╬▒░░░"▒╠╠╬▒░░░░ ⌠╠░░░░    ⌡_
                                                                    ╠ ░╠╠╠╬▒░░░║╠╬╠╬▒░░░ _╠▒░░░     Γ
                                                                    ▐░░░╠╠╠▒░░░╟╠╠╠╬▒░░░░ ╠▒░░░░    [
                                                                    _░.░╠╠╠▒░░░▐╬╠╠╬▒░░░░[╠╬░░░░    │
                                                                    _╙φ░╠╠╠╬▒░░▐╬╬╠╬╬▒░░░[╠╬░░░░  ░≥_
                                                                        ____ ____  __ _______ ____
        /$$$$$$                                           /$$$$$$                                                                /$$                        
        /$$__  $$                                         /$$__  $$                                                              | $$                        
        | $$  \\__/ /$$  /$$  /$$  /$$$$$$   /$$$$$$       | $$  \\ $$  /$$$$$$   /$$$$$$   /$$$$$$   /$$$$$$   /$$$$$$   /$$$$$$  /$$$$$$    /$$$$$$   /$$$$$$ 
        |  $$$$$$ | $$ | $$ | $$ |____  $$ /$$__  $$      | $$$$$$$$ /$$__  $$ /$$__  $$ /$$__  $$ /$$__  $$ /$$__  $$ |____  $$|_  $$_/   /$$__  $$ /$$__  $$
        \\____  $$| $$ | $$ | $$  /$$$$$$$| $$  \\ $$      | $$__  $$| $$  \\ $$| $$  \\ $$| $$  \\__/| $$$$$$$$| $$  \\ $$  /$$$$$$$  | $$    | $$  \\ $$| $$  \\__/
        /$$  \\ $$| $$ | $$ | $$ /$$__  $$| $$  | $$      | $$  | $$| $$  | $$| $$  | $$| $$      | $$_____/| $$  | $$ /$$__  $$  | $$ /$$| $$  | $$| $$      
        |  $$$$$$/|  $$$$$/$$$$/|  $$$$$$$| $$$$$$$/      | $$  | $$|  $$$$$$$|  $$$$$$$| $$      |  $$$$$$$|  $$$$$$$|  $$$$$$$  |  $$$$/|  $$$$$$/| $$      
        \\______/  \\_____/\\___/  \\_______/| $$____/       |__/  |__/ \\____  $$ \\____  $$|__/       \\_______/ \\____  $$ \\_______/   \\___/   \\______/ |__/      
                                        | $$                       /$$  \\ $$ /$$  \\ $$                     /$$  \\ $$                                        
                                        | $$                      |  $$$$$$/|  $$$$$$/                    |  $$$$$$/                                        
                                        |__/                       \\______/  \\______/                      \\______/                                         
                    
         
    */
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity =0.8.11;
    import "@rari-capital/solmate/src/utils/SafeTransferLib.sol";
    import "@rari-capital/solmate/src/tokens/ERC20.sol";
    import "./routers/BaseAggregator.sol";
    /// @title Rainbow swap aggregator contract
    contract RainbowRouter is BaseAggregator {
        /// @dev The address that is the current owner of this contract
        address public owner;
        /// @dev Event emitted when the owner changes
        event OwnerChanged(address indexed newOwner, address indexed oldOwner);
        /// @dev Event emitted when a swap target gets added
        event SwapTargetAdded(address indexed target);
        /// @dev Event emitted when a swap target gets removed
        event SwapTargetRemoved(address indexed target);
        /// @dev Event emitted when token fees are withdrawn
        event TokenWithdrawn(
            address indexed token,
            address indexed target,
            uint256 amount
        );
        /// @dev Event emitted when ETH fees are withdrawn
        event EthWithdrawn(address indexed target, uint256 amount);
        /// @dev modifier that ensures only the owner is allowed to call a specific method
        modifier onlyOwner() {
            require(msg.sender == owner, "ONLY_OWNER");
            _;
        }
        constructor() {
            owner = msg.sender;
            status = 1;
        }
        /// @dev We don't want to accept any ETH, except refunds from aggregators
        /// or the owner (for testing purposes), which can also withdraw
        /// This is done by evaluating the value of status, which is set to 2
        /// only during swaps due to the "nonReentrant" modifier
        receive() external payable {
            require(status == 2 || msg.sender == owner, "NO_RECEIVE");
        }
        /// @dev method to add or remove swap targets from swapTargets
        /// This is required so we only approve "trusted" swap targets
        /// to transfer tokens out of this contract
        /// @param target address of the swap target to add
        /// @param add flag to add or remove the swap target
        function updateSwapTargets(address target, bool add) external onlyOwner {
            swapTargets[target] = add;
            if (add) {
                emit SwapTargetAdded(target);
            } else {
                emit SwapTargetRemoved(target);
            }
        }
        /// @dev method to withdraw ERC20 tokens (from the fees)
        /// @param token address of the token to withdraw
        /// @param to address that's receiving the tokens
        /// @param amount amount of tokens to withdraw
        function withdrawToken(
            address token,
            address to,
            uint256 amount
        ) external onlyOwner {
            require(to != address(0), "ZERO_ADDRESS");
            SafeTransferLib.safeTransfer(ERC20(token), to, amount);
            emit TokenWithdrawn(token, to, amount);
        }
        /// @dev method to withdraw ETH (from the fees)
        /// @param to address that's receiving the ETH
        /// @param amount amount of ETH to withdraw
        function withdrawEth(address to, uint256 amount) external onlyOwner {
            require(to != address(0), "ZERO_ADDRESS");
            SafeTransferLib.safeTransferETH(to, amount);
            emit EthWithdrawn(to, amount);
        }
        /// @dev Transfers ownership of the contract to a new account (`newOwner`).
        /// @param newOwner address of the new owner
        /// Can only be called by the current owner.
        function transferOwnership(address newOwner) external virtual onlyOwner {
            require(newOwner != address(0), "ZERO_ADDRESS");
            require(newOwner != owner, "SAME_OWNER");
            address previousOwner = owner;
            owner = newOwner;
            emit OwnerChanged(newOwner, previousOwner);
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    import {ERC20} from "../tokens/ERC20.sol";
    /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
    /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol)
    /// @author Modified from Gnosis (https://github.com/gnosis/gp-v2-contracts/blob/main/src/contracts/libraries/GPv2SafeERC20.sol)
    /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
    library SafeTransferLib {
        /*///////////////////////////////////////////////////////////////
                                ETH OPERATIONS
        //////////////////////////////////////////////////////////////*/
        function safeTransferETH(address to, uint256 amount) internal {
            bool callStatus;
            assembly {
                // Transfer the ETH and store if it succeeded or not.
                callStatus := call(gas(), to, amount, 0, 0, 0, 0)
            }
            require(callStatus, "ETH_TRANSFER_FAILED");
        }
        /*///////////////////////////////////////////////////////////////
                               ERC20 OPERATIONS
        //////////////////////////////////////////////////////////////*/
        function safeTransferFrom(
            ERC20 token,
            address from,
            address to,
            uint256 amount
        ) internal {
            bool callStatus;
            assembly {
                // Get a pointer to some free memory.
                let freeMemoryPointer := mload(0x40)
                // Write the abi-encoded calldata to memory piece by piece:
                mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
                mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "from" argument.
                mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
                mstore(add(freeMemoryPointer, 68), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
                // Call the token and store if it succeeded or not.
                // We use 100 because the calldata length is 4 + 32 * 3.
                callStatus := call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)
            }
            require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FROM_FAILED");
        }
        function safeTransfer(
            ERC20 token,
            address to,
            uint256 amount
        ) internal {
            bool callStatus;
            assembly {
                // Get a pointer to some free memory.
                let freeMemoryPointer := mload(0x40)
                // Write the abi-encoded calldata to memory piece by piece:
                mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
                mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
                mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
                // Call the token and store if it succeeded or not.
                // We use 68 because the calldata length is 4 + 32 * 2.
                callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
            }
            require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FAILED");
        }
        function safeApprove(
            ERC20 token,
            address to,
            uint256 amount
        ) internal {
            bool callStatus;
            assembly {
                // Get a pointer to some free memory.
                let freeMemoryPointer := mload(0x40)
                // Write the abi-encoded calldata to memory piece by piece:
                mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
                mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
                mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
                // Call the token and store if it succeeded or not.
                // We use 68 because the calldata length is 4 + 32 * 2.
                callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
            }
            require(didLastOptionalReturnCallSucceed(callStatus), "APPROVE_FAILED");
        }
        /*///////////////////////////////////////////////////////////////
                             INTERNAL HELPER LOGIC
        //////////////////////////////////////////////////////////////*/
        function didLastOptionalReturnCallSucceed(bool callStatus) private pure returns (bool success) {
            assembly {
                // Get how many bytes the call returned.
                let returnDataSize := returndatasize()
                // If the call reverted:
                if iszero(callStatus) {
                    // Copy the revert message into memory.
                    returndatacopy(0, 0, returnDataSize)
                    // Revert with the same message.
                    revert(0, returnDataSize)
                }
                switch returnDataSize
                case 32 {
                    // Copy the return data into memory.
                    returndatacopy(0, 0, returnDataSize)
                    // Set success to whether it returned true.
                    success := iszero(iszero(mload(0)))
                }
                case 0 {
                    // There was no return data.
                    success := 1
                }
                default {
                    // It returned some malformed input.
                    success := 0
                }
            }
        }
    }
    // 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/Rari-Capital/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
        //////////////////////////////////////////////////////////////*/
        bytes32 public constant PERMIT_TYPEHASH =
            keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
        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 {
                bytes32 digest = keccak256(
                    abi.encodePacked(
                        "\\x19\\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                    )
                );
                address recoveredAddress = ecrecover(digest, 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);
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity =0.8.11;
    import "@rari-capital/solmate/src/utils/SafeTransferLib.sol";
    import "@rari-capital/solmate/src/tokens/ERC20.sol";
    import "../libraries/PermitHelper.sol";
    /// @title Rainbow base aggregator contract
    contract BaseAggregator {
        /// @dev Used to prevent re-entrancy
        uint256 internal status;
        /// @dev Set of allowed swapTargets.
        mapping(address => bool) public swapTargets;
        /// @dev modifier that prevents reentrancy attacks on specific methods
        modifier nonReentrant() {
            // On the first call to nonReentrant, status will be 1
            require(status != 2, "NON_REENTRANT");
            // Any calls to nonReentrant after this point will fail
            status = 2;
            _;
            // By storing the original value once again, a refund is triggered (see
            // https://eips.ethereum.org/EIPS/eip-2200)
            status = 1;
        }
        /// @dev modifier that ensures only approved targets can be called
        modifier onlyApprovedTarget(address target) {
            require(swapTargets[target], "TARGET_NOT_AUTH");
            _;
        }
        /** EXTERNAL **/
        /// @param buyTokenAddress the address of token that the user should receive
        /// @param target the address of the aggregator contract that will exec the swap
        /// @param swapCallData the calldata that will be passed to the aggregator contract
        /// @param feeAmount the amount of ETH that we will take as a fee
        function fillQuoteEthToToken(
            address buyTokenAddress,
            address payable target,
            bytes calldata swapCallData,
            uint256 feeAmount
        ) external payable nonReentrant onlyApprovedTarget(target) {
            // 1 - Get the initial balances
            uint256 initialTokenBalance = ERC20(buyTokenAddress).balanceOf(
                address(this)
            );
            uint256 initialEthAmount = address(this).balance - msg.value;
            uint256 sellAmount = msg.value - feeAmount;
            // 2 - Call the encoded swap function call on the contract at `target`,
            // passing along any ETH attached to this function call to cover protocol fees
            // minus our fees, which are kept in this contract
            (bool success, bytes memory res) = target.call{value: sellAmount}(
                swapCallData
            );
            // Get the revert message of the call and revert with it if the call failed
            if (!success) {
                assembly {
                    let returndata_size := mload(res)
                    revert(add(32, res), returndata_size)
                }
            }
            // 3 - Make sure we received the tokens
            {
                uint256 finalTokenBalance = ERC20(buyTokenAddress).balanceOf(
                    address(this)
                );
                require(initialTokenBalance < finalTokenBalance, "NO_TOKENS");
            }
            // 4 - Send the received tokens back to the user
            SafeTransferLib.safeTransfer(
                ERC20(buyTokenAddress),
                msg.sender,
                ERC20(buyTokenAddress).balanceOf(address(this)) -
                    initialTokenBalance
            );
            // 5 - Return the remaining ETH to the user (if any)
            {
                uint256 finalEthAmount = address(this).balance - feeAmount;
                if (finalEthAmount > initialEthAmount) {
                    SafeTransferLib.safeTransferETH(
                        msg.sender,
                        finalEthAmount - initialEthAmount
                    );
                }
            }
        }
        /// @param sellTokenAddress the address of token that the user is selling
        /// @param buyTokenAddress the address of token that the user should receive
        /// @param target the address of the aggregator contract that will exec the swap
        /// @param swapCallData the calldata that will be passed to the aggregator contract
        /// @param sellAmount the amount of tokens that the user is selling
        /// @param feeAmount the amount of the tokens to sell that we will take as a fee
        function fillQuoteTokenToToken(
            address sellTokenAddress,
            address buyTokenAddress,
            address payable target,
            bytes calldata swapCallData,
            uint256 sellAmount,
            uint256 feeAmount
        ) external payable nonReentrant onlyApprovedTarget(target) {
            _fillQuoteTokenToToken(
                sellTokenAddress,
                buyTokenAddress,
                target,
                swapCallData,
                sellAmount,
                feeAmount
            );
        }
        /// @dev method that executes ERC20 to ERC20 token swaps with the ability to take a fee from the input
        // and accepts a signature to use permit, so the user doesn't have to make an previous approval transaction
        /// @param sellTokenAddress the address of token that the user is selling
        /// @param buyTokenAddress the address of token that the user should receive
        /// @param target the address of the aggregator contract that will exec the swap
        /// @param swapCallData the calldata that will be passed to the aggregator contract
        /// @param sellAmount the amount of tokens that the user is selling
        /// @param feeAmount the amount of the tokens to sell that we will take as a fee
        /// @param permitData struct containing the value, nonce, deadline, v, r and s values of the permit data
        function fillQuoteTokenToTokenWithPermit(
            address sellTokenAddress,
            address buyTokenAddress,
            address payable target,
            bytes calldata swapCallData,
            uint256 sellAmount,
            uint256 feeAmount,
            PermitHelper.Permit calldata permitData
        ) external payable nonReentrant onlyApprovedTarget(target) {
            // 1 - Apply permit
            PermitHelper.permit(
                permitData,
                sellTokenAddress,
                msg.sender,
                address(this)
            );
            //2 - Call fillQuoteTokenToToken
            _fillQuoteTokenToToken(
                sellTokenAddress,
                buyTokenAddress,
                target,
                swapCallData,
                sellAmount,
                feeAmount
            );
        }
        /// @dev method that executes ERC20 to ETH token swaps with the ability to take a fee from the output
        /// @param sellTokenAddress the address of token that the user is selling
        /// @param target the address of the aggregator contract that will exec the swap
        /// @param swapCallData the calldata that will be passed to the aggregator contract
        /// @param sellAmount the amount of tokens that the user is selling
        /// @param feePercentageBasisPoints the amount of ETH that we will take as a fee in 1e18 basis points (basis points with 4 decimals plus 14 extra decimals of precision)
        function fillQuoteTokenToEth(
            address sellTokenAddress,
            address payable target,
            bytes calldata swapCallData,
            uint256 sellAmount,
            uint256 feePercentageBasisPoints
        ) external payable nonReentrant onlyApprovedTarget(target) {
            _fillQuoteTokenToEth(
                sellTokenAddress,
                target,
                swapCallData,
                sellAmount,
                feePercentageBasisPoints
            );
        }
        /// @dev method that executes ERC20 to ETH token swaps with the ability to take a fee from the output
        // and accepts a signature to use permit, so the user doesn't have to make an previous approval transaction
        /// @param sellTokenAddress the address of token that the user is selling
        /// @param target the address of the aggregator contract that will exec the swap
        /// @param swapCallData the calldata that will be passed to the aggregator contract
        /// @param sellAmount the amount of tokens that the user is selling
        /// @param feePercentageBasisPoints the amount of ETH that we will take as a fee in 1e18 basis points (basis points with 4 decimals plus 14 extra decimals of precision)
        /// @param permitData struct containing the amount, nonce, deadline, v, r and s values of the permit data
        function fillQuoteTokenToEthWithPermit(
            address sellTokenAddress,
            address payable target,
            bytes calldata swapCallData,
            uint256 sellAmount,
            uint256 feePercentageBasisPoints,
            PermitHelper.Permit calldata permitData
        ) external payable nonReentrant onlyApprovedTarget(target) {
            // 1 - Apply permit
            PermitHelper.permit(
                permitData,
                sellTokenAddress,
                msg.sender,
                address(this)
            );
            // 2 - call fillQuoteTokenToEth
            _fillQuoteTokenToEth(
                sellTokenAddress,
                target,
                swapCallData,
                sellAmount,
                feePercentageBasisPoints
            );
        }
        /** INTERNAL **/
        /// @dev internal method that executes ERC20 to ETH token swaps with the ability to take a fee from the output
        function _fillQuoteTokenToEth(
            address sellTokenAddress,
            address payable target,
            bytes calldata swapCallData,
            uint256 sellAmount,
            uint256 feePercentageBasisPoints
        ) internal {
            // 1 - Get the initial ETH amount
            uint256 initialEthAmount = address(this).balance - msg.value;
            // 2 - Move the tokens to this contract
            // NOTE: This implicitly assumes that the the necessary approvals have been granted
            // from msg.sender to the BaseAggregator
            SafeTransferLib.safeTransferFrom(
                ERC20(sellTokenAddress),
                msg.sender,
                address(this),
                sellAmount
            );
            // 3 - Approve the aggregator's contract to swap the tokens
            SafeTransferLib.safeApprove(
                ERC20(sellTokenAddress),
                target,
                sellAmount
            );
            // 4 - Call the encoded swap function call on the contract at `target`,
            // passing along any ETH attached to this function call to cover protocol fees.
            (bool success, bytes memory res) = target.call{value: msg.value}(
                swapCallData
            );
            // Get the revert message of the call and revert with it if the call failed
            if (!success) {
                assembly {
                    let returndata_size := mload(res)
                    revert(add(32, res), returndata_size)
                }
            }
            // 5 - Check that the tokens were fully spent during the swap
            uint256 allowance = ERC20(sellTokenAddress).allowance(
                address(this),
                target
            );
            require(allowance == 0, "ALLOWANCE_NOT_ZERO");
            // 6 - Subtract the fees and send the rest to the user
            // Fees will be held in this contract
            uint256 finalEthAmount = address(this).balance;
            uint256 ethDiff = finalEthAmount - initialEthAmount;
            require(ethDiff > 0, "NO_ETH_BACK");
            if (feePercentageBasisPoints > 0) {
                uint256 fees = (ethDiff * feePercentageBasisPoints) / 1e18;
                uint256 amountMinusFees = ethDiff - fees;
                SafeTransferLib.safeTransferETH(msg.sender, amountMinusFees);
                // when there's no fee, 1inch sends the funds directly to the user
                // we check to prevent sending 0 ETH in that case
            } else if (ethDiff > 0) {
                SafeTransferLib.safeTransferETH(msg.sender, ethDiff);
            }
        }
        /// @dev internal method that executes ERC20 to ERC20 token swaps with the ability to take a fee from the input
        function _fillQuoteTokenToToken(
            address sellTokenAddress,
            address buyTokenAddress,
            address payable target,
            bytes calldata swapCallData,
            uint256 sellAmount,
            uint256 feeAmount
        ) internal {
            // 1 - Get the initial output token balance
            uint256 initialOutputTokenAmount = ERC20(buyTokenAddress).balanceOf(
                address(this)
            );
            // 2 - Move the tokens to this contract (which includes our fees)
            // NOTE: This implicitly assumes that the the necessary approvals have been granted
            // from msg.sender to the BaseAggregator
            SafeTransferLib.safeTransferFrom(
                ERC20(sellTokenAddress),
                msg.sender,
                address(this),
                sellAmount
            );
            // 3 - Approve the aggregator's contract to swap the tokens if needed
            SafeTransferLib.safeApprove(
                ERC20(sellTokenAddress),
                target,
                sellAmount - feeAmount
            );
            // 4 - Call the encoded swap function call on the contract at `target`,
            // passing along any ETH attached to this function call to cover protocol fees.
            (bool success, bytes memory res) = target.call{value: msg.value}(
                swapCallData
            );
            // Get the revert message of the call and revert with it if the call failed
            if (!success) {
                assembly {
                    let returndata_size := mload(res)
                    revert(add(32, res), returndata_size)
                }
            }
            // 5 - Check that the tokens were fully spent during the swap
            uint256 allowance = ERC20(sellTokenAddress).allowance(
                address(this),
                target
            );
            require(allowance == 0, "ALLOWANCE_NOT_ZERO");
            // 6 - Make sure we received the tokens
            uint256 finalOutputTokenAmount = ERC20(buyTokenAddress).balanceOf(
                address(this)
            );
            require(initialOutputTokenAmount < finalOutputTokenAmount, "NO_TOKENS");
            // 7 - Send tokens to the user
            SafeTransferLib.safeTransfer(
                ERC20(buyTokenAddress),
                msg.sender,
                finalOutputTokenAmount - initialOutputTokenAmount
            );
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity =0.8.11;
    import "../interfaces/IERC2612.sol";
    import "../interfaces/IDAI.sol";
    /// @title PermitHelper
    /// @dev Helper methods for using ERC20 Permit (ERC2612 or DAI/CHAI like)
    library PermitHelper {
        struct Permit {
            uint256 value;
            uint256 nonce;
            uint256 deadline;
            bool isDaiStylePermit;
            uint8 v;
            bytes32 r;
            bytes32 s;
        }
        /// @dev permit method helper that will handle both known implementations
        // DAI vs ERC2612 tokens
        /// @param permitData bytes containing the encoded permit signature
        /// @param tokenAddress address of the token that will be permitted
        /// @param holder address that holds the tokens to be permitted
        /// @param spender address that will be permitted to spend the tokens
        function permit(
            Permit memory permitData,
            address tokenAddress,
            address holder,
            address spender
        ) internal {
            if (permitData.isDaiStylePermit) {
                IDAI(tokenAddress).permit(
                    holder,
                    spender,
                    permitData.nonce,
                    permitData.deadline,
                    true,
                    permitData.v,
                    permitData.r,
                    permitData.s
                );
            } else {
                IERC2612(tokenAddress).permit(
                    holder,
                    spender,
                    permitData.value,
                    permitData.deadline,
                    permitData.v,
                    permitData.r,
                    permitData.s
                );
            }
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity =0.8.11;
    import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
    import "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";
    interface IERC2612 is IERC20Metadata, IERC20Permit {
        function _nonces(address owner) external view returns (uint256);
        function version() external view returns (string memory);
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity =0.8.11;
    import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
    import "@uniswap/v3-periphery/contracts/interfaces/external/IERC20PermitAllowed.sol";
    interface IDAI is IERC20Metadata, IERC20PermitAllowed {
        function DOMAIN_SEPARATOR() external view returns (bytes32);
        function PERMIT_TYPEHASH() external pure returns (bytes32);
        function nonces(address owner) external view returns (uint256);
        function version() external view returns (string memory);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
    pragma solidity ^0.8.0;
    import "../IERC20.sol";
    /**
     * @dev Interface for the optional metadata functions from the ERC20 standard.
     *
     * _Available since v4.1._
     */
    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);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
     * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
     *
     * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
     * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
     * need to send a transaction, and thus is not required to hold Ether at all.
     */
    interface IERC20Permit {
        /**
         * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
         * given ``owner``'s signed approval.
         *
         * IMPORTANT: The same issues {IERC20-approve} has related to transaction
         * ordering also apply here.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `deadline` must be a timestamp in the future.
         * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
         * over the EIP712-formatted function arguments.
         * - the signature must use ``owner``'s current nonce (see {nonces}).
         *
         * For more information on the signature format, see the
         * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
         * section].
         */
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
        /**
         * @dev Returns the current nonce for `owner`. This value must be
         * included whenever a signature is generated for {permit}.
         *
         * Every successful call to {permit} increases ``owner``'s nonce by one. This
         * prevents a signature from being used multiple times.
         */
        function nonces(address owner) external view returns (uint256);
        /**
         * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view returns (bytes32);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `recipient`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address recipient, uint256 amount) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `sender` to `recipient` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) external returns (bool);
        /**
         * @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);
    }
    // SPDX-License-Identifier: GPL-2.0-or-later
    pragma solidity >=0.5.0;
    /// @title Interface for permit
    /// @notice Interface used by DAI/CHAI for permit
    interface IERC20PermitAllowed {
        /// @notice Approve the spender to spend some tokens via the holder signature
        /// @dev This is the permit interface used by DAI and CHAI
        /// @param holder The address of the token holder, the token owner
        /// @param spender The address of the token spender
        /// @param nonce The holder's nonce, increases at each call to permit
        /// @param expiry The timestamp at which the permit is no longer valid
        /// @param allowed Boolean that sets approval amount, true for type(uint256).max and false for 0
        /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
        /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
        /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
        function permit(
            address holder,
            address spender,
            uint256 nonce,
            uint256 expiry,
            bool allowed,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
    }