ETH Price: $1,853.83 (-0.20%)

Transaction Decoder

Block:
24207186 at Jan-10-2026 10:08:23 PM +UTC
Transaction Fee:
0.000195591240680264 ETH $0.36
Gas Used:
188,903 Gas / 1.035405688 Gwei

Emitted Events:

317 RangoDiamond.0xdf4363408b2d9811d1e5c23efdb5bae0b7a68bd9de2de1cbae18a11be3e67ef5( 0xdf4363408b2d9811d1e5c23efdb5bae0b7a68bd9de2de1cbae18a11be3e67ef5, 0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000035ab98ca87109, 000000000000000000000000c84f14c250128357c82e1b737bf19e6efb1111bc )
318 RangoDiamond.0xf14fbd8b6e3ad3ae34babfa1f3b6a099f57643662f4cfc24eb335ae8718f534b( 0xf14fbd8b6e3ad3ae34babfa1f3b6a099f57643662f4cfc24eb335ae8718f534b, 0x000000000000000000000000c84f14c250128357c82e1b737bf19e6efb1111bc, 0x00000000000000000000000000000000000000000000000000000000000004ad, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000035ab98ca87109 )
319 0xc38e4e6a15593f908255214653d3d947ca1c2338.0x918554b6bd6e2895ce6553de5de0e1a69db5289aa0e4fe193a0dcd1f14347477( 0x918554b6bd6e2895ce6553de5de0e1a69db5289aa0e4fe193a0dcd1f14347477, a76ae3314d81a92068fabfb5d122c868b3c6e9a911f6f1e989dfba5997f860e4 )
320 MayanForwarder2.ForwardedEth( mayanProtocol=0xc38e4e6a15593f908255214653d3d947ca1c2338, protocolData=0xB866E173000000000000000000000000DE173A506F3F634B189084AEE0C50165E29AF7E3D6F93836679CA32AE3FB0D473B75FADF4B1F6CE40D3E85F916EB0B1DFAB9589E000000000000000000000000000000000000000000000000000000009DA7267B00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003EAE0000000000000000000000000000000000000000000000000000000000005960000000000000000000000000000000000000000000000000000000006962D2BE15866B54D98D04A2F455DF95F84A868D66FD3231C1D5BF676EC1A36D19FFBE9A000000000000000000000000000000000000000000000000000000000000000164A3EDBB6BE66D153FFFA2DEBA2EEDBB13FAFAE6590BB0605AF5AC53F96AD8DE00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002113EFD8200C7580A66C0BBF51D951689F0DA154C484B05B872FE54DCE47E0F5F )
321 RangoDiamond.0x2fc0d44e6ef6b3e7707cacd3cc326511198c3d1598c65dd54be5a9e37ce02f12( 0x2fc0d44e6ef6b3e7707cacd3cc326511198c3d1598c65dd54be5a9e37ce02f12, 000000000000000000000000337685fdab40d39bd02028545a4ffa7d287cc3e2, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000060, 0000000000000000000000000000000000000000000000000000000000000000 )
322 RangoDiamond.0x0e9201911743fd4d03e146f00ad23945dc8f3ffc200906eff25179a52b726f17( 0x0e9201911743fd4d03e146f00ad23945dc8f3ffc200906eff25179a52b726f17, 0x0000000000000000000000000000000060ce4b55815a422fade0e589be404ec3, 0x00000000000000000000000000000000000000000000000000000000000004ad, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000004fabb145d64652a948d72533023f6e7a623c7c53, 00000000000000000000000000000000000000000000000001dbd83ce5a51400, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000de173a506f3f634b189084aee0c50165e29af7e3, 00000000000000000000000000000000000000000000000000000000000000e0, 000000000000000000000000000000000000000000000000000000000000000b, 547275737457616c6c6574000000000000000000000000000000000000000000 )
323 RangoDiamond.0xdf4363408b2d9811d1e5c23efdb5bae0b7a68bd9de2de1cbae18a11be3e67ef5( 0xdf4363408b2d9811d1e5c23efdb5bae0b7a68bd9de2de1cbae18a11be3e67ef5, 0000000000000000000000004fabb145d64652a948d72533023f6e7a623c7c53, 0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000de173a506f3f634b189084aee0c50165e29af7e3 )
324 AdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000069460570c93f9de5e2edbc3052bf10125f0ca22d, 0x000000000000000000000000de173a506f3f634b189084aee0c50165e29af7e3, 0000000000000000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
13.020224175009749029 Eth13.020413078009749029 Eth0.000188903
0xC38e4e6A...7CA1c2338
(Mayan: Swift)
55.227310375781985139 Eth55.361248745781985139 Eth0.13393837
0xc84f14C2...Efb1111bc 3.515549265811442593 Eth3.516493443716873386 Eth0.000944177905430793
0xDE173a50...5e29af7E3
0.136961366411104771 Eth
Nonce: 2
0.001883227264993714 Eth
Nonce: 3
0.135078139146111057

Execution Trace

