// File: src/Errors/GenericErrors.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

error AlreadyInitialized();
error CannotAuthoriseSelf();
error CannotBridgeToSameNetwork();
error ContractCallNotAllowed();
error CumulativeSlippageTooHigh(uint256 minAmount, uint256 receivedAmount);
error ExternalCallFailed();
error InformationMismatch();
error InsufficientBalance(uint256 required, uint256 balance);
error InvalidAmount();
error InvalidCallData();
error InvalidConfig();
error InvalidContract();
error InvalidDestinationChain();
error InvalidFallbackAddress();
error InvalidReceiver();
error InvalidSendingToken();
error NativeAssetNotSupported();
error NativeAssetTransferFailed();
error NoSwapDataProvided();
error NoSwapFromZeroBalance();
error NotAContract();
error NotInitialized();
error NoTransferToNullAddress();
error NullAddrIsNotAnERC20Token();
error NullAddrIsNotAValidSpender();
error OnlyContractOwner();
error RecoveryAddressCannotBeZero();
error ReentrancyError();
error TokenNotSupported();
error UnAuthorized();
error UnsupportedChainId(uint256 chainId);
error ZeroAmount();
error TokenAddressIsZero();
error ZeroPostSwapBalance();
error NativeValueWithERC();
error InvalidBridgeConfigLength();
error InvalidCaller();
error CannotDepositNativeToken();
error NotEnoughBalance(uint256 requested, uint256 available);
error IsNotOwner();

// File: src/Interfaces/IDiamondCut.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

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);
}

// File: src/KanaDiamond.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;

import {LibDiamond} from "./Libraries/LibDiamond.sol";
import {IDiamondCut} from "./Interfaces/IDiamondCut.sol";
import {LibUtil} from "./Libraries/LibUtil.sol";

contract KanaDiamond {
	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 {}
}

// File: src/Libraries/LibBytes.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

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;
	}
}

// File: src/Libraries/LibDiamond.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

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();
		}
	}
}

// File: src/Libraries/LibUtil.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

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);
	}
}