ETH Price: $1,884.94 (-0.45%)

Transaction Decoder

Block:
22001234 at Mar-08-2025 09:22:23 AM +UTC
Transaction Fee:
0.00028778738981965 ETH $0.54
Gas Used:
415,150 Gas / 0.693213031 Gwei

Emitted Events:

241 USDe.Approval( owner=0xB813d6006578D60e70fCC04d21895B05F363AeAf, spender=AggregationRouterV6, value=41664095262766783029991 )
242 USDe.Transfer( from=0xB813d6006578D60e70fCC04d21895B05F363AeAf, to=0x6AA7A8539543210563AF6cf575Cc5079b194D0C8, value=10926138746223216970009 )
243 USDe.Approval( owner=0x6AA7A8539543210563AF6cf575Cc5079b194D0C8, spender=CurveStableSwapNG, value=10926138746223216970009 )
244 USDe.Approval( owner=0x6AA7A8539543210563AF6cf575Cc5079b194D0C8, spender=CurveStableSwapNG, value=0 )
245 USDe.Transfer( from=0x6AA7A8539543210563AF6cf575Cc5079b194D0C8, to=CurveStableSwapNG, value=10926138746223216970009 )
246 FRAXStablecoin.Transfer( from=CurveStableSwapNG, to=0x6AA7A8539543210563AF6cf575Cc5079b194D0C8, value=10940799366699973588980 )
247 CurveStableSwapNG.TokenExchange( buyer=0x6AA7A8539543210563AF6cf575Cc5079b194D0C8, sold_id=1, tokens_sold=10926138746223216970009, bought_id=0, tokens_bought=10940799366699973588980 )
248 FRAXStablecoin.Approval( owner=0x6AA7A8539543210563AF6cf575Cc5079b194D0C8, spender=Vyper_contract, value=10940799366699973588980 )
249 FRAXStablecoin.Transfer( from=0x6AA7A8539543210563AF6cf575Cc5079b194D0C8, to=Vyper_contract, value=10940799366699973588980 )
250 FRAXStablecoin.Approval( owner=0x6AA7A8539543210563AF6cf575Cc5079b194D0C8, spender=Vyper_contract, value=0 )
251 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000dcef968d416a41cdac0ed8702fac8128a64241a2, 0x0000000000000000000000006aa7a8539543210563af6cf575cc5079b194d0c8, 000000000000000000000000000000000000000000000000000000028abea50e )
252 Vyper_contract.TokenExchange( buyer=0x6AA7A8539543210563AF6cf575Cc5079b194D0C8, sold_id=0, tokens_sold=10940799366699973588980, bought_id=1, tokens_bought=10917684494 )
253 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000006aa7a8539543210563af6cf575cc5079b194d0c8, 0x00000000000000000000000031373595f40ea48a7aab6cbcb0d377c6066e2dca, 000000000000000000000000000000000000000000000000000000028abea50e )
254 TetherToken.Transfer( from=0x31373595F40Ea48a7aAb6CBCB0d377C6066E2dCA, to=[Receiver] 0xad3b67bca8935cb510c8d18bd45f0b94f54a968f, value=10918686205 )
255 0x31373595f40ea48a7aab6cbcb0d377c6066e2dca.0x103ed084e94a44c8f5f6ba8e3011507c41063177e29949083c439777d8d63f60( 0x103ed084e94a44c8f5f6ba8e3011507c41063177e29949083c439777d8d63f60, 0000000000000000000000006aa7a8539543210563af6cf575cc5079b194d0c8, 000000000000000000000000ad3b67bca8935cb510c8d18bd45f0b94f54a968f, 000000000000000000000000000000000000000000000000000000028abea50e, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000062, 000000000000000000000000000000000000000000000000000000028abea50e, 000000000000000000000000000000000000000000000000000000028acdedfd )
256 TetherToken.Transfer( from=[Receiver] 0xad3b67bca8935cb510c8d18bd45f0b94f54a968f, to=0xB813d6006578D60e70fCC04d21895B05F363AeAf, value=10917937264 )
257 AggregationRouterV6.OrderFilled( orderHash=C9C174D27F5D6A0C997597104F2A2C4035A1625DB2D21556C6805AA08802DB23, remainingAmount=0 )

Account State Difference:

  Address   Before After State Difference Code
0x11111112...0f8842A65
(Aggregation Router V6)
0x31373595...6066E2dCA
(Titan Builder)
11.808288305278856485 Eth11.808329820278856485 Eth0.000041515
0x4c9EDD58...bff1E68B3
0x5dc1BF6f...736fA0743
0x853d955a...7F175b99e
0xA0b86991...E3606eB48
0xdAC17F95...13D831ec7
0xDcEF968d...8A64241A2
(Curve Finance: Swap)
0xff86DdE3...C53aa5937
2.79892848100598894 Eth
Nonce: 24241
2.79864069361616929 Eth
Nonce: 24242
0.00028778738981965

Execution Trace

0xad3b67bca8935cb510c8d18bd45f0b94f54a968f.146ee74d( )
  • 0x96a3827fda7385e54c79c52d0aa0980e2a42bbc9.146ee74d( )
    • TetherToken.balanceOf( who=0xad3b67BCA8935Cb510C8D18bD45F0b94F54A968f ) => ( 148316162 )
    • AggregationRouterV6.fillOrderArgs( order=[{name:salt, type:uint256, order:1, indexed:false, value:102412815599886129447284593373303511173597606645982699598559457227301685931607, valueString:102412815599886129447284593373303511173597606645982699598559457227301685931607}, {name:maker, type:uint256, order:2, indexed:false, value:1050896658154427395768488047336096006558118555311, valueString:1050896658154427395768488047336096006558118555311}, {name:receiver, type:uint256, order:3, indexed:false, value:0, valueString:0}, {name:makerAsset, type:uint256, order:4, indexed:false, value:437426098194082895061803372304124391670360729779, valueString:437426098194082895061803372304124391670360729779}, {name:takerAsset, type:uint256, order:5, indexed:false, value:1248875146012964071876423320777688075155124985543, valueString:1248875146012964071876423320777688075155124985543}, {name:makingAmount, type:uint256, order:6, indexed:false, value:10926138746223216970009, valueString:10926138746223216970009}, {name:takingAmount, type:uint256, order:7, indexed:false, value:10916454809, valueString:10916454809}, {name:makerTraits, type:uint256, order:8, indexed:false, value:33471150795161712739625987854073848363835857047666517026738313108447551291392, valueString:33471150795161712739625987854073848363835857047666517026738313108447551291392}], r=634B7F59A7B59052D44CF08C0173B567A7EDCF95F91475038D2392FAEA5FC2FE, vs=B5FAEA1FF83887BE49998F4ACE232CEE1219D394CBAE34AAB09EE7E11CE80ED4, amount=10926138746223216970009, takerTraits=61514555360510020612414826994406391039866359852231291108414165642593898880002, args=0x6AA7A8539543210563AF6CF575CC5079B194D0C8000001070000005E0000005E0000005E0000005E0000002F0000000000000000FB2809A5314473E1165F6B58018E20ED8F07B8400002A90000024D67CC0C5C0000B40007FB0003B900900002A90024FB2809A5314473E1165F6B58018E20ED8F07B8400002A90000024D67CC0C5C0000B40007FB0003B900900002A90024FB2809A5314473E1165F6B58018E20ED8F07B84067CC0C38D18BD45F0B94F54A968F0000B09498030AE3416B66DC00240CF7A62884E542B3BDDD0000972AD4499F120902631A0000D61B892B2AD624901185000095770895AD27AD6B0D950000339FB574BDC56763F9950000617556ED277AB322337800006DE5E0E428AC771D77B500005B7D1434EAE4A48B2C86000026813BD1B091EA6BEDBD000000000000000000000000000060AD3B67BCA8935CB510C8D18BD45F0B94F54A968F01000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000031E6AA7A8539543210563AF6CF575CC5079B194D0C8D9C453570000000000000000000000000000000000000000000000000002C800001A0020D6BDBF784C9EDD5852CD905F086C759E8383E09BFF1E68B300A007E5C0D200000000000000000000000000000000000000028A00017A0001600000B051205DC1BF6F1E983C0B21EFB003C105133736FA07434C9EDD5852CD905F086C759E8383E09BFF1E68B300443DF0212400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015120DCEF968D416A41CDAC0ED8702FAC8128A64241A2853D955ACEF822DB058EB8505911ED77F175B99E00443DF0212400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010020D6BDBF78A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48492231373595F40EA48A7AAB6CBCB0D377C6066E2DCAA0B86991C6218B36C1D19D4A2E9EB0CE3606EB4800243EECE7DB000000000000000000000000AD3B67BCA8935CB510C8D18BD45F0B94F54A968F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006200000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002504EA29486A2BE49190000 ) => ( 10926138746223216970009, 10917937264, C9C174D27F5D6A0C997597104F2A2C4035A1625DB2D21556C6805AA08802DB23 )
      • Null: 0x000...001.c9c174d2( )
      • Settlement.getTakingAmount( order=[{name:salt, type:uint256, order:1, indexed:false, value:102412815599886129447284593373303511173597606645982699598559457227301685931607, valueString:102412815599886129447284593373303511173597606645982699598559457227301685931607}, {name:maker, type:uint256, order:2, indexed:false, value:1050896658154427395768488047336096006558118555311, valueString:1050896658154427395768488047336096006558118555311}, {name:receiver, type:uint256, order:3, indexed:false, value:0, valueString:0}, {name:makerAsset, type:uint256, order:4, indexed:false, value:437426098194082895061803372304124391670360729779, valueString:437426098194082895061803372304124391670360729779}, {name:takerAsset, type:uint256, order:5, indexed:false, value:1248875146012964071876423320777688075155124985543, valueString:1248875146012964071876423320777688075155124985543}, {name:makingAmount, type:uint256, order:6, indexed:false, value:10926138746223216970009, valueString:10926138746223216970009}, {name:takingAmount, type:uint256, order:7, indexed:false, value:10916454809, valueString:10916454809}, {name:makerTraits, type:uint256, order:8, indexed:false, value:33471150795161712739625987854073848363835857047666517026738313108447551291392, valueString:33471150795161712739625987854073848363835857047666517026738313108447551291392}], 0x000001070000005E0000005E0000005E0000005E0000002F0000000000000000FB2809A5314473E1165F6B58018E20ED8F07B8400002A90000024D67CC0C5C0000B40007FB0003B900900002A90024FB2809A5314473E1165F6B58018E20ED8F07B8400002A90000024D67CC0C5C0000B40007FB0003B900900002A90024FB2809A5314473E1165F6B58018E20ED8F07B84067CC0C38D18BD45F0B94F54A968F0000B09498030AE3416B66DC00240CF7A62884E542B3BDDD0000972AD4499F120902631A0000D61B892B2AD624901185000095770895AD27AD6B0D950000339FB574BDC56763F9950000617556ED277AB322337800006DE5E0E428AC771D77B500005B7D1434EAE4A48B2C86000026813BD1B091EA6BEDBD000000000000000000000000000060, C9C174D27F5D6A0C997597104F2A2C4035A1625DB2D21556C6805AA08802DB23, 0xad3b67BCA8935Cb510C8D18bD45F0b94F54A968f, makingAmount=10926138746223216970009, 10926138746223216970009, extraData=0x0002A90000024D67CC0C5C0000B40007FB0003B900900002A90024 ) => ( 10917937264 )
      • USDe.transferFrom( from=0xB813d6006578D60e70fCC04d21895B05F363AeAf, to=0x6AA7A8539543210563AF6cf575Cc5079b194D0C8, amount=10926138746223216970009 ) => ( True )
      • 0xad3b67bca8935cb510c8d18bd45f0b94f54a968f.adf38ba1( )
        • 0x96a3827fda7385e54c79c52d0aa0980e2a42bbc9.adf38ba1( )
          • 0x6aa7a8539543210563af6cf575cc5079b194d0c8.d9c45357( )
            • 0x6aa7a8539543210563af6cf575cc5079b194d0c8.d6bdbf78( )
            • 0x6aa7a8539543210563af6cf575cc5079b194d0c8.07e5c0d2( )
            • TetherToken.transferFrom( _from=0xad3b67BCA8935Cb510C8D18bD45F0b94F54A968f, _to=0xB813d6006578D60e70fCC04d21895B05F363AeAf, _value=10917937264 )
            • Settlement.postInteraction( order=[{name:salt, type:uint256, order:1, indexed:false, value:102412815599886129447284593373303511173597606645982699598559457227301685931607, valueString:102412815599886129447284593373303511173597606645982699598559457227301685931607}, {name:maker, type:uint256, order:2, indexed:false, value:1050896658154427395768488047336096006558118555311, valueString:1050896658154427395768488047336096006558118555311}, {name:receiver, type:uint256, order:3, indexed:false, value:0, valueString:0}, {name:makerAsset, type:uint256, order:4, indexed:false, value:437426098194082895061803372304124391670360729779, valueString:437426098194082895061803372304124391670360729779}, {name:takerAsset, type:uint256, order:5, indexed:false, value:1248875146012964071876423320777688075155124985543, valueString:1248875146012964071876423320777688075155124985543}, {name:makingAmount, type:uint256, order:6, indexed:false, value:10926138746223216970009, valueString:10926138746223216970009}, {name:takingAmount, type:uint256, order:7, indexed:false, value:10916454809, valueString:10916454809}, {name:makerTraits, type:uint256, order:8, indexed:false, value:33471150795161712739625987854073848363835857047666517026738313108447551291392, valueString:33471150795161712739625987854073848363835857047666517026738313108447551291392}], extension=0x000001070000005E0000005E0000005E0000005E0000002F0000000000000000FB2809A5314473E1165F6B58018E20ED8F07B8400002A90000024D67CC0C5C0000B40007FB0003B900900002A90024FB2809A5314473E1165F6B58018E20ED8F07B8400002A90000024D67CC0C5C0000B40007FB0003B900900002A90024FB2809A5314473E1165F6B58018E20ED8F07B84067CC0C38D18BD45F0B94F54A968F0000B09498030AE3416B66DC00240CF7A62884E542B3BDDD0000972AD4499F120902631A0000D61B892B2AD624901185000095770895AD27AD6B0D950000339FB574BDC56763F9950000617556ED277AB322337800006DE5E0E428AC771D77B500005B7D1434EAE4A48B2C86000026813BD1B091EA6BEDBD000000000000000000000000000060, orderHash=C9C174D27F5D6A0C997597104F2A2C4035A1625DB2D21556C6805AA08802DB23, taker=0xad3b67BCA8935Cb510C8D18bD45F0b94F54A968f, makingAmount=10926138746223216970009, takingAmount=10917937264, remainingMakingAmount=10926138746223216970009, extraData=0x67CC0C38D18BD45F0B94F54A968F0000B09498030AE3416B66DC00240CF7A62884E542B3BDDD0000972AD4499F120902631A0000D61B892B2AD624901185000095770895AD27AD6B0D950000339FB574BDC56763F9950000617556ED277AB322337800006DE5E0E428AC771D77B500005B7D1434EAE4A48B2C86000026813BD1B091EA6BEDBD000000000000000000000000000060 )
            • TetherToken.balanceOf( who=0xad3b67BCA8935Cb510C8D18bD45F0b94F54A968f ) => ( 149065103 )
              File 1 of 8: USDe
              // SPDX-License-Identifier: GPL-3.0
              pragma solidity 0.8.19;
              import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
              import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
              import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
              import "@openzeppelin/contracts/access/Ownable2Step.sol";
              import "./interfaces/IUSDeDefinitions.sol";
              /**
               * @title USDe
               * @notice USDe Genesis Story: Arthur Hayes' $Nakadollar in "Dust on Crust" 08/03/2023
               */
              contract USDe is Ownable2Step, ERC20Burnable, ERC20Permit, IUSDeDefinitions {
                address public minter;
                constructor(address admin) ERC20("USDe", "USDe") ERC20Permit("USDe") {
                  if (admin == address(0)) revert ZeroAddressException();
                  _transferOwnership(admin);
                }
                function setMinter(address newMinter) external onlyOwner {
                  emit MinterUpdated(newMinter, minter);
                  minter = newMinter;
                }
                function mint(address to, uint256 amount) external {
                  if (msg.sender != minter) revert OnlyMinter();
                  _mint(to, amount);
                }
                function renounceOwnership() public view override onlyOwner {
                  revert CantRenounceOwnership();
                }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
              pragma solidity ^0.8.0;
              import "./IERC20.sol";
              import "./extensions/IERC20Metadata.sol";
              import "../../utils/Context.sol";
              /**
               * @dev Implementation of the {IERC20} interface.
               *
               * This implementation is agnostic to the way tokens are created. This means
               * that a supply mechanism has to be added in a derived contract using {_mint}.
               * For a generic mechanism see {ERC20PresetMinterPauser}.
               *
               * TIP: For a detailed writeup see our guide
               * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
               * to implement supply mechanisms].
               *
               * The default value of {decimals} is 18. To change this, you should override
               * this function so it returns a different value.
               *
               * We have followed general OpenZeppelin Contracts guidelines: functions revert
               * instead returning `false` on failure. This behavior is nonetheless
               * conventional and does not conflict with the expectations of ERC20
               * applications.
               *
               * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
               * This allows applications to reconstruct the allowance for all accounts just
               * by listening to said events. Other implementations of the EIP may not emit
               * these events, as it isn't required by the specification.
               *
               * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
               * functions have been added to mitigate the well-known issues around setting
               * allowances. See {IERC20-approve}.
               */
              contract ERC20 is Context, IERC20, IERC20Metadata {
                  mapping(address => uint256) private _balances;
                  mapping(address => mapping(address => uint256)) private _allowances;
                  uint256 private _totalSupply;
                  string private _name;
                  string private _symbol;
                  /**
                   * @dev Sets the values for {name} and {symbol}.
                   *
                   * All two of these values are immutable: they can only be set once during
                   * construction.
                   */
                  constructor(string memory name_, string memory symbol_) {
                      _name = name_;
                      _symbol = symbol_;
                  }
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() public view virtual override returns (string memory) {
                      return _name;
                  }
                  /**
                   * @dev Returns the symbol of the token, usually a shorter version of the
                   * name.
                   */
                  function symbol() public view virtual override returns (string memory) {
                      return _symbol;
                  }
                  /**
                   * @dev Returns the number of decimals used to get its user representation.
                   * For example, if `decimals` equals `2`, a balance of `505` tokens should
                   * be displayed to a user as `5.05` (`505 / 10 ** 2`).
                   *
                   * Tokens usually opt for a value of 18, imitating the relationship between
                   * Ether and Wei. This is the default value returned by this function, unless
                   * it's overridden.
                   *
                   * NOTE: This information is only used for _display_ purposes: it in
                   * no way affects any of the arithmetic of the contract, including
                   * {IERC20-balanceOf} and {IERC20-transfer}.
                   */
                  function decimals() public view virtual override returns (uint8) {
                      return 18;
                  }
                  /**
                   * @dev See {IERC20-totalSupply}.
                   */
                  function totalSupply() public view virtual override returns (uint256) {
                      return _totalSupply;
                  }
                  /**
                   * @dev See {IERC20-balanceOf}.
                   */
                  function balanceOf(address account) public view virtual override returns (uint256) {
                      return _balances[account];
                  }
                  /**
                   * @dev See {IERC20-transfer}.
                   *
                   * Requirements:
                   *
                   * - `to` cannot be the zero address.
                   * - the caller must have a balance of at least `amount`.
                   */
                  function transfer(address to, uint256 amount) public virtual override returns (bool) {
                      address owner = _msgSender();
                      _transfer(owner, to, amount);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-allowance}.
                   */
                  function allowance(address owner, address spender) public view virtual override returns (uint256) {
                      return _allowances[owner][spender];
                  }
                  /**
                   * @dev See {IERC20-approve}.
                   *
                   * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
                   * `transferFrom`. This is semantically equivalent to an infinite approval.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   */
                  function approve(address spender, uint256 amount) public virtual override returns (bool) {
                      address owner = _msgSender();
                      _approve(owner, spender, amount);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-transferFrom}.
                   *
                   * Emits an {Approval} event indicating the updated allowance. This is not
                   * required by the EIP. See the note at the beginning of {ERC20}.
                   *
                   * NOTE: Does not update the allowance if the current allowance
                   * is the maximum `uint256`.
                   *
                   * Requirements:
                   *
                   * - `from` and `to` cannot be the zero address.
                   * - `from` must have a balance of at least `amount`.
                   * - the caller must have allowance for ``from``'s tokens of at least
                   * `amount`.
                   */
                  function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
                      address spender = _msgSender();
                      _spendAllowance(from, spender, amount);
                      _transfer(from, to, amount);
                      return true;
                  }
                  /**
                   * @dev Atomically increases the allowance granted to `spender` by the caller.
                   *
                   * This is an alternative to {approve} that can be used as a mitigation for
                   * problems described in {IERC20-approve}.
                   *
                   * Emits an {Approval} event indicating the updated allowance.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   */
                  function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                      address owner = _msgSender();
                      _approve(owner, spender, allowance(owner, spender) + addedValue);
                      return true;
                  }
                  /**
                   * @dev Atomically decreases the allowance granted to `spender` by the caller.
                   *
                   * This is an alternative to {approve} that can be used as a mitigation for
                   * problems described in {IERC20-approve}.
                   *
                   * Emits an {Approval} event indicating the updated allowance.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   * - `spender` must have allowance for the caller of at least
                   * `subtractedValue`.
                   */
                  function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                      address owner = _msgSender();
                      uint256 currentAllowance = allowance(owner, spender);
                      require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
                      unchecked {
                          _approve(owner, spender, currentAllowance - subtractedValue);
                      }
                      return true;
                  }
                  /**
                   * @dev Moves `amount` of tokens from `from` to `to`.
                   *
                   * This internal function is equivalent to {transfer}, and can be used to
                   * e.g. implement automatic token fees, slashing mechanisms, etc.
                   *
                   * Emits a {Transfer} event.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `from` must have a balance of at least `amount`.
                   */
                  function _transfer(address from, address to, uint256 amount) internal virtual {
                      require(from != address(0), "ERC20: transfer from the zero address");
                      require(to != address(0), "ERC20: transfer to the zero address");
                      _beforeTokenTransfer(from, to, amount);
                      uint256 fromBalance = _balances[from];
                      require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
                      unchecked {
                          _balances[from] = fromBalance - amount;
                          // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
                          // decrementing then incrementing.
                          _balances[to] += amount;
                      }
                      emit Transfer(from, to, amount);
                      _afterTokenTransfer(from, to, amount);
                  }
                  /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                   * the total supply.
                   *
                   * Emits a {Transfer} event with `from` set to the zero address.
                   *
                   * Requirements:
                   *
                   * - `account` cannot be the zero address.
                   */
                  function _mint(address account, uint256 amount) internal virtual {
                      require(account != address(0), "ERC20: mint to the zero address");
                      _beforeTokenTransfer(address(0), account, amount);
                      _totalSupply += amount;
                      unchecked {
                          // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
                          _balances[account] += amount;
                      }
                      emit Transfer(address(0), account, amount);
                      _afterTokenTransfer(address(0), account, amount);
                  }
                  /**
                   * @dev Destroys `amount` tokens from `account`, reducing the
                   * total supply.
                   *
                   * Emits a {Transfer} event with `to` set to the zero address.
                   *
                   * Requirements:
                   *
                   * - `account` cannot be the zero address.
                   * - `account` must have at least `amount` tokens.
                   */
                  function _burn(address account, uint256 amount) internal virtual {
                      require(account != address(0), "ERC20: burn from the zero address");
                      _beforeTokenTransfer(account, address(0), amount);
                      uint256 accountBalance = _balances[account];
                      require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
                      unchecked {
                          _balances[account] = accountBalance - amount;
                          // Overflow not possible: amount <= accountBalance <= totalSupply.
                          _totalSupply -= amount;
                      }
                      emit Transfer(account, address(0), amount);
                      _afterTokenTransfer(account, address(0), amount);
                  }
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
                   *
                   * This internal function is equivalent to `approve`, and can be used to
                   * e.g. set automatic allowances for certain subsystems, etc.
                   *
                   * Emits an {Approval} event.
                   *
                   * Requirements:
                   *
                   * - `owner` cannot be the zero address.
                   * - `spender` cannot be the zero address.
                   */
                  function _approve(address owner, address spender, uint256 amount) internal virtual {
                      require(owner != address(0), "ERC20: approve from the zero address");
                      require(spender != address(0), "ERC20: approve to the zero address");
                      _allowances[owner][spender] = amount;
                      emit Approval(owner, spender, amount);
                  }
                  /**
                   * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
                   *
                   * Does not update the allowance amount in case of infinite allowance.
                   * Revert if not enough allowance is available.
                   *
                   * Might emit an {Approval} event.
                   */
                  function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
                      uint256 currentAllowance = allowance(owner, spender);
                      if (currentAllowance != type(uint256).max) {
                          require(currentAllowance >= amount, "ERC20: insufficient allowance");
                          unchecked {
                              _approve(owner, spender, currentAllowance - amount);
                          }
                      }
                  }
                  /**
                   * @dev Hook that is called before any transfer of tokens. This includes
                   * minting and burning.
                   *
                   * Calling conditions:
                   *
                   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                   * will be transferred to `to`.
                   * - when `from` is zero, `amount` tokens will be minted for `to`.
                   * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
                   * - `from` and `to` are never both zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
                  /**
                   * @dev Hook that is called after any transfer of tokens. This includes
                   * minting and burning.
                   *
                   * Calling conditions:
                   *
                   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                   * has been transferred to `to`.
                   * - when `from` is zero, `amount` tokens have been minted for `to`.
                   * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
                   * - `from` and `to` are never both zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)
              pragma solidity ^0.8.0;
              import "../ERC20.sol";
              import "../../../utils/Context.sol";
              /**
               * @dev Extension of {ERC20} that allows token holders to destroy both their own
               * tokens and those that they have an allowance for, in a way that can be
               * recognized off-chain (via event analysis).
               */
              abstract contract ERC20Burnable is Context, ERC20 {
                  /**
                   * @dev Destroys `amount` tokens from the caller.
                   *
                   * See {ERC20-_burn}.
                   */
                  function burn(uint256 amount) public virtual {
                      _burn(_msgSender(), amount);
                  }
                  /**
                   * @dev Destroys `amount` tokens from `account`, deducting from the caller's
                   * allowance.
                   *
                   * See {ERC20-_burn} and {ERC20-allowance}.
                   *
                   * Requirements:
                   *
                   * - the caller must have allowance for ``accounts``'s tokens of at least
                   * `amount`.
                   */
                  function burnFrom(address account, uint256 amount) public virtual {
                      _spendAllowance(account, _msgSender(), amount);
                      _burn(account, amount);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/ERC20Permit.sol)
              pragma solidity ^0.8.0;
              import "./IERC20Permit.sol";
              import "../ERC20.sol";
              import "../../../utils/cryptography/ECDSA.sol";
              import "../../../utils/cryptography/EIP712.sol";
              import "../../../utils/Counters.sol";
              /**
               * @dev Implementation 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.
               *
               * _Available since v3.4._
               */
              abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
                  using Counters for Counters.Counter;
                  mapping(address => Counters.Counter) private _nonces;
                  // solhint-disable-next-line var-name-mixedcase
                  bytes32 private constant _PERMIT_TYPEHASH =
                      keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
                  /**
                   * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.
                   * However, to ensure consistency with the upgradeable transpiler, we will continue
                   * to reserve a slot.
                   * @custom:oz-renamed-from _PERMIT_TYPEHASH
                   */
                  // solhint-disable-next-line var-name-mixedcase
                  bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;
                  /**
                   * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
                   *
                   * It's a good idea to use the same `name` that is defined as the ERC20 token name.
                   */
                  constructor(string memory name) EIP712(name, "1") {}
                  /**
                   * @dev See {IERC20Permit-permit}.
                   */
                  function permit(
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) public virtual override {
                      require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
                      bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
                      bytes32 hash = _hashTypedDataV4(structHash);
                      address signer = ECDSA.recover(hash, v, r, s);
                      require(signer == owner, "ERC20Permit: invalid signature");
                      _approve(owner, spender, value);
                  }
                  /**
                   * @dev See {IERC20Permit-nonces}.
                   */
                  function nonces(address owner) public view virtual override returns (uint256) {
                      return _nonces[owner].current();
                  }
                  /**
                   * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
                   */
                  // solhint-disable-next-line func-name-mixedcase
                  function DOMAIN_SEPARATOR() external view override returns (bytes32) {
                      return _domainSeparatorV4();
                  }
                  /**
                   * @dev "Consume a nonce": return the current value and increment.
                   *
                   * _Available since v4.1._
                   */
                  function _useNonce(address owner) internal virtual returns (uint256 current) {
                      Counters.Counter storage nonce = _nonces[owner];
                      current = nonce.current();
                      nonce.increment();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)
              pragma solidity ^0.8.0;
              import "./Ownable.sol";
              /**
               * @dev Contract module which provides 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} and {acceptOwnership}.
               *
               * This module is used through inheritance. It will make available all functions
               * from parent (Ownable).
               */
              abstract contract Ownable2Step is Ownable {
                  address private _pendingOwner;
                  event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
                  /**
                   * @dev Returns the address of the pending owner.
                   */
                  function pendingOwner() public view virtual returns (address) {
                      return _pendingOwner;
                  }
                  /**
                   * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
                   * Can only be called by the current owner.
                   */
                  function transferOwnership(address newOwner) public virtual override onlyOwner {
                      _pendingOwner = newOwner;
                      emit OwnershipTransferStarted(owner(), newOwner);
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
                   * Internal function without access restriction.
                   */
                  function _transferOwnership(address newOwner) internal virtual override {
                      delete _pendingOwner;
                      super._transferOwnership(newOwner);
                  }
                  /**
                   * @dev The new owner accepts the ownership transfer.
                   */
                  function acceptOwnership() public virtual {
                      address sender = _msgSender();
                      require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
                      _transferOwnership(sender);
                  }
              }
              // SPDX-License-Identifier: GPL-3.0
              pragma solidity 0.8.19;
              interface IUSDeDefinitions {
                /// @notice This event is fired when the minter changes
                event MinterUpdated(address indexed newMinter, address indexed oldMinter);
                /// @notice Zero address not allowed
                error ZeroAddressException();
                /// @notice It's not possible to renounce the ownership
                error CantRenounceOwnership();
                /// @notice Only the minter role can perform an action
                error OnlyMinter();
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  /**
                   * @dev Emitted when `value` tokens are moved from one account (`from`) to
                   * another (`to`).
                   *
                   * Note that `value` may be zero.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  /**
                   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                   * a call to {approve}. `value` is the new allowance.
                   */
                  event Approval(address indexed owner, address indexed spender, uint256 value);
                  /**
                   * @dev Returns the amount of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
                  /**
                   * @dev Returns the amount of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
                  /**
                   * @dev Moves `amount` tokens from the caller's account to `to`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address to, uint256 amount) external returns (bool);
                  /**
                   * @dev Returns the remaining number of tokens that `spender` will be
                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                   * zero by default.
                   *
                   * This value changes when {approve} or {transferFrom} are called.
                   */
                  function allowance(address owner, address spender) external view returns (uint256);
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                   * that someone may use both the old and the new allowance by unfortunate
                   * transaction ordering. One possible solution to mitigate this race
                   * condition is to first reduce the spender's allowance to 0 and set the
                   * desired value afterwards:
                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address spender, uint256 amount) external returns (bool);
                  /**
                   * @dev Moves `amount` tokens from `from` to `to` using the
                   * allowance mechanism. `amount` is then deducted from the caller's
                   * allowance.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(address from, address to, uint256 amount) external returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
              pragma solidity ^0.8.0;
              import "../IERC20.sol";
              /**
               * @dev Interface for the optional metadata functions from the ERC20 standard.
               *
               * _Available since v4.1._
               */
              interface IERC20Metadata is IERC20 {
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() external view returns (string memory);
                  /**
                   * @dev Returns the symbol of the token.
                   */
                  function symbol() external view returns (string memory);
                  /**
                   * @dev Returns the decimals places of the token.
                   */
                  function decimals() external view returns (uint8);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
               * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
               *
               * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
               * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
               * need to send a transaction, and thus is not required to hold Ether at all.
               */
              interface IERC20Permit {
                  /**
                   * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                   * given ``owner``'s signed approval.
                   *
                   * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                   * ordering also apply here.
                   *
                   * Emits an {Approval} event.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   * - `deadline` must be a timestamp in the future.
                   * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                   * over the EIP712-formatted function arguments.
                   * - the signature must use ``owner``'s current nonce (see {nonces}).
                   *
                   * For more information on the signature format, see the
                   * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                   * section].
                   */
                  function permit(
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) external;
                  /**
                   * @dev Returns the current nonce for `owner`. This value must be
                   * included whenever a signature is generated for {permit}.
                   *
                   * Every successful call to {permit} increases ``owner``'s nonce by one. This
                   * prevents a signature from being used multiple times.
                   */
                  function nonces(address owner) external view returns (uint256);
                  /**
                   * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                   */
                  // solhint-disable-next-line func-name-mixedcase
                  function DOMAIN_SEPARATOR() external view returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
              pragma solidity ^0.8.0;
              import "../Strings.sol";
              /**
               * @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 {
                  enum RecoverError {
                      NoError,
                      InvalidSignature,
                      InvalidSignatureLength,
                      InvalidSignatureS,
                      InvalidSignatureV // Deprecated in v4.8
                  }
                  function _throwError(RecoverError error) private pure {
                      if (error == RecoverError.NoError) {
                          return; // no error: do nothing
                      } else if (error == RecoverError.InvalidSignature) {
                          revert("ECDSA: invalid signature");
                      } else if (error == RecoverError.InvalidSignatureLength) {
                          revert("ECDSA: invalid signature length");
                      } else if (error == RecoverError.InvalidSignatureS) {
                          revert("ECDSA: invalid signature 's' value");
                      }
                  }
                  /**
                   * @dev Returns the address that signed a hashed message (`hash`) with
                   * `signature` or error string. 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.
                   *
                   * Documentation for signature generation:
                   * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
                   * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
                   *
                   * _Available since v4.3._
                   */
                  function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
                      if (signature.length == 65) {
                          bytes32 r;
                          bytes32 s;
                          uint8 v;
                          // ecrecover takes the signature parameters, and the only way to get them
                          // currently is to use assembly.
                          /// @solidity memory-safe-assembly
                          assembly {
                              r := mload(add(signature, 0x20))
                              s := mload(add(signature, 0x40))
                              v := byte(0, mload(add(signature, 0x60)))
                          }
                          return tryRecover(hash, v, r, s);
                      } else {
                          return (address(0), RecoverError.InvalidSignatureLength);
                      }
                  }
                  /**
                   * @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) {
                      (address recovered, RecoverError error) = tryRecover(hash, signature);
                      _throwError(error);
                      return recovered;
                  }
                  /**
                   * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
                   *
                   * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
                   *
                   * _Available since v4.3._
                   */
                  function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
                      bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
                      uint8 v = uint8((uint256(vs) >> 255) + 27);
                      return tryRecover(hash, v, r, s);
                  }
                  /**
                   * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
                   *
                   * _Available since v4.2._
                   */
                  function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
                      (address recovered, RecoverError error) = tryRecover(hash, r, vs);
                      _throwError(error);
                      return recovered;
                  }
                  /**
                   * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
                   * `r` and `s` signature fields separately.
                   *
                   * _Available since v4.3._
                   */
                  function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
                      // 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 (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): 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) {
                          return (address(0), RecoverError.InvalidSignatureS);
                      }
                      // If the signature is valid (and not malleable), return the signer address
                      address signer = ecrecover(hash, v, r, s);
                      if (signer == address(0)) {
                          return (address(0), RecoverError.InvalidSignature);
                      }
                      return (signer, RecoverError.NoError);
                  }
                  /**
                   * @dev Overload of {ECDSA-recover} that receives the `v`,
                   * `r` and `s` signature fields separately.
                   */
                  function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
                      (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
                      _throwError(error);
                      return recovered;
                  }
                  /**
                   * @dev Returns an Ethereum Signed Message, created from a `hash`. This
                   * produces hash corresponding to the one signed with the
                   * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                   * JSON-RPC method as part of EIP-191.
                   *
                   * See {recover}.
                   */
                  function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
                      // 32 is the length in bytes of hash,
                      // enforced by the type signature above
                      /// @solidity memory-safe-assembly
                      assembly {
                          mstore(0x00, "\\x19Ethereum Signed Message:\
              32")
                          mstore(0x1c, hash)
                          message := keccak256(0x00, 0x3c)
                      }
                  }
                  /**
                   * @dev Returns an Ethereum Signed Message, created from `s`. This
                   * produces hash corresponding to the one signed with the
                   * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                   * JSON-RPC method as part of EIP-191.
                   *
                   * See {recover}.
                   */
                  function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
                      return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
              ", Strings.toString(s.length), s));
                  }
                  /**
                   * @dev Returns an Ethereum Signed Typed Data, created from a
                   * `domainSeparator` and a `structHash`. This produces hash corresponding
                   * to the one signed with the
                   * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
                   * JSON-RPC method as part of EIP-712.
                   *
                   * See {recover}.
                   */
                  function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          let ptr := mload(0x40)
                          mstore(ptr, "\\x19\\x01")
                          mstore(add(ptr, 0x02), domainSeparator)
                          mstore(add(ptr, 0x22), structHash)
                          data := keccak256(ptr, 0x42)
                      }
                  }
                  /**
                   * @dev Returns an Ethereum Signed Data with intended validator, created from a
                   * `validator` and `data` according to the version 0 of EIP-191.
                   *
                   * See {recover}.
                   */
                  function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
                      return keccak256(abi.encodePacked("\\x19\\x00", validator, data));
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)
              pragma solidity ^0.8.8;
              import "./ECDSA.sol";
              import "../ShortStrings.sol";
              import "../../interfaces/IERC5267.sol";
              /**
               * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
               *
               * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
               * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
               * they need in their contracts using a combination of `abi.encode` and `keccak256`.
               *
               * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
               * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
               * ({_hashTypedDataV4}).
               *
               * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
               * the chain id to protect against replay attacks on an eventual fork of the chain.
               *
               * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
               * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
               *
               * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
               * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
               * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
               *
               * _Available since v3.4._
               *
               * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
               */
              abstract contract EIP712 is IERC5267 {
                  using ShortStrings for *;
                  bytes32 private constant _TYPE_HASH =
                      keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
                  // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
                  // invalidate the cached domain separator if the chain id changes.
                  bytes32 private immutable _cachedDomainSeparator;
                  uint256 private immutable _cachedChainId;
                  address private immutable _cachedThis;
                  bytes32 private immutable _hashedName;
                  bytes32 private immutable _hashedVersion;
                  ShortString private immutable _name;
                  ShortString private immutable _version;
                  string private _nameFallback;
                  string private _versionFallback;
                  /**
                   * @dev Initializes the domain separator and parameter caches.
                   *
                   * The meaning of `name` and `version` is specified in
                   * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
                   *
                   * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
                   * - `version`: the current major version of the signing domain.
                   *
                   * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
                   * contract upgrade].
                   */
                  constructor(string memory name, string memory version) {
                      _name = name.toShortStringWithFallback(_nameFallback);
                      _version = version.toShortStringWithFallback(_versionFallback);
                      _hashedName = keccak256(bytes(name));
                      _hashedVersion = keccak256(bytes(version));
                      _cachedChainId = block.chainid;
                      _cachedDomainSeparator = _buildDomainSeparator();
                      _cachedThis = address(this);
                  }
                  /**
                   * @dev Returns the domain separator for the current chain.
                   */
                  function _domainSeparatorV4() internal view returns (bytes32) {
                      if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
                          return _cachedDomainSeparator;
                      } else {
                          return _buildDomainSeparator();
                      }
                  }
                  function _buildDomainSeparator() private view returns (bytes32) {
                      return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
                  }
                  /**
                   * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
                   * function returns the hash of the fully encoded EIP712 message for this domain.
                   *
                   * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
                   *
                   * ```solidity
                   * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
                   *     keccak256("Mail(address to,string contents)"),
                   *     mailTo,
                   *     keccak256(bytes(mailContents))
                   * )));
                   * address signer = ECDSA.recover(digest, signature);
                   * ```
                   */
                  function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
                      return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
                  }
                  /**
                   * @dev See {EIP-5267}.
                   *
                   * _Available since v4.9._
                   */
                  function eip712Domain()
                      public
                      view
                      virtual
                      override
                      returns (
                          bytes1 fields,
                          string memory name,
                          string memory version,
                          uint256 chainId,
                          address verifyingContract,
                          bytes32 salt,
                          uint256[] memory extensions
                      )
                  {
                      return (
                          hex"0f", // 01111
                          _name.toStringWithFallback(_nameFallback),
                          _version.toStringWithFallback(_versionFallback),
                          block.chainid,
                          address(this),
                          bytes32(0),
                          new uint256[](0)
                      );
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
              pragma solidity ^0.8.0;
              /**
               * @title Counters
               * @author Matt Condon (@shrugs)
               * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
               * of elements in a mapping, issuing ERC721 ids, or counting request ids.
               *
               * Include with `using Counters for Counters.Counter;`
               */
              library Counters {
                  struct Counter {
                      // This variable should never be directly accessed by users of the library: interactions must be restricted to
                      // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
                      // this feature: see https://github.com/ethereum/solidity/issues/4637
                      uint256 _value; // default: 0
                  }
                  function current(Counter storage counter) internal view returns (uint256) {
                      return counter._value;
                  }
                  function increment(Counter storage counter) internal {
                      unchecked {
                          counter._value += 1;
                      }
                  }
                  function decrement(Counter storage counter) internal {
                      uint256 value = counter._value;
                      require(value > 0, "Counter: decrement overflow");
                      unchecked {
                          counter._value = value - 1;
                      }
                  }
                  function reset(Counter storage counter) internal {
                      counter._value = 0;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
              pragma solidity ^0.8.0;
              import "../utils/Context.sol";
              /**
               * @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 Ownable is Context {
                  address private _owner;
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  /**
                   * @dev Initializes the contract setting the deployer as the initial owner.
                   */
                  constructor() {
                      _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. Can only be called by the current owner.
                   *
                   * NOTE: Renouncing ownership will leave the contract without an owner,
                   * thereby disabling 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);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
              pragma solidity ^0.8.0;
              import "./math/Math.sol";
              import "./math/SignedMath.sol";
              /**
               * @dev String operations.
               */
              library Strings {
                  bytes16 private constant _SYMBOLS = "0123456789abcdef";
                  uint8 private constant _ADDRESS_LENGTH = 20;
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                   */
                  function toString(uint256 value) internal pure returns (string memory) {
                      unchecked {
                          uint256 length = Math.log10(value) + 1;
                          string memory buffer = new string(length);
                          uint256 ptr;
                          /// @solidity memory-safe-assembly
                          assembly {
                              ptr := add(buffer, add(32, length))
                          }
                          while (true) {
                              ptr--;
                              /// @solidity memory-safe-assembly
                              assembly {
                                  mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                              }
                              value /= 10;
                              if (value == 0) break;
                          }
                          return buffer;
                      }
                  }
                  /**
                   * @dev Converts a `int256` to its ASCII `string` decimal representation.
                   */
                  function toString(int256 value) internal pure returns (string memory) {
                      return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
                  }
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                   */
                  function toHexString(uint256 value) internal pure returns (string memory) {
                      unchecked {
                          return toHexString(value, Math.log256(value) + 1);
                      }
                  }
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                   */
                  function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                      bytes memory buffer = new bytes(2 * length + 2);
                      buffer[0] = "0";
                      buffer[1] = "x";
                      for (uint256 i = 2 * length + 1; i > 1; --i) {
                          buffer[i] = _SYMBOLS[value & 0xf];
                          value >>= 4;
                      }
                      require(value == 0, "Strings: hex length insufficient");
                      return string(buffer);
                  }
                  /**
                   * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
                   */
                  function toHexString(address addr) internal pure returns (string memory) {
                      return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
                  }
                  /**
                   * @dev Returns true if the two strings are equal.
                   */
                  function equal(string memory a, string memory b) internal pure returns (bool) {
                      return keccak256(bytes(a)) == keccak256(bytes(b));
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol)
              pragma solidity ^0.8.8;
              import "./StorageSlot.sol";
              // | string  | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   |
              // | length  | 0x                                                              BB |
              type ShortString is bytes32;
              /**
               * @dev This library provides functions to convert short memory strings
               * into a `ShortString` type that can be used as an immutable variable.
               *
               * Strings of arbitrary length can be optimized using this library if
               * they are short enough (up to 31 bytes) by packing them with their
               * length (1 byte) in a single EVM word (32 bytes). Additionally, a
               * fallback mechanism can be used for every other case.
               *
               * Usage example:
               *
               * ```solidity
               * contract Named {
               *     using ShortStrings for *;
               *
               *     ShortString private immutable _name;
               *     string private _nameFallback;
               *
               *     constructor(string memory contractName) {
               *         _name = contractName.toShortStringWithFallback(_nameFallback);
               *     }
               *
               *     function name() external view returns (string memory) {
               *         return _name.toStringWithFallback(_nameFallback);
               *     }
               * }
               * ```
               */
              library ShortStrings {
                  // Used as an identifier for strings longer than 31 bytes.
                  bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
                  error StringTooLong(string str);
                  error InvalidShortString();
                  /**
                   * @dev Encode a string of at most 31 chars into a `ShortString`.
                   *
                   * This will trigger a `StringTooLong` error is the input string is too long.
                   */
                  function toShortString(string memory str) internal pure returns (ShortString) {
                      bytes memory bstr = bytes(str);
                      if (bstr.length > 31) {
                          revert StringTooLong(str);
                      }
                      return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
                  }
                  /**
                   * @dev Decode a `ShortString` back to a "normal" string.
                   */
                  function toString(ShortString sstr) internal pure returns (string memory) {
                      uint256 len = byteLength(sstr);
                      // using `new string(len)` would work locally but is not memory safe.
                      string memory str = new string(32);
                      /// @solidity memory-safe-assembly
                      assembly {
                          mstore(str, len)
                          mstore(add(str, 0x20), sstr)
                      }
                      return str;
                  }
                  /**
                   * @dev Return the length of a `ShortString`.
                   */
                  function byteLength(ShortString sstr) internal pure returns (uint256) {
                      uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
                      if (result > 31) {
                          revert InvalidShortString();
                      }
                      return result;
                  }
                  /**
                   * @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
                   */
                  function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
                      if (bytes(value).length < 32) {
                          return toShortString(value);
                      } else {
                          StorageSlot.getStringSlot(store).value = value;
                          return ShortString.wrap(_FALLBACK_SENTINEL);
                      }
                  }
                  /**
                   * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
                   */
                  function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
                      if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
                          return toString(value);
                      } else {
                          return store;
                      }
                  }
                  /**
                   * @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
                   *
                   * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
                   * actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
                   */
                  function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
                      if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
                          return byteLength(value);
                      } else {
                          return bytes(store).length;
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)
              pragma solidity ^0.8.0;
              interface IERC5267 {
                  /**
                   * @dev MAY be emitted to signal that the domain could have changed.
                   */
                  event EIP712DomainChanged();
                  /**
                   * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
                   * signature.
                   */
                  function eip712Domain()
                      external
                      view
                      returns (
                          bytes1 fields,
                          string memory name,
                          string memory version,
                          uint256 chainId,
                          address verifyingContract,
                          bytes32 salt,
                          uint256[] memory extensions
                      );
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Standard math utilities missing in the Solidity language.
               */
              library Math {
                  enum Rounding {
                      Down, // Toward negative infinity
                      Up, // Toward infinity
                      Zero // Toward zero
                  }
                  /**
                   * @dev Returns the largest of two numbers.
                   */
                  function max(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a > b ? a : b;
                  }
                  /**
                   * @dev Returns the smallest of two numbers.
                   */
                  function min(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a < b ? a : b;
                  }
                  /**
                   * @dev Returns the average of two numbers. The result is rounded towards
                   * zero.
                   */
                  function average(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b) / 2 can overflow.
                      return (a & b) + (a ^ b) / 2;
                  }
                  /**
                   * @dev Returns the ceiling of the division of two numbers.
                   *
                   * This differs from standard division with `/` in that it rounds up instead
                   * of rounding down.
                   */
                  function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b - 1) / b can overflow on addition, so we distribute.
                      return a == 0 ? 0 : (a - 1) / b + 1;
                  }
                  /**
                   * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                   * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
                   * with further edits by Uniswap Labs also under MIT license.
                   */
                  function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
                      unchecked {
                          // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                          // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                          // variables such that product = prod1 * 2^256 + prod0.
                          uint256 prod0; // Least significant 256 bits of the product
                          uint256 prod1; // Most significant 256 bits of the product
                          assembly {
                              let mm := mulmod(x, y, not(0))
                              prod0 := mul(x, y)
                              prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                          }
                          // Handle non-overflow cases, 256 by 256 division.
                          if (prod1 == 0) {
                              // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                              // The surrounding unchecked block does not change this fact.
                              // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                              return prod0 / denominator;
                          }
                          // Make sure the result is less than 2^256. Also prevents denominator == 0.
                          require(denominator > prod1, "Math: mulDiv overflow");
                          ///////////////////////////////////////////////
                          // 512 by 256 division.
                          ///////////////////////////////////////////////
                          // Make division exact by subtracting the remainder from [prod1 prod0].
                          uint256 remainder;
                          assembly {
                              // Compute remainder using mulmod.
                              remainder := mulmod(x, y, denominator)
                              // Subtract 256 bit number from 512 bit number.
                              prod1 := sub(prod1, gt(remainder, prod0))
                              prod0 := sub(prod0, remainder)
                          }
                          // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                          // See https://cs.stackexchange.com/q/138556/92363.
                          // Does not overflow because the denominator cannot be zero at this stage in the function.
                          uint256 twos = denominator & (~denominator + 1);
                          assembly {
                              // Divide denominator by twos.
                              denominator := div(denominator, twos)
                              // Divide [prod1 prod0] by twos.
                              prod0 := div(prod0, twos)
                              // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                              twos := add(div(sub(0, twos), twos), 1)
                          }
                          // Shift in bits from prod1 into prod0.
                          prod0 |= prod1 * twos;
                          // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                          // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                          // four bits. That is, denominator * inv = 1 mod 2^4.
                          uint256 inverse = (3 * denominator) ^ 2;
                          // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                          // in modular arithmetic, doubling the correct bits in each step.
                          inverse *= 2 - denominator * inverse; // inverse mod 2^8
                          inverse *= 2 - denominator * inverse; // inverse mod 2^16
                          inverse *= 2 - denominator * inverse; // inverse mod 2^32
                          inverse *= 2 - denominator * inverse; // inverse mod 2^64
                          inverse *= 2 - denominator * inverse; // inverse mod 2^128
                          inverse *= 2 - denominator * inverse; // inverse mod 2^256
                          // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                          // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                          // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                          // is no longer required.
                          result = prod0 * inverse;
                          return result;
                      }
                  }
                  /**
                   * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                   */
                  function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
                      uint256 result = mulDiv(x, y, denominator);
                      if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                          result += 1;
                      }
                      return result;
                  }
                  /**
                   * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
                   *
                   * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                   */
                  function sqrt(uint256 a) internal pure returns (uint256) {
                      if (a == 0) {
                          return 0;
                      }
                      // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                      //
                      // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                      // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                      //
                      // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                      // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                      // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                      //
                      // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                      uint256 result = 1 << (log2(a) >> 1);
                      // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                      // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                      // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                      // into the expected uint128 result.
                      unchecked {
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          return min(result, a / result);
                      }
                  }
                  /**
                   * @notice Calculates sqrt(a), following the selected rounding direction.
                   */
                  function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = sqrt(a);
                          return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 2, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >> 128 > 0) {
                              value >>= 128;
                              result += 128;
                          }
                          if (value >> 64 > 0) {
                              value >>= 64;
                              result += 64;
                          }
                          if (value >> 32 > 0) {
                              value >>= 32;
                              result += 32;
                          }
                          if (value >> 16 > 0) {
                              value >>= 16;
                              result += 16;
                          }
                          if (value >> 8 > 0) {
                              value >>= 8;
                              result += 8;
                          }
                          if (value >> 4 > 0) {
                              value >>= 4;
                              result += 4;
                          }
                          if (value >> 2 > 0) {
                              value >>= 2;
                              result += 2;
                          }
                          if (value >> 1 > 0) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log2(value);
                          return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 10, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >= 10 ** 64) {
                              value /= 10 ** 64;
                              result += 64;
                          }
                          if (value >= 10 ** 32) {
                              value /= 10 ** 32;
                              result += 32;
                          }
                          if (value >= 10 ** 16) {
                              value /= 10 ** 16;
                              result += 16;
                          }
                          if (value >= 10 ** 8) {
                              value /= 10 ** 8;
                              result += 8;
                          }
                          if (value >= 10 ** 4) {
                              value /= 10 ** 4;
                              result += 4;
                          }
                          if (value >= 10 ** 2) {
                              value /= 10 ** 2;
                              result += 2;
                          }
                          if (value >= 10 ** 1) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log10(value);
                          return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 256, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   *
                   * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                   */
                  function log256(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >> 128 > 0) {
                              value >>= 128;
                              result += 16;
                          }
                          if (value >> 64 > 0) {
                              value >>= 64;
                              result += 8;
                          }
                          if (value >> 32 > 0) {
                              value >>= 32;
                              result += 4;
                          }
                          if (value >> 16 > 0) {
                              value >>= 16;
                              result += 2;
                          }
                          if (value >> 8 > 0) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log256(value);
                          return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Standard signed math utilities missing in the Solidity language.
               */
              library SignedMath {
                  /**
                   * @dev Returns the largest of two signed numbers.
                   */
                  function max(int256 a, int256 b) internal pure returns (int256) {
                      return a > b ? a : b;
                  }
                  /**
                   * @dev Returns the smallest of two signed numbers.
                   */
                  function min(int256 a, int256 b) internal pure returns (int256) {
                      return a < b ? a : b;
                  }
                  /**
                   * @dev Returns the average of two signed numbers without overflow.
                   * The result is rounded towards zero.
                   */
                  function average(int256 a, int256 b) internal pure returns (int256) {
                      // Formula from the book "Hacker's Delight"
                      int256 x = (a & b) + ((a ^ b) >> 1);
                      return x + (int256(uint256(x) >> 255) & (a ^ b));
                  }
                  /**
                   * @dev Returns the absolute unsigned value of a signed value.
                   */
                  function abs(int256 n) internal pure returns (uint256) {
                      unchecked {
                          // must be unchecked in order to support `n = type(int256).min`
                          return uint256(n >= 0 ? n : -n);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
              // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
              pragma solidity ^0.8.0;
              /**
               * @dev Library for reading and writing primitive types to specific storage slots.
               *
               * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
               * This library helps with reading and writing to such slots without the need for inline assembly.
               *
               * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
               *
               * Example usage to set ERC1967 implementation slot:
               * ```solidity
               * contract ERC1967 {
               *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
               *
               *     function _getImplementation() internal view returns (address) {
               *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
               *     }
               *
               *     function _setImplementation(address newImplementation) internal {
               *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               *
               * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
               * _Available since v4.9 for `string`, `bytes`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  struct StringSlot {
                      string value;
                  }
                  struct BytesSlot {
                      bytes value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` with member `value` located at `slot`.
                   */
                  function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
                   */
                  function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` with member `value` located at `slot`.
                   */
                  function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
                   */
                  function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
              }
              

              File 2 of 8: CurveStableSwapNG
              # pragma version 0.3.10
              # pragma optimize codesize
              # pragma evm-version shanghai
              """
              @title CurveStableSwapNG
              @author Curve.Fi
              @license Copyright (c) Curve.Fi, 2020-2023 - all rights reserved
              @notice Stableswap implementation for up to 8 coins with no rehypothecation,
                      i.e. the AMM does not deposit tokens into other contracts. The Pool contract also
                      records exponential moving averages for coins relative to coin 0.
              @dev Asset Types:
                      0. Standard ERC20 token with no additional features.
                                        Note: Users are advised to do careful due-diligence on
                                              ERC20 tokens that they interact with, as this
                                              contract cannot differentiate between harmless and
                                              malicious ERC20 tokens.
                      1. Oracle - token with rate oracle (e.g. wstETH)
                                  Note: Oracles may be controlled externally by an EOA. Users
                                        are advised to proceed with caution.
                      2. Rebasing - token with rebase (e.g. stETH).
                                    Note: Users and Integrators are advised to understand how
                                          the AMM contract works with rebasing balances.
                      3. ERC4626 - token with convertToAssets method (e.g. sDAI).
                                   Note: Some ERC4626 implementations may be susceptible to
                                         Donation/Inflation attacks. Users are advised to
                                         proceed with caution.
                      NOTE: Pool Cannot support tokens with multiple asset types: e.g. ERC4626
                            with fees are not supported.
                   Supports:
                      1. ERC20 support for return True/revert, return True/False, return None
                      2. ERC20 tokens can have arbitrary decimals (<=18).
                      3. ERC20 tokens that rebase (either positive or fee on transfer)
                      4. ERC20 tokens that have a rate oracle (e.g. wstETH, cbETH, sDAI, etc.)
                         Note: Oracle precision _must_ be 10**18.
                      5. ERC4626 tokens with arbitrary precision (<=18) of Vault token and underlying
                         asset.
                   Additional features include:
                      1. Adds price oracles based on AMM State Price (and _not_ last traded price).
                      2. Adds TVL oracle based on D.
                      3. `exchange_received`: swaps that expect an ERC20 transfer to have occurred
                         prior to executing the swap.
                         Note: a. If pool contains rebasing tokens and one of the `asset_types` is 2 (Rebasing)
                                  then calling `exchange_received` will REVERT.
                               b. If pool contains rebasing token and `asset_types` does not contain 2 (Rebasing)
                                  then this is an incorrect implementation and rebases can be
                                  stolen.
                      4. Adds `get_dx`: Similar to `get_dy` which returns an expected output
                         of coin[j] for given `dx` amount of coin[i], `get_dx` returns expected
                         input of coin[i] for an output amount of coin[j].
                      5. Fees are dynamic: AMM will charge a higher fee if pool depegs. This can cause very
                                           slight discrepancies between calculated fees and realised fees.
              """
              
              from vyper.interfaces import ERC20
              from vyper.interfaces import ERC20Detailed
              from vyper.interfaces import ERC4626
              
              implements: ERC20
              
              # ------------------------------- Interfaces ---------------------------------
              
              interface Factory:
                  def fee_receiver() -> address: view
                  def admin() -> address: view
                  def views_implementation() -> address: view
              
              interface ERC1271:
                  def isValidSignature(_hash: bytes32, _signature: Bytes[65]) -> bytes32: view
              
              interface StableSwapViews:
                  def get_dx(i: int128, j: int128, dy: uint256, pool: address) -> uint256: view
                  def get_dy(i: int128, j: int128, dx: uint256, pool: address) -> uint256: view
                  def dynamic_fee(i: int128, j: int128, pool: address) -> uint256: view
                  def calc_token_amount(
                      _amounts: DynArray[uint256, MAX_COINS],
                      _is_deposit: bool,
                      _pool: address
                  ) -> uint256: view
              
              # --------------------------------- Events -----------------------------------
              
              event Transfer:
                  sender: indexed(address)
                  receiver: indexed(address)
                  value: uint256
              
              event Approval:
                  owner: indexed(address)
                  spender: indexed(address)
                  value: uint256
              
              event TokenExchange:
                  buyer: indexed(address)
                  sold_id: int128
                  tokens_sold: uint256
                  bought_id: int128
                  tokens_bought: uint256
              
              event TokenExchangeUnderlying:
                  buyer: indexed(address)
                  sold_id: int128
                  tokens_sold: uint256
                  bought_id: int128
                  tokens_bought: uint256
              
              event AddLiquidity:
                  provider: indexed(address)
                  token_amounts: DynArray[uint256, MAX_COINS]
                  fees: DynArray[uint256, MAX_COINS]
                  invariant: uint256
                  token_supply: uint256
              
              event RemoveLiquidity:
                  provider: indexed(address)
                  token_amounts: DynArray[uint256, MAX_COINS]
                  fees: DynArray[uint256, MAX_COINS]
                  token_supply: uint256
              
              event RemoveLiquidityOne:
                  provider: indexed(address)
                  token_id: int128
                  token_amount: uint256
                  coin_amount: uint256
                  token_supply: uint256
              
              event RemoveLiquidityImbalance:
                  provider: indexed(address)
                  token_amounts: DynArray[uint256, MAX_COINS]
                  fees: DynArray[uint256, MAX_COINS]
                  invariant: uint256
                  token_supply: uint256
              
              event RampA:
                  old_A: uint256
                  new_A: uint256
                  initial_time: uint256
                  future_time: uint256
              
              event StopRampA:
                  A: uint256
                  t: uint256
              
              event ApplyNewFee:
                  fee: uint256
                  offpeg_fee_multiplier: uint256
              
              event SetNewMATime:
                  ma_exp_time: uint256
                  D_ma_time: uint256
              
              
              MAX_COINS: constant(uint256) = 8  # max coins is 8 in the factory
              MAX_COINS_128: constant(int128) = 8
              
              # ---------------------------- Pool Variables --------------------------------
              
              N_COINS: public(immutable(uint256))
              N_COINS_128: immutable(int128)
              PRECISION: constant(uint256) = 10 ** 18
              
              factory: immutable(Factory)
              coins: public(immutable(DynArray[address, MAX_COINS]))
              asset_types: immutable(DynArray[uint8, MAX_COINS])
              pool_contains_rebasing_tokens: immutable(bool)
              stored_balances: DynArray[uint256, MAX_COINS]
              
              # Fee specific vars
              FEE_DENOMINATOR: constant(uint256) = 10 ** 10
              fee: public(uint256)  # fee * 1e10
              offpeg_fee_multiplier: public(uint256)  # * 1e10
              admin_fee: public(constant(uint256)) = 5000000000
              MAX_FEE: constant(uint256) = 5 * 10 ** 9
              
              # ---------------------- Pool Amplification Parameters -----------------------
              
              A_PRECISION: constant(uint256) = 100
              MAX_A: constant(uint256) = 10 ** 6
              MAX_A_CHANGE: constant(uint256) = 10
              
              initial_A: public(uint256)
              future_A: public(uint256)
              initial_A_time: public(uint256)
              future_A_time: public(uint256)
              
              # ---------------------------- Admin Variables -------------------------------
              
              MIN_RAMP_TIME: constant(uint256) = 86400
              admin_balances: public(DynArray[uint256, MAX_COINS])
              
              # ----------------------- Oracle Specific vars -------------------------------
              
              rate_multipliers: immutable(DynArray[uint256, MAX_COINS])
              # [bytes4 method_id][bytes8 <empty>][bytes20 oracle]
              rate_oracles: immutable(DynArray[uint256, MAX_COINS])
              
              # For ERC4626 tokens, we need:
              call_amount: immutable(DynArray[uint256, MAX_COINS])
              scale_factor: immutable(DynArray[uint256, MAX_COINS])
              
              last_prices_packed: DynArray[uint256, MAX_COINS]  #  packing: last_price, ma_price
              last_D_packed: uint256                            #  packing: last_D, ma_D
              ma_exp_time: public(uint256)
              D_ma_time: public(uint256)
              ma_last_time: public(uint256)                     # packing: ma_last_time_p, ma_last_time_D
              # ma_last_time has a distinction for p and D because p is _not_ updated if
              # users remove_liquidity, but D is.
              
              # shift(2**32 - 1, 224)
              ORACLE_BIT_MASK: constant(uint256) = (2**32 - 1) * 256**28
              
              # --------------------------- ERC20 Specific Vars ----------------------------
              
              name: public(immutable(String[64]))
              symbol: public(immutable(String[32]))
              decimals: public(constant(uint8)) = 18
              version: public(constant(String[8])) = "v7.0.0"
              
              balanceOf: public(HashMap[address, uint256])
              allowance: public(HashMap[address, HashMap[address, uint256]])
              total_supply: uint256
              nonces: public(HashMap[address, uint256])
              
              # keccak256("isValidSignature(bytes32,bytes)")[:4] << 224
              ERC1271_MAGIC_VAL: constant(bytes32) = 0x1626ba7e00000000000000000000000000000000000000000000000000000000
              EIP712_TYPEHASH: constant(bytes32) = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)")
              EIP2612_TYPEHASH: constant(bytes32) = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
              
              VERSION_HASH: constant(bytes32) = keccak256(version)
              NAME_HASH: immutable(bytes32)
              CACHED_CHAIN_ID: immutable(uint256)
              salt: public(immutable(bytes32))
              CACHED_DOMAIN_SEPARATOR: immutable(bytes32)
              
              
              # ------------------------------ AMM Setup -----------------------------------
              
              
              @external
              def __init__(
                  _name: String[32],
                  _symbol: String[10],
                  _A: uint256,
                  _fee: uint256,
                  _offpeg_fee_multiplier: uint256,
                  _ma_exp_time: uint256,
                  _coins: DynArray[address, MAX_COINS],
                  _rate_multipliers: DynArray[uint256, MAX_COINS],
                  _asset_types: DynArray[uint8, MAX_COINS],
                  _method_ids: DynArray[bytes4, MAX_COINS],
                  _oracles: DynArray[address, MAX_COINS],
              ):
                  """
                  @notice Initialize the pool contract
                  @param _name Name of the new plain pool.
                  @param _symbol Symbol for the new plain pool.
                  @param _A Amplification co-efficient - a lower value here means
                            less tolerance for imbalance within the pool's assets.
                            Suggested values include:
                             * Uncollateralized algorithmic stablecoins: 5-10
                             * Non-redeemable, collateralized assets: 100
                             * Redeemable assets: 200-400
                  @param _fee Trade fee, given as an integer with 1e10 precision. The
                              the maximum is 1% (100000000).
                              50% of the fee is distributed to veCRV holders.
                  @param _offpeg_fee_multiplier A multiplier that determines how much to increase
                                                Fees by when assets in the AMM depeg. Example value: 20000000000
                  @param _ma_exp_time Averaging window of oracle. Set as time_in_seconds / ln(2)
                                      Example: for 10 minute EMA, _ma_exp_time is 600 / ln(2) ~= 866
                  @param _coins List of addresses of the coins being used in the pool.
                  @param _rate_multipliers An array of: [10 ** (36 - _coins[n].decimals()), ... for n in range(N_COINS)]
                  @param _asset_types Array of uint8 representing tokens in pool
                  @param _method_ids Array of first four bytes of the Keccak-256 hash of the function signatures
                                     of the oracle addresses that gives rate oracles.
                                     Calculated as: keccak(text=event_signature.replace(" ", ""))[:4]
                  @param _oracles Array of rate oracle addresses.
                  """
              
                  coins = _coins
                  asset_types = _asset_types
                  pool_contains_rebasing_tokens = 2 in asset_types
                  __n_coins: uint256 = len(_coins)
                  N_COINS = __n_coins
                  N_COINS_128 = convert(__n_coins, int128)
              
                  rate_multipliers = _rate_multipliers
              
                  factory = Factory(msg.sender)
              
                  A: uint256 = unsafe_mul(_A, A_PRECISION)
                  self.initial_A = A
                  self.future_A = A
                  self.fee = _fee
                  self.offpeg_fee_multiplier = _offpeg_fee_multiplier
              
                  assert _ma_exp_time != 0
                  self.ma_exp_time = _ma_exp_time
                  self.D_ma_time = 62324  # <--------- 12 hours default on contract start.
                  self.ma_last_time = self.pack_2(block.timestamp, block.timestamp)
              
                  #  ------------------- initialize storage for DynArrays ------------------
              
                  _call_amount: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  _scale_factor: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  _rate_oracles: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
              
                      if i < N_COINS_128 - 1:
                          self.last_prices_packed.append(self.pack_2(10**18, 10**18))
              
                      _rate_oracles.append(convert(_method_ids[i], uint256) * 2**224 | convert(_oracles[i], uint256))
                      self.stored_balances.append(0)
                      self.admin_balances.append(0)
              
                      if _asset_types[i] == 3:
              
                          _call_amount.append(10**convert(ERC20Detailed(_coins[i]).decimals(), uint256))
                          _underlying_asset: address = ERC4626(_coins[i]).asset()
                          _scale_factor.append(10**(18 - convert(ERC20Detailed(_underlying_asset).decimals(), uint256)))
              
                      else:
              
                          _call_amount.append(0)
                          _scale_factor.append(0)
              
                  call_amount = _call_amount
                  scale_factor = _scale_factor
                  rate_oracles = _rate_oracles
              
                  # ----------------------------- ERC20 stuff ------------------------------
              
                  name = _name
                  symbol = _symbol
              
                  # EIP712 related params -----------------
                  NAME_HASH = keccak256(name)
                  salt = block.prevhash
                  CACHED_CHAIN_ID = chain.id
                  CACHED_DOMAIN_SEPARATOR = keccak256(
                      _abi_encode(
                          EIP712_TYPEHASH,
                          NAME_HASH,
                          VERSION_HASH,
                          chain.id,
                          self,
                          salt,
                      )
                  )
              
                  # ------------------------ Fire a transfer event -------------------------
              
                  log Transfer(empty(address), msg.sender, 0)
              
              
              # ------------------ Token transfers in and out of the AMM -------------------
              
              
              @internal
              def _transfer_in(
                  coin_idx: int128,
                  dx: uint256,
                  sender: address,
                  expect_optimistic_transfer: bool,
              ) -> uint256:
                  """
                  @notice Contains all logic to handle ERC20 token transfers.
                  @param coin_idx Index of the coin to transfer in.
                  @param dx amount of `_coin` to transfer into the pool.
                  @param sender address to transfer `_coin` from.
                  @param receiver address to transfer `_coin` to.
                  @param expect_optimistic_transfer True if contract expects an optimistic coin transfer
                  """
                  _dx: uint256 = ERC20(coins[coin_idx]).balanceOf(self)
              
                  # ------------------------- Handle Transfers -----------------------------
              
                  if expect_optimistic_transfer:
              
                      _dx = _dx - self.stored_balances[coin_idx]
                      assert _dx >= dx
              
                  else:
              
                      assert dx > 0  # dev : do not transferFrom 0 tokens into the pool
                      assert ERC20(coins[coin_idx]).transferFrom(
                          sender, self, dx, default_return_value=True
                      )
              
                      _dx = ERC20(coins[coin_idx]).balanceOf(self) - _dx
              
                  # --------------------------- Store transferred in amount ---------------------------
              
                  self.stored_balances[coin_idx] += _dx
              
                  return _dx
              
              
              @internal
              def _transfer_out(_coin_idx: int128, _amount: uint256, receiver: address):
                  """
                  @notice Transfer a single token from the pool to receiver.
                  @dev This function is called by `remove_liquidity` and
                       `remove_liquidity_one_coin`, `_exchange`, `_withdraw_admin_fees` and
                       `remove_liquidity_imbalance` methods.
                  @param _coin_idx Index of the token to transfer out
                  @param _amount Amount of token to transfer out
                  @param receiver Address to send the tokens to
                  """
                  assert receiver != empty(address)  # dev: do not send tokens to zero_address
              
                  if not pool_contains_rebasing_tokens:
              
                      # we need not cache balanceOf pool before swap out
                      self.stored_balances[_coin_idx] -= _amount
                      assert ERC20(coins[_coin_idx]).transfer(
                          receiver, _amount, default_return_value=True
                      )
              
                  else:
              
                      # cache balances pre and post to account for fee on transfers etc.
                      coin_balance: uint256 = ERC20(coins[_coin_idx]).balanceOf(self)
                      assert ERC20(coins[_coin_idx]).transfer(
                          receiver, _amount, default_return_value=True
                      )
                      self.stored_balances[_coin_idx] = coin_balance - _amount
              
              
              # -------------------------- AMM Special Methods -----------------------------
              
              
              @view
              @internal
              def _stored_rates() -> DynArray[uint256, MAX_COINS]:
                  """
                  @notice Gets rate multipliers for each coin.
                  @dev If the coin has a rate oracle that has been properly initialised,
                       this method queries that rate by static-calling an external
                       contract.
                  """
                  rates: DynArray[uint256, MAX_COINS] = rate_multipliers
              
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
              
                      if asset_types[i] == 1 and not rate_oracles[i] == 0:
              
                          # NOTE: fetched_rate is assumed to be 10**18 precision
                          oracle_response: Bytes[32] = raw_call(
                              convert(rate_oracles[i] % 2**160, address),
                              _abi_encode(rate_oracles[i] & ORACLE_BIT_MASK),
                              max_outsize=32,
                              is_static_call=True,
                          )
                          assert len(oracle_response) == 32
                          fetched_rate: uint256 = convert(oracle_response, uint256)
              
                          rates[i] = unsafe_div(rates[i] * fetched_rate, PRECISION)
              
                      elif asset_types[i] == 3:  # ERC4626
              
                          # fetched_rate: uint256 = ERC4626(coins[i]).convertToAssets(call_amount[i]) * scale_factor[i]
                          # here: call_amount has ERC4626 precision, but the returned value is scaled up to 18
                          # using scale_factor which is (18 - n) if underlying asset has n decimals.
                          rates[i] = unsafe_div(
                              rates[i] * ERC4626(coins[i]).convertToAssets(call_amount[i]) * scale_factor[i],
                              PRECISION
                          )  # 1e18 precision
              
                  return rates
              
              
              @view
              @internal
              def _balances() -> DynArray[uint256, MAX_COINS]:
                  """
                  @notice Calculates the pool's balances _excluding_ the admin's balances.
                  @dev If the pool contains rebasing tokens, this method ensures LPs keep all
                          rebases and admin only claims swap fees. This also means that, since
                          admin's balances are stored in an array and not inferred from read balances,
                          the fees in the rebasing token that the admin collects is immune to
                          slashing events.
                  """
                  result: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  balances_i: uint256 = 0
              
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
              
                      if pool_contains_rebasing_tokens:
                          # Read balances by gulping to account for rebases
                          balances_i = ERC20(coins[i]).balanceOf(self) - self.admin_balances[i]
                      else:
                          # Use cached balances
                          balances_i = self.stored_balances[i] - self.admin_balances[i]
              
                      result.append(balances_i)
              
                  return result
              
              
              # -------------------------- AMM Main Functions ------------------------------
              
              
              @external
              @nonreentrant('lock')
              def exchange(
                  i: int128,
                  j: int128,
                  _dx: uint256,
                  _min_dy: uint256,
                  _receiver: address = msg.sender,
              ) -> uint256:
                  """
                  @notice Perform an exchange between two coins
                  @dev Index values can be found via the `coins` public getter method
                  @param i Index value for the coin to send
                  @param j Index value of the coin to receive
                  @param _dx Amount of `i` being exchanged
                  @param _min_dy Minimum amount of `j` to receive
                  @param _receiver Address that receives `j`
                  @return Actual amount of `j` received
                  """
                  return self._exchange(
                      msg.sender,
                      i,
                      j,
                      _dx,
                      _min_dy,
                      _receiver,
                      False
                  )
              
              
              @external
              @nonreentrant('lock')
              def exchange_received(
                  i: int128,
                  j: int128,
                  _dx: uint256,
                  _min_dy: uint256,
                  _receiver: address = msg.sender,
              ) -> uint256:
                  """
                  @notice Perform an exchange between two coins without transferring token in
                  @dev The contract swaps tokens based on a change in balance of coin[i]. The
                       dx = ERC20(coin[i]).balanceOf(self) - self.stored_balances[i]. Users of
                       this method are dex aggregators, arbitrageurs, or other users who do not
                       wish to grant approvals to the contract: they would instead send tokens
                       directly to the contract and call `exchange_received`.
                       Note: This is disabled if pool contains rebasing tokens.
                  @param i Index value for the coin to send
                  @param j Index value of the coin to receive
                  @param _dx Amount of `i` being exchanged
                  @param _min_dy Minimum amount of `j` to receive
                  @param _receiver Address that receives `j`
                  @return Actual amount of `j` received
                  """
                  assert not pool_contains_rebasing_tokens  # dev: exchange_received not supported if pool contains rebasing tokens
                  return self._exchange(
                      msg.sender,
                      i,
                      j,
                      _dx,
                      _min_dy,
                      _receiver,
                      True,  # <--------------------------------------- swap optimistically.
                  )
              
              
              @external
              @nonreentrant('lock')
              def add_liquidity(
                  _amounts: DynArray[uint256, MAX_COINS],
                  _min_mint_amount: uint256,
                  _receiver: address = msg.sender
              ) -> uint256:
                  """
                  @notice Deposit coins into the pool
                  @param _amounts List of amounts of coins to deposit
                  @param _min_mint_amount Minimum amount of LP tokens to mint from the deposit
                  @param _receiver Address that owns the minted LP tokens
                  @return Amount of LP tokens received by depositing
                  """
                  assert _receiver != empty(address)  # dev: do not send LP tokens to zero_address
              
                  amp: uint256 = self._A()
                  old_balances: DynArray[uint256, MAX_COINS] = self._balances()
                  rates: DynArray[uint256, MAX_COINS] = self._stored_rates()
              
                  # Initial invariant
                  D0: uint256 = self.get_D_mem(rates, old_balances, amp)
              
                  total_supply: uint256 = self.total_supply
                  new_balances: DynArray[uint256, MAX_COINS] = old_balances
              
                  # -------------------------- Do Transfers In -----------------------------
              
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
              
                      if _amounts[i] > 0:
              
                          new_balances[i] += self._transfer_in(
                              i,
                              _amounts[i],
                              msg.sender,
                              False,  # expect_optimistic_transfer
                          )
              
                      else:
              
                          assert total_supply != 0  # dev: initial deposit requires all coins
              
                  # ------------------------------------------------------------------------
              
                  # Invariant after change
                  D1: uint256 = self.get_D_mem(rates, new_balances, amp)
                  assert D1 > D0
              
                  # We need to recalculate the invariant accounting for fees
                  # to calculate fair user's share
                  fees: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  mint_amount: uint256 = 0
              
                  if total_supply > 0:
              
                      ideal_balance: uint256 = 0
                      difference: uint256 = 0
                      new_balance: uint256 = 0
              
                      ys: uint256 = unsafe_div(D0 + D1, N_COINS)
                      xs: uint256 = 0
                      _dynamic_fee_i: uint256 = 0
              
                      # Only account for fees if we are not the first to deposit
                      base_fee: uint256 = unsafe_div(
                          unsafe_mul(self.fee, N_COINS),
                          unsafe_mul(4, unsafe_sub(N_COINS, 1))
                      )
              
                      for i in range(N_COINS_128, bound=MAX_COINS_128):
              
                          ideal_balance = D1 * old_balances[i] / D0
                          difference = 0
                          new_balance = new_balances[i]
              
                          if ideal_balance > new_balance:
                              difference = unsafe_sub(ideal_balance, new_balance)
                          else:
                              difference = unsafe_sub(new_balance, ideal_balance)
              
                          # fee[i] = _dynamic_fee(i, j) * difference / FEE_DENOMINATOR
                          xs = unsafe_div(rates[i] * (old_balances[i] + new_balance), PRECISION)
                          _dynamic_fee_i = self._dynamic_fee(xs, ys, base_fee)
                          fees.append(unsafe_div(_dynamic_fee_i * difference, FEE_DENOMINATOR))
                          self.admin_balances[i] += unsafe_div(fees[i] * admin_fee, FEE_DENOMINATOR)
                          new_balances[i] -= fees[i]
              
                      xp: DynArray[uint256, MAX_COINS] = self._xp_mem(rates, new_balances)
                      D1 = self.get_D(xp, amp)  # <--------------- Reuse D1 for new D value.
                      mint_amount = unsafe_div(total_supply * (D1 - D0), D0)
                      self.upkeep_oracles(xp, amp, D1)
              
                  else:
              
                      mint_amount = D1  # Take the dust if there was any
              
                      # (re)instantiate D oracle if totalSupply is zero.
                      self.last_D_packed = self.pack_2(D1, D1)
              
                      # Update D ma time:
                      ma_last_time_unpacked: uint256[2] = self.unpack_2(self.ma_last_time)
                      if ma_last_time_unpacked[1] < block.timestamp:
                          ma_last_time_unpacked[1] = block.timestamp
                          self.ma_last_time = self.pack_2(ma_last_time_unpacked[0], ma_last_time_unpacked[1])
              
                  assert mint_amount >= _min_mint_amount, "Slippage screwed you"
              
                  # Mint pool tokens
                  total_supply += mint_amount
                  self.balanceOf[_receiver] += mint_amount
                  self.total_supply = total_supply
                  log Transfer(empty(address), _receiver, mint_amount)
              
                  log AddLiquidity(msg.sender, _amounts, fees, D1, total_supply)
              
                  return mint_amount
              
              
              @external
              @nonreentrant('lock')
              def remove_liquidity_one_coin(
                  _burn_amount: uint256,
                  i: int128,
                  _min_received: uint256,
                  _receiver: address = msg.sender,
              ) -> uint256:
                  """
                  @notice Withdraw a single coin from the pool
                  @param _burn_amount Amount of LP tokens to burn in the withdrawal
                  @param i Index value of the coin to withdraw
                  @param _min_received Minimum amount of coin to receive
                  @param _receiver Address that receives the withdrawn coins
                  @return Amount of coin received
                  """
                  assert _burn_amount > 0  # dev: do not remove 0 LP tokens
                  dy: uint256 = 0
                  fee: uint256 = 0
                  xp: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  amp: uint256 = empty(uint256)
                  D: uint256 = empty(uint256)
              
                  dy, fee, xp, amp, D = self._calc_withdraw_one_coin(_burn_amount, i)
                  assert dy >= _min_received, "Not enough coins removed"
              
                  self.admin_balances[i] += unsafe_div(fee * admin_fee, FEE_DENOMINATOR)
              
                  self._burnFrom(msg.sender, _burn_amount)
              
                  self._transfer_out(i, dy, _receiver)
              
                  log RemoveLiquidityOne(msg.sender, i, _burn_amount, dy, self.total_supply)
              
                  self.upkeep_oracles(xp, amp, D)
              
                  return dy
              
              
              @external
              @nonreentrant('lock')
              def remove_liquidity_imbalance(
                  _amounts: DynArray[uint256, MAX_COINS],
                  _max_burn_amount: uint256,
                  _receiver: address = msg.sender
              ) -> uint256:
                  """
                  @notice Withdraw coins from the pool in an imbalanced amount
                  @param _amounts List of amounts of underlying coins to withdraw
                  @param _max_burn_amount Maximum amount of LP token to burn in the withdrawal
                  @param _receiver Address that receives the withdrawn coins
                  @return Actual amount of the LP token burned in the withdrawal
                  """
                  amp: uint256 = self._A()
                  rates: DynArray[uint256, MAX_COINS] = self._stored_rates()
                  old_balances: DynArray[uint256, MAX_COINS] = self._balances()
                  D0: uint256 = self.get_D_mem(rates, old_balances, amp)
                  new_balances: DynArray[uint256, MAX_COINS] = old_balances
              
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
              
                      if _amounts[i] != 0:
                          new_balances[i] -= _amounts[i]
                          self._transfer_out(i, _amounts[i], _receiver)
              
                  D1: uint256 = self.get_D_mem(rates, new_balances, amp)
                  base_fee: uint256 = unsafe_div(
                      unsafe_mul(self.fee, N_COINS),
                      unsafe_mul(4, unsafe_sub(N_COINS, 1))
                  )
                  ys: uint256 = unsafe_div((D0 + D1), N_COINS)
              
                  fees: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  dynamic_fee: uint256 = 0
                  xs: uint256 = 0
                  ideal_balance: uint256 = 0
                  difference: uint256 = 0
                  new_balance: uint256 = 0
              
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
              
                      ideal_balance = D1 * old_balances[i] / D0
                      difference = 0
                      new_balance = new_balances[i]
              
                      if ideal_balance > new_balance:
                          difference = unsafe_sub(ideal_balance, new_balance)
                      else:
                          difference = unsafe_sub(new_balance, ideal_balance)
              
                      xs = unsafe_div(rates[i] * (old_balances[i] + new_balance), PRECISION)
                      dynamic_fee = self._dynamic_fee(xs, ys, base_fee)
                      fees.append(unsafe_div(dynamic_fee * difference, FEE_DENOMINATOR))
              
                      self.admin_balances[i] += unsafe_div(fees[i] * admin_fee, FEE_DENOMINATOR)
                      new_balances[i] -= fees[i]
              
                  D1 = self.get_D_mem(rates, new_balances, amp)  # dev: reuse D1 for new D.
                  self.upkeep_oracles(self._xp_mem(rates, new_balances), amp, D1)
              
                  total_supply: uint256 = self.total_supply
                  burn_amount: uint256 = unsafe_div((D0 - D1) * total_supply, D0) + 1
                  assert burn_amount > 1  # dev: zero tokens burned
                  assert burn_amount <= _max_burn_amount, "Slippage screwed you"
              
                  self._burnFrom(msg.sender, burn_amount)
              
                  log RemoveLiquidityImbalance(
                      msg.sender,
                      _amounts,
                      fees,
                      D1,
                      total_supply - burn_amount
                  )
              
                  return burn_amount
              
              
              @external
              @nonreentrant('lock')
              def remove_liquidity(
                  _burn_amount: uint256,
                  _min_amounts: DynArray[uint256, MAX_COINS],
                  _receiver: address = msg.sender,
                  _claim_admin_fees: bool = True,
              ) -> DynArray[uint256, MAX_COINS]:
                  """
                  @notice Withdraw coins from the pool
                  @dev Withdrawal amounts are based on current deposit ratios
                  @param _burn_amount Quantity of LP tokens to burn in the withdrawal
                  @param _min_amounts Minimum amounts of underlying coins to receive
                  @param _receiver Address that receives the withdrawn coins
                  @return List of amounts of coins that were withdrawn
                  """
                  total_supply: uint256 = self.total_supply
                  assert _burn_amount > 0  # dev: invalid burn amount
                  assert len(_min_amounts) == N_COINS  # dev: invalid array length for _min_amounts
              
                  amounts: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  balances: DynArray[uint256, MAX_COINS] = self._balances()
              
                  value: uint256 = 0
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
              
                      value = unsafe_div(balances[i] * _burn_amount, total_supply)
                      assert value >= _min_amounts[i], "Withdrawal resulted in fewer coins than expected"
                      amounts.append(value)
                      self._transfer_out(i, value, _receiver)
              
                  self._burnFrom(msg.sender, _burn_amount)  # <---- Updates self.total_supply
              
                  # --------------------------- Upkeep D_oracle ----------------------------
              
                  ma_last_time_unpacked: uint256[2] = self.unpack_2(self.ma_last_time)
                  last_D_packed_current: uint256 = self.last_D_packed
                  old_D: uint256 = last_D_packed_current & (2**128 - 1)
              
                  self.last_D_packed = self.pack_2(
                      old_D - unsafe_div(old_D * _burn_amount, total_supply),  # new_D = proportionally reduce D.
                      self._calc_moving_average(
                          last_D_packed_current,
                          self.D_ma_time,
                          ma_last_time_unpacked[1]
                      )
                  )
              
                  if ma_last_time_unpacked[1] < block.timestamp:
                      ma_last_time_unpacked[1] = block.timestamp
                      self.ma_last_time = self.pack_2(ma_last_time_unpacked[0], ma_last_time_unpacked[1])
              
                  # ------------------------------- Log event ------------------------------
              
                  log RemoveLiquidity(
                      msg.sender,
                      amounts,
                      empty(DynArray[uint256, MAX_COINS]),
                      unsafe_sub(total_supply, _burn_amount)
                  )
              
                  # ------- Withdraw admin fees if _claim_admin_fees is set to True --------
                  if _claim_admin_fees:
                      self._withdraw_admin_fees()
              
                  return amounts
              
              
              @external
              @nonreentrant('lock')
              def withdraw_admin_fees():
                  """
                  @notice Claim admin fees. Callable by anyone.
                  """
                  self._withdraw_admin_fees()
              
              
              # ------------------------ AMM Internal Functions ----------------------------
              
              
              @view
              @internal
              def _dynamic_fee(xpi: uint256, xpj: uint256, _fee: uint256) -> uint256:
              
                  _offpeg_fee_multiplier: uint256 = self.offpeg_fee_multiplier
                  if _offpeg_fee_multiplier <= FEE_DENOMINATOR:
                      return _fee
              
                  xps2: uint256 = (xpi + xpj) ** 2
                  return unsafe_div(
                      unsafe_mul(_offpeg_fee_multiplier, _fee),
                      unsafe_add(
                          unsafe_sub(_offpeg_fee_multiplier, FEE_DENOMINATOR) * 4 * xpi * xpj / xps2,
                          FEE_DENOMINATOR
                      )
                  )
              
              
              @internal
              def __exchange(
                  x: uint256,
                  _xp: DynArray[uint256, MAX_COINS],
                  rates: DynArray[uint256, MAX_COINS],
                  i: int128,
                  j: int128,
              ) -> uint256:
              
                  amp: uint256 = self._A()
                  D: uint256 = self.get_D(_xp, amp)
                  y: uint256 = self.get_y(i, j, x, _xp, amp, D)
              
                  dy: uint256 = _xp[j] - y - 1  # -1 just in case there were some rounding errors
                  dy_fee: uint256 = unsafe_div(
                      dy * self._dynamic_fee(
                          unsafe_div(_xp[i] + x, 2), unsafe_div(_xp[j] + y, 2), self.fee
                      ),
                      FEE_DENOMINATOR
                  )
              
                  # Convert all to real units
                  dy = (dy - dy_fee) * PRECISION / rates[j]
              
                  self.admin_balances[j] += unsafe_div(
                      unsafe_div(dy_fee * admin_fee, FEE_DENOMINATOR) * PRECISION,
                      rates[j]
                  )
              
                  # Calculate and store state prices:
                  xp: DynArray[uint256, MAX_COINS] = _xp
                  xp[i] = x
                  xp[j] = y
                  # D is not changed because we did not apply a fee
                  self.upkeep_oracles(xp, amp, D)
              
                  return dy
              
              
              @internal
              def _exchange(
                  sender: address,
                  i: int128,
                  j: int128,
                  _dx: uint256,
                  _min_dy: uint256,
                  receiver: address,
                  expect_optimistic_transfer: bool
              ) -> uint256:
              
                  assert i != j  # dev: coin index out of range
                  assert _dx > 0  # dev: do not exchange 0 coins
              
                  rates: DynArray[uint256, MAX_COINS] = self._stored_rates()
                  old_balances: DynArray[uint256, MAX_COINS] = self._balances()
                  xp: DynArray[uint256, MAX_COINS] = self._xp_mem(rates, old_balances)
              
                  # --------------------------- Do Transfer in -----------------------------
              
                  # `dx` is whatever the pool received after ERC20 transfer:
                  dx: uint256 = self._transfer_in(
                      i,
                      _dx,
                      sender,
                      expect_optimistic_transfer
                  )
              
                  # ------------------------------- Exchange -------------------------------
              
                  x: uint256 = xp[i] + unsafe_div(dx * rates[i], PRECISION)
                  dy: uint256 = self.__exchange(x, xp, rates, i, j)
                  assert dy >= _min_dy, "Exchange resulted in fewer coins than expected"
              
                  # --------------------------- Do Transfer out ----------------------------
              
                  self._transfer_out(j, dy, receiver)
              
                  # ------------------------------------------------------------------------
              
                  log TokenExchange(msg.sender, i, dx, j, dy)
              
                  return dy
              
              
              @internal
              def _withdraw_admin_fees():
                  fee_receiver: address = factory.fee_receiver()
                  if fee_receiver == empty(address):
                      return  # Do nothing.
              
                  admin_balances: DynArray[uint256, MAX_COINS] = self.admin_balances
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
              
                      if admin_balances[i] > 0:
              
                          self._transfer_out(i, admin_balances[i], fee_receiver)
                          admin_balances[i] = 0
              
                  self.admin_balances = admin_balances
              
              
              # --------------------------- AMM Math Functions -----------------------------
              
              
              @view
              @internal
              def get_y(
                  i: int128,
                  j: int128,
                  x: uint256,
                  xp: DynArray[uint256, MAX_COINS],
                  _amp: uint256,
                  _D: uint256
              ) -> uint256:
                  """
                  Calculate x[j] if one makes x[i] = x
              
                  Done by solving quadratic equation iteratively.
                  x_1**2 + x_1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A)
                  x_1**2 + b*x_1 = c
              
                  x_1 = (x_1**2 + c) / (2*x_1 + b)
                  """
                  # x in the input is converted to the same price/precision
              
                  assert i != j       # dev: same coin
                  assert j >= 0       # dev: j below zero
                  assert j < N_COINS_128  # dev: j above N_COINS
              
                  # should be unreachable, but good for safety
                  assert i >= 0
                  assert i < N_COINS_128
              
                  amp: uint256 = _amp
                  D: uint256 = _D
              
                  S_: uint256 = 0
                  _x: uint256 = 0
                  y_prev: uint256 = 0
                  c: uint256 = D
                  Ann: uint256 = amp * N_COINS
              
                  for _i in range(MAX_COINS_128):
              
                      if _i == N_COINS_128:
                          break
              
                      if _i == i:
                          _x = x
                      elif _i != j:
                          _x = xp[_i]
                      else:
                          continue
              
                      S_ += _x
                      c = c * D / (_x * N_COINS)
              
                  c = c * D * A_PRECISION / (Ann * N_COINS)
                  b: uint256 = S_ + D * A_PRECISION / Ann  # - D
                  y: uint256 = D
              
                  for _i in range(255):
                      y_prev = y
                      y = (y*y + c) / (2 * y + b - D)
                      # Equality with the precision of 1
                      if y > y_prev:
                          if y - y_prev <= 1:
                              return y
                      else:
                          if y_prev - y <= 1:
                              return y
                  raise
              
              
              @pure
              @internal
              def get_D(_xp: DynArray[uint256, MAX_COINS], _amp: uint256) -> uint256:
                  """
                  D invariant calculation in non-overflowing integer operations
                  iteratively
              
                  A * sum(x_i) * n**n + D = A * D * n**n + D**(n+1) / (n**n * prod(x_i))
              
                  Converging solution:
                  D[j+1] = (A * n**n * sum(x_i) - D[j]**(n+1) / (n**n prod(x_i))) / (A * n**n - 1)
                  """
                  S: uint256 = 0
                  for x in _xp:
                      S += x
                  if S == 0:
                      return 0
              
                  D: uint256 = S
                  Ann: uint256 = _amp * N_COINS
              
                  for i in range(255):
              
                      D_P: uint256 = D
                      for x in _xp:
                          D_P = D_P * D / x
                      D_P /= pow_mod256(N_COINS, N_COINS)
                      Dprev: uint256 = D
              
                      # (Ann * S / A_PRECISION + D_P * N_COINS) * D / ((Ann - A_PRECISION) * D / A_PRECISION + (N_COINS + 1) * D_P)
                      D = (
                          (unsafe_div(Ann * S, A_PRECISION) + D_P * N_COINS) * D
                          /
                          (
                              unsafe_div((Ann - A_PRECISION) * D, A_PRECISION) +
                              unsafe_add(N_COINS, 1) * D_P
                          )
                      )
              
                      # Equality with the precision of 1
                      if D > Dprev:
                          if D - Dprev <= 1:
                              return D
                      else:
                          if Dprev - D <= 1:
                              return D
                  # convergence typically occurs in 4 rounds or less, this should be unreachable!
                  # if it does happen the pool is borked and LPs can withdraw via `remove_liquidity`
                  raise
              
              
              @pure
              @internal
              def get_y_D(
                  A: uint256,
                  i: int128,
                  xp: DynArray[uint256, MAX_COINS],
                  D: uint256
              ) -> uint256:
                  """
                  Calculate x[i] if one reduces D from being calculated for xp to D
              
                  Done by solving quadratic equation iteratively.
                  x_1**2 + x_1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A)
                  x_1**2 + b*x_1 = c
              
                  x_1 = (x_1**2 + c) / (2*x_1 + b)
                  """
                  # x in the input is converted to the same price/precision
              
                  assert i >= 0  # dev: i below zero
                  assert i < N_COINS_128  # dev: i above N_COINS
              
                  S_: uint256 = 0
                  _x: uint256 = 0
                  y_prev: uint256 = 0
                  c: uint256 = D
                  Ann: uint256 = A * N_COINS
              
                  for _i in range(MAX_COINS_128):
              
                      if _i == N_COINS_128:
                          break
              
                      if _i != i:
                          _x = xp[_i]
                      else:
                          continue
                      S_ += _x
                      c = c * D / (_x * N_COINS)
              
                  c = c * D * A_PRECISION / (Ann * N_COINS)
                  b: uint256 = S_ + D * A_PRECISION / Ann
                  y: uint256 = D
              
                  for _i in range(255):
                      y_prev = y
                      y = (y*y + c) / (2 * y + b - D)
                      # Equality with the precision of 1
                      if y > y_prev:
                          if y - y_prev <= 1:
                              return y
                      else:
                          if y_prev - y <= 1:
                              return y
                  raise
              
              
              @view
              @internal
              def _A() -> uint256:
                  """
                  Handle ramping A up or down
                  """
                  t1: uint256 = self.future_A_time
                  A1: uint256 = self.future_A
              
                  if block.timestamp < t1:
                      A0: uint256 = self.initial_A
                      t0: uint256 = self.initial_A_time
                      # Expressions in uint256 cannot have negative numbers, thus "if"
                      if A1 > A0:
                          return A0 + unsafe_sub(A1, A0) * (block.timestamp - t0) / (t1 - t0)
                      else:
                          return A0 - unsafe_sub(A0, A1) * (block.timestamp - t0) / (t1 - t0)
              
                  else:  # when t1 == 0 or block.timestamp >= t1
                      return A1
              
              
              @pure
              @internal
              def _xp_mem(
                  _rates: DynArray[uint256, MAX_COINS],
                  _balances: DynArray[uint256, MAX_COINS]
              ) -> DynArray[uint256, MAX_COINS]:
              
                  result: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
                      result.append(unsafe_div(_rates[i] * _balances[i], PRECISION))
                  return result
              
              
              @view
              @internal
              def get_D_mem(
                  _rates: DynArray[uint256, MAX_COINS],
                  _balances: DynArray[uint256, MAX_COINS],
                  _amp: uint256
              ) -> uint256:
                  xp: DynArray[uint256, MAX_COINS] = self._xp_mem(_rates, _balances)
                  return self.get_D(xp, _amp)
              
              
              @view
              @internal
              def _calc_withdraw_one_coin(
                  _burn_amount: uint256,
                  i: int128
              ) -> (
                  uint256,
                  uint256,
                  DynArray[uint256, MAX_COINS],
                  uint256,
                  uint256
              ):
                  # First, need to calculate
                  # * Get current D
                  # * Solve Eqn against y_i for D - _token_amount
                  amp: uint256 = self._A()
                  rates: DynArray[uint256, MAX_COINS] = self._stored_rates()
                  xp: DynArray[uint256, MAX_COINS] = self._xp_mem(rates, self._balances())
                  D0: uint256 = self.get_D(xp, amp)
              
                  total_supply: uint256 = self.total_supply
                  D1: uint256 = D0 - _burn_amount * D0 / total_supply
                  new_y: uint256 = self.get_y_D(amp, i, xp, D1)
              
                  base_fee: uint256 = unsafe_div(
                      unsafe_mul(self.fee, N_COINS),
                      unsafe_mul(4, unsafe_sub(N_COINS, 1))
                  )
                  xp_reduced: DynArray[uint256, MAX_COINS] = xp
                  ys: uint256 = unsafe_div((D0 + D1), unsafe_mul(2, N_COINS))
              
                  dx_expected: uint256 = 0
                  xp_j: uint256 = 0
                  xavg: uint256 = 0
                  dynamic_fee: uint256 = 0
              
                  for j in range(MAX_COINS_128):
              
                      if j == N_COINS_128:
                          break
              
                      dx_expected = 0
                      xp_j = xp[j]
              
                      if j == i:
                          dx_expected = xp_j * D1 / D0 - new_y
                          xavg = unsafe_div((xp_j + new_y), 2)
                      else:
                          dx_expected = xp_j - xp_j * D1 / D0
                          xavg = xp_j
              
                      dynamic_fee = self._dynamic_fee(xavg, ys, base_fee)
                      xp_reduced[j] = xp_j - unsafe_div(dynamic_fee * dx_expected, FEE_DENOMINATOR)
              
                  dy: uint256 = xp_reduced[i] - self.get_y_D(amp, i, xp_reduced, D1)
                  dy_0: uint256 = (xp[i] - new_y) * PRECISION / rates[i]  # w/o fees
                  dy = unsafe_div((dy - 1) * PRECISION, rates[i])  # Withdraw less to account for rounding errors
              
                  # update xp with new_y for p calculations.
                  xp[i] = new_y
              
                  return dy, dy_0 - dy, xp, amp, D1
              
              
              # -------------------------- AMM Price Methods -------------------------------
              
              @pure
              @internal
              def pack_2(p1: uint256, p2: uint256) -> uint256:
                  assert p1 < 2**128
                  assert p2 < 2**128
                  return p1 | (p2 << 128)
              
              
              @pure
              @internal
              def unpack_2(packed: uint256) -> uint256[2]:
                  return [packed & (2**128 - 1), packed >> 128]
              
              
              @internal
              @pure
              def _get_p(
                  xp: DynArray[uint256, MAX_COINS],
                  amp: uint256,
                  D: uint256,
              ) -> DynArray[uint256, MAX_COINS]:
              
                  # dx_0 / dx_1 only, however can have any number of coins in pool
                  ANN: uint256 = unsafe_mul(amp, N_COINS)
                  Dr: uint256 = unsafe_div(D, pow_mod256(N_COINS, N_COINS))
              
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
                      Dr = Dr * D / xp[i]
              
                  p: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  xp0_A: uint256 = unsafe_div(ANN * xp[0], A_PRECISION)
              
                  for i in range(1, MAX_COINS):
              
                      if i == N_COINS:
                          break
              
                      p.append(10**18 * (xp0_A + unsafe_div(Dr * xp[0], xp[i])) / (xp0_A + Dr))
              
                  return p
              
              
              @internal
              def upkeep_oracles(xp: DynArray[uint256, MAX_COINS], amp: uint256, D: uint256):
                  """
                  @notice Upkeeps price and D oracles.
                  """
                  ma_last_time_unpacked: uint256[2] = self.unpack_2(self.ma_last_time)
                  last_prices_packed_current: DynArray[uint256, MAX_COINS] = self.last_prices_packed
                  last_prices_packed_new: DynArray[uint256, MAX_COINS] = last_prices_packed_current
              
                  spot_price: DynArray[uint256, MAX_COINS] = self._get_p(xp, amp, D)
              
                  # -------------------------- Upkeep price oracle -------------------------
              
                  for i in range(MAX_COINS):
              
                      if i == N_COINS - 1:
                          break
              
                      if spot_price[i] != 0:
              
                          # Update packed prices -----------------
                          last_prices_packed_new[i] = self.pack_2(
                              min(spot_price[i], 2 * 10**18),  # <----- Cap spot value by 2.
                              self._calc_moving_average(
                                  last_prices_packed_current[i],
                                  self.ma_exp_time,
                                  ma_last_time_unpacked[0],  # index 0 is ma_last_time for prices
                              )
                          )
              
                  self.last_prices_packed = last_prices_packed_new
              
                  # ---------------------------- Upkeep D oracle ---------------------------
              
                  last_D_packed_current: uint256 = self.last_D_packed
                  self.last_D_packed = self.pack_2(
                      D,
                      self._calc_moving_average(
                          last_D_packed_current,
                          self.D_ma_time,
                          ma_last_time_unpacked[1],  # index 1 is ma_last_time for D
                      )
                  )
              
                  # Housekeeping: Update ma_last_time for p and D oracles ------------------
                  for i in range(2):
                      if ma_last_time_unpacked[i] < block.timestamp:
                          ma_last_time_unpacked[i] = block.timestamp
              
                  self.ma_last_time = self.pack_2(ma_last_time_unpacked[0], ma_last_time_unpacked[1])
              
              
              @internal
              @view
              def _calc_moving_average(
                  packed_value: uint256,
                  averaging_window: uint256,
                  ma_last_time: uint256
              ) -> uint256:
              
                  last_spot_value: uint256 = packed_value & (2**128 - 1)
                  last_ema_value: uint256 = (packed_value >> 128)
              
                  if ma_last_time < block.timestamp:  # calculate new_ema_value and return that.
                      alpha: uint256 = self.exp(
                          -convert(
                              unsafe_div(unsafe_mul(unsafe_sub(block.timestamp, ma_last_time), 10**18), averaging_window), int256
                          )
                      )
                      return unsafe_div(last_spot_value * (10**18 - alpha) + last_ema_value * alpha, 10**18)
              
                  return last_ema_value
              
              
              @view
              @external
              def last_price(i: uint256) -> uint256:
                  return self.last_prices_packed[i] & (2**128 - 1)
              
              
              @view
              @external
              def ema_price(i: uint256) -> uint256:
                  return (self.last_prices_packed[i] >> 128)
              
              
              @external
              @view
              def get_p(i: uint256) -> uint256:
                  """
                  @notice Returns the AMM State price of token
                  @dev if i = 0, it will return the state price of coin[1].
                  @param i index of state price (0 for coin[1], 1 for coin[2], ...)
                  @return uint256 The state price quoted by the AMM for coin[i+1]
                  """
                  amp: uint256 = self._A()
                  xp: DynArray[uint256, MAX_COINS] = self._xp_mem(
                      self._stored_rates(), self._balances()
                  )
                  D: uint256 = self.get_D(xp, amp)
                  return self._get_p(xp, amp, D)[i]
              
              
              @external
              @view
              @nonreentrant('lock')
              def price_oracle(i: uint256) -> uint256:
                  return self._calc_moving_average(
                      self.last_prices_packed[i],
                      self.ma_exp_time,
                      self.ma_last_time & (2**128 - 1)
                  )
              
              
              @external
              @view
              @nonreentrant('lock')
              def D_oracle() -> uint256:
                  return self._calc_moving_average(
                      self.last_D_packed,
                      self.D_ma_time,
                      self.ma_last_time >> 128
                  )
              
              
              # ----------------------------- Math Utils -----------------------------------
              
              
              @internal
              @pure
              def exp(x: int256) -> uint256:
                  """
                  @dev Calculates the natural exponential function of a signed integer with
                       a precision of 1e18.
                  @notice Note that this function consumes about 810 gas units. The implementation
                          is inspired by Remco Bloemen's implementation under the MIT license here:
                          https://xn--2-umb.com/22/exp-ln.
                  @dev This implementation is derived from Snekmate, which is authored
                       by pcaversaccio (Snekmate), distributed under the AGPL-3.0 license.
                       https://github.com/pcaversaccio/snekmate
                  @param x The 32-byte variable.
                  @return int256 The 32-byte calculation result.
                  """
                  value: int256 = x
              
                  # If the result is `< 0.5`, we return zero. This happens when we have the following:
                  # "x <= floor(log(0.5e18) * 1e18) ~ -42e18".
                  if (x <= -41446531673892822313):
                      return empty(uint256)
              
                  # When the result is "> (2 ** 255 - 1) / 1e18" we cannot represent it as a signed integer.
                  # This happens when "x >= floor(log((2 ** 255 - 1) / 1e18) * 1e18) ~ 135".
                  assert x < 135305999368893231589, "wad_exp overflow"
              
                  # `x` is now in the range "(-42, 136) * 1e18". Convert to "(-42, 136) * 2 ** 96" for higher
                  # intermediate precision and a binary base. This base conversion is a multiplication with
                  # "1e18 / 2 ** 96 = 5 ** 18 / 2 ** 78".
                  value = unsafe_div(x << 78, 5 ** 18)
              
                  # Reduce the range of `x` to "(-½ ln 2, ½ ln 2) * 2 ** 96" by factoring out powers of two
                  # so that "exp(x) = exp(x') * 2 ** k", where `k` is a signer integer. Solving this gives
                  # "k = round(x / log(2))" and "x' = x - k * log(2)". Thus, `k` is in the range "[-61, 195]".
                  k: int256 = unsafe_add(unsafe_div(value << 96, 54916777467707473351141471128), 2 ** 95) >> 96
                  value = unsafe_sub(value, unsafe_mul(k, 54916777467707473351141471128))
              
                  # Evaluate using a "(6, 7)"-term rational approximation. Since `p` is monic,
                  # we will multiply by a scaling factor later.
                  y: int256 = unsafe_add(unsafe_mul(unsafe_add(value, 1346386616545796478920950773328), value) >> 96, 57155421227552351082224309758442)
                  p: int256 = unsafe_add(unsafe_mul(unsafe_add(unsafe_mul(unsafe_sub(unsafe_add(y, value), 94201549194550492254356042504812), y) >> 96,\
                                         28719021644029726153956944680412240), value), 4385272521454847904659076985693276 << 96)
              
                  # We leave `p` in the "2 ** 192" base so that we do not have to scale it up
                  # again for the division.
                  q: int256 = unsafe_add(unsafe_mul(unsafe_sub(value, 2855989394907223263936484059900), value) >> 96, 50020603652535783019961831881945)
                  q = unsafe_sub(unsafe_mul(q, value) >> 96, 533845033583426703283633433725380)
                  q = unsafe_add(unsafe_mul(q, value) >> 96, 3604857256930695427073651918091429)
                  q = unsafe_sub(unsafe_mul(q, value) >> 96, 14423608567350463180887372962807573)
                  q = unsafe_add(unsafe_mul(q, value) >> 96, 26449188498355588339934803723976023)
              
                  # The polynomial `q` has no zeros in the range because all its roots are complex.
                  # No scaling is required, as `p` is already "2 ** 96" too large. Also,
                  # `r` is in the range "(0.09, 0.25) * 2**96" after the division.
                  r: int256 = unsafe_div(p, q)
              
                  # To finalise the calculation, we have to multiply `r` by:
                  #   - the scale factor "s = ~6.031367120",
                  #   - the factor "2 ** k" from the range reduction, and
                  #   - the factor "1e18 / 2 ** 96" for the base conversion.
                  # We do this all at once, with an intermediate result in "2**213" base,
                  # so that the final right shift always gives a positive value.
              
                  # Note that to circumvent Vyper's safecast feature for the potentially
                  # negative parameter value `r`, we first convert `r` to `bytes32` and
                  # subsequently to `uint256`. Remember that the EVM default behaviour is
                  # to use two's complement representation to handle signed integers.
                  return unsafe_mul(convert(convert(r, bytes32), uint256), 3822833074963236453042738258902158003155416615667) >> convert(unsafe_sub(195, k), uint256)
              
              
              # ---------------------------- ERC20 Utils -----------------------------------
              
              @view
              @internal
              def _domain_separator() -> bytes32:
                  if chain.id != CACHED_CHAIN_ID:
                      return keccak256(
                          _abi_encode(
                              EIP712_TYPEHASH,
                              NAME_HASH,
                              VERSION_HASH,
                              chain.id,
                              self,
                              salt,
                          )
                      )
                  return CACHED_DOMAIN_SEPARATOR
              
              
              @internal
              def _transfer(_from: address, _to: address, _value: uint256):
                  # # NOTE: vyper does not allow underflows
                  # #       so the following subtraction would revert on insufficient balance
                  self.balanceOf[_from] -= _value
                  self.balanceOf[_to] += _value
              
                  log Transfer(_from, _to, _value)
              
              
              @internal
              def _burnFrom(_from: address, _burn_amount: uint256):
              
                  self.total_supply -= _burn_amount
                  self.balanceOf[_from] -= _burn_amount
                  log Transfer(_from, empty(address), _burn_amount)
              
              
              @external
              def transfer(_to : address, _value : uint256) -> bool:
                  """
                  @dev Transfer token for a specified address
                  @param _to The address to transfer to.
                  @param _value The amount to be transferred.
                  """
                  self._transfer(msg.sender, _to, _value)
                  return True
              
              
              @external
              def transferFrom(_from : address, _to : address, _value : uint256) -> bool:
                  """
                   @dev Transfer tokens from one address to another.
                   @param _from address The address which you want to send tokens from
                   @param _to address The address which you want to transfer to
                   @param _value uint256 the amount of tokens to be transferred
                  """
                  self._transfer(_from, _to, _value)
              
                  _allowance: uint256 = self.allowance[_from][msg.sender]
                  if _allowance != max_value(uint256):
                      _new_allowance: uint256 = _allowance - _value
                      self.allowance[_from][msg.sender] = _new_allowance
                      log Approval(_from, msg.sender, _new_allowance)
              
                  return True
              
              
              @external
              def approve(_spender : address, _value : uint256) -> bool:
                  """
                  @notice Approve the passed address to transfer the specified amount of
                          tokens on behalf of msg.sender
                  @dev Beware that changing an allowance via this method brings the risk that
                       someone may use both the old and new allowance by unfortunate transaction
                       ordering: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                  @param _spender The address which will transfer the funds
                  @param _value The amount of tokens that may be transferred
                  @return bool success
                  """
                  self.allowance[msg.sender][_spender] = _value
              
                  log Approval(msg.sender, _spender, _value)
                  return True
              
              
              @external
              def permit(
                  _owner: address,
                  _spender: address,
                  _value: uint256,
                  _deadline: uint256,
                  _v: uint8,
                  _r: bytes32,
                  _s: bytes32
              ) -> bool:
                  """
                  @notice Approves spender by owner's signature to expend owner's tokens.
                      See https://eips.ethereum.org/EIPS/eip-2612.
                  @dev Inspired by https://github.com/yearn/yearn-vaults/blob/main/contracts/Vault.vy#L753-L793
                  @dev Supports smart contract wallets which implement ERC1271
                      https://eips.ethereum.org/EIPS/eip-1271
                  @param _owner The address which is a source of funds and has signed the Permit.
                  @param _spender The address which is allowed to spend the funds.
                  @param _value The amount of tokens to be spent.
                  @param _deadline The timestamp after which the Permit is no longer valid.
                  @param _v The bytes[64] of the valid secp256k1 signature of permit by owner
                  @param _r The bytes[0:32] of the valid secp256k1 signature of permit by owner
                  @param _s The bytes[32:64] of the valid secp256k1 signature of permit by owner
                  @return True, if transaction completes successfully
                  """
                  assert _owner != empty(address)
                  assert block.timestamp <= _deadline
              
                  nonce: uint256 = self.nonces[_owner]
                  digest: bytes32 = keccak256(
                      concat(
                          b"\x19\x01",
                          self._domain_separator(),
                          keccak256(_abi_encode(EIP2612_TYPEHASH, _owner, _spender, _value, nonce, _deadline))
                      )
                  )
              
                  if _owner.is_contract:
                      sig: Bytes[65] = concat(_abi_encode(_r, _s), slice(convert(_v, bytes32), 31, 1))
                      # reentrancy not a concern since this is a staticcall
                      assert ERC1271(_owner).isValidSignature(digest, sig) == ERC1271_MAGIC_VAL
                  else:
                      assert ecrecover(digest, convert(_v, uint256), convert(_r, uint256), convert(_s, uint256)) == _owner
              
                  self.allowance[_owner][_spender] = _value
                  self.nonces[_owner] = unsafe_add(nonce, 1)
              
                  log Approval(_owner, _spender, _value)
                  return True
              
              
              @view
              @external
              def DOMAIN_SEPARATOR() -> bytes32:
                  """
                  @notice EIP712 domain separator.
                  @return bytes32 Domain Separator set for the current chain.
                  """
                  return self._domain_separator()
              
              
              # ------------------------- AMM View Functions -------------------------------
              
              
              @view
              @external
              def get_dx(i: int128, j: int128, dy: uint256) -> uint256:
                  """
                  @notice Calculate the current input dx given output dy
                  @dev Index values can be found via the `coins` public getter method
                  @param i Index value for the coin to send
                  @param j Index value of the coin to receive
                  @param dy Amount of `j` being received after exchange
                  @return Amount of `i` predicted
                  """
                  return StableSwapViews(factory.views_implementation()).get_dx(i, j, dy, self)
              
              
              @view
              @external
              def get_dy(i: int128, j: int128, dx: uint256) -> uint256:
                  """
                  @notice Calculate the current output dy given input dx
                  @dev Index values can be found via the `coins` public getter method
                  @param i Index value for the coin to send
                  @param j Index value of the coin to receive
                  @param dx Amount of `i` being exchanged
                  @return Amount of `j` predicted
                  """
                  return StableSwapViews(factory.views_implementation()).get_dy(i, j, dx, self)
              
              
              @view
              @external
              def calc_withdraw_one_coin(_burn_amount: uint256, i: int128) -> uint256:
                  """
                  @notice Calculate the amount received when withdrawing a single coin
                  @param _burn_amount Amount of LP tokens to burn in the withdrawal
                  @param i Index value of the coin to withdraw
                  @return Amount of coin received
                  """
                  return self._calc_withdraw_one_coin(_burn_amount, i)[0]
              
              
              @view
              @external
              @nonreentrant('lock')
              def totalSupply() -> uint256:
                  """
                  @notice The total supply of pool LP tokens
                  @return self.total_supply, 18 decimals.
                  """
                  return self.total_supply
              
              
              @view
              @external
              @nonreentrant('lock')
              def get_virtual_price() -> uint256:
                  """
                  @notice The current virtual price of the pool LP token
                  @dev Useful for calculating profits.
                       The method may be vulnerable to donation-style attacks if implementation
                       contains rebasing tokens. For integrators, caution is advised.
                  @return LP token virtual price normalized to 1e18
                  """
                  amp: uint256 = self._A()
                  xp: DynArray[uint256, MAX_COINS] = self._xp_mem(
                      self._stored_rates(), self._balances()
                  )
                  D: uint256 = self.get_D(xp, amp)
                  # D is in the units similar to DAI (e.g. converted to precision 1e18)
                  # When balanced, D = n * x_u - total virtual value of the portfolio
                  return D * PRECISION / self.total_supply
              
              
              @view
              @external
              def calc_token_amount(
                  _amounts: DynArray[uint256, MAX_COINS],
                  _is_deposit: bool
              ) -> uint256:
                  """
                  @notice Calculate addition or reduction in token supply from a deposit or withdrawal
                  @param _amounts Amount of each coin being deposited
                  @param _is_deposit set True for deposits, False for withdrawals
                  @return Expected amount of LP tokens received
                  """
                  return StableSwapViews(factory.views_implementation()).calc_token_amount(_amounts, _is_deposit, self)
              
              
              @view
              @external
              def A() -> uint256:
                  return unsafe_div(self._A(), A_PRECISION)
              
              
              @view
              @external
              def A_precise() -> uint256:
                  return self._A()
              
              
              @view
              @external
              def balances(i: uint256) -> uint256:
                  """
                  @notice Get the current balance of a coin within the
                          pool, less the accrued admin fees
                  @param i Index value for the coin to query balance of
                  @return Token balance
                  """
                  return self._balances()[i]
              
              
              @view
              @external
              def get_balances() -> DynArray[uint256, MAX_COINS]:
                  return self._balances()
              
              
              @view
              @external
              def stored_rates() -> DynArray[uint256, MAX_COINS]:
                  return self._stored_rates()
              
              
              @view
              @external
              def dynamic_fee(i: int128, j: int128) -> uint256:
                  """
                  @notice Return the fee for swapping between `i` and `j`
                  @param i Index value for the coin to send
                  @param j Index value of the coin to receive
                  @return Swap fee expressed as an integer with 1e10 precision
                  """
                  return StableSwapViews(factory.views_implementation()).dynamic_fee(i, j, self)
              
              
              # --------------------------- AMM Admin Functions ----------------------------
              
              
              @external
              def ramp_A(_future_A: uint256, _future_time: uint256):
                  assert msg.sender == factory.admin()  # dev: only owner
                  assert block.timestamp >= self.initial_A_time + MIN_RAMP_TIME
                  assert _future_time >= block.timestamp + MIN_RAMP_TIME  # dev: insufficient time
              
                  _initial_A: uint256 = self._A()
                  _future_A_p: uint256 = _future_A * A_PRECISION
              
                  assert _future_A > 0 and _future_A < MAX_A
                  if _future_A_p < _initial_A:
                      assert _future_A_p * MAX_A_CHANGE >= _initial_A
                  else:
                      assert _future_A_p <= _initial_A * MAX_A_CHANGE
              
                  self.initial_A = _initial_A
                  self.future_A = _future_A_p
                  self.initial_A_time = block.timestamp
                  self.future_A_time = _future_time
              
                  log RampA(_initial_A, _future_A_p, block.timestamp, _future_time)
              
              
              @external
              def stop_ramp_A():
                  assert msg.sender == factory.admin()  # dev: only owner
              
                  current_A: uint256 = self._A()
                  self.initial_A = current_A
                  self.future_A = current_A
                  self.initial_A_time = block.timestamp
                  self.future_A_time = block.timestamp
                  # now (block.timestamp < t1) is always False, so we return saved A
              
                  log StopRampA(current_A, block.timestamp)
              
              
              @external
              def set_new_fee(_new_fee: uint256, _new_offpeg_fee_multiplier: uint256):
              
                  assert msg.sender == factory.admin()
              
                  # set new fee:
                  assert _new_fee <= MAX_FEE
                  self.fee = _new_fee
              
                  # set new offpeg_fee_multiplier:
                  assert _new_offpeg_fee_multiplier * _new_fee <= MAX_FEE * FEE_DENOMINATOR  # dev: offpeg multiplier exceeds maximum
                  self.offpeg_fee_multiplier = _new_offpeg_fee_multiplier
              
                  log ApplyNewFee(_new_fee, _new_offpeg_fee_multiplier)
              
              
              @external
              def set_ma_exp_time(_ma_exp_time: uint256, _D_ma_time: uint256):
                  """
                  @notice Set the moving average window of the price oracles.
                  @param _ma_exp_time Moving average window for the price oracle. It is time_in_seconds / ln(2).
                  @param _D_ma_time Moving average window for the D oracle. It is time_in_seconds / ln(2).
                  """
                  assert msg.sender == factory.admin()  # dev: only owner
                  assert unsafe_mul(_ma_exp_time, _D_ma_time) > 0  # dev: 0 in input values
              
                  self.ma_exp_time = _ma_exp_time
                  self.D_ma_time = _D_ma_time
              
                  log SetNewMATime(_ma_exp_time, _D_ma_time)

              File 3 of 8: FRAXStablecoin
              {"AccessControl.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.6.0;\n\nimport \"./EnumerableSet.sol\";\nimport \"./Address.sol\";\nimport \"./Context.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n *     require(hasRole(MY_ROLE, msg.sender));\n *     ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role\u0027s admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it.\n */\nabstract contract AccessControl is Context {\n    using EnumerableSet for EnumerableSet.AddressSet;\n    using Address for address;\n\n    struct RoleData {\n        EnumerableSet.AddressSet members;\n        bytes32 adminRole;\n    }\n\n    mapping (bytes32 =\u003e RoleData) private _roles;\n\n    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; //bytes32(uint256(0x4B437D01b575618140442A4975db38850e3f8f5f) \u003c\u003c 96);\n\n    /**\n     * @dev Emitted when `newAdminRole` is set as ``role``\u0027s admin role, replacing `previousAdminRole`\n     *\n     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n     * {RoleAdminChanged} not being emitted signaling this.\n     *\n     * _Available since v3.1._\n     */\n    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n    /**\n     * @dev Emitted when `account` is granted `role`.\n     *\n     * `sender` is the account that originated the contract call, an admin role\n     * bearer except when using {_setupRole}.\n     */\n    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Emitted when `account` is revoked `role`.\n     *\n     * `sender` is the account that originated the contract call:\n     *   - if using `revokeRole`, it is the admin role bearer\n     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)\n     */\n    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) public view returns (bool) {\n        return _roles[role].members.contains(account);\n    }\n\n    /**\n     * @dev Returns the number of accounts that have `role`. Can be used\n     * together with {getRoleMember} to enumerate all bearers of a role.\n     */\n    function getRoleMemberCount(bytes32 role) public view returns (uint256) {\n        return _roles[role].members.length();\n    }\n\n    /**\n     * @dev Returns one of the accounts that have `role`. `index` must be a\n     * value between 0 and {getRoleMemberCount}, non-inclusive.\n     *\n     * Role bearers are not sorted in any particular way, and their ordering may\n     * change at any point.\n     *\n     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n     * you perform all queries on the same block. See the following\n     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n     * for more information.\n     */\n    function getRoleMember(bytes32 role, uint256 index) public view returns (address) {\n        return _roles[role].members.at(index);\n    }\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role\u0027s admin, use {_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) public view returns (bytes32) {\n        return _roles[role].adminRole;\n    }\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``\u0027s admin role.\n     */\n    function grantRole(bytes32 role, address account) public virtual {\n        require(hasRole(_roles[role].adminRole, _msgSender()), \"AccessControl: sender must be an admin to grant\");\n\n        _grantRole(role, account);\n    }\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``\u0027s admin role.\n     */\n    function revokeRole(bytes32 role, address account) public virtual {\n        require(hasRole(_roles[role].adminRole, _msgSender()), \"AccessControl: sender must be an admin to revoke\");\n\n        _revokeRole(role, account);\n    }\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function\u0027s\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been granted `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `account`.\n     */\n    function renounceRole(bytes32 role, address account) public virtual {\n        require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n        _revokeRole(role, account);\n    }\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event. Note that unlike {grantRole}, this function doesn\u0027t perform any\n     * checks on the calling account.\n     *\n     * [WARNING]\n     * ====\n     * This function should only be called from the constructor when setting\n     * up the initial roles for the system.\n     *\n     * Using this function in any other way is effectively circumventing the admin\n     * system imposed by {AccessControl}.\n     * ====\n     */\n    function _setupRole(bytes32 role, address account) internal virtual {\n        _grantRole(role, account);\n    }\n\n    /**\n     * @dev Sets `adminRole` as ``role``\u0027s admin role.\n     *\n     * Emits a {RoleAdminChanged} event.\n     */\n    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n        emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);\n        _roles[role].adminRole = adminRole;\n    }\n\n    function _grantRole(bytes32 role, address account) private {\n        if (_roles[role].members.add(account)) {\n            emit RoleGranted(role, account, _msgSender());\n        }\n    }\n\n    function _revokeRole(bytes32 role, address account) private {\n        if (_roles[role].members.remove(account)) {\n            emit RoleRevoked(role, account, _msgSender());\n        }\n    }\n}\n"},"Address.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies in extcodesize, which returns 0 for contracts in\n        // construction, since the code is only stored at the end of the\n        // constructor execution.\n\n        uint256 size;\n        // solhint-disable-next-line no-inline-assembly\n        assembly { size := extcodesize(account) }\n        return size \u003e 0;\n    }\n\n    /**\n     * @dev Replacement for Solidity\u0027s `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value\n        (bool success, ) = recipient.call{ value: amount }(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain`call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason, it is bubbled up by this\n     * function (like regular Solidity function calls).\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n      return functionCall(target, data, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {\n        return _functionCallWithValue(target, data, 0, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {\n        require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n        return _functionCallWithValue(target, data, value, errorMessage);\n    }\n\n    function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {\n        require(isContract(target), \"Address: call to non-contract\");\n\n        // solhint-disable-next-line avoid-low-level-calls\n        (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);\n        if (success) {\n            return returndata;\n        } else {\n            // Look for revert reason and bubble it up if present\n            if (returndata.length \u003e 0) {\n                // The easiest way to bubble the revert reason is using memory via assembly\n\n                // solhint-disable-next-line no-inline-assembly\n                assembly {\n                    let returndata_size := mload(returndata)\n                    revert(add(32, returndata), returndata_size)\n                }\n            } else {\n                revert(errorMessage);\n            }\n        }\n    }\n}"},"AggregatorV3Interface.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity \u003e=0.6.0;\n\ninterface AggregatorV3Interface {\n\n  function decimals() external view returns (uint8);\n  function description() external view returns (string memory);\n  function version() external view returns (uint256);\n\n  // getRoundData and latestRoundData should both raise \"No data present\"\n  // if they do not have data to report, instead of returning unset values\n  // which could be misinterpreted as actual reported values.\n  function getRoundData(uint80 _roundId)\n    external\n    view\n    returns (\n      uint80 roundId,\n      int256 answer,\n      uint256 startedAt,\n      uint256 updatedAt,\n      uint80 answeredInRound\n    );\n  function latestRoundData()\n    external\n    view\n    returns (\n      uint80 roundId,\n      int256 answer,\n      uint256 startedAt,\n      uint256 updatedAt,\n      uint80 answeredInRound\n    );\n\n}"},"Babylonian.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\n// computes square roots using the babylonian method\n// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method\nlibrary Babylonian {\n    function sqrt(uint y) internal pure returns (uint z) {\n        if (y \u003e 3) {\n            z = y;\n            uint x = y / 2 + 1;\n            while (x \u003c z) {\n                z = x;\n                x = (y / x + x) / 2;\n            }\n        } else if (y != 0) {\n            z = 1;\n        }\n        // else z = 0\n    }\n}"},"BlockMiner.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n// file: BlockMinder.sol\n\n// used to \"waste\" blocks for truffle tests\ncontract BlockMiner {\n    uint256 public blocksMined;\n\n    constructor () public {\n        blocksMined = 0;\n    }\n\n    function mine() public {\n       blocksMined += 1;\n    }\n\n    function blockTime() external view returns (uint256) {\n       return block.timestamp;\n    }\n}"},"ChainlinkETHUSDPriceConsumer.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\npragma experimental ABIEncoderV2;\n\nimport \"./AggregatorV3Interface.sol\";\n\ncontract ChainlinkETHUSDPriceConsumer {\n\n    AggregatorV3Interface internal priceFeed;\n\n\n    constructor() public {\n        priceFeed = AggregatorV3Interface(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);\n    }\n\n    /**\n     * Returns the latest price\n     */\n    function getLatestPrice() public view returns (int) {\n        (\n            , \n            int price,\n            ,\n            ,\n            \n        ) = priceFeed.latestRoundData();\n        return price;\n    }\n\n    function getDecimals() public view returns (uint8) {\n        return priceFeed.decimals();\n    }\n}"},"ChainlinkETHUSDPriceConsumerTest.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\npragma experimental ABIEncoderV2;\n\nimport \"./AggregatorV3Interface.sol\";\n\n// VERY IMPORTANT: UNCOMMENT THIS LATER\n// VERY IMPORTANT: UNCOMMENT THIS LATER\n// VERY IMPORTANT: UNCOMMENT THIS LATER\n// VERY IMPORTANT: UNCOMMENT THIS LATER\n// VERY IMPORTANT: UNCOMMENT THIS LATER\n// VERY IMPORTANT: UNCOMMENT THIS LATER\n// VERY IMPORTANT: UNCOMMENT THIS LATER\n// VERY IMPORTANT: UNCOMMENT THIS LATER\n// VERY IMPORTANT: UNCOMMENT THIS LATER\n// VERY IMPORTANT: UNCOMMENT THIS LATER\n// VERY IMPORTANT: UNCOMMENT THIS LATER\n// VERY IMPORTANT: UNCOMMENT THIS LATER\n// VERY IMPORTANT: UNCOMMENT THIS LATER\n// VERY IMPORTANT: UNCOMMENT THIS LATER\n// import \"@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol\";\n\ncontract ChainlinkETHUSDPriceConsumerTest {\n\n    // VERY IMPORTANT: UNCOMMENT THIS LATER\n    // VERY IMPORTANT: UNCOMMENT THIS LATER\n    // VERY IMPORTANT: UNCOMMENT THIS LATER\n    // VERY IMPORTANT: UNCOMMENT THIS LATER\n    // VERY IMPORTANT: UNCOMMENT THIS LATER\n    // VERY IMPORTANT: UNCOMMENT THIS LATER\n    // VERY IMPORTANT: UNCOMMENT THIS LATER\n    // VERY IMPORTANT: UNCOMMENT THIS LATER\n    // VERY IMPORTANT: UNCOMMENT THIS LATER\n    // VERY IMPORTANT: UNCOMMENT THIS LATER\n    // VERY IMPORTANT: UNCOMMENT THIS LATER\n    // VERY IMPORTANT: UNCOMMENT THIS LATER\n    // VERY IMPORTANT: UNCOMMENT THIS LATER\n    // VERY IMPORTANT: UNCOMMENT THIS LATER\n    // AggregatorV3Interface internal priceFeed;\n\n    /**\n     * Network: Kovan\n     * Aggregator: ETH/USD\n     * Address: 0x9326BFA02ADD2366b30bacB125260Af641031331\n     */\n    /**\n     * Network: Mainnet\n     * Aggregator: ETH/USD\n     * Address: 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419\n     */\n\n     \n    constructor() public {\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // priceFeed = AggregatorV3Interface(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);\n    }\n\n    /**\n     * Returns the latest price\n     */\n    function getLatestPrice() public pure returns (int) {\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // (\n        //     uint80 roundID, \n        //     int price,\n        //     uint startedAt,\n        //     uint timeStamp,\n        //     uint80 answeredInRound\n        // ) = priceFeed.latestRoundData();\n        // // If the round is not complete yet, timestamp is 0\n        // require(timeStamp \u003e 0, \"Round not complete\");\n\n        // This will return something like 32063000000\n        // Divide this by getDecimals to get the \"true\" price\n        // You can can multiply the \"true\" price by 1e6 to get the frax ecosystem \u0027price\u0027\n        // return price;\n\n        return 59000000000;\n    }\n\n    function getDecimals() public pure returns (uint8) {\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // VERY IMPORTANT: UNCOMMENT THIS LATER\n        // return priceFeed.decimals();\n        return 8;\n    }\n}"},"Context.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\n/*\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with GSN meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\ncontract Context {\n    // Empty internal constructor, to prevent people from mistakenly deploying\n    // an instance of this contract, which should be used via inheritance.\n    constructor () internal { }\n\n    function _msgSender() internal view virtual returns (address payable) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes memory) {\n        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691\n        return msg.data;\n    }\n}"},"EnumerableSet.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.6.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n *     // Add the library methods\n *     using EnumerableSet for EnumerableSet.AddressSet;\n *\n *     // Declare a set state variable\n *     EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`\n * (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n    // To implement this library for multiple types with as little code\n    // repetition as possible, we write it in terms of a generic Set type with\n    // bytes32 values.\n    // The Set implementation uses private functions, and user-facing\n    // implementations (such as AddressSet) are just wrappers around the\n    // underlying Set.\n    // This means that we can only create new EnumerableSets for types that fit\n    // in bytes32.\n\n    struct Set {\n        // Storage of set values\n        bytes32[] _values;\n\n        // Position of the value in the `values` array, plus 1 because index 0\n        // means a value is not in the set.\n        mapping (bytes32 =\u003e uint256) _indexes;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function _add(Set storage set, bytes32 value) private returns (bool) {\n        if (!_contains(set, value)) {\n            set._values.push(value);\n            // The value is stored at length-1, but we add 1 to all indexes\n            // and use 0 as a sentinel value\n            set._indexes[value] = set._values.length;\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function _remove(Set storage set, bytes32 value) private returns (bool) {\n        // We read and store the value\u0027s index to prevent multiple reads from the same storage slot\n        uint256 valueIndex = set._indexes[value];\n\n        if (valueIndex != 0) { // Equivalent to contains(set, value)\n            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n            // the array, and then remove the last element (sometimes called as \u0027swap and pop\u0027).\n            // This modifies the order of the array, as noted in {at}.\n\n            uint256 toDeleteIndex = valueIndex - 1;\n            uint256 lastIndex = set._values.length - 1;\n\n            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs\n            // so rarely, we still do the swap anyway to avoid the gas cost of adding an \u0027if\u0027 statement.\n\n            bytes32 lastvalue = set._values[lastIndex];\n\n            // Move the last value to the index where the value to delete is\n            set._values[toDeleteIndex] = lastvalue;\n            // Update the index for the moved value\n            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based\n\n            // Delete the slot where the moved value was stored\n            set._values.pop();\n\n            // Delete the index for the deleted slot\n            delete set._indexes[value];\n\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function _contains(Set storage set, bytes32 value) private view returns (bool) {\n        return set._indexes[value] != 0;\n    }\n\n    /**\n     * @dev Returns the number of values on the set. O(1).\n     */\n    function _length(Set storage set) private view returns (uint256) {\n        return set._values.length;\n    }\n\n   /**\n    * @dev Returns the value stored at position `index` in the set. O(1).\n    *\n    * Note that there are no guarantees on the ordering of values inside the\n    * array, and it may change when more values are added or removed.\n    *\n    * Requirements:\n    *\n    * - `index` must be strictly less than {length}.\n    */\n    function _at(Set storage set, uint256 index) private view returns (bytes32) {\n        require(set._values.length \u003e index, \"EnumerableSet: index out of bounds\");\n        return set._values[index];\n    }\n\n    // AddressSet\n\n    struct AddressSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(AddressSet storage set, address value) internal returns (bool) {\n        return _add(set._inner, bytes32(uint256(value)));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(AddressSet storage set, address value) internal returns (bool) {\n        return _remove(set._inner, bytes32(uint256(value)));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(AddressSet storage set, address value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(uint256(value)));\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(AddressSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n   /**\n    * @dev Returns the value stored at position `index` in the set. O(1).\n    *\n    * Note that there are no guarantees on the ordering of values inside the\n    * array, and it may change when more values are added or removed.\n    *\n    * Requirements:\n    *\n    * - `index` must be strictly less than {length}.\n    */\n    function at(AddressSet storage set, uint256 index) internal view returns (address) {\n        return address(uint256(_at(set._inner, index)));\n    }\n\n\n    // UintSet\n\n    struct UintSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(UintSet storage set, uint256 value) internal returns (bool) {\n        return _add(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(UintSet storage set, uint256 value) internal returns (bool) {\n        return _remove(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns the number of values on the set. O(1).\n     */\n    function length(UintSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n   /**\n    * @dev Returns the value stored at position `index` in the set. O(1).\n    *\n    * Note that there are no guarantees on the ordering of values inside the\n    * array, and it may change when more values are added or removed.\n    *\n    * Requirements:\n    *\n    * - `index` must be strictly less than {length}.\n    */\n    function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n        return uint256(_at(set._inner, index));\n    }\n}\n"},"ERC20.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \"./Context.sol\";\nimport \"./IERC20.sol\";\nimport \"./SafeMath.sol\";\nimport \"./Address.sol\";\n\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20Mintable}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin guidelines: functions revert instead\n * of returning `false` on failure. This behavior is nonetheless conventional\n * and does not conflict with the expectations of ERC20 applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn\u0027t required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\n \ncontract ERC20 is Context, IERC20 {\n    using SafeMath for uint256;\n\n    mapping (address =\u003e uint256) private _balances;\n\n    mapping (address =\u003e mapping (address =\u003e uint256)) private _allowances;\n\n    uint256 private _totalSupply;\n\n    string private _name;\n    string private _symbol;\n    uint8 private _decimals;\n    \n    /**\n     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with\n     * a default value of 18.\n     *\n     * To select a different value for {decimals}, use {_setupDecimals}.\n     *\n     * All three of these values are immutable: they can only be set once during\n     * construction.\n     */\n    constructor (string memory name, string memory symbol) public {\n        _name = name;\n        _symbol = symbol;\n        _decimals = 18;\n    }\n\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() public view returns (string memory) {\n        return _name;\n    }\n\n    /**\n     * @dev Returns the symbol of the token, usually a shorter version of the\n     * name.\n     */\n    function symbol() public view returns (string memory) {\n        return _symbol;\n    }\n\n    /**\n     * @dev Returns the number of decimals used to get its user representation.\n     * For example, if `decimals` equals `2`, a balance of `505` tokens should\n     * be displayed to a user as `5,05` (`505 / 10 ** 2`).\n     *\n     * Tokens usually opt for a value of 18, imitating the relationship between\n     * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is\n     * called.\n     *\n     * NOTE: This information is only used for _display_ purposes: it in\n     * no way affects any of the arithmetic of the contract, including\n     * {IERC20-balanceOf} and {IERC20-transfer}.\n     */\n    function decimals() public view returns (uint8) {\n        return _decimals;\n    }\n\n    /**\n     * @dev See {IERC20-totalSupply}.\n     */\n    function totalSupply() public view override returns (uint256) {\n        return _totalSupply;\n    }\n\n    /**\n     * @dev See {IERC20-balanceOf}.\n     */\n    function balanceOf(address account) public view override returns (uint256) {\n        return _balances[account];\n    }\n\n    /**\n     * @dev See {IERC20-transfer}.\n     *\n     * Requirements:\n     *\n     * - `recipient` cannot be the zero address.\n     * - the caller must have a balance of at least `amount`.\n     */\n    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n        _transfer(_msgSender(), recipient, amount);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-allowance}.\n     */\n    function allowance(address owner, address spender) public view virtual override returns (uint256) {\n        return _allowances[owner][spender];\n    }\n\n    /**\n     * @dev See {IERC20-approve}.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.approve(address spender, uint256 amount)\n     */\n    function approve(address spender, uint256 amount) public virtual override returns (bool) {\n        _approve(_msgSender(), spender, amount);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-transferFrom}.\n     *\n     * Emits an {Approval} event indicating the updated allowance. This is not\n     * required by the EIP. See the note at the beginning of {ERC20};\n     *\n     * Requirements:\n     * - `sender` and `recipient` cannot be the zero address.\n     * - `sender` must have a balance of at least `amount`.\n     * - the caller must have allowance for `sender`\u0027s tokens of at least\n     * `amount`.\n     */\n    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {\n        _transfer(sender, recipient, amount);\n        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, \"ERC20: transfer amount exceeds allowance\"));\n        return true;\n    }\n\n    /**\n     * @dev Atomically increases the allowance granted to `spender` by the caller.\n     *\n     * This is an alternative to {approve} that can be used as a mitigation for\n     * problems described in {IERC20-approve}.\n     *\n     * Emits an {Approval} event indicating the updated allowance.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     */\n    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));\n        return true;\n    }\n\n    /**\n     * @dev Atomically decreases the allowance granted to `spender` by the caller.\n     *\n     * This is an alternative to {approve} that can be used as a mitigation for\n     * problems described in {IERC20-approve}.\n     *\n     * Emits an {Approval} event indicating the updated allowance.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `spender` must have allowance for the caller of at least\n     * `subtractedValue`.\n     */\n    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, \"ERC20: decreased allowance below zero\"));\n        return true;\n    }\n\n    /**\n     * @dev Moves tokens `amount` from `sender` to `recipient`.\n     *\n     * This is internal function is equivalent to {transfer}, and can be used to\n     * e.g. implement automatic token fees, slashing mechanisms, etc.\n     *\n     * Emits a {Transfer} event.\n     *\n     * Requirements:\n     *\n     * - `sender` cannot be the zero address.\n     * - `recipient` cannot be the zero address.\n     * - `sender` must have a balance of at least `amount`.\n     */\n    function _transfer(address sender, address recipient, uint256 amount) internal virtual {\n        require(sender != address(0), \"ERC20: transfer from the zero address\");\n        require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n        _beforeTokenTransfer(sender, recipient, amount);\n\n        _balances[sender] = _balances[sender].sub(amount, \"ERC20: transfer amount exceeds balance\");\n        _balances[recipient] = _balances[recipient].add(amount);\n        emit Transfer(sender, recipient, amount);\n    }\n\n    /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n     * the total supply.\n     *\n     * Emits a {Transfer} event with `from` set to the zero address.\n     *\n     * Requirements\n     *\n     * - `to` cannot be the zero address.\n     */\n    function _mint(address account, uint256 amount) internal virtual {\n        require(account != address(0), \"ERC20: mint to the zero address\");\n\n        _beforeTokenTransfer(address(0), account, amount);\n\n        _totalSupply = _totalSupply.add(amount);\n        _balances[account] = _balances[account].add(amount);\n        emit Transfer(address(0), account, amount);\n    }\n\n    /**\n     * @dev Destroys `amount` tokens from the caller.\n     *\n     * See {ERC20-_burn}.\n     */\n    function burn(uint256 amount) public virtual {\n        _burn(_msgSender(), amount);\n    }\n\n    /**\n     * @dev Destroys `amount` tokens from `account`, deducting from the caller\u0027s\n     * allowance.\n     *\n     * See {ERC20-_burn} and {ERC20-allowance}.\n     *\n     * Requirements:\n     *\n     * - the caller must have allowance for `accounts`\u0027s tokens of at least\n     * `amount`.\n     */\n    function burnFrom(address account, uint256 amount) public virtual {\n        uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, \"ERC20: burn amount exceeds allowance\");\n\n        _approve(account, _msgSender(), decreasedAllowance);\n        _burn(account, amount);\n    }\n\n\n    /**\n     * @dev Destroys `amount` tokens from `account`, reducing the\n     * total supply.\n     *\n     * Emits a {Transfer} event with `to` set to the zero address.\n     *\n     * Requirements\n     *\n     * - `account` cannot be the zero address.\n     * - `account` must have at least `amount` tokens.\n     */\n    function _burn(address account, uint256 amount) internal virtual {\n        require(account != address(0), \"ERC20: burn from the zero address\");\n\n        _beforeTokenTransfer(account, address(0), amount);\n\n        _balances[account] = _balances[account].sub(amount, \"ERC20: burn amount exceeds balance\");\n        _totalSupply = _totalSupply.sub(amount);\n        emit Transfer(account, address(0), amount);\n    }\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.\n     *\n     * This is internal function is equivalent to `approve`, and can be used to\n     * e.g. set automatic allowances for certain subsystems, etc.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `owner` cannot be the zero address.\n     * - `spender` cannot be the zero address.\n     */\n    function _approve(address owner, address spender, uint256 amount) internal virtual {\n        require(owner != address(0), \"ERC20: approve from the zero address\");\n        require(spender != address(0), \"ERC20: approve to the zero address\");\n\n        _allowances[owner][spender] = amount;\n        emit Approval(owner, spender, amount);\n    }\n\n    /**\n     * @dev Destroys `amount` tokens from `account`.`amount` is then deducted\n     * from the caller\u0027s allowance.\n     *\n     * See {_burn} and {_approve}.\n     */\n    function _burnFrom(address account, uint256 amount) internal virtual {\n        _burn(account, amount);\n        _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, \"ERC20: burn amount exceeds allowance\"));\n    }\n\n    /**\n     * @dev Hook that is called before any transfer of tokens. This includes\n     * minting and burning.\n     *\n     * Calling conditions:\n     *\n     * - when `from` and `to` are both non-zero, `amount` of `from`\u0027s tokens\n     * will be to transferred to `to`.\n     * - when `from` is zero, `amount` tokens will be minted for `to`.\n     * - when `to` is zero, `amount` of `from`\u0027s tokens will be burned.\n     * - `from` and `to` are never both zero.\n     *\n     * To learn more about hooks, head to xref:ROOT:using-hooks.adoc[Using Hooks].\n     */\n    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }\n}\n"},"ERC20Custom.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \"./Context.sol\";\nimport \"./IERC20.sol\";\nimport \"./SafeMath.sol\";\nimport \"./Address.sol\";\n\n// Due to compiling issues, _name, _symbol, and _decimals were removed\n\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20Mintable}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin guidelines: functions revert instead\n * of returning `false` on failure. This behavior is nonetheless conventional\n * and does not conflict with the expectations of ERC20 applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn\u0027t required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20Custom is Context, IERC20 {\n    using SafeMath for uint256;\n\n    mapping (address =\u003e uint256) internal _balances;\n\n    mapping (address =\u003e mapping (address =\u003e uint256)) internal _allowances;\n\n    uint256 private _totalSupply;\n\n    /**\n     * @dev See {IERC20-totalSupply}.\n     */\n    function totalSupply() public view override returns (uint256) {\n        return _totalSupply;\n    }\n\n    /**\n     * @dev See {IERC20-balanceOf}.\n     */\n    function balanceOf(address account) public view override returns (uint256) {\n        return _balances[account];\n    }\n\n    /**\n     * @dev See {IERC20-transfer}.\n     *\n     * Requirements:\n     *\n     * - `recipient` cannot be the zero address.\n     * - the caller must have a balance of at least `amount`.\n     */\n    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n        _transfer(_msgSender(), recipient, amount);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-allowance}.\n     */\n    function allowance(address owner, address spender) public view virtual override returns (uint256) {\n        return _allowances[owner][spender];\n    }\n\n    /**\n     * @dev See {IERC20-approve}.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.approve(address spender, uint256 amount)\n     */\n    function approve(address spender, uint256 amount) public virtual override returns (bool) {\n        _approve(_msgSender(), spender, amount);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-transferFrom}.\n     *\n     * Emits an {Approval} event indicating the updated allowance. This is not\n     * required by the EIP. See the note at the beginning of {ERC20};\n     *\n     * Requirements:\n     * - `sender` and `recipient` cannot be the zero address.\n     * - `sender` must have a balance of at least `amount`.\n     * - the caller must have allowance for `sender`\u0027s tokens of at least\n     * `amount`.\n     */\n    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {\n        _transfer(sender, recipient, amount);\n        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, \"ERC20: transfer amount exceeds allowance\"));\n        return true;\n    }\n\n    /**\n     * @dev Atomically increases the allowance granted to `spender` by the caller.\n     *\n     * This is an alternative to {approve} that can be used as a mitigation for\n     * problems described in {IERC20-approve}.\n     *\n     * Emits an {Approval} event indicating the updated allowance.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     */\n    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));\n        return true;\n    }\n\n    /**\n     * @dev Atomically decreases the allowance granted to `spender` by the caller.\n     *\n     * This is an alternative to {approve} that can be used as a mitigation for\n     * problems described in {IERC20-approve}.\n     *\n     * Emits an {Approval} event indicating the updated allowance.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `spender` must have allowance for the caller of at least\n     * `subtractedValue`.\n     */\n    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, \"ERC20: decreased allowance below zero\"));\n        return true;\n    }\n\n    /**\n     * @dev Moves tokens `amount` from `sender` to `recipient`.\n     *\n     * This is internal function is equivalent to {transfer}, and can be used to\n     * e.g. implement automatic token fees, slashing mechanisms, etc.\n     *\n     * Emits a {Transfer} event.\n     *\n     * Requirements:\n     *\n     * - `sender` cannot be the zero address.\n     * - `recipient` cannot be the zero address.\n     * - `sender` must have a balance of at least `amount`.\n     */\n    function _transfer(address sender, address recipient, uint256 amount) internal virtual {\n        require(sender != address(0), \"ERC20: transfer from the zero address\");\n        require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n        _beforeTokenTransfer(sender, recipient, amount);\n\n        _balances[sender] = _balances[sender].sub(amount, \"ERC20: transfer amount exceeds balance\");\n        _balances[recipient] = _balances[recipient].add(amount);\n        emit Transfer(sender, recipient, amount);\n    }\n\n    /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n     * the total supply.\n     *\n     * Emits a {Transfer} event with `from` set to the zero address.\n     *\n     * Requirements\n     *\n     * - `to` cannot be the zero address.\n     */\n    function _mint(address account, uint256 amount) internal virtual {\n        require(account != address(0), \"ERC20: mint to the zero address\");\n\n        _beforeTokenTransfer(address(0), account, amount);\n\n        _totalSupply = _totalSupply.add(amount);\n        _balances[account] = _balances[account].add(amount);\n        emit Transfer(address(0), account, amount);\n    }\n\n    /**\n     * @dev Destroys `amount` tokens from the caller.\n     *\n     * See {ERC20-_burn}.\n     */\n    function burn(uint256 amount) public virtual {\n        _burn(_msgSender(), amount);\n    }\n\n    /**\n     * @dev Destroys `amount` tokens from `account`, deducting from the caller\u0027s\n     * allowance.\n     *\n     * See {ERC20-_burn} and {ERC20-allowance}.\n     *\n     * Requirements:\n     *\n     * - the caller must have allowance for `accounts`\u0027s tokens of at least\n     * `amount`.\n     */\n    function burnFrom(address account, uint256 amount) public virtual {\n        uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, \"ERC20: burn amount exceeds allowance\");\n\n        _approve(account, _msgSender(), decreasedAllowance);\n        _burn(account, amount);\n    }\n\n\n    /**\n     * @dev Destroys `amount` tokens from `account`, reducing the\n     * total supply.\n     *\n     * Emits a {Transfer} event with `to` set to the zero address.\n     *\n     * Requirements\n     *\n     * - `account` cannot be the zero address.\n     * - `account` must have at least `amount` tokens.\n     */\n    function _burn(address account, uint256 amount) internal virtual {\n        require(account != address(0), \"ERC20: burn from the zero address\");\n\n        _beforeTokenTransfer(account, address(0), amount);\n\n        _balances[account] = _balances[account].sub(amount, \"ERC20: burn amount exceeds balance\");\n        _totalSupply = _totalSupply.sub(amount);\n        emit Transfer(account, address(0), amount);\n    }\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.\n     *\n     * This is internal function is equivalent to `approve`, and can be used to\n     * e.g. set automatic allowances for certain subsystems, etc.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `owner` cannot be the zero address.\n     * - `spender` cannot be the zero address.\n     */\n    function _approve(address owner, address spender, uint256 amount) internal virtual {\n        require(owner != address(0), \"ERC20: approve from the zero address\");\n        require(spender != address(0), \"ERC20: approve to the zero address\");\n\n        _allowances[owner][spender] = amount;\n        emit Approval(owner, spender, amount);\n    }\n\n    /**\n     * @dev Destroys `amount` tokens from `account`.`amount` is then deducted\n     * from the caller\u0027s allowance.\n     *\n     * See {_burn} and {_approve}.\n     */\n    function _burnFrom(address account, uint256 amount) internal virtual {\n        _burn(account, amount);\n        _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, \"ERC20: burn amount exceeds allowance\"));\n    }\n\n    /**\n     * @dev Hook that is called before any transfer of tokens. This includes\n     * minting and burning.\n     *\n     * Calling conditions:\n     *\n     * - when `from` and `to` are both non-zero, `amount` of `from`\u0027s tokens\n     * will be to transferred to `to`.\n     * - when `from` is zero, `amount` tokens will be minted for `to`.\n     * - when `to` is zero, `amount` of `from`\u0027s tokens will be burned.\n     * - `from` and `to` are never both zero.\n     *\n     * To learn more about hooks, head to xref:ROOT:using-hooks.adoc[Using Hooks].\n     */\n    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }\n}"},"FakeCollateral.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \"./Context.sol\";\nimport \"./IERC20.sol\";\nimport \"./SafeMath.sol\";\nimport \"./Address.sol\";\n\n// Due to compiling issues, _name, _symbol, and _decimals were removed\n\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20Mintable}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin guidelines: functions revert instead\n * of returning `false` on failure. This behavior is nonetheless conventional\n * and does not conflict with the expectations of ERC20 applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn\u0027t required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract FakeCollateral is Context, IERC20 {\n    using SafeMath for uint256;\n    string public symbol;\n    uint8 public decimals;\n    address public creator_address;\n    uint256 public genesis_supply;\n    uint256 private _totalSupply;\n\n    mapping (address =\u003e uint256) private _balances;\n    mapping (address =\u003e mapping (address =\u003e uint256)) private _allowances;\n    mapping (address =\u003e bool) used;\n\n    constructor(\n        address _creator_address,\n        uint256 _genesis_supply,\n        string memory _symbol,\n        uint8 _decimals\n    ) public {\n        genesis_supply = _genesis_supply;\n        creator_address = _creator_address;\n        symbol = _symbol;\n        decimals = _decimals;\n        _mint(creator_address, genesis_supply);\n    }\n\n    function faucet() public {\n    \tif (used[msg.sender] == false) {\n    \t\tused[msg.sender] = true;\n    \t\t_mint(msg.sender, 1000 * (10 ** uint256(decimals)));\n    \t}\n    }\n\n    /**\n     * @dev See {IERC20-totalSupply}.\n     */\n    function totalSupply() public view override returns (uint256) {\n        return _totalSupply;\n    }\n\n    /**\n     * @dev See {IERC20-balanceOf}.\n     */\n    function balanceOf(address account) public view override returns (uint256) {\n        return _balances[account];\n    }\n\n    /**\n     * @dev See {IERC20-transfer}.\n     *\n     * Requirements:\n     *\n     * - `recipient` cannot be the zero address.\n     * - the caller must have a balance of at least `amount`.\n     */\n    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n        _transfer(_msgSender(), recipient, amount);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-allowance}.\n     */\n    function allowance(address owner, address spender) public view virtual override returns (uint256) {\n        return _allowances[owner][spender];\n    }\n\n    /**\n     * @dev See {IERC20-approve}.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.approve(address spender, uint256 amount)\n     */\n    function approve(address spender, uint256 amount) public virtual override returns (bool) {\n        _approve(_msgSender(), spender, amount);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-transferFrom}.\n     *\n     * Emits an {Approval} event indicating the updated allowance. This is not\n     * required by the EIP. See the note at the beginning of {ERC20};\n     *\n     * Requirements:\n     * - `sender` and `recipient` cannot be the zero address.\n     * - `sender` must have a balance of at least `amount`.\n     * - the caller must have allowance for `sender`\u0027s tokens of at least\n     * `amount`.\n     */\n    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {\n        _transfer(sender, recipient, amount);\n        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, \"ERC20: transfer amount exceeds allowance\"));\n        return true;\n    }\n\n    /**\n     * @dev Atomically increases the allowance granted to `spender` by the caller.\n     *\n     * This is an alternative to {approve} that can be used as a mitigation for\n     * problems described in {IERC20-approve}.\n     *\n     * Emits an {Approval} event indicating the updated allowance.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     */\n    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));\n        return true;\n    }\n\n    /**\n     * @dev Atomically decreases the allowance granted to `spender` by the caller.\n     *\n     * This is an alternative to {approve} that can be used as a mitigation for\n     * problems described in {IERC20-approve}.\n     *\n     * Emits an {Approval} event indicating the updated allowance.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `spender` must have allowance for the caller of at least\n     * `subtractedValue`.\n     */\n    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, \"ERC20: decreased allowance below zero\"));\n        return true;\n    }\n\n    /**\n     * @dev Moves tokens `amount` from `sender` to `recipient`.\n     *\n     * This is internal function is equivalent to {transfer}, and can be used to\n     * e.g. implement automatic token fees, slashing mechanisms, etc.\n     *\n     * Emits a {Transfer} event.\n     *\n     * Requirements:\n     *\n     * - `sender` cannot be the zero address.\n     * - `recipient` cannot be the zero address.\n     * - `sender` must have a balance of at least `amount`.\n     */\n    function _transfer(address sender, address recipient, uint256 amount) internal virtual {\n        require(sender != address(0), \"ERC20: transfer from the zero address\");\n        require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n        _beforeTokenTransfer(sender, recipient, amount);\n\n        _balances[sender] = _balances[sender].sub(amount, \"ERC20: transfer amount exceeds balance\");\n        _balances[recipient] = _balances[recipient].add(amount);\n        emit Transfer(sender, recipient, amount);\n    }\n\n    /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n     * the total supply.\n     *\n     * Emits a {Transfer} event with `from` set to the zero address.\n     *\n     * Requirements\n     *\n     * - `to` cannot be the zero address.\n     */\n    function _mint(address account, uint256 amount) internal virtual {\n        require(account != address(0), \"ERC20: mint to the zero address\");\n\n        _beforeTokenTransfer(address(0), account, amount);\n\n        _totalSupply = _totalSupply.add(amount);\n        _balances[account] = _balances[account].add(amount);\n        emit Transfer(address(0), account, amount);\n    }\n\n    /**\n     * @dev Destroys `amount` tokens from the caller.\n     *\n     * See {ERC20-_burn}.\n     */\n    function burn(uint256 amount) public virtual {\n        _burn(_msgSender(), amount);\n    }\n\n    /**\n     * @dev Destroys `amount` tokens from `account`, deducting from the caller\u0027s\n     * allowance.\n     *\n     * See {ERC20-_burn} and {ERC20-allowance}.\n     *\n     * Requirements:\n     *\n     * - the caller must have allowance for `accounts`\u0027s tokens of at least\n     * `amount`.\n     */\n    function burnFrom(address account, uint256 amount) public virtual {\n        uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, \"ERC20: burn amount exceeds allowance\");\n\n        _approve(account, _msgSender(), decreasedAllowance);\n        _burn(account, amount);\n    }\n\n\n    /**\n     * @dev Destroys `amount` tokens from `account`, reducing the\n     * total supply.\n     *\n     * Emits a {Transfer} event with `to` set to the zero address.\n     *\n     * Requirements\n     *\n     * - `account` cannot be the zero address.\n     * - `account` must have at least `amount` tokens.\n     */\n    function _burn(address account, uint256 amount) internal virtual {\n        require(account != address(0), \"ERC20: burn from the zero address\");\n\n        _beforeTokenTransfer(account, address(0), amount);\n\n        _balances[account] = _balances[account].sub(amount, \"ERC20: burn amount exceeds balance\");\n        _totalSupply = _totalSupply.sub(amount);\n        emit Transfer(account, address(0), amount);\n    }\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.\n     *\n     * This is internal function is equivalent to `approve`, and can be used to\n     * e.g. set automatic allowances for certain subsystems, etc.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `owner` cannot be the zero address.\n     * - `spender` cannot be the zero address.\n     */\n    function _approve(address owner, address spender, uint256 amount) internal virtual {\n        require(owner != address(0), \"ERC20: approve from the zero address\");\n        require(spender != address(0), \"ERC20: approve to the zero address\");\n\n        _allowances[owner][spender] = amount;\n        emit Approval(owner, spender, amount);\n    }\n\n    /**\n     * @dev Destroys `amount` tokens from `account`.`amount` is then deducted\n     * from the caller\u0027s allowance.\n     *\n     * See {_burn} and {_approve}.\n     */\n    function _burnFrom(address account, uint256 amount) internal virtual {\n        _burn(account, amount);\n        _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, \"ERC20: burn amount exceeds allowance\"));\n    }\n\n    /**\n     * @dev Hook that is called before any transfer of tokens. This includes\n     * minting and burning.\n     *\n     * Calling conditions:\n     *\n     * - when `from` and `to` are both non-zero, `amount` of `from`\u0027s tokens\n     * will be to transferred to `to`.\n     * - when `from` is zero, `amount` tokens will be minted for `to`.\n     * - when `to` is zero, `amount` of `from`\u0027s tokens will be burned.\n     * - `from` and `to` are never both zero.\n     *\n     * To learn more about hooks, head to xref:ROOT:using-hooks.adoc[Using Hooks].\n     */\n    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }\n}\n"},"FakeCollateral_USDC.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \"./FakeCollateral.sol\";\n\ncontract FakeCollateral_USDC is FakeCollateral {\n    constructor(\n        address _creator_address,\n        uint256 _genesis_supply,\n        string memory _symbol,\n        uint8 _decimals\n    ) \n    FakeCollateral(_creator_address, _genesis_supply, _symbol, _decimals)\n    public {}\n}"},"FakeCollateral_USDT.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \"./FakeCollateral.sol\";\n\ncontract FakeCollateral_USDT is FakeCollateral {\n    constructor(\n        address _creator_address,\n        uint256 _genesis_supply,\n        string memory _symbol,\n        uint8 _decimals\n    ) \n    FakeCollateral(_creator_address, _genesis_supply, _symbol, _decimals)\n    public {}\n}"},"FakeCollateral_WETH.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \"./FakeCollateral.sol\";\n\ncontract FakeCollateral_WETH is FakeCollateral {\n    constructor(\n        address _creator_address,\n        uint256 _genesis_supply,\n        string memory _symbol,\n        uint8 _decimals\n    ) \n    FakeCollateral(_creator_address, _genesis_supply, _symbol, _decimals)\n    public {}\n}"},"FixedPoint.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./Babylonian.sol\u0027;\n\n// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))\nlibrary FixedPoint {\n    // range: [0, 2**112 - 1]\n    // resolution: 1 / 2**112\n    struct uq112x112 {\n        uint224 _x;\n    }\n\n    // range: [0, 2**144 - 1]\n    // resolution: 1 / 2**112\n    struct uq144x112 {\n        uint _x;\n    }\n\n    uint8 private constant RESOLUTION = 112;\n    uint private constant Q112 = uint(1) \u003c\u003c RESOLUTION;\n    uint private constant Q224 = Q112 \u003c\u003c RESOLUTION;\n\n    // encode a uint112 as a UQ112x112\n    function encode(uint112 x) internal pure returns (uq112x112 memory) {\n        return uq112x112(uint224(x) \u003c\u003c RESOLUTION);\n    }\n\n    // encodes a uint144 as a UQ144x112\n    function encode144(uint144 x) internal pure returns (uq144x112 memory) {\n        return uq144x112(uint256(x) \u003c\u003c RESOLUTION);\n    }\n\n    // divide a UQ112x112 by a uint112, returning a UQ112x112\n    function div(uq112x112 memory self, uint112 x) internal pure returns (uq112x112 memory) {\n        require(x != 0, \u0027FixedPoint: DIV_BY_ZERO\u0027);\n        return uq112x112(self._x / uint224(x));\n    }\n\n    // multiply a UQ112x112 by a uint, returning a UQ144x112\n    // reverts on overflow\n    function mul(uq112x112 memory self, uint y) internal pure returns (uq144x112 memory) {\n        uint z;\n        require(y == 0 || (z = uint(self._x) * y) / y == uint(self._x), \"FixedPoint: MULTIPLICATION_OVERFLOW\");\n        return uq144x112(z);\n    }\n\n    // returns a UQ112x112 which represents the ratio of the numerator to the denominator\n    // equivalent to encode(numerator).div(denominator)\n    function fraction(uint112 numerator, uint112 denominator) internal pure returns (uq112x112 memory) {\n        require(denominator \u003e 0, \"FixedPoint: DIV_BY_ZERO\");\n        return uq112x112((uint224(numerator) \u003c\u003c RESOLUTION) / denominator);\n    }\n\n    // decode a UQ112x112 into a uint112 by truncating after the radix point\n    function decode(uq112x112 memory self) internal pure returns (uint112) {\n        return uint112(self._x \u003e\u003e RESOLUTION);\n    }\n\n    // decode a UQ144x112 into a uint144 by truncating after the radix point\n    function decode144(uq144x112 memory self) internal pure returns (uint144) {\n        return uint144(self._x \u003e\u003e RESOLUTION);\n    }\n\n    // take the reciprocal of a UQ112x112\n    function reciprocal(uq112x112 memory self) internal pure returns (uq112x112 memory) {\n        require(self._x != 0, \u0027FixedPoint: ZERO_RECIPROCAL\u0027);\n        return uq112x112(uint224(Q224 / self._x));\n    }\n\n    // square root of a UQ112x112\n    function sqrt(uq112x112 memory self) internal pure returns (uq112x112 memory) {\n        return uq112x112(uint224(Babylonian.sqrt(uint256(self._x)) \u003c\u003c 56));\n    }\n}"},"Frax.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\npragma experimental ABIEncoderV2;\n\nimport \"./Context.sol\";\nimport \"./IERC20.sol\";\nimport \"./ERC20Custom.sol\";\nimport \"./ERC20.sol\";\nimport \"./SafeMath.sol\";\nimport \"./FXS.sol\";\nimport \"./FraxPool.sol\";\nimport \"./UniswapPairOracle.sol\";\nimport \"./ChainlinkETHUSDPriceConsumer.sol\";\nimport \"./AccessControl.sol\";\n\ncontract FRAXStablecoin is ERC20Custom, AccessControl {\n    using SafeMath for uint256;\n\n    /* ========== STATE VARIABLES ========== */\n    enum PriceChoice { FRAX, FXS }\n    ChainlinkETHUSDPriceConsumer private eth_usd_pricer;\n    uint8 private eth_usd_pricer_decimals;\n    UniswapPairOracle private fraxEthOracle;\n    UniswapPairOracle private fxsEthOracle;\n    string public symbol;\n    string public name;\n    uint8 public constant decimals = 18;\n    address public owner_address;\n    address public creator_address;\n    address public timelock_address; // Governance timelock address\n    address public controller_address; // Controller contract to dynamically adjust system parameters automatically\n    address public fxs_address;\n    address public frax_eth_oracle_address;\n    address public fxs_eth_oracle_address;\n    address public weth_address;\n    address public eth_usd_consumer_address;\n    uint256 public constant genesis_supply = 2000000e18; // 2M FRAX (only for testing, genesis supply will be 5k on Mainnet). This is to help with establishing the Uniswap pools, as they need liquidity\n\n    // The addresses in this array are added by the oracle and these contracts are able to mint frax\n    address[] public frax_pools_array;\n\n    // Mapping is also used for faster verification\n    mapping(address =\u003e bool) public frax_pools; \n\n    // Constants for various precisions\n    uint256 private constant PRICE_PRECISION = 1e6;\n    \n    uint256 public global_collateral_ratio; // 6 decimals of precision, e.g. 924102 = 0.924102\n    uint256 public redemption_fee; // 6 decimals of precision, divide by 1000000 in calculations for fee\n    uint256 public minting_fee; // 6 decimals of precision, divide by 1000000 in calculations for fee\n    uint256 public frax_step; // Amount to change the collateralization ratio by upon refreshCollateralRatio()\n    uint256 public refresh_cooldown; // Seconds to wait before being able to run refreshCollateralRatio() again\n    uint256 public price_target; // The price of FRAX at which the collateral ratio will respond to; this value is only used for the collateral ratio mechanism and not for minting and redeeming which are hardcoded at $1\n    uint256 public price_band; // The bound above and below the price target at which the refreshCollateralRatio() will not change the collateral ratio\n\n    address public DEFAULT_ADMIN_ADDRESS;\n    bytes32 public constant COLLATERAL_RATIO_PAUSER = keccak256(\"COLLATERAL_RATIO_PAUSER\");\n    bool public collateral_ratio_paused = false;\n\n    /* ========== MODIFIERS ========== */\n\n    modifier onlyCollateralRatioPauser() {\n        require(hasRole(COLLATERAL_RATIO_PAUSER, msg.sender));\n        _;\n    }\n\n    modifier onlyPools() {\n       require(frax_pools[msg.sender] == true, \"Only frax pools can call this function\");\n        _;\n    } \n    \n    modifier onlyByOwnerOrGovernance() {\n        require(msg.sender == owner_address || msg.sender == timelock_address || msg.sender == controller_address, \"You are not the owner, controller, or the governance timelock\");\n        _;\n    }\n\n    modifier onlyByOwnerGovernanceOrPool() {\n        require(\n            msg.sender == owner_address \n            || msg.sender == timelock_address \n            || frax_pools[msg.sender] == true, \n            \"You are not the owner, the governance timelock, or a pool\");\n        _;\n    }\n\n    /* ========== CONSTRUCTOR ========== */\n\n    constructor(\n        string memory _name,\n        string memory _symbol,\n        address _creator_address,\n        address _timelock_address\n    ) public {\n        name = _name;\n        symbol = _symbol;\n        creator_address = _creator_address;\n        timelock_address = _timelock_address;\n        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());\n        DEFAULT_ADMIN_ADDRESS = _msgSender();\n        owner_address = _creator_address;\n        _mint(creator_address, genesis_supply);\n        grantRole(COLLATERAL_RATIO_PAUSER, creator_address);\n        grantRole(COLLATERAL_RATIO_PAUSER, timelock_address);\n        frax_step = 2500; // 6 decimals of precision, equal to 0.25%\n        global_collateral_ratio = 1000000; // Frax system starts off fully collateralized (6 decimals of precision)\n        refresh_cooldown = 3600; // Refresh cooldown period is set to 1 hour (3600 seconds) at genesis\n        price_target = 1000000; // Collateral ratio will adjust according to the $1 price target at genesis\n        price_band = 5000; // Collateral ratio will not adjust if between $0.995 and $1.005 at genesis\n    }\n\n    /* ========== VIEWS ========== */\n\n    // Choice = \u0027FRAX\u0027 or \u0027FXS\u0027 for now\n    function oracle_price(PriceChoice choice) internal view returns (uint256) {\n        // Get the ETH / USD price first, and cut it down to 1e6 precision\n        uint256 eth_usd_price = uint256(eth_usd_pricer.getLatestPrice()).mul(PRICE_PRECISION).div(uint256(10) ** eth_usd_pricer_decimals);\n        uint256 price_vs_eth;\n\n        if (choice == PriceChoice.FRAX) {\n            price_vs_eth = uint256(fraxEthOracle.consult(weth_address, PRICE_PRECISION)); // How much FRAX if you put in PRICE_PRECISION WETH\n        }\n        else if (choice == PriceChoice.FXS) {\n            price_vs_eth = uint256(fxsEthOracle.consult(weth_address, PRICE_PRECISION)); // How much FXS if you put in PRICE_PRECISION WETH\n        }\n        else revert(\"INVALID PRICE CHOICE. Needs to be either 0 (FRAX) or 1 (FXS)\");\n\n        // Will be in 1e6 format\n        return eth_usd_price.mul(PRICE_PRECISION).div(price_vs_eth);\n    }\n\n    // Returns X FRAX = 1 USD\n    function frax_price() public view returns (uint256) {\n        return oracle_price(PriceChoice.FRAX);\n    }\n\n    // Returns X FXS = 1 USD\n    function fxs_price()  public view returns (uint256) {\n        return oracle_price(PriceChoice.FXS);\n    }\n\n    function eth_usd_price() public view returns (uint256) {\n        return uint256(eth_usd_pricer.getLatestPrice()).mul(PRICE_PRECISION).div(uint256(10) ** eth_usd_pricer_decimals);\n    }\n\n    // This is needed to avoid costly repeat calls to different getter functions\n    // It is cheaper gas-wise to just dump everything and only use some of the info\n    function frax_info() public view returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256) {\n        return (\n            oracle_price(PriceChoice.FRAX), // frax_price()\n            oracle_price(PriceChoice.FXS), // fxs_price()\n            totalSupply(), // totalSupply()\n            global_collateral_ratio, // global_collateral_ratio()\n            globalCollateralValue(), // globalCollateralValue\n            minting_fee, // minting_fee()\n            redemption_fee, // redemption_fee()\n            uint256(eth_usd_pricer.getLatestPrice()).mul(PRICE_PRECISION).div(uint256(10) ** eth_usd_pricer_decimals) //eth_usd_price\n        );\n    }\n\n    // Iterate through all frax pools and calculate all value of collateral in all pools globally \n    function globalCollateralValue() public view returns (uint256) {\n        uint256 total_collateral_value_d18 = 0; \n\n        for (uint i = 0; i \u003c frax_pools_array.length; i++){ \n            // Exclude null addresses\n            if (frax_pools_array[i] != address(0)){\n                total_collateral_value_d18 = total_collateral_value_d18.add(FraxPool(frax_pools_array[i]).collatDollarBalance());\n            }\n\n        }\n        return total_collateral_value_d18;\n    }\n\n    /* ========== PUBLIC FUNCTIONS ========== */\n    \n    // There needs to be a time interval that this can be called. Otherwise it can be called multiple times per expansion.\n    uint256 public last_call_time; // Last time the refreshCollateralRatio function was called\n    function refreshCollateralRatio() public {\n        require(collateral_ratio_paused == false, \"Collateral Ratio has been paused\");\n        uint256 frax_price_cur = frax_price();\n        require(block.timestamp - last_call_time \u003e= refresh_cooldown, \"Must wait for the refresh cooldown since last refresh\");\n\n        // Step increments are 0.25% (upon genesis, changable by setFraxStep()) \n        \n        if (frax_price_cur \u003e price_target.add(price_band)) { //decrease collateral ratio\n            if(global_collateral_ratio \u003c= frax_step){ //if within a step of 0, go to 0\n                global_collateral_ratio = 0;\n            } else {\n                global_collateral_ratio = global_collateral_ratio.sub(frax_step);\n            }\n        } else if (frax_price_cur \u003c price_target.sub(price_band)) { //increase collateral ratio\n            if(global_collateral_ratio.add(frax_step) \u003e= 1000000){\n                global_collateral_ratio = 1000000; // cap collateral ratio at 1.000000\n            } else {\n                global_collateral_ratio = global_collateral_ratio.add(frax_step);\n            }\n        }\n\n        last_call_time = block.timestamp; // Set the time of the last expansion\n    }\n\n    /* ========== RESTRICTED FUNCTIONS ========== */\n\n    // Used by pools when user redeems\n    function pool_burn_from(address b_address, uint256 b_amount) public onlyPools {\n        super._burnFrom(b_address, b_amount);\n        emit FRAXBurned(b_address, msg.sender, b_amount);\n    }\n\n    // This function is what other frax pools will call to mint new FRAX \n    function pool_mint(address m_address, uint256 m_amount) public onlyPools {\n        super._mint(m_address, m_amount);\n        emit FRAXMinted(msg.sender, m_address, m_amount);\n    }\n\n    // Adds collateral addresses supported, such as tether and busd, must be ERC20 \n    function addPool(address pool_address) public onlyByOwnerOrGovernance {\n        require(frax_pools[pool_address] == false, \"address already exists\");\n        frax_pools[pool_address] = true; \n        frax_pools_array.push(pool_address);\n    }\n\n    // Remove a pool \n    function removePool(address pool_address) public onlyByOwnerOrGovernance {\n        require(frax_pools[pool_address] == true, \"address doesn\u0027t exist already\");\n        \n        // Delete from the mapping\n        delete frax_pools[pool_address];\n\n        // \u0027Delete\u0027 from the array by setting the address to 0x0\n        for (uint i = 0; i \u003c frax_pools_array.length; i++){ \n            if (frax_pools_array[i] == pool_address) {\n                frax_pools_array[i] = address(0); // This will leave a null in the array and keep the indices the same\n                break;\n            }\n        }\n    }\n\n    function setOwner(address _owner_address) external onlyByOwnerOrGovernance {\n        owner_address = _owner_address;\n    }\n\n    function setRedemptionFee(uint256 red_fee) public onlyByOwnerOrGovernance {\n        redemption_fee = red_fee;\n    }\n\n    function setMintingFee(uint256 min_fee) public onlyByOwnerOrGovernance {\n        minting_fee = min_fee;\n    }  \n\n    function setFraxStep(uint256 _new_step) public onlyByOwnerOrGovernance {\n        frax_step = _new_step;\n    }  \n\n    function setPriceTarget (uint256 _new_price_target) public onlyByOwnerOrGovernance {\n        price_target = _new_price_target;\n    }\n\n    function setRefreshCooldown(uint256 _new_cooldown) public onlyByOwnerOrGovernance {\n    \trefresh_cooldown = _new_cooldown;\n    }\n\n    function setFXSAddress(address _fxs_address) public onlyByOwnerOrGovernance {\n        fxs_address = _fxs_address;\n    }\n\n    function setETHUSDOracle(address _eth_usd_consumer_address) public onlyByOwnerOrGovernance {\n        eth_usd_consumer_address = _eth_usd_consumer_address;\n        eth_usd_pricer = ChainlinkETHUSDPriceConsumer(eth_usd_consumer_address);\n        eth_usd_pricer_decimals = eth_usd_pricer.getDecimals();\n    }\n\n    function setTimelock(address new_timelock) external onlyByOwnerOrGovernance {\n        timelock_address = new_timelock;\n    }\n\n    function setController(address _controller_address) external onlyByOwnerOrGovernance {\n        controller_address = _controller_address;\n    }\n\n    function setPriceBand(uint256 _price_band) external onlyByOwnerOrGovernance {\n        price_band = _price_band;\n    }\n\n    // Sets the FRAX_ETH Uniswap oracle address \n    function setFRAXEthOracle(address _frax_oracle_addr, address _weth_address) public onlyByOwnerOrGovernance {\n        frax_eth_oracle_address = _frax_oracle_addr;\n        fraxEthOracle = UniswapPairOracle(_frax_oracle_addr); \n        weth_address = _weth_address;\n    }\n\n    // Sets the FXS_ETH Uniswap oracle address \n    function setFXSEthOracle(address _fxs_oracle_addr, address _weth_address) public onlyByOwnerOrGovernance {\n        fxs_eth_oracle_address = _fxs_oracle_addr;\n        fxsEthOracle = UniswapPairOracle(_fxs_oracle_addr);\n        weth_address = _weth_address;\n    }\n\n    function toggleCollateralRatio() public onlyCollateralRatioPauser {\n        collateral_ratio_paused = !collateral_ratio_paused;\n    }\n\n    /* ========== EVENTS ========== */\n\n    // Track FRAX burned\n    event FRAXBurned(address indexed from, address indexed to, uint256 amount);\n\n    // Track FRAX minted\n    event FRAXMinted(address indexed from, address indexed to, uint256 amount);\n}\n"},"FraxPool.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\npragma experimental ABIEncoderV2;\n\nimport \"./SafeMath.sol\";\nimport \"./FXS.sol\";\nimport \"./Frax.sol\";\nimport \"./ERC20.sol\";\n// import \u0027../../Uniswap/TransferHelper.sol\u0027;\nimport \"./UniswapPairOracle.sol\";\nimport \"./AccessControl.sol\";\n// import \"../../Utils/StringHelpers.sol\";\nimport \"./FraxPoolLibrary.sol\";\n\n/*\n   Same as FraxPool.sol, but has some gas optimizations\n*/\n\n\ncontract FraxPool is AccessControl {\n    using SafeMath for uint256;\n\n    /* ========== STATE VARIABLES ========== */\n\n    ERC20 private collateral_token;\n    address private collateral_address;\n    address private owner_address;\n    // address private oracle_address;\n    address private frax_contract_address;\n    address private fxs_contract_address;\n    address private timelock_address; // Timelock address for the governance contract\n    FRAXShares private FXS;\n    FRAXStablecoin private FRAX;\n    // UniswapPairOracle private oracle;\n    UniswapPairOracle private collatEthOracle;\n    address private collat_eth_oracle_address;\n    address private weth_address;\n\n    uint256 private minting_fee;\n    uint256 private redemption_fee;\n\n    mapping (address =\u003e uint256) public redeemFXSBalances;\n    mapping (address =\u003e uint256) public redeemCollateralBalances;\n    uint256 public unclaimedPoolCollateral;\n    uint256 public unclaimedPoolFXS;\n    mapping (address =\u003e uint256) public lastRedeemed;\n\n    // Constants for various precisions\n    uint256 private constant PRICE_PRECISION = 1e6;\n    uint256 private constant COLLATERAL_RATIO_PRECISION = 1e6;\n    uint256 private constant COLLATERAL_RATIO_MAX = 1e6;\n\n    // Number of decimals needed to get to 18\n    uint256 private missing_decimals;\n    \n    // Pool_ceiling is the total units of collateral that a pool contract can hold\n    uint256 public pool_ceiling = 0;\n\n    // Stores price of the collateral, if price is paused\n    uint256 public pausedPrice = 0;\n\n    // Bonus rate on FXS minted during recollateralizeFRAX(); 6 decimals of precision, set to 0.75% on genesis\n    uint256 public bonus_rate = 7500;\n\n    // Number of blocks to wait before being able to collectRedemption()\n    uint256 public redemption_delay = 1;\n\n    // AccessControl Roles\n    bytes32 private constant MINT_PAUSER = keccak256(\"MINT_PAUSER\");\n    bytes32 private constant REDEEM_PAUSER = keccak256(\"REDEEM_PAUSER\");\n    bytes32 private constant BUYBACK_PAUSER = keccak256(\"BUYBACK_PAUSER\");\n    bytes32 private constant RECOLLATERALIZE_PAUSER = keccak256(\"RECOLLATERALIZE_PAUSER\");\n    bytes32 private constant COLLATERAL_PRICE_PAUSER = keccak256(\"COLLATERAL_PRICE_PAUSER\");\n    \n    // AccessControl state variables\n    bool private mintPaused = false;\n    bool private redeemPaused = false;\n    bool private recollateralizePaused = false;\n    bool private buyBackPaused = false;\n    bool private collateralPricePaused = false;\n\n    /* ========== MODIFIERS ========== */\n\n    modifier onlyByOwnerOrGovernance() {\n        require(msg.sender == timelock_address || msg.sender == owner_address, \"You are not the owner or the governance timelock\");\n        _;\n    }\n\n    modifier notRedeemPaused() {\n        require(redeemPaused == false, \"Redeeming is paused\");\n        _;\n    }\n\n    modifier notMintPaused() {\n        require(mintPaused == false, \"Minting is paused\");\n        _;\n    }\n \n    /* ========== CONSTRUCTOR ========== */\n    \n    constructor(\n        address _frax_contract_address,\n        address _fxs_contract_address,\n        address _collateral_address,\n        address _creator_address,\n        address _timelock_address,\n        uint256 _pool_ceiling\n    ) public {\n        FRAX = FRAXStablecoin(_frax_contract_address);\n        FXS = FRAXShares(_fxs_contract_address);\n        frax_contract_address = _frax_contract_address;\n        fxs_contract_address = _fxs_contract_address;\n        collateral_address = _collateral_address;\n        timelock_address = _timelock_address;\n        owner_address = _creator_address;\n        collateral_token = ERC20(_collateral_address);\n        pool_ceiling = _pool_ceiling;\n        missing_decimals = uint(18).sub(collateral_token.decimals());\n\n        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());\n        grantRole(MINT_PAUSER, timelock_address);\n        grantRole(REDEEM_PAUSER, timelock_address);\n        grantRole(RECOLLATERALIZE_PAUSER, timelock_address);\n        grantRole(BUYBACK_PAUSER, timelock_address);\n        grantRole(COLLATERAL_PRICE_PAUSER, timelock_address);\n    }\n\n    /* ========== VIEWS ========== */\n\n    // Returns dollar value of collateral held in this Frax pool\n    function collatDollarBalance() public view returns (uint256) {\n        uint256 eth_usd_price = FRAX.eth_usd_price();\n        uint256 eth_collat_price = collatEthOracle.consult(weth_address, (PRICE_PRECISION * (10 ** missing_decimals)));\n\n        uint256 collat_usd_price = eth_usd_price.mul(PRICE_PRECISION).div(eth_collat_price);\n        return (collateral_token.balanceOf(address(this)).sub(unclaimedPoolCollateral)).mul(10 ** missing_decimals).mul(collat_usd_price).div(PRICE_PRECISION); //.mul(getCollateralPrice()).div(1e6);    \n    }\n\n    // Returns the value of excess collateral held in this Frax pool, compared to what is needed to maintain the global collateral ratio\n    function availableExcessCollatDV() public view returns (uint256) {\n        uint256 total_supply = FRAX.totalSupply();\n        uint256 global_collateral_ratio = FRAX.global_collateral_ratio();\n        uint256 global_collat_value = FRAX.globalCollateralValue();\n\n        if (global_collateral_ratio \u003e COLLATERAL_RATIO_PRECISION) global_collateral_ratio = COLLATERAL_RATIO_PRECISION; // Handles an overcollateralized contract with CR \u003e 1\n        uint256 required_collat_dollar_value_d18 = (total_supply.mul(global_collateral_ratio)).div(COLLATERAL_RATIO_PRECISION); // Calculates collateral needed to back each 1 FRAX with $1 of collateral at current collat ratio\n        if (global_collat_value \u003e required_collat_dollar_value_d18) return global_collat_value.sub(required_collat_dollar_value_d18);\n        else return 0;\n    }\n\n    /* ========== PUBLIC FUNCTIONS ========== */\n    \n    // Returns the price of the pool collateral in USD\n    function getCollateralPrice() public view returns (uint256) {\n        if(collateralPricePaused == true){\n            return pausedPrice;\n        } else {\n            uint256 eth_usd_price = FRAX.eth_usd_price();\n            return eth_usd_price.mul(PRICE_PRECISION).div(collatEthOracle.consult(weth_address, PRICE_PRECISION * (10 ** missing_decimals)));\n        }\n    }\n\n    function setCollatETHOracle(address _collateral_weth_oracle_address, address _weth_address) external onlyByOwnerOrGovernance {\n        collat_eth_oracle_address = _collateral_weth_oracle_address;\n        collatEthOracle = UniswapPairOracle(_collateral_weth_oracle_address);\n        weth_address = _weth_address;\n    }\n\n    // We separate out the 1t1, fractional and algorithmic minting functions for gas efficiency \n    function mint1t1FRAX(uint256 collateral_amount, uint256 FRAX_out_min) external notMintPaused {\n        uint256 collateral_amount_d18 = collateral_amount * (10 ** missing_decimals);\n        uint256 global_collateral_ratio = FRAX.global_collateral_ratio();\n\n        require(global_collateral_ratio \u003e= COLLATERAL_RATIO_MAX, \"Collateral ratio must be \u003e= 1\");\n        require((collateral_token.balanceOf(address(this))).sub(unclaimedPoolCollateral).add(collateral_amount) \u003c= pool_ceiling, \"[Pool\u0027s Closed]: Ceiling reached\");\n        \n        (uint256 frax_amount_d18) = FraxPoolLibrary.calcMint1t1FRAX(\n            getCollateralPrice(),\n            minting_fee,\n            collateral_amount_d18\n        ); //1 FRAX for each $1 worth of collateral\n\n        require(FRAX_out_min \u003c= frax_amount_d18, \"Slippage limit reached\");\n        collateral_token.transferFrom(msg.sender, address(this), collateral_amount);\n        FRAX.pool_mint(msg.sender, frax_amount_d18);\n    }\n\n    // 0% collateral-backed\n    function mintAlgorithmicFRAX(uint256 fxs_amount_d18, uint256 FRAX_out_min) external notMintPaused {\n        uint256 fxs_price = FRAX.fxs_price();\n        uint256 global_collateral_ratio = FRAX.global_collateral_ratio();\n        require(global_collateral_ratio == 0, \"Collateral ratio must be 0\");\n        \n        (uint256 frax_amount_d18) = FraxPoolLibrary.calcMintAlgorithmicFRAX(\n            minting_fee, \n            fxs_price, // X FXS / 1 USD\n            fxs_amount_d18\n        );\n\n        require(FRAX_out_min \u003c= frax_amount_d18, \"Slippage limit reached\");\n        FXS.pool_burn_from(msg.sender, fxs_amount_d18);\n        FRAX.pool_mint(msg.sender, frax_amount_d18);\n    }\n\n    // Will fail if fully collateralized or fully algorithmic\n    // \u003e 0% and \u003c 100% collateral-backed\n    function mintFractionalFRAX(uint256 collateral_amount, uint256 fxs_amount, uint256 FRAX_out_min) external notMintPaused {\n        uint256 frax_price = FRAX.frax_price();\n        uint256 fxs_price = FRAX.fxs_price();\n        uint256 global_collateral_ratio = FRAX.global_collateral_ratio();\n\n        require(global_collateral_ratio \u003c COLLATERAL_RATIO_MAX \u0026\u0026 global_collateral_ratio \u003e 0, \"Collateral ratio needs to be between .000001 and .999999\");\n        require(collateral_token.balanceOf(address(this)).sub(unclaimedPoolCollateral).add(collateral_amount) \u003c= pool_ceiling, \"Pool ceiling reached, no more FRAX can be minted with this collateral\");\n\n        uint256 collateral_amount_d18 = collateral_amount * (10 ** missing_decimals);\n        FraxPoolLibrary.MintFF_Params memory input_params = FraxPoolLibrary.MintFF_Params(\n            minting_fee, \n            fxs_price,\n            frax_price,\n            getCollateralPrice(),\n            fxs_amount,\n            collateral_amount_d18,\n            (collateral_token.balanceOf(address(this)).sub(unclaimedPoolCollateral)),\n            pool_ceiling,\n            global_collateral_ratio\n        );\n\n        (uint256 mint_amount, uint256 fxs_needed) = FraxPoolLibrary.calcMintFractionalFRAX(input_params);\n\n        require(FRAX_out_min \u003c= mint_amount, \"Slippage limit reached\");\n        require(fxs_needed \u003c= fxs_amount, \"Not enough FXS inputted\");\n        FXS.pool_burn_from(msg.sender, fxs_needed);\n        collateral_token.transferFrom(msg.sender, address(this), collateral_amount);\n        FRAX.pool_mint(msg.sender, mint_amount);\n    }\n\n    // Redeem collateral. 100% collateral-backed\n    function redeem1t1FRAX(uint256 FRAX_amount, uint256 COLLATERAL_out_min) external notRedeemPaused {\n        uint256 global_collateral_ratio = FRAX.global_collateral_ratio();\n        require(global_collateral_ratio == COLLATERAL_RATIO_MAX, \"Collateral ratio must be == 1\");\n\n        // Need to adjust for decimals of collateral\n        uint256 FRAX_amount_precision = FRAX_amount.div(10 ** missing_decimals);\n        (uint256 collateral_needed) = FraxPoolLibrary.calcRedeem1t1FRAX(\n            getCollateralPrice(),\n            FRAX_amount_precision,\n            redemption_fee\n        );\n\n        require(collateral_needed \u003c= collateral_token.balanceOf(address(this)).sub(unclaimedPoolCollateral), \"Not enough collateral in pool\");\n\n        redeemCollateralBalances[msg.sender] = redeemCollateralBalances[msg.sender].add(collateral_needed);\n        unclaimedPoolCollateral = unclaimedPoolCollateral.add(collateral_needed);\n        lastRedeemed[msg.sender] = block.number;\n\n        require(COLLATERAL_out_min \u003c= collateral_needed, \"Slippage limit reached\");\n        \n        // Move all external functions to the end\n        FRAX.pool_burn_from(msg.sender, FRAX_amount);\n    }\n\n    // Will fail if fully collateralized or algorithmic\n    // Redeem FRAX for collateral and FXS. \u003e 0% and \u003c 100% collateral-backed\n    function redeemFractionalFRAX(uint256 FRAX_amount, uint256 FXS_out_min, uint256 COLLATERAL_out_min) external notRedeemPaused {\n        uint256 fxs_price = FRAX.fxs_price();\n        uint256 global_collateral_ratio = FRAX.global_collateral_ratio();\n\n        require(global_collateral_ratio \u003c COLLATERAL_RATIO_MAX \u0026\u0026 global_collateral_ratio \u003e 0, \"Collateral ratio needs to be between .000001 and .999999\");\n        uint256 col_price_usd = getCollateralPrice();\n\n        uint256 FRAX_amount_post_fee = FRAX_amount.sub((FRAX_amount.mul(redemption_fee)).div(PRICE_PRECISION));\n        uint256 fxs_dollar_value_d18 = FRAX_amount_post_fee.sub(FRAX_amount_post_fee.mul(global_collateral_ratio).div(PRICE_PRECISION));\n        uint256 fxs_amount = fxs_dollar_value_d18.mul(PRICE_PRECISION).div(fxs_price);\n\n        // Need to adjust for decimals of collateral\n        uint256 FRAX_amount_precision = FRAX_amount_post_fee.div(10 ** missing_decimals);\n        uint256 collateral_dollar_value = FRAX_amount_precision.mul(global_collateral_ratio).div(PRICE_PRECISION);\n        uint256 collateral_amount = collateral_dollar_value.mul(PRICE_PRECISION).div(col_price_usd);\n\n        redeemCollateralBalances[msg.sender] = redeemCollateralBalances[msg.sender].add(collateral_amount);\n        unclaimedPoolCollateral = unclaimedPoolCollateral.add(collateral_amount);\n\n        redeemFXSBalances[msg.sender] = redeemFXSBalances[msg.sender].add(fxs_amount);\n        unclaimedPoolFXS = unclaimedPoolFXS.add(fxs_amount);\n\n        lastRedeemed[msg.sender] = block.number;\n\n        require(collateral_amount \u003c= collateral_token.balanceOf(address(this)).sub(unclaimedPoolCollateral), \"Not enough collateral in pool\");\n        require(COLLATERAL_out_min \u003c= collateral_amount, \"Slippage limit reached [collateral]\");\n        require(FXS_out_min \u003c= fxs_amount, \"Slippage limit reached [FXS]\");\n        \n        // Move all external functions to the end\n        FRAX.pool_burn_from(msg.sender, FRAX_amount);\n        FXS.pool_mint(address(this), fxs_amount);\n    }\n\n    // Redeem FRAX for FXS. 0% collateral-backed\n    function redeemAlgorithmicFRAX(uint256 FRAX_amount, uint256 FXS_out_min) external notRedeemPaused {\n        uint256 fxs_price = FRAX.fxs_price();\n        uint256 global_collateral_ratio = FRAX.global_collateral_ratio();\n\n        require(global_collateral_ratio == 0, \"Collateral ratio must be 0\"); \n        uint256 fxs_dollar_value_d18 = FRAX_amount;\n        fxs_dollar_value_d18 = fxs_dollar_value_d18.sub((fxs_dollar_value_d18.mul(redemption_fee)).div(PRICE_PRECISION)); //apply redemption fee\n\n        uint256 fxs_amount = fxs_dollar_value_d18.mul(PRICE_PRECISION).div(fxs_price);\n        \n        redeemFXSBalances[msg.sender] = redeemFXSBalances[msg.sender].add(fxs_amount);\n        unclaimedPoolFXS = unclaimedPoolFXS.add(fxs_amount);\n        \n        lastRedeemed[msg.sender] = block.number;\n        \n        require(FXS_out_min \u003c= fxs_amount, \"Slippage limit reached\");\n        // Move all external functions to the end\n        FRAX.pool_burn_from(msg.sender, FRAX_amount);\n        FXS.pool_mint(address(this), fxs_amount);\n    }\n\n    // After a redemption happens, transfer the newly minted FXS and owed collateral from this pool\n    // contract to the user. Redemption is split into two functions to prevent flash loans from being able\n    // to take out FRAX/collateral from the system, use an AMM to trade the new price, and then mint back into the system.\n    function collectRedemption() external {\n        require((lastRedeemed[msg.sender].add(redemption_delay)) \u003c= block.number, \"Must wait for redemption_delay blocks before collecting redemption\");\n        bool sendFXS = false;\n        bool sendCollateral = false;\n        uint FXSAmount;\n        uint CollateralAmount;\n\n        // Use Checks-Effects-Interactions pattern\n        if(redeemFXSBalances[msg.sender] \u003e 0){\n            FXSAmount = redeemFXSBalances[msg.sender];\n            redeemFXSBalances[msg.sender] = 0;\n            unclaimedPoolFXS = unclaimedPoolFXS.sub(FXSAmount);\n\n            sendFXS = true;\n        }\n        \n        if(redeemCollateralBalances[msg.sender] \u003e 0){\n            CollateralAmount = redeemCollateralBalances[msg.sender];\n            redeemCollateralBalances[msg.sender] = 0;\n            unclaimedPoolCollateral = unclaimedPoolCollateral.sub(CollateralAmount);\n\n            sendCollateral = true;\n        }\n\n        if(sendFXS == true){\n            FXS.transfer(msg.sender, FXSAmount);\n        }\n        if(sendCollateral == true){\n            collateral_token.transfer(msg.sender, CollateralAmount);\n        }\n    }\n\n\n    // When the protocol is recollateralizing, we need to give a discount of FXS to hit the new CR target\n    // Thus, if the target collateral ratio is higher than the actual value of collateral, minters get FXS for adding collateral\n    // This function simply rewards anyone that sends collateral to a pool with the same amount of FXS + the bonus rate\n    // Anyone can call this function to recollateralize the protocol and take the extra FXS value from the bonus rate as an arb opportunity\n    function recollateralizeFRAX(uint256 collateral_amount, uint256 FXS_out_min) external {\n        require(recollateralizePaused == false, \"Recollateralize is paused\");\n        uint256 collateral_amount_d18 = collateral_amount * (10 ** missing_decimals);\n        uint256 fxs_price = FRAX.fxs_price();\n        uint256 frax_total_supply = FRAX.totalSupply();\n        uint256 global_collateral_ratio = FRAX.global_collateral_ratio();\n        uint256 global_collat_value = FRAX.globalCollateralValue();\n        \n        (uint256 collateral_units, uint256 amount_to_recollat) = FraxPoolLibrary.calcRecollateralizeFRAXInner(\n            collateral_amount_d18,\n            getCollateralPrice(),\n            global_collat_value,\n            frax_total_supply,\n            global_collateral_ratio\n        ); \n\n        uint256 collateral_units_precision = collateral_units.div(10 ** missing_decimals);\n\n        uint256 fxs_paid_back = amount_to_recollat.mul(uint(1e6).add(bonus_rate)).div(fxs_price);\n\n        require(FXS_out_min \u003c= fxs_paid_back, \"Slippage limit reached\");\n        collateral_token.transferFrom(msg.sender, address(this), collateral_units_precision);\n        FXS.pool_mint(msg.sender, fxs_paid_back);\n        \n    }\n\n    // Function can be called by an FXS holder to have the protocol buy back FXS with excess collateral value from a desired collateral pool\n    // This can also happen if the collateral ratio \u003e 1\n    function buyBackFXS(uint256 FXS_amount, uint256 COLLATERAL_out_min) external {\n        require(buyBackPaused == false, \"Buyback is paused\");\n        uint256 fxs_price = FRAX.fxs_price();\n        \n        FraxPoolLibrary.BuybackFXS_Params memory input_params = FraxPoolLibrary.BuybackFXS_Params(\n            availableExcessCollatDV(),\n            fxs_price,\n            getCollateralPrice(),\n            FXS_amount\n        );\n\n        (uint256 collateral_equivalent_d18) = FraxPoolLibrary.calcBuyBackFXS(input_params);\n        uint256 collateral_precision = collateral_equivalent_d18.div(10 ** missing_decimals);\n\n        require(COLLATERAL_out_min \u003c= collateral_precision, \"Slippage limit reached\");\n        // Give the sender their desired collateral and burn the FXS\n        FXS.pool_burn_from(msg.sender, FXS_amount);\n        collateral_token.transfer(msg.sender, collateral_precision);\n    }\n\n    /* ========== RESTRICTED FUNCTIONS ========== */\n\n    function toggleMinting() external {\n        require(hasRole(MINT_PAUSER, msg.sender));\n        mintPaused = !mintPaused;\n    }\n    \n    function toggleRedeeming() external {\n        require(hasRole(REDEEM_PAUSER, msg.sender));\n        redeemPaused = !redeemPaused;\n    }\n\n    function toggleRecollateralize() external {\n        require(hasRole(RECOLLATERALIZE_PAUSER, msg.sender));\n        recollateralizePaused = !recollateralizePaused;\n    }\n    \n    function toggleBuyBack() external {\n        require(hasRole(BUYBACK_PAUSER, msg.sender));\n        buyBackPaused = !buyBackPaused;\n    }\n\n    function toggleCollateralPrice() external {\n        require(hasRole(COLLATERAL_PRICE_PAUSER, msg.sender));\n        // If pausing, set paused price; else if unpausing, clear pausedPrice\n        if(collateralPricePaused == false){\n            pausedPrice = getCollateralPrice();\n        } else {\n            pausedPrice = 0;\n        }\n        collateralPricePaused = !collateralPricePaused;\n    }\n\n    // Combined into one function due to 24KiB contract memory limit\n    function setPoolParameters(uint256 new_ceiling, uint256 new_bonus_rate, uint256 new_redemption_delay) external onlyByOwnerOrGovernance {\n        pool_ceiling = new_ceiling;\n        bonus_rate = new_bonus_rate;\n        redemption_delay = new_redemption_delay;\n        minting_fee = FRAX.minting_fee();\n        redemption_fee = FRAX.redemption_fee();\n    }\n\n    function setTimelock(address new_timelock) external onlyByOwnerOrGovernance {\n        timelock_address = new_timelock;\n    }\n\n    function setOwner(address _owner_address) external onlyByOwnerOrGovernance {\n        owner_address = _owner_address;\n    }\n\n    /* ========== EVENTS ========== */\n\n}"},"FraxPoolLibrary.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.6.0;\npragma experimental ABIEncoderV2;\n\nimport \"./SafeMath.sol\";\n\n\n\nlibrary FraxPoolLibrary {\n    using SafeMath for uint256;\n\n    // Constants for various precisions\n    uint256 private constant PRICE_PRECISION = 1e6;\n\n    // ================ Structs ================\n    // Needed to lower stack size\n    struct MintFF_Params {\n        uint256 mint_fee; \n        uint256 fxs_price_usd; \n        uint256 frax_price_usd; \n        uint256 col_price_usd;\n        uint256 fxs_amount;\n        uint256 collateral_amount;\n        uint256 collateral_token_balance;\n        uint256 pool_ceiling;\n        uint256 col_ratio;\n    }\n\n    struct BuybackFXS_Params {\n        uint256 excess_collateral_dollar_value_d18;\n        uint256 fxs_price_usd;\n        uint256 col_price_usd;\n        uint256 FXS_amount;\n    }\n\n    // ================ Functions ================\n\n    function calcMint1t1FRAX(uint256 col_price, uint256 mint_fee, uint256 collateral_amount_d18) public pure returns (uint256) {\n        uint256 col_price_usd = col_price;\n        uint256 c_dollar_value_d18 = (collateral_amount_d18.mul(col_price_usd)).div(1e6);\n        return c_dollar_value_d18.sub((c_dollar_value_d18.mul(mint_fee)).div(1e6));\n    }\n\n    function calcMintAlgorithmicFRAX(uint256 mint_fee, uint256 fxs_price_usd, uint256 fxs_amount_d18) public pure returns (uint256) {\n        uint256 fxs_dollar_value_d18 = fxs_amount_d18.mul(fxs_price_usd).div(1e6);\n        return fxs_dollar_value_d18.sub((fxs_dollar_value_d18.mul(mint_fee)).div(1e6));\n    }\n\n    // Must be internal because of the struct\n    function calcMintFractionalFRAX(MintFF_Params memory params) internal pure returns (uint256, uint256) {\n        // Since solidity truncates division, every division operation must be the last operation in the equation to ensure minimum error\n        // The contract must check the proper ratio was sent to mint FRAX. We do this by seeing the minimum mintable FRAX based on each amount \n        uint256 fxs_dollar_value_d18;\n        uint256 c_dollar_value_d18;\n        \n        // Scoping for stack concerns\n        {    \n            // USD amounts of the collateral and the FXS\n            fxs_dollar_value_d18 = params.fxs_amount.mul(params.fxs_price_usd).div(1e6);\n            c_dollar_value_d18 = params.collateral_amount.mul(params.col_price_usd).div(1e6);\n\n        }\n        uint calculated_fxs_dollar_value_d18 = \n                    (c_dollar_value_d18.mul(1e6).div(params.col_ratio))\n                    .sub(c_dollar_value_d18);\n\n        uint calculated_fxs_needed = calculated_fxs_dollar_value_d18.mul(1e6).div(params.fxs_price_usd);\n\n        return (\n            (c_dollar_value_d18.add(calculated_fxs_dollar_value_d18)).sub(((c_dollar_value_d18.add(calculated_fxs_dollar_value_d18)).mul(params.mint_fee)).div(1e6)),\n            calculated_fxs_needed\n        );\n    }\n\n    function calcRedeem1t1FRAX(uint256 col_price_usd, uint256 FRAX_amount, uint256 redemption_fee) public pure returns (uint256) {\n        uint256 collateral_needed_d18 = FRAX_amount.mul(1e6).div(col_price_usd);\n        return collateral_needed_d18.sub((collateral_needed_d18.mul(redemption_fee)).div(1e6));\n    }\n\n    // Must be internal because of the struct\n    function calcBuyBackFXS(BuybackFXS_Params memory params) internal pure returns (uint256) {\n        // If the total collateral value is higher than the amount required at the current collateral ratio then buy back up to the possible FXS with the desired collateral\n        require(params.excess_collateral_dollar_value_d18 \u003e 0, \"No excess collateral to buy back!\");\n\n        // Make sure not to take more than is available\n        uint256 fxs_dollar_value_d18 = params.FXS_amount.mul(params.fxs_price_usd).div(1e6);\n        require(fxs_dollar_value_d18 \u003c= params.excess_collateral_dollar_value_d18, \"You are trying to buy back more than the excess!\");\n\n        // Get the equivalent amount of collateral based on the market value of FXS provided \n        uint256 collateral_equivalent_d18 = fxs_dollar_value_d18.mul(1e6).div(params.col_price_usd);\n        //collateral_equivalent_d18 = collateral_equivalent_d18.sub((collateral_equivalent_d18.mul(params.buyback_fee)).div(1e6));\n\n        return (\n            collateral_equivalent_d18\n        );\n\n    }\n\n\n    // Returns value of collateral that must increase to reach recollateralization target (if 0 means no recollateralization)\n    function recollateralizeAmount(uint256 total_supply, uint256 global_collateral_ratio, uint256 global_collat_value) public pure returns (uint256) {\n        uint256 target_collat_value = total_supply.mul(global_collateral_ratio).div(1e6); // We want 18 decimals of precision so divide by 1e6; total_supply is 1e18 and global_collateral_ratio is 1e6\n        // Subtract the current value of collateral from the target value needed, if higher than 0 then system needs to recollateralize\n        uint256 recollateralization_left = target_collat_value.sub(global_collat_value); // If recollateralization is not needed, throws a subtraction underflow\n        return(recollateralization_left);\n    }\n\n    function calcRecollateralizeFRAXInner(\n        uint256 collateral_amount, \n        uint256 col_price,\n        uint256 global_collat_value,\n        uint256 frax_total_supply,\n        uint256 global_collateral_ratio\n    ) public pure returns (uint256, uint256) {\n        uint256 collat_value_attempted = collateral_amount.mul(col_price).div(1e6);\n        uint256 effective_collateral_ratio = global_collat_value.mul(1e6).div(frax_total_supply); //returns it in 1e6\n        uint256 recollat_possible = (global_collateral_ratio.mul(frax_total_supply).sub(frax_total_supply.mul(effective_collateral_ratio))).div(1e6);\n\n        uint256 amount_to_recollat;\n        if(collat_value_attempted \u003c= recollat_possible){\n            amount_to_recollat = collat_value_attempted;\n        } else {\n            amount_to_recollat = recollat_possible;\n        }\n\n        return (amount_to_recollat.mul(1e6).div(col_price), amount_to_recollat);\n\n    }\n\n}"},"FXS.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\npragma experimental ABIEncoderV2;\n\nimport \"./Context.sol\";\nimport \"./ERC20Custom.sol\";\nimport \"./IERC20.sol\";\nimport \"./Frax.sol\";\nimport \"./SafeMath.sol\";\nimport \"./AccessControl.sol\";\n\ncontract FRAXShares is ERC20Custom, AccessControl {\n    using SafeMath for uint256;\n\n    /* ========== STATE VARIABLES ========== */\n\n    string public symbol;\n    string public name;\n    uint8 public constant decimals = 18;\n    address public FRAXStablecoinAdd;\n    \n    uint256 public constant genesis_supply = 100000000e18; // 100M is printed upon genesis\n    uint256 public FXS_DAO_min; // Minimum FXS required to join DAO groups \n\n    address public owner_address;\n    address public oracle_address;\n    address public timelock_address; // Governance timelock address\n    FRAXStablecoin private FRAX;\n\n    bool public trackingVotes = true; // Tracking votes (only change if need to disable votes)\n\n    // A checkpoint for marking number of votes from a given block\n    struct Checkpoint {\n        uint32 fromBlock;\n        uint96 votes;\n    }\n\n    // A record of votes checkpoints for each account, by index\n    mapping (address =\u003e mapping (uint32 =\u003e Checkpoint)) public checkpoints;\n\n    // The number of checkpoints for each account\n    mapping (address =\u003e uint32) public numCheckpoints;\n\n    /* ========== MODIFIERS ========== */\n\n    modifier onlyPools() {\n       require(FRAX.frax_pools(msg.sender) == true, \"Only frax pools can mint new FRAX\");\n        _;\n    } \n    \n    modifier onlyByOwnerOrGovernance() {\n        require(msg.sender == owner_address || msg.sender == timelock_address, \"You are not an owner or the governance timelock\");\n        _;\n    }\n\n    /* ========== CONSTRUCTOR ========== */\n\n    constructor(\n        string memory _name,\n        string memory _symbol, \n        address _oracle_address,\n        address _owner_address,\n        address _timelock_address\n    ) public {\n        name = _name;\n        symbol = _symbol;\n        owner_address = _owner_address;\n        oracle_address = _oracle_address;\n        timelock_address = _timelock_address;\n        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());\n        _mint(owner_address, genesis_supply);\n\n        // Do a checkpoint for the owner\n        _writeCheckpoint(owner_address, 0, 0, uint96(genesis_supply));\n    }\n\n    /* ========== RESTRICTED FUNCTIONS ========== */\n\n    function setOracle(address new_oracle) external onlyByOwnerOrGovernance {\n        oracle_address = new_oracle;\n    }\n\n    function setTimelock(address new_timelock) external onlyByOwnerOrGovernance {\n        timelock_address = new_timelock;\n    }\n    \n    function setFRAXAddress(address frax_contract_address) external onlyByOwnerOrGovernance {\n        FRAX = FRAXStablecoin(frax_contract_address);\n    }\n    \n    function setFXSMinDAO(uint256 min_FXS) external onlyByOwnerOrGovernance {\n        FXS_DAO_min = min_FXS;\n    }\n\n    function setOwner(address _owner_address) external onlyByOwnerOrGovernance {\n        owner_address = _owner_address;\n    }\n\n    function mint(address to, uint256 amount) public onlyPools {\n        _mint(to, amount);\n    }\n    \n    // This function is what other frax pools will call to mint new FXS (similar to the FRAX mint) \n    function pool_mint(address m_address, uint256 m_amount) external onlyPools {        \n        if(trackingVotes){\n            uint32 srcRepNum = numCheckpoints[address(this)];\n            uint96 srcRepOld = srcRepNum \u003e 0 ? checkpoints[address(this)][srcRepNum - 1].votes : 0;\n            uint96 srcRepNew = add96(srcRepOld, uint96(m_amount), \"pool_mint new votes overflows\");\n            _writeCheckpoint(address(this), srcRepNum, srcRepOld, srcRepNew); // mint new votes\n            trackVotes(address(this), m_address, uint96(m_amount));\n        }\n\n        super._mint(m_address, m_amount);\n        emit FXSMinted(address(this), m_address, m_amount);\n    }\n\n    // This function is what other frax pools will call to burn FXS \n    function pool_burn_from(address b_address, uint256 b_amount) external onlyPools {\n        if(trackingVotes){\n            trackVotes(b_address, address(this), uint96(b_amount));\n            uint32 srcRepNum = numCheckpoints[address(this)];\n            uint96 srcRepOld = srcRepNum \u003e 0 ? checkpoints[address(this)][srcRepNum - 1].votes : 0;\n            uint96 srcRepNew = sub96(srcRepOld, uint96(b_amount), \"pool_burn_from new votes underflows\");\n            _writeCheckpoint(address(this), srcRepNum, srcRepOld, srcRepNew); // burn votes\n        }\n\n        super._burnFrom(b_address, b_amount);\n        emit FXSBurned(b_address, address(this), b_amount);\n    }\n\n    function toggleVotes() external onlyByOwnerOrGovernance {\n        trackingVotes = !trackingVotes;\n    }\n\n    /* ========== OVERRIDDEN PUBLIC FUNCTIONS ========== */\n\n    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n        if(trackingVotes){\n            // Transfer votes\n            trackVotes(_msgSender(), recipient, uint96(amount));\n        }\n\n        _transfer(_msgSender(), recipient, amount);\n        return true;\n    }\n\n    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {\n        if(trackingVotes){\n            // Transfer votes\n            trackVotes(sender, recipient, uint96(amount));\n        }\n\n        _transfer(sender, recipient, amount);\n        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, \"ERC20: transfer amount exceeds allowance\"));\n\n        return true;\n    }\n\n    /* ========== PUBLIC FUNCTIONS ========== */\n\n    /**\n     * @notice Gets the current votes balance for `account`\n     * @param account The address to get votes balance\n     * @return The number of current votes for `account`\n     */\n    function getCurrentVotes(address account) external view returns (uint96) {\n        uint32 nCheckpoints = numCheckpoints[account];\n        return nCheckpoints \u003e 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;\n    }\n\n    /**\n     * @notice Determine the prior number of votes for an account as of a block number\n     * @dev Block number must be a finalized block or else this function will revert to prevent misinformation.\n     * @param account The address of the account to check\n     * @param blockNumber The block number to get the vote balance at\n     * @return The number of votes the account had as of the given block\n     */\n    function getPriorVotes(address account, uint blockNumber) public view returns (uint96) {\n        require(blockNumber \u003c block.number, \"FXS::getPriorVotes: not yet determined\");\n\n        uint32 nCheckpoints = numCheckpoints[account];\n        if (nCheckpoints == 0) {\n            return 0;\n        }\n\n        // First check most recent balance\n        if (checkpoints[account][nCheckpoints - 1].fromBlock \u003c= blockNumber) {\n            return checkpoints[account][nCheckpoints - 1].votes;\n        }\n\n        // Next check implicit zero balance\n        if (checkpoints[account][0].fromBlock \u003e blockNumber) {\n            return 0;\n        }\n\n        uint32 lower = 0;\n        uint32 upper = nCheckpoints - 1;\n        while (upper \u003e lower) {\n            uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow\n            Checkpoint memory cp = checkpoints[account][center];\n            if (cp.fromBlock == blockNumber) {\n                return cp.votes;\n            } else if (cp.fromBlock \u003c blockNumber) {\n                lower = center;\n            } else {\n                upper = center - 1;\n            }\n        }\n        return checkpoints[account][lower].votes;\n    }\n\n    /* ========== INTERNAL FUNCTIONS ========== */\n\n    // From compound\u0027s _moveDelegates\n    // Keep track of votes. \"Delegates\" is a misnomer here\n    function trackVotes(address srcRep, address dstRep, uint96 amount) internal {\n        if (srcRep != dstRep \u0026\u0026 amount \u003e 0) {\n            if (srcRep != address(0)) {\n                uint32 srcRepNum = numCheckpoints[srcRep];\n                uint96 srcRepOld = srcRepNum \u003e 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;\n                uint96 srcRepNew = sub96(srcRepOld, amount, \"FXS::_moveVotes: vote amount underflows\");\n                _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);\n            }\n\n            if (dstRep != address(0)) {\n                uint32 dstRepNum = numCheckpoints[dstRep];\n                uint96 dstRepOld = dstRepNum \u003e 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;\n                uint96 dstRepNew = add96(dstRepOld, amount, \"FXS::_moveVotes: vote amount overflows\");\n                _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);\n            }\n        }\n    }\n\n    function _writeCheckpoint(address voter, uint32 nCheckpoints, uint96 oldVotes, uint96 newVotes) internal {\n      uint32 blockNumber = safe32(block.number, \"FXS::_writeCheckpoint: block number exceeds 32 bits\");\n\n      if (nCheckpoints \u003e 0 \u0026\u0026 checkpoints[voter][nCheckpoints - 1].fromBlock == blockNumber) {\n          checkpoints[voter][nCheckpoints - 1].votes = newVotes;\n      } else {\n          checkpoints[voter][nCheckpoints] = Checkpoint(blockNumber, newVotes);\n          numCheckpoints[voter] = nCheckpoints + 1;\n      }\n\n      emit VoterVotesChanged(voter, oldVotes, newVotes);\n    }\n\n    function safe32(uint n, string memory errorMessage) internal pure returns (uint32) {\n        require(n \u003c 2**32, errorMessage);\n        return uint32(n);\n    }\n\n    function safe96(uint n, string memory errorMessage) internal pure returns (uint96) {\n        require(n \u003c 2**96, errorMessage);\n        return uint96(n);\n    }\n\n    function add96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) {\n        uint96 c = a + b;\n        require(c \u003e= a, errorMessage);\n        return c;\n    }\n\n    function sub96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) {\n        require(b \u003c= a, errorMessage);\n        return a - b;\n    }\n\n    function getChainId() internal pure returns (uint) {\n        uint256 chainId;\n        assembly { chainId := chainid() }\n        return chainId;\n    }\n\n    /* ========== EVENTS ========== */\n    \n    /// @notice An event thats emitted when a voters account\u0027s vote balance changes\n    event VoterVotesChanged(address indexed voter, uint previousBalance, uint newBalance);\n\n    // Track FXS burned\n    event FXSBurned(address indexed from, address indexed to, uint256 amount);\n\n    // Track FXS minted\n    event FXSMinted(address indexed from, address indexed to, uint256 amount);\n\n}\n"},"Governance.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\npragma experimental ABIEncoderV2;\n\nimport \"./FXS.sol\";\n\n// From https://compound.finance/docs/governance\n// and https://github.com/compound-finance/compound-protocol/tree/master/contracts/Governance\ncontract GovernorAlpha {\n    /// @notice The name of this contract\n    string public constant name = \"FXS Governor Alpha\";\n\n    /// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed\n    function quorumVotes() public pure returns (uint) { return 4000000e18; } // 4,000,000 = 4% of FXS\n\n    /// @notice The number of votes required in order for a voter to become a proposer\n    function proposalThreshold() public pure returns (uint) { return 1000000e18; } // 1,000,000 = 1% of FXS\n\n    /// @notice The maximum number of actions that can be included in a proposal\n    function proposalMaxOperations() public pure returns (uint) { return 10; } // 10 actions\n\n    /// @notice The delay before voting on a proposal may take place, once proposed\n    // This also helps protect against flash loan attacks because only the vote balance at the proposal start block is considered\n    function votingDelay() public pure returns (uint) { return 1; } // 1 block\n\n    /// @notice The duration of voting on a proposal, in blocks\n    // function votingPeriod() public pure returns (uint) { return 17280; } // ~3 days in blocks (assuming 15s blocks)\n    uint public votingPeriod = 17280;\n    \n    /// @notice The address of the Timelock\n    TimelockInterface public timelock;\n\n    // The address of the FXS token\n    FRAXShares public fxs;\n\n    /// @notice The address of the Governor Guardian\n    address public guardian;\n\n    /// @notice The total number of proposals\n    uint public proposalCount = 0;\n\n    struct Proposal {\n        // @notice Unique id for looking up a proposal\n        uint id;\n\n        // @notice Creator of the proposal\n        address proposer;\n\n        // @notice The timestamp that the proposal will be available for execution, set once the vote succeeds\n        uint eta;\n\n        // @notice the ordered list of target addresses for calls to be made\n        address[] targets;\n\n        // @notice The ordered list of values (i.e. msg.value) to be passed to the calls to be made\n        uint[] values;\n\n        // @notice The ordered list of function signatures to be called\n        string[] signatures;\n\n        // @notice The ordered list of calldata to be passed to each call\n        bytes[] calldatas;\n\n        // @notice The block at which voting begins: holders must delegate their votes prior to this block\n        uint startBlock;\n\n        // @notice The block at which voting ends: votes must be cast prior to this block\n        uint endBlock;\n\n        // @notice Current number of votes in favor of this proposal\n        uint forVotes;\n\n        // @notice Current number of votes in opposition to this proposal\n        uint againstVotes;\n\n        // @notice Flag marking whether the proposal has been canceled\n        bool canceled;\n\n        // @notice Flag marking whether the proposal has been executed\n        bool executed;\n\n        // @notice Title of the proposal (human-readable)\n        string title;\n\n        // @notice Description of the proposall (human-readable)\n        string description;\n\n        // @notice Receipts of ballots for the entire set of voters\n        mapping (address =\u003e Receipt) receipts;\n    }\n\n    /// @notice Ballot receipt record for a voter\n    struct Receipt {\n        // @notice Whether or not a vote has been cast\n        bool hasVoted;\n\n        // @notice Whether or not the voter supports the proposal\n        bool support;\n\n        // @notice The number of votes the voter had, which were cast\n        uint96 votes;\n    }\n\n    /// @notice Possible states that a proposal may be in\n    enum ProposalState {\n        Pending,\n        Active,\n        Canceled,\n        Defeated,\n        Succeeded,\n        Queued,\n        Expired,\n        Executed\n    }\n\n    /// @notice The official record of all proposals ever proposed\n    mapping (uint =\u003e Proposal) public proposals;\n\n    /// @notice The latest proposal for each proposer\n    mapping (address =\u003e uint) public latestProposalIds;\n\n    /// @notice The EIP-712 typehash for the contract\u0027s domain\n    bytes32 public constant DOMAIN_TYPEHASH = keccak256(\"EIP712Domain(string name,uint256 chainId,address verifyingContract)\");\n\n    /// @notice The EIP-712 typehash for the ballot struct used by the contract\n    bytes32 public constant BALLOT_TYPEHASH = keccak256(\"Ballot(uint256 proposalId,bool support)\");\n\n    /// @notice An event emitted when a new proposal is created\n    event ProposalCreated(uint id, address proposer, address[] targets, uint[] values, string[] signatures, bytes[] calldatas, uint startBlock, uint endBlock, string description);\n\n    /// @notice An event emitted when a vote has been cast on a proposal\n    event VoteCast(address voter, uint proposalId, bool support, uint votes);\n\n    /// @notice An event emitted when a proposal has been canceled\n    event ProposalCanceled(uint id);\n\n    /// @notice An event emitted when a proposal has been queued in the Timelock\n    event ProposalQueued(uint id, uint eta);\n\n    /// @notice An event emitted when a proposal has been executed in the Timelock\n    event ProposalExecuted(uint id);\n\n    constructor(address timelock_, address fxs_, address guardian_) public {\n        timelock = TimelockInterface(timelock_);\n        fxs = FRAXShares(fxs_);\n        guardian = guardian_;\n    }\n\n    function propose(address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas, string memory title, string memory description) public returns (uint) {\n        require(fxs.getPriorVotes(msg.sender, sub256(block.number, 1)) \u003e= proposalThreshold(), \"GovernorAlpha::propose: proposer votes below proposal threshold\");\n        require(targets.length == values.length \u0026\u0026 targets.length == signatures.length \u0026\u0026 targets.length == calldatas.length, \"GovernorAlpha::propose: proposal function information arity mismatch\");\n        require(targets.length != 0, \"GovernorAlpha::propose: must provide actions\");\n        require(targets.length \u003c= proposalMaxOperations(), \"GovernorAlpha::propose: too many actions\");\n\n        uint latestProposalId = latestProposalIds[msg.sender];\n        if (latestProposalId != 0) {\n          ProposalState proposersLatestProposalState = state(latestProposalId);\n          require(proposersLatestProposalState != ProposalState.Active, \"GovernorAlpha::propose: one live proposal per proposer, found an already active proposal\");\n          require(proposersLatestProposalState != ProposalState.Pending, \"GovernorAlpha::propose: one live proposal per proposer, found an already pending proposal\");\n        }\n\n        uint startBlock = add256(block.number, votingDelay());\n        uint endBlock = add256(startBlock, votingPeriod);\n\n        proposalCount++;\n        Proposal memory newProposal = Proposal({\n            id: proposalCount,\n            proposer: msg.sender,\n            eta: 0,\n            targets: targets,\n            values: values,\n            signatures: signatures,\n            calldatas: calldatas,\n            startBlock: startBlock,\n            endBlock: endBlock,\n            forVotes: 0,\n            againstVotes: 0,\n            canceled: false,\n            executed: false,\n            title: title,\n            description: description\n        });\n\n        proposals[newProposal.id] = newProposal;\n        latestProposalIds[newProposal.proposer] = newProposal.id;\n\n        emit ProposalCreated(newProposal.id, msg.sender, targets, values, signatures, calldatas, startBlock, endBlock, description);\n        return newProposal.id;\n    }\n\n    function queue(uint proposalId) public {\n        require(state(proposalId) == ProposalState.Succeeded, \"GovernorAlpha::queue: proposal can only be queued if it succeeded\");\n        Proposal storage proposal = proposals[proposalId];\n        uint eta = add256(block.timestamp, timelock.delay());\n        for (uint i = 0; i \u003c proposal.targets.length; i++) {\n            _queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta);\n        }\n        proposal.eta = eta;\n        emit ProposalQueued(proposalId, eta);\n    }\n\n    function _queueOrRevert(address target, uint value, string memory signature, bytes memory data, uint eta) internal {\n        require(!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), \"GovernorAlpha::_queueOrRevert: proposal action already queued at eta\");\n        timelock.queueTransaction(target, value, signature, data, eta);\n    }\n\n    function execute(uint proposalId) public payable {\n        require(state(proposalId) == ProposalState.Queued, \"GovernorAlpha::execute: proposal can only be executed if it is queued\");\n        Proposal storage proposal = proposals[proposalId];\n        proposal.executed = true;\n        for (uint i = 0; i \u003c proposal.targets.length; i++) {\n            timelock.executeTransaction(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.eta);\n        }\n        emit ProposalExecuted(proposalId);\n    }\n\n    function cancel(uint proposalId) public {\n        ProposalState state = state(proposalId);\n        require(state != ProposalState.Executed, \"GovernorAlpha::cancel: cannot cancel executed proposal\");\n\n        Proposal storage proposal = proposals[proposalId];\n        require(msg.sender == guardian || fxs.getPriorVotes(proposal.proposer, sub256(block.number, 1)) \u003c proposalThreshold(), \"GovernorAlpha::cancel: proposer at or above threshold\");\n\n        proposal.canceled = true;\n        for (uint i = 0; i \u003c proposal.targets.length; i++) {\n            timelock.cancelTransaction(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.eta);\n        }\n\n        emit ProposalCanceled(proposalId);\n    }\n\n    function getActions(uint proposalId) public view returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas) {\n        Proposal storage p = proposals[proposalId];\n        return (p.targets, p.values, p.signatures, p.calldatas);\n    }\n\n    function getReceipt(uint proposalId, address voter) public view returns (Receipt memory) {\n        return proposals[proposalId].receipts[voter];\n    }\n\n    function state(uint proposalId) public view returns (ProposalState) {\n        require(proposalCount \u003e= proposalId \u0026\u0026 proposalId \u003e 0, \"GovernorAlpha::state: invalid proposal id\");\n        Proposal storage proposal = proposals[proposalId];\n        if (proposal.canceled) {\n            return ProposalState.Canceled;\n        } else if (block.number \u003c= proposal.startBlock) {\n            return ProposalState.Pending;\n        } else if (block.number \u003c= proposal.endBlock) {\n            return ProposalState.Active;\n        } else if (proposal.forVotes \u003c= proposal.againstVotes || proposal.forVotes \u003c quorumVotes()) {\n            return ProposalState.Defeated;\n        } else if (proposal.eta == 0) {\n            return ProposalState.Succeeded;\n        } else if (proposal.executed) {\n            return ProposalState.Executed;\n        } else if (block.timestamp \u003e= add256(proposal.eta, timelock.GRACE_PERIOD())) {\n            return ProposalState.Expired;\n        } else {\n            return ProposalState.Queued;\n        }\n    }\n\n    function castVote(uint proposalId, bool support) public {\n        return _castVote(msg.sender, proposalId, support);\n    }\n\n    function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public {\n        bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this)));\n        bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));\n        bytes32 digest = keccak256(abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash));\n        address signatory = ecrecover(digest, v, r, s);\n        require(signatory != address(0), \"GovernorAlpha::castVoteBySig: invalid signature\");\n        return _castVote(signatory, proposalId, support);\n    }\n\n    function _castVote(address voter, uint proposalId, bool support) internal {\n        require(state(proposalId) == ProposalState.Active, \"GovernorAlpha::_castVote: voting is closed\");\n        Proposal storage proposal = proposals[proposalId];\n        Receipt storage receipt = proposal.receipts[voter];\n        require(receipt.hasVoted == false, \"GovernorAlpha::_castVote: voter already voted\");\n        uint96 votes = fxs.getPriorVotes(voter, proposal.startBlock);\n\n        if (support) {\n            proposal.forVotes = add256(proposal.forVotes, votes);\n        } else {\n            proposal.againstVotes = add256(proposal.againstVotes, votes);\n        }\n\n        receipt.hasVoted = true;\n        receipt.support = support;\n        receipt.votes = votes;\n\n        emit VoteCast(voter, proposalId, support, votes);\n    }\n\n    function __acceptAdmin() public {\n        require(msg.sender == guardian, \"GovernorAlpha::__acceptAdmin: sender must be gov guardian\");\n        timelock.acceptAdmin();\n    }\n\n    function __abdicate() public {\n        require(msg.sender == guardian, \"GovernorAlpha::__abdicate: sender must be gov guardian\");\n        guardian = address(0);\n    }\n\n    function __setVotingPeriod(uint period) public {\n        require(msg.sender == guardian, \"GovernorAlpha::__setVotingPeriod: sender must be gov guardian\");\n        votingPeriod = period;\n    }\n\n    function __setTimelockAddress(address timelock_) public {\n        require(msg.sender == guardian, \"GovernorAlpha::__setTimelockAddress: sender must be gov guardian\");\n        timelock = TimelockInterface(timelock_);\n    }\n\n    function __queueSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public {\n        require(msg.sender == guardian, \"GovernorAlpha::__queueSetTimelockPendingAdmin: sender must be gov guardian\");\n        timelock.queueTransaction(address(timelock), 0, \"setPendingAdmin(address)\", abi.encode(newPendingAdmin), eta);\n    }\n\n    function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public {\n        require(msg.sender == guardian, \"GovernorAlpha::__executeSetTimelockPendingAdmin: sender must be gov guardian\");\n        timelock.executeTransaction(address(timelock), 0, \"setPendingAdmin(address)\", abi.encode(newPendingAdmin), eta);\n    }\n\n    function add256(uint256 a, uint256 b) internal pure returns (uint) {\n        uint c = a + b;\n        require(c \u003e= a, \"addition overflow\");\n        return c;\n    }\n\n    function sub256(uint256 a, uint256 b) internal pure returns (uint) {\n        require(b \u003c= a, \"subtraction underflow\");\n        return a - b;\n    }\n\n    function getChainId() internal pure returns (uint) {\n        uint chainId;\n        assembly { chainId := chainid() }\n        return chainId;\n    }\n}\n\ninterface TimelockInterface {\n    function delay() external view returns (uint);\n    function GRACE_PERIOD() external view returns (uint);\n    function acceptAdmin() external;\n    function queuedTransactions(bytes32 hash) external view returns (bool);\n    function queueTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external returns (bytes32);\n    function cancelTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external;\n    function executeTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external payable returns (bytes memory);\n}\n"},"IERC20.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \"./Context.sol\";\nimport \"./SafeMath.sol\";\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP. Does not include\n * the optional functions; to access them see {ERC20Detailed}.\n */\ninterface IERC20 {\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller\u0027s account to `recipient`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address recipient, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller\u0027s tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender\u0027s allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `sender` to `recipient` using the\n     * allowance mechanism. `amount` is then deducted from the caller\u0027s\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n"},"IStakingRewards.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\n\ninterface IStakingRewards {\n    // Views\n    function lastTimeRewardApplicable() external view returns (uint256);\n\n    function rewardPerToken() external view returns (uint256);\n\n    function earned(address account) external view returns (uint256);\n\n    function getRewardForDuration() external view returns (uint256);\n\n    function totalSupply() external view returns (uint256);\n\n    function balanceOf(address account) external view returns (uint256);\n\n    // Mutative\n\n    function stake(uint256 amount) external;\n\n    function withdraw(uint256 amount) external;\n\n    function getReward() external;\n\n    //function exit() external;\n}\n"},"IUniswapV2Callee.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\ninterface IUniswapV2Callee {\n    function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external;\n}\n"},"IUniswapV2ERC20.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\ninterface IUniswapV2ERC20 {\n    event Approval(address indexed owner, address indexed spender, uint value);\n    event Transfer(address indexed from, address indexed to, uint value);\n\n    function name() external pure returns (string memory);\n    function symbol() external pure returns (string memory);\n    function decimals() external pure returns (uint8);\n    function totalSupply() external view returns (uint);\n    function balanceOf(address owner) external view returns (uint);\n    function allowance(address owner, address spender) external view returns (uint);\n\n    function approve(address spender, uint value) external returns (bool);\n    function transfer(address to, uint value) external returns (bool);\n    function transferFrom(address from, address to, uint value) external returns (bool);\n\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\n    function PERMIT_TYPEHASH() external pure returns (bytes32);\n    function nonces(address owner) external view returns (uint);\n\n    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;\n}\n"},"IUniswapV2Factory.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\ninterface IUniswapV2Factory {\n    event PairCreated(address indexed token0, address indexed token1, address pair, uint);\n\n    function feeTo() external view returns (address);\n    function feeToSetter() external view returns (address);\n\n    function getPair(address tokenA, address tokenB) external view returns (address pair);\n    function allPairs(uint) external view returns (address pair);\n    function allPairsLength() external view returns (uint);\n\n    function createPair(address tokenA, address tokenB) external returns (address pair);\n\n    function setFeeTo(address) external;\n    function setFeeToSetter(address) external;\n}\n"},"IUniswapV2Pair.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\ninterface IUniswapV2Pair {\n    event Approval(address indexed owner, address indexed spender, uint value);\n    event Transfer(address indexed from, address indexed to, uint value);\n\n    function name() external pure returns (string memory);\n    function symbol() external pure returns (string memory);\n    function decimals() external pure returns (uint8);\n    function totalSupply() external view returns (uint);\n    function balanceOf(address owner) external view returns (uint);\n    function allowance(address owner, address spender) external view returns (uint);\n\n    function approve(address spender, uint value) external returns (bool);\n    function transfer(address to, uint value) external returns (bool);\n    function transferFrom(address from, address to, uint value) external returns (bool);\n\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\n    function PERMIT_TYPEHASH() external pure returns (bytes32);\n    function nonces(address owner) external view returns (uint);\n\n    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;\n\n    event Mint(address indexed sender, uint amount0, uint amount1);\n    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);\n    event Swap(\n        address indexed sender,\n        uint amount0In,\n        uint amount1In,\n        uint amount0Out,\n        uint amount1Out,\n        address indexed to\n    );\n    event Sync(uint112 reserve0, uint112 reserve1);\n\n    function MINIMUM_LIQUIDITY() external pure returns (uint);\n    function factory() external view returns (address);\n    function token0() external view returns (address);\n    function token1() external view returns (address);\n    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);\n    function price0CumulativeLast() external view returns (uint);\n    function price1CumulativeLast() external view returns (uint);\n    function kLast() external view returns (uint);\n\n    function mint(address to) external returns (uint liquidity);\n    function burn(address to) external returns (uint amount0, uint amount1);\n    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;\n    function skim(address to) external;\n    function sync() external;\n\n    function initialize(address, address) external;\n\n\n\n\n\n\n\n\n\n\n\n\n    \n}\n"},"IUniswapV2Router01.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\ninterface IUniswapV2Router01 {\n    function factory() external pure returns (address);\n    function WETH() external pure returns (address);\n\n    function addLiquidity(\n        address tokenA,\n        address tokenB,\n        uint amountADesired,\n        uint amountBDesired,\n        uint amountAMin,\n        uint amountBMin,\n        address to,\n        uint deadline\n    ) external returns (uint amountA, uint amountB, uint liquidity);\n    function addLiquidityETH(\n        address token,\n        uint amountTokenDesired,\n        uint amountTokenMin,\n        uint amountETHMin,\n        address to,\n        uint deadline\n    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);\n    function removeLiquidity(\n        address tokenA,\n        address tokenB,\n        uint liquidity,\n        uint amountAMin,\n        uint amountBMin,\n        address to,\n        uint deadline\n    ) external returns (uint amountA, uint amountB);\n    function removeLiquidityETH(\n        address token,\n        uint liquidity,\n        uint amountTokenMin,\n        uint amountETHMin,\n        address to,\n        uint deadline\n    ) external returns (uint amountToken, uint amountETH);\n    function removeLiquidityWithPermit(\n        address tokenA,\n        address tokenB,\n        uint liquidity,\n        uint amountAMin,\n        uint amountBMin,\n        address to,\n        uint deadline,\n        bool approveMax, uint8 v, bytes32 r, bytes32 s\n    ) external returns (uint amountA, uint amountB);\n    function removeLiquidityETHWithPermit(\n        address token,\n        uint liquidity,\n        uint amountTokenMin,\n        uint amountETHMin,\n        address to,\n        uint deadline,\n        bool approveMax, uint8 v, bytes32 r, bytes32 s\n    ) external returns (uint amountToken, uint amountETH);\n    function swapExactTokensForTokens(\n        uint amountIn,\n        uint amountOutMin,\n        address[] calldata path,\n        address to,\n        uint deadline\n    ) external returns (uint[] memory amounts);\n    function swapTokensForExactTokens(\n        uint amountOut,\n        uint amountInMax,\n        address[] calldata path,\n        address to,\n        uint deadline\n    ) external returns (uint[] memory amounts);\n    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)\n        external\n        payable\n        returns (uint[] memory amounts);\n    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)\n        external\n        returns (uint[] memory amounts);\n    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)\n        external\n        returns (uint[] memory amounts);\n    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)\n        external\n        payable\n        returns (uint[] memory amounts);\n\n    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);\n    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);\n    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);\n    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);\n    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);\n}"},"IUniswapV2Router02.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./IUniswapV2Router01.sol\u0027;\n\ninterface IUniswapV2Router02 is IUniswapV2Router01 {\n    function removeLiquidityETHSupportingFeeOnTransferTokens(\n        address token,\n        uint liquidity,\n        uint amountTokenMin,\n        uint amountETHMin,\n        address to,\n        uint deadline\n    ) external returns (uint amountETH);\n    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(\n        address token,\n        uint liquidity,\n        uint amountTokenMin,\n        uint amountETHMin,\n        address to,\n        uint deadline,\n        bool approveMax, uint8 v, bytes32 r, bytes32 s\n    ) external returns (uint amountETH);\n\n    function swapExactTokensForTokensSupportingFeeOnTransferTokens(\n        uint amountIn,\n        uint amountOutMin,\n        address[] calldata path,\n        address to,\n        uint deadline\n    ) external;\n    function swapExactETHForTokensSupportingFeeOnTransferTokens(\n        uint amountOutMin,\n        address[] calldata path,\n        address to,\n        uint deadline\n    ) external payable;\n    function swapExactTokensForETHSupportingFeeOnTransferTokens(\n        uint amountIn,\n        uint amountOutMin,\n        address[] calldata path,\n        address to,\n        uint deadline\n    ) external;\n}"},"IWETH.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\ninterface IWETH {\n    function deposit() external payable;\n    function transfer(address to, uint value) external returns (bool);\n    function transferFrom(address src, address dst, uint wad) external returns (bool);\n    function withdraw(uint) external;\n}"},"Math.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n    /**\n     * @dev Returns the largest of two numbers.\n     */\n    function max(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a \u003e= b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two numbers.\n     */\n    function min(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a \u003c b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two numbers. The result is rounded towards\n     * zero.\n     */\n    function average(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b) / 2 can overflow, so we distribute\n        return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);\n    }\n\n    // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)\n    function sqrt(uint y) internal pure returns (uint z) {\n        if (y \u003e 3) {\n            z = y;\n            uint x = y / 2 + 1;\n            while (x \u003c z) {\n                z = x;\n                x = (y / x + x) / 2;\n            }\n        } else if (y != 0) {\n            z = 1;\n        }\n    }\n}"},"MigrationHelper.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\ncontract MigrationHelper {\n  address public owner;\n  uint256 public gov_to_timelock_eta;\n\n  modifier restricted() {\n    if (msg.sender == owner) _;\n  }\n\n  constructor(address _owner) public {\n    owner = _owner;\n  }\n\n  function setGovToTimeLockETA(uint256 _eta) public restricted {\n    gov_to_timelock_eta = _eta;\n  }\n}\n"},"Migrations.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\ncontract Migrations {\n  address public owner;\n  uint public last_completed_migration;\n\n  modifier restricted() {\n    if (msg.sender == owner) _;\n  }\n\n  constructor() public {\n    owner = msg.sender;\n  }\n\n  function setCompleted(uint completed) public restricted {\n    last_completed_migration = completed;\n  }\n}\n"},"Owned.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\n// https://docs.synthetix.io/contracts/Owned\ncontract Owned {\n    address public owner;\n    address public nominatedOwner;\n\n    constructor(address _owner) public {\n        require(_owner != address(0), \"Owner address cannot be 0\");\n        owner = _owner;\n        emit OwnerChanged(address(0), _owner);\n    }\n\n    function nominateNewOwner(address _owner) external onlyOwner {\n        nominatedOwner = _owner;\n        emit OwnerNominated(_owner);\n    }\n\n    function acceptOwnership() external {\n        require(msg.sender == nominatedOwner, \"You must be nominated before you can accept ownership\");\n        emit OwnerChanged(owner, nominatedOwner);\n        owner = nominatedOwner;\n        nominatedOwner = address(0);\n    }\n\n    modifier onlyOwner {\n        require(msg.sender == owner, \"Only the contract owner may perform this action\");\n        _;\n    }\n\n    event OwnerNominated(address newOwner);\n    event OwnerChanged(address oldOwner, address newOwner);\n}"},"Pausable.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\n// Inheritance\nimport \"./Owned.sol\";\n\n// https://docs.synthetix.io/contracts/Pausable\nabstract contract Pausable is Owned {\n    uint public lastPauseTime;\n    bool public paused;\n\n    constructor() internal {\n        // This contract is abstract, and thus cannot be instantiated directly\n        require(owner != address(0), \"Owner must be set\");\n        // Paused will be false, and lastPauseTime will be 0 upon initialisation\n    }\n\n    /**\n     * @notice Change the paused state of the contract\n     * @dev Only the contract owner may call this.\n     */\n    function setPaused(bool _paused) external onlyOwner {\n        // Ensure we\u0027re actually changing the state before we do anything\n        if (_paused == paused) {\n            return;\n        }\n\n        // Set our paused state.\n        paused = _paused;\n\n        // If applicable, set the last pause time.\n        if (paused) {\n            lastPauseTime = now;\n        }\n\n        // Let everyone know that our pause state has changed.\n        emit PauseChanged(paused);\n    }\n\n    event PauseChanged(bool isPaused);\n\n    modifier notPaused {\n        require(!paused, \"This action cannot be performed while the contract is paused\");\n        _;\n    }\n}"},"Pool_USDC.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \"./FraxPool.sol\";\n\ncontract Pool_USDC is FraxPool {\n    address public USDC_address;\n    constructor(\n        address _frax_contract_address,\n        address _fxs_contract_address,\n        address _collateral_address,\n        address _creator_address,\n        address _timelock_address,\n        uint256 _pool_ceiling\n    ) \n    FraxPool(_frax_contract_address, _fxs_contract_address, _collateral_address, _creator_address, _timelock_address, _pool_ceiling)\n    public {\n        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());\n        USDC_address = _collateral_address;\n    }\n}\n"},"Pool_USDT.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \"./FraxPool.sol\";\n\ncontract Pool_USDT is FraxPool {\n    address public USDT_address;\n    constructor(\n        address _frax_contract_address,\n        address _fxs_contract_address,\n        address _collateral_address,\n        address _creator_address,\n        address _timelock_address,\n        uint256 _pool_ceiling\n    ) \n    FraxPool(_frax_contract_address, _fxs_contract_address, _collateral_address, _creator_address, _timelock_address, _pool_ceiling)\n    public {\n        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());\n        USDT_address = _collateral_address;\n    }\n}\n"},"ReentrancyGuard.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\ncontract ReentrancyGuard {\n    // Booleans are more expensive than uint256 or any type that takes up a full\n    // word because each write operation emits an extra SLOAD to first read the\n    // slot\u0027s contents, replace the bits taken up by the boolean, and then write\n    // back. This is the compiler\u0027s defense against contract upgrades and\n    // pointer aliasing, and it cannot be disabled.\n\n    // The values being non-zero value makes deployment a bit more expensive,\n    // but in exchange the refund on every call to nonReentrant will be lower in\n    // amount. Since refunds are capped to a percentage of the total\n    // transaction\u0027s gas, it is best to keep them low in cases like this one, to\n    // increase the likelihood of the full refund coming into effect.\n    uint256 private constant _NOT_ENTERED = 1;\n    uint256 private constant _ENTERED = 2;\n\n    uint256 private _status;\n\n    constructor () internal {\n        _status = _NOT_ENTERED;\n    }\n\n    /**\n     * @dev Prevents a contract from calling itself, directly or indirectly.\n     * Calling a `nonReentrant` function from another `nonReentrant`\n     * function is not supported. It is possible to prevent this from happening\n     * by making the `nonReentrant` function external, and make it call a\n     * `private` function that does the actual work.\n     */\n    modifier nonReentrant() {\n        // On the first call to nonReentrant, _notEntered will be true\n        require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n        // Any calls to nonReentrant after this point will fail\n        _status = _ENTERED;\n\n        _;\n\n        // By storing the original value once again, a refund is triggered (see\n        // https://eips.ethereum.org/EIPS/eip-2200)\n        _status = _NOT_ENTERED;\n    }\n}"},"RewardsDistributionRecipient.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\n// Inheritance\nimport \"./Owned.sol\";\n\n\n// https://docs.synthetix.io/contracts/RewardsDistributionRecipient\nabstract contract RewardsDistributionRecipient is Owned {\n    address public rewardsDistribution;\n\n    //function notifyRewardAmount(uint256 reward) external virtual;\n\n    modifier onlyRewardsDistribution() {\n        require(msg.sender == rewardsDistribution, \"Caller is not RewardsDistribution contract\");\n        _;\n    }\n\n    function setRewardsDistribution(address _rewardsDistribution) external onlyOwner {\n        rewardsDistribution = _rewardsDistribution;\n    }\n}\n"},"SafeERC20.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \"./IERC20.sol\";\nimport \"./SafeMath.sol\";\nimport \"./Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n    using SafeMath for uint256;\n    using Address for address;\n\n    function safeTransfer(IERC20 token, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n    }\n\n    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n    }\n\n    /**\n     * @dev Deprecated. This function has issues similar to the ones found in\n     * {IERC20-approve}, and its usage is discouraged.\n     *\n     * Whenever possible, use {safeIncreaseAllowance} and\n     * {safeDecreaseAllowance} instead.\n     */\n    function safeApprove(IERC20 token, address spender, uint256 value) internal {\n        // safeApprove should only be called when setting an initial allowance,\n        // or when resetting it to zero. To increase and decrease it, use\n        // \u0027safeIncreaseAllowance\u0027 and \u0027safeDecreaseAllowance\u0027\n        // solhint-disable-next-line max-line-length\n        require((value == 0) || (token.allowance(address(this), spender) == 0),\n            \"SafeERC20: approve from non-zero to non-zero allowance\"\n        );\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n    }\n\n    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 newAllowance = token.allowance(address(this), spender).add(value);\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n    }\n\n    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 newAllowance = token.allowance(address(this), spender).sub(value, \"SafeERC20: decreased allowance below zero\");\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     */\n    function _callOptionalReturn(IERC20 token, bytes memory data) private {\n        // We need to perform a low level call here, to bypass Solidity\u0027s return data size checking mechanism, since\n        // we\u0027re implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n        // the target address contains contract code and also asserts for success in the low-level call.\n\n        bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n        if (returndata.length \u003e 0) { // Return data is optional\n            // solhint-disable-next-line max-line-length\n            require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n        }\n    }\n}"},"SafeMath.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\n/**\n * @dev Wrappers over Solidity\u0027s arithmetic operations with added overflow\n * checks.\n *\n * Arithmetic operations in Solidity wrap on overflow. This can easily result\n * in bugs, because programmers usually assume that an overflow raises an\n * error, which is the standard behavior in high level programming languages.\n * `SafeMath` restores this intuition by reverting the transaction when an\n * operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it\u0027s recommended to use it always.\n */\nlibrary SafeMath {\n    /**\n     * @dev Returns the addition of two unsigned integers, reverting on\n     * overflow.\n     *\n     * Counterpart to Solidity\u0027s `+` operator.\n     *\n     * Requirements:\n     * - Addition cannot overflow.\n     */\n    function add(uint256 a, uint256 b) internal pure returns (uint256) {\n        uint256 c = a + b;\n        require(c \u003e= a, \"SafeMath: addition overflow\");\n\n        return c;\n    }\n\n    /**\n     * @dev Returns the subtraction of two unsigned integers, reverting on\n     * overflow (when the result is negative).\n     *\n     * Counterpart to Solidity\u0027s `-` operator.\n     *\n     * Requirements:\n     * - Subtraction cannot overflow.\n     */\n    function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n        return sub(a, b, \"SafeMath: subtraction overflow\");\n    }\n\n    /**\n     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n     * overflow (when the result is negative).\n     *\n     * Counterpart to Solidity\u0027s `-` operator.\n     *\n     * Requirements:\n     * - Subtraction cannot overflow.\n     *\n     * _Available since v2.4.0._\n     */\n    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n        require(b \u003c= a, errorMessage);\n        uint256 c = a - b;\n\n        return c;\n    }\n\n    /**\n     * @dev Returns the multiplication of two unsigned integers, reverting on\n     * overflow.\n     *\n     * Counterpart to Solidity\u0027s `*` operator.\n     *\n     * Requirements:\n     * - Multiplication cannot overflow.\n     */\n    function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n        // Gas optimization: this is cheaper than requiring \u0027a\u0027 not being zero, but the\n        // benefit is lost if \u0027b\u0027 is also tested.\n        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n        if (a == 0) {\n            return 0;\n        }\n\n        uint256 c = a * b;\n        require(c / a == b, \"SafeMath: multiplication overflow\");\n\n        return c;\n    }\n\n    /**\n     * @dev Returns the integer division of two unsigned integers. Reverts on\n     * division by zero. The result is rounded towards zero.\n     *\n     * Counterpart to Solidity\u0027s `/` operator. Note: this function uses a\n     * `revert` opcode (which leaves remaining gas untouched) while Solidity\n     * uses an invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     * - The divisor cannot be zero.\n     */\n    function div(uint256 a, uint256 b) internal pure returns (uint256) {\n        return div(a, b, \"SafeMath: division by zero\");\n    }\n\n    /**\n     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on\n     * division by zero. The result is rounded towards zero.\n     *\n     * Counterpart to Solidity\u0027s `/` operator. Note: this function uses a\n     * `revert` opcode (which leaves remaining gas untouched) while Solidity\n     * uses an invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     * - The divisor cannot be zero.\n     *\n     * _Available since v2.4.0._\n     */\n    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n        // Solidity only automatically asserts when dividing by 0\n        require(b \u003e 0, errorMessage);\n        uint256 c = a / b;\n        // assert(a == b * c + a % b); // There is no case in which this doesn\u0027t hold\n\n        return c;\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n     * Reverts when dividing by zero.\n     *\n     * Counterpart to Solidity\u0027s `%` operator. This function uses a `revert`\n     * opcode (which leaves remaining gas untouched) while Solidity uses an\n     * invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     * - The divisor cannot be zero.\n     */\n    function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n        return mod(a, b, \"SafeMath: modulo by zero\");\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n     * Reverts with custom message when dividing by zero.\n     *\n     * Counterpart to Solidity\u0027s `%` operator. This function uses a `revert`\n     * opcode (which leaves remaining gas untouched) while Solidity uses an\n     * invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     * - The divisor cannot be zero.\n     *\n     * _Available since v2.4.0._\n     */\n    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n        require(b != 0, errorMessage);\n        return a % b;\n    }\n}"},"Stake_FRAX_FXS.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\npragma experimental ABIEncoderV2;\n\nimport \"./StakingRewards.sol\";\n\ncontract Stake_FRAX_FXS is StakingRewards {\n    constructor(\n        address _owner,\n        address _rewardsDistribution,\n        address _rewardsToken,\n        address _stakingToken,\n        address _frax_address,\n        address _timelock_address,\n        uint256 _pool_weight\n    ) \n    StakingRewards(_owner, _rewardsDistribution, _rewardsToken, _stakingToken, _frax_address, _timelock_address, _pool_weight)\n    public {}\n}"},"Stake_FRAX_USDC.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\npragma experimental ABIEncoderV2;\n\nimport \"./StakingRewards.sol\";\n\ncontract Stake_FRAX_USDC is StakingRewards {\n    constructor(\n        address _owner,\n        address _rewardsDistribution,\n        address _rewardsToken,\n        address _stakingToken,\n        address _frax_address,\n        address _timelock_address,\n        uint256 _pool_weight\n    ) \n    StakingRewards(_owner, _rewardsDistribution, _rewardsToken, _stakingToken, _frax_address, _timelock_address, _pool_weight)\n    public {}\n}"},"Stake_FRAX_WETH.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\npragma experimental ABIEncoderV2;\n\nimport \"./StakingRewards.sol\";\n\ncontract Stake_FRAX_WETH is StakingRewards {\n    constructor(\n        address _owner,\n        address _rewardsDistribution,\n        address _rewardsToken,\n        address _stakingToken,\n        address _frax_address,\n        address _timelock_address,\n        uint256 _pool_weight\n    ) \n    StakingRewards(_owner, _rewardsDistribution, _rewardsToken, _stakingToken, _frax_address, _timelock_address, _pool_weight)\n    public {}\n}"},"Stake_FXS_WETH.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\npragma experimental ABIEncoderV2;\n\nimport \"./StakingRewards.sol\";\n\ncontract Stake_FXS_WETH is StakingRewards {\n    constructor(\n        address _owner,\n        address _rewardsDistribution,\n        address _rewardsToken,\n        address _stakingToken,\n        address _frax_address,\n        address _timelock_address,\n        uint256 _pool_weight\n    ) \n    StakingRewards(_owner, _rewardsDistribution, _rewardsToken, _stakingToken, _frax_address, _timelock_address, _pool_weight)\n    public {}\n}"},"StakingRewards.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\npragma experimental ABIEncoderV2;\n\n// Stolen with love from Synthetixio\n// https://raw.githubusercontent.com/Synthetixio/synthetix/develop/contracts/StakingRewards.sol\n\nimport \"./Math.sol\";\nimport \"./SafeMath.sol\";\nimport \"./ERC20.sol\";\nimport \u0027./TransferHelper.sol\u0027;\nimport \"./SafeERC20.sol\";\nimport \"./Frax.sol\";\nimport \"./ReentrancyGuard.sol\";\nimport \"./StringHelpers.sol\";\n\n// Inheritance\nimport \"./IStakingRewards.sol\";\nimport \"./RewardsDistributionRecipient.sol\";\nimport \"./Pausable.sol\";\n\ncontract StakingRewards is IStakingRewards, RewardsDistributionRecipient, ReentrancyGuard, Pausable {\n    using SafeMath for uint256;\n    using SafeERC20 for ERC20;\n\n    /* ========== STATE VARIABLES ========== */\n\n    FRAXStablecoin private FRAX;\n    ERC20 public rewardsToken;\n    ERC20 public stakingToken;\n    uint256 public periodFinish;\n\n    // Constant for various precisions\n    uint256 private constant PRICE_PRECISION = 1e6;\n    uint256 private constant MULTIPLIER_BASE = 1e6;\n\n    // Max reward per second\n    uint256 public rewardRate;\n\n    // uint256 public rewardsDuration = 86400 hours;\n    uint256 public rewardsDuration = 604800; // 7 * 86400  (7 days)\n\n    uint256 public lastUpdateTime;\n    uint256 public rewardPerTokenStored = 0;\n    uint256 private pool_weight; // This staking pool\u0027s percentage of the total FXS being distributed by all pools, 6 decimals of precision\n\n    address public owner_address;\n    address public timelock_address; // Governance timelock address\n\n    uint256 public locked_stake_max_multiplier = 3000000; // 6 decimals of precision. 1x = 1000000\n    uint256 public locked_stake_time_for_max_multiplier = 3 * 365 * 86400; // 3 years\n    uint256 public locked_stake_min_time = 604800; // 7 * 86400  (7 days)\n    string private locked_stake_min_time_str = \"604800\"; // 7 days on genesis\n\n    uint256 public cr_boost_max_multiplier = 3000000; // 6 decimals of precision. 1x = 1000000\n\n    mapping(address =\u003e uint256) public userRewardPerTokenPaid;\n    mapping(address =\u003e uint256) public rewards;\n\n    uint256 private _staking_token_supply = 0;\n    uint256 private _staking_token_boosted_supply = 0;\n    mapping(address =\u003e uint256) private _unlocked_balances;\n    mapping(address =\u003e uint256) private _locked_balances;\n    mapping(address =\u003e uint256) private _boosted_balances;\n\n    mapping(address =\u003e LockedStake[]) private lockedStakes;\n\n    mapping(address =\u003e bool) public greylist;\n\n    bool public unlockedStakes; // Release lock stakes in case of system migration\n\n    struct LockedStake {\n        bytes32 kek_id;\n        uint256 start_timestamp;\n        uint256 amount;\n        uint256 ending_timestamp;\n        uint256 multiplier; // 6 decimals of precision. 1x = 1000000\n    }\n\n    /* ========== CONSTRUCTOR ========== */\n\n    constructor(\n        address _owner,\n        address _rewardsDistribution,\n        address _rewardsToken,\n        address _stakingToken,\n        address _frax_address,\n        address _timelock_address,\n        uint256 _pool_weight\n    ) public Owned(_owner){\n        owner_address = _owner;\n        rewardsToken = ERC20(_rewardsToken);\n        stakingToken = ERC20(_stakingToken);\n        FRAX = FRAXStablecoin(_frax_address);\n        rewardsDistribution = _rewardsDistribution;\n        lastUpdateTime = block.timestamp;\n        timelock_address = _timelock_address;\n        pool_weight = _pool_weight;\n        rewardRate = 380517503805175038; // (uint256(12000000e18)).div(365 * 86400); // Base emission rate of 12M FXS over the first year\n        rewardRate = rewardRate.mul(pool_weight).div(1e6);\n        unlockedStakes = false;\n    }\n\n    /* ========== VIEWS ========== */\n\n    function totalSupply() external override view returns (uint256) {\n        return _staking_token_supply;\n    }\n\n    function totalBoostedSupply() external view returns (uint256) {\n        return _staking_token_boosted_supply;\n    }\n\n    function stakingMultiplier(uint256 secs) public view returns (uint256) {\n        uint256 multiplier = uint(MULTIPLIER_BASE).add(secs.mul(locked_stake_max_multiplier.sub(MULTIPLIER_BASE)).div(locked_stake_time_for_max_multiplier));\n        if (multiplier \u003e locked_stake_max_multiplier) multiplier = locked_stake_max_multiplier;\n        return multiplier;\n    }\n\n    function crBoostMultiplier() public view returns (uint256) {\n        uint256 multiplier = uint(MULTIPLIER_BASE).add((uint(MULTIPLIER_BASE).sub(FRAX.global_collateral_ratio())).mul(cr_boost_max_multiplier.sub(MULTIPLIER_BASE)).div(MULTIPLIER_BASE) );\n        return multiplier;\n    }\n\n    // Total unlocked and locked liquidity tokens\n    function balanceOf(address account) external override view returns (uint256) {\n        return (_unlocked_balances[account]).add(_locked_balances[account]);\n    }\n\n    // Total unlocked liquidity tokens\n    function unlockedBalanceOf(address account) external view returns (uint256) {\n        return _unlocked_balances[account];\n    }\n\n    // Total locked liquidity tokens\n    function lockedBalanceOf(address account) public view returns (uint256) {\n        return _locked_balances[account];\n    }\n\n    // Total \u0027balance\u0027 used for calculating the percent of the pool the account owns\n    // Takes into account the locked stake time multiplier\n    function boostedBalanceOf(address account) external view returns (uint256) {\n        return _boosted_balances[account];\n    }\n\n    function lockedStakesOf(address account) external view returns (LockedStake[] memory) {\n        return lockedStakes[account];\n    }\n\n    function stakingDecimals() external view returns (uint256) {\n        return stakingToken.decimals();\n    }\n\n    function rewardsFor(address account) external view returns (uint256) {\n        // You may have use earned() instead, because of the order in which the contract executes \n        return rewards[account];\n    }\n\n    function lastTimeRewardApplicable() public override view returns (uint256) {\n        return Math.min(block.timestamp, periodFinish);\n    }\n\n    function rewardPerToken() public override view returns (uint256) {\n        if (_staking_token_supply == 0) {\n            return rewardPerTokenStored;\n        }\n        else {\n            return rewardPerTokenStored.add(\n                lastTimeRewardApplicable().sub(lastUpdateTime).mul(rewardRate).mul(crBoostMultiplier()).mul(1e18).div(PRICE_PRECISION).div(_staking_token_boosted_supply)\n            );\n        }\n    }\n\n    function earned(address account) public override view returns (uint256) {\n        return _boosted_balances[account].mul(rewardPerToken().sub(userRewardPerTokenPaid[account])).div(1e18).add(rewards[account]);\n    }\n\n    // function earned(address account) public override view returns (uint256) {\n    //     return _balances[account].mul(rewardPerToken().sub(userRewardPerTokenPaid[account])).add(rewards[account]);\n    // }\n\n    function getRewardForDuration() external override view returns (uint256) {\n        return rewardRate.mul(rewardsDuration).mul(crBoostMultiplier()).div(PRICE_PRECISION);\n    }\n\n    /* ========== MUTATIVE FUNCTIONS ========== */\n\n    function stake(uint256 amount) external override nonReentrant notPaused updateReward(msg.sender) {\n        require(amount \u003e 0, \"Cannot stake 0\");\n        require(greylist[msg.sender] == false, \"address has been greylisted\");\n\n        // Pull the tokens from the staker\n        TransferHelper.safeTransferFrom(address(stakingToken), msg.sender, address(this), amount);\n\n        // Staking token supply and boosted supply\n        _staking_token_supply = _staking_token_supply.add(amount);\n        _staking_token_boosted_supply = _staking_token_boosted_supply.add(amount);\n\n        // Staking token balance and boosted balance\n        _unlocked_balances[msg.sender] = _unlocked_balances[msg.sender].add(amount);\n        _boosted_balances[msg.sender] = _boosted_balances[msg.sender].add(amount);\n\n        emit Staked(msg.sender, amount);\n    }\n\n    function stakeLocked(uint256 amount, uint256 secs) external nonReentrant notPaused updateReward(msg.sender) {\n        require(amount \u003e 0, \"Cannot stake 0\");\n        require(secs \u003e 0, \"Cannot wait for a negative number\");\n        require(greylist[msg.sender] == false, \"address has been greylisted\");\n        require(secs \u003e= locked_stake_min_time, StringHelpers.strConcat(\"Minimum stake time not met (\", locked_stake_min_time_str, \")\") );\n\n        uint256 multiplier = stakingMultiplier(secs);\n        uint256 boostedAmount = amount.mul(multiplier).div(PRICE_PRECISION);\n        lockedStakes[msg.sender].push(LockedStake(\n            keccak256(abi.encodePacked(msg.sender, block.timestamp, amount)),\n            block.timestamp,\n            amount,\n            block.timestamp.add(secs),\n            multiplier\n        ));\n\n        // Pull the tokens from the staker\n        TransferHelper.safeTransferFrom(address(stakingToken), msg.sender, address(this), amount);\n\n        // Staking token supply and boosted supply\n        _staking_token_supply = _staking_token_supply.add(amount);\n        _staking_token_boosted_supply = _staking_token_boosted_supply.add(boostedAmount);\n\n        // Staking token balance and boosted balance\n        _locked_balances[msg.sender] = _locked_balances[msg.sender].add(amount);\n        _boosted_balances[msg.sender] = _boosted_balances[msg.sender].add(boostedAmount);\n\n        emit StakeLocked(msg.sender, amount, secs);\n    }\n\n    function withdraw(uint256 amount) public override nonReentrant updateReward(msg.sender) {\n        require(amount \u003e 0, \"Cannot withdraw 0\");\n\n        // Staking token balance and boosted balance\n        _unlocked_balances[msg.sender] = _unlocked_balances[msg.sender].sub(amount);\n        _boosted_balances[msg.sender] = _boosted_balances[msg.sender].sub(amount);\n\n        // Staking token supply and boosted supply\n        _staking_token_supply = _staking_token_supply.sub(amount);\n        _staking_token_boosted_supply = _staking_token_boosted_supply.sub(amount);\n\n        // Give the tokens to the withdrawer\n        stakingToken.safeTransfer(msg.sender, amount);\n        emit Withdrawn(msg.sender, amount);\n    }\n\n    function withdrawLocked(bytes32 kek_id) public nonReentrant updateReward(msg.sender) {\n        LockedStake memory thisStake;\n        thisStake.amount = 0;\n        uint theIndex;\n        for (uint i = 0; i \u003c lockedStakes[msg.sender].length; i++){ \n            if (kek_id == lockedStakes[msg.sender][i].kek_id){\n                thisStake = lockedStakes[msg.sender][i];\n                theIndex = i;\n                break;\n            }\n        }\n        require(thisStake.kek_id == kek_id, \"Stake not found\");\n        require(block.timestamp \u003e= thisStake.ending_timestamp || unlockedStakes == true, \"Stake is still locked!\");\n\n        uint256 theAmount = thisStake.amount;\n        uint256 boostedAmount = theAmount.mul(thisStake.multiplier).div(PRICE_PRECISION);\n        if (theAmount \u003e 0){\n            // Staking token balance and boosted balance\n            _locked_balances[msg.sender] = _locked_balances[msg.sender].sub(theAmount);\n            _boosted_balances[msg.sender] = _boosted_balances[msg.sender].sub(boostedAmount);\n\n            // Staking token supply and boosted supply\n            _staking_token_supply = _staking_token_supply.sub(theAmount);\n            _staking_token_boosted_supply = _staking_token_boosted_supply.sub(boostedAmount);\n\n            // Remove the stake from the array\n            delete lockedStakes[msg.sender][theIndex];\n\n            // Give the tokens to the withdrawer\n            stakingToken.safeTransfer(msg.sender, theAmount);\n\n            emit WithdrawnLocked(msg.sender, theAmount, kek_id);\n        }\n\n    }\n\n    function getReward() public override nonReentrant updateReward(msg.sender) {\n        uint256 reward = rewards[msg.sender];\n        if (reward \u003e 0) {\n            rewards[msg.sender] = 0;\n            rewardsToken.transfer(msg.sender, reward);\n            emit RewardPaid(msg.sender, reward);\n        }\n    }\n/*\n    function exit() external override {\n        withdraw(_balances[msg.sender]);\n\n        // TODO: Add locked stakes too?\n\n        getReward();\n    }\n*/\n    function renewIfApplicable() external {\n        if (block.timestamp \u003e periodFinish) {\n            retroCatchUp();\n        }\n    }\n\n    // If the period expired, renew it\n    function retroCatchUp() internal {\n        // Failsafe check\n        require(block.timestamp \u003e periodFinish, \"Period has not expired yet!\");\n\n        // Ensure the provided reward amount is not more than the balance in the contract.\n        // This keeps the reward rate in the right range, preventing overflows due to\n        // very high values of rewardRate in the earned and rewardsPerToken functions;\n        // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow.\n        uint256 num_periods_elapsed = uint256(block.timestamp.sub(periodFinish)) / rewardsDuration; // Floor division to the nearest period\n        uint balance = rewardsToken.balanceOf(address(this));\n        require(rewardRate.mul(rewardsDuration).mul(crBoostMultiplier()).mul(num_periods_elapsed + 1).div(PRICE_PRECISION) \u003c= balance, \"Not enough FXS available for rewards!\");\n\n        // uint256 old_lastUpdateTime = lastUpdateTime;\n        // uint256 new_lastUpdateTime = block.timestamp;\n\n        // lastUpdateTime = periodFinish;\n        periodFinish = periodFinish.add((num_periods_elapsed.add(1)).mul(rewardsDuration));\n\n        rewardPerTokenStored = rewardPerToken();\n        lastUpdateTime = lastTimeRewardApplicable();\n\n        emit RewardsPeriodRenewed(address(stakingToken));\n    }\n\n    /* ========== RESTRICTED FUNCTIONS ========== */\n/*\n    // This notifies people that the reward is being changed\n    function notifyRewardAmount(uint256 reward) external override onlyRewardsDistribution updateReward(address(0)) {\n        // Needed to make compiler happy\n\n        \n        // if (block.timestamp \u003e= periodFinish) {\n        //     rewardRate = reward.mul(crBoostMultiplier()).div(rewardsDuration).div(PRICE_PRECISION);\n        // } else {\n        //     uint256 remaining = periodFinish.sub(block.timestamp);\n        //     uint256 leftover = remaining.mul(rewardRate);\n        //     rewardRate = reward.mul(crBoostMultiplier()).add(leftover).div(rewardsDuration).div(PRICE_PRECISION);\n        // }\n\n        // // Ensure the provided reward amount is not more than the balance in the contract.\n        // // This keeps the reward rate in the right range, preventing overflows due to\n        // // very high values of rewardRate in the earned and rewardsPerToken functions;\n        // // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow.\n        // uint balance = rewardsToken.balanceOf(address(this));\n        // require(rewardRate \u003c= balance.div(rewardsDuration), \"Provided reward too high\");\n\n        // lastUpdateTime = block.timestamp;\n        // periodFinish = block.timestamp.add(rewardsDuration);\n        // emit RewardAdded(reward);\n    }\n*/\n    // Added to support recovering LP Rewards from other systems to be distributed to holders\n    function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyByOwnerOrGovernance {\n        // Admin cannot withdraw the staking token from the contract\n        require(tokenAddress != address(stakingToken));\n        ERC20(tokenAddress).transfer(owner_address, tokenAmount);\n        emit Recovered(tokenAddress, tokenAmount);\n    }\n\n    function setRewardsDuration(uint256 _rewardsDuration) external onlyByOwnerOrGovernance {\n        require(\n            periodFinish == 0 || block.timestamp \u003e periodFinish,\n            \"Previous rewards period must be complete before changing the duration for the new period\"\n        );\n        rewardsDuration = _rewardsDuration;\n        emit RewardsDurationUpdated(rewardsDuration);\n    }\n\n    function setMultipliers(uint256 _locked_stake_max_multiplier, uint256 _cr_boost_max_multiplier) external onlyByOwnerOrGovernance {\n        require(_locked_stake_max_multiplier \u003e= 1, \"Multiplier must be greater than or equal to 1\");\n        require(_cr_boost_max_multiplier \u003e= 1, \"Max CR Boost must be greater than or equal to 1\");\n\n        locked_stake_max_multiplier = _locked_stake_max_multiplier;\n        cr_boost_max_multiplier = _cr_boost_max_multiplier;\n        \n        emit MaxCRBoostMultiplier(cr_boost_max_multiplier);\n        emit LockedStakeMaxMultiplierUpdated(locked_stake_max_multiplier);\n    }\n\n    function setLockedStakeTimeForMinAndMaxMultiplier(uint256 _locked_stake_time_for_max_multiplier, uint256 _locked_stake_min_time) external onlyByOwnerOrGovernance {\n        require(_locked_stake_time_for_max_multiplier \u003e= 1, \"Multiplier Max Time must be greater than or equal to 1\");\n        require(_locked_stake_min_time \u003e= 1, \"Multiplier Min Time must be greater than or equal to 1\");\n        \n        locked_stake_time_for_max_multiplier = _locked_stake_time_for_max_multiplier;\n\n        locked_stake_min_time = _locked_stake_min_time;\n        locked_stake_min_time_str = StringHelpers.uint2str(_locked_stake_min_time);\n\n        emit LockedStakeTimeForMaxMultiplier(locked_stake_time_for_max_multiplier);\n        emit LockedStakeMinTime(_locked_stake_min_time);\n    }\n\n    function initializeDefault() external onlyByOwnerOrGovernance {\n        lastUpdateTime = block.timestamp;\n        periodFinish = block.timestamp.add(rewardsDuration);\n        emit DefaultInitialization();\n    }\n\n    function greylistAddress(address _address) external onlyByOwnerOrGovernance {\n        greylist[_address] = !(greylist[_address]);\n    }\n\n    function unlockStakes() external onlyByOwnerOrGovernance {\n        unlockedStakes = !unlockedStakes;\n    }\n\n    function setRewardRate(uint256 _new_rate) external onlyByOwnerOrGovernance {\n        rewardRate = _new_rate;\n    }\n\n    function setOwnerAndTimelock(address _new_owner, address _new_timelock) external onlyByOwnerOrGovernance {\n        owner_address = _new_owner;\n        timelock_address = _new_timelock;\n    }\n\n    /* ========== MODIFIERS ========== */\n\n    modifier updateReward(address account) {\n        // Need to retro-adjust some things if the period hasn\u0027t been renewed, then start a new one\n        if (block.timestamp \u003e periodFinish) {\n            retroCatchUp();\n        }\n        else {\n            rewardPerTokenStored = rewardPerToken();\n            lastUpdateTime = lastTimeRewardApplicable();\n        }\n        if (account != address(0)) {\n            rewards[account] = earned(account);\n            userRewardPerTokenPaid[account] = rewardPerTokenStored;\n        }\n        _;\n    }\n\n    modifier onlyByOwnerOrGovernance() {\n        require(msg.sender == owner_address || msg.sender == timelock_address, \"You are not the owner or the governance timelock\");\n        _;\n    }\n\n    /* ========== EVENTS ========== */\n\n    event RewardAdded(uint256 reward);\n    event Staked(address indexed user, uint256 amount);\n    event StakeLocked(address indexed user, uint256 amount, uint256 secs);\n    event Withdrawn(address indexed user, uint256 amount);\n    event WithdrawnLocked(address indexed user, uint256 amount, bytes32 kek_id);\n    event RewardPaid(address indexed user, uint256 reward);\n    event RewardsDurationUpdated(uint256 newDuration);\n    event Recovered(address token, uint256 amount);\n    event RewardsPeriodRenewed(address token);\n    event DefaultInitialization();\n    event LockedStakeMaxMultiplierUpdated(uint256 multiplier);\n    event LockedStakeTimeForMaxMultiplier(uint256 secs);\n    event LockedStakeMinTime(uint256 secs);\n    event MaxCRBoostMultiplier(uint256 multiplier);\n}\n"},"StringHelpers.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\n\nlibrary StringHelpers {\n    function parseAddr(string memory _a) internal pure returns (address _parsedAddress) {\n        bytes memory tmp = bytes(_a);\n        uint160 iaddr = 0;\n        uint160 b1;\n        uint160 b2;\n        for (uint i = 2; i \u003c 2 + 2 * 20; i += 2) {\n            iaddr *= 256;\n            b1 = uint160(uint8(tmp[i]));\n            b2 = uint160(uint8(tmp[i + 1]));\n            if ((b1 \u003e= 97) \u0026\u0026 (b1 \u003c= 102)) {\n                b1 -= 87;\n            } else if ((b1 \u003e= 65) \u0026\u0026 (b1 \u003c= 70)) {\n                b1 -= 55;\n            } else if ((b1 \u003e= 48) \u0026\u0026 (b1 \u003c= 57)) {\n                b1 -= 48;\n            }\n            if ((b2 \u003e= 97) \u0026\u0026 (b2 \u003c= 102)) {\n                b2 -= 87;\n            } else if ((b2 \u003e= 65) \u0026\u0026 (b2 \u003c= 70)) {\n                b2 -= 55;\n            } else if ((b2 \u003e= 48) \u0026\u0026 (b2 \u003c= 57)) {\n                b2 -= 48;\n            }\n            iaddr += (b1 * 16 + b2);\n        }\n        return address(iaddr);\n    }\n\n    function strCompare(string memory _a, string memory _b) internal pure returns (int _returnCode) {\n        bytes memory a = bytes(_a);\n        bytes memory b = bytes(_b);\n        uint minLength = a.length;\n        if (b.length \u003c minLength) {\n            minLength = b.length;\n        }\n        for (uint i = 0; i \u003c minLength; i ++) {\n            if (a[i] \u003c b[i]) {\n                return -1;\n            } else if (a[i] \u003e b[i]) {\n                return 1;\n            }\n        }\n        if (a.length \u003c b.length) {\n            return -1;\n        } else if (a.length \u003e b.length) {\n            return 1;\n        } else {\n            return 0;\n        }\n    }\n\n    function indexOf(string memory _haystack, string memory _needle) internal pure returns (int _returnCode) {\n        bytes memory h = bytes(_haystack);\n        bytes memory n = bytes(_needle);\n        if (h.length \u003c 1 || n.length \u003c 1 || (n.length \u003e h.length)) {\n            return -1;\n        } else if (h.length \u003e (2 ** 128 - 1)) {\n            return -1;\n        } else {\n            uint subindex = 0;\n            for (uint i = 0; i \u003c h.length; i++) {\n                if (h[i] == n[0]) {\n                    subindex = 1;\n                    while(subindex \u003c n.length \u0026\u0026 (i + subindex) \u003c h.length \u0026\u0026 h[i + subindex] == n[subindex]) {\n                        subindex++;\n                    }\n                    if (subindex == n.length) {\n                        return int(i);\n                    }\n                }\n            }\n            return -1;\n        }\n    }\n\n    function strConcat(string memory _a, string memory _b) internal pure returns (string memory _concatenatedString) {\n        return strConcat(_a, _b, \"\", \"\", \"\");\n    }\n\n    function strConcat(string memory _a, string memory _b, string memory _c) internal pure returns (string memory _concatenatedString) {\n        return strConcat(_a, _b, _c, \"\", \"\");\n    }\n\n    function strConcat(string memory _a, string memory _b, string memory _c, string memory _d) internal pure returns (string memory _concatenatedString) {\n        return strConcat(_a, _b, _c, _d, \"\");\n    }\n\n    function strConcat(string memory _a, string memory _b, string memory _c, string memory _d, string memory _e) internal pure returns (string memory _concatenatedString) {\n        bytes memory _ba = bytes(_a);\n        bytes memory _bb = bytes(_b);\n        bytes memory _bc = bytes(_c);\n        bytes memory _bd = bytes(_d);\n        bytes memory _be = bytes(_e);\n        string memory abcde = new string(_ba.length + _bb.length + _bc.length + _bd.length + _be.length);\n        bytes memory babcde = bytes(abcde);\n        uint k = 0;\n        uint i = 0;\n        for (i = 0; i \u003c _ba.length; i++) {\n            babcde[k++] = _ba[i];\n        }\n        for (i = 0; i \u003c _bb.length; i++) {\n            babcde[k++] = _bb[i];\n        }\n        for (i = 0; i \u003c _bc.length; i++) {\n            babcde[k++] = _bc[i];\n        }\n        for (i = 0; i \u003c _bd.length; i++) {\n            babcde[k++] = _bd[i];\n        }\n        for (i = 0; i \u003c _be.length; i++) {\n            babcde[k++] = _be[i];\n        }\n        return string(babcde);\n    }\n\n    function safeParseInt(string memory _a) internal pure returns (uint _parsedInt) {\n        return safeParseInt(_a, 0);\n    }\n\n    function safeParseInt(string memory _a, uint _b) internal pure returns (uint _parsedInt) {\n        bytes memory bresult = bytes(_a);\n        uint mint = 0;\n        bool decimals = false;\n        for (uint i = 0; i \u003c bresult.length; i++) {\n            if ((uint(uint8(bresult[i])) \u003e= 48) \u0026\u0026 (uint(uint8(bresult[i])) \u003c= 57)) {\n                if (decimals) {\n                   if (_b == 0) break;\n                    else _b--;\n                }\n                mint *= 10;\n                mint += uint(uint8(bresult[i])) - 48;\n            } else if (uint(uint8(bresult[i])) == 46) {\n                require(!decimals, \u0027More than one decimal encountered in string!\u0027);\n                decimals = true;\n            } else {\n                revert(\"Non-numeral character encountered in string!\");\n            }\n        }\n        if (_b \u003e 0) {\n            mint *= 10 ** _b;\n        }\n        return mint;\n    }\n\n    function parseInt(string memory _a) internal pure returns (uint _parsedInt) {\n        return parseInt(_a, 0);\n    }\n\n    function parseInt(string memory _a, uint _b) internal pure returns (uint _parsedInt) {\n        bytes memory bresult = bytes(_a);\n        uint mint = 0;\n        bool decimals = false;\n        for (uint i = 0; i \u003c bresult.length; i++) {\n            if ((uint(uint8(bresult[i])) \u003e= 48) \u0026\u0026 (uint(uint8(bresult[i])) \u003c= 57)) {\n                if (decimals) {\n                   if (_b == 0) {\n                       break;\n                   } else {\n                       _b--;\n                   }\n                }\n                mint *= 10;\n                mint += uint(uint8(bresult[i])) - 48;\n            } else if (uint(uint8(bresult[i])) == 46) {\n                decimals = true;\n            }\n        }\n        if (_b \u003e 0) {\n            mint *= 10 ** _b;\n        }\n        return mint;\n    }\n\n    function uint2str(uint _i) internal pure returns (string memory _uintAsString) {\n        if (_i == 0) {\n            return \"0\";\n        }\n        uint j = _i;\n        uint len;\n        while (j != 0) {\n            len++;\n            j /= 10;\n        }\n        bytes memory bstr = new bytes(len);\n        uint k = len - 1;\n        while (_i != 0) {\n            bstr[k--] = byte(uint8(48 + _i % 10));\n            _i /= 10;\n        }\n        return string(bstr);\n    }\n}"},"SwapToPrice.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./IUniswapV2Pair.sol\u0027;\nimport \u0027./Babylonian.sol\u0027;\nimport \u0027./SafeMath.sol\u0027;\nimport \u0027./TransferHelper.sol\u0027;\nimport \u0027./IERC20.sol\u0027;\nimport \u0027./IUniswapV2Router01.sol\u0027;\nimport \u0027./UniswapV2Library.sol\u0027;\n\ncontract SwapToPrice {\n    using SafeMath for uint256;\n\n    IUniswapV2Router01 public immutable router;\n    address public immutable factory;\n\n    constructor(address factory_, IUniswapV2Router01 router_) public {\n        factory = factory_;\n        router = router_;\n    }\n\n    // computes the direction and magnitude of the profit-maximizing trade\n    function computeProfitMaximizingTrade(\n        uint256 truePriceTokenA,\n        uint256 truePriceTokenB,\n        uint256 reserveA,\n        uint256 reserveB\n    ) pure public returns (bool aToB, uint256 amountIn) {\n        aToB = reserveA.mul(truePriceTokenB) / reserveB \u003c truePriceTokenA;\n\n        uint256 invariant = reserveA.mul(reserveB);\n\n        uint256 leftSide = Babylonian.sqrt(\n            invariant.mul(aToB ? truePriceTokenA : truePriceTokenB).mul(1000) /\n            uint256(aToB ? truePriceTokenB : truePriceTokenA).mul(997)\n        );\n        uint256 rightSide = (aToB ? reserveA.mul(1000) : reserveB.mul(1000)) / 997;\n\n        // compute the amount that must be sent to move the price to the profit-maximizing price\n        amountIn = leftSide.sub(rightSide);\n    }\n\n    // swaps an amount of either token such that the trade is profit-maximizing, given an external true price\n    // true price is expressed in the ratio of token A to token B\n    // caller must approve this contract to spend whichever token is intended to be swapped\n    function swapToPrice(\n        address tokenA,\n        address tokenB,\n        uint256 truePriceTokenA,\n        uint256 truePriceTokenB,\n        uint256 maxSpendTokenA,\n        uint256 maxSpendTokenB,\n        address to,\n        uint256 deadline\n    ) public {\n        // true price is expressed as a ratio, so both values must be non-zero\n        require(truePriceTokenA != 0 \u0026\u0026 truePriceTokenB != 0, \"ExampleSwapToPrice: ZERO_PRICE\");\n        // caller can specify 0 for either if they wish to swap in only one direction, but not both\n        require(maxSpendTokenA != 0 || maxSpendTokenB != 0, \"ExampleSwapToPrice: ZERO_SPEND\");\n\n        bool aToB;\n        uint256 amountIn;\n        {\n            (uint256 reserveA, uint256 reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);\n            (aToB, amountIn) = computeProfitMaximizingTrade(\n                truePriceTokenA, truePriceTokenB,\n                reserveA, reserveB\n            );\n        }\n\n        // spend up to the allowance of the token in\n        uint256 maxSpend = aToB ? maxSpendTokenA : maxSpendTokenB;\n        if (amountIn \u003e maxSpend) {\n            amountIn = maxSpend;\n        }\n\n        address tokenIn = aToB ? tokenA : tokenB;\n        address tokenOut = aToB ? tokenB : tokenA;\n        TransferHelper.safeTransferFrom(tokenIn, msg.sender, address(this), amountIn);\n        TransferHelper.safeApprove(tokenIn, address(router), amountIn);\n\n        address[] memory path = new address[](2);\n        path[0] = tokenIn;\n        path[1] = tokenOut;\n\n        router.swapExactTokensForTokens(\n            amountIn,\n            0, // amountOutMin: we can skip computing this number because the math is tested\n            path,\n            to,\n            deadline\n        );\n    }\n}"},"TestSwap.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \"./FakeCollateral_USDT.sol\";\nimport \"./FakeCollateral_WETH.sol\";\nimport \"./UniswapV2Router02_Modified.sol\";\n\n/* IGNORE THIS CONTRACT, ONLY USED FOR TESTING PURPOSES */\n\ncontract TestSwap {\n\taddress public USDT_address;\n\taddress public WETH_address;\n\tUniswapV2Router02_Modified public router;\n\tFakeCollateral_USDT USDT = FakeCollateral_USDT(USDT);\n\tFakeCollateral_WETH WETH = FakeCollateral_WETH(WETH);\n\n\tconstructor( \n\t\taddress _USDT_address, \n\t\taddress _WETH_address,\n\t\tUniswapV2Router02_Modified _router_address\n\t) public {\n\t\tUSDT_address = _USDT_address;\n\t\tWETH_address = _WETH_address;\n\t\trouter = UniswapV2Router02_Modified(_router_address);\n\t}\n\n\tfunction getPath() public returns (address[] memory) {\n\t\taddress[] memory path = new address[](2);\n\t\tpath[0] = USDT_address;\n\t\tpath[1] = WETH_address;\n\t\treturn path;\n\t}\n\n\tfunction swapUSDTforETH(uint256 amountIn, uint256 amountOutMin) public payable {\n\t\trequire(USDT.transferFrom(msg.sender, address(this), amountIn), \"transferFrom failed.\");\n\t\trequire(USDT.approve(address(router), amountIn), \"approve failed.\");\n\n\t\taddress[] memory path = new address[](2);\n\t\tpath[0] = USDT_address;\n\t\tpath[1] = WETH_address;\n\n\t\trouter.swapExactTokensForETH(amountIn, amountOutMin, path, msg.sender, block.timestamp);\n\t}\n\n}"},"Timelock.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \"./SafeMath.sol\";\n\ncontract Timelock {\n    using SafeMath for uint;\n\n    event NewAdmin(address indexed newAdmin);\n    event NewPendingAdmin(address indexed newPendingAdmin);\n    event NewDelay(uint indexed newDelay);\n    event CancelTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature,  bytes data, uint eta);\n    event ExecuteTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature,  bytes data, uint eta);\n    event QueueTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta);\n\n    uint public constant GRACE_PERIOD = 14 days;\n    uint public constant MINIMUM_DELAY = 2 days;\n    uint public constant MAXIMUM_DELAY = 30 days;\n\n    address public admin;\n    address public pendingAdmin;\n    uint public delay;\n\n    mapping (bytes32 =\u003e bool) public queuedTransactions;\n\n\n    constructor(address admin_, uint delay_) public {\n        require(delay_ \u003e= MINIMUM_DELAY, \"Timelock::constructor: Delay must exceed minimum delay.\");\n        require(delay_ \u003c= MAXIMUM_DELAY, \"Timelock::setDelay: Delay must not exceed maximum delay.\");\n\n        admin = admin_;\n        delay = delay_;\n    }\n\n    //function() external payable { }\n\n    function setDelay(uint delay_) public {\n        require(msg.sender == address(this), \"Timelock::setDelay: Call must come from Timelock.\");\n        require(delay_ \u003e= MINIMUM_DELAY, \"Timelock::setDelay: Delay must exceed minimum delay.\");\n        require(delay_ \u003c= MAXIMUM_DELAY, \"Timelock::setDelay: Delay must not exceed maximum delay.\");\n        delay = delay_;\n\n        emit NewDelay(delay);\n    }\n\n    function acceptAdmin() public {\n        require(msg.sender == pendingAdmin, \"Timelock::acceptAdmin: Call must come from pendingAdmin.\");\n        admin = msg.sender;\n        pendingAdmin = address(0);\n\n        emit NewAdmin(admin);\n    }\n\n    function setPendingAdmin(address pendingAdmin_) public {\n        require(msg.sender == address(this), \"Timelock::setPendingAdmin: Call must come from Timelock.\");\n        pendingAdmin = pendingAdmin_;\n\n        emit NewPendingAdmin(pendingAdmin);\n    }\n\n    function queueTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public returns (bytes32) {\n        require(msg.sender == admin, \"Timelock::queueTransaction: Call must come from admin.\");\n        require(eta \u003e= getBlockTimestamp().add(delay), \"Timelock::queueTransaction: Estimated execution block must satisfy delay.\");\n\n        bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));\n        queuedTransactions[txHash] = true;\n\n        emit QueueTransaction(txHash, target, value, signature, data, eta);\n        return txHash;\n    }\n\n    function cancelTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public {\n        require(msg.sender == admin, \"Timelock::cancelTransaction: Call must come from admin.\");\n\n        bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));\n        queuedTransactions[txHash] = false;\n\n        emit CancelTransaction(txHash, target, value, signature, data, eta);\n    }\n\n    function executeTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public payable returns (bytes memory) {\n        require(msg.sender == admin, \"Timelock::executeTransaction: Call must come from admin.\");\n\n        bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));\n        require(queuedTransactions[txHash], \"Timelock::executeTransaction: Transaction hasn\u0027t been queued.\");\n        require(getBlockTimestamp() \u003e= eta, \"Timelock::executeTransaction: Transaction hasn\u0027t surpassed time lock.\");\n        require(getBlockTimestamp() \u003c= eta.add(GRACE_PERIOD), \"Timelock::executeTransaction: Transaction is stale.\");\n\n        queuedTransactions[txHash] = false;\n\n        bytes memory callData;\n\n        if (bytes(signature).length == 0) {\n            callData = data;\n        } else {\n            callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);\n        }\n\n        // Execute the call\n        (bool success, bytes memory returnData) = target.call{ value: value }(callData);\n        require(success, \"Timelock::executeTransaction: Transaction execution reverted.\");\n\n        emit ExecuteTransaction(txHash, target, value, signature, data, eta);\n\n        return returnData;\n    }\n\n    function getBlockTimestamp() internal view returns (uint) {\n        return block.timestamp;\n    }\n}"},"TokenVesting.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \"./ERC20Custom.sol\";\nimport \"./ERC20.sol\";\nimport \"./SafeMath.sol\";\n\n/**\n * @title TokenVesting\n * @dev A token holder contract that can release its token balance gradually like a\n * typical vesting scheme, with a cliff and vesting period. Optionally revocable by the\n * owner.\n * \n * Modified from OpenZeppelin\u0027s TokenVesting.sol draft\n */\ncontract TokenVesting {\n    // The vesting schedule is time-based (i.e. using block timestamps as opposed to e.g. block numbers), and is\n    // therefore sensitive to timestamp manipulation (which is something miners can do, to a certain degree). Therefore,\n    // it is recommended to avoid using short time durations (less than a minute). Typical vesting schemes, with a\n    // cliff period of a year and a duration of four years, are safe to use.\n    // solhint-disable not-rely-on-time\n\n    using SafeMath for uint256;\n\n    event TokensReleased(uint256 amount);\n    event TokenVestingRevoked();\n\n    // beneficiary of tokens after they are released\n    address private _beneficiary;\n\n    // owner (grantor) of the tokens\n    address private _owner;\n\n    // Durations and timestamps are expressed in UNIX time, the same units as block.timestamp.\n    uint256 private _cliff;\n    uint256 private _start;\n    uint256 private _duration;\n\n    address public _FXS_contract_address;\n    ERC20 FXS;\n    address public _timelock_address;\n    bool public _revocable;\n\n    uint256 private _released;\n    bool public _revoked;\n\n    /**\n     * @dev Creates a vesting contract that vests its balance of any ERC20 token to the\n     * beneficiary, gradually in a linear fashion until start + duration. By then all\n     * of the balance will have vested.\n     * @param beneficiary address of the beneficiary to whom vested tokens are transferred\n     * @param cliffDuration duration in seconds of the cliff in which tokens will begin to vest\n     * @param start the time (as Unix time) at which point vesting starts\n     * @param duration duration in seconds of the period in which the tokens will vest\n     * @param revocable whether the vesting is revocable or not\n     */\n\n    constructor(\n        address beneficiary,\n        uint256 start,\n        uint256 cliffDuration,\n        uint256 duration,\n        bool revocable\n    ) public {\n        require(beneficiary != address(0), \"TokenVesting: beneficiary is the zero address\");\n        // solhint-disable-next-line max-line-length\n        require(cliffDuration \u003c= duration, \"TokenVesting: cliff is longer than duration\");\n        require(duration \u003e 0, \"TokenVesting: duration is 0\");\n        // solhint-disable-next-line max-line-length\n        require(start.add(duration) \u003e block.timestamp, \"TokenVesting: final time is before current time\");\n\n        _beneficiary = beneficiary;\n        _revocable = revocable;\n        _duration = duration;\n        _cliff = start.add(cliffDuration);\n        _start = start;\n        _owner = msg.sender;\n    }\n\n    function setFXSAddress(address FXS_address) public {\n        require(msg.sender == _owner, \"must be set by the owner\");\n        _FXS_contract_address = FXS_address;\n        FXS = ERC20(FXS_address);\n    }\n\n    function setTimelockAddress(address timelock_address) public {\n        require(msg.sender == _owner, \"must be set by the owner\");\n        _timelock_address = timelock_address;\n    }\n\n    /**\n     * @return the beneficiary of the tokens.\n     */\n    function getBeneficiary() public view returns (address) {\n        return _beneficiary;\n    }\n\n    /**\n     * @return the cliff time of the token vesting.\n     */\n    function getCliff() public view returns (uint256) {\n        return _cliff;\n    }\n\n    /**\n     * @return the start time of the token vesting.\n     */\n    function getStart() public view returns (uint256) {\n        return _start;\n    }\n\n    /**\n     * @return the duration of the token vesting.\n     */\n    function getDuration() public view returns (uint256) {\n        return _duration;\n    }\n\n    /**\n     * @return true if the vesting is revocable.\n     */\n    function getRevocable() public view returns (bool) {\n        return _revocable;\n    }\n\n    /**\n     * @return the amount of the token released.\n     */\n    function getReleased() public view returns (uint256) {\n        return _released;\n    }\n\n    /**\n     * @return true if the token is revoked.\n     */\n    function getRevoked() public view returns (bool) {\n        return _revoked;\n    }\n\n    /**\n     * @notice Transfers vested tokens to beneficiary.\n     */\n    function release() public {\n        require(msg.sender == _beneficiary, \"must be the beneficiary to release tokens\");\n        uint256 unreleased = _releasableAmount();\n\n        require(unreleased \u003e 0, \"TokenVesting: no tokens are due\");\n\n        _released = _released.add(unreleased);\n\n        FXS.transfer(_beneficiary, unreleased);\n\n        emit TokensReleased(unreleased);\n    }\n\n    /**\n     * @notice Allows the owner to revoke the vesting. Tokens already vested\n     * remain in the contract, the rest are returned to the owner.\n     */\n    function revoke() public {\n        require(msg.sender == _timelock_address, \"Must be called by the timelock contract\");\n        require(_revocable, \"TokenVesting: cannot revoke\");\n        require(!_revoked, \"TokenVesting: token already revoked\");\n\n        uint256 balance = FXS.balanceOf(address(this));\n\n        uint256 unreleased = _releasableAmount();\n        uint256 refund = balance.sub(unreleased);\n\n        _revoked = true;\n\n        FXS.transfer(_owner, refund);\n\n        emit TokenVestingRevoked();\n    }\n\n    // Added to support recovering possible airdrops\n    function recoverERC20(address tokenAddress, uint256 tokenAmount) external {\n        require(msg.sender == _beneficiary, \"Must be called by the beneficiary\");\n\n        // Cannot recover the staking token or the rewards token\n        require(tokenAddress != _FXS_contract_address, \"Cannot withdraw the FXS through this function\");\n        ERC20(tokenAddress).transfer(_beneficiary, tokenAmount);\n    }\n\n\n    /**\n     * @dev Calculates the amount that has already vested but hasn\u0027t been released yet.\n     */\n    function _releasableAmount() private view returns (uint256) {\n        return _vestedAmount().sub(_released);\n    }\n\n    /**\n     * @dev Calculates the amount that has already vested.\n     */\n    function _vestedAmount() private view returns (uint256) {\n        uint256 currentBalance = FXS.balanceOf(address(this));\n        uint256 totalBalance = currentBalance.add(_released);\n        if (block.timestamp \u003c _cliff) {\n            return 0;\n        } else if (block.timestamp \u003e= _start.add(_duration) || _revoked) {\n            return totalBalance;\n        } else {\n            return totalBalance.mul(block.timestamp.sub(_start)).div(_duration);\n        }\n    }\n\n    uint256[44] private __gap;\n}\n"},"TransferHelper.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\n// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false\nlibrary TransferHelper {\n    function safeApprove(address token, address to, uint value) internal {\n        // bytes4(keccak256(bytes(\u0027approve(address,uint256)\u0027)));\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));\n        require(success \u0026\u0026 (data.length == 0 || abi.decode(data, (bool))), \u0027TransferHelper: APPROVE_FAILED\u0027);\n    }\n\n    function safeTransfer(address token, address to, uint value) internal {\n        // bytes4(keccak256(bytes(\u0027transfer(address,uint256)\u0027)));\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));\n        require(success \u0026\u0026 (data.length == 0 || abi.decode(data, (bool))), \u0027TransferHelper: TRANSFER_FAILED\u0027);\n    }\n\n    function safeTransferFrom(address token, address from, address to, uint value) internal {\n        // bytes4(keccak256(bytes(\u0027transferFrom(address,address,uint256)\u0027)));\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));\n        require(success \u0026\u0026 (data.length == 0 || abi.decode(data, (bool))), \u0027TransferHelper: TRANSFER_FROM_FAILED\u0027);\n    }\n\n    function safeTransferETH(address to, uint value) internal {\n        (bool success,) = to.call{value:value}(new bytes(0));\n        require(success, \u0027TransferHelper: ETH_TRANSFER_FAILED\u0027);\n    }\n}"},"UniswapPairOracle.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./IUniswapV2Factory.sol\u0027;\nimport \u0027./IUniswapV2Pair.sol\u0027;\nimport \u0027./FixedPoint.sol\u0027;\n\nimport \u0027./UniswapV2OracleLibrary.sol\u0027;\nimport \u0027./UniswapV2Library.sol\u0027;\n\n// Fixed window oracle that recomputes the average price for the entire period once every period\n// Note that the price average is only guaranteed to be over at least 1 period, but may be over a longer period\ncontract UniswapPairOracle {\n    using FixedPoint for *;\n    \n    address owner_address;\n    address timelock_address;\n\n    uint public PERIOD = 3600; // 1 hour TWAP (time-weighted average price)\n\n    IUniswapV2Pair public immutable pair;\n    address public immutable token0;\n    address public immutable token1;\n\n    uint    public price0CumulativeLast;\n    uint    public price1CumulativeLast;\n    uint32  public blockTimestampLast;\n    FixedPoint.uq112x112 public price0Average;\n    FixedPoint.uq112x112 public price1Average;\n\n    modifier onlyByOwnerOrGovernance() {\n        require(msg.sender == owner_address || msg.sender == timelock_address, \"You are not an owner or the governance timelock\");\n        _;\n    }\n\n    constructor(address factory, address tokenA, address tokenB, address _owner_address, address _timelock_address) public {\n        IUniswapV2Pair _pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, tokenA, tokenB));\n        pair = _pair;\n        token0 = _pair.token0();\n        token1 = _pair.token1();\n        price0CumulativeLast = _pair.price0CumulativeLast(); // Fetch the current accumulated price value (1 / 0)\n        price1CumulativeLast = _pair.price1CumulativeLast(); // Fetch the current accumulated price value (0 / 1)\n        uint112 reserve0;\n        uint112 reserve1;\n        (reserve0, reserve1, blockTimestampLast) = _pair.getReserves();\n        require(reserve0 != 0 \u0026\u0026 reserve1 != 0, \u0027UniswapPairOracle: NO_RESERVES\u0027); // Ensure that there\u0027s liquidity in the pair\n\n        owner_address = _owner_address;\n        timelock_address = _timelock_address;\n    }\n\n    function setOwner(address _owner_address) external onlyByOwnerOrGovernance {\n        owner_address = _owner_address;\n    }\n\n    function setTimelock(address _timelock_address) external onlyByOwnerOrGovernance {\n        timelock_address = _timelock_address;\n    }\n\n    function setPeriod(uint _period) external onlyByOwnerOrGovernance {\n        PERIOD = _period;\n    }\n\n    function update() external {\n        (uint price0Cumulative, uint price1Cumulative, uint32 blockTimestamp) =\n            UniswapV2OracleLibrary.currentCumulativePrices(address(pair));\n        uint32 timeElapsed = blockTimestamp - blockTimestampLast; // Overflow is desired\n\n        // Ensure that at least one full period has passed since the last update\n        require(timeElapsed \u003e= PERIOD, \u0027UniswapPairOracle: PERIOD_NOT_ELAPSED\u0027);\n\n        // Overflow is desired, casting never truncates\n        // Cumulative price is in (uq112x112 price * seconds) units so we simply wrap it after division by time elapsed\n        price0Average = FixedPoint.uq112x112(uint224((price0Cumulative - price0CumulativeLast) / timeElapsed));\n        price1Average = FixedPoint.uq112x112(uint224((price1Cumulative - price1CumulativeLast) / timeElapsed));\n\n        price0CumulativeLast = price0Cumulative;\n        price1CumulativeLast = price1Cumulative;\n        blockTimestampLast = blockTimestamp;\n    }\n\n    // Note this will always return 0 before update has been called successfully for the first time.\n    function consult(address token, uint amountIn) external view returns (uint amountOut) {\n        if (token == token0) {\n            amountOut = price0Average.mul(amountIn).decode144();\n        } else {\n            require(token == token1, \u0027UniswapPairOracle: INVALID_TOKEN\u0027);\n            amountOut = price1Average.mul(amountIn).decode144();\n        }\n    }\n}\n"},"UniswapPairOracle_FRAX_FXS.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./UniswapPairOracle.sol\u0027;\n\n// Fixed window oracle that recomputes the average price for the entire period once every period\n// Note that the price average is only guaranteed to be over at least 1 period, but may be over a longer period\ncontract UniswapPairOracle_FRAX_FXS is UniswapPairOracle {\n    constructor(address factory, address tokenA, address tokenB, address owner_address, address timelock_address) \n    UniswapPairOracle(factory, tokenA, tokenB, owner_address, timelock_address) \n    public {}\n}"},"UniswapPairOracle_FRAX_USDC.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./UniswapPairOracle.sol\u0027;\n\n// Fixed window oracle that recomputes the average price for the entire period once every period\n// Note that the price average is only guaranteed to be over at least 1 period, but may be over a longer period\ncontract UniswapPairOracle_FRAX_USDC is UniswapPairOracle {\n    constructor(address factory, address tokenA, address tokenB, address owner_address, address timelock_address) \n    UniswapPairOracle(factory, tokenA, tokenB, owner_address, timelock_address) \n    public {}\n}\n"},"UniswapPairOracle_FRAX_USDT.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./UniswapPairOracle.sol\u0027;\n\n// Fixed window oracle that recomputes the average price for the entire period once every period\n// Note that the price average is only guaranteed to be over at least 1 period, but may be over a longer period\ncontract UniswapPairOracle_FRAX_USDT is UniswapPairOracle {\n    constructor(address factory, address tokenA, address tokenB, address owner_address, address timelock_address) \n    UniswapPairOracle(factory, tokenA, tokenB, owner_address, timelock_address)  \n    public {}\n}\n"},"UniswapPairOracle_FRAX_WETH.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./UniswapPairOracle.sol\u0027;\n\n// Fixed window oracle that recomputes the average price for the entire period once every period\n// Note that the price average is only guaranteed to be over at least 1 period, but may be over a longer period\ncontract UniswapPairOracle_FRAX_WETH is UniswapPairOracle {\n    constructor(address factory, address tokenA, address tokenB, address owner_address, address timelock_address) \n    UniswapPairOracle(factory, tokenA, tokenB, owner_address, timelock_address) \n    public {}\n}"},"UniswapPairOracle_FXS_USDC.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./UniswapPairOracle.sol\u0027;\n\n// Fixed window oracle that recomputes the average price for the entire period once every period\n// Note that the price average is only guaranteed to be over at least 1 period, but may be over a longer period\ncontract UniswapPairOracle_FXS_USDC is UniswapPairOracle {\n    constructor(address factory, address tokenA, address tokenB, address owner_address, address timelock_address) \n    UniswapPairOracle(factory, tokenA, tokenB, owner_address, timelock_address) \n    public {}\n}"},"UniswapPairOracle_FXS_USDT.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./UniswapPairOracle.sol\u0027;\n\n// Fixed window oracle that recomputes the average price for the entire period once every period\n// Note that the price average is only guaranteed to be over at least 1 period, but may be over a longer period\ncontract UniswapPairOracle_FXS_USDT is UniswapPairOracle {\n    constructor(address factory, address tokenA, address tokenB, address owner_address, address timelock_address) \n    UniswapPairOracle(factory, tokenA, tokenB, owner_address, timelock_address) \n    public {}\n}"},"UniswapPairOracle_FXS_WETH.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./UniswapPairOracle.sol\u0027;\n\n// Fixed window oracle that recomputes the average price for the entire period once every period\n// Note that the price average is only guaranteed to be over at least 1 period, but may be over a longer period\ncontract UniswapPairOracle_FXS_WETH is UniswapPairOracle {\n    constructor(address factory, address tokenA, address tokenB, address owner_address, address timelock_address) \n    UniswapPairOracle(factory, tokenA, tokenB, owner_address, timelock_address) \n    public {}\n}"},"UniswapPairOracle_USDC_WETH.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./UniswapPairOracle.sol\u0027;\n\n// Fixed window oracle that recomputes the average price for the entire period once every period\n// Note that the price average is only guaranteed to be over at least 1 period, but may be over a longer period\ncontract UniswapPairOracle_USDC_WETH is UniswapPairOracle {\n    constructor(address factory, address tokenA, address tokenB, address owner_address, address timelock_address) \n    UniswapPairOracle(factory, tokenA, tokenB, owner_address, timelock_address) \n    public {}\n}\n"},"UniswapPairOracle_USDT_WETH.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./UniswapPairOracle.sol\u0027;\n\n// Fixed window oracle that recomputes the average price for the entire period once every period\n// Note that the price average is only guaranteed to be over at least 1 period, but may be over a longer period\ncontract UniswapPairOracle_USDT_WETH is UniswapPairOracle {\n    constructor(address factory, address tokenA, address tokenB, address owner_address, address timelock_address) \n    UniswapPairOracle(factory, tokenA, tokenB, owner_address, timelock_address) \n    public {}\n}\n"},"UniswapV2ERC20.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./IUniswapV2ERC20.sol\u0027;\nimport \u0027./SafeMath.sol\u0027;\n\ncontract UniswapV2ERC20 is IUniswapV2ERC20 {\n    using SafeMath for uint;\n\n    string public override constant name = \u0027Uniswap V2\u0027;\n    string public override constant symbol = \u0027UNI-V2\u0027;\n    uint8 public override constant decimals = 18;\n    uint  public override totalSupply;\n    mapping(address =\u003e uint) public override balanceOf;\n    mapping(address =\u003e mapping(address =\u003e uint)) public override allowance;\n\n    bytes32 public override DOMAIN_SEPARATOR;\n    // keccak256(\"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\");\n    bytes32 public constant override PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;\n    mapping(address =\u003e uint) public override nonces;\n\n    event Approval(address indexed owner, address indexed spender, uint value);\n    event Transfer(address indexed from, address indexed to, uint value);\n\n    constructor() public {\n        uint chainId;\n        assembly {\n            chainId := chainid()\n        }\n        DOMAIN_SEPARATOR = keccak256(\n            abi.encode(\n                keccak256(\u0027EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\u0027),\n                keccak256(bytes(name)),\n                keccak256(bytes(\u00271\u0027)),\n                chainId,\n                address(this)\n            )\n        );\n    }\n\n    function _mint(address to, uint value) internal {\n        totalSupply = totalSupply.add(value);\n        balanceOf[to] = balanceOf[to].add(value);\n        emit Transfer(address(0), to, value);\n    }\n\n    function _burn(address from, uint value) internal {\n        balanceOf[from] = balanceOf[from].sub(value);\n        totalSupply = totalSupply.sub(value);\n        emit Transfer(from, address(0), value);\n    }\n\n    function _approve(address owner, address spender, uint value) private {\n        allowance[owner][spender] = value;\n        emit Approval(owner, spender, value);\n    }\n\n    function _transfer(address from, address to, uint value) private {\n        balanceOf[from] = balanceOf[from].sub(value);\n        balanceOf[to] = balanceOf[to].add(value);\n        emit Transfer(from, to, value);\n    }\n\n    function approve(address spender, uint value) external override returns (bool) {\n        _approve(msg.sender, spender, value);\n        return true;\n    }\n\n    function transfer(address to, uint value) external override returns (bool) {\n        _transfer(msg.sender, to, value);\n        return true;\n    }\n\n    function transferFrom(address from, address to, uint value) external override returns (bool) {\n        if (allowance[from][msg.sender] != uint(-1)) {\n            allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);\n        }\n        _transfer(from, to, value);\n        return true;\n    }\n\n    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external override {\n        require(deadline \u003e= block.timestamp, \u0027UniswapV2: EXPIRED\u0027);\n        bytes32 digest = keccak256(\n            abi.encodePacked(\n                \u0027\\x19\\x01\u0027,\n                DOMAIN_SEPARATOR,\n                keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))\n            )\n        );\n        address recoveredAddress = ecrecover(digest, v, r, s);\n        require(recoveredAddress != address(0) \u0026\u0026 recoveredAddress == owner, \u0027UniswapV2: INVALID_SIGNATURE\u0027);\n        _approve(owner, spender, value);\n    }\n}\n"},"UniswapV2Factory.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./IUniswapV2Factory.sol\u0027;\nimport \u0027./UniswapV2Pair.sol\u0027;\n\ncontract UniswapV2Factory is IUniswapV2Factory {\n    address public override feeTo;\n    address public override feeToSetter;\n\n    mapping(address =\u003e mapping(address =\u003e address)) public override getPair;\n    address[] public override allPairs;\n\n    event PairCreated(address indexed token0, address indexed token1, address pair, uint);\n\n    constructor(address _feeToSetter) public {\n        feeToSetter = _feeToSetter;\n    }\n\n    function allPairsLength() external override view returns (uint) {\n        return allPairs.length;\n    }\n\n    function createPair(address tokenA, address tokenB) external override returns (address pair) {\n        require(tokenA != tokenB, \u0027UniswapV2: IDENTICAL_ADDRESSES\u0027);\n        (address token0, address token1) = tokenA \u003c tokenB ? (tokenA, tokenB) : (tokenB, tokenA);\n        require(token0 != address(0), \u0027UniswapV2: ZERO_ADDRESS\u0027);\n        require(getPair[token0][token1] == address(0), \u0027UniswapV2: PAIR_EXISTS\u0027); // single check is sufficient\n        bytes memory bytecode = type(UniswapV2Pair).creationCode;\n        bytes32 salt = keccak256(abi.encodePacked(token0, token1));\n\n        // This creates a new contract\n        assembly {\n            pair := create2(0, add(bytecode, 32), mload(bytecode), salt)\n        }\n        IUniswapV2Pair(pair).initialize(token0, token1);\n        getPair[token0][token1] = pair;\n        getPair[token1][token0] = pair; // populate mapping in the reverse direction\n        allPairs.push(pair);\n        emit PairCreated(token0, token1, pair, allPairs.length);\n    }\n\n    function setFeeTo(address _feeTo) external override {\n        require(msg.sender == feeToSetter, \u0027UniswapV2: FORBIDDEN\u0027);\n        feeTo = _feeTo;\n    }\n\n    function setFeeToSetter(address _feeToSetter) external override {\n        require(msg.sender == feeToSetter, \u0027UniswapV2: FORBIDDEN\u0027);\n        feeToSetter = _feeToSetter;\n    }\n}\n"},"UniswapV2Library.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./IUniswapV2Pair.sol\u0027;\nimport \u0027./IUniswapV2Factory.sol\u0027;\n\nimport \"./SafeMath.sol\";\n\nlibrary UniswapV2Library {\n    using SafeMath for uint;\n\n    // returns sorted token addresses, used to handle return values from pairs sorted in this order\n    function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {\n        require(tokenA != tokenB, \u0027UniswapV2Library: IDENTICAL_ADDRESSES\u0027);\n        (token0, token1) = tokenA \u003c tokenB ? (tokenA, tokenB) : (tokenB, tokenA);\n        require(token0 != address(0), \u0027UniswapV2Library: ZERO_ADDRESS\u0027);\n    }\n\n    // Less efficient than the CREATE2 method below\n    function pairFor(address factory, address tokenA, address tokenB) internal view returns (address pair) {\n        (address token0, address token1) = sortTokens(tokenA, tokenB);\n        pair = IUniswapV2Factory(factory).getPair(token0, token1);\n    }\n\n    // calculates the CREATE2 address for a pair without making any external calls\n    function pairForCreate2(address factory, address tokenA, address tokenB) internal pure returns (address pair) {\n        (address token0, address token1) = sortTokens(tokenA, tokenB);\n        pair = address(uint(keccak256(abi.encodePacked(\n                hex\u0027ff\u0027,\n                factory,\n                keccak256(abi.encodePacked(token0, token1)),\n                hex\u002796e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f\u0027 // init code hash\n            )))); // this matches the CREATE2 in UniswapV2Factory.createPair\n    }\n\n    // fetches and sorts the reserves for a pair\n    function getReserves(address factory, address tokenA, address tokenB) internal view returns (uint reserveA, uint reserveB) {\n        (address token0,) = sortTokens(tokenA, tokenB);\n        (uint reserve0, uint reserve1,) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();\n        (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);\n    }\n\n    // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset\n    function quote(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) {\n        require(amountA \u003e 0, \u0027UniswapV2Library: INSUFFICIENT_AMOUNT\u0027);\n        require(reserveA \u003e 0 \u0026\u0026 reserveB \u003e 0, \u0027UniswapV2Library: INSUFFICIENT_LIQUIDITY\u0027);\n        amountB = amountA.mul(reserveB) / reserveA;\n    }\n\n    // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset\n    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {\n        require(amountIn \u003e 0, \u0027UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT\u0027);\n        require(reserveIn \u003e 0 \u0026\u0026 reserveOut \u003e 0, \u0027UniswapV2Library: INSUFFICIENT_LIQUIDITY\u0027);\n        uint amountInWithFee = amountIn.mul(997);\n        uint numerator = amountInWithFee.mul(reserveOut);\n        uint denominator = reserveIn.mul(1000).add(amountInWithFee);\n        amountOut = numerator / denominator;\n    }\n\n    // given an output amount of an asset and pair reserves, returns a required input amount of the other asset\n    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) {\n        require(amountOut \u003e 0, \u0027UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT\u0027);\n        require(reserveIn \u003e 0 \u0026\u0026 reserveOut \u003e 0, \u0027UniswapV2Library: INSUFFICIENT_LIQUIDITY\u0027);\n        uint numerator = reserveIn.mul(amountOut).mul(1000);\n        uint denominator = reserveOut.sub(amountOut).mul(997);\n        amountIn = (numerator / denominator).add(1);\n    }\n\n    // performs chained getAmountOut calculations on any number of pairs\n    function getAmountsOut(address factory, uint amountIn, address[] memory path) internal view returns (uint[] memory amounts) {\n        require(path.length \u003e= 2, \u0027UniswapV2Library: INVALID_PATH\u0027);\n        amounts = new uint[](path.length);\n        amounts[0] = amountIn;\n        for (uint i; i \u003c path.length - 1; i++) {\n            (uint reserveIn, uint reserveOut) = getReserves(factory, path[i], path[i + 1]);\n            amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);\n        }\n    }\n\n    // performs chained getAmountIn calculations on any number of pairs\n    function getAmountsIn(address factory, uint amountOut, address[] memory path) internal view returns (uint[] memory amounts) {\n        require(path.length \u003e= 2, \u0027UniswapV2Library: INVALID_PATH\u0027);\n        amounts = new uint[](path.length);\n        amounts[amounts.length - 1] = amountOut;\n        for (uint i = path.length - 1; i \u003e 0; i--) {\n            (uint reserveIn, uint reserveOut) = getReserves(factory, path[i - 1], path[i]);\n            amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);\n        }\n    }\n}"},"UniswapV2OracleLibrary.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./IUniswapV2Pair.sol\u0027;\nimport \u0027./FixedPoint.sol\u0027;\n\n// library with helper methods for oracles that are concerned with computing average prices\nlibrary UniswapV2OracleLibrary {\n    using FixedPoint for *;\n\n    // helper function that returns the current block timestamp within the range of uint32, i.e. [0, 2**32 - 1]\n    function currentBlockTimestamp() internal view returns (uint32) {\n        return uint32(block.timestamp % 2 ** 32);\n    }\n\n    // produces the cumulative price using counterfactuals to save gas and avoid a call to sync.\n    function currentCumulativePrices(\n        address pair\n    ) internal view returns (uint price0Cumulative, uint price1Cumulative, uint32 blockTimestamp) {\n        blockTimestamp = currentBlockTimestamp();\n        price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast();\n        price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast();\n\n        // if time has elapsed since the last update on the pair, mock the accumulated price values\n        (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = IUniswapV2Pair(pair).getReserves();\n        if (blockTimestampLast != blockTimestamp) {\n            // subtraction overflow is desired\n            uint32 timeElapsed = blockTimestamp - blockTimestampLast;\n            // addition overflow is desired\n            // counterfactual\n            price0Cumulative += uint(FixedPoint.fraction(reserve1, reserve0)._x) * timeElapsed;\n            // counterfactual\n            price1Cumulative += uint(FixedPoint.fraction(reserve0, reserve1)._x) * timeElapsed;\n        }\n    }\n}"},"UniswapV2Pair.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\n\nimport \u0027./IUniswapV2Pair.sol\u0027;\nimport \u0027./UniswapV2ERC20.sol\u0027;\nimport \u0027./Math.sol\u0027;\nimport \u0027./UQ112x112.sol\u0027;\nimport \u0027./IERC20.sol\u0027;\nimport \u0027./IUniswapV2Factory.sol\u0027;\nimport \u0027./IUniswapV2Callee.sol\u0027;\n\ncontract UniswapV2Pair is IUniswapV2Pair {\n    using SafeMath  for uint;\n    using UQ112x112 for uint224;\n\n    string public override constant name = \u0027Uniswap V2\u0027;\n    string public override constant symbol = \u0027UNI-V2\u0027;\n    uint8 public override constant decimals = 18;\n    uint  public override totalSupply;\n    mapping(address =\u003e uint) public override balanceOf;\n    mapping(address =\u003e mapping(address =\u003e uint)) public override allowance;\n\n    uint public override constant MINIMUM_LIQUIDITY = 10**3;\n    bytes4 private constant SELECTOR = bytes4(keccak256(bytes(\u0027transfer(address,uint256)\u0027)));\n    bytes32 public override DOMAIN_SEPARATOR;\n    // keccak256(\"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\");\n    bytes32 public constant override PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;\n    mapping(address =\u003e uint) public override nonces;\n\n\n    \n\n    address public override factory;\n    address public override token0;\n    address public override token1;\n\n    uint112 private reserve0;           // uses single storage slot, accessible via getReserves\n    uint112 private reserve1;           // uses single storage slot, accessible via getReserves\n    uint32  private blockTimestampLast; // uses single storage slot, accessible via getReserves\n\n    uint public override price0CumulativeLast;\n    uint public override price1CumulativeLast;\n    uint public override kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event\n\n    uint private unlocked = 1;\n    modifier lock() {\n        require(unlocked == 1, \u0027UniswapV2: LOCKED\u0027);\n        unlocked = 0;\n        _;\n        unlocked = 1;\n    }\n\n    function getReserves() public override view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {\n        _reserve0 = reserve0;\n        _reserve1 = reserve1;\n        _blockTimestampLast = blockTimestampLast;\n    }\n\n    function _safeTransfer(address token, address to, uint value) private {\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));\n        require(success \u0026\u0026 (data.length == 0 || abi.decode(data, (bool))), \u0027UniswapV2: TRANSFER_FAILED\u0027);\n    }\n\n    event Mint(address indexed sender, uint amount0, uint amount1);\n    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);\n    event Swap(\n        address indexed sender,\n        uint amount0In,\n        uint amount1In,\n        uint amount0Out,\n        uint amount1Out,\n        address indexed to\n    );\n    event Sync(uint112 reserve0, uint112 reserve1);\n\n    constructor() public {\n        factory = msg.sender;\n    }\n\n    // called once by the factory at time of deployment\n    function initialize(address _token0, address _token1) external override {\n        require(msg.sender == factory, \u0027UniswapV2: FORBIDDEN\u0027); // sufficient check\n        token0 = _token0;\n        token1 = _token1;\n    }\n\n    // update reserves and, on the first call per block, price accumulators\n    function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {\n        require(balance0 \u003c= uint112(-1) \u0026\u0026 balance1 \u003c= uint112(-1), \u0027UniswapV2: OVERFLOW\u0027);\n        uint32 blockTimestamp = uint32(block.timestamp % 2**32);\n        uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired\n        if (timeElapsed \u003e 0 \u0026\u0026 _reserve0 != 0 \u0026\u0026 _reserve1 != 0) {\n            // * never overflows, and + overflow is desired\n            price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;\n            price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;\n        }\n        reserve0 = uint112(balance0);\n        reserve1 = uint112(balance1);\n        blockTimestampLast = blockTimestamp;\n        emit Sync(reserve0, reserve1);\n    }\n\n    // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)\n    function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {\n        address feeTo = IUniswapV2Factory(factory).feeTo();\n        feeOn = feeTo != address(0);\n        uint _kLast = kLast; // gas savings\n        if (feeOn) {\n            if (_kLast != 0) {\n                uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));\n                uint rootKLast = Math.sqrt(_kLast);\n                if (rootK \u003e rootKLast) {\n                    uint numerator = totalSupply.mul(rootK.sub(rootKLast));\n                    uint denominator = rootK.mul(5).add(rootKLast);\n                    uint liquidity = numerator / denominator;\n                    if (liquidity \u003e 0) _mint(feeTo, liquidity);\n                }\n            }\n        } else if (_kLast != 0) {\n            kLast = 0;\n        }\n    }\n\n    // this low-level function should be called from a contract which performs important safety checks\n    function mint(address to) external override lock returns (uint liquidity) {\n        (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings\n        uint balance0 = IERC20(token0).balanceOf(address(this));\n        uint balance1 = IERC20(token1).balanceOf(address(this));\n        uint amount0 = balance0.sub(_reserve0);\n        uint amount1 = balance1.sub(_reserve1);\n        bool feeOn = _mintFee(_reserve0, _reserve1);\n        uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee\n\n        if (_totalSupply == 0) {\n            liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);\n           _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens\n        } else {\n            liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);\n        }\n\n        require(liquidity \u003e 0, \u0027UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED\u0027);\n        _mint(to, liquidity);\n\n        _update(balance0, balance1, _reserve0, _reserve1);\n        if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date\n        emit Mint(msg.sender, amount0, amount1);\n    }\n\n    // this low-level function should be called from a contract which performs important safety checks\n    function burn(address to) external override lock returns (uint amount0, uint amount1) {\n        (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings\n        address _token0 = token0;                                // gas savings\n        address _token1 = token1;                                // gas savings\n        uint balance0 = IERC20(_token0).balanceOf(address(this));\n        uint balance1 = IERC20(_token1).balanceOf(address(this));\n        uint liquidity = balanceOf[address(this)];\n\n        bool feeOn = _mintFee(_reserve0, _reserve1);\n        uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee\n        amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution\n        amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution\n        require(amount0 \u003e 0 \u0026\u0026 amount1 \u003e 0, \u0027UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED\u0027);\n        _burn(address(this), liquidity);\n        _safeTransfer(_token0, to, amount0);\n        _safeTransfer(_token1, to, amount1);\n        balance0 = IERC20(_token0).balanceOf(address(this));\n        balance1 = IERC20(_token1).balanceOf(address(this));\n\n        _update(balance0, balance1, _reserve0, _reserve1);\n        if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date\n        emit Burn(msg.sender, amount0, amount1, to);\n    }\n\n    // this low-level function should be called from a contract which performs important safety checks\n    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external override lock {\n        require(amount0Out \u003e 0 || amount1Out \u003e 0, \u0027UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT\u0027);\n        (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings\n        require(amount0Out \u003c _reserve0 \u0026\u0026 amount1Out \u003c _reserve1, \u0027UniswapV2: INSUFFICIENT_LIQUIDITY\u0027);\n\n        uint balance0;\n        uint balance1;\n        { // scope for _token{0,1}, avoids stack too deep errors\n        address _token0 = token0;\n        address _token1 = token1;\n        require(to != _token0 \u0026\u0026 to != _token1, \u0027UniswapV2: INVALID_TO\u0027);\n        if (amount0Out \u003e 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens\n        if (amount1Out \u003e 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens\n        if (data.length \u003e 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);\n        balance0 = IERC20(_token0).balanceOf(address(this));\n        balance1 = IERC20(_token1).balanceOf(address(this));\n        }\n        uint amount0In = balance0 \u003e _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;\n        uint amount1In = balance1 \u003e _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;\n        require(amount0In \u003e 0 || amount1In \u003e 0, \u0027UniswapV2: INSUFFICIENT_INPUT_AMOUNT\u0027);\n        { // scope for reserve{0,1}Adjusted, avoids stack too deep errors\n        uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));\n        uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));\n        require(balance0Adjusted.mul(balance1Adjusted) \u003e= uint(_reserve0).mul(_reserve1).mul(1000**2), \u0027UniswapV2: K\u0027);\n        }\n\n        _update(balance0, balance1, _reserve0, _reserve1);\n        emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);\n    }\n\n    // force balances to match reserves\n    function skim(address to) external override lock {\n        address _token0 = token0; // gas savings\n        address _token1 = token1; // gas savings\n        _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));\n        _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));\n    }\n\n    // force reserves to match balances\n    function sync() external override lock {\n        _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);\n    }\n\n\n\n    // Migrated over from UniswapV2ERC20. Needed for ^0.6.0\n    // ===============================================\n\n    function _mint(address to, uint value) internal {\n        totalSupply = totalSupply.add(value);\n        balanceOf[to] = balanceOf[to].add(value);\n        emit Transfer(address(0), to, value);\n    }\n\n    function _burn(address from, uint value) internal {\n        balanceOf[from] = balanceOf[from].sub(value);\n        totalSupply = totalSupply.sub(value);\n        emit Transfer(from, address(0), value);\n    }\n\n    function _approve(address owner, address spender, uint value) private {\n        allowance[owner][spender] = value;\n        emit Approval(owner, spender, value);\n    }\n\n    function _transfer(address from, address to, uint value) private {\n        balanceOf[from] = balanceOf[from].sub(value);\n        balanceOf[to] = balanceOf[to].add(value);\n        emit Transfer(from, to, value);\n    }\n\n    function approve(address spender, uint value) external override returns (bool) {\n        _approve(msg.sender, spender, value);\n        return true;\n    }\n\n    function transfer(address to, uint value) external override returns (bool) {\n        _transfer(msg.sender, to, value);\n        return true;\n    }\n\n    function transferFrom(address from, address to, uint value) external override returns (bool) {\n        if (allowance[from][msg.sender] != uint(-1)) {\n            allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);\n        }\n        _transfer(from, to, value);\n        return true;\n    }\n\n    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external override {\n        require(deadline \u003e= block.timestamp, \u0027UniswapV2: EXPIRED\u0027);\n        bytes32 digest = keccak256(\n            abi.encodePacked(\n                \u0027\\x19\\x01\u0027,\n                DOMAIN_SEPARATOR,\n                keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))\n            )\n        );\n        address recoveredAddress = ecrecover(digest, v, r, s);\n        require(recoveredAddress != address(0) \u0026\u0026 recoveredAddress == owner, \u0027UniswapV2: INVALID_SIGNATURE\u0027);\n        _approve(owner, spender, value);\n    }\n\n\n\n}"},"UniswapV2Router02.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./IUniswapV2Factory.sol\u0027;\nimport \u0027./TransferHelper.sol\u0027;\n\nimport \u0027./IUniswapV2Router02.sol\u0027;\nimport \u0027./UniswapV2Library.sol\u0027;\nimport \u0027./SafeMath.sol\u0027;\nimport \u0027./IERC20.sol\u0027;\nimport \u0027./IWETH.sol\u0027;\n\ncontract UniswapV2Router02 is IUniswapV2Router02 {\n    using SafeMath for uint;\n\n    address public immutable override factory;\n    address public immutable override WETH;\n\n    modifier ensure(uint deadline) {\n        require(deadline \u003e= block.timestamp, \u0027UniswapV2Router: EXPIRED\u0027);\n        _;\n    }\n\n    constructor(address _factory, address _WETH) public {\n        factory = _factory;\n        WETH = _WETH;\n    }\n\n    receive() external payable {\n        assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract\n    }\n\n    // **** ADD LIQUIDITY ****\n    function _addLiquidity(\n        address tokenA,\n        address tokenB,\n        uint amountADesired,\n        uint amountBDesired,\n        uint amountAMin,\n        uint amountBMin\n    ) internal virtual returns (uint amountA, uint amountB) {\n        // create the pair if it doesn\u0027t exist yet\n        if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {\n            IUniswapV2Factory(factory).createPair(tokenA, tokenB);\n        }\n        (uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);\n        if (reserveA == 0 \u0026\u0026 reserveB == 0) {\n            (amountA, amountB) = (amountADesired, amountBDesired);\n        } else {\n            uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB);\n            if (amountBOptimal \u003c= amountBDesired) {\n                require(amountBOptimal \u003e= amountBMin, \u0027UniswapV2Router: INSUFFICIENT_B_AMOUNT\u0027);\n                (amountA, amountB) = (amountADesired, amountBOptimal);\n            } else {\n                uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA);\n                assert(amountAOptimal \u003c= amountADesired);\n                require(amountAOptimal \u003e= amountAMin, \u0027UniswapV2Router: INSUFFICIENT_A_AMOUNT\u0027);\n                (amountA, amountB) = (amountAOptimal, amountBDesired);\n            }\n        }\n    }\n    function addLiquidity(\n        address tokenA,\n        address tokenB,\n        uint amountADesired,\n        uint amountBDesired,\n        uint amountAMin,\n        uint amountBMin,\n        address to,\n        uint deadline\n    ) external virtual override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) {\n        (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);\n        address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);\n        TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);\n        TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);\n        liquidity = IUniswapV2Pair(pair).mint(to);\n    }\n    function addLiquidityETH(\n        address token,\n        uint amountTokenDesired,\n        uint amountTokenMin,\n        uint amountETHMin,\n        address to,\n        uint deadline\n    ) external virtual override payable ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) {\n        (amountToken, amountETH) = _addLiquidity(\n            token,\n            WETH,\n            amountTokenDesired,\n            msg.value,\n            amountTokenMin,\n            amountETHMin\n        );\n        address pair = UniswapV2Library.pairFor(factory, token, WETH);\n        TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);\n        IWETH(WETH).deposit{value: amountETH}();\n        assert(IWETH(WETH).transfer(pair, amountETH));\n        liquidity = IUniswapV2Pair(pair).mint(to);\n        // refund dust eth, if any\n        if (msg.value \u003e amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);\n    }\n\n    // **** REMOVE LIQUIDITY ****\n    function removeLiquidity(\n        address tokenA,\n        address tokenB,\n        uint liquidity,\n        uint amountAMin,\n        uint amountBMin,\n        address to,\n        uint deadline\n    ) public virtual override ensure(deadline) returns (uint amountA, uint amountB) {\n        address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);\n        IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair\n        (uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);\n        (address token0,) = UniswapV2Library.sortTokens(tokenA, tokenB);\n        (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);\n        require(amountA \u003e= amountAMin, \u0027UniswapV2Router: INSUFFICIENT_A_AMOUNT\u0027);\n        require(amountB \u003e= amountBMin, \u0027UniswapV2Router: INSUFFICIENT_B_AMOUNT\u0027);\n    }\n    function removeLiquidityETH(\n        address token,\n        uint liquidity,\n        uint amountTokenMin,\n        uint amountETHMin,\n        address to,\n        uint deadline\n    ) public virtual override ensure(deadline) returns (uint amountToken, uint amountETH) {\n        (amountToken, amountETH) = removeLiquidity(\n            token,\n            WETH,\n            liquidity,\n            amountTokenMin,\n            amountETHMin,\n            address(this),\n            deadline\n        );\n        TransferHelper.safeTransfer(token, to, amountToken);\n        IWETH(WETH).withdraw(amountETH);\n        TransferHelper.safeTransferETH(to, amountETH);\n    }\n    function removeLiquidityWithPermit(\n        address tokenA,\n        address tokenB,\n        uint liquidity,\n        uint amountAMin,\n        uint amountBMin,\n        address to,\n        uint deadline,\n        bool approveMax, uint8 v, bytes32 r, bytes32 s\n    ) external virtual override returns (uint amountA, uint amountB) {\n        address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);\n        uint value = approveMax ? uint(-1) : liquidity;\n        IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);\n        (amountA, amountB) = removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline);\n    }\n    function removeLiquidityETHWithPermit(\n        address token,\n        uint liquidity,\n        uint amountTokenMin,\n        uint amountETHMin,\n        address to,\n        uint deadline,\n        bool approveMax, uint8 v, bytes32 r, bytes32 s\n    ) external virtual override returns (uint amountToken, uint amountETH) {\n        address pair = UniswapV2Library.pairFor(factory, token, WETH);\n        uint value = approveMax ? uint(-1) : liquidity;\n        IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);\n        (amountToken, amountETH) = removeLiquidityETH(token, liquidity, amountTokenMin, amountETHMin, to, deadline);\n    }\n\n    // **** REMOVE LIQUIDITY (supporting fee-on-transfer tokens) ****\n    function removeLiquidityETHSupportingFeeOnTransferTokens(\n        address token,\n        uint liquidity,\n        uint amountTokenMin,\n        uint amountETHMin,\n        address to,\n        uint deadline\n    ) public virtual override ensure(deadline) returns (uint amountETH) {\n        (, amountETH) = removeLiquidity(\n            token,\n            WETH,\n            liquidity,\n            amountTokenMin,\n            amountETHMin,\n            address(this),\n            deadline\n        );\n        TransferHelper.safeTransfer(token, to, IERC20(token).balanceOf(address(this)));\n        IWETH(WETH).withdraw(amountETH);\n        TransferHelper.safeTransferETH(to, amountETH);\n    }\n    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(\n        address token,\n        uint liquidity,\n        uint amountTokenMin,\n        uint amountETHMin,\n        address to,\n        uint deadline,\n        bool approveMax, uint8 v, bytes32 r, bytes32 s\n    ) external virtual override returns (uint amountETH) {\n        address pair = UniswapV2Library.pairFor(factory, token, WETH);\n        uint value = approveMax ? uint(-1) : liquidity;\n        IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);\n        amountETH = removeLiquidityETHSupportingFeeOnTransferTokens(\n            token, liquidity, amountTokenMin, amountETHMin, to, deadline\n        );\n    }\n\n    // **** SWAP ****\n    // requires the initial amount to have already been sent to the first pair\n    function _swap(uint[] memory amounts, address[] memory path, address _to) internal virtual {\n        for (uint i; i \u003c path.length - 1; i++) {\n            (address input, address output) = (path[i], path[i + 1]);\n            (address token0,) = UniswapV2Library.sortTokens(input, output);\n            uint amountOut = amounts[i + 1];\n            (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));\n            address to = i \u003c path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;\n            IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)).swap(\n                amount0Out, amount1Out, to, new bytes(0)\n            );\n        }\n    }\n    function swapExactTokensForTokens(\n        uint amountIn,\n        uint amountOutMin,\n        address[] calldata path,\n        address to,\n        uint deadline\n    ) external virtual override ensure(deadline) returns (uint[] memory amounts) {\n        amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);\n        require(amounts[amounts.length - 1] \u003e= amountOutMin, \u0027UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT\u0027);\n        TransferHelper.safeTransferFrom(\n            path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]\n        );\n        _swap(amounts, path, to);\n    }\n    function swapTokensForExactTokens(\n        uint amountOut,\n        uint amountInMax,\n        address[] calldata path,\n        address to,\n        uint deadline\n    ) external virtual override ensure(deadline) returns (uint[] memory amounts) {\n        amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);\n        require(amounts[0] \u003c= amountInMax, \u0027UniswapV2Router: EXCESSIVE_INPUT_AMOUNT\u0027);\n        TransferHelper.safeTransferFrom(\n            path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]\n        );\n        _swap(amounts, path, to);\n    }\n    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)\n        external\n        virtual\n        override\n        payable\n        ensure(deadline)\n        returns (uint[] memory amounts)\n    {\n        require(path[0] == WETH, \u0027UniswapV2Router: INVALID_PATH\u0027);\n        amounts = UniswapV2Library.getAmountsOut(factory, msg.value, path);\n        require(amounts[amounts.length - 1] \u003e= amountOutMin, \u0027UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT\u0027);\n        IWETH(WETH).deposit{value: amounts[0]}();\n        assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));\n        _swap(amounts, path, to);\n    }\n    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)\n        external\n        virtual\n        override\n        ensure(deadline)\n        returns (uint[] memory amounts)\n    {\n        require(path[path.length - 1] == WETH, \u0027UniswapV2Router: INVALID_PATH\u0027);\n        amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);\n        require(amounts[0] \u003c= amountInMax, \u0027UniswapV2Router: EXCESSIVE_INPUT_AMOUNT\u0027);\n        TransferHelper.safeTransferFrom(\n            path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]\n        );\n        _swap(amounts, path, address(this));\n        IWETH(WETH).withdraw(amounts[amounts.length - 1]);\n        TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);\n    }\n    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)\n        external\n        virtual\n        override\n        ensure(deadline)\n        returns (uint[] memory amounts)\n    {\n        require(path[path.length - 1] == WETH, \u0027UniswapV2Router: INVALID_PATH\u0027);\n        amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);\n        require(amounts[amounts.length - 1] \u003e= amountOutMin, \u0027UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT\u0027);\n        TransferHelper.safeTransferFrom(\n            path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]\n        );\n        _swap(amounts, path, address(this));\n        IWETH(WETH).withdraw(amounts[amounts.length - 1]);\n        TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);\n    }\n    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)\n        external\n        virtual\n        override\n        payable\n        ensure(deadline)\n        returns (uint[] memory amounts)\n    {\n        require(path[0] == WETH, \u0027UniswapV2Router: INVALID_PATH\u0027);\n        amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);\n        require(amounts[0] \u003c= msg.value, \u0027UniswapV2Router: EXCESSIVE_INPUT_AMOUNT\u0027);\n        IWETH(WETH).deposit{value: amounts[0]}();\n        assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));\n        _swap(amounts, path, to);\n        // refund dust eth, if any\n        if (msg.value \u003e amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);\n    }\n\n    // **** SWAP (supporting fee-on-transfer tokens) ****\n    // requires the initial amount to have already been sent to the first pair\n    function _swapSupportingFeeOnTransferTokens(address[] memory path, address _to) internal virtual {\n        for (uint i; i \u003c path.length - 1; i++) {\n            (address input, address output) = (path[i], path[i + 1]);\n            (address token0,) = UniswapV2Library.sortTokens(input, output);\n            IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output));\n            uint amountInput;\n            uint amountOutput;\n            { // scope to avoid stack too deep errors\n            (uint reserve0, uint reserve1,) = pair.getReserves();\n            (uint reserveInput, uint reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);\n            amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput);\n            amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput);\n            }\n            (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOutput) : (amountOutput, uint(0));\n            address to = i \u003c path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;\n            pair.swap(amount0Out, amount1Out, to, new bytes(0));\n        }\n    }\n    function swapExactTokensForTokensSupportingFeeOnTransferTokens(\n        uint amountIn,\n        uint amountOutMin,\n        address[] calldata path,\n        address to,\n        uint deadline\n    ) external virtual override ensure(deadline) {\n        TransferHelper.safeTransferFrom(\n            path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn\n        );\n        uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);\n        _swapSupportingFeeOnTransferTokens(path, to);\n        require(\n            IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) \u003e= amountOutMin,\n            \u0027UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT\u0027\n        );\n    }\n    function swapExactETHForTokensSupportingFeeOnTransferTokens(\n        uint amountOutMin,\n        address[] calldata path,\n        address to,\n        uint deadline\n    )\n        external\n        virtual\n        override\n        payable\n        ensure(deadline)\n    {\n        require(path[0] == WETH, \u0027UniswapV2Router: INVALID_PATH\u0027);\n        uint amountIn = msg.value;\n        IWETH(WETH).deposit{value: amountIn}();\n        assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn));\n        uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);\n        _swapSupportingFeeOnTransferTokens(path, to);\n        require(\n            IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) \u003e= amountOutMin,\n            \u0027UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT\u0027\n        );\n    }\n    function swapExactTokensForETHSupportingFeeOnTransferTokens(\n        uint amountIn,\n        uint amountOutMin,\n        address[] calldata path,\n        address to,\n        uint deadline\n    )\n        external\n        virtual\n        override\n        ensure(deadline)\n    {\n        require(path[path.length - 1] == WETH, \u0027UniswapV2Router: INVALID_PATH\u0027);\n        TransferHelper.safeTransferFrom(\n            path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn\n        );\n        _swapSupportingFeeOnTransferTokens(path, address(this));\n        uint amountOut = IERC20(WETH).balanceOf(address(this));\n        require(amountOut \u003e= amountOutMin, \u0027UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT\u0027);\n        IWETH(WETH).withdraw(amountOut);\n        TransferHelper.safeTransferETH(to, amountOut);\n    }\n\n    // **** LIBRARY FUNCTIONS ****\n    function quote(uint amountA, uint reserveA, uint reserveB) public pure virtual override returns (uint amountB) {\n        return UniswapV2Library.quote(amountA, reserveA, reserveB);\n    }\n\n    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut)\n        public\n        pure\n        virtual\n        override\n        returns (uint amountOut)\n    {\n        return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut);\n    }\n\n    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut)\n        public\n        pure\n        virtual\n        override\n        returns (uint amountIn)\n    {\n        return UniswapV2Library.getAmountIn(amountOut, reserveIn, reserveOut);\n    }\n\n    function getAmountsOut(uint amountIn, address[] memory path)\n        public\n        view\n        virtual\n        override\n        returns (uint[] memory amounts)\n    {\n        return UniswapV2Library.getAmountsOut(factory, amountIn, path);\n    }\n\n    function getAmountsIn(uint amountOut, address[] memory path)\n        public\n        view\n        virtual\n        override\n        returns (uint[] memory amounts)\n    {\n        return UniswapV2Library.getAmountsIn(factory, amountOut, path);\n    }\n}"},"UniswapV2Router02_Modified.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./IUniswapV2Factory.sol\u0027;\nimport \u0027./TransferHelper.sol\u0027;\n\nimport \u0027./IUniswapV2Router02.sol\u0027;\nimport \u0027./UniswapV2Library.sol\u0027;\nimport \u0027./SafeMath.sol\u0027;\nimport \u0027./IERC20.sol\u0027;\nimport \u0027./IWETH.sol\u0027;\n\ncontract UniswapV2Router02_Modified is IUniswapV2Router02 {\n    using SafeMath for uint;\n\n    address public immutable override factory;\n    address public immutable override WETH;\n\n    modifier ensure(uint deadline) {\n        require(deadline \u003e= block.timestamp, \u0027UniswapV2Router: EXPIRED\u0027);\n        _;\n    }\n\n    constructor(address _factory, address _WETH) public {\n        factory = _factory;\n        WETH = _WETH;\n    }\n\n    receive() external payable {\n        assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract\n    }\n\n    // **** ADD LIQUIDITY ****\n    function _addLiquidity(\n        address tokenA,\n        address tokenB,\n        uint amountADesired,\n        uint amountBDesired,\n        uint amountAMin,\n        uint amountBMin\n    ) internal virtual returns (uint amountA, uint amountB) {\n        // create the pair if it doesn\u0027t exist yet\n        if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {\n            IUniswapV2Factory(factory).createPair(tokenA, tokenB);\n        }\n        (uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);\n        if (reserveA == 0 \u0026\u0026 reserveB == 0) {\n            (amountA, amountB) = (amountADesired, amountBDesired);\n        } else {\n            uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB);\n            if (amountBOptimal \u003c= amountBDesired) {\n                require(amountBOptimal \u003e= amountBMin, \u0027UniswapV2Router: INSUFFICIENT_B_AMOUNT\u0027);\n                (amountA, amountB) = (amountADesired, amountBOptimal);\n            } else {\n                uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA);\n                assert(amountAOptimal \u003c= amountADesired);\n                require(amountAOptimal \u003e= amountAMin, \u0027UniswapV2Router: INSUFFICIENT_A_AMOUNT\u0027);\n                (amountA, amountB) = (amountAOptimal, amountBDesired);\n            }\n        }\n    }\n    function addLiquidity(\n        address tokenA,\n        address tokenB,\n        uint amountADesired,\n        uint amountBDesired,\n        uint amountAMin,\n        uint amountBMin,\n        address to,\n        uint deadline\n    ) external virtual override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) {\n        (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);\n        address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);\n        TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);\n        TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);\n        liquidity = IUniswapV2Pair(pair).mint(to);\n    }\n    function addLiquidityETH(\n        address token,\n        uint amountTokenDesired,\n        uint amountTokenMin,\n        uint amountETHMin,\n        address to,\n        uint deadline\n    ) external virtual override payable ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) {\n        (amountToken, amountETH) = _addLiquidity(\n            token,\n            WETH,\n            amountTokenDesired,\n            msg.value,\n            amountTokenMin,\n            amountETHMin\n        );\n        address pair = UniswapV2Library.pairFor(factory, token, WETH);\n        TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);\n        \n        \n        TransferHelper.safeTransferFrom(WETH, msg.sender, pair, amountETH);\n\n        // IWETH(WETH).transferFrom(msg.sender, pair, amountETH);\n        // IWETH(WETH).deposit{value: amountETH}();\n        // assert(IWETH(WETH).transfer(pair, amountETH));\n\n        // require(false, \"HELLO: HOW ARE YOU TODAY!\");\n\n        liquidity = IUniswapV2Pair(pair).mint(to); // \u003c\u003c PROBLEM IS HERE\n\n        // refund dust eth, if any\n        if (msg.value \u003e amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);\n    }\n\n    // **** REMOVE LIQUIDITY ****\n    function removeLiquidity(\n        address tokenA,\n        address tokenB,\n        uint liquidity,\n        uint amountAMin,\n        uint amountBMin,\n        address to,\n        uint deadline\n    ) public virtual override ensure(deadline) returns (uint amountA, uint amountB) {\n        address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);\n        IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair\n        (uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);\n        (address token0,) = UniswapV2Library.sortTokens(tokenA, tokenB);\n        (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);\n        require(amountA \u003e= amountAMin, \u0027UniswapV2Router: INSUFFICIENT_A_AMOUNT\u0027);\n        require(amountB \u003e= amountBMin, \u0027UniswapV2Router: INSUFFICIENT_B_AMOUNT\u0027);\n    }\n    function removeLiquidityETH(\n        address token,\n        uint liquidity,\n        uint amountTokenMin,\n        uint amountETHMin,\n        address to,\n        uint deadline\n    ) public virtual override ensure(deadline) returns (uint amountToken, uint amountETH) {\n        (amountToken, amountETH) = removeLiquidity(\n            token,\n            WETH,\n            liquidity,\n            amountTokenMin,\n            amountETHMin,\n            address(this),\n            deadline\n        );\n        TransferHelper.safeTransfer(token, to, amountToken);\n        IWETH(WETH).withdraw(amountETH);\n        TransferHelper.safeTransferETH(to, amountETH);\n    }\n    function removeLiquidityWithPermit(\n        address tokenA,\n        address tokenB,\n        uint liquidity,\n        uint amountAMin,\n        uint amountBMin,\n        address to,\n        uint deadline,\n        bool approveMax, uint8 v, bytes32 r, bytes32 s\n    ) external virtual override returns (uint amountA, uint amountB) {\n        address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);\n        uint value = approveMax ? uint(-1) : liquidity;\n        IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);\n        (amountA, amountB) = removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline);\n    }\n    function removeLiquidityETHWithPermit(\n        address token,\n        uint liquidity,\n        uint amountTokenMin,\n        uint amountETHMin,\n        address to,\n        uint deadline,\n        bool approveMax, uint8 v, bytes32 r, bytes32 s\n    ) external virtual override returns (uint amountToken, uint amountETH) {\n        address pair = UniswapV2Library.pairFor(factory, token, WETH);\n        uint value = approveMax ? uint(-1) : liquidity;\n        IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);\n        (amountToken, amountETH) = removeLiquidityETH(token, liquidity, amountTokenMin, amountETHMin, to, deadline);\n    }\n\n    // **** REMOVE LIQUIDITY (supporting fee-on-transfer tokens) ****\n    function removeLiquidityETHSupportingFeeOnTransferTokens(\n        address token,\n        uint liquidity,\n        uint amountTokenMin,\n        uint amountETHMin,\n        address to,\n        uint deadline\n    ) public virtual override ensure(deadline) returns (uint amountETH) {\n        (, amountETH) = removeLiquidity(\n            token,\n            WETH,\n            liquidity,\n            amountTokenMin,\n            amountETHMin,\n            address(this),\n            deadline\n        );\n        TransferHelper.safeTransfer(token, to, IERC20(token).balanceOf(address(this)));\n        IWETH(WETH).withdraw(amountETH);\n        TransferHelper.safeTransferETH(to, amountETH);\n    }\n    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(\n        address token,\n        uint liquidity,\n        uint amountTokenMin,\n        uint amountETHMin,\n        address to,\n        uint deadline,\n        bool approveMax, uint8 v, bytes32 r, bytes32 s\n    ) external virtual override returns (uint amountETH) {\n        address pair = UniswapV2Library.pairFor(factory, token, WETH);\n        uint value = approveMax ? uint(-1) : liquidity;\n        IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);\n        amountETH = removeLiquidityETHSupportingFeeOnTransferTokens(\n            token, liquidity, amountTokenMin, amountETHMin, to, deadline\n        );\n    }\n\n    // **** SWAP ****\n    // requires the initial amount to have already been sent to the first pair\n    function _swap(uint[] memory amounts, address[] memory path, address _to) internal virtual {\n        for (uint i; i \u003c path.length - 1; i++) {\n            (address input, address output) = (path[i], path[i + 1]);\n            (address token0,) = UniswapV2Library.sortTokens(input, output);\n            uint amountOut = amounts[i + 1];\n            (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));\n            address to = i \u003c path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;\n            IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)).swap(\n                amount0Out, amount1Out, to, new bytes(0)\n            );\n        }\n    }\n    function swapExactTokensForTokens(\n        uint amountIn,\n        uint amountOutMin,\n        address[] calldata path,\n        address to,\n        uint deadline\n    ) external virtual override ensure(deadline) returns (uint[] memory amounts) {\n        amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);\n        require(amounts[amounts.length - 1] \u003e= amountOutMin, \u0027UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT\u0027);\n        TransferHelper.safeTransferFrom(\n            path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]\n        );\n        _swap(amounts, path, to);\n    }\n    function swapTokensForExactTokens(\n        uint amountOut,\n        uint amountInMax,\n        address[] calldata path,\n        address to,\n        uint deadline\n    ) external virtual override ensure(deadline) returns (uint[] memory amounts) {\n        amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);\n        require(amounts[0] \u003c= amountInMax, \u0027UniswapV2Router: EXCESSIVE_INPUT_AMOUNT\u0027);\n        TransferHelper.safeTransferFrom(\n            path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]\n        );\n        _swap(amounts, path, to);\n    }\n    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)\n        external\n        virtual\n        override\n        payable\n        ensure(deadline)\n        returns (uint[] memory amounts)\n    {\n        require(path[0] == WETH, \u0027UniswapV2Router: INVALID_PATH\u0027);\n        amounts = UniswapV2Library.getAmountsOut(factory, msg.value, path);\n        require(amounts[amounts.length - 1] \u003e= amountOutMin, \u0027UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT\u0027);\n        IWETH(WETH).deposit{value: amounts[0]}();\n        assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));\n        _swap(amounts, path, to);\n    }\n    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)\n        external\n        virtual\n        override\n        ensure(deadline)\n        returns (uint[] memory amounts)\n    {\n        require(path[path.length - 1] == WETH, \u0027UniswapV2Router: INVALID_PATH\u0027);\n        amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);\n        require(amounts[0] \u003c= amountInMax, \u0027UniswapV2Router: EXCESSIVE_INPUT_AMOUNT\u0027);\n        TransferHelper.safeTransferFrom(\n            path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]\n        );\n        _swap(amounts, path, address(this));\n        IWETH(WETH).withdraw(amounts[amounts.length - 1]);\n        TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);\n    }\n    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)\n        external\n        virtual\n        override\n        ensure(deadline)\n        returns (uint[] memory amounts)\n    {\n        require(path[path.length - 1] == WETH, \u0027UniswapV2Router: INVALID_PATH\u0027);\n        amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);\n        require(amounts[amounts.length - 1] \u003e= amountOutMin, \u0027UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT\u0027);\n        TransferHelper.safeTransferFrom(\n            path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]\n        );\n        _swap(amounts, path, address(this));\n        IWETH(WETH).withdraw(amounts[amounts.length - 1]);\n        TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);\n    }\n    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)\n        external\n        virtual\n        override\n        payable\n        ensure(deadline)\n        returns (uint[] memory amounts)\n    {\n        require(path[0] == WETH, \u0027UniswapV2Router: INVALID_PATH\u0027);\n        amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);\n        require(amounts[0] \u003c= msg.value, \u0027UniswapV2Router: EXCESSIVE_INPUT_AMOUNT\u0027);\n        IWETH(WETH).deposit{value: amounts[0]}();\n        assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));\n        _swap(amounts, path, to);\n        // refund dust eth, if any\n        if (msg.value \u003e amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);\n    }\n\n    // **** SWAP (supporting fee-on-transfer tokens) ****\n    // requires the initial amount to have already been sent to the first pair\n    function _swapSupportingFeeOnTransferTokens(address[] memory path, address _to) internal virtual {\n        // for (uint i; i \u003c path.length - 1; i++) {\n        //     (address input, address output) = (path[i], path[i + 1]);\n        //     (address token0,) = UniswapV2Library.sortTokens(input, output);\n        //     IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output));\n        //     uint amountInput;\n        //     uint amountOutput;\n        //     { // scope to avoid stack too deep errors\n        //     (uint reserve0, uint reserve1,) = pair.getReserves();\n        //     (uint reserveInput, uint reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);\n        //     amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput);\n        //     amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput);\n        //     }\n        //     (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOutput) : (amountOutput, uint(0));\n        //     address to = i \u003c path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;\n        //     pair.swap(amount0Out, amount1Out, to, new bytes(0));\n        // }\n    }\n    function swapExactTokensForTokensSupportingFeeOnTransferTokens(\n        uint amountIn,\n        uint amountOutMin,\n        address[] calldata path,\n        address to,\n        uint deadline\n    ) external virtual override ensure(deadline) {\n        // TransferHelper.safeTransferFrom(\n        //     path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn\n        // );\n        // uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);\n        // _swapSupportingFeeOnTransferTokens(path, to);\n        // require(\n        //     IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) \u003e= amountOutMin,\n        //     \u0027UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT\u0027\n        // );\n    }\n    function swapExactETHForTokensSupportingFeeOnTransferTokens(\n        uint amountOutMin,\n        address[] calldata path,\n        address to,\n        uint deadline\n    )\n        external\n        virtual\n        override\n        payable\n        ensure(deadline)\n    {\n        // require(path[0] == WETH, \u0027UniswapV2Router: INVALID_PATH\u0027);\n        // uint amountIn = msg.value;\n        // IWETH(WETH).deposit{value: amountIn}();\n        // assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn));\n        // uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);\n        // _swapSupportingFeeOnTransferTokens(path, to);\n        // require(\n        //     IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) \u003e= amountOutMin,\n        //     \u0027UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT\u0027\n        // );\n    }\n    function swapExactTokensForETHSupportingFeeOnTransferTokens(\n        uint amountIn,\n        uint amountOutMin,\n        address[] calldata path,\n        address to,\n        uint deadline\n    )\n        external\n        virtual\n        override\n        ensure(deadline)\n    {\n        // require(path[path.length - 1] == WETH, \u0027UniswapV2Router: INVALID_PATH\u0027);\n        // TransferHelper.safeTransferFrom(\n        //     path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn\n        // );\n        // _swapSupportingFeeOnTransferTokens(path, address(this));\n        // uint amountOut = IERC20(WETH).balanceOf(address(this));\n        // require(amountOut \u003e= amountOutMin, \u0027UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT\u0027);\n        // IWETH(WETH).withdraw(amountOut);\n        // TransferHelper.safeTransferETH(to, amountOut);\n    }\n\n    // **** LIBRARY FUNCTIONS ****\n    function quote(uint amountA, uint reserveA, uint reserveB) public pure virtual override returns (uint amountB) {\n        return UniswapV2Library.quote(amountA, reserveA, reserveB);\n    }\n\n    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut)\n        public\n        pure\n        virtual\n        override\n        returns (uint amountOut)\n    {\n        return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut);\n    }\n\n    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut)\n        public\n        pure\n        virtual\n        override\n        returns (uint amountIn)\n    {\n        return UniswapV2Library.getAmountIn(amountOut, reserveIn, reserveOut);\n    }\n\n    function getAmountsOut(uint amountIn, address[] memory path)\n        public\n        view\n        virtual\n        override\n        returns (uint[] memory amounts)\n    {\n        return UniswapV2Library.getAmountsOut(factory, amountIn, path);\n    }\n\n    function getAmountsIn(uint amountOut, address[] memory path)\n        public\n        view\n        virtual\n        override\n        returns (uint[] memory amounts)\n    {\n        return UniswapV2Library.getAmountsIn(factory, amountOut, path);\n    }\n}"},"UQ112x112.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\n// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))\n\n// range: [0, 2**112 - 1]\n// resolution: 1 / 2**112\n\nlibrary UQ112x112 {\n    uint224 constant Q112 = 2**112;\n\n    // encode a uint112 as a UQ112x112\n    function encode(uint112 y) internal pure returns (uint224 z) {\n        z = uint224(y) * Q112; // never overflows\n    }\n\n    // divide a UQ112x112 by a uint112, returning a UQ112x112\n    function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {\n        z = x / uint224(y);\n    }\n}"},"WETH.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.11;\n\nimport \u0027./IWETH.sol\u0027;\n\n// Copyright (C) 2015, 2016, 2017 Dapphub\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see \u003chttp://www.gnu.org/licenses/\u003e.\n\ncontract WETH is IWETH {\n    string public name     = \"Wrapped Ether\";\n    string public symbol   = \"WETH\";\n    uint8  public decimals = 18;\n\n    event  Approval(address indexed src, address indexed guy, uint wad);\n    event  Transfer(address indexed src, address indexed dst, uint wad);\n    event  Deposit(address indexed dst, uint wad);\n    event  Withdrawal(address indexed src, uint wad);\n\n    mapping (address =\u003e uint)                       public  balanceOf;\n    mapping (address =\u003e mapping (address =\u003e uint))  public  allowance;\n\n    fallback() external payable {\n        deposit();\n    }\n\n    receive() external payable { }\n\n    constructor (address _creator_address ) public \n    {\n        balanceOf[_creator_address] = 1000000e18; // this is for testing only\n    }\n\n\n    function deposit() public override payable {\n        balanceOf[msg.sender] += msg.value;\n        emit Deposit(msg.sender, msg.value);\n    }\n    function withdraw(uint wad) override public {\n        require(balanceOf[msg.sender] \u003e= wad);\n        balanceOf[msg.sender] -= wad;\n        msg.sender.transfer(wad);\n        emit Withdrawal(msg.sender, wad);\n    }\n\n    function totalSupply() public view returns (uint) {\n        return address(this).balance;\n    }\n\n    function approve(address guy, uint wad) public returns (bool) {\n        allowance[msg.sender][guy] = wad;\n        emit Approval(msg.sender, guy, wad);\n        return true;\n    }\n\n    function transfer(address dst, uint wad) public override returns (bool) {\n        return transferFrom(msg.sender, dst, wad);\n    }\n\n    function transferFrom(address src, address dst, uint wad)\n        public\n        override\n        returns (bool)\n    {\n        require(balanceOf[src] \u003e= wad);\n\n        if (src != msg.sender \u0026\u0026 allowance[src][msg.sender] != uint(-1)) {\n            require(allowance[src][msg.sender] \u003e= wad);\n            allowance[src][msg.sender] -= wad;\n        }\n\n        balanceOf[src] -= wad;\n        balanceOf[dst] += wad;\n\n        emit Transfer(src, dst, wad);\n\n        return true;\n    }\n}\n\n\n/*\n                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. \u003chttp://fsf.org/\u003e\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers\u0027 and authors\u0027 protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users\u0027 and\nauthors\u0027 sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users\u0027 freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work\u0027s\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users\u0027 Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work\u0027s\nusers, your or third parties\u0027 legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program\u0027s source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation\u0027s users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party\u0027s predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor\u0027s \"contributor version\".\n\n  A contributor\u0027s \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor\u0027s essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient\u0027s use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others\u0027 Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy\u0027s\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    \u003cone line to give the program\u0027s name and a brief idea of what it does.\u003e\n    Copyright (C) \u003cyear\u003e  \u003cname of author\u003e\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see \u003chttp://www.gnu.org/licenses/\u003e.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    \u003cprogram\u003e  Copyright (C) \u003cyear\u003e  \u003cname of author\u003e\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w\u0027.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c\u0027 for details.\n\nThe hypothetical commands `show w\u0027 and `show c\u0027 should show the appropriate\nparts of the General Public License.  Of course, your program\u0027s commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n\u003chttp://www.gnu.org/licenses/\u003e.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n\u003chttp://www.gnu.org/philosophy/why-not-lgpl.html\u003e.\n\n*/"}}

              File 4 of 8: Vyper_contract
              # @version 0.3.1
              """
              @title StableSwap
              @author Curve.Fi
              @license Copyright (c) Curve.Fi, 2020 - all rights reserved
              """
              from vyper.interfaces import ERC20
              
              
              interface CurveToken:
                  def totalSupply() -> uint256: view
                  def mint(_to: address, _value: uint256) -> bool: nonpayable
                  def burnFrom(_to: address, _value: uint256) -> bool: nonpayable
              
              
              # Events
              event TokenExchange:
                  buyer: indexed(address)
                  sold_id: int128
                  tokens_sold: uint256
                  bought_id: int128
                  tokens_bought: uint256
              
              event AddLiquidity:
                  provider: indexed(address)
                  token_amounts: uint256[N_COINS]
                  fees: uint256[N_COINS]
                  invariant: uint256
                  token_supply: uint256
              
              event RemoveLiquidity:
                  provider: indexed(address)
                  token_amounts: uint256[N_COINS]
                  fees: uint256[N_COINS]
                  token_supply: uint256
              
              event RemoveLiquidityOne:
                  provider: indexed(address)
                  token_amount: uint256
                  coin_amount: uint256
                  token_supply: uint256
              
              event RemoveLiquidityImbalance:
                  provider: indexed(address)
                  token_amounts: uint256[N_COINS]
                  fees: uint256[N_COINS]
                  invariant: uint256
                  token_supply: uint256
              
              event CommitNewAdmin:
                  deadline: indexed(uint256)
                  admin: indexed(address)
              
              event NewAdmin:
                  admin: indexed(address)
              
              event CommitNewFee:
                  deadline: indexed(uint256)
                  fee: uint256
                  admin_fee: uint256
              
              event NewFee:
                  fee: uint256
                  admin_fee: uint256
              
              event RampA:
                  old_A: uint256
                  new_A: uint256
                  initial_time: uint256
                  future_time: uint256
              
              event StopRampA:
                  A: uint256
                  t: uint256
              
              
              # These constants must be set prior to compiling
              N_COINS: constant(int128) = 2
              PRECISION_MUL: constant(uint256[N_COINS]) = [1, 1000000000000]
              RATES: constant(uint256[N_COINS]) = [1000000000000000000, 1000000000000000000000000000000]
              
              # fixed constants
              FEE_DENOMINATOR: constant(uint256) = 10 ** 10
              PRECISION: constant(uint256) = 10 ** 18  # The precision to convert to
              
              MAX_ADMIN_FEE: constant(uint256) = 10 * 10 ** 9
              MAX_FEE: constant(uint256) = 5 * 10 ** 9
              MAX_A: constant(uint256) = 10 ** 6
              MAX_A_CHANGE: constant(uint256) = 10
              
              ADMIN_ACTIONS_DELAY: constant(uint256) = 3 * 86400
              MIN_RAMP_TIME: constant(uint256) = 86400
              
              coins: public(address[N_COINS])
              balances: public(uint256[N_COINS])
              fee: public(uint256)  # fee * 1e10
              admin_fee: public(uint256)  # admin_fee * 1e10
              
              owner: public(address)
              lp_token: public(address)
              
              A_PRECISION: constant(uint256) = 100
              initial_A: public(uint256)
              future_A: public(uint256)
              initial_A_time: public(uint256)
              future_A_time: public(uint256)
              
              admin_actions_deadline: public(uint256)
              transfer_ownership_deadline: public(uint256)
              future_fee: public(uint256)
              future_admin_fee: public(uint256)
              future_owner: public(address)
              
              is_killed: bool
              kill_deadline: uint256
              KILL_DEADLINE_DT: constant(uint256) = 2 * 30 * 86400
              
              
              @external
              def __init__(
                  _owner: address,
                  _coins: address[N_COINS],
                  _pool_token: address,
                  _A: uint256,
                  _fee: uint256,
                  _admin_fee: uint256
              ):
                  """
                  @notice Contract constructor
                  @param _owner Contract owner address
                  @param _coins Addresses of ERC20 conracts of coins
                  @param _pool_token Address of the token representing LP share
                  @param _A Amplification coefficient multiplied by n * (n - 1)
                  @param _fee Fee to charge for exchanges
                  @param _admin_fee Admin fee
                  """
                  for i in range(N_COINS):
                      assert _coins[i] != ZERO_ADDRESS
                  self.coins = _coins
                  self.initial_A = _A * A_PRECISION
                  self.future_A = _A * A_PRECISION
                  self.fee = _fee
                  self.admin_fee = _admin_fee
                  self.owner = _owner
                  self.kill_deadline = block.timestamp + KILL_DEADLINE_DT
                  self.lp_token = _pool_token
              
              
              @view
              @internal
              def _A() -> uint256:
                  """
                  Handle ramping A up or down
                  """
                  t1: uint256 = self.future_A_time
                  A1: uint256 = self.future_A
              
                  if block.timestamp < t1:
                      A0: uint256 = self.initial_A
                      t0: uint256 = self.initial_A_time
                      # Expressions in uint256 cannot have negative numbers, thus "if"
                      if A1 > A0:
                          return A0 + (A1 - A0) * (block.timestamp - t0) / (t1 - t0)
                      else:
                          return A0 - (A0 - A1) * (block.timestamp - t0) / (t1 - t0)
              
                  else:  # when t1 == 0 or block.timestamp >= t1
                      return A1
              
              
              @view
              @external
              def A() -> uint256:
                  return self._A() / A_PRECISION
              
              
              @view
              @external
              def A_precise() -> uint256:
                  return self._A()
              
              
              @view
              @internal
              def _xp() -> uint256[N_COINS]:
                  result: uint256[N_COINS] = RATES
                  for i in range(N_COINS):
                      result[i] = result[i] * self.balances[i] / PRECISION
                  return result
              
              
              @pure
              @internal
              def _xp_mem(_balances: uint256[N_COINS]) -> uint256[N_COINS]:
                  result: uint256[N_COINS] = RATES
                  for i in range(N_COINS):
                      result[i] = result[i] * _balances[i] / PRECISION
                  return result
              
              
              @pure
              @internal
              def _get_D(_xp: uint256[N_COINS], _amp: uint256) -> uint256:
                  """
                  D invariant calculation in non-overflowing integer operations
                  iteratively
              
                  A * sum(x_i) * n**n + D = A * D * n**n + D**(n+1) / (n**n * prod(x_i))
              
                  Converging solution:
                  D[j+1] = (A * n**n * sum(x_i) - D[j]**(n+1) / (n**n prod(x_i))) / (A * n**n - 1)
                  """
                  S: uint256 = 0
                  Dprev: uint256 = 0
              
                  for _x in _xp:
                      S += _x
                  if S == 0:
                      return 0
              
                  D: uint256 = S
                  Ann: uint256 = _amp * N_COINS
                  for _i in range(255):
                      D_P: uint256 = D
                      for _x in _xp:
                          D_P = D_P * D / (_x * N_COINS)  # If division by 0, this will be borked: only withdrawal will work. And that is good
                      Dprev = D
                      D = (Ann * S / A_PRECISION + D_P * N_COINS) * D / ((Ann - A_PRECISION) * D / A_PRECISION + (N_COINS + 1) * D_P)
                      # Equality with the precision of 1
                      if D > Dprev:
                          if D - Dprev <= 1:
                              return D
                      else:
                          if Dprev - D <= 1:
                              return D
                  # convergence typically occurs in 4 rounds or less, this should be unreachable!
                  # if it does happen the pool is borked and LPs can withdraw via `remove_liquidity`
                  raise
              
              
              @view
              @internal
              def _get_D_mem(_balances: uint256[N_COINS], _amp: uint256) -> uint256:
                  return self._get_D(self._xp_mem(_balances), _amp)
              
              
              @view
              @external
              def get_virtual_price() -> uint256:
                  """
                  @notice The current virtual price of the pool LP token
                  @dev Useful for calculating profits
                  @return LP token virtual price normalized to 1e18
                  """
                  D: uint256 = self._get_D(self._xp(), self._A())
                  # D is in the units similar to DAI (e.g. converted to precision 1e18)
                  # When balanced, D = n * x_u - total virtual value of the portfolio
                  token_supply: uint256 = ERC20(self.lp_token).totalSupply()
                  return D * PRECISION / token_supply
              
              
              @view
              @external
              def calc_token_amount(_amounts: uint256[N_COINS], _is_deposit: bool) -> uint256:
                  """
                  @notice Calculate addition or reduction in token supply from a deposit or withdrawal
                  @dev This calculation accounts for slippage, but not fees.
                       Needed to prevent front-running, not for precise calculations!
                  @param _amounts Amount of each coin being deposited
                  @param _is_deposit set True for deposits, False for withdrawals
                  @return Expected amount of LP tokens received
                  """
                  amp: uint256 = self._A()
                  balances: uint256[N_COINS] = self.balances
                  D0: uint256 = self._get_D_mem(balances, amp)
                  for i in range(N_COINS):
                      if _is_deposit:
                          balances[i] += _amounts[i]
                      else:
                          balances[i] -= _amounts[i]
                  D1: uint256 = self._get_D_mem(balances, amp)
                  token_amount: uint256 = CurveToken(self.lp_token).totalSupply()
                  diff: uint256 = 0
                  if _is_deposit:
                      diff = D1 - D0
                  else:
                      diff = D0 - D1
                  return diff * token_amount / D0
              
              
              @external
              @nonreentrant('lock')
              def add_liquidity(_amounts: uint256[N_COINS], _min_mint_amount: uint256) -> uint256:
                  """
                  @notice Deposit coins into the pool
                  @param _amounts List of amounts of coins to deposit
                  @param _min_mint_amount Minimum amount of LP tokens to mint from the deposit
                  @return Amount of LP tokens received by depositing
                  """
                  assert not self.is_killed  # dev: is killed
              
                  amp: uint256 = self._A()
                  old_balances: uint256[N_COINS] = self.balances
              
                  # Initial invariant
                  D0: uint256 = self._get_D_mem(old_balances, amp)
              
                  lp_token: address = self.lp_token
                  token_supply: uint256 = CurveToken(lp_token).totalSupply()
                  new_balances: uint256[N_COINS] = old_balances
                  for i in range(N_COINS):
                      if token_supply == 0:
                          assert _amounts[i] > 0  # dev: initial deposit requires all coins
                      # balances store amounts of c-tokens
                      new_balances[i] += _amounts[i]
              
                  # Invariant after change
                  D1: uint256 = self._get_D_mem(new_balances, amp)
                  assert D1 > D0
              
                  # We need to recalculate the invariant accounting for fees
                  # to calculate fair user's share
                  D2: uint256 = D1
                  fees: uint256[N_COINS] = empty(uint256[N_COINS])
                  mint_amount: uint256 = 0
                  if token_supply > 0:
                      # Only account for fees if we are not the first to deposit
                      fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1))
                      admin_fee: uint256 = self.admin_fee
                      for i in range(N_COINS):
                          ideal_balance: uint256 = D1 * old_balances[i] / D0
                          difference: uint256 = 0
                          new_balance: uint256 = new_balances[i]
                          if ideal_balance > new_balance:
                              difference = ideal_balance - new_balance
                          else:
                              difference = new_balance - ideal_balance
                          fees[i] = fee * difference / FEE_DENOMINATOR
                          self.balances[i] = new_balance - (fees[i] * admin_fee / FEE_DENOMINATOR)
                          new_balances[i] -= fees[i]
                      D2 = self._get_D_mem(new_balances, amp)
                      mint_amount = token_supply * (D2 - D0) / D0
                  else:
                      self.balances = new_balances
                      mint_amount = D1  # Take the dust if there was any
                  assert mint_amount >= _min_mint_amount, "Slippage screwed you"
              
                  # Take coins from the sender
                  for i in range(N_COINS):
                      if _amounts[i] > 0:
                          # "safeTransferFrom" which works for ERC20s which return bool or not
                          _response: Bytes[32] = raw_call(
                              self.coins[i],
                              concat(
                                  method_id("transferFrom(address,address,uint256)"),
                                  convert(msg.sender, bytes32),
                                  convert(self, bytes32),
                                  convert(_amounts[i], bytes32),
                              ),
                              max_outsize=32,
                          )
                          if len(_response) > 0:
                              assert convert(_response, bool)  # dev: failed transfer
                          # end "safeTransferFrom"
              
                  # Mint pool tokens
                  CurveToken(lp_token).mint(msg.sender, mint_amount)
              
                  log AddLiquidity(msg.sender, _amounts, fees, D1, token_supply + mint_amount)
              
                  return mint_amount
              
              
              @view
              @internal
              def _get_y(i: int128, j: int128, x: uint256, _xp: uint256[N_COINS]) -> uint256:
                  """
                  Calculate x[j] if one makes x[i] = x
              
                  Done by solving quadratic equation iteratively.
                  x_1**2 + x_1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A)
                  x_1**2 + b*x_1 = c
              
                  x_1 = (x_1**2 + c) / (2*x_1 + b)
                  """
                  # x in the input is converted to the same price/precision
              
                  assert i != j       # dev: same coin
                  assert j >= 0       # dev: j below zero
                  assert j < N_COINS  # dev: j above N_COINS
              
                  # should be unreachable, but good for safety
                  assert i >= 0
                  assert i < N_COINS
              
                  A: uint256 = self._A()
                  D: uint256 = self._get_D(_xp, A)
                  Ann: uint256 = A * N_COINS
                  c: uint256 = D
                  S: uint256 = 0
                  _x: uint256 = 0
                  y_prev: uint256 = 0
              
                  for _i in range(N_COINS):
                      if _i == i:
                          _x = x
                      elif _i != j:
                          _x = _xp[_i]
                      else:
                          continue
                      S += _x
                      c = c * D / (_x * N_COINS)
                  c = c * D * A_PRECISION / (Ann * N_COINS)
                  b: uint256 = S + D * A_PRECISION / Ann  # - D
                  y: uint256 = D
                  for _i in range(255):
                      y_prev = y
                      y = (y*y + c) / (2 * y + b - D)
                      # Equality with the precision of 1
                      if y > y_prev:
                          if y - y_prev <= 1:
                              return y
                      else:
                          if y_prev - y <= 1:
                              return y
                  raise
              
              
              @view
              @external
              def get_dy(i: int128, j: int128, _dx: uint256) -> uint256:
                  xp: uint256[N_COINS] = self._xp()
                  rates: uint256[N_COINS] = RATES
              
                  x: uint256 = xp[i] + (_dx * rates[i] / PRECISION)
                  y: uint256 = self._get_y(i, j, x, xp)
                  dy: uint256 = xp[j] - y - 1
                  fee: uint256 = self.fee * dy / FEE_DENOMINATOR
                  return (dy - fee) * PRECISION / rates[j]
              
              
              @external
              @nonreentrant('lock')
              def exchange(i: int128, j: int128, _dx: uint256, _min_dy: uint256) -> uint256:
                  """
                  @notice Perform an exchange between two coins
                  @dev Index values can be found via the `coins` public getter method
                  @param i Index value for the coin to send
                  @param j Index valie of the coin to recieve
                  @param _dx Amount of `i` being exchanged
                  @param _min_dy Minimum amount of `j` to receive
                  @return Actual amount of `j` received
                  """
                  assert not self.is_killed  # dev: is killed
              
                  old_balances: uint256[N_COINS] = self.balances
                  xp: uint256[N_COINS] = self._xp_mem(old_balances)
              
                  rates: uint256[N_COINS] = RATES
                  x: uint256 = xp[i] + _dx * rates[i] / PRECISION
                  y: uint256 = self._get_y(i, j, x, xp)
              
                  dy: uint256 = xp[j] - y - 1  # -1 just in case there were some rounding errors
                  dy_fee: uint256 = dy * self.fee / FEE_DENOMINATOR
              
                  # Convert all to real units
                  dy = (dy - dy_fee) * PRECISION / rates[j]
                  assert dy >= _min_dy, "Exchange resulted in fewer coins than expected"
              
                  dy_admin_fee: uint256 = dy_fee * self.admin_fee / FEE_DENOMINATOR
                  dy_admin_fee = dy_admin_fee * PRECISION / rates[j]
              
                  # Change balances exactly in same way as we change actual ERC20 coin amounts
                  self.balances[i] = old_balances[i] + _dx
                  # When rounding errors happen, we undercharge admin fee in favor of LP
                  self.balances[j] = old_balances[j] - dy - dy_admin_fee
              
                  _response: Bytes[32] = raw_call(
                      self.coins[i],
                      concat(
                          method_id("transferFrom(address,address,uint256)"),
                          convert(msg.sender, bytes32),
                          convert(self, bytes32),
                          convert(_dx, bytes32),
                      ),
                      max_outsize=32,
                  )
                  if len(_response) > 0:
                      assert convert(_response, bool)
              
                  _response = raw_call(
                      self.coins[j],
                      concat(
                          method_id("transfer(address,uint256)"),
                          convert(msg.sender, bytes32),
                          convert(dy, bytes32),
                      ),
                      max_outsize=32,
                  )
                  if len(_response) > 0:
                      assert convert(_response, bool)
              
                  log TokenExchange(msg.sender, i, _dx, j, dy)
              
                  return dy
              
              
              @external
              @nonreentrant('lock')
              def remove_liquidity(_amount: uint256, _min_amounts: uint256[N_COINS]) -> uint256[N_COINS]:
                  """
                  @notice Withdraw coins from the pool
                  @dev Withdrawal amounts are based on current deposit ratios
                  @param _amount Quantity of LP tokens to burn in the withdrawal
                  @param _min_amounts Minimum amounts of underlying coins to receive
                  @return List of amounts of coins that were withdrawn
                  """
                  lp_token: address = self.lp_token
                  total_supply: uint256 = CurveToken(lp_token).totalSupply()
                  amounts: uint256[N_COINS] = empty(uint256[N_COINS])
              
                  for i in range(N_COINS):
                      old_balance: uint256 = self.balances[i]
                      value: uint256 = old_balance * _amount / total_supply
                      assert value >= _min_amounts[i], "Withdrawal resulted in fewer coins than expected"
                      self.balances[i] = old_balance - value
                      amounts[i] = value
                      _response: Bytes[32] = raw_call(
                          self.coins[i],
                          concat(
                              method_id("transfer(address,uint256)"),
                              convert(msg.sender, bytes32),
                              convert(value, bytes32),
                          ),
                          max_outsize=32,
                      )
                      if len(_response) > 0:
                          assert convert(_response, bool)
              
                  CurveToken(lp_token).burnFrom(msg.sender, _amount)  # dev: insufficient funds
              
                  log RemoveLiquidity(msg.sender, amounts, empty(uint256[N_COINS]), total_supply - _amount)
              
                  return amounts
              
              
              @external
              @nonreentrant('lock')
              def remove_liquidity_imbalance(_amounts: uint256[N_COINS], _max_burn_amount: uint256) -> uint256:
                  """
                  @notice Withdraw coins from the pool in an imbalanced amount
                  @param _amounts List of amounts of underlying coins to withdraw
                  @param _max_burn_amount Maximum amount of LP token to burn in the withdrawal
                  @return Actual amount of the LP token burned in the withdrawal
                  """
                  assert not self.is_killed  # dev: is killed
              
                  amp: uint256 = self._A()
                  old_balances: uint256[N_COINS] = self.balances
                  D0: uint256 = self._get_D_mem(old_balances, amp)
                  new_balances: uint256[N_COINS] = old_balances
                  for i in range(N_COINS):
                      new_balances[i] -= _amounts[i]
                  D1: uint256 = self._get_D_mem(new_balances, amp)
              
                  fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1))
                  admin_fee: uint256 = self.admin_fee
                  fees: uint256[N_COINS] = empty(uint256[N_COINS])
                  for i in range(N_COINS):
                      new_balance: uint256 = new_balances[i]
                      ideal_balance: uint256 = D1 * old_balances[i] / D0
                      difference: uint256 = 0
                      if ideal_balance > new_balance:
                          difference = ideal_balance - new_balance
                      else:
                          difference = new_balance - ideal_balance
                      fees[i] = fee * difference / FEE_DENOMINATOR
                      self.balances[i] = new_balance - (fees[i] * admin_fee / FEE_DENOMINATOR)
                      new_balances[i] = new_balance - fees[i]
                  D2: uint256 = self._get_D_mem(new_balances, amp)
              
                  lp_token: address = self.lp_token
                  token_supply: uint256 = CurveToken(lp_token).totalSupply()
                  token_amount: uint256 = (D0 - D2) * token_supply / D0
                  assert token_amount != 0  # dev: zero tokens burned
                  token_amount += 1  # In case of rounding errors - make it unfavorable for the "attacker"
                  assert token_amount <= _max_burn_amount, "Slippage screwed you"
              
                  CurveToken(lp_token).burnFrom(msg.sender, token_amount)  # dev: insufficient funds
                  for i in range(N_COINS):
                      if _amounts[i] != 0:
                          _response: Bytes[32] = raw_call(
                              self.coins[i],
                              concat(
                                  method_id("transfer(address,uint256)"),
                                  convert(msg.sender, bytes32),
                                  convert(_amounts[i], bytes32),
                              ),
                              max_outsize=32,
                          )
                          if len(_response) > 0:
                              assert convert(_response, bool)
              
                  log RemoveLiquidityImbalance(msg.sender, _amounts, fees, D1, token_supply - token_amount)
              
                  return token_amount
              
              
              @pure
              @internal
              def _get_y_D(A: uint256, i: int128, _xp: uint256[N_COINS], D: uint256) -> uint256:
                  """
                  Calculate x[i] if one reduces D from being calculated for xp to D
              
                  Done by solving quadratic equation iteratively.
                  x_1**2 + x_1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A)
                  x_1**2 + b*x_1 = c
              
                  x_1 = (x_1**2 + c) / (2*x_1 + b)
                  """
                  # x in the input is converted to the same price/precision
              
                  assert i >= 0  # dev: i below zero
                  assert i < N_COINS  # dev: i above N_COINS
              
                  Ann: uint256 = A * N_COINS
                  c: uint256 = D
                  S: uint256 = 0
                  _x: uint256 = 0
                  y_prev: uint256 = 0
              
                  for _i in range(N_COINS):
                      if _i != i:
                          _x = _xp[_i]
                      else:
                          continue
                      S += _x
                      c = c * D / (_x * N_COINS)
                  c = c * D * A_PRECISION / (Ann * N_COINS)
                  b: uint256 = S + D * A_PRECISION / Ann
                  y: uint256 = D
              
                  for _i in range(255):
                      y_prev = y
                      y = (y*y + c) / (2 * y + b - D)
                      # Equality with the precision of 1
                      if y > y_prev:
                          if y - y_prev <= 1:
                              return y
                      else:
                          if y_prev - y <= 1:
                              return y
                  raise
              
              
              @view
              @internal
              def _calc_withdraw_one_coin(_token_amount: uint256, i: int128) -> (uint256, uint256, uint256):
                  # First, need to calculate
                  # * Get current D
                  # * Solve Eqn against y_i for D - _token_amount
                  amp: uint256 = self._A()
                  xp: uint256[N_COINS] = self._xp()
                  D0: uint256 = self._get_D(xp, amp)
              
                  total_supply: uint256 = CurveToken(self.lp_token).totalSupply()
                  D1: uint256 = D0 - _token_amount * D0 / total_supply
                  new_y: uint256 = self._get_y_D(amp, i, xp, D1)
                  xp_reduced: uint256[N_COINS] = xp
                  fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1))
                  for j in range(N_COINS):
                      dx_expected: uint256 = 0
                      if j == i:
                          dx_expected = xp[j] * D1 / D0 - new_y
                      else:
                          dx_expected = xp[j] - xp[j] * D1 / D0
                      xp_reduced[j] -= fee * dx_expected / FEE_DENOMINATOR
              
                  dy: uint256 = xp_reduced[i] - self._get_y_D(amp, i, xp_reduced, D1)
                  precisions: uint256[N_COINS] = PRECISION_MUL
                  dy = (dy - 1) / precisions[i]  # Withdraw less to account for rounding errors
                  dy_0: uint256 = (xp[i] - new_y) / precisions[i]  # w/o fees
              
                  return dy, dy_0 - dy, total_supply
              
              
              @view
              @external
              def calc_withdraw_one_coin(_token_amount: uint256, i: int128) -> uint256:
                  """
                  @notice Calculate the amount received when withdrawing a single coin
                  @param _token_amount Amount of LP tokens to burn in the withdrawal
                  @param i Index value of the coin to withdraw
                  @return Amount of coin received
                  """
                  return self._calc_withdraw_one_coin(_token_amount, i)[0]
              
              
              @external
              @nonreentrant('lock')
              def remove_liquidity_one_coin(_token_amount: uint256, i: int128, _min_amount: uint256) -> uint256:
                  """
                  @notice Withdraw a single coin from the pool
                  @param _token_amount Amount of LP tokens to burn in the withdrawal
                  @param i Index value of the coin to withdraw
                  @param _min_amount Minimum amount of coin to receive
                  @return Amount of coin received
                  """
                  assert not self.is_killed  # dev: is killed
              
                  dy: uint256 = 0
                  dy_fee: uint256 = 0
                  total_supply: uint256 = 0
                  dy, dy_fee, total_supply = self._calc_withdraw_one_coin(_token_amount, i)
                  assert dy >= _min_amount, "Not enough coins removed"
              
                  self.balances[i] -= (dy + dy_fee * self.admin_fee / FEE_DENOMINATOR)
                  CurveToken(self.lp_token).burnFrom(msg.sender, _token_amount)  # dev: insufficient funds
              
                  _response: Bytes[32] = raw_call(
                      self.coins[i],
                      concat(
                          method_id("transfer(address,uint256)"),
                          convert(msg.sender, bytes32),
                          convert(dy, bytes32),
                      ),
                      max_outsize=32,
                  )
                  if len(_response) > 0:
                      assert convert(_response, bool)
              
                  log RemoveLiquidityOne(msg.sender, _token_amount, dy, total_supply - _token_amount)
              
                  return dy
              
              
              ### Admin functions ###
              @external
              def ramp_A(_future_A: uint256, _future_time: uint256):
                  assert msg.sender == self.owner  # dev: only owner
                  assert block.timestamp >= self.initial_A_time + MIN_RAMP_TIME
                  assert _future_time >= block.timestamp + MIN_RAMP_TIME  # dev: insufficient time
              
                  initial_A: uint256 = self._A()
                  future_A_p: uint256 = _future_A * A_PRECISION
              
                  assert _future_A > 0 and _future_A < MAX_A
                  if future_A_p < initial_A:
                      assert future_A_p * MAX_A_CHANGE >= initial_A
                  else:
                      assert future_A_p <= initial_A * MAX_A_CHANGE
              
                  self.initial_A = initial_A
                  self.future_A = future_A_p
                  self.initial_A_time = block.timestamp
                  self.future_A_time = _future_time
              
                  log RampA(initial_A, future_A_p, block.timestamp, _future_time)
              
              
              @external
              def stop_ramp_A():
                  assert msg.sender == self.owner  # dev: only owner
              
                  current_A: uint256 = self._A()
                  self.initial_A = current_A
                  self.future_A = current_A
                  self.initial_A_time = block.timestamp
                  self.future_A_time = block.timestamp
                  # now (block.timestamp < t1) is always False, so we return saved A
              
                  log StopRampA(current_A, block.timestamp)
              
              
              @external
              def commit_new_fee(_new_fee: uint256, _new_admin_fee: uint256):
                  assert msg.sender == self.owner  # dev: only owner
                  assert self.admin_actions_deadline == 0  # dev: active action
                  assert _new_fee <= MAX_FEE  # dev: fee exceeds maximum
                  assert _new_admin_fee <= MAX_ADMIN_FEE  # dev: admin fee exceeds maximum
              
                  deadline: uint256 = block.timestamp + ADMIN_ACTIONS_DELAY
                  self.admin_actions_deadline = deadline
                  self.future_fee = _new_fee
                  self.future_admin_fee = _new_admin_fee
              
                  log CommitNewFee(deadline, _new_fee, _new_admin_fee)
              
              
              @external
              def apply_new_fee():
                  assert msg.sender == self.owner  # dev: only owner
                  assert block.timestamp >= self.admin_actions_deadline  # dev: insufficient time
                  assert self.admin_actions_deadline != 0  # dev: no active action
              
                  self.admin_actions_deadline = 0
                  fee: uint256 = self.future_fee
                  admin_fee: uint256 = self.future_admin_fee
                  self.fee = fee
                  self.admin_fee = admin_fee
              
                  log NewFee(fee, admin_fee)
              
              
              @external
              def revert_new_parameters():
                  assert msg.sender == self.owner  # dev: only owner
              
                  self.admin_actions_deadline = 0
              
              
              @external
              def commit_transfer_ownership(_owner: address):
                  assert msg.sender == self.owner  # dev: only owner
                  assert self.transfer_ownership_deadline == 0  # dev: active transfer
              
                  deadline: uint256 = block.timestamp + ADMIN_ACTIONS_DELAY
                  self.transfer_ownership_deadline = deadline
                  self.future_owner = _owner
              
                  log CommitNewAdmin(deadline, _owner)
              
              
              @external
              def apply_transfer_ownership():
                  assert msg.sender == self.owner  # dev: only owner
                  assert block.timestamp >= self.transfer_ownership_deadline  # dev: insufficient time
                  assert self.transfer_ownership_deadline != 0  # dev: no active transfer
              
                  self.transfer_ownership_deadline = 0
                  owner: address = self.future_owner
                  self.owner = owner
              
                  log NewAdmin(owner)
              
              
              @external
              def revert_transfer_ownership():
                  assert msg.sender == self.owner  # dev: only owner
              
                  self.transfer_ownership_deadline = 0
              
              
              @view
              @external
              def admin_balances(i: uint256) -> uint256:
                  return ERC20(self.coins[i]).balanceOf(self) - self.balances[i]
              
              
              @external
              def withdraw_admin_fees():
                  assert msg.sender == self.owner  # dev: only owner
              
                  for i in range(N_COINS):
                      coin: address = self.coins[i]
                      value: uint256 = ERC20(coin).balanceOf(self) - self.balances[i]
                      if value > 0:
                          _response: Bytes[32] = raw_call(
                              coin,
                              concat(
                                  method_id("transfer(address,uint256)"),
                                  convert(msg.sender, bytes32),
                                  convert(value, bytes32),
                              ),
                              max_outsize=32,
                          )  # dev: failed transfer
                          if len(_response) > 0:
                              assert convert(_response, bool)
              
              
              @external
              def donate_admin_fees():
                  assert msg.sender == self.owner  # dev: only owner
                  for i in range(N_COINS):
                      self.balances[i] = ERC20(self.coins[i]).balanceOf(self)
              
              
              @external
              def kill_me():
                  assert msg.sender == self.owner  # dev: only owner
                  assert self.kill_deadline > block.timestamp  # dev: deadline has passed
                  self.is_killed = True
              
              
              @external
              def unkill_me():
                  assert msg.sender == self.owner  # dev: only owner
                  self.is_killed = False

              File 5 of 8: FiatTokenProxy
              pragma solidity ^0.4.24;
              
              // File: zos-lib/contracts/upgradeability/Proxy.sol
              
              /**
               * @title Proxy
               * @dev Implements delegation of calls to other contracts, with proper
               * forwarding of return values and bubbling of failures.
               * It defines a fallback function that delegates all calls to the address
               * returned by the abstract _implementation() internal function.
               */
              contract Proxy {
                /**
                 * @dev Fallback function.
                 * Implemented entirely in `_fallback`.
                 */
                function () payable external {
                  _fallback();
                }
              
                /**
                 * @return The Address of the implementation.
                 */
                function _implementation() internal view returns (address);
              
                /**
                 * @dev Delegates execution to an implementation contract.
                 * This is a low level function that doesn't return to its internal call site.
                 * It will return to the external caller whatever the implementation returns.
                 * @param implementation Address to delegate.
                 */
                function _delegate(address implementation) internal {
                  assembly {
                    // Copy msg.data. We take full control of memory in this inline assembly
                    // block because it will not return to Solidity code. We overwrite the
                    // Solidity scratch pad at memory position 0.
                    calldatacopy(0, 0, calldatasize)
              
                    // Call the implementation.
                    // out and outsize are 0 because we don't know the size yet.
                    let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0)
              
                    // Copy the returned data.
                    returndatacopy(0, 0, returndatasize)
              
                    switch result
                    // delegatecall returns 0 on error.
                    case 0 { revert(0, returndatasize) }
                    default { return(0, returndatasize) }
                  }
                }
              
                /**
                 * @dev Function that is run as the first thing in the fallback function.
                 * Can be redefined in derived contracts to add functionality.
                 * Redefinitions must call super._willFallback().
                 */
                function _willFallback() internal {
                }
              
                /**
                 * @dev fallback implementation.
                 * Extracted to enable manual triggering.
                 */
                function _fallback() internal {
                  _willFallback();
                  _delegate(_implementation());
                }
              }
              
              // File: openzeppelin-solidity/contracts/AddressUtils.sol
              
              /**
               * Utility library of inline functions on addresses
               */
              library AddressUtils {
              
                /**
                 * Returns whether the target address is a contract
                 * @dev This function will return false if invoked during the constructor of a contract,
                 * as the code is not actually created until after the constructor finishes.
                 * @param addr address to check
                 * @return whether the target address is a contract
                 */
                function isContract(address addr) internal view returns (bool) {
                  uint256 size;
                  // XXX Currently there is no better way to check if there is a contract in an address
                  // than to check the size of the code at that address.
                  // See https://ethereum.stackexchange.com/a/14016/36603
                  // for more details about how this works.
                  // TODO Check this again before the Serenity release, because all addresses will be
                  // contracts then.
                  // solium-disable-next-line security/no-inline-assembly
                  assembly { size := extcodesize(addr) }
                  return size > 0;
                }
              
              }
              
              // File: zos-lib/contracts/upgradeability/UpgradeabilityProxy.sol
              
              /**
               * @title UpgradeabilityProxy
               * @dev This contract implements a proxy that allows to change the
               * implementation address to which it will delegate.
               * Such a change is called an implementation upgrade.
               */
              contract UpgradeabilityProxy is Proxy {
                /**
                 * @dev Emitted when the implementation is upgraded.
                 * @param implementation Address of the new implementation.
                 */
                event Upgraded(address implementation);
              
                /**
                 * @dev Storage slot with the address of the current implementation.
                 * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is
                 * validated in the constructor.
                 */
                bytes32 private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3;
              
                /**
                 * @dev Contract constructor.
                 * @param _implementation Address of the initial implementation.
                 */
                constructor(address _implementation) public {
                  assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation"));
              
                  _setImplementation(_implementation);
                }
              
                /**
                 * @dev Returns the current implementation.
                 * @return Address of the current implementation
                 */
                function _implementation() internal view returns (address impl) {
                  bytes32 slot = IMPLEMENTATION_SLOT;
                  assembly {
                    impl := sload(slot)
                  }
                }
              
                /**
                 * @dev Upgrades the proxy to a new implementation.
                 * @param newImplementation Address of the new implementation.
                 */
                function _upgradeTo(address newImplementation) internal {
                  _setImplementation(newImplementation);
                  emit Upgraded(newImplementation);
                }
              
                /**
                 * @dev Sets the implementation address of the proxy.
                 * @param newImplementation Address of the new implementation.
                 */
                function _setImplementation(address newImplementation) private {
                  require(AddressUtils.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
              
                  bytes32 slot = IMPLEMENTATION_SLOT;
              
                  assembly {
                    sstore(slot, newImplementation)
                  }
                }
              }
              
              // File: zos-lib/contracts/upgradeability/AdminUpgradeabilityProxy.sol
              
              /**
               * @title AdminUpgradeabilityProxy
               * @dev This contract combines an upgradeability proxy with an authorization
               * mechanism for administrative tasks.
               * All external functions in this contract must be guarded by the
               * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
               * feature proposal that would enable this to be done automatically.
               */
              contract AdminUpgradeabilityProxy is UpgradeabilityProxy {
                /**
                 * @dev Emitted when the administration has been transferred.
                 * @param previousAdmin Address of the previous admin.
                 * @param newAdmin Address of the new admin.
                 */
                event AdminChanged(address previousAdmin, address newAdmin);
              
                /**
                 * @dev Storage slot with the admin of the contract.
                 * This is the keccak-256 hash of "org.zeppelinos.proxy.admin", and is
                 * validated in the constructor.
                 */
                bytes32 private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b;
              
                /**
                 * @dev Modifier to check whether the `msg.sender` is the admin.
                 * If it is, it will run the function. Otherwise, it will delegate the call
                 * to the implementation.
                 */
                modifier ifAdmin() {
                  if (msg.sender == _admin()) {
                    _;
                  } else {
                    _fallback();
                  }
                }
              
                /**
                 * Contract constructor.
                 * It sets the `msg.sender` as the proxy administrator.
                 * @param _implementation address of the initial implementation.
                 */
                constructor(address _implementation) UpgradeabilityProxy(_implementation) public {
                  assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin"));
              
                  _setAdmin(msg.sender);
                }
              
                /**
                 * @return The address of the proxy admin.
                 */
                function admin() external view ifAdmin returns (address) {
                  return _admin();
                }
              
                /**
                 * @return The address of the implementation.
                 */
                function implementation() external view ifAdmin returns (address) {
                  return _implementation();
                }
              
                /**
                 * @dev Changes the admin of the proxy.
                 * Only the current admin can call this function.
                 * @param newAdmin Address to transfer proxy administration to.
                 */
                function changeAdmin(address newAdmin) external ifAdmin {
                  require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
                  emit AdminChanged(_admin(), newAdmin);
                  _setAdmin(newAdmin);
                }
              
                /**
                 * @dev Upgrade the backing implementation of the proxy.
                 * Only the admin can call this function.
                 * @param newImplementation Address of the new implementation.
                 */
                function upgradeTo(address newImplementation) external ifAdmin {
                  _upgradeTo(newImplementation);
                }
              
                /**
                 * @dev Upgrade the backing implementation of the proxy and call a function
                 * on the new implementation.
                 * This is useful to initialize the proxied contract.
                 * @param newImplementation Address of the new implementation.
                 * @param data Data to send as msg.data in the low level call.
                 * It should include the signature and the parameters of the function to be
                 * called, as described in
                 * https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding.
                 */
                function upgradeToAndCall(address newImplementation, bytes data) payable external ifAdmin {
                  _upgradeTo(newImplementation);
                  require(address(this).call.value(msg.value)(data));
                }
              
                /**
                 * @return The admin slot.
                 */
                function _admin() internal view returns (address adm) {
                  bytes32 slot = ADMIN_SLOT;
                  assembly {
                    adm := sload(slot)
                  }
                }
              
                /**
                 * @dev Sets the address of the proxy admin.
                 * @param newAdmin Address of the new proxy admin.
                 */
                function _setAdmin(address newAdmin) internal {
                  bytes32 slot = ADMIN_SLOT;
              
                  assembly {
                    sstore(slot, newAdmin)
                  }
                }
              
                /**
                 * @dev Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal {
                  require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
                  super._willFallback();
                }
              }
              
              // File: contracts/FiatTokenProxy.sol
              
              /**
              * Copyright CENTRE SECZ 2018
              *
              * Permission is hereby granted, free of charge, to any person obtaining a copy 
              * of this software and associated documentation files (the "Software"), to deal 
              * in the Software without restriction, including without limitation the rights 
              * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
              * copies of the Software, and to permit persons to whom the Software is furnished to 
              * do so, subject to the following conditions:
              *
              * The above copyright notice and this permission notice shall be included in all 
              * copies or substantial portions of the Software.
              *
              * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
              * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
              * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
              * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
              * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
              * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
              */
              
              pragma solidity ^0.4.24;
              
              
              /**
               * @title FiatTokenProxy
               * @dev This contract proxies FiatToken calls and enables FiatToken upgrades
              */ 
              contract FiatTokenProxy is AdminUpgradeabilityProxy {
                  constructor(address _implementation) public AdminUpgradeabilityProxy(_implementation) {
                  }
              }

              File 6 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 7 of 8: AggregationRouterV6
              /*
                                                                         ,▄▓▓██▌   ,╓▄▄▓▓▓▓▓▓▓▓▄▄▄,,
                                                                      ,▓██▓███▓▄▓███▓╬╬╬╬╬╬╬╬╬╬╬╬╬▓███▓▄,
                                                                ▄█   ▓██╬╣███████╬▓▀╬╬▓▓▓████████████▓█████▄,
                                                               ▓██▌ ▓██╬╣██████╬▓▌  ██████████████████████▌╙╙▀ⁿ
                                                              ▐████████╬▓████▓▓█╨ ▄ ╟█████████▓▓╬╬╬╬╬▓▓█████▓▄
                                                └▀▓▓▄╓        ╟█▓╣█████▓██████▀ ╓█▌ ███████▓▓▓▓▓╬╬╬╬╬╬╬╬╬╬╬╬▓██▓▄
                                                   └▀████▓▄╥  ▐██╬╬██████████╙ Æ▀─ ▓███▀╚╠╬╩▀▀███████▓▓╬╬╬╬╬╬╬╬╬██▄
                                                      └▀██▓▀▀█████▓╬▓██████▀     ▄█████▒╠"      └╙▓██████▓╬╬╬╬╬╬╬╬██▄
                                                         └▀██▄,└╙▀▀████▌└╙    ^"▀╙╙╙"╙██      @▄    ╙▀███████╬╬╬╬╬╬╬██µ
                                                            └▀██▓▄, ██▌       ╒       ╙█▓     ]▓█▓╔    ▀███████▓╬╬╬╬╬▓█▌
                                                                ▀█████       ▓         ╟█▌    ]╠██▓░▒╓   ▀████████╬╬╬╬╣█▌
                                                                ▐████      ╓█▀█▌      ,██▌    ╚Å███▓▒▒╠╓  ╙█████████╬╬╬╣█▌
                                                                └████     ▓█░░▓█      ▀▀▀    φ▒╫████▒▒▒▒╠╓  █████████▓╬╬▓█µ
                                                                 ╘███µ ▌▄█▓▄▓▀`     ,▀    ,╔╠░▓██████▌╠▒▒▒φ  ██████████╬╬██
                                                                 ▐████µ╙▓▀`     ,▀╙,╔╔φφφ╠░▄▓███████▌░▓╙▒▒▒╠ └██╬███████╬▓█⌐
                                                                 ╫██ ▓▌         ▌φ▒▒░▓██████████████▌▒░▓╚▒▒▒╠ ▓██╬▓██████╣█▌
                                                                 ██▌           ▌╔▒▒▄████████████████▒▒▒░▌╠▒▒▒≥▐██▓╬╬███████▌
                                                                 ██▌      ,╓φ╠▓«▒▒▓████▀  ▀█████████▌▒▒▒╟░▒▒▒▒▐███╬╬╣████▓█▌
                                                                ▐██      ╠▒▄▓▓███▓████└     ▀████████▌▒▒░▌╚▒▒▒▐███▓╬╬████ ╙▌
                                                                ███  )  ╠▒░░░▒░╬████▀        └████████░▒▒░╬∩▒▒▓████╬╬╣███
                                                               ▓██    ╠╠▒▒▐█▀▀▌`░╫██           ███████▒▒▒▒░▒▒½█████╬╬╣███
                                                              ███ ,█▄ ╠▒▒▒╫▌,▄▀,▒╫██           ╟██████▒▒▒░╣⌠▒▓█████╬╬╣██▌
                                                             ╘██µ ██` ╠▒▒░██╬φ╠▄▓██`            ██████░░▌φ╠░▓█████▓╬╬▓██
                                                              ╟██  .φ╠▒░▄█▀░░▄██▀└              █████▌▒╣φ▒░▓██████╬╬╣██
                                                               ▀██▄▄▄╓▄███████▀                ▐█████░▓φ▒▄███████▓╬╣██
                                                                 ╙▀▀▀██▀└                      ████▓▄▀φ▄▓████████╬▓█▀
                                                                                              ▓███╬╩╔╣██████████▓██└
                                                                                            ╓████▀▄▓████████▀████▀
                                                                                          ,▓███████████████─]██╙
                                                                                       ,▄▓██████████████▀└  ╙
                                                                                  ,╓▄▓███████████████▀╙
                                                                           `"▀▀▀████████▀▀▀▀`▄███▀▀└
                                                                                            └└
                                  11\\   11\\                     11\\             11\\   11\\            11\\                                       11\\
                                1111 |  \\__|                    11 |            111\\  11 |           11 |                                      11 |
                                \\_11 |  11\\ 1111111\\   1111111\\ 1111111\\        1111\\ 11 | 111111\\ 111111\\   11\\  11\\  11\\  111111\\   111111\\  11 |  11\\
                                  11 |  11 |11  __11\\ 11  _____|11  __11\\       11 11\\11 |11  __11\\\\_11  _|  11 | 11 | 11 |11  __11\\ 11  __11\\ 11 | 11  |
                                  11 |  11 |11 |  11 |11 /      11 |  11 |      11 \\1111 |11111111 | 11 |    11 | 11 | 11 |11 /  11 |11 |  \\__|111111  /
                                  11 |  11 |11 |  11 |11 |      11 |  11 |      11 |\\111 |11   ____| 11 |11\\ 11 | 11 | 11 |11 |  11 |11 |      11  _11<
                                111111\\ 11 |11 |  11 |\\1111111\\ 11 |  11 |      11 | \\11 |\\1111111\\  \\1111  |\\11111\\1111  |\\111111  |11 |      11 | \\11\\
                                \\______|\\__|\\__|  \\__| \\_______|\\__|  \\__|      \\__|  \\__| \\_______|  \\____/  \\_____\\____/  \\______/ \\__|      \\__|  \\__|
                                             111111\\                                                               11\\     11\\
                                            11  __11\\                                                              11 |    \\__|
                                            11 /  11 | 111111\\   111111\\   111111\\   111111\\   111111\\   111111\\ 111111\\   11\\  111111\\  1111111\\
                                            11111111 |11  __11\\ 11  __11\\ 11  __11\\ 11  __11\\ 11  __11\\  \\____11\\\\_11  _|  11 |11  __11\\ 11  __11\\
                                            11  __11 |11 /  11 |11 /  11 |11 |  \\__|11111111 |11 /  11 | 1111111 | 11 |    11 |11 /  11 |11 |  11 |
                                            11 |  11 |11 |  11 |11 |  11 |11 |      11   ____|11 |  11 |11  __11 | 11 |11\\ 11 |11 |  11 |11 |  11 |
                                            11 |  11 |\\1111111 |\\1111111 |11 |      \\1111111\\ \\1111111 |\\1111111 | \\1111  |11 |\\111111  |11 |  11 |
                                            \\__|  \\__| \\____11 | \\____11 |\\__|       \\_______| \\____11 | \\_______|  \\____/ \\__| \\______/ \\__|  \\__|
                                                      11\\   11 |11\\   11 |                    11\\   11 |
                                                      \\111111  |\\111111  |                    \\111111  |
                                                       \\______/  \\______/                      \\______/
                                                              1111111\\                        11\\
                                                              11  __11\\                       11 |
                                                              11 |  11 | 111111\\  11\\   11\\ 111111\\    111111\\   111111\\
                                                              1111111  |11  __11\\ 11 |  11 |\\_11  _|  11  __11\\ 11  __11\\
                                                              11  __11< 11 /  11 |11 |  11 |  11 |    11111111 |11 |  \\__|
                                                              11 |  11 |11 |  11 |11 |  11 |  11 |11\\ 11   ____|11 |
                                                              11 |  11 |\\111111  |\\111111  |  \\1111  |\\1111111\\ 11 |
                                                              \\__|  \\__| \\______/  \\______/    \\____/  \\_______|\\__|
              */
              // SPDX-License-Identifier: MIT
              // File @1inch/limit-order-protocol-contract/contracts/libraries/[email protected]
              pragma solidity 0.8.23;
              type MakerTraits is uint256;
              /**
               * @title MakerTraitsLib
               * @notice A library to manage and check MakerTraits, which are used to encode the maker's preferences for an order in a single uint256.
               * @dev
               * The MakerTraits type is a uint256 and different parts of the number are used to encode different traits.
               * High bits are used for flags
               * 255 bit `NO_PARTIAL_FILLS_FLAG`          - if set, the order does not allow partial fills
               * 254 bit `ALLOW_MULTIPLE_FILLS_FLAG`      - if set, the order permits multiple fills
               * 253 bit                                  - unused
               * 252 bit `PRE_INTERACTION_CALL_FLAG`      - if set, the order requires pre-interaction call
               * 251 bit `POST_INTERACTION_CALL_FLAG`     - if set, the order requires post-interaction call
               * 250 bit `NEED_CHECK_EPOCH_MANAGER_FLAG`  - if set, the order requires to check the epoch manager
               * 249 bit `HAS_EXTENSION_FLAG`             - if set, the order has extension(s)
               * 248 bit `USE_PERMIT2_FLAG`               - if set, the order uses permit2
               * 247 bit `UNWRAP_WETH_FLAG`               - if set, the order requires to unwrap WETH
               * Low 200 bits are used for allowed sender, expiration, nonceOrEpoch, and series
               * uint80 last 10 bytes of allowed sender address (0 if any)
               * uint40 expiration timestamp (0 if none)
               * uint40 nonce or epoch
               * uint40 series
               */
              library MakerTraitsLib {
                  // Low 200 bits are used for allowed sender, expiration, nonceOrEpoch, and series
                  uint256 private constant _ALLOWED_SENDER_MASK = type(uint80).max;
                  uint256 private constant _EXPIRATION_OFFSET = 80;
                  uint256 private constant _EXPIRATION_MASK = type(uint40).max;
                  uint256 private constant _NONCE_OR_EPOCH_OFFSET = 120;
                  uint256 private constant _NONCE_OR_EPOCH_MASK = type(uint40).max;
                  uint256 private constant _SERIES_OFFSET = 160;
                  uint256 private constant _SERIES_MASK = type(uint40).max;
                  uint256 private constant _NO_PARTIAL_FILLS_FLAG = 1 << 255;
                  uint256 private constant _ALLOW_MULTIPLE_FILLS_FLAG = 1 << 254;
                  uint256 private constant _PRE_INTERACTION_CALL_FLAG = 1 << 252;
                  uint256 private constant _POST_INTERACTION_CALL_FLAG = 1 << 251;
                  uint256 private constant _NEED_CHECK_EPOCH_MANAGER_FLAG = 1 << 250;
                  uint256 private constant _HAS_EXTENSION_FLAG = 1 << 249;
                  uint256 private constant _USE_PERMIT2_FLAG = 1 << 248;
                  uint256 private constant _UNWRAP_WETH_FLAG = 1 << 247;
                  /**
                   * @notice Checks if the order has the extension flag set.
                   * @dev If the `HAS_EXTENSION_FLAG` is set in the makerTraits, then the protocol expects that the order has extension(s).
                   * @param makerTraits The traits of the maker.
                   * @return result A boolean indicating whether the flag is set.
                   */
                  function hasExtension(MakerTraits makerTraits) internal pure returns (bool) {
                      return (MakerTraits.unwrap(makerTraits) & _HAS_EXTENSION_FLAG) != 0;
                  }
                  /**
                   * @notice Checks if the maker allows a specific taker to fill the order.
                   * @param makerTraits The traits of the maker.
                   * @param sender The address of the taker to be checked.
                   * @return result A boolean indicating whether the taker is allowed.
                   */
                  function isAllowedSender(MakerTraits makerTraits, address sender) internal pure returns (bool) {
                      uint160 allowedSender = uint160(MakerTraits.unwrap(makerTraits) & _ALLOWED_SENDER_MASK);
                      return allowedSender == 0 || allowedSender == uint160(sender) & _ALLOWED_SENDER_MASK;
                  }
                  /**
                   * @notice Checks if the order has expired.
                   * @param makerTraits The traits of the maker.
                   * @return result A boolean indicating whether the order has expired.
                   */
                  function isExpired(MakerTraits makerTraits) internal view returns (bool) {
                      uint256 expiration = (MakerTraits.unwrap(makerTraits) >> _EXPIRATION_OFFSET) & _EXPIRATION_MASK;
                      return expiration != 0 && expiration < block.timestamp;  // solhint-disable-line not-rely-on-time
                  }
                  /**
                   * @notice Returns the nonce or epoch of the order.
                   * @param makerTraits The traits of the maker.
                   * @return result The nonce or epoch of the order.
                   */
                  function nonceOrEpoch(MakerTraits makerTraits) internal pure returns (uint256) {
                      return (MakerTraits.unwrap(makerTraits) >> _NONCE_OR_EPOCH_OFFSET) & _NONCE_OR_EPOCH_MASK;
                  }
                  /**
                   * @notice Returns the series of the order.
                   * @param makerTraits The traits of the maker.
                   * @return result The series of the order.
                   */
                  function series(MakerTraits makerTraits) internal pure returns (uint256) {
                      return (MakerTraits.unwrap(makerTraits) >> _SERIES_OFFSET) & _SERIES_MASK;
                  }
                  /**
                    * @notice Determines if the order allows partial fills.
                    * @dev If the _NO_PARTIAL_FILLS_FLAG is not set in the makerTraits, then the order allows partial fills.
                    * @param makerTraits The traits of the maker, determining their preferences for the order.
                    * @return result A boolean indicating whether the maker allows partial fills.
                    */
                  function allowPartialFills(MakerTraits makerTraits) internal pure returns (bool) {
                      return (MakerTraits.unwrap(makerTraits) & _NO_PARTIAL_FILLS_FLAG) == 0;
                  }
                  /**
                   * @notice Checks if the maker needs pre-interaction call.
                   * @param makerTraits The traits of the maker.
                   * @return result A boolean indicating whether the maker needs a pre-interaction call.
                   */
                  function needPreInteractionCall(MakerTraits makerTraits) internal pure returns (bool) {
                      return (MakerTraits.unwrap(makerTraits) & _PRE_INTERACTION_CALL_FLAG) != 0;
                  }
                  /**
                   * @notice Checks if the maker needs post-interaction call.
                   * @param makerTraits The traits of the maker.
                   * @return result A boolean indicating whether the maker needs a post-interaction call.
                   */
                  function needPostInteractionCall(MakerTraits makerTraits) internal pure returns (bool) {
                      return (MakerTraits.unwrap(makerTraits) & _POST_INTERACTION_CALL_FLAG) != 0;
                  }
                  /**
                    * @notice Determines if the order allows multiple fills.
                    * @dev If the _ALLOW_MULTIPLE_FILLS_FLAG is set in the makerTraits, then the maker allows multiple fills.
                    * @param makerTraits The traits of the maker, determining their preferences for the order.
                    * @return result A boolean indicating whether the maker allows multiple fills.
                    */
                  function allowMultipleFills(MakerTraits makerTraits) internal pure returns (bool) {
                      return (MakerTraits.unwrap(makerTraits) & _ALLOW_MULTIPLE_FILLS_FLAG) != 0;
                  }
                  /**
                    * @notice Determines if an order should use the bit invalidator or remaining amount validator.
                    * @dev The bit invalidator can be used if the order does not allow partial or multiple fills.
                    * @param makerTraits The traits of the maker, determining their preferences for the order.
                    * @return result A boolean indicating whether the bit invalidator should be used.
                    * True if the order requires the use of the bit invalidator.
                    */
                  function useBitInvalidator(MakerTraits makerTraits) internal pure returns (bool) {
                      return !allowPartialFills(makerTraits) || !allowMultipleFills(makerTraits);
                  }
                  /**
                   * @notice Checks if the maker needs to check the epoch.
                   * @param makerTraits The traits of the maker.
                   * @return result A boolean indicating whether the maker needs to check the epoch manager.
                   */
                  function needCheckEpochManager(MakerTraits makerTraits) internal pure returns (bool) {
                      return (MakerTraits.unwrap(makerTraits) & _NEED_CHECK_EPOCH_MANAGER_FLAG) != 0;
                  }
                  /**
                   * @notice Checks if the maker uses permit2.
                   * @param makerTraits The traits of the maker.
                   * @return result A boolean indicating whether the maker uses permit2.
                   */
                  function usePermit2(MakerTraits makerTraits) internal pure returns (bool) {
                      return MakerTraits.unwrap(makerTraits) & _USE_PERMIT2_FLAG != 0;
                  }
                  /**
                   * @notice Checks if the maker needs to unwraps WETH.
                   * @param makerTraits The traits of the maker.
                   * @return result A boolean indicating whether the maker needs to unwrap WETH.
                   */
                  function unwrapWeth(MakerTraits makerTraits) internal pure returns (bool) {
                      return MakerTraits.unwrap(makerTraits) & _UNWRAP_WETH_FLAG != 0;
                  }
              }
              // File @1inch/limit-order-protocol-contract/contracts/libraries/[email protected]
              type TakerTraits is uint256;
              /**
               * @title TakerTraitsLib
               * @notice This library to manage and check TakerTraits, which are used to encode the taker's preferences for an order in a single uint256.
               * @dev The TakerTraits are structured as follows:
               * High bits are used for flags
               * 255 bit `_MAKER_AMOUNT_FLAG`           - If set, the taking amount is calculated based on making amount, otherwise making amount is calculated based on taking amount.
               * 254 bit `_UNWRAP_WETH_FLAG`            - If set, the WETH will be unwrapped into ETH before sending to taker.
               * 253 bit `_SKIP_ORDER_PERMIT_FLAG`      - If set, the order skips maker's permit execution.
               * 252 bit `_USE_PERMIT2_FLAG`            - If set, the order uses the permit2 function for authorization.
               * 251 bit `_ARGS_HAS_TARGET`             - If set, then first 20 bytes of args are treated as target address for maker’s funds transfer.
               * 224-247 bits `ARGS_EXTENSION_LENGTH`   - The length of the extension calldata in the args.
               * 200-223 bits `ARGS_INTERACTION_LENGTH` - The length of the interaction calldata in the args.
               * 0-184 bits                             - The threshold amount (the maximum amount a taker agrees to give in exchange for a making amount).
               */
              library TakerTraitsLib {
                  uint256 private constant _MAKER_AMOUNT_FLAG = 1 << 255;
                  uint256 private constant _UNWRAP_WETH_FLAG = 1 << 254;
                  uint256 private constant _SKIP_ORDER_PERMIT_FLAG = 1 << 253;
                  uint256 private constant _USE_PERMIT2_FLAG = 1 << 252;
                  uint256 private constant _ARGS_HAS_TARGET = 1 << 251;
                  uint256 private constant _ARGS_EXTENSION_LENGTH_OFFSET = 224;
                  uint256 private constant _ARGS_EXTENSION_LENGTH_MASK = 0xffffff;
                  uint256 private constant _ARGS_INTERACTION_LENGTH_OFFSET = 200;
                  uint256 private constant _ARGS_INTERACTION_LENGTH_MASK = 0xffffff;
                  uint256 private constant _AMOUNT_MASK = 0x000000000000000000ffffffffffffffffffffffffffffffffffffffffffffff;
                  /**
                   * @notice Checks if the args should contain target address.
                   * @param takerTraits The traits of the taker.
                   * @return result A boolean indicating whether the args should contain target address.
                   */
                  function argsHasTarget(TakerTraits takerTraits) internal pure returns (bool) {
                      return (TakerTraits.unwrap(takerTraits) & _ARGS_HAS_TARGET) != 0;
                  }
                  /**
                   * @notice Retrieves the length of the extension calldata from the takerTraits.
                   * @param takerTraits The traits of the taker.
                   * @return result The length of the extension calldata encoded in the takerTraits.
                   */
                  function argsExtensionLength(TakerTraits takerTraits) internal pure returns (uint256) {
                      return (TakerTraits.unwrap(takerTraits) >> _ARGS_EXTENSION_LENGTH_OFFSET) & _ARGS_EXTENSION_LENGTH_MASK;
                  }
                  /**
                   * @notice Retrieves the length of the interaction calldata from the takerTraits.
                   * @param takerTraits The traits of the taker.
                   * @return result The length of the interaction calldata encoded in the takerTraits.
                   */
                  function argsInteractionLength(TakerTraits takerTraits) internal pure returns (uint256) {
                      return (TakerTraits.unwrap(takerTraits) >> _ARGS_INTERACTION_LENGTH_OFFSET) & _ARGS_INTERACTION_LENGTH_MASK;
                  }
                  /**
                   * @notice Checks if the taking amount should be calculated based on making amount.
                   * @param takerTraits The traits of the taker.
                   * @return result A boolean indicating whether the taking amount should be calculated based on making amount.
                   */
                  function isMakingAmount(TakerTraits takerTraits) internal pure returns (bool) {
                      return (TakerTraits.unwrap(takerTraits) & _MAKER_AMOUNT_FLAG) != 0;
                  }
                  /**
                   * @notice Checks if the order should unwrap WETH and send ETH to taker.
                   * @param takerTraits The traits of the taker.
                   * @return result A boolean indicating whether the order should unwrap WETH.
                   */
                  function unwrapWeth(TakerTraits takerTraits) internal pure returns (bool) {
                      return (TakerTraits.unwrap(takerTraits) & _UNWRAP_WETH_FLAG) != 0;
                  }
                  /**
                   * @notice Checks if the order should skip maker's permit execution.
                   * @param takerTraits The traits of the taker.
                   * @return result A boolean indicating whether the order don't apply permit.
                   */
                  function skipMakerPermit(TakerTraits takerTraits) internal pure returns (bool) {
                      return (TakerTraits.unwrap(takerTraits) & _SKIP_ORDER_PERMIT_FLAG) != 0;
                  }
                  /**
                   * @notice Checks if the order uses the permit2 instead of permit.
                   * @param takerTraits The traits of the taker.
                   * @return result A boolean indicating whether the order uses the permit2.
                   */
                  function usePermit2(TakerTraits takerTraits) internal pure returns (bool) {
                      return (TakerTraits.unwrap(takerTraits) & _USE_PERMIT2_FLAG) != 0;
                  }
                  /**
                   * @notice Retrieves the threshold amount from the takerTraits.
                   * The maximum amount a taker agrees to give in exchange for a making amount.
                   * @param takerTraits The traits of the taker.
                   * @return result The threshold amount encoded in the takerTraits.
                   */
                  function threshold(TakerTraits takerTraits) internal pure returns (uint256) {
                      return TakerTraits.unwrap(takerTraits) & _AMOUNT_MASK;
                  }
              }
              // File @1inch/solidity-utils/contracts/libraries/[email protected]
              type Address is uint256;
              /**
              * @dev Library for working with addresses encoded as uint256 values, which can include flags in the highest bits.
              */
              library AddressLib {
                  uint256 private constant _LOW_160_BIT_MASK = (1 << 160) - 1;
                  /**
                  * @notice Returns the address representation of a uint256.
                  * @param a The uint256 value to convert to an address.
                  * @return The address representation of the provided uint256 value.
                  */
                  function get(Address a) internal pure returns (address) {
                      return address(uint160(Address.unwrap(a) & _LOW_160_BIT_MASK));
                  }
                  /**
                  * @notice Checks if a given flag is set for the provided address.
                  * @param a The address to check for the flag.
                  * @param flag The flag to check for in the provided address.
                  * @return True if the provided flag is set in the address, false otherwise.
                  */
                  function getFlag(Address a, uint256 flag) internal pure returns (bool) {
                      return (Address.unwrap(a) & flag) != 0;
                  }
                  /**
                  * @notice Returns a uint32 value stored at a specific bit offset in the provided address.
                  * @param a The address containing the uint32 value.
                  * @param offset The bit offset at which the uint32 value is stored.
                  * @return The uint32 value stored in the address at the specified bit offset.
                  */
                  function getUint32(Address a, uint256 offset) internal pure returns (uint32) {
                      return uint32(Address.unwrap(a) >> offset);
                  }
                  /**
                  * @notice Returns a uint64 value stored at a specific bit offset in the provided address.
                  * @param a The address containing the uint64 value.
                  * @param offset The bit offset at which the uint64 value is stored.
                  * @return The uint64 value stored in the address at the specified bit offset.
                  */
                  function getUint64(Address a, uint256 offset) internal pure returns (uint64) {
                      return uint64(Address.unwrap(a) >> offset);
                  }
              }
              // File @1inch/limit-order-protocol-contract/contracts/interfaces/[email protected]
              interface IOrderMixin {
                  struct Order {
                      uint256 salt;
                      Address maker;
                      Address receiver;
                      Address makerAsset;
                      Address takerAsset;
                      uint256 makingAmount;
                      uint256 takingAmount;
                      MakerTraits makerTraits;
                  }
                  error InvalidatedOrder();
                  error TakingAmountExceeded();
                  error PrivateOrder();
                  error BadSignature();
                  error OrderExpired();
                  error WrongSeriesNonce();
                  error SwapWithZeroAmount();
                  error PartialFillNotAllowed();
                  error OrderIsNotSuitableForMassInvalidation();
                  error EpochManagerAndBitInvalidatorsAreIncompatible();
                  error ReentrancyDetected();
                  error PredicateIsNotTrue();
                  error TakingAmountTooHigh();
                  error MakingAmountTooLow();
                  error TransferFromMakerToTakerFailed();
                  error TransferFromTakerToMakerFailed();
                  error MismatchArraysLengths();
                  error InvalidPermit2Transfer();
                  error SimulationResults(bool success, bytes res);
                  /**
                   * @notice Emitted when order gets filled
                   * @param orderHash Hash of the order
                   * @param remainingAmount Amount of the maker asset that remains to be filled
                   */
                  event OrderFilled(
                      bytes32 orderHash,
                      uint256 remainingAmount
                  );
                  /**
                   * @notice Emitted when order without `useBitInvalidator` gets cancelled
                   * @param orderHash Hash of the order
                   */
                  event OrderCancelled(
                      bytes32 orderHash
                  );
                  /**
                   * @notice Emitted when order with `useBitInvalidator` gets cancelled
                   * @param maker Maker address
                   * @param slotIndex Slot index that was updated
                   * @param slotValue New slot value
                   */
                  event BitInvalidatorUpdated(
                      address indexed maker,
                      uint256 slotIndex,
                      uint256 slotValue
                  );
                  /**
                   * @notice Returns bitmask for double-spend invalidators based on lowest byte of order.info and filled quotes
                   * @param maker Maker address
                   * @param slot Slot number to return bitmask for
                   * @return result Each bit represents whether corresponding was already invalidated
                   */
                  function bitInvalidatorForOrder(address maker, uint256 slot) external view returns(uint256 result);
                  /**
                   * @notice Returns bitmask for double-spend invalidators based on lowest byte of order.info and filled quotes
                   * @param orderHash Hash of the order
                   * @return remaining Remaining amount of the order
                   */
                  function remainingInvalidatorForOrder(address maker, bytes32 orderHash) external view returns(uint256 remaining);
                  /**
                   * @notice Returns bitmask for double-spend invalidators based on lowest byte of order.info and filled quotes
                   * @param orderHash Hash of the order
                   * @return remainingRaw Inverse of the remaining amount of the order if order was filled at least once, otherwise 0
                   */
                  function rawRemainingInvalidatorForOrder(address maker, bytes32 orderHash) external view returns(uint256 remainingRaw);
                  /**
                   * @notice Cancels order's quote
                   * @param makerTraits Order makerTraits
                   * @param orderHash Hash of the order to cancel
                   */
                  function cancelOrder(MakerTraits makerTraits, bytes32 orderHash) external;
                  /**
                   * @notice Cancels orders' quotes
                   * @param makerTraits Orders makerTraits
                   * @param orderHashes Hashes of the orders to cancel
                   */
                  function cancelOrders(MakerTraits[] calldata makerTraits, bytes32[] calldata orderHashes) external;
                  /**
                   * @notice Cancels all quotes of the maker (works for bit-invalidating orders only)
                   * @param makerTraits Order makerTraits
                   * @param additionalMask Additional bitmask to invalidate orders
                   */
                  function bitsInvalidateForOrder(MakerTraits makerTraits, uint256 additionalMask) external;
                  /**
                   * @notice Returns order hash, hashed with limit order protocol contract EIP712
                   * @param order Order
                   * @return orderHash Hash of the order
                   */
                  function hashOrder(IOrderMixin.Order calldata order) external view returns(bytes32 orderHash);
                  /**
                   * @notice Delegates execution to custom implementation. Could be used to validate if `transferFrom` works properly
                   * @dev The function always reverts and returns the simulation results in revert data.
                   * @param target Addresses that will be delegated
                   * @param data Data that will be passed to delegatee
                   */
                  function simulate(address target, bytes calldata data) external;
                  /**
                   * @notice Fills order's quote, fully or partially (whichever is possible).
                   * @param order Order quote to fill
                   * @param r R component of signature
                   * @param vs VS component of signature
                   * @param amount Taker amount to fill
                   * @param takerTraits Specifies threshold as maximum allowed takingAmount when takingAmount is zero, otherwise specifies
                   * minimum allowed makingAmount. The 2nd (0 based index) highest bit specifies whether taker wants to skip maker's permit.
                   * @return makingAmount Actual amount transferred from maker to taker
                   * @return takingAmount Actual amount transferred from taker to maker
                   * @return orderHash Hash of the filled order
                   */
                  function fillOrder(
                      Order calldata order,
                      bytes32 r,
                      bytes32 vs,
                      uint256 amount,
                      TakerTraits takerTraits
                  ) external payable returns(uint256 makingAmount, uint256 takingAmount, bytes32 orderHash);
                  /**
                   * @notice Same as `fillOrder` but allows to specify arguments that are used by the taker.
                   * @param order Order quote to fill
                   * @param r R component of signature
                   * @param vs VS component of signature
                   * @param amount Taker amount to fill
                   * @param takerTraits Specifies threshold as maximum allowed takingAmount when takingAmount is zero, otherwise specifies
                   * minimum allowed makingAmount. The 2nd (0 based index) highest bit specifies whether taker wants to skip maker's permit.
                   * @param args Arguments that are used by the taker (target, extension, interaction, permit)
                   * @return makingAmount Actual amount transferred from maker to taker
                   * @return takingAmount Actual amount transferred from taker to maker
                   * @return orderHash Hash of the filled order
                   */
                  function fillOrderArgs(
                      IOrderMixin.Order calldata order,
                      bytes32 r,
                      bytes32 vs,
                      uint256 amount,
                      TakerTraits takerTraits,
                      bytes calldata args
                  ) external payable returns(uint256 makingAmount, uint256 takingAmount, bytes32 orderHash);
                  /**
                   * @notice Same as `fillOrder` but uses contract-based signatures.
                   * @param order Order quote to fill
                   * @param signature Signature to confirm quote ownership
                   * @param amount Taker amount to fill
                   * @param takerTraits Specifies threshold as maximum allowed takingAmount when takingAmount is zero, otherwise specifies
                   * minimum allowed makingAmount. The 2nd (0 based index) highest bit specifies whether taker wants to skip maker's permit.
                   * @return makingAmount Actual amount transferred from maker to taker
                   * @return takingAmount Actual amount transferred from taker to maker
                   * @return orderHash Hash of the filled order
                   * @dev See tests for examples
                   */
                  function fillContractOrder(
                      Order calldata order,
                      bytes calldata signature,
                      uint256 amount,
                      TakerTraits takerTraits
                  ) external returns(uint256 makingAmount, uint256 takingAmount, bytes32 orderHash);
                  /**
                   * @notice Same as `fillContractOrder` but allows to specify arguments that are used by the taker.
                   * @param order Order quote to fill
                   * @param signature Signature to confirm quote ownership
                   * @param amount Taker amount to fill
                   * @param takerTraits Specifies threshold as maximum allowed takingAmount when takingAmount is zero, otherwise specifies
                   * minimum allowed makingAmount. The 2nd (0 based index) highest bit specifies whether taker wants to skip maker's permit.
                   * @param args Arguments that are used by the taker (target, extension, interaction, permit)
                   * @return makingAmount Actual amount transferred from maker to taker
                   * @return takingAmount Actual amount transferred from taker to maker
                   * @return orderHash Hash of the filled order
                   * @dev See tests for examples
                   */
                  function fillContractOrderArgs(
                      Order calldata order,
                      bytes calldata signature,
                      uint256 amount,
                      TakerTraits takerTraits,
                      bytes calldata args
                  ) external returns(uint256 makingAmount, uint256 takingAmount, bytes32 orderHash);
              }
              // File @1inch/limit-order-protocol-contract/contracts/interfaces/[email protected]
              interface IAmountGetter {
                  /**
                   * @notice View method that gets called to determine the actual making amount
                   * @param order Order being processed
                   * @param extension Order extension data
                   * @param orderHash Hash of the order being processed
                   * @param taker Taker address
                   * @param takingAmount Actual taking amount
                   * @param remainingMakingAmount Order remaining making amount
                   * @param extraData Extra data
                   */
                  function getMakingAmount(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      bytes32 orderHash,
                      address taker,
                      uint256 takingAmount,
                      uint256 remainingMakingAmount,
                      bytes calldata extraData
                  ) external view returns (uint256);
                  /**
                   * @notice View method that gets called to determine the actual making amount
                   * @param order Order being processed
                   * @param extension Order extension data
                   * @param orderHash Hash of the order being processed
                   * @param taker Taker address
                   * @param makingAmount Actual taking amount
                   * @param remainingMakingAmount Order remaining making amount
                   * @param extraData Extra data
                   */
                  function getTakingAmount(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      bytes32 orderHash,
                      address taker,
                      uint256 makingAmount,
                      uint256 remainingMakingAmount,
                      bytes calldata extraData
                  ) external view returns (uint256);
              }
              // File @1inch/limit-order-protocol-contract/contracts/interfaces/[email protected]
              interface IPostInteraction {
                  /**
                   * @notice Callback method that gets called after all fund transfers
                   * @param order Order being processed
                   * @param extension Order extension data
                   * @param orderHash Hash of the order being processed
                   * @param taker Taker address
                   * @param makingAmount Actual making amount
                   * @param takingAmount Actual taking amount
                   * @param remainingMakingAmount Order remaining making amount
                   * @param extraData Extra data
                   */
                  function postInteraction(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      bytes32 orderHash,
                      address taker,
                      uint256 makingAmount,
                      uint256 takingAmount,
                      uint256 remainingMakingAmount,
                      bytes calldata extraData
                  ) external;
              }
              // File @1inch/limit-order-protocol-contract/contracts/interfaces/[email protected]
              interface IPreInteraction {
                  /**
                   * @notice Callback method that gets called before any funds transfers
                   * @param order Order being processed
                   * @param extension Order extension data
                   * @param orderHash Hash of the order being processed
                   * @param taker Taker address
                   * @param makingAmount Actual making amount
                   * @param takingAmount Actual taking amount
                   * @param remainingMakingAmount Order remaining making amount
                   * @param extraData Extra data
                   */
                  function preInteraction(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      bytes32 orderHash,
                      address taker,
                      uint256 makingAmount,
                      uint256 takingAmount,
                      uint256 remainingMakingAmount,
                      bytes calldata extraData
                  ) external;
              }
              // File @1inch/limit-order-protocol-contract/contracts/interfaces/[email protected]
              /**
               * @title Interface for interactor which acts after `maker -> taker` transfer but before `taker -> maker` transfer.
               * @notice The order filling steps are `preInteraction` =>` Transfer "maker -> taker"` => **`Interaction`** => `Transfer "taker -> maker"` => `postInteraction`
               */
              interface ITakerInteraction {
                  /**
                   * @dev This callback allows to interactively handle maker aseets to produce takers assets, doesn't supports ETH as taker assets
                   * @notice Callback method that gets called after maker fund transfer but before taker fund transfer
                   * @param order Order being processed
                   * @param extension Order extension data
                   * @param orderHash Hash of the order being processed
                   * @param taker Taker address
                   * @param makingAmount Actual making amount
                   * @param takingAmount Actual taking amount
                   * @param remainingMakingAmount Order remaining making amount
                   * @param extraData Extra data
                   */
                  function takerInteraction(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      bytes32 orderHash,
                      address taker,
                      uint256 makingAmount,
                      uint256 takingAmount,
                      uint256 remainingMakingAmount,
                      bytes calldata extraData
                  ) external;
              }
              // File @1inch/limit-order-protocol-contract/contracts/libraries/[email protected]
              type Offsets is uint256;
              /// @title OffsetsLib
              /// @dev A library for retrieving values by offsets from a concatenated calldata.
              library OffsetsLib {
                  /// @dev Error to be thrown when the offset is out of bounds.
                  error OffsetOutOfBounds();
                  /**
                   * @notice Retrieves the field value calldata corresponding to the provided field index from the concatenated calldata.
                   * @dev
                   * The function performs the following steps:
                   * 1. Retrieve the start and end of the segment corresponding to the provided index from the offsets array.
                   * 2. Get the value from segment using offset and length calculated based on the start and end of the segment.
                   * 3. Throw `OffsetOutOfBounds` error if the length of the segment is greater than the length of the concatenated data.
                   * @param offsets The offsets encoding the start and end of each segment within the concatenated calldata.
                   * @param concat The concatenated calldata.
                   * @param index The index of the segment to retrieve. The field index 0 corresponds to the lowest bytes of the offsets array.
                   * @return result The calldata from a segment of the concatenated calldata corresponding to the provided index.
                   */
                  function get(Offsets offsets, bytes calldata concat, uint256 index) internal pure returns(bytes calldata result) {
                      bytes4 exception = OffsetOutOfBounds.selector;
                      assembly ("memory-safe") {  // solhint-disable-line no-inline-assembly
                          let bitShift := shl(5, index)                                   // bitShift = index * 32
                          let begin := and(0xffffffff, shr(bitShift, shl(32, offsets)))   // begin = offsets[ bitShift : bitShift + 32 ]
                          let end := and(0xffffffff, shr(bitShift, offsets))              // end   = offsets[ bitShift + 32 : bitShift + 64 ]
                          result.offset := add(concat.offset, begin)
                          result.length := sub(end, begin)
                          if gt(end, concat.length) {
                              mstore(0, exception)
                              revert(0, 4)
                          }
                      }
                  }
              }
              // File @1inch/limit-order-protocol-contract/contracts/libraries/[email protected]
              /**
               * @title ExtensionLib
               * @notice Library for retrieving extensions information for the IOrderMixin Interface.
               */
              library ExtensionLib {
                  using AddressLib for Address;
                  using OffsetsLib for Offsets;
                  enum DynamicField {
                      MakerAssetSuffix,
                      TakerAssetSuffix,
                      MakingAmountData,
                      TakingAmountData,
                      Predicate,
                      MakerPermit,
                      PreInteractionData,
                      PostInteractionData,
                      CustomData
                  }
                  /**
                   * @notice Returns the MakerAssetSuffix from the provided extension calldata.
                   * @param extension The calldata from which the MakerAssetSuffix is to be retrieved.
                   * @return calldata Bytes representing the MakerAssetSuffix.
                   */
                  function makerAssetSuffix(bytes calldata extension) internal pure returns(bytes calldata) {
                      return _get(extension, DynamicField.MakerAssetSuffix);
                  }
                  /**
                   * @notice Returns the TakerAssetSuffix from the provided extension calldata.
                   * @param extension The calldata from which the TakerAssetSuffix is to be retrieved.
                   * @return calldata Bytes representing the TakerAssetSuffix.
                   */
                  function takerAssetSuffix(bytes calldata extension) internal pure returns(bytes calldata) {
                      return _get(extension, DynamicField.TakerAssetSuffix);
                  }
                  /**
                   * @notice Returns the MakingAmountData from the provided extension calldata.
                   * @param extension The calldata from which the MakingAmountData is to be retrieved.
                   * @return calldata Bytes representing the MakingAmountData.
                   */
                  function makingAmountData(bytes calldata extension) internal pure returns(bytes calldata) {
                      return _get(extension, DynamicField.MakingAmountData);
                  }
                  /**
                   * @notice Returns the TakingAmountData from the provided extension calldata.
                   * @param extension The calldata from which the TakingAmountData is to be retrieved.
                   * @return calldata Bytes representing the TakingAmountData.
                   */
                  function takingAmountData(bytes calldata extension) internal pure returns(bytes calldata) {
                      return _get(extension, DynamicField.TakingAmountData);
                  }
                  /**
                   * @notice Returns the order's predicate from the provided extension calldata.
                   * @param extension The calldata from which the predicate is to be retrieved.
                   * @return calldata Bytes representing the predicate.
                   */
                  function predicate(bytes calldata extension) internal pure returns(bytes calldata) {
                      return _get(extension, DynamicField.Predicate);
                  }
                  /**
                   * @notice Returns the maker's permit from the provided extension calldata.
                   * @param extension The calldata from which the maker's permit is to be retrieved.
                   * @return calldata Bytes representing the maker's permit.
                   */
                  function makerPermit(bytes calldata extension) internal pure returns(bytes calldata) {
                      return _get(extension, DynamicField.MakerPermit);
                  }
                  /**
                   * @notice Returns the pre-interaction from the provided extension calldata.
                   * @param extension The calldata from which the pre-interaction is to be retrieved.
                   * @return calldata Bytes representing the pre-interaction.
                   */
                  function preInteractionTargetAndData(bytes calldata extension) internal pure returns(bytes calldata) {
                      return _get(extension, DynamicField.PreInteractionData);
                  }
                  /**
                   * @notice Returns the post-interaction from the provided extension calldata.
                   * @param extension The calldata from which the post-interaction is to be retrieved.
                   * @return calldata Bytes representing the post-interaction.
                   */
                  function postInteractionTargetAndData(bytes calldata extension) internal pure returns(bytes calldata) {
                      return _get(extension, DynamicField.PostInteractionData);
                  }
                  /**
                   * @notice Returns extra suffix data from the provided extension calldata.
                   * @param extension The calldata from which the extra suffix data is to be retrieved.
                   * @return calldata Bytes representing the extra suffix data.
                   */
                  function customData(bytes calldata extension) internal pure returns(bytes calldata) {
                      if (extension.length < 0x20) return msg.data[:0];
                      uint256 offsets = uint256(bytes32(extension));
                      unchecked {
                          return extension[0x20 + (offsets >> 224):];
                      }
                  }
                  /**
                   * @notice Retrieves a specific field from the provided extension calldata.
                   * @dev The first 32 bytes of an extension calldata contain offsets to the end of each field within the calldata.
                   * @param extension The calldata from which the field is to be retrieved.
                   * @param field The specific dynamic field to retrieve from the extension.
                   * @return calldata Bytes representing the requested field.
                   */
                  function _get(bytes calldata extension, DynamicField field) private pure returns(bytes calldata) {
                      if (extension.length < 0x20) return msg.data[:0];
                      Offsets offsets;
                      bytes calldata concat;
                      assembly ("memory-safe") {  // solhint-disable-line no-inline-assembly
                          offsets := calldataload(extension.offset)
                          concat.offset := add(extension.offset, 0x20)
                          concat.length := sub(extension.length, 0x20)
                      }
                      return offsets.get(concat, uint256(field));
                  }
              }
              // File @1inch/limit-order-protocol-contract/contracts/libraries/[email protected]
              /// @title The helper library to calculate linearly taker amount from maker amount and vice versa.
              library AmountCalculatorLib {
                  /// @notice Calculates maker amount
                  /// @return Result Floored maker amount
                  function getMakingAmount(uint256 orderMakerAmount, uint256 orderTakerAmount, uint256 swapTakerAmount) internal pure returns(uint256) {
                      if ((swapTakerAmount | orderMakerAmount) >> 128 == 0) {
                          unchecked {
                              return (swapTakerAmount * orderMakerAmount) / orderTakerAmount;
                          }
                      }
                      return swapTakerAmount * orderMakerAmount / orderTakerAmount;
                  }
                  /// @notice Calculates taker amount
                  /// @return Result Ceiled taker amount
                  function getTakingAmount(uint256 orderMakerAmount, uint256 orderTakerAmount, uint256 swapMakerAmount) internal pure returns(uint256) {
                      if ((swapMakerAmount | orderTakerAmount) >> 128 == 0) {
                          unchecked {
                              return (swapMakerAmount * orderTakerAmount + orderMakerAmount - 1) / orderMakerAmount;
                          }
                      }
                      return (swapMakerAmount * orderTakerAmount + orderMakerAmount - 1) / orderMakerAmount;
                  }
              }
              // File @openzeppelin/contracts/interfaces/[email protected]
              // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1271.sol)
              /**
               * @dev Interface of the ERC1271 standard signature validation method for
               * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
               */
              interface IERC1271 {
                  /**
                   * @dev Should return whether the signature provided is valid for the provided data
                   * @param hash      Hash of the data to be signed
                   * @param signature Signature byte array associated with _data
                   */
                  function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
              }
              // File @1inch/solidity-utils/contracts/libraries/[email protected]
              library ECDSA {
                  // 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 (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): 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.
                  uint256 private constant _S_BOUNDARY = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 + 1;
                  uint256 private constant _COMPACT_S_MASK = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
                  uint256 private constant _COMPACT_V_SHIFT = 255;
                  function recover(
                      bytes32 hash,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) internal view returns (address signer) {
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          if lt(s, _S_BOUNDARY) {
                              let ptr := mload(0x40)
                              mstore(ptr, hash)
                              mstore(add(ptr, 0x20), v)
                              mstore(add(ptr, 0x40), r)
                              mstore(add(ptr, 0x60), s)
                              mstore(0, 0)
                              pop(staticcall(gas(), 0x1, ptr, 0x80, 0, 0x20))
                              signer := mload(0)
                          }
                      }
                  }
                  function recover(
                      bytes32 hash,
                      bytes32 r,
                      bytes32 vs
                  ) internal view returns (address signer) {
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let s := and(vs, _COMPACT_S_MASK)
                          if lt(s, _S_BOUNDARY) {
                              let ptr := mload(0x40)
                              mstore(ptr, hash)
                              mstore(add(ptr, 0x20), add(27, shr(_COMPACT_V_SHIFT, vs)))
                              mstore(add(ptr, 0x40), r)
                              mstore(add(ptr, 0x60), s)
                              mstore(0, 0)
                              pop(staticcall(gas(), 0x1, ptr, 0x80, 0, 0x20))
                              signer := mload(0)
                          }
                      }
                  }
                  /// @dev WARNING!!!
                  /// There is a known signature malleability issue with two representations of signatures!
                  /// Even though this function is able to verify both standard 65-byte and compact 64-byte EIP-2098 signatures
                  /// one should never use raw signatures for any kind of invalidation logic in their code.
                  /// As the standard and compact representations are interchangeable any invalidation logic that relies on
                  /// signature uniqueness will get rekt.
                  /// More info: https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-4h98-2769-gh6h
                  function recover(bytes32 hash, bytes calldata signature) internal view returns (address signer) {
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let ptr := mload(0x40)
                          // memory[ptr:ptr+0x80] = (hash, v, r, s)
                          switch signature.length
                          case 65 {
                              // memory[ptr+0x20:ptr+0x80] = (v, r, s)
                              mstore(add(ptr, 0x20), byte(0, calldataload(add(signature.offset, 0x40))))
                              calldatacopy(add(ptr, 0x40), signature.offset, 0x40)
                          }
                          case 64 {
                              // memory[ptr+0x20:ptr+0x80] = (v, r, s)
                              let vs := calldataload(add(signature.offset, 0x20))
                              mstore(add(ptr, 0x20), add(27, shr(_COMPACT_V_SHIFT, vs)))
                              calldatacopy(add(ptr, 0x40), signature.offset, 0x20)
                              mstore(add(ptr, 0x60), and(vs, _COMPACT_S_MASK))
                          }
                          default {
                              ptr := 0
                          }
                          if ptr {
                              if lt(mload(add(ptr, 0x60)), _S_BOUNDARY) {
                                  // memory[ptr:ptr+0x20] = (hash)
                                  mstore(ptr, hash)
                                  mstore(0, 0)
                                  pop(staticcall(gas(), 0x1, ptr, 0x80, 0, 0x20))
                                  signer := mload(0)
                              }
                          }
                      }
                  }
                  function recoverOrIsValidSignature(
                      address signer,
                      bytes32 hash,
                      bytes calldata signature
                  ) internal view returns (bool success) {
                      if (signer == address(0)) return false;
                      if ((signature.length == 64 || signature.length == 65) && recover(hash, signature) == signer) {
                          return true;
                      }
                      return isValidSignature(signer, hash, signature);
                  }
                  function recoverOrIsValidSignature(
                      address signer,
                      bytes32 hash,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) internal view returns (bool success) {
                      if (signer == address(0)) return false;
                      if (recover(hash, v, r, s) == signer) {
                          return true;
                      }
                      return isValidSignature(signer, hash, v, r, s);
                  }
                  function recoverOrIsValidSignature(
                      address signer,
                      bytes32 hash,
                      bytes32 r,
                      bytes32 vs
                  ) internal view returns (bool success) {
                      if (signer == address(0)) return false;
                      if (recover(hash, r, vs) == signer) {
                          return true;
                      }
                      return isValidSignature(signer, hash, r, vs);
                  }
                  function recoverOrIsValidSignature65(
                      address signer,
                      bytes32 hash,
                      bytes32 r,
                      bytes32 vs
                  ) internal view returns (bool success) {
                      if (signer == address(0)) return false;
                      if (recover(hash, r, vs) == signer) {
                          return true;
                      }
                      return isValidSignature65(signer, hash, r, vs);
                  }
                  function isValidSignature(
                      address signer,
                      bytes32 hash,
                      bytes calldata signature
                  ) internal view returns (bool success) {
                      // (bool success, bytes memory data) = signer.staticcall(abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature));
                      // return success && data.length >= 4 && abi.decode(data, (bytes4)) == IERC1271.isValidSignature.selector;
                      bytes4 selector = IERC1271.isValidSignature.selector;
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let ptr := mload(0x40)
                          mstore(ptr, selector)
                          mstore(add(ptr, 0x04), hash)
                          mstore(add(ptr, 0x24), 0x40)
                          mstore(add(ptr, 0x44), signature.length)
                          calldatacopy(add(ptr, 0x64), signature.offset, signature.length)
                          if staticcall(gas(), signer, ptr, add(0x64, signature.length), 0, 0x20) {
                              success := and(eq(selector, mload(0)), eq(returndatasize(), 0x20))
                          }
                      }
                  }
                  function isValidSignature(
                      address signer,
                      bytes32 hash,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) internal view returns (bool success) {
                      bytes4 selector = IERC1271.isValidSignature.selector;
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let ptr := mload(0x40)
                          mstore(ptr, selector)
                          mstore(add(ptr, 0x04), hash)
                          mstore(add(ptr, 0x24), 0x40)
                          mstore(add(ptr, 0x44), 65)
                          mstore(add(ptr, 0x64), r)
                          mstore(add(ptr, 0x84), s)
                          mstore8(add(ptr, 0xa4), v)
                          if staticcall(gas(), signer, ptr, 0xa5, 0, 0x20) {
                              success := and(eq(selector, mload(0)), eq(returndatasize(), 0x20))
                          }
                      }
                  }
                  function isValidSignature(
                      address signer,
                      bytes32 hash,
                      bytes32 r,
                      bytes32 vs
                  ) internal view returns (bool success) {
                      // (bool success, bytes memory data) = signer.staticcall(abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, abi.encodePacked(r, vs)));
                      // return success && data.length >= 4 && abi.decode(data, (bytes4)) == IERC1271.isValidSignature.selector;
                      bytes4 selector = IERC1271.isValidSignature.selector;
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let ptr := mload(0x40)
                          mstore(ptr, selector)
                          mstore(add(ptr, 0x04), hash)
                          mstore(add(ptr, 0x24), 0x40)
                          mstore(add(ptr, 0x44), 64)
                          mstore(add(ptr, 0x64), r)
                          mstore(add(ptr, 0x84), vs)
                          if staticcall(gas(), signer, ptr, 0xa4, 0, 0x20) {
                              success := and(eq(selector, mload(0)), eq(returndatasize(), 0x20))
                          }
                      }
                  }
                  function isValidSignature65(
                      address signer,
                      bytes32 hash,
                      bytes32 r,
                      bytes32 vs
                  ) internal view returns (bool success) {
                      // (bool success, bytes memory data) = signer.staticcall(abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, abi.encodePacked(r, vs & ~uint256(1 << 255), uint8(vs >> 255))));
                      // return success && data.length >= 4 && abi.decode(data, (bytes4)) == IERC1271.isValidSignature.selector;
                      bytes4 selector = IERC1271.isValidSignature.selector;
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let ptr := mload(0x40)
                          mstore(ptr, selector)
                          mstore(add(ptr, 0x04), hash)
                          mstore(add(ptr, 0x24), 0x40)
                          mstore(add(ptr, 0x44), 65)
                          mstore(add(ptr, 0x64), r)
                          mstore(add(ptr, 0x84), and(vs, _COMPACT_S_MASK))
                          mstore8(add(ptr, 0xa4), add(27, shr(_COMPACT_V_SHIFT, vs)))
                          if staticcall(gas(), signer, ptr, 0xa5, 0, 0x20) {
                              success := and(eq(selector, mload(0)), eq(returndatasize(), 0x20))
                          }
                      }
                  }
                  function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 res) {
                      // 32 is the length in bytes of hash, enforced by the type signature above
                      // return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
              32", hash));
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          mstore(0, 0x19457468657265756d205369676e6564204d6573736167653a0a333200000000) // "\\x19Ethereum Signed Message:\
              32"
                          mstore(28, hash)
                          res := keccak256(0, 60)
                      }
                  }
                  function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 res) {
                      // return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let ptr := mload(0x40)
                          mstore(ptr, 0x1901000000000000000000000000000000000000000000000000000000000000) // "\\x19\\x01"
                          mstore(add(ptr, 0x02), domainSeparator)
                          mstore(add(ptr, 0x22), structHash)
                          res := keccak256(ptr, 66)
                      }
                  }
              }
              // File @1inch/limit-order-protocol-contract/contracts/[email protected]
              /**
               * @title OrderLib
               * @dev The library provides common functionality for processing and manipulating limit orders.
               * It provides functionality to calculate and verify order hashes, calculate trade amounts, and validate
               * extension data associated with orders. The library also contains helper methods to get the receiver of
               * an order and call getter functions.
               */
               library OrderLib {
                  using AddressLib for Address;
                  using MakerTraitsLib for MakerTraits;
                  using ExtensionLib for bytes;
                  /// @dev Error to be thrown when the extension data of an order is missing.
                  error MissingOrderExtension();
                  /// @dev Error to be thrown when the order has an unexpected extension.
                  error UnexpectedOrderExtension();
                  /// @dev Error to be thrown when the order extension hash is invalid.
                  error InvalidExtensionHash();
                  /// @dev The typehash of the order struct.
                  bytes32 constant internal _LIMIT_ORDER_TYPEHASH = keccak256(
                      "Order("
                          "uint256 salt,"
                          "address maker,"
                          "address receiver,"
                          "address makerAsset,"
                          "address takerAsset,"
                          "uint256 makingAmount,"
                          "uint256 takingAmount,"
                          "uint256 makerTraits"
                      ")"
                  );
                  uint256 constant internal _ORDER_STRUCT_SIZE = 0x100;
                  uint256 constant internal _DATA_HASH_SIZE = 0x120;
                  /**
                    * @notice Calculates the hash of an order.
                    * @param order The order to be hashed.
                    * @param domainSeparator The domain separator to be used for the EIP-712 hashing.
                    * @return result The keccak256 hash of the order data.
                    */
                  function hash(IOrderMixin.Order calldata order, bytes32 domainSeparator) internal pure returns(bytes32 result) {
                      bytes32 typehash = _LIMIT_ORDER_TYPEHASH;
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let ptr := mload(0x40)
                          // keccak256(abi.encode(_LIMIT_ORDER_TYPEHASH, order));
                          mstore(ptr, typehash)
                          calldatacopy(add(ptr, 0x20), order, _ORDER_STRUCT_SIZE)
                          result := keccak256(ptr, _DATA_HASH_SIZE)
                      }
                      result = ECDSA.toTypedDataHash(domainSeparator, result);
                  }
                  /**
                    * @notice Returns the receiver address for an order.
                    * @param order The order.
                    * @return receiver The address of the receiver, either explicitly defined in the order or the maker's address if not specified.
                    */
                  function getReceiver(IOrderMixin.Order calldata order) internal pure returns(address /*receiver*/) {
                      address receiver = order.receiver.get();
                      return receiver != address(0) ? receiver : order.maker.get();
                  }
                  /**
                    * @notice Calculates the making amount based on the requested taking amount.
                    * @dev If getter is specified in the extension data, the getter is called to calculate the making amount,
                    * otherwise the making amount is calculated linearly.
                    * @param order The order.
                    * @param extension The extension data associated with the order.
                    * @param requestedTakingAmount The amount the taker wants to take.
                    * @param remainingMakingAmount The remaining amount of the asset left to fill.
                    * @param orderHash The hash of the order.
                    * @return makingAmount The amount of the asset the maker receives.
                    */
                  function calculateMakingAmount(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      uint256 requestedTakingAmount,
                      uint256 remainingMakingAmount,
                      bytes32 orderHash
                  ) internal view returns(uint256) {
                      bytes calldata data = extension.makingAmountData();
                      if (data.length == 0) {
                          // Linear proportion
                          return AmountCalculatorLib.getMakingAmount(order.makingAmount, order.takingAmount, requestedTakingAmount);
                      }
                      return IAmountGetter(address(bytes20(data))).getMakingAmount(
                          order,
                          extension,
                          orderHash,
                          msg.sender,
                          requestedTakingAmount,
                          remainingMakingAmount,
                          data[20:]
                      );
                  }
                  /**
                    * @notice Calculates the taking amount based on the requested making amount.
                    * @dev If getter is specified in the extension data, the getter is called to calculate the taking amount,
                    * otherwise the taking amount is calculated linearly.
                    * @param order The order.
                    * @param extension The extension data associated with the order.
                    * @param requestedMakingAmount The amount the maker wants to receive.
                    * @param remainingMakingAmount The remaining amount of the asset left to be filled.
                    * @param orderHash The hash of the order.
                    * @return takingAmount The amount of the asset the taker takes.
                    */
                  function calculateTakingAmount(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      uint256 requestedMakingAmount,
                      uint256 remainingMakingAmount,
                      bytes32 orderHash
                  ) internal view returns(uint256) {
                      bytes calldata data = extension.takingAmountData();
                      if (data.length == 0) {
                          // Linear proportion
                          return AmountCalculatorLib.getTakingAmount(order.makingAmount, order.takingAmount, requestedMakingAmount);
                      }
                      return IAmountGetter(address(bytes20(data))).getTakingAmount(
                          order,
                          extension,
                          orderHash,
                          msg.sender,
                          requestedMakingAmount,
                          remainingMakingAmount,
                          data[20:]
                      );
                  }
                  /**
                    * @dev Validates the extension associated with an order.
                    * @param order The order to validate against.
                    * @param extension The extension associated with the order.
                    * @return valid True if the extension is valid, false otherwise.
                    * @return errorSelector The error selector if the extension is invalid, 0x00000000 otherwise.
                    */
                  function isValidExtension(IOrderMixin.Order calldata order, bytes calldata extension) internal pure returns(bool, bytes4) {
                      if (order.makerTraits.hasExtension()) {
                          if (extension.length == 0) return (false, MissingOrderExtension.selector);
                          // Lowest 160 bits of the order salt must be equal to the lowest 160 bits of the extension hash
                          if (uint256(keccak256(extension)) & type(uint160).max != order.salt & type(uint160).max) return (false, InvalidExtensionHash.selector);
                      } else {
                          if (extension.length > 0) return (false, UnexpectedOrderExtension.selector);
                      }
                      return (true, 0x00000000);
                  }
              }
              // File @1inch/limit-order-protocol-contract/contracts/helpers/[email protected]
              /// @title A helper contract for executing boolean functions on arbitrary target call results
              contract PredicateHelper {
                  error ArbitraryStaticCallFailed();
                  /// @notice Calls every target with corresponding data
                  /// @return Result True if call to any target returned True. Otherwise, false
                  function or(uint256 offsets, bytes calldata data) public view returns(bool) {
                      uint256 previous;
                      for (uint256 current; (current = uint32(offsets)) != 0; offsets >>= 32) {
                          (bool success, uint256 res) = _staticcallForUint(address(this), data[previous:current]);
                          if (success && res == 1) {
                              return true;
                          }
                          previous = current;
                      }
                      return false;
                  }
                  /// @notice Calls every target with corresponding data
                  /// @return Result True if calls to all targets returned True. Otherwise, false
                  function and(uint256 offsets, bytes calldata data) public view returns(bool) {
                      uint256 previous;
                      for (uint256 current; (current = uint32(offsets)) != 0; offsets >>= 32) {
                          (bool success, uint256 res) = _staticcallForUint(address(this), data[previous:current]);
                          if (!success || res != 1) {
                              return false;
                          }
                          previous = current;
                      }
                      return true;
                  }
                  /// @notice Calls target with specified data and tests if it's equal to 0
                  /// @return Result True if call to target returns 0. Otherwise, false
                  function not(bytes calldata data) public view returns(bool) {
                      (bool success, uint256 res) = _staticcallForUint(address(this), data);
                      return success && res == 0;
                  }
                  /// @notice Calls target with specified data and tests if it's equal to the value
                  /// @param value Value to test
                  /// @return Result True if call to target returns the same value as `value`. Otherwise, false
                  function eq(uint256 value, bytes calldata data) public view returns(bool) {
                      (bool success, uint256 res) = _staticcallForUint(address(this), data);
                      return success && res == value;
                  }
                  /// @notice Calls target with specified data and tests if it's lower than value
                  /// @param value Value to test
                  /// @return Result True if call to target returns value which is lower than `value`. Otherwise, false
                  function lt(uint256 value, bytes calldata data) public view returns(bool) {
                      (bool success, uint256 res) = _staticcallForUint(address(this), data);
                      return success && res < value;
                  }
                  /// @notice Calls target with specified data and tests if it's bigger than value
                  /// @param value Value to test
                  /// @return Result True if call to target returns value which is bigger than `value`. Otherwise, false
                  function gt(uint256 value, bytes calldata data) public view returns(bool) {
                      (bool success, uint256 res) = _staticcallForUint(address(this), data);
                      return success && res > value;
                  }
                  /// @notice Performs an arbitrary call to target with data
                  /// @return Result Bytes transmuted to uint256
                  function arbitraryStaticCall(address target, bytes calldata data) public view returns(uint256) {
                      (bool success, uint256 res) = _staticcallForUint(target, data);
                      if (!success) revert ArbitraryStaticCallFailed();
                      return res;
                  }
                  function _staticcallForUint(address target, bytes calldata data) internal view returns(bool success, uint256 res) {
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let ptr := mload(0x40)
                          calldatacopy(ptr, data.offset, data.length)
                          success := staticcall(gas(), target, ptr, data.length, 0x0, 0x20)
                          success := and(success, eq(returndatasize(), 32))
                          if success {
                              res := mload(0)
                          }
                      }
                  }
              }
              // File @1inch/limit-order-protocol-contract/contracts/helpers/[email protected]
              /// @title A helper contract to manage nonce with the series
              contract SeriesEpochManager {
                  error AdvanceEpochFailed();
                  event EpochIncreased(address indexed maker, uint256 series, uint256 newEpoch);
                  // {
                  //    1: {
                  //        '0x762f73Ad...842Ffa8': 0,
                  //        '0xd20c41ee...32aaDe2': 1
                  //    },
                  //    2: {
                  //        '0x762f73Ad...842Ffa8': 3,
                  //        '0xd20c41ee...32aaDe2': 15
                  //    },
                  //    ...
                  // }
                  mapping(uint256 seriesId => uint256 epoch) private _epochs;
                  /// @notice Returns nonce for `maker` and `series`
                  function epoch(address maker, uint96 series) public view returns(uint256) {
                      return _epochs[uint160(maker) | (uint256(series) << 160)];
                  }
                  /// @notice Advances nonce by one
                  function increaseEpoch(uint96 series) external {
                      advanceEpoch(series, 1);
                  }
                  /// @notice Advances nonce by specified amount
                  function advanceEpoch(uint96 series, uint256 amount) public {
                      if (amount == 0 || amount > 255) revert AdvanceEpochFailed();
                      unchecked {
                          uint256 key = uint160(msg.sender) | (uint256(series) << 160);
                          uint256 newEpoch = _epochs[key] + amount;
                          _epochs[key] = newEpoch;
                          emit EpochIncreased(msg.sender, series, newEpoch);
                      }
                  }
                  /// @notice Checks if `maker` has specified `makerEpoch` for `series`
                  /// @return Result True if `maker` has specified epoch. Otherwise, false
                  function epochEquals(address maker, uint256 series, uint256 makerEpoch) public view returns(bool) {
                      return _epochs[uint160(maker) | (uint256(series) << 160)] == makerEpoch;
                  }
              }
              // File @1inch/limit-order-protocol-contract/contracts/libraries/[email protected]
              /**
               * @title BitInvalidatorLib
               * @dev The library provides a mechanism to invalidate objects based on a bit invalidator.
               * The bit invalidator holds a mapping where each key represents a slot number and each value contains an integer.
               * Each bit of the integer represents whether the object with corresponding index is valid or has been invalidated (0 - valid, 1 - invalidated).
               * The nonce given to access or invalidate an entity's state follows this structure:
               * - bits [0..7] represent the object state index in the slot.
               * - bits [8..255] represent the slot number (mapping key).
               */
              library BitInvalidatorLib {
                  /// @dev The error is thrown when an attempt is made to invalidate an already invalidated entity.
                  error BitInvalidatedOrder();
                  struct Data {
                      mapping(uint256 slotIndex => uint256 slotData) _raw;
                  }
                  /**
                   * @notice Retrieves the validity status of entities in a specific slot.
                   * @dev Each bit in the returned value corresponds to the validity of an entity. 0 for valid, 1 for invalidated.
                   * @param self The data structure.
                   * @param nonce The nonce identifying the slot.
                   * @return result The validity status of entities in the slot as a uint256.
                   */
                  function checkSlot(Data storage self, uint256 nonce) internal view returns(uint256) {
                      uint256 invalidatorSlot = nonce >> 8;
                      return self._raw[invalidatorSlot];
                  }
                  /**
                   * @notice Checks the validity of a specific entity and invalidates it if valid.
                   * @dev Throws an error if the entity has already been invalidated.
                   * @param self The data structure.
                   * @param nonce The nonce identifying the slot and the entity.
                   */
                  function checkAndInvalidate(Data storage self, uint256 nonce) internal {
                      uint256 invalidatorSlot = nonce >> 8;
                      uint256 invalidatorBit = 1 << (nonce & 0xff);
                      uint256 invalidator = self._raw[invalidatorSlot];
                      if (invalidator & invalidatorBit == invalidatorBit) revert BitInvalidatedOrder();
                      self._raw[invalidatorSlot] = invalidator | invalidatorBit;
                  }
                  /**
                   * @notice Invalidates multiple entities in a single slot.
                   * @dev The entities to be invalidated are identified by setting their corresponding bits to 1 in a mask.
                   * @param self The data structure.
                   * @param nonce The nonce identifying the slot.
                   * @param additionalMask A mask of bits to be invalidated.
                   * @return result Resulting validity status of entities in the slot as a uint256.
                   */
                  function massInvalidate(Data storage self, uint256 nonce, uint256 additionalMask) internal returns(uint256 result) {
                      uint256 invalidatorSlot = nonce >> 8;
                      uint256 invalidatorBits = (1 << (nonce & 0xff)) | additionalMask;
                      result = self._raw[invalidatorSlot] | invalidatorBits;
                      self._raw[invalidatorSlot] = result;
                  }
              }
              // File @1inch/limit-order-protocol-contract/contracts/libraries/[email protected]
              library Errors {
                  error InvalidMsgValue();
                  error ETHTransferFailed();
              }
              // File @1inch/limit-order-protocol-contract/contracts/libraries/[email protected]
              type RemainingInvalidator is uint256;
              /**
               * @title RemainingInvalidatorLib
               * @notice The library provides a mechanism to invalidate order based on the remaining amount of the order.
               * @dev The remaining amount is used as a nonce to invalidate the order.
               * When order is created, the remaining invalidator is 0.
               * When order is filled, the remaining invalidator is the inverse of the remaining amount.
               */
              library RemainingInvalidatorLib {
                  /// @dev The error is thrown when an attempt is made to invalidate an already invalidated entity.
                  error RemainingInvalidatedOrder();
                  /**
                   * @notice Checks if an order is new based on the invalidator value.
                   * @param invalidator The remaining invalidator of the order.
                   * @return result Whether the order is new or not.
                   */
                  function isNewOrder(RemainingInvalidator invalidator) internal pure returns(bool) {
                      return RemainingInvalidator.unwrap(invalidator) == 0;
                  }
                  /**
                   * @notice Retrieves the remaining amount for an order.
                   * @dev If the order is unknown, a RemainingInvalidatedOrder error is thrown.
                   * @param invalidator The remaining invalidator for the order.
                   * @return result The remaining amount for the order.
                   */
                  function remaining(RemainingInvalidator invalidator) internal pure returns(uint256) {
                      uint256 value = RemainingInvalidator.unwrap(invalidator);
                      if (value == 0) {
                          revert RemainingInvalidatedOrder();
                      }
                      unchecked {
                          return ~value;
                      }
                  }
                  /**
                   * @notice Calculates the remaining amount for an order.
                   * @dev If the order is unknown, the order maker amount is returned.
                   * @param invalidator The remaining invalidator for the order.
                   * @param orderMakerAmount The amount to return if the order is new.
                   * @return result The remaining amount for the order.
                   */
                  function remaining(RemainingInvalidator invalidator, uint256 orderMakerAmount) internal pure returns(uint256) {
                      uint256 value = RemainingInvalidator.unwrap(invalidator);
                      if (value == 0) {
                          return orderMakerAmount;
                      }
                      unchecked {
                          return ~value;
                      }
                  }
                  /**
                   * @notice Calculates the remaining invalidator of the order.
                   * @param remainingMakingAmount The remaining making amount of the order.
                   * @param makingAmount The making amount of the order.
                   * @return result The remaining invalidator for the order.
                   */
                  function remains(uint256 remainingMakingAmount, uint256 makingAmount) internal pure returns(RemainingInvalidator) {
                      unchecked {
                          return RemainingInvalidator.wrap(~(remainingMakingAmount - makingAmount));
                      }
                  }
                  /**
                   * @notice Provides the remaining invalidator for a fully filled order.
                   * @return result The remaining invalidator for a fully filled order.
                   */
                  function fullyFilled() internal pure returns(RemainingInvalidator) {
                      return RemainingInvalidator.wrap(type(uint256).max);
                  }
              }
              // File @openzeppelin/contracts/token/ERC20/[email protected]
              // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  /**
                   * @dev Emitted when `value` tokens are moved from one account (`from`) to
                   * another (`to`).
                   *
                   * Note that `value` may be zero.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  /**
                   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                   * a call to {approve}. `value` is the new allowance.
                   */
                  event Approval(address indexed owner, address indexed spender, uint256 value);
                  /**
                   * @dev Returns the value of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
                  /**
                   * @dev Returns the value of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
                  /**
                   * @dev Moves a `value` amount of tokens from the caller's account to `to`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address to, uint256 value) external returns (bool);
                  /**
                   * @dev Returns the remaining number of tokens that `spender` will be
                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                   * zero by default.
                   *
                   * This value changes when {approve} or {transferFrom} are called.
                   */
                  function allowance(address owner, address spender) external view returns (uint256);
                  /**
                   * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
                   * caller's tokens.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                   * that someone may use both the old and the new allowance by unfortunate
                   * transaction ordering. One possible solution to mitigate this race
                   * condition is to first reduce the spender's allowance to 0 and set the
                   * desired value afterwards:
                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address spender, uint256 value) external returns (bool);
                  /**
                   * @dev Moves a `value` amount of tokens from `from` to `to` using the
                   * allowance mechanism. `value` is then deducted from the caller's
                   * allowance.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(address from, address to, uint256 value) external returns (bool);
              }
              // File @1inch/solidity-utils/contracts/interfaces/[email protected]
              interface IWETH is IERC20 {
                  event Deposit(address indexed dst, uint256 wad);
                  event Withdrawal(address indexed src, uint256 wad);
                  function deposit() external payable;
                  function withdraw(uint256 amount) external;
              }
              // File @1inch/solidity-utils/contracts/interfaces/[email protected]
              interface IDaiLikePermit {
                  function permit(
                      address holder,
                      address spender,
                      uint256 nonce,
                      uint256 expiry,
                      bool allowed,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) external;
              }
              // File @1inch/solidity-utils/contracts/interfaces/[email protected]
              interface IPermit2 {
                  struct PermitDetails {
                      // ERC20 token address
                      address token;
                      // the maximum amount allowed to spend
                      uint160 amount;
                      // timestamp at which a spender's token allowances become invalid
                      uint48 expiration;
                      // an incrementing value indexed per owner,token,and spender for each signature
                      uint48 nonce;
                  }
                  /// @notice The permit message signed for a single token allownce
                  struct PermitSingle {
                      // the permit data for a single token alownce
                      PermitDetails details;
                      // address permissioned on the allowed tokens
                      address spender;
                      // deadline on the permit signature
                      uint256 sigDeadline;
                  }
                  /// @notice Packed allowance
                  struct PackedAllowance {
                      // amount allowed
                      uint160 amount;
                      // permission expiry
                      uint48 expiration;
                      // an incrementing value indexed per owner,token,and spender for each signature
                      uint48 nonce;
                  }
                  function transferFrom(address user, address spender, uint160 amount, address token) external;
                  function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;
                  function allowance(address user, address token, address spender) external view returns (PackedAllowance memory);
              }
              // File @1inch/solidity-utils/contracts/libraries/[email protected]
              /// @title Revert reason forwarder.
              library RevertReasonForwarder {
                  /// @dev Forwards latest externall call revert.
                  function reRevert() internal pure {
                      // bubble up revert reason from latest external call
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let ptr := mload(0x40)
                          returndatacopy(ptr, 0, returndatasize())
                          revert(ptr, returndatasize())
                      }
                  }
                  /// @dev Returns latest external call revert reason.
                  function reReason() internal pure returns (bytes memory reason) {
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          reason := mload(0x40)
                          let length := returndatasize()
                          mstore(reason, length)
                          returndatacopy(add(reason, 0x20), 0, length)
                          mstore(0x40, add(reason, add(0x20, length)))
                      }
                  }
              }
              // File @openzeppelin/contracts/token/ERC20/extensions/[email protected]
              // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
              /**
               * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
               * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
               *
               * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
               * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
               * need to send a transaction, and thus is not required to hold Ether at all.
               *
               * ==== Security Considerations
               *
               * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
               * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
               * considered as an intention to spend the allowance in any specific way. The second is that because permits have
               * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
               * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
               * generally recommended is:
               *
               * ```solidity
               * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
               *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
               *     doThing(..., value);
               * }
               *
               * function doThing(..., uint256 value) public {
               *     token.safeTransferFrom(msg.sender, address(this), value);
               *     ...
               * }
               * ```
               *
               * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
               * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
               * {SafeERC20-safeTransferFrom}).
               *
               * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
               * contracts should have entry points that don't rely on permit.
               */
              interface IERC20Permit {
                  /**
                   * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                   * given ``owner``'s signed approval.
                   *
                   * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                   * ordering also apply here.
                   *
                   * Emits an {Approval} event.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   * - `deadline` must be a timestamp in the future.
                   * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                   * over the EIP712-formatted function arguments.
                   * - the signature must use ``owner``'s current nonce (see {nonces}).
                   *
                   * For more information on the signature format, see the
                   * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                   * section].
                   *
                   * CAUTION: See Security Considerations above.
                   */
                  function permit(
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) external;
                  /**
                   * @dev Returns the current nonce for `owner`. This value must be
                   * included whenever a signature is generated for {permit}.
                   *
                   * Every successful call to {permit} increases ``owner``'s nonce by one. This
                   * prevents a signature from being used multiple times.
                   */
                  function nonces(address owner) external view returns (uint256);
                  /**
                   * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                   */
                  // solhint-disable-next-line func-name-mixedcase
                  function DOMAIN_SEPARATOR() external view returns (bytes32);
              }
              // File @1inch/solidity-utils/contracts/libraries/[email protected]
              /**
               * @title Implements efficient safe methods for ERC20 interface.
               * @notice Compared to the standard ERC20, this implementation offers several enhancements:
               * 1. more gas-efficient, providing significant savings in transaction costs.
               * 2. support for different permit implementations
               * 3. forceApprove functionality
               * 4. support for WETH deposit and withdraw
               */
              library SafeERC20 {
                  error SafeTransferFailed();
                  error SafeTransferFromFailed();
                  error ForceApproveFailed();
                  error SafeIncreaseAllowanceFailed();
                  error SafeDecreaseAllowanceFailed();
                  error SafePermitBadLength();
                  error Permit2TransferAmountTooHigh();
                  // Uniswap Permit2 address
                  address private constant _PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
                  bytes4 private constant _PERMIT_LENGTH_ERROR = 0x68275857;  // SafePermitBadLength.selector
                  uint256 private constant _RAW_CALL_GAS_LIMIT = 5000;
                  /**
                   * @notice Fetches the balance of a specific ERC20 token held by an account.
                   * Consumes less gas then regular `ERC20.balanceOf`.
                   * @dev Note that the implementation does not perform dirty bits cleaning, so it is the
                   * responsibility of the caller to make sure that the higher 96 bits of the `account` parameter are clean.
                   * @param token The IERC20 token contract for which the balance will be fetched.
                   * @param account The address of the account whose token balance will be fetched.
                   * @return tokenBalance The balance of the specified ERC20 token held by the account.
                   */
                  function safeBalanceOf(
                      IERC20 token,
                      address account
                  ) internal view returns(uint256 tokenBalance) {
                      bytes4 selector = IERC20.balanceOf.selector;
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          mstore(0x00, selector)
                          mstore(0x04, account)
                          let success := staticcall(gas(), token, 0x00, 0x24, 0x00, 0x20)
                          tokenBalance := mload(0)
                          if or(iszero(success), lt(returndatasize(), 0x20)) {
                              let ptr := mload(0x40)
                              returndatacopy(ptr, 0, returndatasize())
                              revert(ptr, returndatasize())
                          }
                      }
                  }
                  /**
                   * @notice Attempts to safely transfer tokens from one address to another.
                   * @dev If permit2 is true, uses the Permit2 standard; otherwise uses the standard ERC20 transferFrom.
                   * Either requires `true` in return data, or requires target to be smart-contract and empty return data.
                   * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
                   * the caller to make sure that the higher 96 bits of the `from` and `to` parameters are clean.
                   * @param token The IERC20 token contract from which the tokens will be transferred.
                   * @param from The address from which the tokens will be transferred.
                   * @param to The address to which the tokens will be transferred.
                   * @param amount The amount of tokens to transfer.
                   * @param permit2 If true, uses the Permit2 standard for the transfer; otherwise uses the standard ERC20 transferFrom.
                   */
                  function safeTransferFromUniversal(
                      IERC20 token,
                      address from,
                      address to,
                      uint256 amount,
                      bool permit2
                  ) internal {
                      if (permit2) {
                          safeTransferFromPermit2(token, from, to, amount);
                      } else {
                          safeTransferFrom(token, from, to, amount);
                      }
                  }
                  /**
                   * @notice Attempts to safely transfer tokens from one address to another using the ERC20 standard.
                   * @dev Either requires `true` in return data, or requires target to be smart-contract and empty return data.
                   * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
                   * the caller to make sure that the higher 96 bits of the `from` and `to` parameters are clean.
                   * @param token The IERC20 token contract from which the tokens will be transferred.
                   * @param from The address from which the tokens will be transferred.
                   * @param to The address to which the tokens will be transferred.
                   * @param amount The amount of tokens to transfer.
                   */
                  function safeTransferFrom(
                      IERC20 token,
                      address from,
                      address to,
                      uint256 amount
                  ) internal {
                      bytes4 selector = token.transferFrom.selector;
                      bool success;
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let data := mload(0x40)
                          mstore(data, selector)
                          mstore(add(data, 0x04), from)
                          mstore(add(data, 0x24), to)
                          mstore(add(data, 0x44), amount)
                          success := call(gas(), token, 0, data, 100, 0x0, 0x20)
                          if success {
                              switch returndatasize()
                              case 0 {
                                  success := gt(extcodesize(token), 0)
                              }
                              default {
                                  success := and(gt(returndatasize(), 31), eq(mload(0), 1))
                              }
                          }
                      }
                      if (!success) revert SafeTransferFromFailed();
                  }
                  /**
                   * @notice Attempts to safely transfer tokens from one address to another using the Permit2 standard.
                   * @dev Either requires `true` in return data, or requires target to be smart-contract and empty return data.
                   * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
                   * the caller to make sure that the higher 96 bits of the `from` and `to` parameters are clean.
                   * @param token The IERC20 token contract from which the tokens will be transferred.
                   * @param from The address from which the tokens will be transferred.
                   * @param to The address to which the tokens will be transferred.
                   * @param amount The amount of tokens to transfer.
                   */
                  function safeTransferFromPermit2(
                      IERC20 token,
                      address from,
                      address to,
                      uint256 amount
                  ) internal {
                      if (amount > type(uint160).max) revert Permit2TransferAmountTooHigh();
                      bytes4 selector = IPermit2.transferFrom.selector;
                      bool success;
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let data := mload(0x40)
                          mstore(data, selector)
                          mstore(add(data, 0x04), from)
                          mstore(add(data, 0x24), to)
                          mstore(add(data, 0x44), amount)
                          mstore(add(data, 0x64), token)
                          success := call(gas(), _PERMIT2, 0, data, 0x84, 0x0, 0x0)
                          if success {
                              success := gt(extcodesize(_PERMIT2), 0)
                          }
                      }
                      if (!success) revert SafeTransferFromFailed();
                  }
                  /**
                   * @notice Attempts to safely transfer tokens to another address.
                   * @dev Either requires `true` in return data, or requires target to be smart-contract and empty return data.
                   * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
                   * the caller to make sure that the higher 96 bits of the `to` parameter are clean.
                   * @param token The IERC20 token contract from which the tokens will be transferred.
                   * @param to The address to which the tokens will be transferred.
                   * @param value The amount of tokens to transfer.
                   */
                  function safeTransfer(
                      IERC20 token,
                      address to,
                      uint256 value
                  ) internal {
                      if (!_makeCall(token, token.transfer.selector, to, value)) {
                          revert SafeTransferFailed();
                      }
                  }
                  /**
                   * @notice Attempts to approve a spender to spend a certain amount of tokens.
                   * @dev If `approve(from, to, amount)` fails, it tries to set the allowance to zero, and retries the `approve` call.
                   * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
                   * the caller to make sure that the higher 96 bits of the `spender` parameter are clean.
                   * @param token The IERC20 token contract on which the call will be made.
                   * @param spender The address which will spend the funds.
                   * @param value The amount of tokens to be spent.
                   */
                  function forceApprove(
                      IERC20 token,
                      address spender,
                      uint256 value
                  ) internal {
                      if (!_makeCall(token, token.approve.selector, spender, value)) {
                          if (
                              !_makeCall(token, token.approve.selector, spender, 0) ||
                              !_makeCall(token, token.approve.selector, spender, value)
                          ) {
                              revert ForceApproveFailed();
                          }
                      }
                  }
                  /**
                   * @notice Safely increases the allowance of a spender.
                   * @dev Increases with safe math check. Checks if the increased allowance will overflow, if yes, then it reverts the transaction.
                   * Then uses `forceApprove` to increase the allowance.
                   * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
                   * the caller to make sure that the higher 96 bits of the `spender` parameter are clean.
                   * @param token The IERC20 token contract on which the call will be made.
                   * @param spender The address which will spend the funds.
                   * @param value The amount of tokens to increase the allowance by.
                   */
                  function safeIncreaseAllowance(
                      IERC20 token,
                      address spender,
                      uint256 value
                  ) internal {
                      uint256 allowance = token.allowance(address(this), spender);
                      if (value > type(uint256).max - allowance) revert SafeIncreaseAllowanceFailed();
                      forceApprove(token, spender, allowance + value);
                  }
                  /**
                   * @notice Safely decreases the allowance of a spender.
                   * @dev Decreases with safe math check. Checks if the decreased allowance will underflow, if yes, then it reverts the transaction.
                   * Then uses `forceApprove` to increase the allowance.
                   * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
                   * the caller to make sure that the higher 96 bits of the `spender` parameter are clean.
                   * @param token The IERC20 token contract on which the call will be made.
                   * @param spender The address which will spend the funds.
                   * @param value The amount of tokens to decrease the allowance by.
                   */
                  function safeDecreaseAllowance(
                      IERC20 token,
                      address spender,
                      uint256 value
                  ) internal {
                      uint256 allowance = token.allowance(address(this), spender);
                      if (value > allowance) revert SafeDecreaseAllowanceFailed();
                      forceApprove(token, spender, allowance - value);
                  }
                  /**
                   * @notice Attempts to execute the `permit` function on the provided token with the sender and contract as parameters.
                   * Permit type is determined automatically based on permit calldata (IERC20Permit, IDaiLikePermit, and IPermit2).
                   * @dev Wraps `tryPermit` function and forwards revert reason if permit fails.
                   * @param token The IERC20 token to execute the permit function on.
                   * @param permit The permit data to be used in the function call.
                   */
                  function safePermit(IERC20 token, bytes calldata permit) internal {
                      if (!tryPermit(token, msg.sender, address(this), permit)) RevertReasonForwarder.reRevert();
                  }
                  /**
                   * @notice Attempts to execute the `permit` function on the provided token with custom owner and spender parameters.
                   * Permit type is determined automatically based on permit calldata (IERC20Permit, IDaiLikePermit, and IPermit2).
                   * @dev Wraps `tryPermit` function and forwards revert reason if permit fails.
                   * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
                   * the caller to make sure that the higher 96 bits of the `owner` and `spender` parameters are clean.
                   * @param token The IERC20 token to execute the permit function on.
                   * @param owner The owner of the tokens for which the permit is made.
                   * @param spender The spender allowed to spend the tokens by the permit.
                   * @param permit The permit data to be used in the function call.
                   */
                  function safePermit(IERC20 token, address owner, address spender, bytes calldata permit) internal {
                      if (!tryPermit(token, owner, spender, permit)) RevertReasonForwarder.reRevert();
                  }
                  /**
                   * @notice Attempts to execute the `permit` function on the provided token with the sender and contract as parameters.
                   * @dev Invokes `tryPermit` with sender as owner and contract as spender.
                   * @param token The IERC20 token to execute the permit function on.
                   * @param permit The permit data to be used in the function call.
                   * @return success Returns true if the permit function was successfully executed, false otherwise.
                   */
                  function tryPermit(IERC20 token, bytes calldata permit) internal returns(bool success) {
                      return tryPermit(token, msg.sender, address(this), permit);
                  }
                  /**
                   * @notice The function attempts to call the permit function on a given ERC20 token.
                   * @dev The function is designed to support a variety of permit functions, namely: IERC20Permit, IDaiLikePermit, and IPermit2.
                   * It accommodates both Compact and Full formats of these permit types.
                   * Please note, it is expected that the `expiration` parameter for the compact Permit2 and the `deadline` parameter
                   * for the compact Permit are to be incremented by one before invoking this function. This approach is motivated by
                   * gas efficiency considerations; as the unlimited expiration period is likely to be the most common scenario, and
                   * zeros are cheaper to pass in terms of gas cost. Thus, callers should increment the expiration or deadline by one
                   * before invocation for optimized performance.
                   * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
                   * the caller to make sure that the higher 96 bits of the `owner` and `spender` parameters are clean.
                   * @param token The address of the ERC20 token on which to call the permit function.
                   * @param owner The owner of the tokens. This address should have signed the off-chain permit.
                   * @param spender The address which will be approved for transfer of tokens.
                   * @param permit The off-chain permit data, containing different fields depending on the type of permit function.
                   * @return success A boolean indicating whether the permit call was successful.
                   */
                  function tryPermit(IERC20 token, address owner, address spender, bytes calldata permit) internal returns(bool success) {
                      // load function selectors for different permit standards
                      bytes4 permitSelector = IERC20Permit.permit.selector;
                      bytes4 daiPermitSelector = IDaiLikePermit.permit.selector;
                      bytes4 permit2Selector = IPermit2.permit.selector;
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let ptr := mload(0x40)
                          // Switch case for different permit lengths, indicating different permit standards
                          switch permit.length
                          // Compact IERC20Permit
                          case 100 {
                              mstore(ptr, permitSelector)     // store selector
                              mstore(add(ptr, 0x04), owner)   // store owner
                              mstore(add(ptr, 0x24), spender) // store spender
                              // Compact IERC20Permit.permit(uint256 value, uint32 deadline, uint256 r, uint256 vs)
                              {  // stack too deep
                                  let deadline := shr(224, calldataload(add(permit.offset, 0x20))) // loads permit.offset 0x20..0x23
                                  let vs := calldataload(add(permit.offset, 0x44))                 // loads permit.offset 0x44..0x63
                                  calldatacopy(add(ptr, 0x44), permit.offset, 0x20)            // store value     = copy permit.offset 0x00..0x19
                                  mstore(add(ptr, 0x64), sub(deadline, 1))                     // store deadline  = deadline - 1
                                  mstore(add(ptr, 0x84), add(27, shr(255, vs)))                // store v         = most significant bit of vs + 27 (27 or 28)
                                  calldatacopy(add(ptr, 0xa4), add(permit.offset, 0x24), 0x20) // store r         = copy permit.offset 0x24..0x43
                                  mstore(add(ptr, 0xc4), shr(1, shl(1, vs)))                   // store s         = vs without most significant bit
                              }
                              // IERC20Permit.permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
                              success := call(gas(), token, 0, ptr, 0xe4, 0, 0)
                          }
                          // Compact IDaiLikePermit
                          case 72 {
                              mstore(ptr, daiPermitSelector)  // store selector
                              mstore(add(ptr, 0x04), owner)   // store owner
                              mstore(add(ptr, 0x24), spender) // store spender
                              // Compact IDaiLikePermit.permit(uint32 nonce, uint32 expiry, uint256 r, uint256 vs)
                              {  // stack too deep
                                  let expiry := shr(224, calldataload(add(permit.offset, 0x04))) // loads permit.offset 0x04..0x07
                                  let vs := calldataload(add(permit.offset, 0x28))               // loads permit.offset 0x28..0x47
                                  mstore(add(ptr, 0x44), shr(224, calldataload(permit.offset))) // store nonce   = copy permit.offset 0x00..0x03
                                  mstore(add(ptr, 0x64), sub(expiry, 1))                        // store expiry  = expiry - 1
                                  mstore(add(ptr, 0x84), true)                                  // store allowed = true
                                  mstore(add(ptr, 0xa4), add(27, shr(255, vs)))                 // store v       = most significant bit of vs + 27 (27 or 28)
                                  calldatacopy(add(ptr, 0xc4), add(permit.offset, 0x08), 0x20)  // store r       = copy permit.offset 0x08..0x27
                                  mstore(add(ptr, 0xe4), shr(1, shl(1, vs)))                    // store s       = vs without most significant bit
                              }
                              // IDaiLikePermit.permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s)
                              success := call(gas(), token, 0, ptr, 0x104, 0, 0)
                          }
                          // IERC20Permit
                          case 224 {
                              mstore(ptr, permitSelector)
                              calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata
                              // IERC20Permit.permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
                              success := call(gas(), token, 0, ptr, 0xe4, 0, 0)
                          }
                          // IDaiLikePermit
                          case 256 {
                              mstore(ptr, daiPermitSelector)
                              calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata
                              // IDaiLikePermit.permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s)
                              success := call(gas(), token, 0, ptr, 0x104, 0, 0)
                          }
                          // Compact IPermit2
                          case 96 {
                              // Compact IPermit2.permit(uint160 amount, uint32 expiration, uint32 nonce, uint32 sigDeadline, uint256 r, uint256 vs)
                              mstore(ptr, permit2Selector)  // store selector
                              mstore(add(ptr, 0x04), owner) // store owner
                              mstore(add(ptr, 0x24), token) // store token
                              calldatacopy(add(ptr, 0x50), permit.offset, 0x14)             // store amount = copy permit.offset 0x00..0x13
                              // and(0xffffffffffff, ...) - conversion to uint48
                              mstore(add(ptr, 0x64), and(0xffffffffffff, sub(shr(224, calldataload(add(permit.offset, 0x14))), 1))) // store expiration = ((permit.offset 0x14..0x17 - 1) & 0xffffffffffff)
                              mstore(add(ptr, 0x84), shr(224, calldataload(add(permit.offset, 0x18)))) // store nonce = copy permit.offset 0x18..0x1b
                              mstore(add(ptr, 0xa4), spender)                               // store spender
                              // and(0xffffffffffff, ...) - conversion to uint48
                              mstore(add(ptr, 0xc4), and(0xffffffffffff, sub(shr(224, calldataload(add(permit.offset, 0x1c))), 1))) // store sigDeadline = ((permit.offset 0x1c..0x1f - 1) & 0xffffffffffff)
                              mstore(add(ptr, 0xe4), 0x100)                                 // store offset = 256
                              mstore(add(ptr, 0x104), 0x40)                                 // store length = 64
                              calldatacopy(add(ptr, 0x124), add(permit.offset, 0x20), 0x20) // store r      = copy permit.offset 0x20..0x3f
                              calldatacopy(add(ptr, 0x144), add(permit.offset, 0x40), 0x20) // store vs     = copy permit.offset 0x40..0x5f
                              // IPermit2.permit(address owner, PermitSingle calldata permitSingle, bytes calldata signature)
                              success := call(gas(), _PERMIT2, 0, ptr, 0x164, 0, 0)
                          }
                          // IPermit2
                          case 352 {
                              mstore(ptr, permit2Selector)
                              calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata
                              // IPermit2.permit(address owner, PermitSingle calldata permitSingle, bytes calldata signature)
                              success := call(gas(), _PERMIT2, 0, ptr, 0x164, 0, 0)
                          }
                          // Unknown
                          default {
                              mstore(ptr, _PERMIT_LENGTH_ERROR)
                              revert(ptr, 4)
                          }
                      }
                  }
                  /**
                   * @dev Executes a low level call to a token contract, making it resistant to reversion and erroneous boolean returns.
                   * @param token The IERC20 token contract on which the call will be made.
                   * @param selector The function signature that is to be called on the token contract.
                   * @param to The address to which the token amount will be transferred.
                   * @param amount The token amount to be transferred.
                   * @return success A boolean indicating if the call was successful. Returns 'true' on success and 'false' on failure.
                   * In case of success but no returned data, validates that the contract code exists.
                   * In case of returned data, ensures that it's a boolean `true`.
                   */
                  function _makeCall(
                      IERC20 token,
                      bytes4 selector,
                      address to,
                      uint256 amount
                  ) private returns (bool success) {
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let data := mload(0x40)
                          mstore(data, selector)
                          mstore(add(data, 0x04), to)
                          mstore(add(data, 0x24), amount)
                          success := call(gas(), token, 0, data, 0x44, 0x0, 0x20)
                          if success {
                              switch returndatasize()
                              case 0 {
                                  success := gt(extcodesize(token), 0)
                              }
                              default {
                                  success := and(gt(returndatasize(), 31), eq(mload(0), 1))
                              }
                          }
                      }
                  }
                  /**
                   * @notice Safely deposits a specified amount of Ether into the IWETH contract. Consumes less gas then regular `IWETH.deposit`.
                   * @param weth The IWETH token contract.
                   * @param amount The amount of Ether to deposit into the IWETH contract.
                   */
                  function safeDeposit(IWETH weth, uint256 amount) internal {
                      if (amount > 0) {
                          bytes4 selector = IWETH.deposit.selector;
                          assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                              mstore(0, selector)
                              if iszero(call(gas(), weth, amount, 0, 4, 0, 0)) {
                                  let ptr := mload(0x40)
                                  returndatacopy(ptr, 0, returndatasize())
                                  revert(ptr, returndatasize())
                              }
                          }
                      }
                  }
                  /**
                   * @notice Safely withdraws a specified amount of wrapped Ether from the IWETH contract. Consumes less gas then regular `IWETH.withdraw`.
                   * @dev Uses inline assembly to interact with the IWETH contract.
                   * @param weth The IWETH token contract.
                   * @param amount The amount of wrapped Ether to withdraw from the IWETH contract.
                   */
                  function safeWithdraw(IWETH weth, uint256 amount) internal {
                      bytes4 selector = IWETH.withdraw.selector;
                      assembly ("memory-safe") {  // solhint-disable-line no-inline-assembly
                          mstore(0, selector)
                          mstore(4, amount)
                          if iszero(call(gas(), weth, 0, 0, 0x24, 0, 0)) {
                              let ptr := mload(0x40)
                              returndatacopy(ptr, 0, returndatasize())
                              revert(ptr, returndatasize())
                          }
                      }
                  }
                  /**
                   * @notice Safely withdraws a specified amount of wrapped Ether from the IWETH contract to a specified recipient.
                   * Consumes less gas then regular `IWETH.withdraw`.
                   * @param weth The IWETH token contract.
                   * @param amount The amount of wrapped Ether to withdraw from the IWETH contract.
                   * @param to The recipient of the withdrawn Ether.
                   */
                  function safeWithdrawTo(IWETH weth, uint256 amount, address to) internal {
                      safeWithdraw(weth, amount);
                      if (to != address(this)) {
                          assembly ("memory-safe") {  // solhint-disable-line no-inline-assembly
                              if iszero(call(_RAW_CALL_GAS_LIMIT, to, amount, 0, 0, 0, 0)) {
                                  let ptr := mload(0x40)
                                  returndatacopy(ptr, 0, returndatasize())
                                  revert(ptr, returndatasize())
                              }
                          }
                      }
                  }
              }
              // File @1inch/solidity-utils/contracts/[email protected]
              abstract contract EthReceiver {
                  error EthDepositRejected();
                  receive() external payable {
                      _receive();
                  }
                  function _receive() internal virtual {
                      // solhint-disable-next-line avoid-tx-origin
                      if (msg.sender == tx.origin) revert EthDepositRejected();
                  }
              }
              // File @1inch/solidity-utils/contracts/[email protected]
              abstract contract OnlyWethReceiver is EthReceiver {
                  address private immutable _WETH; // solhint-disable-line var-name-mixedcase
                  constructor(address weth) {
                      _WETH = address(weth);
                  }
                  function _receive() internal virtual override {
                      if (msg.sender != _WETH) revert EthDepositRejected();
                  }
              }
              // File @1inch/solidity-utils/contracts/[email protected]
              abstract contract PermitAndCall {
                  using SafeERC20 for IERC20;
                  function permitAndCall(bytes calldata permit, bytes calldata action) external payable {
                      IERC20(address(bytes20(permit))).tryPermit(permit[20:]);
                      // solhint-disable-next-line no-inline-assembly
                      assembly ("memory-safe") {
                          let ptr := mload(0x40)
                          calldatacopy(ptr, action.offset, action.length)
                          let success := delegatecall(gas(), address(), ptr, action.length, 0, 0)
                          returndatacopy(ptr, 0, returndatasize())
                          switch success
                          case 0 {
                              revert(ptr, returndatasize())
                          }
                          default {
                              return(ptr, returndatasize())
                          }
                      }
                  }
              }
              // File @openzeppelin/contracts/interfaces/[email protected]
              // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol)
              interface IERC5267 {
                  /**
                   * @dev MAY be emitted to signal that the domain could have changed.
                   */
                  event EIP712DomainChanged();
                  /**
                   * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
                   * signature.
                   */
                  function eip712Domain()
                      external
                      view
                      returns (
                          bytes1 fields,
                          string memory name,
                          string memory version,
                          uint256 chainId,
                          address verifyingContract,
                          bytes32 salt,
                          uint256[] memory extensions
                      );
              }
              // File @openzeppelin/contracts/utils/math/[email protected]
              // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
              /**
               * @dev Standard math utilities missing in the Solidity language.
               */
              library Math {
                  /**
                   * @dev Muldiv operation overflow.
                   */
                  error MathOverflowedMulDiv();
                  enum Rounding {
                      Floor, // Toward negative infinity
                      Ceil, // Toward positive infinity
                      Trunc, // Toward zero
                      Expand // Away from zero
                  }
                  /**
                   * @dev Returns the addition of two unsigned integers, with an overflow flag.
                   */
                  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.
                   */
                  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.
                   */
                  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.
                   */
                  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.
                   */
                  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 largest of two numbers.
                   */
                  function max(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a > b ? a : b;
                  }
                  /**
                   * @dev Returns the smallest of two numbers.
                   */
                  function min(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a < b ? a : b;
                  }
                  /**
                   * @dev Returns the average of two numbers. The result is rounded towards
                   * zero.
                   */
                  function average(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b) / 2 can overflow.
                      return (a & b) + (a ^ b) / 2;
                  }
                  /**
                   * @dev Returns the ceiling of the division of two numbers.
                   *
                   * This differs from standard division with `/` in that it rounds towards infinity instead
                   * of rounding towards zero.
                   */
                  function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                      if (b == 0) {
                          // Guarantee the same behavior as in a regular Solidity division.
                          return a / b;
                      }
                      // (a + b - 1) / b can overflow on addition, so we distribute.
                      return a == 0 ? 0 : (a - 1) / b + 1;
                  }
                  /**
                   * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
                   * denominator == 0.
                   * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
                   * Uniswap Labs also under MIT license.
                   */
                  function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
                      unchecked {
                          // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                          // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                          // variables such that product = prod1 * 2^256 + prod0.
                          uint256 prod0 = x * y; // Least significant 256 bits of the product
                          uint256 prod1; // Most significant 256 bits of the product
                          assembly {
                              let mm := mulmod(x, y, not(0))
                              prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                          }
                          // Handle non-overflow cases, 256 by 256 division.
                          if (prod1 == 0) {
                              // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                              // The surrounding unchecked block does not change this fact.
                              // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                              return prod0 / denominator;
                          }
                          // Make sure the result is less than 2^256. Also prevents denominator == 0.
                          if (denominator <= prod1) {
                              revert MathOverflowedMulDiv();
                          }
                          ///////////////////////////////////////////////
                          // 512 by 256 division.
                          ///////////////////////////////////////////////
                          // Make division exact by subtracting the remainder from [prod1 prod0].
                          uint256 remainder;
                          assembly {
                              // Compute remainder using mulmod.
                              remainder := mulmod(x, y, denominator)
                              // Subtract 256 bit number from 512 bit number.
                              prod1 := sub(prod1, gt(remainder, prod0))
                              prod0 := sub(prod0, remainder)
                          }
                          // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
                          // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
                          uint256 twos = denominator & (0 - denominator);
                          assembly {
                              // Divide denominator by twos.
                              denominator := div(denominator, twos)
                              // Divide [prod1 prod0] by twos.
                              prod0 := div(prod0, twos)
                              // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                              twos := add(div(sub(0, twos), twos), 1)
                          }
                          // Shift in bits from prod1 into prod0.
                          prod0 |= prod1 * twos;
                          // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                          // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                          // four bits. That is, denominator * inv = 1 mod 2^4.
                          uint256 inverse = (3 * denominator) ^ 2;
                          // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
                          // works in modular arithmetic, doubling the correct bits in each step.
                          inverse *= 2 - denominator * inverse; // inverse mod 2^8
                          inverse *= 2 - denominator * inverse; // inverse mod 2^16
                          inverse *= 2 - denominator * inverse; // inverse mod 2^32
                          inverse *= 2 - denominator * inverse; // inverse mod 2^64
                          inverse *= 2 - denominator * inverse; // inverse mod 2^128
                          inverse *= 2 - denominator * inverse; // inverse mod 2^256
                          // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                          // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                          // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                          // is no longer required.
                          result = prod0 * inverse;
                          return result;
                      }
                  }
                  /**
                   * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                   */
                  function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
                      uint256 result = mulDiv(x, y, denominator);
                      if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
                          result += 1;
                      }
                      return result;
                  }
                  /**
                   * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
                   * towards zero.
                   *
                   * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                   */
                  function sqrt(uint256 a) internal pure returns (uint256) {
                      if (a == 0) {
                          return 0;
                      }
                      // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                      //
                      // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                      // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                      //
                      // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                      // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                      // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                      //
                      // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                      uint256 result = 1 << (log2(a) >> 1);
                      // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                      // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                      // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                      // into the expected uint128 result.
                      unchecked {
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          return min(result, a / result);
                      }
                  }
                  /**
                   * @notice Calculates sqrt(a), following the selected rounding direction.
                   */
                  function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = sqrt(a);
                          return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 2 of a positive value rounded towards zero.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >> 128 > 0) {
                              value >>= 128;
                              result += 128;
                          }
                          if (value >> 64 > 0) {
                              value >>= 64;
                              result += 64;
                          }
                          if (value >> 32 > 0) {
                              value >>= 32;
                              result += 32;
                          }
                          if (value >> 16 > 0) {
                              value >>= 16;
                              result += 16;
                          }
                          if (value >> 8 > 0) {
                              value >>= 8;
                              result += 8;
                          }
                          if (value >> 4 > 0) {
                              value >>= 4;
                              result += 4;
                          }
                          if (value >> 2 > 0) {
                              value >>= 2;
                              result += 2;
                          }
                          if (value >> 1 > 0) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log2(value);
                          return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 10 of a positive value rounded towards zero.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >= 10 ** 64) {
                              value /= 10 ** 64;
                              result += 64;
                          }
                          if (value >= 10 ** 32) {
                              value /= 10 ** 32;
                              result += 32;
                          }
                          if (value >= 10 ** 16) {
                              value /= 10 ** 16;
                              result += 16;
                          }
                          if (value >= 10 ** 8) {
                              value /= 10 ** 8;
                              result += 8;
                          }
                          if (value >= 10 ** 4) {
                              value /= 10 ** 4;
                              result += 4;
                          }
                          if (value >= 10 ** 2) {
                              value /= 10 ** 2;
                              result += 2;
                          }
                          if (value >= 10 ** 1) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log10(value);
                          return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 256 of a positive value rounded towards zero.
                   * Returns 0 if given 0.
                   *
                   * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                   */
                  function log256(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >> 128 > 0) {
                              value >>= 128;
                              result += 16;
                          }
                          if (value >> 64 > 0) {
                              value >>= 64;
                              result += 8;
                          }
                          if (value >> 32 > 0) {
                              value >>= 32;
                              result += 4;
                          }
                          if (value >> 16 > 0) {
                              value >>= 16;
                              result += 2;
                          }
                          if (value >> 8 > 0) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log256(value);
                          return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
                   */
                  function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
                      return uint8(rounding) % 2 == 1;
                  }
              }
              // File @openzeppelin/contracts/utils/math/[email protected]
              // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
              /**
               * @dev Standard signed math utilities missing in the Solidity language.
               */
              library SignedMath {
                  /**
                   * @dev Returns the largest of two signed numbers.
                   */
                  function max(int256 a, int256 b) internal pure returns (int256) {
                      return a > b ? a : b;
                  }
                  /**
                   * @dev Returns the smallest of two signed numbers.
                   */
                  function min(int256 a, int256 b) internal pure returns (int256) {
                      return a < b ? a : b;
                  }
                  /**
                   * @dev Returns the average of two signed numbers without overflow.
                   * The result is rounded towards zero.
                   */
                  function average(int256 a, int256 b) internal pure returns (int256) {
                      // Formula from the book "Hacker's Delight"
                      int256 x = (a & b) + ((a ^ b) >> 1);
                      return x + (int256(uint256(x) >> 255) & (a ^ b));
                  }
                  /**
                   * @dev Returns the absolute unsigned value of a signed value.
                   */
                  function abs(int256 n) internal pure returns (uint256) {
                      unchecked {
                          // must be unchecked in order to support `n = type(int256).min`
                          return uint256(n >= 0 ? n : -n);
                      }
                  }
              }
              // File @openzeppelin/contracts/utils/[email protected]
              // OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
              /**
               * @dev String operations.
               */
              library Strings {
                  bytes16 private constant HEX_DIGITS = "0123456789abcdef";
                  uint8 private constant ADDRESS_LENGTH = 20;
                  /**
                   * @dev The `value` string doesn't fit in the specified `length`.
                   */
                  error StringsInsufficientHexLength(uint256 value, uint256 length);
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                   */
                  function toString(uint256 value) internal pure returns (string memory) {
                      unchecked {
                          uint256 length = Math.log10(value) + 1;
                          string memory buffer = new string(length);
                          uint256 ptr;
                          /// @solidity memory-safe-assembly
                          assembly {
                              ptr := add(buffer, add(32, length))
                          }
                          while (true) {
                              ptr--;
                              /// @solidity memory-safe-assembly
                              assembly {
                                  mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                              }
                              value /= 10;
                              if (value == 0) break;
                          }
                          return buffer;
                      }
                  }
                  /**
                   * @dev Converts a `int256` to its ASCII `string` decimal representation.
                   */
                  function toStringSigned(int256 value) internal pure returns (string memory) {
                      return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
                  }
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                   */
                  function toHexString(uint256 value) internal pure returns (string memory) {
                      unchecked {
                          return toHexString(value, Math.log256(value) + 1);
                      }
                  }
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                   */
                  function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                      uint256 localValue = value;
                      bytes memory buffer = new bytes(2 * length + 2);
                      buffer[0] = "0";
                      buffer[1] = "x";
                      for (uint256 i = 2 * length + 1; i > 1; --i) {
                          buffer[i] = HEX_DIGITS[localValue & 0xf];
                          localValue >>= 4;
                      }
                      if (localValue != 0) {
                          revert StringsInsufficientHexLength(value, length);
                      }
                      return string(buffer);
                  }
                  /**
                   * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
                   * representation.
                   */
                  function toHexString(address addr) internal pure returns (string memory) {
                      return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
                  }
                  /**
                   * @dev Returns true if the two strings are equal.
                   */
                  function equal(string memory a, string memory b) internal pure returns (bool) {
                      return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
                  }
              }
              // File @openzeppelin/contracts/utils/cryptography/[email protected]
              // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
              /**
               * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
               *
               * The library provides methods for generating a hash of a message that conforms to the
               * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
               * specifications.
               */
              library MessageHashUtils {
                  /**
                   * @dev Returns the keccak256 digest of an EIP-191 signed data with version
                   * `0x45` (`personal_sign` messages).
                   *
                   * The digest is calculated by prefixing a bytes32 `messageHash` with
                   * `"\\x19Ethereum Signed Message:\
              32"` and hashing the result. It corresponds with the
                   * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
                   *
                   * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
                   * keccak256, although any bytes32 value can be safely used because the final digest will
                   * be re-hashed.
                   *
                   * See {ECDSA-recover}.
                   */
                  function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          mstore(0x00, "\\x19Ethereum Signed Message:\
              32") // 32 is the bytes-length of messageHash
                          mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
                          digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
                      }
                  }
                  /**
                   * @dev Returns the keccak256 digest of an EIP-191 signed data with version
                   * `0x45` (`personal_sign` messages).
                   *
                   * The digest is calculated by prefixing an arbitrary `message` with
                   * `"\\x19Ethereum Signed Message:\
              " + len(message)` and hashing the result. It corresponds with the
                   * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
                   *
                   * See {ECDSA-recover}.
                   */
                  function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
                      return
                          keccak256(bytes.concat("\\x19Ethereum Signed Message:\
              ", bytes(Strings.toString(message.length)), message));
                  }
                  /**
                   * @dev Returns the keccak256 digest of an EIP-191 signed data with version
                   * `0x00` (data with intended validator).
                   *
                   * The digest is calculated by prefixing an arbitrary `data` with `"\\x19\\x00"` and the intended
                   * `validator` address. Then hashing the result.
                   *
                   * See {ECDSA-recover}.
                   */
                  function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
                      return keccak256(abi.encodePacked(hex"19_00", validator, data));
                  }
                  /**
                   * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
                   *
                   * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
                   * `\\x19\\x01` and hashing the result. It corresponds to the hash signed by the
                   * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
                   *
                   * See {ECDSA-recover}.
                   */
                  function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          let ptr := mload(0x40)
                          mstore(ptr, hex"19_01")
                          mstore(add(ptr, 0x02), domainSeparator)
                          mstore(add(ptr, 0x22), structHash)
                          digest := keccak256(ptr, 0x42)
                      }
                  }
              }
              // File @openzeppelin/contracts/utils/[email protected]
              // OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
              // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
              /**
               * @dev Library for reading and writing primitive types to specific storage slots.
               *
               * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
               * This library helps with reading and writing to such slots without the need for inline assembly.
               *
               * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
               *
               * Example usage to set ERC1967 implementation slot:
               * ```solidity
               * contract ERC1967 {
               *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
               *
               *     function _getImplementation() internal view returns (address) {
               *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
               *     }
               *
               *     function _setImplementation(address newImplementation) internal {
               *         require(newImplementation.code.length > 0);
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  struct StringSlot {
                      string value;
                  }
                  struct BytesSlot {
                      bytes value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` with member `value` located at `slot`.
                   */
                  function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
                   */
                  function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` with member `value` located at `slot`.
                   */
                  function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
                   */
                  function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
              }
              // File @openzeppelin/contracts/utils/[email protected]
              // OpenZeppelin Contracts (last updated v5.0.0) (utils/ShortStrings.sol)
              // | string  | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   |
              // | length  | 0x                                                              BB |
              type ShortString is bytes32;
              /**
               * @dev This library provides functions to convert short memory strings
               * into a `ShortString` type that can be used as an immutable variable.
               *
               * Strings of arbitrary length can be optimized using this library if
               * they are short enough (up to 31 bytes) by packing them with their
               * length (1 byte) in a single EVM word (32 bytes). Additionally, a
               * fallback mechanism can be used for every other case.
               *
               * Usage example:
               *
               * ```solidity
               * contract Named {
               *     using ShortStrings for *;
               *
               *     ShortString private immutable _name;
               *     string private _nameFallback;
               *
               *     constructor(string memory contractName) {
               *         _name = contractName.toShortStringWithFallback(_nameFallback);
               *     }
               *
               *     function name() external view returns (string memory) {
               *         return _name.toStringWithFallback(_nameFallback);
               *     }
               * }
               * ```
               */
              library ShortStrings {
                  // Used as an identifier for strings longer than 31 bytes.
                  bytes32 private constant FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
                  error StringTooLong(string str);
                  error InvalidShortString();
                  /**
                   * @dev Encode a string of at most 31 chars into a `ShortString`.
                   *
                   * This will trigger a `StringTooLong` error is the input string is too long.
                   */
                  function toShortString(string memory str) internal pure returns (ShortString) {
                      bytes memory bstr = bytes(str);
                      if (bstr.length > 31) {
                          revert StringTooLong(str);
                      }
                      return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
                  }
                  /**
                   * @dev Decode a `ShortString` back to a "normal" string.
                   */
                  function toString(ShortString sstr) internal pure returns (string memory) {
                      uint256 len = byteLength(sstr);
                      // using `new string(len)` would work locally but is not memory safe.
                      string memory str = new string(32);
                      /// @solidity memory-safe-assembly
                      assembly {
                          mstore(str, len)
                          mstore(add(str, 0x20), sstr)
                      }
                      return str;
                  }
                  /**
                   * @dev Return the length of a `ShortString`.
                   */
                  function byteLength(ShortString sstr) internal pure returns (uint256) {
                      uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
                      if (result > 31) {
                          revert InvalidShortString();
                      }
                      return result;
                  }
                  /**
                   * @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
                   */
                  function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
                      if (bytes(value).length < 32) {
                          return toShortString(value);
                      } else {
                          StorageSlot.getStringSlot(store).value = value;
                          return ShortString.wrap(FALLBACK_SENTINEL);
                      }
                  }
                  /**
                   * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
                   */
                  function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
                      if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
                          return toString(value);
                      } else {
                          return store;
                      }
                  }
                  /**
                   * @dev Return the length of a string that was encoded to `ShortString` or written to storage using
                   * {setWithFallback}.
                   *
                   * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
                   * actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
                   */
                  function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
                      if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
                          return byteLength(value);
                      } else {
                          return bytes(store).length;
                      }
                  }
              }
              // File @openzeppelin/contracts/utils/cryptography/[email protected]
              // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/EIP712.sol)
              /**
               * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
               *
               * The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose
               * encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract
               * does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to
               * produce the hash of their typed data using a combination of `abi.encode` and `keccak256`.
               *
               * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
               * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
               * ({_hashTypedDataV4}).
               *
               * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
               * the chain id to protect against replay attacks on an eventual fork of the chain.
               *
               * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
               * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
               *
               * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
               * separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the
               * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
               *
               * @custom:oz-upgrades-unsafe-allow state-variable-immutable
               */
              abstract contract EIP712 is IERC5267 {
                  using ShortStrings for *;
                  bytes32 private constant TYPE_HASH =
                      keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
                  // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
                  // invalidate the cached domain separator if the chain id changes.
                  bytes32 private immutable _cachedDomainSeparator;
                  uint256 private immutable _cachedChainId;
                  address private immutable _cachedThis;
                  bytes32 private immutable _hashedName;
                  bytes32 private immutable _hashedVersion;
                  ShortString private immutable _name;
                  ShortString private immutable _version;
                  string private _nameFallback;
                  string private _versionFallback;
                  /**
                   * @dev Initializes the domain separator and parameter caches.
                   *
                   * The meaning of `name` and `version` is specified in
                   * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
                   *
                   * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
                   * - `version`: the current major version of the signing domain.
                   *
                   * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
                   * contract upgrade].
                   */
                  constructor(string memory name, string memory version) {
                      _name = name.toShortStringWithFallback(_nameFallback);
                      _version = version.toShortStringWithFallback(_versionFallback);
                      _hashedName = keccak256(bytes(name));
                      _hashedVersion = keccak256(bytes(version));
                      _cachedChainId = block.chainid;
                      _cachedDomainSeparator = _buildDomainSeparator();
                      _cachedThis = address(this);
                  }
                  /**
                   * @dev Returns the domain separator for the current chain.
                   */
                  function _domainSeparatorV4() internal view returns (bytes32) {
                      if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
                          return _cachedDomainSeparator;
                      } else {
                          return _buildDomainSeparator();
                      }
                  }
                  function _buildDomainSeparator() private view returns (bytes32) {
                      return keccak256(abi.encode(TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
                  }
                  /**
                   * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
                   * function returns the hash of the fully encoded EIP712 message for this domain.
                   *
                   * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
                   *
                   * ```solidity
                   * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
                   *     keccak256("Mail(address to,string contents)"),
                   *     mailTo,
                   *     keccak256(bytes(mailContents))
                   * )));
                   * address signer = ECDSA.recover(digest, signature);
                   * ```
                   */
                  function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
                      return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
                  }
                  /**
                   * @dev See {IERC-5267}.
                   */
                  function eip712Domain()
                      public
                      view
                      virtual
                      returns (
                          bytes1 fields,
                          string memory name,
                          string memory version,
                          uint256 chainId,
                          address verifyingContract,
                          bytes32 salt,
                          uint256[] memory extensions
                      )
                  {
                      return (
                          hex"0f", // 01111
                          _EIP712Name(),
                          _EIP712Version(),
                          block.chainid,
                          address(this),
                          bytes32(0),
                          new uint256[](0)
                      );
                  }
                  /**
                   * @dev The name parameter for the EIP712 domain.
                   *
                   * NOTE: By default this function reads _name which is an immutable value.
                   * It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
                   */
                  // solhint-disable-next-line func-name-mixedcase
                  function _EIP712Name() internal view returns (string memory) {
                      return _name.toStringWithFallback(_nameFallback);
                  }
                  /**
                   * @dev The version parameter for the EIP712 domain.
                   *
                   * NOTE: By default this function reads _version which is an immutable value.
                   * It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
                   */
                  // solhint-disable-next-line func-name-mixedcase
                  function _EIP712Version() internal view returns (string memory) {
                      return _version.toStringWithFallback(_versionFallback);
                  }
              }
              // File @openzeppelin/contracts/utils/[email protected]
              // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
              /**
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
                  function _contextSuffixLength() internal view virtual returns (uint256) {
                      return 0;
                  }
              }
              // File @openzeppelin/contracts/utils/[email protected]
              // OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
              /**
               * @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 Pausable is Context {
                  bool private _paused;
                  /**
                   * @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);
                  /**
                   * @dev The operation failed because the contract is paused.
                   */
                  error EnforcedPause();
                  /**
                   * @dev The operation failed because the contract is not paused.
                   */
                  error ExpectedPause();
                  /**
                   * @dev Initializes the contract in unpaused state.
                   */
                  constructor() {
                      _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 {
                      if (paused()) {
                          revert EnforcedPause();
                      }
                  }
                  /**
                   * @dev Throws if the contract is not paused.
                   */
                  function _requirePaused() internal view virtual {
                      if (!paused()) {
                          revert ExpectedPause();
                      }
                  }
                  /**
                   * @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());
                  }
              }
              // File @1inch/limit-order-protocol-contract/contracts/[email protected]
              /// @title Limit Order mixin
              abstract contract OrderMixin is IOrderMixin, EIP712, PredicateHelper, SeriesEpochManager, Pausable, OnlyWethReceiver, PermitAndCall {
                  using SafeERC20 for IERC20;
                  using SafeERC20 for IWETH;
                  using OrderLib for IOrderMixin.Order;
                  using ExtensionLib for bytes;
                  using AddressLib for Address;
                  using MakerTraitsLib for MakerTraits;
                  using TakerTraitsLib for TakerTraits;
                  using BitInvalidatorLib for BitInvalidatorLib.Data;
                  using RemainingInvalidatorLib for RemainingInvalidator;
                  IWETH private immutable _WETH;  // solhint-disable-line var-name-mixedcase
                  mapping(address maker => BitInvalidatorLib.Data data) private _bitInvalidator;
                  mapping(address maker => mapping(bytes32 orderHash => RemainingInvalidator remaining)) private _remainingInvalidator;
                  constructor(IWETH weth) OnlyWethReceiver(address(weth)) {
                      _WETH = weth;
                  }
                  /**
                   * @notice See {IOrderMixin-bitInvalidatorForOrder}.
                   */
                  function bitInvalidatorForOrder(address maker, uint256 slot) external view returns(uint256 /* result */) {
                      return _bitInvalidator[maker].checkSlot(slot);
                  }
                  /**
                   * @notice See {IOrderMixin-remainingInvalidatorForOrder}.
                   */
                  function remainingInvalidatorForOrder(address maker, bytes32 orderHash) external view returns(uint256 /* remaining */) {
                      return _remainingInvalidator[maker][orderHash].remaining();
                  }
                  /**
                   * @notice See {IOrderMixin-rawRemainingInvalidatorForOrder}.
                   */
                  function rawRemainingInvalidatorForOrder(address maker, bytes32 orderHash) external view returns(uint256 /* remainingRaw */) {
                      return RemainingInvalidator.unwrap(_remainingInvalidator[maker][orderHash]);
                  }
                  /**
                   * @notice See {IOrderMixin-simulate}.
                   */
                  function simulate(address target, bytes calldata data) external {
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory result) = target.delegatecall(data);
                      revert SimulationResults(success, result);
                  }
                  /**
                   * @notice See {IOrderMixin-cancelOrder}.
                   */
                  function cancelOrder(MakerTraits makerTraits, bytes32 orderHash) public {
                      if (makerTraits.useBitInvalidator()) {
                          uint256 invalidator = _bitInvalidator[msg.sender].massInvalidate(makerTraits.nonceOrEpoch(), 0);
                          emit BitInvalidatorUpdated(msg.sender, makerTraits.nonceOrEpoch() >> 8, invalidator);
                      } else {
                          _remainingInvalidator[msg.sender][orderHash] = RemainingInvalidatorLib.fullyFilled();
                          emit OrderCancelled(orderHash);
                      }
                  }
                  /**
                   * @notice See {IOrderMixin-cancelOrders}.
                   */
                  function cancelOrders(MakerTraits[] calldata makerTraits, bytes32[] calldata orderHashes) external {
                      if (makerTraits.length != orderHashes.length) revert MismatchArraysLengths();
                      unchecked {
                          for (uint256 i = 0; i < makerTraits.length; i++) {
                              cancelOrder(makerTraits[i], orderHashes[i]);
                          }
                      }
                  }
                  /**
                   * @notice See {IOrderMixin-bitsInvalidateForOrder}.
                   */
                  function bitsInvalidateForOrder(MakerTraits makerTraits, uint256 additionalMask) external {
                      if (!makerTraits.useBitInvalidator()) revert OrderIsNotSuitableForMassInvalidation();
                      uint256 invalidator = _bitInvalidator[msg.sender].massInvalidate(makerTraits.nonceOrEpoch(), additionalMask);
                      emit BitInvalidatorUpdated(msg.sender, makerTraits.nonceOrEpoch() >> 8, invalidator);
                  }
                   /**
                   * @notice See {IOrderMixin-hashOrder}.
                   */
                  function hashOrder(IOrderMixin.Order calldata order) external view returns(bytes32) {
                      return order.hash(_domainSeparatorV4());
                  }
                  /**
                   * @notice See {IOrderMixin-checkPredicate}.
                   */
                  function checkPredicate(bytes calldata predicate) public view returns(bool) {
                      (bool success, uint256 res) = _staticcallForUint(address(this), predicate);
                      return success && res == 1;
                  }
                  /**
                   * @notice See {IOrderMixin-fillOrder}.
                   */
                  function fillOrder(
                      IOrderMixin.Order calldata order,
                      bytes32 r,
                      bytes32 vs,
                      uint256 amount,
                      TakerTraits takerTraits
                  ) external payable returns(uint256 /* makingAmount */, uint256 /* takingAmount */, bytes32 /* orderHash */) {
                      return _fillOrder(order, r, vs, amount, takerTraits, msg.sender, msg.data[:0], msg.data[:0]);
                  }
                  /**
                   * @notice See {IOrderMixin-fillOrderArgs}.
                   */
                  function fillOrderArgs(
                      IOrderMixin.Order calldata order,
                      bytes32 r,
                      bytes32 vs,
                      uint256 amount,
                      TakerTraits takerTraits,
                      bytes calldata args
                  ) external payable returns(uint256 /* makingAmount */, uint256 /* takingAmount */, bytes32 /* orderHash */) {
                      (
                          address target,
                          bytes calldata extension,
                          bytes calldata interaction
                      ) = _parseArgs(takerTraits, args);
                      return _fillOrder(order, r, vs, amount, takerTraits, target, extension, interaction);
                  }
                  function _fillOrder(
                      IOrderMixin.Order calldata order,
                      bytes32 r,
                      bytes32 vs,
                      uint256 amount,
                      TakerTraits takerTraits,
                      address target,
                      bytes calldata extension,
                      bytes calldata interaction
                  ) private returns(uint256 makingAmount, uint256 takingAmount, bytes32 orderHash) {
                      // Check signature and apply order/maker permit only on the first fill
                      orderHash = order.hash(_domainSeparatorV4());
                      uint256 remainingMakingAmount = _checkRemainingMakingAmount(order, orderHash);
                      if (remainingMakingAmount == order.makingAmount) {
                          address maker = order.maker.get();
                          if (maker == address(0) || maker != ECDSA.recover(orderHash, r, vs)) revert BadSignature();
                          if (!takerTraits.skipMakerPermit()) {
                              bytes calldata makerPermit = extension.makerPermit();
                              if (makerPermit.length >= 20) {
                                  // proceed only if taker is willing to execute permit and its length is enough to store address
                                  IERC20(address(bytes20(makerPermit))).tryPermit(maker, address(this), makerPermit[20:]);
                                  if (!order.makerTraits.useBitInvalidator()) {
                                      // Bit orders are not subjects for reentrancy, but we still need to check remaining-based orders for reentrancy
                                      if (!_remainingInvalidator[order.maker.get()][orderHash].isNewOrder()) revert ReentrancyDetected();
                                  }
                              }
                          }
                      }
                      (makingAmount, takingAmount) = _fill(order, orderHash, remainingMakingAmount, amount, takerTraits, target, extension, interaction);
                  }
                  /**
                   * @notice See {IOrderMixin-fillContractOrder}.
                   */
                  function fillContractOrder(
                      IOrderMixin.Order calldata order,
                      bytes calldata signature,
                      uint256 amount,
                      TakerTraits takerTraits
                  ) external returns(uint256 /* makingAmount */, uint256 /* takingAmount */, bytes32 /* orderHash */) {
                      return _fillContractOrder(order, signature, amount, takerTraits, msg.sender, msg.data[:0], msg.data[:0]);
                  }
                  /**
                   * @notice See {IOrderMixin-fillContractOrderArgs}.
                   */
                  function fillContractOrderArgs(
                      IOrderMixin.Order calldata order,
                      bytes calldata signature,
                      uint256 amount,
                      TakerTraits takerTraits,
                      bytes calldata args
                  ) external returns(uint256 /* makingAmount */, uint256 /* takingAmount */, bytes32 /* orderHash */) {
                      (
                          address target,
                          bytes calldata extension,
                          bytes calldata interaction
                      ) = _parseArgs(takerTraits, args);
                      return _fillContractOrder(order, signature, amount, takerTraits, target, extension, interaction);
                  }
                  function _fillContractOrder(
                      IOrderMixin.Order calldata order,
                      bytes calldata signature,
                      uint256 amount,
                      TakerTraits takerTraits,
                      address target,
                      bytes calldata extension,
                      bytes calldata interaction
                  ) private returns(uint256 makingAmount, uint256 takingAmount, bytes32 orderHash) {
                      // Check signature only on the first fill
                      orderHash = order.hash(_domainSeparatorV4());
                      uint256 remainingMakingAmount = _checkRemainingMakingAmount(order, orderHash);
                      if (remainingMakingAmount == order.makingAmount) {
                          if (!ECDSA.isValidSignature(order.maker.get(), orderHash, signature)) revert BadSignature();
                      }
                      (makingAmount, takingAmount) = _fill(order, orderHash, remainingMakingAmount, amount, takerTraits, target, extension, interaction);
                  }
                  /**
                    * @notice Fills an order and transfers making amount to a specified target.
                    * @dev If the target is zero assigns it the caller's address.
                    * The function flow is as follows:
                    * 1. Validate order
                    * 2. Call maker pre-interaction
                    * 3. Transfer maker asset to taker
                    * 4. Call taker interaction
                    * 5. Transfer taker asset to maker
                    * 5. Call maker post-interaction
                    * 6. Emit OrderFilled event
                    * @param order The order details.
                    * @param orderHash The hash of the order.
                    * @param extension The extension calldata of the order.
                    * @param remainingMakingAmount The remaining amount to be filled.
                    * @param amount The order amount.
                    * @param takerTraits The taker preferences for the order.
                    * @param target The address to which the order is filled.
                    * @param interaction The interaction calldata.
                    * @return makingAmount The computed amount that the maker will get.
                    * @return takingAmount The computed amount that the taker will send.
                    */
                  function _fill(
                      IOrderMixin.Order calldata order,
                      bytes32 orderHash,
                      uint256 remainingMakingAmount,
                      uint256 amount,
                      TakerTraits takerTraits,
                      address target,
                      bytes calldata extension,
                      bytes calldata interaction
                  ) private whenNotPaused() returns(uint256 makingAmount, uint256 takingAmount) {
                      // Validate order
                      {
                          (bool valid, bytes4 validationResult) = order.isValidExtension(extension);
                          if (!valid) {
                              // solhint-disable-next-line no-inline-assembly
                              assembly ("memory-safe") {
                                  mstore(0, validationResult)
                                  revert(0, 4)
                              }
                          }
                      }
                      if (!order.makerTraits.isAllowedSender(msg.sender)) revert PrivateOrder();
                      if (order.makerTraits.isExpired()) revert OrderExpired();
                      if (order.makerTraits.needCheckEpochManager()) {
                          if (order.makerTraits.useBitInvalidator()) revert EpochManagerAndBitInvalidatorsAreIncompatible();
                          if (!epochEquals(order.maker.get(), order.makerTraits.series(), order.makerTraits.nonceOrEpoch())) revert WrongSeriesNonce();
                      }
                      // Check if orders predicate allows filling
                      if (extension.length > 0) {
                          bytes calldata predicate = extension.predicate();
                          if (predicate.length > 0) {
                              if (!checkPredicate(predicate)) revert PredicateIsNotTrue();
                          }
                      }
                      // Compute maker and taker assets amount
                      if (takerTraits.isMakingAmount()) {
                          makingAmount = Math.min(amount, remainingMakingAmount);
                          takingAmount = order.calculateTakingAmount(extension, makingAmount, remainingMakingAmount, orderHash);
                          uint256 threshold = takerTraits.threshold();
                          if (threshold > 0) {
                              // Check rate: takingAmount / makingAmount <= threshold / amount
                              if (amount == makingAmount) {  // Gas optimization, no SafeMath.mul()
                                  if (takingAmount > threshold) revert TakingAmountTooHigh();
                              } else {
                                  if (takingAmount * amount > threshold * makingAmount) revert TakingAmountTooHigh();
                              }
                          }
                      }
                      else {
                          takingAmount = amount;
                          makingAmount = order.calculateMakingAmount(extension, takingAmount, remainingMakingAmount, orderHash);
                          if (makingAmount > remainingMakingAmount) {
                              // Try to decrease taking amount because computed making amount exceeds remaining amount
                              makingAmount = remainingMakingAmount;
                              takingAmount = order.calculateTakingAmount(extension, makingAmount, remainingMakingAmount, orderHash);
                              if (takingAmount > amount) revert TakingAmountExceeded();
                          }
                          uint256 threshold = takerTraits.threshold();
                          if (threshold > 0) {
                              // Check rate: makingAmount / takingAmount >= threshold / amount
                              if (amount == takingAmount) { // Gas optimization, no SafeMath.mul()
                                  if (makingAmount < threshold) revert MakingAmountTooLow();
                              } else {
                                  if (makingAmount * amount < threshold * takingAmount) revert MakingAmountTooLow();
                              }
                          }
                      }
                      if (!order.makerTraits.allowPartialFills() && makingAmount != order.makingAmount) revert PartialFillNotAllowed();
                      unchecked { if (makingAmount * takingAmount == 0) revert SwapWithZeroAmount(); }
                      // Invalidate order depending on makerTraits
                      if (order.makerTraits.useBitInvalidator()) {
                          _bitInvalidator[order.maker.get()].checkAndInvalidate(order.makerTraits.nonceOrEpoch());
                      } else {
                          _remainingInvalidator[order.maker.get()][orderHash] = RemainingInvalidatorLib.remains(remainingMakingAmount, makingAmount);
                      }
                      // Pre interaction, where maker can prepare funds interactively
                      if (order.makerTraits.needPreInteractionCall()) {
                          bytes calldata data = extension.preInteractionTargetAndData();
                          address listener = order.maker.get();
                          if (data.length > 19) {
                              listener = address(bytes20(data));
                              data = data[20:];
                          }
                          IPreInteraction(listener).preInteraction(
                              order, extension, orderHash, msg.sender, makingAmount, takingAmount, remainingMakingAmount, data
                          );
                      }
                      // Maker => Taker
                      {
                          bool needUnwrap = order.makerAsset.get() == address(_WETH) && takerTraits.unwrapWeth();
                          address receiver = needUnwrap ? address(this) : target;
                          if (order.makerTraits.usePermit2()) {
                              if (extension.makerAssetSuffix().length > 0) revert InvalidPermit2Transfer();
                              IERC20(order.makerAsset.get()).safeTransferFromPermit2(order.maker.get(), receiver, makingAmount);
                          } else {
                              if (!_callTransferFromWithSuffix(
                                  order.makerAsset.get(),
                                  order.maker.get(),
                                  receiver,
                                  makingAmount,
                                  extension.makerAssetSuffix()
                              )) revert TransferFromMakerToTakerFailed();
                          }
                          if (needUnwrap) {
                              _WETH.safeWithdrawTo(makingAmount, target);
                          }
                      }
                      if (interaction.length > 19) {
                          // proceed only if interaction length is enough to store address
                          ITakerInteraction(address(bytes20(interaction))).takerInteraction(
                              order, extension, orderHash, msg.sender, makingAmount, takingAmount, remainingMakingAmount, interaction[20:]
                          );
                      }
                      // Taker => Maker
                      if (order.takerAsset.get() == address(_WETH) && msg.value > 0) {
                          if (msg.value < takingAmount) revert Errors.InvalidMsgValue();
                          if (msg.value > takingAmount) {
                              unchecked {
                                  // solhint-disable-next-line avoid-low-level-calls
                                  (bool success, ) = msg.sender.call{value: msg.value - takingAmount}("");
                                  if (!success) revert Errors.ETHTransferFailed();
                              }
                          }
                          if (order.makerTraits.unwrapWeth()) {
                              // solhint-disable-next-line avoid-low-level-calls
                              (bool success, ) = order.getReceiver().call{value: takingAmount}("");
                              if (!success) revert Errors.ETHTransferFailed();
                          } else {
                              _WETH.safeDeposit(takingAmount);
                              _WETH.safeTransfer(order.getReceiver(), takingAmount);
                          }
                      } else {
                          if (msg.value != 0) revert Errors.InvalidMsgValue();
                          bool needUnwrap = order.takerAsset.get() == address(_WETH) && order.makerTraits.unwrapWeth();
                          address receiver = needUnwrap ? address(this) : order.getReceiver();
                          if (takerTraits.usePermit2()) {
                              if (extension.takerAssetSuffix().length > 0) revert InvalidPermit2Transfer();
                              IERC20(order.takerAsset.get()).safeTransferFromPermit2(msg.sender, receiver, takingAmount);
                          } else {
                              if (!_callTransferFromWithSuffix(
                                  order.takerAsset.get(),
                                  msg.sender,
                                  receiver,
                                  takingAmount,
                                  extension.takerAssetSuffix()
                              )) revert TransferFromTakerToMakerFailed();
                          }
                          if (needUnwrap) {
                              _WETH.safeWithdrawTo(takingAmount, order.getReceiver());
                          }
                      }
                      // Post interaction, where maker can handle funds interactively
                      if (order.makerTraits.needPostInteractionCall()) {
                          bytes calldata data = extension.postInteractionTargetAndData();
                          address listener = order.maker.get();
                          if (data.length > 19) {
                              listener = address(bytes20(data));
                              data = data[20:];
                          }
                          IPostInteraction(listener).postInteraction(
                              order, extension, orderHash, msg.sender, makingAmount, takingAmount, remainingMakingAmount, data
                          );
                      }
                      emit OrderFilled(orderHash, remainingMakingAmount - makingAmount);
                  }
                  /**
                    * @notice Processes the taker interaction arguments.
                    * @param takerTraits The taker preferences for the order.
                    * @param args The taker interaction arguments.
                    * @return target The address to which the order is filled.
                    * @return extension The extension calldata of the order.
                    * @return interaction The interaction calldata.
                    */
                  function _parseArgs(TakerTraits takerTraits, bytes calldata args)
                      private
                      view
                      returns(
                          address target,
                          bytes calldata extension,
                          bytes calldata interaction
                      )
                  {
                      if (takerTraits.argsHasTarget()) {
                          target = address(bytes20(args));
                          args = args[20:];
                      } else {
                          target = msg.sender;
                      }
                      uint256 extensionLength = takerTraits.argsExtensionLength();
                      if (extensionLength > 0) {
                          extension = args[:extensionLength];
                          args = args[extensionLength:];
                      } else {
                          extension = msg.data[:0];
                      }
                      uint256 interactionLength = takerTraits.argsInteractionLength();
                      if (interactionLength > 0) {
                          interaction = args[:interactionLength];
                      } else {
                          interaction = msg.data[:0];
                      }
                  }
                  /**
                    * @notice Checks the remaining making amount for the order.
                    * @dev If the order has been invalidated, the function will revert.
                    * @param order The order to check.
                    * @param orderHash The hash of the order.
                    * @return remainingMakingAmount The remaining amount of the order.
                    */
                  function _checkRemainingMakingAmount(IOrderMixin.Order calldata order, bytes32 orderHash) private view returns(uint256 remainingMakingAmount) {
                      if (order.makerTraits.useBitInvalidator()) {
                          remainingMakingAmount = order.makingAmount;
                      } else {
                          remainingMakingAmount = _remainingInvalidator[order.maker.get()][orderHash].remaining(order.makingAmount);
                      }
                      if (remainingMakingAmount == 0) revert InvalidatedOrder();
                  }
                  /**
                    * @notice Calls the transferFrom function with an arbitrary suffix.
                    * @dev The suffix is appended to the end of the standard ERC20 transferFrom function parameters.
                    * @param asset The token to be transferred.
                    * @param from The address to transfer the token from.
                    * @param to The address to transfer the token to.
                    * @param amount The amount of the token to transfer.
                    * @param suffix The suffix (additional data) to append to the end of the transferFrom call.
                    * @return success A boolean indicating whether the transfer was successful.
                    */
                  function _callTransferFromWithSuffix(address asset, address from, address to, uint256 amount, bytes calldata suffix) private returns(bool success) {
                      bytes4 selector = IERC20.transferFrom.selector;
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let data := mload(0x40)
                          mstore(data, selector)
                          mstore(add(data, 0x04), from)
                          mstore(add(data, 0x24), to)
                          mstore(add(data, 0x44), amount)
                          if suffix.length {
                              calldatacopy(add(data, 0x64), suffix.offset, suffix.length)
                          }
                          let status := call(gas(), asset, 0, data, add(0x64, suffix.length), 0x0, 0x20)
                          success := and(status, or(iszero(returndatasize()), and(gt(returndatasize(), 31), eq(mload(0), 1))))
                      }
                  }
              }
              // File @1inch/solidity-utils/contracts/interfaces/[email protected]
              interface IERC20MetadataUppercase {
                  function NAME() external view returns (string memory); // solhint-disable-line func-name-mixedcase
                  function SYMBOL() external view returns (string memory); // solhint-disable-line func-name-mixedcase
              }
              // File @1inch/solidity-utils/contracts/libraries/[email protected]
              /// @title Library with gas-efficient string operations
              library StringUtil {
                  function toHex(uint256 value) internal pure returns (string memory) {
                      return toHex(abi.encodePacked(value));
                  }
                  function toHex(address value) internal pure returns (string memory) {
                      return toHex(abi.encodePacked(value));
                  }
                  /// @dev this is the assembly adaptation of highly optimized toHex16 code from Mikhail Vladimirov
                  /// https://stackoverflow.com/a/69266989
                  function toHex(bytes memory data) internal pure returns (string memory result) {
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          function _toHex16(input) -> output {
                              output := or(
                                  and(input, 0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000),
                                  shr(64, and(input, 0x0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000))
                              )
                              output := or(
                                  and(output, 0xFFFFFFFF000000000000000000000000FFFFFFFF000000000000000000000000),
                                  shr(32, and(output, 0x00000000FFFFFFFF000000000000000000000000FFFFFFFF0000000000000000))
                              )
                              output := or(
                                  and(output, 0xFFFF000000000000FFFF000000000000FFFF000000000000FFFF000000000000),
                                  shr(16, and(output, 0x0000FFFF000000000000FFFF000000000000FFFF000000000000FFFF00000000))
                              )
                              output := or(
                                  and(output, 0xFF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000),
                                  shr(8, and(output, 0x00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000))
                              )
                              output := or(
                                  shr(4, and(output, 0xF000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000)),
                                  shr(8, and(output, 0x0F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F00))
                              )
                              output := add(
                                  add(0x3030303030303030303030303030303030303030303030303030303030303030, output),
                                  mul(
                                      and(
                                          shr(4, add(output, 0x0606060606060606060606060606060606060606060606060606060606060606)),
                                          0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F
                                      ),
                                      7 // Change 7 to 39 for lower case output
                                  )
                              )
                          }
                          result := mload(0x40)
                          let length := mload(data)
                          let resultLength := shl(1, length)
                          let toPtr := add(result, 0x22) // 32 bytes for length + 2 bytes for '0x'
                          mstore(0x40, add(toPtr, resultLength)) // move free memory pointer
                          mstore(add(result, 2), 0x3078) // 0x3078 is right aligned so we write to `result + 2`
                          // to store the last 2 bytes in the beginning of the string
                          mstore(result, add(resultLength, 2)) // extra 2 bytes for '0x'
                          for {
                              let fromPtr := add(data, 0x20)
                              let endPtr := add(fromPtr, length)
                          } lt(fromPtr, endPtr) {
                              fromPtr := add(fromPtr, 0x20)
                          } {
                              let rawData := mload(fromPtr)
                              let hexData := _toHex16(rawData)
                              mstore(toPtr, hexData)
                              toPtr := add(toPtr, 0x20)
                              hexData := _toHex16(shl(128, rawData))
                              mstore(toPtr, hexData)
                              toPtr := add(toPtr, 0x20)
                          }
                      }
                  }
              }
              // File @openzeppelin/contracts/token/ERC20/extensions/[email protected]
              // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
              /**
               * @dev Interface for the optional metadata functions from the ERC20 standard.
               */
              interface IERC20Metadata is IERC20 {
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() external view returns (string memory);
                  /**
                   * @dev Returns the symbol of the token.
                   */
                  function symbol() external view returns (string memory);
                  /**
                   * @dev Returns the decimals places of the token.
                   */
                  function decimals() external view returns (uint8);
              }
              // File @1inch/solidity-utils/contracts/libraries/[email protected]
              /// @title Library, which allows usage of ETH as ERC20 and ERC20 itself. Uses SafeERC20 library for ERC20 interface.
              library UniERC20 {
                  using SafeERC20 for IERC20;
                  error InsufficientBalance();
                  error ApproveCalledOnETH();
                  error NotEnoughValue();
                  error FromIsNotSender();
                  error ToIsNotThis();
                  error ETHTransferFailed();
                  uint256 private constant _RAW_CALL_GAS_LIMIT = 5000;
                  IERC20 private constant _ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
                  IERC20 private constant _ZERO_ADDRESS = IERC20(address(0));
                  /// @dev Returns true if `token` is ETH.
                  function isETH(IERC20 token) internal pure returns (bool) {
                      return (token == _ZERO_ADDRESS || token == _ETH_ADDRESS);
                  }
                  /// @dev Returns `account` ERC20 `token` balance.
                  function uniBalanceOf(IERC20 token, address account) internal view returns (uint256) {
                      if (isETH(token)) {
                          return account.balance;
                      } else {
                          return token.balanceOf(account);
                      }
                  }
                  /// @dev `token` transfer `to` `amount`.
                  /// Note that this function does nothing in case of zero amount.
                  function uniTransfer(
                      IERC20 token,
                      address payable to,
                      uint256 amount
                  ) internal {
                      if (amount > 0) {
                          if (isETH(token)) {
                              if (address(this).balance < amount) revert InsufficientBalance();
                              // solhint-disable-next-line avoid-low-level-calls
                              (bool success, ) = to.call{value: amount, gas: _RAW_CALL_GAS_LIMIT}("");
                              if (!success) revert ETHTransferFailed();
                          } else {
                              token.safeTransfer(to, amount);
                          }
                      }
                  }
                  /// @dev `token` transfer `from` `to` `amount`.
                  /// Note that this function does nothing in case of zero amount.
                  function uniTransferFrom(
                      IERC20 token,
                      address payable from,
                      address to,
                      uint256 amount
                  ) internal {
                      if (amount > 0) {
                          if (isETH(token)) {
                              if (msg.value < amount) revert NotEnoughValue();
                              if (from != msg.sender) revert FromIsNotSender();
                              if (to != address(this)) revert ToIsNotThis();
                              if (msg.value > amount) {
                                  // Return remainder if exist
                                  unchecked {
                                      // solhint-disable-next-line avoid-low-level-calls
                                      (bool success, ) = from.call{value: msg.value - amount, gas: _RAW_CALL_GAS_LIMIT}("");
                                      if (!success) revert ETHTransferFailed();
                                  }
                              }
                          } else {
                              token.safeTransferFrom(from, to, amount);
                          }
                      }
                  }
                  /// @dev Returns `token` symbol from ERC20 metadata.
                  function uniSymbol(IERC20 token) internal view returns (string memory) {
                      return _uniDecode(token, IERC20Metadata.symbol.selector, IERC20MetadataUppercase.SYMBOL.selector);
                  }
                  /// @dev Returns `token` name from ERC20 metadata.
                  function uniName(IERC20 token) internal view returns (string memory) {
                      return _uniDecode(token, IERC20Metadata.name.selector, IERC20MetadataUppercase.NAME.selector);
                  }
                  /// @dev Reverts if `token` is ETH, otherwise performs ERC20 forceApprove.
                  function uniApprove(
                      IERC20 token,
                      address to,
                      uint256 amount
                  ) internal {
                      if (isETH(token)) revert ApproveCalledOnETH();
                      token.forceApprove(to, amount);
                  }
                  /// @dev 20K gas is provided to account for possible implementations of name/symbol
                  /// (token implementation might be behind proxy or store the value in storage)
                  function _uniDecode(
                      IERC20 token,
                      bytes4 lowerCaseSelector,
                      bytes4 upperCaseSelector
                  ) private view returns (string memory result) {
                      if (isETH(token)) {
                          return "ETH";
                      }
                      (bool success, bytes memory data) = address(token).staticcall{gas: 20000}(
                          abi.encodeWithSelector(lowerCaseSelector)
                      );
                      if (!success) {
                          (success, data) = address(token).staticcall{gas: 20000}(abi.encodeWithSelector(upperCaseSelector));
                      }
                      if (success && data.length >= 0x40) {
                          (uint256 offset, uint256 len) = abi.decode(data, (uint256, uint256));
                          /*
                              return data is padded up to 32 bytes with ABI encoder 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 overall data length is greater or equal than string length + extra 64 bytes
                          */
                          if (offset == 0x20 && data.length >= 0x40 + len) {
                              assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                                  result := add(data, 0x40)
                              }
                              return result;
                          }
                      }
                      if (success && data.length == 32) {
                          uint256 len = 0;
                          while (len < data.length && data[len] >= 0x20 && data[len] <= 0x7E) {
                              unchecked {
                                  len++;
                              }
                          }
                          if (len > 0) {
                              assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                                  mstore(data, len)
                              }
                              return string(data);
                          }
                      }
                      return StringUtil.toHex(address(token));
                  }
              }
              // File @openzeppelin/contracts/access/[email protected]
              // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
              /**
               * @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.
               *
               * The initial owner is set to the address provided by the deployer. 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 Ownable is Context {
                  address private _owner;
                  /**
                   * @dev The caller account is not authorized to perform an operation.
                   */
                  error OwnableUnauthorizedAccount(address account);
                  /**
                   * @dev The owner is not a valid owner account. (eg. `address(0)`)
                   */
                  error OwnableInvalidOwner(address owner);
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  /**
                   * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
                   */
                  constructor(address initialOwner) {
                      if (initialOwner == address(0)) {
                          revert OwnableInvalidOwner(address(0));
                      }
                      _transferOwnership(initialOwner);
                  }
                  /**
                   * @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 {
                      if (owner() != _msgSender()) {
                          revert OwnableUnauthorizedAccount(_msgSender());
                      }
                  }
                  /**
                   * @dev Leaves the contract without owner. It will not be possible to call
                   * `onlyOwner` functions. Can only be called by the current owner.
                   *
                   * NOTE: Renouncing ownership will leave the contract without an owner,
                   * thereby disabling 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 {
                      if (newOwner == address(0)) {
                          revert OwnableInvalidOwner(address(0));
                      }
                      _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);
                  }
              }
              // File contracts/helpers/RouterErrors.sol
              library RouterErrors {
                  error ReturnAmountIsNotEnough(uint256 result, uint256 minReturn);
                  error InvalidMsgValue();
                  error ERC20TransferFailed();
                  error Permit2TransferFromFailed();
                  error ApproveFailed();
              }
              // File contracts/interfaces/IClipperExchange.sol
              /// @title Clipper interface subset used in swaps
              interface IClipperExchange {
                  struct Signature {
                      uint8 v;
                      bytes32 r;
                      bytes32 s;
                  }
                  function sellEthForToken(address outputToken, uint256 inputAmount, uint256 outputAmount, uint256 goodUntil, address destinationAddress, Signature calldata theSignature, bytes calldata auxiliaryData) external payable;
                  function sellTokenForEth(address inputToken, uint256 inputAmount, uint256 outputAmount, uint256 goodUntil, address destinationAddress, Signature calldata theSignature, bytes calldata auxiliaryData) external;
                  function swap(address inputToken, address outputToken, uint256 inputAmount, uint256 outputAmount, uint256 goodUntil, address destinationAddress, Signature calldata theSignature, bytes calldata auxiliaryData) external;
              }
              // File contracts/routers/ClipperRouter.sol
              /**
               * @title ClipperRouter
               * @notice Clipper router that allows to use `IClipperExchange` for swaps.
               */
              contract ClipperRouter is Pausable, EthReceiver {
                  using SafeERC20 for IERC20;
                  using SafeERC20 for IWETH;
                  using AddressLib for Address;
                  uint256 private constant _PERMIT2_FLAG = 1 << 255;
                  uint256 private constant _SIGNATURE_S_MASK = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
                  uint256 private constant _SIGNATURE_V_SHIFT = 255;
                  bytes5 private constant _INCH_TAG = "1INCH";
                  uint256 private constant _INCH_TAG_LENGTH = 5;
                  IERC20 private constant _ETH = IERC20(address(0));
                  IWETH private immutable _WETH;  // solhint-disable-line var-name-mixedcase
                  constructor(IWETH weth) {
                      _WETH = weth;
                  }
                  /**
                  * @notice Same as `clipperSwapTo` but uses `msg.sender` as recipient.
                  * @param clipperExchange Clipper pool address.
                  * @param srcToken Source token and flags.
                  * @param dstToken Destination token.
                  * @param inputAmount Amount of source tokens to swap.
                  * @param outputAmount Amount of destination tokens to receive.
                  * @param goodUntil Clipper parameter.
                  * @param r Clipper order signature (r part).
                  * @param vs Clipper order signature (vs part).
                  * @return returnAmount Amount of destination tokens received.
                  */
                  function clipperSwap(
                      IClipperExchange clipperExchange,
                      Address srcToken,
                      IERC20 dstToken,
                      uint256 inputAmount,
                      uint256 outputAmount,
                      uint256 goodUntil,
                      bytes32 r,
                      bytes32 vs
                  ) external payable returns(uint256 returnAmount) {
                      return clipperSwapTo(clipperExchange, payable(msg.sender), srcToken, dstToken, inputAmount, outputAmount, goodUntil, r, vs);
                  }
                  /**
                  * @notice Performs swap using Clipper exchange. Wraps and unwraps ETH if required.
                  *         Sending non-zero `msg.value` for anything but ETH swaps is prohibited.
                  * @param clipperExchange Clipper pool address.
                  * @param recipient Address that will receive swap funds.
                  * @param srcToken Source token and flags.
                  * @param dstToken Destination token.
                  * @param inputAmount Amount of source tokens to swap.
                  * @param outputAmount Amount of destination tokens to receive.
                  * @param goodUntil Clipper parameter.
                  * @param r Clipper order signature (r part).
                  * @param vs Clipper order signature (vs part).
                  * @return returnAmount Amount of destination tokens received.
                  */
                  function clipperSwapTo(
                      IClipperExchange clipperExchange,
                      address payable recipient,
                      Address srcToken,
                      IERC20 dstToken,
                      uint256 inputAmount,
                      uint256 outputAmount,
                      uint256 goodUntil,
                      bytes32 r,
                      bytes32 vs
                  ) public payable whenNotPaused() returns(uint256 returnAmount) {
                      IERC20 srcToken_ = IERC20(srcToken.get());
                      if (srcToken_ == _ETH) {
                          if (msg.value != inputAmount) revert RouterErrors.InvalidMsgValue();
                      } else {
                          if (msg.value != 0) revert RouterErrors.InvalidMsgValue();
                          srcToken_.safeTransferFromUniversal(msg.sender, address(clipperExchange), inputAmount, srcToken.getFlag(_PERMIT2_FLAG));
                      }
                      if (srcToken_ == _ETH) {
                          // clipperExchange.sellEthForToken{value: inputAmount}(address(dstToken), inputAmount, outputAmount, goodUntil, recipient, signature, _INCH_TAG);
                          address clipper = address(clipperExchange);
                          bytes4 selector = clipperExchange.sellEthForToken.selector;
                          assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                              let ptr := mload(0x40)
                              mstore(ptr, selector)
                              mstore(add(ptr, 0x04), dstToken)
                              mstore(add(ptr, 0x24), inputAmount)
                              mstore(add(ptr, 0x44), outputAmount)
                              mstore(add(ptr, 0x64), goodUntil)
                              mstore(add(ptr, 0x84), recipient)
                              mstore(add(ptr, 0xa4), add(27, shr(_SIGNATURE_V_SHIFT, vs)))
                              mstore(add(ptr, 0xc4), r)
                              mstore(add(ptr, 0xe4), and(vs, _SIGNATURE_S_MASK))
                              mstore(add(ptr, 0x104), 0x120)
                              mstore(add(ptr, 0x124), _INCH_TAG_LENGTH)
                              mstore(add(ptr, 0x144), _INCH_TAG)
                              if iszero(call(gas(), clipper, inputAmount, ptr, 0x149, 0, 0)) {
                                  returndatacopy(ptr, 0, returndatasize())
                                  revert(ptr, returndatasize())
                              }
                          }
                      } else if (dstToken == _ETH) {
                          // clipperExchange.sellTokenForEth(address(srcToken_), inputAmount, outputAmount, goodUntil, recipient, signature, _INCH_TAG);
                          address clipper = address(clipperExchange);
                          bytes4 selector = clipperExchange.sellTokenForEth.selector;
                          assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                              let ptr := mload(0x40)
                              mstore(ptr, selector)
                              mstore(add(ptr, 0x04), srcToken_)
                              mstore(add(ptr, 0x24), inputAmount)
                              mstore(add(ptr, 0x44), outputAmount)
                              mstore(add(ptr, 0x64), goodUntil)
                              switch iszero(dstToken)
                              case 1 {
                                  mstore(add(ptr, 0x84), recipient)
                              }
                              default {
                                  mstore(add(ptr, 0x84), address())
                              }
                              mstore(add(ptr, 0xa4), add(27, shr(_SIGNATURE_V_SHIFT, vs)))
                              mstore(add(ptr, 0xc4), r)
                              mstore(add(ptr, 0xe4), and(vs, _SIGNATURE_S_MASK))
                              mstore(add(ptr, 0x104), 0x120)
                              mstore(add(ptr, 0x124), _INCH_TAG_LENGTH)
                              mstore(add(ptr, 0x144), _INCH_TAG)
                              if iszero(call(gas(), clipper, 0, ptr, 0x149, 0, 0)) {
                                  returndatacopy(ptr, 0, returndatasize())
                                  revert(ptr, returndatasize())
                              }
                          }
                      } else {
                          // clipperExchange.swap(address(srcToken_), address(dstToken), inputAmount, outputAmount, goodUntil, recipient, signature, _INCH_TAG);
                          address clipper = address(clipperExchange);
                          bytes4 selector = clipperExchange.swap.selector;
                          assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                              let ptr := mload(0x40)
                              mstore(ptr, selector)
                              mstore(add(ptr, 0x04), srcToken_)
                              mstore(add(ptr, 0x24), dstToken)
                              mstore(add(ptr, 0x44), inputAmount)
                              mstore(add(ptr, 0x64), outputAmount)
                              mstore(add(ptr, 0x84), goodUntil)
                              mstore(add(ptr, 0xa4), recipient)
                              mstore(add(ptr, 0xc4), add(27, shr(_SIGNATURE_V_SHIFT, vs)))
                              mstore(add(ptr, 0xe4), r)
                              mstore(add(ptr, 0x104), and(vs, _SIGNATURE_S_MASK))
                              mstore(add(ptr, 0x124), 0x140)
                              mstore(add(ptr, 0x144), _INCH_TAG_LENGTH)
                              mstore(add(ptr, 0x164), _INCH_TAG)
                              if iszero(call(gas(), clipper, 0, ptr, 0x169, 0, 0)) {
                                  returndatacopy(ptr, 0, returndatasize())
                                  revert(ptr, returndatasize())
                              }
                          }
                      }
                      return outputAmount;
                  }
              }
              // File contracts/interfaces/IAggregationExecutor.sol
              /// @title Interface for making arbitrary calls during swap
              interface IAggregationExecutor {
                  /// @notice propagates information about original msg.sender and executes arbitrary data
                  function execute(address msgSender) external payable returns(uint256);  // 0x4b64e492
              }
              // File contracts/routers/GenericRouter.sol
              /**
               * @title GenericRouter
               * @notice Router that allows to use `IAggregationExecutor` for swaps.
               */
              contract GenericRouter is Pausable, EthReceiver {
                  using UniERC20 for IERC20;
                  using SafeERC20 for IERC20;
                  error ZeroMinReturn();
                  uint256 private constant _PARTIAL_FILL = 1 << 0;
                  uint256 private constant _REQUIRES_EXTRA_ETH = 1 << 1;
                  uint256 private constant _USE_PERMIT2 = 1 << 2;
                  struct SwapDescription {
                      IERC20 srcToken;
                      IERC20 dstToken;
                      address payable srcReceiver;
                      address payable dstReceiver;
                      uint256 amount;
                      uint256 minReturnAmount;
                      uint256 flags;
                  }
                  /**
                  * @notice Performs a swap, delegating all calls encoded in `data` to `executor`. See tests for usage examples.
                  * @dev Router keeps 1 wei of every token on the contract balance for gas optimisations reasons.
                  *      This affects first swap of every token by leaving 1 wei on the contract.
                  * @param executor Aggregation executor that executes calls described in `data`.
                  * @param desc Swap description.
                  * @param data Encoded calls that `caller` should execute in between of swaps.
                  * @return returnAmount Resulting token amount.
                  * @return spentAmount Source token amount.
                  */
                  function swap(
                      IAggregationExecutor executor,
                      SwapDescription calldata desc,
                      bytes calldata data
                  )
                      external
                      payable
                      whenNotPaused()
                      returns (
                          uint256 returnAmount,
                          uint256 spentAmount
                      )
                  {
                      if (desc.minReturnAmount == 0) revert ZeroMinReturn();
                      IERC20 srcToken = desc.srcToken;
                      IERC20 dstToken = desc.dstToken;
                      bool srcETH = srcToken.isETH();
                      if (desc.flags & _REQUIRES_EXTRA_ETH != 0) {
                          if (msg.value <= (srcETH ? desc.amount : 0)) revert RouterErrors.InvalidMsgValue();
                      } else {
                          if (msg.value != (srcETH ? desc.amount : 0)) revert RouterErrors.InvalidMsgValue();
                      }
                      if (!srcETH) {
                          srcToken.safeTransferFromUniversal(msg.sender, desc.srcReceiver, desc.amount, desc.flags & _USE_PERMIT2 != 0);
                      }
                      returnAmount = _execute(executor, msg.sender, desc.amount, data);
                      spentAmount = desc.amount;
                      if (desc.flags & _PARTIAL_FILL != 0) {
                          uint256 unspentAmount = srcToken.uniBalanceOf(address(this));
                          if (unspentAmount > 1) {
                              // we leave 1 wei on the router for gas optimisations reasons
                              unchecked { unspentAmount--; }
                              spentAmount -= unspentAmount;
                              srcToken.uniTransfer(payable(msg.sender), unspentAmount);
                          }
                          if (returnAmount * desc.amount < desc.minReturnAmount * spentAmount) revert RouterErrors.ReturnAmountIsNotEnough(returnAmount, desc.minReturnAmount * spentAmount / desc.amount);
                      } else {
                          if (returnAmount < desc.minReturnAmount) revert RouterErrors.ReturnAmountIsNotEnough(returnAmount, desc.minReturnAmount);
                      }
                      address payable dstReceiver = (desc.dstReceiver == address(0)) ? payable(msg.sender) : desc.dstReceiver;
                      dstToken.uniTransfer(dstReceiver, returnAmount);
                  }
                  function _execute(
                      IAggregationExecutor executor,
                      address srcTokenOwner,
                      uint256 inputAmount,
                      bytes calldata data
                  ) private returns(uint256 result) {
                      bytes4 executeSelector = executor.execute.selector;
                      assembly ("memory-safe") {  // solhint-disable-line no-inline-assembly
                          let ptr := mload(0x40)
                          mstore(ptr, executeSelector)
                          mstore(add(ptr, 0x04), srcTokenOwner)
                          calldatacopy(add(ptr, 0x24), data.offset, data.length)
                          mstore(add(add(ptr, 0x24), data.length), inputAmount)
                          if iszero(call(gas(), executor, callvalue(), ptr, add(0x44, data.length), 0, 0x20)) {
                              returndatacopy(ptr, 0, returndatasize())
                              revert(ptr, returndatasize())
                          }
                          result := mload(0)
                      }
                  }
              }
              // File contracts/interfaces/IUniswapV3Pool.sol
              interface IUniswapV3Pool {
                  /// @notice Emitted by the pool for any swaps between token0 and token1
                  /// @param sender The address that initiated the swap call, and that received the callback
                  /// @param recipient The address that received the output of the swap
                  /// @param amount0 The delta of the token0 balance of the pool
                  /// @param amount1 The delta of the token1 balance of the pool
                  /// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96
                  /// @param liquidity The liquidity of the pool after the swap
                  /// @param tick The log base 1.0001 of price of the pool after the swap
                  event Swap(
                      address indexed sender,
                      address indexed recipient,
                      int256 amount0,
                      int256 amount1,
                      uint160 sqrtPriceX96,
                      uint128 liquidity,
                      int24 tick
                  );
                  /// @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);
              }
              // File contracts/interfaces/IUniswapV3SwapCallback.sol
              /// @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;
              }
              // File contracts/libs/ProtocolLib.sol
              library ProtocolLib {
                  using AddressLib for Address;
                  enum Protocol {
                      UniswapV2,
                      UniswapV3,
                      Curve
                  }
                  uint256 private constant _PROTOCOL_OFFSET = 253;
                  uint256 private constant _WETH_UNWRAP_FLAG = 1 << 252;
                  uint256 private constant _WETH_NOT_WRAP_FLAG = 1 << 251;
                  uint256 private constant _USE_PERMIT2_FLAG = 1 << 250;
                  function protocol(Address self) internal pure returns(Protocol) {
                      // there is no need to mask because protocol is stored in the highest 3 bits
                      return Protocol((Address.unwrap(self) >> _PROTOCOL_OFFSET));
                  }
                  function shouldUnwrapWeth(Address self) internal pure returns(bool) {
                      return self.getFlag(_WETH_UNWRAP_FLAG);
                  }
                  function shouldWrapWeth(Address self) internal pure returns(bool) {
                      return !self.getFlag(_WETH_NOT_WRAP_FLAG);
                  }
                  function usePermit2(Address self) internal pure returns(bool) {
                      return self.getFlag(_USE_PERMIT2_FLAG);
                  }
                  function addressForPreTransfer(Address self) internal view returns(address) {
                      if (protocol(self) == Protocol.UniswapV2) {
                          return self.get();
                      }
                      return address(this);
                  }
              }
              // File contracts/routers/UnoswapRouter.sol
              /**
               * @title UnoswapRouter
               * @notice A router contract for executing token swaps on Unoswap-compatible decentralized exchanges: UniswapV3, UniswapV2, Curve.
               */
              contract UnoswapRouter is Pausable, EthReceiver, IUniswapV3SwapCallback {
                  using SafeERC20 for IERC20;
                  using SafeERC20 for IWETH;
                  using AddressLib for Address;
                  using ProtocolLib for Address;
                  error BadPool();
                  error BadCurveSwapSelector();
                  /// @dev WETH address is network-specific and needs to be changed before deployment.
                  /// It can not be moved to immutable as immutables are not supported in assembly
                  address private constant _WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
                  address private constant _ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                  address private constant _PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
                  bytes4 private constant _WETH_DEPOSIT_CALL_SELECTOR = 0xd0e30db0;
                  bytes4 private constant _WETH_WITHDRAW_CALL_SELECTOR = 0x2e1a7d4d;
                  uint256 private constant _ADDRESS_MASK = 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff;
                  uint256 private constant _SELECTORS = (
                      (uint256(uint32(IUniswapV3Pool.token0.selector)) << 224) |
                      (uint256(uint32(IUniswapV3Pool.token1.selector)) << 192) |
                      (uint256(uint32(IUniswapV3Pool.fee.selector)) << 160) |
                      (uint256(uint32(IERC20.transfer.selector)) << 128) |
                      (uint256(uint32(IERC20.transferFrom.selector)) << 96) |
                      (uint256(uint32(IPermit2.transferFrom.selector)) << 64)
                  );
                  uint256 private constant _TOKEN0_SELECTOR_OFFSET = 0;
                  uint256 private constant _TOKEN1_SELECTOR_OFFSET = 4;
                  uint256 private constant _FEE_SELECTOR_OFFSET = 8;
                  uint256 private constant _TRANSFER_SELECTOR_OFFSET = 12;
                  uint256 private constant _TRANSFER_FROM_SELECTOR_OFFSET = 16;
                  uint256 private constant _PERMIT2_TRANSFER_FROM_SELECTOR_OFFSET = 20;
                  bytes32 private constant _POOL_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;
                  bytes32 private constant _FF_FACTORY = 0xff1F98431c8aD98523631AE4a59f267346ea31F9840000000000000000000000;
                  // =====================================================================
                  //                          Methods with 1 pool
                  // =====================================================================
                  /**
                  * @notice Swaps `amount` of the specified `token` for another token using an Unoswap-compatible exchange's pool,
                  *         with a minimum return specified by `minReturn`.
                  * @param token The address of the token to be swapped.
                  * @param amount The amount of tokens to be swapped.
                  * @param minReturn The minimum amount of tokens to be received after the swap.
                  * @param dex The address of the Unoswap-compatible exchange's pool.
                  * @return returnAmount The actual amount of tokens received after the swap.
                  */
                  function unoswap(Address token, uint256 amount, uint256 minReturn, Address dex) external returns(uint256 returnAmount) {
                      returnAmount = _unoswapTo(msg.sender, msg.sender, token, amount, minReturn, dex);
                  }
                  /**
                  * @notice Swaps `amount` of the specified `token` for another token using an Unoswap-compatible exchange's pool,
                  *         sending the resulting tokens to the `to` address, with a minimum return specified by `minReturn`.
                  * @param to The address to receive the swapped tokens.
                  * @param token The address of the token to be swapped.
                  * @param amount The amount of tokens to be swapped.
                  * @param minReturn The minimum amount of tokens to be received after the swap.
                  * @param dex The address of the Unoswap-compatible exchange's pool.
                  * @return returnAmount The actual amount of tokens received after the swap.
                  */
                  function unoswapTo(Address to, Address token, uint256 amount, uint256 minReturn, Address dex) external returns(uint256 returnAmount) {
                      returnAmount = _unoswapTo(msg.sender, to.get(), token, amount, minReturn, dex);
                  }
                  /**
                  * @notice Swaps ETH for another token using an Unoswap-compatible exchange's pool, with a minimum return specified by `minReturn`.
                  *         The function is payable and requires the sender to attach ETH.
                  *         It is necessary to check if it's cheaper to use _WETH_NOT_WRAP_FLAG in `dex` Address (for example: for Curve pools).
                  * @param minReturn The minimum amount of tokens to be received after the swap.
                  * @param dex The address of the Unoswap-compatible exchange's pool.
                  * @return returnAmount The actual amount of tokens received after the swap.
                  */
                  function ethUnoswap(uint256 minReturn, Address dex) external payable returns(uint256 returnAmount) {
                      if (dex.shouldWrapWeth()) {
                          IWETH(_WETH).safeDeposit(msg.value);
                      }
                      returnAmount = _unoswapTo(address(this), msg.sender, Address.wrap(uint160(_WETH)), msg.value, minReturn, dex);
                  }
                  /**
                  * @notice Swaps ETH for another token using an Unoswap-compatible exchange's pool, sending the resulting tokens to the `to` address,
                  *         with a minimum return specified by `minReturn`. The function is payable and requires the sender to attach ETH.
                  *         It is necessary to check if it's cheaper to use _WETH_NOT_WRAP_FLAG in `dex` Address (for example: for Curve pools).
                  * @param to The address to receive the swapped tokens.
                  * @param minReturn The minimum amount of tokens to be received after the swap.
                  * @param dex The address of the Unoswap-compatible exchange's pool.
                  * @return returnAmount The actual amount of tokens received after the swap.
                  */
                  function ethUnoswapTo(Address to, uint256 minReturn, Address dex) external payable returns(uint256 returnAmount) {
                      if (dex.shouldWrapWeth()) {
                          IWETH(_WETH).safeDeposit(msg.value);
                      }
                      returnAmount = _unoswapTo(address(this), to.get(), Address.wrap(uint160(_WETH)), msg.value, minReturn, dex);
                  }
                  function _unoswapTo(address from, address to, Address token, uint256 amount, uint256 minReturn, Address dex) private whenNotPaused() returns(uint256 returnAmount) {
                      if (dex.shouldUnwrapWeth()) {
                          returnAmount = _unoswap(from, address(this), token, amount, minReturn, dex);
                          IWETH(_WETH).safeWithdrawTo(returnAmount, to);
                      } else {
                          returnAmount = _unoswap(from, to, token, amount, minReturn, dex);
                      }
                  }
                  // =====================================================================
                  //                    Methods with 2 sequential pools
                  // =====================================================================
                  /**
                  * @notice Swaps `amount` of the specified `token` for another token using two Unoswap-compatible exchange pools (`dex` and `dex2`) sequentially,
                  *         with a minimum return specified by `minReturn`.
                  * @param token The address of the token to be swapped.
                  * @param amount The amount of tokens to be swapped.
                  * @param minReturn The minimum amount of tokens to be received after the swap.
                  * @param dex The address of the first Unoswap-compatible exchange's pool.
                  * @param dex2 The address of the second Unoswap-compatible exchange's pool.
                  * @return returnAmount The actual amount of tokens received after the swap through both pools.
                  */
                  function unoswap2(Address token, uint256 amount, uint256 minReturn, Address dex, Address dex2) external returns(uint256 returnAmount) {
                      returnAmount = _unoswapTo2(msg.sender, msg.sender, token, amount, minReturn, dex, dex2);
                  }
                  /**
                  * @notice Swaps `amount` of the specified `token` for another token using two Unoswap-compatible exchange pools (`dex` and `dex2`) sequentially,
                  *         sending the resulting tokens to the `to` address, with a minimum return specified by `minReturn`.
                  * @param to The address to receive the swapped tokens.
                  * @param token The address of the token to be swapped.
                  * @param amount The amount of tokens to be swapped.
                  * @param minReturn The minimum amount of tokens to be received after the swap.
                  * @param dex The address of the first Unoswap-compatible exchange's pool.
                  * @param dex2 The address of the second Unoswap-compatible exchange's pool.
                  * @return returnAmount The actual amount of tokens received after the swap through both pools.
                  */
                  function unoswapTo2(Address to, Address token, uint256 amount, uint256 minReturn, Address dex, Address dex2) external returns(uint256 returnAmount) {
                      returnAmount = _unoswapTo2(msg.sender, to.get(), token, amount, minReturn, dex, dex2);
                  }
                  /**
                  * @notice Swaps ETH for another token using two Unoswap-compatible exchange pools (`dex` and `dex2`) sequentially,
                  *         with a minimum return specified by `minReturn`. The function is payable and requires the sender to attach ETH.
                  *         It is necessary to check if it's cheaper to use _WETH_NOT_WRAP_FLAG in `dex` Address (for example: for Curve pools).
                  * @param minReturn The minimum amount of tokens to be received after the swap.
                  * @param dex The address of the first Unoswap-compatible exchange's pool.
                  * @param dex2 The address of the second Unoswap-compatible exchange's pool.
                  * @return returnAmount The actual amount of tokens received after the swap through both pools.
                  */
                  function ethUnoswap2(uint256 minReturn, Address dex, Address dex2) external payable returns(uint256 returnAmount) {
                      if (dex.shouldWrapWeth()) {
                          IWETH(_WETH).safeDeposit(msg.value);
                      }
                      returnAmount = _unoswapTo2(address(this), msg.sender, Address.wrap(uint160(_WETH)), msg.value, minReturn, dex, dex2);
                  }
                  /**
                  * @notice Swaps ETH for another token using two Unoswap-compatible exchange pools (`dex` and `dex2`) sequentially,
                  *         sending the resulting tokens to the `to` address, with a minimum return specified by `minReturn`.
                  *         The function is payable and requires the sender to attach ETH.
                  *         It is necessary to check if it's cheaper to use _WETH_NOT_WRAP_FLAG in `dex` Address (for example: for Curve pools).
                  * @param to The address to receive the swapped tokens.
                  * @param minReturn The minimum amount of tokens to be received after the swap.
                  * @param dex The address of the first Unoswap-compatible exchange's pool.
                  * @param dex2 The address of the second Unoswap-compatible exchange's pool.
                  * @return returnAmount The actual amount of tokens received after the swap through both pools.
                  */
                  function ethUnoswapTo2(Address to, uint256 minReturn, Address dex, Address dex2) external payable returns(uint256 returnAmount) {
                      if (dex.shouldWrapWeth()) {
                          IWETH(_WETH).safeDeposit(msg.value);
                      }
                      returnAmount = _unoswapTo2(address(this), to.get(), Address.wrap(uint160(_WETH)), msg.value, minReturn, dex, dex2);
                  }
                  function _unoswapTo2(address from, address to, Address token, uint256 amount, uint256 minReturn, Address dex, Address dex2) private whenNotPaused() returns(uint256 returnAmount) {
                      address pool2 = dex2.addressForPreTransfer();
                      address target = dex2.shouldUnwrapWeth() ? address(this) : to;
                      returnAmount = _unoswap(from, pool2, token, amount, 0, dex);
                      returnAmount = _unoswap(pool2, target, Address.wrap(0), returnAmount, minReturn, dex2);
                      if (dex2.shouldUnwrapWeth()) {
                          IWETH(_WETH).safeWithdrawTo(returnAmount, to);
                      }
                  }
                  // =====================================================================
                  //                    Methods with 3 sequential pools
                  // =====================================================================
                  /**
                  * @notice Swaps `amount` of the specified `token` for another token using three Unoswap-compatible exchange pools
                  *         (`dex`, `dex2`, and `dex3`) sequentially, with a minimum return specified by `minReturn`.
                  * @param token The address of the token to be swapped.
                  * @param amount The amount of tokens to be swapped.
                  * @param minReturn The minimum amount of tokens to be received after the swap.
                  * @param dex The address of the first Unoswap-compatible exchange's pool.
                  * @param dex2 The address of the second Unoswap-compatible exchange's pool.
                  * @param dex3 The address of the third Unoswap-compatible exchange's pool.
                  * @return returnAmount The actual amount of tokens received after the swap through all three pools.
                  */
                  function unoswap3(Address token, uint256 amount, uint256 minReturn, Address dex, Address dex2, Address dex3) external returns(uint256 returnAmount) {
                      returnAmount = _unoswapTo3(msg.sender, msg.sender, token, amount, minReturn, dex, dex2, dex3);
                  }
                  /**
                  * @notice Swaps `amount` of the specified `token` for another token using three Unoswap-compatible exchange pools
                  *         (`dex`, `dex2`, and `dex3`) sequentially, sending the resulting tokens to the `to` address, with a minimum return specified by `minReturn`.
                  * @param to The address to receive the swapped tokens.
                  * @param token The address of the token to be swapped.
                  * @param amount The amount of tokens to be swapped.
                  * @param minReturn The minimum amount of tokens to be received after the swap.
                  * @param dex The address of the first Unoswap-compatible exchange's pool.
                  * @param dex2 The address of the second Unoswap-compatible exchange's pool.
                  * @param dex3 The address of the third Unoswap-compatible exchange's pool.
                  * @return returnAmount The actual amount of tokens received after the swap through all three pools.
                  */
                  function unoswapTo3(Address to, Address token, uint256 amount, uint256 minReturn, Address dex, Address dex2, Address dex3) external returns(uint256 returnAmount) {
                      returnAmount = _unoswapTo3(msg.sender, to.get(), token, amount, minReturn, dex, dex2, dex3);
                  }
                  /**
                  * @notice Swaps ETH for another token using three Unoswap-compatible exchange pools (`dex`, `dex2`, and `dex3`) sequentially,
                  *         with a minimum return specified by `minReturn`. The function is payable and requires the sender to attach ETH.
                  *         It is necessary to check if it's cheaper to use _WETH_NOT_WRAP_FLAG in `dex` Address (for example: for Curve pools).
                  * @param minReturn The minimum amount of tokens to be received after the swap.
                  * @param dex The address of the first Unoswap-compatible exchange's pool.
                  * @param dex2 The address of the second Unoswap-compatible exchange's pool.
                  * @param dex3 The address of the third Unoswap-compatible exchange's pool.
                  * @return returnAmount The actual amount of tokens received after the swap through all three pools.
                  */
                  function ethUnoswap3(uint256 minReturn, Address dex, Address dex2, Address dex3) external payable returns(uint256 returnAmount) {
                      if (dex.shouldWrapWeth()) {
                          IWETH(_WETH).safeDeposit(msg.value);
                      }
                      returnAmount = _unoswapTo3(address(this), msg.sender, Address.wrap(uint160(_WETH)), msg.value, minReturn, dex, dex2, dex3);
                  }
                  /**
                  * @notice Swaps ETH for another token using three Unoswap-compatible exchange pools (`dex`, `dex2`, and `dex3`) sequentially,
                  *         sending the resulting tokens to the `to` address, with a minimum return specified by `minReturn`.
                  *         The function is payable and requires the sender to attach ETH.
                  *         It is necessary to check if it's cheaper to use _WETH_NOT_WRAP_FLAG in `dex` Address (for example: for Curve pools).
                  * @param to The address to receive the swapped tokens.
                  * @param minReturn The minimum amount of tokens to be received after the swap.
                  * @param dex The address of the first Unoswap-compatible exchange's pool.
                  * @param dex2 The address of the second Unoswap-compatible exchange's pool.
                  * @param dex3 The address of the third Unoswap-compatible exchange's pool.
                  * @return returnAmount The actual amount of tokens received after the swap through all three pools.
                  */
                  function ethUnoswapTo3(Address to, uint256 minReturn, Address dex, Address dex2, Address dex3) external payable returns(uint256 returnAmount) {
                      if (dex.shouldWrapWeth()) {
                          IWETH(_WETH).safeDeposit(msg.value);
                      }
                      returnAmount = _unoswapTo3(address(this), to.get(), Address.wrap(uint160(_WETH)), msg.value, minReturn, dex, dex2, dex3);
                  }
                  function _unoswapTo3(address from, address to, Address token, uint256 amount, uint256 minReturn, Address dex, Address dex2, Address dex3) private whenNotPaused() returns(uint256 returnAmount) {
                      address pool2 = dex2.addressForPreTransfer();
                      address pool3 = dex3.addressForPreTransfer();
                      address target = dex3.shouldUnwrapWeth() ? address(this) : to;
                      returnAmount = _unoswap(from, pool2, token, amount, 0, dex);
                      returnAmount = _unoswap(pool2, pool3, Address.wrap(0), returnAmount, 0, dex2);
                      returnAmount = _unoswap(pool3, target, Address.wrap(0), returnAmount, minReturn, dex3);
                      if (dex3.shouldUnwrapWeth()) {
                          IWETH(_WETH).safeWithdrawTo(returnAmount, to);
                      }
                  }
                  function _unoswap(
                      address spender,
                      address recipient,
                      Address token,
                      uint256 amount,
                      uint256 minReturn,
                      Address dex
                  ) private returns(uint256 returnAmount) {
                      ProtocolLib.Protocol protocol = dex.protocol();
                      if (protocol == ProtocolLib.Protocol.UniswapV3) {
                          returnAmount = _unoswapV3(spender, recipient, amount, minReturn, dex);
                      } else if (protocol == ProtocolLib.Protocol.UniswapV2) {
                          if (spender == address(this)) {
                              IERC20(token.get()).safeTransfer(dex.get(), amount);
                          } else if (spender == msg.sender) {
                              IERC20(token.get()).safeTransferFromUniversal(msg.sender, dex.get(), amount, dex.usePermit2());
                          }
                          returnAmount = _unoswapV2(recipient, amount, minReturn, dex);
                      } else if (protocol == ProtocolLib.Protocol.Curve) {
                          if (spender == msg.sender && msg.value == 0) {
                              IERC20(token.get()).safeTransferFromUniversal(msg.sender, address(this), amount, dex.usePermit2());
                          }
                          returnAmount = _curfe(recipient, amount, minReturn, dex);
                      }
                  }
                  uint256 private constant _UNISWAP_V2_ZERO_FOR_ONE_OFFSET = 247;
                  uint256 private constant _UNISWAP_V2_ZERO_FOR_ONE_MASK = 0x01;
                  uint256 private constant _UNISWAP_V2_NUMERATOR_OFFSET = 160;
                  uint256 private constant _UNISWAP_V2_NUMERATOR_MASK = 0xffffffff;
                  bytes4 private constant _UNISWAP_V2_PAIR_RESERVES_CALL_SELECTOR = 0x0902f1ac;
                  bytes4 private constant _UNISWAP_V2_PAIR_SWAP_CALL_SELECTOR = 0x022c0d9f;
                  uint256 private constant _UNISWAP_V2_DENOMINATOR = 1e9;
                  uint256 private constant _UNISWAP_V2_DEFAULT_NUMERATOR = 997_000_000;
                  error ReservesCallFailed();
                  function _unoswapV2(
                      address recipient,
                      uint256 amount,
                      uint256 minReturn,
                      Address dex
                  ) private returns(uint256 ret) {
                      bytes4 returnAmountNotEnoughException = RouterErrors.ReturnAmountIsNotEnough.selector;
                      bytes4 reservesCallFailedException = ReservesCallFailed.selector;
                      assembly ("memory-safe") {  // solhint-disable-line no-inline-assembly
                          let pool := and(dex, _ADDRESS_MASK)
                          let zeroForOne := and(shr(_UNISWAP_V2_ZERO_FOR_ONE_OFFSET, dex), _UNISWAP_V2_ZERO_FOR_ONE_MASK)
                          let numerator := and(shr(_UNISWAP_V2_NUMERATOR_OFFSET, dex), _UNISWAP_V2_NUMERATOR_MASK)
                          if iszero(numerator) {
                              numerator := _UNISWAP_V2_DEFAULT_NUMERATOR
                          }
                          let ptr := mload(0x40)
                          mstore(0, _UNISWAP_V2_PAIR_RESERVES_CALL_SELECTOR)
                          if iszero(staticcall(gas(), pool, 0, 4, 0, 0x40)) {
                              returndatacopy(ptr, 0, returndatasize())
                              revert(ptr, returndatasize())
                          }
                          if sub(returndatasize(), 0x60) {
                              mstore(0, reservesCallFailedException)
                              revert(0, 4)
                          }
                          let reserve0 := mload(mul(0x20, iszero(zeroForOne)))
                          let reserve1 := mload(mul(0x20, zeroForOne))
                          // this will not overflow as reserve0, reserve1 and ret fit to 112 bit and numerator and _DENOMINATOR fit to 32 bit
                          ret := mul(amount, numerator)
                          ret := div(mul(ret, reserve1), add(ret, mul(reserve0, _UNISWAP_V2_DENOMINATOR)))
                          if lt(ret, minReturn) {
                              mstore(ptr, returnAmountNotEnoughException)
                              mstore(add(ptr, 0x04), ret)
                              mstore(add(ptr, 0x24), minReturn)
                              revert(ptr, 0x44)
                          }
                          mstore(ptr, _UNISWAP_V2_PAIR_SWAP_CALL_SELECTOR)
                          mstore(add(ptr, 0x04), mul(ret, iszero(zeroForOne)))
                          mstore(add(ptr, 0x24), mul(ret, zeroForOne))
                          mstore(add(ptr, 0x44), recipient)
                          mstore(add(ptr, 0x64), 0x80)
                          mstore(add(ptr, 0x84), 0)
                          if iszero(call(gas(), pool, 0, ptr, 0xa4, 0, 0)) {
                              returndatacopy(ptr, 0, returndatasize())
                              revert(ptr, returndatasize())
                          }
                      }
                  }
                  /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
                  uint160 private constant _UNISWAP_V3_MIN_SQRT_RATIO = 4295128739 + 1;
                  /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
                  uint160 private constant _UNISWAP_V3_MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342 - 1;
                  uint256 private constant _UNISWAP_V3_ZERO_FOR_ONE_OFFSET = 247;
                  uint256 private constant _UNISWAP_V3_ZERO_FOR_ONE_MASK = 0x01;
                  function _unoswapV3(
                      address spender,
                      address recipient,
                      uint256 amount,
                      uint256 minReturn,
                      Address dex
                  ) private returns(uint256 ret) {
                      bytes4 swapSelector = IUniswapV3Pool.swap.selector;
                      bool usePermit2 = dex.usePermit2();
                      assembly ("memory-safe") {  // solhint-disable-line no-inline-assembly
                          let pool := and(dex, _ADDRESS_MASK)
                          let zeroForOne := and(shr(_UNISWAP_V3_ZERO_FOR_ONE_OFFSET, dex), _UNISWAP_V3_ZERO_FOR_ONE_MASK)
                          let ptr := mload(0x40)
                          mstore(ptr, swapSelector)
                          mstore(add(ptr, 0x04), recipient)
                          mstore(add(ptr, 0x24), zeroForOne)
                          mstore(add(ptr, 0x44), amount)
                          switch zeroForOne
                          case 1 {
                              mstore(add(ptr, 0x64), _UNISWAP_V3_MIN_SQRT_RATIO)
                          }
                          case 0 {
                              mstore(add(ptr, 0x64), _UNISWAP_V3_MAX_SQRT_RATIO)
                          }
                          mstore(add(ptr, 0x84), 0xa0)
                          mstore(add(ptr, 0xa4), 0x40)
                          mstore(add(ptr, 0xc4), spender)
                          mstore(add(ptr, 0xe4), usePermit2)
                          if iszero(call(gas(), pool, 0, ptr, 0x0104, 0, 0x40)) {
                              returndatacopy(ptr, 0, returndatasize())
                              revert(ptr, returndatasize())
                          }
                          ret := sub(0, mload(mul(0x20, zeroForOne)))
                      }
                      if (ret < minReturn) revert RouterErrors.ReturnAmountIsNotEnough(ret, minReturn);
                  }
                  uint256 private constant _CURVE_SWAP_SELECTOR_IDX_OFFSET = 184;
                  uint256 private constant _CURVE_SWAP_SELECTOR_IDX_MASK = 0xff;
                  uint256 private constant _CURVE_FROM_COINS_SELECTOR_OFFSET = 192;
                  uint256 private constant _CURVE_FROM_COINS_SELECTOR_MASK = 0xff;
                  uint256 private constant _CURVE_FROM_COINS_ARG_OFFSET = 200;
                  uint256 private constant _CURVE_FROM_COINS_ARG_MASK = 0xff;
                  uint256 private constant _CURVE_TO_COINS_SELECTOR_OFFSET = 208;
                  uint256 private constant _CURVE_TO_COINS_SELECTOR_MASK = 0xff;
                  uint256 private constant _CURVE_TO_COINS_ARG_OFFSET = 216;
                  uint256 private constant _CURVE_TO_COINS_ARG_MASK = 0xff;
                  uint256 private constant _CURVE_FROM_TOKEN_OFFSET = 224;
                  uint256 private constant _CURVE_FROM_TOKEN_MASK = 0xff;
                  uint256 private constant _CURVE_TO_TOKEN_OFFSET = 232;
                  uint256 private constant _CURVE_TO_TOKEN_MASK = 0xff;
                  uint256 private constant _CURVE_INPUT_WETH_DEPOSIT_OFFSET = 240;
                  uint256 private constant _CURVE_INPUT_WETH_WITHDRAW_OFFSET = 241;
                  uint256 private constant _CURVE_SWAP_USE_ETH_OFFSET = 242;
                  uint256 private constant _CURVE_SWAP_HAS_ARG_USE_ETH_OFFSET = 243;
                  uint256 private constant _CURVE_SWAP_HAS_ARG_DESTINATION_OFFSET = 244;
                  uint256 private constant _CURVE_OUTPUT_WETH_DEPOSIT_OFFSET = 245;
                  uint256 private constant _CURVE_OUTPUT_WETH_WITHDRAW_OFFSET = 246;
                  uint256 private constant _CURVE_SWAP_USE_SECOND_OUTPUT_OFFSET = 247;
                  uint256 private constant _CURVE_SWAP_HAS_ARG_CALLBACK_OFFSET = 249;
                  // Curve Pool function selectors for different `coins` methods. For details, see contracts/interfaces/ICurvePool.sol
                  bytes32 private constant _CURVE_COINS_SELECTORS = 0x87cb4f5723746eb8c6610657b739953eb9947eb0000000000000000000000000;
                  // Curve Pool function selectors for different `exchange` methods. For details, see contracts/interfaces/ICurvePool.sol
                  bytes32 private constant _CURVE_SWAP_SELECTORS_1 = 0x3df02124a6417ed6ddc1f59d44ee1986ed4ae2b8bf5ed0562f7865a837cab679;
                  bytes32 private constant _CURVE_SWAP_SELECTORS_2 = 0x2a064e3c5b41b90865b2489ba64833a0e2ad025a394747c5cb7558f1ce7d6503;
                  bytes32 private constant _CURVE_SWAP_SELECTORS_3 = 0xd2e2833add96994f000000000000000000000000000000000000000000000000;
                  uint256 private constant _CURVE_MAX_SELECTOR_INDEX = 17;
                  function _curfe(
                      address recipient,
                      uint256 amount,
                      uint256 minReturn,
                      Address dex
                  ) private returns(uint256 ret) {
                      bytes4 callbackSelector = this.curveSwapCallback.selector;
                      assembly ("memory-safe") {  // solhint-disable-line no-inline-assembly
                          function reRevert() {
                              let ptr := mload(0x40)
                              returndatacopy(ptr, 0, returndatasize())
                              revert(ptr, returndatasize())
                          }
                          function callReturnSize(status) -> rds {
                              if iszero(status) {
                                  reRevert()
                              }
                              rds := returndatasize()
                          }
                          function tokenBalanceOf(tokenAddress, accountAddress) -> tokenBalance {
                              mstore(0, 0x70a0823100000000000000000000000000000000000000000000000000000000)
                              mstore(4, accountAddress)
                              if iszero(callReturnSize(staticcall(gas(), tokenAddress, 0, 0x24, 0, 0x20))) {
                                  revert(0, 0)
                              }
                              tokenBalance := mload(0)
                          }
                          function asmApprove(token, to, value, mem) {
                              let selector := 0x095ea7b300000000000000000000000000000000000000000000000000000000 // IERC20.approve.selector
                              let exception := 0x3e3f8f7300000000000000000000000000000000000000000000000000000000 // error ApproveFailed()
                              if iszero(_asmCall(token, selector, to, value, mem)) {
                                  if iszero(_asmCall(token, selector, to, 0, mem)) {
                                      mstore(mem, exception)
                                      revert(mem, 4)
                                  }
                                  if iszero(_asmCall(token, selector, to, value, mem)) {
                                      mstore(mem, exception)
                                      revert(mem, 4)
                                  }
                              }
                          }
                          function _asmCall(token, selector, to, value, mem) -> done {
                              mstore(mem, selector)
                              mstore(add(mem, 0x04), to)
                              mstore(add(mem, 0x24), value)
                              let success := call(gas(), token, 0, mem, 0x44, 0x0, 0x20)
                              done := and(
                                  success,
                                  or(
                                      iszero(returndatasize()),
                                      and(gt(returndatasize(), 31), eq(mload(0), 1))
                                  )
                              )
                          }
                          function curveCoins(pool, selectorOffset, index) -> coin {
                              mstore(0, _CURVE_COINS_SELECTORS)
                              mstore(add(selectorOffset, 4), index)
                              if iszero(staticcall(gas(), pool, selectorOffset, 0x24, 0, 0x20)) {
                                  reRevert()
                              }
                              coin := mload(0)
                          }
                          let pool := and(dex, _ADDRESS_MASK)
                          let useEth := and(shr(_CURVE_SWAP_USE_ETH_OFFSET, dex), 0x01)
                          let hasCallback := and(shr(_CURVE_SWAP_HAS_ARG_CALLBACK_OFFSET, dex), 0x01)
                          if and(shr(_CURVE_INPUT_WETH_DEPOSIT_OFFSET, dex), 0x01) {
                              // Deposit ETH to WETH
                              mstore(0, _WETH_DEPOSIT_CALL_SELECTOR)
                              if iszero(call(gas(), _WETH, amount, 0, 4, 0, 0)) {
                                  reRevert()
                              }
                          }
                          if and(shr(_CURVE_INPUT_WETH_WITHDRAW_OFFSET, dex), 0x01) {
                              // Withdraw ETH from WETH
                              mstore(0, _WETH_WITHDRAW_CALL_SELECTOR)
                              mstore(4, amount)
                              if iszero(call(gas(), _WETH, 0, 0, 0x24, 0, 0)) {
                                  reRevert()
                              }
                          }
                          let toToken
                          {  // Stack too deep
                              let toSelectorOffset := and(shr(_CURVE_TO_COINS_SELECTOR_OFFSET, dex), _CURVE_TO_COINS_SELECTOR_MASK)
                              let toTokenIndex := and(shr(_CURVE_TO_COINS_ARG_OFFSET, dex), _CURVE_TO_COINS_ARG_MASK)
                              toToken := curveCoins(pool, toSelectorOffset, toTokenIndex)
                          }
                          let toTokenIsEth := or(eq(toToken, _ETH), eq(toToken, _WETH))
                          // use approve when the callback is not used AND (raw ether is not used at all OR ether is used on the output)
                          if and(iszero(hasCallback), or(iszero(useEth), toTokenIsEth)) {
                              let fromSelectorOffset := and(shr(_CURVE_FROM_COINS_SELECTOR_OFFSET, dex), _CURVE_FROM_COINS_SELECTOR_MASK)
                              let fromTokenIndex := and(shr(_CURVE_FROM_COINS_ARG_OFFSET, dex), _CURVE_FROM_COINS_ARG_MASK)
                              let fromToken := curveCoins(pool, fromSelectorOffset, fromTokenIndex)
                              if eq(fromToken, _ETH) {
                                  fromToken := _WETH
                              }
                              asmApprove(fromToken, pool, amount, mload(0x40))
                          }
                          // Swap
                          let ptr := mload(0x40)
                          {  // stack too deep
                              let selectorIndex := and(shr(_CURVE_SWAP_SELECTOR_IDX_OFFSET, dex), _CURVE_SWAP_SELECTOR_IDX_MASK)
                              if gt(selectorIndex, _CURVE_MAX_SELECTOR_INDEX) {
                                  mstore(0, 0xa231cb8200000000000000000000000000000000000000000000000000000000)  // BadCurveSwapSelector()
                                  revert(0, 4)
                              }
                              mstore(ptr, _CURVE_SWAP_SELECTORS_1)
                              mstore(add(ptr, 0x20), _CURVE_SWAP_SELECTORS_2)
                              mstore(add(ptr, 0x40), _CURVE_SWAP_SELECTORS_3)
                              ptr := add(ptr, mul(selectorIndex, 4))
                          }
                          mstore(add(ptr, 0x04), and(shr(_CURVE_FROM_TOKEN_OFFSET, dex), _CURVE_FROM_TOKEN_MASK))
                          mstore(add(ptr, 0x24), and(shr(_CURVE_TO_TOKEN_OFFSET, dex), _CURVE_TO_TOKEN_MASK))
                          mstore(add(ptr, 0x44), amount)
                          mstore(add(ptr, 0x64), minReturn)
                          let offset := 0x84
                          if and(shr(_CURVE_SWAP_HAS_ARG_USE_ETH_OFFSET, dex), 0x01) {
                              mstore(add(ptr, offset), useEth)
                              offset := add(offset, 0x20)
                          }
                          switch hasCallback
                          case 1 {
                              mstore(add(ptr, offset), address())
                              mstore(add(ptr, add(offset, 0x20)), recipient)
                              mstore(add(ptr, add(offset, 0x40)), callbackSelector)
                              offset := add(offset, 0x60)
                          }
                          default {
                              if and(shr(_CURVE_SWAP_HAS_ARG_DESTINATION_OFFSET, dex), 0x01) {
                                  mstore(add(ptr, offset), recipient)
                                  offset := add(offset, 0x20)
                              }
                          }
                          // swap call
                          // value is passed when useEth is set but toToken is not ETH
                          switch callReturnSize(call(gas(), pool, mul(mul(amount, useEth), iszero(toTokenIsEth)), ptr, offset, 0, 0x40))
                          case 0 {
                              // we expect that curve pools that do not return any value also do not have the recipient argument
                              switch and(useEth, toTokenIsEth)
                              case 1 {
                                  ret := balance(address())
                              }
                              default {
                                  ret := tokenBalanceOf(toToken, address())
                              }
                              ret := sub(ret, 1)  // keep 1 wei
                          }
                          default {
                              ret := mload(mul(0x20, and(shr(_CURVE_SWAP_USE_SECOND_OUTPUT_OFFSET, dex), 0x01)))
                          }
                          if iszero(and(shr(_CURVE_SWAP_HAS_ARG_DESTINATION_OFFSET, dex), 0x01)) {
                              if and(shr(_CURVE_OUTPUT_WETH_DEPOSIT_OFFSET, dex), 0x01) {
                                  // Deposit ETH to WETH
                                  mstore(0, _WETH_DEPOSIT_CALL_SELECTOR)
                                  if iszero(call(gas(), _WETH, ret, 0, 4, 0, 0)) {
                                      reRevert()
                                  }
                              }
                              if and(shr(_CURVE_OUTPUT_WETH_WITHDRAW_OFFSET, dex), 0x01) {
                                  // Withdraw ETH from WETH
                                  mstore(0, _WETH_WITHDRAW_CALL_SELECTOR)
                                  mstore(4, ret)
                                  if iszero(call(gas(), _WETH, 0, 0, 0x24, 0, 0)) {
                                      reRevert()
                                  }
                              }
                              // Post transfer toToken if needed
                              if xor(recipient, address()) {
                                  switch and(useEth, toTokenIsEth)
                                  case 1 {
                                      if iszero(call(gas(), recipient, ret, 0, 0, 0, 0)) {
                                          reRevert()
                                      }
                                  }
                                  default {
                                      if eq(toToken, _ETH) {
                                          toToken := _WETH
                                      }
                                      // toToken.transfer(recipient, ret)
                                      if iszero(_asmCall(toToken, 0xa9059cbb00000000000000000000000000000000000000000000000000000000, recipient, ret, ptr)) {
                                          mstore(ptr, 0xf27f64e400000000000000000000000000000000000000000000000000000000)  // error ERC20TransferFailed()
                                          revert(ptr, 4)
                                      }
                                  }
                              }
                          }
                      }
                      if (ret < minReturn) revert RouterErrors.ReturnAmountIsNotEnough(ret, minReturn);
                  }
                  /**
                   * @notice Called by Curve pool during the swap operation initiated by `_curfe`.
                   * @dev This function can be called by anyone assuming there are no tokens
                   * stored on this contract between transactions.
                   * @param inCoin Address of the token to be exchanged.
                   * @param dx Amount of tokens to be exchanged.
                   */
                  function curveSwapCallback(
                      address /* sender */,
                      address /* receiver */,
                      address inCoin,
                      uint256 dx,
                      uint256 /* dy */
                  ) external {
                      IERC20(inCoin).safeTransfer(msg.sender, dx);
                  }
                  /**
                   * @notice See {IUniswapV3SwapCallback-uniswapV3SwapCallback}
                   *         Called by UniswapV3 pool during the swap operation initiated by `_unoswapV3`.
                   *         This callback function ensures the proper transfer of tokens based on the swap's
                   *         configuration. It handles the transfer of tokens by either directly transferring
                   *         the tokens from the payer to the recipient, or by using a secondary permit contract
                   *         to transfer the tokens if required by the pool. It verifies the correct pool is
                   *         calling the function and uses inline assembly for efficient execution and to access
                   *         low-level EVM features.
                   */
                  function uniswapV3SwapCallback(
                      int256 amount0Delta,
                      int256 amount1Delta,
                      bytes calldata /* data */
                  ) external override {
                      uint256 selectors = _SELECTORS;
                      assembly ("memory-safe") {  // solhint-disable-line no-inline-assembly
                          function reRevert() {
                              let ptr := mload(0x40)
                              returndatacopy(ptr, 0, returndatasize())
                              revert(ptr, returndatasize())
                          }
                          function safeERC20(target, value, mem, memLength, outLen) {
                              let status := call(gas(), target, value, mem, memLength, 0, outLen)
                              if iszero(status) {
                                  reRevert()
                              }
                              let success := or(
                                  iszero(returndatasize()),                       // empty return data
                                  and(gt(returndatasize(), 31), eq(mload(0), 1))  // true in return data
                              )
                              if iszero(success) {
                                  mstore(0, 0xf27f64e400000000000000000000000000000000000000000000000000000000)  // ERC20TransferFailed()
                                  revert(0, 4)
                              }
                          }
                          let emptyPtr := mload(0x40)
                          let resultPtr := add(emptyPtr, 0x15)  // 0x15 = _FF_FACTORY size
                          mstore(emptyPtr, selectors)
                          let amount
                          let token
                          switch sgt(amount0Delta, 0)
                          case 1 {
                              if iszero(staticcall(gas(), caller(), add(emptyPtr, _TOKEN0_SELECTOR_OFFSET), 0x4, resultPtr, 0x20)) {
                                  reRevert()
                              }
                              token := mload(resultPtr)
                              amount := amount0Delta
                          }
                          default {
                              if iszero(staticcall(gas(), caller(), add(emptyPtr, _TOKEN1_SELECTOR_OFFSET), 0x4, add(resultPtr, 0x20), 0x20)) {
                                  reRevert()
                              }
                              token := mload(add(resultPtr, 0x20))
                              amount := amount1Delta
                          }
                          let payer := calldataload(0x84)
                          let usePermit2 := calldataload(0xa4)
                          switch eq(payer, address())
                          case 1 {
                              // IERC20(token.get()).safeTransfer(msg.sender,amount)
                              mstore(add(emptyPtr, add(_TRANSFER_SELECTOR_OFFSET, 0x04)), caller())
                              mstore(add(emptyPtr, add(_TRANSFER_SELECTOR_OFFSET, 0x24)), amount)
                              safeERC20(token, 0, add(emptyPtr, _TRANSFER_SELECTOR_OFFSET), 0x44, 0x20)
                          }
                          default {
                              switch sgt(amount0Delta, 0)
                              case 1 {
                                  if iszero(staticcall(gas(), caller(), add(emptyPtr, _TOKEN1_SELECTOR_OFFSET), 0x4, add(resultPtr, 0x20), 0x20)) {
                                      reRevert()
                                  }
                              }
                              default {
                                  if iszero(staticcall(gas(), caller(), add(emptyPtr, _TOKEN0_SELECTOR_OFFSET), 0x4, resultPtr, 0x20)) {
                                      reRevert()
                                  }
                              }
                              if iszero(staticcall(gas(), caller(), add(emptyPtr, _FEE_SELECTOR_OFFSET), 0x4, add(resultPtr, 0x40), 0x20)) {
                                  reRevert()
                              }
                              mstore(emptyPtr, _FF_FACTORY)
                              mstore(resultPtr, keccak256(resultPtr, 0x60)) // Compute the inner hash in-place
                              mstore(add(resultPtr, 0x20), _POOL_INIT_CODE_HASH)
                              let pool := and(keccak256(emptyPtr, 0x55), _ADDRESS_MASK)
                              if xor(pool, caller()) {
                                  mstore(0, 0xb2c0272200000000000000000000000000000000000000000000000000000000)  // BadPool()
                                  revert(0, 4)
                              }
                              switch usePermit2
                              case 1 {
                                  // permit2.transferFrom(payer, msg.sender, amount, token);
                                  mstore(emptyPtr, selectors)
                                  emptyPtr := add(emptyPtr, _PERMIT2_TRANSFER_FROM_SELECTOR_OFFSET)
                                  mstore(add(emptyPtr, 0x04), payer)
                                  mstore(add(emptyPtr, 0x24), caller())
                                  mstore(add(emptyPtr, 0x44), amount)
                                  mstore(add(emptyPtr, 0x64), token)
                                  let success := call(gas(), _PERMIT2, 0, emptyPtr, 0x84, 0, 0)
                                  if success {
                                      success := gt(extcodesize(_PERMIT2), 0)
                                  }
                                  if iszero(success) {
                                      mstore(0, 0xc3f9d33200000000000000000000000000000000000000000000000000000000)  // Permit2TransferFromFailed()
                                      revert(0, 4)
                                  }
                              }
                              case 0 {
                                  // IERC20(token.get()).safeTransferFrom(payer, msg.sender, amount);
                                  mstore(emptyPtr, selectors)
                                  emptyPtr := add(emptyPtr, _TRANSFER_FROM_SELECTOR_OFFSET)
                                  mstore(add(emptyPtr, 0x04), payer)
                                  mstore(add(emptyPtr, 0x24), caller())
                                  mstore(add(emptyPtr, 0x44), amount)
                                  safeERC20(token, 0, emptyPtr, 0x64, 0x20)
                              }
                          }
                      }
                  }
              }
              // File contracts/AggregationRouterV6.sol
              /// @notice Main contract incorporates a number of routers to perform swaps and limit orders protocol to fill limit orders
              contract AggregationRouterV6 is EIP712("1inch Aggregation Router", "6"), Ownable, Pausable,
                  ClipperRouter, GenericRouter, UnoswapRouter, PermitAndCall, OrderMixin
              {
                  using UniERC20 for IERC20;
                  error ZeroAddress();
                  /**
                   * @dev Sets the wrapped eth token and clipper exhange interface
                   * Both values are immutable: they can only be set once during
                   * construction.
                   */
                  constructor(IWETH weth)
                      ClipperRouter(weth)
                      OrderMixin(weth)
                      Ownable(msg.sender)
                  {
                      if (address(weth) == address(0)) revert ZeroAddress();
                  }
                  /**
                   * @notice Retrieves funds accidently sent directly to the contract address
                   * @param token ERC20 token to retrieve
                   * @param amount amount to retrieve
                   */
                  function rescueFunds(IERC20 token, uint256 amount) external onlyOwner {
                      token.uniTransfer(payable(msg.sender), amount);
                  }
                  /**
                   * @notice Pauses all the trading functionality in the contract.
                   */
                  function pause() external onlyOwner {
                      _pause();
                  }
                  /**
                   * @notice Unpauses all the trading functionality in the contract.
                   */
                  function unpause() external onlyOwner {
                      _unpause();
                  }
                  function _receive() internal override(EthReceiver, OnlyWethReceiver) {
                      EthReceiver._receive();
                  }
              }
              

              File 8 of 8: Settlement
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "./IOrderMixin.sol";
              interface IAmountGetter {
                  /**
                   * @notice View method that gets called to determine the actual making amount
                   * @param order Order being processed
                   * @param extension Order extension data
                   * @param orderHash Hash of the order being processed
                   * @param taker Taker address
                   * @param takingAmount Actual taking amount
                   * @param remainingMakingAmount Order remaining making amount
                   * @param extraData Extra data
                   */
                  function getMakingAmount(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      bytes32 orderHash,
                      address taker,
                      uint256 takingAmount,
                      uint256 remainingMakingAmount,
                      bytes calldata extraData
                  ) external view returns (uint256);
                  /**
                   * @notice View method that gets called to determine the actual making amount
                   * @param order Order being processed
                   * @param extension Order extension data
                   * @param orderHash Hash of the order being processed
                   * @param taker Taker address
                   * @param makingAmount Actual taking amount
                   * @param remainingMakingAmount Order remaining making amount
                   * @param extraData Extra data
                   */
                  function getTakingAmount(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      bytes32 orderHash,
                      address taker,
                      uint256 makingAmount,
                      uint256 remainingMakingAmount,
                      bytes calldata extraData
                  ) external view returns (uint256);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "@1inch/solidity-utils/contracts/libraries/AddressLib.sol";
              import "../libraries/MakerTraitsLib.sol";
              import "../libraries/TakerTraitsLib.sol";
              interface IOrderMixin {
                  struct Order {
                      uint256 salt;
                      Address maker;
                      Address receiver;
                      Address makerAsset;
                      Address takerAsset;
                      uint256 makingAmount;
                      uint256 takingAmount;
                      MakerTraits makerTraits;
                  }
                  error InvalidatedOrder();
                  error TakingAmountExceeded();
                  error PrivateOrder();
                  error BadSignature();
                  error OrderExpired();
                  error WrongSeriesNonce();
                  error SwapWithZeroAmount();
                  error PartialFillNotAllowed();
                  error OrderIsNotSuitableForMassInvalidation();
                  error EpochManagerAndBitInvalidatorsAreIncompatible();
                  error ReentrancyDetected();
                  error PredicateIsNotTrue();
                  error TakingAmountTooHigh();
                  error MakingAmountTooLow();
                  error TransferFromMakerToTakerFailed();
                  error TransferFromTakerToMakerFailed();
                  error MismatchArraysLengths();
                  error InvalidPermit2Transfer();
                  error SimulationResults(bool success, bytes res);
                  /**
                   * @notice Emitted when order gets filled
                   * @param orderHash Hash of the order
                   * @param remainingAmount Amount of the maker asset that remains to be filled
                   */
                  event OrderFilled(
                      bytes32 orderHash,
                      uint256 remainingAmount
                  );
                  /**
                   * @notice Emitted when order without `useBitInvalidator` gets cancelled
                   * @param orderHash Hash of the order
                   */
                  event OrderCancelled(
                      bytes32 orderHash
                  );
                  /**
                   * @notice Emitted when order with `useBitInvalidator` gets cancelled
                   * @param maker Maker address
                   * @param slotIndex Slot index that was updated
                   * @param slotValue New slot value
                   */
                  event BitInvalidatorUpdated(
                      address indexed maker,
                      uint256 slotIndex,
                      uint256 slotValue
                  );
                  /**
                   * @notice Returns bitmask for double-spend invalidators based on lowest byte of order.info and filled quotes
                   * @param maker Maker address
                   * @param slot Slot number to return bitmask for
                   * @return result Each bit represents whether corresponding was already invalidated
                   */
                  function bitInvalidatorForOrder(address maker, uint256 slot) external view returns(uint256 result);
                  /**
                   * @notice Returns bitmask for double-spend invalidators based on lowest byte of order.info and filled quotes
                   * @param orderHash Hash of the order
                   * @return remaining Remaining amount of the order
                   */
                  function remainingInvalidatorForOrder(address maker, bytes32 orderHash) external view returns(uint256 remaining);
                  /**
                   * @notice Returns bitmask for double-spend invalidators based on lowest byte of order.info and filled quotes
                   * @param orderHash Hash of the order
                   * @return remainingRaw Inverse of the remaining amount of the order if order was filled at least once, otherwise 0
                   */
                  function rawRemainingInvalidatorForOrder(address maker, bytes32 orderHash) external view returns(uint256 remainingRaw);
                  /**
                   * @notice Cancels order's quote
                   * @param makerTraits Order makerTraits
                   * @param orderHash Hash of the order to cancel
                   */
                  function cancelOrder(MakerTraits makerTraits, bytes32 orderHash) external;
                  /**
                   * @notice Cancels orders' quotes
                   * @param makerTraits Orders makerTraits
                   * @param orderHashes Hashes of the orders to cancel
                   */
                  function cancelOrders(MakerTraits[] calldata makerTraits, bytes32[] calldata orderHashes) external;
                  /**
                   * @notice Cancels all quotes of the maker (works for bit-invalidating orders only)
                   * @param makerTraits Order makerTraits
                   * @param additionalMask Additional bitmask to invalidate orders
                   */
                  function bitsInvalidateForOrder(MakerTraits makerTraits, uint256 additionalMask) external;
                  /**
                   * @notice Returns order hash, hashed with limit order protocol contract EIP712
                   * @param order Order
                   * @return orderHash Hash of the order
                   */
                  function hashOrder(IOrderMixin.Order calldata order) external view returns(bytes32 orderHash);
                  /**
                   * @notice Delegates execution to custom implementation. Could be used to validate if `transferFrom` works properly
                   * @dev The function always reverts and returns the simulation results in revert data.
                   * @param target Addresses that will be delegated
                   * @param data Data that will be passed to delegatee
                   */
                  function simulate(address target, bytes calldata data) external;
                  /**
                   * @notice Fills order's quote, fully or partially (whichever is possible).
                   * @param order Order quote to fill
                   * @param r R component of signature
                   * @param vs VS component of signature
                   * @param amount Taker amount to fill
                   * @param takerTraits Specifies threshold as maximum allowed takingAmount when takingAmount is zero, otherwise specifies
                   * minimum allowed makingAmount. The 2nd (0 based index) highest bit specifies whether taker wants to skip maker's permit.
                   * @return makingAmount Actual amount transferred from maker to taker
                   * @return takingAmount Actual amount transferred from taker to maker
                   * @return orderHash Hash of the filled order
                   */
                  function fillOrder(
                      Order calldata order,
                      bytes32 r,
                      bytes32 vs,
                      uint256 amount,
                      TakerTraits takerTraits
                  ) external payable returns(uint256 makingAmount, uint256 takingAmount, bytes32 orderHash);
                  /**
                   * @notice Same as `fillOrder` but allows to specify arguments that are used by the taker.
                   * @param order Order quote to fill
                   * @param r R component of signature
                   * @param vs VS component of signature
                   * @param amount Taker amount to fill
                   * @param takerTraits Specifies threshold as maximum allowed takingAmount when takingAmount is zero, otherwise specifies
                   * minimum allowed makingAmount. The 2nd (0 based index) highest bit specifies whether taker wants to skip maker's permit.
                   * @param args Arguments that are used by the taker (target, extension, interaction, permit)
                   * @return makingAmount Actual amount transferred from maker to taker
                   * @return takingAmount Actual amount transferred from taker to maker
                   * @return orderHash Hash of the filled order
                   */
                  function fillOrderArgs(
                      IOrderMixin.Order calldata order,
                      bytes32 r,
                      bytes32 vs,
                      uint256 amount,
                      TakerTraits takerTraits,
                      bytes calldata args
                  ) external payable returns(uint256 makingAmount, uint256 takingAmount, bytes32 orderHash);
                  /**
                   * @notice Same as `fillOrder` but uses contract-based signatures.
                   * @param order Order quote to fill
                   * @param signature Signature to confirm quote ownership
                   * @param amount Taker amount to fill
                   * @param takerTraits Specifies threshold as maximum allowed takingAmount when takingAmount is zero, otherwise specifies
                   * minimum allowed makingAmount. The 2nd (0 based index) highest bit specifies whether taker wants to skip maker's permit.
                   * @return makingAmount Actual amount transferred from maker to taker
                   * @return takingAmount Actual amount transferred from taker to maker
                   * @return orderHash Hash of the filled order
                   * @dev See tests for examples
                   */
                  function fillContractOrder(
                      Order calldata order,
                      bytes calldata signature,
                      uint256 amount,
                      TakerTraits takerTraits
                  ) external returns(uint256 makingAmount, uint256 takingAmount, bytes32 orderHash);
                  /**
                   * @notice Same as `fillContractOrder` but allows to specify arguments that are used by the taker.
                   * @param order Order quote to fill
                   * @param signature Signature to confirm quote ownership
                   * @param amount Taker amount to fill
                   * @param takerTraits Specifies threshold as maximum allowed takingAmount when takingAmount is zero, otherwise specifies
                   * minimum allowed makingAmount. The 2nd (0 based index) highest bit specifies whether taker wants to skip maker's permit.
                   * @param args Arguments that are used by the taker (target, extension, interaction, permit)
                   * @return makingAmount Actual amount transferred from maker to taker
                   * @return takingAmount Actual amount transferred from taker to maker
                   * @return orderHash Hash of the filled order
                   * @dev See tests for examples
                   */
                  function fillContractOrderArgs(
                      Order calldata order,
                      bytes calldata signature,
                      uint256 amount,
                      TakerTraits takerTraits,
                      bytes calldata args
                  ) external returns(uint256 makingAmount, uint256 takingAmount, bytes32 orderHash);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "./IOrderMixin.sol";
              interface IPostInteraction {
                  /**
                   * @notice Callback method that gets called after all fund transfers
                   * @param order Order being processed
                   * @param extension Order extension data
                   * @param orderHash Hash of the order being processed
                   * @param taker Taker address
                   * @param makingAmount Actual making amount
                   * @param takingAmount Actual taking amount
                   * @param remainingMakingAmount Order remaining making amount
                   * @param extraData Extra data
                   */
                  function postInteraction(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      bytes32 orderHash,
                      address taker,
                      uint256 makingAmount,
                      uint256 takingAmount,
                      uint256 remainingMakingAmount,
                      bytes calldata extraData
                  ) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "./IOrderMixin.sol";
              interface IPreInteraction {
                  /**
                   * @notice Callback method that gets called before any funds transfers
                   * @param order Order being processed
                   * @param extension Order extension data
                   * @param orderHash Hash of the order being processed
                   * @param taker Taker address
                   * @param makingAmount Actual making amount
                   * @param takingAmount Actual taking amount
                   * @param remainingMakingAmount Order remaining making amount
                   * @param extraData Extra data
                   */
                  function preInteraction(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      bytes32 orderHash,
                      address taker,
                      uint256 makingAmount,
                      uint256 takingAmount,
                      uint256 remainingMakingAmount,
                      bytes calldata extraData
                  ) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              type MakerTraits is uint256;
              /**
               * @title MakerTraitsLib
               * @notice A library to manage and check MakerTraits, which are used to encode the maker's preferences for an order in a single uint256.
               * @dev
               * The MakerTraits type is a uint256 and different parts of the number are used to encode different traits.
               * High bits are used for flags
               * 255 bit `NO_PARTIAL_FILLS_FLAG`          - if set, the order does not allow partial fills
               * 254 bit `ALLOW_MULTIPLE_FILLS_FLAG`      - if set, the order permits multiple fills
               * 253 bit                                  - unused
               * 252 bit `PRE_INTERACTION_CALL_FLAG`      - if set, the order requires pre-interaction call
               * 251 bit `POST_INTERACTION_CALL_FLAG`     - if set, the order requires post-interaction call
               * 250 bit `NEED_CHECK_EPOCH_MANAGER_FLAG`  - if set, the order requires to check the epoch manager
               * 249 bit `HAS_EXTENSION_FLAG`             - if set, the order has extension(s)
               * 248 bit `USE_PERMIT2_FLAG`               - if set, the order uses permit2
               * 247 bit `UNWRAP_WETH_FLAG`               - if set, the order requires to unwrap WETH
               * Low 200 bits are used for allowed sender, expiration, nonceOrEpoch, and series
               * uint80 last 10 bytes of allowed sender address (0 if any)
               * uint40 expiration timestamp (0 if none)
               * uint40 nonce or epoch
               * uint40 series
               */
              library MakerTraitsLib {
                  // Low 200 bits are used for allowed sender, expiration, nonceOrEpoch, and series
                  uint256 private constant _ALLOWED_SENDER_MASK = type(uint80).max;
                  uint256 private constant _EXPIRATION_OFFSET = 80;
                  uint256 private constant _EXPIRATION_MASK = type(uint40).max;
                  uint256 private constant _NONCE_OR_EPOCH_OFFSET = 120;
                  uint256 private constant _NONCE_OR_EPOCH_MASK = type(uint40).max;
                  uint256 private constant _SERIES_OFFSET = 160;
                  uint256 private constant _SERIES_MASK = type(uint40).max;
                  uint256 private constant _NO_PARTIAL_FILLS_FLAG = 1 << 255;
                  uint256 private constant _ALLOW_MULTIPLE_FILLS_FLAG = 1 << 254;
                  uint256 private constant _PRE_INTERACTION_CALL_FLAG = 1 << 252;
                  uint256 private constant _POST_INTERACTION_CALL_FLAG = 1 << 251;
                  uint256 private constant _NEED_CHECK_EPOCH_MANAGER_FLAG = 1 << 250;
                  uint256 private constant _HAS_EXTENSION_FLAG = 1 << 249;
                  uint256 private constant _USE_PERMIT2_FLAG = 1 << 248;
                  uint256 private constant _UNWRAP_WETH_FLAG = 1 << 247;
                  /**
                   * @notice Checks if the order has the extension flag set.
                   * @dev If the `HAS_EXTENSION_FLAG` is set in the makerTraits, then the protocol expects that the order has extension(s).
                   * @param makerTraits The traits of the maker.
                   * @return result A boolean indicating whether the flag is set.
                   */
                  function hasExtension(MakerTraits makerTraits) internal pure returns (bool) {
                      return (MakerTraits.unwrap(makerTraits) & _HAS_EXTENSION_FLAG) != 0;
                  }
                  /**
                   * @notice Checks if the maker allows a specific taker to fill the order.
                   * @param makerTraits The traits of the maker.
                   * @param sender The address of the taker to be checked.
                   * @return result A boolean indicating whether the taker is allowed.
                   */
                  function isAllowedSender(MakerTraits makerTraits, address sender) internal pure returns (bool) {
                      uint160 allowedSender = uint160(MakerTraits.unwrap(makerTraits) & _ALLOWED_SENDER_MASK);
                      return allowedSender == 0 || allowedSender == uint160(sender) & _ALLOWED_SENDER_MASK;
                  }
                  /**
                   * @notice Checks if the order has expired.
                   * @param makerTraits The traits of the maker.
                   * @return result A boolean indicating whether the order has expired.
                   */
                  function isExpired(MakerTraits makerTraits) internal view returns (bool) {
                      uint256 expiration = (MakerTraits.unwrap(makerTraits) >> _EXPIRATION_OFFSET) & _EXPIRATION_MASK;
                      return expiration != 0 && expiration < block.timestamp;  // solhint-disable-line not-rely-on-time
                  }
                  /**
                   * @notice Returns the nonce or epoch of the order.
                   * @param makerTraits The traits of the maker.
                   * @return result The nonce or epoch of the order.
                   */
                  function nonceOrEpoch(MakerTraits makerTraits) internal pure returns (uint256) {
                      return (MakerTraits.unwrap(makerTraits) >> _NONCE_OR_EPOCH_OFFSET) & _NONCE_OR_EPOCH_MASK;
                  }
                  /**
                   * @notice Returns the series of the order.
                   * @param makerTraits The traits of the maker.
                   * @return result The series of the order.
                   */
                  function series(MakerTraits makerTraits) internal pure returns (uint256) {
                      return (MakerTraits.unwrap(makerTraits) >> _SERIES_OFFSET) & _SERIES_MASK;
                  }
                  /**
                    * @notice Determines if the order allows partial fills.
                    * @dev If the _NO_PARTIAL_FILLS_FLAG is not set in the makerTraits, then the order allows partial fills.
                    * @param makerTraits The traits of the maker, determining their preferences for the order.
                    * @return result A boolean indicating whether the maker allows partial fills.
                    */
                  function allowPartialFills(MakerTraits makerTraits) internal pure returns (bool) {
                      return (MakerTraits.unwrap(makerTraits) & _NO_PARTIAL_FILLS_FLAG) == 0;
                  }
                  /**
                   * @notice Checks if the maker needs pre-interaction call.
                   * @param makerTraits The traits of the maker.
                   * @return result A boolean indicating whether the maker needs a pre-interaction call.
                   */
                  function needPreInteractionCall(MakerTraits makerTraits) internal pure returns (bool) {
                      return (MakerTraits.unwrap(makerTraits) & _PRE_INTERACTION_CALL_FLAG) != 0;
                  }
                  /**
                   * @notice Checks if the maker needs post-interaction call.
                   * @param makerTraits The traits of the maker.
                   * @return result A boolean indicating whether the maker needs a post-interaction call.
                   */
                  function needPostInteractionCall(MakerTraits makerTraits) internal pure returns (bool) {
                      return (MakerTraits.unwrap(makerTraits) & _POST_INTERACTION_CALL_FLAG) != 0;
                  }
                  /**
                    * @notice Determines if the order allows multiple fills.
                    * @dev If the _ALLOW_MULTIPLE_FILLS_FLAG is set in the makerTraits, then the maker allows multiple fills.
                    * @param makerTraits The traits of the maker, determining their preferences for the order.
                    * @return result A boolean indicating whether the maker allows multiple fills.
                    */
                  function allowMultipleFills(MakerTraits makerTraits) internal pure returns (bool) {
                      return (MakerTraits.unwrap(makerTraits) & _ALLOW_MULTIPLE_FILLS_FLAG) != 0;
                  }
                  /**
                    * @notice Determines if an order should use the bit invalidator or remaining amount validator.
                    * @dev The bit invalidator can be used if the order does not allow partial or multiple fills.
                    * @param makerTraits The traits of the maker, determining their preferences for the order.
                    * @return result A boolean indicating whether the bit invalidator should be used.
                    * True if the order requires the use of the bit invalidator.
                    */
                  function useBitInvalidator(MakerTraits makerTraits) internal pure returns (bool) {
                      return !allowPartialFills(makerTraits) || !allowMultipleFills(makerTraits);
                  }
                  /**
                   * @notice Checks if the maker needs to check the epoch.
                   * @param makerTraits The traits of the maker.
                   * @return result A boolean indicating whether the maker needs to check the epoch manager.
                   */
                  function needCheckEpochManager(MakerTraits makerTraits) internal pure returns (bool) {
                      return (MakerTraits.unwrap(makerTraits) & _NEED_CHECK_EPOCH_MANAGER_FLAG) != 0;
                  }
                  /**
                   * @notice Checks if the maker uses permit2.
                   * @param makerTraits The traits of the maker.
                   * @return result A boolean indicating whether the maker uses permit2.
                   */
                  function usePermit2(MakerTraits makerTraits) internal pure returns (bool) {
                      return MakerTraits.unwrap(makerTraits) & _USE_PERMIT2_FLAG != 0;
                  }
                  /**
                   * @notice Checks if the maker needs to unwraps WETH.
                   * @param makerTraits The traits of the maker.
                   * @return result A boolean indicating whether the maker needs to unwrap WETH.
                   */
                  function unwrapWeth(MakerTraits makerTraits) internal pure returns (bool) {
                      return MakerTraits.unwrap(makerTraits) & _UNWRAP_WETH_FLAG != 0;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              type TakerTraits is uint256;
              /**
               * @title TakerTraitsLib
               * @notice This library to manage and check TakerTraits, which are used to encode the taker's preferences for an order in a single uint256.
               * @dev The TakerTraits are structured as follows:
               * High bits are used for flags
               * 255 bit `_MAKER_AMOUNT_FLAG`           - If set, the taking amount is calculated based on making amount, otherwise making amount is calculated based on taking amount.
               * 254 bit `_UNWRAP_WETH_FLAG`            - If set, the WETH will be unwrapped into ETH before sending to taker.
               * 253 bit `_SKIP_ORDER_PERMIT_FLAG`      - If set, the order skips maker's permit execution.
               * 252 bit `_USE_PERMIT2_FLAG`            - If set, the order uses the permit2 function for authorization.
               * 251 bit `_ARGS_HAS_TARGET`             - If set, then first 20 bytes of args are treated as target address for maker’s funds transfer.
               * 224-247 bits `ARGS_EXTENSION_LENGTH`   - The length of the extension calldata in the args.
               * 200-223 bits `ARGS_INTERACTION_LENGTH` - The length of the interaction calldata in the args.
               * 0-184 bits                             - The threshold amount (the maximum amount a taker agrees to give in exchange for a making amount).
               */
              library TakerTraitsLib {
                  uint256 private constant _MAKER_AMOUNT_FLAG = 1 << 255;
                  uint256 private constant _UNWRAP_WETH_FLAG = 1 << 254;
                  uint256 private constant _SKIP_ORDER_PERMIT_FLAG = 1 << 253;
                  uint256 private constant _USE_PERMIT2_FLAG = 1 << 252;
                  uint256 private constant _ARGS_HAS_TARGET = 1 << 251;
                  uint256 private constant _ARGS_EXTENSION_LENGTH_OFFSET = 224;
                  uint256 private constant _ARGS_EXTENSION_LENGTH_MASK = 0xffffff;
                  uint256 private constant _ARGS_INTERACTION_LENGTH_OFFSET = 200;
                  uint256 private constant _ARGS_INTERACTION_LENGTH_MASK = 0xffffff;
                  uint256 private constant _AMOUNT_MASK = 0x000000000000000000ffffffffffffffffffffffffffffffffffffffffffffff;
                  /**
                   * @notice Checks if the args should contain target address.
                   * @param takerTraits The traits of the taker.
                   * @return result A boolean indicating whether the args should contain target address.
                   */
                  function argsHasTarget(TakerTraits takerTraits) internal pure returns (bool) {
                      return (TakerTraits.unwrap(takerTraits) & _ARGS_HAS_TARGET) != 0;
                  }
                  /**
                   * @notice Retrieves the length of the extension calldata from the takerTraits.
                   * @param takerTraits The traits of the taker.
                   * @return result The length of the extension calldata encoded in the takerTraits.
                   */
                  function argsExtensionLength(TakerTraits takerTraits) internal pure returns (uint256) {
                      return (TakerTraits.unwrap(takerTraits) >> _ARGS_EXTENSION_LENGTH_OFFSET) & _ARGS_EXTENSION_LENGTH_MASK;
                  }
                  /**
                   * @notice Retrieves the length of the interaction calldata from the takerTraits.
                   * @param takerTraits The traits of the taker.
                   * @return result The length of the interaction calldata encoded in the takerTraits.
                   */
                  function argsInteractionLength(TakerTraits takerTraits) internal pure returns (uint256) {
                      return (TakerTraits.unwrap(takerTraits) >> _ARGS_INTERACTION_LENGTH_OFFSET) & _ARGS_INTERACTION_LENGTH_MASK;
                  }
                  /**
                   * @notice Checks if the taking amount should be calculated based on making amount.
                   * @param takerTraits The traits of the taker.
                   * @return result A boolean indicating whether the taking amount should be calculated based on making amount.
                   */
                  function isMakingAmount(TakerTraits takerTraits) internal pure returns (bool) {
                      return (TakerTraits.unwrap(takerTraits) & _MAKER_AMOUNT_FLAG) != 0;
                  }
                  /**
                   * @notice Checks if the order should unwrap WETH and send ETH to taker.
                   * @param takerTraits The traits of the taker.
                   * @return result A boolean indicating whether the order should unwrap WETH.
                   */
                  function unwrapWeth(TakerTraits takerTraits) internal pure returns (bool) {
                      return (TakerTraits.unwrap(takerTraits) & _UNWRAP_WETH_FLAG) != 0;
                  }
                  /**
                   * @notice Checks if the order should skip maker's permit execution.
                   * @param takerTraits The traits of the taker.
                   * @return result A boolean indicating whether the order don't apply permit.
                   */
                  function skipMakerPermit(TakerTraits takerTraits) internal pure returns (bool) {
                      return (TakerTraits.unwrap(takerTraits) & _SKIP_ORDER_PERMIT_FLAG) != 0;
                  }
                  /**
                   * @notice Checks if the order uses the permit2 instead of permit.
                   * @param takerTraits The traits of the taker.
                   * @return result A boolean indicating whether the order uses the permit2.
                   */
                  function usePermit2(TakerTraits takerTraits) internal pure returns (bool) {
                      return (TakerTraits.unwrap(takerTraits) & _USE_PERMIT2_FLAG) != 0;
                  }
                  /**
                   * @notice Retrieves the threshold amount from the takerTraits.
                   * The maximum amount a taker agrees to give in exchange for a making amount.
                   * @param takerTraits The traits of the taker.
                   * @return result The threshold amount encoded in the takerTraits.
                   */
                  function threshold(TakerTraits takerTraits) internal pure returns (uint256) {
                      return TakerTraits.unwrap(takerTraits) & _AMOUNT_MASK;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              interface IDaiLikePermit {
                  function permit(
                      address holder,
                      address spender,
                      uint256 nonce,
                      uint256 expiry,
                      bool allowed,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              interface IERC20MetadataUppercase {
                  function NAME() external view returns (string memory); // solhint-disable-line func-name-mixedcase
                  function SYMBOL() external view returns (string memory); // solhint-disable-line func-name-mixedcase
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              interface IPermit2 {
                  struct PermitDetails {
                      // ERC20 token address
                      address token;
                      // the maximum amount allowed to spend
                      uint160 amount;
                      // timestamp at which a spender's token allowances become invalid
                      uint48 expiration;
                      // an incrementing value indexed per owner,token,and spender for each signature
                      uint48 nonce;
                  }
                  /// @notice The permit message signed for a single token allownce
                  struct PermitSingle {
                      // the permit data for a single token alownce
                      PermitDetails details;
                      // address permissioned on the allowed tokens
                      address spender;
                      // deadline on the permit signature
                      uint256 sigDeadline;
                  }
                  /// @notice Packed allowance
                  struct PackedAllowance {
                      // amount allowed
                      uint160 amount;
                      // permission expiry
                      uint48 expiration;
                      // an incrementing value indexed per owner,token,and spender for each signature
                      uint48 nonce;
                  }
                  function transferFrom(address user, address spender, uint160 amount, address token) external;
                  function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;
                  function allowance(address user, address token, address spender) external view returns (PackedAllowance memory);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              interface IWETH is IERC20 {
                  event Deposit(address indexed dst, uint256 wad);
                  event Withdrawal(address indexed src, uint256 wad);
                  function deposit() external payable;
                  function withdraw(uint256 amount) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              type Address is uint256;
              /**
              * @dev Library for working with addresses encoded as uint256 values, which can include flags in the highest bits.
              */
              library AddressLib {
                  uint256 private constant _LOW_160_BIT_MASK = (1 << 160) - 1;
                  /**
                  * @notice Returns the address representation of a uint256.
                  * @param a The uint256 value to convert to an address.
                  * @return The address representation of the provided uint256 value.
                  */
                  function get(Address a) internal pure returns (address) {
                      return address(uint160(Address.unwrap(a) & _LOW_160_BIT_MASK));
                  }
                  /**
                  * @notice Checks if a given flag is set for the provided address.
                  * @param a The address to check for the flag.
                  * @param flag The flag to check for in the provided address.
                  * @return True if the provided flag is set in the address, false otherwise.
                  */
                  function getFlag(Address a, uint256 flag) internal pure returns (bool) {
                      return (Address.unwrap(a) & flag) != 0;
                  }
                  /**
                  * @notice Returns a uint32 value stored at a specific bit offset in the provided address.
                  * @param a The address containing the uint32 value.
                  * @param offset The bit offset at which the uint32 value is stored.
                  * @return The uint32 value stored in the address at the specified bit offset.
                  */
                  function getUint32(Address a, uint256 offset) internal pure returns (uint32) {
                      return uint32(Address.unwrap(a) >> offset);
                  }
                  /**
                  * @notice Returns a uint64 value stored at a specific bit offset in the provided address.
                  * @param a The address containing the uint64 value.
                  * @param offset The bit offset at which the uint64 value is stored.
                  * @return The uint64 value stored in the address at the specified bit offset.
                  */
                  function getUint64(Address a, uint256 offset) internal pure returns (uint64) {
                      return uint64(Address.unwrap(a) >> offset);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /// @title Revert reason forwarder.
              library RevertReasonForwarder {
                  /// @dev Forwards latest externall call revert.
                  function reRevert() internal pure {
                      // bubble up revert reason from latest external call
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let ptr := mload(0x40)
                          returndatacopy(ptr, 0, returndatasize())
                          revert(ptr, returndatasize())
                      }
                  }
                  /// @dev Returns latest external call revert reason.
                  function reReason() internal pure returns (bytes memory reason) {
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          reason := mload(0x40)
                          let length := returndatasize()
                          mstore(reason, length)
                          returndatacopy(add(reason, 0x20), 0, length)
                          mstore(0x40, add(reason, add(0x20, length)))
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
              import "../interfaces/IDaiLikePermit.sol";
              import "../interfaces/IPermit2.sol";
              import "../interfaces/IWETH.sol";
              import "../libraries/RevertReasonForwarder.sol";
              /**
               * @title Implements efficient safe methods for ERC20 interface.
               * @notice Compared to the standard ERC20, this implementation offers several enhancements:
               * 1. more gas-efficient, providing significant savings in transaction costs.
               * 2. support for different permit implementations
               * 3. forceApprove functionality
               * 4. support for WETH deposit and withdraw
               */
              library SafeERC20 {
                  error SafeTransferFailed();
                  error SafeTransferFromFailed();
                  error ForceApproveFailed();
                  error SafeIncreaseAllowanceFailed();
                  error SafeDecreaseAllowanceFailed();
                  error SafePermitBadLength();
                  error Permit2TransferAmountTooHigh();
                  // Uniswap Permit2 address
                  address private constant _PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
                  bytes4 private constant _PERMIT_LENGTH_ERROR = 0x68275857;  // SafePermitBadLength.selector
                  uint256 private constant _RAW_CALL_GAS_LIMIT = 5000;
                  /**
                   * @notice Fetches the balance of a specific ERC20 token held by an account.
                   * Consumes less gas then regular `ERC20.balanceOf`.
                   * @dev Note that the implementation does not perform dirty bits cleaning, so it is the
                   * responsibility of the caller to make sure that the higher 96 bits of the `account` parameter are clean.
                   * @param token The IERC20 token contract for which the balance will be fetched.
                   * @param account The address of the account whose token balance will be fetched.
                   * @return tokenBalance The balance of the specified ERC20 token held by the account.
                   */
                  function safeBalanceOf(
                      IERC20 token,
                      address account
                  ) internal view returns(uint256 tokenBalance) {
                      bytes4 selector = IERC20.balanceOf.selector;
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          mstore(0x00, selector)
                          mstore(0x04, account)
                          let success := staticcall(gas(), token, 0x00, 0x24, 0x00, 0x20)
                          tokenBalance := mload(0)
                          if or(iszero(success), lt(returndatasize(), 0x20)) {
                              let ptr := mload(0x40)
                              returndatacopy(ptr, 0, returndatasize())
                              revert(ptr, returndatasize())
                          }
                      }
                  }
                  /**
                   * @notice Attempts to safely transfer tokens from one address to another.
                   * @dev If permit2 is true, uses the Permit2 standard; otherwise uses the standard ERC20 transferFrom.
                   * Either requires `true` in return data, or requires target to be smart-contract and empty return data.
                   * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
                   * the caller to make sure that the higher 96 bits of the `from` and `to` parameters are clean.
                   * @param token The IERC20 token contract from which the tokens will be transferred.
                   * @param from The address from which the tokens will be transferred.
                   * @param to The address to which the tokens will be transferred.
                   * @param amount The amount of tokens to transfer.
                   * @param permit2 If true, uses the Permit2 standard for the transfer; otherwise uses the standard ERC20 transferFrom.
                   */
                  function safeTransferFromUniversal(
                      IERC20 token,
                      address from,
                      address to,
                      uint256 amount,
                      bool permit2
                  ) internal {
                      if (permit2) {
                          safeTransferFromPermit2(token, from, to, amount);
                      } else {
                          safeTransferFrom(token, from, to, amount);
                      }
                  }
                  /**
                   * @notice Attempts to safely transfer tokens from one address to another using the ERC20 standard.
                   * @dev Either requires `true` in return data, or requires target to be smart-contract and empty return data.
                   * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
                   * the caller to make sure that the higher 96 bits of the `from` and `to` parameters are clean.
                   * @param token The IERC20 token contract from which the tokens will be transferred.
                   * @param from The address from which the tokens will be transferred.
                   * @param to The address to which the tokens will be transferred.
                   * @param amount The amount of tokens to transfer.
                   */
                  function safeTransferFrom(
                      IERC20 token,
                      address from,
                      address to,
                      uint256 amount
                  ) internal {
                      bytes4 selector = token.transferFrom.selector;
                      bool success;
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let data := mload(0x40)
                          mstore(data, selector)
                          mstore(add(data, 0x04), from)
                          mstore(add(data, 0x24), to)
                          mstore(add(data, 0x44), amount)
                          success := call(gas(), token, 0, data, 100, 0x0, 0x20)
                          if success {
                              switch returndatasize()
                              case 0 {
                                  success := gt(extcodesize(token), 0)
                              }
                              default {
                                  success := and(gt(returndatasize(), 31), eq(mload(0), 1))
                              }
                          }
                      }
                      if (!success) revert SafeTransferFromFailed();
                  }
                  /**
                   * @notice Attempts to safely transfer tokens from one address to another using the Permit2 standard.
                   * @dev Either requires `true` in return data, or requires target to be smart-contract and empty return data.
                   * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
                   * the caller to make sure that the higher 96 bits of the `from` and `to` parameters are clean.
                   * @param token The IERC20 token contract from which the tokens will be transferred.
                   * @param from The address from which the tokens will be transferred.
                   * @param to The address to which the tokens will be transferred.
                   * @param amount The amount of tokens to transfer.
                   */
                  function safeTransferFromPermit2(
                      IERC20 token,
                      address from,
                      address to,
                      uint256 amount
                  ) internal {
                      if (amount > type(uint160).max) revert Permit2TransferAmountTooHigh();
                      bytes4 selector = IPermit2.transferFrom.selector;
                      bool success;
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let data := mload(0x40)
                          mstore(data, selector)
                          mstore(add(data, 0x04), from)
                          mstore(add(data, 0x24), to)
                          mstore(add(data, 0x44), amount)
                          mstore(add(data, 0x64), token)
                          success := call(gas(), _PERMIT2, 0, data, 0x84, 0x0, 0x0)
                          if success {
                              success := gt(extcodesize(_PERMIT2), 0)
                          }
                      }
                      if (!success) revert SafeTransferFromFailed();
                  }
                  /**
                   * @notice Attempts to safely transfer tokens to another address.
                   * @dev Either requires `true` in return data, or requires target to be smart-contract and empty return data.
                   * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
                   * the caller to make sure that the higher 96 bits of the `to` parameter are clean.
                   * @param token The IERC20 token contract from which the tokens will be transferred.
                   * @param to The address to which the tokens will be transferred.
                   * @param value The amount of tokens to transfer.
                   */
                  function safeTransfer(
                      IERC20 token,
                      address to,
                      uint256 value
                  ) internal {
                      if (!_makeCall(token, token.transfer.selector, to, value)) {
                          revert SafeTransferFailed();
                      }
                  }
                  /**
                   * @notice Attempts to approve a spender to spend a certain amount of tokens.
                   * @dev If `approve(from, to, amount)` fails, it tries to set the allowance to zero, and retries the `approve` call.
                   * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
                   * the caller to make sure that the higher 96 bits of the `spender` parameter are clean.
                   * @param token The IERC20 token contract on which the call will be made.
                   * @param spender The address which will spend the funds.
                   * @param value The amount of tokens to be spent.
                   */
                  function forceApprove(
                      IERC20 token,
                      address spender,
                      uint256 value
                  ) internal {
                      if (!_makeCall(token, token.approve.selector, spender, value)) {
                          if (
                              !_makeCall(token, token.approve.selector, spender, 0) ||
                              !_makeCall(token, token.approve.selector, spender, value)
                          ) {
                              revert ForceApproveFailed();
                          }
                      }
                  }
                  /**
                   * @notice Safely increases the allowance of a spender.
                   * @dev Increases with safe math check. Checks if the increased allowance will overflow, if yes, then it reverts the transaction.
                   * Then uses `forceApprove` to increase the allowance.
                   * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
                   * the caller to make sure that the higher 96 bits of the `spender` parameter are clean.
                   * @param token The IERC20 token contract on which the call will be made.
                   * @param spender The address which will spend the funds.
                   * @param value The amount of tokens to increase the allowance by.
                   */
                  function safeIncreaseAllowance(
                      IERC20 token,
                      address spender,
                      uint256 value
                  ) internal {
                      uint256 allowance = token.allowance(address(this), spender);
                      if (value > type(uint256).max - allowance) revert SafeIncreaseAllowanceFailed();
                      forceApprove(token, spender, allowance + value);
                  }
                  /**
                   * @notice Safely decreases the allowance of a spender.
                   * @dev Decreases with safe math check. Checks if the decreased allowance will underflow, if yes, then it reverts the transaction.
                   * Then uses `forceApprove` to increase the allowance.
                   * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
                   * the caller to make sure that the higher 96 bits of the `spender` parameter are clean.
                   * @param token The IERC20 token contract on which the call will be made.
                   * @param spender The address which will spend the funds.
                   * @param value The amount of tokens to decrease the allowance by.
                   */
                  function safeDecreaseAllowance(
                      IERC20 token,
                      address spender,
                      uint256 value
                  ) internal {
                      uint256 allowance = token.allowance(address(this), spender);
                      if (value > allowance) revert SafeDecreaseAllowanceFailed();
                      forceApprove(token, spender, allowance - value);
                  }
                  /**
                   * @notice Attempts to execute the `permit` function on the provided token with the sender and contract as parameters.
                   * Permit type is determined automatically based on permit calldata (IERC20Permit, IDaiLikePermit, and IPermit2).
                   * @dev Wraps `tryPermit` function and forwards revert reason if permit fails.
                   * @param token The IERC20 token to execute the permit function on.
                   * @param permit The permit data to be used in the function call.
                   */
                  function safePermit(IERC20 token, bytes calldata permit) internal {
                      if (!tryPermit(token, msg.sender, address(this), permit)) RevertReasonForwarder.reRevert();
                  }
                  /**
                   * @notice Attempts to execute the `permit` function on the provided token with custom owner and spender parameters.
                   * Permit type is determined automatically based on permit calldata (IERC20Permit, IDaiLikePermit, and IPermit2).
                   * @dev Wraps `tryPermit` function and forwards revert reason if permit fails.
                   * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
                   * the caller to make sure that the higher 96 bits of the `owner` and `spender` parameters are clean.
                   * @param token The IERC20 token to execute the permit function on.
                   * @param owner The owner of the tokens for which the permit is made.
                   * @param spender The spender allowed to spend the tokens by the permit.
                   * @param permit The permit data to be used in the function call.
                   */
                  function safePermit(IERC20 token, address owner, address spender, bytes calldata permit) internal {
                      if (!tryPermit(token, owner, spender, permit)) RevertReasonForwarder.reRevert();
                  }
                  /**
                   * @notice Attempts to execute the `permit` function on the provided token with the sender and contract as parameters.
                   * @dev Invokes `tryPermit` with sender as owner and contract as spender.
                   * @param token The IERC20 token to execute the permit function on.
                   * @param permit The permit data to be used in the function call.
                   * @return success Returns true if the permit function was successfully executed, false otherwise.
                   */
                  function tryPermit(IERC20 token, bytes calldata permit) internal returns(bool success) {
                      return tryPermit(token, msg.sender, address(this), permit);
                  }
                  /**
                   * @notice The function attempts to call the permit function on a given ERC20 token.
                   * @dev The function is designed to support a variety of permit functions, namely: IERC20Permit, IDaiLikePermit, and IPermit2.
                   * It accommodates both Compact and Full formats of these permit types.
                   * Please note, it is expected that the `expiration` parameter for the compact Permit2 and the `deadline` parameter
                   * for the compact Permit are to be incremented by one before invoking this function. This approach is motivated by
                   * gas efficiency considerations; as the unlimited expiration period is likely to be the most common scenario, and
                   * zeros are cheaper to pass in terms of gas cost. Thus, callers should increment the expiration or deadline by one
                   * before invocation for optimized performance.
                   * Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
                   * the caller to make sure that the higher 96 bits of the `owner` and `spender` parameters are clean.
                   * @param token The address of the ERC20 token on which to call the permit function.
                   * @param owner The owner of the tokens. This address should have signed the off-chain permit.
                   * @param spender The address which will be approved for transfer of tokens.
                   * @param permit The off-chain permit data, containing different fields depending on the type of permit function.
                   * @return success A boolean indicating whether the permit call was successful.
                   */
                  function tryPermit(IERC20 token, address owner, address spender, bytes calldata permit) internal returns(bool success) {
                      // load function selectors for different permit standards
                      bytes4 permitSelector = IERC20Permit.permit.selector;
                      bytes4 daiPermitSelector = IDaiLikePermit.permit.selector;
                      bytes4 permit2Selector = IPermit2.permit.selector;
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let ptr := mload(0x40)
                          // Switch case for different permit lengths, indicating different permit standards
                          switch permit.length
                          // Compact IERC20Permit
                          case 100 {
                              mstore(ptr, permitSelector)     // store selector
                              mstore(add(ptr, 0x04), owner)   // store owner
                              mstore(add(ptr, 0x24), spender) // store spender
                              // Compact IERC20Permit.permit(uint256 value, uint32 deadline, uint256 r, uint256 vs)
                              {  // stack too deep
                                  let deadline := shr(224, calldataload(add(permit.offset, 0x20))) // loads permit.offset 0x20..0x23
                                  let vs := calldataload(add(permit.offset, 0x44))                 // loads permit.offset 0x44..0x63
                                  calldatacopy(add(ptr, 0x44), permit.offset, 0x20)            // store value     = copy permit.offset 0x00..0x19
                                  mstore(add(ptr, 0x64), sub(deadline, 1))                     // store deadline  = deadline - 1
                                  mstore(add(ptr, 0x84), add(27, shr(255, vs)))                // store v         = most significant bit of vs + 27 (27 or 28)
                                  calldatacopy(add(ptr, 0xa4), add(permit.offset, 0x24), 0x20) // store r         = copy permit.offset 0x24..0x43
                                  mstore(add(ptr, 0xc4), shr(1, shl(1, vs)))                   // store s         = vs without most significant bit
                              }
                              // IERC20Permit.permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
                              success := call(gas(), token, 0, ptr, 0xe4, 0, 0)
                          }
                          // Compact IDaiLikePermit
                          case 72 {
                              mstore(ptr, daiPermitSelector)  // store selector
                              mstore(add(ptr, 0x04), owner)   // store owner
                              mstore(add(ptr, 0x24), spender) // store spender
                              // Compact IDaiLikePermit.permit(uint32 nonce, uint32 expiry, uint256 r, uint256 vs)
                              {  // stack too deep
                                  let expiry := shr(224, calldataload(add(permit.offset, 0x04))) // loads permit.offset 0x04..0x07
                                  let vs := calldataload(add(permit.offset, 0x28))               // loads permit.offset 0x28..0x47
                                  mstore(add(ptr, 0x44), shr(224, calldataload(permit.offset))) // store nonce   = copy permit.offset 0x00..0x03
                                  mstore(add(ptr, 0x64), sub(expiry, 1))                        // store expiry  = expiry - 1
                                  mstore(add(ptr, 0x84), true)                                  // store allowed = true
                                  mstore(add(ptr, 0xa4), add(27, shr(255, vs)))                 // store v       = most significant bit of vs + 27 (27 or 28)
                                  calldatacopy(add(ptr, 0xc4), add(permit.offset, 0x08), 0x20)  // store r       = copy permit.offset 0x08..0x27
                                  mstore(add(ptr, 0xe4), shr(1, shl(1, vs)))                    // store s       = vs without most significant bit
                              }
                              // IDaiLikePermit.permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s)
                              success := call(gas(), token, 0, ptr, 0x104, 0, 0)
                          }
                          // IERC20Permit
                          case 224 {
                              mstore(ptr, permitSelector)
                              calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata
                              // IERC20Permit.permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
                              success := call(gas(), token, 0, ptr, 0xe4, 0, 0)
                          }
                          // IDaiLikePermit
                          case 256 {
                              mstore(ptr, daiPermitSelector)
                              calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata
                              // IDaiLikePermit.permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s)
                              success := call(gas(), token, 0, ptr, 0x104, 0, 0)
                          }
                          // Compact IPermit2
                          case 96 {
                              // Compact IPermit2.permit(uint160 amount, uint32 expiration, uint32 nonce, uint32 sigDeadline, uint256 r, uint256 vs)
                              mstore(ptr, permit2Selector)  // store selector
                              mstore(add(ptr, 0x04), owner) // store owner
                              mstore(add(ptr, 0x24), token) // store token
                              calldatacopy(add(ptr, 0x50), permit.offset, 0x14)             // store amount = copy permit.offset 0x00..0x13
                              // and(0xffffffffffff, ...) - conversion to uint48
                              mstore(add(ptr, 0x64), and(0xffffffffffff, sub(shr(224, calldataload(add(permit.offset, 0x14))), 1))) // store expiration = ((permit.offset 0x14..0x17 - 1) & 0xffffffffffff)
                              mstore(add(ptr, 0x84), shr(224, calldataload(add(permit.offset, 0x18)))) // store nonce = copy permit.offset 0x18..0x1b
                              mstore(add(ptr, 0xa4), spender)                               // store spender
                              // and(0xffffffffffff, ...) - conversion to uint48
                              mstore(add(ptr, 0xc4), and(0xffffffffffff, sub(shr(224, calldataload(add(permit.offset, 0x1c))), 1))) // store sigDeadline = ((permit.offset 0x1c..0x1f - 1) & 0xffffffffffff)
                              mstore(add(ptr, 0xe4), 0x100)                                 // store offset = 256
                              mstore(add(ptr, 0x104), 0x40)                                 // store length = 64
                              calldatacopy(add(ptr, 0x124), add(permit.offset, 0x20), 0x20) // store r      = copy permit.offset 0x20..0x3f
                              calldatacopy(add(ptr, 0x144), add(permit.offset, 0x40), 0x20) // store vs     = copy permit.offset 0x40..0x5f
                              // IPermit2.permit(address owner, PermitSingle calldata permitSingle, bytes calldata signature)
                              success := call(gas(), _PERMIT2, 0, ptr, 0x164, 0, 0)
                          }
                          // IPermit2
                          case 352 {
                              mstore(ptr, permit2Selector)
                              calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata
                              // IPermit2.permit(address owner, PermitSingle calldata permitSingle, bytes calldata signature)
                              success := call(gas(), _PERMIT2, 0, ptr, 0x164, 0, 0)
                          }
                          // Unknown
                          default {
                              mstore(ptr, _PERMIT_LENGTH_ERROR)
                              revert(ptr, 4)
                          }
                      }
                  }
                  /**
                   * @dev Executes a low level call to a token contract, making it resistant to reversion and erroneous boolean returns.
                   * @param token The IERC20 token contract on which the call will be made.
                   * @param selector The function signature that is to be called on the token contract.
                   * @param to The address to which the token amount will be transferred.
                   * @param amount The token amount to be transferred.
                   * @return success A boolean indicating if the call was successful. Returns 'true' on success and 'false' on failure.
                   * In case of success but no returned data, validates that the contract code exists.
                   * In case of returned data, ensures that it's a boolean `true`.
                   */
                  function _makeCall(
                      IERC20 token,
                      bytes4 selector,
                      address to,
                      uint256 amount
                  ) private returns (bool success) {
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          let data := mload(0x40)
                          mstore(data, selector)
                          mstore(add(data, 0x04), to)
                          mstore(add(data, 0x24), amount)
                          success := call(gas(), token, 0, data, 0x44, 0x0, 0x20)
                          if success {
                              switch returndatasize()
                              case 0 {
                                  success := gt(extcodesize(token), 0)
                              }
                              default {
                                  success := and(gt(returndatasize(), 31), eq(mload(0), 1))
                              }
                          }
                      }
                  }
                  /**
                   * @notice Safely deposits a specified amount of Ether into the IWETH contract. Consumes less gas then regular `IWETH.deposit`.
                   * @param weth The IWETH token contract.
                   * @param amount The amount of Ether to deposit into the IWETH contract.
                   */
                  function safeDeposit(IWETH weth, uint256 amount) internal {
                      if (amount > 0) {
                          bytes4 selector = IWETH.deposit.selector;
                          assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                              mstore(0, selector)
                              if iszero(call(gas(), weth, amount, 0, 4, 0, 0)) {
                                  let ptr := mload(0x40)
                                  returndatacopy(ptr, 0, returndatasize())
                                  revert(ptr, returndatasize())
                              }
                          }
                      }
                  }
                  /**
                   * @notice Safely withdraws a specified amount of wrapped Ether from the IWETH contract. Consumes less gas then regular `IWETH.withdraw`.
                   * @dev Uses inline assembly to interact with the IWETH contract.
                   * @param weth The IWETH token contract.
                   * @param amount The amount of wrapped Ether to withdraw from the IWETH contract.
                   */
                  function safeWithdraw(IWETH weth, uint256 amount) internal {
                      bytes4 selector = IWETH.withdraw.selector;
                      assembly ("memory-safe") {  // solhint-disable-line no-inline-assembly
                          mstore(0, selector)
                          mstore(4, amount)
                          if iszero(call(gas(), weth, 0, 0, 0x24, 0, 0)) {
                              let ptr := mload(0x40)
                              returndatacopy(ptr, 0, returndatasize())
                              revert(ptr, returndatasize())
                          }
                      }
                  }
                  /**
                   * @notice Safely withdraws a specified amount of wrapped Ether from the IWETH contract to a specified recipient.
                   * Consumes less gas then regular `IWETH.withdraw`.
                   * @param weth The IWETH token contract.
                   * @param amount The amount of wrapped Ether to withdraw from the IWETH contract.
                   * @param to The recipient of the withdrawn Ether.
                   */
                  function safeWithdrawTo(IWETH weth, uint256 amount, address to) internal {
                      safeWithdraw(weth, amount);
                      if (to != address(this)) {
                          assembly ("memory-safe") {  // solhint-disable-line no-inline-assembly
                              if iszero(call(_RAW_CALL_GAS_LIMIT, to, amount, 0, 0, 0, 0)) {
                                  let ptr := mload(0x40)
                                  returndatacopy(ptr, 0, returndatasize())
                                  revert(ptr, returndatasize())
                              }
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /// @title Library with gas-efficient string operations
              library StringUtil {
                  function toHex(uint256 value) internal pure returns (string memory) {
                      return toHex(abi.encodePacked(value));
                  }
                  function toHex(address value) internal pure returns (string memory) {
                      return toHex(abi.encodePacked(value));
                  }
                  /// @dev this is the assembly adaptation of highly optimized toHex16 code from Mikhail Vladimirov
                  /// https://stackoverflow.com/a/69266989
                  function toHex(bytes memory data) internal pure returns (string memory result) {
                      assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                          function _toHex16(input) -> output {
                              output := or(
                                  and(input, 0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000),
                                  shr(64, and(input, 0x0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000))
                              )
                              output := or(
                                  and(output, 0xFFFFFFFF000000000000000000000000FFFFFFFF000000000000000000000000),
                                  shr(32, and(output, 0x00000000FFFFFFFF000000000000000000000000FFFFFFFF0000000000000000))
                              )
                              output := or(
                                  and(output, 0xFFFF000000000000FFFF000000000000FFFF000000000000FFFF000000000000),
                                  shr(16, and(output, 0x0000FFFF000000000000FFFF000000000000FFFF000000000000FFFF00000000))
                              )
                              output := or(
                                  and(output, 0xFF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000),
                                  shr(8, and(output, 0x00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000))
                              )
                              output := or(
                                  shr(4, and(output, 0xF000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000)),
                                  shr(8, and(output, 0x0F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F00))
                              )
                              output := add(
                                  add(0x3030303030303030303030303030303030303030303030303030303030303030, output),
                                  mul(
                                      and(
                                          shr(4, add(output, 0x0606060606060606060606060606060606060606060606060606060606060606)),
                                          0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F
                                      ),
                                      7 // Change 7 to 39 for lower case output
                                  )
                              )
                          }
                          result := mload(0x40)
                          let length := mload(data)
                          let resultLength := shl(1, length)
                          let toPtr := add(result, 0x22) // 32 bytes for length + 2 bytes for '0x'
                          mstore(0x40, add(toPtr, resultLength)) // move free memory pointer
                          mstore(add(result, 2), 0x3078) // 0x3078 is right aligned so we write to `result + 2`
                          // to store the last 2 bytes in the beginning of the string
                          mstore(result, add(resultLength, 2)) // extra 2 bytes for '0x'
                          for {
                              let fromPtr := add(data, 0x20)
                              let endPtr := add(fromPtr, length)
                          } lt(fromPtr, endPtr) {
                              fromPtr := add(fromPtr, 0x20)
                          } {
                              let rawData := mload(fromPtr)
                              let hexData := _toHex16(rawData)
                              mstore(toPtr, hexData)
                              toPtr := add(toPtr, 0x20)
                              hexData := _toHex16(shl(128, rawData))
                              mstore(toPtr, hexData)
                              toPtr := add(toPtr, 0x20)
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              import "../interfaces/IERC20MetadataUppercase.sol";
              import "./SafeERC20.sol";
              import "./StringUtil.sol";
              /// @title Library, which allows usage of ETH as ERC20 and ERC20 itself. Uses SafeERC20 library for ERC20 interface.
              library UniERC20 {
                  using SafeERC20 for IERC20;
                  error InsufficientBalance();
                  error ApproveCalledOnETH();
                  error NotEnoughValue();
                  error FromIsNotSender();
                  error ToIsNotThis();
                  error ETHTransferFailed();
                  uint256 private constant _RAW_CALL_GAS_LIMIT = 5000;
                  IERC20 private constant _ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
                  IERC20 private constant _ZERO_ADDRESS = IERC20(address(0));
                  /// @dev Returns true if `token` is ETH.
                  function isETH(IERC20 token) internal pure returns (bool) {
                      return (token == _ZERO_ADDRESS || token == _ETH_ADDRESS);
                  }
                  /// @dev Returns `account` ERC20 `token` balance.
                  function uniBalanceOf(IERC20 token, address account) internal view returns (uint256) {
                      if (isETH(token)) {
                          return account.balance;
                      } else {
                          return token.balanceOf(account);
                      }
                  }
                  /// @dev `token` transfer `to` `amount`.
                  /// Note that this function does nothing in case of zero amount.
                  function uniTransfer(
                      IERC20 token,
                      address payable to,
                      uint256 amount
                  ) internal {
                      if (amount > 0) {
                          if (isETH(token)) {
                              if (address(this).balance < amount) revert InsufficientBalance();
                              // solhint-disable-next-line avoid-low-level-calls
                              (bool success, ) = to.call{value: amount, gas: _RAW_CALL_GAS_LIMIT}("");
                              if (!success) revert ETHTransferFailed();
                          } else {
                              token.safeTransfer(to, amount);
                          }
                      }
                  }
                  /// @dev `token` transfer `from` `to` `amount`.
                  /// Note that this function does nothing in case of zero amount.
                  function uniTransferFrom(
                      IERC20 token,
                      address payable from,
                      address to,
                      uint256 amount
                  ) internal {
                      if (amount > 0) {
                          if (isETH(token)) {
                              if (msg.value < amount) revert NotEnoughValue();
                              if (from != msg.sender) revert FromIsNotSender();
                              if (to != address(this)) revert ToIsNotThis();
                              if (msg.value > amount) {
                                  // Return remainder if exist
                                  unchecked {
                                      // solhint-disable-next-line avoid-low-level-calls
                                      (bool success, ) = from.call{value: msg.value - amount, gas: _RAW_CALL_GAS_LIMIT}("");
                                      if (!success) revert ETHTransferFailed();
                                  }
                              }
                          } else {
                              token.safeTransferFrom(from, to, amount);
                          }
                      }
                  }
                  /// @dev Returns `token` symbol from ERC20 metadata.
                  function uniSymbol(IERC20 token) internal view returns (string memory) {
                      return _uniDecode(token, IERC20Metadata.symbol.selector, IERC20MetadataUppercase.SYMBOL.selector);
                  }
                  /// @dev Returns `token` name from ERC20 metadata.
                  function uniName(IERC20 token) internal view returns (string memory) {
                      return _uniDecode(token, IERC20Metadata.name.selector, IERC20MetadataUppercase.NAME.selector);
                  }
                  /// @dev Reverts if `token` is ETH, otherwise performs ERC20 forceApprove.
                  function uniApprove(
                      IERC20 token,
                      address to,
                      uint256 amount
                  ) internal {
                      if (isETH(token)) revert ApproveCalledOnETH();
                      token.forceApprove(to, amount);
                  }
                  /// @dev 20K gas is provided to account for possible implementations of name/symbol
                  /// (token implementation might be behind proxy or store the value in storage)
                  function _uniDecode(
                      IERC20 token,
                      bytes4 lowerCaseSelector,
                      bytes4 upperCaseSelector
                  ) private view returns (string memory result) {
                      if (isETH(token)) {
                          return "ETH";
                      }
                      (bool success, bytes memory data) = address(token).staticcall{gas: 20000}(
                          abi.encodeWithSelector(lowerCaseSelector)
                      );
                      if (!success) {
                          (success, data) = address(token).staticcall{gas: 20000}(abi.encodeWithSelector(upperCaseSelector));
                      }
                      if (success && data.length >= 0x40) {
                          (uint256 offset, uint256 len) = abi.decode(data, (uint256, uint256));
                          /*
                              return data is padded up to 32 bytes with ABI encoder 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 overall data length is greater or equal than string length + extra 64 bytes
                          */
                          if (offset == 0x20 && data.length >= 0x40 + len) {
                              assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                                  result := add(data, 0x40)
                              }
                              return result;
                          }
                      }
                      if (success && data.length == 32) {
                          uint256 len = 0;
                          while (len < data.length && data[len] >= 0x20 && data[len] <= 0x7E) {
                              unchecked {
                                  len++;
                              }
                          }
                          if (len > 0) {
                              assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
                                  mstore(data, len)
                              }
                              return string(data);
                          }
                      }
                      return StringUtil.toHex(address(token));
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
              pragma solidity ^0.8.20;
              import {Context} from "../utils/Context.sol";
              /**
               * @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.
               *
               * The initial owner is set to the address provided by the deployer. 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 Ownable is Context {
                  address private _owner;
                  /**
                   * @dev The caller account is not authorized to perform an operation.
                   */
                  error OwnableUnauthorizedAccount(address account);
                  /**
                   * @dev The owner is not a valid owner account. (eg. `address(0)`)
                   */
                  error OwnableInvalidOwner(address owner);
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  /**
                   * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
                   */
                  constructor(address initialOwner) {
                      if (initialOwner == address(0)) {
                          revert OwnableInvalidOwner(address(0));
                      }
                      _transferOwnership(initialOwner);
                  }
                  /**
                   * @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 {
                      if (owner() != _msgSender()) {
                          revert OwnableUnauthorizedAccount(_msgSender());
                      }
                  }
                  /**
                   * @dev Leaves the contract without owner. It will not be possible to call
                   * `onlyOwner` functions. Can only be called by the current owner.
                   *
                   * NOTE: Renouncing ownership will leave the contract without an owner,
                   * thereby disabling 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 {
                      if (newOwner == address(0)) {
                          revert OwnableInvalidOwner(address(0));
                      }
                      _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);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
              pragma solidity ^0.8.20;
              import {IERC20} from "../IERC20.sol";
              /**
               * @dev Interface for the optional metadata functions from the ERC20 standard.
               */
              interface IERC20Metadata is IERC20 {
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() external view returns (string memory);
                  /**
                   * @dev Returns the symbol of the token.
                   */
                  function symbol() external view returns (string memory);
                  /**
                   * @dev Returns the decimals places of the token.
                   */
                  function decimals() external view returns (uint8);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
               * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
               *
               * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
               * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
               * need to send a transaction, and thus is not required to hold Ether at all.
               *
               * ==== Security Considerations
               *
               * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
               * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
               * considered as an intention to spend the allowance in any specific way. The second is that because permits have
               * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
               * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
               * generally recommended is:
               *
               * ```solidity
               * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
               *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
               *     doThing(..., value);
               * }
               *
               * function doThing(..., uint256 value) public {
               *     token.safeTransferFrom(msg.sender, address(this), value);
               *     ...
               * }
               * ```
               *
               * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
               * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
               * {SafeERC20-safeTransferFrom}).
               *
               * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
               * contracts should have entry points that don't rely on permit.
               */
              interface IERC20Permit {
                  /**
                   * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                   * given ``owner``'s signed approval.
                   *
                   * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                   * ordering also apply here.
                   *
                   * Emits an {Approval} event.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   * - `deadline` must be a timestamp in the future.
                   * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                   * over the EIP712-formatted function arguments.
                   * - the signature must use ``owner``'s current nonce (see {nonces}).
                   *
                   * For more information on the signature format, see the
                   * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                   * section].
                   *
                   * CAUTION: See Security Considerations above.
                   */
                  function permit(
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) external;
                  /**
                   * @dev Returns the current nonce for `owner`. This value must be
                   * included whenever a signature is generated for {permit}.
                   *
                   * Every successful call to {permit} increases ``owner``'s nonce by one. This
                   * prevents a signature from being used multiple times.
                   */
                  function nonces(address owner) external view returns (uint256);
                  /**
                   * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                   */
                  // solhint-disable-next-line func-name-mixedcase
                  function DOMAIN_SEPARATOR() external view returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  /**
                   * @dev Emitted when `value` tokens are moved from one account (`from`) to
                   * another (`to`).
                   *
                   * Note that `value` may be zero.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  /**
                   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                   * a call to {approve}. `value` is the new allowance.
                   */
                  event Approval(address indexed owner, address indexed spender, uint256 value);
                  /**
                   * @dev Returns the value of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
                  /**
                   * @dev Returns the value of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
                  /**
                   * @dev Moves a `value` amount of tokens from the caller's account to `to`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address to, uint256 value) external returns (bool);
                  /**
                   * @dev Returns the remaining number of tokens that `spender` will be
                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                   * zero by default.
                   *
                   * This value changes when {approve} or {transferFrom} are called.
                   */
                  function allowance(address owner, address spender) external view returns (uint256);
                  /**
                   * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
                   * caller's tokens.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                   * that someone may use both the old and the new allowance by unfortunate
                   * transaction ordering. One possible solution to mitigate this race
                   * condition is to first reduce the spender's allowance to 0 and set the
                   * desired value afterwards:
                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address spender, uint256 value) external returns (bool);
                  /**
                   * @dev Moves a `value` amount of tokens from `from` to `to` using the
                   * allowance mechanism. `value` is then deducted from the caller's
                   * allowance.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(address from, address to, uint256 value) external returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
                  function _contextSuffixLength() internal view virtual returns (uint256) {
                      return 0;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Standard math utilities missing in the Solidity language.
               */
              library Math {
                  /**
                   * @dev Muldiv operation overflow.
                   */
                  error MathOverflowedMulDiv();
                  enum Rounding {
                      Floor, // Toward negative infinity
                      Ceil, // Toward positive infinity
                      Trunc, // Toward zero
                      Expand // Away from zero
                  }
                  /**
                   * @dev Returns the addition of two unsigned integers, with an overflow flag.
                   */
                  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.
                   */
                  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.
                   */
                  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.
                   */
                  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.
                   */
                  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 largest of two numbers.
                   */
                  function max(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a > b ? a : b;
                  }
                  /**
                   * @dev Returns the smallest of two numbers.
                   */
                  function min(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a < b ? a : b;
                  }
                  /**
                   * @dev Returns the average of two numbers. The result is rounded towards
                   * zero.
                   */
                  function average(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b) / 2 can overflow.
                      return (a & b) + (a ^ b) / 2;
                  }
                  /**
                   * @dev Returns the ceiling of the division of two numbers.
                   *
                   * This differs from standard division with `/` in that it rounds towards infinity instead
                   * of rounding towards zero.
                   */
                  function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                      if (b == 0) {
                          // Guarantee the same behavior as in a regular Solidity division.
                          return a / b;
                      }
                      // (a + b - 1) / b can overflow on addition, so we distribute.
                      return a == 0 ? 0 : (a - 1) / b + 1;
                  }
                  /**
                   * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
                   * denominator == 0.
                   * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
                   * Uniswap Labs also under MIT license.
                   */
                  function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
                      unchecked {
                          // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                          // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                          // variables such that product = prod1 * 2^256 + prod0.
                          uint256 prod0 = x * y; // Least significant 256 bits of the product
                          uint256 prod1; // Most significant 256 bits of the product
                          assembly {
                              let mm := mulmod(x, y, not(0))
                              prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                          }
                          // Handle non-overflow cases, 256 by 256 division.
                          if (prod1 == 0) {
                              // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                              // The surrounding unchecked block does not change this fact.
                              // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                              return prod0 / denominator;
                          }
                          // Make sure the result is less than 2^256. Also prevents denominator == 0.
                          if (denominator <= prod1) {
                              revert MathOverflowedMulDiv();
                          }
                          ///////////////////////////////////////////////
                          // 512 by 256 division.
                          ///////////////////////////////////////////////
                          // Make division exact by subtracting the remainder from [prod1 prod0].
                          uint256 remainder;
                          assembly {
                              // Compute remainder using mulmod.
                              remainder := mulmod(x, y, denominator)
                              // Subtract 256 bit number from 512 bit number.
                              prod1 := sub(prod1, gt(remainder, prod0))
                              prod0 := sub(prod0, remainder)
                          }
                          // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
                          // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
                          uint256 twos = denominator & (0 - denominator);
                          assembly {
                              // Divide denominator by twos.
                              denominator := div(denominator, twos)
                              // Divide [prod1 prod0] by twos.
                              prod0 := div(prod0, twos)
                              // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                              twos := add(div(sub(0, twos), twos), 1)
                          }
                          // Shift in bits from prod1 into prod0.
                          prod0 |= prod1 * twos;
                          // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                          // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                          // four bits. That is, denominator * inv = 1 mod 2^4.
                          uint256 inverse = (3 * denominator) ^ 2;
                          // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
                          // works in modular arithmetic, doubling the correct bits in each step.
                          inverse *= 2 - denominator * inverse; // inverse mod 2^8
                          inverse *= 2 - denominator * inverse; // inverse mod 2^16
                          inverse *= 2 - denominator * inverse; // inverse mod 2^32
                          inverse *= 2 - denominator * inverse; // inverse mod 2^64
                          inverse *= 2 - denominator * inverse; // inverse mod 2^128
                          inverse *= 2 - denominator * inverse; // inverse mod 2^256
                          // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                          // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                          // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                          // is no longer required.
                          result = prod0 * inverse;
                          return result;
                      }
                  }
                  /**
                   * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                   */
                  function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
                      uint256 result = mulDiv(x, y, denominator);
                      if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
                          result += 1;
                      }
                      return result;
                  }
                  /**
                   * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
                   * towards zero.
                   *
                   * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                   */
                  function sqrt(uint256 a) internal pure returns (uint256) {
                      if (a == 0) {
                          return 0;
                      }
                      // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                      //
                      // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                      // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                      //
                      // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                      // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                      // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                      //
                      // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                      uint256 result = 1 << (log2(a) >> 1);
                      // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                      // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                      // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                      // into the expected uint128 result.
                      unchecked {
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          return min(result, a / result);
                      }
                  }
                  /**
                   * @notice Calculates sqrt(a), following the selected rounding direction.
                   */
                  function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = sqrt(a);
                          return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 2 of a positive value rounded towards zero.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >> 128 > 0) {
                              value >>= 128;
                              result += 128;
                          }
                          if (value >> 64 > 0) {
                              value >>= 64;
                              result += 64;
                          }
                          if (value >> 32 > 0) {
                              value >>= 32;
                              result += 32;
                          }
                          if (value >> 16 > 0) {
                              value >>= 16;
                              result += 16;
                          }
                          if (value >> 8 > 0) {
                              value >>= 8;
                              result += 8;
                          }
                          if (value >> 4 > 0) {
                              value >>= 4;
                              result += 4;
                          }
                          if (value >> 2 > 0) {
                              value >>= 2;
                              result += 2;
                          }
                          if (value >> 1 > 0) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log2(value);
                          return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 10 of a positive value rounded towards zero.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >= 10 ** 64) {
                              value /= 10 ** 64;
                              result += 64;
                          }
                          if (value >= 10 ** 32) {
                              value /= 10 ** 32;
                              result += 32;
                          }
                          if (value >= 10 ** 16) {
                              value /= 10 ** 16;
                              result += 16;
                          }
                          if (value >= 10 ** 8) {
                              value /= 10 ** 8;
                              result += 8;
                          }
                          if (value >= 10 ** 4) {
                              value /= 10 ** 4;
                              result += 4;
                          }
                          if (value >= 10 ** 2) {
                              value /= 10 ** 2;
                              result += 2;
                          }
                          if (value >= 10 ** 1) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log10(value);
                          return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 256 of a positive value rounded towards zero.
                   * Returns 0 if given 0.
                   *
                   * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                   */
                  function log256(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >> 128 > 0) {
                              value >>= 128;
                              result += 16;
                          }
                          if (value >> 64 > 0) {
                              value >>= 64;
                              result += 8;
                          }
                          if (value >> 32 > 0) {
                              value >>= 32;
                              result += 4;
                          }
                          if (value >> 16 > 0) {
                              value >>= 16;
                              result += 2;
                          }
                          if (value >> 8 > 0) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log256(value);
                          return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
                   */
                  function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
                      return uint8(rounding) % 2 == 1;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.23;
              import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
              import { IOrderMixin } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IOrderMixin.sol";
              import { IPostInteraction } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IPostInteraction.sol";
              import { IPreInteraction } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IPreInteraction.sol";
              import { IAmountGetter } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IAmountGetter.sol";
              /**
               * @title Base Extension contract
               * @notice Contract to define the basic functionality for the limit orders settlement.
               */
              contract BaseExtension is IPreInteraction, IPostInteraction, IAmountGetter {
                  error OnlyLimitOrderProtocol();
                  uint256 private constant _BASE_POINTS = 10_000_000; // 100%
                  uint256 private constant _GAS_PRICE_BASE = 1_000_000; // 1000 means 1 Gwei
                  address private immutable _LIMIT_ORDER_PROTOCOL;
                  /// @dev Modifier to check if the caller is the limit order protocol contract.
                  modifier onlyLimitOrderProtocol {
                      if (msg.sender != _LIMIT_ORDER_PROTOCOL) revert OnlyLimitOrderProtocol();
                      _;
                  }
                  /**
                   * @notice Initializes the contract.
                   * @param limitOrderProtocol The limit order protocol contract.
                   */
                  constructor(address limitOrderProtocol) {
                      _LIMIT_ORDER_PROTOCOL = limitOrderProtocol;
                  }
                  /**
                   * See {IAmountGetter-getMakingAmount}
                   */
                  function getMakingAmount(
                      IOrderMixin.Order calldata order,
                      bytes calldata /* extension */,
                      bytes32 /* orderHash */,
                      address /* taker */,
                      uint256 takingAmount,
                      uint256 /* remainingMakingAmount */,
                      bytes calldata extraData
                  ) external view returns (uint256) {
                      uint256 rateBump = _getRateBump(extraData);
                      return Math.mulDiv(order.makingAmount, takingAmount * _BASE_POINTS, order.takingAmount * (_BASE_POINTS + rateBump));
                  }
                  /**
                   * See {IAmountGetter-getTakingAmount}
                   */
                  function getTakingAmount(
                      IOrderMixin.Order calldata order,
                      bytes calldata /* extension */,
                      bytes32 /* orderHash */,
                      address /* taker */,
                      uint256 makingAmount,
                      uint256 /* remainingMakingAmount */,
                      bytes calldata extraData
                  ) external view returns (uint256) {
                      uint256 rateBump = _getRateBump(extraData);
                      return Math.mulDiv(order.takingAmount, makingAmount * (_BASE_POINTS + rateBump), order.makingAmount * _BASE_POINTS, Math.Rounding.Ceil);
                  }
                  /**
                   * See {IPreInteraction-preInteraction}
                   */
                  function preInteraction(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      bytes32 orderHash,
                      address taker,
                      uint256 makingAmount,
                      uint256 takingAmount,
                      uint256 remainingMakingAmount,
                      bytes calldata extraData
                  ) external onlyLimitOrderProtocol {
                      _preInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData);
                  }
                  /**
                   * See {IPostInteraction-postInteraction}
                   */
                  function postInteraction(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      bytes32 orderHash,
                      address taker,
                      uint256 makingAmount,
                      uint256 takingAmount,
                      uint256 remainingMakingAmount,
                      bytes calldata extraData
                  ) external onlyLimitOrderProtocol {
                      _postInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData);
                  }
                  function _preInteraction(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      bytes32 orderHash,
                      address taker,
                      uint256 makingAmount,
                      uint256 takingAmount,
                      uint256 remainingMakingAmount,
                      bytes calldata extraData
                  ) internal virtual {}
                  function _postInteraction(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      bytes32 orderHash,
                      address taker,
                      uint256 makingAmount,
                      uint256 takingAmount,
                      uint256 remainingMakingAmount,
                      bytes calldata extraData
                  ) internal virtual {}
                  /**
                   * @dev Parses auction rate bump data from the `auctionDetails` field.
                   * `gasBumpEstimate` and `gasPriceEstimate` are used to estimate the transaction costs
                   * which are then offset from the auction rate bump.
                   * @param auctionDetails AuctionDetails is a tightly packed struct of the following format:
                   * ```
                   * struct AuctionDetails {
                   *     bytes3 gasBumpEstimate;
                   *     bytes4 gasPriceEstimate;
                   *     bytes4 auctionStartTime;
                   *     bytes3 auctionDuration;
                   *     bytes3 initialRateBump;
                   *     (bytes3,bytes2)[N] pointsAndTimeDeltas;
                   * }
                   * ```
                   * @return rateBump The rate bump.
                   */
                  function _getRateBump(bytes calldata auctionDetails) private view returns (uint256) {
                      unchecked {
                          uint256 gasBumpEstimate = uint24(bytes3(auctionDetails[0:3]));
                          uint256 gasPriceEstimate = uint32(bytes4(auctionDetails[3:7]));
                          uint256 gasBump = gasBumpEstimate == 0 || gasPriceEstimate == 0 ? 0 : gasBumpEstimate * block.basefee / gasPriceEstimate / _GAS_PRICE_BASE;
                          uint256 auctionStartTime = uint32(bytes4(auctionDetails[7:11]));
                          uint256 auctionFinishTime = auctionStartTime + uint24(bytes3(auctionDetails[11:14]));
                          uint256 initialRateBump = uint24(bytes3(auctionDetails[14:17]));
                          uint256 auctionBump = _getAuctionBump(auctionStartTime, auctionFinishTime, initialRateBump, auctionDetails[17:]);
                          return auctionBump > gasBump ? auctionBump - gasBump : 0;
                      }
                  }
                  /**
                   * @dev Calculates auction price bump. Auction is represented as a piecewise linear function with `N` points.
                   * Each point is represented as a pair of `(rateBump, timeDelta)`, where `rateBump` is the
                   * rate bump in basis points and `timeDelta` is the time delta in seconds.
                   * The rate bump is interpolated linearly between the points.
                   * The last point is assumed to be `(0, auctionDuration)`.
                   * @param auctionStartTime The time when the auction starts.
                   * @param auctionFinishTime The time when the auction finishes.
                   * @param initialRateBump The initial rate bump.
                   * @param pointsAndTimeDeltas The points and time deltas structure.
                   * @return The rate bump at the current time.
                   */
                  function _getAuctionBump(uint256 auctionStartTime, uint256 auctionFinishTime, uint256 initialRateBump, bytes calldata pointsAndTimeDeltas) private view returns (uint256) {
                      unchecked {
                          if (block.timestamp <= auctionStartTime) {
                              return initialRateBump;
                          } else if (block.timestamp >= auctionFinishTime) {
                              return 0;
                          }
                          uint256 currentPointTime = auctionStartTime;
                          uint256 currentRateBump = initialRateBump;
                          while (pointsAndTimeDeltas.length > 0) {
                              uint256 nextRateBump = uint24(bytes3(pointsAndTimeDeltas[:3]));
                              uint256 nextPointTime = currentPointTime + uint16(bytes2(pointsAndTimeDeltas[3:5]));
                              if (block.timestamp <= nextPointTime) {
                                  return ((block.timestamp - currentPointTime) * nextRateBump + (nextPointTime - block.timestamp) * currentRateBump) / (nextPointTime - currentPointTime);
                              }
                              currentRateBump = nextRateBump;
                              currentPointTime = nextPointTime;
                              pointsAndTimeDeltas = pointsAndTimeDeltas[5:];
                          }
                          return (auctionFinishTime - block.timestamp) * currentRateBump / (auctionFinishTime - currentPointTime);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /**
               * @title Extension Library
               * @notice Library to retrieve data from the bitmap.
               */
              library ExtensionLib {
                  bytes1 private constant _RESOLVER_FEE_FLAG = 0x01;
                  bytes1 private constant _INTEGRATOR_FEE_FLAG = 0x02;
                  bytes1 private constant _CUSTOM_RECEIVER_FLAG = 0x04;
                  uint256 private constant _WHITELIST_SHIFT = 3;
                  /**
                   * @notice Checks if the resolver fee is enabled
                   * @param extraData Data to be processed in the extension
                   * @return True if the resolver fee is enabled
                   */
                  function resolverFeeEnabled(bytes calldata extraData) internal pure returns (bool) {
                      return extraData[extraData.length - 1] & _RESOLVER_FEE_FLAG == _RESOLVER_FEE_FLAG;
                  }
                  /**
                   * @notice Checks if the integrator fee is enabled
                   * @param extraData Data to be processed in the extension
                   * @return True if the integrator fee is enabled
                   */
                  function integratorFeeEnabled(bytes calldata extraData) internal pure returns (bool) {
                      return extraData[extraData.length - 1] & _INTEGRATOR_FEE_FLAG == _INTEGRATOR_FEE_FLAG;
                  }
                  /**
                   * @notice Checks if the custom receiver is enabled
                   * @param extraData Data to be processed in the extension
                   * @return True if the custom receiver is specified
                   */
                  function hasCustomReceiver(bytes calldata extraData) internal pure returns (bool) {
                      return extraData[extraData.length - 1] & _CUSTOM_RECEIVER_FLAG == _CUSTOM_RECEIVER_FLAG;
                  }
                  /**
                   * @notice Gets the number of resolvers in the whitelist
                   * @param extraData Data to be processed in the extension
                   * @return The number of resolvers in the whitelist
                   */
                  function resolversCount(bytes calldata extraData) internal pure returns (uint256) {
                      return uint8(extraData[extraData.length - 1]) >> _WHITELIST_SHIFT;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.23;
              import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
              import { IOrderMixin } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IOrderMixin.sol";
              import { MakerTraits, MakerTraitsLib } from "@1inch/limit-order-protocol-contract/contracts/libraries/MakerTraitsLib.sol";
              import { SafeERC20 } from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol";
              import { Address, AddressLib } from "@1inch/solidity-utils/contracts/libraries/AddressLib.sol";
              import { UniERC20 } from "@1inch/solidity-utils/contracts/libraries/UniERC20.sol";
              import { BaseExtension } from "./BaseExtension.sol";
              import { ExtensionLib } from "./ExtensionLib.sol";
              /**
               * @title Integrator Fee Extension
               * @notice Abstract contract designed to integrate fee processing within the post-interaction phase of order execution.
               */
              abstract contract IntegratorFeeExtension is BaseExtension, Ownable {
                  using SafeERC20 for IERC20;
                  using AddressLib for Address;
                  using ExtensionLib for bytes;
                  using MakerTraitsLib for MakerTraits;
                  using UniERC20 for IERC20;
                  /**
                   * @dev Eth transfer failed. The target fallback may have reverted.
                   */
                  error EthTransferFailed();
                  /// @dev Allows fees in range [1e-5, 0.65536]
                  uint256 private constant _FEE_BASE = 1e5;
                  address private immutable _WETH;
                  constructor(address weth) {
                      _WETH = weth;
                  }
                  /**
                   * @notice Fallback function to receive ETH.
                   */
                  receive() external payable {}
                  /**
                   * @param extraData Structured data of length n bytes, segmented as follows:
                   * [0:2]   - Fee percentage in basis points.
                   * [2:22]  - Integrator address.
                   * [22:42] - Custom receiver address.
                   * [42:n]  - ExtraData for other extensions, not utilized by this integration fee extension.
                   * [n] - Bitmap indicating usage flags, where `xxxx xx1x` signifies integration fee usage. Other bits in this bitmap are not used by this extension.
                   */
                  function _postInteraction(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      bytes32 orderHash,
                      address taker,
                      uint256 makingAmount,
                      uint256 takingAmount,
                      uint256 remainingMakingAmount,
                      bytes calldata extraData
                  ) internal virtual override {
                      if (extraData.integratorFeeEnabled()) {
                          uint256 fee = takingAmount * uint256(uint16(bytes2(extraData))) / _FEE_BASE;
                          address feeRecipient = address(bytes20(extraData[2:22]));
                          extraData = extraData[22:];
                          address receiver = order.maker.get();
                          if (extraData.hasCustomReceiver()) {
                              receiver = address(bytes20(extraData));
                              extraData = extraData[20:];
                          }
                          bool isEth = order.takerAsset.get() == address(_WETH) && order.makerTraits.unwrapWeth();
                          if (isEth) {
                              if (fee > 0) {
                                  _sendEth(feeRecipient, fee);
                              }
                              unchecked {
                                  _sendEth(receiver, takingAmount - fee);
                              }
                          } else {
                              if (fee > 0) {
                                  IERC20(order.takerAsset.get()).safeTransfer(feeRecipient, fee);
                              }
                              unchecked {
                                  IERC20(order.takerAsset.get()).safeTransfer(receiver, takingAmount - fee);
                              }
                          }
                      }
                      super._postInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData);
                  }
                  /**
                   * @notice Retrieves funds accidently sent directly to the contract address
                   * @param token ERC20 token to retrieve
                   * @param amount amount to retrieve
                   */
                  function rescueFunds(IERC20 token, uint256 amount) external onlyOwner {
                      token.uniTransfer(payable(msg.sender), amount);
                  }
                  function _sendEth(address target, uint256 amount) private {
                      (bool success, ) = target.call{value: amount}("");
                      if (!success) {
                          revert EthTransferFailed();
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.23;
              import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              import { IOrderMixin } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IOrderMixin.sol";
              import { FeeBankCharger } from "../FeeBankCharger.sol";
              import { BaseExtension } from "./BaseExtension.sol";
              import { ExtensionLib } from "./ExtensionLib.sol";
              /**
               * @title Resolver Fee Extension
               * @notice Abstract contract used as an extension in settlement contract to charge a fee resolver in the `postInteraction` method.
               */
              abstract contract ResolverFeeExtension is BaseExtension, FeeBankCharger {
                  using ExtensionLib for bytes;
                  uint256 private constant _ORDER_FEE_BASE_POINTS = 1e15;
                  constructor(IERC20 feeToken, address owner) FeeBankCharger(feeToken, owner) {}
                  /**
                   * @dev Calculates the resolver fee.
                   * @param fee Scaled resolver fee.
                   * @param orderMakingAmount Making amount from the order.
                   * @param actualMakingAmount Making amount that was actually filled.
                   * @return resolverFee Calculated resolver fee.
                   */
                  function _getResolverFee(
                      uint256 fee,
                      uint256 orderMakingAmount,
                      uint256 actualMakingAmount
                  ) internal pure virtual returns(uint256) {
                      return fee * _ORDER_FEE_BASE_POINTS * actualMakingAmount / orderMakingAmount;
                  }
                  /**
                   * @param extraData Structured data of length n bytes, segmented as follows:
                   * [0:4] - Resolver fee information.
                   * [4:n] - ExtraData for other extensions, not utilized by this resolver fee extension.
                   * [n] - Bitmap indicating usage flags, where `xxxx xxx1` signifies resolver fee usage. Other bits in this bitmap are not used by this extension.
                   */
                  function _postInteraction(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      bytes32 orderHash,
                      address taker,
                      uint256 makingAmount,
                      uint256 takingAmount,
                      uint256 remainingMakingAmount,
                      bytes calldata extraData
                  ) internal virtual override {
                      if (extraData.resolverFeeEnabled()) {
                          uint256 resolverFee = _getResolverFee(uint256(uint32(bytes4(extraData[:4]))), order.makingAmount, makingAmount);
                          _chargeFee(taker, resolverFee);
                          extraData = extraData[4:];
                      }
                      super._postInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.23;
              import { IOrderMixin } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IOrderMixin.sol";
              import { BaseExtension } from "./BaseExtension.sol";
              import { ExtensionLib } from "./ExtensionLib.sol";
              /**
               * @title Whitelist Extension
               * @notice Abstract contract designed to check resolvers from orders in whitelist within the post-interaction phase of order execution.
               * Ensures that only transactions from whitelisted resolvers are processed, enhancing security and compliance.
               */
              abstract contract WhitelistExtension is BaseExtension {
                  using ExtensionLib for bytes;
                  error ResolverIsNotWhitelisted();
                  /**
                   * @dev Validates whether the resolver is whitelisted.
                   * @param whitelist Whitelist is tightly packed struct of the following format:
                   * ```
                   * struct WhitelistDetails {
                   *     bytes4 auctionStartTime;
                   *     (bytes10,bytes2)[N] resolversAddressesAndTimeDeltas;
                   * }
                   * ```
                   * Resolvers in the list are sorted in ascending order by the time when they are allowed to interact with the order.
                   * Time deltas represent the time in seconds between the adjacent resolvers.
                   * Only 10 lowest bytes of the resolver address are used for comparison.
                   * @param whitelistSize The amount of resolvers in the whitelist.
                   * @param resolver The resolver to check.
                   * @return Whether the resolver is whitelisted.
                   */
                  function _isWhitelisted(bytes calldata whitelist, uint256 whitelistSize, address resolver) internal view virtual returns (bool) {
                      unchecked {
                          uint256 allowedTime = uint32(bytes4(whitelist[0:4])); // initially set to auction start time
                          whitelist = whitelist[4:];
                          uint80 maskedResolverAddress = uint80(uint160(resolver));
                          for (uint256 i = 0; i < whitelistSize; i++) {
                              uint80 whitelistedAddress = uint80(bytes10(whitelist[:10]));
                              allowedTime += uint16(bytes2(whitelist[10:12])); // add next time delta
                              if (maskedResolverAddress == whitelistedAddress) {
                                  return allowedTime <= block.timestamp;
                              } else if (allowedTime > block.timestamp) {
                                  return false;
                              }
                              whitelist = whitelist[12:];
                          }
                          return false;
                      }
                  }
                  /**
                   * @param extraData Structured data of length n bytes, segmented as follows:
                   * [0:k] - Data as defined by the `whitelist` parameter for the `_isWhitelisted` method,
                   *         where k depends on the amount of resolvers in the whitelist, as indicated by the bitmap in the last byte.
                   * [k:n] - ExtraData for other extensions, not utilized by this whitelist extension.
                   * [n]   - Bitmap `VVVV Vxxx` where V bits represent the amount of resolvers in the whitelist. The remaining bits in this bitmap are not used by this extension.
                   */
                  function _postInteraction(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      bytes32 orderHash,
                      address taker,
                      uint256 makingAmount,
                      uint256 takingAmount,
                      uint256 remainingMakingAmount,
                      bytes calldata extraData
                  ) internal virtual override {
                      uint256 resolversCount = extraData.resolversCount();
                      unchecked {
                          uint256 whitelistSize = 4 + resolversCount * 12;
                          if (!_isWhitelisted(extraData[:whitelistSize], resolversCount, taker)) revert ResolverIsNotWhitelisted();
                          super._postInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData[whitelistSize:]);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.23;
              import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              import { SafeERC20 } from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol";
              import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
              import { IFeeBankCharger } from "./interfaces/IFeeBankCharger.sol";
              import { IFeeBank } from "./interfaces/IFeeBank.sol";
              /**
               * @title FeeBank
               * @notice FeeBank contract introduces a credit system for paying fees.
               * A user can deposit tokens to the FeeBank contract, obtain credits and then use them to pay fees.
               * @dev FeeBank is coupled with FeeBankCharger to actually charge fees.
               */
              contract FeeBank is IFeeBank, Ownable {
                  using SafeERC20 for IERC20;
                  error ZeroAddress();
                  IERC20 private immutable _FEE_TOKEN;
                  IFeeBankCharger private immutable _CHARGER;
                  mapping(address account => uint256 availableCredit) private _accountDeposits;
                  constructor(IFeeBankCharger charger, IERC20 feeToken, address owner) Ownable(owner) {
                      if (address(feeToken) == address(0)) revert ZeroAddress();
                      _CHARGER = charger;
                      _FEE_TOKEN = feeToken;
                  }
                  /**
                   * @notice See {IFeeBank-availableCredit}.
                   */
                  function availableCredit(address account) external view returns (uint256) {
                      return _CHARGER.availableCredit(account);
                  }
                  /**
                   * @notice See {IFeeBank-deposit}.
                   */
                  function deposit(uint256 amount) external returns (uint256) {
                      return _depositFor(msg.sender, amount);
                  }
                  /**
                   * @notice See {IFeeBank-depositFor}.
                   */
                  function depositFor(address account, uint256 amount) external returns (uint256) {
                      return _depositFor(account, amount);
                  }
                  /**
                   * @notice See {IFeeBank-depositWithPermit}.
                   */
                  function depositWithPermit(uint256 amount, bytes calldata permit) external returns (uint256) {
                      return depositForWithPermit(msg.sender, amount, permit);
                  }
                  /**
                   * @notice See {IFeeBank-depositForWithPermit}.
                   */
                  function depositForWithPermit(
                      address account,
                      uint256 amount,
                      bytes calldata permit
                  ) public returns (uint256) {
                      _FEE_TOKEN.safePermit(permit);
                      return _depositFor(account, amount);
                  }
                  /**
                   * @notice See {IFeeBank-withdraw}.
                   */
                  function withdraw(uint256 amount) external returns (uint256) {
                      return _withdrawTo(msg.sender, amount);
                  }
                  /**
                   * @notice See {IFeeBank-withdrawTo}.
                   */
                  function withdrawTo(address account, uint256 amount) external returns (uint256) {
                      return _withdrawTo(account, amount);
                  }
                  /**
                   * @notice Admin method returns commissions spent by users.
                   * @param accounts Accounts whose commissions are being withdrawn.
                   * @return totalAccountFees The total amount of accounts commissions.
                   */
                  function gatherFees(address[] calldata accounts) external onlyOwner returns (uint256 totalAccountFees) {
                      uint256 accountsLength = accounts.length;
                      unchecked {
                          for (uint256 i = 0; i < accountsLength; ++i) {
                              address account = accounts[i];
                              uint256 accountDeposit = _accountDeposits[account];
                              uint256 availableCredit_ = _CHARGER.availableCredit(account);
                              _accountDeposits[account] = availableCredit_;
                              totalAccountFees += accountDeposit - availableCredit_;  // overflow is impossible due to checks in FeeBankCharger
                          }
                      }
                      _FEE_TOKEN.safeTransfer(msg.sender, totalAccountFees);
                  }
                  function _depositFor(address account, uint256 amount) internal returns (uint256 totalAvailableCredit) {
                      if (account == address(0)) revert ZeroAddress();
                      _FEE_TOKEN.safeTransferFrom(msg.sender, address(this), amount);
                      unchecked {
                          _accountDeposits[account] += amount;  // overflow is impossible due to limited _FEE_TOKEN supply
                      }
                      totalAvailableCredit = _CHARGER.increaseAvailableCredit(account, amount);
                  }
                  function _withdrawTo(address account, uint256 amount) internal returns (uint256 totalAvailableCredit) {
                      totalAvailableCredit = _CHARGER.decreaseAvailableCredit(msg.sender, amount);
                      unchecked {
                          _accountDeposits[msg.sender] -= amount;  // underflow is impossible due to checks in FeeBankCharger
                      }
                      _FEE_TOKEN.safeTransfer(account, amount);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.23;
              import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              import { IFeeBank } from "./interfaces/IFeeBank.sol";
              import { IFeeBankCharger } from "./interfaces/IFeeBankCharger.sol";
              import { FeeBank } from "./FeeBank.sol";
              /**
               * @title FeeBankCharger
               * @notice FeeBankCharger contract implements logic to increase or decrease users' credits in FeeBank.
               */
              contract FeeBankCharger is IFeeBankCharger {
                  error OnlyFeeBankAccess();
                  error NotEnoughCredit();
                  /**
                   * @notice See {IFeeBankCharger-feeBank}.
                   */
                  IFeeBank public immutable FEE_BANK;
                  mapping(address => uint256) private _creditAllowance;
                  /**
                   * @dev Modifier to check if the sender is a FEE_BANK contract.
                   */
                  modifier onlyFeeBank() {
                      if (msg.sender != address(FEE_BANK)) revert OnlyFeeBankAccess();
                      _;
                  }
                  constructor(IERC20 feeToken, address owner) {
                      FEE_BANK = new FeeBank(this, feeToken, owner);
                  }
                  /**
                   * @notice See {IFeeBankCharger-availableCredit}.
                   */
                  function availableCredit(address account) external view returns (uint256) {
                      return _creditAllowance[account];
                  }
                  /**
                   * @notice See {IFeeBankCharger-increaseAvailableCredit}.
                   */
                  function increaseAvailableCredit(address account, uint256 amount) external onlyFeeBank returns (uint256 allowance) {
                      allowance = _creditAllowance[account];
                      unchecked {
                          allowance += amount;  // overflow is impossible due to limited _token supply
                      }
                      _creditAllowance[account] = allowance;
                  }
                  /**
                   * @notice See {IFeeBankCharger-decreaseAvailableCredit}.
                   */
                  function decreaseAvailableCredit(address account, uint256 amount) external onlyFeeBank returns (uint256 allowance) {
                      return _creditAllowance[account] -= amount;  // checked math is needed to prevent underflow
                  }
                  /**
                   * @notice Internal function that charges a specified fee from a given account's credit allowance.
                   * @dev Reverts with 'NotEnoughCredit' if the account's credit allowance is insufficient to cover the fee.
                   * @param account The address of the account from which the fee is being charged.
                   * @param fee The amount of fee to be charged from the account.
                   */
                  function _chargeFee(address account, uint256 fee) internal virtual {
                      if (fee > 0) {
                          uint256 currentAllowance = _creditAllowance[account];
                          if (currentAllowance < fee) revert NotEnoughCredit();
                          unchecked {
                              _creditAllowance[account] = currentAllowance - fee;
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              interface IFeeBank {
                  /**
                   * @notice Returns the available credit for a given account in the FeeBank contract.
                   * @param account The address of the account for which the available credit is being queried.
                   * @return availableCredit The available credit of the queried account.
                   */
                  function availableCredit(address account) external view returns (uint256 availableCredit);
                  /**
                   * @notice Increases the caller's available credit by the specified amount.
                   * @param amount The amount of credit to be added to the caller's account.
                   * @return totalAvailableCredit The updated available credit of the caller's account.
                   */
                  function deposit(uint256 amount) external returns (uint256 totalAvailableCredit);
                  /**
                   * @notice Increases the specified account's available credit by the specified amount.
                   * @param account The address of the account for which the available credit is being increased.
                   * @param amount The amount of credit to be added to the account.
                   * @return totalAvailableCredit The updated available credit of the specified account.
                   */
                  function depositFor(address account, uint256 amount) external returns (uint256 totalAvailableCredit);
                  /**
                   * @notice Increases the caller's available credit by a specified amount with permit.
                   * @param amount The amount of credit to be added to the caller's account.
                   * @param permit The permit data authorizing the transaction.
                   * @return totalAvailableCredit The updated available credit of the caller's account.
                   */
                  function depositWithPermit(uint256 amount, bytes calldata permit) external returns (uint256 totalAvailableCredit);
                  /**
                   * @notice Increases the specified account's available credit by a specified amount with permit.
                   * @param account The address of the account for which the available credit is being increased.
                   * @param amount The amount of credit to be added to the account.
                   * @param permit The permit data authorizing the transaction.
                   * @return totalAvailableCredit The updated available credit of the specified account.
                   */
                  function depositForWithPermit(address account, uint256 amount, bytes calldata permit) external returns (uint256 totalAvailableCredit);
                  /**
                   * @notice Withdraws a specified amount of credit from the caller's account.
                   * @param amount The amount of credit to be withdrawn from the caller's account.
                   * @return totalAvailableCredit The updated available credit of the caller's account.
                   */
                  function withdraw(uint256 amount) external returns (uint256 totalAvailableCredit);
                  /**
                   * @notice Withdraws a specified amount of credit to the specified account.
                   * @param account The address of the account to which the credit is being withdrawn.
                   * @param amount The amount of credit to be withdrawn.
                   * @return totalAvailableCredit The updated available credit of the caller's account.
                   */
                  function withdrawTo(address account, uint256 amount) external returns (uint256 totalAvailableCredit);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import { IFeeBank } from "./IFeeBank.sol";
              interface IFeeBankCharger {
                  /**
                   * @notice Returns the instance of the FeeBank contract.
                   * @return The instance of the FeeBank contract.
                   */
                  function FEE_BANK() external view returns (IFeeBank); // solhint-disable-line func-name-mixedcase
                  /**
                   * @notice Returns the available credit for a given account.
                   * @param account The address of the account for which the available credit is being queried.
                   * @return The available credit of the queried account.
                   */
                  function availableCredit(address account) external view returns (uint256);
                  /**
                   * @notice Increases the available credit of a given account by a specified amount.
                   * @param account The address of the account for which the available credit is being increased.
                   * @param amount The amount by which the available credit will be increased.
                   * @return allowance The updated available credit of the specified account.
                   */
                  function increaseAvailableCredit(address account, uint256 amount) external returns (uint256 allowance);
                  /**
                   * @notice Decreases the available credit of a given account by a specified amount.
                   * @param account The address of the account for which the available credit is being decreased.
                   * @param amount The amount by which the available credit will be decreased.
                   * @return allowance The updated available credit of the specified account.
                   */
                  function decreaseAvailableCredit(address account, uint256 amount) external returns (uint256 allowance);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.23;
              import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              import { IOrderMixin } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IOrderMixin.sol";
              import { SimpleSettlement } from "./SimpleSettlement.sol";
              /**
               * @title Settlement contract
               * @notice Contract to execute limit orders settlement on Mainnet, created by Fusion mode.
               */
              contract Settlement is SimpleSettlement {
                  error InvalidPriorityFee();
                  constructor(address limitOrderProtocol, IERC20 feeToken, address weth, address owner)
                      SimpleSettlement(limitOrderProtocol, feeToken, weth, owner)
                  {}
                  function _postInteraction(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      bytes32 orderHash,
                      address taker,
                      uint256 makingAmount,
                      uint256 takingAmount,
                      uint256 remainingMakingAmount,
                      bytes calldata extraData
                  ) internal virtual override {
                      if (!_isPriorityFeeValid()) revert InvalidPriorityFee();
                      super._postInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData);
                  }
                  /**
                   * @dev Validates priority fee according to the spec
                   * https://snapshot.org/#/1inch.eth/proposal/0xa040c60050147a0f67042ae024673e92e813b5d2c0f748abf70ddfa1ed107cbe
                   * For blocks with baseFee <10.6 gwei – the priorityFee is capped at 70% of the baseFee.
                   * For blocks with baseFee between 10.6 gwei and 104.1 gwei – the priorityFee is capped at 50% of the baseFee.
                   * For blocks with baseFee >104.1 gwei – priorityFee is capped at 65% of the block’s baseFee.
                   */
                  function _isPriorityFeeValid() internal view returns(bool) {
                      unchecked {
                          uint256 baseFee = block.basefee;
                          uint256 priorityFee = tx.gasprice - baseFee;
                          if (baseFee < 10.6 gwei) {
                              return priorityFee * 100 <= baseFee * 70;
                          } else if (baseFee > 104.1 gwei) {
                              return priorityFee * 100 <= baseFee * 65;
                          } else {
                              return priorityFee * 2 <= baseFee;
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.23;
              import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
              import { IOrderMixin } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IOrderMixin.sol";
              import { BaseExtension } from "./extensions/BaseExtension.sol";
              import { IntegratorFeeExtension } from "./extensions/IntegratorFeeExtension.sol";
              import { ResolverFeeExtension } from "./extensions/ResolverFeeExtension.sol";
              import { WhitelistExtension } from "./extensions/WhitelistExtension.sol";
              /**
               * @title Simple Settlement contract
               * @notice Contract to execute limit orders settlement, created by Fusion mode.
               */
              contract SimpleSettlement is WhitelistExtension, ResolverFeeExtension, IntegratorFeeExtension {
                  /**
                   * @notice Initializes the contract.
                   * @param limitOrderProtocol The limit order protocol contract.
                   * @param feeToken The token to charge protocol fees in.
                   * @param weth The WETH address.
                   * @param owner The owner of the contract.
                   */
                  constructor(address limitOrderProtocol, IERC20 feeToken, address weth, address owner)
                      BaseExtension(limitOrderProtocol)
                      ResolverFeeExtension(feeToken, owner)
                      IntegratorFeeExtension(weth)
                      Ownable(owner)
                  {}
                  function _postInteraction(
                      IOrderMixin.Order calldata order,
                      bytes calldata extension,
                      bytes32 orderHash,
                      address taker,
                      uint256 makingAmount,
                      uint256 takingAmount,
                      uint256 remainingMakingAmount,
                      bytes calldata extraData
                  ) internal virtual override(WhitelistExtension, ResolverFeeExtension, IntegratorFeeExtension) {
                      super._postInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData);
                  }
              }