ETH Price: $2,023.77 (+4.15%)

Transaction Decoder

Block:
19767496 at Apr-30-2024 10:21:23 AM +UTC
Transaction Fee:
0.002740843329192144 ETH $5.55
Gas Used:
240,966 Gas / 11.374398584 Gwei

Emitted Events:

234 TetherToken.Transfer( from=[Sender] 0x4b135fff5a67ee6ca34a1d406ce19e3cb5726d38, to=0x74de5d4FCbf63E00296fd95d33236B9794016631, value=510000000 )
235 TetherToken.Transfer( from=0x74de5d4FCbf63E00296fd95d33236B9794016631, to=UniswapV2Pair, value=505537500 )
236 VOWToken.Sent( operator=UniswapV2Pair, from=UniswapV2Pair, to=0x74de5d4FCbf63E00296fd95d33236B9794016631, amount=1018315832198344777420, data=0x, operatorData=0x )
237 VOWToken.Transfer( from=UniswapV2Pair, to=0x74de5d4FCbf63E00296fd95d33236B9794016631, value=1018315832198344777420 )
238 UniswapV2Pair.Sync( reserve0=1274651220819548255498869, reserve1=631401001615 )
239 UniswapV2Pair.Swap( sender=0xa9c0cded336699547aac4f9de5a11ada979bc59a, amount0In=0, amount1In=505537500, amount0Out=1018315832198344777420, amount1Out=0, to=0x74de5d4FCbf63E00296fd95d33236B9794016631 )
240 OpenOceanExchangeProxy.0x76af224a143865a50b41496e1a73622698692c565c1214bc862f18e22d829c5e( 0x76af224a143865a50b41496e1a73622698692c565c1214bc862f18e22d829c5e, 0x00000000000000000000000074de5d4fcbf63e00296fd95d33236b9794016631, 0x000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7, 0x0000000000000000000000001bbf25e71ec48b84d773809b4ba55b6f4be946fb, 00000000000000000000000074de5d4fcbf63e00296fd95d33236b9794016631, 000000000000000000000000000000000000000000000000000000001e21e3dc, 000000000000000000000000000000000000000000000000000000001e21e3dc, 00000000000000000000000000000000000000000000003733f8961daaa09ecc, 0000000000000000000000000000000000000000000000365e95a3a1c936ab23, 0000000000000000000000000000000000000000000000377aa3245bf1e4313d, 000000000000000000000000ef53a4bd0e16ccc9116770a41c4bd3ad1147bd4f )
241 TetherToken.Transfer( from=0x74de5d4FCbf63E00296fd95d33236B9794016631, to=GnosisSafeProxy, value=4462500 )
242 VOWToken.Sent( operator=0x74de5d4FCbf63E00296fd95d33236B9794016631, from=0x74de5d4FCbf63E00296fd95d33236B9794016631, to=[Sender] 0x4b135fff5a67ee6ca34a1d406ce19e3cb5726d38, amount=1018315832198344777420, data=0x, operatorData=0x )
243 VOWToken.Transfer( from=0x74de5d4FCbf63E00296fd95d33236B9794016631, to=[Sender] 0x4b135fff5a67ee6ca34a1d406ce19e3cb5726d38, value=1018315832198344777420 )
244 0x881d40237659c251811cec9c364ef91dc08d300c.0xbeee1e6e7fe307ddcf84b0a16137a4430ad5e2480fc4f4a8e250ab56ccd7630d( 0xbeee1e6e7fe307ddcf84b0a16137a4430ad5e2480fc4f4a8e250ab56ccd7630d, 0xcbf46790f422d91c0bc3f78784cac5d123211479f745ef4d632dfd71fd4d53f3, 0x0000000000000000000000004b135fff5a67ee6ca34a1d406ce19e3cb5726d38 )

Account State Difference:

  Address   Before After State Difference Code
0x1BBf25e7...F4bE946Fb
0x1E497687...6a5707db2
0x4B135fFF...cb5726D38
0.02497546373792375 Eth
Nonce: 7
0.022234620408731606 Eth
Nonce: 8
0.002740843329192144
(beaverbuild)
19.432966846664835307 Eth19.432970466313194469 Eth0.000003619648359162
0xdAC17F95...13D831ec7

Execution Trace

