ETH Price: $2,514.72 (-0.17%)

Transaction Decoder

Block:
22838863 at Jul-03-2025 01:01:23 PM +UTC
Transaction Fee:
0.000217107382225657 ETH $0.55
Gas Used:
55,657 Gas / 3.900810001 Gwei

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
12.378341457727140252 Eth12.378452771727140252 Eth0.000111314
0xb3165FC2...3c45d5784
0.020458246132441053 Eth
Nonce: 17
0.020241138750215396 Eth
Nonce: 19
0.000217107382225657From: 0 To: 22892026855592066050609947431602401211538835161166308139

Execution Trace

0xb3165fc27b63d6fe64ccc57949851943c45d5784.e9ae5c53( )
  • ETH 0.019936300685502553 LiFiDiamond.25d374e8( )
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.17;
    error TokenAddressIsZero();
    error TokenNotSupported();
    error CannotBridgeToSameNetwork();
    error ZeroPostSwapBalance();
    error NoSwapDataProvided();
    error NativeValueWithERC();
    error ContractCallNotAllowed();
    error NullAddrIsNotAValidSpender();
    error NullAddrIsNotAnERC20Token();
    error NoTransferToNullAddress();
    error NativeAssetTransferFailed();
    error InvalidBridgeConfigLength();
    error InvalidAmount();
    error InvalidContract();
    error InvalidConfig();
    error UnsupportedChainId(uint256 chainId);
    error InvalidReceiver();
    error InvalidDestinationChain();
    error InvalidSendingToken();
    error InvalidCaller();
    error AlreadyInitialized();
    error NotInitialized();
    error OnlyContractOwner();
    error CannotAuthoriseSelf();
    error RecoveryAddressCannotBeZero();
    error CannotDepositNativeToken();
    error InvalidCallData();
    error NativeAssetNotSupported();
    error UnAuthorized();
    error NoSwapFromZeroBalance();
    error InvalidFallbackAddress();
    error CumulativeSlippageTooHigh(uint256 minAmount, uint256 receivedAmount);
    error InsufficientBalance(uint256 required, uint256 balance);
    error ZeroAmount();
    error InvalidFee();
    error InformationMismatch();
    error NotAContract();
    error NotEnoughBalance(uint256 requested, uint256 available);
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.17;
    interface IDiamondCut {
        enum FacetCutAction {
            Add,
            Replace,
            Remove
        }
        // Add=0, Replace=1, Remove=2
        struct FacetCut {
            address facetAddress;
            FacetCutAction action;
            bytes4[] functionSelectors;
        }
        /// @notice Add/replace/remove any number of functions and optionally execute
        ///         a function with delegatecall
        /// @param _diamondCut Contains the facet addresses and function selectors
        /// @param _init The address of the contract or facet to execute _calldata
        /// @param _calldata A function call, including function selector and arguments
        ///                  _calldata is executed with delegatecall on _init
        function diamondCut(
            FacetCut[] calldata _diamondCut,
            address _init,
            bytes calldata _calldata
        ) external;
        event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.17;
    import { LibDiamond } from "./Libraries/LibDiamond.sol";
    import { IDiamondCut } from "./Interfaces/IDiamondCut.sol";
    import { LibUtil } from "./Libraries/LibUtil.sol";
    contract LiFiDiamond {
        constructor(address _contractOwner, address _diamondCutFacet) payable {
            LibDiamond.setContractOwner(_contractOwner);
            // Add the diamondCut external function from the diamondCutFacet
            IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1);
            bytes4[] memory functionSelectors = new bytes4[](1);
            functionSelectors[0] = IDiamondCut.diamondCut.selector;
            cut[0] = IDiamondCut.FacetCut({
                facetAddress: _diamondCutFacet,
                action: IDiamondCut.FacetCutAction.Add,
                functionSelectors: functionSelectors
            });
            LibDiamond.diamondCut(cut, address(0), "");
        }
        // Find facet for function that is called and execute the
        // function if a facet is found and return any value.
        // solhint-disable-next-line no-complex-fallback
        fallback() external payable {
            LibDiamond.DiamondStorage storage ds;
            bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION;
            // get diamond storage
            // solhint-disable-next-line no-inline-assembly
            assembly {
                ds.slot := position
            }
            // get facet from function selector
            address facet = ds.selectorToFacetAndPosition[msg.sig].facetAddress;
            if (facet == address(0)) {
                revert LibDiamond.FunctionDoesNotExist();
            }
            // Execute external function from facet using delegatecall and return any value.
            // solhint-disable-next-line no-inline-assembly
            assembly {
                // copy function selector and any arguments
                calldatacopy(0, 0, calldatasize())
                // execute function call using the facet
                let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
                // get any return value
                returndatacopy(0, 0, returndatasize())
                // return any return value or error back to the caller
                switch result
                case 0 {
                    revert(0, returndatasize())
                }
                default {
                    return(0, returndatasize())
                }
            }
        }
        // Able to receive ether
        // solhint-disable-next-line no-empty-blocks
        receive() external payable {}
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.17;
    library LibBytes {
        // solhint-disable no-inline-assembly
        // LibBytes specific errors
        error SliceOverflow();
        error SliceOutOfBounds();
        error AddressOutOfBounds();
        error UintOutOfBounds();
        // -------------------------
        function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) {
            bytes memory tempBytes;
            assembly {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)
                // Store the length of the first bytes array at the beginning of
                // the memory for tempBytes.
                let length := mload(_preBytes)
                mstore(tempBytes, length)
                // Maintain a memory counter for the current write location in the
                // temp bytes array by adding the 32 bytes for the array length to
                // the starting location.
                let mc := add(tempBytes, 0x20)
                // Stop copying when the memory counter reaches the length of the
                // first bytes array.
                let end := add(mc, length)
                for {
                    // Initialize a copy counter to the start of the _preBytes data,
                    // 32 bytes into its memory.
                    let cc := add(_preBytes, 0x20)
                } lt(mc, end) {
                    // Increase both counters by 32 bytes each iteration.
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    // Write the _preBytes data into the tempBytes memory 32 bytes
                    // at a time.
                    mstore(mc, mload(cc))
                }
                // Add the length of _postBytes to the current length of tempBytes
                // and store it as the new length in the first 32 bytes of the
                // tempBytes memory.
                length := mload(_postBytes)
                mstore(tempBytes, add(length, mload(tempBytes)))
                // Move the memory counter back from a multiple of 0x20 to the
                // actual end of the _preBytes data.
                mc := end
                // Stop copying when the memory counter reaches the new combined
                // length of the arrays.
                end := add(mc, length)
                for {
                    let cc := add(_postBytes, 0x20)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    mstore(mc, mload(cc))
                }
                // Update the free-memory pointer by padding our last write location
                // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
                // next 32 byte block, then round down to the nearest multiple of
                // 32. If the sum of the length of the two arrays is zero then add
                // one before rounding down to leave a blank 32 bytes (the length block with 0).
                mstore(
                    0x40,
                    and(
                        add(add(end, iszero(add(length, mload(_preBytes)))), 31),
                        not(31) // Round down to the nearest 32 bytes.
                    )
                )
            }
            return tempBytes;
        }
        function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
            assembly {
                // Read the first 32 bytes of _preBytes storage, which is the length
                // of the array. (We don't need to use the offset into the slot
                // because arrays use the entire slot.)
                let fslot := sload(_preBytes.slot)
                // Arrays of 31 bytes or less have an even value in their slot,
                // while longer arrays have an odd value. The actual length is
                // the slot divided by two for odd values, and the lowest order
                // byte divided by two for even values.
                // If the slot is even, bitwise and the slot with 255 and divide by
                // two to get the length. If the slot is odd, bitwise and the slot
                // with -1 and divide by two.
                let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
                let mlength := mload(_postBytes)
                let newlength := add(slength, mlength)
                // slength can contain both the length and contents of the array
                // if length < 32 bytes so let's prepare for that
                // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                switch add(lt(slength, 32), lt(newlength, 32))
                case 2 {
                    // Since the new array still fits in the slot, we just need to
                    // update the contents of the slot.
                    // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                    sstore(
                        _preBytes.slot,
                        // all the modifications to the slot are inside this
                        // next block
                        add(
                            // we can just add to the slot contents because the
                            // bytes we want to change are the LSBs
                            fslot,
                            add(
                                mul(
                                    div(
                                        // load the bytes from memory
                                        mload(add(_postBytes, 0x20)),
                                        // zero all bytes to the right
                                        exp(0x100, sub(32, mlength))
                                    ),
                                    // and now shift left the number of bytes to
                                    // leave space for the length in the slot
                                    exp(0x100, sub(32, newlength))
                                ),
                                // increase length by the double of the memory
                                // bytes length
                                mul(mlength, 2)
                            )
                        )
                    )
                }
                case 1 {
                    // The stored value fits in the slot, but the combined value
                    // will exceed it.
                    // get the keccak hash to get the contents of the array
                    mstore(0x0, _preBytes.slot)
                    let sc := add(keccak256(0x0, 0x20), div(slength, 32))
                    // save new length
                    sstore(_preBytes.slot, add(mul(newlength, 2), 1))
                    // The contents of the _postBytes array start 32 bytes into
                    // the structure. Our first read should obtain the `submod`
                    // bytes that can fit into the unused space in the last word
                    // of the stored array. To get this, we read 32 bytes starting
                    // from `submod`, so the data we read overlaps with the array
                    // contents by `submod` bytes. Masking the lowest-order
                    // `submod` bytes allows us to add that value directly to the
                    // stored value.
                    let submod := sub(32, slength)
                    let mc := add(_postBytes, submod)
                    let end := add(_postBytes, mlength)
                    let mask := sub(exp(0x100, submod), 1)
                    sstore(
                        sc,
                        add(
                            and(fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00),
                            and(mload(mc), mask)
                        )
                    )
                    for {
                        mc := add(mc, 0x20)
                        sc := add(sc, 1)
                    } lt(mc, end) {
                        sc := add(sc, 1)
                        mc := add(mc, 0x20)
                    } {
                        sstore(sc, mload(mc))
                    }
                    mask := exp(0x100, sub(mc, end))
                    sstore(sc, mul(div(mload(mc), mask), mask))
                }
                default {
                    // get the keccak hash to get the contents of the array
                    mstore(0x0, _preBytes.slot)
                    // Start copying to the last used word of the stored array.
                    let sc := add(keccak256(0x0, 0x20), div(slength, 32))
                    // save new length
                    sstore(_preBytes.slot, add(mul(newlength, 2), 1))
                    // Copy over the first `submod` bytes of the new data as in
                    // case 1 above.
                    let slengthmod := mod(slength, 32)
                    let submod := sub(32, slengthmod)
                    let mc := add(_postBytes, submod)
                    let end := add(_postBytes, mlength)
                    let mask := sub(exp(0x100, submod), 1)
                    sstore(sc, add(sload(sc), and(mload(mc), mask)))
                    for {
                        sc := add(sc, 1)
                        mc := add(mc, 0x20)
                    } lt(mc, end) {
                        sc := add(sc, 1)
                        mc := add(mc, 0x20)
                    } {
                        sstore(sc, mload(mc))
                    }
                    mask := exp(0x100, sub(mc, end))
                    sstore(sc, mul(div(mload(mc), mask), mask))
                }
            }
        }
        function slice(
            bytes memory _bytes,
            uint256 _start,
            uint256 _length
        ) internal pure returns (bytes memory) {
            if (_length + 31 < _length) revert SliceOverflow();
            if (_bytes.length < _start + _length) revert SliceOutOfBounds();
            bytes memory tempBytes;
            assembly {
                switch iszero(_length)
                case 0 {
                    // Get a location of some free memory and store it in tempBytes as
                    // Solidity does for memory variables.
                    tempBytes := mload(0x40)
                    // The first word of the slice result is potentially a partial
                    // word read from the original array. To read it, we calculate
                    // the length of that partial word and start copying that many
                    // bytes into the array. The first word we copy will start with
                    // data we don't care about, but the last `lengthmod` bytes will
                    // land at the beginning of the contents of the new array. When
                    // we're done copying, we overwrite the full first word with
                    // the actual length of the slice.
                    let lengthmod := and(_length, 31)
                    // The multiplication in the next line is necessary
                    // because when slicing multiples of 32 bytes (lengthmod == 0)
                    // the following copy loop was copying the origin's length
                    // and then ending prematurely not copying everything it should.
                    let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                    let end := add(mc, _length)
                    for {
                        // The multiplication in the next line has the same exact purpose
                        // as the one above.
                        let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                    } lt(mc, end) {
                        mc := add(mc, 0x20)
                        cc := add(cc, 0x20)
                    } {
                        mstore(mc, mload(cc))
                    }
                    mstore(tempBytes, _length)
                    //update free-memory pointer
                    //allocating the array padded to 32 bytes like the compiler does now
                    mstore(0x40, and(add(mc, 31), not(31)))
                }
                //if we want a zero-length slice let's just return a zero-length array
                default {
                    tempBytes := mload(0x40)
                    //zero out the 32 bytes slice we are about to return
                    //we need to do it because Solidity does not garbage collect
                    mstore(tempBytes, 0)
                    mstore(0x40, add(tempBytes, 0x20))
                }
            }
            return tempBytes;
        }
        function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
            if (_bytes.length < _start + 20) {
                revert AddressOutOfBounds();
            }
            address tempAddress;
            assembly {
                tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
            }
            return tempAddress;
        }
        function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
            if (_bytes.length < _start + 1) {
                revert UintOutOfBounds();
            }
            uint8 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0x1), _start))
            }
            return tempUint;
        }
        function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
            if (_bytes.length < _start + 2) {
                revert UintOutOfBounds();
            }
            uint16 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0x2), _start))
            }
            return tempUint;
        }
        function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
            if (_bytes.length < _start + 4) {
                revert UintOutOfBounds();
            }
            uint32 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0x4), _start))
            }
            return tempUint;
        }
        function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
            if (_bytes.length < _start + 8) {
                revert UintOutOfBounds();
            }
            uint64 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0x8), _start))
            }
            return tempUint;
        }
        function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
            if (_bytes.length < _start + 12) {
                revert UintOutOfBounds();
            }
            uint96 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0xc), _start))
            }
            return tempUint;
        }
        function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
            if (_bytes.length < _start + 16) {
                revert UintOutOfBounds();
            }
            uint128 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0x10), _start))
            }
            return tempUint;
        }
        function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
            if (_bytes.length < _start + 32) {
                revert UintOutOfBounds();
            }
            uint256 tempUint;
            assembly {
                tempUint := mload(add(add(_bytes, 0x20), _start))
            }
            return tempUint;
        }
        function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
            if (_bytes.length < _start + 32) {
                revert UintOutOfBounds();
            }
            bytes32 tempBytes32;
            assembly {
                tempBytes32 := mload(add(add(_bytes, 0x20), _start))
            }
            return tempBytes32;
        }
        function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
            bool success = true;
            assembly {
                let length := mload(_preBytes)
                // if lengths don't match the arrays are not equal
                switch eq(length, mload(_postBytes))
                case 1 {
                    // cb is a circuit breaker in the for loop since there's
                    //  no said feature for inline assembly loops
                    // cb = 1 - don't breaker
                    // cb = 0 - break
                    let cb := 1
                    let mc := add(_preBytes, 0x20)
                    let end := add(mc, length)
                    for {
                        let cc := add(_postBytes, 0x20)
                        // the next line is the loop condition:
                        // while(uint256(mc < end) + cb == 2)
                    } eq(add(lt(mc, end), cb), 2) {
                        mc := add(mc, 0x20)
                        cc := add(cc, 0x20)
                    } {
                        // if any of these checks fails then arrays are not equal
                        if iszero(eq(mload(mc), mload(cc))) {
                            // unsuccess:
                            success := 0
                            cb := 0
                        }
                    }
                }
                default {
                    // unsuccess:
                    success := 0
                }
            }
            return success;
        }
        function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) {
            bool success = true;
            assembly {
                // we know _preBytes_offset is 0
                let fslot := sload(_preBytes.slot)
                // Decode the length of the stored array like in concatStorage().
                let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
                let mlength := mload(_postBytes)
                // if lengths don't match the arrays are not equal
                switch eq(slength, mlength)
                case 1 {
                    // slength can contain both the length and contents of the array
                    // if length < 32 bytes so let's prepare for that
                    // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                    if iszero(iszero(slength)) {
                        switch lt(slength, 32)
                        case 1 {
                            // blank the last byte which is the length
                            fslot := mul(div(fslot, 0x100), 0x100)
                            if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                                // unsuccess:
                                success := 0
                            }
                        }
                        default {
                            // cb is a circuit breaker in the for loop since there's
                            //  no said feature for inline assembly loops
                            // cb = 1 - don't breaker
                            // cb = 0 - break
                            let cb := 1
                            // get the keccak hash to get the contents of the array
                            mstore(0x0, _preBytes.slot)
                            let sc := keccak256(0x0, 0x20)
                            let mc := add(_postBytes, 0x20)
                            let end := add(mc, mlength)
                            // the next line is the loop condition:
                            // while(uint256(mc < end) + cb == 2)
                            // solhint-disable-next-line no-empty-blocks
                            for {
                            } eq(add(lt(mc, end), cb), 2) {
                                sc := add(sc, 1)
                                mc := add(mc, 0x20)
                            } {
                                if iszero(eq(sload(sc), mload(mc))) {
                                    // unsuccess:
                                    success := 0
                                    cb := 0
                                }
                            }
                        }
                    }
                }
                default {
                    // unsuccess:
                    success := 0
                }
            }
            return success;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.17;
    import { IDiamondCut } from "../Interfaces/IDiamondCut.sol";
    import { LibUtil } from "../Libraries/LibUtil.sol";
    import { OnlyContractOwner } from "../Errors/GenericErrors.sol";
    /// Implementation of EIP-2535 Diamond Standard
    /// https://eips.ethereum.org/EIPS/eip-2535
    library LibDiamond {
        bytes32 internal constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage");
        // Diamond specific errors
        error IncorrectFacetCutAction();
        error NoSelectorsInFace();
        error FunctionAlreadyExists();
        error FacetAddressIsZero();
        error FacetAddressIsNotZero();
        error FacetContainsNoCode();
        error FunctionDoesNotExist();
        error FunctionIsImmutable();
        error InitZeroButCalldataNotEmpty();
        error CalldataEmptyButInitNotZero();
        error InitReverted();
        // ----------------
        struct FacetAddressAndPosition {
            address facetAddress;
            uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array
        }
        struct FacetFunctionSelectors {
            bytes4[] functionSelectors;
            uint256 facetAddressPosition; // position of facetAddress in facetAddresses array
        }
        struct DiamondStorage {
            // maps function selector to the facet address and
            // the position of the selector in the facetFunctionSelectors.selectors array
            mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition;
            // maps facet addresses to function selectors
            mapping(address => FacetFunctionSelectors) facetFunctionSelectors;
            // facet addresses
            address[] facetAddresses;
            // Used to query if a contract implements an interface.
            // Used to implement ERC-165.
            mapping(bytes4 => bool) supportedInterfaces;
            // owner of the contract
            address contractOwner;
        }
        function diamondStorage() internal pure returns (DiamondStorage storage ds) {
            bytes32 position = DIAMOND_STORAGE_POSITION;
            // solhint-disable-next-line no-inline-assembly
            assembly {
                ds.slot := position
            }
        }
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        function setContractOwner(address _newOwner) internal {
            DiamondStorage storage ds = diamondStorage();
            address previousOwner = ds.contractOwner;
            ds.contractOwner = _newOwner;
            emit OwnershipTransferred(previousOwner, _newOwner);
        }
        function contractOwner() internal view returns (address contractOwner_) {
            contractOwner_ = diamondStorage().contractOwner;
        }
        function enforceIsContractOwner() internal view {
            if (msg.sender != diamondStorage().contractOwner) revert OnlyContractOwner();
        }
        event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata);
        // Internal function version of diamondCut
        function diamondCut(
            IDiamondCut.FacetCut[] memory _diamondCut,
            address _init,
            bytes memory _calldata
        ) internal {
            for (uint256 facetIndex; facetIndex < _diamondCut.length; ) {
                IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action;
                if (action == IDiamondCut.FacetCutAction.Add) {
                    addFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
                } else if (action == IDiamondCut.FacetCutAction.Replace) {
                    replaceFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
                } else if (action == IDiamondCut.FacetCutAction.Remove) {
                    removeFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
                } else {
                    revert IncorrectFacetCutAction();
                }
                unchecked {
                    ++facetIndex;
                }
            }
            emit DiamondCut(_diamondCut, _init, _calldata);
            initializeDiamondCut(_init, _calldata);
        }
        function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
            if (_functionSelectors.length == 0) {
                revert NoSelectorsInFace();
            }
            DiamondStorage storage ds = diamondStorage();
            if (LibUtil.isZeroAddress(_facetAddress)) {
                revert FacetAddressIsZero();
            }
            uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
            // add new facet address if it does not exist
            if (selectorPosition == 0) {
                addFacet(ds, _facetAddress);
            }
            for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; ) {
                bytes4 selector = _functionSelectors[selectorIndex];
                address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
                if (!LibUtil.isZeroAddress(oldFacetAddress)) {
                    revert FunctionAlreadyExists();
                }
                addFunction(ds, selector, selectorPosition, _facetAddress);
                unchecked {
                    ++selectorPosition;
                    ++selectorIndex;
                }
            }
        }
        function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
            if (_functionSelectors.length == 0) {
                revert NoSelectorsInFace();
            }
            DiamondStorage storage ds = diamondStorage();
            if (LibUtil.isZeroAddress(_facetAddress)) {
                revert FacetAddressIsZero();
            }
            uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
            // add new facet address if it does not exist
            if (selectorPosition == 0) {
                addFacet(ds, _facetAddress);
            }
            for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; ) {
                bytes4 selector = _functionSelectors[selectorIndex];
                address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
                if (oldFacetAddress == _facetAddress) {
                    revert FunctionAlreadyExists();
                }
                removeFunction(ds, oldFacetAddress, selector);
                addFunction(ds, selector, selectorPosition, _facetAddress);
                unchecked {
                    ++selectorPosition;
                    ++selectorIndex;
                }
            }
        }
        function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
            if (_functionSelectors.length == 0) {
                revert NoSelectorsInFace();
            }
            DiamondStorage storage ds = diamondStorage();
            // if function does not exist then do nothing and return
            if (!LibUtil.isZeroAddress(_facetAddress)) {
                revert FacetAddressIsNotZero();
            }
            for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; ) {
                bytes4 selector = _functionSelectors[selectorIndex];
                address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
                removeFunction(ds, oldFacetAddress, selector);
                unchecked {
                    ++selectorIndex;
                }
            }
        }
        function addFacet(DiamondStorage storage ds, address _facetAddress) internal {
            enforceHasContractCode(_facetAddress);
            ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds.facetAddresses.length;
            ds.facetAddresses.push(_facetAddress);
        }
        function addFunction(
            DiamondStorage storage ds,
            bytes4 _selector,
            uint96 _selectorPosition,
            address _facetAddress
        ) internal {
            ds.selectorToFacetAndPosition[_selector].functionSelectorPosition = _selectorPosition;
            ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(_selector);
            ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress;
        }
        function removeFunction(
            DiamondStorage storage ds,
            address _facetAddress,
            bytes4 _selector
        ) internal {
            if (LibUtil.isZeroAddress(_facetAddress)) {
                revert FunctionDoesNotExist();
            }
            // an immutable function is a function defined directly in a diamond
            if (_facetAddress == address(this)) {
                revert FunctionIsImmutable();
            }
            // replace selector with last selector, then delete last selector
            uint256 selectorPosition = ds.selectorToFacetAndPosition[_selector].functionSelectorPosition;
            uint256 lastSelectorPosition = ds.facetFunctionSelectors[_facetAddress].functionSelectors.length - 1;
            // if not the same then replace _selector with lastSelector
            if (selectorPosition != lastSelectorPosition) {
                bytes4 lastSelector = ds.facetFunctionSelectors[_facetAddress].functionSelectors[lastSelectorPosition];
                ds.facetFunctionSelectors[_facetAddress].functionSelectors[selectorPosition] = lastSelector;
                ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition = uint96(selectorPosition);
            }
            // delete the last selector
            ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop();
            delete ds.selectorToFacetAndPosition[_selector];
            // if no more selectors for facet address then delete the facet address
            if (lastSelectorPosition == 0) {
                // replace facet address with last facet address and delete last facet address
                uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1;
                uint256 facetAddressPosition = ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
                if (facetAddressPosition != lastFacetAddressPosition) {
                    address lastFacetAddress = ds.facetAddresses[lastFacetAddressPosition];
                    ds.facetAddresses[facetAddressPosition] = lastFacetAddress;
                    ds.facetFunctionSelectors[lastFacetAddress].facetAddressPosition = facetAddressPosition;
                }
                ds.facetAddresses.pop();
                delete ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
            }
        }
        function initializeDiamondCut(address _init, bytes memory _calldata) internal {
            if (LibUtil.isZeroAddress(_init)) {
                if (_calldata.length != 0) {
                    revert InitZeroButCalldataNotEmpty();
                }
            } else {
                if (_calldata.length == 0) {
                    revert CalldataEmptyButInitNotZero();
                }
                if (_init != address(this)) {
                    enforceHasContractCode(_init);
                }
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory error) = _init.delegatecall(_calldata);
                if (!success) {
                    if (error.length > 0) {
                        // bubble up the error
                        revert(string(error));
                    } else {
                        revert InitReverted();
                    }
                }
            }
        }
        function enforceHasContractCode(address _contract) internal view {
            uint256 contractSize;
            // solhint-disable-next-line no-inline-assembly
            assembly {
                contractSize := extcodesize(_contract)
            }
            if (contractSize == 0) {
                revert FacetContainsNoCode();
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.17;
    import "./LibBytes.sol";
    library LibUtil {
        using LibBytes for bytes;
        function getRevertMsg(bytes memory _res) internal pure returns (string memory) {
            // If the _res length is less than 68, then the transaction failed silently (without a revert message)
            if (_res.length < 68) return "Transaction reverted silently";
            bytes memory revertData = _res.slice(4, _res.length - 4); // Remove the selector which is the first 4 bytes
            return abi.decode(revertData, (string)); // All that remains is the revert string
        }
        /// @notice Determines whether the given address is the zero address
        /// @param addr The address to verify
        /// @return Boolean indicating if the address is the zero address
        function isZeroAddress(address addr) internal pure returns (bool) {
            return addr == address(0);
        }
    }