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;