Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xe95F6EAeaE1E4d650576Af600b33D9F7e5f9f7fd
Balance 0 ETH
Nonce 1
Code Size 7782 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

7782 bytes
0x6080604052600436106100eb575f3560e01c8063710e5d2f11610089578063af48bcb911610058578063af48bcb9146102c8578063d81a0553146102f2578063deab3ebd1461031c578063ee7d72b414610346576100fa565b8063710e5d2f1461022257806372a1d3c51461024a578063868ec067146102745780638ef261291461029e576100fa565b8063249bf7e5116100c5578063249bf7e51461017857806331ac9920146101a257806332ec5d43146101ca57806357eb8db4146101f2576100fa565b8063040d41c2146100fe57806306af20da146101265780631632bc2d1461014e576100fa565b366100fa576100f861036e565b005b5f80fd5b348015610109575f80fd5b50610124600480360381019061011f919061140d565b6103d5565b005b348015610131575f80fd5b5061014c6004803603810190610147919061162e565b6103e7565b005b348015610159575f80fd5b50610162610465565b60405161016f9190611697565b60405180910390f35b348015610183575f80fd5b5061018c61046b565b6040516101999190611697565b60405180910390f35b3480156101ad575f80fd5b506101c860048036038101906101c3919061140d565b610470565b005b3480156101d5575f80fd5b506101f060048036038101906101eb919061140d565b6104fa565b005b61020c60048036038101906102079190611767565b61050c565b6040516102199190611697565b60405180910390f35b34801561022d575f80fd5b506102486004803603810190610243919061140d565b610a1b565b005b348015610255575f80fd5b5061025e610a69565b60405161026b9190611697565b60405180910390f35b34801561027f575f80fd5b50610288610a6f565b6040516102959190611697565b60405180910390f35b3480156102a9575f80fd5b506102b2610a75565b6040516102bf9190611697565b60405180910390f35b3480156102d3575f80fd5b506102dc610a7b565b6040516102e99190611697565b60405180910390f35b3480156102fd575f80fd5b50610306610a81565b60405161031391906117e9565b60405180910390f35b348015610327575f80fd5b50610330610aa5565b60405161033d9190611697565b60405180910390f35b348015610351575f80fd5b5061036c6004803603810190610367919061140d565b610aab565b005b3273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16036103d3576040517f1b10b0f900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b6103dd610abc565b8060048190555050565b6103ef610abc565b6103f881610b43565b5f825190505f5b8181101561045f575f61042c85838151811061041e5761041d611802565b5b602002602001015130610bab565b905061045385838151811061044457610443611802565b5b60200260200101518583610c8e565b816001019150506103ff565b50505050565b60065481565b5f5481565b610478610abc565b6127108111156104b4576040517fa91cddf700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001548111156104f0576040517fbb39a6cf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060028190555050565b610502610abc565b8060038190555050565b5f61051684610e38565b7f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff16845f01602081019061055f919061182f565b73ffffffffffffffffffffffffffffffffffffffff16036105bc578360a0013534146105b7576040517f3612305100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61065f565b5f34146105f5576040517f3612305100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61065e3385604001602081019061060c919061185a565b8660a00135876101e001602081019061062591906118ba565b885f016020810190610637919061182f565b73ffffffffffffffffffffffffffffffffffffffff166110b090949392919063ffffffff16565b5b61066b848685856110db565b90505f8460e0013590505f856101000135866101a0013561068c9190611912565b8311156106b357856101000135836106a49190611945565b925085610100013590506106e2565b856101a001358311156106e057856101a00135836106d19190611945565b9050856101a0013592506106e1565b5b5b5f808760c0013585101580156107015750876101a001358860c0013510155b1561073457876101a001358860c0013561071b9190611945565b91508760c001358561072d9190611945565b9050610773565b848860c0013511801561074b5750876101a0013585115b1561076b57876101a00135856107619190611945565b91505f9050610772565b5f91505f90505b5b5f805f8414158061078457505f8314155b15610902575f6127108b61012001358661079e9190611978565b6107a891906119e6565b90505f6127108c6101600135866107bf9190611978565b6107c991906119e6565b905080826107d79190611912565b93505f82876107e69190611945565b90505f6127108e6101800135886107fd9190611978565b61080791906119e6565b905080826108159190611912565b945084866108239190611912565b8b61082e9190611945565b9a505f86146108c0575f73ffffffffffffffffffffffffffffffffffffffff168e6080016020810190610861919061185a565b73ffffffffffffffffffffffffffffffffffffffff16146108b0576108ab8e6020016020810190610892919061182f565b8f60800160208101906108a5919061185a565b88610c8e565b6108bf565b85856108bc9190611912565b94505b5b6108fd8e60200160208101906108d6919061182f565b7f0000000000000000000000003cffef055725974e32a660a617fc999b67e9196e87610c8e565b505050505b896101c00135871015610941576040517f77dbabbd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109708a6020016020810190610957919061182f565b8b606001602081019061096a919061185a565b89610c8e565b3373ffffffffffffffffffffffffffffffffffffffff168a61020001357fc3abfc5221d6a68c4aea21a86d5ec3f135dd55a8b3949268207ba6ddc1a4dce08c60600160208101906109c1919061185a565b8d5f0160208101906109d3919061182f565b8e60a001358f60200160208101906109eb919061182f565b8d8d8d8b8b604051610a0599989796959493929190611a91565b60405180910390a3505050505050949350505050565b610a23610abc565b612710811115610a5f576040517f5326d40400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060018190555050565b60035481565b60045481565b60055481565b60025481565b7f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b60015481565b610ab3610abc565b805f8190555050565b7f0000000000000000000000003cffef055725974e32a660a617fc999b67e9196e73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610b41576040517ffcb6435400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610ba8576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b5f7f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610c0d575f8231905080915050610c88565b8273ffffffffffffffffffffffffffffffffffffffff166370a08231836040518263ffffffff1660e01b8152600401610c4691906117e9565b602060405180830381865afa158015610c61573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c859190611b30565b90505b92915050565b5f8114610e01577f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610dd0575f47905081811015610d26576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8373ffffffffffffffffffffffffffffffffffffffff16835f5490604051610d4e90611b88565b5f60405180830381858888f193505050503d805f8114610d89576040519150601f19603f3d011682016040523d82523d5f602084013e610d8e565b606091505b5050905080610dc9576040517ff4b3b1bc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050610dfc565b610dfb82828573ffffffffffffffffffffffffffffffffffffffff166111a39092919063ffffffff16565b5b610e33565b6040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b6127106001548260c00135610e4d9190611978565b610e5791906119e6565b8161010001351115610e95576040517f0b7c929700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6127106002548260c00135610eaa9190611978565b610eb491906119e6565b8161010001351015610ef2576040517f849a79a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003548161012001351115610f33576040517f7d7ebb2200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005548161014001351015610f74576040517f8060d88400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004548161016001351115610fb5576040517f6f18e92c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006548161018001351015610ff6576040517f4116e5e900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61101181604001602081019061100c919061185a565b610b43565b61102c816060016020810190611027919061185a565b610b43565b5f816101c001350361106a576040517f334ee9a100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060c00135816101c0013511156110ad576040517f443a26cb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b80156110c7576110c2858585856111f1565b6110d4565b6110d3858585856112e1565b5b5050505050565b5f808560200160208101906110f0919061182f565b90505f6110fd8230610bab565b90508573ffffffffffffffffffffffffffffffffffffffff1663c0dcf8043487878b6020016020810190611131919061182f565b6040518563ffffffff1660e01b815260040161114f93929190611e00565b5f604051808303818588803b158015611166575f80fd5b505af1158015611178573d5f803e3d5ffd5b50505050505f6111888330610bab565b905081816111969190611945565b9350505050949350505050565b6111b68363a9059cbb60e01b8484611379565b6111ec576040517ffb7f507900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b73ffffffffffffffffffffffffffffffffffffffff8016811115611241576040517f8112e11900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6336c7851660e01b90505f6040518281528560048201528460248201528360448201528660648201525f806084835f6e22d473030f116ddee9f6b43ac78ba35af1915081156112a1575f6e22d473030f116ddee9f6b43ac78ba33b1191505b50806112d9576040517ff405907100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b5f6323b872dd60e01b90505f60405182815285600482015284602482015283604482015260205f6064835f8b5af191508115611339573d5f81146113305760015f5114601f3d11169250611337565b5f883b1192505b505b5080611371576040517ff405907100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b5f60405184815283600482015282602482015260205f6044835f8a5af1915081156113c0573d5f81146113b75760015f5114601f3d111692506113be565b5f873b1192505b505b50949350505050565b5f604051905090565b5f80fd5b5f80fd5b5f819050919050565b6113ec816113da565b81146113f6575f80fd5b50565b5f81359050611407816113e3565b92915050565b5f60208284031215611422576114216113d2565b5b5f61142f848285016113f9565b91505092915050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6114828261143c565b810181811067ffffffffffffffff821117156114a1576114a061144c565b5b80604052505050565b5f6114b36113c9565b90506114bf8282611479565b919050565b5f67ffffffffffffffff8211156114de576114dd61144c565b5b602082029050602081019050919050565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61151c826114f3565b9050919050565b5f61152d82611512565b9050919050565b61153d81611523565b8114611547575f80fd5b50565b5f8135905061155881611534565b92915050565b5f61157061156b846114c4565b6114aa565b90508083825260208201905060208402830185811115611593576115926114ef565b5b835b818110156115bc57806115a8888261154a565b845260208401935050602081019050611595565b5050509392505050565b5f82601f8301126115da576115d9611438565b5b81356115ea84826020860161155e565b91505092915050565b5f6115fd826114f3565b9050919050565b61160d816115f3565b8114611617575f80fd5b50565b5f8135905061162881611604565b92915050565b5f8060408385031215611644576116436113d2565b5b5f83013567ffffffffffffffff811115611661576116606113d6565b5b61166d858286016115c6565b925050602061167e8582860161161a565b9150509250929050565b611691816113da565b82525050565b5f6020820190506116aa5f830184611688565b92915050565b5f6116ba82611512565b9050919050565b6116ca816116b0565b81146116d4575f80fd5b50565b5f813590506116e5816116c1565b92915050565b5f80fd5b5f6102208284031215611705576117046116eb565b5b81905092915050565b5f80fd5b5f8083601f84011261172757611726611438565b5b8235905067ffffffffffffffff8111156117445761174361170e565b5b6020830191508360208202830111156117605761175f6114ef565b5b9250929050565b5f805f8061026085870312156117805761177f6113d2565b5b5f61178d878288016116d7565b945050602061179e878288016116ef565b93505061024085013567ffffffffffffffff8111156117c0576117bf6113d6565b5b6117cc87828801611712565b925092505092959194509250565b6117e381611512565b82525050565b5f6020820190506117fc5f8301846117da565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f60208284031215611844576118436113d2565b5b5f6118518482850161154a565b91505092915050565b5f6020828403121561186f5761186e6113d2565b5b5f61187c8482850161161a565b91505092915050565b5f8115159050919050565b61189981611885565b81146118a3575f80fd5b50565b5f813590506118b481611890565b92915050565b5f602082840312156118cf576118ce6113d2565b5b5f6118dc848285016118a6565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61191c826113da565b9150611927836113da565b925082820190508082111561193f5761193e6118e5565b5b92915050565b5f61194f826113da565b915061195a836113da565b9250828203905081811115611972576119716118e5565b5b92915050565b5f611982826113da565b915061198d836113da565b925082820261199b816113da565b915082820484148315176119b2576119b16118e5565b5b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f6119f0826113da565b91506119fb836113da565b925082611a0b57611a0a6119b9565b5b828204905092915050565b5f819050919050565b5f611a39611a34611a2f846114f3565b611a16565b6114f3565b9050919050565b5f611a4a82611a1f565b9050919050565b5f611a5b82611a40565b9050919050565b611a6b81611a51565b82525050565b5f611a7b82611a40565b9050919050565b611a8b81611a71565b82525050565b5f61012082019050611aa55f83018c611a62565b611ab2602083018b611a82565b611abf604083018a611688565b611acc6060830189611a82565b611ad96080830188611688565b611ae660a0830187611688565b611af360c0830186611688565b611b0060e0830185611688565b611b0e610100830184611688565b9a9950505050505050505050565b5f81519050611b2a816113e3565b92915050565b5f60208284031215611b4557611b446113d2565b5b5f611b5284828501611b1c565b91505092915050565b5f81905092915050565b50565b5f611b735f83611b5b565b9150611b7e82611b65565b5f82019050919050565b5f611b9282611b68565b9150819050919050565b5f82825260208201905092915050565b5f819050919050565b611bbe81611512565b8114611bc8575f80fd5b50565b5f81359050611bd981611bb5565b92915050565b5f611bed6020840184611bcb565b905092915050565b611bfe81611512565b82525050565b5f611c1260208401846113f9565b905092915050565b611c23816113da565b82525050565b5f80fd5b5f80fd5b5f80fd5b5f8083356001602003843603038112611c5157611c50611c31565b5b83810192508235915060208301925067ffffffffffffffff821115611c7957611c78611c29565b5b600182023603831315611c8f57611c8e611c2d565b5b509250929050565b5f82825260208201905092915050565b828183375f83830152505050565b5f611cc08385611c97565b9350611ccd838584611ca7565b611cd68361143c565b840190509392505050565b5f60608301611cf25f840184611bdf565b611cfe5f860182611bf5565b50611d0c6020840184611c04565b611d196020860182611c1a565b50611d276040840184611c35565b8583036040870152611d3a838284611cb5565b925050508091505092915050565b5f611d538383611ce1565b905092915050565b5f82356001606003833603038112611d7657611d75611c31565b5b82810191505092915050565b5f602082019050919050565b5f611d998385611b9c565b935083602084028501611dab84611bac565b805f5b87811015611dee578484038952611dc58284611d5b565b611dcf8582611d48565b9450611dda83611d82565b925060208a01995050600181019050611dae565b50829750879450505050509392505050565b5f6040820190508181035f830152611e19818587611d8e565b9050611e286020830184611a82565b94935050505056fea2646970667358221220db503b3dc91ddabf6167e94fff144b7f8996d31893e96c3683e3ae47c3b8896b64736f6c63430008180033