Metamask: Swap Router.5f575529( )
  • TetherToken.transferFrom( _from=0x4B135fFF5a67eE6ca34A1d406ce19e3cb5726D38, _to=0x74de5d4FCbf63E00296fd95d33236B9794016631, _value=510000000 )
  • MetaMask: Swaps Spender.e3547335( )
    • 0x03fc94d98d2a3fd1034dbab9bac65dba00e65add.92f5f037( )
      • TetherToken.allowance( _owner=0x74de5d4FCbf63E00296fd95d33236B9794016631, _spender=0x6352a56caadC4F1E25CD6c75970Fa768A3304e64 ) => ( remaining=115792089237316195423570985008687907853269984665640564039457584007913129639935 )
      • OpenOceanExchangeProxy.90411a32( )
        • OpenOceanExchange.swap( caller=0xa9C0cdEd336699547aaC4f9De5A11Ada979BC59a, desc=[{name:srcToken, type:address, order:1, indexed:false, value:0xdAC17F958D2ee523a2206206994597C13D831ec7, valueString:0xdAC17F958D2ee523a2206206994597C13D831ec7}, {name:dstToken, type:address, order:2, indexed:false, value:0x1BBf25e71EC48B84d773809B4bA55B6F4bE946Fb, valueString:0x1BBf25e71EC48B84d773809B4bA55B6F4bE946Fb}, {name:srcReceiver, type:address, order:3, indexed:false, value:0x1E49768714E438E789047f48FD386686a5707db2, valueString:0x1E49768714E438E789047f48FD386686a5707db2}, {name:dstReceiver, type:address, order:4, indexed:false, value:0x74de5d4FCbf63E00296fd95d33236B9794016631, valueString:0x74de5d4FCbf63E00296fd95d33236B9794016631}, {name:amount, type:uint256, order:5, indexed:false, value:505537500, valueString:505537500}, {name:minReturnAmount, type:uint256, order:6, indexed:false, value:1002939713506671766307, valueString:1002939713506671766307}, {name:guaranteedAmount, type:uint256, order:7, indexed:false, value:1023407870925175271741, valueString:1023407870925175271741}, {name:flags, type:uint256, order:8, indexed:false, value:2, valueString:2}, {name:referrer, type:address, order:9, indexed:false, value:0xEf53A4Bd0E16cCC9116770A41C4bD3aD1147BD4f, valueString:0xEf53A4Bd0E16cCC9116770A41C4bD3aD1147BD4f}, {name:permit, type:bytes, order:10, indexed:false, value:0x, valueString:0x}], calls= ) => ( returnAmount=1018315832198344777420 )
          • TetherToken.transferFrom( _from=0x74de5d4FCbf63E00296fd95d33236B9794016631, _to=0x1E49768714E438E789047f48FD386686a5707db2, _value=505537500 )
          • VOWToken.balanceOf( _holder=0x74de5d4FCbf63E00296fd95d33236B9794016631 ) => ( balance_=0 )
          • 0xa9c0cded336699547aac4f9de5a11ada979bc59a.a8920d2b( )
            • 0xa9c0cded336699547aac4f9de5a11ada979bc59a.0c7e1209( )
            • VOWToken.balanceOf( _holder=0x74de5d4FCbf63E00296fd95d33236B9794016631 ) => ( balance_=1018315832198344777420 )
            • TetherToken.transfer( _to=0x2aCf35C9A3F4c5C3F4c78EF5Fb64c3EE82f07c45, _value=4462500 )
            • TetherToken.balanceOf( who=0x74de5d4FCbf63E00296fd95d33236B9794016631 ) => ( 0 )
            • VOWToken.balanceOf( _holder=0x74de5d4FCbf63E00296fd95d33236B9794016631 ) => ( balance_=1018315832198344777420 )
            • VOWToken.transfer( _to=0x4B135fFF5a67eE6ca34A1d406ce19e3cb5726D38, _value=1018315832198344777420 ) => ( success_=True )
              • LToken.b750dc46( )
                • ERC1820Registry.getInterfaceImplementer( _addr=0x74de5d4FCbf63E00296fd95d33236B9794016631, _interfaceHash=29DDB589B1FB5FC7CF394961C1ADF5F8C6454761ADF795E67FE149F658ABE895 ) => ( 0x0000000000000000000000000000000000000000 )
                • ERC1820Registry.getInterfaceImplementer( _addr=0x4B135fFF5a67eE6ca34A1d406ce19e3cb5726D38, _interfaceHash=B281FC8C12954D22544DB45DE3159A39272895B169A852B314F9CC762E44C53B ) => ( 0x0000000000000000000000000000000000000000 )
                  File 1 of 8: TetherToken
                  pragma solidity ^0.4.17;
                  
                  /**
                   * @title SafeMath
                   * @dev Math operations with safety checks that throw on error
                   */
                  library SafeMath {
                      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                          if (a == 0) {
                              return 0;
                          }
                          uint256 c = a * b;
                          assert(c / a == b);
                          return c;
                      }
                  
                      function div(uint256 a, uint256 b) internal pure returns (uint256) {
                          // assert(b > 0); // Solidity automatically throws when dividing by 0
                          uint256 c = a / b;
                          // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                          return c;
                      }
                  
                      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                          assert(b <= a);
                          return a - b;
                      }
                  
                      function add(uint256 a, uint256 b) internal pure returns (uint256) {
                          uint256 c = a + b;
                          assert(c >= a);
                          return c;
                      }
                  }
                  
                  /**
                   * @title Ownable
                   * @dev The Ownable contract has an owner address, and provides basic authorization control
                   * functions, this simplifies the implementation of "user permissions".
                   */
                  contract Ownable {
                      address public owner;
                  
                      /**
                        * @dev The Ownable constructor sets the original `owner` of the contract to the sender
                        * account.
                        */
                      function Ownable() public {
                          owner = msg.sender;
                      }
                  
                      /**
                        * @dev Throws if called by any account other than the owner.
                        */
                      modifier onlyOwner() {
                          require(msg.sender == owner);
                          _;
                      }
                  
                      /**
                      * @dev Allows the current owner to transfer control of the contract to a newOwner.
                      * @param newOwner The address to transfer ownership to.
                      */
                      function transferOwnership(address newOwner) public onlyOwner {
                          if (newOwner != address(0)) {
                              owner = newOwner;
                          }
                      }
                  
                  }
                  
                  /**
                   * @title ERC20Basic
                   * @dev Simpler version of ERC20 interface
                   * @dev see https://github.com/ethereum/EIPs/issues/20
                   */
                  contract ERC20Basic {
                      uint public _totalSupply;
                      function totalSupply() public constant returns (uint);
                      function balanceOf(address who) public constant returns (uint);
                      function transfer(address to, uint value) public;
                      event Transfer(address indexed from, address indexed to, uint value);
                  }
                  
                  /**
                   * @title ERC20 interface
                   * @dev see https://github.com/ethereum/EIPs/issues/20
                   */
                  contract ERC20 is ERC20Basic {
                      function allowance(address owner, address spender) public constant returns (uint);
                      function transferFrom(address from, address to, uint value) public;
                      function approve(address spender, uint value) public;
                      event Approval(address indexed owner, address indexed spender, uint value);
                  }
                  
                  /**
                   * @title Basic token
                   * @dev Basic version of StandardToken, with no allowances.
                   */
                  contract BasicToken is Ownable, ERC20Basic {
                      using SafeMath for uint;
                  
                      mapping(address => uint) public balances;
                  
                      // additional variables for use if transaction fees ever became necessary
                      uint public basisPointsRate = 0;
                      uint public maximumFee = 0;
                  
                      /**
                      * @dev Fix for the ERC20 short address attack.
                      */
                      modifier onlyPayloadSize(uint size) {
                          require(!(msg.data.length < size + 4));
                          _;
                      }
                  
                      /**
                      * @dev transfer token for a specified address
                      * @param _to The address to transfer to.
                      * @param _value The amount to be transferred.
                      */
                      function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
                          uint fee = (_value.mul(basisPointsRate)).div(10000);
                          if (fee > maximumFee) {
                              fee = maximumFee;
                          }
                          uint sendAmount = _value.sub(fee);
                          balances[msg.sender] = balances[msg.sender].sub(_value);
                          balances[_to] = balances[_to].add(sendAmount);
                          if (fee > 0) {
                              balances[owner] = balances[owner].add(fee);
                              Transfer(msg.sender, owner, fee);
                          }
                          Transfer(msg.sender, _to, sendAmount);
                      }
                  
                      /**
                      * @dev Gets the balance of the specified address.
                      * @param _owner The address to query the the balance of.
                      * @return An uint representing the amount owned by the passed address.
                      */
                      function balanceOf(address _owner) public constant returns (uint balance) {
                          return balances[_owner];
                      }
                  
                  }
                  
                  /**
                   * @title Standard ERC20 token
                   *
                   * @dev Implementation of the basic standard token.
                   * @dev https://github.com/ethereum/EIPs/issues/20
                   * @dev Based oncode by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
                   */
                  contract StandardToken is BasicToken, ERC20 {
                  
                      mapping (address => mapping (address => uint)) public allowed;
                  
                      uint public constant MAX_UINT = 2**256 - 1;
                  
                      /**
                      * @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 uint the amount of tokens to be transferred
                      */
                      function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) {
                          var _allowance = allowed[_from][msg.sender];
                  
                          // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
                          // if (_value > _allowance) throw;
                  
                          uint fee = (_value.mul(basisPointsRate)).div(10000);
                          if (fee > maximumFee) {
                              fee = maximumFee;
                          }
                          if (_allowance < MAX_UINT) {
                              allowed[_from][msg.sender] = _allowance.sub(_value);
                          }
                          uint sendAmount = _value.sub(fee);
                          balances[_from] = balances[_from].sub(_value);
                          balances[_to] = balances[_to].add(sendAmount);
                          if (fee > 0) {
                              balances[owner] = balances[owner].add(fee);
                              Transfer(_from, owner, fee);
                          }
                          Transfer(_from, _to, sendAmount);
                      }
                  
                      /**
                      * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
                      * @param _spender The address which will spend the funds.
                      * @param _value The amount of tokens to be spent.
                      */
                      function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
                  
                          // To change the approve amount you first have to reduce the addresses`
                          //  allowance to zero by calling `approve(_spender, 0)` if it is not
                          //  already 0 to mitigate the race condition described here:
                          //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                          require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));
                  
                          allowed[msg.sender][_spender] = _value;
                          Approval(msg.sender, _spender, _value);
                      }
                  
                      /**
                      * @dev Function to check the amount of tokens than 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 uint specifying the amount of tokens still available for the spender.
                      */
                      function allowance(address _owner, address _spender) public constant returns (uint remaining) {
                          return allowed[_owner][_spender];
                      }
                  
                  }
                  
                  
                  /**
                   * @title Pausable
                   * @dev Base contract which allows children to implement an emergency stop mechanism.
                   */
                  contract Pausable is Ownable {
                    event Pause();
                    event Unpause();
                  
                    bool public paused = false;
                  
                  
                    /**
                     * @dev Modifier to make a function callable only when the contract is not paused.
                     */
                    modifier whenNotPaused() {
                      require(!paused);
                      _;
                    }
                  
                    /**
                     * @dev Modifier to make a function callable only when the contract is paused.
                     */
                    modifier whenPaused() {
                      require(paused);
                      _;
                    }
                  
                    /**
                     * @dev called by the owner to pause, triggers stopped state
                     */
                    function pause() onlyOwner whenNotPaused public {
                      paused = true;
                      Pause();
                    }
                  
                    /**
                     * @dev called by the owner to unpause, returns to normal state
                     */
                    function unpause() onlyOwner whenPaused public {
                      paused = false;
                      Unpause();
                    }
                  }
                  
                  contract BlackList is Ownable, BasicToken {
                  
                      /////// Getters to allow the same blacklist to be used also by other contracts (including upgraded Tether) ///////
                      function getBlackListStatus(address _maker) external constant returns (bool) {
                          return isBlackListed[_maker];
                      }
                  
                      function getOwner() external constant returns (address) {
                          return owner;
                      }
                  
                      mapping (address => bool) public isBlackListed;
                      
                      function addBlackList (address _evilUser) public onlyOwner {
                          isBlackListed[_evilUser] = true;
                          AddedBlackList(_evilUser);
                      }
                  
                      function removeBlackList (address _clearedUser) public onlyOwner {
                          isBlackListed[_clearedUser] = false;
                          RemovedBlackList(_clearedUser);
                      }
                  
                      function destroyBlackFunds (address _blackListedUser) public onlyOwner {
                          require(isBlackListed[_blackListedUser]);
                          uint dirtyFunds = balanceOf(_blackListedUser);
                          balances[_blackListedUser] = 0;
                          _totalSupply -= dirtyFunds;
                          DestroyedBlackFunds(_blackListedUser, dirtyFunds);
                      }
                  
                      event DestroyedBlackFunds(address _blackListedUser, uint _balance);
                  
                      event AddedBlackList(address _user);
                  
                      event RemovedBlackList(address _user);
                  
                  }
                  
                  contract UpgradedStandardToken is StandardToken{
                      // those methods are called by the legacy contract
                      // and they must ensure msg.sender to be the contract address
                      function transferByLegacy(address from, address to, uint value) public;
                      function transferFromByLegacy(address sender, address from, address spender, uint value) public;
                      function approveByLegacy(address from, address spender, uint value) public;
                  }
                  
                  contract TetherToken is Pausable, StandardToken, BlackList {
                  
                      string public name;
                      string public symbol;
                      uint public decimals;
                      address public upgradedAddress;
                      bool public deprecated;
                  
                      //  The contract can be initialized with a number of tokens
                      //  All the tokens are deposited to the owner address
                      //
                      // @param _balance Initial supply of the contract
                      // @param _name Token Name
                      // @param _symbol Token symbol
                      // @param _decimals Token decimals
                      function TetherToken(uint _initialSupply, string _name, string _symbol, uint _decimals) public {
                          _totalSupply = _initialSupply;
                          name = _name;
                          symbol = _symbol;
                          decimals = _decimals;
                          balances[owner] = _initialSupply;
                          deprecated = false;
                      }
                  
                      // Forward ERC20 methods to upgraded contract if this one is deprecated
                      function transfer(address _to, uint _value) public whenNotPaused {
                          require(!isBlackListed[msg.sender]);
                          if (deprecated) {
                              return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value);
                          } else {
                              return super.transfer(_to, _value);
                          }
                      }
                  
                      // Forward ERC20 methods to upgraded contract if this one is deprecated
                      function transferFrom(address _from, address _to, uint _value) public whenNotPaused {
                          require(!isBlackListed[_from]);
                          if (deprecated) {
                              return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value);
                          } else {
                              return super.transferFrom(_from, _to, _value);
                          }
                      }
                  
                      // Forward ERC20 methods to upgraded contract if this one is deprecated
                      function balanceOf(address who) public constant returns (uint) {
                          if (deprecated) {
                              return UpgradedStandardToken(upgradedAddress).balanceOf(who);
                          } else {
                              return super.balanceOf(who);
                          }
                      }
                  
                      // Forward ERC20 methods to upgraded contract if this one is deprecated
                      function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
                          if (deprecated) {
                              return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value);
                          } else {
                              return super.approve(_spender, _value);
                          }
                      }
                  
                      // Forward ERC20 methods to upgraded contract if this one is deprecated
                      function allowance(address _owner, address _spender) public constant returns (uint remaining) {
                          if (deprecated) {
                              return StandardToken(upgradedAddress).allowance(_owner, _spender);
                          } else {
                              return super.allowance(_owner, _spender);
                          }
                      }
                  
                      // deprecate current contract in favour of a new one
                      function deprecate(address _upgradedAddress) public onlyOwner {
                          deprecated = true;
                          upgradedAddress = _upgradedAddress;
                          Deprecate(_upgradedAddress);
                      }
                  
                      // deprecate current contract if favour of a new one
                      function totalSupply() public constant returns (uint) {
                          if (deprecated) {
                              return StandardToken(upgradedAddress).totalSupply();
                          } else {
                              return _totalSupply;
                          }
                      }
                  
                      // Issue a new amount of tokens
                      // these tokens are deposited into the owner address
                      //
                      // @param _amount Number of tokens to be issued
                      function issue(uint amount) public onlyOwner {
                          require(_totalSupply + amount > _totalSupply);
                          require(balances[owner] + amount > balances[owner]);
                  
                          balances[owner] += amount;
                          _totalSupply += amount;
                          Issue(amount);
                      }
                  
                      // Redeem tokens.
                      // These tokens are withdrawn from the owner address
                      // if the balance must be enough to cover the redeem
                      // or the call will fail.
                      // @param _amount Number of tokens to be issued
                      function redeem(uint amount) public onlyOwner {
                          require(_totalSupply >= amount);
                          require(balances[owner] >= amount);
                  
                          _totalSupply -= amount;
                          balances[owner] -= amount;
                          Redeem(amount);
                      }
                  
                      function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner {
                          // Ensure transparency by hardcoding limit beyond which fees can never be added
                          require(newBasisPoints < 20);
                          require(newMaxFee < 50);
                  
                          basisPointsRate = newBasisPoints;
                          maximumFee = newMaxFee.mul(10**decimals);
                  
                          Params(basisPointsRate, maximumFee);
                      }
                  
                      // Called when new token are issued
                      event Issue(uint amount);
                  
                      // Called when tokens are redeemed
                      event Redeem(uint amount);
                  
                      // Called when contract is deprecated
                      event Deprecate(address newAddress);
                  
                      // Called if contract ever adds fees
                      event Params(uint feeBasisPoints, uint maxFee);
                  }

                  File 2 of 8: UniswapV2Pair
                  // File: contracts/interfaces/IUniswapV2Pair.sol
                  
                  pragma solidity >=0.5.0;
                  
                  interface IUniswapV2Pair {
                      event Approval(address indexed owner, address indexed spender, uint value);
                      event Transfer(address indexed from, address indexed to, uint value);
                  
                      function name() external pure returns (string memory);
                      function symbol() external pure returns (string memory);
                      function decimals() external pure returns (uint8);
                      function totalSupply() external view returns (uint);
                      function balanceOf(address owner) external view returns (uint);
                      function allowance(address owner, address spender) external view returns (uint);
                  
                      function approve(address spender, uint value) external returns (bool);
                      function transfer(address to, uint value) external returns (bool);
                      function transferFrom(address from, address to, uint value) external returns (bool);
                  
                      function DOMAIN_SEPARATOR() external view returns (bytes32);
                      function PERMIT_TYPEHASH() external pure returns (bytes32);
                      function nonces(address owner) external view returns (uint);
                  
                      function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
                  
                      event Mint(address indexed sender, uint amount0, uint amount1);
                      event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
                      event Swap(
                          address indexed sender,
                          uint amount0In,
                          uint amount1In,
                          uint amount0Out,
                          uint amount1Out,
                          address indexed to
                      );
                      event Sync(uint112 reserve0, uint112 reserve1);
                  
                      function MINIMUM_LIQUIDITY() external pure returns (uint);
                      function factory() external view returns (address);
                      function token0() external view returns (address);
                      function token1() external view returns (address);
                      function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
                      function price0CumulativeLast() external view returns (uint);
                      function price1CumulativeLast() external view returns (uint);
                      function kLast() external view returns (uint);
                  
                      function mint(address to) external returns (uint liquidity);
                      function burn(address to) external returns (uint amount0, uint amount1);
                      function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
                      function skim(address to) external;
                      function sync() external;
                  
                      function initialize(address, address) external;
                  }
                  
                  // File: contracts/interfaces/IUniswapV2ERC20.sol
                  
                  pragma solidity >=0.5.0;
                  
                  interface IUniswapV2ERC20 {
                      event Approval(address indexed owner, address indexed spender, uint value);
                      event Transfer(address indexed from, address indexed to, uint value);
                  
                      function name() external pure returns (string memory);
                      function symbol() external pure returns (string memory);
                      function decimals() external pure returns (uint8);
                      function totalSupply() external view returns (uint);
                      function balanceOf(address owner) external view returns (uint);
                      function allowance(address owner, address spender) external view returns (uint);
                  
                      function approve(address spender, uint value) external returns (bool);
                      function transfer(address to, uint value) external returns (bool);
                      function transferFrom(address from, address to, uint value) external returns (bool);
                  
                      function DOMAIN_SEPARATOR() external view returns (bytes32);
                      function PERMIT_TYPEHASH() external pure returns (bytes32);
                      function nonces(address owner) external view returns (uint);
                  
                      function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
                  }
                  
                  // File: contracts/libraries/SafeMath.sol
                  
                  pragma solidity =0.5.16;
                  
                  // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
                  
                  library SafeMath {
                      function add(uint x, uint y) internal pure returns (uint z) {
                          require((z = x + y) >= x, 'ds-math-add-overflow');
                      }
                  
                      function sub(uint x, uint y) internal pure returns (uint z) {
                          require((z = x - y) <= x, 'ds-math-sub-underflow');
                      }
                  
                      function mul(uint x, uint y) internal pure returns (uint z) {
                          require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
                      }
                  }
                  
                  // File: contracts/UniswapV2ERC20.sol
                  
                  pragma solidity =0.5.16;
                  
                  
                  
                  contract UniswapV2ERC20 is IUniswapV2ERC20 {
                      using SafeMath for uint;
                  
                      string public constant name = 'Uniswap V2';
                      string public constant symbol = 'UNI-V2';
                      uint8 public constant decimals = 18;
                      uint  public totalSupply;
                      mapping(address => uint) public balanceOf;
                      mapping(address => mapping(address => uint)) public allowance;
                  
                      bytes32 public DOMAIN_SEPARATOR;
                      // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
                      bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
                      mapping(address => uint) public nonces;
                  
                      event Approval(address indexed owner, address indexed spender, uint value);
                      event Transfer(address indexed from, address indexed to, uint value);
                  
                      constructor() public {
                          uint chainId;
                          assembly {
                              chainId := chainid
                          }
                          DOMAIN_SEPARATOR = keccak256(
                              abi.encode(
                                  keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
                                  keccak256(bytes(name)),
                                  keccak256(bytes('1')),
                                  chainId,
                                  address(this)
                              )
                          );
                      }
                  
                      function _mint(address to, uint value) internal {
                          totalSupply = totalSupply.add(value);
                          balanceOf[to] = balanceOf[to].add(value);
                          emit Transfer(address(0), to, value);
                      }
                  
                      function _burn(address from, uint value) internal {
                          balanceOf[from] = balanceOf[from].sub(value);
                          totalSupply = totalSupply.sub(value);
                          emit Transfer(from, address(0), value);
                      }
                  
                      function _approve(address owner, address spender, uint value) private {
                          allowance[owner][spender] = value;
                          emit Approval(owner, spender, value);
                      }
                  
                      function _transfer(address from, address to, uint value) private {
                          balanceOf[from] = balanceOf[from].sub(value);
                          balanceOf[to] = balanceOf[to].add(value);
                          emit Transfer(from, to, value);
                      }
                  
                      function approve(address spender, uint value) external returns (bool) {
                          _approve(msg.sender, spender, value);
                          return true;
                      }
                  
                      function transfer(address to, uint value) external returns (bool) {
                          _transfer(msg.sender, to, value);
                          return true;
                      }
                  
                      function transferFrom(address from, address to, uint value) external returns (bool) {
                          if (allowance[from][msg.sender] != uint(-1)) {
                              allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
                          }
                          _transfer(from, to, value);
                          return true;
                      }
                  
                      function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
                          require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
                          bytes32 digest = keccak256(
                              abi.encodePacked(
                                  '\x19\x01',
                                  DOMAIN_SEPARATOR,
                                  keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                              )
                          );
                          address recoveredAddress = ecrecover(digest, v, r, s);
                          require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
                          _approve(owner, spender, value);
                      }
                  }
                  
                  // File: contracts/libraries/Math.sol
                  
                  pragma solidity =0.5.16;
                  
                  // a library for performing various math operations
                  
                  library Math {
                      function min(uint x, uint y) internal pure returns (uint z) {
                          z = x < y ? x : y;
                      }
                  
                      // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
                      function sqrt(uint y) internal pure returns (uint z) {
                          if (y > 3) {
                              z = y;
                              uint x = y / 2 + 1;
                              while (x < z) {
                                  z = x;
                                  x = (y / x + x) / 2;
                              }
                          } else if (y != 0) {
                              z = 1;
                          }
                      }
                  }
                  
                  // File: contracts/libraries/UQ112x112.sol
                  
                  pragma solidity =0.5.16;
                  
                  // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
                  
                  // range: [0, 2**112 - 1]
                  // resolution: 1 / 2**112
                  
                  library UQ112x112 {
                      uint224 constant Q112 = 2**112;
                  
                      // encode a uint112 as a UQ112x112
                      function encode(uint112 y) internal pure returns (uint224 z) {
                          z = uint224(y) * Q112; // never overflows
                      }
                  
                      // divide a UQ112x112 by a uint112, returning a UQ112x112
                      function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
                          z = x / uint224(y);
                      }
                  }
                  
                  // File: contracts/interfaces/IERC20.sol
                  
                  pragma solidity >=0.5.0;
                  
                  interface IERC20 {
                      event Approval(address indexed owner, address indexed spender, uint value);
                      event Transfer(address indexed from, address indexed to, uint value);
                  
                      function name() external view returns (string memory);
                      function symbol() external view returns (string memory);
                      function decimals() external view returns (uint8);
                      function totalSupply() external view returns (uint);
                      function balanceOf(address owner) external view returns (uint);
                      function allowance(address owner, address spender) external view returns (uint);
                  
                      function approve(address spender, uint value) external returns (bool);
                      function transfer(address to, uint value) external returns (bool);
                      function transferFrom(address from, address to, uint value) external returns (bool);
                  }
                  
                  // File: contracts/interfaces/IUniswapV2Factory.sol
                  
                  pragma solidity >=0.5.0;
                  
                  interface IUniswapV2Factory {
                      event PairCreated(address indexed token0, address indexed token1, address pair, uint);
                  
                      function feeTo() external view returns (address);
                      function feeToSetter() external view returns (address);
                  
                      function getPair(address tokenA, address tokenB) external view returns (address pair);
                      function allPairs(uint) external view returns (address pair);
                      function allPairsLength() external view returns (uint);
                  
                      function createPair(address tokenA, address tokenB) external returns (address pair);
                  
                      function setFeeTo(address) external;
                      function setFeeToSetter(address) external;
                  }
                  
                  // File: contracts/interfaces/IUniswapV2Callee.sol
                  
                  pragma solidity >=0.5.0;
                  
                  interface IUniswapV2Callee {
                      function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external;
                  }
                  
                  // File: contracts/UniswapV2Pair.sol
                  
                  pragma solidity =0.5.16;
                  
                  
                  
                  
                  
                  
                  
                  
                  contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
                      using SafeMath  for uint;
                      using UQ112x112 for uint224;
                  
                      uint public constant MINIMUM_LIQUIDITY = 10**3;
                      bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
                  
                      address public factory;
                      address public token0;
                      address public token1;
                  
                      uint112 private reserve0;           // uses single storage slot, accessible via getReserves
                      uint112 private reserve1;           // uses single storage slot, accessible via getReserves
                      uint32  private blockTimestampLast; // uses single storage slot, accessible via getReserves
                  
                      uint public price0CumulativeLast;
                      uint public price1CumulativeLast;
                      uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event
                  
                      uint private unlocked = 1;
                      modifier lock() {
                          require(unlocked == 1, 'UniswapV2: LOCKED');
                          unlocked = 0;
                          _;
                          unlocked = 1;
                      }
                  
                      function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
                          _reserve0 = reserve0;
                          _reserve1 = reserve1;
                          _blockTimestampLast = blockTimestampLast;
                      }
                  
                      function _safeTransfer(address token, address to, uint value) private {
                          (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
                          require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
                      }
                  
                      event Mint(address indexed sender, uint amount0, uint amount1);
                      event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
                      event Swap(
                          address indexed sender,
                          uint amount0In,
                          uint amount1In,
                          uint amount0Out,
                          uint amount1Out,
                          address indexed to
                      );
                      event Sync(uint112 reserve0, uint112 reserve1);
                  
                      constructor() public {
                          factory = msg.sender;
                      }
                  
                      // called once by the factory at time of deployment
                      function initialize(address _token0, address _token1) external {
                          require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
                          token0 = _token0;
                          token1 = _token1;
                      }
                  
                      // update reserves and, on the first call per block, price accumulators
                      function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
                          require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
                          uint32 blockTimestamp = uint32(block.timestamp % 2**32);
                          uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
                          if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
                              // * never overflows, and + overflow is desired
                              price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
                              price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
                          }
                          reserve0 = uint112(balance0);
                          reserve1 = uint112(balance1);
                          blockTimestampLast = blockTimestamp;
                          emit Sync(reserve0, reserve1);
                      }
                  
                      // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
                      function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
                          address feeTo = IUniswapV2Factory(factory).feeTo();
                          feeOn = feeTo != address(0);
                          uint _kLast = kLast; // gas savings
                          if (feeOn) {
                              if (_kLast != 0) {
                                  uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
                                  uint rootKLast = Math.sqrt(_kLast);
                                  if (rootK > rootKLast) {
                                      uint numerator = totalSupply.mul(rootK.sub(rootKLast));
                                      uint denominator = rootK.mul(5).add(rootKLast);
                                      uint liquidity = numerator / denominator;
                                      if (liquidity > 0) _mint(feeTo, liquidity);
                                  }
                              }
                          } else if (_kLast != 0) {
                              kLast = 0;
                          }
                      }
                  
                      // this low-level function should be called from a contract which performs important safety checks
                      function mint(address to) external lock returns (uint liquidity) {
                          (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                          uint balance0 = IERC20(token0).balanceOf(address(this));
                          uint balance1 = IERC20(token1).balanceOf(address(this));
                          uint amount0 = balance0.sub(_reserve0);
                          uint amount1 = balance1.sub(_reserve1);
                  
                          bool feeOn = _mintFee(_reserve0, _reserve1);
                          uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
                          if (_totalSupply == 0) {
                              liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
                             _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
                          } else {
                              liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
                          }
                          require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
                          _mint(to, liquidity);
                  
                          _update(balance0, balance1, _reserve0, _reserve1);
                          if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
                          emit Mint(msg.sender, amount0, amount1);
                      }
                  
                      // this low-level function should be called from a contract which performs important safety checks
                      function burn(address to) external lock returns (uint amount0, uint amount1) {
                          (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                          address _token0 = token0;                                // gas savings
                          address _token1 = token1;                                // gas savings
                          uint balance0 = IERC20(_token0).balanceOf(address(this));
                          uint balance1 = IERC20(_token1).balanceOf(address(this));
                          uint liquidity = balanceOf[address(this)];
                  
                          bool feeOn = _mintFee(_reserve0, _reserve1);
                          uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
                          amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
                          amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
                          require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
                          _burn(address(this), liquidity);
                          _safeTransfer(_token0, to, amount0);
                          _safeTransfer(_token1, to, amount1);
                          balance0 = IERC20(_token0).balanceOf(address(this));
                          balance1 = IERC20(_token1).balanceOf(address(this));
                  
                          _update(balance0, balance1, _reserve0, _reserve1);
                          if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
                          emit Burn(msg.sender, amount0, amount1, to);
                      }
                  
                      // this low-level function should be called from a contract which performs important safety checks
                      function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
                          require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
                          (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                          require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
                  
                          uint balance0;
                          uint balance1;
                          { // scope for _token{0,1}, avoids stack too deep errors
                          address _token0 = token0;
                          address _token1 = token1;
                          require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
                          if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
                          if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
                          if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
                          balance0 = IERC20(_token0).balanceOf(address(this));
                          balance1 = IERC20(_token1).balanceOf(address(this));
                          }
                          uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
                          uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
                          require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
                          { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
                          uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
                          uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
                          require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
                          }
                  
                          _update(balance0, balance1, _reserve0, _reserve1);
                          emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
                      }
                  
                      // force balances to match reserves
                      function skim(address to) external lock {
                          address _token0 = token0; // gas savings
                          address _token1 = token1; // gas savings
                          _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
                          _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
                      }
                  
                      // force reserves to match balances
                      function sync() external lock {
                          _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
                      }
                  }

                  File 3 of 8: VOWToken
                  // File: contracts/thirdParty/ECDSA.sol
                  
                  // Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/cryptography/ECDSA.sol
                  // Line 60 added to original source in accordance with recommendation on accepting signatures with 0/1 for v
                  
                  pragma solidity ^0.6.0;
                  
                  /**
                   * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
                   *
                   * These functions can be used to verify that a message was signed by the holder
                   * of the private keys of a given address.
                   */
                  library ECDSA {
                      /**
                       * @dev Returns the address that signed a hashed message (`hash`) with
                       * `signature`. This address can then be used for verification purposes.
                       *
                       * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                       * this function rejects them by requiring the `s` value to be in the lower
                       * half order, and the `v` value to be either 27 or 28.
                       *
                       * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                       * verification to be secure: it is possible to craft signatures that
                       * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                       * this is by receiving a hash of the original message (which may otherwise
                       * be too long), and then calling {toEthSignedMessageHash} on it.
                       */
                      function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
                          // Check the signature length
                          if (signature.length != 65) {
                              revert("ECDSA: invalid signature length");
                          }
                  
                          // Divide the signature in r, s and v variables
                          bytes32 r;
                          bytes32 s;
                          uint8 v;
                  
                          // ecrecover takes the signature parameters, and the only way to get them
                          // currently is to use assembly.
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              r := mload(add(signature, 0x20))
                              s := mload(add(signature, 0x40))
                              v := byte(0, mload(add(signature, 0x60)))
                          }
                  
                          // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                          // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                          // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
                          // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                          //
                          // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                          // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                          // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                          // these malleable signatures as well.
                          if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                              revert("ECDSA: invalid signature 's' value");
                          }
                  
                          if (v < 27) v += 27;
                  
                          if (v != 27 && v != 28) {
                              revert("ECDSA: invalid signature 'v' value");
                          }
                  
                          // If the signature is valid (and not malleable), return the signer address
                          address signer = ecrecover(hash, v, r, s);
                          require(signer != address(0), "ECDSA: invalid signature");
                  
                          return signer;
                      }
                  
                      /**
                       * @dev Returns an Ethereum Signed Message, created from a `hash`. This
                       * replicates the behavior of the
                       * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
                       * JSON-RPC method.
                       *
                       * See {recover}.
                       */
                      function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
                          // 32 is the length in bytes of hash,
                          // enforced by the type signature above
                          return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
                      }
                  }
                  
                  // File: contracts/interfaces/IERC777.sol
                  
                  pragma solidity 0.6.7;
                  
                  // As defined in https://eips.ethereum.org/EIPS/eip-777
                  interface IERC777 {
                    event Sent(address indexed operator, address indexed from, address indexed to, uint256 amount, bytes data,
                        bytes operatorData);
                    event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);
                    event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);
                    event AuthorizedOperator(address indexed operator,address indexed holder);
                    event RevokedOperator(address indexed operator, address indexed holder);
                  
                    function name() external view returns (string memory);
                    function symbol() external view returns (string memory);
                    function totalSupply() external view returns (uint256);
                    function balanceOf(address holder) external view returns (uint256);
                    function granularity() external view returns (uint256);
                    function defaultOperators() external view returns (address[] memory);
                    function isOperatorFor(address operator, address holder) external view returns (bool);
                    function authorizeOperator(address operator) external;
                    function revokeOperator(address operator) external;
                    function send(address to, uint256 amount, bytes calldata data) external;
                    function operatorSend(address from, address to, uint256 amount, bytes calldata data, bytes calldata operatorData) external;
                    function burn(uint256 amount, bytes calldata data) external;
                    function operatorBurn( address from, uint256 amount, bytes calldata data, bytes calldata operatorData) external;
                  }
                  
                  // File: contracts/interfaces/IERC20.sol
                  
                  pragma solidity 0.6.7;
                  
                  // As described in https://eips.ethereum.org/EIPS/eip-20
                  interface IERC20 {
                    event Transfer(address indexed from, address indexed to, uint256 value);
                    event Approval(address indexed owner, address indexed spender, uint256 value);
                  
                    function name() external view returns (string memory); // optional method - see eip spec
                    function symbol() external view returns (string memory); // optional method - see eip spec
                    function decimals() external view returns (uint8); // optional method - see eip spec
                    function totalSupply() external view returns (uint256);
                    function balanceOf(address owner) external view returns (uint256);
                    function transfer(address to, uint256 value) external returns (bool);
                    function transferFrom(address from, address to, uint256 value) external returns (bool);
                    function approve(address spender, uint256 value) external returns (bool);
                    function allowance(address owner, address spender) external view returns (uint256);
                  }
                  
                  // File: contracts/thirdParty/interfaces/IERC1820Registry.sol
                  
                  // From open https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/introspection/IERC1820Registry.sol
                  
                  pragma solidity ^0.6.0;
                  
                  /**
                   * @dev Interface of the global ERC1820 Registry, as defined in the
                   * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register
                   * implementers for interfaces in this registry, as well as query support.
                   *
                   * Implementers may be shared by multiple accounts, and can also implement more
                   * than a single interface for each account. Contracts can implement interfaces
                   * for themselves, but externally-owned accounts (EOA) must delegate this to a
                   * contract.
                   *
                   * {IERC165} interfaces can also be queried via the registry.
                   *
                   * For an in-depth explanation and source code analysis, see the EIP text.
                   */
                  interface IERC1820Registry {
                      /**
                       * @dev Sets `newManager` as the manager for `account`. A manager of an
                       * account is able to set interface implementers for it.
                       *
                       * By default, each account is its own manager. Passing a value of `0x0` in
                       * `newManager` will reset the manager to this initial state.
                       *
                       * Emits a {ManagerChanged} event.
                       *
                       * Requirements:
                       *
                       * - the caller must be the current manager for `account`.
                       */
                      function setManager(address account, address newManager) external;
                  
                      /**
                       * @dev Returns the manager for `account`.
                       *
                       * See {setManager}.
                       */
                      function getManager(address account) external view returns (address);
                  
                      /**
                       * @dev Sets the `implementer` contract as ``account``'s implementer for
                       * `interfaceHash`.
                       *
                       * `account` being the zero address is an alias for the caller's address.
                       * The zero address can also be used in `implementer` to remove an old one.
                       *
                       * See {interfaceHash} to learn how these are created.
                       *
                       * Emits an {InterfaceImplementerSet} event.
                       *
                       * Requirements:
                       *
                       * - the caller must be the current manager for `account`.
                       * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not
                       * end in 28 zeroes).
                       * - `implementer` must implement {IERC1820Implementer} and return true when
                       * queried for support, unless `implementer` is the caller. See
                       * {IERC1820Implementer-canImplementInterfaceForAddress}.
                       */
                      function setInterfaceImplementer(address account, bytes32 interfaceHash, address implementer) external;
                  
                      /**
                       * @dev Returns the implementer of `interfaceHash` for `account`. If no such
                       * implementer is registered, returns the zero address.
                       *
                       * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28
                       * zeroes), `account` will be queried for support of it.
                       *
                       * `account` being the zero address is an alias for the caller's address.
                       */
                      function getInterfaceImplementer(address account, bytes32 interfaceHash) external view returns (address);
                  
                      /**
                       * @dev Returns the interface hash for an `interfaceName`, as defined in the
                       * corresponding
                       * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP].
                       */
                      function interfaceHash(string calldata interfaceName) external pure returns (bytes32);
                  
                      /**
                       *  @notice Updates the cache with whether the contract implements an ERC165 interface or not.
                       *  @param account Address of the contract for which to update the cache.
                       *  @param interfaceId ERC165 interface for which to update the cache.
                       */
                      function updateERC165Cache(address account, bytes4 interfaceId) external;
                  
                      /**
                       *  @notice Checks whether a contract implements an ERC165 interface or not.
                       *  If the result is not cached a direct lookup on the contract address is performed.
                       *  If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
                       *  {updateERC165Cache} with the contract address.
                       *  @param account Address of the contract to check.
                       *  @param interfaceId ERC165 interface to check.
                       *  @return True if `account` implements `interfaceId`, false otherwise.
                       */
                      function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool);
                  
                      /**
                       *  @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
                       *  @param account Address of the contract to check.
                       *  @param interfaceId ERC165 interface to check.
                       *  @return True if `account` implements `interfaceId`, false otherwise.
                       */
                      function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool);
                  
                      event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer);
                  
                      event ManagerChanged(address indexed account, address indexed newManager);
                  }
                  
                  // File: contracts/interfaces/IERC777Sender.sol
                  
                  pragma solidity 0.6.7;
                  
                  // As defined in the 'ERC777TokensSender And The tokensToSend Hook' section of https://eips.ethereum.org/EIPS/eip-777
                  interface IERC777Sender {
                    function tokensToSend(address operator, address from, address to, uint256 amount, bytes calldata data,
                        bytes calldata operatorData) external;
                  }
                  
                  // File: contracts/interfaces/IERC777Recipient.sol
                  
                  pragma solidity 0.6.7;
                  
                  // As defined in the 'ERC777TokensRecipient And The tokensReceived Hook' section of https://eips.ethereum.org/EIPS/eip-777
                  interface IERC777Recipient {
                    function tokensReceived(address operator, address from, address to, uint256 amount, bytes calldata data,
                        bytes calldata operatorData) external;
                  }
                  
                  // File: contracts/thirdParty/SafeMath.sol
                  
                  // Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/SafeMath.sol
                  
                  pragma solidity ^0.6.0;
                  
                  /**
                   * @dev Wrappers over Solidity's arithmetic operations with added overflow
                   * checks.
                   *
                   * Arithmetic operations in Solidity wrap on overflow. This can easily result
                   * in bugs, because programmers usually assume that an overflow raises an
                   * error, which is the standard behavior in high level programming languages.
                   * `SafeMath` restores this intuition by reverting the transaction when an
                   * operation overflows.
                   *
                   * Using this library instead of the unchecked operations eliminates an entire
                   * class of bugs, so it's recommended to use it always.
                   */
                  library SafeMath {
                      /**
                       * @dev Returns the addition of two unsigned integers, reverting on
                       * overflow.
                       *
                       * Counterpart to Solidity's `+` operator.
                       *
                       * Requirements:
                       * - Addition cannot overflow.
                       */
                      function add(uint256 a, uint256 b) internal pure returns (uint256) {
                          uint256 c = a + b;
                          require(c >= a, "SafeMath: addition overflow");
                  
                          return c;
                      }
                  
                      /**
                       * @dev Returns the subtraction of two unsigned integers, reverting on
                       * overflow (when the result is negative).
                       *
                       * Counterpart to Solidity's `-` operator.
                       *
                       * Requirements:
                       * - Subtraction cannot overflow.
                       */
                      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                          return sub(a, b, "SafeMath: subtraction overflow");
                      }
                  
                      /**
                       * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                       * overflow (when the result is negative).
                       *
                       * Counterpart to Solidity's `-` operator.
                       *
                       * Requirements:
                       * - Subtraction cannot overflow.
                       *
                       * _Available since v2.4.0._
                       */
                      function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                          require(b <= a, errorMessage);
                          uint256 c = a - b;
                  
                          return c;
                      }
                  
                      /**
                       * @dev Returns the multiplication of two unsigned integers, reverting on
                       * overflow.
                       *
                       * Counterpart to Solidity's `*` operator.
                       *
                       * Requirements:
                       * - Multiplication cannot overflow.
                       */
                      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                          // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                          // benefit is lost if 'b' is also tested.
                          // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                          if (a == 0) {
                              return 0;
                          }
                  
                          uint256 c = a * b;
                          require(c / a == b, "SafeMath: multiplication overflow");
                  
                          return c;
                      }
                  
                      /**
                       * @dev Returns the integer division of two unsigned integers. Reverts on
                       * division by zero. The result is rounded towards zero.
                       *
                       * Counterpart to Solidity's `/` operator. Note: this function uses a
                       * `revert` opcode (which leaves remaining gas untouched) while Solidity
                       * uses an invalid opcode to revert (consuming all remaining gas).
                       *
                       * Requirements:
                       * - The divisor cannot be zero.
                       */
                      function div(uint256 a, uint256 b) internal pure returns (uint256) {
                          return div(a, b, "SafeMath: division by zero");
                      }
                  
                      /**
                       * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
                       * division by zero. The result is rounded towards zero.
                       *
                       * Counterpart to Solidity's `/` operator. Note: this function uses a
                       * `revert` opcode (which leaves remaining gas untouched) while Solidity
                       * uses an invalid opcode to revert (consuming all remaining gas).
                       *
                       * Requirements:
                       * - The divisor cannot be zero.
                       *
                       * _Available since v2.4.0._
                       */
                      function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                          // Solidity only automatically asserts when dividing by 0
                          require(b > 0, errorMessage);
                          uint256 c = a / b;
                          // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                  
                          return c;
                      }
                  
                      /**
                       * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                       * Reverts when dividing by zero.
                       *
                       * Counterpart to Solidity's `%` operator. This function uses a `revert`
                       * opcode (which leaves remaining gas untouched) while Solidity uses an
                       * invalid opcode to revert (consuming all remaining gas).
                       *
                       * Requirements:
                       * - The divisor cannot be zero.
                       */
                      function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                          return mod(a, b, "SafeMath: modulo by zero");
                      }
                  
                      /**
                       * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                       * Reverts with custom message when dividing by zero.
                       *
                       * Counterpart to Solidity's `%` operator. This function uses a `revert`
                       * opcode (which leaves remaining gas untouched) while Solidity uses an
                       * invalid opcode to revert (consuming all remaining gas).
                       *
                       * Requirements:
                       * - The divisor cannot be zero.
                       *
                       * _Available since v2.4.0._
                       */
                      function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                          require(b != 0, errorMessage);
                          return a % b;
                      }
                  }
                  
                  // File: contracts/libraries/LToken.sol
                  
                  pragma solidity 0.6.7;
                  
                  
                  
                  
                  
                  struct TokenState {
                    uint256 totalSupply;
                    mapping(address => uint256) balances;
                    mapping(address => mapping(address => uint256)) approvals;
                    mapping(address => mapping(address => bool)) authorizedOperators;
                    address[] defaultOperators;
                    mapping(address => bool) defaultOperatorIsRevoked;
                    mapping(address => bool) minters;
                  }
                  
                  library LToken {
                    using SafeMath for uint256;
                  
                    event Transfer(address indexed from, address indexed to, uint256 value);
                    event Approval(address indexed owner, address indexed spender, uint256 value);
                    event Sent(address indexed operator, address indexed from, address indexed to, uint256 amount, bytes data,
                        bytes operatorData);
                    event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);
                    event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);
                    event AuthorizedOperator(address indexed operator, address indexed holder);
                    event RevokedOperator(address indexed operator, address indexed holder);
                  
                    // Universal address as defined in Registry Contract Address section of https://eips.ethereum.org/EIPS/eip-1820
                    IERC1820Registry constant internal ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
                    // precalculated hashes - see https://github.com/ethereum/solidity/issues/4024
                    // keccak256("ERC777TokensSender")
                    bytes32 constant internal ERC777_TOKENS_SENDER_HASH = 0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895;
                    // keccak256("ERC777TokensRecipient")
                    bytes32 constant internal ERC777_TOKENS_RECIPIENT_HASH = 0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b;
                  
                    modifier checkSenderNotOperator(address _operator) {
                      require(_operator != msg.sender, "Cannot be operator for self");
                      _;
                    }
                  
                    function initState(TokenState storage _tokenState, uint8 _decimals, uint256 _initialSupply)
                      external
                    {
                      _tokenState.defaultOperators.push(address(this));
                      _tokenState.totalSupply = _initialSupply.mul(10**uint256(_decimals));
                      _tokenState.balances[msg.sender] = _tokenState.totalSupply;
                    }
                  
                    function transferFrom(TokenState storage _tokenState, address _from, address _to, uint256 _value)
                      external
                    {
                      _tokenState.approvals[_from][msg.sender] = _tokenState.approvals[_from][msg.sender].sub(_value, "Amount not approved");
                      doSend(_tokenState, msg.sender, _from, _to, _value, "", "", false);
                    }
                  
                    function approve(TokenState storage _tokenState, address _spender, uint256 _value)
                      external
                    {
                      require(_spender != address(0), "Cannot approve to zero address");
                      _tokenState.approvals[msg.sender][_spender] = _value;
                      emit Approval(msg.sender, _spender, _value);
                    }
                  
                    function authorizeOperator(TokenState storage _tokenState, address _operator)
                      checkSenderNotOperator(_operator)
                      external
                    {
                      if (_operator == address(this))
                        _tokenState.defaultOperatorIsRevoked[msg.sender] = false;
                      else
                        _tokenState.authorizedOperators[_operator][msg.sender] = true;
                      emit AuthorizedOperator(_operator, msg.sender);
                    }
                  
                    function revokeOperator(TokenState storage _tokenState, address _operator)
                      checkSenderNotOperator(_operator)
                      external
                    {
                      if (_operator == address(this))
                        _tokenState.defaultOperatorIsRevoked[msg.sender] = true;
                      else
                        _tokenState.authorizedOperators[_operator][msg.sender] = false;
                      emit RevokedOperator(_operator, msg.sender);
                    }
                  
                    function authorizeMinter(TokenState storage _tokenState, address _minter)
                      external
                    {
                      _tokenState.minters[_minter] = true;
                    }
                  
                    function revokeMinter(TokenState storage _tokenState, address _minter)
                      external
                    {
                      _tokenState.minters[_minter] = false;
                    }
                  
                    function doMint(TokenState storage _tokenState, address _to, uint256 _amount)
                      external
                    {
                      assert(_to != address(0));
                  
                      _tokenState.totalSupply = _tokenState.totalSupply.add(_amount);
                      _tokenState.balances[_to] = _tokenState.balances[_to].add(_amount);
                  
                      // From ERC777: The token contract MUST call the tokensReceived hook after updating the state.
                      receiveHook(address(this), address(0), _to, _amount, "", "", true);
                  
                      emit Minted(address(this), _to, _amount, "", "");
                      emit Transfer(address(0), _to, _amount);
                    }
                  
                    function doBurn(TokenState storage _tokenState, address _operator, address _from, uint256 _amount, bytes calldata _data,
                        bytes calldata _operatorData)
                      external
                    {
                      assert(_from != address(0));
                      // From ERC777: The token contract MUST call the tokensToSend hook before updating the state.
                      sendHook(_operator, _from, address(0), _amount, _data, _operatorData);
                  
                      _tokenState.balances[_from] = _tokenState.balances[_from].sub(_amount, "Cannot burn more than balance");
                      _tokenState.totalSupply = _tokenState.totalSupply.sub(_amount);
                  
                      emit Burned(_operator, _from, _amount, _data, _operatorData);
                      emit Transfer(_from, address(0), _amount);
                    }
                  
                    function doSend(TokenState storage _tokenState, address _operator, address _from, address _to, uint256 _amount,
                        bytes memory _data, bytes memory _operatorData, bool _enforceERC777)
                      public
                    {
                      assert(_from != address(0));
                  
                      require(_to != address(0), "Cannot send funds to 0 address");
                      // From ERC777: The token contract MUST call the tokensToSend hook before updating the state.
                      sendHook(_operator, _from, _to, _amount, _data, _operatorData);
                  
                      _tokenState.balances[_from] = _tokenState.balances[_from].sub(_amount, "Amount exceeds available funds");
                      _tokenState.balances[_to] = _tokenState.balances[_to].add(_amount);
                  
                      emit Sent(_operator, _from, _to, _amount, _data, _operatorData);
                      emit Transfer(_from, _to, _amount);
                  
                      // From ERC777: The token contract MUST call the tokensReceived hook after updating the state.
                      receiveHook(_operator, _from, _to, _amount, _data, _operatorData, _enforceERC777);
                    }
                  
                    function receiveHook(address _operator, address _from, address _to, uint256 _amount, bytes memory _data,
                        bytes memory _operatorData, bool _enforceERC777)
                      public
                    {
                      address implementer = ERC1820_REGISTRY.getInterfaceImplementer(_to, ERC777_TOKENS_RECIPIENT_HASH);
                      if (implementer != address(0))
                        IERC777Recipient(implementer).tokensReceived(_operator, _from, _to, _amount, _data, _operatorData);
                      else if (_enforceERC777)
                        require(!isContract(_to), "Must be registered with ERC1820");
                    }
                  
                    function sendHook(address _operator, address _from, address _to, uint256 _amount, bytes memory _data,
                        bytes memory _operatorData)
                      public
                    {
                      address implementer = ERC1820_REGISTRY.getInterfaceImplementer(_from, ERC777_TOKENS_SENDER_HASH);
                      if (implementer != address(0))
                        IERC777Sender(implementer).tokensToSend(_operator, _from, _to, _amount, _data, _operatorData);
                    }
                  
                    function isContract(address _account)
                      private
                      view
                      returns (bool isContract_)
                    {
                      uint256 size;
                  
                      assembly {
                        size := extcodesize(_account)
                      }
                  
                      isContract_ = size != 0;
                    }
                  }
                  
                  // File: contracts/Token.sol
                  
                  pragma solidity 0.6.7;
                  
                  
                  
                  
                  /**
                   * Implements ERC777 with ERC20 as defined in https://eips.ethereum.org/EIPS/eip-777, with minting support.
                   * NOTE: Minting is internal only: derive from this contract according to usage.
                   */
                  contract Token is IERC777, IERC20 {
                  
                    string private tokenName;
                    string private tokenSymbol;
                    uint8 constant private tokenDecimals = 18;
                    uint256 constant private tokenGranularity = 1;
                    TokenState public tokenState;
                  
                    // Universal address as defined in Registry Contract Address section of https://eips.ethereum.org/EIPS/eip-1820
                    IERC1820Registry constant internal ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
                    // keccak256("ERC777Token")
                    bytes32 constant internal ERC777_TOKEN_HASH = 0xac7fbab5f54a3ca8194167523c6753bfeb96a445279294b6125b68cce2177054;
                    // keccak256("ERC20Token")
                    bytes32 constant internal ERC20_TOKEN_HASH = 0xaea199e31a596269b42cdafd93407f14436db6e4cad65417994c2eb37381e05a;
                  
                    event AuthorizedMinter(address minter);
                    event RevokedMinter(address minter);
                  
                    constructor(string memory _name, string memory _symbol, uint256 _initialSupply)
                      internal
                    {
                      require(bytes(_name).length != 0, "Needs a name");
                      require(bytes(_symbol).length != 0, "Needs a symbol");
                      tokenName = _name;
                      tokenSymbol = _symbol;
                      LToken.initState(tokenState, tokenDecimals, _initialSupply);
                  
                      ERC1820_REGISTRY.setInterfaceImplementer(address(this), ERC777_TOKEN_HASH, address(this));
                      ERC1820_REGISTRY.setInterfaceImplementer(address(this), ERC20_TOKEN_HASH, address(this));
                    }
                  
                    modifier onlyOperator(address _holder) {
                      require(isOperatorFor(msg.sender, _holder), "Not an operator");
                      _;
                    }
                  
                    modifier onlyMinter {
                      require(tokenState.minters[msg.sender], "onlyMinter");
                      _;
                    }
                  
                    function name()
                      external
                      view
                      override(IERC777, IERC20)
                      returns (string memory name_)
                    {
                      name_ = tokenName;
                    }
                  
                    function symbol()
                      external
                      view
                      override(IERC777, IERC20)
                      returns (string memory symbol_)
                    {
                      symbol_ = tokenSymbol;
                    }
                  
                    function decimals()
                      external
                      view
                      override
                      returns (uint8 decimals_)
                    {
                      decimals_ = tokenDecimals;
                    }
                  
                    function granularity()
                      external
                      view
                      override
                      returns (uint256 granularity_)
                    {
                      granularity_ = tokenGranularity;
                    }
                  
                    function balanceOf(address _holder)
                      external
                      override(IERC777, IERC20)
                      view
                      returns (uint256 balance_)
                    {
                      balance_ = tokenState.balances[_holder];
                    }
                  
                    function transfer(address _to, uint256 _value)
                      external
                      override
                      returns (bool success_)
                    {
                      doSend(msg.sender, msg.sender, _to, _value, "", "", false);
                      success_ = true;
                    }
                  
                    function transferFrom(address _from, address _to, uint256 _value)
                      external
                      override
                      returns (bool success_)
                    {
                      LToken.transferFrom(tokenState, _from, _to, _value);
                      success_ = true;
                    }
                  
                    function approve(address _spender, uint256 _value)
                      external
                      override
                      returns (bool success_)
                    {
                      LToken.approve(tokenState, _spender, _value);
                      success_ = true;
                    }
                  
                    function allowance(address _holder, address _spender)
                      external
                      view
                      override
                      returns (uint256 remaining_)
                    {
                      remaining_ = tokenState.approvals[_holder][_spender];
                    }
                  
                    function defaultOperators()
                      external
                      view
                      override
                      returns (address[] memory)
                    {
                      return tokenState.defaultOperators;
                    }
                  
                    function authorizeOperator(address _operator)
                      external
                      override
                    {
                      LToken.authorizeOperator(tokenState, _operator);
                    }
                  
                    function revokeOperator(address _operator)
                      external
                      override
                    {
                      LToken.revokeOperator(tokenState, _operator);
                    }
                  
                    function send(address _to, uint256 _amount, bytes calldata _data)
                      external
                      override
                    {
                      doSend(msg.sender, msg.sender, _to, _amount, _data, "", true);
                    }
                  
                    function operatorSend(address _from, address _to, uint256 _amount, bytes calldata _data, bytes calldata _operatorData)
                      external
                      override
                      onlyOperator(_from)
                    {
                      doSend(msg.sender, _from, _to, _amount, _data, _operatorData, true);
                    }
                  
                    function burn(uint256 _amount, bytes calldata _data)
                      external
                      override
                    {
                      doBurn(msg.sender, msg.sender, _amount, _data, "");
                    }
                  
                    function operatorBurn(address _from, uint256 _amount, bytes calldata _data, bytes calldata _operatorData)
                      external
                      override
                      onlyOperator(_from)
                    {
                      doBurn(msg.sender, _from, _amount, _data, _operatorData);
                    }
                  
                    function mint(address _to, uint256 _amount)
                      external
                      onlyMinter
                    {
                      LToken.doMint(tokenState, _to, _amount);
                    }
                  
                    function totalSupply()
                      external
                      view
                      override(IERC777, IERC20)
                      returns (uint256 totalSupply_)
                    {
                      totalSupply_ = tokenState.totalSupply;
                    }
                  
                    function isOperatorFor(address _operator, address _holder)
                      public
                      view
                      override
                      returns (bool isOperatorFor_)
                    {
                      isOperatorFor_ = (_operator == _holder || tokenState.authorizedOperators[_operator][_holder]
                          || _operator == address(this) && !tokenState.defaultOperatorIsRevoked[_holder]);
                    }
                  
                    function doSend(address _operator, address _from, address _to, uint256 _amount, bytes memory _data,
                        bytes memory _operatorData, bool _enforceERC777)
                      internal
                      virtual
                    {
                      LToken.doSend(tokenState, _operator, _from, _to, _amount, _data, _operatorData, _enforceERC777);
                    }
                  
                    function doBurn(address _operator, address _from, uint256 _amount, bytes memory _data, bytes memory _operatorData)
                      internal
                    {
                      LToken.doBurn(tokenState, _operator, _from, _amount, _data, _operatorData);
                    }
                  
                    function authorizeMinter(address _minter)
                      internal
                    {
                      LToken.authorizeMinter(tokenState, _minter);
                  
                      emit AuthorizedMinter(_minter);
                    }
                  
                    function revokeMinter(address _minter)
                      internal
                    {
                      LToken.revokeMinter(tokenState, _minter);
                  
                      emit RevokedMinter(_minter);
                    }
                  }
                  
                  // File: contracts/Owned.sol
                  
                  pragma solidity 0.6.7;
                  
                  contract Owned {
                  
                    address public owner = msg.sender;
                  
                    event LogOwnershipTransferred(address indexed owner, address indexed newOwner);
                  
                    modifier onlyOwner {
                      require(msg.sender == owner, "Sender must be owner");
                      _;
                    }
                  
                    function setOwner(address _owner)
                      external
                      onlyOwner
                    {
                      require(_owner != address(0), "Owner cannot be zero address");
                      emit LogOwnershipTransferred(owner, _owner);
                      owner = _owner;
                    }
                  }
                  
                  // File: contracts/VOWToken.sol
                  
                  pragma solidity 0.6.7;
                  
                  
                  
                  
                  /**
                   * ERC777/20 contract which also:
                   * - is owned
                   * - supports proxying of own tokens (only if signed correctly)
                   * - supports partner contracts, keyed by hash
                   * - supports minting (only by owner approved contracts)
                   * - has a USD price
                   */
                  contract VOWToken is Token, IERC777Recipient, Owned {
                  
                    mapping (bytes32 => bool) public proxyProofs;
                    uint256[2] public usdRate;
                    address public usdRateSetter;
                    mapping(bytes32 => address payable) public partnerContracts;
                  
                    // precalculated hash - see https://github.com/ethereum/solidity/issues/4024
                    // keccak256("ERC777TokensRecipient")
                    bytes32 constant internal ERC777_TOKENS_RECIPIENT_HASH = 0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b;
                  
                    event LogUSDRateSetterSet(address indexed usdRateSetter);
                    event LogUSDRateSet(uint256 numTokens, uint256 numUSD);
                    event LogProxiedTokens(address indexed from, address indexed to, uint256 amount, bytes data, uint256 nonce, bytes proof);
                    event LogPartnerContractSet(bytes32 indexed keyHash, address indexed partnerContract);
                    event LogMintPermissionSet(address indexed contractAddress, bool canMint);
                  
                    constructor(string memory _name, string memory _symbol, uint256 _initialSupply, uint256[2] memory _initialUSDRate)
                      public
                      Token(_name, _symbol, _initialSupply)
                    {
                      doSetUSDRate(_initialUSDRate[0], _initialUSDRate[1]);
                  
                      ERC1820_REGISTRY.setInterfaceImplementer(address(this), ERC777_TOKENS_RECIPIENT_HASH, address(this));
                    }
                  
                    modifier onlyUSDRateSetter() {
                      require(msg.sender == usdRateSetter, "onlyUSDRateSetter");
                      _;
                    }
                  
                    modifier onlyOwnTokens {
                      require(msg.sender == address(this), "onlyOwnTokens");
                      _;
                    }
                  
                    modifier addressNotNull(address _address) {
                      require(_address != address(0), "Address cannot be null");
                      _;
                    }
                  
                    function tokensReceived(address /* _operator */, address /* _from */, address /* _to */, uint256 _amount,
                        bytes calldata _data, bytes calldata /* _operatorData */)
                      external
                      override
                      onlyOwnTokens
                    {
                      (address from, address to, uint256 amount, bytes memory data, uint256 nonce, bytes memory proof) =
                          abi.decode(_data, (address, address, uint256, bytes, uint256, bytes));
                      checkProxying(from, to, amount, data, nonce, proof);
                  
                      if (_amount != 0)
                        this.send(from, _amount, "");
                  
                      this.operatorSend(from, to, amount, data, _data);
                  
                      emit LogProxiedTokens(from, to, amount, data, nonce, proof);
                    }
                  
                    function setPartnerContract(bytes32 _keyHash, address payable _partnerContract)
                      external
                      onlyOwner
                      addressNotNull(_partnerContract)
                    {
                      require(_keyHash != bytes32(0), "Missing key hash");
                      partnerContracts[_keyHash] = _partnerContract;
                  
                      emit LogPartnerContractSet(_keyHash, _partnerContract);
                    }
                  
                    function setUSDRateSetter(address _usdRateSetter)
                      external
                      onlyOwner
                      addressNotNull(_usdRateSetter)
                    {
                      usdRateSetter = _usdRateSetter;
                  
                      emit LogUSDRateSetterSet(_usdRateSetter);
                    }
                  
                    function setUSDRate(uint256 _numTokens, uint256 _numUSD)
                      external
                      onlyUSDRateSetter
                    {
                      doSetUSDRate(_numTokens, _numUSD);
                  
                      emit LogUSDRateSet(_numTokens, _numUSD);
                    }
                  
                    function setMintPermission(address _contract, bool _canMint)
                      external
                      onlyOwner
                      addressNotNull(_contract)
                    {
                      if (_canMint)
                        authorizeMinter(_contract);
                      else
                        revokeMinter(_contract);
                  
                      emit LogMintPermissionSet(_contract, _canMint);
                    }
                  
                    function doSetUSDRate(uint256 _numTokens, uint256 _numUSD)
                      private
                    {
                      require(_numTokens != 0, "numTokens cannot be zero");
                      require(_numUSD != 0, "numUSD cannot be zero");
                      usdRate = [_numTokens, _numUSD];
                    }
                  
                    function checkProxying(address _from, address _to, uint256 _amount, bytes memory _data, uint256 _nonce, bytes memory _proof)
                      private
                    {
                      require(!proxyProofs[keccak256(_proof)], "Proxy proof not unique");
                      proxyProofs[keccak256(_proof)] = true;
                      bytes32 hash = keccak256(abi.encodePacked(address(this), _from, _to, _amount, _data, _nonce));
                      address signer = ECDSA.recover(ECDSA.toEthSignedMessageHash(hash), _proof);
                      require(signer == _from, "Bad signer");
                    }
                  }

                  File 4 of 8: OpenOceanExchangeProxy
                  // File: @openzeppelin/contracts/proxy/Proxy.sol
                  
                  // SPDX-License-Identifier: MIT
                  
                  pragma solidity >=0.6.0 <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 {}
                  }
                  
                  // File: @openzeppelin/contracts/utils/Address.sol
                  
                  pragma solidity >=0.6.2 <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);
                              }
                          }
                      }
                  }
                  
                  // File: @openzeppelin/contracts/proxy/UpgradeableProxy.sol
                  
                  pragma solidity >=0.6.0 <0.8.0;
                  
                  /**
                   * @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.
                   *
                   * Upgradeability is only provided internally through {_upgradeTo}. For an externally upgradeable proxy see
                   * {TransparentUpgradeableProxy}.
                   */
                  contract UpgradeableProxy is Proxy {
                      /**
                       * @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) public payable {
                          assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                          _setImplementation(_logic);
                          if (_data.length > 0) {
                              Address.functionDelegateCall(_logic, _data);
                          }
                      }
                  
                      /**
                       * @dev Emitted when the implementation is upgraded.
                       */
                      event Upgraded(address indexed implementation);
                  
                      /**
                       * @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 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  
                      /**
                       * @dev Returns the current implementation address.
                       */
                      function _implementation() internal view virtual override returns (address impl) {
                          bytes32 slot = _IMPLEMENTATION_SLOT;
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              impl := sload(slot)
                          }
                      }
                  
                      /**
                       * @dev Upgrades the proxy to a new implementation.
                       *
                       * Emits an {Upgraded} event.
                       */
                      function _upgradeTo(address newImplementation) internal virtual {
                          _setImplementation(newImplementation);
                          emit Upgraded(newImplementation);
                      }
                  
                      /**
                       * @dev Stores a new address in the EIP1967 implementation slot.
                       */
                      function _setImplementation(address newImplementation) private {
                          require(Address.isContract(newImplementation), "UpgradeableProxy: new implementation is not a contract");
                  
                          bytes32 slot = _IMPLEMENTATION_SLOT;
                  
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              sstore(slot, newImplementation)
                          }
                      }
                  }
                  
                  // File: @openzeppelin/contracts/proxy/TransparentUpgradeableProxy.sol
                  
                  pragma solidity >=0.6.0 <0.8.0;
                  
                  /**
                   * @dev This contract implements a proxy that is upgradeable by an admin.
                   *
                   * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
                   * clashing], which can potentially be used in an attack, this contract uses the
                   * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
                   * things that go hand in hand:
                   *
                   * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
                   * that call matches one of the admin functions exposed by the proxy itself.
                   * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
                   * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
                   * "admin cannot fallback to proxy target".
                   *
                   * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
                   * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
                   * to sudden errors when trying to call a function from the proxy implementation.
                   *
                   * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
                   * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
                   */
                  contract TransparentUpgradeableProxy is UpgradeableProxy {
                      /**
                       * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                       * optionally initialized with `_data` as explained in {UpgradeableProxy-constructor}.
                       */
                      constructor(
                          address _logic,
                          address admin_,
                          bytes memory _data
                      ) public payable UpgradeableProxy(_logic, _data) {
                          assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                          _setAdmin(admin_);
                      }
                  
                      /**
                       * @dev Emitted when the admin account has changed.
                       */
                      event AdminChanged(address previousAdmin, address newAdmin);
                  
                      /**
                       * @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 private constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                  
                      /**
                       * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                       */
                      modifier ifAdmin() {
                          if (msg.sender == _admin()) {
                              _;
                          } else {
                              _fallback();
                          }
                      }
                  
                      /**
                       * @dev Returns the current admin.
                       *
                       * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                       *
                       * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                       * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                       * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                       */
                      function admin() external ifAdmin returns (address admin_) {
                          admin_ = _admin();
                      }
                  
                      /**
                       * @dev Returns the current implementation.
                       *
                       * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                       *
                       * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                       * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                       * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                       */
                      function implementation() external ifAdmin returns (address implementation_) {
                          implementation_ = _implementation();
                      }
                  
                      /**
                       * @dev Changes the admin of the proxy.
                       *
                       * Emits an {AdminChanged} event.
                       *
                       * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                       */
                      function changeAdmin(address newAdmin) external virtual ifAdmin {
                          require(newAdmin != address(0), "TransparentUpgradeableProxy: new admin is the zero address");
                          emit AdminChanged(_admin(), newAdmin);
                          _setAdmin(newAdmin);
                      }
                  
                      /**
                       * @dev Upgrade the implementation of the proxy.
                       *
                       * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                       */
                      function upgradeTo(address newImplementation) external virtual ifAdmin {
                          _upgradeTo(newImplementation);
                      }
                  
                      /**
                       * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                       * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                       * proxied contract.
                       *
                       * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                       */
                      function upgradeToAndCall(address newImplementation, bytes calldata data) external payable virtual ifAdmin {
                          _upgradeTo(newImplementation);
                          Address.functionDelegateCall(newImplementation, data);
                      }
                  
                      /**
                       * @dev Returns the current admin.
                       */
                      function _admin() internal view virtual returns (address adm) {
                          bytes32 slot = _ADMIN_SLOT;
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              adm := sload(slot)
                          }
                      }
                  
                      /**
                       * @dev Stores a new address in the EIP1967 admin slot.
                       */
                      function _setAdmin(address newAdmin) private {
                          bytes32 slot = _ADMIN_SLOT;
                  
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              sstore(slot, newAdmin)
                          }
                      }
                  
                      /**
                       * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                       */
                      function _beforeFallback() internal virtual override {
                          require(msg.sender != _admin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                          super._beforeFallback();
                      }
                  }
                  
                  // File: contracts/OpenOceanExchangeProxy.sol
                  
                  pragma solidity ^0.6.12;
                  
                  contract OpenOceanExchangeProxy is TransparentUpgradeableProxy {
                      constructor(
                          address logic,
                          address admin,
                          bytes memory data
                      ) public TransparentUpgradeableProxy(logic, admin, data) {}
                  }

                  File 5 of 8: GnosisSafeProxy
                  // SPDX-License-Identifier: LGPL-3.0-only
                  pragma solidity >=0.7.0 <0.9.0;
                  
                  /// @title IProxy - Helper interface to access masterCopy of the Proxy on-chain
                  /// @author Richard Meissner - <richard@gnosis.io>
                  interface IProxy {
                      function masterCopy() external view returns (address);
                  }
                  
                  /// @title GnosisSafeProxy - Generic proxy contract allows to execute all transactions applying the code of a master contract.
                  /// @author Stefan George - <stefan@gnosis.io>
                  /// @author Richard Meissner - <richard@gnosis.io>
                  contract GnosisSafeProxy {
                      // singleton always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated.
                      // To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt`
                      address internal singleton;
                  
                      /// @dev Constructor function sets address of singleton contract.
                      /// @param _singleton Singleton address.
                      constructor(address _singleton) {
                          require(_singleton != address(0), "Invalid singleton address provided");
                          singleton = _singleton;
                      }
                  
                      /// @dev Fallback function forwards all transactions and returns all received return data.
                      fallback() external payable {
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              let _singleton := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
                              // 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s
                              if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) {
                                  mstore(0, _singleton)
                                  return(0, 0x20)
                              }
                              calldatacopy(0, 0, calldatasize())
                              let success := delegatecall(gas(), _singleton, 0, calldatasize(), 0, 0)
                              returndatacopy(0, 0, returndatasize())
                              if eq(success, 0) {
                                  revert(0, returndatasize())
                              }
                              return(0, returndatasize())
                          }
                      }
                  }
                  
                  /// @title Proxy Factory - Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
                  /// @author Stefan George - <stefan@gnosis.pm>
                  contract GnosisSafeProxyFactory {
                      event ProxyCreation(GnosisSafeProxy proxy, address singleton);
                  
                      /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
                      /// @param singleton Address of singleton contract.
                      /// @param data Payload for message call sent to new proxy contract.
                      function createProxy(address singleton, bytes memory data) public returns (GnosisSafeProxy proxy) {
                          proxy = new GnosisSafeProxy(singleton);
                          if (data.length > 0)
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  if eq(call(gas(), proxy, 0, add(data, 0x20), mload(data), 0, 0), 0) {
                                      revert(0, 0)
                                  }
                              }
                          emit ProxyCreation(proxy, singleton);
                      }
                  
                      /// @dev Allows to retrieve the runtime code of a deployed Proxy. This can be used to check that the expected Proxy was deployed.
                      function proxyRuntimeCode() public pure returns (bytes memory) {
                          return type(GnosisSafeProxy).runtimeCode;
                      }
                  
                      /// @dev Allows to retrieve the creation code used for the Proxy deployment. With this it is easily possible to calculate predicted address.
                      function proxyCreationCode() public pure returns (bytes memory) {
                          return type(GnosisSafeProxy).creationCode;
                      }
                  
                      /// @dev Allows to create new proxy contact using CREATE2 but it doesn't run the initializer.
                      ///      This method is only meant as an utility to be called from other methods
                      /// @param _singleton Address of singleton contract.
                      /// @param initializer Payload for message call sent to new proxy contract.
                      /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                      function deployProxyWithNonce(
                          address _singleton,
                          bytes memory initializer,
                          uint256 saltNonce
                      ) internal returns (GnosisSafeProxy proxy) {
                          // If the initializer changes the proxy address should change too. Hashing the initializer data is cheaper than just concatinating it
                          bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce));
                          bytes memory deploymentData = abi.encodePacked(type(GnosisSafeProxy).creationCode, uint256(uint160(_singleton)));
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)
                          }
                          require(address(proxy) != address(0), "Create2 call failed");
                      }
                  
                      /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
                      /// @param _singleton Address of singleton contract.
                      /// @param initializer Payload for message call sent to new proxy contract.
                      /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                      function createProxyWithNonce(
                          address _singleton,
                          bytes memory initializer,
                          uint256 saltNonce
                      ) public returns (GnosisSafeProxy proxy) {
                          proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
                          if (initializer.length > 0)
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  if eq(call(gas(), proxy, 0, add(initializer, 0x20), mload(initializer), 0, 0), 0) {
                                      revert(0, 0)
                                  }
                              }
                          emit ProxyCreation(proxy, _singleton);
                      }
                  
                      /// @dev Allows to create new proxy contact, execute a message call to the new proxy and call a specified callback within one transaction
                      /// @param _singleton Address of singleton contract.
                      /// @param initializer Payload for message call sent to new proxy contract.
                      /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                      /// @param callback Callback that will be invoced after the new proxy contract has been successfully deployed and initialized.
                      function createProxyWithCallback(
                          address _singleton,
                          bytes memory initializer,
                          uint256 saltNonce,
                          IProxyCreationCallback callback
                      ) public returns (GnosisSafeProxy proxy) {
                          uint256 saltNonceWithCallback = uint256(keccak256(abi.encodePacked(saltNonce, callback)));
                          proxy = createProxyWithNonce(_singleton, initializer, saltNonceWithCallback);
                          if (address(callback) != address(0)) callback.proxyCreated(proxy, _singleton, initializer, saltNonce);
                      }
                  
                      /// @dev Allows to get the address for a new proxy contact created via `createProxyWithNonce`
                      ///      This method is only meant for address calculation purpose when you use an initializer that would revert,
                      ///      therefore the response is returned with a revert. When calling this method set `from` to the address of the proxy factory.
                      /// @param _singleton Address of singleton contract.
                      /// @param initializer Payload for message call sent to new proxy contract.
                      /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                      function calculateCreateProxyWithNonceAddress(
                          address _singleton,
                          bytes calldata initializer,
                          uint256 saltNonce
                      ) external returns (GnosisSafeProxy proxy) {
                          proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
                          revert(string(abi.encodePacked(proxy)));
                      }
                  }
                  
                  interface IProxyCreationCallback {
                      function proxyCreated(
                          GnosisSafeProxy proxy,
                          address _singleton,
                          bytes calldata initializer,
                          uint256 saltNonce
                      ) external;
                  }

                  File 6 of 8: OpenOceanExchange
                  /**
                   *Submitted for verification at Etherscan.io on 2023-05-10
                  */
                  
                  // File: @openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol
                  
                  // SPDX-License-Identifier: MIT
                  // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
                  
                  pragma solidity ^0.8.1;
                  
                  /**
                   * @dev Collection of functions related to the address type
                   */
                  library AddressUpgradeable {
                      /**
                       * @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
                       * ====
                       *
                       * [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://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                       *
                       * IMPORTANT: because control is transferred to `recipient`, care must be
                       * taken to not create reentrancy vulnerabilities. Consider using
                       * {ReentrancyGuard} or the
                       * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                       */
                      function sendValue(address payable recipient, uint256 amount) internal {
                          require(address(this).balance >= amount, "Address: insufficient balance");
                  
                          (bool success, ) = recipient.call{value: amount}("");
                          require(success, "Address: unable to send value, recipient may have reverted");
                      }
                  
                      /**
                       * @dev Performs a Solidity function call using a low level `call`. A
                       * plain `call` is an unsafe replacement for a function call: use this
                       * function instead.
                       *
                       * If `target` reverts with a revert reason, it is bubbled up by this
                       * function (like regular Solidity function calls).
                       *
                       * Returns the raw returned data. To convert to the expected return value,
                       * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                       *
                       * Requirements:
                       *
                       * - `target` must be a contract.
                       * - calling `target` with `data` must not revert.
                       *
                       * _Available since v3.1._
                       */
                      function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                          return 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 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);
                          }
                      }
                  }
                  
                  // File: @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol
                  
                  
                  // OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)
                  
                  pragma solidity ^0.8.2;
                  
                  /**
                   * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
                   * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
                   * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
                   * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
                   *
                   * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
                   * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
                   * case an upgrade adds a module that needs to be initialized.
                   *
                   * For example:
                   *
                   * [.hljs-theme-light.nopadding]
                   * ```
                   * contract MyToken is ERC20Upgradeable {
                   *     function initialize() initializer public {
                   *         __ERC20_init("MyToken", "MTK");
                   *     }
                   * }
                   * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
                   *     function initializeV2() reinitializer(2) public {
                   *         __ERC20Permit_init("MyToken");
                   *     }
                   * }
                   * ```
                   *
                   * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
                   * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
                   *
                   * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
                   * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
                   *
                   * [CAUTION]
                   * ====
                   * Avoid leaving a contract uninitialized.
                   *
                   * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
                   * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
                   * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
                   *
                   * [.hljs-theme-light.nopadding]
                   * ```
                   * /// @custom:oz-upgrades-unsafe-allow constructor
                   * constructor() {
                   *     _disableInitializers();
                   * }
                   * ```
                   * ====
                   */
                  abstract contract Initializable {
                      /**
                       * @dev Indicates that the contract has been initialized.
                       * @custom:oz-retyped-from bool
                       */
                      uint8 private _initialized;
                  
                      /**
                       * @dev Indicates that the contract is in the process of being initialized.
                       */
                      bool private _initializing;
                  
                      /**
                       * @dev Triggered when the contract has been initialized or reinitialized.
                       */
                      event Initialized(uint8 version);
                  
                      /**
                       * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
                       * `onlyInitializing` functions can be used to initialize parent contracts.
                       *
                       * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
                       * constructor.
                       *
                       * Emits an {Initialized} event.
                       */
                      modifier initializer() {
                          bool isTopLevelCall = !_initializing;
                          require(
                              (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                              "Initializable: contract is already initialized"
                          );
                          _initialized = 1;
                          if (isTopLevelCall) {
                              _initializing = true;
                          }
                          _;
                          if (isTopLevelCall) {
                              _initializing = false;
                              emit Initialized(1);
                          }
                      }
                  
                      /**
                       * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
                       * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
                       * used to initialize parent contracts.
                       *
                       * A reinitializer may be used after the original initialization step. This is essential to configure modules that
                       * are added through upgrades and that require initialization.
                       *
                       * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
                       * cannot be nested. If one is invoked in the context of another, execution will revert.
                       *
                       * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
                       * a contract, executing them in the right order is up to the developer or operator.
                       *
                       * WARNING: setting the version to 255 will prevent any future reinitialization.
                       *
                       * Emits an {Initialized} event.
                       */
                      modifier reinitializer(uint8 version) {
                          require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                          _initialized = version;
                          _initializing = true;
                          _;
                          _initializing = false;
                          emit Initialized(version);
                      }
                  
                      /**
                       * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
                       * {initializer} and {reinitializer} modifiers, directly or indirectly.
                       */
                      modifier onlyInitializing() {
                          require(_initializing, "Initializable: contract is not initializing");
                          _;
                      }
                  
                      /**
                       * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
                       * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
                       * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
                       * through proxies.
                       *
                       * Emits an {Initialized} event the first time it is successfully executed.
                       */
                      function _disableInitializers() internal virtual {
                          require(!_initializing, "Initializable: contract is initializing");
                          if (_initialized < type(uint8).max) {
                              _initialized = type(uint8).max;
                              emit Initialized(type(uint8).max);
                          }
                      }
                  
                      /**
                       * @dev Returns the highest version that has been initialized. See {reinitializer}.
                       */
                      function _getInitializedVersion() internal view returns (uint8) {
                          return _initialized;
                      }
                  
                      /**
                       * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
                       */
                      function _isInitializing() internal view returns (bool) {
                          return _initializing;
                      }
                  }
                  
                  // File: @openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol
                  
                  
                  // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
                  
                  pragma solidity ^0.8.0;
                  
                  /**
                   * @dev Provides information about the current execution context, including the
                   * sender of the transaction and its data. While these are generally available
                   * via msg.sender and msg.data, they should not be accessed in such a direct
                   * manner, since when dealing with meta-transactions the account sending and
                   * paying for execution may not be the actual sender (as far as an application
                   * is concerned).
                   *
                   * This contract is only required for intermediate, library-like contracts.
                   */
                  abstract contract ContextUpgradeable is Initializable {
                      function __Context_init() internal onlyInitializing {
                      }
                  
                      function __Context_init_unchained() internal onlyInitializing {
                      }
                      function _msgSender() internal view virtual returns (address) {
                          return msg.sender;
                      }
                  
                      function _msgData() internal view virtual returns (bytes calldata) {
                          return msg.data;
                      }
                  
                      /**
                       * @dev This empty reserved space is put in place to allow future versions to add new
                       * variables without shifting down storage in the inheritance chain.
                       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                       */
                      uint256[50] private __gap;
                  }
                  
                  // File: @openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol
                  
                  
                  // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
                  
                  pragma solidity ^0.8.0;
                  
                  
                  /**
                   * @dev Contract module which provides a basic access control mechanism, where
                   * there is an account (an owner) that can be granted exclusive access to
                   * specific functions.
                   *
                   * By default, the owner account will be the one that deploys the contract. This
                   * can later be changed with {transferOwnership}.
                   *
                   * This module is used through inheritance. It will make available the modifier
                   * `onlyOwner`, which can be applied to your functions to restrict their use to
                   * the owner.
                   */
                  abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
                      address private _owner;
                  
                      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  
                      /**
                       * @dev Initializes the contract setting the deployer as the initial owner.
                       */
                      function __Ownable_init() internal onlyInitializing {
                          __Ownable_init_unchained();
                      }
                  
                      function __Ownable_init_unchained() internal onlyInitializing {
                          _transferOwnership(_msgSender());
                      }
                  
                      /**
                       * @dev Throws if called by any account other than the owner.
                       */
                      modifier onlyOwner() {
                          _checkOwner();
                          _;
                      }
                  
                      /**
                       * @dev Returns the address of the current owner.
                       */
                      function owner() public view virtual returns (address) {
                          return _owner;
                      }
                  
                      /**
                       * @dev Throws if the sender is not the owner.
                       */
                      function _checkOwner() internal view virtual {
                          require(owner() == _msgSender(), "Ownable: caller is not the owner");
                      }
                  
                      /**
                       * @dev Leaves the contract without owner. It will not be possible to call
                       * `onlyOwner` functions anymore. Can only be called by the current owner.
                       *
                       * NOTE: Renouncing ownership will leave the contract without an owner,
                       * thereby removing any functionality that is only available to the owner.
                       */
                      function renounceOwnership() public virtual onlyOwner {
                          _transferOwnership(address(0));
                      }
                  
                      /**
                       * @dev Transfers ownership of the contract to a new account (`newOwner`).
                       * Can only be called by the current owner.
                       */
                      function transferOwnership(address newOwner) public virtual onlyOwner {
                          require(newOwner != address(0), "Ownable: new owner is the zero address");
                          _transferOwnership(newOwner);
                      }
                  
                      /**
                       * @dev Transfers ownership of the contract to a new account (`newOwner`).
                       * Internal function without access restriction.
                       */
                      function _transferOwnership(address newOwner) internal virtual {
                          address oldOwner = _owner;
                          _owner = newOwner;
                          emit OwnershipTransferred(oldOwner, newOwner);
                      }
                  
                      /**
                       * @dev This empty reserved space is put in place to allow future versions to add new
                       * variables without shifting down storage in the inheritance chain.
                       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                       */
                      uint256[49] private __gap;
                  }
                  
                  // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
                  
                  
                  // OpenZeppelin Contracts (last updated v4.6.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);
                  }
                  
                  // File: @openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol
                  
                  
                  // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
                  
                  pragma solidity ^0.8.0;
                  
                  /**
                   * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
                   * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
                   *
                   * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
                   * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
                   * need to send a transaction, and thus is not required to hold Ether at all.
                   */
                  interface IERC20Permit {
                      /**
                       * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                       * given ``owner``'s signed approval.
                       *
                       * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                       * ordering also apply here.
                       *
                       * Emits an {Approval} event.
                       *
                       * Requirements:
                       *
                       * - `spender` cannot be the zero address.
                       * - `deadline` must be a timestamp in the future.
                       * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                       * over the EIP712-formatted function arguments.
                       * - the signature must use ``owner``'s current nonce (see {nonces}).
                       *
                       * For more information on the signature format, see the
                       * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                       * section].
                       */
                      function permit(
                          address owner,
                          address spender,
                          uint256 value,
                          uint256 deadline,
                          uint8 v,
                          bytes32 r,
                          bytes32 s
                      ) external;
                  
                      /**
                       * @dev Returns the current nonce for `owner`. This value must be
                       * included whenever a signature is generated for {permit}.
                       *
                       * Every successful call to {permit} increases ``owner``'s nonce by one. This
                       * prevents a signature from being used multiple times.
                       */
                      function nonces(address owner) external view returns (uint256);
                  
                      /**
                       * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                       */
                      // solhint-disable-next-line func-name-mixedcase
                      function DOMAIN_SEPARATOR() external view returns (bytes32);
                  }
                  
                  // File: @openzeppelin/contracts/utils/Address.sol
                  
                  
                  // OpenZeppelin Contracts (last updated v4.8.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
                       * ====
                       *
                       * [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://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                       *
                       * IMPORTANT: because control is transferred to `recipient`, care must be
                       * taken to not create reentrancy vulnerabilities. Consider using
                       * {ReentrancyGuard} or the
                       * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                       */
                      function sendValue(address payable recipient, uint256 amount) internal {
                          require(address(this).balance >= amount, "Address: insufficient balance");
                  
                          (bool success, ) = recipient.call{value: amount}("");
                          require(success, "Address: unable to send value, recipient may have reverted");
                      }
                  
                      /**
                       * @dev Performs a Solidity function call using a low level `call`. A
                       * plain `call` is an unsafe replacement for a function call: use this
                       * function instead.
                       *
                       * If `target` reverts with a revert reason, it is bubbled up by this
                       * function (like regular Solidity function calls).
                       *
                       * Returns the raw returned data. To convert to the expected return value,
                       * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                       *
                       * Requirements:
                       *
                       * - `target` must be a contract.
                       * - calling `target` with `data` must not revert.
                       *
                       * _Available since v3.1._
                       */
                      function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                          return 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);
                          }
                      }
                  }
                  
                  // File: @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
                  
                  
                  // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
                  
                  pragma solidity ^0.8.0;
                  
                  
                  
                  /**
                   * @title SafeERC20
                   * @dev Wrappers around ERC20 operations that throw on failure (when the token
                   * contract returns false). Tokens that return no value (and instead revert or
                   * throw on failure) are also supported, non-reverting calls are assumed to be
                   * successful.
                   * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
                   * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
                   */
                  library SafeERC20 {
                      using Address for address;
                  
                      function safeTransfer(
                          IERC20 token,
                          address to,
                          uint256 value
                      ) internal {
                          _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                      }
                  
                      function safeTransferFrom(
                          IERC20 token,
                          address from,
                          address to,
                          uint256 value
                      ) internal {
                          _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                      }
                  
                      /**
                       * @dev Deprecated. This function has issues similar to the ones found in
                       * {IERC20-approve}, and its usage is discouraged.
                       *
                       * Whenever possible, use {safeIncreaseAllowance} and
                       * {safeDecreaseAllowance} instead.
                       */
                      function safeApprove(
                          IERC20 token,
                          address spender,
                          uint256 value
                      ) internal {
                          // safeApprove should only be called when setting an initial allowance,
                          // or when resetting it to zero. To increase and decrease it, use
                          // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                          require(
                              (value == 0) || (token.allowance(address(this), spender) == 0),
                              "SafeERC20: approve from non-zero to non-zero allowance"
                          );
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                      }
                  
                      function safeIncreaseAllowance(
                          IERC20 token,
                          address spender,
                          uint256 value
                      ) internal {
                          uint256 newAllowance = token.allowance(address(this), spender) + value;
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                      }
                  
                      function safeDecreaseAllowance(
                          IERC20 token,
                          address spender,
                          uint256 value
                      ) internal {
                          unchecked {
                              uint256 oldAllowance = token.allowance(address(this), spender);
                              require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                              uint256 newAllowance = oldAllowance - value;
                              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                          }
                      }
                  
                      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");
                          if (returndata.length > 0) {
                              // Return data is optional
                              require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                          }
                      }
                  }
                  
                  // File: @openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol
                  
                  
                  // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
                  
                  pragma solidity ^0.8.0;
                  
                  
                  /**
                   * @dev Contract module which allows children to implement an emergency stop
                   * mechanism that can be triggered by an authorized account.
                   *
                   * This module is used through inheritance. It will make available the
                   * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
                   * the functions of your contract. Note that they will not be pausable by
                   * simply including this module, only once the modifiers are put in place.
                   */
                  abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
                      /**
                       * @dev Emitted when the pause is triggered by `account`.
                       */
                      event Paused(address account);
                  
                      /**
                       * @dev Emitted when the pause is lifted by `account`.
                       */
                      event Unpaused(address account);
                  
                      bool private _paused;
                  
                      /**
                       * @dev Initializes the contract in unpaused state.
                       */
                      function __Pausable_init() internal onlyInitializing {
                          __Pausable_init_unchained();
                      }
                  
                      function __Pausable_init_unchained() internal onlyInitializing {
                          _paused = false;
                      }
                  
                      /**
                       * @dev Modifier to make a function callable only when the contract is not paused.
                       *
                       * Requirements:
                       *
                       * - The contract must not be paused.
                       */
                      modifier whenNotPaused() {
                          _requireNotPaused();
                          _;
                      }
                  
                      /**
                       * @dev Modifier to make a function callable only when the contract is paused.
                       *
                       * Requirements:
                       *
                       * - The contract must be paused.
                       */
                      modifier whenPaused() {
                          _requirePaused();
                          _;
                      }
                  
                      /**
                       * @dev Returns true if the contract is paused, and false otherwise.
                       */
                      function paused() public view virtual returns (bool) {
                          return _paused;
                      }
                  
                      /**
                       * @dev Throws if the contract is paused.
                       */
                      function _requireNotPaused() internal view virtual {
                          require(!paused(), "Pausable: paused");
                      }
                  
                      /**
                       * @dev Throws if the contract is not paused.
                       */
                      function _requirePaused() internal view virtual {
                          require(paused(), "Pausable: not paused");
                      }
                  
                      /**
                       * @dev Triggers stopped state.
                       *
                       * Requirements:
                       *
                       * - The contract must not be paused.
                       */
                      function _pause() internal virtual whenNotPaused {
                          _paused = true;
                          emit Paused(_msgSender());
                      }
                  
                      /**
                       * @dev Returns to normal state.
                       *
                       * Requirements:
                       *
                       * - The contract must be paused.
                       */
                      function _unpause() internal virtual whenPaused {
                          _paused = false;
                          emit Unpaused(_msgSender());
                      }
                  
                      /**
                       * @dev This empty reserved space is put in place to allow future versions to add new
                       * variables without shifting down storage in the inheritance chain.
                       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                       */
                      uint256[49] private __gap;
                  }
                  
                  // File: contracts/interfaces/IOpenOceanCaller.sol
                  
                  
                  pragma solidity ^0.8.0;
                  
                  
                  interface IOpenOceanCaller {
                      struct CallDescription {
                          uint256 target;
                          uint256 gasLimit;
                          uint256 value;
                          bytes data;
                      }
                  
                      function makeCall(CallDescription memory desc) external;
                  
                      function makeCalls(CallDescription[] memory desc) external payable;
                  }
                  
                  // File: contracts/libraries/RevertReasonParser.sol
                  
                  
                  pragma solidity ^0.8.0;
                  
                  library RevertReasonParser {
                      function parse(bytes memory data, string memory prefix) internal pure returns (string memory) {
                          // https://solidity.readthedocs.io/en/latest/control-structures.html#revert
                          // We assume that revert reason is abi-encoded as Error(string)
                  
                          // 68 = 4-byte selector 0x08c379a0 + 32 bytes offset + 32 bytes length
                          if (data.length >= 68 && data[0] == "\x08" && data[1] == "\xc3" && data[2] == "\x79" && data[3] == "\xa0") {
                              string memory reason;
                              // solhint-disable no-inline-assembly
                              assembly {
                                  // 68 = 32 bytes data length + 4-byte selector + 32 bytes offset
                                  reason := add(data, 68)
                              }
                              /*
                                  revert reason is padded up to 32 bytes with ABI encoder: Error(string)
                                  also sometimes there is extra 32 bytes of zeros padded in the end:
                                  https://github.com/ethereum/solidity/issues/10170
                                  because of that we can't check for equality and instead check
                                  that string length + extra 68 bytes is less than overall data length
                              */
                              require(data.length >= 68 + bytes(reason).length, "Invalid revert reason");
                              return string(abi.encodePacked(prefix, "Error(", reason, ")"));
                          }
                          // 36 = 4-byte selector 0x4e487b71 + 32 bytes integer
                          else if (data.length == 36 && data[0] == "\x4e" && data[1] == "\x48" && data[2] == "\x7b" && data[3] == "\x71") {
                              uint256 code;
                              // solhint-disable no-inline-assembly
                              assembly {
                                  // 36 = 32 bytes data length + 4-byte selector
                                  code := mload(add(data, 36))
                              }
                              return string(abi.encodePacked(prefix, "Panic(", _toHex(code), ")"));
                          }
                  
                          return string(abi.encodePacked(prefix, "Unknown()"));
                      }
                  
                      function _toHex(uint256 value) private pure returns (string memory) {
                          return _toHex(abi.encodePacked(value));
                      }
                  
                      function _toHex(bytes memory data) private pure returns (string memory) {
                          bytes memory alphabet = "0123456789abcdef";
                          bytes memory str = new bytes(2 + data.length * 2);
                          str[0] = "0";
                          str[1] = "x";
                          for (uint256 i = 0; i < data.length; i++) {
                              str[2 * i + 2] = alphabet[uint8(data[i] >> 4)];
                              str[2 * i + 3] = alphabet[uint8(data[i] & 0x0f)];
                          }
                          return string(str);
                      }
                  }
                  
                  // File: @openzeppelin/contracts/utils/math/SafeMath.sol
                  
                  
                  // OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol)
                  
                  pragma solidity ^0.8.0;
                  
                  // CAUTION
                  // This version of SafeMath should only be used with Solidity 0.8 or later,
                  // because it relies on the compiler's built in overflow checks.
                  
                  /**
                   * @dev Wrappers over Solidity's arithmetic operations.
                   *
                   * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
                   * now has built in overflow checking.
                   */
                  library SafeMath {
                      /**
                       * @dev Returns the addition of two unsigned integers, with an overflow flag.
                       *
                       * _Available since v3.4._
                       */
                      function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                          unchecked {
                              uint256 c = a + b;
                              if (c < a) return (false, 0);
                              return (true, c);
                          }
                      }
                  
                      /**
                       * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
                       *
                       * _Available since v3.4._
                       */
                      function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                          unchecked {
                              if (b > a) return (false, 0);
                              return (true, a - b);
                          }
                      }
                  
                      /**
                       * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
                       *
                       * _Available since v3.4._
                       */
                      function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                          unchecked {
                              // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                              // benefit is lost if 'b' is also tested.
                              // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                              if (a == 0) return (true, 0);
                              uint256 c = a * b;
                              if (c / a != b) return (false, 0);
                              return (true, c);
                          }
                      }
                  
                      /**
                       * @dev Returns the division of two unsigned integers, with a division by zero flag.
                       *
                       * _Available since v3.4._
                       */
                      function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                          unchecked {
                              if (b == 0) return (false, 0);
                              return (true, a / b);
                          }
                      }
                  
                      /**
                       * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
                       *
                       * _Available since v3.4._
                       */
                      function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                          unchecked {
                              if (b == 0) return (false, 0);
                              return (true, a % b);
                          }
                      }
                  
                      /**
                       * @dev Returns the addition of two unsigned integers, reverting on
                       * overflow.
                       *
                       * Counterpart to Solidity's `+` operator.
                       *
                       * Requirements:
                       *
                       * - Addition cannot overflow.
                       */
                      function add(uint256 a, uint256 b) internal pure returns (uint256) {
                          return a + b;
                      }
                  
                      /**
                       * @dev Returns the subtraction of two unsigned integers, reverting on
                       * overflow (when the result is negative).
                       *
                       * Counterpart to Solidity's `-` operator.
                       *
                       * Requirements:
                       *
                       * - Subtraction cannot overflow.
                       */
                      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                          return a - b;
                      }
                  
                      /**
                       * @dev Returns the multiplication of two unsigned integers, reverting on
                       * overflow.
                       *
                       * Counterpart to Solidity's `*` operator.
                       *
                       * Requirements:
                       *
                       * - Multiplication cannot overflow.
                       */
                      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                          return a * b;
                      }
                  
                      /**
                       * @dev Returns the integer division of two unsigned integers, reverting on
                       * division by zero. The result is rounded towards zero.
                       *
                       * Counterpart to Solidity's `/` operator.
                       *
                       * Requirements:
                       *
                       * - The divisor cannot be zero.
                       */
                      function div(uint256 a, uint256 b) internal pure returns (uint256) {
                          return a / b;
                      }
                  
                      /**
                       * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                       * reverting when dividing by zero.
                       *
                       * Counterpart to Solidity's `%` operator. This function uses a `revert`
                       * opcode (which leaves remaining gas untouched) while Solidity uses an
                       * invalid opcode to revert (consuming all remaining gas).
                       *
                       * Requirements:
                       *
                       * - The divisor cannot be zero.
                       */
                      function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                          return a % b;
                      }
                  
                      /**
                       * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                       * overflow (when the result is negative).
                       *
                       * CAUTION: This function is deprecated because it requires allocating memory for the error
                       * message unnecessarily. For custom revert reasons use {trySub}.
                       *
                       * Counterpart to Solidity's `-` operator.
                       *
                       * Requirements:
                       *
                       * - Subtraction cannot overflow.
                       */
                      function sub(
                          uint256 a,
                          uint256 b,
                          string memory errorMessage
                      ) internal pure returns (uint256) {
                          unchecked {
                              require(b <= a, errorMessage);
                              return a - b;
                          }
                      }
                  
                      /**
                       * @dev Returns the integer division of two unsigned integers, reverting with custom message on
                       * division by zero. The result is rounded towards zero.
                       *
                       * Counterpart to Solidity's `/` operator. Note: this function uses a
                       * `revert` opcode (which leaves remaining gas untouched) while Solidity
                       * uses an invalid opcode to revert (consuming all remaining gas).
                       *
                       * Requirements:
                       *
                       * - The divisor cannot be zero.
                       */
                      function div(
                          uint256 a,
                          uint256 b,
                          string memory errorMessage
                      ) internal pure returns (uint256) {
                          unchecked {
                              require(b > 0, errorMessage);
                              return a / b;
                          }
                      }
                  
                      /**
                       * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                       * reverting with custom message when dividing by zero.
                       *
                       * CAUTION: This function is deprecated because it requires allocating memory for the error
                       * message unnecessarily. For custom revert reasons use {tryMod}.
                       *
                       * Counterpart to Solidity's `%` operator. This function uses a `revert`
                       * opcode (which leaves remaining gas untouched) while Solidity uses an
                       * invalid opcode to revert (consuming all remaining gas).
                       *
                       * Requirements:
                       *
                       * - The divisor cannot be zero.
                       */
                      function mod(
                          uint256 a,
                          uint256 b,
                          string memory errorMessage
                      ) internal pure returns (uint256) {
                          unchecked {
                              require(b > 0, errorMessage);
                              return a % b;
                          }
                      }
                  }
                  
                  // File: contracts/libraries/UniversalERC20.sol
                  
                  
                  pragma solidity ^0.8.0;
                  
                  
                  
                  library UniversalERC20 {
                      using SafeMath for uint256;
                      using SafeERC20 for IERC20;
                  
                      IERC20 internal constant ZERO_ADDRESS = IERC20(0x0000000000000000000000000000000000000000);
                      IERC20 internal constant ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
                      IERC20 internal constant MATIC_ADDRESS = IERC20(0x0000000000000000000000000000000000001010);
                  
                      function universalTransfer(
                          IERC20 token,
                          address payable to,
                          uint256 amount
                      ) internal {
                          if (amount > 0) {
                              if (isETH(token)) {
                                  (bool result, ) = to.call{value: amount}("");
                                  require(result, "Failed to transfer ETH");
                              } else {
                                  token.safeTransfer(to, amount);
                              }
                          }
                      }
                  
                      function universalApprove(
                          IERC20 token,
                          address to,
                          uint256 amount
                      ) internal {
                          require(!isETH(token), "Approve called on ETH");
                  
                          if (amount == 0) {
                              token.safeApprove(to, 0);
                          } else {
                              uint256 allowance = token.allowance(address(this), to);
                              if (allowance < amount) {
                                  if (allowance > 0) {
                                      token.safeApprove(to, 0);
                                  }
                                  token.safeApprove(to, amount);
                              }
                          }
                      }
                  
                      function universalBalanceOf(IERC20 token, address account) internal view returns (uint256) {
                          if (isETH(token)) {
                              return account.balance;
                          } else {
                              return token.balanceOf(account);
                          }
                      }
                  
                      function isETH(IERC20 token) internal pure returns (bool) {
                          return
                              address(token) == address(ETH_ADDRESS) ||
                              address(token) == address(MATIC_ADDRESS) ||
                              address(token) == address(ZERO_ADDRESS);
                      }
                  }
                  
                  // File: contracts/libraries/EthRejector.sol
                  
                  
                  pragma solidity ^0.8.0;
                  
                  abstract contract EthRejector {
                      receive() external payable {
                          // require(msg.sender != tx.origin, "ETH deposit rejected");
                      }
                  }
                  
                  // File: contracts/libraries/Permitable.sol
                  
                  
                  pragma solidity ^0.8.0;
                  
                  /// @title Interface for DAI-style permits
                  interface IDaiLikePermit {
                      function permit(
                          address holder,
                          address spender,
                          uint256 nonce,
                          uint256 expiry,
                          bool allowed,
                          uint8 v,
                          bytes32 r,
                          bytes32 s
                      ) external;
                  }
                  
                  /// @title Base contract with common permit handling logics
                  contract Permitable {
                      function _permit(address token, bytes calldata permit) internal {
                          if (permit.length > 0) {
                              bool success;
                              bytes memory result;
                              if (permit.length == 32 * 7) {
                                  // solhint-disable-next-line avoid-low-level-calls
                                  (success, result) = token.call(abi.encodePacked(IERC20Permit.permit.selector, permit));
                              } else if (permit.length == 32 * 8) {
                                  // solhint-disable-next-line avoid-low-level-calls
                                  (success, result) = token.call(abi.encodePacked(IDaiLikePermit.permit.selector, permit));
                              } else {
                                  revert("Wrong permit length");
                              }
                              if (!success) {
                                  revert(RevertReasonParser.parse(result, "Permit failed: "));
                              }
                          }
                      }
                  }
                  
                  // File: contracts/UniswapV2Exchange.sol
                  
                  
                  
                  pragma solidity ^0.8.0;
                  
                  
                  contract UniswapV2Exchange is EthRejector, Permitable {
                      uint256 private constant TRANSFER_FROM_CALL_SELECTOR_32 = 0x23b872dd00000000000000000000000000000000000000000000000000000000;
                      uint256 private constant WETH_DEPOSIT_CALL_SELECTOR_32 = 0xd0e30db000000000000000000000000000000000000000000000000000000000;
                      uint256 private constant WETH_WITHDRAW_CALL_SELECTOR_32 = 0x2e1a7d4d00000000000000000000000000000000000000000000000000000000;
                      uint256 private constant ERC20_TRANSFER_CALL_SELECTOR_32 = 0xa9059cbb00000000000000000000000000000000000000000000000000000000;
                      uint256 private constant ADDRESS_MASK = 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff;
                      uint256 private constant REVERSE_MASK = 0x8000000000000000000000000000000000000000000000000000000000000000;
                      uint256 private constant WETH_MASK = 0x4000000000000000000000000000000000000000000000000000000000000000;
                      uint256 private constant NUMERATOR_MASK = 0x0000000000000000ffffffff0000000000000000000000000000000000000000;
                      uint256 private constant WETH = 0x000000000000000000000000C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
                      uint256 private constant UNISWAP_PAIR_RESERVES_CALL_SELECTOR_32 =
                          0x0902f1ac00000000000000000000000000000000000000000000000000000000;
                      uint256 private constant UNISWAP_PAIR_SWAP_CALL_SELECTOR_32 =
                          0x022c0d9f00000000000000000000000000000000000000000000000000000000;
                      uint256 private constant DENOMINATOR = 1000000000;
                      uint256 private constant NUMERATOR_OFFSET = 160;
                  
                      function callUniswapToWithPermit(
                          IERC20 srcToken,
                          uint256 amount,
                          uint256 minReturn,
                          bytes32[] calldata pools,
                          bytes calldata permit,
                          address payable recipient
                      ) external returns (uint256 returnAmount) {
                          _permit(address(srcToken), permit);
                          return callUniswapTo(srcToken, amount, minReturn, pools, recipient);
                      }
                  
                      function callUniswapWithPermit(
                          IERC20 srcToken,
                          uint256 amount,
                          uint256 minReturn,
                          bytes32[] calldata pools,
                          bytes calldata permit
                      ) external returns (uint256 returnAmount) {
                          _permit(address(srcToken), permit);
                          return callUniswap(srcToken, amount, minReturn, pools);
                      }
                  
                      function callUniswapTo(
                          IERC20 srcToken,
                          uint256 amount,
                          uint256 minReturn,
                          bytes32[] calldata /* pools */,
                          address payable recipient
                      ) public payable returns (uint256 returnAmount) {
                          assembly {
                              // solhint-disable-line no-inline-assembly
                              function reRevert() {
                                  returndatacopy(0, 0, returndatasize())
                                  revert(0, returndatasize())
                              }
                  
                              function revertWithReason(m, len) {
                                  mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
                                  mstore(0x20, 0x0000002000000000000000000000000000000000000000000000000000000000)
                                  mstore(0x40, m)
                                  revert(0, len)
                              }
                  
                              function swap(emptyPtr, swapAmount, pair, reversed, numerator, dst) -> ret {
                                  mstore(emptyPtr, UNISWAP_PAIR_RESERVES_CALL_SELECTOR_32)
                                  if iszero(staticcall(gas(), pair, emptyPtr, 0x4, emptyPtr, 0x40)) {
                                      reRevert()
                                  }
                  
                                  let reserve0 := mload(emptyPtr)
                                  let reserve1 := mload(add(emptyPtr, 0x20))
                                  if reversed {
                                      let tmp := reserve0
                                      reserve0 := reserve1
                                      reserve1 := tmp
                                  }
                                  ret := mul(swapAmount, numerator)
                                  ret := div(mul(ret, reserve1), add(ret, mul(reserve0, DENOMINATOR)))
                  
                                  mstore(emptyPtr, UNISWAP_PAIR_SWAP_CALL_SELECTOR_32)
                                  switch reversed
                                  case 0 {
                                      mstore(add(emptyPtr, 0x04), 0)
                                      mstore(add(emptyPtr, 0x24), ret)
                                  }
                                  default {
                                      mstore(add(emptyPtr, 0x04), ret)
                                      mstore(add(emptyPtr, 0x24), 0)
                                  }
                                  mstore(add(emptyPtr, 0x44), dst)
                                  mstore(add(emptyPtr, 0x64), 0x80)
                                  mstore(add(emptyPtr, 0x84), 0)
                                  if iszero(call(gas(), pair, 0, emptyPtr, 0xa4, 0, 0)) {
                                      reRevert()
                                  }
                              }
                  
                              function callSwap(emptyPtr, token, srcAmount, swapCaller, receiver, min) -> ret {
                                  let poolsOffset := add(calldataload(0x64), 0x4)
                                  let poolsEndOffset := calldataload(poolsOffset)
                                  poolsOffset := add(poolsOffset, 0x20)
                                  poolsEndOffset := add(poolsOffset, mul(0x20, poolsEndOffset))
                                  let rawPair := calldataload(poolsOffset)
                                  switch token
                                  case 0 {
                                      if iszero(eq(srcAmount, callvalue())) {
                                          revertWithReason(0x00000011696e76616c6964206d73672e76616c75650000000000000000000000, 0x55) // "invalid msg.value"
                                      }
                  
                                      mstore(emptyPtr, WETH_DEPOSIT_CALL_SELECTOR_32)
                                      if iszero(call(gas(), WETH, srcAmount, emptyPtr, 0x4, 0, 0)) {
                                          reRevert()
                                      }
                  
                                      mstore(emptyPtr, ERC20_TRANSFER_CALL_SELECTOR_32)
                                      mstore(add(emptyPtr, 0x4), and(rawPair, ADDRESS_MASK))
                                      mstore(add(emptyPtr, 0x24), srcAmount)
                                      if iszero(call(gas(), WETH, 0, emptyPtr, 0x44, 0, 0)) {
                                          reRevert()
                                      }
                                  }
                                  default {
                                      if callvalue() {
                                          revertWithReason(0x00000011696e76616c6964206d73672e76616c75650000000000000000000000, 0x55) // "invalid msg.value"
                                      }
                  
                                      mstore(emptyPtr, TRANSFER_FROM_CALL_SELECTOR_32)
                                      mstore(add(emptyPtr, 0x4), swapCaller)
                                      mstore(add(emptyPtr, 0x24), and(rawPair, ADDRESS_MASK))
                                      mstore(add(emptyPtr, 0x44), srcAmount)
                                      if iszero(call(gas(), token, 0, emptyPtr, 0x64, 0, 0)) {
                                          reRevert()
                                      }
                                  }
                  
                                  ret := srcAmount
                  
                                  for {
                                      let i := add(poolsOffset, 0x20)
                                  } lt(i, poolsEndOffset) {
                                      i := add(i, 0x20)
                                  } {
                                      let nextRawPair := calldataload(i)
                  
                                      ret := swap(
                                          emptyPtr,
                                          ret,
                                          and(rawPair, ADDRESS_MASK),
                                          and(rawPair, REVERSE_MASK),
                                          shr(NUMERATOR_OFFSET, and(rawPair, NUMERATOR_MASK)),
                                          and(nextRawPair, ADDRESS_MASK)
                                      )
                  
                                      rawPair := nextRawPair
                                  }
                  
                                  ret := swap(
                                      emptyPtr,
                                      ret,
                                      and(rawPair, ADDRESS_MASK),
                                      and(rawPair, REVERSE_MASK),
                                      shr(NUMERATOR_OFFSET, and(rawPair, NUMERATOR_MASK)),
                                      address()
                                  )
                  
                                  if lt(ret, min) {
                                      revertWithReason(0x000000164d696e2072657475726e206e6f742072656163686564000000000000, 0x5a) // "Min return not reached"
                                  }
                  
                                  mstore(emptyPtr, 0xd21220a700000000000000000000000000000000000000000000000000000000)
                                  if and(rawPair, REVERSE_MASK) {
                                      mstore(emptyPtr, 0x0dfe168100000000000000000000000000000000000000000000000000000000)
                                  }
                                  if iszero(staticcall(gas(), and(rawPair, ADDRESS_MASK), emptyPtr, 0x4, emptyPtr, 0x40)) {
                                      reRevert()
                                  }
                                  let dstToken := mload(emptyPtr)
                  
                                  let finalAmount := div(
                                      mul(calldataload(0x44), 0x2710),
                                      sub(
                                          10000,
                                          shr(
                                              232,
                                              and(
                                                  calldataload(add(add(calldataload(0x64), 0x4), 0x20)),
                                                  0x00ffff0000000000000000000000000000000000000000000000000000000000
                                              )
                                          )
                                      )
                                  )
                                  switch gt(ret, finalAmount)
                                  case 1 {
                                      switch and(rawPair, WETH_MASK)
                                      case 0 {
                                          mstore(emptyPtr, ERC20_TRANSFER_CALL_SELECTOR_32)
                                          mstore(add(emptyPtr, 0x4), receiver)
                                          mstore(add(emptyPtr, 0x24), finalAmount)
                                          if iszero(call(gas(), dstToken, 0, emptyPtr, 0x44, 0, 0)) {
                                              reRevert()
                                          }
                  
                                          mstore(add(emptyPtr, 0x4), 0x353c1f0bc78fbbc245b3C93ef77b1DCC5b77D2A0)
                                          mstore(add(emptyPtr, 0x24), sub(ret, finalAmount))
                                          if iszero(call(gas(), dstToken, 0, emptyPtr, 0x44, 0, 0)) {
                                              reRevert()
                                          }
                                      }
                                      default {
                                          mstore(emptyPtr, WETH_WITHDRAW_CALL_SELECTOR_32)
                                          mstore(add(emptyPtr, 0x04), ret)
                                          if iszero(call(gas(), WETH, 0, emptyPtr, 0x24, 0, 0)) {
                                              reRevert()
                                          }
                  
                                          if iszero(call(gas(), receiver, finalAmount, 0, 0, 0, 0)) {
                                              reRevert()
                                          }
                  
                                          if iszero(call(gas(), 0x353c1f0bc78fbbc245b3C93ef77b1DCC5b77D2A0, sub(ret, finalAmount), 0, 0, 0, 0)) {
                                              reRevert()
                                          }
                                      }
                                  }
                                  default {
                                      switch and(rawPair, WETH_MASK)
                                      case 0 {
                                          mstore(emptyPtr, ERC20_TRANSFER_CALL_SELECTOR_32)
                                          mstore(add(emptyPtr, 0x4), receiver)
                                          mstore(add(emptyPtr, 0x24), ret)
                                          if iszero(call(gas(), dstToken, 0, emptyPtr, 0x44, 0, 0)) {
                                              reRevert()
                                          }
                                      }
                                      default {
                                          mstore(emptyPtr, WETH_WITHDRAW_CALL_SELECTOR_32)
                                          mstore(add(emptyPtr, 0x04), ret)
                                          if iszero(call(gas(), WETH, 0, emptyPtr, 0x24, 0, 0)) {
                                              reRevert()
                                          }
                  
                                          if iszero(call(gas(), receiver, ret, 0, 0, 0, 0)) {
                                              reRevert()
                                          }
                                      }
                                  }
                              }
                  
                              let emptyPtr := mload(0x40)
                              mstore(0x40, add(emptyPtr, 0xc0))
                              returnAmount := callSwap(emptyPtr, srcToken, amount, caller(), recipient, minReturn)
                          }
                      }
                  
                      function callUniswap(
                          IERC20 srcToken,
                          uint256 amount,
                          uint256 minReturn,
                          bytes32[] calldata pools
                      ) public payable returns (uint256 returnAmount) {
                          return callUniswapTo(srcToken, amount, minReturn, pools, payable(msg.sender));
                      }
                  }
                  
                  // File: @openzeppelin/contracts/utils/math/SafeCast.sol
                  
                  
                  // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
                  // This file was procedurally generated from scripts/generate/templates/SafeCast.js.
                  
                  pragma solidity ^0.8.0;
                  
                  /**
                   * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
                   * checks.
                   *
                   * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
                   * easily result in undesired exploitation or bugs, since developers usually
                   * assume that overflows raise errors. `SafeCast` restores this intuition by
                   * reverting the transaction when such an operation overflows.
                   *
                   * Using this library instead of the unchecked operations eliminates an entire
                   * class of bugs, so it's recommended to use it always.
                   *
                   * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
                   * all math on `uint256` and `int256` and then downcasting.
                   */
                  library SafeCast {
                      /**
                       * @dev Returns the downcasted uint248 from uint256, reverting on
                       * overflow (when the input is greater than largest uint248).
                       *
                       * Counterpart to Solidity's `uint248` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 248 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint248(uint256 value) internal pure returns (uint248) {
                          require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
                          return uint248(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint240 from uint256, reverting on
                       * overflow (when the input is greater than largest uint240).
                       *
                       * Counterpart to Solidity's `uint240` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 240 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint240(uint256 value) internal pure returns (uint240) {
                          require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
                          return uint240(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint232 from uint256, reverting on
                       * overflow (when the input is greater than largest uint232).
                       *
                       * Counterpart to Solidity's `uint232` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 232 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint232(uint256 value) internal pure returns (uint232) {
                          require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
                          return uint232(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint224 from uint256, reverting on
                       * overflow (when the input is greater than largest uint224).
                       *
                       * Counterpart to Solidity's `uint224` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 224 bits
                       *
                       * _Available since v4.2._
                       */
                      function toUint224(uint256 value) internal pure returns (uint224) {
                          require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
                          return uint224(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint216 from uint256, reverting on
                       * overflow (when the input is greater than largest uint216).
                       *
                       * Counterpart to Solidity's `uint216` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 216 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint216(uint256 value) internal pure returns (uint216) {
                          require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
                          return uint216(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint208 from uint256, reverting on
                       * overflow (when the input is greater than largest uint208).
                       *
                       * Counterpart to Solidity's `uint208` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 208 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint208(uint256 value) internal pure returns (uint208) {
                          require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
                          return uint208(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint200 from uint256, reverting on
                       * overflow (when the input is greater than largest uint200).
                       *
                       * Counterpart to Solidity's `uint200` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 200 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint200(uint256 value) internal pure returns (uint200) {
                          require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
                          return uint200(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint192 from uint256, reverting on
                       * overflow (when the input is greater than largest uint192).
                       *
                       * Counterpart to Solidity's `uint192` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 192 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint192(uint256 value) internal pure returns (uint192) {
                          require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
                          return uint192(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint184 from uint256, reverting on
                       * overflow (when the input is greater than largest uint184).
                       *
                       * Counterpart to Solidity's `uint184` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 184 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint184(uint256 value) internal pure returns (uint184) {
                          require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
                          return uint184(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint176 from uint256, reverting on
                       * overflow (when the input is greater than largest uint176).
                       *
                       * Counterpart to Solidity's `uint176` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 176 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint176(uint256 value) internal pure returns (uint176) {
                          require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
                          return uint176(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint168 from uint256, reverting on
                       * overflow (when the input is greater than largest uint168).
                       *
                       * Counterpart to Solidity's `uint168` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 168 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint168(uint256 value) internal pure returns (uint168) {
                          require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
                          return uint168(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint160 from uint256, reverting on
                       * overflow (when the input is greater than largest uint160).
                       *
                       * Counterpart to Solidity's `uint160` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 160 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint160(uint256 value) internal pure returns (uint160) {
                          require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
                          return uint160(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint152 from uint256, reverting on
                       * overflow (when the input is greater than largest uint152).
                       *
                       * Counterpart to Solidity's `uint152` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 152 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint152(uint256 value) internal pure returns (uint152) {
                          require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
                          return uint152(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint144 from uint256, reverting on
                       * overflow (when the input is greater than largest uint144).
                       *
                       * Counterpart to Solidity's `uint144` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 144 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint144(uint256 value) internal pure returns (uint144) {
                          require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
                          return uint144(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint136 from uint256, reverting on
                       * overflow (when the input is greater than largest uint136).
                       *
                       * Counterpart to Solidity's `uint136` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 136 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint136(uint256 value) internal pure returns (uint136) {
                          require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
                          return uint136(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint128 from uint256, reverting on
                       * overflow (when the input is greater than largest uint128).
                       *
                       * Counterpart to Solidity's `uint128` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 128 bits
                       *
                       * _Available since v2.5._
                       */
                      function toUint128(uint256 value) internal pure returns (uint128) {
                          require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
                          return uint128(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint120 from uint256, reverting on
                       * overflow (when the input is greater than largest uint120).
                       *
                       * Counterpart to Solidity's `uint120` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 120 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint120(uint256 value) internal pure returns (uint120) {
                          require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
                          return uint120(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint112 from uint256, reverting on
                       * overflow (when the input is greater than largest uint112).
                       *
                       * Counterpart to Solidity's `uint112` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 112 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint112(uint256 value) internal pure returns (uint112) {
                          require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
                          return uint112(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint104 from uint256, reverting on
                       * overflow (when the input is greater than largest uint104).
                       *
                       * Counterpart to Solidity's `uint104` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 104 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint104(uint256 value) internal pure returns (uint104) {
                          require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
                          return uint104(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint96 from uint256, reverting on
                       * overflow (when the input is greater than largest uint96).
                       *
                       * Counterpart to Solidity's `uint96` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 96 bits
                       *
                       * _Available since v4.2._
                       */
                      function toUint96(uint256 value) internal pure returns (uint96) {
                          require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
                          return uint96(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint88 from uint256, reverting on
                       * overflow (when the input is greater than largest uint88).
                       *
                       * Counterpart to Solidity's `uint88` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 88 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint88(uint256 value) internal pure returns (uint88) {
                          require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
                          return uint88(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint80 from uint256, reverting on
                       * overflow (when the input is greater than largest uint80).
                       *
                       * Counterpart to Solidity's `uint80` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 80 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint80(uint256 value) internal pure returns (uint80) {
                          require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
                          return uint80(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint72 from uint256, reverting on
                       * overflow (when the input is greater than largest uint72).
                       *
                       * Counterpart to Solidity's `uint72` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 72 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint72(uint256 value) internal pure returns (uint72) {
                          require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
                          return uint72(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint64 from uint256, reverting on
                       * overflow (when the input is greater than largest uint64).
                       *
                       * Counterpart to Solidity's `uint64` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 64 bits
                       *
                       * _Available since v2.5._
                       */
                      function toUint64(uint256 value) internal pure returns (uint64) {
                          require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
                          return uint64(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint56 from uint256, reverting on
                       * overflow (when the input is greater than largest uint56).
                       *
                       * Counterpart to Solidity's `uint56` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 56 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint56(uint256 value) internal pure returns (uint56) {
                          require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
                          return uint56(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint48 from uint256, reverting on
                       * overflow (when the input is greater than largest uint48).
                       *
                       * Counterpart to Solidity's `uint48` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 48 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint48(uint256 value) internal pure returns (uint48) {
                          require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
                          return uint48(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint40 from uint256, reverting on
                       * overflow (when the input is greater than largest uint40).
                       *
                       * Counterpart to Solidity's `uint40` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 40 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint40(uint256 value) internal pure returns (uint40) {
                          require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
                          return uint40(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint32 from uint256, reverting on
                       * overflow (when the input is greater than largest uint32).
                       *
                       * Counterpart to Solidity's `uint32` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 32 bits
                       *
                       * _Available since v2.5._
                       */
                      function toUint32(uint256 value) internal pure returns (uint32) {
                          require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
                          return uint32(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint24 from uint256, reverting on
                       * overflow (when the input is greater than largest uint24).
                       *
                       * Counterpart to Solidity's `uint24` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 24 bits
                       *
                       * _Available since v4.7._
                       */
                      function toUint24(uint256 value) internal pure returns (uint24) {
                          require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
                          return uint24(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint16 from uint256, reverting on
                       * overflow (when the input is greater than largest uint16).
                       *
                       * Counterpart to Solidity's `uint16` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 16 bits
                       *
                       * _Available since v2.5._
                       */
                      function toUint16(uint256 value) internal pure returns (uint16) {
                          require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
                          return uint16(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted uint8 from uint256, reverting on
                       * overflow (when the input is greater than largest uint8).
                       *
                       * Counterpart to Solidity's `uint8` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 8 bits
                       *
                       * _Available since v2.5._
                       */
                      function toUint8(uint256 value) internal pure returns (uint8) {
                          require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
                          return uint8(value);
                      }
                  
                      /**
                       * @dev Converts a signed int256 into an unsigned uint256.
                       *
                       * Requirements:
                       *
                       * - input must be greater than or equal to 0.
                       *
                       * _Available since v3.0._
                       */
                      function toUint256(int256 value) internal pure returns (uint256) {
                          require(value >= 0, "SafeCast: value must be positive");
                          return uint256(value);
                      }
                  
                      /**
                       * @dev Returns the downcasted int248 from int256, reverting on
                       * overflow (when the input is less than smallest int248 or
                       * greater than largest int248).
                       *
                       * Counterpart to Solidity's `int248` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 248 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt248(int256 value) internal pure returns (int248 downcasted) {
                          downcasted = int248(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int240 from int256, reverting on
                       * overflow (when the input is less than smallest int240 or
                       * greater than largest int240).
                       *
                       * Counterpart to Solidity's `int240` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 240 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt240(int256 value) internal pure returns (int240 downcasted) {
                          downcasted = int240(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int232 from int256, reverting on
                       * overflow (when the input is less than smallest int232 or
                       * greater than largest int232).
                       *
                       * Counterpart to Solidity's `int232` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 232 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt232(int256 value) internal pure returns (int232 downcasted) {
                          downcasted = int232(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int224 from int256, reverting on
                       * overflow (when the input is less than smallest int224 or
                       * greater than largest int224).
                       *
                       * Counterpart to Solidity's `int224` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 224 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt224(int256 value) internal pure returns (int224 downcasted) {
                          downcasted = int224(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int216 from int256, reverting on
                       * overflow (when the input is less than smallest int216 or
                       * greater than largest int216).
                       *
                       * Counterpart to Solidity's `int216` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 216 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt216(int256 value) internal pure returns (int216 downcasted) {
                          downcasted = int216(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int208 from int256, reverting on
                       * overflow (when the input is less than smallest int208 or
                       * greater than largest int208).
                       *
                       * Counterpart to Solidity's `int208` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 208 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt208(int256 value) internal pure returns (int208 downcasted) {
                          downcasted = int208(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int200 from int256, reverting on
                       * overflow (when the input is less than smallest int200 or
                       * greater than largest int200).
                       *
                       * Counterpart to Solidity's `int200` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 200 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt200(int256 value) internal pure returns (int200 downcasted) {
                          downcasted = int200(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int192 from int256, reverting on
                       * overflow (when the input is less than smallest int192 or
                       * greater than largest int192).
                       *
                       * Counterpart to Solidity's `int192` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 192 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt192(int256 value) internal pure returns (int192 downcasted) {
                          downcasted = int192(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int184 from int256, reverting on
                       * overflow (when the input is less than smallest int184 or
                       * greater than largest int184).
                       *
                       * Counterpart to Solidity's `int184` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 184 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt184(int256 value) internal pure returns (int184 downcasted) {
                          downcasted = int184(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int176 from int256, reverting on
                       * overflow (when the input is less than smallest int176 or
                       * greater than largest int176).
                       *
                       * Counterpart to Solidity's `int176` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 176 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt176(int256 value) internal pure returns (int176 downcasted) {
                          downcasted = int176(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int168 from int256, reverting on
                       * overflow (when the input is less than smallest int168 or
                       * greater than largest int168).
                       *
                       * Counterpart to Solidity's `int168` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 168 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt168(int256 value) internal pure returns (int168 downcasted) {
                          downcasted = int168(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int160 from int256, reverting on
                       * overflow (when the input is less than smallest int160 or
                       * greater than largest int160).
                       *
                       * Counterpart to Solidity's `int160` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 160 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt160(int256 value) internal pure returns (int160 downcasted) {
                          downcasted = int160(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int152 from int256, reverting on
                       * overflow (when the input is less than smallest int152 or
                       * greater than largest int152).
                       *
                       * Counterpart to Solidity's `int152` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 152 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt152(int256 value) internal pure returns (int152 downcasted) {
                          downcasted = int152(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int144 from int256, reverting on
                       * overflow (when the input is less than smallest int144 or
                       * greater than largest int144).
                       *
                       * Counterpart to Solidity's `int144` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 144 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt144(int256 value) internal pure returns (int144 downcasted) {
                          downcasted = int144(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int136 from int256, reverting on
                       * overflow (when the input is less than smallest int136 or
                       * greater than largest int136).
                       *
                       * Counterpart to Solidity's `int136` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 136 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt136(int256 value) internal pure returns (int136 downcasted) {
                          downcasted = int136(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int128 from int256, reverting on
                       * overflow (when the input is less than smallest int128 or
                       * greater than largest int128).
                       *
                       * Counterpart to Solidity's `int128` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 128 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt128(int256 value) internal pure returns (int128 downcasted) {
                          downcasted = int128(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int120 from int256, reverting on
                       * overflow (when the input is less than smallest int120 or
                       * greater than largest int120).
                       *
                       * Counterpart to Solidity's `int120` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 120 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt120(int256 value) internal pure returns (int120 downcasted) {
                          downcasted = int120(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int112 from int256, reverting on
                       * overflow (when the input is less than smallest int112 or
                       * greater than largest int112).
                       *
                       * Counterpart to Solidity's `int112` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 112 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt112(int256 value) internal pure returns (int112 downcasted) {
                          downcasted = int112(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int104 from int256, reverting on
                       * overflow (when the input is less than smallest int104 or
                       * greater than largest int104).
                       *
                       * Counterpart to Solidity's `int104` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 104 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt104(int256 value) internal pure returns (int104 downcasted) {
                          downcasted = int104(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int96 from int256, reverting on
                       * overflow (when the input is less than smallest int96 or
                       * greater than largest int96).
                       *
                       * Counterpart to Solidity's `int96` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 96 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt96(int256 value) internal pure returns (int96 downcasted) {
                          downcasted = int96(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int88 from int256, reverting on
                       * overflow (when the input is less than smallest int88 or
                       * greater than largest int88).
                       *
                       * Counterpart to Solidity's `int88` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 88 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt88(int256 value) internal pure returns (int88 downcasted) {
                          downcasted = int88(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int80 from int256, reverting on
                       * overflow (when the input is less than smallest int80 or
                       * greater than largest int80).
                       *
                       * Counterpart to Solidity's `int80` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 80 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt80(int256 value) internal pure returns (int80 downcasted) {
                          downcasted = int80(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int72 from int256, reverting on
                       * overflow (when the input is less than smallest int72 or
                       * greater than largest int72).
                       *
                       * Counterpart to Solidity's `int72` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 72 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt72(int256 value) internal pure returns (int72 downcasted) {
                          downcasted = int72(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int64 from int256, reverting on
                       * overflow (when the input is less than smallest int64 or
                       * greater than largest int64).
                       *
                       * Counterpart to Solidity's `int64` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 64 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt64(int256 value) internal pure returns (int64 downcasted) {
                          downcasted = int64(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int56 from int256, reverting on
                       * overflow (when the input is less than smallest int56 or
                       * greater than largest int56).
                       *
                       * Counterpart to Solidity's `int56` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 56 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt56(int256 value) internal pure returns (int56 downcasted) {
                          downcasted = int56(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int48 from int256, reverting on
                       * overflow (when the input is less than smallest int48 or
                       * greater than largest int48).
                       *
                       * Counterpart to Solidity's `int48` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 48 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt48(int256 value) internal pure returns (int48 downcasted) {
                          downcasted = int48(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int40 from int256, reverting on
                       * overflow (when the input is less than smallest int40 or
                       * greater than largest int40).
                       *
                       * Counterpart to Solidity's `int40` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 40 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt40(int256 value) internal pure returns (int40 downcasted) {
                          downcasted = int40(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int32 from int256, reverting on
                       * overflow (when the input is less than smallest int32 or
                       * greater than largest int32).
                       *
                       * Counterpart to Solidity's `int32` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 32 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt32(int256 value) internal pure returns (int32 downcasted) {
                          downcasted = int32(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int24 from int256, reverting on
                       * overflow (when the input is less than smallest int24 or
                       * greater than largest int24).
                       *
                       * Counterpart to Solidity's `int24` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 24 bits
                       *
                       * _Available since v4.7._
                       */
                      function toInt24(int256 value) internal pure returns (int24 downcasted) {
                          downcasted = int24(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int16 from int256, reverting on
                       * overflow (when the input is less than smallest int16 or
                       * greater than largest int16).
                       *
                       * Counterpart to Solidity's `int16` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 16 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt16(int256 value) internal pure returns (int16 downcasted) {
                          downcasted = int16(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
                      }
                  
                      /**
                       * @dev Returns the downcasted int8 from int256, reverting on
                       * overflow (when the input is less than smallest int8 or
                       * greater than largest int8).
                       *
                       * Counterpart to Solidity's `int8` operator.
                       *
                       * Requirements:
                       *
                       * - input must fit into 8 bits
                       *
                       * _Available since v3.1._
                       */
                      function toInt8(int256 value) internal pure returns (int8 downcasted) {
                          downcasted = int8(value);
                          require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
                      }
                  
                      /**
                       * @dev Converts an unsigned uint256 into a signed int256.
                       *
                       * Requirements:
                       *
                       * - input must be less than or equal to maxInt256.
                       *
                       * _Available since v3.0._
                       */
                      function toInt256(uint256 value) internal pure returns (int256) {
                          // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
                          require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
                          return int256(value);
                      }
                  }
                  
                  // File: contracts/interfaces/IUniswapV3.sol
                  
                  
                  pragma solidity ^0.8.0;
                  pragma experimental ABIEncoderV2;
                  
                  interface IUniswapV3Pool {
                      /// @notice Swap token0 for token1, or token1 for token0
                      /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback
                      /// @param recipient The address to receive the output of the swap
                      /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0
                      /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
                      /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
                      /// value after the swap. If one for zero, the price cannot be greater than this value after the swap
                      /// @param data Any data to be passed through to the callback
                      /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
                      /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
                      function swap(
                          address recipient,
                          bool zeroForOne,
                          int256 amountSpecified,
                          uint160 sqrtPriceLimitX96,
                          bytes calldata data
                      ) external returns (int256 amount0, int256 amount1);
                  
                      /// @notice The first of the two tokens of the pool, sorted by address
                      /// @return The token contract address
                      function token0() external view returns (address);
                  
                      /// @notice The second of the two tokens of the pool, sorted by address
                      /// @return The token contract address
                      function token1() external view returns (address);
                  
                      /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6
                      /// @return The fee
                      function fee() external view returns (uint24);
                  }
                  
                  /// @title Callback for IUniswapV3PoolActions#swap
                  /// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
                  interface IUniswapV3SwapCallback {
                      /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
                      /// @dev In the implementation you must pay the pool tokens owed for the swap.
                      /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
                      /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
                      /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
                      /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
                      /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
                      /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
                      /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
                      function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external;
                  }
                  
                  /// @title Callback for IAlgebraPoolActions#swap
                  /// @notice Any contract that calls IAlgebraPoolActions#swap must implement this interface
                  /// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
                  /// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
                  interface IAlgebraSwapCallback {
                      /// @notice Called to `msg.sender` after executing a swap via IAlgebraPool#swap.
                      /// @dev In the implementation you must pay the pool tokens owed for the swap.
                      /// The caller of this method must be checked to be a AlgebraPool deployed by the canonical AlgebraFactory.
                      /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
                      /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
                      /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
                      /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
                      /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
                      /// @param data Any data passed through by the caller via the IAlgebraPoolActions#swap call
                      function algebraSwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external;
                  }
                  
                  interface IiZiSwapFactory {
                      /// @notice Get pool of (tokenX, tokenY, fee), address(0) for not exists.
                      /// @param tokenX address of tokenX
                      /// @param tokenY address of tokenY
                      /// @param fee fee amount
                      /// @return address of pool
                      function pool(address tokenX, address tokenY, uint24 fee) external view returns (address);
                  }
                  
                  interface IiZiSwapCallback {
                      /// @notice Called to msg.sender in iZiSwapPool#swapY2X(DesireX) call.
                      /// @param x Amount of tokenX trader will acquire
                      /// @param y Amount of tokenY trader will pay
                      /// @param data Any dadta passed though by the msg.sender via the iZiSwapPool#swapY2X(DesireX) call
                      function swapY2XCallback(uint256 x, uint256 y, bytes calldata data) external;
                  
                      /// @notice Called to msg.sender in iZiSwapPool#swapX2Y(DesireY) call.
                      /// @param x Amount of tokenX trader will pay
                      /// @param y Amount of tokenY trader will require
                      /// @param data Any dadta passed though by the msg.sender via the iZiSwapPool#swapX2Y(DesireY) call
                      function swapX2YCallback(uint256 x, uint256 y, bytes calldata data) external;
                  }
                  
                  interface IiZiSwapPool {
                      function pointDelta() external view returns (int24);
                  
                      /// @notice Point status in the pool.
                      /// @param point the point
                      /// @return liquidSum the total amount of liquidity that uses the point either as left endpoint or right endpoint
                      /// @return liquidDelta how much liquidity changes when the pool price crosses the point from left to right
                      /// @return accFeeXOut_128 the fee growth on the other side of the point from the current point in tokenX
                      /// @return accFeeYOut_128 the fee growth on the other side of the point from the current point in tokenY
                      /// @return isEndpt whether the point is an endpoint of a some miner's liquidity, true if liquidSum > 0
                      function points(
                          int24 point
                      ) external view returns (uint128 liquidSum, int128 liquidDelta, uint256 accFeeXOut_128, uint256 accFeeYOut_128, bool isEndpt);
                  
                      /// @notice Returns 256 packed point (statusVal>0) boolean values. See PointBitmap for more information.
                      function pointBitmap(int16 wordPosition) external view returns (uint256);
                  
                      /// @notice Swap tokenY for tokenX, given max amount of tokenY user willing to pay.
                      /// @param recipient the address to receive tokenX
                      /// @param amount the max amount of tokenY user willing to pay
                      /// @param highPt the highest point(price) of x/y during swap
                      /// @param data any data to be passed through to the callback
                      /// @return amountX amount of tokenX payed
                      /// @return amountY amount of tokenY acquired
                      function swapY2X(
                          address recipient,
                          uint128 amount,
                          int24 highPt,
                          bytes calldata data
                      ) external returns (uint256 amountX, uint256 amountY);
                  
                      /// @notice Swap tokenY for tokenX, given amount of tokenX user desires.
                      /// @param recipient the address to receive tokenX
                      /// @param desireX the amount of tokenX user desires
                      /// @param highPt the highest point(price) of x/y during swap
                      /// @param data any data to be passed through to the callback
                      /// @return amountX amount of tokenX payed
                      /// @return amountY amount of tokenY acquired
                      function swapY2XDesireX(
                          address recipient,
                          uint128 desireX,
                          int24 highPt,
                          bytes calldata data
                      ) external returns (uint256 amountX, uint256 amountY);
                  
                      /// @notice Swap tokenX for tokenY, given max amount of tokenX user willing to pay.
                      /// @param recipient the address to receive tokenY
                      /// @param amount the max amount of tokenX user willing to pay
                      /// @param lowPt the lowest point(price) of x/y during swap
                      /// @param data any data to be passed through to the callback
                      /// @return amountX amount of tokenX acquired
                      /// @return amountY amount of tokenY payed
                      function swapX2Y(
                          address recipient,
                          uint128 amount,
                          int24 lowPt,
                          bytes calldata data
                      ) external returns (uint256 amountX, uint256 amountY);
                  
                      /// @notice Swap tokenX for tokenY, given amount of tokenY user desires.
                      /// @param recipient the address to receive tokenY
                      /// @param desireY the amount of tokenY user desires
                      /// @param lowPt the lowest point(price) of x/y during swap
                      /// @param data any data to be passed through to the callback
                      /// @return amountX amount of tokenX acquired
                      /// @return amountY amount of tokenY payed
                      function swapX2YDesireY(
                          address recipient,
                          uint128 desireY,
                          int24 lowPt,
                          bytes calldata data
                      ) external returns (uint256 amountX, uint256 amountY);
                  
                      /// @notice LimitOrder info on a given point.
                      /// @param point the given point
                      /// @return sellingX total amount of tokenX selling on the point
                      /// @return earnY total amount of unclaimed earned tokenY
                      /// @return accEarnY total amount of earned tokenY(via selling tokenX) by all users at this point as of the last swap
                      /// @return sellingY total amount of tokenYselling on the point
                      /// @return earnX total amount of unclaimed earned tokenX
                      /// @return accEarnX total amount of earned tokenX(via selling tokenY) by all users at this point as of the last swap
                      function limitOrderData(
                          int24 point
                      ) external view returns (uint128 sellingX, uint128 earnY, uint256 accEarnY, uint128 sellingY, uint128 earnX, uint256 accEarnX);
                  }
                  
                  /// @title Callback for IPancakeV3PoolActions#swap
                  /// @notice Any contract that calls IPancakeV3PoolActions#swap must implement this interface
                  interface IPancakeV3SwapCallback {
                      /// @notice Called to `msg.sender` after executing a swap via IPancakeV3Pool#swap.
                      /// @dev In the implementation you must pay the pool tokens owed for the swap.
                      /// The caller of this method must be checked to be a PancakeV3Pool deployed by the canonical PancakeV3Factory.
                      /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
                      /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
                      /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
                      /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
                      /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
                      /// @param data Any data passed through by the caller via the IPancakeV3PoolActions#swap call
                      function pancakeV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external;
                  }
                  
                  interface IMaverickV1Pool {
                      /// @notice swap tokens
                      /// @param recipient address that will receive the output tokens
                      /// @param amount amount of token that is either the input if exactOutput
                      //is false or the output if exactOutput is true
                      /// @param tokenAIn bool indicating whether tokenA is the input
                      /// @param exactOutput bool indicating whether the amount specified is the
                      //exact output amount (true)
                      /// @param sqrtPriceLimit limiting sqrt price of the swap.  A value of 0
                      //indicates no limit.  Limit is only engaged for exactOutput=false.  If the
                      //limit is reached only part of the input amount will be swapped and the
                      //callback will only require that amount of the swap to be paid.
                      /// @param data callback function that swap will call so that the
                      //caller can transfer tokens
                      function swap(
                          address recipient,
                          uint256 amount,
                          bool tokenAIn,
                          bool exactOutput,
                          uint256 sqrtPriceLimit,
                          bytes calldata data
                      ) external returns (uint256 amountIn, uint256 amountOut);
                  }
                  
                  interface IMaverickV1SwapCallback {
                      function swapCallback(uint256 amountIn, uint256 amountOut, bytes calldata data) external;
                  }
                  
                  interface IMaverickV1Factory {
                      /// @notice lookup table for whether a pool is owned by the factory
                      function isFactoryPool(address pool) external view returns (bool);
                  }
                  
                  // File: contracts/interfaces/IWETH.sol
                  
                  
                  pragma solidity ^0.8.0;
                  
                  /// @title Interface for WETH tokens
                  interface IWETH is IERC20 {
                      function deposit() external payable;
                  
                      function withdraw(uint256 amount) external;
                  }
                  
                  // File: contracts/UniswapV3Exchange.sol
                  
                  
                  pragma solidity ^0.8.0;
                  
                  
                  
                  
                  
                  
                  contract UniswapV3Exchange is EthRejector, Permitable, IUniswapV3SwapCallback {
                      using Address for address payable;
                      using SafeERC20 for IERC20;
                      using SafeMath for uint256;
                  
                      uint256 private constant _ONE_FOR_ZERO_MASK = 1 << 255;
                      uint256 private constant _WETH_WRAP_MASK = 1 << 254;
                      uint256 private constant _WETH_UNWRAP_MASK = 1 << 253;
                      bytes32 private constant _POOL_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;
                      bytes32 private constant _FF_FACTORY = 0xff1F98431c8aD98523631AE4a59f267346ea31F9840000000000000000000000;
                      bytes32 private constant _SELECTORS = 0x0dfe1681d21220a7ddca3f430000000000000000000000000000000000000000;
                      uint256 private constant _ADDRESS_MASK = 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff;
                      /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
                      uint160 private constant _MIN_SQRT_RATIO = 4295128739 + 1;
                      /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
                      uint160 private constant _MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342 - 1;
                      /// @dev Change for different chains
                      address private constant _WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
                  
                      /// @notice Same as `uniswapV3SwapTo` but calls permit first,
                      /// allowing to approve token spending and make a swap in one transaction.
                      /// @param recipient Address that will receive swap funds
                      /// @param srcToken Source token
                      /// @param amount Amount of source tokens to swap
                      /// @param minReturn Minimal allowed returnAmount to make transaction commit
                      /// @param pools Pools chain used for swaps. Pools src and dst tokens should match to make swap happen
                      /// @param permit Should contain valid permit that can be used in `IERC20Permit.permit` calls.
                      /// See tests for examples
                      function uniswapV3SwapToWithPermit(
                          address payable recipient,
                          IERC20 srcToken,
                          uint256 amount,
                          uint256 minReturn,
                          uint256[] calldata pools,
                          bytes calldata permit
                      ) external returns (uint256 returnAmount) {
                          _permit(address(srcToken), permit);
                          return uniswapV3SwapTo(recipient, amount, minReturn, pools);
                      }
                  
                      /// @notice Same as `uniswapV3SwapTo` but uses `msg.sender` as recipient
                      /// @param amount Amount of source tokens to swap
                      /// @param minReturn Minimal allowed returnAmount to make transaction commit
                      /// @param pools Pools chain used for swaps. Pools src and dst tokens should match to make swap happen
                      function uniswapV3Swap(
                          uint256 amount,
                          uint256 minReturn,
                          uint256[] calldata pools
                      ) external payable returns (uint256 returnAmount) {
                          return uniswapV3SwapTo(payable(msg.sender), amount, minReturn, pools);
                      }
                  
                      /// @notice Performs swap using Uniswap V3 exchange. Wraps and unwraps ETH if required.
                      /// Sending non-zero `msg.value` for anything but ETH swaps is prohibited
                      /// @param recipient Address that will receive swap funds
                      /// @param amount Amount of source tokens to swap
                      /// @param minReturn Minimal allowed returnAmount to make transaction commit
                      /// @param pools Pools chain used for swaps. Pools src and dst tokens should match to make swap happen
                      function uniswapV3SwapTo(
                          address payable recipient,
                          uint256 amount,
                          uint256 minReturn,
                          uint256[] calldata pools
                      ) public payable returns (uint256 returnAmount) {
                          uint256 len = pools.length;
                          address dstToken;
                          require(len > 0, "UniswapV3: empty pools");
                          uint256 lastIndex = len - 1;
                          returnAmount = amount;
                          bool wrapWeth = pools[0] & _WETH_WRAP_MASK > 0;
                          bool unwrapWeth = pools[lastIndex] & _WETH_UNWRAP_MASK > 0;
                          if (wrapWeth) {
                              require(msg.value == amount, "UniswapV3: wrong msg.value");
                              IWETH(_WETH).deposit{value: amount}();
                          } else {
                              require(msg.value == 0, "UniswapV3: msg.value should be 0");
                          }
                          if (len > 1) {
                              (returnAmount, ) = _makeSwap(address(this), wrapWeth ? address(this) : msg.sender, pools[0], returnAmount);
                              for (uint256 i = 1; i < lastIndex; i++) {
                                  (returnAmount, ) = _makeSwap(address(this), address(this), pools[i], returnAmount);
                              }
                              (returnAmount, dstToken) = _makeSwap(address(this), address(this), pools[lastIndex], returnAmount);
                          } else {
                              (returnAmount, dstToken) = _makeSwap(address(this), wrapWeth ? address(this) : msg.sender, pools[0], returnAmount);
                          }
                  
                          require(returnAmount >= minReturn, "UniswapV3: min return");
                  
                          assembly {
                              function reRevert() {
                                  returndatacopy(0, 0, returndatasize())
                                  revert(0, returndatasize())
                              }
                  
                              function run(_returnAmount, _recipient, _unwrapWeth, _dstToken) {
                                  let slp := shr(
                                      232,
                                      and(
                                          calldataload(add(add(calldataload(0x64), 0x4), 0x20)),
                                          0x00ffff0000000000000000000000000000000000000000000000000000000000
                                      )
                                  )
                                  let finalAmount := div(mul(calldataload(0x44), 0x2710), sub(10000, slp))
                                  let emptyPtr := mload(0x40)
                                  switch gt(_returnAmount, finalAmount)
                                  case 1 {
                                      switch _unwrapWeth
                                      case 0 {
                                          mstore(emptyPtr, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
                                          mstore(add(emptyPtr, 0x4), _recipient)
                                          mstore(add(emptyPtr, 0x24), finalAmount)
                                          if iszero(call(gas(), _dstToken, 0, emptyPtr, 0x44, 0, 0)) {
                                              reRevert()
                                          }
                  
                                          mstore(add(emptyPtr, 0x4), 0x353c1f0bc78fbbc245b3C93ef77b1DCC5b77D2A0)
                                          mstore(add(emptyPtr, 0x24), sub(_returnAmount, finalAmount))
                                          if iszero(call(gas(), _dstToken, 0, emptyPtr, 0x44, 0, 0)) {
                                              reRevert()
                                          }
                                      }
                                      default {
                                          mstore(emptyPtr, 0x2e1a7d4d00000000000000000000000000000000000000000000000000000000)
                                          mstore(add(emptyPtr, 0x04), _returnAmount)
                                          if iszero(
                                              call(gas(), 0x000000000000000000000000C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, 0, emptyPtr, 0x24, 0, 0)
                                          ) {
                                              reRevert()
                                          }
                  
                                          if iszero(call(gas(), _recipient, finalAmount, 0, 0, 0, 0)) {
                                              reRevert()
                                          }
                  
                                          if iszero(
                                              call(gas(), 0x353c1f0bc78fbbc245b3C93ef77b1DCC5b77D2A0, sub(_returnAmount, finalAmount), 0, 0, 0, 0)
                                          ) {
                                              reRevert()
                                          }
                                      }
                                  }
                                  default {
                                      switch _unwrapWeth
                                      case 0 {
                                          mstore(emptyPtr, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
                                          mstore(add(emptyPtr, 0x4), _recipient)
                                          mstore(add(emptyPtr, 0x24), _returnAmount)
                                          if iszero(call(gas(), _dstToken, 0, emptyPtr, 0x44, 0, 0)) {
                                              reRevert()
                                          }
                                      }
                                      default {
                                          mstore(emptyPtr, 0x2e1a7d4d00000000000000000000000000000000000000000000000000000000)
                                          mstore(add(emptyPtr, 0x04), _returnAmount)
                                          if iszero(
                                              call(gas(), 0x000000000000000000000000C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, 0, emptyPtr, 0x24, 0, 0)
                                          ) {
                                              reRevert()
                                          }
                  
                                          if iszero(call(gas(), _recipient, _returnAmount, 0, 0, 0, 0)) {
                                              reRevert()
                                          }
                                      }
                                  }
                              }
                  
                              run(returnAmount, recipient, unwrapWeth, dstToken)
                          }
                      }
                  
                      /// @inheritdoc IUniswapV3SwapCallback
                      function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata /* data */) external override {
                          IERC20 token0;
                          IERC20 token1;
                          bytes32 ffFactoryAddress = _FF_FACTORY;
                          bytes32 poolInitCodeHash = _POOL_INIT_CODE_HASH;
                          address payer;
                  
                          assembly {
                              // solhint-disable-line no-inline-assembly
                              function reRevert() {
                                  returndatacopy(0, 0, returndatasize())
                                  revert(0, returndatasize())
                              }
                  
                              function revertWithReason(m, len) {
                                  mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000)
                                  mstore(0x20, 0x0000002000000000000000000000000000000000000000000000000000000000)
                                  mstore(0x40, m)
                                  revert(0, len)
                              }
                  
                              let emptyPtr := mload(0x40)
                              let resultPtr := add(emptyPtr, 0x20)
                              mstore(emptyPtr, _SELECTORS)
                  
                              if iszero(staticcall(gas(), caller(), emptyPtr, 0x4, resultPtr, 0x20)) {
                                  reRevert()
                              }
                              token0 := mload(resultPtr)
                              if iszero(staticcall(gas(), caller(), add(emptyPtr, 0x4), 0x4, resultPtr, 0x20)) {
                                  reRevert()
                              }
                              token1 := mload(resultPtr)
                              if iszero(staticcall(gas(), caller(), add(emptyPtr, 0x8), 0x4, resultPtr, 0x20)) {
                                  reRevert()
                              }
                              let fee := mload(resultPtr)
                  
                              let p := emptyPtr
                              mstore(p, ffFactoryAddress)
                              p := add(p, 21)
                              // Compute the inner hash in-place
                              mstore(p, token0)
                              mstore(add(p, 32), token1)
                              mstore(add(p, 64), fee)
                              mstore(p, keccak256(p, 96))
                              p := add(p, 32)
                              mstore(p, poolInitCodeHash)
                              let pool := and(keccak256(emptyPtr, 85), _ADDRESS_MASK)
                  
                              if iszero(eq(pool, caller())) {
                                  revertWithReason(0x00000010554e495633523a2062616420706f6f6c000000000000000000000000, 0x54) // UniswapV3: bad pool
                              }
                  
                              calldatacopy(emptyPtr, 0x84, 0x20)
                              payer := mload(emptyPtr)
                          }
                  
                          if (amount0Delta > 0) {
                              if (payer == address(this)) {
                                  token0.safeTransfer(msg.sender, uint256(amount0Delta));
                              } else {
                                  token0.safeTransferFrom(payer, msg.sender, uint256(amount0Delta));
                              }
                          }
                          if (amount1Delta > 0) {
                              if (payer == address(this)) {
                                  token1.safeTransfer(msg.sender, uint256(amount1Delta));
                              } else {
                                  token1.safeTransferFrom(payer, msg.sender, uint256(amount1Delta));
                              }
                          }
                      }
                  
                      function _makeSwap(address recipient, address payer, uint256 pool, uint256 amount) private returns (uint256, address) {
                          bool zeroForOne = pool & _ONE_FOR_ZERO_MASK == 0;
                          if (zeroForOne) {
                              (, int256 amount1) = IUniswapV3Pool(address(uint160(pool))).swap(
                                  recipient,
                                  zeroForOne,
                                  SafeCast.toInt256(amount),
                                  _MIN_SQRT_RATIO,
                                  abi.encode(payer)
                              );
                              return (SafeCast.toUint256(-amount1), IUniswapV3Pool(address(uint160(pool))).token1());
                          } else {
                              (int256 amount0, ) = IUniswapV3Pool(address(uint160(pool))).swap(
                                  recipient,
                                  zeroForOne,
                                  SafeCast.toInt256(amount),
                                  _MAX_SQRT_RATIO,
                                  abi.encode(payer)
                              );
                              return (SafeCast.toUint256(-amount0), IUniswapV3Pool(address(uint160(pool))).token0());
                          }
                      }
                  }
                  
                  // File: contracts/OpenOceanExchange.sol
                  
                  
                  
                  pragma solidity ^0.8.0;
                  
                  
                  
                  
                  
                  
                  
                  
                  
                  contract OpenOceanExchange is OwnableUpgradeable, PausableUpgradeable, UniswapV2Exchange, UniswapV3Exchange {
                      using SafeMath for uint256;
                      using SafeERC20 for IERC20;
                      using UniversalERC20 for IERC20;
                  
                      uint256 private constant _PARTIAL_FILL = 0x01;
                      uint256 private constant _SHOULD_CLAIM = 0x02;
                  
                      struct SwapDescription {
                          IERC20 srcToken;
                          IERC20 dstToken;
                          address srcReceiver;
                          address dstReceiver;
                          uint256 amount;
                          uint256 minReturnAmount;
                          uint256 guaranteedAmount;
                          uint256 flags;
                          address referrer;
                          bytes permit;
                      }
                  
                      event Swapped(
                          address indexed sender,
                          IERC20 indexed srcToken,
                          IERC20 indexed dstToken,
                          address dstReceiver,
                          uint256 amount,
                          uint256 spentAmount,
                          uint256 returnAmount,
                          uint256 minReturnAmount,
                          uint256 guaranteedAmount,
                          address referrer
                      );
                  
                      function initialize() public initializer {
                          OwnableUpgradeable.__Ownable_init();
                          PausableUpgradeable.__Pausable_init();
                      }
                  
                      function swap(
                          IOpenOceanCaller caller,
                          SwapDescription calldata desc,
                          IOpenOceanCaller.CallDescription[] calldata calls
                      ) external payable whenNotPaused returns (uint256 returnAmount) {
                          require(desc.minReturnAmount > 0, "Min return should not be 0");
                          require(calls.length > 0, "Call data should exist");
                  
                          uint256 flags = desc.flags;
                          IERC20 srcToken = desc.srcToken;
                          IERC20 dstToken = desc.dstToken;
                  
                          require(msg.value == (srcToken.isETH() ? desc.amount : 0), "Invalid msg.value");
                  
                          if (flags & _SHOULD_CLAIM != 0) {
                              require(!srcToken.isETH(), "Claim token is ETH");
                              _claim(srcToken, desc.srcReceiver, desc.amount, desc.permit);
                          }
                  
                          address dstReceiver = (desc.dstReceiver == address(0)) ? msg.sender : desc.dstReceiver;
                          uint256 initialSrcBalance = (flags & _PARTIAL_FILL != 0) ? srcToken.universalBalanceOf(msg.sender) : 0;
                          uint256 initialDstBalance = dstToken.universalBalanceOf(dstReceiver);
                  
                          caller.makeCalls{value: msg.value}(calls);
                  
                          uint256 spentAmount = desc.amount;
                          returnAmount = dstToken.universalBalanceOf(dstReceiver).sub(initialDstBalance);
                  
                          if (flags & _PARTIAL_FILL != 0) {
                              spentAmount = initialSrcBalance.add(desc.amount).sub(srcToken.universalBalanceOf(msg.sender));
                              require(returnAmount.mul(desc.amount) >= desc.minReturnAmount.mul(spentAmount), "Return amount is not enough");
                          } else {
                              require(returnAmount >= desc.minReturnAmount, "Return amount is not enough");
                          }
                  
                          _emitSwapped(desc, srcToken, dstToken, dstReceiver, spentAmount, returnAmount);
                      }
                  
                      function _emitSwapped(
                          SwapDescription calldata desc,
                          IERC20 srcToken,
                          IERC20 dstToken,
                          address dstReceiver,
                          uint256 spentAmount,
                          uint256 returnAmount
                      ) private {
                          emit Swapped(
                              msg.sender,
                              srcToken,
                              dstToken,
                              dstReceiver,
                              desc.amount,
                              spentAmount,
                              returnAmount,
                              desc.minReturnAmount,
                              desc.guaranteedAmount,
                              desc.referrer
                          );
                      }
                  
                      function _claim(IERC20 token, address dst, uint256 amount, bytes calldata permit) private {
                          // TODO: Is it safe to call permit on tokens without implemented permit? Fallback will be called. Is it bad for proxies?
                  
                          if (permit.length == 32 * 7) {
                              // solhint-disable-next-line avoid-low-level-calls
                              (bool success, bytes memory result) = address(token).call(abi.encodeWithSelector(IERC20Permit.permit.selector, permit));
                              if (!success) {
                                  revert(RevertReasonParser.parse(result, "Permit call failed: "));
                              }
                          }
                  
                          token.safeTransferFrom(msg.sender, dst, amount);
                      }
                  
                      function rescueFunds(IERC20 token, uint256 amount) external onlyOwner {
                          token.universalTransfer(payable(msg.sender), amount);
                      }
                  
                      function pause() external onlyOwner {
                          _pause();
                      }
                  }

                  File 7 of 8: LToken
                  // File: contracts\thirdParty\interfaces\IERC1820Registry.sol
                  
                  // From open https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/introspection/IERC1820Registry.sol
                  
                  pragma solidity ^0.6.0;
                  
                  /**
                   * @dev Interface of the global ERC1820 Registry, as defined in the
                   * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register
                   * implementers for interfaces in this registry, as well as query support.
                   *
                   * Implementers may be shared by multiple accounts, and can also implement more
                   * than a single interface for each account. Contracts can implement interfaces
                   * for themselves, but externally-owned accounts (EOA) must delegate this to a
                   * contract.
                   *
                   * {IERC165} interfaces can also be queried via the registry.
                   *
                   * For an in-depth explanation and source code analysis, see the EIP text.
                   */
                  interface IERC1820Registry {
                      /**
                       * @dev Sets `newManager` as the manager for `account`. A manager of an
                       * account is able to set interface implementers for it.
                       *
                       * By default, each account is its own manager. Passing a value of `0x0` in
                       * `newManager` will reset the manager to this initial state.
                       *
                       * Emits a {ManagerChanged} event.
                       *
                       * Requirements:
                       *
                       * - the caller must be the current manager for `account`.
                       */
                      function setManager(address account, address newManager) external;
                  
                      /**
                       * @dev Returns the manager for `account`.
                       *
                       * See {setManager}.
                       */
                      function getManager(address account) external view returns (address);
                  
                      /**
                       * @dev Sets the `implementer` contract as ``account``'s implementer for
                       * `interfaceHash`.
                       *
                       * `account` being the zero address is an alias for the caller's address.
                       * The zero address can also be used in `implementer` to remove an old one.
                       *
                       * See {interfaceHash} to learn how these are created.
                       *
                       * Emits an {InterfaceImplementerSet} event.
                       *
                       * Requirements:
                       *
                       * - the caller must be the current manager for `account`.
                       * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not
                       * end in 28 zeroes).
                       * - `implementer` must implement {IERC1820Implementer} and return true when
                       * queried for support, unless `implementer` is the caller. See
                       * {IERC1820Implementer-canImplementInterfaceForAddress}.
                       */
                      function setInterfaceImplementer(address account, bytes32 interfaceHash, address implementer) external;
                  
                      /**
                       * @dev Returns the implementer of `interfaceHash` for `account`. If no such
                       * implementer is registered, returns the zero address.
                       *
                       * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28
                       * zeroes), `account` will be queried for support of it.
                       *
                       * `account` being the zero address is an alias for the caller's address.
                       */
                      function getInterfaceImplementer(address account, bytes32 interfaceHash) external view returns (address);
                  
                      /**
                       * @dev Returns the interface hash for an `interfaceName`, as defined in the
                       * corresponding
                       * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP].
                       */
                      function interfaceHash(string calldata interfaceName) external pure returns (bytes32);
                  
                      /**
                       *  @notice Updates the cache with whether the contract implements an ERC165 interface or not.
                       *  @param account Address of the contract for which to update the cache.
                       *  @param interfaceId ERC165 interface for which to update the cache.
                       */
                      function updateERC165Cache(address account, bytes4 interfaceId) external;
                  
                      /**
                       *  @notice Checks whether a contract implements an ERC165 interface or not.
                       *  If the result is not cached a direct lookup on the contract address is performed.
                       *  If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
                       *  {updateERC165Cache} with the contract address.
                       *  @param account Address of the contract to check.
                       *  @param interfaceId ERC165 interface to check.
                       *  @return True if `account` implements `interfaceId`, false otherwise.
                       */
                      function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool);
                  
                      /**
                       *  @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
                       *  @param account Address of the contract to check.
                       *  @param interfaceId ERC165 interface to check.
                       *  @return True if `account` implements `interfaceId`, false otherwise.
                       */
                      function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool);
                  
                      event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer);
                  
                      event ManagerChanged(address indexed account, address indexed newManager);
                  }
                  
                  // File: contracts\interfaces\IERC777Sender.sol
                  
                  pragma solidity 0.6.7;
                  
                  // As defined in the 'ERC777TokensSender And The tokensToSend Hook' section of https://eips.ethereum.org/EIPS/eip-777
                  interface IERC777Sender {
                    function tokensToSend(address operator, address from, address to, uint256 amount, bytes calldata data,
                        bytes calldata operatorData) external;
                  }
                  
                  // File: contracts\interfaces\IERC777Recipient.sol
                  
                  pragma solidity 0.6.7;
                  
                  // As defined in the 'ERC777TokensRecipient And The tokensReceived Hook' section of https://eips.ethereum.org/EIPS/eip-777
                  interface IERC777Recipient {
                    function tokensReceived(address operator, address from, address to, uint256 amount, bytes calldata data,
                        bytes calldata operatorData) external;
                  }
                  
                  // File: contracts\thirdParty\SafeMath.sol
                  
                  // Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/SafeMath.sol
                  
                  pragma solidity ^0.6.0;
                  
                  /**
                   * @dev Wrappers over Solidity's arithmetic operations with added overflow
                   * checks.
                   *
                   * Arithmetic operations in Solidity wrap on overflow. This can easily result
                   * in bugs, because programmers usually assume that an overflow raises an
                   * error, which is the standard behavior in high level programming languages.
                   * `SafeMath` restores this intuition by reverting the transaction when an
                   * operation overflows.
                   *
                   * Using this library instead of the unchecked operations eliminates an entire
                   * class of bugs, so it's recommended to use it always.
                   */
                  library SafeMath {
                      /**
                       * @dev Returns the addition of two unsigned integers, reverting on
                       * overflow.
                       *
                       * Counterpart to Solidity's `+` operator.
                       *
                       * Requirements:
                       * - Addition cannot overflow.
                       */
                      function add(uint256 a, uint256 b) internal pure returns (uint256) {
                          uint256 c = a + b;
                          require(c >= a, "SafeMath: addition overflow");
                  
                          return c;
                      }
                  
                      /**
                       * @dev Returns the subtraction of two unsigned integers, reverting on
                       * overflow (when the result is negative).
                       *
                       * Counterpart to Solidity's `-` operator.
                       *
                       * Requirements:
                       * - Subtraction cannot overflow.
                       */
                      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                          return sub(a, b, "SafeMath: subtraction overflow");
                      }
                  
                      /**
                       * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                       * overflow (when the result is negative).
                       *
                       * Counterpart to Solidity's `-` operator.
                       *
                       * Requirements:
                       * - Subtraction cannot overflow.
                       *
                       * _Available since v2.4.0._
                       */
                      function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                          require(b <= a, errorMessage);
                          uint256 c = a - b;
                  
                          return c;
                      }
                  
                      /**
                       * @dev Returns the multiplication of two unsigned integers, reverting on
                       * overflow.
                       *
                       * Counterpart to Solidity's `*` operator.
                       *
                       * Requirements:
                       * - Multiplication cannot overflow.
                       */
                      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                          // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                          // benefit is lost if 'b' is also tested.
                          // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                          if (a == 0) {
                              return 0;
                          }
                  
                          uint256 c = a * b;
                          require(c / a == b, "SafeMath: multiplication overflow");
                  
                          return c;
                      }
                  
                      /**
                       * @dev Returns the integer division of two unsigned integers. Reverts on
                       * division by zero. The result is rounded towards zero.
                       *
                       * Counterpart to Solidity's `/` operator. Note: this function uses a
                       * `revert` opcode (which leaves remaining gas untouched) while Solidity
                       * uses an invalid opcode to revert (consuming all remaining gas).
                       *
                       * Requirements:
                       * - The divisor cannot be zero.
                       */
                      function div(uint256 a, uint256 b) internal pure returns (uint256) {
                          return div(a, b, "SafeMath: division by zero");
                      }
                  
                      /**
                       * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
                       * division by zero. The result is rounded towards zero.
                       *
                       * Counterpart to Solidity's `/` operator. Note: this function uses a
                       * `revert` opcode (which leaves remaining gas untouched) while Solidity
                       * uses an invalid opcode to revert (consuming all remaining gas).
                       *
                       * Requirements:
                       * - The divisor cannot be zero.
                       *
                       * _Available since v2.4.0._
                       */
                      function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                          // Solidity only automatically asserts when dividing by 0
                          require(b > 0, errorMessage);
                          uint256 c = a / b;
                          // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                  
                          return c;
                      }
                  
                      /**
                       * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                       * Reverts when dividing by zero.
                       *
                       * Counterpart to Solidity's `%` operator. This function uses a `revert`
                       * opcode (which leaves remaining gas untouched) while Solidity uses an
                       * invalid opcode to revert (consuming all remaining gas).
                       *
                       * Requirements:
                       * - The divisor cannot be zero.
                       */
                      function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                          return mod(a, b, "SafeMath: modulo by zero");
                      }
                  
                      /**
                       * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                       * Reverts with custom message when dividing by zero.
                       *
                       * Counterpart to Solidity's `%` operator. This function uses a `revert`
                       * opcode (which leaves remaining gas untouched) while Solidity uses an
                       * invalid opcode to revert (consuming all remaining gas).
                       *
                       * Requirements:
                       * - The divisor cannot be zero.
                       *
                       * _Available since v2.4.0._
                       */
                      function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                          require(b != 0, errorMessage);
                          return a % b;
                      }
                  }
                  
                  // File: contracts\libraries\LToken.sol
                  
                  pragma solidity 0.6.7;
                  
                  
                  
                  
                  
                  struct TokenState {
                    uint256 totalSupply;
                    mapping(address => uint256) balances;
                    mapping(address => mapping(address => uint256)) approvals;
                    mapping(address => mapping(address => bool)) authorizedOperators;
                    address[] defaultOperators;
                    mapping(address => bool) defaultOperatorIsRevoked;
                    mapping(address => bool) minters;
                  }
                  
                  library LToken {
                    using SafeMath for uint256;
                  
                    event Transfer(address indexed from, address indexed to, uint256 value);
                    event Approval(address indexed owner, address indexed spender, uint256 value);
                    event Sent(address indexed operator, address indexed from, address indexed to, uint256 amount, bytes data,
                        bytes operatorData);
                    event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);
                    event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);
                    event AuthorizedOperator(address indexed operator, address indexed holder);
                    event RevokedOperator(address indexed operator, address indexed holder);
                  
                    // Universal address as defined in Registry Contract Address section of https://eips.ethereum.org/EIPS/eip-1820
                    IERC1820Registry constant internal ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
                    // precalculated hashes - see https://github.com/ethereum/solidity/issues/4024
                    // keccak256("ERC777TokensSender")
                    bytes32 constant internal ERC777_TOKENS_SENDER_HASH = 0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895;
                    // keccak256("ERC777TokensRecipient")
                    bytes32 constant internal ERC777_TOKENS_RECIPIENT_HASH = 0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b;
                  
                    modifier checkSenderNotOperator(address _operator) {
                      require(_operator != msg.sender, "Cannot be operator for self");
                      _;
                    }
                  
                    function initState(TokenState storage _tokenState, uint8 _decimals, uint256 _initialSupply)
                      external
                    {
                      _tokenState.defaultOperators.push(address(this));
                      _tokenState.totalSupply = _initialSupply.mul(10**uint256(_decimals));
                      _tokenState.balances[msg.sender] = _tokenState.totalSupply;
                    }
                  
                    function transferFrom(TokenState storage _tokenState, address _from, address _to, uint256 _value)
                      external
                    {
                      _tokenState.approvals[_from][msg.sender] = _tokenState.approvals[_from][msg.sender].sub(_value, "Amount not approved");
                      doSend(_tokenState, msg.sender, _from, _to, _value, "", "", false);
                    }
                  
                    function approve(TokenState storage _tokenState, address _spender, uint256 _value)
                      external
                    {
                      require(_spender != address(0), "Cannot approve to zero address");
                      _tokenState.approvals[msg.sender][_spender] = _value;
                      emit Approval(msg.sender, _spender, _value);
                    }
                  
                    function authorizeOperator(TokenState storage _tokenState, address _operator)
                      checkSenderNotOperator(_operator)
                      external
                    {
                      if (_operator == address(this))
                        _tokenState.defaultOperatorIsRevoked[msg.sender] = false;
                      else
                        _tokenState.authorizedOperators[_operator][msg.sender] = true;
                      emit AuthorizedOperator(_operator, msg.sender);
                    }
                  
                    function revokeOperator(TokenState storage _tokenState, address _operator)
                      checkSenderNotOperator(_operator)
                      external
                    {
                      if (_operator == address(this))
                        _tokenState.defaultOperatorIsRevoked[msg.sender] = true;
                      else
                        _tokenState.authorizedOperators[_operator][msg.sender] = false;
                      emit RevokedOperator(_operator, msg.sender);
                    }
                  
                    function authorizeMinter(TokenState storage _tokenState, address _minter)
                      external
                    {
                      _tokenState.minters[_minter] = true;
                    }
                  
                    function revokeMinter(TokenState storage _tokenState, address _minter)
                      external
                    {
                      _tokenState.minters[_minter] = false;
                    }
                  
                    function doMint(TokenState storage _tokenState, address _to, uint256 _amount)
                      external
                    {
                      assert(_to != address(0));
                  
                      _tokenState.totalSupply = _tokenState.totalSupply.add(_amount);
                      _tokenState.balances[_to] = _tokenState.balances[_to].add(_amount);
                  
                      // From ERC777: The token contract MUST call the tokensReceived hook after updating the state.
                      receiveHook(address(this), address(0), _to, _amount, "", "", true);
                  
                      emit Minted(address(this), _to, _amount, "", "");
                      emit Transfer(address(0), _to, _amount);
                    }
                  
                    function doBurn(TokenState storage _tokenState, address _operator, address _from, uint256 _amount, bytes calldata _data,
                        bytes calldata _operatorData)
                      external
                    {
                      assert(_from != address(0));
                      // From ERC777: The token contract MUST call the tokensToSend hook before updating the state.
                      sendHook(_operator, _from, address(0), _amount, _data, _operatorData);
                  
                      _tokenState.balances[_from] = _tokenState.balances[_from].sub(_amount, "Cannot burn more than balance");
                      _tokenState.totalSupply = _tokenState.totalSupply.sub(_amount);
                  
                      emit Burned(_operator, _from, _amount, _data, _operatorData);
                      emit Transfer(_from, address(0), _amount);
                    }
                  
                    function doSend(TokenState storage _tokenState, address _operator, address _from, address _to, uint256 _amount,
                        bytes memory _data, bytes memory _operatorData, bool _enforceERC777)
                      public
                    {
                      assert(_from != address(0));
                  
                      require(_to != address(0), "Cannot send funds to 0 address");
                      // From ERC777: The token contract MUST call the tokensToSend hook before updating the state.
                      sendHook(_operator, _from, _to, _amount, _data, _operatorData);
                  
                      _tokenState.balances[_from] = _tokenState.balances[_from].sub(_amount, "Amount exceeds available funds");
                      _tokenState.balances[_to] = _tokenState.balances[_to].add(_amount);
                  
                      emit Sent(_operator, _from, _to, _amount, _data, _operatorData);
                      emit Transfer(_from, _to, _amount);
                  
                      // From ERC777: The token contract MUST call the tokensReceived hook after updating the state.
                      receiveHook(_operator, _from, _to, _amount, _data, _operatorData, _enforceERC777);
                    }
                  
                    function receiveHook(address _operator, address _from, address _to, uint256 _amount, bytes memory _data,
                        bytes memory _operatorData, bool _enforceERC777)
                      public
                    {
                      address implementer = ERC1820_REGISTRY.getInterfaceImplementer(_to, ERC777_TOKENS_RECIPIENT_HASH);
                      if (implementer != address(0))
                        IERC777Recipient(implementer).tokensReceived(_operator, _from, _to, _amount, _data, _operatorData);
                      else if (_enforceERC777)
                        require(!isContract(_to), "Must be registered with ERC1820");
                    }
                  
                    function sendHook(address _operator, address _from, address _to, uint256 _amount, bytes memory _data,
                        bytes memory _operatorData)
                      public
                    {
                      address implementer = ERC1820_REGISTRY.getInterfaceImplementer(_from, ERC777_TOKENS_SENDER_HASH);
                      if (implementer != address(0))
                        IERC777Sender(implementer).tokensToSend(_operator, _from, _to, _amount, _data, _operatorData);
                    }
                  
                    function isContract(address _account)
                      private
                      view
                      returns (bool isContract_)
                    {
                      uint256 size;
                  
                      assembly {
                        size := extcodesize(_account)
                      }
                  
                      isContract_ = size != 0;
                    }
                  }

                  File 8 of 8: ERC1820Registry
                  /* ERC1820 Pseudo-introspection Registry Contract
                   * This standard defines a universal registry smart contract where any address (contract or regular account) can
                   * register which interface it supports and which smart contract is responsible for its implementation.
                   *
                   * Written in 2019 by Jordi Baylina and Jacques Dafflon
                   *
                   * To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to
                   * this software to the public domain worldwide. This software is distributed without any warranty.
                   *
                   * You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see
                   * <http://creativecommons.org/publicdomain/zero/1.0/>.
                   *
                   *    ███████╗██████╗  ██████╗ ██╗ █████╗ ██████╗  ██████╗
                   *    ██╔════╝██╔══██╗██╔════╝███║██╔══██╗╚════██╗██╔═████╗
                   *    █████╗  ██████╔╝██║     ╚██║╚█████╔╝ █████╔╝██║██╔██║
                   *    ██╔══╝  ██╔══██╗██║      ██║██╔══██╗██╔═══╝ ████╔╝██║
                   *    ███████╗██║  ██║╚██████╗ ██║╚█████╔╝███████╗╚██████╔╝
                   *    ╚══════╝╚═╝  ╚═╝ ╚═════╝ ╚═╝ ╚════╝ ╚══════╝ ╚═════╝
                   *
                   *    ██████╗ ███████╗ ██████╗ ██╗███████╗████████╗██████╗ ██╗   ██╗
                   *    ██╔══██╗██╔════╝██╔════╝ ██║██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝
                   *    ██████╔╝█████╗  ██║  ███╗██║███████╗   ██║   ██████╔╝ ╚████╔╝
                   *    ██╔══██╗██╔══╝  ██║   ██║██║╚════██║   ██║   ██╔══██╗  ╚██╔╝
                   *    ██║  ██║███████╗╚██████╔╝██║███████║   ██║   ██║  ██║   ██║
                   *    ╚═╝  ╚═╝╚══════╝ ╚═════╝ ╚═╝╚══════╝   ╚═╝   ╚═╝  ╚═╝   ╚═╝
                   *
                   */
                  pragma solidity 0.5.3;
                  // IV is value needed to have a vanity address starting with '0x1820'.
                  // IV: 53759
                  
                  /// @dev The interface a contract MUST implement if it is the implementer of
                  /// some (other) interface for any address other than itself.
                  interface ERC1820ImplementerInterface {
                      /// @notice Indicates whether the contract implements the interface 'interfaceHash' for the address 'addr' or not.
                      /// @param interfaceHash keccak256 hash of the name of the interface
                      /// @param addr Address for which the contract will implement the interface
                      /// @return ERC1820_ACCEPT_MAGIC only if the contract implements 'interfaceHash' for the address 'addr'.
                      function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32);
                  }
                  
                  
                  /// @title ERC1820 Pseudo-introspection Registry Contract
                  /// @author Jordi Baylina and Jacques Dafflon
                  /// @notice This contract is the official implementation of the ERC1820 Registry.
                  /// @notice For more details, see https://eips.ethereum.org/EIPS/eip-1820
                  contract ERC1820Registry {
                      /// @notice ERC165 Invalid ID.
                      bytes4 constant internal INVALID_ID = 0xffffffff;
                      /// @notice Method ID for the ERC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`).
                      bytes4 constant internal ERC165ID = 0x01ffc9a7;
                      /// @notice Magic value which is returned if a contract implements an interface on behalf of some other address.
                      bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC"));
                  
                      /// @notice mapping from addresses and interface hashes to their implementers.
                      mapping(address => mapping(bytes32 => address)) internal interfaces;
                      /// @notice mapping from addresses to their manager.
                      mapping(address => address) internal managers;
                      /// @notice flag for each address and erc165 interface to indicate if it is cached.
                      mapping(address => mapping(bytes4 => bool)) internal erc165Cached;
                  
                      /// @notice Indicates a contract is the 'implementer' of 'interfaceHash' for 'addr'.
                      event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);
                      /// @notice Indicates 'newManager' is the address of the new manager for 'addr'.
                      event ManagerChanged(address indexed addr, address indexed newManager);
                  
                      /// @notice Query if an address implements an interface and through which contract.
                      /// @param _addr Address being queried for the implementer of an interface.
                      /// (If '_addr' is the zero address then 'msg.sender' is assumed.)
                      /// @param _interfaceHash Keccak256 hash of the name of the interface as a string.
                      /// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient' interface.
                      /// @return The address of the contract which implements the interface '_interfaceHash' for '_addr'
                      /// or '0' if '_addr' did not register an implementer for this interface.
                      function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) {
                          address addr = _addr == address(0) ? msg.sender : _addr;
                          if (isERC165Interface(_interfaceHash)) {
                              bytes4 erc165InterfaceHash = bytes4(_interfaceHash);
                              return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : address(0);
                          }
                          return interfaces[addr][_interfaceHash];
                      }
                  
                      /// @notice Sets the contract which implements a specific interface for an address.
                      /// Only the manager defined for that address can set it.
                      /// (Each address is the manager for itself until it sets a new manager.)
                      /// @param _addr Address for which to set the interface.
                      /// (If '_addr' is the zero address then 'msg.sender' is assumed.)
                      /// @param _interfaceHash Keccak256 hash of the name of the interface as a string.
                      /// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient' interface.
                      /// @param _implementer Contract address implementing '_interfaceHash' for '_addr'.
                      function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {
                          address addr = _addr == address(0) ? msg.sender : _addr;
                          require(getManager(addr) == msg.sender, "Not the manager");
                  
                          require(!isERC165Interface(_interfaceHash), "Must not be an ERC165 hash");
                          if (_implementer != address(0) && _implementer != msg.sender) {
                              require(
                                  ERC1820ImplementerInterface(_implementer)
                                      .canImplementInterfaceForAddress(_interfaceHash, addr) == ERC1820_ACCEPT_MAGIC,
                                  "Does not implement the interface"
                              );
                          }
                          interfaces[addr][_interfaceHash] = _implementer;
                          emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);
                      }
                  
                      /// @notice Sets '_newManager' as manager for '_addr'.
                      /// The new manager will be able to call 'setInterfaceImplementer' for '_addr'.
                      /// @param _addr Address for which to set the new manager.
                      /// @param _newManager Address of the new manager for 'addr'. (Pass '0x0' to reset the manager to '_addr'.)
                      function setManager(address _addr, address _newManager) external {
                          require(getManager(_addr) == msg.sender, "Not the manager");
                          managers[_addr] = _newManager == _addr ? address(0) : _newManager;
                          emit ManagerChanged(_addr, _newManager);
                      }
                  
                      /// @notice Get the manager of an address.
                      /// @param _addr Address for which to return the manager.
                      /// @return Address of the manager for a given address.
                      function getManager(address _addr) public view returns(address) {
                          // By default the manager of an address is the same address
                          if (managers[_addr] == address(0)) {
                              return _addr;
                          } else {
                              return managers[_addr];
                          }
                      }
                  
                      /// @notice Compute the keccak256 hash of an interface given its name.
                      /// @param _interfaceName Name of the interface.
                      /// @return The keccak256 hash of an interface name.
                      function interfaceHash(string calldata _interfaceName) external pure returns(bytes32) {
                          return keccak256(abi.encodePacked(_interfaceName));
                      }
                  
                      /* --- ERC165 Related Functions --- */
                      /* --- Developed in collaboration with William Entriken. --- */
                  
                      /// @notice Updates the cache with whether the contract implements an ERC165 interface or not.
                      /// @param _contract Address of the contract for which to update the cache.
                      /// @param _interfaceId ERC165 interface for which to update the cache.
                      function updateERC165Cache(address _contract, bytes4 _interfaceId) external {
                          interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(
                              _contract, _interfaceId) ? _contract : address(0);
                          erc165Cached[_contract][_interfaceId] = true;
                      }
                  
                      /// @notice Checks whether a contract implements an ERC165 interface or not.
                      //  If the result is not cached a direct lookup on the contract address is performed.
                      //  If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
                      //  'updateERC165Cache' with the contract address.
                      /// @param _contract Address of the contract to check.
                      /// @param _interfaceId ERC165 interface to check.
                      /// @return True if '_contract' implements '_interfaceId', false otherwise.
                      function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) {
                          if (!erc165Cached[_contract][_interfaceId]) {
                              return implementsERC165InterfaceNoCache(_contract, _interfaceId);
                          }
                          return interfaces[_contract][_interfaceId] == _contract;
                      }
                  
                      /// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
                      /// @param _contract Address of the contract to check.
                      /// @param _interfaceId ERC165 interface to check.
                      /// @return True if '_contract' implements '_interfaceId', false otherwise.
                      function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) {
                          uint256 success;
                          uint256 result;
                  
                          (success, result) = noThrowCall(_contract, ERC165ID);
                          if (success == 0 || result == 0) {
                              return false;
                          }
                  
                          (success, result) = noThrowCall(_contract, INVALID_ID);
                          if (success == 0 || result != 0) {
                              return false;
                          }
                  
                          (success, result) = noThrowCall(_contract, _interfaceId);
                          if (success == 1 && result == 1) {
                              return true;
                          }
                          return false;
                      }
                  
                      /// @notice Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not.
                      /// @param _interfaceHash The hash to check.
                      /// @return True if '_interfaceHash' is an ERC165 interface (ending with 28 zeroes), false otherwise.
                      function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {
                          return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;
                      }
                  
                      /// @dev Make a call on a contract without throwing if the function does not exist.
                      function noThrowCall(address _contract, bytes4 _interfaceId)
                          internal view returns (uint256 success, uint256 result)
                      {
                          bytes4 erc165ID = ERC165ID;
                  
                          assembly {
                              let x := mload(0x40)               // Find empty storage location using "free memory pointer"
                              mstore(x, erc165ID)                // Place signature at beginning of empty storage
                              mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature
                  
                              success := staticcall(
                                  30000,                         // 30k gas
                                  _contract,                     // To addr
                                  x,                             // Inputs are stored at location x
                                  0x24,                          // Inputs are 36 (4 + 32) bytes long
                                  x,                             // Store output over input (saves space)
                                  0x20                           // Outputs are 32 bytes long
                              )
                  
                              result := mload(x)                 // Load the result
                          }
                      }
                  }