ETH Price: $1,899.78 (+0.66%)

Transaction Decoder

Block:
22027802 at Mar-12-2025 02:28:35 AM +UTC
Transaction Fee:
0.000134536080295979 ETH $0.26
Gas Used:
183,899 Gas / 0.731575921 Gwei

Emitted Events:

391 FeeCollector.FeesCollected( _token=0x00000000...000000000, _integrator=0x1Bcc58D1...d61C0C2a0, _integratorFee=213751780467055, _lifiFee=11250093708792 )
392 LiFiDiamond.0x7bfdfdb5e3a3776976e53cb0607060f54c5312701c8cba1155cc4d5394440b38( 0x7bfdfdb5e3a3776976e53cb0607060f54c5312701c8cba1155cc4d5394440b38, 32b1400727280ceb3b0368cf3ac7c548e249b8e1405ef19dec85d79eec321c0c, 000000000000000000000000bd6c7b0d2f68c2b7805d88388319cfb6ecb50ea9, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000000005e0b0f6f1084fc, 000000000000000000000000000000000000000000000000005d3e6c1a47cd95, 0000000000000000000000000000000000000000000000000000000067d0f153 )
393 MayanSwift.OrderCreated( key=7C1749DFDA7A619FAE96783ABCD06800380300598052864307BC0F240EECDE40 )
394 MayanForwarder.ForwardedEth( mayanProtocol=MayanSwift, protocolData=0xB866E173000000000000000000000000913BC6CC6C8BC73E1E0812AE96365E4F49558E2A000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023E410F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000652300000000000000000000000000000000000000000000000000000000000059600000000000000000000000000000000000000000000000000000000067D0F8A3536D328DD5F9EB4ABD4E7D8815CA0E6D7FA632D119F915CE3B383497BFDEFE170000000000000000000000000000000000000000000000000000000000000001F31E1B125DC6B5BDD16C2EE0E6E18DF1858279EA5FD8E8F3A5584ADB9C19D1C800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002BFE17C18560FBCAE2157C961C3FC1B52A9C33ACF00EF3DB4F49059EB003E5714 )
395 LiFiDiamond.0x7be3e48a8a8b4d32138937e1809ac83481fffe48e49bb60e43ed1d3d50349e4c( 0x7be3e48a8a8b4d32138937e1809ac83481fffe48e49bb60e43ed1d3d50349e4c, 0x32b1400727280ceb3b0368cf3ac7c548e249b8e1405ef19dec85d79eec321c0c, 0x000000000000000000000000000000000000000000000000000416edef1601be, 536d328dd5f9eb4abd4e7d8815ca0e6d7fa632d119f915ce3b383497bfdefe17 )
396 LiFiDiamond.0xcba69f43792f9f399347222505213b55af8e0b0b54b893085c2e27ecbe1644f1( 0xcba69f43792f9f399347222505213b55af8e0b0b54b893085c2e27ecbe1644f1, 0000000000000000000000000000000000000000000000000000000000000020, 32b1400727280ceb3b0368cf3ac7c548e249b8e1405ef19dec85d79eec321c0c, 0000000000000000000000000000000000000000000000000000000000000140, 0000000000000000000000000000000000000000000000000000000000000180, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000011f111f111f111f111f111f111f111f111f111f1, 000000000000000000000000000000000000000000000000005d3e6a81d89000, 000000000000000000000000000000000000000000000000000416edef1601be, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000005, 6d6179616e000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000007, 7068616e746f6d00000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x913BC6cC...F49558e2a
0.027951180524255342 Eth
Nonce: 11
0.001345842569783516 Eth
Nonce: 12
0.026605337954471826
(beaverbuild)
13.695483581019246442 Eth13.695498109040246442 Eth0.000014528021
0xbD6C7B0d...6EcB50eA9 484.131116416699851155 Eth484.131341418574027002 Eth0.000225001874175847
0xC38e4e6A...7CA1c2338 25.128531521916062849 Eth25.154777321916062849 Eth0.0262458

Execution Trace

ETH 0.026470808726570236 LiFiDiamond.30c48952( )
  • ETH 0.026470808726570236 MayanFacet.swapAndStartBridgeTokensViaMayan( _bridgeData=[{name:transactionId, type:bytes32, order:1, indexed:false, value:32B1400727280CEB3B0368CF3AC7C548E249B8E1405EF19DEC85D79EEC321C0C, valueString:32B1400727280CEB3B0368CF3AC7C548E249B8E1405EF19DEC85D79EEC321C0C}, {name:bridge, type:string, order:2, indexed:false, value:mayan, valueString:mayan}, {name:integrator, type:string, order:3, indexed:false, value:phantom, valueString:phantom}, {name:referrer, type:address, order:4, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:sendingAssetId, type:address, order:5, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:receiver, type:address, order:6, indexed:false, value:0x11f111f111f111F111f111f111F111f111f111F1, valueString:0x11f111f111f111F111f111f111F111f111f111F1}, {name:minAmount, type:uint256, order:7, indexed:false, value:26245806852394389, valueString:26245806852394389}, {name:destinationChainId, type:uint256, order:8, indexed:false, value:1151111081099710, valueString:1151111081099710}, {name:hasSourceSwaps, type:bool, order:9, indexed:false, value:true, valueString:True}, {name:hasDestinationCall, type:bool, order:10, indexed:false, value:false, valueString:False}], _swapData=, _mayanData=[{name:nonEVMReceiver, type:bytes32, order:1, indexed:false, value:536D328DD5F9EB4ABD4E7D8815CA0E6D7FA632D119F915CE3B383497BFDEFE17, valueString:536D328DD5F9EB4ABD4E7D8815CA0E6D7FA632D119F915CE3B383497BFDEFE17}, {name:mayanProtocol, type:address, order:2, indexed:false, value:0xC38e4e6A15593f908255214653d3D947CA1c2338, valueString:0xC38e4e6A15593f908255214653d3D947CA1c2338}, {name:protocolData, type:bytes, order:3, indexed:false, value:0xB866E173000000000000000000000000913BC6CC6C8BC73E1E0812AE96365E4F49558E2A000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023E410F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000652300000000000000000000000000000000000000000000000000000000000059600000000000000000000000000000000000000000000000000000000067D0F8A3536D328DD5F9EB4ABD4E7D8815CA0E6D7FA632D119F915CE3B383497BFDEFE170000000000000000000000000000000000000000000000000000000000000001F31E1B125DC6B5BDD16C2EE0E6E18DF1858279EA5FD8E8F3A5584ADB9C19D1C800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002BFE17C18560FBCAE2157C961C3FC1B52A9C33ACF00EF3DB4F49059EB003E5714, valueString:0xB866E173000000000000000000000000913BC6CC6C8BC73E1E0812AE96365E4F49558E2A000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023E410F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000652300000000000000000000000000000000000000000000000000000000000059600000000000000000000000000000000000000000000000000000000067D0F8A3536D328DD5F9EB4ABD4E7D8815CA0E6D7FA632D119F915CE3B383497BFDEFE170000000000000000000000000000000000000000000000000000000000000001F31E1B125DC6B5BDD16C2EE0E6E18DF1858279EA5FD8E8F3A5584ADB9C19D1C800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002BFE17C18560FBCAE2157C961C3FC1B52A9C33ACF00EF3DB4F49059EB003E5714}] )
    • ETH 0.026470808726570236 FeeCollector.collectNativeFees( integratorFee=213751780467055, lifiFee=11250093708792, integratorAddress=0x1Bcc58D165e5374D7B492B21c0a572Fd61C0C2a0 )
      • ETH 0.026245806852394389 LiFiDiamond.CALL( )
      • ETH 0.0262458 MayanForwarder.forwardEth( mayanProtocol=0xC38e4e6A15593f908255214653d3D947CA1c2338, protocolData=0xB866E173000000000000000000000000913BC6CC6C8BC73E1E0812AE96365E4F49558E2A000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023E410F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000652300000000000000000000000000000000000000000000000000000000000059600000000000000000000000000000000000000000000000000000000067D0F8A3536D328DD5F9EB4ABD4E7D8815CA0E6D7FA632D119F915CE3B383497BFDEFE170000000000000000000000000000000000000000000000000000000000000001F31E1B125DC6B5BDD16C2EE0E6E18DF1858279EA5FD8E8F3A5584ADB9C19D1C800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002BFE17C18560FBCAE2157C961C3FC1B52A9C33ACF00EF3DB4F49059EB003E5714 )
        • ETH 0.0262458 MayanSwift.createOrderWithEth( params=[{name:trader, type:bytes32, order:1, indexed:false, value:000000000000000000000000913BC6CC6C8BC73E1E0812AE96365E4F49558E2A, valueString:000000000000000000000000913BC6CC6C8BC73E1E0812AE96365E4F49558E2A}, {name:tokenOut, type:bytes32, order:2, indexed:false, value:0000000000000000000000000000000000000000000000000000000000000000, valueString:0000000000000000000000000000000000000000000000000000000000000000}, {name:minAmountOut, type:uint64, order:3, indexed:false, value:37634319, valueString:37634319}, {name:gasDrop, type:uint64, order:4, indexed:false, value:0, valueString:0}, {name:cancelFee, type:uint64, order:5, indexed:false, value:25891, valueString:25891}, {name:refundFee, type:uint64, order:6, indexed:false, value:22880, valueString:22880}, {name:deadline, type:uint64, order:7, indexed:false, value:1741748387, valueString:1741748387}, {name:destAddr, type:bytes32, order:8, indexed:false, value:536D328DD5F9EB4ABD4E7D8815CA0E6D7FA632D119F915CE3B383497BFDEFE17, valueString:536D328DD5F9EB4ABD4E7D8815CA0E6D7FA632D119F915CE3B383497BFDEFE17}, {name:destChainId, type:uint16, order:9, indexed:false, value:1, valueString:1}, {name:referrerAddr, type:bytes32, order:10, indexed:false, value:F31E1B125DC6B5BDD16C2EE0E6E18DF1858279EA5FD8E8F3A5584ADB9C19D1C8, valueString:F31E1B125DC6B5BDD16C2EE0E6E18DF1858279EA5FD8E8F3A5584ADB9C19D1C8}, {name:referrerBps, type:uint8, order:11, indexed:false, value:0, valueString:0}, {name:auctionMode, type:uint8, order:12, indexed:false, value:2, valueString:2}, {name:random, type:bytes32, order:13, indexed:false, value:BFE17C18560FBCAE2157C961C3FC1B52A9C33ACF00EF3DB4F49059EB003E5714, valueString:BFE17C18560FBCAE2157C961C3FC1B52A9C33ACF00EF3DB4F49059EB003E5714}] ) => ( orderHash=7C1749DFDA7A619FAE96783ABCD06800380300598052864307BC0F240EECDE40 )
          • FeeManager.calcProtocolBps( amountIn=2624580, tokenIn=0x0000000000000000000000000000000000000000, tokenOut=0000000000000000000000000000000000000000000000000000000000000000, destChain=1, referrerBps=0 ) => ( 3 )
          • Wormhole.STATICCALL( )
            • Implementation.DELEGATECALL( )
            • Wormhole.STATICCALL( )
              • Implementation.DELEGATECALL( )
              • ETH 0.000000006852394389 0x913bc6cc6c8bc73e1e0812ae96365e4f49558e2a.CALL( )
                File 1 of 8: LiFiDiamond
                // 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);
                    }
                }
                

                File 2 of 8: FeeCollector
                // SPDX-License-Identifier: UNLICENSED
                pragma solidity 0.8.13;
                import { LibAsset } from "../Libraries/LibAsset.sol";
                /// @title Fee Collector
                /// @author LI.FI (https://li.fi)
                /// @notice Provides functionality for collecting integrator fees
                contract FeeCollector {
                    /// State ///
                    // Integrator -> TokenAddress -> Balance
                    mapping(address => mapping(address => uint256)) private _balances;
                    // TokenAddress -> Balance
                    mapping(address => uint256) private _lifiBalances;
                    address public owner;
                    address public pendingOwner;
                    /// Errors ///
                    error Unauthorized(address);
                    error NoNullOwner();
                    error NewOwnerMustNotBeSelf();
                    error NoPendingOwnershipTransfer();
                    error NotPendingOwner();
                    error TransferFailure();
                    /// Events ///
                    event FeesCollected(address indexed _token, address indexed _integrator, uint256 _integratorFee, uint256 _lifiFee);
                    event FeesWithdrawn(address indexed _token, address indexed _to, uint256 _amount);
                    event LiFiFeesWithdrawn(address indexed _token, address indexed _to, uint256 _amount);
                    event OwnershipTransferRequested(address indexed _from, address indexed _to);
                    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                    /// Constructor ///
                    constructor(address _owner) {
                        owner = _owner;
                    }
                    /// External Methods ///
                    /// @notice Collects fees for the integrator
                    /// @param tokenAddress address of the token to collect fees for
                    /// @param integratorFee amount of fees to collect going to the integrator
                    /// @param lifiFee amount of fees to collect going to lifi
                    /// @param integratorAddress address of the integrator
                    function collectTokenFees(
                        address tokenAddress,
                        uint256 integratorFee,
                        uint256 lifiFee,
                        address integratorAddress
                    ) external {
                        LibAsset.depositAsset(tokenAddress, integratorFee + lifiFee);
                        _balances[integratorAddress][tokenAddress] += integratorFee;
                        _lifiBalances[tokenAddress] += lifiFee;
                        emit FeesCollected(tokenAddress, integratorAddress, integratorFee, lifiFee);
                    }
                    /// @notice Collects fees for the integrator in native token
                    /// @param integratorFee amount of fees to collect going to the integrator
                    /// @param lifiFee amount of fees to collect going to lifi
                    /// @param integratorAddress address of the integrator
                    function collectNativeFees(
                        uint256 integratorFee,
                        uint256 lifiFee,
                        address integratorAddress
                    ) external payable {
                        _balances[integratorAddress][LibAsset.NULL_ADDRESS] += integratorFee;
                        _lifiBalances[LibAsset.NULL_ADDRESS] += lifiFee;
                        uint256 remaining = msg.value - (integratorFee + lifiFee);
                        // Prevent extra native token from being locked in the contract
                        if (remaining > 0) {
                            (bool success, ) = msg.sender.call{ value: remaining }("");
                            if (!success) {
                                revert TransferFailure();
                            }
                        }
                        emit FeesCollected(LibAsset.NULL_ADDRESS, integratorAddress, integratorFee, lifiFee);
                    }
                    /// @notice Withdraw fees and sends to the integrator
                    /// @param tokenAddress address of the token to withdraw fees for
                    function withdrawIntegratorFees(address tokenAddress) external {
                        uint256 balance = _balances[msg.sender][tokenAddress];
                        if (balance == 0) {
                            return;
                        }
                        _balances[msg.sender][tokenAddress] = 0;
                        LibAsset.transferAsset(tokenAddress, payable(msg.sender), balance);
                        emit FeesWithdrawn(tokenAddress, msg.sender, balance);
                    }
                    /// @notice Batch withdraw fees and sends to the integrator
                    /// @param tokenAddresses addresses of the tokens to withdraw fees for
                    function batchWithdrawIntegratorFees(address[] memory tokenAddresses) external {
                        uint256 length = tokenAddresses.length;
                        uint256 balance;
                        for (uint256 i = 0; i < length; i++) {
                            balance = _balances[msg.sender][tokenAddresses[i]];
                            if (balance == 0) {
                                continue;
                            }
                            _balances[msg.sender][tokenAddresses[i]] = 0;
                            LibAsset.transferAsset(tokenAddresses[i], payable(msg.sender), balance);
                            emit FeesWithdrawn(tokenAddresses[i], msg.sender, balance);
                        }
                    }
                    /// @notice Withdraws fees and sends to lifi
                    /// @param tokenAddress address of the token to withdraw fees for
                    function withdrawLifiFees(address tokenAddress) external {
                        _enforceIsContractOwner();
                        uint256 balance = _lifiBalances[tokenAddress];
                        if (balance == 0) {
                            return;
                        }
                        _lifiBalances[tokenAddress] = 0;
                        LibAsset.transferAsset(tokenAddress, payable(owner), balance);
                        emit LiFiFeesWithdrawn(tokenAddress, msg.sender, balance);
                    }
                    /// @notice Batch withdraws fees and sends to lifi
                    /// @param tokenAddresses addresses of the tokens to withdraw fees for
                    function batchWithdrawLifiFees(address[] memory tokenAddresses) external {
                        _enforceIsContractOwner();
                        uint256 length = tokenAddresses.length;
                        uint256 balance;
                        for (uint256 i = 0; i < length; i++) {
                            balance = _lifiBalances[tokenAddresses[i]];
                            if (balance == 0) {
                                continue;
                            }
                            _lifiBalances[tokenAddresses[i]] = 0;
                            LibAsset.transferAsset(tokenAddresses[i], payable(owner), balance);
                            emit LiFiFeesWithdrawn(tokenAddresses[i], msg.sender, balance);
                        }
                    }
                    /// @notice Returns the balance of the integrator
                    /// @param integratorAddress address of the integrator
                    /// @param tokenAddress address of the token to get the balance of
                    function getTokenBalance(address integratorAddress, address tokenAddress) external view returns (uint256) {
                        return _balances[integratorAddress][tokenAddress];
                    }
                    /// @notice Returns the balance of lifi
                    /// @param tokenAddress address of the token to get the balance of
                    function getLifiTokenBalance(address tokenAddress) external view returns (uint256) {
                        return _lifiBalances[tokenAddress];
                    }
                    /// @notice Intitiates transfer of ownership to a new address
                    /// @param _newOwner the address to transfer ownership to
                    function transferOwnership(address _newOwner) external {
                        _enforceIsContractOwner();
                        if (_newOwner == LibAsset.NULL_ADDRESS) revert NoNullOwner();
                        if (_newOwner == owner) revert NewOwnerMustNotBeSelf();
                        pendingOwner = _newOwner;
                        emit OwnershipTransferRequested(msg.sender, pendingOwner);
                    }
                    /// @notice Cancel transfer of ownership
                    function cancelOnwershipTransfer() external {
                        _enforceIsContractOwner();
                        if (pendingOwner == LibAsset.NULL_ADDRESS) revert NoPendingOwnershipTransfer();
                        pendingOwner = LibAsset.NULL_ADDRESS;
                    }
                    /// @notice Confirms transfer of ownership to the calling address (msg.sender)
                    function confirmOwnershipTransfer() external {
                        if (msg.sender != pendingOwner) revert NotPendingOwner();
                        owner = pendingOwner;
                        pendingOwner = LibAsset.NULL_ADDRESS;
                        emit OwnershipTransferred(owner, pendingOwner);
                    }
                    /// Private Methods ///
                    /// @notice Ensures that the calling address is the owner of the contract
                    function _enforceIsContractOwner() private view {
                        if (msg.sender != owner) {
                            revert Unauthorized(msg.sender);
                        }
                    }
                }
                // SPDX-License-Identifier: UNLICENSED
                pragma solidity 0.8.13;
                import { NullAddrIsNotAnERC20Token, NullAddrIsNotAValidSpender, NoTransferToNullAddress, InvalidAmount, NativeValueWithERC, NativeAssetTransferFailed } from "../Errors/GenericErrors.sol";
                import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
                import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                /// @title LibAsset
                /// @author Connext <[email protected]>
                /// @notice This library contains helpers for dealing with onchain transfers
                ///         of assets, including accounting for the native asset `assetId`
                ///         conventions and any noncompliant ERC20 transfers
                library LibAsset {
                    uint256 private constant MAX_INT = type(uint256).max;
                    address internal constant NULL_ADDRESS = 0x0000000000000000000000000000000000000000; //address(0)
                    /// @dev All native assets use the empty address for their asset id
                    ///      by convention
                    address internal constant NATIVE_ASSETID = NULL_ADDRESS; //address(0)
                    /// @notice Gets the balance of the inheriting contract for the given asset
                    /// @param assetId The asset identifier to get the balance of
                    /// @return Balance held by contracts using this library
                    function getOwnBalance(address assetId) internal view returns (uint256) {
                        return assetId == NATIVE_ASSETID ? address(this).balance : IERC20(assetId).balanceOf(address(this));
                    }
                    /// @notice Transfers ether from the inheriting contract to a given
                    ///         recipient
                    /// @param recipient Address to send ether to
                    /// @param amount Amount to send to given recipient
                    function transferNativeAsset(address payable recipient, uint256 amount) private {
                        if (recipient == NULL_ADDRESS) revert NoTransferToNullAddress();
                        // solhint-disable-next-line avoid-low-level-calls
                        (bool success, ) = recipient.call{ value: amount }("");
                        if (!success) revert NativeAssetTransferFailed();
                    }
                    /// @notice Gives MAX approval for another address to spend tokens
                    /// @param assetId Token address to transfer
                    /// @param spender Address to give spend approval to
                    /// @param amount Amount to approve for spending
                    function maxApproveERC20(
                        IERC20 assetId,
                        address spender,
                        uint256 amount
                    ) internal {
                        if (address(assetId) == NATIVE_ASSETID) return;
                        if (spender == NULL_ADDRESS) revert NullAddrIsNotAValidSpender();
                        uint256 allowance = assetId.allowance(address(this), spender);
                        if (allowance < amount) SafeERC20.safeApprove(IERC20(assetId), spender, MAX_INT);
                    }
                    /// @notice Transfers tokens from the inheriting contract to a given
                    ///         recipient
                    /// @param assetId Token address to transfer
                    /// @param recipient Address to send token to
                    /// @param amount Amount to send to given recipient
                    function transferERC20(
                        address assetId,
                        address recipient,
                        uint256 amount
                    ) private {
                        if (isNativeAsset(assetId)) revert NullAddrIsNotAnERC20Token();
                        SafeERC20.safeTransfer(IERC20(assetId), recipient, amount);
                    }
                    /// @notice Transfers tokens from a sender to a given recipient
                    /// @param assetId Token address to transfer
                    /// @param from Address of sender/owner
                    /// @param to Address of recipient/spender
                    /// @param amount Amount to transfer from owner to spender
                    function transferFromERC20(
                        address assetId,
                        address from,
                        address to,
                        uint256 amount
                    ) internal {
                        if (assetId == NATIVE_ASSETID) revert NullAddrIsNotAnERC20Token();
                        if (to == NULL_ADDRESS) revert NoTransferToNullAddress();
                        SafeERC20.safeTransferFrom(IERC20(assetId), from, to, amount);
                    }
                    /// @notice Deposits an asset into the contract and performs checks to avoid NativeValueWithERC
                    /// @param tokenId Token to deposit
                    /// @param amount Amount to deposit
                    /// @param isNative Wether the token is native or ERC20
                    function depositAsset(
                        address tokenId,
                        uint256 amount,
                        bool isNative
                    ) internal {
                        if (amount == 0) revert InvalidAmount();
                        if (isNative) {
                            if (msg.value != amount) revert InvalidAmount();
                        } else {
                            if (msg.value != 0) revert NativeValueWithERC();
                            uint256 _fromTokenBalance = LibAsset.getOwnBalance(tokenId);
                            LibAsset.transferFromERC20(tokenId, msg.sender, address(this), amount);
                            if (LibAsset.getOwnBalance(tokenId) - _fromTokenBalance != amount) revert InvalidAmount();
                        }
                    }
                    /// @notice Overload for depositAsset(address tokenId, uint256 amount, bool isNative)
                    /// @param tokenId Token to deposit
                    /// @param amount Amount to deposit
                    function depositAsset(address tokenId, uint256 amount) internal {
                        return depositAsset(tokenId, amount, tokenId == NATIVE_ASSETID);
                    }
                    /// @notice Determines whether the given assetId is the native asset
                    /// @param assetId The asset identifier to evaluate
                    /// @return Boolean indicating if the asset is the native asset
                    function isNativeAsset(address assetId) internal pure returns (bool) {
                        return assetId == NATIVE_ASSETID;
                    }
                    /// @notice Wrapper function to transfer a given asset (native or erc20) to
                    ///         some recipient. Should handle all non-compliant return value
                    ///         tokens as well by using the SafeERC20 contract by open zeppelin.
                    /// @param assetId Asset id for transfer (address(0) for native asset,
                    ///                token address for erc20s)
                    /// @param recipient Address to send asset to
                    /// @param amount Amount to send to given recipient
                    function transferAsset(
                        address assetId,
                        address payable recipient,
                        uint256 amount
                    ) internal {
                        (assetId == NATIVE_ASSETID)
                            ? transferNativeAsset(recipient, amount)
                            : transferERC20(assetId, recipient, amount);
                    }
                    /// @dev Checks whether the given address is a contract and contains code
                    function isContract(address _contractAddr) internal view returns (bool) {
                        uint256 size;
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            size := extcodesize(_contractAddr)
                        }
                        return size > 0;
                    }
                }
                // SPDX-License-Identifier: MIT
                pragma solidity 0.8.13;
                error InvalidAmount();
                error TokenAddressIsZero();
                error CannotBridgeToSameNetwork();
                error ZeroPostSwapBalance();
                error InvalidBridgeConfigLength();
                error NoSwapDataProvided();
                error NativeValueWithERC();
                error ContractCallNotAllowed();
                error NullAddrIsNotAValidSpender();
                error NullAddrIsNotAnERC20Token();
                error NoTransferToNullAddress();
                error NativeAssetTransferFailed();
                error InvalidContract();
                error InvalidConfig();
                // SPDX-License-Identifier: MIT
                pragma solidity ^0.8.0;
                import "../IERC20.sol";
                import "../../../utils/Address.sol";
                /**
                 * @title SafeERC20
                 * @dev Wrappers around ERC20 operations that throw on failure (when the token
                 * contract returns false). Tokens that return no value (and instead revert or
                 * throw on failure) are also supported, non-reverting calls are assumed to be
                 * successful.
                 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                 */
                library SafeERC20 {
                    using Address for address;
                    function safeTransfer(
                        IERC20 token,
                        address to,
                        uint256 value
                    ) internal {
                        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                    }
                    function safeTransferFrom(
                        IERC20 token,
                        address from,
                        address to,
                        uint256 value
                    ) internal {
                        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                    }
                    /**
                     * @dev Deprecated. This function has issues similar to the ones found in
                     * {IERC20-approve}, and its usage is discouraged.
                     *
                     * Whenever possible, use {safeIncreaseAllowance} and
                     * {safeDecreaseAllowance} instead.
                     */
                    function safeApprove(
                        IERC20 token,
                        address spender,
                        uint256 value
                    ) internal {
                        // safeApprove should only be called when setting an initial allowance,
                        // or when resetting it to zero. To increase and decrease it, use
                        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                        require(
                            (value == 0) || (token.allowance(address(this), spender) == 0),
                            "SafeERC20: approve from non-zero to non-zero allowance"
                        );
                        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                    }
                    function safeIncreaseAllowance(
                        IERC20 token,
                        address spender,
                        uint256 value
                    ) internal {
                        uint256 newAllowance = token.allowance(address(this), spender) + value;
                        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                    }
                    function safeDecreaseAllowance(
                        IERC20 token,
                        address spender,
                        uint256 value
                    ) internal {
                        unchecked {
                            uint256 oldAllowance = token.allowance(address(this), spender);
                            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                            uint256 newAllowance = oldAllowance - value;
                            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                        }
                    }
                    /**
                     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                     * on the return value: the return value is optional (but if data is returned, it must not be false).
                     * @param token The token targeted by the call.
                     * @param data The call data (encoded using abi.encode or one of its variants).
                     */
                    function _callOptionalReturn(IERC20 token, bytes memory data) private {
                        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                        // the target address contains contract code and also asserts for success in the low-level call.
                        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                        if (returndata.length > 0) {
                            // Return data is optional
                            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                        }
                    }
                }
                // SPDX-License-Identifier: MIT
                pragma solidity ^0.8.0;
                /**
                 * @dev Interface of the ERC20 standard as defined in the EIP.
                 */
                interface IERC20 {
                    /**
                     * @dev Returns the amount of tokens in existence.
                     */
                    function totalSupply() external view returns (uint256);
                    /**
                     * @dev Returns the amount of tokens owned by `account`.
                     */
                    function balanceOf(address account) external view returns (uint256);
                    /**
                     * @dev Moves `amount` tokens from the caller's account to `recipient`.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transfer(address recipient, uint256 amount) external returns (bool);
                    /**
                     * @dev Returns the remaining number of tokens that `spender` will be
                     * allowed to spend on behalf of `owner` through {transferFrom}. This is
                     * zero by default.
                     *
                     * This value changes when {approve} or {transferFrom} are called.
                     */
                    function allowance(address owner, address spender) external view returns (uint256);
                    /**
                     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * IMPORTANT: Beware that changing an allowance with this method brings the risk
                     * that someone may use both the old and the new allowance by unfortunate
                     * transaction ordering. One possible solution to mitigate this race
                     * condition is to first reduce the spender's allowance to 0 and set the
                     * desired value afterwards:
                     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                     *
                     * Emits an {Approval} event.
                     */
                    function approve(address spender, uint256 amount) external returns (bool);
                    /**
                     * @dev Moves `amount` tokens from `sender` to `recipient` using the
                     * allowance mechanism. `amount` is then deducted from the caller's
                     * allowance.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transferFrom(
                        address sender,
                        address recipient,
                        uint256 amount
                    ) external returns (bool);
                    /**
                     * @dev Emitted when `value` tokens are moved from one account (`from`) to
                     * another (`to`).
                     *
                     * Note that `value` may be zero.
                     */
                    event Transfer(address indexed from, address indexed to, uint256 value);
                    /**
                     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                     * a call to {approve}. `value` is the new allowance.
                     */
                    event Approval(address indexed owner, address indexed spender, uint256 value);
                }
                // SPDX-License-Identifier: MIT
                pragma solidity ^0.8.0;
                /**
                 * @dev Collection of functions related to the address type
                 */
                library Address {
                    /**
                     * @dev Returns true if `account` is a contract.
                     *
                     * [IMPORTANT]
                     * ====
                     * It is unsafe to assume that an address for which this function returns
                     * false is an externally-owned account (EOA) and not a contract.
                     *
                     * Among others, `isContract` will return false for the following
                     * types of addresses:
                     *
                     *  - an externally-owned account
                     *  - a contract in construction
                     *  - an address where a contract will be created
                     *  - an address where a contract lived, but was destroyed
                     * ====
                     */
                    function isContract(address account) internal view returns (bool) {
                        // This method relies on extcodesize, which returns 0 for contracts in
                        // construction, since the code is only stored at the end of the
                        // constructor execution.
                        uint256 size;
                        assembly {
                            size := extcodesize(account)
                        }
                        return size > 0;
                    }
                    /**
                     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                     * `recipient`, forwarding all available gas and reverting on errors.
                     *
                     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                     * of certain opcodes, possibly making contracts go over the 2300 gas limit
                     * imposed by `transfer`, making them unable to receive funds via
                     * `transfer`. {sendValue} removes this limitation.
                     *
                     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                     *
                     * IMPORTANT: because control is transferred to `recipient`, care must be
                     * taken to not create reentrancy vulnerabilities. Consider using
                     * {ReentrancyGuard} or the
                     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                     */
                    function sendValue(address payable recipient, uint256 amount) internal {
                        require(address(this).balance >= amount, "Address: insufficient balance");
                        (bool success, ) = recipient.call{value: amount}("");
                        require(success, "Address: unable to send value, recipient may have reverted");
                    }
                    /**
                     * @dev Performs a Solidity function call using a low level `call`. A
                     * plain `call` is an unsafe replacement for a function call: use this
                     * function instead.
                     *
                     * If `target` reverts with a revert reason, it is bubbled up by this
                     * function (like regular Solidity function calls).
                     *
                     * Returns the raw returned data. To convert to the expected return value,
                     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                     *
                     * Requirements:
                     *
                     * - `target` must be a contract.
                     * - calling `target` with `data` must not revert.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionCall(target, data, "Address: low-level call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                     * `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, 0, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but also transferring `value` wei to `target`.
                     *
                     * Requirements:
                     *
                     * - the calling contract must have an ETH balance of at least `value`.
                     * - the called Solidity function must be `payable`.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(
                        address target,
                        bytes memory data,
                        uint256 value
                    ) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                     * with `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(
                        address target,
                        bytes memory data,
                        uint256 value,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        require(address(this).balance >= value, "Address: insufficient balance for call");
                        require(isContract(target), "Address: call to non-contract");
                        (bool success, bytes memory returndata) = target.call{value: value}(data);
                        return verifyCallResult(success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                        return functionStaticCall(target, data, "Address: low-level static call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal view returns (bytes memory) {
                        require(isContract(target), "Address: static call to non-contract");
                        (bool success, bytes memory returndata) = target.staticcall(data);
                        return verifyCallResult(success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        require(isContract(target), "Address: delegate call to non-contract");
                        (bool success, bytes memory returndata) = target.delegatecall(data);
                        return verifyCallResult(success, returndata, errorMessage);
                    }
                    /**
                     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                     * revert reason using the provided one.
                     *
                     * _Available since v4.3._
                     */
                    function verifyCallResult(
                        bool success,
                        bytes memory returndata,
                        string memory errorMessage
                    ) internal pure returns (bytes memory) {
                        if (success) {
                            return returndata;
                        } else {
                            // Look for revert reason and bubble it up if present
                            if (returndata.length > 0) {
                                // The easiest way to bubble the revert reason is using memory via assembly
                                assembly {
                                    let returndata_size := mload(returndata)
                                    revert(add(32, returndata), returndata_size)
                                }
                            } else {
                                revert(errorMessage);
                            }
                        }
                    }
                }
                

                File 3 of 8: MayanSwift
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
                pragma solidity ^0.8.0;
                /**
                 * @dev Contract module that helps prevent reentrant calls to a function.
                 *
                 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
                 * available, which can be applied to functions to make sure there are no nested
                 * (reentrant) calls to them.
                 *
                 * Note that because there is a single `nonReentrant` guard, functions marked as
                 * `nonReentrant` may not call one another. This can be worked around by making
                 * those functions `private`, and then adding `external` `nonReentrant` entry
                 * points to them.
                 *
                 * TIP: If you would like to learn more about reentrancy and alternative ways
                 * to protect against it, check out our blog post
                 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
                 */
                abstract contract ReentrancyGuard {
                    // Booleans are more expensive than uint256 or any type that takes up a full
                    // word because each write operation emits an extra SLOAD to first read the
                    // slot's contents, replace the bits taken up by the boolean, and then write
                    // back. This is the compiler's defense against contract upgrades and
                    // pointer aliasing, and it cannot be disabled.
                    // The values being non-zero value makes deployment a bit more expensive,
                    // but in exchange the refund on every call to nonReentrant will be lower in
                    // amount. Since refunds are capped to a percentage of the total
                    // transaction's gas, it is best to keep them low in cases like this one, to
                    // increase the likelihood of the full refund coming into effect.
                    uint256 private constant _NOT_ENTERED = 1;
                    uint256 private constant _ENTERED = 2;
                    uint256 private _status;
                    constructor() {
                        _status = _NOT_ENTERED;
                    }
                    /**
                     * @dev Prevents a contract from calling itself, directly or indirectly.
                     * Calling a `nonReentrant` function from another `nonReentrant`
                     * function is not supported. It is possible to prevent this from happening
                     * by making the `nonReentrant` function external, and making it call a
                     * `private` function that does the actual work.
                     */
                    modifier nonReentrant() {
                        _nonReentrantBefore();
                        _;
                        _nonReentrantAfter();
                    }
                    function _nonReentrantBefore() private {
                        // On the first call to nonReentrant, _status will be _NOT_ENTERED
                        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
                        // Any calls to nonReentrant after this point will fail
                        _status = _ENTERED;
                    }
                    function _nonReentrantAfter() private {
                        // By storing the original value once again, a refund is triggered (see
                        // https://eips.ethereum.org/EIPS/eip-2200)
                        _status = _NOT_ENTERED;
                    }
                    /**
                     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
                     * `nonReentrant` function in the call stack.
                     */
                    function _reentrancyGuardEntered() internal view returns (bool) {
                        return _status == _ENTERED;
                    }
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
                pragma solidity ^0.8.0;
                /**
                 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
                 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
                 *
                 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
                 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
                 * need to send a transaction, and thus is not required to hold Ether at all.
                 */
                interface IERC20Permit {
                    /**
                     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                     * given ``owner``'s signed approval.
                     *
                     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                     * ordering also apply here.
                     *
                     * Emits an {Approval} event.
                     *
                     * Requirements:
                     *
                     * - `spender` cannot be the zero address.
                     * - `deadline` must be a timestamp in the future.
                     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                     * over the EIP712-formatted function arguments.
                     * - the signature must use ``owner``'s current nonce (see {nonces}).
                     *
                     * For more information on the signature format, see the
                     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                     * section].
                     */
                    function permit(
                        address owner,
                        address spender,
                        uint256 value,
                        uint256 deadline,
                        uint8 v,
                        bytes32 r,
                        bytes32 s
                    ) external;
                    /**
                     * @dev Returns the current nonce for `owner`. This value must be
                     * included whenever a signature is generated for {permit}.
                     *
                     * Every successful call to {permit} increases ``owner``'s nonce by one. This
                     * prevents a signature from being used multiple times.
                     */
                    function nonces(address owner) external view returns (uint256);
                    /**
                     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                     */
                    // solhint-disable-next-line func-name-mixedcase
                    function DOMAIN_SEPARATOR() external view returns (bytes32);
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
                pragma solidity ^0.8.0;
                /**
                 * @dev Interface of the ERC20 standard as defined in the EIP.
                 */
                interface IERC20 {
                    /**
                     * @dev Emitted when `value` tokens are moved from one account (`from`) to
                     * another (`to`).
                     *
                     * Note that `value` may be zero.
                     */
                    event Transfer(address indexed from, address indexed to, uint256 value);
                    /**
                     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                     * a call to {approve}. `value` is the new allowance.
                     */
                    event Approval(address indexed owner, address indexed spender, uint256 value);
                    /**
                     * @dev Returns the amount of tokens in existence.
                     */
                    function totalSupply() external view returns (uint256);
                    /**
                     * @dev Returns the amount of tokens owned by `account`.
                     */
                    function balanceOf(address account) external view returns (uint256);
                    /**
                     * @dev Moves `amount` tokens from the caller's account to `to`.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transfer(address to, uint256 amount) external returns (bool);
                    /**
                     * @dev Returns the remaining number of tokens that `spender` will be
                     * allowed to spend on behalf of `owner` through {transferFrom}. This is
                     * zero by default.
                     *
                     * This value changes when {approve} or {transferFrom} are called.
                     */
                    function allowance(address owner, address spender) external view returns (uint256);
                    /**
                     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * IMPORTANT: Beware that changing an allowance with this method brings the risk
                     * that someone may use both the old and the new allowance by unfortunate
                     * transaction ordering. One possible solution to mitigate this race
                     * condition is to first reduce the spender's allowance to 0 and set the
                     * desired value afterwards:
                     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                     *
                     * Emits an {Approval} event.
                     */
                    function approve(address spender, uint256 amount) external returns (bool);
                    /**
                     * @dev Moves `amount` tokens from `from` to `to` using the
                     * allowance mechanism. `amount` is then deducted from the caller's
                     * allowance.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transferFrom(address from, address to, uint256 amount) external returns (bool);
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
                pragma solidity ^0.8.0;
                import "../IERC20.sol";
                import "../extensions/IERC20Permit.sol";
                import "../../../utils/Address.sol";
                /**
                 * @title SafeERC20
                 * @dev Wrappers around ERC20 operations that throw on failure (when the token
                 * contract returns false). Tokens that return no value (and instead revert or
                 * throw on failure) are also supported, non-reverting calls are assumed to be
                 * successful.
                 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                 */
                library SafeERC20 {
                    using Address for address;
                    /**
                     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
                     * non-reverting calls are assumed to be successful.
                     */
                    function safeTransfer(IERC20 token, address to, uint256 value) internal {
                        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                    }
                    /**
                     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
                     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
                     */
                    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                    }
                    /**
                     * @dev Deprecated. This function has issues similar to the ones found in
                     * {IERC20-approve}, and its usage is discouraged.
                     *
                     * Whenever possible, use {safeIncreaseAllowance} and
                     * {safeDecreaseAllowance} instead.
                     */
                    function safeApprove(IERC20 token, address spender, uint256 value) internal {
                        // safeApprove should only be called when setting an initial allowance,
                        // or when resetting it to zero. To increase and decrease it, use
                        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                        require(
                            (value == 0) || (token.allowance(address(this), spender) == 0),
                            "SafeERC20: approve from non-zero to non-zero allowance"
                        );
                        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                    }
                    /**
                     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                     * non-reverting calls are assumed to be successful.
                     */
                    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                        uint256 oldAllowance = token.allowance(address(this), spender);
                        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
                    }
                    /**
                     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                     * non-reverting calls are assumed to be successful.
                     */
                    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                        unchecked {
                            uint256 oldAllowance = token.allowance(address(this), spender);
                            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
                        }
                    }
                    /**
                     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
                     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
                     * to be set to zero before setting it to a non-zero value, such as USDT.
                     */
                    function forceApprove(IERC20 token, address spender, uint256 value) internal {
                        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
                        if (!_callOptionalReturnBool(token, approvalCall)) {
                            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
                            _callOptionalReturn(token, approvalCall);
                        }
                    }
                    /**
                     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
                     * Revert on invalid signature.
                     */
                    function safePermit(
                        IERC20Permit token,
                        address owner,
                        address spender,
                        uint256 value,
                        uint256 deadline,
                        uint8 v,
                        bytes32 r,
                        bytes32 s
                    ) internal {
                        uint256 nonceBefore = token.nonces(owner);
                        token.permit(owner, spender, value, deadline, v, r, s);
                        uint256 nonceAfter = token.nonces(owner);
                        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
                    }
                    /**
                     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                     * on the return value: the return value is optional (but if data is returned, it must not be false).
                     * @param token The token targeted by the call.
                     * @param data The call data (encoded using abi.encode or one of its variants).
                     */
                    function _callOptionalReturn(IERC20 token, bytes memory data) private {
                        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
                        // the target address contains contract code and also asserts for success in the low-level call.
                        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                    }
                    /**
                     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                     * on the return value: the return value is optional (but if data is returned, it must not be false).
                     * @param token The token targeted by the call.
                     * @param data The call data (encoded using abi.encode or one of its variants).
                     *
                     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
                     */
                    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
                        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
                        // and not revert is the subcall reverts.
                        (bool success, bytes memory returndata) = address(token).call(data);
                        return
                            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
                    }
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
                pragma solidity ^0.8.1;
                /**
                 * @dev Collection of functions related to the address type
                 */
                library Address {
                    /**
                     * @dev Returns true if `account` is a contract.
                     *
                     * [IMPORTANT]
                     * ====
                     * It is unsafe to assume that an address for which this function returns
                     * false is an externally-owned account (EOA) and not a contract.
                     *
                     * Among others, `isContract` will return false for the following
                     * types of addresses:
                     *
                     *  - an externally-owned account
                     *  - a contract in construction
                     *  - an address where a contract will be created
                     *  - an address where a contract lived, but was destroyed
                     *
                     * Furthermore, `isContract` will also return true if the target contract within
                     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                     * which only has an effect at the end of a transaction.
                     * ====
                     *
                     * [IMPORTANT]
                     * ====
                     * You shouldn't rely on `isContract` to protect against flash loan attacks!
                     *
                     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                     * constructor.
                     * ====
                     */
                    function isContract(address account) internal view returns (bool) {
                        // This method relies on extcodesize/address.code.length, which returns 0
                        // for contracts in construction, since the code is only stored at the end
                        // of the constructor execution.
                        return account.code.length > 0;
                    }
                    /**
                     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                     * `recipient`, forwarding all available gas and reverting on errors.
                     *
                     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                     * of certain opcodes, possibly making contracts go over the 2300 gas limit
                     * imposed by `transfer`, making them unable to receive funds via
                     * `transfer`. {sendValue} removes this limitation.
                     *
                     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                     *
                     * IMPORTANT: because control is transferred to `recipient`, care must be
                     * taken to not create reentrancy vulnerabilities. Consider using
                     * {ReentrancyGuard} or the
                     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                     */
                    function sendValue(address payable recipient, uint256 amount) internal {
                        require(address(this).balance >= amount, "Address: insufficient balance");
                        (bool success, ) = recipient.call{value: amount}("");
                        require(success, "Address: unable to send value, recipient may have reverted");
                    }
                    /**
                     * @dev Performs a Solidity function call using a low level `call`. A
                     * plain `call` is an unsafe replacement for a function call: use this
                     * function instead.
                     *
                     * If `target` reverts with a revert reason, it is bubbled up by this
                     * function (like regular Solidity function calls).
                     *
                     * Returns the raw returned data. To convert to the expected return value,
                     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                     *
                     * Requirements:
                     *
                     * - `target` must be a contract.
                     * - calling `target` with `data` must not revert.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                     * `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, 0, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but also transferring `value` wei to `target`.
                     *
                     * Requirements:
                     *
                     * - the calling contract must have an ETH balance of at least `value`.
                     * - the called Solidity function must be `payable`.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                     * with `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(
                        address target,
                        bytes memory data,
                        uint256 value,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        require(address(this).balance >= value, "Address: insufficient balance for call");
                        (bool success, bytes memory returndata) = target.call{value: value}(data);
                        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                        return functionStaticCall(target, data, "Address: low-level static call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal view returns (bytes memory) {
                        (bool success, bytes memory returndata) = target.staticcall(data);
                        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        (bool success, bytes memory returndata) = target.delegatecall(data);
                        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                    }
                    /**
                     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                     *
                     * _Available since v4.8._
                     */
                    function verifyCallResultFromTarget(
                        address target,
                        bool success,
                        bytes memory returndata,
                        string memory errorMessage
                    ) internal view returns (bytes memory) {
                        if (success) {
                            if (returndata.length == 0) {
                                // only check isContract if the call was successful and the return data is empty
                                // otherwise we already know that it was a contract
                                require(isContract(target), "Address: call to non-contract");
                            }
                            return returndata;
                        } else {
                            _revert(returndata, errorMessage);
                        }
                    }
                    /**
                     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                     * revert reason or using the provided one.
                     *
                     * _Available since v4.3._
                     */
                    function verifyCallResult(
                        bool success,
                        bytes memory returndata,
                        string memory errorMessage
                    ) internal pure returns (bytes memory) {
                        if (success) {
                            return returndata;
                        } else {
                            _revert(returndata, errorMessage);
                        }
                    }
                    function _revert(bytes memory returndata, string memory errorMessage) private pure {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            /// @solidity memory-safe-assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
                // SPDX-License-Identifier: MIT
                pragma solidity ^0.8.0;
                interface IERC1271 {
                    /// @dev Should return whether the signature provided is valid for the provided data
                    /// @param hash      Hash of the data to be signed
                    /// @param signature Signature byte array associated with _data
                    /// @return magicValue The bytes4 magic value 0x1626ba7e
                    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
                }// SPDX-License-Identifier: MIT
                pragma solidity ^0.8.0;
                interface IFeeManager {
                    function calcProtocolBps(
                        uint64 amountIn,
                        address tokenIn,
                        bytes32 tokenOut,
                        uint16 destChain,
                        uint8 referrerBps
                    ) external view returns (uint8);
                \tfunction feeCollector() external view returns (address);
                }
                // SPDX-License-Identifier: Apache 2
                pragma solidity ^0.8.0;
                interface IWormhole {
                    struct GuardianSet {
                        address[] keys;
                        uint32 expirationTime;
                    }
                    struct Signature {
                        bytes32 r;
                        bytes32 s;
                        uint8 v;
                        uint8 guardianIndex;
                    }
                    struct VM {
                        uint8 version;
                        uint32 timestamp;
                        uint32 nonce;
                        uint16 emitterChainId;
                        bytes32 emitterAddress;
                        uint64 sequence;
                        uint8 consistencyLevel;
                        bytes payload;
                        uint32 guardianSetIndex;
                        Signature[] signatures;
                        bytes32 hash;
                    }
                    struct ContractUpgrade {
                        bytes32 module;
                        uint8 action;
                        uint16 chain;
                        address newContract;
                    }
                    struct GuardianSetUpgrade {
                        bytes32 module;
                        uint8 action;
                        uint16 chain;
                        GuardianSet newGuardianSet;
                        uint32 newGuardianSetIndex;
                    }
                    struct SetMessageFee {
                        bytes32 module;
                        uint8 action;
                        uint16 chain;
                        uint256 messageFee;
                    }
                    struct TransferFees {
                        bytes32 module;
                        uint8 action;
                        uint16 chain;
                        uint256 amount;
                        bytes32 recipient;
                    }
                    struct RecoverChainId {
                        bytes32 module;
                        uint8 action;
                        uint256 evmChainId;
                        uint16 newChainId;
                    }
                    event LogMessagePublished(address indexed sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel);
                    event ContractUpgraded(address indexed oldContract, address indexed newContract);
                    event GuardianSetAdded(uint32 indexed index);
                    function publishMessage(
                        uint32 nonce,
                        bytes memory payload,
                        uint8 consistencyLevel
                    ) external payable returns (uint64 sequence);
                    function initialize() external;
                    function parseAndVerifyVM(bytes calldata encodedVM) external view returns (VM memory vm, bool valid, string memory reason);
                    function verifyVM(VM memory vm) external view returns (bool valid, string memory reason);
                    function verifySignatures(bytes32 hash, Signature[] memory signatures, GuardianSet memory guardianSet) external pure returns (bool valid, string memory reason);
                    function parseVM(bytes memory encodedVM) external pure returns (VM memory vm);
                    function quorum(uint numGuardians) external pure returns (uint numSignaturesRequiredForQuorum);
                    function getGuardianSet(uint32 index) external view returns (GuardianSet memory);
                    function getCurrentGuardianSetIndex() external view returns (uint32);
                    function getGuardianSetExpiry() external view returns (uint32);
                    function governanceActionIsConsumed(bytes32 hash) external view returns (bool);
                    function isInitialized(address impl) external view returns (bool);
                    function chainId() external view returns (uint16);
                    function isFork() external view returns (bool);
                    function governanceChainId() external view returns (uint16);
                    function governanceContract() external view returns (bytes32);
                    function messageFee() external view returns (uint256);
                    function evmChainId() external view returns (uint256);
                    function nextSequence(address emitter) external view returns (uint64);
                    function parseContractUpgrade(bytes memory encodedUpgrade) external pure returns (ContractUpgrade memory cu);
                    function parseGuardianSetUpgrade(bytes memory encodedUpgrade) external pure returns (GuardianSetUpgrade memory gsu);
                    function parseSetMessageFee(bytes memory encodedSetMessageFee) external pure returns (SetMessageFee memory smf);
                    function parseTransferFees(bytes memory encodedTransferFees) external pure returns (TransferFees memory tf);
                    function parseRecoverChainId(bytes memory encodedRecoverChainId) external pure returns (RecoverChainId memory rci);
                    function submitContractUpgrade(bytes memory _vm) external;
                    function submitSetMessageFee(bytes memory _vm) external;
                    function submitNewGuardianSet(bytes memory _vm) external;
                    function submitTransferFees(bytes memory _vm) external;
                    function submitRecoverChainId(bytes memory _vm) external;
                }
                // SPDX-License-Identifier: Unlicense
                /*
                 * @title Solidity Bytes Arrays Utils
                 * @author Gonçalo Sá <[email protected]>
                 *
                 * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
                 *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
                 */
                pragma solidity >=0.8.0 <0.9.0;
                library BytesLib {
                    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 mlengthmod := mod(mlength, 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)
                    {
                        require(_length + 31 >= _length, "slice_overflow");
                        require(_bytes.length >= _start + _length, "slice_outOfBounds");
                        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) {
                        require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
                        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) {
                        require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
                        uint8 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0x1), _start))
                        }
                        return tempUint;
                    }
                    function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
                        require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
                        uint16 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0x2), _start))
                        }
                        return tempUint;
                    }
                    function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
                        require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
                        uint32 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0x4), _start))
                        }
                        return tempUint;
                    }
                    function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
                        require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
                        uint64 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0x8), _start))
                        }
                        return tempUint;
                    }
                    function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
                        require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
                        uint96 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0xc), _start))
                        }
                        return tempUint;
                    }
                    function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
                        require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
                        uint128 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0x10), _start))
                        }
                        return tempUint;
                    }
                    function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
                        require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
                        uint256 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0x20), _start))
                        }
                        return tempUint;
                    }
                    function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
                        require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
                        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)
                                        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.0;
                import {IERC1271} from "../interfaces/IERC1271.sol";
                library SignatureVerifier {
                \t/// @notice Thrown when the passed in signature is not a valid length
                \terror InvalidSignatureLength();
                \t/// @notice Thrown when the recovered signer is equal to the zero address
                \terror InvalidSignature();
                \t/// @notice Thrown when the recovered signer does not equal the claimedSigner
                \terror InvalidSigner();
                \t/// @notice Thrown when the recovered contract signature is incorrect
                \terror InvalidContractSignature();
                \tbytes32 constant UPPER_BIT_MASK = (0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
                \tfunction verify(bytes calldata signature, bytes32 hash, address claimedSigner) external view {
                \t\tbytes32 r;
                \t\tbytes32 s;
                \t\tuint8 v;
                \t\tif (claimedSigner.code.length == 0) {
                \t\t\tif (signature.length == 65) {
                \t\t\t\t(r, s) = abi.decode(signature, (bytes32, bytes32));
                \t\t\t\tv = uint8(signature[64]);
                \t\t\t} else if (signature.length == 64) {
                \t\t\t\t// EIP-2098
                \t\t\t\tbytes32 vs;
                \t\t\t\t(r, vs) = abi.decode(signature, (bytes32, bytes32));
                \t\t\t\ts = vs & UPPER_BIT_MASK;
                \t\t\t\tv = uint8(uint256(vs >> 255)) + 27;
                \t\t\t} else {
                \t\t\t\trevert InvalidSignatureLength();
                \t\t\t}
                \t\t\taddress signer = ecrecover(hash, v, r, s);
                \t\t\tif (signer == address(0)) revert InvalidSignature();
                \t\t\tif (signer != claimedSigner) revert InvalidSigner();
                \t\t} else {
                \t\t\tbytes4 magicValue = IERC1271(claimedSigner).isValidSignature(hash, signature);
                \t\t\tif (magicValue != IERC1271.isValidSignature.selector) revert InvalidContractSignature();
                \t\t}
                \t}
                }// SPDX-License-Identifier: MIT
                pragma solidity ^0.8.0;
                import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
                import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
                import "./interfaces/IWormhole.sol";
                import "./interfaces/IFeeManager.sol";
                import "./libs/BytesLib.sol";
                import "./libs/SignatureVerifier.sol";
                contract MayanSwift is ReentrancyGuard {
                \tevent OrderCreated(bytes32 key);
                \tevent OrderFulfilled(bytes32 key, uint64 sequence, uint256 netAmount);
                \tevent OrderUnlocked(bytes32 key);
                \tevent OrderCanceled(bytes32 key, uint64 sequence);
                \tevent OrderRefunded(bytes32 key, uint256 netAmount);
                \tusing SafeERC20 for IERC20;
                \tusing BytesLib for bytes;
                \tusing SignatureVerifier for bytes;
                \tuint16 constant SOLANA_CHAIN_ID = 1;
                \tuint8 constant BPS_FEE_LIMIT = 50;
                \tuint8 constant NATIVE_DECIMALS = 18;
                \tIWormhole public immutable wormhole;
                \tuint16 public immutable auctionChainId;
                \tbytes32 public immutable auctionAddr;
                \tbytes32 public immutable solanaEmitter;
                \tIFeeManager public feeManager;
                \tuint8 public consistencyLevel;
                \taddress public guardian;
                \taddress public nextGuardian;
                \tbool public paused;
                \tbytes32 private domainSeparator;
                \tmapping(bytes32 => Order) public orders;
                \tmapping(bytes32 => UnlockMsg) public unlockMsgs;
                \terror Paused();
                \terror Unauthorized();
                \terror InvalidAction();
                \terror InvalidBpsFee();
                \terror InvalidOrderStatus();
                \terror InvalidOrderHash();
                \terror InvalidEmitterChain();
                \terror InvalidEmitterAddress();
                \terror InvalidSrcChain();
                \terror OrderNotExists();
                \terror SmallAmountIn();
                \terror FeesTooHigh();
                \terror InvalidGasDrop();
                \terror InvalidDestChain();
                \terror DuplicateOrder();
                \terror InvalidAmount();
                \terror DeadlineViolation();
                \terror InvalidWormholeFee();
                \terror InvalidAuctionMode();
                \terror InvalidEvmAddr();
                \tstruct Order {
                \t\tStatus status;
                \t\tuint64 amountIn;
                \t\tuint16 destChainId;
                \t}
                \tstruct OrderParams {
                \t\tbytes32 trader;
                \t\tbytes32 tokenOut;
                \t\tuint64 minAmountOut;
                \t\tuint64 gasDrop;
                \t\tuint64 cancelFee;
                \t\tuint64 refundFee;
                \t\tuint64 deadline;
                \t\tbytes32 destAddr;
                \t\tuint16 destChainId;
                \t\tbytes32 referrerAddr;
                \t\tuint8 referrerBps;
                \t\tuint8 auctionMode;
                \t\tbytes32 random;
                \t}
                \tstruct PermitParams {
                \t\tuint256 value;
                \t\tuint256 deadline;
                \t\tuint8 v;
                \t\tbytes32 r;
                \t\tbytes32 s;
                \t}
                \tstruct Key {
                \t\tbytes32 trader;
                \t\tuint16 srcChainId;
                \t\tbytes32 tokenIn;
                \t\tbytes32 destAddr;
                \t\tuint16 destChainId;
                \t\tbytes32 tokenOut;
                \t\tuint64 minAmountOut;
                \t\tuint64 gasDrop;
                \t\tuint64 cancelFee;
                \t\tuint64 refundFee;
                \t\tuint64 deadline;
                \t\tbytes32 referrerAddr;
                \t\tuint8 referrerBps;
                \t\tuint8 protocolBps;
                \t\tuint8 auctionMode;
                \t\tbytes32 random;
                \t}
                \tstruct PaymentParams {
                \t\taddress destAddr;
                \t\taddress tokenOut;
                \t\tuint64 promisedAmount;
                \t\tuint64 gasDrop;
                \t\taddress referrerAddr;
                \t\tuint8 referrerBps;
                \t\tuint8 protocolBps;
                \t\tbool batch;
                \t}
                \tenum Status {
                \t\tCREATED,
                \t\tFULFILLED,
                \t\tUNLOCKED,
                \t\tCANCELED,
                \t\tREFUNDED
                \t}
                \tenum Action {
                \t\tNONE,
                \t\tFULFILL,
                \t\tUNLOCK,
                \t\tREFUND,
                \t\tBATCH_UNLOCK
                \t}
                \tenum AuctionMode {
                \t\tNONE,
                \t\tBYPASS,
                \t\tENGLISH
                \t}
                \tstruct UnlockMsg {
                \t\tuint8 action;
                \t\tbytes32 orderHash;
                \t\tuint16 srcChainId;
                \t\tbytes32 tokenIn;
                \t\tbytes32 recipient;
                \t}
                \tstruct RefundMsg {
                \t\tuint8 action;
                \t\tbytes32 orderHash;
                \t\tuint16 srcChainId;
                \t\tbytes32 tokenIn;
                \t\tbytes32 recipient;
                \t\tbytes32 canceler;
                \t\tuint64 cancelFee;
                \t\tuint64 refundFee;\t
                \t}
                \tstruct FulfillMsg {
                \t\tuint8 action;
                \t\tbytes32 orderHash;
                \t\tuint16 destChainId;
                \t\tbytes32 destAddr;
                \t\tbytes32 driver;
                \t\tbytes32 tokenOut;
                \t\tuint64 promisedAmount;
                \t\tuint64 gasDrop;
                \t\tuint64 deadline;
                \t\tbytes32 referrerAddr;
                \t\tuint8 referrerBps;
                \t\tuint8 protocolBps;
                \t\tuint16 srcChainId;
                \t\tbytes32 tokenIn;
                \t}
                \tstruct TransferParams {
                \t\taddress from;
                \t\tuint256 validAfter;
                \t\tuint256 validBefore;
                \t}
                \tconstructor(
                \t\taddress _wormhole,
                \t\taddress _feeManager,
                \t\tuint16 _auctionChainId,
                \t\tbytes32 _auctionAddr,
                \t\tbytes32 _solanaEmitter,
                \t\tuint8 _consistencyLevel
                \t) {
                \t\tguardian = msg.sender;
                \t\twormhole = IWormhole(_wormhole);
                \t\tfeeManager = IFeeManager(_feeManager);
                \t\tauctionChainId = _auctionChainId;
                \t\tauctionAddr = _auctionAddr;
                \t\tsolanaEmitter = _solanaEmitter;
                \t\tconsistencyLevel = _consistencyLevel;
                \t\tdomainSeparator = keccak256(abi.encode(
                \t\t\tkeccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"),
                \t\t\tkeccak256("Mayan Swift"),
                \t\t\tuint256(block.chainid),
                \t\t\taddress(this)
                \t\t));
                \t}
                \tfunction createOrderWithEth(OrderParams memory params) nonReentrant external payable returns (bytes32 orderHash) {
                \t\tif (paused) {
                \t\t\trevert Paused();
                \t\t}
                \t\tuint64 normlizedAmountIn = uint64(normalizeAmount(msg.value, NATIVE_DECIMALS));
                \t\tif (normlizedAmountIn == 0) {
                \t\t\trevert SmallAmountIn();
                \t\t}
                \t\tif (params.cancelFee + params.refundFee >= normlizedAmountIn) {
                \t\t\trevert FeesTooHigh();
                \t\t}
                \t\tif (params.tokenOut == bytes32(0) && params.gasDrop != 0) {
                \t\t\trevert InvalidGasDrop();
                \t\t}
                \t\tuint8 protocolBps = feeManager.calcProtocolBps(normlizedAmountIn, address(0), params.tokenOut, params.destChainId, params.referrerBps);
                \t\tif (params.referrerBps > BPS_FEE_LIMIT || protocolBps > BPS_FEE_LIMIT) {
                \t\t\trevert InvalidBpsFee();
                \t\t}
                \t\tKey memory key = buildKey(params, bytes32(0), wormhole.chainId(), protocolBps);
                \t\torderHash = keccak256(encodeKey(key));
                \t\tif (params.destChainId == 0 || params.destChainId == wormhole.chainId()) {
                \t\t\trevert InvalidDestChain();
                \t\t}
                \t\tif (orders[orderHash].destChainId != 0) {
                \t\t\trevert DuplicateOrder();
                \t\t}
                \t\torders[orderHash] = Order({
                \t\t\tstatus: Status.CREATED,
                \t\t\tamountIn: normlizedAmountIn,
                \t\t\tdestChainId: params.destChainId
                \t\t});
                \t\t
                \t\temit OrderCreated(orderHash);
                \t}
                \tfunction createOrderWithToken(
                \t\taddress tokenIn,
                \t\tuint256 amountIn,
                \t\tOrderParams memory params
                \t) nonReentrant external returns (bytes32 orderHash) {
                \t\tif (paused) {
                \t\t\trevert Paused();
                \t\t}
                \t\tamountIn = pullTokensFrom(tokenIn, amountIn, msg.sender);
                \t\tuint64 normlizedAmountIn = uint64(normalizeAmount(amountIn, decimalsOf(tokenIn)));
                \t\tif (normlizedAmountIn == 0) {
                \t\t\trevert SmallAmountIn();
                \t\t}
                \t\tif (params.cancelFee + params.refundFee >= normlizedAmountIn) {
                \t\t\trevert FeesTooHigh();
                \t\t}
                \t\tif (params.tokenOut == bytes32(0) && params.gasDrop != 0) {
                \t\t\trevert InvalidGasDrop();
                \t\t}
                \t\tuint8 protocolBps = feeManager.calcProtocolBps(normlizedAmountIn, tokenIn, params.tokenOut, params.destChainId, params.referrerBps);
                \t\tif (params.referrerBps > BPS_FEE_LIMIT || protocolBps > BPS_FEE_LIMIT) {
                \t\t\trevert InvalidBpsFee();
                \t\t}
                \t\tKey memory key = buildKey(params, bytes32(uint256(uint160(tokenIn))), wormhole.chainId(), protocolBps);
                \t\torderHash = keccak256(encodeKey(key));
                \t\tif (params.destChainId == 0 || params.destChainId == wormhole.chainId()) {
                \t\t\trevert InvalidDestChain();
                \t\t}
                \t\tif (orders[orderHash].destChainId != 0) {
                \t\t\trevert DuplicateOrder();
                \t\t}
                \t\torders[orderHash] = Order({
                \t\t\tstatus: Status.CREATED,
                \t\t\tamountIn: normlizedAmountIn,
                \t\t\tdestChainId: params.destChainId
                \t\t});
                \t\temit OrderCreated(orderHash);
                \t}
                \tfunction createOrderWithSig(
                \t\taddress tokenIn,
                \t\tuint256 amountIn,
                \t\tOrderParams memory params,
                \t\tuint256 submissionFee,
                \t\tbytes calldata signedOrderHash,
                \t\tPermitParams calldata permitParams
                \t) nonReentrant external returns (bytes32 orderHash) {
                \t\tif (paused) {
                \t\t\trevert Paused();
                \t\t}
                \t\taddress trader = truncateAddress(params.trader);
                \t\tuint256 allowance = IERC20(tokenIn).allowance(trader, address(this));
                \t\tif (allowance < amountIn + submissionFee) {
                \t\t\texecPermit(tokenIn, trader, permitParams);
                \t\t}
                \t\tamountIn = pullTokensFrom(tokenIn, amountIn, trader);
                \t\tif (submissionFee > 0) {
                \t\t\tIERC20(tokenIn).safeTransferFrom(trader, msg.sender, submissionFee);
                \t\t}
                \t\tuint64 normlizedAmountIn = uint64(normalizeAmount(amountIn, decimalsOf(tokenIn)));
                \t\tif (normlizedAmountIn == 0) {
                \t\t\trevert SmallAmountIn();
                \t\t}
                \t\tif (params.cancelFee + params.refundFee >= normlizedAmountIn) {
                \t\t\trevert FeesTooHigh();
                \t\t}
                \t\tif (params.tokenOut == bytes32(0) && params.gasDrop != 0) {
                \t\t\trevert InvalidGasDrop();
                \t\t}
                \t\tuint8 protocolBps = feeManager.calcProtocolBps(normlizedAmountIn, tokenIn, params.tokenOut, params.destChainId, params.referrerBps);
                \t\tif (params.referrerBps > BPS_FEE_LIMIT || protocolBps > BPS_FEE_LIMIT) {
                \t\t\trevert InvalidBpsFee();
                \t\t}
                \t\torderHash = keccak256(encodeKey(buildKey(params, bytes32(uint256(uint160(tokenIn))), wormhole.chainId(), protocolBps)));
                \t\tsignedOrderHash.verify(hashTypedData(orderHash, amountIn, submissionFee), trader);
                \t\tif (params.destChainId == 0 || params.destChainId == wormhole.chainId()) {
                \t\t\trevert InvalidDestChain();
                \t\t}
                \t\tif (orders[orderHash].destChainId != 0) {
                \t\t\trevert DuplicateOrder();
                \t\t}
                \t\torders[orderHash] = Order({
                \t\t\tstatus: Status.CREATED,
                \t\t\tamountIn: normlizedAmountIn,
                \t\t\tdestChainId: params.destChainId
                \t\t});
                \t\temit OrderCreated(orderHash);
                \t}
                \tfunction fulfillOrder(
                \t\tuint256 fulfillAmount,
                \t\tbytes memory encodedVm,
                \t\tbytes32 recepient,
                \t\tbool batch
                \t) nonReentrant public payable returns (uint64 sequence) {
                \t\t(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(encodedVm);
                \t\trequire(valid, reason);
                \t\tif (vm.emitterChainId != auctionChainId) {
                \t\t\trevert InvalidEmitterChain();
                \t\t}
                \t\tif (vm.emitterAddress != auctionAddr) {
                \t\t\trevert InvalidEmitterAddress();
                \t\t}
                \t\tFulfillMsg memory fulfillMsg = parseFulfillPayload(vm.payload);
                \t\taddress tokenOut = truncateAddress(fulfillMsg.tokenOut);
                \t\tif (tokenOut != address(0)) {
                \t\t\tfulfillAmount = pullTokensFrom(tokenOut, fulfillAmount, msg.sender);
                \t\t}
                \t\tif (fulfillMsg.destChainId != wormhole.chainId()) {
                \t\t\trevert InvalidDestChain();
                \t\t}
                \t\tif (truncateAddress(fulfillMsg.driver) != tx.origin) {
                \t\t\trevert Unauthorized();
                \t\t}
                \t\tif (block.timestamp > fulfillMsg.deadline) {
                \t\t\trevert DeadlineViolation();
                \t\t}
                \t\tif (orders[fulfillMsg.orderHash].status != Status.CREATED) {
                \t\t\trevert InvalidOrderStatus();
                \t\t}
                \t\torders[fulfillMsg.orderHash].status = Status.FULFILLED;
                \t\tPaymentParams memory paymentParams = PaymentParams({
                \t\t\tdestAddr: truncateAddress(fulfillMsg.destAddr),
                \t\t\ttokenOut: tokenOut,
                \t\t\tpromisedAmount: fulfillMsg.promisedAmount,
                \t\t\tgasDrop: fulfillMsg.gasDrop,
                \t\t\treferrerAddr: truncateAddress(fulfillMsg.referrerAddr),
                \t\t\treferrerBps: fulfillMsg.referrerBps,
                \t\t\tprotocolBps: fulfillMsg.protocolBps,
                \t\t\tbatch: batch
                \t\t});
                \t\tuint256 netAmount = makePayments(fulfillAmount, paymentParams);
                \t\tUnlockMsg memory unlockMsg = UnlockMsg({
                \t\t\taction: uint8(Action.UNLOCK),
                \t\t\torderHash: fulfillMsg.orderHash,
                \t\t\tsrcChainId: fulfillMsg.srcChainId,
                \t\t\ttokenIn: fulfillMsg.tokenIn,
                \t\t\trecipient: recepient
                \t\t});
                \t\tif (batch) {
                \t\t\tunlockMsgs[fulfillMsg.orderHash] = unlockMsg;
                \t\t} else {
                \t\t\tbytes memory encoded = encodeUnlockMsg(unlockMsg);
                \t\t\tsequence = wormhole.publishMessage{
                \t\t\t\tvalue : wormhole.messageFee()
                \t\t\t}(0, encoded, consistencyLevel);
                \t\t}
                \t\temit OrderFulfilled(fulfillMsg.orderHash, sequence, netAmount);
                \t}
                \tfunction fulfillSimple(
                \t\tuint256 fulfillAmount,
                \t\tbytes32 orderHash,
                \t\tuint16 srcChainId,
                \t\tbytes32 tokenIn,
                \t\tuint8 protocolBps,
                \t\tOrderParams memory params,
                \t\tbytes32 recepient,
                \t\tbool batch
                \t) public nonReentrant payable returns (uint64 sequence) {
                \t\tif (params.auctionMode != uint8(AuctionMode.BYPASS)) {
                \t\t\trevert InvalidAuctionMode();
                \t\t}
                \t\taddress tokenOut = truncateAddress(params.tokenOut);
                \t\tif (tokenOut != address(0)) {
                \t\t\tfulfillAmount = pullTokensFrom(tokenOut, fulfillAmount, msg.sender);
                \t\t}\t
                \t\tparams.destChainId = wormhole.chainId();
                \t\tKey memory key = buildKey(params, tokenIn, srcChainId, protocolBps);
                \t\tbytes32 computedOrderHash = keccak256(encodeKey(key));
                \t\tif (computedOrderHash != orderHash) {
                \t\t\trevert InvalidOrderHash();
                \t\t}
                \t\tif (block.timestamp > key.deadline) {
                \t\t\trevert DeadlineViolation();
                \t\t}
                \t\tif (orders[computedOrderHash].status != Status.CREATED) {
                \t\t\trevert InvalidOrderStatus();
                \t\t}
                \t\torders[computedOrderHash].status = Status.FULFILLED;
                \t\tPaymentParams memory paymentParams = PaymentParams({
                \t\t\tdestAddr: truncateAddress(key.destAddr),
                \t\t\ttokenOut: tokenOut,
                \t\t\tpromisedAmount: key.minAmountOut,
                \t\t\tgasDrop: key.gasDrop,
                \t\t\treferrerAddr: truncateAddress(key.referrerAddr),
                \t\t\treferrerBps: key.referrerBps,
                \t\t\tprotocolBps: protocolBps,
                \t\t\tbatch: batch
                \t\t});
                \t\tuint256 netAmount = makePayments(fulfillAmount, paymentParams);
                \t\tUnlockMsg memory unlockMsg = UnlockMsg({
                \t\t\taction: uint8(Action.UNLOCK),
                \t\t\torderHash: computedOrderHash,
                \t\t\tsrcChainId: key.srcChainId,
                \t\t\ttokenIn: key.tokenIn,
                \t\t\trecipient: recepient
                \t\t});
                \t\tif (batch) {
                \t\t\tunlockMsgs[computedOrderHash] = unlockMsg;
                \t\t} else {
                \t\t\tbytes memory encoded = encodeUnlockMsg(unlockMsg);
                \t\t\tsequence = wormhole.publishMessage{
                \t\t\t\tvalue : wormhole.messageFee()
                \t\t\t}(0, encoded, consistencyLevel);
                \t\t}
                \t\temit OrderFulfilled(computedOrderHash, sequence, netAmount);
                \t}
                \tfunction unlockOrder(UnlockMsg memory unlockMsg, Order memory order) internal {
                \t\tif (unlockMsg.srcChainId != wormhole.chainId()) {
                \t\t\trevert InvalidSrcChain();
                \t\t}
                \t\tif (order.destChainId == 0) {
                \t\t\trevert OrderNotExists();
                \t\t}
                \t\tif (order.status != Status.CREATED) {
                \t\t\trevert InvalidOrderStatus();
                \t\t}
                \t\torders[unlockMsg.orderHash].status = Status.UNLOCKED;
                \t\t
                \t\taddress recipient = truncateAddress(unlockMsg.recipient);
                \t\taddress tokenIn = truncateAddress(unlockMsg.tokenIn);
                \t\tuint8 decimals;
                \t\tif (tokenIn == address(0)) {
                \t\t\tdecimals = NATIVE_DECIMALS;
                \t\t} else {
                \t\t\tdecimals = decimalsOf(tokenIn);
                \t\t}
                \t\tuint256 amountIn = deNormalizeAmount(order.amountIn, decimals);
                \t\tif (tokenIn == address(0)) {
                \t\t\tpayViaCall(recipient, amountIn);
                \t\t} else {
                \t\t\tIERC20(tokenIn).safeTransfer(recipient, amountIn);
                \t\t}
                \t\t
                \t\temit OrderUnlocked(unlockMsg.orderHash);
                \t}
                \tfunction cancelOrder(
                \t\tbytes32 tokenIn,
                \t\tOrderParams memory params,
                \t\tuint16 srcChainId,
                \t\tuint8 protocolBps,
                \t\tbytes32 canceler
                \t) public nonReentrant payable returns (uint64 sequence) {
                \t\tparams.destChainId = wormhole.chainId();
                \t\tKey memory key = buildKey(params, tokenIn, srcChainId, protocolBps);
                \t\tbytes32 orderHash = keccak256(encodeKey(key));
                \t\tOrder memory order = orders[orderHash];
                \t\tif (block.timestamp <= key.deadline) {
                \t\t\trevert DeadlineViolation();
                \t\t}
                \t\tif (order.status != Status.CREATED) {
                \t\t\trevert InvalidOrderStatus();
                \t\t}
                \t\torders[orderHash].status = Status.CANCELED;
                \t\tRefundMsg memory refundMsg = RefundMsg({
                \t\t\taction: uint8(Action.REFUND),
                \t\t\torderHash: orderHash,
                \t\t\tsrcChainId: key.srcChainId,
                \t\t\ttokenIn: key.tokenIn,
                \t\t\trecipient: key.trader,
                \t\t\tcanceler: canceler,
                \t\t\tcancelFee: key.cancelFee,
                \t\t\trefundFee: key.refundFee
                \t\t});
                \t\tbytes memory encoded = encodeRefundMsg(refundMsg);
                \t\tsequence = wormhole.publishMessage{
                \t\t\tvalue : msg.value
                \t\t}(0, encoded, consistencyLevel);
                \t\temit OrderCanceled(orderHash, sequence);
                \t}
                \tfunction refundOrder(bytes memory encodedVm) nonReentrant() public {
                \t\t(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(encodedVm);
                \t\trequire(valid, reason);
                \t\tRefundMsg memory refundMsg = parseRefundPayload(vm.payload);
                \t\tOrder memory order = orders[refundMsg.orderHash];
                \t\tif (refundMsg.srcChainId != wormhole.chainId()) {
                \t\t\trevert InvalidSrcChain();
                \t\t}
                \t\tif (order.destChainId == 0) {
                \t\t\trevert OrderNotExists();
                \t\t}
                \t\tif (order.status != Status.CREATED) {
                \t\t\trevert InvalidOrderStatus();
                \t\t}
                \t\torders[refundMsg.orderHash].status = Status.REFUNDED;
                \t\tif (vm.emitterChainId != order.destChainId) {
                \t\t\trevert InvalidEmitterChain();
                \t\t}
                \t\tif (vm.emitterAddress != solanaEmitter && truncateAddress(vm.emitterAddress) != address(this)) {
                \t\t\trevert InvalidEmitterAddress();
                \t\t}
                \t\taddress recipient = truncateAddress(refundMsg.recipient);
                \t\t// no error if canceler is invalid
                \t\taddress canceler = address(uint160(uint256(refundMsg.canceler)));
                \t\taddress tokenIn = truncateAddress(refundMsg.tokenIn);
                \t\t
                \t\tuint8 decimals;
                \t\tif (tokenIn == address(0)) {
                \t\t\tdecimals = NATIVE_DECIMALS;
                \t\t} else {
                \t\t\tdecimals = decimalsOf(tokenIn);
                \t\t}
                \t\tuint256 cancelFee = deNormalizeAmount(refundMsg.cancelFee, decimals);
                \t\tuint256 refundFee = deNormalizeAmount(refundMsg.refundFee, decimals);
                \t\tuint256 amountIn = deNormalizeAmount(order.amountIn, decimals);
                \t\tuint256 netAmount = amountIn - cancelFee - refundFee;
                \t\tif (tokenIn == address(0)) {
                \t\t\tpayViaCall(canceler, cancelFee);
                \t\t\tpayViaCall(msg.sender, refundFee);
                \t\t\tpayViaCall(recipient, netAmount);
                \t\t} else {
                \t\t\tIERC20(tokenIn).safeTransfer(canceler, cancelFee);
                \t\t\tIERC20(tokenIn).safeTransfer(msg.sender, refundFee);
                \t\t\tIERC20(tokenIn).safeTransfer(recipient, netAmount);
                \t\t}
                \t\temit OrderRefunded(refundMsg.orderHash, netAmount);
                \t}
                \tfunction unlockSingle(bytes memory encodedVm) nonReentrant public {
                \t\t(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(encodedVm);
                \t\trequire(valid, reason);
                \t\tUnlockMsg memory unlockMsg = parseUnlockPayload(vm.payload);
                \t\tOrder memory order = orders[unlockMsg.orderHash];
                \t\tif (vm.emitterChainId != order.destChainId) {
                \t\t\trevert InvalidEmitterChain();
                \t\t}
                \t\tif (vm.emitterAddress != solanaEmitter && truncateAddress(vm.emitterAddress) != address(this)) {
                \t\t\trevert InvalidEmitterAddress();
                \t\t}
                \t\tunlockOrder(unlockMsg, order);
                \t}
                \tfunction unlockBatch(bytes memory encodedVm) nonReentrant public {
                \t\t(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(encodedVm);
                \t\trequire(valid, reason);
                \t\tuint8 action = vm.payload.toUint8(0);
                \t\tuint index = 1;
                \t\tif (action != uint8(Action.BATCH_UNLOCK)) {
                \t\t\trevert InvalidAction();
                \t\t}
                \t\tuint16 count = vm.payload.toUint16(index);
                \t\tindex += 2;
                \t\tfor (uint i=0; i<count; i++) {
                \t\t\tUnlockMsg memory unlockMsg = UnlockMsg({
                \t\t\t\taction: uint8(Action.UNLOCK),
                \t\t\t\torderHash: vm.payload.toBytes32(index),
                \t\t\t\tsrcChainId: vm.payload.toUint16(index + 32),
                \t\t\t\ttokenIn: vm.payload.toBytes32(index + 34),
                \t\t\t\trecipient: vm.payload.toBytes32(index + 66)
                \t\t\t});
                \t\t\tindex += 98;
                \t\t\tOrder memory order = orders[unlockMsg.orderHash];
                \t\t\tif (order.status != Status.CREATED) {
                \t\t\t\tcontinue;
                \t\t\t}
                \t\t\tif (vm.emitterChainId != order.destChainId) {
                \t\t\t\trevert InvalidEmitterChain();
                \t\t\t}
                \t\t\tif (vm.emitterAddress != solanaEmitter && truncateAddress(vm.emitterAddress) != address(this)) {
                \t\t\t\trevert InvalidEmitterAddress();
                \t\t\t}
                \t\t\tunlockOrder(unlockMsg, order);
                \t\t}
                \t}
                \tfunction postBatch(bytes32[] memory orderHashes) public payable returns (uint64 sequence) {
                \t\tbytes memory encoded = abi.encodePacked(uint8(Action.BATCH_UNLOCK), uint16(orderHashes.length));
                \t\tfor(uint i=0; i<orderHashes.length; i++) {
                \t\t\tUnlockMsg memory unlockMsg = unlockMsgs[orderHashes[i]];
                \t\t\tif (unlockMsg.action != uint8(Action.UNLOCK)) {
                \t\t\t\trevert InvalidAction();
                \t\t\t}
                \t\t\tbytes memory encodedUnlock = abi.encodePacked(
                \t\t\t\tunlockMsg.orderHash,
                \t\t\t\tunlockMsg.srcChainId,
                \t\t\t\tunlockMsg.tokenIn,
                \t\t\t\tunlockMsg.recipient
                \t\t\t);
                \t\t\tencoded = abi.encodePacked(encoded, encodedUnlock);
                \t\t}
                \t\t
                \t\tsequence = wormhole.publishMessage{
                \t\t\tvalue : msg.value
                \t\t}(0, encoded, consistencyLevel);
                \t}
                \tfunction makePayments(
                \t\tuint256 fulfillAmount,
                \t\tPaymentParams memory params
                \t) internal returns (uint256 netAmount) {
                \t\tuint8 decimals;
                \t\tif (params.tokenOut == address(0)) {
                \t\t\tdecimals = NATIVE_DECIMALS;
                \t\t} else {
                \t\t\tdecimals = decimalsOf(params.tokenOut);
                \t\t}
                \t\t
                \t\tuint256 referrerAmount = 0;
                \t\tif (params.referrerAddr != address(0) && params.referrerBps != 0) {
                \t\t\treferrerAmount = fulfillAmount * params.referrerBps / 10000;
                \t\t}
                \t\tuint256 protocolAmount = 0;
                \t\tif (params.protocolBps != 0) {
                \t\t\tprotocolAmount = fulfillAmount * params.protocolBps / 10000;
                \t\t}
                \t\tnetAmount = fulfillAmount - referrerAmount - protocolAmount;
                \t\tuint256 promisedAmount = deNormalizeAmount(params.promisedAmount, decimals);
                \t\tif (netAmount < promisedAmount) {
                \t\t\trevert InvalidAmount();
                \t\t}
                \t\tif (params.tokenOut == address(0)) {
                \t\t\tif (
                \t\t\t\t(params.batch && msg.value != fulfillAmount) ||
                \t\t\t\t(!params.batch && msg.value != fulfillAmount + wormhole.messageFee())
                \t\t\t) {
                \t\t\t\trevert InvalidWormholeFee();
                \t\t\t}
                \t\t\tif (referrerAmount > 0) {
                \t\t\t\tpayViaCall(params.referrerAddr, referrerAmount);
                \t\t\t}
                \t\t\tif (protocolAmount > 0) {
                \t\t\t\tpayViaCall(feeManager.feeCollector(), protocolAmount);
                \t\t\t}
                \t\t\tpayViaCall(params.destAddr, netAmount);
                \t\t} else {
                \t\t\tif (params.gasDrop > 0) {
                \t\t\t\tuint256 gasDrop = deNormalizeAmount(params.gasDrop, NATIVE_DECIMALS);
                \t\t\t\tif (
                \t\t\t\t\t(params.batch && msg.value != gasDrop) ||
                \t\t\t\t\t(!params.batch && msg.value != gasDrop + wormhole.messageFee())
                \t\t\t\t) {
                \t\t\t\t\trevert InvalidGasDrop();
                \t\t\t\t}
                \t\t\t\tpayViaCall(params.destAddr, gasDrop);
                \t\t\t} else if (
                \t\t\t\t(params.batch && msg.value != 0) ||
                \t\t\t\t(!params.batch && msg.value != wormhole.messageFee())
                \t\t\t) {
                \t\t\t\trevert InvalidWormholeFee();
                \t\t\t}
                \t\t\t
                \t\t\tif (referrerAmount > 0) {
                \t\t\t\tIERC20(params.tokenOut).safeTransfer(params.referrerAddr, referrerAmount);
                \t\t\t}
                \t\t\tif (protocolAmount > 0) {
                \t\t\t\tIERC20(params.tokenOut).safeTransfer(feeManager.feeCollector(), protocolAmount);
                \t\t\t}
                \t\t\tIERC20(params.tokenOut).safeTransfer(params.destAddr, netAmount);
                \t\t}
                \t}
                \tfunction buildKey(OrderParams memory params, bytes32 tokenIn, uint16 srcChainId, uint8 protocolBps) internal pure returns (Key memory) {
                \t\treturn Key({
                \t\t\ttrader: params.trader,
                \t\t\tsrcChainId: srcChainId,
                \t\t\ttokenIn: tokenIn,
                \t\t\ttokenOut: params.tokenOut,
                \t\t\tminAmountOut: params.minAmountOut,
                \t\t\tgasDrop: params.gasDrop,
                \t\t\tcancelFee: params.cancelFee,
                \t\t\trefundFee: params.refundFee,
                \t\t\tdeadline: params.deadline,
                \t\t\tdestAddr: params.destAddr,
                \t\t\tdestChainId: params.destChainId,
                \t\t\treferrerAddr: params.referrerAddr,
                \t\t\treferrerBps: params.referrerBps,
                \t\t\tprotocolBps: protocolBps,
                \t\t\tauctionMode: params.auctionMode,
                \t\t\trandom: params.random
                \t\t});
                \t}
                \tfunction parseFulfillPayload(bytes memory encoded) public pure returns (FulfillMsg memory fulfillMsg) {
                \t\tuint index = 0;
                \t\tfulfillMsg.action = encoded.toUint8(index);
                \t\tindex += 1;
                \t\tif (fulfillMsg.action != uint8(Action.FULFILL)) {
                \t\t\trevert InvalidAction();
                \t\t}
                \t\tfulfillMsg.orderHash = encoded.toBytes32(index);
                \t\tindex += 32;
                \t\tfulfillMsg.srcChainId = encoded.toUint16(index);
                \t\tindex += 2;
                \t\tfulfillMsg.tokenIn = encoded.toBytes32(index);
                \t\tindex += 32;
                \t\tfulfillMsg.destAddr = encoded.toBytes32(index);
                \t\tindex += 32;
                \t\tfulfillMsg.destChainId = encoded.toUint16(index);
                \t\tindex += 2;
                \t\tfulfillMsg.tokenOut = encoded.toBytes32(index);
                \t\tindex += 32;
                \t\tfulfillMsg.promisedAmount = encoded.toUint64(index);
                \t\tindex += 8;
                \t\tfulfillMsg.gasDrop = encoded.toUint64(index);
                \t\tindex += 8;
                \t\tfulfillMsg.deadline = encoded.toUint64(index);
                \t\tindex += 8;
                \t\tfulfillMsg.referrerAddr = encoded.toBytes32(index);
                \t\tindex += 32;
                \t\tfulfillMsg.referrerBps = encoded.toUint8(index);
                \t\tindex += 1;
                \t\tfulfillMsg.protocolBps = encoded.toUint8(index);
                \t\tindex += 1;
                \t\tfulfillMsg.driver = encoded.toBytes32(index);
                \t\tindex += 32;
                \t}
                \tfunction parseUnlockPayload(bytes memory encoded) public pure returns (UnlockMsg memory unlockMsg) {
                \t\tuint index = 0;
                \t\tunlockMsg.action = encoded.toUint8(index);
                \t\tindex += 1;
                \t\tif (unlockMsg.action != uint8(Action.UNLOCK)) {
                \t\t\trevert InvalidAction();
                \t\t}
                \t\tunlockMsg.orderHash = encoded.toBytes32(index);
                \t\tindex += 32;
                \t\tunlockMsg.srcChainId = encoded.toUint16(index);
                \t\tindex += 2;
                \t\tunlockMsg.tokenIn = encoded.toBytes32(index);
                \t\tindex += 32;
                \t\tunlockMsg.recipient = encoded.toBytes32(index);
                \t\tindex += 32;
                \t}
                \tfunction parseRefundPayload(bytes memory encoded) public pure returns (RefundMsg memory refundMsg) {
                \t\tuint index = 0;
                \t\trefundMsg.action = encoded.toUint8(index);
                \t\tindex += 1;
                \t\tif (refundMsg.action != uint8(Action.REFUND)) {
                \t\t\trevert InvalidAction();
                \t\t}
                \t\trefundMsg.orderHash = encoded.toBytes32(index);
                \t\tindex += 32;
                \t\trefundMsg.srcChainId = encoded.toUint16(index);
                \t\tindex += 2;
                \t\trefundMsg.tokenIn = encoded.toBytes32(index);
                \t\tindex += 32;
                \t\trefundMsg.recipient = encoded.toBytes32(index);
                \t\tindex += 32;
                \t\trefundMsg.canceler = encoded.toBytes32(index);
                \t\tindex += 32;
                \t\trefundMsg.cancelFee = encoded.toUint64(index);
                \t\tindex += 8;
                \t\trefundMsg.refundFee = encoded.toUint64(index);
                \t\tindex += 8;
                \t}
                \tfunction encodeKey(Key memory key) internal pure returns (bytes memory encoded) {
                \t\tencoded = abi.encodePacked(
                \t\t\tkey.trader,
                \t\t\tkey.srcChainId,
                \t\t\tkey.tokenIn,
                \t\t\tkey.destAddr,
                \t\t\tkey.destChainId,
                \t\t\tkey.tokenOut,
                \t\t\tkey.minAmountOut,
                \t\t\tkey.gasDrop,
                \t\t\tkey.cancelFee,
                \t\t\tkey.refundFee,
                \t\t\tkey.deadline,
                \t\t\tkey.referrerAddr,
                \t\t\tkey.referrerBps
                \t\t);
                \t\tencoded = encoded.concat(abi.encodePacked(key.protocolBps, key.auctionMode, key.random));
                \t}
                \tfunction encodeUnlockMsg(UnlockMsg memory unlockMsg) internal pure returns (bytes memory encoded) {
                \t\tencoded = abi.encodePacked(
                \t\t\tunlockMsg.action,
                \t\t\tunlockMsg.orderHash,
                \t\t\tunlockMsg.srcChainId,
                \t\t\tunlockMsg.tokenIn,
                \t\t\tunlockMsg.recipient
                \t\t);
                \t}
                \tfunction encodeRefundMsg(RefundMsg memory refundMsg) internal pure returns (bytes memory encoded) {
                \t\tencoded = abi.encodePacked(
                \t\t\trefundMsg.action,
                \t\t\trefundMsg.orderHash,
                \t\t\trefundMsg.srcChainId,
                \t\t\trefundMsg.tokenIn,
                \t\t\trefundMsg.recipient,
                \t\t\trefundMsg.canceler,
                \t\t\trefundMsg.cancelFee,
                \t\t\trefundMsg.refundFee
                \t\t);
                \t}
                \tfunction payViaCall(address to, uint256 amount) internal {
                \t\t(bool success, ) = payable(to).call{value: amount}('');
                \t\trequire(success, 'payment failed');
                \t}
                \tfunction truncateAddress(bytes32 b) internal pure returns (address) {
                \t\tif (bytes12(b) != 0) {
                \t\t\trevert InvalidEvmAddr();
                \t\t}
                \t\treturn address(uint160(uint256(b)));
                \t}
                \tfunction decimalsOf(address token) internal view returns(uint8) {
                \t\t(,bytes memory queriedDecimals) = token.staticcall(abi.encodeWithSignature('decimals()'));
                \t\treturn abi.decode(queriedDecimals, (uint8));
                \t}
                \tfunction normalizeAmount(uint256 amount, uint8 decimals) internal pure returns(uint256) {
                \t\tif (decimals > 8) {
                \t\t\tamount /= 10 ** (decimals - 8);
                \t\t}
                \t\treturn amount;
                \t}
                \tfunction deNormalizeAmount(uint256 amount, uint8 decimals) internal pure returns(uint256) {
                \t\tif (decimals > 8) {
                \t\t\tamount *= 10 ** (decimals - 8);
                \t\t}
                \t\treturn amount;
                \t}
                \tfunction hashTypedData(bytes32 orderHash, uint256 amountIn, uint256 submissionFee) internal view returns (bytes32) {
                \t\tbytes memory encoded = abi.encode(keccak256("CreateOrder(bytes32 OrderId,uint256 InputAmount,uint256 SubmissionFee)"), orderHash, amountIn, submissionFee);
                \t\treturn toTypedDataHash(domainSeparator, keccak256(encoded));
                \t}
                \tfunction toTypedDataHash(bytes32 _domainSeparator, bytes32 _structHash) internal pure returns (bytes32 digest) {
                \t\tassembly {
                \t\t\tlet ptr := mload(0x40)
                \t\t\tmstore(ptr, "\\x19\\x01")
                \t\t\tmstore(add(ptr, 0x02), _domainSeparator)
                \t\t\tmstore(add(ptr, 0x22), _structHash)
                \t\t\tdigest := keccak256(ptr, 0x42)
                \t\t}
                \t}
                \tfunction pullTokensFrom(address tokenIn, uint256 amount, address from) internal returns (uint256) {
                \t\tuint256 balance = IERC20(tokenIn).balanceOf(address(this));
                \t\tIERC20(tokenIn).safeTransferFrom(from, address(this), amount);
                \t\treturn IERC20(tokenIn).balanceOf(address(this)) - balance;
                \t}
                \tfunction execPermit(
                \t\taddress token,
                \t\taddress owner,
                \t\tPermitParams calldata permitParams
                \t) internal {
                \t\tIERC20Permit(token).permit(
                \t\t\towner,
                \t\t\taddress(this),
                \t\t\tpermitParams.value,
                \t\t\tpermitParams.deadline,
                \t\t\tpermitParams.v,
                \t\t\tpermitParams.r,
                \t\t\tpermitParams.s
                \t\t);
                \t}
                \tfunction setPause(bool _pause) public {
                \t\tif (msg.sender != guardian) {
                \t\t\trevert Unauthorized();
                \t\t}
                \t\tpaused = _pause;
                \t}
                \tfunction setFeeManager(address _feeManager) public {
                \t\tif (msg.sender != guardian) {
                \t\t\trevert Unauthorized();
                \t\t}
                \t\tfeeManager = IFeeManager(_feeManager);
                \t}
                \tfunction setConsistencyLevel(uint8 _consistencyLevel) public {
                \t\tif (msg.sender != guardian) {
                \t\t\trevert Unauthorized();
                \t\t}
                \t\tconsistencyLevel = _consistencyLevel;
                \t}
                \tfunction changeGuardian(address newGuardian) public {
                \t\tif (msg.sender != guardian) {
                \t\t\trevert Unauthorized();
                \t\t}
                \t\tnextGuardian = newGuardian;
                \t}
                \tfunction claimGuardian() public {
                \t\tif (msg.sender != nextGuardian) {
                \t\t\trevert Unauthorized();
                \t\t}
                \t\tguardian = nextGuardian;
                \t}
                \tfunction getOrders(bytes32[] memory orderHashes) public view returns (Order[] memory) {
                \t\tOrder[] memory result = new Order[](orderHashes.length);
                \t\tfor (uint i=0; i<orderHashes.length; i++) {
                \t\t\tresult[i] = orders[orderHashes[i]];
                \t\t}
                \t\treturn result;
                \t}
                \treceive() external payable {}
                }

                File 4 of 8: MayanForwarder
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
                pragma solidity ^0.8.0;
                /**
                 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
                 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
                 *
                 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
                 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
                 * need to send a transaction, and thus is not required to hold Ether at all.
                 */
                interface IERC20Permit {
                    /**
                     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                     * given ``owner``'s signed approval.
                     *
                     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                     * ordering also apply here.
                     *
                     * Emits an {Approval} event.
                     *
                     * Requirements:
                     *
                     * - `spender` cannot be the zero address.
                     * - `deadline` must be a timestamp in the future.
                     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                     * over the EIP712-formatted function arguments.
                     * - the signature must use ``owner``'s current nonce (see {nonces}).
                     *
                     * For more information on the signature format, see the
                     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                     * section].
                     */
                    function permit(
                        address owner,
                        address spender,
                        uint256 value,
                        uint256 deadline,
                        uint8 v,
                        bytes32 r,
                        bytes32 s
                    ) external;
                    /**
                     * @dev Returns the current nonce for `owner`. This value must be
                     * included whenever a signature is generated for {permit}.
                     *
                     * Every successful call to {permit} increases ``owner``'s nonce by one. This
                     * prevents a signature from being used multiple times.
                     */
                    function nonces(address owner) external view returns (uint256);
                    /**
                     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                     */
                    // solhint-disable-next-line func-name-mixedcase
                    function DOMAIN_SEPARATOR() external view returns (bytes32);
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
                pragma solidity ^0.8.0;
                /**
                 * @dev Interface of the ERC20 standard as defined in the EIP.
                 */
                interface IERC20 {
                    /**
                     * @dev Emitted when `value` tokens are moved from one account (`from`) to
                     * another (`to`).
                     *
                     * Note that `value` may be zero.
                     */
                    event Transfer(address indexed from, address indexed to, uint256 value);
                    /**
                     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                     * a call to {approve}. `value` is the new allowance.
                     */
                    event Approval(address indexed owner, address indexed spender, uint256 value);
                    /**
                     * @dev Returns the amount of tokens in existence.
                     */
                    function totalSupply() external view returns (uint256);
                    /**
                     * @dev Returns the amount of tokens owned by `account`.
                     */
                    function balanceOf(address account) external view returns (uint256);
                    /**
                     * @dev Moves `amount` tokens from the caller's account to `to`.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transfer(address to, uint256 amount) external returns (bool);
                    /**
                     * @dev Returns the remaining number of tokens that `spender` will be
                     * allowed to spend on behalf of `owner` through {transferFrom}. This is
                     * zero by default.
                     *
                     * This value changes when {approve} or {transferFrom} are called.
                     */
                    function allowance(address owner, address spender) external view returns (uint256);
                    /**
                     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * IMPORTANT: Beware that changing an allowance with this method brings the risk
                     * that someone may use both the old and the new allowance by unfortunate
                     * transaction ordering. One possible solution to mitigate this race
                     * condition is to first reduce the spender's allowance to 0 and set the
                     * desired value afterwards:
                     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                     *
                     * Emits an {Approval} event.
                     */
                    function approve(address spender, uint256 amount) external returns (bool);
                    /**
                     * @dev Moves `amount` tokens from `from` to `to` using the
                     * allowance mechanism. `amount` is then deducted from the caller's
                     * allowance.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transferFrom(address from, address to, uint256 amount) external returns (bool);
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
                pragma solidity ^0.8.0;
                import "../IERC20.sol";
                import "../extensions/IERC20Permit.sol";
                import "../../../utils/Address.sol";
                /**
                 * @title SafeERC20
                 * @dev Wrappers around ERC20 operations that throw on failure (when the token
                 * contract returns false). Tokens that return no value (and instead revert or
                 * throw on failure) are also supported, non-reverting calls are assumed to be
                 * successful.
                 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                 */
                library SafeERC20 {
                    using Address for address;
                    /**
                     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
                     * non-reverting calls are assumed to be successful.
                     */
                    function safeTransfer(IERC20 token, address to, uint256 value) internal {
                        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                    }
                    /**
                     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
                     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
                     */
                    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                    }
                    /**
                     * @dev Deprecated. This function has issues similar to the ones found in
                     * {IERC20-approve}, and its usage is discouraged.
                     *
                     * Whenever possible, use {safeIncreaseAllowance} and
                     * {safeDecreaseAllowance} instead.
                     */
                    function safeApprove(IERC20 token, address spender, uint256 value) internal {
                        // safeApprove should only be called when setting an initial allowance,
                        // or when resetting it to zero. To increase and decrease it, use
                        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                        require(
                            (value == 0) || (token.allowance(address(this), spender) == 0),
                            "SafeERC20: approve from non-zero to non-zero allowance"
                        );
                        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                    }
                    /**
                     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                     * non-reverting calls are assumed to be successful.
                     */
                    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                        uint256 oldAllowance = token.allowance(address(this), spender);
                        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
                    }
                    /**
                     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                     * non-reverting calls are assumed to be successful.
                     */
                    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                        unchecked {
                            uint256 oldAllowance = token.allowance(address(this), spender);
                            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
                        }
                    }
                    /**
                     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
                     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
                     * to be set to zero before setting it to a non-zero value, such as USDT.
                     */
                    function forceApprove(IERC20 token, address spender, uint256 value) internal {
                        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
                        if (!_callOptionalReturnBool(token, approvalCall)) {
                            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
                            _callOptionalReturn(token, approvalCall);
                        }
                    }
                    /**
                     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
                     * Revert on invalid signature.
                     */
                    function safePermit(
                        IERC20Permit token,
                        address owner,
                        address spender,
                        uint256 value,
                        uint256 deadline,
                        uint8 v,
                        bytes32 r,
                        bytes32 s
                    ) internal {
                        uint256 nonceBefore = token.nonces(owner);
                        token.permit(owner, spender, value, deadline, v, r, s);
                        uint256 nonceAfter = token.nonces(owner);
                        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
                    }
                    /**
                     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                     * on the return value: the return value is optional (but if data is returned, it must not be false).
                     * @param token The token targeted by the call.
                     * @param data The call data (encoded using abi.encode or one of its variants).
                     */
                    function _callOptionalReturn(IERC20 token, bytes memory data) private {
                        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
                        // the target address contains contract code and also asserts for success in the low-level call.
                        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                    }
                    /**
                     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                     * on the return value: the return value is optional (but if data is returned, it must not be false).
                     * @param token The token targeted by the call.
                     * @param data The call data (encoded using abi.encode or one of its variants).
                     *
                     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
                     */
                    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
                        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
                        // and not revert is the subcall reverts.
                        (bool success, bytes memory returndata) = address(token).call(data);
                        return
                            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
                    }
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
                pragma solidity ^0.8.1;
                /**
                 * @dev Collection of functions related to the address type
                 */
                library Address {
                    /**
                     * @dev Returns true if `account` is a contract.
                     *
                     * [IMPORTANT]
                     * ====
                     * It is unsafe to assume that an address for which this function returns
                     * false is an externally-owned account (EOA) and not a contract.
                     *
                     * Among others, `isContract` will return false for the following
                     * types of addresses:
                     *
                     *  - an externally-owned account
                     *  - a contract in construction
                     *  - an address where a contract will be created
                     *  - an address where a contract lived, but was destroyed
                     *
                     * Furthermore, `isContract` will also return true if the target contract within
                     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                     * which only has an effect at the end of a transaction.
                     * ====
                     *
                     * [IMPORTANT]
                     * ====
                     * You shouldn't rely on `isContract` to protect against flash loan attacks!
                     *
                     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                     * constructor.
                     * ====
                     */
                    function isContract(address account) internal view returns (bool) {
                        // This method relies on extcodesize/address.code.length, which returns 0
                        // for contracts in construction, since the code is only stored at the end
                        // of the constructor execution.
                        return account.code.length > 0;
                    }
                    /**
                     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                     * `recipient`, forwarding all available gas and reverting on errors.
                     *
                     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                     * of certain opcodes, possibly making contracts go over the 2300 gas limit
                     * imposed by `transfer`, making them unable to receive funds via
                     * `transfer`. {sendValue} removes this limitation.
                     *
                     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                     *
                     * IMPORTANT: because control is transferred to `recipient`, care must be
                     * taken to not create reentrancy vulnerabilities. Consider using
                     * {ReentrancyGuard} or the
                     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                     */
                    function sendValue(address payable recipient, uint256 amount) internal {
                        require(address(this).balance >= amount, "Address: insufficient balance");
                        (bool success, ) = recipient.call{value: amount}("");
                        require(success, "Address: unable to send value, recipient may have reverted");
                    }
                    /**
                     * @dev Performs a Solidity function call using a low level `call`. A
                     * plain `call` is an unsafe replacement for a function call: use this
                     * function instead.
                     *
                     * If `target` reverts with a revert reason, it is bubbled up by this
                     * function (like regular Solidity function calls).
                     *
                     * Returns the raw returned data. To convert to the expected return value,
                     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                     *
                     * Requirements:
                     *
                     * - `target` must be a contract.
                     * - calling `target` with `data` must not revert.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                     * `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, 0, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but also transferring `value` wei to `target`.
                     *
                     * Requirements:
                     *
                     * - the calling contract must have an ETH balance of at least `value`.
                     * - the called Solidity function must be `payable`.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                     * with `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(
                        address target,
                        bytes memory data,
                        uint256 value,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        require(address(this).balance >= value, "Address: insufficient balance for call");
                        (bool success, bytes memory returndata) = target.call{value: value}(data);
                        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                        return functionStaticCall(target, data, "Address: low-level static call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal view returns (bytes memory) {
                        (bool success, bytes memory returndata) = target.staticcall(data);
                        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        (bool success, bytes memory returndata) = target.delegatecall(data);
                        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                    }
                    /**
                     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                     *
                     * _Available since v4.8._
                     */
                    function verifyCallResultFromTarget(
                        address target,
                        bool success,
                        bytes memory returndata,
                        string memory errorMessage
                    ) internal view returns (bytes memory) {
                        if (success) {
                            if (returndata.length == 0) {
                                // only check isContract if the call was successful and the return data is empty
                                // otherwise we already know that it was a contract
                                require(isContract(target), "Address: call to non-contract");
                            }
                            return returndata;
                        } else {
                            _revert(returndata, errorMessage);
                        }
                    }
                    /**
                     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                     * revert reason or using the provided one.
                     *
                     * _Available since v4.3._
                     */
                    function verifyCallResult(
                        bool success,
                        bytes memory returndata,
                        string memory errorMessage
                    ) internal pure returns (bytes memory) {
                        if (success) {
                            return returndata;
                        } else {
                            _revert(returndata, errorMessage);
                        }
                    }
                    function _revert(bytes memory returndata, string memory errorMessage) private pure {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            /// @solidity memory-safe-assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
                // SPDX-License-Identifier: Unlicense
                /*
                 * @title Solidity Bytes Arrays Utils
                 * @author Gonçalo Sá <[email protected]>
                 *
                 * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
                 *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
                 */
                pragma solidity >=0.8.0 <0.9.0;
                library BytesLib {
                    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 mlengthmod := mod(mlength, 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)
                    {
                        require(_length + 31 >= _length, "slice_overflow");
                        require(_bytes.length >= _start + _length, "slice_outOfBounds");
                        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) {
                        require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
                        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) {
                        require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
                        uint8 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0x1), _start))
                        }
                        return tempUint;
                    }
                    function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
                        require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
                        uint16 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0x2), _start))
                        }
                        return tempUint;
                    }
                    function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
                        require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
                        uint32 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0x4), _start))
                        }
                        return tempUint;
                    }
                    function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
                        require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
                        uint64 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0x8), _start))
                        }
                        return tempUint;
                    }
                    function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
                        require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
                        uint96 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0xc), _start))
                        }
                        return tempUint;
                    }
                    function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
                        require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
                        uint128 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0x10), _start))
                        }
                        return tempUint;
                    }
                    function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
                        require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
                        uint256 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0x20), _start))
                        }
                        return tempUint;
                    }
                    function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
                        require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
                        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)
                                        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.0;
                import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
                import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
                import "./libs/BytesLib.sol";
                contract MayanForwarder {
                \tusing SafeERC20 for IERC20;
                \tusing BytesLib for bytes;
                \tevent SwapAndForwarded(uint256 amount);
                \taddress public guardian;
                \taddress public nextGuardian;
                \tmapping(address => bool) public swapProtocols;
                \tmapping(address => bool) public mayanProtocols;
                \tevent ForwardedEth(address mayanProtocol, bytes protocolData);
                \tevent ForwardedERC20(address token, uint256 amount, address mayanProtocol, bytes protocolData);
                \tevent SwapAndForwardedEth(uint256 amountIn, address swapProtocol, address middleToken, uint256 middleAmount, address mayanProtocol, bytes mayanData);
                \tevent SwapAndForwardedERC20(address tokenIn, uint256 amountIn, address swapProtocol, address middleToken, uint256 middleAmount, address mayanProtocol, bytes mayanData);
                \terror UnsupportedProtocol();
                \tstruct PermitParams {
                \t\tuint256 value;
                \t\tuint256 deadline;
                \t\tuint8 v;
                \t\tbytes32 r;
                \t\tbytes32 s;
                \t}
                \tconstructor(address _guardian, address[] memory _swapProtocols, address[] memory _mayanProtocols) {
                \t\tguardian = _guardian;
                \t\tfor (uint256 i = 0; i < _swapProtocols.length; i++) {
                \t\t\tswapProtocols[_swapProtocols[i]] = true;
                \t\t}
                \t\tfor (uint256 i = 0; i < _mayanProtocols.length; i++) {
                \t\t\tmayanProtocols[_mayanProtocols[i]] = true;
                \t\t}
                \t}
                \tfunction forwardEth(
                \t\taddress mayanProtocol,
                \t\tbytes calldata protocolData
                \t) external payable {
                \t\tif (!mayanProtocols[mayanProtocol]) {
                \t\t\trevert UnsupportedProtocol();
                \t\t}
                \t\t(bool success, bytes memory returnedData) = mayanProtocol.call{value: msg.value}(protocolData);
                \t\trequire(success, string(returnedData));
                \t\temit ForwardedEth(mayanProtocol, protocolData);
                \t}
                \t
                \tfunction forwardERC20(
                \t\taddress tokenIn,
                \t\tuint256 amountIn,
                \t\tPermitParams calldata permitParams,
                \t\taddress mayanProtocol,
                \t\tbytes calldata protocolData
                \t\t) external payable {
                \t\tif (!mayanProtocols[mayanProtocol]) {
                \t\t\trevert UnsupportedProtocol();
                \t\t}
                \t\tpullTokenIn(tokenIn, amountIn, permitParams);
                \t\tmaxApproveIfNeeded(tokenIn, mayanProtocol, amountIn);
                \t\t(bool success, bytes memory returnedData) = mayanProtocol.call{value: msg.value}(protocolData);
                \t\trequire(success, string(returnedData));
                \t\temit ForwardedERC20(tokenIn, amountIn, mayanProtocol, protocolData);
                \t}
                \tfunction swapAndForwardEth(
                \t\tuint256 amountIn,
                \t\taddress swapProtocol,
                \t\tbytes calldata swapData,
                \t\taddress middleToken,
                \t\tuint256 minMiddleAmount,
                \t\taddress mayanProtocol,
                \t\tbytes calldata mayanData
                \t) external payable {
                \t\tif (!swapProtocols[swapProtocol] || !mayanProtocols[mayanProtocol]) {
                \t\t\trevert UnsupportedProtocol();
                \t\t}
                \t\trequire(middleToken != address(0), "middleToken cannot be zero address");
                \t\trequire(msg.value >= amountIn, "insufficient amountIn");
                \t\tuint256 middleAmount = IERC20(middleToken).balanceOf(address(this));
                \t\t(bool success, bytes memory returnedData) = swapProtocol.call{value: amountIn}(swapData);
                \t\trequire(success, string(returnedData));
                \t\tmiddleAmount = IERC20(middleToken).balanceOf(address(this)) - middleAmount;
                \t\trequire(middleAmount >= minMiddleAmount, "MayanForwarder: insufficient middle token amount");
                \t\tmaxApproveIfNeeded(middleToken, mayanProtocol, middleAmount);
                \t\tbytes memory modifiedData = replaceMiddleAmount(mayanData, middleAmount);
                \t\t(success, returnedData) = mayanProtocol.call{value: msg.value - amountIn}(modifiedData);
                \t\trequire(success, string(returnedData));
                \t\temit SwapAndForwardedEth(amountIn, swapProtocol, middleToken, middleAmount, mayanProtocol, mayanData);
                \t}
                \tfunction swapAndForwardERC20(
                \t\taddress tokenIn,
                \t\tuint256 amountIn,
                \t\tPermitParams calldata permitParams,
                \t\taddress swapProtocol,
                \t\tbytes calldata swapData,
                \t\taddress middleToken,
                \t\tuint256 minMiddleAmount,
                \t\taddress mayanProtocol,
                \t\tbytes calldata mayanData
                \t) external payable {
                \t\tif (!swapProtocols[swapProtocol] || !mayanProtocols[mayanProtocol]) {
                \t\t\trevert UnsupportedProtocol();
                \t\t}
                \t\trequire(tokenIn != middleToken, "tokenIn and tokenOut must be different");
                \t\tpullTokenIn(tokenIn, amountIn, permitParams);
                \t\tmaxApproveIfNeeded(tokenIn, swapProtocol, amountIn);
                \t\tuint256 middleAmount = IERC20(middleToken).balanceOf(address(this));
                \t\t(bool success, bytes memory returnedData) = swapProtocol.call{value: 0}(swapData);
                \t\trequire(success, string(returnedData));
                \t\tmiddleAmount = IERC20(middleToken).balanceOf(address(this)) - middleAmount;
                \t\trequire(middleAmount >= minMiddleAmount, "insufficient middle token");
                \t\tmaxApproveIfNeeded(middleToken, mayanProtocol, middleAmount);
                \t\tbytes memory modifiedData = replaceMiddleAmount(mayanData, middleAmount);
                \t\t(success, returnedData) = mayanProtocol.call{value: msg.value}(modifiedData);
                \t\trequire(success, string(returnedData));
                \t\ttransferBackRemaining(tokenIn, amountIn);
                \t\temit SwapAndForwardedERC20(tokenIn, amountIn, swapProtocol, middleToken, middleAmount, mayanProtocol, mayanData);
                \t}
                \tfunction replaceMiddleAmount(bytes calldata mayanData, uint256 middleAmount) internal pure returns(bytes memory) {
                \t\trequire(mayanData.length >= 68, "Mayan data too short");
                \t\tbytes memory modifiedData = new bytes(mayanData.length);
                \t\t// Copy the function selector and token in
                \t\tfor (uint i = 0; i < 36; i++) {
                \t\t\tmodifiedData[i] = mayanData[i];
                \t\t}
                \t\t// Encode the amount and place it into the modified call data
                \t\t// Starting from byte 36 to byte 67 (32 bytes for uint256)
                \t\tbytes memory encodedAmount = abi.encode(middleAmount);
                \t\tfor (uint i = 0; i < 32; i++) {
                \t\t\tmodifiedData[i + 36] = encodedAmount[i];
                \t\t}
                \t\t// Copy the rest of the original data after the first argument
                \t\tfor (uint i = 68; i < mayanData.length; i++) {
                \t\t\tmodifiedData[i] = mayanData[i];
                \t\t}
                \t\treturn modifiedData;
                \t}
                \tfunction maxApproveIfNeeded(address tokenAddr, address spender, uint256 amount) internal {
                \t\tIERC20 token = IERC20(tokenAddr);
                \t\tuint256 currentAllowance = token.allowance(address(this), spender);
                \t\tif (currentAllowance < amount) {
                \t\t\ttoken.safeApprove(spender, 0);
                \t\t\ttoken.safeApprove(spender, type(uint256).max);
                \t\t}
                \t}
                \tfunction execPermit(
                \t\taddress token,
                \t\taddress owner,
                \t\tPermitParams calldata permitParams
                \t) internal {
                \t\tIERC20Permit(token).permit(
                \t\t\towner,
                \t\t\taddress(this),
                \t\t\tpermitParams.value,
                \t\t\tpermitParams.deadline,
                \t\t\tpermitParams.v,
                \t\t\tpermitParams.r,
                \t\t\tpermitParams.s
                \t\t);
                \t}
                \tfunction pullTokenIn(
                \t\taddress tokenIn,
                \t\tuint256 amountIn,
                \t\tPermitParams calldata permitParams
                \t) internal {
                \t\tuint256 allowance = IERC20(tokenIn).allowance(msg.sender, address(this));
                \t\tif (allowance < amountIn) {
                \t\t\texecPermit(tokenIn, msg.sender, permitParams);
                \t\t}
                \t\tIERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
                \t}
                \tfunction transferBackRemaining(address token, uint256 maxAmount) internal {
                \t\tuint256 remaining = IERC20(token).balanceOf(address(this));
                \t\tif (remaining > 0 && remaining <= maxAmount) {
                \t\t\tIERC20(token).safeTransfer(msg.sender, remaining);
                \t\t}
                \t}
                \tfunction rescueToken(address token, uint256 amount, address to) public {
                \t\trequire(msg.sender == guardian, 'only guardian');
                \t\tIERC20(token).safeTransfer(to, amount);
                \t}
                \tfunction rescueEth(uint256 amount, address payable to) public {
                \t\trequire(msg.sender == guardian, 'only guardian');
                \t\trequire(to != address(0), 'transfer to the zero address');
                \t\tto.transfer(amount);
                \t}
                \tfunction changeGuardian(address newGuardian) public {
                \t\trequire(msg.sender == guardian, 'only guardian');
                \t\tnextGuardian = newGuardian;
                \t}
                \tfunction claimGuardian() public {
                \t\trequire(msg.sender == nextGuardian, 'only next guardian');
                \t\tguardian = nextGuardian;
                \t}
                \tfunction setSwapProtocol(address swapProtocol, bool enabled) public {
                \t\trequire(msg.sender == guardian, 'only guardian');
                \t\tswapProtocols[swapProtocol] = enabled;
                \t}
                \tfunction setMayanProtocol(address mayanProtocol, bool enabled) public {
                \t\trequire(msg.sender == guardian, 'only guardian');
                \t\tmayanProtocols[mayanProtocol] = enabled;
                \t}
                }

                File 5 of 8: MayanFacet
                // SPDX-License-Identifier: MIT
                pragma solidity ^0.8.17;
                import { ILiFi } from "../Interfaces/ILiFi.sol";
                import { LibAsset, IERC20 } from "../Libraries/LibAsset.sol";
                import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
                import { LibSwap } from "../Libraries/LibSwap.sol";
                import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol";
                import { SwapperV2 } from "../Helpers/SwapperV2.sol";
                import { Validatable } from "../Helpers/Validatable.sol";
                import { IMayan } from "../Interfaces/IMayan.sol";
                /// @title Mayan Facet
                /// @author LI.FI (https://li.fi)
                /// @notice Provides functionality for bridging through Mayan Bridge
                /// @custom:version 1.1.0
                contract MayanFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable {
                    /// Storage ///
                    bytes32 internal constant NAMESPACE = keccak256("com.lifi.facets.mayan");
                    address internal constant NON_EVM_ADDRESS =
                        0x11f111f111f111F111f111f111F111f111f111F1;
                    IMayan public immutable mayan;
                    /// @dev Mayan specific bridge data
                    /// @param nonEVMReceiver The address of the non-EVM receiver if applicable
                    /// @param mayanProtocol The address of the Mayan protocol final contract
                    /// @param protocolData The protocol data for the Mayan protocol
                    struct MayanData {
                        bytes32 nonEVMReceiver;
                        address mayanProtocol;
                        bytes protocolData;
                    }
                    /// Errors ///
                    error InvalidReceiver(address expected, address actual);
                    error InvalidNonEVMReceiver(bytes32 expected, bytes32 actual);
                    /// Events ///
                    event BridgeToNonEVMChain(
                        bytes32 indexed transactionId,
                        uint256 indexed destinationChainId,
                        bytes32 receiver
                    );
                    /// Constructor ///
                    /// @notice Constructor for the contract.
                    constructor(IMayan _mayan) {
                        mayan = _mayan;
                    }
                    /// External Methods ///
                    /// @notice Bridges tokens via Mayan
                    /// @param _bridgeData The core information needed for bridging
                    /// @param _mayanData Data specific to Mayan
                    function startBridgeTokensViaMayan(
                        ILiFi.BridgeData memory _bridgeData,
                        MayanData calldata _mayanData
                    )
                        external
                        payable
                        nonReentrant
                        refundExcessNative(payable(msg.sender))
                        validateBridgeData(_bridgeData)
                        doesNotContainSourceSwaps(_bridgeData)
                        doesNotContainDestinationCalls(_bridgeData)
                    {
                        LibAsset.depositAsset(
                            _bridgeData.sendingAssetId,
                            _bridgeData.minAmount
                        );
                        if (LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) {
                            // Normalize the amount to 8 decimals
                            _bridgeData.minAmount = _normalizeAmount(
                                _bridgeData.minAmount,
                                18
                            );
                        }
                        _startBridge(_bridgeData, _mayanData);
                    }
                    /// @notice Performs a swap before bridging via Mayan
                    /// @param _bridgeData The core information needed for bridging
                    /// @param _swapData An array of swap related data for performing swaps before bridging
                    /// @param _mayanData Data specific to Mayan
                    function swapAndStartBridgeTokensViaMayan(
                        ILiFi.BridgeData memory _bridgeData,
                        LibSwap.SwapData[] calldata _swapData,
                        MayanData memory _mayanData
                    )
                        external
                        payable
                        nonReentrant
                        refundExcessNative(payable(msg.sender))
                        containsSourceSwaps(_bridgeData)
                        doesNotContainDestinationCalls(_bridgeData)
                        validateBridgeData(_bridgeData)
                    {
                        _bridgeData.minAmount = _depositAndSwap(
                            _bridgeData.transactionId,
                            _bridgeData.minAmount,
                            _swapData,
                            payable(msg.sender)
                        );
                        uint256 decimals;
                        bool isNative = LibAsset.isNativeAsset(_bridgeData.sendingAssetId);
                        decimals = isNative
                            ? 18
                            : ERC20(_bridgeData.sendingAssetId).decimals();
                        // Normalize the amount to 8 decimals
                        _bridgeData.minAmount = _normalizeAmount(
                            _bridgeData.minAmount,
                            uint8(decimals)
                        );
                        // Native values are not passed as calldata
                        if (!isNative) {
                            // Update the protocol data with the new input amount
                            _mayanData.protocolData = _replaceInputAmount(
                                _mayanData.protocolData,
                                _bridgeData.minAmount
                            );
                        }
                        _startBridge(_bridgeData, _mayanData);
                    }
                    /// Internal Methods ///
                    /// @dev Contains the business logic for the bridge via Mayan
                    /// @param _bridgeData The core information needed for bridging
                    /// @param _mayanData Data specific to Mayan
                    function _startBridge(
                        ILiFi.BridgeData memory _bridgeData,
                        MayanData memory _mayanData
                    ) internal {
                        // Validate receiver address
                        if (_bridgeData.receiver == NON_EVM_ADDRESS) {
                            if (_mayanData.nonEVMReceiver == bytes32(0)) {
                                revert InvalidNonEVMReceiver(
                                    _mayanData.nonEVMReceiver,
                                    bytes32(0)
                                );
                            }
                            bytes32 receiver = _parseReceiver(_mayanData.protocolData);
                            if (_mayanData.nonEVMReceiver != receiver) {
                                revert InvalidNonEVMReceiver(
                                    _mayanData.nonEVMReceiver,
                                    receiver
                                );
                            }
                        } else {
                            address receiver = address(
                                uint160(uint256(_parseReceiver(_mayanData.protocolData)))
                            );
                            if (_bridgeData.receiver != receiver) {
                                revert InvalidReceiver(_bridgeData.receiver, receiver);
                            }
                        }
                        IMayan.PermitParams memory emptyPermitParams;
                        if (!LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) {
                            LibAsset.maxApproveERC20(
                                IERC20(_bridgeData.sendingAssetId),
                                address(mayan),
                                _bridgeData.minAmount
                            );
                            mayan.forwardERC20(
                                _bridgeData.sendingAssetId,
                                _bridgeData.minAmount,
                                emptyPermitParams,
                                _mayanData.mayanProtocol,
                                _mayanData.protocolData
                            );
                        } else {
                            mayan.forwardEth{ value: _bridgeData.minAmount }(
                                _mayanData.mayanProtocol,
                                _mayanData.protocolData
                            );
                        }
                        if (_bridgeData.receiver == NON_EVM_ADDRESS) {
                            emit BridgeToNonEVMChain(
                                _bridgeData.transactionId,
                                _bridgeData.destinationChainId,
                                _mayanData.nonEVMReceiver
                            );
                        }
                        emit LiFiTransferStarted(_bridgeData);
                    }
                    // @dev Parses the receiver address from the protocol data
                    // @param protocolData The protocol data for the Mayan protocol
                    // @return receiver The receiver address
                    function _parseReceiver(
                        bytes memory protocolData
                    ) internal pure returns (bytes32 receiver) {
                        bytes4 selector;
                        assembly {
                            // Load the selector from the protocol data
                            selector := mload(add(protocolData, 0x20))
                            // Shift the selector to the right by 224 bits to match shape of literal in switch statement
                            let shiftedSelector := shr(224, selector)
                            switch shiftedSelector
                            // Note: [*bytes32*] = location of receiver address
                            case 0x94454a5d {
                                // 0x94454a5d bridgeWithFee(address,uint256,uint64,uint64,[*bytes32*],(uint32,bytes32,bytes32))
                                receiver := mload(add(protocolData, 0xa4)) // MayanCircle::bridgeWithFee()
                            }
                            case 0x32ad465f {
                                // 0x32ad465f bridgeWithLockedFee(address,uint256,uint64,uint256,(uint32,[*bytes32*],bytes32))
                                receiver := mload(add(protocolData, 0xc4)) // MayanCircle::bridgeWithLockedFee()
                            }
                            case 0xafd9b706 {
                                // 0xafd9b706 createOrder((address,uint256,uint64,[*bytes32*],uint16,bytes32,uint64,uint64,uint64,bytes32,uint8),(uint32,bytes32,bytes32))
                                receiver := mload(add(protocolData, 0x84)) // MayanCircle::createOrder()
                            }
                            case 0x6111ad25 {
                                // 0x6111ad25 swap((uint64,uint64,uint64),(bytes32,uint16,bytes32,[*bytes32*],uint16,bytes32,bytes32),bytes32,uint16,(uint256,uint64,uint64,bool,uint64,bytes),address,uint256)
                                receiver := mload(add(protocolData, 0xe4)) // MayanSwap::swap()
                            }
                            case 0x1eb1cff0 {
                                // 0x1eb1cff0 wrapAndSwapETH((uint64,uint64,uint64),(bytes32,uint16,bytes32,[*bytes32*],uint16,bytes32,bytes32),bytes32,uint16,(uint256,uint64,uint64,bool,uint64,bytes))
                                receiver := mload(add(protocolData, 0xe4)) // MayanSwap::wrapAndSwapETH()
                            }
                            case 0xb866e173 {
                                // 0xb866e173 createOrderWithEth((bytes32,bytes32,uint64,uint64,uint64,uint64,uint64,[*bytes32*],uint16,bytes32,uint8,uint8,bytes32))
                                receiver := mload(add(protocolData, 0x104)) // MayanSwift::createOrderWithEth()
                            }
                            case 0x8e8d142b {
                                // 0x8e8d142b createOrderWithToken(address,uint256,(bytes32,bytes32,uint64,uint64,uint64,uint64,uint64,[*bytes32*],uint16,bytes32,uint8,uint8,bytes32))
                                receiver := mload(add(protocolData, 0x144)) // MayanSwift::createOrderWithToken()
                            }
                            case 0x1c59b7fc {
                                // 0x1c59b7fc MayanCircle::createOrder((address,uint256,uint64,bytes32,uint16,bytes32,uint64,uint64,uint64,bytes32,uint8))
                                receiver := mload(add(protocolData, 0x84))
                            }
                            case 0x9be95bb4 {
                                // 0x9be95bb4 MayanCircle::bridgeWithLockedFee(address,uint256,uint64,uint256,uint32,bytes32)
                                receiver := mload(add(protocolData, 0xc4))
                            }
                            case 0x2072197f {
                                // 0x2072197f MayanCircle::bridgeWithFee(address,uint256,uint64,uint64,bytes32,uint32,uint8,bytes)
                                receiver := mload(add(protocolData, 0xa4))
                            }
                            default {
                                receiver := 0x0
                            }
                        }
                    }
                    // @dev Normalizes the amount to 8 decimals
                    // @param amount The amount to normalize
                    // @param decimals The number of decimals in the asset
                    function _normalizeAmount(
                        uint256 amount,
                        uint8 decimals
                    ) internal pure returns (uint256) {
                        if (decimals > 8) {
                            amount /= 10 ** (decimals - 8);
                            amount *= 10 ** (decimals - 8);
                        }
                        return amount;
                    }
                    // @dev Replaces the input amount in the protocol data
                    // @param protocolData The protocol data for the Mayan protocol
                    // @param inputAmount The new input amount
                    // @return modifiedData The modified protocol data
                    function _replaceInputAmount(
                        bytes memory protocolData,
                        uint256 inputAmount
                    ) internal pure returns (bytes memory) {
                        require(protocolData.length >= 68, "protocol data too short");
                        bytes memory modifiedData = new bytes(protocolData.length);
                        bytes4 functionSelector = bytes4(protocolData[0]) |
                            (bytes4(protocolData[1]) >> 8) |
                            (bytes4(protocolData[2]) >> 16) |
                            (bytes4(protocolData[3]) >> 24);
                        uint256 amountIndex;
                        // Only the wh swap method has the amount as last argument
                        bytes4 swapSelector = 0x6111ad25;
                        if (functionSelector == swapSelector) {
                            amountIndex = protocolData.length - 256;
                        } else {
                            amountIndex = 36;
                        }
                        // Copy the function selector and params before amount in
                        for (uint i = 0; i < amountIndex; i++) {
                            modifiedData[i] = protocolData[i];
                        }
                        // Encode the amount and place it into the modified call data
                        bytes memory encodedAmount = abi.encode(inputAmount);
                        for (uint i = 0; i < 32; i++) {
                            modifiedData[i + amountIndex] = encodedAmount[i];
                        }
                        // Copy the rest of the original data after the input argument
                        for (uint i = amountIndex + 32; i < protocolData.length; i++) {
                            modifiedData[i] = protocolData[i];
                        }
                        return modifiedData;
                    }
                }
                // SPDX-License-Identifier: MIT
                /// @custom:version 1.0.0
                pragma solidity ^0.8.17;
                interface ILiFi {
                    /// Structs ///
                    struct BridgeData {
                        bytes32 transactionId;
                        string bridge;
                        string integrator;
                        address referrer;
                        address sendingAssetId;
                        address receiver;
                        uint256 minAmount;
                        uint256 destinationChainId;
                        bool hasSourceSwaps;
                        bool hasDestinationCall;
                    }
                    /// Events ///
                    event LiFiTransferStarted(ILiFi.BridgeData bridgeData);
                    event LiFiTransferCompleted(
                        bytes32 indexed transactionId,
                        address receivingAssetId,
                        address receiver,
                        uint256 amount,
                        uint256 timestamp
                    );
                    event LiFiTransferRecovered(
                        bytes32 indexed transactionId,
                        address receivingAssetId,
                        address receiver,
                        uint256 amount,
                        uint256 timestamp
                    );
                    event LiFiGenericSwapCompleted(
                        bytes32 indexed transactionId,
                        string integrator,
                        string referrer,
                        address receiver,
                        address fromAssetId,
                        address toAssetId,
                        uint256 fromAmount,
                        uint256 toAmount
                    );
                    // Deprecated but kept here to include in ABI to parse historic events
                    event LiFiSwappedGeneric(
                        bytes32 indexed transactionId,
                        string integrator,
                        string referrer,
                        address fromAssetId,
                        address toAssetId,
                        uint256 fromAmount,
                        uint256 toAmount
                    );
                }
                // SPDX-License-Identifier: UNLICENSED
                pragma solidity ^0.8.17;
                import { InsufficientBalance, NullAddrIsNotAnERC20Token, NullAddrIsNotAValidSpender, NoTransferToNullAddress, InvalidAmount, NativeAssetTransferFailed } from "../Errors/GenericErrors.sol";
                import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
                import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                import { LibSwap } from "./LibSwap.sol";
                /// @title LibAsset
                /// @custom:version 1.0.2
                /// @notice This library contains helpers for dealing with onchain transfers
                ///         of assets, including accounting for the native asset `assetId`
                ///         conventions and any noncompliant ERC20 transfers
                library LibAsset {
                    uint256 private constant MAX_UINT = type(uint256).max;
                    address internal constant NULL_ADDRESS = address(0);
                    address internal constant NON_EVM_ADDRESS =
                        0x11f111f111f111F111f111f111F111f111f111F1;
                    /// @dev All native assets use the empty address for their asset id
                    ///      by convention
                    address internal constant NATIVE_ASSETID = NULL_ADDRESS; //address(0)
                    /// @notice Gets the balance of the inheriting contract for the given asset
                    /// @param assetId The asset identifier to get the balance of
                    /// @return Balance held by contracts using this library
                    function getOwnBalance(address assetId) internal view returns (uint256) {
                        return
                            isNativeAsset(assetId)
                                ? address(this).balance
                                : IERC20(assetId).balanceOf(address(this));
                    }
                    /// @notice Transfers ether from the inheriting contract to a given
                    ///         recipient
                    /// @param recipient Address to send ether to
                    /// @param amount Amount to send to given recipient
                    function transferNativeAsset(
                        address payable recipient,
                        uint256 amount
                    ) private {
                        if (recipient == NULL_ADDRESS) revert NoTransferToNullAddress();
                        if (amount > address(this).balance)
                            revert InsufficientBalance(amount, address(this).balance);
                        // solhint-disable-next-line avoid-low-level-calls
                        (bool success, ) = recipient.call{ value: amount }("");
                        if (!success) revert NativeAssetTransferFailed();
                    }
                    /// @notice If the current allowance is insufficient, the allowance for a given spender
                    /// is set to MAX_UINT.
                    /// @param assetId Token address to transfer
                    /// @param spender Address to give spend approval to
                    /// @param amount Amount to approve for spending
                    function maxApproveERC20(
                        IERC20 assetId,
                        address spender,
                        uint256 amount
                    ) internal {
                        if (isNativeAsset(address(assetId))) {
                            return;
                        }
                        if (spender == NULL_ADDRESS) {
                            revert NullAddrIsNotAValidSpender();
                        }
                        if (assetId.allowance(address(this), spender) < amount) {
                            SafeERC20.forceApprove(IERC20(assetId), spender, MAX_UINT);
                        }
                    }
                    /// @notice Transfers tokens from the inheriting contract to a given
                    ///         recipient
                    /// @param assetId Token address to transfer
                    /// @param recipient Address to send token to
                    /// @param amount Amount to send to given recipient
                    function transferERC20(
                        address assetId,
                        address recipient,
                        uint256 amount
                    ) private {
                        if (isNativeAsset(assetId)) {
                            revert NullAddrIsNotAnERC20Token();
                        }
                        if (recipient == NULL_ADDRESS) {
                            revert NoTransferToNullAddress();
                        }
                        uint256 assetBalance = IERC20(assetId).balanceOf(address(this));
                        if (amount > assetBalance) {
                            revert InsufficientBalance(amount, assetBalance);
                        }
                        SafeERC20.safeTransfer(IERC20(assetId), recipient, amount);
                    }
                    /// @notice Transfers tokens from a sender to a given recipient
                    /// @param assetId Token address to transfer
                    /// @param from Address of sender/owner
                    /// @param to Address of recipient/spender
                    /// @param amount Amount to transfer from owner to spender
                    function transferFromERC20(
                        address assetId,
                        address from,
                        address to,
                        uint256 amount
                    ) internal {
                        if (isNativeAsset(assetId)) {
                            revert NullAddrIsNotAnERC20Token();
                        }
                        if (to == NULL_ADDRESS) {
                            revert NoTransferToNullAddress();
                        }
                        IERC20 asset = IERC20(assetId);
                        uint256 prevBalance = asset.balanceOf(to);
                        SafeERC20.safeTransferFrom(asset, from, to, amount);
                        if (asset.balanceOf(to) - prevBalance != amount) {
                            revert InvalidAmount();
                        }
                    }
                    function depositAsset(address assetId, uint256 amount) internal {
                        if (amount == 0) revert InvalidAmount();
                        if (isNativeAsset(assetId)) {
                            if (msg.value < amount) revert InvalidAmount();
                        } else {
                            uint256 balance = IERC20(assetId).balanceOf(msg.sender);
                            if (balance < amount) revert InsufficientBalance(amount, balance);
                            transferFromERC20(assetId, msg.sender, address(this), amount);
                        }
                    }
                    function depositAssets(LibSwap.SwapData[] calldata swaps) internal {
                        for (uint256 i = 0; i < swaps.length; ) {
                            LibSwap.SwapData calldata swap = swaps[i];
                            if (swap.requiresDeposit) {
                                depositAsset(swap.sendingAssetId, swap.fromAmount);
                            }
                            unchecked {
                                i++;
                            }
                        }
                    }
                    /// @notice Determines whether the given assetId is the native asset
                    /// @param assetId The asset identifier to evaluate
                    /// @return Boolean indicating if the asset is the native asset
                    function isNativeAsset(address assetId) internal pure returns (bool) {
                        return assetId == NATIVE_ASSETID;
                    }
                    /// @notice Wrapper function to transfer a given asset (native or erc20) to
                    ///         some recipient. Should handle all non-compliant return value
                    ///         tokens as well by using the SafeERC20 contract by open zeppelin.
                    /// @param assetId Asset id for transfer (address(0) for native asset,
                    ///                token address for erc20s)
                    /// @param recipient Address to send asset to
                    /// @param amount Amount to send to given recipient
                    function transferAsset(
                        address assetId,
                        address payable recipient,
                        uint256 amount
                    ) internal {
                        isNativeAsset(assetId)
                            ? transferNativeAsset(recipient, amount)
                            : transferERC20(assetId, recipient, amount);
                    }
                    /// @dev Checks whether the given address is a contract and contains code
                    function isContract(address _contractAddr) internal view returns (bool) {
                        uint256 size;
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            size := extcodesize(_contractAddr)
                        }
                        return size > 0;
                    }
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
                pragma solidity ^0.8.0;
                import "./IERC20.sol";
                import "./extensions/IERC20Metadata.sol";
                import "../../utils/Context.sol";
                /**
                 * @dev Implementation of the {IERC20} interface.
                 *
                 * This implementation is agnostic to the way tokens are created. This means
                 * that a supply mechanism has to be added in a derived contract using {_mint}.
                 * For a generic mechanism see {ERC20PresetMinterPauser}.
                 *
                 * TIP: For a detailed writeup see our guide
                 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
                 * to implement supply mechanisms].
                 *
                 * The default value of {decimals} is 18. To change this, you should override
                 * this function so it returns a different value.
                 *
                 * We have followed general OpenZeppelin Contracts guidelines: functions revert
                 * instead returning `false` on failure. This behavior is nonetheless
                 * conventional and does not conflict with the expectations of ERC20
                 * applications.
                 *
                 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
                 * This allows applications to reconstruct the allowance for all accounts just
                 * by listening to said events. Other implementations of the EIP may not emit
                 * these events, as it isn't required by the specification.
                 *
                 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
                 * functions have been added to mitigate the well-known issues around setting
                 * allowances. See {IERC20-approve}.
                 */
                contract ERC20 is Context, IERC20, IERC20Metadata {
                    mapping(address => uint256) private _balances;
                    mapping(address => mapping(address => uint256)) private _allowances;
                    uint256 private _totalSupply;
                    string private _name;
                    string private _symbol;
                    /**
                     * @dev Sets the values for {name} and {symbol}.
                     *
                     * All two of these values are immutable: they can only be set once during
                     * construction.
                     */
                    constructor(string memory name_, string memory symbol_) {
                        _name = name_;
                        _symbol = symbol_;
                    }
                    /**
                     * @dev Returns the name of the token.
                     */
                    function name() public view virtual override returns (string memory) {
                        return _name;
                    }
                    /**
                     * @dev Returns the symbol of the token, usually a shorter version of the
                     * name.
                     */
                    function symbol() public view virtual override returns (string memory) {
                        return _symbol;
                    }
                    /**
                     * @dev Returns the number of decimals used to get its user representation.
                     * For example, if `decimals` equals `2`, a balance of `505` tokens should
                     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
                     *
                     * Tokens usually opt for a value of 18, imitating the relationship between
                     * Ether and Wei. This is the default value returned by this function, unless
                     * it's overridden.
                     *
                     * NOTE: This information is only used for _display_ purposes: it in
                     * no way affects any of the arithmetic of the contract, including
                     * {IERC20-balanceOf} and {IERC20-transfer}.
                     */
                    function decimals() public view virtual override returns (uint8) {
                        return 18;
                    }
                    /**
                     * @dev See {IERC20-totalSupply}.
                     */
                    function totalSupply() public view virtual override returns (uint256) {
                        return _totalSupply;
                    }
                    /**
                     * @dev See {IERC20-balanceOf}.
                     */
                    function balanceOf(address account) public view virtual override returns (uint256) {
                        return _balances[account];
                    }
                    /**
                     * @dev See {IERC20-transfer}.
                     *
                     * Requirements:
                     *
                     * - `to` cannot be the zero address.
                     * - the caller must have a balance of at least `amount`.
                     */
                    function transfer(address to, uint256 amount) public virtual override returns (bool) {
                        address owner = _msgSender();
                        _transfer(owner, to, amount);
                        return true;
                    }
                    /**
                     * @dev See {IERC20-allowance}.
                     */
                    function allowance(address owner, address spender) public view virtual override returns (uint256) {
                        return _allowances[owner][spender];
                    }
                    /**
                     * @dev See {IERC20-approve}.
                     *
                     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
                     * `transferFrom`. This is semantically equivalent to an infinite approval.
                     *
                     * Requirements:
                     *
                     * - `spender` cannot be the zero address.
                     */
                    function approve(address spender, uint256 amount) public virtual override returns (bool) {
                        address owner = _msgSender();
                        _approve(owner, spender, amount);
                        return true;
                    }
                    /**
                     * @dev See {IERC20-transferFrom}.
                     *
                     * Emits an {Approval} event indicating the updated allowance. This is not
                     * required by the EIP. See the note at the beginning of {ERC20}.
                     *
                     * NOTE: Does not update the allowance if the current allowance
                     * is the maximum `uint256`.
                     *
                     * Requirements:
                     *
                     * - `from` and `to` cannot be the zero address.
                     * - `from` must have a balance of at least `amount`.
                     * - the caller must have allowance for ``from``'s tokens of at least
                     * `amount`.
                     */
                    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
                        address spender = _msgSender();
                        _spendAllowance(from, spender, amount);
                        _transfer(from, to, amount);
                        return true;
                    }
                    /**
                     * @dev Atomically increases the allowance granted to `spender` by the caller.
                     *
                     * This is an alternative to {approve} that can be used as a mitigation for
                     * problems described in {IERC20-approve}.
                     *
                     * Emits an {Approval} event indicating the updated allowance.
                     *
                     * Requirements:
                     *
                     * - `spender` cannot be the zero address.
                     */
                    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                        address owner = _msgSender();
                        _approve(owner, spender, allowance(owner, spender) + addedValue);
                        return true;
                    }
                    /**
                     * @dev Atomically decreases the allowance granted to `spender` by the caller.
                     *
                     * This is an alternative to {approve} that can be used as a mitigation for
                     * problems described in {IERC20-approve}.
                     *
                     * Emits an {Approval} event indicating the updated allowance.
                     *
                     * Requirements:
                     *
                     * - `spender` cannot be the zero address.
                     * - `spender` must have allowance for the caller of at least
                     * `subtractedValue`.
                     */
                    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                        address owner = _msgSender();
                        uint256 currentAllowance = allowance(owner, spender);
                        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
                        unchecked {
                            _approve(owner, spender, currentAllowance - subtractedValue);
                        }
                        return true;
                    }
                    /**
                     * @dev Moves `amount` of tokens from `from` to `to`.
                     *
                     * This internal function is equivalent to {transfer}, and can be used to
                     * e.g. implement automatic token fees, slashing mechanisms, etc.
                     *
                     * Emits a {Transfer} event.
                     *
                     * Requirements:
                     *
                     * - `from` cannot be the zero address.
                     * - `to` cannot be the zero address.
                     * - `from` must have a balance of at least `amount`.
                     */
                    function _transfer(address from, address to, uint256 amount) internal virtual {
                        require(from != address(0), "ERC20: transfer from the zero address");
                        require(to != address(0), "ERC20: transfer to the zero address");
                        _beforeTokenTransfer(from, to, amount);
                        uint256 fromBalance = _balances[from];
                        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
                        unchecked {
                            _balances[from] = fromBalance - amount;
                            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
                            // decrementing then incrementing.
                            _balances[to] += amount;
                        }
                        emit Transfer(from, to, amount);
                        _afterTokenTransfer(from, to, amount);
                    }
                    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                     * the total supply.
                     *
                     * Emits a {Transfer} event with `from` set to the zero address.
                     *
                     * Requirements:
                     *
                     * - `account` cannot be the zero address.
                     */
                    function _mint(address account, uint256 amount) internal virtual {
                        require(account != address(0), "ERC20: mint to the zero address");
                        _beforeTokenTransfer(address(0), account, amount);
                        _totalSupply += amount;
                        unchecked {
                            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
                            _balances[account] += amount;
                        }
                        emit Transfer(address(0), account, amount);
                        _afterTokenTransfer(address(0), account, amount);
                    }
                    /**
                     * @dev Destroys `amount` tokens from `account`, reducing the
                     * total supply.
                     *
                     * Emits a {Transfer} event with `to` set to the zero address.
                     *
                     * Requirements:
                     *
                     * - `account` cannot be the zero address.
                     * - `account` must have at least `amount` tokens.
                     */
                    function _burn(address account, uint256 amount) internal virtual {
                        require(account != address(0), "ERC20: burn from the zero address");
                        _beforeTokenTransfer(account, address(0), amount);
                        uint256 accountBalance = _balances[account];
                        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
                        unchecked {
                            _balances[account] = accountBalance - amount;
                            // Overflow not possible: amount <= accountBalance <= totalSupply.
                            _totalSupply -= amount;
                        }
                        emit Transfer(account, address(0), amount);
                        _afterTokenTransfer(account, address(0), amount);
                    }
                    /**
                     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
                     *
                     * This internal function is equivalent to `approve`, and can be used to
                     * e.g. set automatic allowances for certain subsystems, etc.
                     *
                     * Emits an {Approval} event.
                     *
                     * Requirements:
                     *
                     * - `owner` cannot be the zero address.
                     * - `spender` cannot be the zero address.
                     */
                    function _approve(address owner, address spender, uint256 amount) internal virtual {
                        require(owner != address(0), "ERC20: approve from the zero address");
                        require(spender != address(0), "ERC20: approve to the zero address");
                        _allowances[owner][spender] = amount;
                        emit Approval(owner, spender, amount);
                    }
                    /**
                     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
                     *
                     * Does not update the allowance amount in case of infinite allowance.
                     * Revert if not enough allowance is available.
                     *
                     * Might emit an {Approval} event.
                     */
                    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
                        uint256 currentAllowance = allowance(owner, spender);
                        if (currentAllowance != type(uint256).max) {
                            require(currentAllowance >= amount, "ERC20: insufficient allowance");
                            unchecked {
                                _approve(owner, spender, currentAllowance - amount);
                            }
                        }
                    }
                    /**
                     * @dev Hook that is called before any transfer of tokens. This includes
                     * minting and burning.
                     *
                     * Calling conditions:
                     *
                     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                     * will be transferred to `to`.
                     * - when `from` is zero, `amount` tokens will be minted for `to`.
                     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
                     * - `from` and `to` are never both zero.
                     *
                     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                     */
                    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
                    /**
                     * @dev Hook that is called after any transfer of tokens. This includes
                     * minting and burning.
                     *
                     * Calling conditions:
                     *
                     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                     * has been transferred to `to`.
                     * - when `from` is zero, `amount` tokens have been minted for `to`.
                     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
                     * - `from` and `to` are never both zero.
                     *
                     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                     */
                    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
                }
                // SPDX-License-Identifier: MIT
                /// @custom:version 1.0.0
                pragma solidity ^0.8.17;
                import { LibAsset } from "./LibAsset.sol";
                import { LibUtil } from "./LibUtil.sol";
                import { InvalidContract, NoSwapFromZeroBalance, InsufficientBalance } from "../Errors/GenericErrors.sol";
                import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                library LibSwap {
                    struct SwapData {
                        address callTo;
                        address approveTo;
                        address sendingAssetId;
                        address receivingAssetId;
                        uint256 fromAmount;
                        bytes callData;
                        bool requiresDeposit;
                    }
                    event AssetSwapped(
                        bytes32 transactionId,
                        address dex,
                        address fromAssetId,
                        address toAssetId,
                        uint256 fromAmount,
                        uint256 toAmount,
                        uint256 timestamp
                    );
                    function swap(bytes32 transactionId, SwapData calldata _swap) internal {
                        if (!LibAsset.isContract(_swap.callTo)) revert InvalidContract();
                        uint256 fromAmount = _swap.fromAmount;
                        if (fromAmount == 0) revert NoSwapFromZeroBalance();
                        uint256 nativeValue = LibAsset.isNativeAsset(_swap.sendingAssetId)
                            ? _swap.fromAmount
                            : 0;
                        uint256 initialSendingAssetBalance = LibAsset.getOwnBalance(
                            _swap.sendingAssetId
                        );
                        uint256 initialReceivingAssetBalance = LibAsset.getOwnBalance(
                            _swap.receivingAssetId
                        );
                        if (nativeValue == 0) {
                            LibAsset.maxApproveERC20(
                                IERC20(_swap.sendingAssetId),
                                _swap.approveTo,
                                _swap.fromAmount
                            );
                        }
                        if (initialSendingAssetBalance < _swap.fromAmount) {
                            revert InsufficientBalance(
                                _swap.fromAmount,
                                initialSendingAssetBalance
                            );
                        }
                        // solhint-disable-next-line avoid-low-level-calls
                        (bool success, bytes memory res) = _swap.callTo.call{
                            value: nativeValue
                        }(_swap.callData);
                        if (!success) {
                            LibUtil.revertWith(res);
                        }
                        uint256 newBalance = LibAsset.getOwnBalance(_swap.receivingAssetId);
                        emit AssetSwapped(
                            transactionId,
                            _swap.callTo,
                            _swap.sendingAssetId,
                            _swap.receivingAssetId,
                            _swap.fromAmount,
                            newBalance > initialReceivingAssetBalance
                                ? newBalance - initialReceivingAssetBalance
                                : newBalance,
                            block.timestamp
                        );
                    }
                }
                // SPDX-License-Identifier: UNLICENSED
                /// @custom:version 1.0.0
                pragma solidity ^0.8.17;
                /// @title Reentrancy Guard
                /// @author LI.FI (https://li.fi)
                /// @notice Abstract contract to provide protection against reentrancy
                abstract contract ReentrancyGuard {
                    /// Storage ///
                    bytes32 private constant NAMESPACE = keccak256("com.lifi.reentrancyguard");
                    /// Types ///
                    struct ReentrancyStorage {
                        uint256 status;
                    }
                    /// Errors ///
                    error ReentrancyError();
                    /// Constants ///
                    uint256 private constant _NOT_ENTERED = 0;
                    uint256 private constant _ENTERED = 1;
                    /// Modifiers ///
                    modifier nonReentrant() {
                        ReentrancyStorage storage s = reentrancyStorage();
                        if (s.status == _ENTERED) revert ReentrancyError();
                        s.status = _ENTERED;
                        _;
                        s.status = _NOT_ENTERED;
                    }
                    /// Private Methods ///
                    /// @dev fetch local storage
                    function reentrancyStorage()
                        private
                        pure
                        returns (ReentrancyStorage storage data)
                    {
                        bytes32 position = NAMESPACE;
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            data.slot := position
                        }
                    }
                }
                // SPDX-License-Identifier: MIT
                /// @custom:version 1.0.0
                pragma solidity ^0.8.17;
                import { ILiFi } from "../Interfaces/ILiFi.sol";
                import { LibSwap } from "../Libraries/LibSwap.sol";
                import { LibAsset } from "../Libraries/LibAsset.sol";
                import { LibAllowList } from "../Libraries/LibAllowList.sol";
                import { ContractCallNotAllowed, NoSwapDataProvided, CumulativeSlippageTooHigh } from "../Errors/GenericErrors.sol";
                /// @title Swapper
                /// @author LI.FI (https://li.fi)
                /// @notice Abstract contract to provide swap functionality
                contract SwapperV2 is ILiFi {
                    /// Types ///
                    /// @dev only used to get around "Stack Too Deep" errors
                    struct ReserveData {
                        bytes32 transactionId;
                        address payable leftoverReceiver;
                        uint256 nativeReserve;
                    }
                    /// Modifiers ///
                    /// @dev Sends any leftover balances back to the user
                    /// @notice Sends any leftover balances to the user
                    /// @param _swaps Swap data array
                    /// @param _leftoverReceiver Address to send leftover tokens to
                    /// @param _initialBalances Array of initial token balances
                    modifier noLeftovers(
                        LibSwap.SwapData[] calldata _swaps,
                        address payable _leftoverReceiver,
                        uint256[] memory _initialBalances
                    ) {
                        uint256 numSwaps = _swaps.length;
                        if (numSwaps != 1) {
                            address finalAsset = _swaps[numSwaps - 1].receivingAssetId;
                            uint256 curBalance;
                            _;
                            for (uint256 i = 0; i < numSwaps - 1; ) {
                                address curAsset = _swaps[i].receivingAssetId;
                                // Handle multi-to-one swaps
                                if (curAsset != finalAsset) {
                                    curBalance =
                                        LibAsset.getOwnBalance(curAsset) -
                                        _initialBalances[i];
                                    if (curBalance > 0) {
                                        LibAsset.transferAsset(
                                            curAsset,
                                            _leftoverReceiver,
                                            curBalance
                                        );
                                    }
                                }
                                unchecked {
                                    ++i;
                                }
                            }
                        } else {
                            _;
                        }
                    }
                    /// @dev Sends any leftover balances back to the user reserving native tokens
                    /// @notice Sends any leftover balances to the user
                    /// @param _swaps Swap data array
                    /// @param _leftoverReceiver Address to send leftover tokens to
                    /// @param _initialBalances Array of initial token balances
                    modifier noLeftoversReserve(
                        LibSwap.SwapData[] calldata _swaps,
                        address payable _leftoverReceiver,
                        uint256[] memory _initialBalances,
                        uint256 _nativeReserve
                    ) {
                        uint256 numSwaps = _swaps.length;
                        if (numSwaps != 1) {
                            address finalAsset = _swaps[numSwaps - 1].receivingAssetId;
                            uint256 curBalance;
                            _;
                            for (uint256 i = 0; i < numSwaps - 1; ) {
                                address curAsset = _swaps[i].receivingAssetId;
                                // Handle multi-to-one swaps
                                if (curAsset != finalAsset) {
                                    curBalance =
                                        LibAsset.getOwnBalance(curAsset) -
                                        _initialBalances[i];
                                    uint256 reserve = LibAsset.isNativeAsset(curAsset)
                                        ? _nativeReserve
                                        : 0;
                                    if (curBalance > 0) {
                                        LibAsset.transferAsset(
                                            curAsset,
                                            _leftoverReceiver,
                                            curBalance - reserve
                                        );
                                    }
                                }
                                unchecked {
                                    ++i;
                                }
                            }
                        } else {
                            _;
                        }
                    }
                    /// @dev Refunds any excess native asset sent to the contract after the main function
                    /// @notice Refunds any excess native asset sent to the contract after the main function
                    /// @param _refundReceiver Address to send refunds to
                    modifier refundExcessNative(address payable _refundReceiver) {
                        uint256 initialBalance = address(this).balance - msg.value;
                        _;
                        uint256 finalBalance = address(this).balance;
                        if (finalBalance > initialBalance) {
                            LibAsset.transferAsset(
                                LibAsset.NATIVE_ASSETID,
                                _refundReceiver,
                                finalBalance - initialBalance
                            );
                        }
                    }
                    /// Internal Methods ///
                    /// @dev Deposits value, executes swaps, and performs minimum amount check
                    /// @param _transactionId the transaction id associated with the operation
                    /// @param _minAmount the minimum amount of the final asset to receive
                    /// @param _swaps Array of data used to execute swaps
                    /// @param _leftoverReceiver The address to send leftover funds to
                    /// @return uint256 result of the swap
                    function _depositAndSwap(
                        bytes32 _transactionId,
                        uint256 _minAmount,
                        LibSwap.SwapData[] calldata _swaps,
                        address payable _leftoverReceiver
                    ) internal returns (uint256) {
                        uint256 numSwaps = _swaps.length;
                        if (numSwaps == 0) {
                            revert NoSwapDataProvided();
                        }
                        address finalTokenId = _swaps[numSwaps - 1].receivingAssetId;
                        uint256 initialBalance = LibAsset.getOwnBalance(finalTokenId);
                        if (LibAsset.isNativeAsset(finalTokenId)) {
                            initialBalance -= msg.value;
                        }
                        uint256[] memory initialBalances = _fetchBalances(_swaps);
                        LibAsset.depositAssets(_swaps);
                        _executeSwaps(
                            _transactionId,
                            _swaps,
                            _leftoverReceiver,
                            initialBalances
                        );
                        uint256 newBalance = LibAsset.getOwnBalance(finalTokenId) -
                            initialBalance;
                        if (newBalance < _minAmount) {
                            revert CumulativeSlippageTooHigh(_minAmount, newBalance);
                        }
                        return newBalance;
                    }
                    /// @dev Deposits value, executes swaps, and performs minimum amount check and reserves native token for fees
                    /// @param _transactionId the transaction id associated with the operation
                    /// @param _minAmount the minimum amount of the final asset to receive
                    /// @param _swaps Array of data used to execute swaps
                    /// @param _leftoverReceiver The address to send leftover funds to
                    /// @param _nativeReserve Amount of native token to prevent from being swept back to the caller
                    function _depositAndSwap(
                        bytes32 _transactionId,
                        uint256 _minAmount,
                        LibSwap.SwapData[] calldata _swaps,
                        address payable _leftoverReceiver,
                        uint256 _nativeReserve
                    ) internal returns (uint256) {
                        uint256 numSwaps = _swaps.length;
                        if (numSwaps == 0) {
                            revert NoSwapDataProvided();
                        }
                        address finalTokenId = _swaps[numSwaps - 1].receivingAssetId;
                        uint256 initialBalance = LibAsset.getOwnBalance(finalTokenId);
                        if (LibAsset.isNativeAsset(finalTokenId)) {
                            initialBalance -= msg.value;
                        }
                        uint256[] memory initialBalances = _fetchBalances(_swaps);
                        LibAsset.depositAssets(_swaps);
                        ReserveData memory rd = ReserveData(
                            _transactionId,
                            _leftoverReceiver,
                            _nativeReserve
                        );
                        _executeSwaps(rd, _swaps, initialBalances);
                        uint256 newBalance = LibAsset.getOwnBalance(finalTokenId) -
                            initialBalance;
                        if (LibAsset.isNativeAsset(finalTokenId)) {
                            newBalance -= _nativeReserve;
                        }
                        if (newBalance < _minAmount) {
                            revert CumulativeSlippageTooHigh(_minAmount, newBalance);
                        }
                        return newBalance;
                    }
                    /// Private Methods ///
                    /// @dev Executes swaps and checks that DEXs used are in the allowList
                    /// @param _transactionId the transaction id associated with the operation
                    /// @param _swaps Array of data used to execute swaps
                    /// @param _leftoverReceiver Address to send leftover tokens to
                    /// @param _initialBalances Array of initial balances
                    function _executeSwaps(
                        bytes32 _transactionId,
                        LibSwap.SwapData[] calldata _swaps,
                        address payable _leftoverReceiver,
                        uint256[] memory _initialBalances
                    ) internal noLeftovers(_swaps, _leftoverReceiver, _initialBalances) {
                        uint256 numSwaps = _swaps.length;
                        for (uint256 i = 0; i < numSwaps; ) {
                            LibSwap.SwapData calldata currentSwap = _swaps[i];
                            if (
                                !((LibAsset.isNativeAsset(currentSwap.sendingAssetId) ||
                                    LibAllowList.contractIsAllowed(currentSwap.approveTo)) &&
                                    LibAllowList.contractIsAllowed(currentSwap.callTo) &&
                                    LibAllowList.selectorIsAllowed(
                                        bytes4(currentSwap.callData[:4])
                                    ))
                            ) revert ContractCallNotAllowed();
                            LibSwap.swap(_transactionId, currentSwap);
                            unchecked {
                                ++i;
                            }
                        }
                    }
                    /// @dev Executes swaps and checks that DEXs used are in the allowList
                    /// @param _reserveData Data passed used to reserve native tokens
                    /// @param _swaps Array of data used to execute swaps
                    function _executeSwaps(
                        ReserveData memory _reserveData,
                        LibSwap.SwapData[] calldata _swaps,
                        uint256[] memory _initialBalances
                    )
                        internal
                        noLeftoversReserve(
                            _swaps,
                            _reserveData.leftoverReceiver,
                            _initialBalances,
                            _reserveData.nativeReserve
                        )
                    {
                        uint256 numSwaps = _swaps.length;
                        for (uint256 i = 0; i < numSwaps; ) {
                            LibSwap.SwapData calldata currentSwap = _swaps[i];
                            if (
                                !((LibAsset.isNativeAsset(currentSwap.sendingAssetId) ||
                                    LibAllowList.contractIsAllowed(currentSwap.approveTo)) &&
                                    LibAllowList.contractIsAllowed(currentSwap.callTo) &&
                                    LibAllowList.selectorIsAllowed(
                                        bytes4(currentSwap.callData[:4])
                                    ))
                            ) revert ContractCallNotAllowed();
                            LibSwap.swap(_reserveData.transactionId, currentSwap);
                            unchecked {
                                ++i;
                            }
                        }
                    }
                    /// @dev Fetches balances of tokens to be swapped before swapping.
                    /// @param _swaps Array of data used to execute swaps
                    /// @return uint256[] Array of token balances.
                    function _fetchBalances(
                        LibSwap.SwapData[] calldata _swaps
                    ) private view returns (uint256[] memory) {
                        uint256 numSwaps = _swaps.length;
                        uint256[] memory balances = new uint256[](numSwaps);
                        address asset;
                        for (uint256 i = 0; i < numSwaps; ) {
                            asset = _swaps[i].receivingAssetId;
                            balances[i] = LibAsset.getOwnBalance(asset);
                            if (LibAsset.isNativeAsset(asset)) {
                                balances[i] -= msg.value;
                            }
                            unchecked {
                                ++i;
                            }
                        }
                        return balances;
                    }
                }
                // SPDX-License-Identifier: UNLICENSED
                /// @custom:version 1.0.0
                pragma solidity ^0.8.17;
                import { LibAsset } from "../Libraries/LibAsset.sol";
                import { LibUtil } from "../Libraries/LibUtil.sol";
                import { InvalidReceiver, InformationMismatch, InvalidSendingToken, InvalidAmount, NativeAssetNotSupported, InvalidDestinationChain, CannotBridgeToSameNetwork } from "../Errors/GenericErrors.sol";
                import { ILiFi } from "../Interfaces/ILiFi.sol";
                import { LibSwap } from "../Libraries/LibSwap.sol";
                contract Validatable {
                    modifier validateBridgeData(ILiFi.BridgeData memory _bridgeData) {
                        if (LibUtil.isZeroAddress(_bridgeData.receiver)) {
                            revert InvalidReceiver();
                        }
                        if (_bridgeData.minAmount == 0) {
                            revert InvalidAmount();
                        }
                        if (_bridgeData.destinationChainId == block.chainid) {
                            revert CannotBridgeToSameNetwork();
                        }
                        _;
                    }
                    modifier noNativeAsset(ILiFi.BridgeData memory _bridgeData) {
                        if (LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) {
                            revert NativeAssetNotSupported();
                        }
                        _;
                    }
                    modifier onlyAllowSourceToken(
                        ILiFi.BridgeData memory _bridgeData,
                        address _token
                    ) {
                        if (_bridgeData.sendingAssetId != _token) {
                            revert InvalidSendingToken();
                        }
                        _;
                    }
                    modifier onlyAllowDestinationChain(
                        ILiFi.BridgeData memory _bridgeData,
                        uint256 _chainId
                    ) {
                        if (_bridgeData.destinationChainId != _chainId) {
                            revert InvalidDestinationChain();
                        }
                        _;
                    }
                    modifier containsSourceSwaps(ILiFi.BridgeData memory _bridgeData) {
                        if (!_bridgeData.hasSourceSwaps) {
                            revert InformationMismatch();
                        }
                        _;
                    }
                    modifier doesNotContainSourceSwaps(ILiFi.BridgeData memory _bridgeData) {
                        if (_bridgeData.hasSourceSwaps) {
                            revert InformationMismatch();
                        }
                        _;
                    }
                    modifier doesNotContainDestinationCalls(
                        ILiFi.BridgeData memory _bridgeData
                    ) {
                        if (_bridgeData.hasDestinationCall) {
                            revert InformationMismatch();
                        }
                        _;
                    }
                }
                // SPDX-License-Identifier: MIT
                /// @custom:version 1.0.0
                pragma solidity ^0.8.17;
                interface IMayan {
                    struct PermitParams {
                        uint256 value;
                        uint256 deadline;
                        uint8 v;
                        bytes32 r;
                        bytes32 s;
                    }
                    function forwardEth(
                        address mayanProtocol,
                        bytes calldata protocolData
                    ) external payable;
                    function forwardERC20(
                        address tokenIn,
                        uint256 amountIn,
                        PermitParams calldata permitParams,
                        address mayanProtocol,
                        bytes calldata protocolData
                    ) external payable;
                }
                // SPDX-License-Identifier: MIT
                /// @custom:version 1.0.0
                pragma solidity ^0.8.17;
                error AlreadyInitialized();
                error CannotAuthoriseSelf();
                error CannotBridgeToSameNetwork();
                error ContractCallNotAllowed();
                error CumulativeSlippageTooHigh(uint256 minAmount, uint256 receivedAmount);
                error DiamondIsPaused();
                error ExternalCallFailed();
                error FunctionDoesNotExist();
                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 WithdrawFailed();
                error ZeroAmount();
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol)
                pragma solidity ^0.8.0;
                import "../IERC20.sol";
                import "../extensions/IERC20Permit.sol";
                import "../../../utils/Address.sol";
                /**
                 * @title SafeERC20
                 * @dev Wrappers around ERC20 operations that throw on failure (when the token
                 * contract returns false). Tokens that return no value (and instead revert or
                 * throw on failure) are also supported, non-reverting calls are assumed to be
                 * successful.
                 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                 */
                library SafeERC20 {
                    using Address for address;
                    /**
                     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
                     * non-reverting calls are assumed to be successful.
                     */
                    function safeTransfer(IERC20 token, address to, uint256 value) internal {
                        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                    }
                    /**
                     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
                     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
                     */
                    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                    }
                    /**
                     * @dev Deprecated. This function has issues similar to the ones found in
                     * {IERC20-approve}, and its usage is discouraged.
                     *
                     * Whenever possible, use {safeIncreaseAllowance} and
                     * {safeDecreaseAllowance} instead.
                     */
                    function safeApprove(IERC20 token, address spender, uint256 value) internal {
                        // safeApprove should only be called when setting an initial allowance,
                        // or when resetting it to zero. To increase and decrease it, use
                        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                        require(
                            (value == 0) || (token.allowance(address(this), spender) == 0),
                            "SafeERC20: approve from non-zero to non-zero allowance"
                        );
                        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                    }
                    /**
                     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                     * non-reverting calls are assumed to be successful.
                     */
                    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                        uint256 oldAllowance = token.allowance(address(this), spender);
                        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
                    }
                    /**
                     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                     * non-reverting calls are assumed to be successful.
                     */
                    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                        unchecked {
                            uint256 oldAllowance = token.allowance(address(this), spender);
                            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
                        }
                    }
                    /**
                     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
                     * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to
                     * 0 before setting it to a non-zero value.
                     */
                    function forceApprove(IERC20 token, address spender, uint256 value) internal {
                        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
                        if (!_callOptionalReturnBool(token, approvalCall)) {
                            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
                            _callOptionalReturn(token, approvalCall);
                        }
                    }
                    /**
                     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
                     * Revert on invalid signature.
                     */
                    function safePermit(
                        IERC20Permit token,
                        address owner,
                        address spender,
                        uint256 value,
                        uint256 deadline,
                        uint8 v,
                        bytes32 r,
                        bytes32 s
                    ) internal {
                        uint256 nonceBefore = token.nonces(owner);
                        token.permit(owner, spender, value, deadline, v, r, s);
                        uint256 nonceAfter = token.nonces(owner);
                        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
                    }
                    /**
                     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                     * on the return value: the return value is optional (but if data is returned, it must not be false).
                     * @param token The token targeted by the call.
                     * @param data The call data (encoded using abi.encode or one of its variants).
                     */
                    function _callOptionalReturn(IERC20 token, bytes memory data) private {
                        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
                        // the target address contains contract code and also asserts for success in the low-level call.
                        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                    }
                    /**
                     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                     * on the return value: the return value is optional (but if data is returned, it must not be false).
                     * @param token The token targeted by the call.
                     * @param data The call data (encoded using abi.encode or one of its variants).
                     *
                     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
                     */
                    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
                        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
                        // and not revert is the subcall reverts.
                        (bool success, bytes memory returndata) = address(token).call(data);
                        return
                            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
                    }
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
                pragma solidity ^0.8.0;
                /**
                 * @dev Interface of the ERC20 standard as defined in the EIP.
                 */
                interface IERC20 {
                    /**
                     * @dev Emitted when `value` tokens are moved from one account (`from`) to
                     * another (`to`).
                     *
                     * Note that `value` may be zero.
                     */
                    event Transfer(address indexed from, address indexed to, uint256 value);
                    /**
                     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                     * a call to {approve}. `value` is the new allowance.
                     */
                    event Approval(address indexed owner, address indexed spender, uint256 value);
                    /**
                     * @dev Returns the amount of tokens in existence.
                     */
                    function totalSupply() external view returns (uint256);
                    /**
                     * @dev Returns the amount of tokens owned by `account`.
                     */
                    function balanceOf(address account) external view returns (uint256);
                    /**
                     * @dev Moves `amount` tokens from the caller's account to `to`.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transfer(address to, uint256 amount) external returns (bool);
                    /**
                     * @dev Returns the remaining number of tokens that `spender` will be
                     * allowed to spend on behalf of `owner` through {transferFrom}. This is
                     * zero by default.
                     *
                     * This value changes when {approve} or {transferFrom} are called.
                     */
                    function allowance(address owner, address spender) external view returns (uint256);
                    /**
                     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * IMPORTANT: Beware that changing an allowance with this method brings the risk
                     * that someone may use both the old and the new allowance by unfortunate
                     * transaction ordering. One possible solution to mitigate this race
                     * condition is to first reduce the spender's allowance to 0 and set the
                     * desired value afterwards:
                     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                     *
                     * Emits an {Approval} event.
                     */
                    function approve(address spender, uint256 amount) external returns (bool);
                    /**
                     * @dev Moves `amount` tokens from `from` to `to` using the
                     * allowance mechanism. `amount` is then deducted from the caller's
                     * allowance.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transferFrom(address from, address to, uint256 amount) external returns (bool);
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
                pragma solidity ^0.8.0;
                import "../IERC20.sol";
                /**
                 * @dev Interface for the optional metadata functions from the ERC20 standard.
                 *
                 * _Available since v4.1._
                 */
                interface IERC20Metadata is IERC20 {
                    /**
                     * @dev Returns the name of the token.
                     */
                    function name() external view returns (string memory);
                    /**
                     * @dev Returns the symbol of the token.
                     */
                    function symbol() external view returns (string memory);
                    /**
                     * @dev Returns the decimals places of the token.
                     */
                    function decimals() external view returns (uint8);
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
                pragma solidity ^0.8.0;
                /**
                 * @dev Provides information about the current execution context, including the
                 * sender of the transaction and its data. While these are generally available
                 * via msg.sender and msg.data, they should not be accessed in such a direct
                 * manner, since when dealing with meta-transactions the account sending and
                 * paying for execution may not be the actual sender (as far as an application
                 * is concerned).
                 *
                 * This contract is only required for intermediate, library-like contracts.
                 */
                abstract contract Context {
                    function _msgSender() internal view virtual returns (address) {
                        return msg.sender;
                    }
                    function _msgData() internal view virtual returns (bytes calldata) {
                        return msg.data;
                    }
                }
                // SPDX-License-Identifier: MIT
                /// @custom:version 1.0.0
                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);
                    }
                    function revertWith(bytes memory data) internal pure {
                        assembly {
                            let dataSize := mload(data) // Load the size of the data
                            let dataPtr := add(data, 0x20) // Advance data pointer to the next word
                            revert(dataPtr, dataSize) // Revert with the given data
                        }
                    }
                }
                // SPDX-License-Identifier: MIT
                /// @custom:version 1.0.0
                pragma solidity ^0.8.17;
                import { InvalidContract } from "../Errors/GenericErrors.sol";
                /// @title Lib Allow List
                /// @author LI.FI (https://li.fi)
                /// @notice Library for managing and accessing the conract address allow list
                library LibAllowList {
                    /// Storage ///
                    bytes32 internal constant NAMESPACE =
                        keccak256("com.lifi.library.allow.list");
                    struct AllowListStorage {
                        mapping(address => bool) allowlist;
                        mapping(bytes4 => bool) selectorAllowList;
                        address[] contracts;
                    }
                    /// @dev Adds a contract address to the allow list
                    /// @param _contract the contract address to add
                    function addAllowedContract(address _contract) internal {
                        _checkAddress(_contract);
                        AllowListStorage storage als = _getStorage();
                        if (als.allowlist[_contract]) return;
                        als.allowlist[_contract] = true;
                        als.contracts.push(_contract);
                    }
                    /// @dev Checks whether a contract address has been added to the allow list
                    /// @param _contract the contract address to check
                    function contractIsAllowed(
                        address _contract
                    ) internal view returns (bool) {
                        return _getStorage().allowlist[_contract];
                    }
                    /// @dev Remove a contract address from the allow list
                    /// @param _contract the contract address to remove
                    function removeAllowedContract(address _contract) internal {
                        AllowListStorage storage als = _getStorage();
                        if (!als.allowlist[_contract]) {
                            return;
                        }
                        als.allowlist[_contract] = false;
                        uint256 length = als.contracts.length;
                        // Find the contract in the list
                        for (uint256 i = 0; i < length; i++) {
                            if (als.contracts[i] == _contract) {
                                // Move the last element into the place to delete
                                als.contracts[i] = als.contracts[length - 1];
                                // Remove the last element
                                als.contracts.pop();
                                break;
                            }
                        }
                    }
                    /// @dev Fetch contract addresses from the allow list
                    function getAllowedContracts() internal view returns (address[] memory) {
                        return _getStorage().contracts;
                    }
                    /// @dev Add a selector to the allow list
                    /// @param _selector the selector to add
                    function addAllowedSelector(bytes4 _selector) internal {
                        _getStorage().selectorAllowList[_selector] = true;
                    }
                    /// @dev Removes a selector from the allow list
                    /// @param _selector the selector to remove
                    function removeAllowedSelector(bytes4 _selector) internal {
                        _getStorage().selectorAllowList[_selector] = false;
                    }
                    /// @dev Returns if selector has been added to the allow list
                    /// @param _selector the selector to check
                    function selectorIsAllowed(bytes4 _selector) internal view returns (bool) {
                        return _getStorage().selectorAllowList[_selector];
                    }
                    /// @dev Fetch local storage struct
                    function _getStorage()
                        internal
                        pure
                        returns (AllowListStorage storage als)
                    {
                        bytes32 position = NAMESPACE;
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            als.slot := position
                        }
                    }
                    /// @dev Contains business logic for validating a contract address.
                    /// @param _contract address of the dex to check
                    function _checkAddress(address _contract) private view {
                        if (_contract == address(0)) revert InvalidContract();
                        if (_contract.code.length == 0) revert InvalidContract();
                    }
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
                pragma solidity ^0.8.0;
                /**
                 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
                 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
                 *
                 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
                 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
                 * need to send a transaction, and thus is not required to hold Ether at all.
                 */
                interface IERC20Permit {
                    /**
                     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                     * given ``owner``'s signed approval.
                     *
                     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                     * ordering also apply here.
                     *
                     * Emits an {Approval} event.
                     *
                     * Requirements:
                     *
                     * - `spender` cannot be the zero address.
                     * - `deadline` must be a timestamp in the future.
                     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                     * over the EIP712-formatted function arguments.
                     * - the signature must use ``owner``'s current nonce (see {nonces}).
                     *
                     * For more information on the signature format, see the
                     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                     * section].
                     */
                    function permit(
                        address owner,
                        address spender,
                        uint256 value,
                        uint256 deadline,
                        uint8 v,
                        bytes32 r,
                        bytes32 s
                    ) external;
                    /**
                     * @dev Returns the current nonce for `owner`. This value must be
                     * included whenever a signature is generated for {permit}.
                     *
                     * Every successful call to {permit} increases ``owner``'s nonce by one. This
                     * prevents a signature from being used multiple times.
                     */
                    function nonces(address owner) external view returns (uint256);
                    /**
                     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                     */
                    // solhint-disable-next-line func-name-mixedcase
                    function DOMAIN_SEPARATOR() external view returns (bytes32);
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
                pragma solidity ^0.8.1;
                /**
                 * @dev Collection of functions related to the address type
                 */
                library Address {
                    /**
                     * @dev Returns true if `account` is a contract.
                     *
                     * [IMPORTANT]
                     * ====
                     * It is unsafe to assume that an address for which this function returns
                     * false is an externally-owned account (EOA) and not a contract.
                     *
                     * Among others, `isContract` will return false for the following
                     * types of addresses:
                     *
                     *  - an externally-owned account
                     *  - a contract in construction
                     *  - an address where a contract will be created
                     *  - an address where a contract lived, but was destroyed
                     *
                     * Furthermore, `isContract` will also return true if the target contract within
                     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                     * which only has an effect at the end of a transaction.
                     * ====
                     *
                     * [IMPORTANT]
                     * ====
                     * You shouldn't rely on `isContract` to protect against flash loan attacks!
                     *
                     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                     * constructor.
                     * ====
                     */
                    function isContract(address account) internal view returns (bool) {
                        // This method relies on extcodesize/address.code.length, which returns 0
                        // for contracts in construction, since the code is only stored at the end
                        // of the constructor execution.
                        return account.code.length > 0;
                    }
                    /**
                     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                     * `recipient`, forwarding all available gas and reverting on errors.
                     *
                     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                     * of certain opcodes, possibly making contracts go over the 2300 gas limit
                     * imposed by `transfer`, making them unable to receive funds via
                     * `transfer`. {sendValue} removes this limitation.
                     *
                     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                     *
                     * IMPORTANT: because control is transferred to `recipient`, care must be
                     * taken to not create reentrancy vulnerabilities. Consider using
                     * {ReentrancyGuard} or the
                     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                     */
                    function sendValue(address payable recipient, uint256 amount) internal {
                        require(address(this).balance >= amount, "Address: insufficient balance");
                        (bool success, ) = recipient.call{value: amount}("");
                        require(success, "Address: unable to send value, recipient may have reverted");
                    }
                    /**
                     * @dev Performs a Solidity function call using a low level `call`. A
                     * plain `call` is an unsafe replacement for a function call: use this
                     * function instead.
                     *
                     * If `target` reverts with a revert reason, it is bubbled up by this
                     * function (like regular Solidity function calls).
                     *
                     * Returns the raw returned data. To convert to the expected return value,
                     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                     *
                     * Requirements:
                     *
                     * - `target` must be a contract.
                     * - calling `target` with `data` must not revert.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                     * `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, 0, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but also transferring `value` wei to `target`.
                     *
                     * Requirements:
                     *
                     * - the calling contract must have an ETH balance of at least `value`.
                     * - the called Solidity function must be `payable`.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                     * with `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(
                        address target,
                        bytes memory data,
                        uint256 value,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        require(address(this).balance >= value, "Address: insufficient balance for call");
                        (bool success, bytes memory returndata) = target.call{value: value}(data);
                        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                        return functionStaticCall(target, data, "Address: low-level static call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal view returns (bytes memory) {
                        (bool success, bytes memory returndata) = target.staticcall(data);
                        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        (bool success, bytes memory returndata) = target.delegatecall(data);
                        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                    }
                    /**
                     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                     *
                     * _Available since v4.8._
                     */
                    function verifyCallResultFromTarget(
                        address target,
                        bool success,
                        bytes memory returndata,
                        string memory errorMessage
                    ) internal view returns (bytes memory) {
                        if (success) {
                            if (returndata.length == 0) {
                                // only check isContract if the call was successful and the return data is empty
                                // otherwise we already know that it was a contract
                                require(isContract(target), "Address: call to non-contract");
                            }
                            return returndata;
                        } else {
                            _revert(returndata, errorMessage);
                        }
                    }
                    /**
                     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                     * revert reason or using the provided one.
                     *
                     * _Available since v4.3._
                     */
                    function verifyCallResult(
                        bool success,
                        bytes memory returndata,
                        string memory errorMessage
                    ) internal pure returns (bytes memory) {
                        if (success) {
                            return returndata;
                        } else {
                            _revert(returndata, errorMessage);
                        }
                    }
                    function _revert(bytes memory returndata, string memory errorMessage) private pure {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            /// @solidity memory-safe-assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
                // SPDX-License-Identifier: MIT
                /// @custom:version 1.0.0
                pragma solidity ^0.8.17;
                library LibBytes {
                    // solhint-disable no-inline-assembly
                    // LibBytes specific errors
                    error SliceOverflow();
                    error SliceOutOfBounds();
                    error AddressOutOfBounds();
                    bytes16 private constant _SYMBOLS = "0123456789abcdef";
                    // -------------------------
                    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;
                    }
                    /// Copied from OpenZeppelin's `Strings.sol` utility library.
                    /// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/8335676b0e99944eef6a742e16dcd9ff6e68e609/contracts/utils/Strings.sol
                    function toHexString(
                        uint256 value,
                        uint256 length
                    ) internal pure returns (string memory) {
                        bytes memory buffer = new bytes(2 * length + 2);
                        buffer[0] = "0";
                        buffer[1] = "x";
                        for (uint256 i = 2 * length + 1; i > 1; --i) {
                            buffer[i] = _SYMBOLS[value & 0xf];
                            value >>= 4;
                        }
                        require(value == 0, "Strings: hex length insufficient");
                        return string(buffer);
                    }
                }
                

                File 6 of 8: FeeManager
                // SPDX-License-Identifier: MIT
                pragma solidity ^0.8.0;
                import "./interfaces/IFeeManager.sol";
                import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
                contract FeeManager is IFeeManager {
                \tusing SafeERC20 for IERC20;
                \taddress public operator;
                \taddress public nextOperator;
                \tuint8 public baseBps;
                \taddress public treasury;
                \tconstructor(address _operator, uint8 _baseBps) {
                \t\toperator = _operator;
                \t\tbaseBps = _baseBps;
                \t}
                \t
                \tfunction calcProtocolBps(
                \t\tuint64 amountIn,
                \t\taddress tokenIn,
                \t\tbytes32 tokenOut,
                \t\tuint16 destChain,
                \t\tuint8 referrerBps
                \t) external view override returns (uint8) {
                \t\tif (referrerBps > baseBps) {
                \t\t\treturn referrerBps;
                \t\t} else {
                \t\t\treturn baseBps;
                \t\t}
                \t}
                \tfunction feeCollector() external view override returns (address) {
                \t\tif (treasury != address(0)) {
                \t\t\treturn treasury;
                \t\t} else {
                \t\t\treturn address(this);
                \t\t}
                \t}
                \tfunction changeOperator(address _nextOperator) external {
                \t\trequire(msg.sender == operator, 'only operator');
                \t\tnextOperator = _nextOperator;
                \t}\t
                \tfunction claimOperator() external {
                \t\trequire(msg.sender == nextOperator, 'only next operator');
                \t\toperator = nextOperator;
                \t}
                \tfunction sweepToken(address token, uint256 amount, address to) public {
                \t\trequire(msg.sender == operator, 'only operator');
                \t\tIERC20(token).safeTransfer(to, amount);
                \t}
                \tfunction sweepEth(uint256 amount, address payable to) public {
                \t\trequire(msg.sender == operator, 'only operator');
                \t\trequire(to != address(0), 'transfer to the zero address');
                \t\tto.transfer(amount);
                \t}
                \tfunction setBaseBps(uint8 _baseBps) external {
                \t\trequire(msg.sender == operator, 'only operator');
                \t\tbaseBps = _baseBps;
                \t}
                \tfunction setTreasury(address _treasury) external {
                \t\trequire(msg.sender == operator, 'only operator');
                \t\ttreasury = _treasury;
                \t}
                \treceive() external payable {}
                }// SPDX-License-Identifier: MIT
                pragma solidity ^0.8.0;
                interface IFeeManager {
                    function calcProtocolBps(
                        uint64 amountIn,
                        address tokenIn,
                        bytes32 tokenOut,
                        uint16 destChain,
                        uint8 referrerBps
                    ) external view returns (uint8);
                \tfunction feeCollector() external view returns (address);
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
                pragma solidity ^0.8.0;
                /**
                 * @dev Interface of the ERC20 standard as defined in the EIP.
                 */
                interface IERC20 {
                    /**
                     * @dev Emitted when `value` tokens are moved from one account (`from`) to
                     * another (`to`).
                     *
                     * Note that `value` may be zero.
                     */
                    event Transfer(address indexed from, address indexed to, uint256 value);
                    /**
                     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                     * a call to {approve}. `value` is the new allowance.
                     */
                    event Approval(address indexed owner, address indexed spender, uint256 value);
                    /**
                     * @dev Returns the amount of tokens in existence.
                     */
                    function totalSupply() external view returns (uint256);
                    /**
                     * @dev Returns the amount of tokens owned by `account`.
                     */
                    function balanceOf(address account) external view returns (uint256);
                    /**
                     * @dev Moves `amount` tokens from the caller's account to `to`.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transfer(address to, uint256 amount) external returns (bool);
                    /**
                     * @dev Returns the remaining number of tokens that `spender` will be
                     * allowed to spend on behalf of `owner` through {transferFrom}. This is
                     * zero by default.
                     *
                     * This value changes when {approve} or {transferFrom} are called.
                     */
                    function allowance(address owner, address spender) external view returns (uint256);
                    /**
                     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * IMPORTANT: Beware that changing an allowance with this method brings the risk
                     * that someone may use both the old and the new allowance by unfortunate
                     * transaction ordering. One possible solution to mitigate this race
                     * condition is to first reduce the spender's allowance to 0 and set the
                     * desired value afterwards:
                     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                     *
                     * Emits an {Approval} event.
                     */
                    function approve(address spender, uint256 amount) external returns (bool);
                    /**
                     * @dev Moves `amount` tokens from `from` to `to` using the
                     * allowance mechanism. `amount` is then deducted from the caller's
                     * allowance.
                     *
                     * Returns a boolean value indicating whether the operation succeeded.
                     *
                     * Emits a {Transfer} event.
                     */
                    function transferFrom(address from, address to, uint256 amount) external returns (bool);
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
                pragma solidity ^0.8.0;
                import "../IERC20.sol";
                import "../extensions/IERC20Permit.sol";
                import "../../../utils/Address.sol";
                /**
                 * @title SafeERC20
                 * @dev Wrappers around ERC20 operations that throw on failure (when the token
                 * contract returns false). Tokens that return no value (and instead revert or
                 * throw on failure) are also supported, non-reverting calls are assumed to be
                 * successful.
                 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                 */
                library SafeERC20 {
                    using Address for address;
                    /**
                     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
                     * non-reverting calls are assumed to be successful.
                     */
                    function safeTransfer(IERC20 token, address to, uint256 value) internal {
                        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                    }
                    /**
                     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
                     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
                     */
                    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                    }
                    /**
                     * @dev Deprecated. This function has issues similar to the ones found in
                     * {IERC20-approve}, and its usage is discouraged.
                     *
                     * Whenever possible, use {safeIncreaseAllowance} and
                     * {safeDecreaseAllowance} instead.
                     */
                    function safeApprove(IERC20 token, address spender, uint256 value) internal {
                        // safeApprove should only be called when setting an initial allowance,
                        // or when resetting it to zero. To increase and decrease it, use
                        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                        require(
                            (value == 0) || (token.allowance(address(this), spender) == 0),
                            "SafeERC20: approve from non-zero to non-zero allowance"
                        );
                        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                    }
                    /**
                     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                     * non-reverting calls are assumed to be successful.
                     */
                    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                        uint256 oldAllowance = token.allowance(address(this), spender);
                        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
                    }
                    /**
                     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                     * non-reverting calls are assumed to be successful.
                     */
                    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                        unchecked {
                            uint256 oldAllowance = token.allowance(address(this), spender);
                            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
                        }
                    }
                    /**
                     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
                     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
                     * to be set to zero before setting it to a non-zero value, such as USDT.
                     */
                    function forceApprove(IERC20 token, address spender, uint256 value) internal {
                        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
                        if (!_callOptionalReturnBool(token, approvalCall)) {
                            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
                            _callOptionalReturn(token, approvalCall);
                        }
                    }
                    /**
                     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
                     * Revert on invalid signature.
                     */
                    function safePermit(
                        IERC20Permit token,
                        address owner,
                        address spender,
                        uint256 value,
                        uint256 deadline,
                        uint8 v,
                        bytes32 r,
                        bytes32 s
                    ) internal {
                        uint256 nonceBefore = token.nonces(owner);
                        token.permit(owner, spender, value, deadline, v, r, s);
                        uint256 nonceAfter = token.nonces(owner);
                        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
                    }
                    /**
                     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                     * on the return value: the return value is optional (but if data is returned, it must not be false).
                     * @param token The token targeted by the call.
                     * @param data The call data (encoded using abi.encode or one of its variants).
                     */
                    function _callOptionalReturn(IERC20 token, bytes memory data) private {
                        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
                        // the target address contains contract code and also asserts for success in the low-level call.
                        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                    }
                    /**
                     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                     * on the return value: the return value is optional (but if data is returned, it must not be false).
                     * @param token The token targeted by the call.
                     * @param data The call data (encoded using abi.encode or one of its variants).
                     *
                     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
                     */
                    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
                        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
                        // and not revert is the subcall reverts.
                        (bool success, bytes memory returndata) = address(token).call(data);
                        return
                            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
                    }
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
                pragma solidity ^0.8.1;
                /**
                 * @dev Collection of functions related to the address type
                 */
                library Address {
                    /**
                     * @dev Returns true if `account` is a contract.
                     *
                     * [IMPORTANT]
                     * ====
                     * It is unsafe to assume that an address for which this function returns
                     * false is an externally-owned account (EOA) and not a contract.
                     *
                     * Among others, `isContract` will return false for the following
                     * types of addresses:
                     *
                     *  - an externally-owned account
                     *  - a contract in construction
                     *  - an address where a contract will be created
                     *  - an address where a contract lived, but was destroyed
                     *
                     * Furthermore, `isContract` will also return true if the target contract within
                     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                     * which only has an effect at the end of a transaction.
                     * ====
                     *
                     * [IMPORTANT]
                     * ====
                     * You shouldn't rely on `isContract` to protect against flash loan attacks!
                     *
                     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                     * constructor.
                     * ====
                     */
                    function isContract(address account) internal view returns (bool) {
                        // This method relies on extcodesize/address.code.length, which returns 0
                        // for contracts in construction, since the code is only stored at the end
                        // of the constructor execution.
                        return account.code.length > 0;
                    }
                    /**
                     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                     * `recipient`, forwarding all available gas and reverting on errors.
                     *
                     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                     * of certain opcodes, possibly making contracts go over the 2300 gas limit
                     * imposed by `transfer`, making them unable to receive funds via
                     * `transfer`. {sendValue} removes this limitation.
                     *
                     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                     *
                     * IMPORTANT: because control is transferred to `recipient`, care must be
                     * taken to not create reentrancy vulnerabilities. Consider using
                     * {ReentrancyGuard} or the
                     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                     */
                    function sendValue(address payable recipient, uint256 amount) internal {
                        require(address(this).balance >= amount, "Address: insufficient balance");
                        (bool success, ) = recipient.call{value: amount}("");
                        require(success, "Address: unable to send value, recipient may have reverted");
                    }
                    /**
                     * @dev Performs a Solidity function call using a low level `call`. A
                     * plain `call` is an unsafe replacement for a function call: use this
                     * function instead.
                     *
                     * If `target` reverts with a revert reason, it is bubbled up by this
                     * function (like regular Solidity function calls).
                     *
                     * Returns the raw returned data. To convert to the expected return value,
                     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                     *
                     * Requirements:
                     *
                     * - `target` must be a contract.
                     * - calling `target` with `data` must not revert.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                     * `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, 0, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but also transferring `value` wei to `target`.
                     *
                     * Requirements:
                     *
                     * - the calling contract must have an ETH balance of at least `value`.
                     * - the called Solidity function must be `payable`.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                     * with `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(
                        address target,
                        bytes memory data,
                        uint256 value,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        require(address(this).balance >= value, "Address: insufficient balance for call");
                        (bool success, bytes memory returndata) = target.call{value: value}(data);
                        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                        return functionStaticCall(target, data, "Address: low-level static call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal view returns (bytes memory) {
                        (bool success, bytes memory returndata) = target.staticcall(data);
                        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        (bool success, bytes memory returndata) = target.delegatecall(data);
                        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                    }
                    /**
                     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                     *
                     * _Available since v4.8._
                     */
                    function verifyCallResultFromTarget(
                        address target,
                        bool success,
                        bytes memory returndata,
                        string memory errorMessage
                    ) internal view returns (bytes memory) {
                        if (success) {
                            if (returndata.length == 0) {
                                // only check isContract if the call was successful and the return data is empty
                                // otherwise we already know that it was a contract
                                require(isContract(target), "Address: call to non-contract");
                            }
                            return returndata;
                        } else {
                            _revert(returndata, errorMessage);
                        }
                    }
                    /**
                     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                     * revert reason or using the provided one.
                     *
                     * _Available since v4.3._
                     */
                    function verifyCallResult(
                        bool success,
                        bytes memory returndata,
                        string memory errorMessage
                    ) internal pure returns (bytes memory) {
                        if (success) {
                            return returndata;
                        } else {
                            _revert(returndata, errorMessage);
                        }
                    }
                    function _revert(bytes memory returndata, string memory errorMessage) private pure {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            /// @solidity memory-safe-assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
                // SPDX-License-Identifier: MIT
                // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
                pragma solidity ^0.8.0;
                /**
                 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
                 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
                 *
                 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
                 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
                 * need to send a transaction, and thus is not required to hold Ether at all.
                 */
                interface IERC20Permit {
                    /**
                     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                     * given ``owner``'s signed approval.
                     *
                     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                     * ordering also apply here.
                     *
                     * Emits an {Approval} event.
                     *
                     * Requirements:
                     *
                     * - `spender` cannot be the zero address.
                     * - `deadline` must be a timestamp in the future.
                     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                     * over the EIP712-formatted function arguments.
                     * - the signature must use ``owner``'s current nonce (see {nonces}).
                     *
                     * For more information on the signature format, see the
                     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                     * section].
                     */
                    function permit(
                        address owner,
                        address spender,
                        uint256 value,
                        uint256 deadline,
                        uint8 v,
                        bytes32 r,
                        bytes32 s
                    ) external;
                    /**
                     * @dev Returns the current nonce for `owner`. This value must be
                     * included whenever a signature is generated for {permit}.
                     *
                     * Every successful call to {permit} increases ``owner``'s nonce by one. This
                     * prevents a signature from being used multiple times.
                     */
                    function nonces(address owner) external view returns (uint256);
                    /**
                     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                     */
                    // solhint-disable-next-line func-name-mixedcase
                    function DOMAIN_SEPARATOR() external view returns (bytes32);
                }
                

                File 7 of 8: Wormhole
                // contracts/Wormhole.sol
                // SPDX-License-Identifier: Apache 2
                pragma solidity ^0.8.0;
                import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
                contract Wormhole is ERC1967Proxy {
                    constructor (address implementation, bytes memory initData) ERC1967Proxy(
                        implementation,
                        initData
                    ) { }
                }// SPDX-License-Identifier: MIT
                pragma solidity ^0.8.0;
                import "../Proxy.sol";
                import "./ERC1967Upgrade.sol";
                /**
                 * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
                 * implementation address that can be changed. This address is stored in storage in the location specified by
                 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
                 * implementation behind the proxy.
                 */
                contract ERC1967Proxy is Proxy, ERC1967Upgrade {
                    /**
                     * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
                     *
                     * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
                     * function call, and allows initializating the storage of the proxy like a Solidity constructor.
                     */
                    constructor(address _logic, bytes memory _data) payable {
                        assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                        _upgradeToAndCall(_logic, _data, false);
                    }
                    /**
                     * @dev Returns the current implementation address.
                     */
                    function _implementation() internal view virtual override returns (address impl) {
                        return ERC1967Upgrade._getImplementation();
                    }
                }
                // SPDX-License-Identifier: MIT
                pragma solidity ^0.8.2;
                import "../beacon/IBeacon.sol";
                import "../../utils/Address.sol";
                import "../../utils/StorageSlot.sol";
                /**
                 * @dev This abstract contract provides getters and event emitting update functions for
                 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
                 *
                 * _Available since v4.1._
                 *
                 * @custom:oz-upgrades-unsafe-allow delegatecall
                 */
                abstract contract ERC1967Upgrade {
                    // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                    bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                    /**
                     * @dev Storage slot with the address of the current implementation.
                     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                     * validated in the constructor.
                     */
                    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                    /**
                     * @dev Emitted when the implementation is upgraded.
                     */
                    event Upgraded(address indexed implementation);
                    /**
                     * @dev Returns the current implementation address.
                     */
                    function _getImplementation() internal view returns (address) {
                        return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                    }
                    /**
                     * @dev Stores a new address in the EIP1967 implementation slot.
                     */
                    function _setImplementation(address newImplementation) private {
                        require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                        StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                    }
                    /**
                     * @dev Perform implementation upgrade
                     *
                     * Emits an {Upgraded} event.
                     */
                    function _upgradeTo(address newImplementation) internal {
                        _setImplementation(newImplementation);
                        emit Upgraded(newImplementation);
                    }
                    /**
                     * @dev Perform implementation upgrade with additional setup call.
                     *
                     * Emits an {Upgraded} event.
                     */
                    function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
                        _setImplementation(newImplementation);
                        emit Upgraded(newImplementation);
                        if (data.length > 0 || forceCall) {
                            Address.functionDelegateCall(newImplementation, data);
                        }
                    }
                    /**
                     * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                     *
                     * Emits an {Upgraded} event.
                     */
                    function _upgradeToAndCallSecure(address newImplementation, bytes memory data, bool forceCall) internal {
                        address oldImplementation = _getImplementation();
                        // Initial upgrade and setup call
                        _setImplementation(newImplementation);
                        if (data.length > 0 || forceCall) {
                            Address.functionDelegateCall(newImplementation, data);
                        }
                        // Perform rollback test if not already in progress
                        StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
                        if (!rollbackTesting.value) {
                            // Trigger rollback using upgradeTo from the new implementation
                            rollbackTesting.value = true;
                            Address.functionDelegateCall(
                                newImplementation,
                                abi.encodeWithSignature(
                                    "upgradeTo(address)",
                                    oldImplementation
                                )
                            );
                            rollbackTesting.value = false;
                            // Check rollback was effective
                            require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
                            // Finally reset to the new implementation and log the upgrade
                            _setImplementation(newImplementation);
                            emit Upgraded(newImplementation);
                        }
                    }
                    /**
                     * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                     * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                     *
                     * Emits a {BeaconUpgraded} event.
                     */
                    function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
                        _setBeacon(newBeacon);
                        emit BeaconUpgraded(newBeacon);
                        if (data.length > 0 || forceCall) {
                            Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                        }
                    }
                    /**
                     * @dev Storage slot with the admin of the contract.
                     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                     * validated in the constructor.
                     */
                    bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                    /**
                     * @dev Emitted when the admin account has changed.
                     */
                    event AdminChanged(address previousAdmin, address newAdmin);
                    /**
                     * @dev Returns the current admin.
                     */
                    function _getAdmin() internal view returns (address) {
                        return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                    }
                    /**
                     * @dev Stores a new address in the EIP1967 admin slot.
                     */
                    function _setAdmin(address newAdmin) private {
                        require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                        StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                    }
                    /**
                     * @dev Changes the admin of the proxy.
                     *
                     * Emits an {AdminChanged} event.
                     */
                    function _changeAdmin(address newAdmin) internal {
                        emit AdminChanged(_getAdmin(), newAdmin);
                        _setAdmin(newAdmin);
                    }
                    /**
                     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                     * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                     */
                    bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                    /**
                     * @dev Emitted when the beacon is upgraded.
                     */
                    event BeaconUpgraded(address indexed beacon);
                    /**
                     * @dev Returns the current beacon.
                     */
                    function _getBeacon() internal view returns (address) {
                        return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                    }
                    /**
                     * @dev Stores a new beacon in the EIP1967 beacon slot.
                     */
                    function _setBeacon(address newBeacon) private {
                        require(
                            Address.isContract(newBeacon),
                            "ERC1967: new beacon is not a contract"
                        );
                        require(
                            Address.isContract(IBeacon(newBeacon).implementation()),
                            "ERC1967: beacon implementation is not a contract"
                        );
                        StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                    }
                }
                // SPDX-License-Identifier: MIT
                pragma solidity ^0.8.0;
                /**
                 * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
                 * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
                 * be specified by overriding the virtual {_implementation} function.
                 *
                 * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
                 * different contract through the {_delegate} function.
                 *
                 * The success and return data of the delegated call will be returned back to the caller of the proxy.
                 */
                abstract contract Proxy {
                    /**
                     * @dev Delegates the current call to `implementation`.
                     *
                     * This function does not return to its internall call site, it will return directly to the external caller.
                     */
                    function _delegate(address implementation) internal virtual {
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            // Copy msg.data. We take full control of memory in this inline assembly
                            // block because it will not return to Solidity code. We overwrite the
                            // Solidity scratch pad at memory position 0.
                            calldatacopy(0, 0, calldatasize())
                            // Call the implementation.
                            // out and outsize are 0 because we don't know the size yet.
                            let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                            // Copy the returned data.
                            returndatacopy(0, 0, returndatasize())
                            switch result
                            // delegatecall returns 0 on error.
                            case 0 { revert(0, returndatasize()) }
                            default { return(0, returndatasize()) }
                        }
                    }
                    /**
                     * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
                     * and {_fallback} should delegate.
                     */
                    function _implementation() internal view virtual returns (address);
                    /**
                     * @dev Delegates the current call to the address returned by `_implementation()`.
                     *
                     * This function does not return to its internall call site, it will return directly to the external caller.
                     */
                    function _fallback() internal virtual {
                        _beforeFallback();
                        _delegate(_implementation());
                    }
                    /**
                     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                     * function in the contract matches the call data.
                     */
                    fallback () external payable virtual {
                        _fallback();
                    }
                    /**
                     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                     * is empty.
                     */
                    receive () external payable virtual {
                        _fallback();
                    }
                    /**
                     * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                     * call, or as part of the Solidity `fallback` or `receive` functions.
                     *
                     * If overriden should call `super._beforeFallback()`.
                     */
                    function _beforeFallback() internal virtual {
                    }
                }
                // SPDX-License-Identifier: MIT
                pragma solidity ^0.8.0;
                /**
                 * @dev This is the interface that {BeaconProxy} expects of its beacon.
                 */
                interface IBeacon {
                    /**
                     * @dev Must return an address that can be used as a delegate call target.
                     *
                     * {BeaconProxy} will check that this address is a contract.
                     */
                    function implementation() external view returns (address);
                }
                // SPDX-License-Identifier: MIT
                pragma solidity ^0.8.0;
                /**
                 * @dev Collection of functions related to the address type
                 */
                library Address {
                    /**
                     * @dev Returns true if `account` is a contract.
                     *
                     * [IMPORTANT]
                     * ====
                     * It is unsafe to assume that an address for which this function returns
                     * false is an externally-owned account (EOA) and not a contract.
                     *
                     * Among others, `isContract` will return false for the following
                     * types of addresses:
                     *
                     *  - an externally-owned account
                     *  - a contract in construction
                     *  - an address where a contract will be created
                     *  - an address where a contract lived, but was destroyed
                     * ====
                     */
                    function isContract(address account) internal view returns (bool) {
                        // This method relies on extcodesize, which returns 0 for contracts in
                        // construction, since the code is only stored at the end of the
                        // constructor execution.
                        uint256 size;
                        // solhint-disable-next-line no-inline-assembly
                        assembly { size := extcodesize(account) }
                        return size > 0;
                    }
                    /**
                     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                     * `recipient`, forwarding all available gas and reverting on errors.
                     *
                     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                     * of certain opcodes, possibly making contracts go over the 2300 gas limit
                     * imposed by `transfer`, making them unable to receive funds via
                     * `transfer`. {sendValue} removes this limitation.
                     *
                     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                     *
                     * IMPORTANT: because control is transferred to `recipient`, care must be
                     * taken to not create reentrancy vulnerabilities. Consider using
                     * {ReentrancyGuard} or the
                     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                     */
                    function sendValue(address payable recipient, uint256 amount) internal {
                        require(address(this).balance >= amount, "Address: insufficient balance");
                        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                        (bool success, ) = recipient.call{ value: amount }("");
                        require(success, "Address: unable to send value, recipient may have reverted");
                    }
                    /**
                     * @dev Performs a Solidity function call using a low level `call`. A
                     * plain`call` is an unsafe replacement for a function call: use this
                     * function instead.
                     *
                     * If `target` reverts with a revert reason, it is bubbled up by this
                     * function (like regular Solidity function calls).
                     *
                     * Returns the raw returned data. To convert to the expected return value,
                     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                     *
                     * Requirements:
                     *
                     * - `target` must be a contract.
                     * - calling `target` with `data` must not revert.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionCall(target, data, "Address: low-level call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                     * `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, 0, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but also transferring `value` wei to `target`.
                     *
                     * Requirements:
                     *
                     * - the calling contract must have an ETH balance of at least `value`.
                     * - the called Solidity function must be `payable`.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                     * with `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                        require(address(this).balance >= value, "Address: insufficient balance for call");
                        require(isContract(target), "Address: call to non-contract");
                        // solhint-disable-next-line avoid-low-level-calls
                        (bool success, bytes memory returndata) = target.call{ value: value }(data);
                        return _verifyCallResult(success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                        return functionStaticCall(target, data, "Address: low-level static call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                        require(isContract(target), "Address: static call to non-contract");
                        // solhint-disable-next-line avoid-low-level-calls
                        (bool success, bytes memory returndata) = target.staticcall(data);
                        return _verifyCallResult(success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                        require(isContract(target), "Address: delegate call to non-contract");
                        // solhint-disable-next-line avoid-low-level-calls
                        (bool success, bytes memory returndata) = target.delegatecall(data);
                        return _verifyCallResult(success, returndata, errorMessage);
                    }
                    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                        if (success) {
                            return returndata;
                        } else {
                            // Look for revert reason and bubble it up if present
                            if (returndata.length > 0) {
                                // The easiest way to bubble the revert reason is using memory via assembly
                                // solhint-disable-next-line no-inline-assembly
                                assembly {
                                    let returndata_size := mload(returndata)
                                    revert(add(32, returndata), returndata_size)
                                }
                            } else {
                                revert(errorMessage);
                            }
                        }
                    }
                }
                // SPDX-License-Identifier: MIT
                pragma solidity ^0.8.0;
                /**
                 * @dev Library for reading and writing primitive types to specific storage slots.
                 *
                 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
                 * This library helps with reading and writing to such slots without the need for inline assembly.
                 *
                 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
                 *
                 * Example usage to set ERC1967 implementation slot:
                 * ```
                 * contract ERC1967 {
                 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                 *
                 *     function _getImplementation() internal view returns (address) {
                 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                 *     }
                 *
                 *     function _setImplementation(address newImplementation) internal {
                 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                 *     }
                 * }
                 * ```
                 *
                 * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
                 */
                library StorageSlot {
                    struct AddressSlot {
                        address value;
                    }
                    struct BooleanSlot {
                        bool value;
                    }
                    struct Bytes32Slot {
                        bytes32 value;
                    }
                    struct Uint256Slot {
                        uint256 value;
                    }
                    /**
                     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                     */
                    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                        assembly {
                            r.slot := slot
                        }
                    }
                    /**
                     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                     */
                    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                        assembly {
                            r.slot := slot
                        }
                    }
                    /**
                     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                     */
                    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                        assembly {
                            r.slot := slot
                        }
                    }
                    /**
                     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                     */
                    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                        assembly {
                            r.slot := slot
                        }
                    }
                }
                

                File 8 of 8: Implementation
                // SPDX-License-Identifier: MIT
                pragma solidity ^0.8.2;
                import "../beacon/IBeacon.sol";
                import "../../utils/Address.sol";
                import "../../utils/StorageSlot.sol";
                /**
                 * @dev This abstract contract provides getters and event emitting update functions for
                 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
                 *
                 * _Available since v4.1._
                 *
                 * @custom:oz-upgrades-unsafe-allow delegatecall
                 */
                abstract contract ERC1967Upgrade {
                    // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                    bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                    /**
                     * @dev Storage slot with the address of the current implementation.
                     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                     * validated in the constructor.
                     */
                    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                    /**
                     * @dev Emitted when the implementation is upgraded.
                     */
                    event Upgraded(address indexed implementation);
                    /**
                     * @dev Returns the current implementation address.
                     */
                    function _getImplementation() internal view returns (address) {
                        return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                    }
                    /**
                     * @dev Stores a new address in the EIP1967 implementation slot.
                     */
                    function _setImplementation(address newImplementation) private {
                        require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                        StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                    }
                    /**
                     * @dev Perform implementation upgrade
                     *
                     * Emits an {Upgraded} event.
                     */
                    function _upgradeTo(address newImplementation) internal {
                        _setImplementation(newImplementation);
                        emit Upgraded(newImplementation);
                    }
                    /**
                     * @dev Perform implementation upgrade with additional setup call.
                     *
                     * Emits an {Upgraded} event.
                     */
                    function _upgradeToAndCall(
                        address newImplementation,
                        bytes memory data,
                        bool forceCall
                    ) internal {
                        _upgradeTo(newImplementation);
                        if (data.length > 0 || forceCall) {
                            Address.functionDelegateCall(newImplementation, data);
                        }
                    }
                    /**
                     * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                     *
                     * Emits an {Upgraded} event.
                     */
                    function _upgradeToAndCallSecure(
                        address newImplementation,
                        bytes memory data,
                        bool forceCall
                    ) internal {
                        address oldImplementation = _getImplementation();
                        // Initial upgrade and setup call
                        _setImplementation(newImplementation);
                        if (data.length > 0 || forceCall) {
                            Address.functionDelegateCall(newImplementation, data);
                        }
                        // Perform rollback test if not already in progress
                        StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
                        if (!rollbackTesting.value) {
                            // Trigger rollback using upgradeTo from the new implementation
                            rollbackTesting.value = true;
                            Address.functionDelegateCall(
                                newImplementation,
                                abi.encodeWithSignature("upgradeTo(address)", oldImplementation)
                            );
                            rollbackTesting.value = false;
                            // Check rollback was effective
                            require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
                            // Finally reset to the new implementation and log the upgrade
                            _upgradeTo(newImplementation);
                        }
                    }
                    /**
                     * @dev Storage slot with the admin of the contract.
                     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                     * validated in the constructor.
                     */
                    bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                    /**
                     * @dev Emitted when the admin account has changed.
                     */
                    event AdminChanged(address previousAdmin, address newAdmin);
                    /**
                     * @dev Returns the current admin.
                     */
                    function _getAdmin() internal view returns (address) {
                        return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                    }
                    /**
                     * @dev Stores a new address in the EIP1967 admin slot.
                     */
                    function _setAdmin(address newAdmin) private {
                        require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                        StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                    }
                    /**
                     * @dev Changes the admin of the proxy.
                     *
                     * Emits an {AdminChanged} event.
                     */
                    function _changeAdmin(address newAdmin) internal {
                        emit AdminChanged(_getAdmin(), newAdmin);
                        _setAdmin(newAdmin);
                    }
                    /**
                     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                     * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                     */
                    bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                    /**
                     * @dev Emitted when the beacon is upgraded.
                     */
                    event BeaconUpgraded(address indexed beacon);
                    /**
                     * @dev Returns the current beacon.
                     */
                    function _getBeacon() internal view returns (address) {
                        return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                    }
                    /**
                     * @dev Stores a new beacon in the EIP1967 beacon slot.
                     */
                    function _setBeacon(address newBeacon) private {
                        require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                        require(
                            Address.isContract(IBeacon(newBeacon).implementation()),
                            "ERC1967: beacon implementation is not a contract"
                        );
                        StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                    }
                    /**
                     * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                     * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                     *
                     * Emits a {BeaconUpgraded} event.
                     */
                    function _upgradeBeaconToAndCall(
                        address newBeacon,
                        bytes memory data,
                        bool forceCall
                    ) internal {
                        _setBeacon(newBeacon);
                        emit BeaconUpgraded(newBeacon);
                        if (data.length > 0 || forceCall) {
                            Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                        }
                    }
                }
                // SPDX-License-Identifier: MIT
                pragma solidity ^0.8.0;
                /**
                 * @dev This is the interface that {BeaconProxy} expects of its beacon.
                 */
                interface IBeacon {
                    /**
                     * @dev Must return an address that can be used as a delegate call target.
                     *
                     * {BeaconProxy} will check that this address is a contract.
                     */
                    function implementation() external view returns (address);
                }
                // SPDX-License-Identifier: MIT
                pragma solidity ^0.8.0;
                /**
                 * @dev Collection of functions related to the address type
                 */
                library Address {
                    /**
                     * @dev Returns true if `account` is a contract.
                     *
                     * [IMPORTANT]
                     * ====
                     * It is unsafe to assume that an address for which this function returns
                     * false is an externally-owned account (EOA) and not a contract.
                     *
                     * Among others, `isContract` will return false for the following
                     * types of addresses:
                     *
                     *  - an externally-owned account
                     *  - a contract in construction
                     *  - an address where a contract will be created
                     *  - an address where a contract lived, but was destroyed
                     * ====
                     */
                    function isContract(address account) internal view returns (bool) {
                        // This method relies on extcodesize, which returns 0 for contracts in
                        // construction, since the code is only stored at the end of the
                        // constructor execution.
                        uint256 size;
                        assembly {
                            size := extcodesize(account)
                        }
                        return size > 0;
                    }
                    /**
                     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                     * `recipient`, forwarding all available gas and reverting on errors.
                     *
                     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                     * of certain opcodes, possibly making contracts go over the 2300 gas limit
                     * imposed by `transfer`, making them unable to receive funds via
                     * `transfer`. {sendValue} removes this limitation.
                     *
                     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                     *
                     * IMPORTANT: because control is transferred to `recipient`, care must be
                     * taken to not create reentrancy vulnerabilities. Consider using
                     * {ReentrancyGuard} or the
                     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                     */
                    function sendValue(address payable recipient, uint256 amount) internal {
                        require(address(this).balance >= amount, "Address: insufficient balance");
                        (bool success, ) = recipient.call{value: amount}("");
                        require(success, "Address: unable to send value, recipient may have reverted");
                    }
                    /**
                     * @dev Performs a Solidity function call using a low level `call`. A
                     * plain `call` is an unsafe replacement for a function call: use this
                     * function instead.
                     *
                     * If `target` reverts with a revert reason, it is bubbled up by this
                     * function (like regular Solidity function calls).
                     *
                     * Returns the raw returned data. To convert to the expected return value,
                     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                     *
                     * Requirements:
                     *
                     * - `target` must be a contract.
                     * - calling `target` with `data` must not revert.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionCall(target, data, "Address: low-level call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                     * `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, 0, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but also transferring `value` wei to `target`.
                     *
                     * Requirements:
                     *
                     * - the calling contract must have an ETH balance of at least `value`.
                     * - the called Solidity function must be `payable`.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(
                        address target,
                        bytes memory data,
                        uint256 value
                    ) internal returns (bytes memory) {
                        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                     * with `errorMessage` as a fallback revert reason when `target` reverts.
                     *
                     * _Available since v3.1._
                     */
                    function functionCallWithValue(
                        address target,
                        bytes memory data,
                        uint256 value,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        require(address(this).balance >= value, "Address: insufficient balance for call");
                        require(isContract(target), "Address: call to non-contract");
                        (bool success, bytes memory returndata) = target.call{value: value}(data);
                        return verifyCallResult(success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                        return functionStaticCall(target, data, "Address: low-level static call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a static call.
                     *
                     * _Available since v3.3._
                     */
                    function functionStaticCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal view returns (bytes memory) {
                        require(isContract(target), "Address: static call to non-contract");
                        (bool success, bytes memory returndata) = target.staticcall(data);
                        return verifyCallResult(success, returndata, errorMessage);
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                    }
                    /**
                     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                     * but performing a delegate call.
                     *
                     * _Available since v3.4._
                     */
                    function functionDelegateCall(
                        address target,
                        bytes memory data,
                        string memory errorMessage
                    ) internal returns (bytes memory) {
                        require(isContract(target), "Address: delegate call to non-contract");
                        (bool success, bytes memory returndata) = target.delegatecall(data);
                        return verifyCallResult(success, returndata, errorMessage);
                    }
                    /**
                     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                     * revert reason using the provided one.
                     *
                     * _Available since v4.3._
                     */
                    function verifyCallResult(
                        bool success,
                        bytes memory returndata,
                        string memory errorMessage
                    ) internal pure returns (bytes memory) {
                        if (success) {
                            return returndata;
                        } else {
                            // Look for revert reason and bubble it up if present
                            if (returndata.length > 0) {
                                // The easiest way to bubble the revert reason is using memory via assembly
                                assembly {
                                    let returndata_size := mload(returndata)
                                    revert(add(32, returndata), returndata_size)
                                }
                            } else {
                                revert(errorMessage);
                            }
                        }
                    }
                }
                // SPDX-License-Identifier: MIT
                pragma solidity ^0.8.0;
                /**
                 * @dev Library for reading and writing primitive types to specific storage slots.
                 *
                 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
                 * This library helps with reading and writing to such slots without the need for inline assembly.
                 *
                 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
                 *
                 * Example usage to set ERC1967 implementation slot:
                 * ```
                 * contract ERC1967 {
                 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                 *
                 *     function _getImplementation() internal view returns (address) {
                 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                 *     }
                 *
                 *     function _setImplementation(address newImplementation) internal {
                 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                 *     }
                 * }
                 * ```
                 *
                 * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
                 */
                library StorageSlot {
                    struct AddressSlot {
                        address value;
                    }
                    struct BooleanSlot {
                        bool value;
                    }
                    struct Bytes32Slot {
                        bytes32 value;
                    }
                    struct Uint256Slot {
                        uint256 value;
                    }
                    /**
                     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                     */
                    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                        assembly {
                            r.slot := slot
                        }
                    }
                    /**
                     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                     */
                    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                        assembly {
                            r.slot := slot
                        }
                    }
                    /**
                     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                     */
                    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                        assembly {
                            r.slot := slot
                        }
                    }
                    /**
                     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                     */
                    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                        assembly {
                            r.slot := slot
                        }
                    }
                }
                // contracts/Getters.sol
                // SPDX-License-Identifier: Apache 2
                pragma solidity ^0.8.0;
                import "./State.sol";
                contract Getters is State {
                    function getGuardianSet(uint32 index) public view returns (Structs.GuardianSet memory) {
                        return _state.guardianSets[index];
                    }
                    function getCurrentGuardianSetIndex() public view returns (uint32) {
                        return _state.guardianSetIndex;
                    }
                    function getGuardianSetExpiry() public view returns (uint32) {
                        return _state.guardianSetExpiry;
                    }
                    function governanceActionIsConsumed(bytes32 hash) public view returns (bool) {
                        return _state.consumedGovernanceActions[hash];
                    }
                    function isInitialized(address impl) public view returns (bool) {
                        return _state.initializedImplementations[impl];
                    }
                    function chainId() public view returns (uint16) {
                        return _state.provider.chainId;
                    }
                    function evmChainId() public view returns (uint256) {
                        return _state.evmChainId;
                    }
                    function isFork() public view returns (bool) {
                        return evmChainId() != block.chainid;
                    }
                    function governanceChainId() public view returns (uint16){
                        return _state.provider.governanceChainId;
                    }
                    function governanceContract() public view returns (bytes32){
                        return _state.provider.governanceContract;
                    }
                    function messageFee() public view returns (uint256) {
                        return _state.messageFee;
                    }
                    function nextSequence(address emitter) public view returns (uint64) {
                        return _state.sequences[emitter];
                    }
                }// contracts/Governance.sol
                // SPDX-License-Identifier: Apache 2
                pragma solidity ^0.8.0;
                import "./Structs.sol";
                import "./GovernanceStructs.sol";
                import "./Messages.sol";
                import "./Setters.sol";
                import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
                /**
                 * @dev `Governance` defines a means to enacting changes to the core bridge contract,
                 * guardianSets, message fees, and transfer fees
                 */
                abstract contract Governance is GovernanceStructs, Messages, Setters, ERC1967Upgrade {
                    event ContractUpgraded(address indexed oldContract, address indexed newContract);
                    event GuardianSetAdded(uint32 indexed index);
                    // "Core" (left padded)
                    bytes32 constant module = 0x00000000000000000000000000000000000000000000000000000000436f7265;
                    /**
                     * @dev Upgrades a contract via Governance VAA/VM
                     */
                    function submitContractUpgrade(bytes memory _vm) public {
                        require(!isFork(), "invalid fork");
                        Structs.VM memory vm = parseVM(_vm);
                        // Verify the VAA is valid before processing it
                        (bool isValid, string memory reason) = verifyGovernanceVM(vm);
                        require(isValid, reason);
                        GovernanceStructs.ContractUpgrade memory upgrade = parseContractUpgrade(vm.payload);
                        // Verify the VAA is for this module
                        require(upgrade.module == module, "Invalid Module");
                        // Verify the VAA is for this chain
                        require(upgrade.chain == chainId(), "Invalid Chain");
                        // Record the governance action as consumed
                        setGovernanceActionConsumed(vm.hash);
                        // Upgrades the implementation to the new contract
                        upgradeImplementation(upgrade.newContract);
                    }
                    /**
                     * @dev Sets a `messageFee` via Governance VAA/VM
                     */
                    function submitSetMessageFee(bytes memory _vm) public {
                        Structs.VM memory vm = parseVM(_vm);
                        // Verify the VAA is valid before processing it
                        (bool isValid, string memory reason) = verifyGovernanceVM(vm);
                        require(isValid, reason);
                        GovernanceStructs.SetMessageFee memory upgrade = parseSetMessageFee(vm.payload);
                        // Verify the VAA is for this module
                        require(upgrade.module == module, "Invalid Module");
                        // Verify the VAA is for this chain
                        require(upgrade.chain == chainId() && !isFork(), "Invalid Chain");
                        // Record the governance action as consumed to prevent reentry
                        setGovernanceActionConsumed(vm.hash);
                        // Updates the messageFee
                        setMessageFee(upgrade.messageFee);
                    }
                    /**
                     * @dev Deploys a new `guardianSet` via Governance VAA/VM
                     */
                    function submitNewGuardianSet(bytes memory _vm) public {
                        Structs.VM memory vm = parseVM(_vm);
                        // Verify the VAA is valid before processing it
                        (bool isValid, string memory reason) = verifyGovernanceVM(vm);
                        require(isValid, reason);
                        GovernanceStructs.GuardianSetUpgrade memory upgrade = parseGuardianSetUpgrade(vm.payload);
                        // Verify the VAA is for this module
                        require(upgrade.module == module, "invalid Module");
                        // Verify the VAA is for this chain
                        require((upgrade.chain == chainId() && !isFork()) || upgrade.chain == 0, "invalid Chain");
                        // Verify the Guardian Set keys are not empty, this guards
                        // against the accidential upgrade to an empty GuardianSet
                        require(upgrade.newGuardianSet.keys.length > 0, "new guardian set is empty");
                        // Verify that the index is incrementing via a predictable +1 pattern
                        require(upgrade.newGuardianSetIndex == getCurrentGuardianSetIndex() + 1, "index must increase in steps of 1");
                        // Record the governance action as consumed to prevent reentry
                        setGovernanceActionConsumed(vm.hash);
                        // Trigger a time-based expiry of current guardianSet
                        expireGuardianSet(getCurrentGuardianSetIndex());
                        // Add the new guardianSet to guardianSets
                        storeGuardianSet(upgrade.newGuardianSet, upgrade.newGuardianSetIndex);
                        // Makes the new guardianSet effective
                        updateGuardianSetIndex(upgrade.newGuardianSetIndex);
                    }
                    /**
                     * @dev Submits transfer fees to the recipient via Governance VAA/VM
                     */
                    function submitTransferFees(bytes memory _vm) public {
                        Structs.VM memory vm = parseVM(_vm);
                        // Verify the VAA is valid before processing it
                        (bool isValid, string memory reason) = verifyGovernanceVM(vm);
                        require(isValid, reason);
                        // Obtains the transfer from the VAA payload
                        GovernanceStructs.TransferFees memory transfer = parseTransferFees(vm.payload);
                        // Verify the VAA is for this module
                        require(transfer.module == module, "invalid Module");
                        // Verify the VAA is for this chain
                        require((transfer.chain == chainId() && !isFork()) || transfer.chain == 0, "invalid Chain");
                        // Record the governance action as consumed to prevent reentry
                        setGovernanceActionConsumed(vm.hash);
                        // Obtains the recipient address to be paid transfer fees
                        address payable recipient = payable(address(uint160(uint256(transfer.recipient))));
                        // Transfers transfer fees to the recipient
                        recipient.transfer(transfer.amount);
                    }
                    /**
                    * @dev Updates the `chainId` and `evmChainId` on a forked chain via Governance VAA/VM
                    */
                    function submitRecoverChainId(bytes memory _vm) public {
                        require(isFork(), "not a fork");
                        Structs.VM memory vm = parseVM(_vm);
                        // Verify the VAA is valid before processing it
                        (bool isValid, string memory reason) = verifyGovernanceVM(vm);
                        require(isValid, reason);
                        GovernanceStructs.RecoverChainId memory rci = parseRecoverChainId(vm.payload);
                        // Verify the VAA is for this module
                        require(rci.module == module, "invalid Module");
                        // Verify the VAA is for this chain
                        require(rci.evmChainId == block.chainid, "invalid EVM Chain");
                        // Record the governance action as consumed to prevent reentry
                        setGovernanceActionConsumed(vm.hash);
                        // Update the chainIds
                        setEvmChainId(rci.evmChainId);
                        setChainId(rci.newChainId);
                    }
                    /**
                     * @dev Upgrades the `currentImplementation` with a `newImplementation`
                     */
                    function upgradeImplementation(address newImplementation) internal {
                        address currentImplementation = _getImplementation();
                        _upgradeTo(newImplementation);
                        // Call initialize function of the new implementation
                        (bool success, bytes memory reason) = newImplementation.delegatecall(abi.encodeWithSignature("initialize()"));
                        require(success, string(reason));
                        emit ContractUpgraded(currentImplementation, newImplementation);
                    }
                    /**
                     * @dev Verifies a Governance VAA/VM is valid
                     */
                    function verifyGovernanceVM(Structs.VM memory vm) internal view returns (bool, string memory){
                        // Verify the VAA is valid
                        (bool isValid, string memory reason) = verifyVM(vm);
                        if (!isValid){
                            return (false, reason);
                        }
                        // only current guardianset can sign governance packets
                        if (vm.guardianSetIndex != getCurrentGuardianSetIndex()) {
                            return (false, "not signed by current guardian set");
                        }
                        // Verify the VAA is from the governance chain (Solana)
                        if (uint16(vm.emitterChainId) != governanceChainId()) {
                            return (false, "wrong governance chain");
                        }
                        // Verify the emitter contract is the governance contract (0x4 left padded)
                        if (vm.emitterAddress != governanceContract()) {
                            return (false, "wrong governance contract");
                        }
                        // Verify this governance action hasn't already been
                        // consumed to prevent reentry and replay
                        if (governanceActionIsConsumed(vm.hash)){
                            return (false, "governance action already consumed");
                        }
                        // Confirm the governance VAA/VM is valid
                        return (true, "");
                    }
                }// contracts/GovernanceStructs.sol
                // SPDX-License-Identifier: Apache 2
                pragma solidity ^0.8.0;
                import "./libraries/external/BytesLib.sol";
                import "./Structs.sol";
                /**
                 * @dev `GovernanceStructs` defines a set of structs and parsing functions
                 * for minimal struct validation
                 */
                contract GovernanceStructs {
                    using BytesLib for bytes;
                    enum GovernanceAction {
                        UpgradeContract,
                        UpgradeGuardianset
                    }
                    struct ContractUpgrade {
                        bytes32 module;
                        uint8 action;
                        uint16 chain;
                        address newContract;
                    }
                    struct GuardianSetUpgrade {
                        bytes32 module;
                        uint8 action;
                        uint16 chain;
                        Structs.GuardianSet newGuardianSet;
                        uint32 newGuardianSetIndex;
                    }
                    struct SetMessageFee {
                        bytes32 module;
                        uint8 action;
                        uint16 chain;
                        uint256 messageFee;
                    }
                    struct TransferFees {
                        bytes32 module;
                        uint8 action;
                        uint16 chain;
                        uint256 amount;
                        bytes32 recipient;
                    }
                    struct RecoverChainId {
                        bytes32 module;
                        uint8 action;
                        uint256 evmChainId;
                        uint16 newChainId;
                    }
                    /// @dev Parse a contract upgrade (action 1) with minimal validation
                    function parseContractUpgrade(bytes memory encodedUpgrade) public pure returns (ContractUpgrade memory cu) {
                        uint index = 0;
                        cu.module = encodedUpgrade.toBytes32(index);
                        index += 32;
                        cu.action = encodedUpgrade.toUint8(index);
                        index += 1;
                        require(cu.action == 1, "invalid ContractUpgrade");
                        cu.chain = encodedUpgrade.toUint16(index);
                        index += 2;
                        cu.newContract = address(uint160(uint256(encodedUpgrade.toBytes32(index))));
                        index += 32;
                        require(encodedUpgrade.length == index, "invalid ContractUpgrade");
                    }
                    /// @dev Parse a guardianSet upgrade (action 2) with minimal validation
                    function parseGuardianSetUpgrade(bytes memory encodedUpgrade) public pure returns (GuardianSetUpgrade memory gsu) {
                        uint index = 0;
                        gsu.module = encodedUpgrade.toBytes32(index);
                        index += 32;
                        gsu.action = encodedUpgrade.toUint8(index);
                        index += 1;
                        require(gsu.action == 2, "invalid GuardianSetUpgrade");
                        gsu.chain = encodedUpgrade.toUint16(index);
                        index += 2;
                        gsu.newGuardianSetIndex = encodedUpgrade.toUint32(index);
                        index += 4;
                        uint8 guardianLength = encodedUpgrade.toUint8(index);
                        index += 1;
                        gsu.newGuardianSet = Structs.GuardianSet({
                            keys : new address[](guardianLength),
                            expirationTime : 0
                        });
                        for(uint i = 0; i < guardianLength; i++) {
                            gsu.newGuardianSet.keys[i] = encodedUpgrade.toAddress(index);
                            index += 20;
                        }
                        require(encodedUpgrade.length == index, "invalid GuardianSetUpgrade");
                    }
                    /// @dev Parse a setMessageFee (action 3) with minimal validation
                    function parseSetMessageFee(bytes memory encodedSetMessageFee) public pure returns (SetMessageFee memory smf) {
                        uint index = 0;
                        smf.module = encodedSetMessageFee.toBytes32(index);
                        index += 32;
                        smf.action = encodedSetMessageFee.toUint8(index);
                        index += 1;
                        require(smf.action == 3, "invalid SetMessageFee");
                        smf.chain = encodedSetMessageFee.toUint16(index);
                        index += 2;
                        smf.messageFee = encodedSetMessageFee.toUint256(index);
                        index += 32;
                        require(encodedSetMessageFee.length == index, "invalid SetMessageFee");
                    }
                    /// @dev Parse a transferFees (action 4) with minimal validation
                    function parseTransferFees(bytes memory encodedTransferFees) public pure returns (TransferFees memory tf) {
                        uint index = 0;
                        tf.module = encodedTransferFees.toBytes32(index);
                        index += 32;
                        tf.action = encodedTransferFees.toUint8(index);
                        index += 1;
                        require(tf.action == 4, "invalid TransferFees");
                        tf.chain = encodedTransferFees.toUint16(index);
                        index += 2;
                        tf.amount = encodedTransferFees.toUint256(index);
                        index += 32;
                        tf.recipient = encodedTransferFees.toBytes32(index);
                        index += 32;
                        require(encodedTransferFees.length == index, "invalid TransferFees");
                    }
                    /// @dev Parse a recoverChainId (action 5) with minimal validation
                    function parseRecoverChainId(bytes memory encodedRecoverChainId) public pure returns (RecoverChainId memory rci) {
                        uint index = 0;
                        rci.module = encodedRecoverChainId.toBytes32(index);
                        index += 32;
                        rci.action = encodedRecoverChainId.toUint8(index);
                        index += 1;
                        require(rci.action == 5, "invalid RecoverChainId");
                        rci.evmChainId = encodedRecoverChainId.toUint256(index);
                        index += 32;
                        rci.newChainId = encodedRecoverChainId.toUint16(index);
                        index += 2;
                        require(encodedRecoverChainId.length == index, "invalid RecoverChainId");
                    }
                }// contracts/Implementation.sol
                // SPDX-License-Identifier: Apache 2
                pragma solidity ^0.8.0;
                pragma experimental ABIEncoderV2;
                import "./Governance.sol";
                import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
                contract Implementation is Governance {
                    event LogMessagePublished(address indexed sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel);
                    // Publish a message to be attested by the Wormhole network
                    function publishMessage(
                        uint32 nonce,
                        bytes memory payload,
                        uint8 consistencyLevel
                    ) public payable returns (uint64 sequence) {
                        // check fee
                        require(msg.value == messageFee(), "invalid fee");
                        sequence = useSequence(msg.sender);
                        // emit log
                        emit LogMessagePublished(msg.sender, sequence, nonce, payload, consistencyLevel);
                    }
                    function useSequence(address emitter) internal returns (uint64 sequence) {
                        sequence = nextSequence(emitter);
                        setNextSequence(emitter, sequence + 1);
                    }
                    function initialize() initializer public virtual {
                        // this function needs to be exposed for an upgrade to pass
                        uint256 evmChainId;
                        uint16 chain = chainId();
                        // Wormhole chain ids explicitly enumerated
                        if        (chain == 2)  { evmChainId = 1;          // ethereum
                        } else if (chain == 4)  { evmChainId = 56;         // bsc
                        } else if (chain == 5)  { evmChainId = 137;        // polygon
                        } else if (chain == 6)  { evmChainId = 43114;      // avalanche
                        } else if (chain == 7)  { evmChainId = 42262;      // oasis
                        } else if (chain == 9)  { evmChainId = 1313161554; // aurora
                        } else if (chain == 10) { evmChainId = 250;        // fantom
                        } else if (chain == 11) { evmChainId = 686;        // karura
                        } else if (chain == 12) { evmChainId = 787;        // acala
                        } else if (chain == 13) { evmChainId = 8217;       // klaytn
                        } else if (chain == 14) { evmChainId = 42220;      // celo
                        } else if (chain == 16) { evmChainId = 1284;       // moonbeam
                        } else if (chain == 17) { evmChainId = 245022934;  // neon
                        } else if (chain == 23) { evmChainId = 42161;      // arbitrum
                        } else if (chain == 24) { evmChainId = 10;         // optimism
                        } else if (chain == 25) { evmChainId = 100;        // gnosis
                        } else {
                            revert("Unknown chain id.");
                        }
                        setEvmChainId(evmChainId);
                    }
                    modifier initializer() {
                        address implementation = ERC1967Upgrade._getImplementation();
                        require(
                            !isInitialized(implementation),
                            "already initialized"
                        );
                        setInitialized(implementation);
                        _;
                    }
                    fallback() external payable {revert("unsupported");}
                    receive() external payable {revert("the Wormhole contract does not accept assets");}
                }
                // contracts/Messages.sol
                // SPDX-License-Identifier: Apache 2
                pragma solidity ^0.8.0;
                pragma experimental ABIEncoderV2;
                import "./Getters.sol";
                import "./Structs.sol";
                import "./libraries/external/BytesLib.sol";
                contract Messages is Getters {
                    using BytesLib for bytes;
                    /// @dev parseAndVerifyVM serves to parse an encodedVM and wholy validate it for consumption
                    function parseAndVerifyVM(bytes calldata encodedVM) public view returns (Structs.VM memory vm, bool valid, string memory reason) {
                        vm = parseVM(encodedVM);
                        (valid, reason) = verifyVM(vm);
                    }
                   /**
                    * @dev `verifyVM` serves to validate an arbitrary vm against a valid Guardian set
                    *  - it aims to make sure the VM is for a known guardianSet
                    *  - it aims to ensure the guardianSet is not expired
                    *  - it aims to ensure the VM has reached quorum
                    *  - it aims to verify the signatures provided against the guardianSet
                    */
                    function verifyVM(Structs.VM memory vm) public view returns (bool valid, string memory reason) {
                        /// @dev Obtain the current guardianSet for the guardianSetIndex provided
                        Structs.GuardianSet memory guardianSet = getGuardianSet(vm.guardianSetIndex);
                       /**
                        * @dev Checks whether the guardianSet has zero keys
                        * WARNING: This keys check is critical to ensure the guardianSet has keys present AND to ensure
                        * that guardianSet key size doesn't fall to zero and negatively impact quorum assessment.  If guardianSet
                        * key length is 0 and vm.signatures length is 0, this could compromise the integrity of both vm and
                        * signature verification.
                        */
                        if(guardianSet.keys.length == 0){
                            return (false, "invalid guardian set");
                        }
                        /// @dev Checks if VM guardian set index matches the current index (unless the current set is expired).
                        if(vm.guardianSetIndex != getCurrentGuardianSetIndex() && guardianSet.expirationTime < block.timestamp){
                            return (false, "guardian set has expired");
                        }
                       /**
                        * @dev We're using a fixed point number transformation with 1 decimal to deal with rounding.
                        *   WARNING: This quorum check is critical to assessing whether we have enough Guardian signatures to validate a VM
                        *   if making any changes to this, obtain additional peer review. If guardianSet key length is 0 and
                        *   vm.signatures length is 0, this could compromise the integrity of both vm and signature verification.
                        */
                        if (vm.signatures.length < quorum(guardianSet.keys.length)){
                            return (false, "no quorum");
                        }
                        /// @dev Verify the proposed vm.signatures against the guardianSet
                        (bool signaturesValid, string memory invalidReason) = verifySignatures(vm.hash, vm.signatures, guardianSet);
                        if(!signaturesValid){
                            return (false, invalidReason);
                        }
                        /// If we are here, we've validated the VM is a valid multi-sig that matches the guardianSet.
                        return (true, "");
                    }
                    /**
                     * @dev verifySignatures serves to validate arbitrary sigatures against an arbitrary guardianSet
                     *  - it intentionally does not solve for expectations within guardianSet (you should use verifyVM if you need these protections)
                     *  - it intentioanlly does not solve for quorum (you should use verifyVM if you need these protections)
                     *  - it intentionally returns true when signatures is an empty set (you should use verifyVM if you need these protections)
                     */
                    function verifySignatures(bytes32 hash, Structs.Signature[] memory signatures, Structs.GuardianSet memory guardianSet) public pure returns (bool valid, string memory reason) {
                        uint8 lastIndex = 0;
                        uint256 guardianCount = guardianSet.keys.length;
                        for (uint i = 0; i < signatures.length; i++) {
                            Structs.Signature memory sig = signatures[i];
                            /// Ensure that provided signature indices are ascending only
                            require(i == 0 || sig.guardianIndex > lastIndex, "signature indices must be ascending");
                            lastIndex = sig.guardianIndex;
                            /// @dev Ensure that the provided signature index is within the
                            /// bounds of the guardianSet. This is implicitly checked by the array
                            /// index operation below, so this check is technically redundant.
                            /// However, reverting explicitly here ensures that a bug is not
                            /// introduced accidentally later due to the nontrivial storage
                            /// semantics of solidity.
                            require(sig.guardianIndex < guardianCount, "guardian index out of bounds");
                            /// Check to see if the signer of the signature does not match a specific Guardian key at the provided index
                            if(ecrecover(hash, sig.v, sig.r, sig.s) != guardianSet.keys[sig.guardianIndex]){
                                return (false, "VM signature invalid");
                            }
                        }
                        /// If we are here, we've validated that the provided signatures are valid for the provided guardianSet
                        return (true, "");
                    }
                    /**
                     * @dev parseVM serves to parse an encodedVM into a vm struct
                     *  - it intentionally performs no validation functions, it simply parses raw into a struct
                     */
                    function parseVM(bytes memory encodedVM) public pure virtual returns (Structs.VM memory vm) {
                        uint index = 0;
                        vm.version = encodedVM.toUint8(index);
                        index += 1;
                        // SECURITY: Note that currently the VM.version is not part of the hash 
                        // and for reasons described below it cannot be made part of the hash. 
                        // This means that this field's integrity is not protected and cannot be trusted. 
                        // This is not a problem today since there is only one accepted version, but it 
                        // could be a problem if we wanted to allow other versions in the future. 
                        require(vm.version == 1, "VM version incompatible"); 
                        vm.guardianSetIndex = encodedVM.toUint32(index);
                        index += 4;
                        // Parse Signatures
                        uint256 signersLen = encodedVM.toUint8(index);
                        index += 1;
                        vm.signatures = new Structs.Signature[](signersLen);
                        for (uint i = 0; i < signersLen; i++) {
                            vm.signatures[i].guardianIndex = encodedVM.toUint8(index);
                            index += 1;
                            vm.signatures[i].r = encodedVM.toBytes32(index);
                            index += 32;
                            vm.signatures[i].s = encodedVM.toBytes32(index);
                            index += 32;
                            vm.signatures[i].v = encodedVM.toUint8(index) + 27;
                            index += 1;
                        }
                        /*
                        Hash the body
                        SECURITY: Do not change the way the hash of a VM is computed! 
                        Changing it could result into two different hashes for the same observation. 
                        But xDapps rely on the hash of an observation for replay protection.
                        */
                        bytes memory body = encodedVM.slice(index, encodedVM.length - index);
                        vm.hash = keccak256(abi.encodePacked(keccak256(body)));
                        // Parse the body
                        vm.timestamp = encodedVM.toUint32(index);
                        index += 4;
                        vm.nonce = encodedVM.toUint32(index);
                        index += 4;
                        vm.emitterChainId = encodedVM.toUint16(index);
                        index += 2;
                        vm.emitterAddress = encodedVM.toBytes32(index);
                        index += 32;
                        vm.sequence = encodedVM.toUint64(index);
                        index += 8;
                        vm.consistencyLevel = encodedVM.toUint8(index);
                        index += 1;
                        vm.payload = encodedVM.slice(index, encodedVM.length - index);
                    }
                    /**
                     * @dev quorum serves solely to determine the number of signatures required to acheive quorum
                     */
                    function quorum(uint numGuardians) public pure virtual returns (uint numSignaturesRequiredForQuorum) {
                        // The max number of guardians is 255
                        require(numGuardians < 256, "too many guardians");
                        return ((numGuardians * 2) / 3) + 1;
                    }
                }
                // contracts/Setters.sol
                // SPDX-License-Identifier: Apache 2
                pragma solidity ^0.8.0;
                import "./State.sol";
                contract Setters is State {
                    function updateGuardianSetIndex(uint32 newIndex) internal {
                        _state.guardianSetIndex = newIndex;
                    }
                    function expireGuardianSet(uint32 index) internal {
                        _state.guardianSets[index].expirationTime = uint32(block.timestamp) + 86400;
                    }
                    function storeGuardianSet(Structs.GuardianSet memory set, uint32 index) internal {
                        _state.guardianSets[index] = set;
                    }
                    function setInitialized(address implementatiom) internal {
                        _state.initializedImplementations[implementatiom] = true;
                    }
                    function setGovernanceActionConsumed(bytes32 hash) internal {
                        _state.consumedGovernanceActions[hash] = true;
                    }
                    function setChainId(uint16 chainId) internal {
                        _state.provider.chainId = chainId;
                    }
                    function setGovernanceChainId(uint16 chainId) internal {
                        _state.provider.governanceChainId = chainId;
                    }
                    function setGovernanceContract(bytes32 governanceContract) internal {
                        _state.provider.governanceContract = governanceContract;
                    }
                    function setMessageFee(uint256 newFee) internal {
                        _state.messageFee = newFee;
                    }
                    function setNextSequence(address emitter, uint64 sequence) internal {
                        _state.sequences[emitter] = sequence;
                    }
                    function setEvmChainId(uint256 evmChainId) internal {
                        require(evmChainId == block.chainid, "invalid evmChainId");
                        _state.evmChainId = evmChainId;
                    }
                }// contracts/State.sol
                // SPDX-License-Identifier: Apache 2
                pragma solidity ^0.8.0;
                import "./Structs.sol";
                contract Events {
                    event LogGuardianSetChanged(
                        uint32 oldGuardianIndex,
                        uint32 newGuardianIndex
                    );
                    event LogMessagePublished(
                        address emitter_address,
                        uint32 nonce,
                        bytes payload
                    );
                }
                contract Storage {
                    struct WormholeState {
                        Structs.Provider provider;
                        // Mapping of guardian_set_index => guardian set
                        mapping(uint32 => Structs.GuardianSet) guardianSets;
                        // Current active guardian set index
                        uint32 guardianSetIndex;
                        // Period for which a guardian set stays active after it has been replaced
                        uint32 guardianSetExpiry;
                        // Sequence numbers per emitter
                        mapping(address => uint64) sequences;
                        // Mapping of consumed governance actions
                        mapping(bytes32 => bool) consumedGovernanceActions;
                        // Mapping of initialized implementations
                        mapping(address => bool) initializedImplementations;
                        uint256 messageFee;
                        // EIP-155 Chain ID
                        uint256 evmChainId;
                    }
                }
                contract State {
                    Storage.WormholeState _state;
                }
                // contracts/Structs.sol
                // SPDX-License-Identifier: Apache 2
                pragma solidity ^0.8.0;
                interface Structs {
                \tstruct Provider {
                \t\tuint16 chainId;
                \t\tuint16 governanceChainId;
                \t\tbytes32 governanceContract;
                \t}
                \tstruct GuardianSet {
                \t\taddress[] keys;
                \t\tuint32 expirationTime;
                \t}
                \tstruct Signature {
                \t\tbytes32 r;
                \t\tbytes32 s;
                \t\tuint8 v;
                \t\tuint8 guardianIndex;
                \t}
                \tstruct VM {
                \t\tuint8 version;
                \t\tuint32 timestamp;
                \t\tuint32 nonce;
                \t\tuint16 emitterChainId;
                \t\tbytes32 emitterAddress;
                \t\tuint64 sequence;
                \t\tuint8 consistencyLevel;
                \t\tbytes payload;
                \t\tuint32 guardianSetIndex;
                \t\tSignature[] signatures;
                \t\tbytes32 hash;
                \t}
                }
                // SPDX-License-Identifier: Unlicense
                /*
                 * @title Solidity Bytes Arrays Utils
                 * @author Gonçalo Sá <[email protected]>
                 *
                 * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
                 *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
                 */
                pragma solidity >=0.8.0 <0.9.0;
                library BytesLib {
                    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 mlengthmod := mod(mlength, 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)
                    {
                        require(_length + 31 >= _length, "slice_overflow");
                        require(_bytes.length >= _start + _length, "slice_outOfBounds");
                        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) {
                        require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
                        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) {
                        require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
                        uint8 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0x1), _start))
                        }
                        return tempUint;
                    }
                    function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
                        require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
                        uint16 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0x2), _start))
                        }
                        return tempUint;
                    }
                    function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
                        require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
                        uint32 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0x4), _start))
                        }
                        return tempUint;
                    }
                    function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
                        require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
                        uint64 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0x8), _start))
                        }
                        return tempUint;
                    }
                    function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
                        require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
                        uint96 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0xc), _start))
                        }
                        return tempUint;
                    }
                    function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
                        require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
                        uint128 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0x10), _start))
                        }
                        return tempUint;
                    }
                    function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
                        require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
                        uint256 tempUint;
                        assembly {
                            tempUint := mload(add(add(_bytes, 0x20), _start))
                        }
                        return tempUint;
                    }
                    function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
                        require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
                        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)
                                        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;
                    }
                }