ETH 0.134882547905430793 RangoDiamond.14d08fca( )
  • ETH 0.134882547905430793 RangoSwapperFacet.onChainSwaps( request=[{name:requestId, type:address, order:1, indexed:false, value:0x0000000060CE4b55815a422faDe0E589BE404Ec3, valueString:0x0000000060CE4b55815a422faDe0E589BE404Ec3}, {name:fromToken, type:address, order:2, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:toToken, type:address, order:3, indexed:false, value:0x4Fabb145d64652a948d72533023f6E7A623C7C53, valueString:0x4Fabb145d64652a948d72533023f6E7A623C7C53}, {name:amountIn, type:uint256, order:4, indexed:false, value:133938370000000000, valueString:133938370000000000}, {name:platformFee, type:uint256, order:5, indexed:false, value:0, valueString:0}, {name:destinationExecutorFee, type:uint256, order:6, indexed:false, value:0, valueString:0}, {name:affiliateFee, type:uint256, order:7, indexed:false, value:944177905430793, valueString:944177905430793}, {name:affiliatorAddress, type:address, order:8, indexed:false, value:0xc84f14C250128357C82E1b737Bf19e6Efb1111bc, valueString:0xc84f14C250128357C82E1b737Bf19e6Efb1111bc}, {name:minimumAmountExpected, type:uint256, order:9, indexed:false, value:0, valueString:0}, {name:feeFromInputToken, type:bool, order:10, indexed:false, value:true, valueString:True}, {name:dAppTag, type:uint16, order:11, indexed:false, value:1197, valueString:1197}, {name:dAppName, type:string, order:12, indexed:false, value:TrustWallet, valueString:TrustWallet}], calls=, receiver=0xDE173a506f3F634b189084aee0c50165e29af7E3 ) => ( [] )
    • AdminUpgradeabilityProxy.70a08231( )
      • BUSDImplementation.balanceOf( _addr=0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d ) => ( 0 )
      • ETH 0.000944177905430793 0xc84f14c250128357c82e1b737bf19e6efb1111bc.CALL( )
      • ETH 0.13393837 MayanForwarder2.forwardEth( mayanProtocol=0xC38e4e6A15593f908255214653d3D947CA1c2338, protocolData=0xB866E173000000000000000000000000DE173A506F3F634B189084AEE0C50165E29AF7E3D6F93836679CA32AE3FB0D473B75FADF4B1F6CE40D3E85F916EB0B1DFAB9589E000000000000000000000000000000000000000000000000000000009DA7267B00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003EAE0000000000000000000000000000000000000000000000000000000000005960000000000000000000000000000000000000000000000000000000006962D2BE15866B54D98D04A2F455DF95F84A868D66FD3231C1D5BF676EC1A36D19FFBE9A000000000000000000000000000000000000000000000000000000000000000164A3EDBB6BE66D153FFFA2DEBA2EEDBB13FAFAE6590BB0605AF5AC53F96AD8DE00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002113EFD8200C7580A66C0BBF51D951689F0DA154C484B05B872FE54DCE47E0F5F )
        • ETH 0.13393837 Mayan: Swift.b866e173( )
          • 0xf93191d350117723dbeda5484a3b0996d285cecf.02fb1eec( )
          • Wormhole.STATICCALL( )
            • 0x3c3d457f1522d3540ab3325aa5f1864e34cba9d0.DELEGATECALL( )
            • Wormhole.STATICCALL( )
              • 0x3c3d457f1522d3540ab3325aa5f1864e34cba9d0.DELEGATECALL( )
              • AdminUpgradeabilityProxy.70a08231( )
                • BUSDImplementation.balanceOf( _addr=0x69460570c93f9DE5E2edbC3052bf10125f0Ca22d ) => ( 0 )
                • AdminUpgradeabilityProxy.a9059cbb( )
                  • BUSDImplementation.transfer( _to=0xDE173a506f3F634b189084aee0c50165e29af7E3, _value=0 ) => ( True )
                    onChainSwaps[RangoSwapperFacet (ln:462)]
                    File 1 of 6: RangoDiamond
                    // SPDX-License-Identifier: MIT
                    pragma solidity 0.8.16;
                    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.16;
                    import { IDiamondCut } from "../interfaces/IDiamondCut.sol";
                    /// Implementation of EIP-2535 Diamond Standard
                    /// https://eips.ethereum.org/EIPS/eip-2535
                    library LibDiamond {
                        /// @dev keccak256("diamond.standard.diamond.storage");
                        bytes32 internal constant DIAMOND_STORAGE_POSITION = hex"c8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c";
                        // Diamond specific errors
                        error IncorrectFacetCutAction();
                        error NoSelectorsInFacet();
                        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 {
                            require(msg.sender == diamondStorage().contractOwner, "LibDiamond: Must be contract owner");
                        }
                        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 (_facetAddress == address(0)) {
                                revert FacetAddressIsZero();
                            }
                            if (_functionSelectors.length == 0) {
                                revert NoSelectorsInFacet();
                            }
                            DiamondStorage storage ds = diamondStorage();
                            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 != address(0)) {
                                    revert FunctionAlreadyExists();
                                }
                                addFunction(ds, selector, selectorPosition, _facetAddress);
                                unchecked {
                                    ++selectorPosition;
                                    ++selectorIndex;
                                }
                            }
                        }
                        function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
                            if (_functionSelectors.length == 0) {
                                revert NoSelectorsInFacet();
                            }
                            if (_facetAddress == address(0)) {
                                revert FacetAddressIsZero();
                            }
                            DiamondStorage storage ds = diamondStorage();
                            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 NoSelectorsInFacet();
                            }
                            DiamondStorage storage ds = diamondStorage();
                            // if function does not exist then do nothing and return
                            if (_facetAddress != address(0)) {
                                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 (_facetAddress == address(0)) {
                                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 (_init == address(0)) {
                                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: LGPL-3.0-only
                    pragma solidity 0.8.16;
                    import { LibDiamond } from "../libraries/LibDiamond.sol";
                    import { IDiamondCut } from "../interfaces/IDiamondCut.sol";
                    contract RangoDiamond {
                        constructor(address _contractOwner, address _diamondCutFacet) payable {
                            LibDiamond.setContractOwner(_contractOwner);
                            // Add the diamondCut external function from the diamondCutFacet
                            IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1);
                            bytes4[] memory functionSelectors = new bytes4[](1);
                            functionSelectors[0] = IDiamondCut.diamondCut.selector;
                            cut[0] = IDiamondCut.FacetCut({
                                facetAddress: _diamondCutFacet,
                                action: IDiamondCut.FacetCutAction.Add,
                                functionSelectors: functionSelectors
                            });
                            LibDiamond.diamondCut(cut, address(0), "");
                        }
                        // Find facet for function that is called and execute the
                        // function if a facet is found and return any value.
                        // solhint-disable-next-line no-complex-fallback
                        fallback() external payable {
                            LibDiamond.DiamondStorage storage ds;
                            bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION;
                            // get diamond storage
                            // solhint-disable-next-line no-inline-assembly
                            assembly {
                                ds.slot := position
                            }
                            // get facet from function selector
                            address facet = ds.selectorToFacetAndPosition[msg.sig].facetAddress;
                            if (facet == address(0)) {
                                revert LibDiamond.FunctionDoesNotExist();
                            }
                            // Execute external function from facet using delegatecall and return any value.
                            // solhint-disable-next-line no-inline-assembly
                            assembly {
                                // copy function selector and any arguments
                                calldatacopy(0, 0, calldatasize())
                                // execute function call using the facet
                                let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
                                // get any return value
                                returndatacopy(0, 0, returndatasize())
                                // return any return value or error back to the caller
                                switch result
                                case 0 {
                                    revert(0, returndatasize())
                                }
                                default {
                                    return(0, returndatasize())
                                }
                            }
                        }
                        // Able to receive ether
                        // solhint-disable-next-line no-empty-blocks
                        receive() external payable {}
                    }

                    File 2 of 6: MayanForwarder2
                    // 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á <goncalo.sa@consensys.net>
                     *
                     * @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 MayanForwarder2 {
                    \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, ) = mayanProtocol.call{value: msg.value}(protocolData);
                    \t\trequire(success, "mayan protocol call failed");
                    \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, ) = mayanProtocol.call{value: msg.value}(protocolData);
                    \t\trequire(success, "mayan protocol call failed");
                    \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, ) = swapProtocol.call{value: amountIn}(swapData);
                    \t\trequire(success, "swap call failed");
                    \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, ) = mayanProtocol.call{value: msg.value - amountIn}(modifiedData);
                    \t\trequire(success, "mayan protocol call failed");
                    \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\tuint256 amountBefore = IERC20(tokenIn).balanceOf(address(this));
                    \t\tpullTokenIn(tokenIn, amountIn, permitParams);
                    \t\tuint256 middleAmount;
                    \t\tif (middleToken != address(0)) {
                    \t\t\tmiddleAmount = IERC20(middleToken).balanceOf(address(this));
                    \t\t} else {
                    \t\t\tmiddleAmount = address(this).balance;
                    \t\t}
                    \t\tmaxApproveIfNeeded(tokenIn, swapProtocol, amountIn);
                    \t\t(bool success, ) = swapProtocol.call{value: 0}(swapData);
                    \t\trequire(success, "swap protocol call failed");
                    \t\tif (middleToken != address(0)) {
                    \t\t\tmiddleAmount = IERC20(middleToken).balanceOf(address(this)) - middleAmount;
                    \t\t} else {
                    \t\t\tmiddleAmount = address(this).balance - middleAmount;
                    \t\t}
                    \t\trequire(middleAmount >= minMiddleAmount, "insufficient middle token");
                    \t\tuint256 val = msg.value;
                    \t\tif (middleToken == address(0)) {
                    \t\t\tval += middleAmount;
                    \t\t} else {
                    \t\t\tmaxApproveIfNeeded(middleToken, mayanProtocol, middleAmount);
                    \t\t}
                    \t\t(success, ) = mayanProtocol.call{value: val}(middleToken == address(0) ? mayanData : replaceMiddleAmount(mayanData, middleAmount));
                    \t\trequire(success, "mayan protocol call failed");
                    \t\ttransferBackRemaining(tokenIn, amountBefore);
                    \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 amountBefore) internal {
                    \t\tuint256 remaining = IERC20(token).balanceOf(address(this));
                    \t\tif (remaining > amountBefore) {
                    \t\t\tIERC20(token).safeTransfer(msg.sender, remaining - amountBefore);
                    \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\t(bool success, ) = payable(to).call{value: amount}('');
                    \t\trequire(success, 'payment failed');
                    \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}
                    \treceive() external payable {}
                    }

                    File 3 of 6: AdminUpgradeabilityProxy
                    // File: contracts/zeppelin/Proxy.sol
                    
                    pragma solidity 0.4.24;
                    
                    /**
                     * @title Proxy
                     * @dev Implements delegation of calls to other contracts, with proper
                     * forwarding of return values and bubbling of failures.
                     * It defines a fallback function that delegates all calls to the address
                     * returned by the abstract _implementation() internal function.
                     */
                    contract Proxy {
                        /**
                         * @dev Fallback function.
                         * Implemented entirely in `_fallback`.
                         */
                        function () payable external {
                            _fallback();
                        }
                    
                        /**
                         * @return The Address of the implementation.
                         */
                        function _implementation() internal view returns (address);
                    
                        /**
                         * @dev Delegates execution to an implementation contract.
                         * This is a low level function that doesn't return to its internal call site.
                         * It will return to the external caller whatever the implementation returns.
                         * @param implementation Address to delegate.
                         */
                        function _delegate(address implementation) internal {
                            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 Function that is run as the first thing in the fallback function.
                         * Can be redefined in derived contracts to add functionality.
                         * Redefinitions must call super._willFallback().
                         */
                        function _willFallback() internal {
                        }
                    
                        /**
                         * @dev fallback implementation.
                         * Extracted to enable manual triggering.
                         */
                        function _fallback() internal {
                            _willFallback();
                            _delegate(_implementation());
                        }
                    }
                    
                    // File: contracts/zeppelin/AddressUtils.sol
                    
                    pragma solidity 0.4.24;
                    
                    
                    /**
                     * Utility library of inline functions on addresses
                     */
                    library AddressUtils {
                    
                        /**
                         * Returns whether the target address is a contract
                         * @dev This function will return false if invoked during the constructor of a contract,
                         * as the code is not actually created until after the constructor finishes.
                         * @param addr address to check
                         * @return whether the target address is a contract
                         */
                        function isContract(address addr) internal view returns (bool) {
                            uint256 size;
                            // XXX Currently there is no better way to check if there is a contract in an address
                            // than to check the size of the code at that address.
                            // See https://ethereum.stackexchange.com/a/14016/36603
                            // for more details about how this works.
                            // TODO Check this again before the Serenity release, because all addresses will be
                            // contracts then.
                            // solium-disable-next-line security/no-inline-assembly
                            assembly { size := extcodesize(addr) }
                            return size > 0;
                        }
                    
                    }
                    
                    // File: contracts/zeppelin/UpgradeabilityProxy.sol
                    
                    pragma solidity 0.4.24;
                    
                    
                    
                    /**
                     * @title UpgradeabilityProxy
                     * @dev This contract implements a proxy that allows to change the
                     * implementation address to which it will delegate.
                     * Such a change is called an implementation upgrade.
                     */
                    contract UpgradeabilityProxy is Proxy {
                        /**
                         * @dev Emitted when the implementation is upgraded.
                         * @param implementation Address of the new implementation.
                         */
                        event Upgraded(address implementation);
                    
                        /**
                         * @dev Storage slot with the address of the current implementation.
                         * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is
                         * validated in the constructor.
                         */
                        bytes32 private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3;
                    
                        /**
                         * @dev Contract constructor.
                         * @param _implementation Address of the initial implementation.
                         */
                        constructor(address _implementation) public {
                            assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation"));
                    
                            _setImplementation(_implementation);
                        }
                    
                        /**
                         * @dev Returns the current implementation.
                         * @return Address of the current implementation
                         */
                        function _implementation() internal view returns (address impl) {
                            bytes32 slot = IMPLEMENTATION_SLOT;
                            assembly {
                                impl := sload(slot)
                            }
                        }
                    
                        /**
                         * @dev Upgrades the proxy to a new implementation.
                         * @param newImplementation Address of the new implementation.
                         */
                        function _upgradeTo(address newImplementation) internal {
                            _setImplementation(newImplementation);
                            emit Upgraded(newImplementation);
                        }
                    
                        /**
                         * @dev Sets the implementation address of the proxy.
                         * @param newImplementation Address of the new implementation.
                         */
                        function _setImplementation(address newImplementation) private {
                            require(AddressUtils.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
                    
                            bytes32 slot = IMPLEMENTATION_SLOT;
                    
                            assembly {
                                sstore(slot, newImplementation)
                            }
                        }
                    }
                    
                    // File: contracts/zeppelin/AdminUpgradeabilityProxy.sol
                    
                    pragma solidity 0.4.24;
                    
                    
                    /**
                     * @title AdminUpgradeabilityProxy
                     * @dev This contract combines an upgradeability proxy with an authorization
                     * mechanism for administrative tasks.
                     * All external functions in this contract must be guarded by the
                     * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
                     * feature proposal that would enable this to be done automatically.
                     */
                    contract AdminUpgradeabilityProxy is UpgradeabilityProxy {
                        /**
                         * @dev Emitted when the administration has been transferred.
                         * @param previousAdmin Address of the previous admin.
                         * @param newAdmin Address of the new admin.
                         */
                        event AdminChanged(address previousAdmin, address newAdmin);
                    
                        /**
                         * @dev Storage slot with the admin of the contract.
                         * This is the keccak-256 hash of "org.zeppelinos.proxy.admin", and is
                         * validated in the constructor.
                         */
                        bytes32 private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b;
                    
                        /**
                         * @dev Modifier to check whether the `msg.sender` is the admin.
                         * If it is, it will run the function. Otherwise, it will delegate the call
                         * to the implementation.
                         */
                        modifier ifAdmin() {
                            if (msg.sender == _admin()) {
                                _;
                            } else {
                                _fallback();
                            }
                        }
                    
                        /**
                         * Contract constructor.
                         * It sets the `msg.sender` as the proxy administrator.
                         * @param _implementation address of the initial implementation.
                         */
                        constructor(address _implementation) UpgradeabilityProxy(_implementation) public {
                            assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin"));
                    
                            _setAdmin(msg.sender);
                        }
                    
                        /**
                         * @return The address of the proxy admin.
                         */
                        function admin() external view ifAdmin returns (address) {
                            return _admin();
                        }
                    
                        /**
                         * @return The address of the implementation.
                         */
                        function implementation() external view ifAdmin returns (address) {
                            return _implementation();
                        }
                    
                        /**
                         * @dev Changes the admin of the proxy.
                         * Only the current admin can call this function.
                         * @param newAdmin Address to transfer proxy administration to.
                         */
                        function changeAdmin(address newAdmin) external ifAdmin {
                            require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
                            emit AdminChanged(_admin(), newAdmin);
                            _setAdmin(newAdmin);
                        }
                    
                        /**
                         * @dev Upgrade the backing implementation of the proxy.
                         * Only the admin can call this function.
                         * @param newImplementation Address of the new implementation.
                         */
                        function upgradeTo(address newImplementation) external ifAdmin {
                            _upgradeTo(newImplementation);
                        }
                    
                        /**
                         * @dev Upgrade the backing implementation of the proxy and call a function
                         * on the new implementation.
                         * This is useful to initialize the proxied contract.
                         * @param newImplementation Address of the new implementation.
                         * @param data Data to send as msg.data in the low level call.
                         * It should include the signature and the parameters of the function to be
                         * called, as described in
                         * https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding.
                         */
                        function upgradeToAndCall(address newImplementation, bytes data) payable external ifAdmin {
                            _upgradeTo(newImplementation);
                            require(address(this).call.value(msg.value)(data));
                        }
                    
                        /**
                         * @return The admin slot.
                         */
                        function _admin() internal view returns (address adm) {
                            bytes32 slot = ADMIN_SLOT;
                            assembly {
                                adm := sload(slot)
                            }
                        }
                    
                        /**
                         * @dev Sets the address of the proxy admin.
                         * @param newAdmin Address of the new proxy admin.
                         */
                        function _setAdmin(address newAdmin) internal {
                            bytes32 slot = ADMIN_SLOT;
                    
                            assembly {
                                sstore(slot, newAdmin)
                            }
                        }
                    
                        /**
                         * @dev Only fall back when the sender is not the admin.
                         */
                        function _willFallback() internal {
                            require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
                            super._willFallback();
                        }
                    }

                    File 4 of 6: RangoSwapperFacet
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
                    pragma solidity ^0.8.20;
                    /**
                     * @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.
                     *
                     * ==== Security Considerations
                     *
                     * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
                     * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
                     * considered as an intention to spend the allowance in any specific way. The second is that because permits have
                     * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
                     * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
                     * generally recommended is:
                     *
                     * ```solidity
                     * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
                     *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
                     *     doThing(..., value);
                     * }
                     *
                     * function doThing(..., uint256 value) public {
                     *     token.safeTransferFrom(msg.sender, address(this), value);
                     *     ...
                     * }
                     * ```
                     *
                     * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
                     * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
                     * {SafeERC20-safeTransferFrom}).
                     *
                     * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
                     * contracts should have entry points that don't rely on permit.
                     */
                    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].
                         *
                         * CAUTION: See Security Considerations above.
                         */
                        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 v5.0.0) (token/ERC20/IERC20.sol)
                    pragma solidity ^0.8.20;
                    /**
                     * @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 value of tokens in existence.
                         */
                        function totalSupply() external view returns (uint256);
                        /**
                         * @dev Returns the value of tokens owned by `account`.
                         */
                        function balanceOf(address account) external view returns (uint256);
                        /**
                         * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
                        /**
                         * @dev Moves a `value` amount of tokens from `from` to `to` using the
                         * allowance mechanism. `value` 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 value) external returns (bool);
                    }
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
                    pragma solidity ^0.8.20;
                    import {IERC20} from "../IERC20.sol";
                    import {IERC20Permit} from "../extensions/IERC20Permit.sol";
                    import {Address} from "../../../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 An operation with an ERC20 token failed.
                         */
                        error SafeERC20FailedOperation(address token);
                        /**
                         * @dev Indicates a failed `decreaseAllowance` request.
                         */
                        error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
                        /**
                         * @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 {
                            bytes memory returndata = address(token).functionCall(abi.encodeCall(token.transfer, (to, value)));
                            if (address(token)!=0xa614f803B6FD780986A42c78Ec9c7f77e6DeD13C && returndata.length != 0 && !abi.decode(returndata, (bool))) {
                                revert SafeERC20FailedOperation(address(token));
                            }
                            // _callOptionalReturn(token, abi.encodeCall(token.transfer, (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.encodeCall(token.transferFrom, (from, to, 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);
                            forceApprove(token, spender, oldAllowance + value);
                        }
                        /**
                         * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
                         * value, non-reverting calls are assumed to be successful.
                         */
                        function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
                            unchecked {
                                uint256 currentAllowance = token.allowance(address(this), spender);
                                if (currentAllowance < requestedDecrease) {
                                    revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
                                }
                                forceApprove(token, spender, currentAllowance - requestedDecrease);
                            }
                        }
                        /**
                         * @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.encodeCall(token.approve, (spender, value));
                            if (!_callOptionalReturnBool(token, approvalCall)) {
                                _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
                                _callOptionalReturn(token, approvalCall);
                            }
                        }
                        /**
                         * @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);
                            if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
                                revert SafeERC20FailedOperation(address(token));
                            }
                        }
                        /**
                         * @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(token).code.length > 0;
                        }
                    }
                    // SPDX-License-Identifier: MIT
                    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
                    pragma solidity ^0.8.20;
                    /**
                     * @dev Collection of functions related to the address type
                     */
                    library Address {
                        /**
                         * @dev The ETH balance of the account is not enough to perform the operation.
                         */
                        error AddressInsufficientBalance(address account);
                        /**
                         * @dev There's no code at `target` (it is not a contract).
                         */
                        error AddressEmptyCode(address target);
                        /**
                         * @dev A call to an address target failed. The target may have reverted.
                         */
                        error FailedInnerCall();
                        /**
                         * @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                         */
                        function sendValue(address payable recipient, uint256 amount) internal {
                            if (address(this).balance < amount) {
                                revert AddressInsufficientBalance(address(this));
                            }
                            (bool success, ) = recipient.call{value: amount}("");
                            if (!success) {
                                revert FailedInnerCall();
                            }
                        }
                        /**
                         * @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 or custom error, it is bubbled
                         * up by this function (like regular Solidity function calls). However, if
                         * the call reverted with no returned reason, this function reverts with a
                         * {FailedInnerCall} error.
                         *
                         * 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.
                         */
                        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                            return functionCallWithValue(target, data, 0);
                        }
                        /**
                         * @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`.
                         */
                        function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                            if (address(this).balance < value) {
                                revert AddressInsufficientBalance(address(this));
                            }
                            (bool success, bytes memory returndata) = target.call{value: value}(data);
                            return verifyCallResultFromTarget(target, success, returndata);
                        }
                        /**
                         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                         * but performing a static call.
                         */
                        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                            (bool success, bytes memory returndata) = target.staticcall(data);
                            return verifyCallResultFromTarget(target, success, returndata);
                        }
                        /**
                         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                         * but performing a delegate call.
                         */
                        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                            (bool success, bytes memory returndata) = target.delegatecall(data);
                            return verifyCallResultFromTarget(target, success, returndata);
                        }
                        /**
                         * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
                         * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
                         * unsuccessful call.
                         */
                        function verifyCallResultFromTarget(
                            address target,
                            bool success,
                            bytes memory returndata
                        ) internal view returns (bytes memory) {
                            if (!success) {
                                _revert(returndata);
                            } else {
                                // only check if target is a contract if the call was successful and the return data is empty
                                // otherwise we already know that it was a contract
                                if (returndata.length == 0 && target.code.length == 0) {
                                    revert AddressEmptyCode(target);
                                }
                                return returndata;
                            }
                        }
                        /**
                         * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
                         * revert reason or with a default {FailedInnerCall} error.
                         */
                        function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
                            if (!success) {
                                _revert(returndata);
                            } else {
                                return returndata;
                            }
                        }
                        /**
                         * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
                         */
                        function _revert(bytes memory returndata) 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 FailedInnerCall();
                            }
                        }
                    }
                    // SPDX-License-Identifier: LGPL-3.0-only
                    pragma solidity 0.8.25;
                    import "../../libraries/LibDiamond.sol";
                    import "../../libraries/LibSwapper.sol";
                    import "../../utils/ReentrancyGuard.sol";
                    import "../../libraries/LibPausable.sol";
                    contract RangoSwapperFacet is ReentrancyGuard{
                        /// Events ///
                        /// @notice initializes the base swapper and sets the init params
                        /// @param _weth Address of wrapped token (WETH, WBNB, etc.) on the current chain
                        function initBaseSwapper(address _weth, address payable _feeReceiver) public {
                            LibDiamond.enforceIsContractOwner();
                            LibSwapper.setWeth(_weth);    
                            LibSwapper.updateFeeContractAddress(_feeReceiver);           
                        }
                        /// @notice Sets the wallet that receives Rango's fees from now on
                        /// @param _address The receiver wallet address
                        function updateFeeReceiver(address payable _address) external {
                            LibDiamond.enforceIsContractOwner();
                            LibSwapper.updateFeeContractAddress(_address);
                        }
                        /// @notice Transfers an ERC20 token from this contract to msg.sender
                        /// @dev This endpoint is to return money to a user if we didn't handle failure correctly and the money is still in the contract
                        /// @dev Currently the money goes to admin and they should manually transfer it to a wallet later
                        /// @param _tokenAddress The address of ERC20 token to be transferred
                        /// @param _amount The amount of money that should be transfered
                        function refund(address _tokenAddress, uint256 _amount) external {
                            LibDiamond.enforceIsContractOwner();
                            LibPausable.enforceNotPaused();
                            IERC20 ercToken = IERC20(_tokenAddress);
                            uint balance = ercToken.balanceOf(address(this));
                            require(balance >= _amount, "Insufficient balance");
                            SafeERC20.safeTransfer(ercToken, msg.sender, _amount);
                            emit LibSwapper.Refunded(_tokenAddress, _amount);
                        }
                        /// @notice Transfers the native token from this contract to msg.sender
                        /// @dev This endpoint is to return money to a user if we didn't handle failure correctly and the money is still in the contract
                        /// @dev Currently the money goes to admin and they should manually transfer it to a wallet later
                        /// @param _amount The amount of native token that should be transfered
                        function refundNative(uint256 _amount) external {
                            LibDiamond.enforceIsContractOwner();
                            LibPausable.enforceNotPaused();
                            uint balance = address(this).balance;
                            require(balance >= _amount, "Insufficient balance");
                            LibSwapper._sendToken(LibSwapper.ETH, _amount, msg.sender, false);
                            emit LibSwapper.Refunded(LibSwapper.ETH, _amount);
                        }
                        /// @notice Does a simple on-chain swap
                        /// @param request The general swap request containing from/to token and fee/affiliate rewards
                        /// @param calls The list of DEX calls
                        /// @param receiver The address that should receive the output of swaps.
                        /// @return The byte array result of all DEX calls
                        function onChainSwaps(
                            LibSwapper.SwapRequest memory request,
                            LibSwapper.Call[] calldata calls,
                            address receiver
                        ) external payable nonReentrant returns (bytes[] memory) {
                            LibPausable.enforceNotPaused();
                            require(receiver != LibSwapper.ETH, "receiver cannot be address(0)");
                            (bytes[] memory result, uint outputAmount) = LibSwapper.onChainSwapsInternal(request, calls, 0);
                            LibSwapper.emitSwapEvent(request, outputAmount, receiver);
                            LibSwapper._sendToken(request.toToken, outputAmount, receiver, false);
                            return result;
                        }
                        function isContractWhitelisted(address _contractAddress) external view returns (bool) {
                            LibDiamond.enforceIsContractOwner();
                            LibSwapper.BaseSwapperStorage storage baseSwapperStorage = LibSwapper.getBaseSwapperStorage();
                            return baseSwapperStorage.whitelistContracts[_contractAddress];
                        } 
                    }// SPDX-License-Identifier: MIT
                    pragma solidity 0.8.25;
                    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: LGPL-3.0-only
                    pragma solidity 0.8.25;
                    interface IRango {
                        struct RangoBridgeRequest {
                            address requestId;
                            address token;
                            uint amount;
                            uint platformFee;
                            uint affiliateFee;
                            address payable affiliatorAddress;
                            uint destinationExecutorFee;
                            uint16 dAppTag;
                            string dAppName;
                        }
                        enum BridgeType {
                            Across, 
                            CBridge, 
                            Hop, 
                            Hyphen, 
                            Multichain, 
                            Stargate, 
                            Synapse, 
                            Thorchain, 
                            Symbiosis, 
                            Axelar, 
                            Voyager, 
                            Poly, 
                            OptimismBridge, 
                            ArbitrumBridge, 
                            Wormhole, 
                            AllBridge, 
                            CCTP, 
                            Connext, 
                            NitroAssetForwarder, 
                            DeBridge, 
                            YBridge, 
                            Swft, 
                            Orbiter,
                            ChainFlip
                        }
                        /// @notice Status of cross-chain swap
                        /// @param Succeeded The whole process is success and end-user received the desired token in the destination
                        /// @param RefundInSource Bridge was out of liquidity and middle asset (ex: USDC) is returned to user on source chain
                        /// @param RefundInDestination Our handler on dest chain this.executeMessageWithTransfer failed and we send middle asset (ex: USDC) to user on destination chain
                        /// @param SwapFailedInDestination Everything was ok, but the final DEX on destination failed (ex: Market price change and slippage)
                        enum CrossChainOperationStatus {
                            Succeeded,
                            RefundInSource,
                            RefundInDestination,
                            SwapFailedInDestination
                        }
                        event RangoBridgeInitiated(
                            address indexed requestId,
                            address bridgeToken,
                            uint256 bridgeAmount,
                            address receiver,
                            uint destinationChainId,
                            bool hasInterchainMessage,
                            bool hasDestinationSwap,
                            uint8 indexed bridgeId,
                            uint16 indexed dAppTag,
                            string dAppName
                        );
                        event RangoBridgeCompleted(
                            address indexed requestId,
                            address indexed token,
                            address indexed originalSender,
                            address receiver,
                            uint amount,
                            CrossChainOperationStatus status,
                            uint16 dAppTag
                        );
                    }// SPDX-License-Identifier: GPL-3.0-only
                    pragma solidity 0.8.25;
                    interface IWETH {
                        function deposit() external payable;
                        function withdraw(uint256) external;
                    }
                    // SPDX-License-Identifier: MIT
                    pragma solidity 0.8.25;
                    import { IDiamondCut } from "../interfaces/IDiamondCut.sol";
                    /// Implementation of EIP-2535 Diamond Standard
                    /// https://eips.ethereum.org/EIPS/eip-2535
                    library LibDiamond {
                        /// Storage ///
                        bytes32 internal constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage");
                        // Diamond specific errors
                        error IncorrectFacetCutAction();
                        error NoSelectorsInFacet();
                        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 {
                            require(msg.sender == diamondStorage().contractOwner, "LibDiamond: Must be contract owner");
                        }
                        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 (_facetAddress == address(0)) {
                                revert FacetAddressIsZero();
                            }
                            if (_functionSelectors.length == 0) {
                                revert NoSelectorsInFacet();
                            }
                            DiamondStorage storage ds = diamondStorage();
                            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 != address(0)) {
                                    revert FunctionAlreadyExists();
                                }
                                addFunction(ds, selector, selectorPosition, _facetAddress);
                                unchecked {
                                    ++selectorPosition;
                                    ++selectorIndex;
                                }
                            }
                        }
                        function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
                            if (_functionSelectors.length == 0) {
                                revert NoSelectorsInFacet();
                            }
                            if (_facetAddress == address(0)) {
                                revert FacetAddressIsZero();
                            }
                            DiamondStorage storage ds = diamondStorage();
                            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 NoSelectorsInFacet();
                            }
                            DiamondStorage storage ds = diamondStorage();
                            // if function does not exist then do nothing and return
                            if (_facetAddress != address(0)) {
                                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 (_facetAddress == address(0)) {
                                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 (_init == address(0)) {
                                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: LGPL-3.0-only
                    pragma solidity 0.8.25;
                    /// @title Pausable Library
                    /// @author 0xiDen
                    /// @notice This library provides pausable feature across entire diamond protected methods. Be advised only methods that call `enforceNotPaused` will be protected!
                    library LibPausable {
                        /// Storage ///
                        bytes32 private constant NAMESPACE = keccak256("exchange.rango.library.pausable");
                        /// Types ///
                        struct PausableStorage {
                            bool isPaused;
                        }
                        /// Events ///
                        /// @notice Notifies that Rango's paused state is updated
                        /// @param _oldPausedState The previous paused state
                        /// @param _newPausedState The new fee wallet address
                        event PausedStateUpdated(bool _oldPausedState, bool _newPausedState);
                        /// Errors ///
                        /// Constants ///
                        /// Modifiers ///
                        /// Internal Methods ///
                        /// @notice Sets the isPaused state for Rango
                        /// @param _paused The receiver wallet address
                        function updatePauseState(bool _paused) internal {
                            PausableStorage storage pausableStorage = getPausableStorage();
                            bool oldState = pausableStorage.isPaused;
                            pausableStorage.isPaused = _paused;
                            emit PausedStateUpdated(oldState, _paused);
                        }
                        function enforceNotPaused() internal view {
                            PausableStorage storage pausableStorage = getPausableStorage();
                            require(pausableStorage.isPaused == false, "Paused");
                        }
                        /// Private Methods ///
                        /// @dev fetch local storage
                        function getPausableStorage() private pure returns (PausableStorage storage data) {
                            bytes32 position = NAMESPACE;
                            // solhint-disable-next-line no-inline-assembly
                            assembly {
                                data.slot := position
                            }
                        }
                    }// SPDX-License-Identifier: LGPL-3.0-only
                    pragma solidity 0.8.25;
                    import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
                    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
                    import "../interfaces/IWETH.sol";
                    import "../interfaces/IRango.sol";
                    /// @title BaseSwapper
                    /// @author 0xiden
                    /// @notice library to provide swap functionality
                    library LibSwapper {
                        bytes32 internal constant BASE_SWAPPER_NAMESPACE = keccak256("exchange.rango.library.swapper");
                        address payable constant ETH = payable(0x0000000000000000000000000000000000000000);
                        struct BaseSwapperStorage {
                            address payable feeContractAddress;
                            address WETH;
                            mapping(address => bool) whitelistContracts;
                            mapping(address => mapping(bytes4 => bool)) whitelistMethods;
                        }
                        /// @notice Emitted if any fee transfer was required
                        /// @param token The address of received token, address(0) for native
                        /// @param affiliatorAddress The address of affiliate wallet
                        /// @param platformFee The amount received as platform fee
                        /// @param destinationExecutorFee The amount received to execute transaction on destination (only for cross chain txs)
                        /// @param affiliateFee The amount received by affiliate
                        /// @param dAppTag Optional identifier to make tracking easier.
                        event FeeInfo(
                            address token,
                            address indexed affiliatorAddress,
                            uint platformFee,
                            uint destinationExecutorFee,
                            uint affiliateFee,
                            uint16 indexed dAppTag
                        );
                        /// @notice A call to another dex or contract done and here is the result
                        /// @param target The address of dex or contract that is called
                        /// @param success A boolean indicating that the call was success or not
                        /// @param returnData The response of function call
                        event CallResult(address target, bool success, bytes returnData);
                        /// @notice A swap request is done and we also emit the output
                        /// @param requestId Optional parameter to make tracking of transaction easier
                        /// @param fromToken Input token address to be swapped from
                        /// @param toToken Output token address to be swapped to
                        /// @param amountIn Input amount of fromToken that is being swapped
                        /// @param dAppTag Optional identifier to make tracking easier
                        /// @param outputAmount The output amount of the swap, measured by the balance change before and after the swap
                        /// @param receiver The address to receive the output of swap. Can be address(0) when swap is before a bridge action
                        /// @param dAppName The human readable name of the dApp
                        event RangoSwap(
                            address indexed requestId,
                            address fromToken,
                            address toToken,
                            uint amountIn,
                            uint minimumAmountExpected,
                            uint16 indexed dAppTag,
                            uint outputAmount,
                            address receiver,
                            string dAppName
                        );
                        /// @notice Output amount of a dex calls is logged
                        /// @param _token The address of output token, ZERO address for native
                        /// @param amount The amount of output
                        event DexOutput(address _token, uint amount);
                        /// @notice The output money (ERC20/Native) is sent to a wallet
                        /// @param _token The token that is sent to a wallet, ZERO address for native
                        /// @param _amount The sent amount
                        /// @param _receiver The receiver wallet address
                        event SendToken(address _token, uint256 _amount, address _receiver);
                        /// @notice Notifies that Rango's fee receiver address updated
                        /// @param _oldAddress The previous fee wallet address
                        /// @param _newAddress The new fee wallet address
                        event FeeContractAddressUpdated(address _oldAddress, address _newAddress);
                        /// @notice Notifies that WETH address is updated
                        /// @param _oldAddress The previous weth address
                        /// @param _newAddress The new weth address
                        event WethContractAddressUpdated(address _oldAddress, address _newAddress);
                        /// @notice Notifies that admin manually refunded some money
                        /// @param _token The address of refunded token, 0x000..00 address for native token
                        /// @param _amount The amount that is refunded
                        event Refunded(address _token, uint _amount);
                        /// @notice The requested call data which is computed off-chain and passed to the contract
                        /// @dev swapFromToken and amount parameters are only helper params and the actual amount and
                        /// token are set in callData
                        /// @param spender The contract which the approval is given to if swapFromToken is not native.
                        /// @param target The dex contract address that should be called
                        /// @param swapFromToken Token address of to be used in the swap.
                        /// @param amount The amount to be approved or native amount sent.
                        /// @param callData The required data field that should be give to the dex contract to perform swap
                        struct Call {
                            address spender;
                            address payable target;
                            address swapFromToken;
                            address swapToToken;
                            bool needsTransferFromUser;
                            uint amount;
                            bytes callData;
                        }
                        /// @notice General swap request which is given to us in all relevant functions
                        /// @param requestId The request id passed to make tracking transactions easier
                        /// @param fromToken The source token that is going to be swapped (in case of simple swap or swap + bridge) or the briding token (in case of solo bridge)
                        /// @param toToken The output token of swapping. This is the output of DEX step and is also input of bridging step
                        /// @param amountIn The amount of input token to be swapped
                        /// @param platformFee The amount of fee charged by platform
                        /// @param destinationExecutorFee The amount of fee required for relayer execution on the destination
                        /// @param affiliateFee The amount of fee charged by affiliator dApp
                        /// @param affiliatorAddress The wallet address that the affiliator fee should be sent to
                        /// @param minimumAmountExpected The minimum amount of toToken expected after executing Calls
                        /// @param feeFromInputToken If set to true, the fees will be taken from input token and otherwise, from output token. (platformFee,destinationExecutorFee,affiliateFee)
                        /// @param dAppTag An optional parameter
                        /// @param dAppName The Name of the dApp
                        struct SwapRequest {
                            address requestId;
                            address fromToken;
                            address toToken;
                            uint amountIn;
                            uint platformFee;
                            uint destinationExecutorFee;
                            uint affiliateFee;
                            address payable affiliatorAddress;
                            uint minimumAmountExpected;
                            bool feeFromInputToken;
                            uint16 dAppTag;
                            string dAppName;
                        }
                        /// @notice initializes the base swapper and sets the init params (such as Wrapped token address)
                        /// @param _weth Address of wrapped token (WETH, WBNB, etc.) on the current chain
                        function setWeth(address _weth) internal {
                            BaseSwapperStorage storage baseStorage = getBaseSwapperStorage();
                            address oldAddress = baseStorage.WETH;
                            baseStorage.WETH = _weth;
                            require(_weth != address(0), "Invalid WETH!");
                            emit WethContractAddressUpdated(oldAddress, _weth);
                        }
                        /// @notice Sets the wallet that receives Rango's fees from now on
                        /// @param _address The receiver wallet address
                        function updateFeeContractAddress(address payable _address) internal {
                            BaseSwapperStorage storage baseSwapperStorage = getBaseSwapperStorage();
                            address oldAddress = baseSwapperStorage.feeContractAddress;
                            baseSwapperStorage.feeContractAddress = _address;
                            emit FeeContractAddressUpdated(oldAddress, _address);
                        }
                        /// Whitelist ///
                        /// @notice Adds a contract to the whitelisted DEXes that can be called
                        /// @param contractAddress The address of the DEX
                        function addWhitelist(address contractAddress) internal {
                            BaseSwapperStorage storage baseStorage = getBaseSwapperStorage();
                            baseStorage.whitelistContracts[contractAddress] = true;
                        }
                        /// @notice Adds a method of contract to the whitelisted DEXes that can be called
                        /// @param contractAddress The address of the DEX
                        /// @param methodIds The method of the DEX
                        function addMethodWhitelists(address contractAddress, bytes4[] calldata methodIds) internal {
                            BaseSwapperStorage storage baseStorage = getBaseSwapperStorage();
                            baseStorage.whitelistContracts[contractAddress] = true;
                            for (uint i = 0; i < methodIds.length; i++)
                                baseStorage.whitelistMethods[contractAddress][methodIds[i]] = true;
                        }
                        /// @notice Adds a method of contract to the whitelisted DEXes that can be called
                        /// @param contractAddress The address of the DEX
                        /// @param methodId The method of the DEX
                        function addMethodWhitelist(address contractAddress, bytes4 methodId) internal {
                            BaseSwapperStorage storage baseStorage = getBaseSwapperStorage();
                            baseStorage.whitelistContracts[contractAddress] = true;
                            baseStorage.whitelistMethods[contractAddress][methodId] = true;
                        }
                        /// @notice Removes a contract from the whitelisted DEXes
                        /// @param contractAddress The address of the DEX or dApp
                        function removeWhitelist(address contractAddress) internal {
                            BaseSwapperStorage storage baseStorage = getBaseSwapperStorage();
                            delete baseStorage.whitelistContracts[contractAddress];
                        }
                        /// @notice Removes a method of contract from the whitelisted DEXes
                        /// @param contractAddress The address of the DEX or dApp
                        /// @param methodId The method of the DEX
                        function removeMethodWhitelist(address contractAddress, bytes4 methodId) internal {
                            BaseSwapperStorage storage baseStorage = getBaseSwapperStorage();
                            delete baseStorage.whitelistMethods[contractAddress][methodId];
                        }
                        function onChainSwapsPreBridge(
                            SwapRequest memory request,
                            Call[] calldata calls,
                            uint extraFee
                        ) internal returns (uint out) {
                            uint minimumRequiredValue = getPreBridgeMinAmount(request) + extraFee;
                            require(msg.value >= minimumRequiredValue, 'Send more ETH to cover input amount + fee');
                            (, out) = onChainSwapsInternal(request, calls, extraFee);
                            // when there is a bridge after swap, set the receiver in swap event to address(0)
                            emitSwapEvent(request, out, ETH);
                            return out;
                        }
                        /// @notice Internal function to compute output amount of DEXes
                        /// @param request The general swap request containing from/to token and fee/affiliate rewards
                        /// @param calls The list of DEX calls
                        /// @param extraNativeFee The amount of native tokens to keep and not return to user as excess amount.
                        /// @return The response of all DEX calls and the output amount of the whole process
                        function onChainSwapsInternal(
                            SwapRequest memory request,
                            Call[] calldata calls,
                            uint256 extraNativeFee
                        ) internal returns (bytes[] memory, uint) {
                            uint toBalanceBefore = getBalanceOf(request.toToken);
                            uint fromBalanceBefore = getBalanceOf(request.fromToken);
                            uint256[] memory initialBalancesList = getInitialBalancesList(calls);
                            // transfer tokens from user for SwapRequest and Calls that require transfer from user.
                            transferTokensFromUserForSwapRequest(request);
                            transferTokensFromUserForCalls(calls);
                            bytes[] memory result = callSwapsAndFees(request, calls);
                            // check if any extra tokens were taken from contract and return excess tokens if any.
                            returnExcessAmounts(request, calls, initialBalancesList);
                            // get balance after returning excesses.
                            uint fromBalanceAfter = getBalanceOf(request.fromToken);
                            // check over-expense of fromToken and return excess if any.
                            if (request.fromToken != ETH) {
                                require(fromBalanceAfter >= fromBalanceBefore, "Source token balance on contract must not decrease after swap");
                                if (fromBalanceAfter > fromBalanceBefore)
                                    _sendToken(request.fromToken, fromBalanceAfter - fromBalanceBefore, msg.sender);
                            }
                            else {
                                require(fromBalanceAfter >= fromBalanceBefore - msg.value + extraNativeFee, "Source token balance on contract must not decrease after swap");
                                // When we are keeping extraNativeFee for bridgingFee, we should consider it in calculations.
                                if (fromBalanceAfter > fromBalanceBefore - msg.value + extraNativeFee)
                                    _sendToken(request.fromToken, fromBalanceAfter + msg.value - fromBalanceBefore - extraNativeFee, msg.sender);
                            }
                            uint toBalanceAfter = getBalanceOf(request.toToken);
                            uint secondaryBalance = toBalanceAfter - toBalanceBefore;
                            require(secondaryBalance >= request.minimumAmountExpected, "Output is less than minimum expected");
                            return (result, secondaryBalance);
                        }
                        /// @notice Private function to handle fetching money from wallet to contract, reduce fee/affiliate, perform DEX calls
                        /// @param request The general swap request containing from/to token and fee/affiliate rewards
                        /// @param calls The list of DEX calls
                        /// @dev It checks the whitelisting of all DEX addresses + having enough msg.value as input
                        /// @return The bytes of all DEX calls response
                        function callSwapsAndFees(SwapRequest memory request, Call[] calldata calls) private returns (bytes[] memory) {
                            BaseSwapperStorage storage baseSwapperStorage = getBaseSwapperStorage();
                            for (uint256 i = 0; i < calls.length; i++) {
                                require(baseSwapperStorage.whitelistContracts[calls[i].spender], "Contract spender not whitelisted");
                                require(baseSwapperStorage.whitelistContracts[calls[i].target], "Contract target not whitelisted");
                                bytes4 sig = bytes4(calls[i].callData[: 4]);
                                require(baseSwapperStorage.whitelistMethods[calls[i].target][sig], "Unauthorized call data!");
                            }
                            // Get Fees Before swap
                            collectFeesBeforeSwap(request);
                            // Execute swap Calls
                            bytes[] memory returnData = new bytes[](calls.length);
                            address tmpSwapFromToken;
                            for (uint256 i = 0; i < calls.length; i++) {
                                tmpSwapFromToken = calls[i].swapFromToken;
                                bool isTokenNative = tmpSwapFromToken == ETH;
                                if (isTokenNative == false)
                                    approveMax(tmpSwapFromToken, calls[i].spender, calls[i].amount);
                                (bool success, bytes memory ret) = isTokenNative
                                ? calls[i].target.call{value : calls[i].amount}(calls[i].callData)
                                : calls[i].target.call(calls[i].callData);
                                emit CallResult(calls[i].target, success, ret);
                                if (!success)
                                    revert(_getRevertMsg(ret));
                                returnData[i] = ret;
                            }
                            // Get Fees After swap
                            collectFeesAfterSwap(request);
                            return returnData;
                        }
                        /// @notice Approves an ERC20 token to a contract to transfer from the current contract
                        /// @param token The address of an ERC20 token
                        /// @param spender The contract address that should be approved
                        /// @param value The amount that should be approved
                        function approve(address token, address spender, uint value) internal {
                            SafeERC20.forceApprove(IERC20(token), spender, value);
                        }
                        /// @notice Approves an ERC20 token to a contract to transfer from the current contract, approves for inf value
                        /// @param token The address of an ERC20 token
                        /// @param spender The contract address that should be approved
                        /// @param value The desired allowance. If current allowance is less than this value, infinite allowance will be given
                        function approveMax(address token, address spender, uint value) internal {
                            uint256 currentAllowance = IERC20(token).allowance(address(this), spender);
                            if (currentAllowance < value) {
                                SafeERC20.forceApprove(IERC20(token), spender, type(uint256).max);
                            }
                        }
                        function _sendToken(address _token, uint256 _amount, address _receiver) internal {
                            (_token == ETH) ? _sendNative(_receiver, _amount) : SafeERC20.safeTransfer(IERC20(_token), _receiver, _amount);
                        }
                        function sumFees(IRango.RangoBridgeRequest memory request) internal pure returns (uint256) {
                            return request.platformFee + request.affiliateFee + request.destinationExecutorFee;
                        }
                        function sumFees(SwapRequest memory request) internal pure returns (uint256) {
                            return request.platformFee + request.affiliateFee + request.destinationExecutorFee;
                        }
                         function getPreBridgeMinAmount(SwapRequest memory request) internal pure returns (uint256) {
                            bool isNative = request.fromToken == ETH;
                            if (request.feeFromInputToken) {
                                return (isNative ? request.platformFee + request.affiliateFee + request.amountIn + request.destinationExecutorFee : 0);
                            }
                            return (isNative ? request.amountIn : 0);
                        }
                        function collectFeesForSwap(SwapRequest memory request) internal {
                            BaseSwapperStorage storage baseSwapperStorage = getBaseSwapperStorage();
                            // Get Platform fee
                            bool hasPlatformFee = request.platformFee > 0;
                            bool hasDestExecutorFee = request.destinationExecutorFee > 0;
                            bool hasAffiliateFee = request.affiliateFee > 0;
                            address feeToken = request.feeFromInputToken ? request.fromToken : request.toToken;
                            if (hasPlatformFee || hasDestExecutorFee) {
                                require(baseSwapperStorage.feeContractAddress != ETH, "Fee contract address not set");
                                _sendToken(feeToken, request.platformFee + request.destinationExecutorFee, baseSwapperStorage.feeContractAddress, false);
                            }
                            // Get affiliate fee
                            if (hasAffiliateFee) {
                                require(request.affiliatorAddress != ETH, "Invalid affiliatorAddress");
                                _sendToken(feeToken, request.affiliateFee, request.affiliatorAddress, false);
                            }
                            // emit Fee event
                            if (hasPlatformFee || hasDestExecutorFee || hasAffiliateFee) {
                                emit FeeInfo(
                                    feeToken,
                                    request.affiliatorAddress,
                                    request.platformFee,
                                    request.destinationExecutorFee,
                                    request.affiliateFee,
                                    request.dAppTag
                                );
                            }
                        }
                        function collectFees(IRango.RangoBridgeRequest memory request) internal {
                            // Get Platform fee
                            bool hasPlatformFee = request.platformFee > 0;
                            bool hasDestExecutorFee = request.destinationExecutorFee > 0;
                            bool hasAffiliateFee = request.affiliateFee > 0;
                            bool hasAnyFee = hasPlatformFee || hasDestExecutorFee || hasAffiliateFee;
                            if (!hasAnyFee) {
                                return;
                            }
                            BaseSwapperStorage storage baseSwapperStorage = getBaseSwapperStorage();
                            if (hasPlatformFee || hasDestExecutorFee) {
                                require(baseSwapperStorage.feeContractAddress != ETH, "Fee contract address not set");
                                _sendToken(request.token, request.platformFee + request.destinationExecutorFee, baseSwapperStorage.feeContractAddress, false);
                            }
                            // Get affiliate fee
                            if (hasAffiliateFee) {
                                require(request.affiliatorAddress != ETH, "Invalid affiliatorAddress");
                                _sendToken(request.token, request.affiliateFee, request.affiliatorAddress, false);
                            }
                            // emit Fee event
                            emit FeeInfo(
                                request.token,
                                request.affiliatorAddress,
                                request.platformFee,
                                request.destinationExecutorFee,
                                request.affiliateFee,
                                request.dAppTag
                            );
                        }
                        function collectFeesBeforeSwap(SwapRequest memory request) internal {
                            if (request.feeFromInputToken) {
                                collectFeesForSwap(request);
                            }
                        }
                        function collectFeesAfterSwap(SwapRequest memory request) internal {
                            if (!request.feeFromInputToken) {
                                collectFeesForSwap(request);
                            }
                        }
                        function collectFeesFromSender(IRango.RangoBridgeRequest memory request) internal {
                            // Get Platform fee
                            bool hasPlatformFee = request.platformFee > 0;
                            bool hasDestExecutorFee = request.destinationExecutorFee > 0;
                            bool hasAffiliateFee = request.affiliateFee > 0;
                            bool hasAnyFee = hasPlatformFee || hasDestExecutorFee || hasAffiliateFee;
                            if (!hasAnyFee) {
                                return;
                            }
                            bool isSourceNative = request.token == ETH;
                            BaseSwapperStorage storage baseSwapperStorage = getBaseSwapperStorage();
                            if (hasPlatformFee || hasDestExecutorFee) {
                                require(baseSwapperStorage.feeContractAddress != ETH, "Fee contract address not set");
                                if (isSourceNative)
                                    _sendToken(request.token, request.platformFee + request.destinationExecutorFee, baseSwapperStorage.feeContractAddress, false);
                                else
                                    SafeERC20.safeTransferFrom(
                                        IERC20(request.token),
                                        msg.sender,
                                        baseSwapperStorage.feeContractAddress,
                                        request.platformFee + request.destinationExecutorFee
                                    );
                            }
                            // Get affiliate fee
                            if (hasAffiliateFee) {
                                require(request.affiliatorAddress != ETH, "Invalid affiliatorAddress");
                                if (isSourceNative)
                                    _sendToken(request.token, request.affiliateFee, request.affiliatorAddress, false);
                                else
                                    SafeERC20.safeTransferFrom(
                                        IERC20(request.token),
                                        msg.sender,
                                        request.affiliatorAddress,
                                        request.affiliateFee
                                    );
                            }
                            // emit Fee event
                            emit FeeInfo(
                                request.token,
                                request.affiliatorAddress,
                                request.platformFee,
                                request.destinationExecutorFee,
                                request.affiliateFee,
                                request.dAppTag
                            );
                        }
                        /// @notice An internal function to send a token from the current contract to another contract or wallet
                        /// @dev This function also can convert WETH to ETH before sending if _withdraw flat is set to true
                        /// @dev To send native token _token param should be set to address zero, otherwise we assume it's an ERC20 transfer
                        /// @param _token The token that is going to be sent to a wallet, ZERO address for native
                        /// @param _amount The sent amount
                        /// @param _receiver The receiver wallet address or contract
                        /// @param _withdraw If true, indicates that we should swap WETH to ETH before sending the money and _nativeOut must also be true
                        function _sendToken(
                            address _token,
                            uint256 _amount,
                            address _receiver,
                            bool _withdraw
                        ) internal {
                            BaseSwapperStorage storage baseStorage = getBaseSwapperStorage();
                            emit SendToken(_token, _amount, _receiver);
                            bool nativeOut = _token == LibSwapper.ETH;
                            if (_withdraw) {
                                require(_token == baseStorage.WETH, "token mismatch");
                                IWETH(baseStorage.WETH).withdraw(_amount);
                                nativeOut = true;
                            }
                            if (nativeOut) {
                                _sendNative(_receiver, _amount);
                            } else {
                                SafeERC20.safeTransfer(IERC20(_token), _receiver, _amount);
                            }
                        }
                        /// @notice An internal function to send native token to a contract or wallet
                        /// @param _receiver The address that will receive the native token
                        /// @param _amount The amount of the native token that should be sent
                        function _sendNative(address _receiver, uint _amount) internal {
                            (bool sent,) = _receiver.call{value : _amount}("");
                            require(sent, "failed to send native");
                        }
                        /// @notice A utility function to fetch storage from a predefined random slot using assembly
                        /// @return s The storage object
                        function getBaseSwapperStorage() internal pure returns (BaseSwapperStorage storage s) {
                            bytes32 namespace = BASE_SWAPPER_NAMESPACE;
                            // solhint-disable-next-line no-inline-assembly
                            assembly {
                                s.slot := namespace
                            }
                        }
                        /// @notice To extract revert message from a DEX/contract call to represent to the end-user in the blockchain
                        /// @param _returnData The resulting bytes of a failed call to a DEX or contract
                        /// @return A string that describes what was the error
                        function _getRevertMsg(bytes memory _returnData) internal pure returns (string memory) {
                            // If the _res length is less than 68, then the transaction failed silently (without a revert message)
                            if (_returnData.length < 68) return 'Transaction reverted silently';
                            assembly {
                            // Slice the sighash.
                                _returnData := add(_returnData, 0x04)
                            }
                            return abi.decode(_returnData, (string));
                            // All that remains is the revert string
                        }
                        function getBalanceOf(address token) internal view returns (uint) {
                            return token == ETH ? address(this).balance : IERC20(token).balanceOf(address(this));
                        }
                        /// @notice Fetches the balances of swapToTokens.
                        /// @dev this fetches the balances for swapToToken of swap Calls. If native eth is received, the balance has already increased so we subtract msg.value.
                        function getInitialBalancesList(Call[] calldata calls) internal view returns (uint256[] memory) {
                            uint callsLength = calls.length;
                            uint256[] memory balancesList = new uint256[](callsLength);
                            address token;
                            for (uint256 i = 0; i < callsLength; i++) {
                                token = calls[i].swapToToken;
                                balancesList[i] = getBalanceOf(token);
                                if (token == ETH)
                                    balancesList[i] -= msg.value;
                            }
                            return balancesList;
                        }
                        /// This function transfers tokens from users based on the SwapRequest, it transfers amountIn + fees.
                        function transferTokensFromUserForSwapRequest(SwapRequest memory request) private {
                            uint transferAmount = request.amountIn + (request.feeFromInputToken ? sumFees(request) : 0);
                            if (request.fromToken != ETH)
                                SafeERC20.safeTransferFrom(IERC20(request.fromToken), msg.sender, address(this), transferAmount);
                            else
                                require(msg.value >= transferAmount);
                        }
                        /// This function iterates on calls and if needsTransferFromUser, transfers tokens from user
                        function transferTokensFromUserForCalls(Call[] calldata calls) private {
                            uint callsLength = calls.length;
                            Call calldata call;
                            address token;
                            for (uint256 i = 0; i < callsLength; i++) {
                                call = calls[i];
                                token = call.swapFromToken;
                                if (call.needsTransferFromUser && token != ETH)
                                    SafeERC20.safeTransferFrom(IERC20(call.swapFromToken), msg.sender, address(this), call.amount);
                            }
                        }
                        /// @dev returns any excess token left by the contract.
                        /// We iterate over `swapToToken`s because each swapToToken is either the request.toToken or is the output of
                        /// another `Call` in the list of swaps which itself either has transferred tokens from user,
                        /// or is a middle token that is the output of another `Call`.
                        function returnExcessAmounts(
                            SwapRequest memory request,
                            Call[] calldata calls,
                            uint256[] memory initialBalancesList) internal {
                            uint excessAmountToToken;
                            address tmpSwapToToken;
                            uint currentBalanceTo;
                            for (uint256 i = 0; i < calls.length; i++) {
                                tmpSwapToToken = calls[i].swapToToken;
                                currentBalanceTo = getBalanceOf(tmpSwapToToken);
                                excessAmountToToken = currentBalanceTo - initialBalancesList[i];
                                if (excessAmountToToken > 0 && tmpSwapToToken != request.toToken) {
                                    _sendToken(tmpSwapToToken, excessAmountToToken, msg.sender);
                                }
                            }
                        }
                        function emitSwapEvent(SwapRequest memory request, uint output, address receiver) internal {
                            emit RangoSwap(
                                request.requestId,
                                request.fromToken,
                                request.toToken,
                                request.amountIn,
                                request.minimumAmountExpected,
                                request.dAppTag,
                                output,
                                receiver,
                                request.dAppName
                            );
                        }
                    }
                    // SPDX-License-Identifier: LGPL-3.0-only
                    pragma solidity 0.8.25;
                    /// @title Reentrancy Guard
                    /// @author 
                    /// @notice Abstract contract to provide protection against reentrancy
                    abstract contract ReentrancyGuard {
                        /// Storage ///
                        bytes32 private constant NAMESPACE = keccak256("exchange.rango.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
                            }
                        }
                    }
                    

                    File 5 of 6: BUSDImplementation
                    // File: contracts/zeppelin/SafeMath.sol
                    
                    pragma solidity 0.4.24;
                    
                    
                    /**
                     * @title SafeMath
                     * @dev Math operations with safety checks that throw on error
                     */
                    library SafeMath {
                        /**
                        * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
                        */
                        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b <= a);
                            uint256 c = a - b;
                    
                            return c;
                        }
                    
                        /**
                        * @dev Adds two numbers, reverts on overflow.
                        */
                        function add(uint256 a, uint256 b) internal pure returns (uint256) {
                            uint256 c = a + b;
                            require(c >= a);
                    
                            return c;
                        }
                    }
                    
                    // File: contracts/BUSDImplementation.sol
                    
                    pragma solidity 0.4.24;
                    pragma experimental "v0.5.0";
                    
                    
                    
                    /**
                     * @title BUSDImplementation
                     * @dev this contract is a Pausable ERC20 token with Burn and Mint
                     * controlled by a central SupplyController. By implementing BUSDImplementation
                     * this contract also includes external methods for setting
                     * a new implementation contract for the Proxy.
                     * NOTE: The storage defined here will actually be held in the Proxy
                     * contract and all calls to this contract should be made through
                     * the proxy, including admin actions done as owner or supplyController.
                     * Any call to transfer against this contract should fail
                     * with insufficient funds since no tokens will be issued there.
                     */
                    contract BUSDImplementation {
                    
                        /**
                         * MATH
                         */
                    
                        using SafeMath for uint256;
                    
                        /**
                         * DATA
                         */
                    
                        // INITIALIZATION DATA
                        bool private initialized = false;
                    
                        // ERC20 BASIC DATA
                        mapping(address => uint256) internal balances;
                        uint256 internal totalSupply_;
                        string public constant name = "BUSD"; // solium-disable-line
                        string public constant symbol = "BUSD"; // solium-disable-line uppercase
                        uint8 public constant decimals = 18; // solium-disable-line uppercase
                    
                        // ERC20 DATA
                        mapping(address => mapping(address => uint256)) internal allowed;
                    
                        // OWNER DATA
                        address public owner;
                        address public proposedOwner;
                    
                        // PAUSABILITY DATA
                        bool public paused = false;
                    
                        // ASSET PROTECTION DATA
                        address public assetProtectionRole;
                        mapping(address => bool) internal frozen;
                    
                        // SUPPLY CONTROL DATA
                        address public supplyController;
                    
                        // DELEGATED TRANSFER DATA
                        address public betaDelegateWhitelister;
                        mapping(address => bool) internal betaDelegateWhitelist;
                        mapping(address => uint256) internal nextSeqs;
                        // EIP191 header for EIP712 prefix
                        string constant internal EIP191_HEADER = "\x19\x01";
                        // Hash of the EIP712 Domain Separator Schema
                        bytes32 constant internal EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(
                            "EIP712Domain(string name,address verifyingContract)"
                        );
                        bytes32 constant internal EIP712_DELEGATED_TRANSFER_SCHEMA_HASH = keccak256(
                            "BetaDelegatedTransfer(address to,uint256 value,uint256 fee,uint256 seq,uint256 deadline)"
                        );
                        // Hash of the EIP712 Domain Separator data
                        // solhint-disable-next-line var-name-mixedcase
                        bytes32 public EIP712_DOMAIN_HASH;
                    
                        /**
                         * EVENTS
                         */
                    
                        // ERC20 BASIC EVENTS
                        event Transfer(address indexed from, address indexed to, uint256 value);
                    
                        // ERC20 EVENTS
                        event Approval(
                            address indexed owner,
                            address indexed spender,
                            uint256 value
                        );
                    
                        // OWNABLE EVENTS
                        event OwnershipTransferProposed(
                            address indexed currentOwner,
                            address indexed proposedOwner
                        );
                        event OwnershipTransferDisregarded(
                            address indexed oldProposedOwner
                        );
                        event OwnershipTransferred(
                            address indexed oldOwner,
                            address indexed newOwner
                        );
                    
                        // PAUSABLE EVENTS
                        event Pause();
                        event Unpause();
                    
                        // ASSET PROTECTION EVENTS
                        event AddressFrozen(address indexed addr);
                        event AddressUnfrozen(address indexed addr);
                        event FrozenAddressWiped(address indexed addr);
                        event AssetProtectionRoleSet (
                            address indexed oldAssetProtectionRole,
                            address indexed newAssetProtectionRole
                        );
                    
                        // SUPPLY CONTROL EVENTS
                        event SupplyIncreased(address indexed to, uint256 value);
                        event SupplyDecreased(address indexed from, uint256 value);
                        event SupplyControllerSet(
                            address indexed oldSupplyController,
                            address indexed newSupplyController
                        );
                    
                        // DELEGATED TRANSFER EVENTS
                        event BetaDelegatedTransfer(
                            address indexed from, address indexed to, uint256 value, uint256 seq, uint256 fee
                        );
                        event BetaDelegateWhitelisterSet(
                            address indexed oldWhitelister,
                            address indexed newWhitelister
                        );
                        event BetaDelegateWhitelisted(address indexed newDelegate);
                        event BetaDelegateUnwhitelisted(address indexed oldDelegate);
                    
                        /**
                         * FUNCTIONALITY
                         */
                    
                        // INITIALIZATION FUNCTIONALITY
                    
                        /**
                         * @dev sets 0 initials tokens, the owner, and the supplyController.
                         * this serves as the constructor for the proxy but compiles to the
                         * memory model of the Implementation contract.
                         */
                        function initialize() public {
                            require(!initialized, "already initialized");
                            owner = msg.sender;
                            proposedOwner = address(0);
                            assetProtectionRole = address(0);
                            totalSupply_ = 0;
                            supplyController = msg.sender;
                            initialized = true;
                        }
                    
                        /**
                         * The constructor is used here to ensure that the implementation
                         * contract is initialized. An uncontrolled implementation
                         * contract might lead to misleading state
                         * for users who accidentally interact with it.
                         */
                        constructor() public {
                            initialize();
                            pause();
                            // Added in V2
                            initializeDomainSeparator();
                        }
                    
                        /**
                         * @dev To be called when upgrading the contract using upgradeAndCall to add delegated transfers
                         */
                        function initializeDomainSeparator() public {
                            // hash the name context with the contract address
                            EIP712_DOMAIN_HASH = keccak256(abi.encodePacked(// solium-disable-line
                                    EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
                                    keccak256(bytes(name)),
                                    bytes32(address(this))
                                ));
                        }
                    
                        // ERC20 BASIC FUNCTIONALITY
                    
                        /**
                        * @dev Total number of tokens in existence
                        */
                        function totalSupply() public view returns (uint256) {
                            return totalSupply_;
                        }
                    
                        /**
                        * @dev Transfer token to a specified address from msg.sender
                        * Note: the use of Safemath ensures that _value is nonnegative.
                        * @param _to The address to transfer to.
                        * @param _value The amount to be transferred.
                        */
                        function transfer(address _to, uint256 _value) public whenNotPaused returns (bool) {
                            require(_to != address(0), "cannot transfer to address zero");
                            require(!frozen[_to] && !frozen[msg.sender], "address frozen");
                            require(_value <= balances[msg.sender], "insufficient funds");
                    
                            balances[msg.sender] = balances[msg.sender].sub(_value);
                            balances[_to] = balances[_to].add(_value);
                            emit Transfer(msg.sender, _to, _value);
                            return true;
                        }
                    
                        /**
                        * @dev Gets the balance of the specified address.
                        * @param _addr The address to query the the balance of.
                        * @return An uint256 representing the amount owned by the passed address.
                        */
                        function balanceOf(address _addr) public view returns (uint256) {
                            return balances[_addr];
                        }
                    
                        // ERC20 FUNCTIONALITY
                    
                        /**
                         * @dev Transfer tokens from one address to another
                         * @param _from address The address which you want to send tokens from
                         * @param _to address The address which you want to transfer to
                         * @param _value uint256 the amount of tokens to be transferred
                         */
                        function transferFrom(
                            address _from,
                            address _to,
                            uint256 _value
                        )
                        public
                        whenNotPaused
                        returns (bool)
                        {
                            require(_to != address(0), "cannot transfer to address zero");
                            require(!frozen[_to] && !frozen[_from] && !frozen[msg.sender], "address frozen");
                            require(_value <= balances[_from], "insufficient funds");
                            require(_value <= allowed[_from][msg.sender], "insufficient allowance");
                    
                            balances[_from] = balances[_from].sub(_value);
                            balances[_to] = balances[_to].add(_value);
                            allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
                            emit Transfer(_from, _to, _value);
                            return true;
                        }
                    
                        /**
                         * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
                         * 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
                         * @param _spender The address which will spend the funds.
                         * @param _value The amount of tokens to be spent.
                         */
                        function approve(address _spender, uint256 _value) public whenNotPaused returns (bool) {
                            require(!frozen[_spender] && !frozen[msg.sender], "address frozen");
                            allowed[msg.sender][_spender] = _value;
                            emit Approval(msg.sender, _spender, _value);
                            return true;
                        }
                    
                        /**
                         * @dev Function to check the amount of tokens that an owner allowed to a spender.
                         * @param _owner address The address which owns the funds.
                         * @param _spender address The address which will spend the funds.
                         * @return A uint256 specifying the amount of tokens still available for the spender.
                         */
                        function allowance(
                            address _owner,
                            address _spender
                        )
                        public
                        view
                        returns (uint256)
                        {
                            return allowed[_owner][_spender];
                        }
                    
                        // OWNER FUNCTIONALITY
                    
                        /**
                         * @dev Throws if called by any account other than the owner.
                         */
                        modifier onlyOwner() {
                            require(msg.sender == owner, "onlyOwner");
                            _;
                        }
                    
                        /**
                         * @dev Allows the current owner to begin transferring control of the contract to a proposedOwner
                         * @param _proposedOwner The address to transfer ownership to.
                         */
                        function proposeOwner(address _proposedOwner) public onlyOwner {
                            require(_proposedOwner != address(0), "cannot transfer ownership to address zero");
                            require(msg.sender != _proposedOwner, "caller already is owner");
                            proposedOwner = _proposedOwner;
                            emit OwnershipTransferProposed(owner, proposedOwner);
                        }
                    
                        /**
                         * @dev Allows the current owner or proposed owner to cancel transferring control of the contract to a proposedOwner
                         */
                        function disregardProposeOwner() public {
                            require(msg.sender == proposedOwner || msg.sender == owner, "only proposedOwner or owner");
                            require(proposedOwner != address(0), "can only disregard a proposed owner that was previously set");
                            address _oldProposedOwner = proposedOwner;
                            proposedOwner = address(0);
                            emit OwnershipTransferDisregarded(_oldProposedOwner);
                        }
                    
                        /**
                         * @dev Allows the proposed owner to complete transferring control of the contract to the proposedOwner.
                         */
                        function claimOwnership() public {
                            require(msg.sender == proposedOwner, "onlyProposedOwner");
                            address _oldOwner = owner;
                            owner = proposedOwner;
                            proposedOwner = address(0);
                            emit OwnershipTransferred(_oldOwner, owner);
                        }
                    
                        /**
                         * @dev Reclaim all BUSD at the contract address.
                         * This sends the BUSD tokens that this contract add holding to the owner.
                         * Note: this is not affected by freeze constraints.
                         */
                        function reclaimBUSD() external onlyOwner {
                            uint256 _balance = balances[this];
                            balances[this] = 0;
                            balances[owner] = balances[owner].add(_balance);
                            emit Transfer(this, owner, _balance);
                        }
                    
                        // PAUSABILITY FUNCTIONALITY
                    
                        /**
                         * @dev Modifier to make a function callable only when the contract is not paused.
                         */
                        modifier whenNotPaused() {
                            require(!paused, "whenNotPaused");
                            _;
                        }
                    
                        /**
                         * @dev called by the owner to pause, triggers stopped state
                         */
                        function pause() public onlyOwner {
                            require(!paused, "already paused");
                            paused = true;
                            emit Pause();
                        }
                    
                        /**
                         * @dev called by the owner to unpause, returns to normal state
                         */
                        function unpause() public onlyOwner {
                            require(paused, "already unpaused");
                            paused = false;
                            emit Unpause();
                        }
                    
                        // ASSET PROTECTION FUNCTIONALITY
                    
                        /**
                         * @dev Sets a new asset Protection role address.
                         * @param _newAssetProtectionRole The new address allowed to freeze/unfreeze addresses and seize their tokens.
                         */
                        function setAssetProtectionRole(address _newAssetProtectionRole) public {
                            require(msg.sender == assetProtectionRole || msg.sender == owner, "only assetProtectionRole or Owner");
                            emit AssetProtectionRoleSet(assetProtectionRole, _newAssetProtectionRole);
                            assetProtectionRole = _newAssetProtectionRole;
                        }
                    
                        modifier onlyAssetProtectionRole() {
                            require(msg.sender == assetProtectionRole, "onlyAssetProtectionRole");
                            _;
                        }
                    
                        /**
                         * @dev Freezes an address balance from being transferred.
                         * @param _addr The new address to freeze.
                         */
                        function freeze(address _addr) public onlyAssetProtectionRole {
                            require(!frozen[_addr], "address already frozen");
                            frozen[_addr] = true;
                            emit AddressFrozen(_addr);
                        }
                    
                        /**
                         * @dev Unfreezes an address balance allowing transfer.
                         * @param _addr The new address to unfreeze.
                         */
                        function unfreeze(address _addr) public onlyAssetProtectionRole {
                            require(frozen[_addr], "address already unfrozen");
                            frozen[_addr] = false;
                            emit AddressUnfrozen(_addr);
                        }
                    
                        /**
                         * @dev Wipes the balance of a frozen address, burning the tokens
                         * and setting the approval to zero.
                         * @param _addr The new frozen address to wipe.
                         */
                        function wipeFrozenAddress(address _addr) public onlyAssetProtectionRole {
                            require(frozen[_addr], "address is not frozen");
                            uint256 _balance = balances[_addr];
                            balances[_addr] = 0;
                            totalSupply_ = totalSupply_.sub(_balance);
                            emit FrozenAddressWiped(_addr);
                            emit SupplyDecreased(_addr, _balance);
                            emit Transfer(_addr, address(0), _balance);
                        }
                    
                        /**
                        * @dev Gets whether the address is currently frozen.
                        * @param _addr The address to check if frozen.
                        * @return A bool representing whether the given address is frozen.
                        */
                        function isFrozen(address _addr) public view returns (bool) {
                            return frozen[_addr];
                        }
                    
                        // SUPPLY CONTROL FUNCTIONALITY
                    
                        /**
                         * @dev Sets a new supply controller address.
                         * @param _newSupplyController The address allowed to burn/mint tokens to control supply.
                         */
                        function setSupplyController(address _newSupplyController) public {
                            require(msg.sender == supplyController || msg.sender == owner, "only SupplyController or Owner");
                            require(_newSupplyController != address(0), "cannot set supply controller to address zero");
                            emit SupplyControllerSet(supplyController, _newSupplyController);
                            supplyController = _newSupplyController;
                        }
                    
                        modifier onlySupplyController() {
                            require(msg.sender == supplyController, "onlySupplyController");
                            _;
                        }
                    
                        /**
                         * @dev Increases the total supply by minting the specified number of tokens to the supply controller account.
                         * @param _value The number of tokens to add.
                         * @return A boolean that indicates if the operation was successful.
                         */
                        function increaseSupply(uint256 _value) public onlySupplyController returns (bool success) {
                            totalSupply_ = totalSupply_.add(_value);
                            balances[supplyController] = balances[supplyController].add(_value);
                            emit SupplyIncreased(supplyController, _value);
                            emit Transfer(address(0), supplyController, _value);
                            return true;
                        }
                    
                        /**
                         * @dev Decreases the total supply by burning the specified number of tokens from the supply controller account.
                         * @param _value The number of tokens to remove.
                         * @return A boolean that indicates if the operation was successful.
                         */
                        function decreaseSupply(uint256 _value) public onlySupplyController returns (bool success) {
                            require(_value <= balances[supplyController], "not enough supply");
                            balances[supplyController] = balances[supplyController].sub(_value);
                            totalSupply_ = totalSupply_.sub(_value);
                            emit SupplyDecreased(supplyController, _value);
                            emit Transfer(supplyController, address(0), _value);
                            return true;
                        }
                    
                        // DELEGATED TRANSFER FUNCTIONALITY
                    
                        /**
                         * @dev returns the next seq for a target address.
                         * The transactor must submit nextSeqOf(transactor) in the next transaction for it to be valid.
                         * Note: that the seq context is specific to this smart contract.
                         * @param target The target address.
                         * @return the seq.
                         */
                        //
                        function nextSeqOf(address target) public view returns (uint256) {
                            return nextSeqs[target];
                        }
                    
                        /**
                         * @dev Performs a transfer on behalf of the from address, identified by its signature on the delegatedTransfer msg.
                         * Splits a signature byte array into r,s,v for convenience.
                         * @param sig the signature of the delgatedTransfer msg.
                         * @param to The address to transfer to.
                         * @param value The amount to be transferred.
                         * @param fee an optional ERC20 fee paid to the executor of betaDelegatedTransfer by the from address.
                         * @param seq a sequencing number included by the from address specific to this contract to protect from replays.
                         * @param deadline a block number after which the pre-signed transaction has expired.
                         * @return A boolean that indicates if the operation was successful.
                         */
                        function betaDelegatedTransfer(
                            bytes sig, address to, uint256 value, uint256 fee, uint256 seq, uint256 deadline
                        ) public returns (bool) {
                            require(sig.length == 65, "signature should have length 65");
                            bytes32 r;
                            bytes32 s;
                            uint8 v;
                            assembly {
                                r := mload(add(sig, 32))
                                s := mload(add(sig, 64))
                                v := byte(0, mload(add(sig, 96)))
                            }
                            require(_betaDelegatedTransfer(r, s, v, to, value, fee, seq, deadline), "failed transfer");
                            return true;
                        }
                    
                        /**
                         * @dev Performs a transfer on behalf of the from address, identified by its signature on the betaDelegatedTransfer msg.
                         * Note: both the delegate and transactor sign in the fees. The transactor, however,
                         * has no control over the gas price, and therefore no control over the transaction time.
                         * Beta prefix chosen to avoid a name clash with an emerging standard in ERC865 or elsewhere.
                         * Internal to the contract - see betaDelegatedTransfer and betaDelegatedTransferBatch.
                         * @param r the r signature of the delgatedTransfer msg.
                         * @param s the s signature of the delgatedTransfer msg.
                         * @param v the v signature of the delgatedTransfer msg.
                         * @param to The address to transfer to.
                         * @param value The amount to be transferred.
                         * @param fee an optional ERC20 fee paid to the delegate of betaDelegatedTransfer by the from address.
                         * @param seq a sequencing number included by the from address specific to this contract to protect from replays.
                         * @param deadline a block number after which the pre-signed transaction has expired.
                         * @return A boolean that indicates if the operation was successful.
                         */
                        function _betaDelegatedTransfer(
                            bytes32 r, bytes32 s, uint8 v, address to, uint256 value, uint256 fee, uint256 seq, uint256 deadline
                        ) internal whenNotPaused returns (bool) {
                            require(betaDelegateWhitelist[msg.sender], "Beta feature only accepts whitelisted delegates");
                            require(value > 0 || fee > 0, "cannot transfer zero tokens with zero fee");
                            require(block.number <= deadline, "transaction expired");
                            // prevent sig malleability from ecrecover()
                            require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "signature incorrect");
                            require(v == 27 || v == 28, "signature incorrect");
                    
                            // EIP712 scheme: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md
                            bytes32 delegatedTransferHash = keccak256(abi.encodePacked(// solium-disable-line
                                    EIP712_DELEGATED_TRANSFER_SCHEMA_HASH, bytes32(to), value, fee, seq, deadline
                                ));
                            bytes32 hash = keccak256(abi.encodePacked(EIP191_HEADER, EIP712_DOMAIN_HASH, delegatedTransferHash));
                            address _from = ecrecover(hash, v, r, s);
                    
                            require(_from != address(0), "error determining from address from signature");
                            require(to != address(0), "canno use address zero");
                            require(!frozen[to] && !frozen[_from] && !frozen[msg.sender], "address frozen");
                            require(value.add(fee) <= balances[_from], "insufficent fund");
                            require(nextSeqs[_from] == seq, "incorrect seq");
                    
                            nextSeqs[_from] = nextSeqs[_from].add(1);
                            balances[_from] = balances[_from].sub(value.add(fee));
                    
                            if (fee != 0) {
                                balances[msg.sender] = balances[msg.sender].add(fee);
                                emit Transfer(_from, msg.sender, fee);
                            }
                    
                            balances[to] = balances[to].add(value);
                            emit Transfer(_from, to, value);
                    
                            emit BetaDelegatedTransfer(_from, to, value, seq, fee);
                            return true;
                        }
                    
                        /**
                         * @dev Performs an atomic batch of transfers on behalf of the from addresses, identified by their signatures.
                         * Lack of nested array support in arguments requires all arguments to be passed as equal size arrays where
                         * delegated transfer number i is the combination of all arguments at index i
                         * @param r the r signatures of the delgatedTransfer msg.
                         * @param s the s signatures of the delgatedTransfer msg.
                         * @param v the v signatures of the delgatedTransfer msg.
                         * @param to The addresses to transfer to.
                         * @param value The amounts to be transferred.
                         * @param fee optional ERC20 fees paid to the delegate of betaDelegatedTransfer by the from address.
                         * @param seq sequencing numbers included by the from address specific to this contract to protect from replays.
                         * @param deadline block numbers after which the pre-signed transactions have expired.
                         * @return A boolean that indicates if the operation was successful.
                         */
                        function betaDelegatedTransferBatch(
                            bytes32[] r, bytes32[] s, uint8[] v, address[] to, uint256[] value, uint256[] fee, uint256[] seq, uint256[] deadline
                        ) public returns (bool) {
                            require(r.length == s.length && r.length == v.length && r.length == to.length && r.length == value.length, "length mismatch");
                            require(r.length == fee.length && r.length == seq.length && r.length == deadline.length, "length mismatch");
                    
                            for (uint i = 0; i < r.length; i++) {
                                require(
                                    _betaDelegatedTransfer(r[i], s[i], v[i], to[i], value[i], fee[i], seq[i], deadline[i]),
                                    "failed transfer"
                                );
                            }
                            return true;
                        }
                    
                        /**
                        * @dev Gets whether the address is currently whitelisted for betaDelegateTransfer.
                        * @param _addr The address to check if whitelisted.
                        * @return A bool representing whether the given address is whitelisted.
                        */
                        function isWhitelistedBetaDelegate(address _addr) public view returns (bool) {
                            return betaDelegateWhitelist[_addr];
                        }
                    
                        /**
                         * @dev Sets a new betaDelegate whitelister.
                         * @param _newWhitelister The address allowed to whitelist betaDelegates.
                         */
                        function setBetaDelegateWhitelister(address _newWhitelister) public {
                            require(msg.sender == betaDelegateWhitelister || msg.sender == owner, "only Whitelister or Owner");
                            betaDelegateWhitelister = _newWhitelister;
                            emit BetaDelegateWhitelisterSet(betaDelegateWhitelister, _newWhitelister);
                        }
                    
                        modifier onlyBetaDelegateWhitelister() {
                            require(msg.sender == betaDelegateWhitelister, "onlyBetaDelegateWhitelister");
                            _;
                        }
                    
                        /**
                         * @dev Whitelists an address to allow calling BetaDelegatedTransfer.
                         * @param _addr The new address to whitelist.
                         */
                        function whitelistBetaDelegate(address _addr) public onlyBetaDelegateWhitelister {
                            require(!betaDelegateWhitelist[_addr], "delegate already whitelisted");
                            betaDelegateWhitelist[_addr] = true;
                            emit BetaDelegateWhitelisted(_addr);
                        }
                    
                        /**
                         * @dev Unwhitelists an address to disallow calling BetaDelegatedTransfer.
                         * @param _addr The new address to whitelist.
                         */
                        function unwhitelistBetaDelegate(address _addr) public onlyBetaDelegateWhitelister {
                            require(betaDelegateWhitelist[_addr], "delegate not whitelisted");
                            betaDelegateWhitelist[_addr] = false;
                            emit BetaDelegateUnwhitelisted(_addr);
                        }
                    }

                    File 6 of 6: 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
                            }
                        }
                    }