ETH Price: $2,201.20 (-5.97%)

Contract Diff Checker

Contract Name:
TransferFacet

Contract Source Code:

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import { TransferHelper } from "../../libraries/TransferHelper.sol";
import { Utils } from "../../libraries/Utils.sol";

import { ISignatureTransfer } from "../../interfaces/ISignatureTransfer.sol";
import { IWrappedNative } from "../../interfaces/IWrappedNative.sol";

import { ITransferFacet } from "./interfaces/ITransferFacet.sol";

/// @title TransferFacet
/// @notice A facet for handling token transfers in a diamond-like proxy contract.
/// @dev Facilitates ERC20 token transfers, permit-based transfers via Permit2,
///      and native token unwrapping using a wrapped native token contract.
contract TransferFacet is ITransferFacet {
    // =========================
    // storage
    // =========================

    /// @dev The address of the Wrapped Native token contract (e.g., WETH).
    ///      Immutable, set during construction. Used in `unwrapNativeAndTransferTo` for withdrawing native tokens.
    IWrappedNative private immutable _wrappedNative;

    /// @dev The address of the Permit2 contract for signature-based transfers.
    ///      Immutable, set during construction. Used in `transferFromPermit2` and `getNonceForPermit2`.
    ISignatureTransfer private immutable _permit2;

    // =========================
    // constructor
    // =========================

    /// @notice Initializes the TransferFacet with Wrapped Native and Permit2 contract addresses.
    /// @dev Sets the immutable `_wrappedNative` and `_permit2` addresses.
    /// @param wrappedNative The address of the Wrapped Native token contract.
    /// @param permit2 The address of the Permit2 contract.
    constructor(address wrappedNative, address permit2) {
        _wrappedNative = IWrappedNative(wrappedNative);
        _permit2 = ISignatureTransfer(permit2);
    }

    // =========================
    // getters
    // =========================

    /// @inheritdoc ITransferFacet
    function getNonceForPermit2(address user) external view returns (uint256 nonce) {
        address permit2 = address(_permit2);

        assembly ("memory-safe") {
            for {
                let wordPosition

                // nonceBitmap(address,uint256) selector
                mstore(0, 0x4fe02b44)
                mstore(32, user)
                // re-write the third word in memory with zero
                mstore(64, wordPosition)
            } 1 {
                wordPosition := add(wordPosition, 1)
                mstore(64, wordPosition)
            } {
                // 28 - calldata start
                // 68 - calldata length
                // 96 - returndata start
                // 32 - returndata length
                pop(staticcall(gas(), permit2, 28, 68, 96, 32))

                let result := mload(96)

                switch result
                case 0 {
                    nonce := shl(8, wordPosition)
                    break
                }
                case 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff { continue }
                default {
                    if eq(and(result, 0xffffffffffffffffffffffffffffffff), 0xffffffffffffffffffffffffffffffff) {
                        result := shr(128, result)
                        nonce := add(nonce, 128)
                    }

                    if eq(and(result, 0xffffffffffffffff), 0xffffffffffffffff) {
                        result := shr(64, result)
                        nonce := add(nonce, 64)
                    }
                    if eq(and(result, 0xffffffff), 0xffffffff) {
                        result := shr(32, result)
                        nonce := add(nonce, 32)
                    }
                    if eq(and(result, 0xffff), 0xffff) {
                        result := shr(16, result)
                        nonce := add(nonce, 16)
                    }
                    if eq(and(result, 0xff), 0xff) {
                        result := shr(8, result)
                        nonce := add(nonce, 8)
                    }
                    if eq(and(result, 0x0f), 0x0f) {
                        result := shr(4, result)
                        nonce := add(nonce, 4)
                    }
                    if eq(and(result, 3), 3) {
                        result := shr(2, result)
                        nonce := add(nonce, 2)
                    }
                    if eq(and(result, 1), 1) {
                        result := shr(1, result)
                        nonce := add(nonce, 1)
                    }

                    nonce := add(nonce, shl(8, wordPosition))

                    break
                }
            }
        }
    }

    // =========================
    // functions
    // =========================

    /// @inheritdoc ITransferFacet
    function transferToken(
        address to,
        address[] calldata tokens
    )
        external
        returns (bytes32[] memory tokensAndAmounts)
    {
        uint256 length = tokens.length;

        tokensAndAmounts = Utils.decodeTokensAndAmountsArray();
        while (length > 0) {
            unchecked {
                --length;
            }
            address token = tokens[length];
            uint256 amount = Utils.getAmountFromArrayAndDelete({ tokensAndAmounts: tokensAndAmounts, token: token });

            if (amount > 0) {
                TransferHelper.safeTransfer({ token: token, to: to, value: amount });
            }
        }
    }

    /// @inheritdoc ITransferFacet
    function unwrapNativeAndTransferTo(address to) external returns (bytes32[] memory tokensAndAmounts) {
        tokensAndAmounts = Utils.decodeTokensAndAmountsArray();
        uint256 amount =
            Utils.getAmountFromArrayAndDelete({ tokensAndAmounts: tokensAndAmounts, token: address(_wrappedNative) });
        if (amount > 0) {
            _wrappedNative.withdraw({ wad: amount });

            TransferHelper.safeTransferNative({ to: to, value: amount });
        }
    }
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { IERC20 } from "forge-std/interfaces/IERC20.sol";
import { IErrors } from "../interfaces/IErrors.sol";

/// @title TransferHelper
/// @notice A helper library for safe transfers, approvals, and balance checks.
/// @dev Provides safe functions for ERC20 token and native currency transfers.
library TransferHelper {
    // =========================
    // functions
    // =========================

    /// @notice Executes a safe transfer from one address to another.
    /// @dev Uses low-level call to ensure proper error handling.
    /// @param token Address of the ERC20 token to transfer.
    /// @param from Address of the sender.
    /// @param to Address of the recipient.
    /// @param value Amount to transfer.
    function safeTransferFrom(address token, address from, uint256 value, address to) internal {
        if (!_makeCall(token, abi.encodeCall(IERC20.transferFrom, (from, to, value)))) {
            revert IErrors.TransferHelper_TransferFromError();
        }
    }

    /// @notice Executes a safe transfer.
    /// @dev Uses low-level call to ensure proper error handling.
    /// @param token Address of the ERC20 token to transfer.
    /// @param to Address of the recipient.
    /// @param value Amount to transfer.
    function safeTransfer(address token, address to, uint256 value) internal {
        if (!_makeCall(token, abi.encodeCall(IERC20.transfer, (to, value)))) {
            revert IErrors.TransferHelper_TransferError();
        }
    }

    /// @notice Executes a safe approval.
    /// @dev Uses low-level calls to handle cases where allowance is not zero
    /// and tokens which are not supports approve with non-zero allowance.
    /// @param token Address of the ERC20 token to approve.
    /// @param spender Address of the account that gets the approval.
    /// @param value Amount to approve.
    function safeApprove(address token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(IERC20.approve, (spender, value));

        if (!_makeCall(token, approvalCall)) {
            if (!_makeCall(token, abi.encodeCall(IERC20.approve, (spender, 0))) || !_makeCall(token, approvalCall)) {
                revert IErrors.TransferHelper_ApproveError();
            }
        }
    }

    /// @notice Retrieves the balance of an account safely.
    /// @dev Uses low-level staticcall to ensure proper error handling.
    /// @param token Address of the ERC20 token.
    /// @param account Address of the account to fetch balance for.
    /// @return The balance of the account.
    function safeGetBalance(address token, address account) internal view returns (uint256) {
        (bool success, bytes memory data) = token.staticcall(abi.encodeWithSelector(IERC20.balanceOf.selector, account));
        if (!success || data.length == 0) {
            revert IErrors.TransferHelper_GetBalanceError();
        }
        return abi.decode(data, (uint256));
    }

    /// @notice Executes a safe transfer of native currency (e.g., ETH).
    /// @dev Uses low-level call to ensure proper error handling.
    /// @param to Address of the recipient.
    /// @param value Amount to transfer.
    function safeTransferNative(address to, uint256 value) internal {
        assembly ("memory-safe") {
            if iszero(call(gas(), to, value, 0, 0, 0, 0)) {
                // revert IErrors.TransferHelper_TransferNativeError();
                mstore(0, 0xb1a0fdf8)
                revert(28, 4)
            }

            // send anonymous event with the `to` address
            mstore(0, to)
            log0(0, 32)
        }
    }

    // =========================
    // private function
    // =========================

    /// @dev Helper function to make a low-level call for token methods.
    /// @dev Ensures correct return value and decodes it.
    ///
    /// @param token Address to make the call on.
    /// @param data Calldata for the low-level call.
    /// @return True if the call succeeded, false otherwise.
    function _makeCall(address token, bytes memory data) private returns (bool) {
        (bool success, bytes memory returnData) = token.call(data);
        return success && (returnData.length == 0 || abi.decode(returnData, (bool)));
    }
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import { CAST_INT_CONSTANT, ADDRESS_MASK } from "./Constants.sol";

import { IErrors } from "../interfaces/IErrors.sol";

/// @title Utils
/// @notice General utility library for encoding, validation, array manipulation, and hashing in the router system.
/// @dev Includes math validation, calldata decoding, address masking, Permit2 witness hashing, and custom data encoders.
library Utils {
    /// @notice Checks that `greaterValue > lowerValue` and returns their difference.
    /// @dev Reverts if the condition is not satisfied.
    /// @param greaterValue Expected to be greater.
    /// @param lowerValue Expected to be lower.
    /// @return difference The subtraction result.
    function compareAmountsWithDifference(
        uint256 greaterValue,
        uint256 lowerValue
    )
        internal
        pure
        returns (uint256 difference)
    {
        compareAmounts({ greaterValue: greaterValue, lowerValue: lowerValue });
        unchecked {
            difference = greaterValue - lowerValue;
        }
    }

    /// @notice Reverts if `greaterValue < lowerValue`.
    function compareAmounts(uint256 greaterValue, uint256 lowerValue) internal pure {
        if (greaterValue < lowerValue) {
            revert IErrors.ValueLowerThanExpected({
                expectedGreaterValue: greaterValue,
                expectedLowerBalance: lowerValue
            });
        }
    }

    /// @notice Reverts if `greaterValue <= lowerValue`.
    function strictCompareAmounts(uint256 greaterValue, uint256 lowerValue) internal pure {
        if (greaterValue <= lowerValue) {
            revert IErrors.ValueLowerThanExpected({
                expectedGreaterValue: greaterValue,
                expectedLowerBalance: lowerValue
            });
        }
    }

    /// @notice Reverts if `left != right`.
    function expectEqual(uint256 left, uint256 right) internal pure {
        assembly ("memory-safe") {
            if sub(left, right) {
                // IErrors.ValuesNotEqual()
                mstore(0, 0xc8431659)
                revert(28, 4)
            }
        }
    }

    /// @notice Performs a low-level call and returns success.
    function makeCall(address addr, bytes memory data) internal returns (bool success) {
        (success,) = addr.call(data);
    }

    /// @notice Validates whether a uint256 can be safely casted to int256.
    function checkIntCast(uint256 amount) internal pure {
        // cast a uint256 to a int256, revert on overflow
        // https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/SafeCast.sol#L24
        if (amount > CAST_INT_CONSTANT) {
            revert IErrors.InvalidIntCast();
        }
    }

    /// @notice Returns current contract address as bytes32.
    function addressThisBytes32() internal view returns (bytes32 result) {
        assembly ("memory-safe") {
            result := address()
        }
    }

    /// @notice Extracts high 96 bits from a bytes32.
    function getHighOrder96Bits(bytes32 encodedValue) internal pure returns (uint256 result) {
        assembly ("memory-safe") {
            result := shr(160, encodedValue)
        }
    }

    /// @notice Extracts address from lower 160 bits of a bytes32.
    function bytes32ToAddress(bytes32 encodedValue) internal pure returns (address result) {
        assembly ("memory-safe") {
            result := and(encodedValue, ADDRESS_MASK)
        }
    }

    /// @notice Encodes token address and amount into a bytes32 word.
    function encodeTokenAndAmount(address token, uint256 amount) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            result := add(shl(160, amount), token)
        }
    }

    /// @notice Encodes two uint128 values into a single uint256.
    function encodeTwoAmounts(
        uint256 lowerBitsAmount,
        uint256 upperBitsAmount
    )
        internal
        pure
        returns (uint256 result)
    {
        assembly ("memory-safe") {
            result := add(shl(128, upperBitsAmount), lowerBitsAmount)
        }
    }

    /// @notice Decodes two uint128 values from a single uint256.
    function decodeTwoAmounts(uint256 encodedValue)
        internal
        pure
        returns (uint256 lowerBitsAmount, uint256 upperBitsAmount)
    {
        assembly ("memory-safe") {
            upperBitsAmount := shr(128, encodedValue)
            lowerBitsAmount := shr(128, shl(128, encodedValue))
        }
    }

    // Permit2 Witness for meta transactions
    string internal constant DATA_SENDER_AND_FEE_DETAILS_TYPE =
        "DataSenderAndFeeDetails(bytes[] data,address sender,address feeToken,uint256 feeAmount)";
    bytes32 internal constant DATA_SENDER_AND_FEE_DETAILS_TYPEHASH =
        0x5926d5c484218bc647e0624d5a08d6123e30560a9383b1ac7192a5ddcc02d686;
    string internal constant TOKEN_PERMISSIONS_TYPE = "TokenPermissions(address token,uint256 amount)";

    /// @notice Returns EIP-712 Permit2 hash for data, sender, feeToken, and feeAmount.
    function hashDataSenderAndFeeDetails(
        bytes[] calldata data,
        address sender,
        address feeToken,
        uint256 feeAmount
    )
        internal
        pure
        returns (bytes32 hash)
    {
        assembly ("memory-safe") {
            let length := data.length
            let ptr := mload(64)
            for {
                let dst := ptr
                let offset := data.offset
                let lengthCache := length
            } lengthCache {
                lengthCache := sub(lengthCache, 1)
                offset := add(offset, 32)
                dst := add(dst, 32)
            } {
                let dataOffset := add(data.offset, calldataload(offset))
                let dataLength := calldataload(dataOffset)
                calldatacopy(dst, add(dataOffset, 32), dataLength)
                mstore(dst, keccak256(dst, dataLength))
            }
            mstore(add(ptr, 32), keccak256(ptr, mul(length, 32)))
            mstore(ptr, DATA_SENDER_AND_FEE_DETAILS_TYPEHASH)
            mstore(add(ptr, 64), sender)
            mstore(add(ptr, 96), feeToken)
            mstore(add(ptr, 128), feeAmount)
            hash := keccak256(ptr, 160)
        }
    }

    /// @notice Returns full EIP-712 Permit2 witness type string.
    function getWitnessTypeString() internal pure returns (string memory) {
        return string(
            abi.encodePacked(
                "DataSenderAndFeeDetails dataSenderAndFeeDetails)",
                DATA_SENDER_AND_FEE_DETAILS_TYPE,
                TOKEN_PERMISSIONS_TYPE
            )
        );
    }

    /// @notice Decodes a bytes32[] from calldata tail segment.
    function decodeTokensAndAmountsArray() internal pure returns (bytes32[] memory tokensAndAmounts) {
        assembly ("memory-safe") {
            tokensAndAmounts := mload(64)

            let length := calldataload(sub(calldatasize(), 32))
            mstore(tokensAndAmounts, length)
            mstore(64, add(tokensAndAmounts, add(32, mul(32, length))))
            if length {
                calldatacopy(add(tokensAndAmounts, 32), sub(calldatasize(), add(32, mul(32, length))), mul(32, length))
            }
        }
    }

    /// @notice Creates a bytes32[] array from tokens and amounts.
    function createTokensAndAmountsArray(
        address wrappedNative,
        address[] calldata tokens,
        uint256[] calldata amounts
    )
        internal
        pure
        returns (bytes32[] memory tokensAndAmounts)
    {
        unchecked {
            assembly ("memory-safe") {
                tokensAndAmounts := mload(64)
            }

            uint256 length = amounts.length;
            for (uint256 i; i < length; i++) {
                address token = tokens[i];
                addElementToArrayOrAddAmount({
                    tokensAndAmounts: tokensAndAmounts,
                    element: encodeTokenAndAmount({ token: token > address(0) ? token : wrappedNative, amount: amounts[i] })
                });
            }
        }
    }

    /// @notice Retrieves and removes a token from the array. Returns its amount.
    function getAmountFromArrayAndDelete(
        bytes32[] memory tokensAndAmounts,
        address token
    )
        internal
        pure
        returns (uint256 amount)
    {
        assembly ("memory-safe") {
            for {
                let length := mload(tokensAndAmounts)
                let offset := add(tokensAndAmounts, mul(32, length)) // last element of the array
                let i
                let offsetCache := offset
            } lt(i, length) {
                i := add(i, 1)
                offsetCache := sub(offsetCache, 32)
            } {
                let tokenAndAmount := mload(offsetCache)
                if eq(token, and(tokenAndAmount, ADDRESS_MASK)) {
                    mstore(offsetCache, mload(offset))
                    mstore(tokensAndAmounts, sub(length, 1))
                    amount := shr(160, tokenAndAmount)
                    break
                }
            }
        }
    }

    /// @notice Adds an entry to the array or increases the amount if it already exists.
    function addElementToArrayOrAddAmount(bytes32[] memory tokensAndAmounts, bytes32 element) internal pure {
        assembly ("memory-safe") {
            let length := mload(tokensAndAmounts)
            let flag := 1
            for {
                let i := length
                let offset := add(tokensAndAmounts, 32)
            } i {
                i := sub(i, 1)
                offset := add(offset, 32)
            } {
                let value := mload(offset)
                if eq(and(element, ADDRESS_MASK), and(value, ADDRESS_MASK)) {
                    mstore(offset, add(value, and(element, not(ADDRESS_MASK))))
                    flag := 0
                    break
                }
            }

            if flag {
                let offset := add(tokensAndAmounts, add(32, mul(32, length)))
                mstore(offset, element)
                mstore(tokensAndAmounts, add(length, 1))
                mstore(64, add(offset, 32))
            }
        }
    }

    /// @notice Ensures all addresses in an array are unique.
    function checkUnique(address[] calldata addresses) internal pure {
        unchecked {
            uint256 length = addresses.length;
            if (length > 0) {
                for (uint256 i; i < length - 1; ++i) {
                    for (uint256 j = i + 1; j < length; ++j) {
                        if (addresses[i] == addresses[j]) {
                            revert IErrors.DuplicateToken();
                        }
                    }
                }
            }
        }
    }
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title SignatureTransfer
/// @notice Handles ERC20 token transfers through signature based actions
/// @dev Requires user's token approval on the Permit2 contract
interface ISignatureTransfer {
    /// @notice The token and amount details for a transfer signed in the permit transfer signature
    struct TokenPermissions {
        // ERC20 token address
        address token;
        // the maximum amount that can be spent
        uint256 amount;
    }

    /// @notice Used to reconstruct the signed permit message for multiple token transfers
    /// @dev Do not need to pass in spender address as it is required that it is msg.sender
    /// @dev Note that a user still signs over a spender address
    struct PermitBatchTransferFrom {
        // the tokens and corresponding amounts permitted for a transfer
        TokenPermissions[] permitted;
        // a unique value for every token owner's signature to prevent signature replays
        uint256 nonce;
        // deadline on the permit signature
        uint256 deadline;
    }

    /// @notice Specifies the recipient address and amount for batched transfers.
    /// @dev Recipients and amounts correspond to the index of the signed token permissions array.
    /// @dev Reverts if the requested amount is greater than the permitted signed amount.
    struct SignatureTransferDetails {
        // recipient address
        address to;
        // spender requested amount
        uint256 requestedAmount;
    }

    /// @notice A map from token owner address and a caller specified word index to a bitmap. Used to set bits in the bitmap to prevent against signature replay protection
    /// @dev Uses unordered nonces so that permit messages do not need to be spent in a certain order
    /// @dev The mapping is indexed first by the token owner, then by an index specified in the nonce
    /// @dev It returns a uint256 bitmap
    /// @dev The index, or wordPosition is capped at type(uint248).max
    function nonceBitmap(address user, uint256 wordPosition) external view returns (uint256);

    /// @notice Transfers multiple tokens using a signed permit message
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails Specifies the recipient and requested amount for the token transfer
    /// @param signature The signature to verify
    function permitTransferFrom(
        PermitBatchTransferFrom memory permit,
        SignatureTransferDetails[] calldata transferDetails,
        address owner,
        bytes calldata signature
    )
        external;

    /// @notice Transfers multiple tokens using a signed permit message
    /// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
    /// @notice Includes extra data provided by the caller to verify signature over
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails Specifies the recipient and requested amount for the token transfer
    /// @param witness Extra data to include when checking the user signature
    /// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
    /// @param signature The signature to verify
    function permitWitnessTransferFrom(
        PermitBatchTransferFrom memory permit,
        SignatureTransferDetails[] calldata transferDetails,
        address owner,
        bytes32 witness,
        string calldata witnessTypeString,
        bytes calldata signature
    )
        external;
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IWrappedNative {
    function deposit() external payable;

    function withdraw(uint256 wad) external;
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title ITransferFacet - interface for TransferFacet
interface ITransferFacet {
    // =========================
    // getters
    // =========================

    /// @notice Retrieves the next available nonce for a user's Permit2 signature.
    /// @dev Queries the Permit2 contract's `nonceBitmap` function to find the first unused nonce for the user,
    ///      then iterating over nonce words.
    /// @param user The address of the user whose nonce is being queried.
    /// @return nonce The next available nonce for the user's Permit2 signature.
    function getNonceForPermit2(address user) external view returns (uint256);

    // =========================
    // functions
    // =========================

    /// @notice Transfers multiple ERC20 tokens to a specified address.
    /// @dev Iterates over the provided token array, retrieves amounts from `TransientStorageLibrary`,
    ///      and transfers non-zero amounts using `TransferHelper`.
    /// @param to The recipient address for the token transfers.
    /// @param tokens The array of ERC20 token addresses to transfer.
    function transferToken(
        address to,
        address[] calldata tokens
    )
        external
        returns (bytes32[] memory tokensAndAmounts);

    /// @notice Unwraps Wrapped Native tokens to native currency and transfers to a specified address.
    /// @dev Retrieves the Wrapped Native token amount from `TransientStorageLibrary`,
    ///      calls `withdraw` on the Wrapped Native contract, and transfers the native currency using `TransferHelper`.
    /// @param to The recipient address for the native currency transfer.
    function unwrapNativeAndTransferTo(address to) external returns (bytes32[] memory tokensAndAmounts);
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;

/// @dev Interface of the ERC20 standard as defined in the EIP.
/// @dev This includes the optional name, symbol, and decimals metadata.
interface IERC20 {
    /// @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`).
    event Transfer(address indexed from, address indexed to, uint256 value);

    /// @dev Emitted when the allowance of a `spender` for an `owner` is set, where `value`
    /// is the new allowance.
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /// @notice Returns the amount of tokens in existence.
    function totalSupply() external view returns (uint256);

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

    /// @notice Moves `amount` tokens from the caller's account to `to`.
    function transfer(address to, uint256 amount) external returns (bool);

    /// @notice Returns the remaining number of tokens that `spender` is allowed
    /// to spend on behalf of `owner`
    function allowance(address owner, address spender) external view returns (uint256);

    /// @notice Sets `amount` as the allowance of `spender` over the caller's tokens.
    /// @dev Be aware of front-running risks: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
    function approve(address spender, uint256 amount) external returns (bool);

    /// @notice Moves `amount` tokens from `from` to `to` using the allowance mechanism.
    /// `amount` is then deducted from the caller's allowance.
    function transferFrom(address from, address to, uint256 amount) external returns (bool);

    /// @notice Returns the name of the token.
    function name() external view returns (string memory);

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

    /// @notice Returns the decimals places of the token.
    function decimals() external view returns (uint8);
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title IErrors
/// @notice Centralized error definitions for EntryPoint and all related facets and libraries.
/// @dev This interface defines custom errors used throughout the protocol for gas-efficient reverts.
interface IErrors {
    // =========================
    // EntryPoint errors
    // =========================

    /// @dev Thrown when the function selector is not found in any facet or module.
    /// @param selector The 4-byte function selector that was not matched.
    error EntryPoint_FunctionDoesNotExist(bytes4 selector);

    /// @dev Thrown when attempting to add a module that is already registered.
    /// @param methodSignature The 4-byte signature of the module that is already added.
    error EntryPoint_ModuleAlreadyAdded(bytes4 methodSignature);

    /// @dev Thrown when trying to update or remove a module that was never added.
    /// @param methodSignature The 4-byte signature of the missing module.
    error EntryPoint_ModuleNotAdded(bytes4 methodSignature);

    /// @dev Thrown when a function is called externally but is restricted to internal delegation.
    error EntryPoint_OnlySelfCall();

    // =========================
    // Ownable errors
    // =========================

    /// @dev Thrown when the caller is not the current contract owner.
    /// @param sender The address attempting the restricted action.
    error Ownable_SenderIsNotOwner(address sender);

    /// @dev Thrown when the caller is not the pending new owner in a two-step ownership transfer.
    /// @param caller The address attempting to accept ownership.
    error Ownable_CallerIsNotTheNewOwner(address caller);

    /// @dev Thrown when trying to set the new owner to the zero address.
    error Ownable_NewOwnerCannotBeAddressZero();

    // =========================
    // AcrossFacet errors
    // =========================

    /// @dev Thrown when `handleV3AcrossMessage` is called by an address other than the SpokePool.
    /// @dev Ensures only the designated SpokePool can trigger the message handler.
    error AcrossFacet_NotSpokePool();

    // =========================
    // MultiswapRouterFacet errors
    // =========================

    /// @dev Thrown when a Uniswap V2 swap fails.
    error MultiswapRouterFacet_FailedV2Swap();

    /// @dev Thrown when the provided `Multiswap2Calldata` is invalid or malformed.
    error MultiswapRouterFacet_InvalidMultiswap2Calldata();

    /// @dev Thrown when a Uniswap V3 swap fails, typically due to insufficient liquidity.
    error MultiswapRouterFacet_FailedV3Swap();

    /// @dev Thrown when the sender in the fallback function is not the expected Uniswap V3 pool.
    error MultiswapRouterFacet_SenderMustBePool();

    // =========================
    // StargateFacet errors
    // =========================

    /// @dev Thrown when `lzCompose` is called by an address other than the configured LayerZero endpoint.
    error StargateFacet_NotLZEndpoint();

    /// @dev Thrown when attempting to use Stargate with an unsupported native asset.
    /// @dev Stargate supports only ERC20 tokens; native tokens are not allowed.
    error StargateFacet_UnsupportedAsset();

    // =========================
    // TransferHelper errors
    // =========================

    /// @dev Thrown when `safeTransferFrom` fails to execute.
    error TransferHelper_TransferFromError();

    /// @dev Thrown when `safeTransfer` fails to execute.
    error TransferHelper_TransferError();

    /// @dev Thrown when `safeApprove` fails to update allowance.
    error TransferHelper_ApproveError();

    /// @dev Thrown when `safeGetBalance` fails to read the token balance.
    error TransferHelper_GetBalanceError();

    /// @dev Thrown when attempting to transfer native tokens and the operation fails.
    error TransferHelper_TransferNativeError();

    // =========================
    // HelperV2 errors
    // =========================

    /// @dev Thrown when the input amount is less than the required minimum in Uniswap V2-style pools.
    error UniswapV2_InsufficientInputAmount();

    /// @dev Thrown when the output amount is less than the required minimum in Uniswap V2-style pools.
    error UniswapV2_InsufficientOutputAmount();

    /// @dev Thrown when there's not enough liquidity in a Uniswap V2-style pool to execute the swap.
    error UniswapV2_InsufficientLiquidity();

    // =========================
    // Utils errors
    // =========================

    /// @dev Thrown when a uint256 value cannot be safely cast to int256.
    error InvalidIntCast();

    /// @dev Thrown when an expected greater value is actually lower than the comparison value.
    /// @param expectedGreaterValue The value that was expected to be larger.
    /// @param expectedLowerBalance The value that was expected to be smaller.
    error ValueLowerThanExpected(uint256 expectedGreaterValue, uint256 expectedLowerBalance);

    /// @dev Thrown when two values that were expected to match do not.
    error ValuesNotEqual();

    /// @dev Thrown when duplicate tokens are detected in an array that must be unique.
    error DuplicateToken();
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// =========================
// General Constants
// =========================

/// @dev Represents 10^18, commonly used for token decimals (e.g., ETH, ERC20).
/// @dev Useful for arithmetic operations requiring high precision.
uint256 constant E18 = 1e18;

/// @dev Represents 10^6, used as swap fee denominator.
uint256 constant E6 = 1e6;

/// @dev Represents 2^96, used for sqrtPriceX96 calculations.
uint256 constant _2E96 = 2 ** 96;

// =========================
// TransientStorageLibrary Constants
// =========================

/// @dev Storage slot for the callback address in `TransientStorageLibrary`.
///      Computed as keccak256("callback.facet.storage") - 1. Used in `getCallbackAddress` and `setCallbackAddress`.
bytes32 constant CALLBACK_FACET_STORAGE = 0x1248b983d56fa782b7a88ee11066fc0746058888ea550df970b9eea952d65dd0;

/// @dev Storage slot used to temporarily hold the sender address across internal calls.
///      Calculated as keccak256("sender.facet.storage") - 1.
bytes32 constant SENDER_FACET_STORAGE = 0x289cc669fe96ce33e95427b15b06e5cf0e5e79eb9894ad468d456975ce05c197;

// =========================
// FeeLibrary Constants
// =========================

/// @dev Storage slot for caching the fee recipient address and fee amount.
///      Packed as: (fee << 160) | address. Calculated as keccak256(\"feeContract.storage\") - 1.
bytes32 constant FEE_CONTRACT_STORAGE = 0xde699227b1a7fb52a64c41a77682cef2fe2815e2a233a451b6c9f64b1abac290;

// =========================
// MultiswapRouterFacet Constants
// =========================

/// @dev Bitmask to detect if a pool is a UniswapV3-type pair.
///      Applied as: `if (pair & UNISWAP_V3_MASK != 0) => UniswapV3 logic`.
uint256 constant UNISWAP_V3_MASK = 0x8000000000000000000000000000000000000000000000000000000000000000;

/// @dev Bitmask to extract only the lower 160 bits from a bytes32-encoded address (standard EVM address size).
address constant ADDRESS_MASK = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF;

/// @dev Bitmask to extract the fee value (uint24) from an encoded pool value (bits 160–183).
uint24 constant FEE_MASK = 0xffffff;

/// @dev Minimum sqrt price ratio plus one. Used as the minimum bound in UniswapV3 swap calculations.
///      Corresponds to the smallest sqrtPriceX96 value + 1 allowed by the protocol.
uint160 constant MIN_SQRT_RATIO_PLUS_ONE = 4_295_128_740;

/// @dev Maximum sqrt price ratio minus one. Used as the maximum bound in UniswapV3 swap calculations.
///      Corresponds to the largest sqrtPriceX96 value - 1 allowed by the protocol.
uint160 constant MAX_SQRT_RATIO_MINUS_ONE = 1_461_446_703_485_210_103_287_273_052_203_988_822_378_723_970_341;

/// @dev Represents the maximum int256 value (2^255 - 1), used to safely cast uint256 to int256.
///      Used in UniswapV3 swap logic to ensure compatibility with `amountSpecified: int256`.
uint256 constant CAST_INT_CONSTANT =
    57_896_044_618_658_097_711_785_492_504_343_953_926_634_992_332_820_282_019_728_792_003_956_564_819_967;

/// @dev Bitmask to extract a 60-bit percentage field from packed data (commonly used in fee/routing logic).
uint256 constant PERCENTAGE_MASK = 0xfffffffffffffff;

Please enter a contract address above to load the contract details and source code.

Context size (optional):