Verified Source Code Full Match

Compiler: v0.8.24+commit.e11b9ed9 EVM: cancun Optimization: No
GluexRouter.sol 449 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {EthReceiver} from "./utils/EthReceiver.sol";
import {Interaction} from "./base/RouterStructs.sol";
import {IExecutor} from "./interfaces/IExecutor.sol";
import {IERC20} from "./interfaces/IERC20.sol";
import {SafeERC20} from "./lib/SafeERC20.sol";

/**
 * @title GluexRouter
 * @notice A versatile router contract that enables the execution of on-chain interactions using the `IExecutor` interface.
 * @dev This contract provides functionality for routing tokens through various interactions, collecting routing fees,
 *      and enforcing strict slippage and routing rules for optimal security and usability.
 */
contract GluexRouter is EthReceiver {
    using SafeERC20 for IERC20;

    // Errors
    error InsufficientBalance();
    error NativeTransferFailed();
    error OnlyGlueTreasury();
    error ZeroAddress();
    error NegativeSlippageLimit();
    error RoutingFeeTooHigh();
    error RoutingFeeTooLow();
    error PartnerSurplusShareTooHigh();
    error ProtocolSurplusShareTooLow();
    error PartnerSlippageShareTooHigh();
    error ProtocolSlippageShareTooLow();
    error InvalidSlippage();
    error SlippageLimitTooLarge();
    error InvalidNativeTokenInputAmount();
    error MaxFeeLimitExceeded();
    error MinFeeLimitExceeded();
    error MinFeeTooHigh();

    // Events
    /**
     * @notice Emitted when a routing operation is completed.
     * @param uniquePID The unique identifier for the partner.
     * @param userAddress The address of the user who initiated the route.
     * @param outputReceiver The address of the receiver of the output token.
     * @param inputToken The ERC20 token used as input.
     * @param inputAmount The amount of input token used for routing.
     * @param outputToken The ERC20 token received as output.
     * @param finalOutputAmount The actual output amount received after routing.
     * @param partnerFee The fee charged for the partner.
     * @param routingFee The fee charged for the routing operation.
     * @param partnerShare The share of surplus and slippage given to the partner.
     * @param protocolShare The share of surplus and slippage given to the GlueX protocol.
     */
    event Routed(
        bytes32 indexed uniquePID,
        address indexed userAddress,
        address outputReceiver,
        IERC20 inputToken,
        uint256 inputAmount,
        IERC20 outputToken,
        uint256 finalOutputAmount,
        uint256 partnerFee,
        uint256 routingFee,
        uint256 partnerShare,
        uint256 protocolShare
    );

    // DataTypes
    /**
     * @dev A generic structure defining the parameters for a route.
     */
    struct RouteDescription {
        IERC20 inputToken; // Token used as input for the route
        IERC20 outputToken; // Token received as output from the route
        address payable inputReceiver; // Address to receive the input token
        address payable outputReceiver; // Address to receive the output token
        address payable partnerAddress; // Address of the partner receiving surplus share
        uint256 inputAmount; // Amount of input token
        uint256 outputAmount; // Optimizer output amount
        uint256 partnerFee; // Fee charged by the partner
        uint256 routingFee; // Fee charged for routing operation
        uint256 partnerSurplusShare; // Percentage (in bps) of surplus shared with the partner
        uint256 protocolSurplusShare; // Percentage (in bps) of surplus shared with GlueX
        uint256 partnerSlippageShare; // Percentage (in bps) of slippage shared with the partner
        uint256 protocolSlippageShare; // Percentage (in bps) of slippage shared with GlueX
        uint256 effectiveOutputAmount; // Effective output amount for the user
        uint256 minOutputAmount; // Minimum acceptable output amount
        bool isPermit2; // Whether to use Permit2 for token transfers
        bytes32 uniquePID; // Unique identifier for the partner
    }

    // Constants
    uint256 public _RAW_CALL_GAS_LIMIT = 5500;
    uint256 public _MAX_FEE = 15; // 15 bps (0.15%)
    uint256 public _MIN_FEE = 0; // 0 bps (0.00%)
    uint256 public _MAX_PARTNER_SURPLUS_SHARE_LIMIT = 5000; // 50% (5000 bps)
    uint256 public _MAX_PARTNER_SLIPPAGE_SHARE_LIMIT = 3300; // 33% (3300 bps)
    uint256 public _MIN_PROTOCOL_SURPLUS_SHARE_LIMIT = 5000; // 50% (5000 bps)
    uint256 public _MIN_PROTOCOL_SLIPPAGE_SHARE_LIMIT = 3000; // 30% (3000 bps)

    // State Variables
    address public immutable _nativeToken; // Address of the native token (e.g., Ether on Ethereum)
    address internal immutable _gluexTreasury; // Address of the GlueX treasury contract

    /**
     * @dev Initializes the contract with the treasury address and native token address.
     * @param gluexTreasury The address of the Glue treasury contract.
     * @param nativeToken The address of the native token.
     */
    constructor(address gluexTreasury, address nativeToken) {
        // Ensure the addresses are not zero
        checkZeroAddress(gluexTreasury);

        _gluexTreasury = gluexTreasury;
        _nativeToken = nativeToken;
    }

    /**
     * @dev Modifier to restrict access to treasury-only functions.
     */
    modifier onlyTreasury() {
        checkTreasury();
        _;
    }

    /**
     * @notice Verifies the caller is the Glue treasury.
     * @dev Reverts with `OnlyGlueTreasury` if the caller is not the treasury.
     */
    function checkTreasury() internal view {
        if (msg.sender != _gluexTreasury) revert OnlyGlueTreasury();
    }

    /**
     * @notice Verifies the given address is not zero.
     * @param addr The address to verify.
     * @dev Reverts with `ZeroAddress` if the address is zero.
     */
    function checkZeroAddress(address addr) internal pure {
        if (addr == address(0)) revert ZeroAddress();
    }

    /**
     * @notice Collects routing fees from specified tokens and transfers them to the given receiver.
     * @param feeTokens An array of ERC20 tokens from which fees will be collected.
     * @param receiver The address where collected fees will be transferred.
     */
    function collectFees(IERC20[] memory feeTokens, address payable receiver)
        external
        onlyTreasury
    {
        // Ensure the receiver address is valid
        checkZeroAddress(receiver);

        // Collect fees for each token
        uint256 len = feeTokens.length;
        for (uint256 i = 0; i < len; ) {
            uint256 feeBalance = uniBalanceOf(feeTokens[i], address(this));
            uniTransfer(feeTokens[i], receiver, feeBalance);

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Executes a route using the specified executor and interactions.
     * @param executor The executor contract that performs the interactions.
     * @param desc The route description containing input, output, and fee details.
     * @param interactions The interactions encoded for execution by the executor.
     * @return finalOutputAmount The final amount of output token received.
     * @dev Ensures strict validation of slippage, routing fees, and input/output parameters.
     */
    function swap(
        IExecutor executor,
        RouteDescription calldata desc,
        Interaction[] calldata interactions
    ) external payable returns (uint256 finalOutputAmount) {

        // Validate the route description
        validateSwap(desc);

        // Token transfer validation
        if (address(desc.inputToken) == _nativeToken) {
            if (msg.value != desc.inputAmount) revert InvalidNativeTokenInputAmount();
        } else {
            if (msg.value != 0) revert InvalidNativeTokenInputAmount();
            desc.inputToken.safeTransferFromUniversal(
                msg.sender,
                desc.inputReceiver,
                desc.inputAmount,
                desc.isPermit2
            );
        }

        // Execute the interactions using executor
        finalOutputAmount = executeInteractions(
            desc,
            executor,
            interactions
        );

        // Calculate final output amount
        uint256 partnerFee = desc.partnerFee;
        uint256 routingFee = 0;
        if (finalOutputAmount > desc.effectiveOutputAmount + desc.routingFee) {
            finalOutputAmount = finalOutputAmount - desc.routingFee;
            routingFee = desc.routingFee;
        } else if (finalOutputAmount > desc.effectiveOutputAmount) {
            routingFee = finalOutputAmount - desc.effectiveOutputAmount;
            finalOutputAmount = desc.effectiveOutputAmount;
        } else {
            finalOutputAmount = finalOutputAmount;
        }

        // Surplus and Slippage calculation
        uint256 surplus = 0;
        uint256 slippage = 0;
        if (finalOutputAmount >= desc.outputAmount && desc.outputAmount >= desc.effectiveOutputAmount) {
            surplus = desc.outputAmount - desc.effectiveOutputAmount;
            slippage = finalOutputAmount - desc.outputAmount;
        } else if (desc.outputAmount > finalOutputAmount && finalOutputAmount > desc.effectiveOutputAmount) {
            surplus = finalOutputAmount - desc.effectiveOutputAmount;
            slippage = 0;
        } else {
            surplus = 0;
            slippage = 0;
        }

        uint256 partnerShare = 0;
        uint256 protocolShare = 0;
        if (surplus != 0 || slippage != 0) {
            // Calculate and transfer partner surplus
            uint256 partnerSurplus = (surplus * desc.partnerSurplusShare) / 10000;
            uint256 partnerSlippage = (slippage * desc.partnerSlippageShare) / 10000;
            partnerShare = partnerSurplus + partnerSlippage;

            // Calculate and transfer routing surplus
            uint256 protocolSurplus = surplus - partnerSurplus;
            uint256 protocolSlippage = (slippage * desc.protocolSlippageShare) / 10000;
            protocolShare = protocolSurplus + protocolSlippage;

            finalOutputAmount -= (partnerShare + protocolShare);

            if (partnerShare != 0) {
                if (desc.partnerAddress != address(0)) {
                    uniTransfer(
                        desc.outputToken,
                        desc.partnerAddress,
                        partnerShare
                    );
                } else {
                    protocolShare += partnerShare; // If no partner address, add to protocol share
                }
            }

            uniTransfer(
                desc.outputToken,
                payable(_gluexTreasury),
                protocolShare
            );
        }

        // Ensure final output amount meets the minimum required
        if (finalOutputAmount < desc.minOutputAmount) revert NegativeSlippageLimit();

        // Transfer the final output amount to the output receiver
        uniTransfer(
            desc.outputToken,
            desc.outputReceiver,
            finalOutputAmount
        );

        emit Routed(
            desc.uniquePID,
            msg.sender,
            desc.outputReceiver,
            desc.inputToken,
            desc.inputAmount,
            desc.outputToken,
            finalOutputAmount,
            partnerFee,
            routingFee,
            partnerShare,
            protocolShare
        );
    }

    /**
     * @notice Validates the parameters of a swap operation.
     * @param desc The route description containing swap details.
     * @dev Ensures that routing fees, partner surplus shares, and slippage limits are within acceptable ranges.
     */
    function validateSwap(
        RouteDescription calldata desc
    ) internal view {
        // Validate routing fee
        if (desc.routingFee > (desc.outputAmount * _MAX_FEE) / 10000) revert RoutingFeeTooHigh();
        if (desc.routingFee < (desc.outputAmount * _MIN_FEE) / 10000) revert RoutingFeeTooLow();

        // Validate surplus sharing
        if (desc.partnerSurplusShare > _MAX_PARTNER_SURPLUS_SHARE_LIMIT) revert PartnerSurplusShareTooHigh();
        if (desc.protocolSurplusShare < _MIN_PROTOCOL_SURPLUS_SHARE_LIMIT) revert ProtocolSurplusShareTooLow();

        // Validate slippage sharing
        if (desc.partnerSlippageShare > _MAX_PARTNER_SLIPPAGE_SHARE_LIMIT) revert PartnerSlippageShareTooHigh();
        if (desc.protocolSlippageShare < _MIN_PROTOCOL_SLIPPAGE_SHARE_LIMIT) revert ProtocolSlippageShareTooLow();

        // Validate non-zero addresses
        checkZeroAddress(desc.inputReceiver);
        checkZeroAddress(desc.outputReceiver);

        // Validate route parameters
        if (desc.minOutputAmount == 0) revert InvalidSlippage();
        if (desc.minOutputAmount > desc.outputAmount) revert SlippageLimitTooLarge();
    }

    /**
     * @notice Executes the interactions defined in the route description using the specified executor.
     * @param desc The route description containing input, output, and interaction details.
     * @param executor The executor contract that will perform the interactions.
     * @param interactions The interactions to be executed.
     * @return finalOutputAmount The final amount of output token received after executing the interactions.
     */
    function executeInteractions(
        RouteDescription calldata desc,
        IExecutor executor,
        Interaction[] calldata interactions
    ) internal returns (uint256 finalOutputAmount) {
        // Execute interactions through the executor
        IERC20 outputToken = desc.outputToken;
        uint256 outputBalanceBefore = uniBalanceOf(outputToken, address(this));
        executor.executeRoute{value: msg.value}(
            interactions,
            desc.outputToken
        );
        uint256 outputBalanceAfter = uniBalanceOf(outputToken, address(this));

        finalOutputAmount = outputBalanceAfter - outputBalanceBefore;
    }

    /**
     * @notice Retrieves the balance of a specified token for a given account.
     * @param token The ERC20 token to check.
     * @param account The account address to query the balance for.
     * @return The balance of the token for the account.
     */
    function uniBalanceOf(IERC20 token, address account)
        internal
        view
        returns (uint256)
    {
        if (address(token) == _nativeToken) {
            uint256 contractBalance;
            assembly {
                contractBalance := balance(account)
            }
            return contractBalance;
        } else {
            return token.balanceOf(account);
        }
    }

    /**
     * @notice Transfers a specified amount of a token to a given address.
     * @param token The ERC20 token to transfer.
     * @param to The address to transfer the token to.
     * @param amount The amount of the token to transfer.
     * @dev Handles both native token and ERC20 transfers.
     */
    function uniTransfer(
        IERC20 token,
        address payable to,
        uint256 amount
    ) internal {
        if (amount != 0) {
            if (address(token) == _nativeToken) {
                uint256 contractBalance;
                assembly {
                    contractBalance := selfbalance()
                }
                if (contractBalance < amount) revert InsufficientBalance();
                (bool success, ) = to.call{
                    value: amount,
                    gas: _RAW_CALL_GAS_LIMIT
                }("");
                if (!success) revert NativeTransferFailed();
            } else {
                token.safeTransfer(to, amount);
            }
        } else {
            revert InsufficientBalance();
        }
    }

    /**
     * @notice Updates the gas limit for raw calls made by the contract.
     * @param gasLimit The new gas limit to be set.
     * @dev This function is restricted to the treasury.
     */
    function setGasLimit(uint256 gasLimit) external onlyTreasury {
        _RAW_CALL_GAS_LIMIT = gasLimit;
    }

    /**
     * @notice Updates the maximum fee that can be charged by the contract.
     * @param maxFee The new maximum fee to be set.
     * @dev This function is restricted to the treasury.
     */
    function setMaxFee(uint256 maxFee) external onlyTreasury {
        if (maxFee > 10000) revert MaxFeeLimitExceeded();
        _MAX_FEE = maxFee;
    }

    /**
     * @notice Updates the minimum fee that can be charged by the contract.
     * @param minFee The new minimum fee to be set.
     * @dev This function is restricted to the treasury.
     */
    function setMinFee(uint256 minFee) external onlyTreasury {
        if (minFee > 10000) revert MinFeeLimitExceeded();
        if (minFee > _MAX_FEE) revert MinFeeTooHigh();
        _MIN_FEE = minFee;
    }

    /**
     * @notice Updates the partner surplus share limit.
     * @param partnerSurplusShareLimit The new limit for partner surplus share.
     * @dev This function is restricted to the treasury.
     */
    function setPartnerSurplusShareLimit(uint256 partnerSurplusShareLimit)
        external
        onlyTreasury
    {
        _MAX_PARTNER_SURPLUS_SHARE_LIMIT = partnerSurplusShareLimit;
    }

    /**
     * @notice Updates the partner slippage share limit.
     * @param partnerSlippageShareLimit The new limit for partner slippage share.
     * @dev This function is restricted to the treasury.
     */
    function setPartnerSlippageShareLimit(uint256 partnerSlippageShareLimit)
        external
        onlyTreasury
    {        
        _MAX_PARTNER_SLIPPAGE_SHARE_LIMIT = partnerSlippageShareLimit;
    }
}
RouterStructs.sol 9 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

/// @dev generic smart contract interaction
struct Interaction {
    address target;
    uint256 value;
    bytes callData;
}
IDaiLikePermit.sol 17 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

// File @1inch/solidity-utils/contracts/interfaces/[email protected]

interface IDaiLikePermit {
    function permit(
        address holder,
        address spender,
        uint256 nonce,
        uint256 expiry,
        bool allowed,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}
IERC20.sol 88 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

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

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

    /**
     * @dev Returns the 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);
}
IERC20Permit.sol 92 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

// File @openzeppelin/contracts/token/ERC20/extensions/[email protected]

// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

/**
 * @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.
 *
 * ==== 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);
}
IExecutor.sol 14 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {Interaction} from "../base/RouterStructs.sol";
import {IERC20} from "./IERC20.sol";

/// @title Interface for making arbitrary calls
interface IExecutor {
    /// @notice propagates information about original msg.sender and executes arbitrary data
    function executeRoute(
        Interaction[] calldata interactions,
        IERC20 outputToken
    ) external payable;
}
IPermit2.sol 54 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

// File @1inch/solidity-utils/contracts/interfaces/[email protected]

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);
}
RevertReasonForwarder.sol 30 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

// File @1inch/solidity-utils/contracts/libraries/[email protected]

/// @title Revert reason forwarder.
library RevertReasonForwarder {
    /// @dev Forwards latest externall call revert.
    function reRevert() internal pure {
        // bubble up revert reason from latest external call
        assembly ("memory-safe") {
            // solhint-disable-line no-inline-assembly
            let ptr := mload(0x40)
            returndatacopy(ptr, 0, returndatasize())
            revert(ptr, returndatasize())
        }
    }

    /// @dev Returns latest external call revert reason.
    function reReason() internal pure returns (bytes memory reason) {
        assembly ("memory-safe") {
            // solhint-disable-line no-inline-assembly
            reason := mload(0x40)
            let length := returndatasize()
            mstore(reason, length)
            returndatacopy(add(reason, 0x20), 0, length)
            mstore(0x40, add(reason, add(0x20, length)))
        }
    }
}
SafeERC20.sol 412 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {IERC20} from "../interfaces/IERC20.sol";
import {IPermit2} from "../interfaces/IPermit2.sol";
import {IERC20Permit} from "../interfaces/IERC20Permit.sol";
import {IDaiLikePermit} from "../interfaces/IDaiLikePermit.sol";
import {RevertReasonForwarder} from "../lib/RevertReasonForwarder.sol";

// File @1inch/solidity-utils/contracts/libraries/[email protected]

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

    // 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 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 {
        if (!tryPermit(token, msg.sender, address(this), permit))
            RevertReasonForwarder.reRevert();
    }

    /**
     * @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 {
        if (!tryPermit(token, owner, spender, permit))
            RevertReasonForwarder.reRevert();
    }

    /**
     * @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))
                }
            }
        }
    }
}
EthReceiver.sol 15 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

abstract contract EthReceiver {
    error EthDepositRejected();

    receive() external payable {
        _receive();
    }

    function _receive() internal virtual {
        // solhint-disable-next-line avoid-tx-origin
        if (msg.sender == tx.origin) revert EthDepositRejected();
    }
}

Read Contract

_MAX_FEE 0xdeab3ebd → uint256
_MAX_PARTNER_SLIPPAGE_SHARE_LIMIT 0x868ec067 → uint256
_MAX_PARTNER_SURPLUS_SHARE_LIMIT 0x72a1d3c5 → uint256
_MIN_FEE 0xaf48bcb9 → uint256
_MIN_PROTOCOL_SLIPPAGE_SHARE_LIMIT 0x1632bc2d → uint256
_MIN_PROTOCOL_SURPLUS_SHARE_LIMIT 0x8ef26129 → uint256
_RAW_CALL_GAS_LIMIT 0x249bf7e5 → uint256
_nativeToken 0xd81a0553 → address

Write Contract 7 functions

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

collectFees 0x06af20da
address[] feeTokens
address receiver
setGasLimit 0xee7d72b4
uint256 gasLimit
setMaxFee 0x710e5d2f
uint256 maxFee
setMinFee 0x31ac9920
uint256 minFee
setPartnerSlippageShareLimit 0x040d41c2
uint256 partnerSlippageShareLimit
setPartnerSurplusShareLimit 0x32ec5d43
uint256 partnerSurplusShareLimit
swap 0x32af3139
address executor
tuple desc
tuple[] interactions
returns: uint256

Recent Transactions

No transactions found for this address