Contract 0xad79a2611c083d2b65ecb503165599b865801d44 2

 
Txn Hash Method
Block
From
To
Value [Txn Fee]
0xc92c9f92a5f6267777ab584a6986dbfa620ed4dcb0d71b0888df044db97165290x60806040203297152022-08-15 16:20:4745 days 23 hrs ago0x2ab12a0ab00d0060ed2ff8d329ad1999a3fccfce IN  Create: BadgerBridgeZeroControllerArb0 ETH0.011449250528 ETH
[ Download CSV Export 
Latest 12 internal transactions
Parent Txn Hash Block From To Value
0xcbd0926e575e118c1402ba835bed62dcdca27da985dd4c957c81379474a2388e207060962022-08-19 16:36:3741 days 22 hrs ago 0x9880fcd5d42e8f4c2148f2c1187df050be3dbd17 0xad79a2611c083d2b65ecb503165599b865801d440.276942246636810445 ETH
0xcbd0926e575e118c1402ba835bed62dcdca27da985dd4c957c81379474a2388e207060962022-08-19 16:36:3741 days 22 hrs ago 0x9880fcd5d42e8f4c2148f2c1187df050be3dbd17 0xad79a2611c083d2b65ecb503165599b865801d440 ETH
0xbabebefb4a512d894b9703df7adaab13328e675fbb528d707295d7132f35f6e1207053532022-08-19 16:27:1741 days 22 hrs ago 0x9880fcd5d42e8f4c2148f2c1187df050be3dbd17 0xad79a2611c083d2b65ecb503165599b865801d440 ETH
0x0afb9794a077caedaf9993a69a423c7076312a57a98242fa10fdc5a8aec72877206506362022-08-19 4:20:5542 days 11 hrs ago 0x9880fcd5d42e8f4c2148f2c1187df050be3dbd17 0xad79a2611c083d2b65ecb503165599b865801d440 ETH
0x94000532fc86738e2f352fb256abccae859ad3a61491c445fe17830b9b45bcec205628962022-08-18 4:11:1043 days 11 hrs ago 0x9880fcd5d42e8f4c2148f2c1187df050be3dbd17 0xad79a2611c083d2b65ecb503165599b865801d440 ETH
0x8ee70daa803ceaf92a70aca2fdf4643389948320ffd89ed94240204b7393fe1f205628262022-08-18 4:09:2243 days 11 hrs ago 0x9880fcd5d42e8f4c2148f2c1187df050be3dbd17 0xad79a2611c083d2b65ecb503165599b865801d440.024033081802793884 ETH
0x8ee70daa803ceaf92a70aca2fdf4643389948320ffd89ed94240204b7393fe1f205628262022-08-18 4:09:2243 days 11 hrs ago 0x9880fcd5d42e8f4c2148f2c1187df050be3dbd17 0xad79a2611c083d2b65ecb503165599b865801d440 ETH
0x22d2350ff246305808c4e8c4b6abfd56a684d6d1d533ca741ff3c7172b3c55cd205627072022-08-18 4:07:3643 days 11 hrs ago 0x9880fcd5d42e8f4c2148f2c1187df050be3dbd17 0xad79a2611c083d2b65ecb503165599b865801d440 ETH
0x763e2a7fa98418c79966b8cdfaf9e505f2e6d7877339c78c0eb940c5fd99d2b1205621342022-08-18 3:55:0243 days 11 hrs ago 0x9880fcd5d42e8f4c2148f2c1187df050be3dbd17 0xad79a2611c083d2b65ecb503165599b865801d440 ETH
0x5851b9a896d71908d6afc38a568c1fdffd553a62994f48d1f66dd4792b8472bc204520112022-08-17 0:42:0544 days 14 hrs ago 0x9880fcd5d42e8f4c2148f2c1187df050be3dbd17 0xad79a2611c083d2b65ecb503165599b865801d440.03 ETH
0x0581fafc4fbb63b139fc5bc636b1f088f1826d3132903c5698ae11df7818b9bf204519142022-08-17 0:41:3644 days 14 hrs ago 0x9880fcd5d42e8f4c2148f2c1187df050be3dbd17 0xad79a2611c083d2b65ecb503165599b865801d440 ETH
0xddcbf55f685d2a72072c8de2375cf2f791b6b11bfc0f23beb242fc79f1d0c2d5204518882022-08-17 0:41:3644 days 14 hrs ago 0x9880fcd5d42e8f4c2148f2c1187df050be3dbd17 0xad79a2611c083d2b65ecb503165599b865801d440 ETH
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
BadgerBridgeZeroControllerArb

Compiler Version
v0.7.6+commit.7338295f

Optimization Enabled:
Yes with 5 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 140 : BadgerBridgeZeroController.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0;
pragma abicoder v2;

import { IUniswapV2Router02 } from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import { ISwapRouter } from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import { UniswapV2Library } from "../libraries/UniswapV2Library.sol";
import { ZeroLib } from "../libraries/ZeroLib.sol";
import { IERC2612Permit } from "../interfaces/IERC2612Permit.sol";
import { IRenCrv } from "../interfaces/CurvePools/IRenCrv.sol";
import { SplitSignatureLib } from "../libraries/SplitSignatureLib.sol";
import { IBadgerSettPeak } from "../interfaces/IBadgerSettPeak.sol";
import { IWETH } from "../interfaces/IWETH.sol";
import { ICurveFi } from "../interfaces/ICurveFi.sol";
import { IGateway } from "../interfaces/IGateway.sol";
import { ICurveTricrypto } from "../interfaces/CurvePools/ICurveTricrypto.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IyVault } from "../interfaces/IyVault.sol";
import { ISett } from "../interfaces/ISett.sol";
import { Math } from "@openzeppelin/contracts/math/Math.sol";
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { ECDSA } from "@openzeppelin/contracts/cryptography/ECDSA.sol";
import { EIP712Upgradeable } from "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol";

contract BadgerBridgeZeroController is EIP712Upgradeable {
  using SafeERC20 for IERC20;
  using SafeMath for *;
  uint256 public fee;
  address public governance;
  address public strategist;

  address constant btcGateway = 0xe4b679400F0f267212D5D812B95f58C83243EE71;
  address constant router = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
  address constant routerv3 = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
  address constant factory = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
  address constant usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
  address constant weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
  address constant wbtc = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;
  address constant renbtc = 0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D;
  address constant renCrv = 0x93054188d876f558f4a66B2EF1d97d16eDf0895B;
  address constant threepool = 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7;
  address constant tricrypto = 0x80466c64868E1ab14a1Ddf27A676C3fcBE638Fe5;
  address constant renCrvLp = 0x49849C98ae39Fff122806C06791Fa73784FB3675;
  address constant bCrvRen = 0x6dEf55d2e18486B9dDfaA075bc4e4EE0B28c1545;
  address constant settPeak = 0x41671BA1abcbA387b9b2B752c205e22e916BE6e3;
  address constant usdt = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
  uint24 constant wethWbtcFee = 500;
  uint24 constant usdcWethFee = 500;
  uint256 public governanceFee;
  bytes32 constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
  bytes32 constant LOCK_SLOT = keccak256("upgrade-lock-v3");
  uint256 constant GAS_COST = uint256(42e4);
  uint256 constant ETH_RESERVE = uint256(5 ether);
  uint256 internal renbtcForOneETHPrice;
  uint256 internal burnFee;
  uint256 public keeperReward;
  uint256 public constant REPAY_GAS_DIFF = 41510;
  uint256 public constant BURN_GAS_DIFF = 41118;
  mapping(address => uint256) public nonces;
  bytes32 internal PERMIT_DOMAIN_SEPARATOR_WBTC;
  mapping(address => uint256) public noncesUsdt;
  bytes32 constant PERMIT_DOMAIN_SEPARATOR_USDT_SLOT = keccak256("usdt-permit");

  function getUsdtDomainSeparator() public view returns (bytes32) {
    bytes32 separator_slot = PERMIT_DOMAIN_SEPARATOR_USDT_SLOT;
    bytes32 separator;
    assembly {
      separator := sload(separator_slot)
    }
    return separator;
  }

  function setStrategist(address _strategist) public {
    require(msg.sender == governance, "!governance");
    strategist = _strategist;
  }

  function setGovernance(address _governance) public {
    require(msg.sender == governance, "!governance");
    governance = _governance;
  }

  function approveUpgrade(bool lock) public {
    bool isLocked;
    bytes32 lock_slot = LOCK_SLOT;
    bytes32 permit_slot = PERMIT_DOMAIN_SEPARATOR_USDT_SLOT;

    assembly {
      isLocked := sload(lock_slot)
    }
    require(!isLocked, "cannot run upgrade function");

    IERC20(usdt).safeApprove(tricrypto, ~uint256(0) >> 2);
    bytes32 permit_usdt_separator = keccak256(
      abi.encode(
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
        keccak256("USDT"),
        keccak256("1"),
        getChainId(),
        usdt
      )
    );

    assembly {
      sstore(lock_slot, lock)
      sstore(permit_slot, permit_usdt_separator)
    }
  }

  function computeCalldataGasDiff() internal pure returns (uint256 diff) {
    if (true) return 0; // TODO: implement exact gas metering
    // EVM charges less for zero bytes, we must compute the offset for refund
    // TODO make this efficient
    uint256 sz;
    assembly {
      sz := calldatasize()
    }
    diff = sz.mul(uint256(68));
    bytes memory slice;
    for (uint256 i = 0; i < sz; i += 0x20) {
      uint256 word;
      assembly {
        word := calldataload(i)
      }
      for (uint256 i = 0; i < 256 && ((uint256(~0) << i) & word) != 0; i += 8) {
        if ((word >> i) & 0xff != 0) diff -= 64;
      }
    }
  }

  function getChainId() internal pure returns (uint256 result) {
    assembly {
      result := chainid()
    }
  }

  function setParameters(
    uint256 _governanceFee,
    uint256 _fee,
    uint256 _burnFee,
    uint256 _keeperReward
  ) public {
    require(governance == msg.sender, "!governance");
    governanceFee = _governanceFee;
    fee = _fee;
    burnFee = _burnFee;
    keeperReward = _keeperReward;
  }

  function initialize(address _governance, address _strategist) public initializer {
    fee = uint256(25e14);
    burnFee = uint256(4e15);
    governanceFee = uint256(5e17);
    governance = _governance;
    strategist = _strategist;
    keeperReward = uint256(1 ether).div(1000);
    IERC20(renbtc).safeApprove(btcGateway, ~uint256(0) >> 2);
    IERC20(renbtc).safeApprove(renCrv, ~uint256(0) >> 2);
    IERC20(wbtc).safeApprove(renCrv, ~uint256(0) >> 2);
    IERC20(wbtc).safeApprove(tricrypto, ~uint256(0) >> 2);
    IERC20(usdt).safeApprove(tricrypto, ~uint256(0) >> 2);
    IERC20(wbtc).safeApprove(routerv3, ~uint256(0) >> 2);
    IERC20(usdc).safeApprove(routerv3, ~uint256(0) >> 2);
    PERMIT_DOMAIN_SEPARATOR_WBTC = keccak256(
      abi.encode(
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
        keccak256("WBTC"),
        keccak256("1"),
        getChainId(),
        wbtc
      )
    );
    bytes32 permit_separator_usdt = keccak256(
      abi.encode(
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
        keccak256("USDT"),
        keccak256("1"),
        getChainId(),
        usdt
      )
    );
    bytes32 permit_slot = PERMIT_DOMAIN_SEPARATOR_USDT_SLOT;
    assembly {
      sstore(permit_slot, permit_separator_usdt)
    }
  }

  function applyRatio(uint256 v, uint256 n) internal pure returns (uint256 result) {
    result = v.mul(n).div(uint256(1 ether));
  }

  function toWBTC(uint256 amount) internal returns (uint256 amountOut) {
    uint256 amountStart = IERC20(wbtc).balanceOf(address(this));
    (bool success, ) = renCrv.call(abi.encodeWithSelector(IRenCrv.exchange.selector, 0, 1, amount));
    amountOut = IERC20(wbtc).balanceOf(address(this)).sub(amountStart);
  }

  function toUSDC(
    uint256 minOut,
    uint256 amountIn,
    address out
  ) internal returns (uint256 amountOut) {
    uint256 wbtcAmountIn = toWBTC(amountIn);
    bytes memory path = abi.encodePacked(wbtc, wethWbtcFee, weth, usdcWethFee, usdc);
    ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({
      recipient: out,
      deadline: block.timestamp + 1,
      amountIn: wbtcAmountIn,
      amountOutMinimum: minOut,
      path: path
    });
    amountOut = ISwapRouter(routerv3).exactInput(params);
  }

  function quote() internal {
    (uint256 amountWeth, uint256 amountRenBTC) = UniswapV2Library.getReserves(factory, weth, renbtc);
    renbtcForOneETHPrice = UniswapV2Library.quote(uint256(1 ether), amountWeth, amountRenBTC);
  }

  function renBTCtoETH(
    uint256 minOut,
    uint256 amountIn,
    address out
  ) internal returns (uint256 amountOut) {
    uint256 wbtcAmountOut = toWBTC(amountIn);
    ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
      tokenIn: wbtc,
      tokenOut: weth,
      fee: wethWbtcFee,
      recipient: address(this),
      deadline: block.timestamp + 1,
      amountIn: wbtcAmountOut,
      amountOutMinimum: minOut,
      sqrtPriceLimitX96: 0
    });
    amountOut = ISwapRouter(routerv3).exactInputSingle(params);
    IWETH(weth).withdraw(amountOut);
    address payable recipient = address(uint160(out));
    recipient.transfer(amountOut);
  }

  function burnApproved(
    address from,
    address asset,
    uint256 amount,
    uint256 minOut,
    bytes memory destination
  ) public payable returns (uint256 amountToBurn) {
    require(asset == wbtc || asset == usdc || asset == renbtc || asset == address(0x0), "!approved-module");
    if (asset != address(0x0)) IERC20(asset).transferFrom(msg.sender, address(this), amount);
    amountToBurn = asset == wbtc ? toRenBTC(amount.sub(applyRatio(amount, burnFee))) : asset == usdc
      ? fromUSDC(minOut, amount.sub(applyRatio(amount, burnFee)))
      : asset == renbtc
      ? amount
      : fromETHToRenBTC(minOut, msg.value.sub(applyRatio(msg.value, burnFee)));
    IGateway(btcGateway).burn(destination, amountToBurn);
  }

  function toRenBTC(uint256 amountIn) internal returns (uint256 amountOut) {
    uint256 balanceStart = IERC20(renbtc).balanceOf(address(this));
    (bool success, ) = renCrv.call(abi.encodeWithSelector(IRenCrv.exchange.selector, 1, 0, amountIn));
    amountOut = IERC20(renbtc).balanceOf(address(this)).sub(balanceStart);
  }

  function fromUSDC(uint256 minOut, uint256 amountIn) internal returns (uint256 amountOut) {
    bytes memory path = abi.encodePacked(usdc, usdcWethFee, weth, wethWbtcFee, wbtc);
    ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({
      recipient: address(this),
      deadline: block.timestamp + 1,
      amountIn: amountIn,
      amountOutMinimum: minOut,
      path: path
    });
    amountOut = ISwapRouter(routerv3).exactInput(params);
    amountOut = toRenBTC(amountOut);
  }

  function fromETHToRenBTC(uint256 minOut, uint256 amountIn) internal returns (uint256 amountOut) {
    uint256 amountStart = IERC20(renbtc).balanceOf(address(this));
    ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
      tokenIn: weth,
      tokenOut: wbtc,
      fee: wethWbtcFee,
      recipient: address(this),
      deadline: block.timestamp + 1,
      amountIn: amountIn,
      amountOutMinimum: minOut,
      sqrtPriceLimitX96: 0
    });
    amountOut = ISwapRouter(routerv3).exactInputSingle{ value: amountIn }(params);
    (bool success, ) = renCrv.call(abi.encodeWithSelector(IRenCrv.exchange.selector, 1, 0, amountOut, 1));
    require(success, "!curve");
    amountOut = IERC20(renbtc).balanceOf(address(this)).sub(amountStart);
  }

  function toETH() internal returns (uint256 amountOut) {
    uint256 wbtcStart = IERC20(wbtc).balanceOf(address(this));

    uint256 amountStart = address(this).balance;
    (bool success, ) = tricrypto.call(
      abi.encodeWithSelector(ICurveTricrypto.exchange.selector, 1, 2, wbtcStart, 0, true)
    );
    amountOut = address(this).balance.sub(amountStart);
  }

  function fromUSDT(uint256 amountIn) internal returns (uint256 amountOut) {
    uint256 wbtcIn = IERC20(wbtc).balanceOf(address(this));
    (bool success, ) = tricrypto.call(
      abi.encodeWithSelector(ICurveTricrypto.exchange.selector, 0, 1, amountIn, 0, false)
    );
    require(success, "!curve");
    wbtcIn = IERC20(wbtc).balanceOf(address(this)).sub(wbtcIn);
    amountOut = toRenBTC(wbtcIn);
  }

  function toUSDT(uint256 amountIn) internal returns (uint256 amountOut) {
    uint256 wbtcOut = toWBTC(amountIn);
    amountOut = IERC20(usdt).balanceOf(address(this));
    (bool success, ) = tricrypto.call(
      abi.encodeWithSelector(ICurveTricrypto.exchange.selector, 1, 0, wbtcOut, 0, false)
    );
    require(success, "!curve");
    amountOut = IERC20(usdt).balanceOf(address(this)).sub(amountOut);
  }

  receive() external payable {
    // no-op
  }

  function earn() public {
    quote();
    toWBTC(IERC20(renbtc).balanceOf(address(this)));
    toETH();
    uint256 balance = address(this).balance;
    if (balance > ETH_RESERVE) {
      uint256 output = balance - ETH_RESERVE;
      uint256 toGovernance = applyRatio(output, governanceFee);
      bool success;
      address payable governancePayable = address(uint160(governance));
      (success, ) = governancePayable.call{ value: toGovernance, gas: gasleft() }("");
      require(success, "error sending to governance");
      address payable strategistPayable = address(uint160(strategist));
      (success, ) = strategistPayable.call{ value: output.sub(toGovernance), gas: gasleft() }("");
      require(success, "error sending to strategist");
    }
  }

  function computeRenBTCGasFee(uint256 gasCost, uint256 gasPrice) internal view returns (uint256 result) {
    result = gasCost.mul(tx.gasprice).mul(renbtcForOneETHPrice).div(uint256(1 ether));
  }

  function deductMintFee(uint256 amountIn, uint256 multiplier) internal view returns (uint256 amount) {
    amount = amountIn.sub(applyFee(amountIn, fee, multiplier));
  }

  function deductBurnFee(uint256 amountIn, uint256 multiplier) internal view returns (uint256 amount) {
    amount = amountIn.sub(applyFee(amountIn, burnFee, multiplier));
  }

  function applyFee(
    uint256 amountIn,
    uint256 _fee,
    uint256 multiplier
  ) internal view returns (uint256 amount) {
    amount = computeRenBTCGasFee(GAS_COST.add(keeperReward.div(tx.gasprice)), tx.gasprice).add(
      applyRatio(amountIn, _fee)
    );
  }

  struct LoanParams {
    address to;
    address asset;
    uint256 nonce;
    uint256 amount;
    address module;
    address underwriter;
    bytes data;
    uint256 minOut;
    uint256 _mintAmount;
    uint256 gasDiff;
  }

  function toTypedDataHash(LoanParams memory params) internal view returns (bytes32 result) {
    bytes32 digest = _hashTypedDataV4(
      keccak256(
        abi.encode(
          keccak256(
            "TransferRequest(address asset,uint256 amount,address underwriter,address module,uint256 nonce,bytes data)"
          ),
          params.asset,
          params.amount,
          params.underwriter,
          params.module,
          params.nonce,
          keccak256(params.data)
        )
      )
    );
    return digest;
  }

  function repay(
    address underwriter,
    address to,
    address asset,
    uint256 amount,
    uint256 actualAmount,
    uint256 nonce,
    address module,
    bytes32 nHash,
    bytes memory data,
    bytes memory signature
  ) public returns (uint256 amountOut) {
    require(msg.data.length <= 516, "too much calldata");
    uint256 _gasBefore = gasleft();
    LoanParams memory params;
    {
      require(
        module == wbtc || module == usdc || module == renbtc || module == usdt || module == address(0x0),
        "!approved-module"
      );
      params = LoanParams({
        to: to,
        asset: asset,
        amount: amount,
        nonce: nonce,
        module: module,
        underwriter: underwriter,
        data: data,
        minOut: 1,
        _mintAmount: 0,
        gasDiff: computeCalldataGasDiff()
      });
      if (data.length > 0) (params.minOut) = abi.decode(data, (uint256));
    }
    bytes32 digest = toTypedDataHash(params);

    params._mintAmount = IGateway(btcGateway).mint(
      keccak256(abi.encode(params.to, params.nonce, params.module, params.data)),
      actualAmount,
      nHash,
      signature
    );

    {
      amountOut = module == wbtc ? toWBTC(deductMintFee(params._mintAmount, 1)) : module == address(0x0)
        ? renBTCtoETH(params.minOut, deductMintFee(params._mintAmount, 1), to)
        : module == usdt
        ? toUSDT(deductMintFee(params._mintAmount, 1))
        : module == usdc
        ? toUSDC(params.minOut, deductMintFee(params._mintAmount, 1), to)
        : deductMintFee(params._mintAmount, 1);
    }
    {
      if (module != usdc && module != address(0x0)) IERC20(module).safeTransfer(to, amountOut);
    }
    {
      tx.origin.transfer(
        Math.min(
          _gasBefore.sub(gasleft()).add(REPAY_GAS_DIFF).add(params.gasDiff).mul(tx.gasprice).add(keeperReward),
          address(this).balance
        )
      );
    }
  }

  function computeBurnNonce(BurnLocals memory params) internal view returns (uint256 result) {
    result = uint256(
      keccak256(
        abi.encodePacked(params.asset, params.amount, params.deadline, params.nonce, params.data, params.destination)
      )
    );
    while (result < block.timestamp) {
      // negligible probability of this
      result = uint256(keccak256(abi.encodePacked(result)));
    }
  }

  function computeERC20PermitDigest(bytes32 domainSeparator, BurnLocals memory params)
    internal
    view
    returns (bytes32 result)
  {
    result = keccak256(
      abi.encodePacked(
        "\x19\x01",
        domainSeparator,
        keccak256(abi.encode(PERMIT_TYPEHASH, params.to, address(this), params.nonce, computeBurnNonce(params), true))
      )
    );
  }

  struct BurnLocals {
    address to;
    address asset;
    uint256 amount;
    uint256 deadline;
    uint256 nonce;
    bytes data;
    uint256 minOut;
    uint256 burnNonce;
    uint256 gasBefore;
    uint256 gasDiff;
    uint8 v;
    bytes32 r;
    bytes32 s;
    bytes destination;
    bytes signature;
  }

  function burn(
    address to,
    address asset,
    uint256 amount,
    uint256 deadline,
    bytes memory data,
    bytes memory destination,
    bytes memory signature
  ) public returns (uint256 amountToBurn) {
    require(msg.data.length <= 580, "too much calldata");
    BurnLocals memory params = BurnLocals({
      to: to,
      asset: asset,
      amount: amount,
      deadline: deadline,
      data: data,
      nonce: 0,
      burnNonce: 0,
      v: uint8(0),
      r: bytes32(0),
      s: bytes32(0),
      destination: destination,
      signature: signature,
      gasBefore: gasleft(),
      minOut: 1,
      gasDiff: 0
    });
    {
      params.gasDiff = computeCalldataGasDiff();
      if (params.data.length > 0) (params.minOut) = abi.decode(params.data, (uint256));
    }
    require(block.timestamp < params.deadline, "!deadline");
    if (params.asset == wbtc) {
      params.nonce = nonces[to];
      nonces[params.to]++;
      require(
        params.to == ECDSA.recover(computeERC20PermitDigest(PERMIT_DOMAIN_SEPARATOR_WBTC, params), params.signature),
        "!signature"
      ); //  wbtc does not implement ERC20Permit
      {
        IERC20(params.asset).transferFrom(params.to, address(this), params.amount);
        amountToBurn = toRenBTC(deductBurnFee(params.amount, 1));
      }
    } else if (params.asset == usdt) {
      params.nonce = noncesUsdt[to];
      noncesUsdt[params.to]++;
      require(
        params.to == ECDSA.recover(computeERC20PermitDigest(getUsdtDomainSeparator(), params), params.signature),
        "!signature"
      ); //  usdt does not implement ERC20Permit
      {
        (bool success, ) = params.asset.call(
          abi.encodeWithSelector(IERC20.transferFrom.selector, params.to, address(this), params.amount)
        );
        require(success, "!usdt");
      }
      amountToBurn = deductBurnFee(fromUSDT(params.amount), 1);
    } else if (params.asset == renbtc) {
      {
        params.nonce = IERC2612Permit(params.asset).nonces(params.to);
        params.burnNonce = computeBurnNonce(params);
      }
      {
        (params.v, params.r, params.s) = SplitSignatureLib.splitSignature(params.signature);
        IERC2612Permit(params.asset).permit(
          params.to,
          address(this),
          params.nonce,
          params.burnNonce,
          true,
          params.v,
          params.r,
          params.s
        );
      }
      {
        IERC20(params.asset).transferFrom(params.to, address(this), params.amount);
      }
      amountToBurn = deductBurnFee(params.amount, 1);
    } else if (params.asset == usdc) {
      {
        params.nonce = IERC2612Permit(params.asset).nonces(params.to);
        params.burnNonce = computeBurnNonce(params);
      }
      {
        (params.v, params.r, params.s) = SplitSignatureLib.splitSignature(params.signature);
        IERC2612Permit(params.asset).permit(
          params.to,
          address(this),
          params.amount,
          params.burnNonce,
          params.v,
          params.r,
          params.s
        );
      }
      {
        IERC20(params.asset).transferFrom(params.to, address(this), params.amount);
      }
      amountToBurn = deductBurnFee(fromUSDC(params.minOut, params.amount), 1);
    } else revert("!supported-asset");
    {
      IGateway(btcGateway).burn(params.destination, amountToBurn);
    }
    {
      tx.origin.transfer(
        Math.min(
          params.gasBefore.sub(gasleft()).add(BURN_GAS_DIFF).add(params.gasDiff).mul(tx.gasprice).add(keeperReward),
          address(this).balance
        )
      );
    }
  }

  function burnETH(uint256 minOut, bytes memory destination) public payable returns (uint256 amountToBurn) {
    amountToBurn = fromETHToRenBTC(minOut, msg.value.sub(applyRatio(msg.value, burnFee)));
    IGateway(btcGateway).burn(destination, amountToBurn);
  }

  function fallbackMint(
    address underwriter,
    address to,
    address asset,
    uint256 amount,
    uint256 actualAmount,
    uint256 nonce,
    address module,
    bytes32 nHash,
    bytes memory data,
    bytes memory signature
  ) public {
    LoanParams memory params = LoanParams({
      to: to,
      asset: asset,
      amount: amount,
      nonce: nonce,
      module: module,
      underwriter: underwriter,
      data: data,
      minOut: 1,
      _mintAmount: 0,
      gasDiff: 0
    });
    bytes32 digest = toTypedDataHash(params);
    uint256 _actualAmount = IGateway(btcGateway).mint(
      keccak256(abi.encode(params.to, params.nonce, params.module, params.data)),
      actualAmount,
      nHash,
      signature
    );
    IERC20(asset).safeTransfer(to, _actualAmount);
  }
}

File 2 of 140 : IUniswapV2Router02.sol
pragma solidity >=0.6.2;

import './IUniswapV2Router01.sol';

interface IUniswapV2Router02 is IUniswapV2Router01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}

File 3 of 140 : ISwapRouter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;

import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';

/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface ISwapRouter is IUniswapV3SwapCallback {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);

    struct ExactInputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);

    struct ExactOutputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);

    struct ExactOutputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}

File 4 of 140 : UniswapV2Library.sol
pragma solidity >=0.5.0;

import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";

import "@openzeppelin/contracts/math/SafeMath.sol";

library UniswapV2Library {
  using SafeMath for uint256;

  // returns sorted token addresses, used to handle return values from pairs sorted in this order
  function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
    require(tokenA != tokenB, "UniswapV2Library: IDENTICAL_ADDRESSES");
    (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
    require(token0 != address(0), "UniswapV2Library: ZERO_ADDRESS");
  }

  // calculates the CREATE2 address for a pair without making any external calls
  function pairFor(
    address factory,
    address tokenA,
    address tokenB
  ) internal pure returns (address pair) {
    (address token0, address token1) = sortTokens(tokenA, tokenB);
    pair = address(
      uint160(
        uint256(
          keccak256(
            abi.encodePacked(
              hex"ff",
              factory,
              keccak256(abi.encodePacked(token0, token1)),
              hex"96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" // init code hash
            )
          )
        )
      )
    );
  }

  // fetches and sorts the reserves for a pair
  function getReserves(
    address factory,
    address tokenA,
    address tokenB
  ) internal view returns (uint256 reserveA, uint256 reserveB) {
    (address token0, ) = sortTokens(tokenA, tokenB);
    (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();
    (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
  }

  // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
  function quote(
    uint256 amountA,
    uint256 reserveA,
    uint256 reserveB
  ) internal pure returns (uint256 amountB) {
    require(amountA > 0, "UniswapV2Library: INSUFFICIENT_AMOUNT");
    require(reserveA > 0 && reserveB > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");
    amountB = amountA.mul(reserveB) / reserveA;
  }
}

File 5 of 140 : ZeroLib.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0 <0.8.0;

/**
@title helper functions for the Zero contract suite
@author raymondpulver
*/
library ZeroLib {
  enum LoanStatusCode {
    UNINITIALIZED,
    UNPAID,
    PAID
  }
  struct LoanParams {
    address to;
    address asset;
    uint256 amount;
    uint256 nonce;
    address module;
    bytes data;
  }
  struct MetaParams {
    address from;
    uint256 nonce;
    bytes data;
    address module;
    address asset;
  }
  struct LoanStatus {
    address underwriter;
    LoanStatusCode status;
  }
  struct BalanceSheet {
    uint128 loaned;
    uint128 required;
    uint256 repaid;
  }
}

File 6 of 140 : IERC2612Permit.sol
interface IERC2612Permit {
  /**
   * @dev Sets `amount` 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:
   *
   * - `owner` cannot be the zero address.
   * - `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 holder,
    address spender,
    uint256 nonce,
    uint256 expiry,
    bool allowed,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

  function permit(
    address holder,
    address spender,
    uint256 value,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

  /**
   * @dev Returns the current ERC2612 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);

  function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 7 of 140 : IRenCrv.sol
interface IRenCrv {
  function exchange(
    int128 i,
    int128 j,
    uint256 dx,
    uint256 min_dy
  ) external;
}

File 8 of 140 : SplitSignatureLib.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0;

library SplitSignatureLib {
  function splitSignature(bytes memory signature)
    internal
    pure
    returns (
      uint8 v,
      bytes32 r,
      bytes32 s
    )
  {
    if (signature.length == 65) {
      assembly {
        r := mload(add(signature, 0x20))
        s := mload(add(signature, 0x40))
        v := byte(0, mload(add(signature, 0x60)))
      }
    } else if (signature.length == 64) {
      assembly {
        r := mload(add(signature, 0x20))
        let vs := mload(add(signature, 0x40))
        s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
        v := add(shr(255, vs), 27)
      }
    }
  }
}

File 9 of 140 : IBadgerSettPeak.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0;

interface IBadgerSettPeak {
  function mint(
    uint256,
    uint256,
    bytes32[] calldata
  ) external returns (uint256);

  function redeem(uint256, uint256) external returns (uint256);
}

File 10 of 140 : IWETH.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.5.0 <0.8.0;

interface IWETH {
  function withdraw(uint256) external;
}

File 11 of 140 : ICurveFi.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0;

interface ICurveFi {
  function add_liquidity(uint256[2] calldata amounts, uint256 idx) external;

  function remove_liquidity_one_coin(
    uint256,
    int128,
    uint256
  ) external;
}

File 12 of 140 : IGateway.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

interface IMintGateway {
  function mint(
    bytes32 _pHash,
    uint256 _amount,
    bytes32 _nHash,
    bytes calldata _sig
  ) external returns (uint256);

  function mintFee() external view returns (uint256);
}

interface IBurnGateway {
  function burn(bytes memory _to, uint256 _amountScaled) external returns (uint256);

  function burnFee() external view returns (uint256);
}

interface IGateway is IMintGateway, IBurnGateway {

}

/*
interface IGateway is IMintGateway, IBurnGateway {
    function mint(
        bytes32 _pHash,
        uint256 _amount,
        bytes32 _nHash,
        bytes calldata _sig
    ) external returns (uint256);

    function mintFee() external view returns (uint256);

    function burn(bytes calldata _to, uint256 _amountScaled)
        external
        returns (uint256);

    function burnFee() external view returns (uint256);
}
*/

File 13 of 140 : ICurveTricrypto.sol
pragma solidity >=0.6.0 <0.8.0;

interface ICurveTricrypto {
  function exchange(
    uint256 i,
    uint256 j,
    uint256 dx,
    uint256 min_dy,
    bool use_eth
  ) external payable;
}

File 14 of 140 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 15 of 140 : IyVault.sol
pragma solidity >=0.6.0 <0.8.0;

import { IERC20 } from "oz410/token/ERC20/IERC20.sol";

abstract contract IyVault is IERC20 {
  function pricePerShare() external view virtual returns (uint256);

  function getPricePerFullShare() external view virtual returns (uint256);

  function totalAssets() external view virtual returns (uint256);

  function deposit(uint256 _amount) external virtual returns (uint256);

  function withdraw(uint256 maxShares) external virtual returns (uint256);

  function want() external virtual returns (address);

  function decimals() external view virtual returns (uint8);
}

File 16 of 140 : ISett.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0;

interface ISett {
  function deposit(uint256) external;

  function withdraw(uint256) external;
}

File 17 of 140 : Math.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @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, so we distribute
        return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
    }
}

File 18 of 140 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}

File 19 of 140 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using SafeMath for uint256;
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 20 of 140 : ECDSA.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        // Check the signature length
        if (signature.length != 65) {
            revert("ECDSA: invalid signature length");
        }

        // Divide the signature in r, s and v variables
        bytes32 r;
        bytes32 s;
        uint8 v;

        // ecrecover takes the signature parameters, and the only way to get them
        // currently is to use assembly.
        // solhint-disable-next-line no-inline-assembly
        assembly {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := byte(0, mload(add(signature, 0x60)))
        }

        return recover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover-bytes32-bytes-} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
        require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        require(signer != address(0), "ECDSA: invalid signature");

        return signer;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * replicates the behavior of the
     * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
     * JSON-RPC method.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }
}

File 21 of 140 : EIP712Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;
import "../proxy/Initializable.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].
 *
 * _Available since v3.4._
 */
abstract contract EIP712Upgradeable is Initializable {
    /* solhint-disable var-name-mixedcase */
    bytes32 private _HASHED_NAME;
    bytes32 private _HASHED_VERSION;
    bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
    /* solhint-enable var-name-mixedcase */

    /**
     * @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].
     */
    function __EIP712_init(string memory name, string memory version) internal initializer {
        __EIP712_init_unchained(name, version);
    }

    function __EIP712_init_unchained(string memory name, string memory version) internal initializer {
        bytes32 hashedName = keccak256(bytes(name));
        bytes32 hashedVersion = keccak256(bytes(version));
        _HASHED_NAME = hashedName;
        _HASHED_VERSION = hashedVersion;
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash());
    }

    function _buildDomainSeparator(bytes32 typeHash, bytes32 name, bytes32 version) private view returns (bytes32) {
        return keccak256(
            abi.encode(
                typeHash,
                name,
                version,
                _getChainId(),
                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 keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), structHash));
    }

    function _getChainId() private view returns (uint256 chainId) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        // solhint-disable-next-line no-inline-assembly
        assembly {
            chainId := chainid()
        }
    }

    /**
     * @dev The hash of the name parameter for the EIP712 domain.
     *
     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
     * are a concern.
     */
    function _EIP712NameHash() internal virtual view returns (bytes32) {
        return _HASHED_NAME;
    }

    /**
     * @dev The hash of the version parameter for the EIP712 domain.
     *
     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
     * are a concern.
     */
    function _EIP712VersionHash() internal virtual view returns (bytes32) {
        return _HASHED_VERSION;
    }
    uint256[50] private __gap;
}

File 22 of 140 : IUniswapV2Router01.sol
pragma solidity >=0.6.2;

interface IUniswapV2Router01 {
    function factory() external pure returns (address);
    function WETH() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity);
    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountToken, uint amountETH);
    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETHWithPermit(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountToken, uint amountETH);
    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapTokensForExactTokens(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);
    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);

    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}

File 23 of 140 : IUniswapV3SwapCallback.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @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 24 of 140 : IUniswapV2Pair.sol
pragma solidity >=0.5.0;

interface IUniswapV2Pair {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function kLast() external view returns (uint);

    function mint(address to) external returns (uint liquidity);
    function burn(address to) external returns (uint amount0, uint amount1);
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function skim(address to) external;
    function sync() external;

    function initialize(address, address) external;
}

File 25 of 140 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 26 of 140 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.2 <0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain`call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
      return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 27 of 140 : Initializable.sol
// SPDX-License-Identifier: MIT

// solhint-disable-next-line compiler-version
pragma solidity >=0.4.24 <0.8.0;

import "../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 */
abstract contract Initializable {

    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }

    /// @dev Returns true if and only if the function is running in the constructor
    function _isConstructor() private view returns (bool) {
        return !AddressUpgradeable.isContract(address(this));
    }
}

File 28 of 140 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain`call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
      return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 29 of 140 : DummyBurnCaller.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.9.0;
import { BadgerBridgeZeroController } from "../controllers/BadgerBridgeZeroController.sol";
import { IERC2612Permit } from "../interfaces/IERC2612Permit.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../libraries/SplitSignatureLib.sol";

contract DummyBurnCaller {
  constructor(address controller, address renzec) {
    IERC20(renzec).approve(controller, ~uint256(0) >> 2);
  }

  function callBurn(
    address controller,
    address from,
    address asset,
    uint256 amount,
    uint256 deadline,
    bytes memory signature,
    bytes memory destination
  ) public {
    (uint8 v, bytes32 r, bytes32 s) = SplitSignatureLib.splitSignature(signature);
    uint256 nonce = IERC2612Permit(asset).nonces(from);
    IERC2612Permit(asset).permit(from, address(this), nonce, deadline, true, v, r, s);
    address payable _controller = address(uint160(controller));
    BadgerBridgeZeroController(_controller).burnApproved(from, asset, amount, 1, destination);
  }
}

File 30 of 140 : ZeroController.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.8.0;

import { EIP712Upgradeable } from "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol";
import { IERC20 } from "oz410/token/ERC20/ERC20.sol";
import { SafeERC20 } from "oz410/token/ERC20/SafeERC20.sol";
import { IZeroMeta } from "../interfaces/IZeroMeta.sol";
import { IZeroModule } from "../interfaces/IZeroModule.sol";
import { ZeroUnderwriterLock } from "../underwriter/ZeroUnderwriterLock.sol";
import { ZeroLib } from "../libraries/ZeroLib.sol";
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { ControllerUpgradeable } from "./ControllerUpgradeable.sol";
import { EIP712 } from "oz410/drafts/EIP712.sol";
import { ECDSA } from "oz410/cryptography/ECDSA.sol";
import { FactoryLib } from "../libraries/factory/FactoryLib.sol";
import { SplitSignatureLib } from "../libraries/SplitSignatureLib.sol";
import { ZeroUnderwriterLockBytecodeLib } from "../libraries/bytecode/ZeroUnderwriterLockBytecodeLib.sol";
import { IGateway } from "../interfaces/IGateway.sol";
import { IGatewayRegistry } from "../interfaces/IGatewayRegistry.sol";
import { IStrategy } from "../interfaces/IStrategy.sol";
import { SafeMath } from "oz410/math/SafeMath.sol";
import { LockForImplLib } from "../libraries/LockForImplLib.sol";
import { IERC2612Permit } from "../interfaces/IERC2612Permit.sol";
import "../interfaces/IConverter.sol";
import { ZeroControllerTemplate } from "./ZeroControllerTemplate.sol";
import "@openzeppelin/contracts/math/Math.sol";
import "hardhat/console.sol";

/**
@title upgradeable contract which determines the authority of a given address to sign off on loans
@author raymondpulver
*/
contract ZeroController is ZeroControllerTemplate {
  using SafeMath for uint256;
  using SafeERC20 for *;

  function getChainId() internal view returns (uint8 response) {
    assembly {
      response := chainid()
    }
  }

  function setFee(uint256 _fee) public {
    require(msg.sender == governance, "!governance");
    fee = _fee;
  }

  function approveModule(address module, bool isApproved) public virtual {
    require(msg.sender == governance, "!governance");
    approvedModules[module] = isApproved;
  }

  function setBaseFeeByAsset(address _asset, uint256 _fee) public {
    require(msg.sender == governance, "!governance");
    baseFeeByAsset[_asset] = _fee;
  }

  function deductFee(uint256 _amount, address _asset) internal view returns (uint256 result) {
    result = _amount.mul(uint256(1 ether).sub(fee)).div(uint256(1 ether)).sub(baseFeeByAsset[_asset]);
  }

  function addFee(uint256 _amount, address _asset) internal view returns (uint256 result) {
    result = _amount.mul(uint256(1 ether).add(fee)).div(uint256(1 ether)).add(baseFeeByAsset[_asset]);
  }

  function initialize(address _rewards, address _gatewayRegistry) public {
    __Ownable_init_unchained();
    __Controller_init_unchained(_rewards);
    __EIP712_init_unchained("ZeroController", "1");
    gatewayRegistry = _gatewayRegistry;
    underwriterLockImpl = FactoryLib.deployImplementation(
      ZeroUnderwriterLockBytecodeLib.get(),
      "zero.underwriter.lock-implementation"
    );

    maxGasPrice = 100e9;
    maxGasRepay = 250000;
    maxGasLoan = 500000;
  }

  modifier onlyUnderwriter() {
    require(ownerOf[uint256(uint160(address(lockFor(msg.sender))))] != address(0x0), "must be called by underwriter");
    _;
  }

  function setGasParameters(
    uint256 _maxGasPrice,
    uint256 _maxGasRepay,
    uint256 _maxGasLoan,
    uint256 _maxGasBurn
  ) public {
    require(msg.sender == governance, "!governance");
    maxGasPrice = _maxGasPrice;
    maxGasRepay = _maxGasRepay;
    maxGasLoan = _maxGasLoan;
    maxGasBurn = _maxGasBurn;
  }

  function balanceOf(address _owner) public view override returns (uint256 result) {
    result = _balanceOf(_owner);
  }

  function lockFor(address underwriter) public view returns (ZeroUnderwriterLock result) {
    result = LockForImplLib.lockFor(address(this), underwriterLockImpl, underwriter);
  }

  function mint(address underwriter, address vault) public virtual {
    address lock = FactoryLib.deploy(underwriterLockImpl, bytes32(uint256(uint160(underwriter))));
    ZeroUnderwriterLock(lock).initialize(vault);
    ownerOf[uint256(uint160(lock))] = msg.sender;
  }

  function _typedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
    return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
  }

  function fallbackMint(
    address underwriter,
    address to,
    address asset,
    uint256 amount,
    uint256 actualAmount,
    uint256 nonce,
    address module,
    bytes32 nHash,
    bytes memory data,
    bytes memory signature
  ) public {
    ZeroLib.LoanParams memory params = ZeroLib.LoanParams({
      to: to,
      asset: asset,
      amount: amount,
      nonce: nonce,
      module: module,
      data: data
    });
    bytes32 digest = toTypedDataHash(params, underwriter);
    require(loanStatus[digest].status == ZeroLib.LoanStatusCode.UNINITIALIZED, "loan already exists");
    uint256 _actualAmount = IGateway(IGatewayRegistry(gatewayRegistry).getGatewayByToken(asset)).mint(
      keccak256(abi.encode(params.to, params.nonce, params.module, params.data)),
      actualAmount,
      nHash,
      signature
    );
    delete (loanStatus[digest]);
    IERC20(asset).safeTransfer(to, _actualAmount);
  }

  function repay(
    address underwriter,
    address to,
    address asset,
    uint256 amount,
    uint256 actualAmount,
    uint256 nonce,
    address module,
    bytes32 nHash,
    bytes memory data,
    bytes memory signature
  ) public {
    uint256 _gasBefore = gasleft();
    ZeroLib.LoanParams memory params = ZeroLib.LoanParams({
      to: to,
      asset: asset,
      amount: amount,
      nonce: nonce,
      module: module,
      data: data
    });
    bytes32 digest = toTypedDataHash(params, underwriter);
    require(loanStatus[digest].status == ZeroLib.LoanStatusCode.UNPAID, "loan is not in the UNPAID state");

    ZeroUnderwriterLock lock = ZeroUnderwriterLock(lockFor(msg.sender));
    lock.trackIn(actualAmount);
    uint256 _mintAmount = IGateway(IGatewayRegistry(gatewayRegistry).getGatewayByToken(asset)).mint(
      keccak256(abi.encode(params.to, params.nonce, params.module, params.data)),
      actualAmount,
      nHash,
      signature
    );
    IZeroModule(module).repayLoan(params.to, asset, _mintAmount, nonce, data);
    depositAll(asset);
    uint256 _gasRefund = Math.min(_gasBefore.sub(gasleft()), maxGasLoan).mul(maxGasPrice);
    IStrategy(strategies[params.asset]).permissionedEther(tx.origin, _gasRefund);
  }

  function depositAll(address _asset) internal {
    // deposit all of the asset in the vault
    uint256 _balance = IERC20(_asset).balanceOf(address(this));
    IERC20(_asset).safeTransfer(strategies[_asset], _balance);
  }

  function toTypedDataHash(ZeroLib.LoanParams memory params, address underwriter)
    internal
    view
    returns (bytes32 result)
  {
    bytes32 digest = _hashTypedDataV4(
      keccak256(
        abi.encode(
          keccak256(
            "TransferRequest(address asset,uint256 amount,address underwriter,address module,uint256 nonce,bytes data)"
          ),
          params.asset,
          params.amount,
          underwriter,
          params.module,
          params.nonce,
          keccak256(params.data)
        )
      )
    );
    return digest;
  }

  function toMetaTypedDataHash(ZeroLib.MetaParams memory params, address underwriter)
    internal
    view
    returns (bytes32 result)
  {
    result = _hashTypedDataV4(
      keccak256(
        abi.encode(
          keccak256("MetaRequest(address asset,address underwriter,address module,uint256 nonce,bytes data)"),
          params.asset,
          underwriter,
          params.module,
          params.nonce,
          keccak256(params.data)
        )
      )
    );
  }

  function convertGasUsedToRen(uint256 _gasUsed, address asset) internal view returns (uint256 gasUsedInRen) {
    address converter = converters[IStrategy(strategies[asset]).nativeWrapper()][
      IStrategy(strategies[asset]).vaultWant()
    ];
    gasUsedInRen = IConverter(converter).estimate(_gasUsed); //convert txGas from ETH to wBTC
    gasUsedInRen = IConverter(converters[IStrategy(strategies[asset]).vaultWant()][asset]).estimate(gasUsedInRen);
    // ^convert txGas from wBTC to renBTC
  }

  function loan(
    address to,
    address asset,
    uint256 amount,
    uint256 nonce,
    address module,
    bytes memory data,
    bytes memory userSignature
  ) public onlyUnderwriter {
    require(approvedModules[module], "!approved");
    uint256 _gasBefore = gasleft();
    ZeroLib.LoanParams memory params = ZeroLib.LoanParams({
      to: to,
      asset: asset,
      amount: amount,
      nonce: nonce,
      module: module,
      data: data
    });
    bytes32 digest = toTypedDataHash(params, msg.sender);
    require(ECDSA.recover(digest, userSignature) == params.to, "invalid signature");
    require(loanStatus[digest].status == ZeroLib.LoanStatusCode.UNINITIALIZED, "already spent this loan");
    loanStatus[digest] = ZeroLib.LoanStatus({ underwriter: msg.sender, status: ZeroLib.LoanStatusCode.UNPAID });
    uint256 actual = params.amount.sub(params.amount.mul(uint256(25e15)).div(1e18));

    ZeroUnderwriterLock(lockFor(msg.sender)).trackOut(params.module, actual);
    uint256 _txGas = maxGasPrice.mul(maxGasRepay.add(maxGasLoan));
    _txGas = convertGasUsedToRen(_txGas, params.asset);
    // ^convert txGas from ETH to renBTC
    uint256 _amountSent = IStrategy(strategies[params.asset]).permissionedSend(
      module,
      deductFee(params.amount, params.asset).sub(_txGas)
    );
    IZeroModule(module).receiveLoan(params.to, params.asset, _amountSent, params.nonce, params.data);
    uint256 _gasRefund = Math.min(_gasBefore.sub(gasleft()), maxGasLoan).mul(maxGasPrice);
    IStrategy(strategies[params.asset]).permissionedEther(tx.origin, _gasRefund);
  }

  struct MetaLocals {
    uint256 gasUsed;
    uint256 gasUsedInRen;
    bytes32 digest;
    uint256 txGas;
    uint256 gasAtStart;
    uint256 gasRefund;
    uint256 balanceBefore;
    uint256 renBalanceDiff;
  }

  function meta(
    address from,
    address asset,
    address module,
    uint256 nonce,
    bytes memory data,
    bytes memory signature
  ) public onlyUnderwriter returns (uint256 gasValueAndFee) {
    require(approvedModules[module], "!approved");
    MetaLocals memory locals;
    locals.gasAtStart = gasleft();
    ZeroLib.MetaParams memory params = ZeroLib.MetaParams({
      from: from,
      asset: asset,
      module: module,
      nonce: nonce,
      data: data
    });

    ZeroUnderwriterLock lock = ZeroUnderwriterLock(lockFor(msg.sender));
    locals.digest = toMetaTypedDataHash(params, msg.sender);
    address recovered = ECDSA.recover(locals.digest, signature);
    require(recovered == params.from, "invalid signature");
    IZeroMeta(module).receiveMeta(from, asset, nonce, data);
    address converter = converters[IStrategy(strategies[params.asset]).nativeWrapper()][
      IStrategy(strategies[params.asset]).vaultWant()
    ];

    //calculate gas used
    locals.gasUsed = Math.min(locals.gasAtStart.sub(gasleft()), maxGasLoan);
    locals.gasRefund = locals.gasUsed.mul(maxGasPrice);
    locals.gasUsedInRen = convertGasUsedToRen(locals.gasRefund, params.asset);
    //deduct fee on the gas amount
    gasValueAndFee = addFee(locals.gasUsedInRen, params.asset);
    //loan out gas
    console.log(asset);
    IStrategy(strategies[params.asset]).permissionedEther(tx.origin, locals.gasRefund);
    locals.balanceBefore = IERC20(params.asset).balanceOf(address(this));
    console.log(locals.balanceBefore);
    lock.trackIn(gasValueAndFee);
    IZeroMeta(module).repayMeta(gasValueAndFee);
    locals.renBalanceDiff = IERC20(params.asset).balanceOf(address(this)).sub(locals.balanceBefore);
    console.log(IERC20(params.asset).balanceOf(address(this)));
    require(locals.renBalanceDiff >= locals.gasUsedInRen, "not enough provided for gas");
    depositAll(params.asset);
  }

  function toERC20PermitDigest(
    address token,
    address owner,
    address spender,
    uint256 value,
    uint256 deadline
  ) internal view returns (bytes32 result) {
    result = keccak256(
      abi.encodePacked(
        "\x19\x01",
        IERC2612Permit(token).DOMAIN_SEPARATOR(),
        keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, IERC2612Permit(token).nonces(owner), deadline))
      )
    );
  }

  function computeBurnNonce(
    address asset,
    uint256 amount,
    uint256 deadline,
    uint256 nonce,
    bytes memory destination
  ) public view returns (uint256 result) {
    result = uint256(keccak256(abi.encodePacked(asset, amount, deadline, nonce, destination)));
    while (result < block.timestamp) {
      // negligible probability of this
      result = uint256(keccak256(abi.encodePacked(result)));
    }
  }

  function burn(
    address to,
    address asset,
    uint256 amount,
    uint256 deadline,
    bytes memory destination,
    bytes memory signature
  ) public onlyUnderwriter {
    require(block.timestamp < deadline, "!deadline");
    {
      (uint8 v, bytes32 r, bytes32 s) = SplitSignatureLib.splitSignature(signature);
      uint256 nonce = IERC2612Permit(asset).nonces(to);
      IERC2612Permit(asset).permit(
        to,
        address(this),
        nonce,
        computeBurnNonce(asset, amount, deadline, nonce, destination),
        true,
        v,
        r,
        s
      );
    }
    IERC20(asset).transferFrom(to, address(this), amount);
    uint256 gasUsed = maxGasPrice.mul(maxGasRepay.add(maxGasBurn));
    IStrategy(strategies[asset]).permissionedEther(tx.origin, gasUsed);
    uint256 gasInRen = convertGasUsedToRen(gasUsed, asset);
    uint256 actualAmount = deductFee(amount.sub(gasInRen), asset);
    IGateway gateway = IGatewayRegistry(gatewayRegistry).getGatewayByToken(asset);
    require(IERC20(asset).approve(address(gateway), actualAmount), "!approve");
    gateway.burn(destination, actualAmount);
    depositAll(asset);
  }
}

File 31 of 140 : ERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "../../utils/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.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.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of 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 {
    using SafeMath for uint256;

    mapping (address => uint256) private _balances;

    mapping (address => mapping (address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;
    uint8 private _decimals;

    /**
     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
     * a default value of 18.
     *
     * To select a different value for {decimals}, use {_setupDecimals}.
     *
     * All three of these values are immutable: they can only be set once during
     * construction.
     */
    constructor (string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
        _decimals = 18;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual 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 value {ERC20} uses, unless {_setupDecimals} is
     * called.
     *
     * 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 returns (uint8) {
        return _decimals;
    }

    /**
     * @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:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, 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}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), 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}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        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) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(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) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is 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:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(address sender, address recipient, uint256 amount) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, 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:
     *
     * - `to` 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 = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(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);

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(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 Sets {decimals} to a value other than the default one of 18.
     *
     * WARNING: This function should only be called from the constructor. Most
     * applications that interact with token contracts will not expect
     * {decimals} to ever change, and may work incorrectly if it does.
     */
    function _setupDecimals(uint8 decimals_) internal virtual {
        _decimals = decimals_;
    }

    /**
     * @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 to 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 { }
}

File 32 of 140 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using SafeMath for uint256;
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 33 of 140 : IZeroMeta.sol
pragma solidity >=0.6.0;

interface IZeroMeta {
  function receiveMeta(
    address from,
    address asset,
    uint256 nonce,
    bytes memory data
  ) external;

  function repayMeta(uint256 value) external;
}

File 34 of 140 : IZeroModule.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0;

interface IZeroModule {
  function repayLoan(
    address _to,
    address _asset,
    uint256 _actualAmount,
    uint256 _amount,
    bytes memory _data
  ) external;

  function receiveLoan(
    address _to,
    address _asset,
    uint256 _actual,
    uint256 _nonce,
    bytes memory _data
  ) external;

  function computeReserveRequirement(uint256 _in) external view returns (uint256);

  function want() external view returns (address);
}

File 35 of 140 : ZeroUnderwriterLock.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

import { IZeroModule } from "../interfaces/IZeroModule.sol";
import { Initializable } from "oz410/proxy/Initializable.sol";
import { IERC20 } from "oz410/token/ERC20/ERC20.sol";
import { IERC721 } from "oz410/token/ERC721/IERC721.sol";
import { SafeMath } from "oz410/math/SafeMath.sol";
import { IyVault } from "../interfaces/IyVault.sol";
import { ZeroController } from "../controllers/ZeroController.sol";
import { ZeroLib } from "../libraries/ZeroLib.sol";
import { SafeERC20 } from "oz410/token/ERC20/SafeERC20.sol";

import "hardhat/console.sol";

/**
@title contract to hold locked underwriter funds while the underwriter is active
@author raymondpulver
*/
contract ZeroUnderwriterLock is Initializable {
  using SafeMath for *;
  using SafeERC20 for *;
  ZeroController public controller;
  address public vault;
  ZeroLib.BalanceSheet internal _balanceSheet;

  modifier onlyController() {
    require(msg.sender == address(controller), "!controller");
    _;
  }

  modifier onlyOwner() {
    require(msg.sender == owner(), "must be called by owner");
    _;
  }

  function balanceSheet()
    public
    view
    returns (
      uint256 loaned,
      uint256 required,
      uint256 repaid
    )
  {
    (loaned, required, repaid) = (uint256(_balanceSheet.loaned), uint256(_balanceSheet.required), _balanceSheet.repaid);
  }

  function owed() public view returns (uint256 result) {
    if (_balanceSheet.loaned >= _balanceSheet.repaid) {
      result = uint256(_balanceSheet.loaned).sub(_balanceSheet.repaid);
    } else {
      result = 0;
    }
  }

  function reserve() public view returns (uint256 result) {
    result = IyVault(vault).balanceOf(address(this)).mul(IyVault(vault).getPricePerFullShare()).div(uint256(1 ether));
  }

  function owner() public view returns (address result) {
    result = IERC721(address(controller)).ownerOf(uint256(uint160(address(this))));
  }

  /**
  @notice sets the owner to the ZeroUnderwriterNFT
  @param _vault the address of the LP token which will be either burned or redeemed when the NFT is destroyed
  */
  function initialize(address _vault) public {
    controller = ZeroController(msg.sender);
    vault = _vault;
  }

  /**
  @notice send back non vault tokens if they are stuck
  @param _token the token to send the entire balance of to the sender
  */
  function skim(address _token) public {
    require(address(vault) != _token, "cannot skim vault token");
    IERC20(_token).safeTransfer(msg.sender, IERC20(_token).balanceOf(address(this)));
  }

  /**
  @notice destroy this contract and send all vault tokens to NFT contract
  */
  function burn(address receiver) public onlyController {
    require(
      IyVault(vault).transfer(receiver, IyVault(vault).balanceOf(address(this))),
      "failed to transfer vault token to receiver"
    );
    selfdestruct(payable(msg.sender));
  }

  function trackOut(address module, uint256 amount) public {
    require(msg.sender == address(controller), "!controller");
    uint256 loanedAfter = uint256(_balanceSheet.loaned).add(amount);
    uint256 _owed = owed();
    (_balanceSheet.loaned, _balanceSheet.required) = (
      uint128(loanedAfter),
      uint128(
        uint256(_balanceSheet.required).mul(_owed).div(uint256(1 ether)).add(
          IZeroModule(module).computeReserveRequirement(amount).mul(uint256(1 ether)).div(_owed.add(amount))
        )
      )
    );
  }

  function _logSheet() internal view {
    console.log("required", _balanceSheet.required);
    console.log("loaned", _balanceSheet.loaned);
    console.log("repaid", _balanceSheet.repaid);
  }

  function trackIn(uint256 amount) public {
    require(msg.sender == address(controller), "!controller");
    uint256 _owed = owed();
    uint256 _adjusted = uint256(_balanceSheet.required).mul(_owed).div(uint256(1 ether));
    _balanceSheet.required = _owed < amount || _adjusted < amount
      ? uint128(0)
      : uint128(_adjusted.sub(amount).mul(uint256(1 ether)).div(_owed.sub(amount)));
    _balanceSheet.repaid = _balanceSheet.repaid.add(amount);
  }
}

File 36 of 140 : OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/Initializable.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 OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal initializer {
        __Context_init_unchained();
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal initializer {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = 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");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
    uint256[49] private __gap;
}

File 37 of 140 : ControllerUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.8.0;

import "oz410/token/ERC20/IERC20.sol";
import "oz410/math/SafeMath.sol";
import "oz410/utils/Address.sol";
import "oz410/token/ERC20/SafeERC20.sol";
import "oz410/proxy/Initializable.sol";

import "../interfaces/IConverter.sol";
import "../interfaces/IOneSplitAudit.sol";
import "../interfaces/IStrategy.sol";

contract ControllerUpgradeable {
  using SafeERC20 for IERC20;
  using Address for address;
  using SafeMath for uint256;

  address public governance;
  address public strategist;

  address public onesplit;
  address public rewards;
  mapping(address => address) public vaults;
  mapping(address => address) public strategies;
  mapping(address => mapping(address => address)) public converters;

  mapping(address => mapping(address => bool)) public approvedStrategies;

  uint256 public split = 500;
  uint256 public constant max = 10000;

  function __Controller_init_unchained(address _rewards) internal {
    governance = msg.sender;
    strategist = msg.sender;
    onesplit = address(0x50FDA034C0Ce7a8f7EFDAebDA7Aa7cA21CC1267e);
    rewards = _rewards;
  }

  function setRewards(address _rewards) public {
    require(msg.sender == governance, "!governance");
    rewards = _rewards;
  }

  function setStrategist(address _strategist) public {
    require(msg.sender == governance, "!governance");
    strategist = _strategist;
  }

  function setSplit(uint256 _split) public {
    require(msg.sender == governance, "!governance");
    split = _split;
  }

  function setOneSplit(address _onesplit) public {
    require(msg.sender == governance, "!governance");
    onesplit = _onesplit;
  }

  function setGovernance(address _governance) public {
    require(msg.sender == governance, "!governance");
    governance = _governance;
  }

  function setVault(address _token, address _vault) public {
    require(msg.sender == strategist || msg.sender == governance, "!strategist");
    require(vaults[_token] == address(0), "vault");
    vaults[_token] = _vault;
  }

  function approveStrategy(address _token, address _strategy) public {
    require(msg.sender == governance, "!governance");
    approvedStrategies[_token][_strategy] = true;
  }

  function revokeStrategy(address _token, address _strategy) public {
    require(msg.sender == governance, "!governance");
    approvedStrategies[_token][_strategy] = false;
  }

  function setConverter(
    address _input,
    address _output,
    address _converter
  ) public {
    require(msg.sender == strategist || msg.sender == governance, "!strategist");
    converters[_input][_output] = _converter;
  }

  function setStrategy(
    address _token,
    address _strategy,
    bool _abandon
  ) public {
    require(msg.sender == strategist || msg.sender == governance, "!strategist");
    require(approvedStrategies[_token][_strategy] == true, "!approved");

    address _current = strategies[_token];
    if (_current != address(0) || _abandon) {
      IStrategy(_current).withdrawAll();
    }
    strategies[_token] = _strategy;
  }

  function earn(address _token, uint256 _amount) public {
    address _strategy = strategies[_token];
    address _want = IStrategy(_strategy).want();
    if (_want != _token) {
      address converter = converters[_token][_want];
      IERC20(_token).safeTransfer(converter, _amount);
      _amount = IConverter(converter).convert(_strategy);
      IERC20(_want).safeTransfer(_strategy, _amount);
    } else {
      IERC20(_token).safeTransfer(_strategy, _amount);
    }
    IStrategy(_strategy).deposit();
  }

  function _balanceOf(address _token) internal view virtual returns (uint256) {
    return IStrategy(strategies[_token]).balanceOf();
  }

  function balanceOf(address _token) public view virtual returns (uint256) {
    return _balanceOf(_token);
  }

  function withdrawAll(address _token) public {
    require(msg.sender == strategist || msg.sender == governance, "!strategist");
    IStrategy(strategies[_token]).withdrawAll();
  }

  function inCaseTokensGetStuck(address _token, uint256 _amount) public {
    require(msg.sender == strategist || msg.sender == governance, "!governance");
    IERC20(_token).safeTransfer(msg.sender, _amount);
  }

  function inCaseStrategyTokenGetStuck(address _strategy, address _token) public {
    require(msg.sender == strategist || msg.sender == governance, "!governance");
    IStrategy(_strategy).withdraw(_token);
  }

  function getExpectedReturn(
    address _strategy,
    address _token,
    uint256 parts
  ) public view returns (uint256 expected) {
    uint256 _balance = IERC20(_token).balanceOf(_strategy);
    address _want = IStrategy(_strategy).want();
    (expected, ) = IOneSplitAudit(onesplit).getExpectedReturn(_token, _want, _balance, parts, 0);
  }

  // Only allows to withdraw non-core strategy tokens ~ this is over and above normal yield
  function yearn(
    address _strategy,
    address _token,
    uint256 parts
  ) public {
    require(msg.sender == strategist || msg.sender == governance, "!governance");
    // This contract should never have value in it, but just incase since this is a public call
    uint256 _before = IERC20(_token).balanceOf(address(this));
    IStrategy(_strategy).withdraw(_token);
    uint256 _after = IERC20(_token).balanceOf(address(this));
    if (_after > _before) {
      uint256 _amount = _after.sub(_before);
      address _want = IStrategy(_strategy).want();
      uint256[] memory _distribution;
      uint256 _expected;
      _before = IERC20(_want).balanceOf(address(this));
      IERC20(_token).safeApprove(onesplit, 0);
      IERC20(_token).safeApprove(onesplit, _amount);
      (_expected, _distribution) = IOneSplitAudit(onesplit).getExpectedReturn(_token, _want, _amount, parts, 0);
      IOneSplitAudit(onesplit).swap(_token, _want, _amount, _expected, _distribution, 0);
      _after = IERC20(_want).balanceOf(address(this));
      if (_after > _before) {
        _amount = _after.sub(_before);
        uint256 _reward = _amount.mul(split).div(max);
        earn(_want, _amount.sub(_reward));
        IERC20(_want).safeTransfer(rewards, _reward);
      }
    }
  }

  function _withdraw(address _token, uint256 _amount) internal virtual {
    IStrategy(strategies[_token]).withdraw(_amount);
  }

  function withdraw(address _token, uint256 _amount) public {
    require(msg.sender == vaults[_token], "!vault");
    _withdraw(_token, _amount);
  }
}

File 38 of 140 : EIP712.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @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].
 *
 * _Available since v3.4._
 */
abstract contract EIP712 {
    /* solhint-disable var-name-mixedcase */
    // 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 _CACHED_DOMAIN_SEPARATOR;
    uint256 private immutable _CACHED_CHAIN_ID;

    bytes32 private immutable _HASHED_NAME;
    bytes32 private immutable _HASHED_VERSION;
    bytes32 private immutable _TYPE_HASH;
    /* solhint-enable var-name-mixedcase */

    /**
     * @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) {
        bytes32 hashedName = keccak256(bytes(name));
        bytes32 hashedVersion = keccak256(bytes(version));
        bytes32 typeHash = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
        _HASHED_NAME = hashedName;
        _HASHED_VERSION = hashedVersion;
        _CACHED_CHAIN_ID = _getChainId();
        _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
        _TYPE_HASH = typeHash;
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view virtual returns (bytes32) {
        if (_getChainId() == _CACHED_CHAIN_ID) {
            return _CACHED_DOMAIN_SEPARATOR;
        } else {
            return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
        }
    }

    function _buildDomainSeparator(bytes32 typeHash, bytes32 name, bytes32 version) private view returns (bytes32) {
        return keccak256(
            abi.encode(
                typeHash,
                name,
                version,
                _getChainId(),
                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 keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), structHash));
    }

    function _getChainId() private view returns (uint256 chainId) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        // solhint-disable-next-line no-inline-assembly
        assembly {
            chainId := chainid()
        }
    }
}

File 39 of 140 : ECDSA.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        // Check the signature length
        if (signature.length != 65) {
            revert("ECDSA: invalid signature length");
        }

        // Divide the signature in r, s and v variables
        bytes32 r;
        bytes32 s;
        uint8 v;

        // ecrecover takes the signature parameters, and the only way to get them
        // currently is to use assembly.
        // solhint-disable-next-line no-inline-assembly
        assembly {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := byte(0, mload(add(signature, 0x60)))
        }

        return recover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover-bytes32-bytes-} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
        require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        require(signer != address(0), "ECDSA: invalid signature");

        return signer;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * replicates the behavior of the
     * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
     * JSON-RPC method.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }
}

File 40 of 140 : FactoryLib.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0 <0.8.0;

import { Implementation } from "./Implementation.sol";
import { Create2 } from "oz410/utils/Create2.sol";

/**
@title clone factory library
@notice deploys implementation or clones
*/
library FactoryLib {
  function assembleCreationCode(address implementation) internal pure returns (bytes memory result) {
    result = new bytes(0x37);
    bytes20 targetBytes = bytes20(implementation);
    assembly {
      let clone := add(result, 0x20)
      mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
      mstore(add(clone, 0x14), targetBytes)
      mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
    }
  }

  function computeAddress(
    address creator,
    address implementation,
    bytes32 salt
  ) internal pure returns (address result) {
    result = Create2.computeAddress(salt, keccak256(assembleCreationCode(implementation)), creator);
  }

  function computeImplementationAddress(
    address creator,
    bytes32 bytecodeHash,
    string memory id
  ) internal pure returns (address result) {
    result = Create2.computeAddress(keccak256(abi.encodePacked(id)), bytecodeHash, creator);
  }

  /// @notice Deploys a given master Contract as a clone.
  /// Any ETH transferred with this call is forwarded to the new clone.
  /// Emits `LogDeploy`.
  /// @param implementation Address of implementation
  /// @param salt Salt to use
  /// @return cloneAddress Address of the created clone contract.
  function deploy(address implementation, bytes32 salt) internal returns (address cloneAddress) {
    bytes memory creationCode = assembleCreationCode(implementation);
    assembly {
      cloneAddress := create2(0, add(0x20, creationCode), 0x37, salt)
    }
  }

  function deployImplementation(bytes memory creationCode, string memory id) internal returns (address implementation) {
    bytes32 salt = keccak256(abi.encodePacked(id));
    assembly {
      implementation := create2(0, add(0x20, creationCode), mload(creationCode), salt)
    }
  }
}

File 41 of 140 : ZeroUnderwriterLockBytecodeLib.sol
// SPDX-License-Identifier: MIT

import { ZeroUnderwriterLock } from "../../underwriter/ZeroUnderwriterLock.sol";

library ZeroUnderwriterLockBytecodeLib {
  function get() external pure returns (bytes memory result) {
    result = type(ZeroUnderwriterLock).creationCode;
  }
}

File 42 of 140 : IGatewayRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

import { IERC20 } from "oz410/token/ERC20/IERC20.sol";

import "./IGateway.sol";

/// @notice GatewayRegistry is a mapping from assets to their associated
/// RenERC20 and Gateway contracts.
interface IGatewayRegistry {
  /// @dev The symbol is included twice because strings have to be hashed
  /// first in order to be used as a log index/topic.
  event LogGatewayRegistered(
    string _symbol,
    string indexed _indexedSymbol,
    address indexed _tokenAddress,
    address indexed _gatewayAddress
  );
  event LogGatewayDeregistered(
    string _symbol,
    string indexed _indexedSymbol,
    address indexed _tokenAddress,
    address indexed _gatewayAddress
  );
  event LogGatewayUpdated(
    address indexed _tokenAddress,
    address indexed _currentGatewayAddress,
    address indexed _newGatewayAddress
  );

  /// @dev To get all the registered gateways use count = 0.
  function getGateways(address _start, uint256 _count) external view returns (address[] memory);

  /// @dev To get all the registered RenERC20s use count = 0.
  function getRenTokens(address _start, uint256 _count) external view returns (address[] memory);

  /// @notice Returns the Gateway contract for the given RenERC20
  ///         address.
  ///
  /// @param _tokenAddress The address of the RenERC20 contract.
  function getGatewayByToken(address _tokenAddress) external view returns (IGateway);

  /// @notice Returns the Gateway contract for the given RenERC20
  ///         symbol.
  ///
  /// @param _tokenSymbol The symbol of the RenERC20 contract.
  function getGatewayBySymbol(string calldata _tokenSymbol) external view returns (IGateway);

  /// @notice Returns the RenERC20 address for the given token symbol.
  ///
  /// @param _tokenSymbol The symbol of the RenERC20 contract to
  ///        lookup.
  function getTokenBySymbol(string calldata _tokenSymbol) external view returns (IERC20);
}

File 43 of 140 : IStrategy.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

interface StrategyAPI {
  function name() external view returns (string memory);

  function vault() external view returns (address);

  function nativeWrapper() external view returns (address);

  function want() external view returns (address);

  function vaultWant() external view returns (address);

  function apiVersion() external pure returns (string memory);

  function keeper() external view returns (address);

  function isActive() external view returns (bool);

  function delegatedAssets() external view returns (uint256);

  function estimatedTotalAssets() external view returns (uint256);

  function tendTrigger(uint256 callCost) external view returns (bool);

  function tend() external;

  function harvestTrigger(uint256 callCost) external view returns (bool);

  function harvest() external;

  event Harvested(uint256 profit, uint256 loss, uint256 debtPayment, uint256 debtOutstanding);
}

abstract contract IStrategy is StrategyAPI {
  function permissionedSend(address _module, uint256 _amount) external virtual returns (uint256);

  function withdrawAll() external virtual;

  function deposit() external virtual;

  function balanceOf() external view virtual returns (uint256);

  function withdraw(uint256) external virtual;

  function withdraw(address) external virtual;

  function permissionedEther(address, uint256) external virtual;
}

File 44 of 140 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}

File 45 of 140 : LockForImplLib.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0 <0.8.0;

import { FactoryLib } from "./factory/FactoryLib.sol";
import { ZeroUnderwriterLock } from "../underwriter/ZeroUnderwriterLock.sol";

/**
@title lockFor implementation
@author raymondpulver
*/
library LockForImplLib {
  function lockFor(
    address nft,
    address underwriterLockImpl,
    address underwriter
  ) internal view returns (ZeroUnderwriterLock result) {
    result = ZeroUnderwriterLock(
      FactoryLib.computeAddress(nft, underwriterLockImpl, bytes32(uint256(uint160(underwriter))))
    );
  }
}

File 46 of 140 : IConverter.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.5.0 <0.8.0;

interface IConverter {
  function convert(address) external returns (uint256);

  function estimate(uint256) external view returns (uint256);
}

File 47 of 140 : ZeroControllerTemplate.sol
pragma solidity >=0.6.0;
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { ControllerUpgradeable } from "./ControllerUpgradeable.sol";
import { EIP712Upgradeable } from "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol";
import { ZeroLib } from "../libraries/ZeroLib.sol";

contract ZeroControllerTemplate is ControllerUpgradeable, OwnableUpgradeable, EIP712Upgradeable {
  uint256 internal maxGasPrice = 100e9;
  uint256 internal maxGasRepay = 250000;
  uint256 internal maxGasLoan = 500000;
  string internal constant UNDERWRITER_LOCK_IMPLEMENTATION_ID = "zero.underwriter.lock-implementation";
  address internal underwriterLockImpl;
  mapping(bytes32 => ZeroLib.LoanStatus) public loanStatus;
  bytes32 internal constant ZERO_DOMAIN_SALT = 0xb225c57bf2111d6955b97ef0f55525b5a400dc909a5506e34b102e193dd53406;
  bytes32 internal constant ZERO_DOMAIN_NAME_HASH = keccak256("ZeroController.RenVMBorrowMessage");
  bytes32 internal constant ZERO_DOMAIN_VERSION_HASH = keccak256("v2");
  bytes32 internal constant ZERO_RENVM_BORROW_MESSAGE_TYPE_HASH =
    keccak256("RenVMBorrowMessage(address module,uint256 amount,address underwriter,uint256 pNonce,bytes pData)");
  bytes32 internal constant TYPE_HASH = keccak256("TransferRequest(address asset,uint256 amount)");
  bytes32 internal ZERO_DOMAIN_SEPARATOR;
  bytes32 internal constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
  mapping(uint256 => address) public ownerOf;

  uint256 public fee;
  address public gatewayRegistry;
  mapping(address => uint256) public baseFeeByAsset;
  mapping(address => bool) public approvedModules;
  uint256 internal maxGasBurn = 500000;
}

File 48 of 140 : console.sol
// SPDX-License-Identifier: MIT
pragma solidity >= 0.4.22 <0.9.0;

library console {
	address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);

	function _sendLogPayload(bytes memory payload) private view {
		uint256 payloadLength = payload.length;
		address consoleAddress = CONSOLE_ADDRESS;
		assembly {
			let payloadStart := add(payload, 32)
			let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
		}
	}

	function log() internal view {
		_sendLogPayload(abi.encodeWithSignature("log()"));
	}

	function logInt(int p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(int)", p0));
	}

	function logUint(uint p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
	}

	function logString(string memory p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
	}

	function logBool(bool p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
	}

	function logAddress(address p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address)", p0));
	}

	function logBytes(bytes memory p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes)", p0));
	}

	function logBytes1(bytes1 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0));
	}

	function logBytes2(bytes2 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0));
	}

	function logBytes3(bytes3 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0));
	}

	function logBytes4(bytes4 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0));
	}

	function logBytes5(bytes5 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0));
	}

	function logBytes6(bytes6 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0));
	}

	function logBytes7(bytes7 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0));
	}

	function logBytes8(bytes8 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0));
	}

	function logBytes9(bytes9 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0));
	}

	function logBytes10(bytes10 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0));
	}

	function logBytes11(bytes11 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0));
	}

	function logBytes12(bytes12 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0));
	}

	function logBytes13(bytes13 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0));
	}

	function logBytes14(bytes14 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0));
	}

	function logBytes15(bytes15 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0));
	}

	function logBytes16(bytes16 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0));
	}

	function logBytes17(bytes17 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0));
	}

	function logBytes18(bytes18 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0));
	}

	function logBytes19(bytes19 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0));
	}

	function logBytes20(bytes20 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0));
	}

	function logBytes21(bytes21 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0));
	}

	function logBytes22(bytes22 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0));
	}

	function logBytes23(bytes23 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0));
	}

	function logBytes24(bytes24 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0));
	}

	function logBytes25(bytes25 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0));
	}

	function logBytes26(bytes26 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0));
	}

	function logBytes27(bytes27 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0));
	}

	function logBytes28(bytes28 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0));
	}

	function logBytes29(bytes29 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0));
	}

	function logBytes30(bytes30 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0));
	}

	function logBytes31(bytes31 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0));
	}

	function logBytes32(bytes32 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0));
	}

	function log(uint p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
	}

	function log(string memory p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
	}

	function log(bool p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
	}

	function log(address p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address)", p0));
	}

	function log(uint p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1));
	}

	function log(uint p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1));
	}

	function log(uint p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1));
	}

	function log(uint p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1));
	}

	function log(string memory p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1));
	}

	function log(string memory p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
	}

	function log(string memory p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1));
	}

	function log(string memory p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1));
	}

	function log(bool p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1));
	}

	function log(bool p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1));
	}

	function log(bool p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1));
	}

	function log(bool p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1));
	}

	function log(address p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1));
	}

	function log(address p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1));
	}

	function log(address p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1));
	}

	function log(address p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1));
	}

	function log(uint p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2));
	}

	function log(uint p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2));
	}

	function log(uint p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2));
	}

	function log(uint p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2));
	}

	function log(uint p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2));
	}

	function log(uint p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2));
	}

	function log(uint p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2));
	}

	function log(uint p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2));
	}

	function log(uint p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2));
	}

	function log(uint p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2));
	}

	function log(uint p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2));
	}

	function log(uint p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2));
	}

	function log(string memory p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2));
	}

	function log(string memory p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2));
	}

	function log(string memory p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2));
	}

	function log(string memory p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2));
	}

	function log(bool p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2));
	}

	function log(bool p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2));
	}

	function log(bool p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2));
	}

	function log(bool p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2));
	}

	function log(bool p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2));
	}

	function log(bool p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2));
	}

	function log(bool p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2));
	}

	function log(bool p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2));
	}

	function log(bool p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2));
	}

	function log(bool p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2));
	}

	function log(bool p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2));
	}

	function log(bool p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2));
	}

	function log(address p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2));
	}

	function log(address p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2));
	}

	function log(address p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2));
	}

	function log(address p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2));
	}

	function log(address p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2));
	}

	function log(address p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2));
	}

	function log(address p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2));
	}

	function log(address p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2));
	}

	function log(address p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2));
	}

	function log(address p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2));
	}

	function log(address p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2));
	}

	function log(address p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2));
	}

	function log(address p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2));
	}

	function log(address p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2));
	}

	function log(address p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2));
	}

	function log(address p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2));
	}

	function log(uint p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3));
	}

}

File 49 of 140 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <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 GSN 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 payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

File 50 of 140 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain`call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
      return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 51 of 140 : Initializable.sol
// SPDX-License-Identifier: MIT

// solhint-disable-next-line compiler-version
pragma solidity >=0.4.24 <0.8.0;

import "../utils/Address.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 */
abstract contract Initializable {

    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }

    /// @dev Returns true if and only if the function is running in the constructor
    function _isConstructor() private view returns (bool) {
        return !Address.isContract(address(this));
    }
}

File 52 of 140 : IERC721.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "../../introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    /**
      * @dev Safely transfers `tokenId` token from `from` to `to`.
      *
      * Requirements:
      *
      * - `from` cannot be the zero address.
      * - `to` cannot be the zero address.
      * - `tokenId` token must exist and be owned by `from`.
      * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
      * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
      *
      * Emits a {Transfer} event.
      */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
}

File 53 of 140 : IERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 54 of 140 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;
import "../proxy/Initializable.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 GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract ContextUpgradeable is Initializable {
    function __Context_init() internal initializer {
        __Context_init_unchained();
    }

    function __Context_init_unchained() internal initializer {
    }
    function _msgSender() internal view virtual returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
    uint256[50] private __gap;
}

File 55 of 140 : IOneSplitAudit.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.5.0 <0.8.0;

interface IOneSplitAudit {
  function swap(
    address fromToken,
    address destToken,
    uint256 amount,
    uint256 minReturn,
    uint256[] calldata distribution,
    uint256 flags
  ) external payable returns (uint256 returnAmount);

  function getExpectedReturn(
    address fromToken,
    address destToken,
    uint256 amount,
    uint256 parts,
    uint256 flags // See constants in IOneSplit.sol
  ) external view returns (uint256 returnAmount, uint256[] memory distribution);
}

File 56 of 140 : Implementation.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0 <0.8.0;

/**
@title must be inherited by a contract that will be deployed with ZeroFactoryLib
@author raymondpulver
*/
abstract contract Implementation {
  /**
  @notice ensure the contract cannot be initialized twice
  */
  function lock() public virtual {
    // no other logic
  }
}

File 57 of 140 : Create2.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
 * `CREATE2` can be used to compute in advance the address where a smart
 * contract will be deployed, which allows for interesting new mechanisms known
 * as 'counterfactual interactions'.
 *
 * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
 * information.
 */
library Create2 {
    /**
     * @dev Deploys a contract using `CREATE2`. The address where the contract
     * will be deployed can be known in advance via {computeAddress}.
     *
     * The bytecode for a contract can be obtained from Solidity with
     * `type(contractName).creationCode`.
     *
     * Requirements:
     *
     * - `bytecode` must not be empty.
     * - `salt` must have not been used for `bytecode` already.
     * - the factory must have a balance of at least `amount`.
     * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
     */
    function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address) {
        address addr;
        require(address(this).balance >= amount, "Create2: insufficient balance");
        require(bytecode.length != 0, "Create2: bytecode length is zero");
        // solhint-disable-next-line no-inline-assembly
        assembly {
            addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
        }
        require(addr != address(0), "Create2: Failed on deploy");
        return addr;
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
     * `bytecodeHash` or `salt` will result in a new destination address.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
        return computeAddress(salt, bytecodeHash, address(this));
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
     * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address) {
        bytes32 _data = keccak256(
            abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash)
        );
        return address(uint160(uint256(_data)));
    }
}

File 58 of 140 : TrivialUnderwriter.sol
// SPDX-License-Identifier: MIT

import { Ownable } from "oz410/access/Ownable.sol";
import { ZeroController } from "../controllers/ZeroController.sol";

/**
@title contract that is the simplest underwriter, just a proxy with an owner tag
@author raymondpulver
*/
contract TrivialUnderwriter is Ownable {
  address payable public immutable controller;

  constructor(address payable _controller) Ownable() {
    controller = _controller;
  }

  function bubble(bool success, bytes memory response) internal pure {
    assembly {
      if iszero(success) {
        revert(add(0x20, response), mload(response))
      }
      return(add(0x20, response), mload(response))
    }
  }

  /**
  @notice proxy a regular call to an arbitrary contract
  @param target the to address of the transaction
  @param data the calldata for the transaction
  */
  function proxy(address payable target, bytes memory data) public payable onlyOwner {
    (bool success, bytes memory response) = target.call{ value: msg.value }(data);
    bubble(success, response);
  }

  function loan(
    address to,
    address asset,
    uint256 amount,
    uint256 nonce,
    address module,
    bytes memory data,
    bytes memory userSignature
  ) public {
    require(msg.sender == owner(), "must be called by owner");
    ZeroController(controller).loan(to, asset, amount, nonce, module, data, userSignature);
  }

  function repay(
    address underwriter,
    address to,
    address asset,
    uint256 amount,
    uint256 actualAmount,
    uint256 nonce,
    address module,
    bytes32 nHash,
    bytes memory data,
    bytes memory signature
  ) public {
    require(msg.sender == owner(), "must be called by owner");
    ZeroController(controller).repay(
      underwriter,
      to,
      asset,
      amount,
      actualAmount,
      nonce,
      module,
      nHash,
      data,
      signature
    );
  }

  /**
  @notice handles any other call and forwards to the controller
  */
  fallback() external payable {
    require(msg.sender == owner(), "must be called by owner");
    (bool success, bytes memory response) = controller.call{ value: msg.value }(msg.data);
    bubble(success, response);
  }
}

File 59 of 140 : Ownable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.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 () {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = 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");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

File 60 of 140 : yVaultUpgradeable.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.8.0;

import "oz410/token/ERC20/IERC20.sol";
import "oz410/math/SafeMath.sol";
import "oz410/utils/Address.sol";
import "oz410/token/ERC20/SafeERC20.sol";
import "oz410/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "oz410/access/Ownable.sol";

import "../interfaces/yearn/IController.sol";

contract yVaultUpgradeable is ERC20Upgradeable {
  using SafeERC20 for IERC20;
  using Address for address;
  using SafeMath for uint256;

  IERC20 public token;

  uint256 public min = 25;
  uint256 public constant max = 30;

  address public governance;
  address public controller;

  mapping(address => bool) public whitelist;

  modifier isWhitelisted() {
    require(whitelist[msg.sender], "!whitelist");
    _;
  }

  modifier onlyGovernance() {
    require(msg.sender == governance);
    _;
  }

  function addToWhitelist(address[] calldata entries) external onlyGovernance {
    for (uint256 i = 0; i < entries.length; i++) {
      address entry = entries[i];
      require(entry != address(0));

      whitelist[entry] = true;
    }
  }

  function removeFromWhitelist(address[] calldata entries) external onlyGovernance {
    for (uint256 i = 0; i < entries.length; i++) {
      address entry = entries[i];
      whitelist[entry] = false;
    }
  }

  function __yVault_init_unchained(
    address _token,
    address _controller,
    string memory _name,
    string memory _symbol
  ) public initializer {
    __ERC20_init_unchained(_name, _symbol);
    token = IERC20(_token);
    governance = msg.sender;
    controller = _controller;
  }

  function decimals() public view override returns (uint8) {
    return ERC20(address(token)).decimals();
  }

  function balance() public view returns (uint256) {
    return token.balanceOf(address(this)).add(IController(controller).balanceOf(address(token)));
  }

  function setMin(uint256 _min) external {
    require(msg.sender == governance, "!governance");
    min = _min;
  }

  function setGovernance(address _governance) public {
    require(msg.sender == governance, "!governance");
    governance = _governance;
  }

  function setController(address _controller) public {
    require(msg.sender == governance, "!governance");
    controller = _controller;
  }

  // Custom logic in here for how much the vault allows to be borrowed
  // Sets minimum required on-hand to keep small withdrawals cheap
  function available() public view returns (uint256) {
    return token.balanceOf(address(this)).mul(min).div(max);
  }

  function earn() public {
    uint256 _bal = available();
    token.safeTransfer(controller, _bal);
    IController(controller).earn(address(token), _bal);
  }

  function depositAll() external {
    deposit(token.balanceOf(msg.sender));
  }

  function deposit(uint256 _amount) public {
    uint256 _pool = balance();
    uint256 _before = token.balanceOf(address(this));
    token.safeTransferFrom(msg.sender, address(this), _amount);
    uint256 _after = token.balanceOf(address(this));
    _amount = _after.sub(_before); // Additional check for deflationary tokens
    uint256 shares = 0;
    if (totalSupply() == 0) {
      shares = _amount;
    } else {
      shares = (_amount.mul(totalSupply())).div(_pool);
    }
    _mint(msg.sender, shares);
  }

  function withdrawAll() external {
    withdraw(balanceOf(msg.sender));
  }

  // Used to swap any borrowed reserve over the debt limit to liquidate to 'token'
  function harvest(address reserve, uint256 amount) external {
    require(msg.sender == controller, "!controller");
    require(reserve != address(token), "token");
    IERC20(reserve).safeTransfer(controller, amount);
  }

  // No rebalance implementation for lower fees and faster swaps
  function withdraw(uint256 _shares) public {
    uint256 r = (balance().mul(_shares)).div(totalSupply());
    _burn(msg.sender, _shares);

    // Check balance
    uint256 b = token.balanceOf(address(this));
    if (b < r) {
      uint256 _withdraw = r.sub(b);
      IController(controller).withdraw(address(token), _withdraw);
      uint256 _after = token.balanceOf(address(this));
      uint256 _diff = _after.sub(b);
      if (_diff < _withdraw) {
        r = b.add(_diff);
      }
    }

    token.safeTransfer(msg.sender, r);
  }

  function getPricePerFullShare() public view returns (uint256) {
    return balance().mul(1e18).div(totalSupply());
  }
}

File 61 of 140 : ERC20Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "../../utils/ContextUpgradeable.sol";
import "./IERC20Upgradeable.sol";
import "../../math/SafeMathUpgradeable.sol";
import "../../proxy/Initializable.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.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of 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 ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable {
    using SafeMathUpgradeable for uint256;

    mapping (address => uint256) private _balances;

    mapping (address => mapping (address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;
    uint8 private _decimals;

    /**
     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
     * a default value of 18.
     *
     * To select a different value for {decimals}, use {_setupDecimals}.
     *
     * All three of these values are immutable: they can only be set once during
     * construction.
     */
    function __ERC20_init(string memory name_, string memory symbol_) internal initializer {
        __Context_init_unchained();
        __ERC20_init_unchained(name_, symbol_);
    }

    function __ERC20_init_unchained(string memory name_, string memory symbol_) internal initializer {
        _name = name_;
        _symbol = symbol_;
        _decimals = 18;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual 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 value {ERC20} uses, unless {_setupDecimals} is
     * called.
     *
     * 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 returns (uint8) {
        return _decimals;
    }

    /**
     * @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:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, 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}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), 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}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        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) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(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) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is 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:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(address sender, address recipient, uint256 amount) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, 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:
     *
     * - `to` 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 = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(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);

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(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 Sets {decimals} to a value other than the default one of 18.
     *
     * WARNING: This function should only be called from the constructor. Most
     * applications that interact with token contracts will not expect
     * {decimals} to ever change, and may work incorrectly if it does.
     */
    function _setupDecimals(uint8 decimals_) internal virtual {
        _decimals = decimals_;
    }

    /**
     * @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 to 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 { }
    uint256[44] private __gap;
}

File 62 of 140 : IController.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.5.0 <0.8.0;

interface IController {
  function withdraw(address, uint256) external;

  function balanceOf(address) external view returns (uint256);

  function earn(address, uint256) external;

  function want(address) external view returns (address);

  function rewards() external view returns (address);

  function vaults(address) external view returns (address);

  function strategies(address) external view returns (address);

  function approvedStrategies(address, address) external view returns (bool);
}

File 63 of 140 : IERC20Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 64 of 140 : SafeMathUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMathUpgradeable {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}

File 65 of 140 : yVault.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.8.0;

import "oz410/token/ERC20/IERC20.sol";
import "oz410/math/SafeMath.sol";
import "oz410/utils/Address.sol";
import "oz410/token/ERC20/SafeERC20.sol";
import "oz410/token/ERC20/ERC20.sol";
import "oz410/access/Ownable.sol";

import "../interfaces/yearn/IController.sol";

contract yVault is ERC20 {
  using SafeERC20 for IERC20;
  using Address for address;
  using SafeMath for uint256;

  IERC20 public token;

  uint256 public min = 25;
  uint256 public constant max = 30;

  address public governance;
  address public controller;

  constructor(
    address _token,
    address _controller,
    string memory _name,
    string memory _symbol
  ) ERC20(_name, _symbol) {
    token = IERC20(_token);
    governance = msg.sender;
    controller = _controller;
  }

  function decimals() public view override returns (uint8) {
    return ERC20(address(token)).decimals();
  }

  function balance() public view returns (uint256) {
    return token.balanceOf(address(this)).add(IController(controller).balanceOf(address(token)));
  }

  function setMin(uint256 _min) external {
    require(msg.sender == governance, "!governance");
    min = _min;
  }

  function setGovernance(address _governance) public {
    require(msg.sender == governance, "!governance");
    governance = _governance;
  }

  function setController(address _controller) public {
    require(msg.sender == governance, "!governance");
    controller = _controller;
  }

  // Custom logic in here for how much the vault allows to be borrowed
  // Sets minimum required on-hand to keep small withdrawals cheap
  function available() public view returns (uint256) {
    return token.balanceOf(address(this)).mul(min).div(max);
  }

  function earn() public {
    uint256 _bal = available();
    token.safeTransfer(controller, _bal);
    IController(controller).earn(address(token), _bal);
  }

  function depositAll() external {
    deposit(token.balanceOf(msg.sender));
  }

  function deposit(uint256 _amount) public {
    uint256 _pool = balance();
    uint256 _before = token.balanceOf(address(this));
    token.safeTransferFrom(msg.sender, address(this), _amount);
    uint256 _after = token.balanceOf(address(this));
    _amount = _after.sub(_before); // Additional check for deflationary tokens
    uint256 shares = 0;
    if (totalSupply() == 0) {
      shares = _amount;
    } else {
      shares = (_amount.mul(totalSupply())).div(_pool);
    }
    _mint(msg.sender, shares);
  }

  function withdrawAll() external {
    withdraw(balanceOf(msg.sender));
  }

  // Used to swap any borrowed reserve over the debt limit to liquidate to 'token'
  function harvest(address reserve, uint256 amount) external {
    require(msg.sender == controller, "!controller");
    require(reserve != address(token), "token");
    IERC20(reserve).safeTransfer(controller, amount);
  }

  // No rebalance implementation for lower fees and faster swaps
  function withdraw(uint256 _shares) public {
    uint256 r = (balance().mul(_shares)).div(totalSupply());
    _burn(msg.sender, _shares);

    // Check balance
    uint256 b = token.balanceOf(address(this));
    if (b < r) {
      uint256 _withdraw = r.sub(b);
      IController(controller).withdraw(address(token), _withdraw);
      uint256 _after = token.balanceOf(address(this));
      uint256 _diff = _after.sub(b);
      if (_diff < _withdraw) {
        r = b.add(_diff);
      }
    }

    token.safeTransfer(msg.sender, r);
  }

  function getPricePerFullShare() public view returns (uint256) {
    return balance().mul(1e18).div(totalSupply());
  }
}

File 66 of 140 : DummyVault.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.8.0;

import { yVault } from "../vendor/yearn/vaults/yVault.sol";
import { IERC20 } from "oz410/token/ERC20/IERC20.sol";
import { ERC20 } from "oz410/token/ERC20/ERC20.sol";

contract DummyVault is ERC20 {
  address public immutable want;
  address public immutable controller;

  constructor(
    address _want,
    address _controller,
    string memory _name,
    string memory _symbol
  ) ERC20(_name, _symbol) {
    want = _want;
    controller = _controller;
  }

  function estimateShares(uint256 _amount) external view returns (uint256) {
    return _amount;
  }

  function deposit(uint256 _amount) public returns (uint256) {
    IERC20(want).transferFrom(msg.sender, address(this), _amount);
    _mint(msg.sender, _amount);
    return _amount;
  }

  function withdraw(uint256 _amount) public returns (uint256) {
    _burn(msg.sender, _amount);
    IERC20(want).transfer(msg.sender, _amount);
    return _amount;
  }

  function pricePerShare() public pure returns (uint256) {
    return uint256(1e18);
  }
}

File 67 of 140 : StrategyRenVMEthereum.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

import "oz410/token/ERC20/IERC20.sol";
import "oz410/math/SafeMath.sol";
import "oz410/utils/Address.sol";
import "oz410/token/ERC20/SafeERC20.sol";
import "../interfaces/IStrategy.sol";
import "../interfaces/IyVault.sol";
import "../interfaces/IWETH.sol";
import "../interfaces/IConverter.sol";
import { StrategyAPI } from "../interfaces/IStrategy.sol";
import { IController } from "../interfaces/IController.sol";
import { IUniswapV2Router02 } from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import { ICurvePool } from "../interfaces/ICurvePool.sol";
import { IZeroModule } from "../interfaces/IZeroModule.sol";

contract StrategyRenVMEthereum {
  using SafeERC20 for IERC20;
  using Address for address;
  using SafeMath for uint256;

  address public immutable vault;
  address public immutable nativeWrapper;
  address public immutable want;
  int128 public constant wantIndex = 0;

  address public immutable vaultWant;
  int128 public constant vaultWantIndex = 1;

  string public constant name = "0confirmation RenVM Strategy";
  bool public constant isActive = true;

  uint256 public constant wantReserve = 1000000;
  uint256 public constant gasReserve = uint256(1e17);
  address public immutable controller;
  address public governance;
  address public strategist;

  modifier onlyController() {
    require(msg.sender == controller, "!controller");
    _;
  }

  constructor(
    address _controller,
    address _want,
    address _nativeWrapper,
    address _vault,
    address _vaultWant
  ) {
    nativeWrapper = _nativeWrapper;
    want = _want;
    vault = _vault;
    vaultWant = _vaultWant;
    governance = msg.sender;
    strategist = msg.sender;
    controller = _controller;
    IERC20(_vaultWant).safeApprove(address(_vault), type(uint256).max);
  }

  receive() external payable {}

  function deposit() external virtual {
    //First conditional handles having too much of want in the Strategy
    uint256 _want = IERC20(want).balanceOf(address(this)); //amount of tokens we want
    if (_want > wantReserve) {
      // Then we can deposit excess tokens into the vault
      address converter = IController(controller).converters(want, vaultWant);
      require(converter != address(0x0), "!converter");
      uint256 _excess = _want.sub(wantReserve);
      require(IERC20(want).transfer(converter, _excess), "!transfer");
      uint256 _amountOut = IConverter(converter).convert(address(0x0));
      IyVault(vault).deposit(_amountOut);
    }
    //Second conditional handles having too little of gasWant in the Strategy

    uint256 _gasWant = address(this).balance; //ETH balance
    if (_gasWant < gasReserve) {
      // if ETH balance < ETH reserve
      _gasWant = gasReserve.sub(_gasWant);
      address _converter = IController(controller).converters(nativeWrapper, vaultWant);
      uint256 _vaultWant = IConverter(_converter).estimate(_gasWant); //_gasWant is estimated from wETH to wBTC
      uint256 _sharesDeficit = estimateShares(_vaultWant); //Estimate shares of wBTC
      // Works up to this point
      require(IERC20(vault).balanceOf(address(this)) > _sharesDeficit, "!enough"); //revert if shares needed > shares held
      uint256 _amountOut = IyVault(vault).withdraw(_sharesDeficit);
      address converter = IController(controller).converters(vaultWant, nativeWrapper);
      IERC20(vaultWant).transfer(converter, _amountOut);
      _amountOut = IConverter(converter).convert(address(this));
      address _unwrapper = IController(controller).converters(nativeWrapper, address(0x0));
      IERC20(nativeWrapper).transfer(_unwrapper, _amountOut);
      IConverter(_unwrapper).convert(address(this));
    }
  }

  function _withdraw(uint256 _amount, address _asset) private returns (uint256) {
    require(_asset == want || _asset == vaultWant, "asset not supported");
    if (_amount == 0) {
      return 0;
    }
    address converter = IController(controller).converters(want, vaultWant);
    // _asset is wBTC and want is renBTC
    if (_asset == want) {
      // if asset is what the strategy wants
      //then we can't directly withdraw it
      _amount = IConverter(converter).estimate(_amount);
    }
    uint256 _shares = estimateShares(_amount);
    _amount = IyVault(vault).withdraw(_shares);
    if (_asset == want) {
      // if asset is what the strategy wants
      IConverter toWant = IConverter(IController(controller).converters(vaultWant, want));
      IERC20(vaultWant).transfer(address(toWant), _amount);
      _amount = toWant.convert(address(0x0));
    }
    return _amount;
  }

  function permissionedEther(address payable _target, uint256 _amount) external virtual onlyController {
    // _amount is the amount of ETH to refund
    if (_amount > gasReserve) {
      _amount = IConverter(IController(controller).converters(nativeWrapper, vaultWant)).estimate(_amount);
      uint256 _sharesDeficit = estimateShares(_amount);
      uint256 _amountOut = IyVault(vault).withdraw(_sharesDeficit);
      address _vaultConverter = IController(controller).converters(vaultWant, nativeWrapper);
      address _converter = IController(controller).converters(nativeWrapper, address(0x0));
      IERC20(vaultWant).transfer(_vaultConverter, _amountOut);
      _amount = IConverter(_vaultConverter).convert(address(this));
      IERC20(nativeWrapper).transfer(_converter, _amount);
      _amount = IConverter(_converter).convert(address(this));
    }
    _target.transfer(_amount);
  }

  function withdraw(uint256 _amount) external virtual onlyController {
    IERC20(want).safeTransfer(address(controller), _withdraw(_amount, want));
  }

  function withdrawAll() external virtual onlyController {
    IERC20(want).safeTransfer(address(controller), _withdraw(IERC20(vault).balanceOf(address(this)), want));
  }

  function balanceOf() external view virtual returns (uint256) {
    return IyVault(vault).balanceOf(address(this));
  }

  function estimateShares(uint256 _amount) internal virtual returns (uint256) {
    return _amount.mul(10**IyVault(vault).decimals()).div(IyVault(vault).pricePerShare());
  }

  function permissionedSend(address _module, uint256 _amount) external virtual onlyController returns (uint256) {
    uint256 _reserve = IERC20(want).balanceOf(address(this));
    address _want = IZeroModule(_module).want();
    if (_amount > _reserve || _want != want) {
      _amount = _withdraw(_amount, _want);
    }
    IERC20(_want).safeTransfer(_module, _amount);
    return _amount;
  }
}

File 68 of 140 : IController.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.8.0;

interface IController {
  function governance() external view returns (address);

  function rewards() external view returns (address);

  function withdraw(address, uint256) external;

  function balanceOf(address) external view returns (uint256);

  function earn(address, uint256) external;

  function want(address) external view returns (address);

  function vaults(address) external view returns (address);

  function strategies(address) external view returns (address);

  function approvedStrategies(address, address) external view returns (bool);

  function converters(address, address) external view returns (address);
}

File 69 of 140 : ICurvePool.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.8.0;

interface ICurvePool {
  function get_dy(
    int128,
    int128,
    uint256
  ) external view returns (uint256);

  function get_dy(
    uint256,
    uint256,
    uint256
  ) external view returns (uint256);

  function get_dy_underlying(
    int128,
    int128,
    uint256
  ) external view returns (uint256);

  function get_dy_underlying(
    uint256,
    uint256,
    uint256
  ) external view returns (uint256);

  function exchange(
    int128,
    int128,
    uint256,
    uint256
  ) external;

  function exchange(
    uint256,
    uint256,
    uint256,
    uint256
  ) external;

  function exchange_underlying(
    int128,
    int128,
    uint256,
    uint256
  ) external;

  function exchange_underlying(
    uint256,
    uint256,
    uint256,
    uint256
  ) external;

  function coins(int128) external view returns (address);

  function coins(int256) external view returns (address);

  function coins(uint128) external view returns (address);

  function coins(uint256) external view returns (address);

  function underlying_coins(int128) external view returns (address);

  function underlying_coins(uint128) external view returns (address);

  function underlying_coins(int256) external view returns (address);

  function underlying_coins(uint256) external view returns (address);
}

File 70 of 140 : StrategyRenVMArbitrum.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

import "oz410/token/ERC20/IERC20.sol";
import "oz410/math/SafeMath.sol";
import "oz410/utils/Address.sol";
import "oz410/token/ERC20/SafeERC20.sol";
import "../interfaces/IStrategy.sol";
import "../interfaces/IyVault.sol";
import "../interfaces/IWETH.sol";
import "../interfaces/IConverter.sol";
import { StrategyAPI } from "../interfaces/IStrategy.sol";
import { IController } from "../interfaces/IController.sol";
import { IUniswapV2Router02 } from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import { ICurvePool } from "../interfaces/ICurvePool.sol";
import { IZeroModule } from "../interfaces/IZeroModule.sol";

contract StrategyRenVMArbitrum {
  using SafeERC20 for IERC20;
  using Address for address;
  using SafeMath for uint256;

  address public immutable vault;
  address public immutable nativeWrapper;
  address public immutable want;
  int128 public constant wantIndex = 0;

  address public immutable vaultWant;
  int128 public constant vaultWantIndex = 1;

  string public constant name = "0confirmation RenVM Strategy";
  bool public constant isActive = true;

  uint256 public constant wantReserve = 1000000;
  uint256 public constant gasReserve = uint256(1e17);
  address public immutable controller;
  address public governance;
  address public strategist;

  modifier onlyController() {
    require(msg.sender == controller, "!controller");
    _;
  }

  constructor(
    address _controller,
    address _want,
    address _nativeWrapper,
    address _vault,
    address _vaultWant
  ) {
    nativeWrapper = _nativeWrapper;
    want = _want;
    vault = _vault;
    vaultWant = _vaultWant;
    governance = msg.sender;
    strategist = msg.sender;
    controller = _controller;
    IERC20(_vaultWant).safeApprove(address(_vault), type(uint256).max);
  }

  receive() external payable {}

  function deposit() external virtual {
    //First conditional handles having too much of want in the Strategy
    uint256 _want = IERC20(want).balanceOf(address(this)); //amount of tokens we want
    if (_want > wantReserve) {
      // Then we can deposit excess tokens into the vault
      address converter = IController(controller).converters(want, vaultWant);
      require(converter != address(0x0), "!converter");
      uint256 _excess = _want.sub(wantReserve);
      require(IERC20(want).transfer(converter, _excess), "!transfer");
      uint256 _amountOut = IConverter(converter).convert(address(0x0));
      IyVault(vault).deposit(_amountOut);
    }
    //Second conditional handles having too little of gasWant in the Strategy

    uint256 _gasWant = address(this).balance; //ETH balance
    if (_gasWant < gasReserve) {
      // if ETH balance < ETH reserve
      _gasWant = gasReserve.sub(_gasWant);
      address _converter = IController(controller).converters(nativeWrapper, vaultWant);
      uint256 _vaultWant = IConverter(_converter).estimate(_gasWant); //_gasWant is estimated from wETH to wBTC
      uint256 _sharesDeficit = estimateShares(_vaultWant); //Estimate shares of wBTC
      // Works up to this point
      require(IERC20(vault).balanceOf(address(this)) > _sharesDeficit, "!enough"); //revert if shares needed > shares held
      uint256 _amountOut = IyVault(vault).withdraw(_sharesDeficit);
      address converter = IController(controller).converters(vaultWant, nativeWrapper);
      IERC20(vaultWant).transfer(converter, _amountOut);
      _amountOut = IConverter(converter).convert(address(this));
      address _unwrapper = IController(controller).converters(nativeWrapper, address(0x0));
      IERC20(nativeWrapper).transfer(_unwrapper, _amountOut);
      IConverter(_unwrapper).convert(address(this));
    }
  }

  function _withdraw(uint256 _amount, address _asset) private returns (uint256) {
    require(_asset == want || _asset == vaultWant, "asset not supported");
    if (_amount == 0) {
      return 0;
    }
    address converter = IController(controller).converters(want, vaultWant);
    // _asset is wBTC and want is renBTC
    if (_asset == want) {
      // if asset is what the strategy wants
      //then we can't directly withdraw it
      _amount = IConverter(converter).estimate(_amount);
    }
    uint256 _shares = estimateShares(_amount);
    _amount = IyVault(vault).withdraw(_shares);
    if (_asset == want) {
      // if asset is what the strategy wants
      IConverter toWant = IConverter(IController(controller).converters(vaultWant, want));
      IERC20(vaultWant).transfer(address(toWant), _amount);
      _amount = toWant.convert(address(0x0));
    }
    return _amount;
  }

  function permissionedEther(address payable _target, uint256 _amount) external virtual onlyController {
    // _amount is the amount of ETH to refund
    if (_amount > gasReserve) {
      _amount = IConverter(IController(controller).converters(nativeWrapper, vaultWant)).estimate(_amount);
      uint256 _sharesDeficit = estimateShares(_amount);
      uint256 _amountOut = IyVault(vault).withdraw(_sharesDeficit);
      address _vaultConverter = IController(controller).converters(vaultWant, nativeWrapper);
      address _converter = IController(controller).converters(nativeWrapper, address(0x0));
      IERC20(vaultWant).transfer(_vaultConverter, _amountOut);
      _amount = IConverter(_vaultConverter).convert(address(this));
      IERC20(nativeWrapper).transfer(_converter, _amount);
      _amount = IConverter(_converter).convert(address(this));
    }
    _target.transfer(_amount);
  }

  function withdraw(uint256 _amount) external virtual onlyController {
    IERC20(want).safeTransfer(address(controller), _withdraw(_amount, want));
  }

  function withdrawAll() external virtual onlyController {
    IERC20(want).safeTransfer(address(controller), _withdraw(IERC20(vault).balanceOf(address(this)), want));
  }

  function balanceOf() external view virtual returns (uint256) {
    return IyVault(vault).balanceOf(address(this));
  }

  function estimateShares(uint256 _amount) internal virtual returns (uint256) {
    return _amount.mul(10**IyVault(vault).decimals()).div(IyVault(vault).pricePerShare());
  }

  function permissionedSend(address _module, uint256 _amount) external virtual onlyController returns (uint256) {
    uint256 _reserve = IERC20(want).balanceOf(address(this));
    address _want = IZeroModule(_module).want();
    if (_amount > _reserve || _want != want) {
      _amount = _withdraw(_amount, _want);
    }
    IERC20(_want).safeTransfer(_module, _amount);
    return _amount;
  }
}

File 71 of 140 : StrategyRenVM.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

import "oz410/token/ERC20/IERC20.sol";
import "oz410/math/SafeMath.sol";
import "oz410/utils/Address.sol";
import "oz410/token/ERC20/SafeERC20.sol";
import "../interfaces/IStrategy.sol";
import "../interfaces/IyVault.sol";
import "../interfaces/IWETH.sol";
import "../interfaces/IConverter.sol";
import { StrategyAPI } from "../interfaces/IStrategy.sol";
import { IController } from "../interfaces/IController.sol";
import { IUniswapV2Router02 } from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import { ICurvePool } from "../interfaces/ICurvePool.sol";
import { IZeroModule } from "../interfaces/IZeroModule.sol";

contract StrategyRenVM {
  using SafeERC20 for IERC20;
  using Address for address;
  using SafeMath for uint256;

  address public immutable vault;
  address public immutable nativeWrapper;
  address public immutable want;
  int128 public constant wantIndex = 0;

  address public immutable vaultWant;
  int128 public constant vaultWantIndex = 1;

  string public constant name = "0confirmation RenVM Strategy";
  bool public constant isActive = true;

  uint256 public constant wantReserve = 1000000;
  uint256 public constant gasReserve = uint256(5 ether);
  address public immutable controller;
  address public governance;
  address public strategist;

  modifier onlyController() {
    require(msg.sender == controller, "!controller");
    _;
  }

  constructor(
    address _controller,
    address _want,
    address _nativeWrapper,
    address _vault,
    address _vaultWant
  ) {
    nativeWrapper = _nativeWrapper;
    want = _want;
    vault = _vault;
    vaultWant = _vaultWant;
    governance = msg.sender;
    strategist = msg.sender;
    controller = _controller;
    IERC20(_vaultWant).safeApprove(address(_vault), type(uint256).max);
  }

  receive() external payable {}

  function deposit() external virtual {
    //First conditional handles having too much of want in the Strategy
    uint256 _want = IERC20(want).balanceOf(address(this)); //amount of tokens we want
    if (_want > wantReserve) {
      // Then we can deposit excess tokens into the vault
      address converter = IController(controller).converters(want, vaultWant);
      require(converter != address(0x0), "!converter");
      uint256 _excess = _want.sub(wantReserve);
      require(IERC20(want).transfer(converter, _excess), "!transfer");
      uint256 _amountOut = IConverter(converter).convert(address(0x0));
      IyVault(vault).deposit(_amountOut);
    }
    //Second conditional handles having too little of gasWant in the Strategy

    uint256 _gasWant = address(this).balance; //ETH balance
    if (_gasWant < gasReserve) {
      // if ETH balance < ETH reserve
      _gasWant = gasReserve.sub(_gasWant);
      address _converter = IController(controller).converters(nativeWrapper, vaultWant);
      uint256 _vaultWant = IConverter(_converter).estimate(_gasWant); //_gasWant is estimated from wETH to wBTC
      uint256 _sharesDeficit = estimateShares(_vaultWant); //Estimate shares of wBTC
      // Works up to this point
      require(IERC20(vault).balanceOf(address(this)) > _sharesDeficit, "!enough"); //revert if shares needed > shares held
      uint256 _amountOut = IyVault(vault).withdraw(_sharesDeficit);
      address converter = IController(controller).converters(vaultWant, nativeWrapper);
      IERC20(vaultWant).transfer(converter, _amountOut);
      _amountOut = IConverter(converter).convert(address(this));
      address _unwrapper = IController(controller).converters(nativeWrapper, address(0x0));
      IERC20(nativeWrapper).transfer(_unwrapper, _amountOut);
      IConverter(_unwrapper).convert(address(this));
    }
  }

  function _withdraw(uint256 _amount, address _asset) private returns (uint256) {
    require(_asset == want || _asset == vaultWant, "asset not supported");
    if (_amount == 0) {
      return 0;
    }
    address converter = IController(controller).converters(want, vaultWant);
    // _asset is wBTC and want is renBTC
    if (_asset == want) {
      // if asset is what the strategy wants
      //then we can't directly withdraw it
      _amount = IConverter(converter).estimate(_amount);
    }
    uint256 _shares = estimateShares(_amount);
    _amount = IyVault(vault).withdraw(_shares);
    if (_asset == want) {
      // if asset is what the strategy wants
      IConverter toWant = IConverter(IController(controller).converters(vaultWant, want));
      IERC20(vaultWant).transfer(address(toWant), _amount);
      _amount = toWant.convert(address(0x0));
    }
    return _amount;
  }

  function permissionedEther(address payable _target, uint256 _amount) external virtual onlyController {
    // _amount is the amount of ETH to refund
    if (_amount > gasReserve) {
      _amount = IConverter(IController(controller).converters(nativeWrapper, vaultWant)).estimate(_amount);
      uint256 _sharesDeficit = estimateShares(_amount);
      uint256 _amountOut = IyVault(vault).withdraw(_sharesDeficit);
      address _vaultConverter = IController(controller).converters(vaultWant, nativeWrapper);
      address _converter = IController(controller).converters(nativeWrapper, address(0x0));
      IERC20(vaultWant).transfer(_vaultConverter, _amountOut);
      _amount = IConverter(_vaultConverter).convert(address(this));
      IERC20(nativeWrapper).transfer(_converter, _amount);
      _amount = IConverter(_converter).convert(address(this));
    }
    _target.transfer(_amount);
  }

  function withdraw(uint256 _amount) external virtual onlyController {
    IERC20(want).safeTransfer(address(controller), _withdraw(_amount, want));
  }

  function withdrawAll() external virtual onlyController {
    IERC20(want).safeTransfer(address(controller), _withdraw(IERC20(vault).balanceOf(address(this)), want));
  }

  function balanceOf() external view virtual returns (uint256) {
    return IyVault(vault).balanceOf(address(this));
  }

  function estimateShares(uint256 _amount) internal virtual returns (uint256) {
    return _amount.mul(10**IyVault(vault).decimals()).div(IyVault(vault).pricePerShare());
  }

  function permissionedSend(address _module, uint256 _amount) external virtual onlyController returns (uint256) {
    uint256 _reserve = IERC20(want).balanceOf(address(this));
    address _want = IZeroModule(_module).want();
    if (_amount > _reserve || _want != want) {
      _amount = _withdraw(_amount, _want);
    }
    IERC20(_want).safeTransfer(_module, _amount);
    return _amount;
  }
}

File 72 of 140 : Swap.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

import { SwapLib } from "./SwapLib.sol";
import { SafeMath } from "oz410/math/SafeMath.sol";
import { IUniswapV2Router02 } from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import { IERC20 } from "oz410/token/ERC20/IERC20.sol";
import { SafeERC20 } from "oz410/token/ERC20/SafeERC20.sol";
import { IController } from "../interfaces/IController.sol";
import { IZeroModule } from "../interfaces/IZeroModule.sol";

contract Swap is IZeroModule {
  using SafeERC20 for *;
  using SafeMath for *;
  mapping(uint256 => SwapLib.SwapRecord) public outstanding;
  address public immutable controller;
  address public immutable governance;
  uint256 public blockTimeout;
  address public immutable fiat; //USDC
  address public immutable wNative; //wETH
  address public immutable override want; //wBTC
  address public immutable router; //Sushi V2
  address public immutable controllerWant; // Controller want (renBTC)

  modifier onlyController() {
    require(msg.sender == controller, "!controller");
    _;
  }

  constructor(
    address _controller,
    address _wNative,
    address _want,
    address _router,
    address _fiat,
    address _controllerWant
  ) {
    controller = _controller;
    wNative = _wNative;
    want = _want;
    router = _router;
    fiat = _fiat;
    controllerWant = _controllerWant;
    governance = IController(_controller).governance();
    IERC20(_want).safeApprove(_router, ~uint256(0));
    IERC20(_fiat).safeApprove(_router, ~uint256(0));
  }

  function setBlockTimeout(uint256 _ct) public {
    require(msg.sender == governance, "!governance");
    blockTimeout = _ct;
  }

  function defaultLoan(uint256 _nonce) public {
    require(block.number >= outstanding[_nonce].when + blockTimeout);
    require(outstanding[_nonce].qty != 0, "!outstanding");
    uint256 _amountSwapped = swapTokens(fiat, controllerWant, outstanding[_nonce].qty);
    IERC20(controllerWant).safeTransfer(controller, _amountSwapped);
    delete outstanding[_nonce];
  }

  function receiveLoan(
    address _to,
    address _asset,
    uint256 _actual,
    uint256 _nonce,
    bytes memory _data
  ) public override onlyController {
    uint256 amountSwapped = swapTokens(want, fiat, _actual);
    outstanding[_nonce] = SwapLib.SwapRecord({ qty: amountSwapped, when: uint64(block.timestamp), token: _asset });
  }

  function swapTokens(
    address _tokenIn,
    address _tokenOut,
    uint256 _amountIn
  ) internal returns (uint256) {
    address[] memory _path = new address[](3);
    _path[0] = _tokenIn;
    _path[1] = wNative;
    _path[2] = _tokenOut;
    IERC20(_tokenIn).approve(router, _amountIn);
    uint256 _amountOut = IUniswapV2Router02(router).swapExactTokensForTokens(
      _amountIn,
      1,
      _path,
      address(this),
      block.timestamp
    )[_path.length - 1];
    return _amountOut;
  }

  function repayLoan(
    address _to,
    address _asset,
    uint256 _actualAmount,
    uint256 _nonce,
    bytes memory _data
  ) public override onlyController {
    require(outstanding[_nonce].qty != 0, "!outstanding");
    IERC20(fiat).safeTransfer(_to, outstanding[_nonce].qty);
    delete outstanding[_nonce];
  }

  function computeReserveRequirement(uint256 _in) external view override returns (uint256) {
    return _in.mul(uint256(1e17)).div(uint256(1 ether));
  }
}

File 73 of 140 : SwapLib.sol
// SPDX-License-Identifier: MIT

library SwapLib {
  struct SwapRecord {
    address token;
    uint64 when;
    uint256 qty;
  }
}

File 74 of 140 : SwapRelease.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import { Swap } from "../modules/Swap.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract SwapRelease {
  address constant swap = 0x129F31e121B0A8C05bf10347F34976238F1f15DC;
  address constant wbtc = 0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6;
  address constant governance = 0x12fBc372dc2f433392CC6caB29CFBcD5082EF494;
  address constant usdc = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174;

  fallback() external {
    IERC20(wbtc).transfer(swap, IERC20(wbtc).balanceOf(address(this)));
    Swap(swap).receiveLoan(address(0), address(0), IERC20(wbtc).balanceOf(swap), 1, hex"");
    Swap(swap).repayLoan(governance, address(0), IERC20(usdc).balanceOf(swap), uint256(1), hex"");
  }
}

File 75 of 140 : ZeroDistributor.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.6.0 <0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/cryptography/MerkleProof.sol";
import { IMerkleDistributor } from "../interfaces/IMerkleDistributor.sol";

contract ZeroDistributor is IMerkleDistributor {
  address public immutable override token;
  bytes32 public immutable override merkleRoot;
  address public immutable treasury;

  // This is a packed array of booleans.
  mapping(uint256 => uint256) private claimedBitMap;

  constructor(
    address token_,
    address treasury_,
    bytes32 merkleRoot_
  ) {
    token = token_;
    treasury = treasury_;
    merkleRoot = merkleRoot_;
  }

  function isClaimed(uint256 index) public view override returns (bool) {
    uint256 claimedWordIndex = index / 256;
    uint256 claimedBitIndex = index % 256;
    uint256 claimedWord = claimedBitMap[claimedWordIndex];
    uint256 mask = (1 << claimedBitIndex);
    return claimedWord & mask == mask;
  }

  function _setClaimed(uint256 index) private {
    uint256 claimedWordIndex = index / 256;
    uint256 claimedBitIndex = index % 256;
    claimedBitMap[claimedWordIndex] = claimedBitMap[claimedWordIndex] | (1 << claimedBitIndex);
  }

  function claim(
    uint256 index,
    address account,
    uint256 amount,
    bytes32[] calldata merkleProof
  ) external override {
    require(!isClaimed(index), "MerkleDistributor: Drop already claimed.");

    // Verify the merkle proof.
    bytes32 node = keccak256(abi.encodePacked(index, account, amount));
    require(MerkleProof.verify(merkleProof, merkleRoot, node), "MerkleDistributor: Invalid proof.");

    // Mark it claimed and send the token.
    _setClaimed(index);
    require(IERC20(token).transferFrom(treasury, account, amount), "MerkleDistributor: Transfer failed.");

    emit Claimed(index, account, amount);
  }
}

File 76 of 140 : MerkleProof.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev These functions deal with verification of Merkle trees (hash trees),
 */
library MerkleProof {
    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        bytes32 computedHash = leaf;

        for (uint256 i = 0; i < proof.length; i++) {
            bytes32 proofElement = proof[i];

            if (computedHash <= proofElement) {
                // Hash(current computed hash + current element of the proof)
                computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
            } else {
                // Hash(current element of the proof + current computed hash)
                computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
            }
        }

        // Check if the computed hash (root) is equal to the provided root
        return computedHash == root;
    }
}

File 77 of 140 : IMerkleDistributor.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.5.0 <0.8.0;

// Allows anyone to claim a token if they exist in a merkle root.
interface IMerkleDistributor {
  // Returns the address of the token distributed by this contract.
  function token() external view returns (address);

  // Returns the merkle root of the merkle tree containing account balances available to claim.
  function merkleRoot() external view returns (bytes32);

  // Returns true if the index has been marked claimed.
  function isClaimed(uint256 index) external view returns (bool);

  // Claim the given amount of the token to the given address. Reverts if the inputs are invalid.
  function claim(
    uint256 index,
    address account,
    uint256 amount,
    bytes32[] calldata merkleProof
  ) external;

  // This event is triggered whenever a call to #claim succeeds.
  event Claimed(uint256 index, address account, uint256 amount);
}

File 78 of 140 : ERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "../../utils/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.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.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of 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 {
    using SafeMath for uint256;

    mapping (address => uint256) private _balances;

    mapping (address => mapping (address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;
    uint8 private _decimals;

    /**
     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
     * a default value of 18.
     *
     * To select a different value for {decimals}, use {_setupDecimals}.
     *
     * All three of these values are immutable: they can only be set once during
     * construction.
     */
    constructor (string memory name_, string memory symbol_) public {
        _name = name_;
        _symbol = symbol_;
        _decimals = 18;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual 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 value {ERC20} uses, unless {_setupDecimals} is
     * called.
     *
     * 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 returns (uint8) {
        return _decimals;
    }

    /**
     * @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:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, 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}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), 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}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        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) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(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) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is 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:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(address sender, address recipient, uint256 amount) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, 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:
     *
     * - `to` 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 = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(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);

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(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 Sets {decimals} to a value other than the default one of 18.
     *
     * WARNING: This function should only be called from the constructor. Most
     * applications that interact with token contracts will not expect
     * {decimals} to ever change, and may work incorrectly if it does.
     */
    function _setupDecimals(uint8 decimals_) internal virtual {
        _decimals = decimals_;
    }

    /**
     * @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 to 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 { }
}

File 79 of 140 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <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 GSN 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 payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

File 80 of 140 : Ownable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <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 () internal {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = 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");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

File 81 of 140 : ProxyAdmin.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "../access/Ownable.sol";
import "./TransparentUpgradeableProxy.sol";

/**
 * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
 * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
 */
contract ProxyAdmin is Ownable {

    /**
     * @dev Returns the current implementation of `proxy`.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
        // We need to manually run the static call since the getter cannot be flagged as view
        // bytes4(keccak256("implementation()")) == 0x5c60da1b
        (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
        require(success);
        return abi.decode(returndata, (address));
    }

    /**
     * @dev Returns the current admin of `proxy`.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
        // We need to manually run the static call since the getter cannot be flagged as view
        // bytes4(keccak256("admin()")) == 0xf851a440
        (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
        require(success);
        return abi.decode(returndata, (address));
    }

    /**
     * @dev Changes the admin of `proxy` to `newAdmin`.
     *
     * Requirements:
     *
     * - This contract must be the current admin of `proxy`.
     */
    function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
        proxy.changeAdmin(newAdmin);
    }

    /**
     * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
        proxy.upgradeTo(implementation);
    }

    /**
     * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
     * {TransparentUpgradeableProxy-upgradeToAndCall}.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function upgradeAndCall(TransparentUpgradeableProxy proxy, address implementation, bytes memory data) public payable virtual onlyOwner {
        proxy.upgradeToAndCall{value: msg.value}(implementation, data);
    }
}

File 82 of 140 : TransparentUpgradeableProxy.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "./UpgradeableProxy.sol";

/**
 * @dev This contract implements a proxy that is upgradeable by an admin.
 *
 * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
 * clashing], which can potentially be used in an attack, this contract uses the
 * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
 * things that go hand in hand:
 *
 * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
 * that call matches one of the admin functions exposed by the proxy itself.
 * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
 * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
 * "admin cannot fallback to proxy target".
 *
 * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
 * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
 * to sudden errors when trying to call a function from the proxy implementation.
 *
 * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
 * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
 */
contract TransparentUpgradeableProxy is UpgradeableProxy {
    /**
     * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
     * optionally initialized with `_data` as explained in {UpgradeableProxy-constructor}.
     */
    constructor(address _logic, address admin_, bytes memory _data) public payable UpgradeableProxy(_logic, _data) {
        assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
        _setAdmin(admin_);
    }

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 private constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
     */
    modifier ifAdmin() {
        if (msg.sender == _admin()) {
            _;
        } else {
            _fallback();
        }
    }

    /**
     * @dev Returns the current admin.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
     * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
     */
    function admin() external ifAdmin returns (address admin_) {
        admin_ = _admin();
    }

    /**
     * @dev Returns the current implementation.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
     * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
     */
    function implementation() external ifAdmin returns (address implementation_) {
        implementation_ = _implementation();
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
     */
    function changeAdmin(address newAdmin) external virtual ifAdmin {
        require(newAdmin != address(0), "TransparentUpgradeableProxy: new admin is the zero address");
        emit AdminChanged(_admin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev Upgrade the implementation of the proxy.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
     */
    function upgradeTo(address newImplementation) external virtual ifAdmin {
        _upgradeTo(newImplementation);
    }

    /**
     * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
     * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
     * proxied contract.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
     */
    function upgradeToAndCall(address newImplementation, bytes calldata data) external payable virtual ifAdmin {
        _upgradeTo(newImplementation);
        Address.functionDelegateCall(newImplementation, data);
    }

    /**
     * @dev Returns the current admin.
     */
    function _admin() internal view virtual returns (address adm) {
        bytes32 slot = _ADMIN_SLOT;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            adm := sload(slot)
        }
    }

    /**
     * @dev Stores a new address in the EIP1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        bytes32 slot = _ADMIN_SLOT;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            sstore(slot, newAdmin)
        }
    }

    /**
     * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
     */
    function _beforeFallback() internal virtual override {
        require(msg.sender != _admin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
        super._beforeFallback();
    }
}

File 83 of 140 : UpgradeableProxy.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "./Proxy.sol";
import "../utils/Address.sol";

/**
 * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
 * implementation address that can be changed. This address is stored in storage in the location specified by
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
 * implementation behind the proxy.
 *
 * Upgradeability is only provided internally through {_upgradeTo}. For an externally upgradeable proxy see
 * {TransparentUpgradeableProxy}.
 */
contract UpgradeableProxy is Proxy {
    /**
     * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
     *
     * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
     * function call, and allows initializating the storage of the proxy like a Solidity constructor.
     */
    constructor(address _logic, bytes memory _data) public payable {
        assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
        _setImplementation(_logic);
        if(_data.length > 0) {
            Address.functionDelegateCall(_logic, _data);
        }
    }

    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev Returns the current implementation address.
     */
    function _implementation() internal view virtual override returns (address impl) {
        bytes32 slot = _IMPLEMENTATION_SLOT;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            impl := sload(slot)
        }
    }

    /**
     * @dev Upgrades the proxy to a new implementation.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeTo(address newImplementation) internal virtual {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);
    }

    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        require(Address.isContract(newImplementation), "UpgradeableProxy: new implementation is not a contract");

        bytes32 slot = _IMPLEMENTATION_SLOT;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            sstore(slot, newImplementation)
        }
    }
}

File 84 of 140 : Proxy.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
 * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
 * be specified by overriding the virtual {_implementation} function.
 *
 * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
 * different contract through the {_delegate} function.
 *
 * The success and return data of the delegated call will be returned back to the caller of the proxy.
 */
abstract contract Proxy {
    /**
     * @dev Delegates the current call to `implementation`.
     *
     * This function does not return to its internall call site, it will return directly to the external caller.
     */
    function _delegate(address implementation) internal virtual {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
        }
    }

    /**
     * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
     * and {_fallback} should delegate.
     */
    function _implementation() internal view virtual returns (address);

    /**
     * @dev Delegates the current call to the address returned by `_implementation()`.
     *
     * This function does not return to its internall call site, it will return directly to the external caller.
     */
    function _fallback() internal virtual {
        _beforeFallback();
        _delegate(_implementation());
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
     * function in the contract matches the call data.
     */
    fallback () external payable virtual {
        _fallback();
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
     * is empty.
     */
    receive () external payable virtual {
        _fallback();
    }

    /**
     * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
     * call, or as part of the Solidity `fallback` or `receive` functions.
     *
     * If overriden should call `super._beforeFallback()`.
     */
    function _beforeFallback() internal virtual {
    }
}

File 85 of 140 : BadgerBridgeZeroControllerDeployer.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0;

import { BadgerBridgeZeroControllerMatic } from "../controllers/BadgerBridgeZeroControllerMatic.sol";
import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/TransparentUpgradeableProxy.sol";
import { ProxyAdmin } from "@openzeppelin/contracts/proxy/ProxyAdmin.sol";

contract BadgerBridgeZeroControllerDeployer {
  address constant governance = 0x4A423AB37d70c00e8faA375fEcC4577e3b376aCa;
  event Deployment(address indexed proxy);

  constructor() {
    address logic = address(new BadgerBridgeZeroControllerMatic());
    ProxyAdmin proxy = new ProxyAdmin();
    ProxyAdmin(proxy).transferOwnership(governance);
    emit Deployment(
      address(
        new TransparentUpgradeableProxy(
          logic,
          address(proxy),
          abi.encodeWithSelector(BadgerBridgeZeroControllerMatic.initialize.selector, governance, governance)
        )
      )
    );
    selfdestruct(msg.sender);
  }
}

File 86 of 140 : BadgerBridgeZeroControllerMatic.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0;
pragma abicoder v2;

import { ISwapRouter } from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import { UniswapV2Library } from "../libraries/UniswapV2Library.sol";
import { ZeroLib } from "../libraries/ZeroLib.sol";
import { IERC2612Permit } from "../interfaces/IERC2612Permit.sol";
import { ICurveInt128 } from "../interfaces/CurvePools/ICurveInt128.sol";
import { SplitSignatureLib } from "../libraries/SplitSignatureLib.sol";
import { IBadgerSettPeak } from "../interfaces/IBadgerSettPeak.sol";
import { ICurveFi } from "../interfaces/ICurveFi.sol";
import { IGateway } from "../interfaces/IGateway.sol";
import { IWETH9 } from "@uniswap/v3-periphery/contracts/interfaces/external/IWETH9.sol";
import { ICurveETHUInt256 } from "../interfaces/CurvePools/ICurveETHUInt256.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IyVault } from "../interfaces/IyVault.sol";
import { ISett } from "../interfaces/ISett.sol";
import { Math } from "@openzeppelin/contracts/math/Math.sol";
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { IQuoter } from "@uniswap/v3-periphery/contracts/interfaces/IQuoter.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { ECDSA } from "@openzeppelin/contracts/cryptography/ECDSA.sol";
import { EIP712Upgradeable } from "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol";

contract BadgerBridgeZeroControllerMatic is EIP712Upgradeable {
  using SafeERC20 for IERC20;
  using SafeMath for *;
  uint256 public fee;
  address public governance;
  address public strategist;

  address constant btcGateway = 0x05Cadbf3128BcB7f2b89F3dD55E5B0a036a49e20;
  address constant routerv3 = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
  address constant usdc = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174;
  address constant weth = 0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619;
  address constant wbtc = 0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6;
  address constant renbtc = 0xDBf31dF14B66535aF65AaC99C32e9eA844e14501;
  address constant renCrv = 0xC2d95EEF97Ec6C17551d45e77B590dc1F9117C67;
  address constant tricrypto = 0x960ea3e3C7FB317332d990873d354E18d7645590;
  address constant wmatic = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270;
  address constant quoter = 0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6;
  address constant renCrvLp = 0xf8a57c1d3b9629b77b6726a042ca48990A84Fb49;
  uint24 constant wethWbtcFee = 500;
  uint24 constant wethMaticFee = 500;
  uint24 constant usdcWethFee = 500;
  uint256 public governanceFee;
  bytes32 constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
  bytes32 constant LOCK_SLOT = keccak256("upgrade-lock-v2");
  uint256 constant GAS_COST = uint256(642e3);
  uint256 constant ETH_RESERVE = uint256(5 ether);
  uint256 internal renbtcForOneETHPrice;
  uint256 internal burnFee;
  uint256 public keeperReward;
  uint256 public constant REPAY_GAS_DIFF = 41510;
  uint256 public constant BURN_GAS_DIFF = 41118;
  mapping(address => uint256) public nonces;
  bytes32 internal PERMIT_DOMAIN_SEPARATOR_WBTC;

  function setStrategist(address _strategist) public {
    require(msg.sender == governance, "!governance");
    strategist = _strategist;
  }

  function setGovernance(address _governance) public {
    require(msg.sender == governance, "!governance");
    governance = _governance;
  }

  function computeCalldataGasDiff() internal pure returns (uint256 diff) {
    if (true) return 0; // TODO: implement exact gas metering
    // EVM charges less for zero bytes, we must compute the offset for refund
    // TODO make this efficient
    uint256 sz;
    assembly {
      sz := calldatasize()
    }
    diff = sz.mul(uint256(68));
    bytes memory slice;
    for (uint256 i = 0; i < sz; i += 0x20) {
      uint256 word;
      assembly {
        word := calldataload(i)
      }
      for (uint256 i = 0; i < 256 && ((uint256(~0) << i) & word) != 0; i += 8) {
        if ((word >> i) & 0xff != 0) diff -= 64;
      }
    }
  }

  function getChainId() internal pure returns (uint256 result) {
    assembly {
      result := chainid()
    }
  }

  function setParameters(
    uint256 _governanceFee,
    uint256 _fee,
    uint256 _burnFee,
    uint256 _keeperReward
  ) public {
    require(governance == msg.sender, "!governance");
    governanceFee = _governanceFee;
    fee = _fee;
    burnFee = _burnFee;
    keeperReward = _keeperReward;
  }

  function initialize(address _governance, address _strategist) public initializer {
    fee = uint256(25e14);
    burnFee = uint256(4e15);
    governanceFee = uint256(5e17);
    governance = _governance;
    strategist = _strategist;
    keeperReward = uint256(1 ether).div(1000);
    //IERC20(renbtc).safeApprove(btcGateway, ~uint256(0) >> 2);
    IERC20(renbtc).safeApprove(renCrv, ~uint256(0) >> 2);
    IERC20(wbtc).safeApprove(renCrv, ~uint256(0) >> 2);
    IERC20(wbtc).safeApprove(tricrypto, ~uint256(0) >> 2);
    IERC20(wbtc).safeApprove(routerv3, ~uint256(0) >> 2);
    IERC20(usdc).safeApprove(routerv3, ~uint256(0) >> 2);
    PERMIT_DOMAIN_SEPARATOR_WBTC = keccak256(
      abi.encode(
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
        keccak256("WBTC"),
        keccak256("1"),
        getChainId(),
        wbtc
      )
    );
  }

  function applyRatio(uint256 v, uint256 n) internal pure returns (uint256 result) {
    result = v.mul(n).div(uint256(1 ether));
  }

  function toWBTC(uint256 amount) internal returns (uint256 amountOut) {
    return ICurveInt128(renCrv).exchange_underlying(1, 0, amount, 1);
  }

  function toUSDC(
    uint256 minOut,
    uint256 amountIn,
    address out
  ) internal returns (uint256 amountOut) {
    uint256 wbtcAmountIn = toWBTC(amountIn);
    bytes memory path = abi.encodePacked(wbtc, wethWbtcFee, weth, usdcWethFee, usdc);
    ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({
      recipient: out,
      deadline: block.timestamp + 1,
      amountIn: wbtcAmountIn,
      amountOutMinimum: minOut,
      path: path
    });
    amountOut = ISwapRouter(routerv3).exactInput(params);
  }

  function quote() internal {
    bytes memory path = abi.encodePacked(wmatic, wethMaticFee, weth, wethWbtcFee, wbtc);
    uint256 amountOut = IQuoter(quoter).quoteExactInput(path, 1 ether);
    renbtcForOneETHPrice = ICurveInt128(renCrv).get_dy_underlying(1, 0, amountOut);
  }

  function renBTCtoETH(
    uint256 minOut,
    uint256 amountIn,
    address out
  ) internal returns (uint256 amountOut) {
    uint256 wbtcAmountOut = toWBTC(amountIn);
    bytes memory path = abi.encodePacked(wbtc, wethWbtcFee, weth, wethMaticFee, wmatic);
    ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({
      recipient: address(this),
      deadline: block.timestamp + 1,
      amountIn: wbtcAmountOut,
      amountOutMinimum: minOut,
      path: path
    });
    amountOut = ISwapRouter(routerv3).exactInput(params);
    address payable to = address(uint160(out));
    IWETH9(wmatic).withdraw(amountOut);
    to.transfer(amountOut);
  }

  function fromUSDC(uint256 minOut, uint256 amountIn) internal returns (uint256 amountOut) {
    bytes memory path = abi.encodePacked(usdc, usdcWethFee, weth, wethWbtcFee, wbtc);
    ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({
      recipient: address(this),
      deadline: block.timestamp + 1,
      amountIn: amountIn,
      amountOutMinimum: minOut,
      path: path
    });
    amountOut = ISwapRouter(routerv3).exactInput(params);
    amountOut = toRenBTC(amountOut);
  }

  function toRenBTC(uint256 amountIn) internal returns (uint256 amountOut) {
    return ICurveInt128(renCrv).exchange_underlying(0, 1, amountIn, 1);
  }

  function fromETHToRenBTC(uint256 minOut, uint256 amountIn) internal returns (uint256 amountOut) {
    bytes memory path = abi.encodePacked(wmatic, wethMaticFee, weth, wethWbtcFee, wbtc);
    ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({
      recipient: address(this),
      deadline: block.timestamp + 1,
      amountIn: amountIn,
      amountOutMinimum: minOut,
      path: path
    });
    amountOut = ISwapRouter(routerv3).exactInput{ value: amountIn }(params);
    return toRenBTC(amountOut);
  }

  function toETH() internal returns (uint256 amountOut) {
    uint256 wbtcStart = IERC20(wbtc).balanceOf(address(this));

    uint256 amountStart = address(this).balance;
    (bool success, ) = tricrypto.call(
      abi.encodeWithSelector(ICurveETHUInt256.exchange.selector, 1, 2, wbtcStart, 0, true)
    );
    amountOut = address(this).balance.sub(amountStart);
  }

  receive() external payable {
    // no-op
  }

  function earn() public {
    quote();
    toWBTC(IERC20(renbtc).balanceOf(address(this)));
    toETH();
    uint256 balance = address(this).balance;
    if (balance > ETH_RESERVE) {
      uint256 output = balance - ETH_RESERVE;
      uint256 toGovernance = applyRatio(output, governanceFee);
      bool success;
      address payable governancePayable = address(uint160(governance));
      (success, ) = governancePayable.call{ value: toGovernance, gas: gasleft() }("");
      require(success, "error sending to governance");
      address payable strategistPayable = address(uint160(strategist));
      (success, ) = strategistPayable.call{ value: output.sub(toGovernance), gas: gasleft() }("");
      require(success, "error sending to strategist");
    }
  }

  function computeRenBTCGasFee(uint256 gasCost, uint256 gasPrice) internal view returns (uint256 result) {
    result = gasCost.mul(tx.gasprice).mul(renbtcForOneETHPrice).div(uint256(1 ether));
  }

  function deductMintFee(uint256 amountIn, uint256 multiplier) internal view returns (uint256 amount) {
    amount = amountIn.sub(applyFee(amountIn, fee, multiplier));
  }

  function deductBurnFee(uint256 amountIn, uint256 multiplier) internal view returns (uint256 amount) {
    amount = amountIn.sub(applyFee(amountIn, burnFee, multiplier));
  }

  function applyFee(
    uint256 amountIn,
    uint256 _fee,
    uint256 multiplier
  ) internal view returns (uint256 amount) {
    amount = computeRenBTCGasFee(GAS_COST.add(keeperReward.div(tx.gasprice)), tx.gasprice).add(
      applyRatio(amountIn, _fee)
    );
  }

  struct LoanParams {
    address to;
    address asset;
    uint256 nonce;
    uint256 amount;
    address module;
    address underwriter;
    bytes data;
    uint256 minOut;
    uint256 _mintAmount;
    uint256 gasDiff;
  }

  function toTypedDataHash(LoanParams memory params) internal view returns (bytes32 result) {
    bytes32 digest = _hashTypedDataV4(
      keccak256(
        abi.encode(
          keccak256(
            "TransferRequest(address asset,uint256 amount,address underwriter,address module,uint256 nonce,bytes data)"
          ),
          params.asset,
          params.amount,
          params.underwriter,
          params.module,
          params.nonce,
          keccak256(params.data)
        )
      )
    );
    return digest;
  }

  function repay(
    address underwriter,
    address to,
    address asset,
    uint256 amount,
    uint256 actualAmount,
    uint256 nonce,
    address module,
    bytes32 nHash,
    bytes memory data,
    bytes memory signature
  ) public returns (uint256 amountOut) {
    require(msg.data.length <= 516, "too much calldata");
    uint256 _gasBefore = gasleft();
    LoanParams memory params;
    {
      require(module == wbtc || module == usdc || module == renbtc || module == address(0x0), "!approved-module");
      params = LoanParams({
        to: to,
        asset: asset,
        amount: amount,
        nonce: nonce,
        module: module,
        underwriter: underwriter,
        data: data,
        minOut: 1,
        _mintAmount: 0,
        gasDiff: computeCalldataGasDiff()
      });
      if (data.length > 0) (params.minOut) = abi.decode(data, (uint256));
    }
    bytes32 digest = toTypedDataHash(params);

    params._mintAmount = IGateway(btcGateway).mint(
      keccak256(abi.encode(params.to, params.nonce, params.module, params.data)),
      actualAmount,
      nHash,
      signature
    );
    {
      amountOut = module == wbtc ? toWBTC(deductMintFee(params._mintAmount, 1)) : module == address(0x0)
        ? renBTCtoETH(params.minOut, deductMintFee(params._mintAmount, 1), to)
        : module == usdc
        ? toUSDC(params.minOut, deductMintFee(params._mintAmount, 1), to)
        : deductMintFee(params._mintAmount, 1);
    }
    {
      if (module != usdc && module != address(0x0)) IERC20(module).safeTransfer(to, amountOut);
    }
    {
      tx.origin.transfer(
        Math.min(
          _gasBefore.sub(gasleft()).add(REPAY_GAS_DIFF).add(params.gasDiff).mul(tx.gasprice).add(keeperReward),
          address(this).balance
        )
      );
    }
  }

  function computeBurnNonce(BurnLocals memory params) internal view returns (uint256 result) {
    result = uint256(
      keccak256(
        abi.encodePacked(params.asset, params.amount, params.deadline, params.nonce, params.data, params.destination)
      )
    );
    while (result < block.timestamp) {
      // negligible probability of this
      result = uint256(keccak256(abi.encodePacked(result)));
    }
  }

  function computeERC20PermitDigest(bytes32 domainSeparator, BurnLocals memory params)
    internal
    view
    returns (bytes32 result)
  {
    result = keccak256(
      abi.encodePacked(
        "\x19\x01",
        domainSeparator,
        keccak256(abi.encode(PERMIT_TYPEHASH, params.to, address(this), params.nonce, computeBurnNonce(params), true))
      )
    );
  }

  struct BurnLocals {
    address to;
    address asset;
    uint256 amount;
    uint256 deadline;
    uint256 nonce;
    bytes data;
    uint256 minOut;
    uint256 burnNonce;
    uint256 gasBefore;
    uint256 gasDiff;
    uint8 v;
    bytes32 r;
    bytes32 s;
    bytes destination;
    bytes signature;
  }

  function burn(
    address to,
    address asset,
    uint256 amount,
    uint256 deadline,
    bytes memory data,
    bytes memory destination,
    bytes memory signature
  ) public returns (uint256 amountToBurn) {
    require(msg.data.length <= 580, "too much calldata");
    BurnLocals memory params = BurnLocals({
      to: to,
      asset: asset,
      amount: amount,
      deadline: deadline,
      data: data,
      nonce: 0,
      burnNonce: 0,
      v: uint8(0),
      r: bytes32(0),
      s: bytes32(0),
      destination: destination,
      signature: signature,
      gasBefore: gasleft(),
      minOut: 1,
      gasDiff: 0
    });
    {
      params.gasDiff = computeCalldataGasDiff();
      if (params.data.length > 0) (params.minOut) = abi.decode(params.data, (uint256));
    }
    require(block.timestamp < params.deadline, "!deadline");

    if (params.asset == wbtc) {
      params.nonce = nonces[to];
      nonces[params.to]++;
      require(
        params.to == ECDSA.recover(computeERC20PermitDigest(PERMIT_DOMAIN_SEPARATOR_WBTC, params), params.signature),
        "!signature"
      ); //  wbtc does not implement ERC20Permit
      {
        IERC20(params.asset).transferFrom(params.to, address(this), params.amount);
        amountToBurn = toRenBTC(deductBurnFee(params.amount, 1));
      }
    } else if (params.asset == renbtc) {
      {
        params.nonce = IERC2612Permit(params.asset).nonces(params.to);
        params.burnNonce = computeBurnNonce(params);
      }
      {
        (params.v, params.r, params.s) = SplitSignatureLib.splitSignature(params.signature);
        IERC2612Permit(params.asset).permit(
          params.to,
          address(this),
          params.nonce,
          params.burnNonce,
          true,
          params.v,
          params.r,
          params.s
        );
      }
      {
        IERC20(params.asset).transferFrom(params.to, address(this), params.amount);
      }
      amountToBurn = deductBurnFee(params.amount, 1);
    } else if (params.asset == usdc) {
      {
        params.nonce = IERC2612Permit(params.asset).nonces(params.to);
        params.burnNonce = computeBurnNonce(params);
      }
      {
        (params.v, params.r, params.s) = SplitSignatureLib.splitSignature(params.signature);
        IERC2612Permit(params.asset).permit(
          params.to,
          address(this),
          params.amount,
          params.burnNonce,
          params.v,
          params.r,
          params.s
        );
      }
      {
        IERC20(params.asset).transferFrom(params.to, address(this), params.amount);
      }
      amountToBurn = deductBurnFee(fromUSDC(params.minOut, params.amount), 1);
    } else revert("!supported-asset");
    {
      IGateway(btcGateway).burn(params.destination, amountToBurn);
    }
    {
      tx.origin.transfer(
        Math.min(
          params.gasBefore.sub(gasleft()).add(BURN_GAS_DIFF).add(params.gasDiff).mul(tx.gasprice).add(keeperReward),
          address(this).balance
        )
      );
    }
  }

  function burnETH(uint256 minOut, bytes memory destination) public payable returns (uint256 amountToBurn) {
    amountToBurn = fromETHToRenBTC(minOut, msg.value.sub(applyRatio(msg.value, burnFee)));
    IGateway(btcGateway).burn(destination, amountToBurn);
  }

  function burnApproved(
    address from,
    address asset,
    uint256 amount,
    uint256 minOut,
    bytes memory destination
  ) public payable returns (uint256 amountToBurn) {
    require(asset == wbtc || asset == usdc || asset == renbtc || asset == address(0x0), "!approved-module");
    if (asset != address(0x0)) IERC20(asset).transferFrom(msg.sender, address(this), amount);
    amountToBurn = asset == wbtc ? toRenBTC(amount.sub(applyRatio(amount, burnFee))) : asset == usdc
      ? fromUSDC(minOut, amount.sub(applyRatio(amount, burnFee)))
      : asset == renbtc
      ? amount
      : fromETHToRenBTC(minOut, msg.value.sub(applyRatio(msg.value, burnFee)));
    IGateway(btcGateway).burn(destination, amountToBurn);
  }

  function fallbackMint(
    address underwriter,
    address to,
    address asset,
    uint256 amount,
    uint256 actualAmount,
    uint256 nonce,
    address module,
    bytes32 nHash,
    bytes memory data,
    bytes memory signature
  ) public {
    LoanParams memory params = LoanParams({
      to: to,
      asset: asset,
      amount: amount,
      nonce: nonce,
      module: module,
      underwriter: underwriter,
      data: data,
      minOut: 1,
      _mintAmount: 0,
      gasDiff: 0
    });
    bytes32 digest = toTypedDataHash(params);
    uint256 _actualAmount = IGateway(btcGateway).mint(
      keccak256(abi.encode(params.to, params.nonce, params.module, params.data)),
      actualAmount,
      nHash,
      signature
    );
    IERC20(asset).safeTransfer(to, _actualAmount);
  }
}

File 87 of 140 : ICurveInt128.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.8.0;

interface ICurveInt128 {
  function get_dy(
    int128,
    int128,
    uint256
  ) external view returns (uint256);

  function get_dy_underlying(
    int128,
    int128,
    uint256
  ) external view returns (uint256);

  function exchange(
    int128,
    int128,
    uint256,
    uint256
  ) external returns (uint256);

  function exchange_underlying(
    int128,
    int128,
    uint256,
    uint256
  ) external returns (uint256);

  function coins(int128) external view returns (address);
}

File 88 of 140 : IWETH9.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';

/// @title Interface for WETH9
interface IWETH9 is IERC20 {
    /// @notice Deposit ether to get wrapped ether
    function deposit() external payable;

    /// @notice Withdraw wrapped ether to get ether
    function withdraw(uint256) external;
}

File 89 of 140 : ICurveETHUInt256.sol
pragma solidity >=0.6.0 <0.8.0;

interface ICurveETHUInt256 {
  function exchange(
    uint256 i,
    uint256 j,
    uint256 dx,
    uint256 min_dy,
    bool use_eth
  ) external payable returns (uint256);
}

File 90 of 140 : IQuoter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;

/// @title Quoter Interface
/// @notice Supports quoting the calculated amounts from exact input or exact output swaps
/// @dev These functions are not marked view because they rely on calling non-view functions and reverting
/// to compute the result. They are also not gas efficient and should not be called on-chain.
interface IQuoter {
    /// @notice Returns the amount out received for a given exact input swap without executing the swap
    /// @param path The path of the swap, i.e. each token pair and the pool fee
    /// @param amountIn The amount of the first token to swap
    /// @return amountOut The amount of the last token that would be received
    function quoteExactInput(bytes memory path, uint256 amountIn) external returns (uint256 amountOut);

    /// @notice Returns the amount out received for a given exact input but for a swap of a single pool
    /// @param tokenIn The token being swapped in
    /// @param tokenOut The token being swapped out
    /// @param fee The fee of the token pool to consider for the pair
    /// @param amountIn The desired input amount
    /// @param sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
    /// @return amountOut The amount of `tokenOut` that would be received
    function quoteExactInputSingle(
        address tokenIn,
        address tokenOut,
        uint24 fee,
        uint256 amountIn,
        uint160 sqrtPriceLimitX96
    ) external returns (uint256 amountOut);

    /// @notice Returns the amount in required for a given exact output swap without executing the swap
    /// @param path The path of the swap, i.e. each token pair and the pool fee. Path must be provided in reverse order
    /// @param amountOut The amount of the last token to receive
    /// @return amountIn The amount of first token required to be paid
    function quoteExactOutput(bytes memory path, uint256 amountOut) external returns (uint256 amountIn);

    /// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool
    /// @param tokenIn The token being swapped in
    /// @param tokenOut The token being swapped out
    /// @param fee The fee of the token pool to consider for the pair
    /// @param amountOut The desired output amount
    /// @param sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
    /// @return amountIn The amount required as the input for the swap in order to receive `amountOut`
    function quoteExactOutputSingle(
        address tokenIn,
        address tokenOut,
        uint24 fee,
        uint256 amountOut,
        uint160 sqrtPriceLimitX96
    ) external returns (uint256 amountIn);
}

File 91 of 140 : ZeroControllerTest.sol
pragma solidity >=0.6.0;

import { ZeroControllerTemplate } from "../controllers/ZeroControllerTemplate.sol";

contract ZeroControllerTest is ZeroControllerTemplate {
  function approveModule(address module, bool flag) public {
    approvedModules[module] = flag;
  }
}

File 92 of 140 : ControllerFundsReleaser.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

import { ZeroController } from "../controllers/ZeroController.sol";
import { ZeroLib } from "../libraries/ZeroLib.sol";

contract ControllerFundsRelease {
  address public governance;
  address public strategist;

  address public onesplit;
  address public rewards;
  mapping(address => address) public vaults;
  mapping(address => address) public strategies;
  mapping(address => mapping(address => bool)) public approvedStrategies;

  uint256 public split = 500;
  uint256 public constant max = 10000;
  uint256 internal maxGasPrice = 100e9;
  uint256 internal maxGasRepay = 250000;
  uint256 internal maxGasLoan = 500000;
  string internal constant UNDERWRITER_LOCK_IMPLEMENTATION_ID = "zero.underwriter.lock-implementation";
  address internal underwriterLockImpl;
  mapping(bytes32 => ZeroLib.LoanStatus) public loanStatus;
  bytes32 internal constant ZERO_DOMAIN_SALT = 0xb225c57bf2111d6955b97ef0f55525b5a400dc909a5506e34b102e193dd53406;
  bytes32 internal constant ZERO_DOMAIN_NAME_HASH = keccak256("ZeroController.RenVMBorrowMessage");
  bytes32 internal constant ZERO_DOMAIN_VERSION_HASH = keccak256("v2");
  bytes32 internal constant ZERO_RENVM_BORROW_MESSAGE_TYPE_HASH =
    keccak256("RenVMBorrowMessage(address module,uint256 amount,address underwriter,uint256 pNonce,bytes pData)");
  bytes32 internal constant TYPE_HASH = keccak256("TransferRequest(address asset,uint256 amount)");
  bytes32 internal ZERO_DOMAIN_SEPARATOR;

  function converters(address, address) public view returns (address) {
    return address(this);
  }

  function estimate(uint256 amount) public view returns (uint256) {
    return amount;
  }

  function convert(address) public returns (uint256) {
    return 5000000;
  }

  function proxy(
    address to,
    bytes memory data,
    uint256 value
  ) public returns (bool) {
    require(governance == msg.sender, "!governance");
    (bool success, bytes memory result) = to.call{ value: value }(data);
    if (!success)
      assembly {
        revert(add(0x20, result), mload(result))
      }
  }
}

File 93 of 140 : LockForLib.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0 <0.8.0;

import { ZeroUnderwriterLock } from "../underwriter/ZeroUnderwriterLock.sol";
import { LockForImplLib } from "./LockForImplLib.sol";

/**
@title lockFor for external linking
@author raymondpulver
*/
library LockForLib {
  function lockFor(
    address nft,
    address underwriterLockImpl,
    address underwriter
  ) external view returns (ZeroUnderwriterLock result) {
    result = LockForImplLib.lockFor(nft, underwriterLockImpl, underwriter);
  }
}

File 94 of 140 : FactoryImplLib.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0 <0.8.0;

import { Implementation } from "./Implementation.sol";
import { Create2 } from "oz410/utils/Create2.sol";

/**
@title clone factory library
@notice deploys implementation or clones
*/
library FactoryImplLib {
  function assembleCreationCode(address implementation) internal pure returns (bytes memory result) {
    result = new bytes(0x37);
    bytes20 targetBytes = bytes20(implementation);
    assembly {
      let clone := add(result, 0x20)
      mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
      mstore(add(clone, 0x14), targetBytes)
      mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
    }
  }

  function computeAddress(
    address creator,
    address implementation,
    bytes32 salt
  ) internal pure returns (address result) {
    result = Create2.computeAddress(salt, keccak256(assembleCreationCode(implementation)), creator);
  }

  function computeImplementationAddress(
    address creator,
    bytes32 bytecodeHash,
    string memory id
  ) internal pure returns (address result) {
    result = Create2.computeAddress(keccak256(abi.encodePacked(id)), bytecodeHash, creator);
  }

  /// @notice Deploys a given master Contract as a clone.
  /// Any ETH transferred with this call is forwarded to the new clone.
  /// Emits `LogDeploy`.
  /// @param implementation Address of implementation
  /// @param salt Salt to use
  /// @return cloneAddress Address of the created clone contract.
  function deploy(address implementation, bytes32 salt) internal returns (address cloneAddress) {
    bytes memory creationCode = assembleCreationCode(implementation);
    assembly {
      cloneAddress := create2(0, add(0x20, creationCode), 0x37, salt)
    }
  }

  function deployImplementation(bytes memory creationCode, string memory id) internal returns (address implementation) {
    bytes32 salt = keccak256(abi.encodePacked(id));
    assembly {
      implementation := create2(0, add(0x20, creationCode), mload(creationCode), salt)
    }
  }
}

File 95 of 140 : MetaExecutorEthereum.sol
pragma solidity >=0.6.0 <0.8.0;
import { ArbitrumConvertLib } from "./ArbitrumConvertLib.sol";
import { IZeroMeta } from "../interfaces/IZeroMeta.sol";
import { SafeMath } from "oz410/math/SafeMath.sol";
import { IERC20 } from "oz410/token/ERC20/IERC20.sol";
import { SafeERC20 } from "oz410/token/ERC20/SafeERC20.sol";
import { IController } from "../interfaces/IController.sol";
import { ICurveETHUInt256 } from "../interfaces/CurvePools/ICurveETHUInt256.sol";
import { IRenCrvArbitrum } from "../interfaces/CurvePools/IRenCrvArbitrum.sol";
import "hardhat/console.sol";

contract MetaExecutorEthereum is IZeroMeta {
  using SafeERC20 for *;
  using SafeMath for *;
  mapping(uint256 => ArbitrumConvertLib.ConvertRecord) public outstanding;
  address public immutable controller;
  uint256 public blockTimeout;
  address public constant weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
  address public constant wbtc = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;
  address public constant want = 0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D;
  address public constant renCrvArbitrum = 0x93054188d876f558f4a66B2EF1d97d16eDf0895B;
  address public constant tricryptoArbitrum = 0x80466c64868E1ab14a1Ddf27A676C3fcBE638Fe5;
  uint256 public capacity;
  struct ConvertRecord {
    uint128 volume;
    uint128 when;
  }
  mapping(uint256 => ConvertRecord) public records;
  modifier onlyController() {
    require(msg.sender == controller, "!controller");
    _;
  }

  function governance() public view returns (address) {
    return IController(controller).governance();
  }

  function setBlockTimeout(uint256 _amount) public {
    require(msg.sender == governance(), "!governance");
    blockTimeout = _amount;
  }

  constructor(
    address _controller,
    uint256 _capacity,
    uint256 _blockTimeout
  ) {
    controller = _controller;
    capacity = _capacity;
    blockTimeout = _blockTimeout;
    IERC20(want).safeApprove(renCrvArbitrum, ~uint256(0) >> 2);
    IERC20(wbtc).safeApprove(tricryptoArbitrum, ~uint256(0) >> 2);
  }

  receive() external payable {
    // no-op
  }

  function receiveMeta(
    address from,
    address asset,
    uint256 nonce,
    bytes memory data
  ) public override onlyController {
    // stuff here
  }

  function repayMeta(uint256 value) public override onlyController {
    // stuff here
    console.log(IERC20(want).balanceOf(address(this)));
    IERC20(want).safeTransfer(controller, value);
    console.log(want, value);
  }

  function computeReserveRequirement(uint256 _in) external view returns (uint256) {
    return _in.mul(12e17).div(1e18); // 120% collateralized
  }
}

File 96 of 140 : ArbitrumConvertLib.sol
// SPDX-License-Identifier: MIT

library ArbitrumConvertLib {
  struct ConvertRecord {
    uint256 when;
    uint256 qty;
    uint256 qtyETH;
  }
}

File 97 of 140 : IRenCrvArbitrum.sol
interface IRenCrvArbitrum {
  function exchange(
    int128 i,
    int128 j,
    uint256 dx,
    uint256 min_dy,
    address recipient
  ) external returns (uint256);
}

File 98 of 140 : MetaExecutor.sol
pragma solidity >=0.6.0 <0.8.0;
import { ArbitrumConvertLib } from "./ArbitrumConvertLib.sol";
import { IZeroMeta } from "../interfaces/IZeroMeta.sol";
import { SafeMath } from "oz410/math/SafeMath.sol";
import { IERC20 } from "oz410/token/ERC20/IERC20.sol";
import { SafeERC20 } from "oz410/token/ERC20/SafeERC20.sol";
import { IController } from "../interfaces/IController.sol";
import { ICurveETHUInt256 } from "../interfaces/CurvePools/ICurveETHUInt256.sol";
import { IRenCrvArbitrum } from "../interfaces/CurvePools/IRenCrvArbitrum.sol";
import "hardhat/console.sol";

contract MetaExecutor is IZeroMeta {
  using SafeERC20 for *;
  using SafeMath for *;
  mapping(uint256 => ArbitrumConvertLib.ConvertRecord) public outstanding;
  address public immutable controller;
  uint256 public blockTimeout;
  address public constant weth = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;
  address public constant wbtc = 0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f;
  address public constant want = 0xDBf31dF14B66535aF65AaC99C32e9eA844e14501;
  address public constant renCrvArbitrum = 0x3E01dD8a5E1fb3481F0F589056b428Fc308AF0Fb;
  address public constant tricryptoArbitrum = 0x960ea3e3C7FB317332d990873d354E18d7645590;
  uint256 public capacity;
  struct ConvertRecord {
    uint128 volume;
    uint128 when;
  }
  mapping(uint256 => ConvertRecord) public records;
  modifier onlyController() {
    require(msg.sender == controller, "!controller");
    _;
  }

  function governance() public view returns (address) {
    return IController(controller).governance();
  }

  function setBlockTimeout(uint256 _amount) public {
    require(msg.sender == governance(), "!governance");
    blockTimeout = _amount;
  }

  constructor(
    address _controller,
    uint256 _capacity,
    uint256 _blockTimeout
  ) {
    controller = _controller;
    capacity = _capacity;
    blockTimeout = _blockTimeout;
    IERC20(want).safeApprove(renCrvArbitrum, ~uint256(0) >> 2);
    IERC20(wbtc).safeApprove(tricryptoArbitrum, ~uint256(0) >> 2);
  }

  receive() external payable {
    // no-op
  }

  function receiveMeta(
    address from,
    address asset,
    uint256 nonce,
    bytes memory data
  ) public override onlyController {
    // stuff here
  }

  function repayMeta(uint256 value) public override onlyController {
    // stuff here
    console.log(IERC20(want).balanceOf(address(this)));
    IERC20(want).safeTransfer(controller, value);
    console.log(want, value);
  }

  function computeReserveRequirement(uint256 _in) external view returns (uint256) {
    return _in.mul(12e17).div(1e18); // 120% collateralized
  }
}

File 99 of 140 : BadgerBridge.sol
pragma solidity >=0.6.0 <0.8.0;
import { BadgerBridgeLib } from "./BadgerBridgeLib.sol";
import { SafeMath } from "oz410/math/SafeMath.sol";
import { IERC20 } from "oz410/token/ERC20/IERC20.sol";
import { SafeERC20 } from "oz410/token/ERC20/SafeERC20.sol";
import { IController } from "../interfaces/IController.sol";
import { ICurveETHUInt256 } from "../interfaces/CurvePools/ICurveETHUInt256.sol";
import { IRenCrv } from "../interfaces/CurvePools/IRenCrv.sol";
import { IZeroModule } from "../interfaces/IZeroModule.sol";

contract BadgerBridge is IZeroModule {
  using SafeERC20 for *;
  using SafeMath for *;
  mapping(uint256 => BadgerBridgeLib.ConvertRecord) public outstanding;
  address public immutable controller;
  address public immutable governance;
  uint256 public blockTimeout;
  address public constant weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
  address public constant wbtc = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;
  address public constant override want = wbtc;
  address public constant renCrv = 0x93054188d876f558f4a66B2EF1d97d16eDf0895B;
  address public constant tricrypto = 0x80466c64868E1ab14a1Ddf27A676C3fcBE638Fe5;
  modifier onlyController() {
    require(msg.sender == controller, "!controller");
    _;
  }

  constructor(address _controller) {
    controller = _controller;
    governance = IController(_controller).governance();
    IERC20(want).safeApprove(renCrv, ~uint256(0) >> 2);
    IERC20(wbtc).safeApprove(tricrypto, ~uint256(0) >> 2);
  }

  function setBlockTimeout(uint256 _ct) public {
    require(msg.sender == governance, "!governance");
    blockTimeout = _ct;
  }

  function isActive(BadgerBridgeLib.ConvertRecord storage record) internal view returns (bool) {
    return record.qty != 0;
  }

  function defaultLoan(uint256 _nonce) public {
    require(block.number >= outstanding[_nonce].when + blockTimeout);
    require(isActive(outstanding[_nonce]), "!outstanding");
    uint256 _amountSwappedBack = outstanding[_nonce].qty;
    IERC20(want).safeTransfer(controller, _amountSwappedBack);
    delete outstanding[_nonce];
  }

  function receiveLoan(
    address _to,
    address, /* _asset */
    uint256 _actual,
    uint256 _nonce,
    bytes memory /* _data */
  ) public override onlyController {
    outstanding[_nonce] = BadgerBridgeLib.ConvertRecord({ qty: uint128(_actual), when: uint128(block.timestamp) });
  }

  receive() external payable {
    // no-op
  }

  function repayLoan(
    address _to,
    address, /* _asset */
    uint256, /* _actualAmount */
    uint256 _nonce,
    bytes memory /* _data */
  ) public override onlyController {
    require(outstanding[_nonce].qty != 0, "!outstanding");
    IERC20(want).safeTransfer(_to, outstanding[_nonce].qty);
    delete outstanding[_nonce];
  }

  function computeReserveRequirement(uint256 _in) external view override returns (uint256) {
    return _in.mul(uint256(1e17)).div(uint256(1 ether));
  }
}

File 100 of 140 : BadgerBridgeLib.sol
// SPDX-License-Identifier: MIT

library BadgerBridgeLib {
  struct ConvertRecord {
    uint128 when;
    uint128 qty;
  }
}

File 101 of 140 : BadgerBridgeZeroControllerAvax.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
pragma abicoder v2;

import { IUniswapV2Router02 } from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import { ISwapRouter } from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import { JoeLibrary } from "../libraries/JoeLibrary.sol";
import { ZeroLib } from "../libraries/ZeroLib.sol";
import { IERC2612Permit } from "../interfaces/IERC2612Permit.sol";
import { IRenCrv } from "../interfaces/CurvePools/IRenCrv.sol";
import { SplitSignatureLib } from "../libraries/SplitSignatureLib.sol";
import { IBadgerSettPeak } from "../interfaces/IBadgerSettPeak.sol";
import { ICurveFi } from "../interfaces/ICurveFiAvax.sol";
import { IGateway } from "../interfaces/IGateway.sol";
import { ICurveUInt256 } from "../interfaces/CurvePools/ICurveUInt256.sol";
import { ICurveInt128 } from "../interfaces/CurvePools/ICurveInt128Avax.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IyVault } from "../interfaces/IyVault.sol";
import { ISett } from "../interfaces/ISett.sol";
import { Math } from "@openzeppelin/contracts/math/Math.sol";
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { ECDSA } from "@openzeppelin/contracts/cryptography/ECDSA.sol";
import { EIP712Upgradeable } from "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol";
import { ICurveFi as ICurveFiRen } from "../interfaces/ICurveFi.sol";
import { IJoeRouter02 } from "@traderjoe-xyz/core/contracts/traderjoe/interfaces/IJoeRouter02.sol";

contract BadgerBridgeZeroControllerAvax is EIP712Upgradeable {
  using SafeERC20 for IERC20;
  using SafeMath for *;
  uint256 public fee;
  address public governance;
  address public strategist;

  address constant btcGateway = 0x05Cadbf3128BcB7f2b89F3dD55E5B0a036a49e20;
  address constant factory = 0x9Ad6C38BE94206cA50bb0d90783181662f0Cfa10;
  address constant crvUsd = 0x7f90122BF0700F9E7e1F688fe926940E8839F353;
  address constant av3Crv = 0x1337BedC9D22ecbe766dF105c9623922A27963EC;
  address constant usdc = 0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664;
  address constant usdc_native = 0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E;
  address constant usdcpool = 0x3a43A5851A3e3E0e25A3c1089670269786be1577;
  address constant wavax = 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7;
  address constant weth = 0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB;
  address constant wbtc = 0x50b7545627a5162F82A992c33b87aDc75187B218;
  address constant avWbtc = 0x686bEF2417b6Dc32C50a3cBfbCC3bb60E1e9a15D;
  address constant renbtc = 0xDBf31dF14B66535aF65AaC99C32e9eA844e14501;
  address constant renCrv = 0x16a7DA911A4DD1d83F3fF066fE28F3C792C50d90;
  address constant tricrypto = 0xB755B949C126C04e0348DD881a5cF55d424742B2;
  address constant renCrvLp = 0xC2b1DF84112619D190193E48148000e3990Bf627;
  address constant joeRouter = 0x60aE616a2155Ee3d9A68541Ba4544862310933d4;
  address constant bCrvRen = 0x6dEf55d2e18486B9dDfaA075bc4e4EE0B28c1545;
  address constant settPeak = 0x41671BA1abcbA387b9b2B752c205e22e916BE6e3;
  address constant ibbtc = 0xc4E15973E6fF2A35cC804c2CF9D2a1b817a8b40F;
  uint256 public governanceFee;
  bytes32 constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
  uint256 constant GAS_COST = uint256(124e4);
  uint256 constant IBBTC_GAS_COST = uint256(7e5);
  uint256 constant ETH_RESERVE = uint256(5 ether);
  bytes32 constant LOCK_SLOT = keccak256("upgrade-lock-v1-avax");
  uint256 internal renbtcForOneETHPrice;
  uint256 internal burnFee;
  uint256 public keeperReward;
  uint256 public constant REPAY_GAS_DIFF = 41510;
  uint256 public constant BURN_GAS_DIFF = 41118;
  mapping(address => uint256) public nonces;
  mapping(address => uint256) public noncesUsdc;
  bytes32 internal PERMIT_DOMAIN_SEPARATOR_WBTC;
  bytes32 internal PERMIT_DOMAIN_SEPARATOR_IBBTC;
  bytes32 internal PERMIT_DOMAIN_SEPARATOR_USDC;

  function setStrategist(address _strategist) public {
    require(msg.sender == governance, "!governance");
    strategist = _strategist;
  }

  function postUpgrade() public {
    bool isLocked;
    bytes32 upgradeSlot = LOCK_SLOT;

    assembly {
      isLocked := sload(upgradeSlot)
    }
    require(!isLocked, "already upgraded");
    IERC20(usdc).safeApprove(usdcpool, ~uint256(0) >> 2);
    IERC20(usdc_native).safeApprove(usdcpool, ~uint256(0) >> 2);
    isLocked = true;
    assembly {
      sstore(upgradeSlot, isLocked)
    }
  }

  function setGovernance(address _governance) public {
    require(msg.sender == governance, "!governance");
    governance = _governance;
  }

  function computeCalldataGasDiff() internal pure returns (uint256 diff) {
    if (true) return 0; // TODO: implement exact gas metering
    // EVM charges less for zero bytes, we must compute the offset for refund
    // TODO make this efficient
    uint256 sz;
    assembly {
      sz := calldatasize()
    }
    diff = sz.mul(uint256(68));
    bytes memory slice;
    for (uint256 i = 0; i < sz; i += 0x20) {
      uint256 word;
      assembly {
        word := calldataload(i)
      }
      for (uint256 i = 0; i < 256 && ((uint256(~0) << i) & word) != 0; i += 8) {
        if ((word >> i) & 0xff != 0) diff -= 64;
      }
    }
  }

  function getChainId() internal pure returns (uint256 result) {
    assembly {
      result := chainid()
    }
  }

  function setParameters(
    uint256 _governanceFee,
    uint256 _fee,
    uint256 _burnFee,
    uint256 _keeperReward
  ) public {
    require(governance == msg.sender, "!governance");
    governanceFee = _governanceFee;
    fee = _fee;
    burnFee = _burnFee;
    keeperReward = _keeperReward;
  }

  function initialize(address _governance, address _strategist) public initializer {
    fee = uint256(25e14);
    burnFee = uint256(4e15);
    governanceFee = uint256(5e17);
    governance = _governance;
    strategist = _strategist;
    keeperReward = uint256(1 ether).div(1000);
    //IERC20(renbtc).safeApprove(btcGateway, ~uint256(0) >> 2);
    IERC20(renbtc).safeApprove(renCrv, ~uint256(0) >> 2);
    IERC20(avWbtc).safeApprove(renCrv, ~uint256(0) >> 2);
    IERC20(wbtc).safeApprove(renCrv, ~uint256(0) >> 2);
    IERC20(avWbtc).safeApprove(tricrypto, ~uint256(0) >> 2);
    IERC20(wbtc).safeApprove(joeRouter, ~uint256(0) >> 2);
    IERC20(weth).safeApprove(tricrypto, ~uint256(0) >> 2);
    IERC20(weth).safeApprove(joeRouter, ~uint256(0) >> 2);
    IERC20(wavax).safeApprove(joeRouter, ~uint256(0) >> 2);
    IERC20(av3Crv).safeApprove(crvUsd, ~uint256(0) >> 2);
    IERC20(av3Crv).safeApprove(tricrypto, ~uint256(0) >> 2);
    IERC20(usdc).safeApprove(crvUsd, ~uint256(0) >> 2);
    IERC20(usdc).safeApprove(usdcpool, ~uint256(0) >> 2);
    IERC20(usdc_native).safeApprove(usdcpool, ~uint256(0) >> 2);
    IERC20(renCrvLp).safeApprove(bCrvRen, ~uint256(0) >> 2);
    //IERC20(bCrvRen).safeApprove(settPeak, ~uint256(0) >> 2);
    PERMIT_DOMAIN_SEPARATOR_WBTC = keccak256(
      abi.encode(
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
        keccak256("WBTC"),
        keccak256("1"),
        getChainId(),
        wbtc
      )
    );
    PERMIT_DOMAIN_SEPARATOR_USDC = keccak256(
      abi.encode(
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
        keccak256("USD Coin"),
        keccak256("1"),
        getChainId(),
        usdc
      )
    );
    PERMIT_DOMAIN_SEPARATOR_IBBTC = keccak256(
      abi.encode(
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
        keccak256("ibBTC"),
        keccak256("1"),
        getChainId(),
        ibbtc
      )
    );
  }

  function applyRatio(uint256 v, uint256 n) internal pure returns (uint256 result) {
    result = v.mul(n).div(uint256(1 ether));
  }

  function toWBTC(uint256 amount, bool useUnderlying) internal returns (uint256 amountOut) {
    if (useUnderlying) amountOut = ICurveInt128(renCrv).exchange_underlying(1, 0, amount, 1);
    else amountOut = ICurveInt128(renCrv).exchange(1, 0, amount, 1);
  }

  function toIBBTC(uint256 amountIn) internal returns (uint256 amountOut) {
    uint256[2] memory amounts;
    amounts[0] = amountIn;
    ICurveFiRen(renCrv).add_liquidity(amounts, 0);
    ISett(bCrvRen).deposit(IERC20(renCrvLp).balanceOf(address(this)));
    amountOut = IBadgerSettPeak(settPeak).mint(0, IERC20(bCrvRen).balanceOf(address(this)), new bytes32[](0));
  }

  function toUSDC(uint256 minOut, uint256 amountIn) internal returns (uint256 amountOut) {
    uint256 usdAmount = IERC20(av3Crv).balanceOf(address(this));
    uint256 wbtcAmount = toWBTC(amountIn, false);
    ICurveUInt256(tricrypto).exchange(1, 0, wbtcAmount, 1);
    usdAmount = IERC20(av3Crv).balanceOf(address(this)).sub(usdAmount);
    amountOut = ICurveFi(crvUsd).remove_liquidity_one_coin(usdAmount, 1, 1, true);
  }

  function toUSDCNative(uint256 amountIn) internal returns (uint256 amountOut) {
    amountOut = toUSDC(1, amountIn);
    amountOut = ICurveInt128(usdcpool).exchange(0, 1, amountOut, 1, address(this));
  }

  function quote() internal {
    (uint256 amountWavax, uint256 amountWBTC) = JoeLibrary.getReserves(factory, wavax, wbtc);
    uint256 amount = JoeLibrary.quote(1 ether, amountWavax, amountWBTC);
    renbtcForOneETHPrice = ICurveInt128(renCrv).get_dy(1, 0, amount);
  }

  function toRenBTC(uint256 amountIn, bool useUnderlying) internal returns (uint256 amountOut) {
    if (useUnderlying) amountOut = ICurveInt128(renCrv).exchange_underlying(0, 1, amountIn, 1);
    else amountOut = ICurveInt128(renCrv).exchange(0, 1, amountIn, 1);
  }

  function renBTCtoETH(
    uint256 minOut,
    uint256 amountIn,
    address out
  ) internal returns (uint256 amountOut) {
    uint256 wbtcAmount = toWBTC(amountIn, true);
    address[] memory path = new address[](2);
    path[0] = wbtc;
    path[1] = wavax;
    uint256[] memory amounts = IJoeRouter02(joeRouter).swapExactTokensForAVAX(
      wbtcAmount,
      minOut,
      path,
      out,
      block.timestamp + 1
    );
    amountOut = amounts[1];
  }

  function fromIBBTC(uint256 amountIn) internal returns (uint256 amountOut) {
    uint256 amountStart = IERC20(renbtc).balanceOf(address(this));
    IBadgerSettPeak(settPeak).redeem(0, amountIn);
    ISett(bCrvRen).withdraw(IERC20(bCrvRen).balanceOf(address(this)));
    ICurveFiRen(renCrv).remove_liquidity_one_coin(IERC20(renCrvLp).balanceOf(address(this)), 0, 0);
    amountOut = IERC20(renbtc).balanceOf(address(this)).sub(amountStart);
  }

  function fromUSDC(uint256 minOut, uint256 amountIn) internal returns (uint256 amountOut) {
    uint256 wbtcAmount = IERC20(avWbtc).balanceOf(address(this));
    uint256[3] memory amounts;
    amounts[1] = amountIn;
    amountOut = ICurveFi(crvUsd).add_liquidity(amounts, 1, true);
    ICurveUInt256(tricrypto).exchange(0, 1, amountOut, 1);
    wbtcAmount = IERC20(avWbtc).balanceOf(address(this)).sub(wbtcAmount);
    amountOut = toRenBTC(wbtcAmount, false);
  }

  function fromUSDCNative(uint256 amountIn) internal returns (uint256 amountOut) {
    uint256 usdceAmountIn = ICurveInt128(usdcpool).exchange(1, 0, amountIn, 1, address(this));
    return fromUSDC(1, usdceAmountIn);
  }

  function fromETHToRenBTC(uint256 minOut, uint256 amountIn) internal returns (uint256 amountOut) {
    address[] memory path = new address[](2);
    path[0] = wavax;
    path[1] = wbtc;

    uint256[] memory amounts = IJoeRouter02(joeRouter).swapExactAVAXForTokens{ value: amountIn }(
      minOut,
      path,
      address(this),
      block.timestamp + 1
    );
    amountOut = toRenBTC(amounts[1], true);
  }

  function toETH() internal returns (uint256 amountOut) {
    uint256 wbtcAmount = IERC20(wbtc).balanceOf(address(this));
    address[] memory path = new address[](2);
    path[0] = wbtc;
    path[1] = wavax;
    uint256[] memory amounts = IJoeRouter02(joeRouter).swapExactTokensForAVAX(
      wbtcAmount,
      1,
      path,
      address(this),
      block.timestamp + 1
    );
    amountOut = amounts[1];
  }

  receive() external payable {
    // no-op
  }

  function earn() public {
    quote();
    toWBTC(IERC20(renbtc).balanceOf(address(this)), true);
    toETH();
    uint256 balance = address(this).balance;
    if (balance > ETH_RESERVE) {
      uint256 output = balance - ETH_RESERVE;
      uint256 toGovernance = applyRatio(output, governanceFee);
      bool success;
      address payable governancePayable = address(uint160(governance));
      (success, ) = governancePayable.call{ value: toGovernance, gas: gasleft() }("");
      require(success, "error sending to governance");
      address payable strategistPayable = address(uint160(strategist));
      (success, ) = strategistPayable.call{ value: output.sub(toGovernance), gas: gasleft() }("");
      require(success, "error sending to strategist");
    }
  }

  function computeRenBTCGasFee(uint256 gasCost, uint256 gasPrice) internal view returns (uint256 result) {
    result = gasCost.mul(tx.gasprice).mul(renbtcForOneETHPrice).div(uint256(1 ether));
  }

  function deductMintFee(uint256 amountIn, uint256 multiplier) internal view returns (uint256 amount) {
    amount = amountIn.sub(applyFee(amountIn, fee, multiplier));
  }

  function deductIBBTCMintFee(uint256 amountIn, uint256 multiplier) internal view returns (uint256 amount) {
    amount = amountIn.sub(applyIBBTCFee(amountIn, fee, multiplier));
  }

  function deductBurnFee(uint256 amountIn, uint256 multiplier) internal view returns (uint256 amount) {
    amount = amountIn.sub(applyFee(amountIn, burnFee, multiplier));
  }

  function deductIBBTCBurnFee(uint256 amountIn, uint256 multiplier) internal view returns (uint256 amount) {
    amount = amountIn.sub(applyIBBTCFee(amountIn, burnFee, multiplier));
  }

  function applyFee(
    uint256 amountIn,
    uint256 _fee,
    uint256 multiplier
  ) internal view returns (uint256 amount) {
    amount = computeRenBTCGasFee(GAS_COST.add(keeperReward.div(tx.gasprice)), tx.gasprice).add(
      applyRatio(amountIn, _fee)
    );
  }

  function applyIBBTCFee(
    uint256 amountIn,
    uint256 _fee,
    uint256 multiplier
  ) internal view returns (uint256 amount) {
    amount = computeRenBTCGasFee(IBBTC_GAS_COST.add(keeperReward.div(tx.gasprice)), tx.gasprice).add(
      applyRatio(amountIn, _fee)
    );
  }

  struct LoanParams {
    address to;
    address asset;
    uint256 nonce;
    uint256 amount;
    address module;
    address underwriter;
    bytes data;
    uint256 minOut;
    uint256 _mintAmount;
    uint256 gasDiff;
  }

  function toTypedDataHash(LoanParams memory params) internal view returns (bytes32 result) {
    bytes32 digest = _hashTypedDataV4(
      keccak256(
        abi.encode(
          keccak256(
            "TransferRequest(address asset,uint256 amount,address underwriter,address module,uint256 nonce,bytes data)"
          ),
          params.asset,
          params.amount,
          params.underwriter,
          params.module,
          params.nonce,
          keccak256(params.data)
        )
      )
    );
    return digest;
  }

  function repay(
    address underwriter,
    address to,
    address asset,
    uint256 amount,
    uint256 actualAmount,
    uint256 nonce,
    address module,
    bytes32 nHash,
    bytes memory data,
    bytes memory signature
  ) public returns (uint256 amountOut) {
    require(msg.data.length <= 516, "too much calldata");
    uint256 _gasBefore = gasleft();
    LoanParams memory params;
    {
      require(
        module == wbtc || module == usdc || module == renbtc || module == address(0x0) || module == usdc_native,
        "!approved-module"
      );
      params = LoanParams({
        to: to,
        asset: asset,
        amount: amount,
        nonce: nonce,
        module: module,
        underwriter: underwriter,
        data: data,
        minOut: 1,
        _mintAmount: 0,
        gasDiff: computeCalldataGasDiff()
      });
      if (data.length > 0) (params.minOut) = abi.decode(data, (uint256));
    }
    bytes32 digest = toTypedDataHash(params);

    params._mintAmount = IGateway(btcGateway).mint(
      keccak256(abi.encode(params.to, params.nonce, params.module, params.data)),
      actualAmount,
      nHash,
      signature
    );
    {
      amountOut = module == wbtc ? toWBTC(deductMintFee(params._mintAmount, 1), true) : module == address(0x0)
        ? renBTCtoETH(params.minOut, deductMintFee(params._mintAmount, 1), to)
        : module == usdc
        ? toUSDC(params.minOut, deductMintFee(params._mintAmount, 1))
        : module == usdc_native
        ? toUSDCNative(deductMintFee(params._mintAmount, 1))
        : deductMintFee(params._mintAmount, 1);
    }
    {
      if (module != address(0x0)) IERC20(module).safeTransfer(to, amountOut);
    }
    {
      tx.origin.transfer(
        Math.min(
          _gasBefore.sub(gasleft()).add(REPAY_GAS_DIFF).add(params.gasDiff).mul(tx.gasprice).add(keeperReward),
          address(this).balance
        )
      );
    }
  }

  function computeBurnNonce(BurnLocals memory params) internal view returns (uint256 result) {
    result = uint256(
      keccak256(
        abi.encodePacked(params.asset, params.amount, params.deadline, params.nonce, params.data, params.destination)
      )
    );
    while (result < block.timestamp) {
      // negligible probability of this
      result = uint256(keccak256(abi.encodePacked(result)));
    }
  }

  function computeERC20PermitDigest(bytes32 domainSeparator, BurnLocals memory params)
    internal
    view
    returns (bytes32 result)
  {
    result = keccak256(
      abi.encodePacked(
        "\x19\x01",
        domainSeparator,
        keccak256(abi.encode(PERMIT_TYPEHASH, params.to, address(this), params.nonce, computeBurnNonce(params), true))
      )
    );
  }

  struct BurnLocals {
    address to;
    address asset;
    uint256 amount;
    uint256 deadline;
    uint256 nonce;
    bytes data;
    uint256 minOut;
    uint256 burnNonce;
    uint256 gasBefore;
    uint256 gasDiff;
    uint8 v;
    bytes32 r;
    bytes32 s;
    bytes destination;
    bytes signature;
  }

  function burn(
    address to,
    address asset,
    uint256 amount,
    uint256 deadline,
    bytes memory data,
    bytes memory destination,
    bytes memory signature
  ) public returns (uint256 amountToBurn) {
    require(msg.data.length <= 580, "too much calldata");
    BurnLocals memory params = BurnLocals({
      to: to,
      asset: asset,
      amount: amount,
      deadline: deadline,
      data: data,
      nonce: 0,
      burnNonce: 0,
      v: uint8(0),
      r: bytes32(0),
      s: bytes32(0),
      destination: destination,
      signature: signature,
      gasBefore: gasleft(),
      minOut: 1,
      gasDiff: 0
    });
    {
      params.gasDiff = computeCalldataGasDiff();
      if (params.data.length > 0) (params.minOut) = abi.decode(params.data, (uint256));
    }
    require(block.timestamp < params.deadline, "!deadline");

    if (params.asset == wbtc) {
      params.nonce = nonces[to];
      nonces[params.to]++;
      require(
        params.to == ECDSA.recover(computeERC20PermitDigest(PERMIT_DOMAIN_SEPARATOR_WBTC, params), params.signature),
        "!signature"
      ); //  wbtc does not implement ERC20Permit
      {
        IERC20(params.asset).transferFrom(params.to, address(this), params.amount);
        amountToBurn = toRenBTC(deductBurnFee(params.amount, 1), true);
      }
    } else if (asset == usdc_native) {
      {
        params.nonce = IERC2612Permit(params.asset).nonces(params.to);
        params.burnNonce = computeBurnNonce(params);
      }
      {
        (params.v, params.r, params.s) = SplitSignatureLib.splitSignature(params.signature);
        IERC2612Permit(params.asset).permit(
          params.to,
          address(this),
          params.amount,
          params.burnNonce,
          params.v,
          params.r,
          params.s
        );
      }
      {
        IERC20(params.asset).transferFrom(params.to, address(this), params.amount);
        amountToBurn = deductBurnFee(fromUSDCNative(params.amount), 1);
      }
    } else if (params.asset == renbtc) {
      {
        params.nonce = IERC2612Permit(params.asset).nonces(params.to);
        params.burnNonce = computeBurnNonce(params);
      }
      {
        (params.v, params.r, params.s) = SplitSignatureLib.splitSignature(params.signature);
        IERC2612Permit(params.asset).permit(
          params.to,
          address(this),
          params.nonce,
          params.burnNonce,
          true,
          params.v,
          params.r,
          params.s
        );
      }
      {
        IERC20(params.asset).transferFrom(params.to, address(this), params.amount);
      }
      amountToBurn = deductBurnFee(params.amount, 1);
    } else if (params.asset == usdc) {
      params.nonce = noncesUsdc[to];
      noncesUsdc[params.to]++;
      require(
        params.to == ECDSA.recover(computeERC20PermitDigest(PERMIT_DOMAIN_SEPARATOR_USDC, params), params.signature),
        "!signature"
      ); //  usdc.e does not implement ERC20Permit
      {
        IERC20(params.asset).transferFrom(params.to, address(this), params.amount);
      }
      amountToBurn = deductBurnFee(fromUSDC(params.minOut, params.amount), 1);
    } else revert("!supported-asset");
    {
      IGateway(btcGateway).burn(params.destination, amountToBurn);
    }
    {
      tx.origin.transfer(
        Math.min(
          params.gasBefore.sub(gasleft()).add(BURN_GAS_DIFF).add(params.gasDiff).mul(tx.gasprice).add(keeperReward),
          address(this).balance
        )
      );
    }
  }

  function burnETH(uint256 minOut, bytes memory destination) public payable returns (uint256 amountToBurn) {
    amountToBurn = fromETHToRenBTC(minOut, msg.value.sub(applyRatio(msg.value, burnFee)));
    IGateway(btcGateway).burn(destination, amountToBurn);
  }

  function burnApproved(
    address from,
    address asset,
    uint256 amount,
    uint256 minOut,
    bytes memory destination
  ) public payable returns (uint256 amountToBurn) {
    require(asset == wbtc || asset == usdc || asset == renbtc || asset == address(0x0), "!approved-module");
    if (asset != address(0x0)) IERC20(asset).transferFrom(msg.sender, address(this), amount);
    amountToBurn = asset == wbtc ? toRenBTC(amount.sub(applyRatio(amount, burnFee)), true) : asset == usdc
      ? fromUSDC(minOut, amount.sub(applyRatio(amount, burnFee)))
      : asset == renbtc
      ? amount
      : fromETHToRenBTC(minOut, msg.value.sub(applyRatio(msg.value, burnFee)));
    IGateway(btcGateway).burn(destination, amountToBurn);
  }

  function fallbackMint(
    address underwriter,
    address to,
    address asset,
    uint256 amount,
    uint256 actualAmount,
    uint256 nonce,
    address module,
    bytes32 nHash,
    bytes memory data,
    bytes memory signature
  ) public {
    LoanParams memory params = LoanParams({
      to: to,
      asset: asset,
      amount: amount,
      nonce: nonce,
      module: module,
      underwriter: underwriter,
      data: data,
      minOut: 1,
      _mintAmount: 0,
      gasDiff: 0
    });
    bytes32 digest = toTypedDataHash(params);
    uint256 _actualAmount = IGateway(btcGateway).mint(
      keccak256(abi.encode(params.to, params.nonce, params.module, params.data)),
      actualAmount,
      nHash,
      signature
    );
    IERC20(asset).safeTransfer(to, _actualAmount);
  }
}

File 102 of 140 : JoeLibrary.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.5.0;

import "@traderjoe-xyz/core/contracts/traderjoe/interfaces/IJoePair.sol";

import "oz410/math/SafeMath.sol";

library JoeLibrary {
  using SafeMath for uint256;

  // returns sorted token addresses, used to handle return values from pairs sorted in this order
  function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
    require(tokenA != tokenB, "JoeLibrary: IDENTICAL_ADDRESSES");
    (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
    require(token0 != address(0), "JoeLibrary: ZERO_ADDRESS");
  }

  // calculates the CREATE2 address for a pair without making any external calls
  function pairFor(
    address factory,
    address tokenA,
    address tokenB
  ) internal pure returns (address pair) {
    (address token0, address token1) = sortTokens(tokenA, tokenB);
    pair = address(
      uint256(
        keccak256(
          abi.encodePacked(
            hex"ff",
            factory,
            keccak256(abi.encodePacked(token0, token1)),
            hex"0bbca9af0511ad1a1da383135cf3a8d2ac620e549ef9f6ae3a4c33c2fed0af91" // init code fuji
          )
        )
      )
    );
  }

  // fetches and sorts the reserves for a pair
  function getReserves(
    address factory,
    address tokenA,
    address tokenB
  ) internal view returns (uint256 reserveA, uint256 reserveB) {
    (address token0, ) = sortTokens(tokenA, tokenB);
    (uint256 reserve0, uint256 reserve1, ) = IJoePair(pairFor(factory, tokenA, tokenB)).getReserves();
    (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
  }

  // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
  function quote(
    uint256 amountA,
    uint256 reserveA,
    uint256 reserveB
  ) internal pure returns (uint256 amountB) {
    require(amountA > 0, "JoeLibrary: INSUFFICIENT_AMOUNT");
    require(reserveA > 0 && reserveB > 0, "JoeLibrary: INSUFFICIENT_LIQUIDITY");
    amountB = amountA.mul(reserveB) / reserveA;
  }

  // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
  function getAmountOut(
    uint256 amountIn,
    uint256 reserveIn,
    uint256 reserveOut
  ) internal pure returns (uint256 amountOut) {
    require(amountIn > 0, "JoeLibrary: INSUFFICIENT_INPUT_AMOUNT");
    require(reserveIn > 0 && reserveOut > 0, "JoeLibrary: INSUFFICIENT_LIQUIDITY");
    uint256 amountInWithFee = amountIn.mul(997);
    uint256 numerator = amountInWithFee.mul(reserveOut);
    uint256 denominator = reserveIn.mul(1000).add(amountInWithFee);
    amountOut = numerator / denominator;
  }

  // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
  function getAmountIn(
    uint256 amountOut,
    uint256 reserveIn,
    uint256 reserveOut
  ) internal pure returns (uint256 amountIn) {
    require(amountOut > 0, "JoeLibrary: INSUFFICIENT_OUTPUT_AMOUNT");
    require(reserveIn > 0 && reserveOut > 0, "JoeLibrary: INSUFFICIENT_LIQUIDITY");
    uint256 numerator = reserveIn.mul(amountOut).mul(1000);
    uint256 denominator = reserveOut.sub(amountOut).mul(997);
    amountIn = (numerator / denominator).add(1);
  }

  // performs chained getAmountOut calculations on any number of pairs
  function getAmountsOut(
    address factory,
    uint256 amountIn,
    address[] memory path
  ) internal view returns (uint256[] memory amounts) {
    require(path.length >= 2, "JoeLibrary: INVALID_PATH");
    amounts = new uint256[](path.length);
    amounts[0] = amountIn;
    for (uint256 i; i < path.length - 1; i++) {
      (uint256 reserveIn, uint256 reserveOut) = getReserves(factory, path[i], path[i + 1]);
      amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
    }
  }

  // performs chained getAmountIn calculations on any number of pairs
  function getAmountsIn(
    address factory,
    uint256 amountOut,
    address[] memory path
  ) internal view returns (uint256[] memory amounts) {
    require(path.length >= 2, "JoeLibrary: INVALID_PATH");
    amounts = new uint256[](path.length);
    amounts[amounts.length - 1] = amountOut;
    for (uint256 i = path.length - 1; i > 0; i--) {
      (uint256 reserveIn, uint256 reserveOut) = getReserves(factory, path[i - 1], path[i]);
      amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
    }
  }
}

File 103 of 140 : ICurveFiAvax.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0;

interface ICurveFi {
  function add_liquidity(
    uint256[3] calldata amounts,
    uint256 min_amount,
    bool use_underlying
  ) external returns (uint256);

  function remove_liquidity_one_coin(
    uint256,
    int128,
    uint256,
    bool
  ) external returns (uint256);
}

File 104 of 140 : ICurveUInt256.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.8.0;

interface ICurveUInt256 {
  function get_dy(
    uint256,
    uint256,
    uint256
  ) external view returns (uint256);

  function exchange(
    uint256,
    uint256,
    uint256,
    uint256
  ) external returns (uint256);

  function coins(uint256) external view returns (address);
}

File 105 of 140 : ICurveInt128Avax.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.8.0;

interface ICurveInt128 {
  function get_dy(
    int128,
    int128,
    uint256
  ) external view returns (uint256);

  function get_dy_underlying(
    int128,
    int128,
    uint256
  ) external view returns (uint256);

  function exchange(
    int128,
    int128,
    uint256,
    uint256
  ) external returns (uint256);

  function exchange(
    int128,
    int128,
    uint256,
    uint256,
    address
  ) external returns (uint256);

  function exchange_underlying(
    int128,
    int128,
    uint256,
    uint256
  ) external returns (uint256);

  function coins(int128) external view returns (address);
}

File 106 of 140 : IJoeRouter02.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.6.2;

import "./IJoeRouter01.sol";

interface IJoeRouter02 is IJoeRouter01 {
    function removeLiquidityAVAXSupportingFeeOnTransferTokens(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountAVAXMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountAVAX);

    function removeLiquidityAVAXWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountAVAXMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountAVAX);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external;

    function swapExactAVAXForTokensSupportingFeeOnTransferTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable;

    function swapExactTokensForAVAXSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external;
}

File 107 of 140 : IJoePair.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.5.0;

interface IJoePair {
    event Approval(address indexed owner, address indexed spender, uint256 value);
    event Transfer(address indexed from, address indexed to, uint256 value);

    function name() external pure returns (string memory);

    function symbol() external pure returns (string memory);

    function decimals() external pure returns (uint8);

    function totalSupply() external view returns (uint256);

    function balanceOf(address owner) external view returns (uint256);

    function allowance(address owner, address spender) external view returns (uint256);

    function approve(address spender, uint256 value) external returns (bool);

    function transfer(address to, uint256 value) external returns (bool);

    function transferFrom(
        address from,
        address to,
        uint256 value
    ) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);

    function PERMIT_TYPEHASH() external pure returns (bytes32);

    function nonces(address owner) external view returns (uint256);

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    event Mint(address indexed sender, uint256 amount0, uint256 amount1);
    event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint256 amount0In,
        uint256 amount1In,
        uint256 amount0Out,
        uint256 amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint256);

    function factory() external view returns (address);

    function token0() external view returns (address);

    function token1() external view returns (address);

    function getReserves()
        external
        view
        returns (
            uint112 reserve0,
            uint112 reserve1,
            uint32 blockTimestampLast
        );

    function price0CumulativeLast() external view returns (uint256);

    function price1CumulativeLast() external view returns (uint256);

    function kLast() external view returns (uint256);

    function mint(address to) external returns (uint256 liquidity);

    function burn(address to) external returns (uint256 amount0, uint256 amount1);

    function swap(
        uint256 amount0Out,
        uint256 amount1Out,
        address to,
        bytes calldata data
    ) external;

    function skim(address to) external;

    function sync() external;

    function initialize(address, address) external;
}

File 108 of 140 : IJoeRouter01.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.6.2;

interface IJoeRouter01 {
    function factory() external pure returns (address);

    function WAVAX() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    )
        external
        returns (
            uint256 amountA,
            uint256 amountB,
            uint256 liquidity
        );

    function addLiquidityAVAX(
        address token,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountAVAXMin,
        address to,
        uint256 deadline
    )
        external
        payable
        returns (
            uint256 amountToken,
            uint256 amountAVAX,
            uint256 liquidity
        );

    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB);

    function removeLiquidityAVAX(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountAVAXMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountToken, uint256 amountAVAX);

    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountA, uint256 amountB);

    function removeLiquidityAVAXWithPermit(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountAVAXMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountToken, uint256 amountAVAX);

    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapTokensForExactTokens(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapExactAVAXForTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable returns (uint256[] memory amounts);

    function swapTokensForExactAVAX(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapExactTokensForAVAX(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapAVAXForExactTokens(
        uint256 amountOut,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable returns (uint256[] memory amounts);

    function quote(
        uint256 amountA,
        uint256 reserveA,
        uint256 reserveB
    ) external pure returns (uint256 amountB);

    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) external pure returns (uint256 amountOut);

    function getAmountIn(
        uint256 amountOut,
        uint256 reserveIn,
        uint256 reserveOut
    ) external pure returns (uint256 amountIn);

    function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts);

    function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts);
}

File 109 of 140 : BadgerBridgeZeroControllerOptimism.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0;
pragma abicoder v2;

import { ISwapRouter } from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import { UniswapV2Library } from "../libraries/UniswapV2Library.sol";
import { ZeroLib } from "../libraries/ZeroLib.sol";
import { IERC2612Permit } from "../interfaces/IERC2612Permit.sol";
import { ICurveInt128 } from "../interfaces/CurvePools/ICurveInt128.sol";
import { SplitSignatureLib } from "../libraries/SplitSignatureLib.sol";
import { IBadgerSettPeak } from "../interfaces/IBadgerSettPeak.sol";
import { ICurveFi } from "../interfaces/ICurveFi.sol";
import { IGateway } from "../interfaces/IGateway.sol";
import { IWETH9 } from "@uniswap/v3-periphery/contracts/interfaces/external/IWETH9.sol";
import { ICurveETHUInt256 } from "../interfaces/CurvePools/ICurveETHUInt256.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IyVault } from "../interfaces/IyVault.sol";
import { ISett } from "../interfaces/ISett.sol";
import { Math } from "@openzeppelin/contracts/math/Math.sol";
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { IQuoter } from "@uniswap/v3-periphery/contracts/interfaces/IQuoter.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { ECDSA } from "@openzeppelin/contracts/cryptography/ECDSA.sol";
import { EIP712Upgradeable } from "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol";

contract BadgerBridgeZeroControllerOptimism is EIP712Upgradeable {
  using SafeERC20 for IERC20;
  using SafeMath for *;
  uint256 public fee;
  address public governance;
  address public strategist;

  address constant btcGateway = 0xB538901719936e628A9b9AF64A5a4Dbc273305cd;
  address constant renbtc = 0x85f6583762Bc76d775eAB9A7456db344f12409F7;
  uint256 public governanceFee;
  bytes32 constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
  bytes32 constant LOCK_SLOT = keccak256("upgrade-lock-v2");
  uint256 constant GAS_COST = uint256(642e3);
  uint256 constant ETH_RESERVE = uint256(5 ether);
  uint256 internal renbtcForOneETHPrice;
  uint256 internal burnFee;
  uint256 public keeperReward;
  uint256 public constant REPAY_GAS_DIFF = 41510;
  uint256 public constant BURN_GAS_DIFF = 41118;

  function setStrategist(address _strategist) public {
    require(msg.sender == governance, "!governance");
    strategist = _strategist;
  }

  function setGovernance(address _governance) public {
    require(msg.sender == governance, "!governance");
    governance = _governance;
  }

  function computeCalldataGasDiff() internal pure returns (uint256 diff) {
    if (true) return 0; // TODO: implement exact gas metering
    // EVM charges less for zero bytes, we must compute the offset for refund
    // TODO make this efficient
    uint256 sz;
    assembly {
      sz := calldatasize()
    }
    diff = sz.mul(uint256(68));
    bytes memory slice;
    for (uint256 i = 0; i < sz; i += 0x20) {
      uint256 word;
      assembly {
        word := calldataload(i)
      }
      for (uint256 i = 0; i < 256 && ((uint256(~0) << i) & word) != 0; i += 8) {
        if ((word >> i) & 0xff != 0) diff -= 64;
      }
    }
  }

  function getChainId() internal pure returns (uint256 result) {
    assembly {
      result := chainid()
    }
  }

  function setParameters(
    uint256 _governanceFee,
    uint256 _fee,
    uint256 _burnFee,
    uint256 _keeperReward
  ) public {
    require(governance == msg.sender, "!governance");
    governanceFee = _governanceFee;
    fee = _fee;
    burnFee = _burnFee;
    keeperReward = _keeperReward;
  }

  function initialize(address _governance, address _strategist) public initializer {
    fee = uint256(25e14);
    burnFee = uint256(4e15);
    governanceFee = uint256(5e17);
    governance = _governance;
    strategist = _strategist;
    keeperReward = uint256(1 ether).div(1000);
  }

  function applyRatio(uint256 v, uint256 n) internal pure returns (uint256 result) {
    result = v.mul(n).div(uint256(1 ether));
  }

  function quote() internal {}

  receive() external payable {
    // no-op
  }

  function earn() public {
    quote();
    uint256 balance = address(this).balance;
    if (balance > ETH_RESERVE) {
      uint256 output = balance - ETH_RESERVE;
      uint256 toGovernance = applyRatio(output, governanceFee);
      bool success;
      address payable governancePayable = address(uint160(governance));
      (success, ) = governancePayable.call{ value: toGovernance, gas: gasleft() }("");
      require(success, "error sending to governance");
      address payable strategistPayable = address(uint160(strategist));
      (success, ) = strategistPayable.call{ value: output.sub(toGovernance), gas: gasleft() }("");
      require(success, "error sending to strategist");
    }
  }

  function computeRenBTCGasFee(uint256 gasCost, uint256 gasPrice) internal view returns (uint256 result) {
    result = gasCost.mul(tx.gasprice).mul(renbtcForOneETHPrice).div(uint256(1 ether));
  }

  function deductMintFee(uint256 amountIn, uint256 multiplier) internal view returns (uint256 amount) {
    amount = amountIn.sub(applyFee(amountIn, fee, multiplier));
  }

  function deductBurnFee(uint256 amountIn, uint256 multiplier) internal view returns (uint256 amount) {
    amount = amountIn.sub(applyFee(amountIn, burnFee, multiplier));
  }

  function applyFee(
    uint256 amountIn,
    uint256 _fee,
    uint256 multiplier
  ) internal view returns (uint256 amount) {
    amount = computeRenBTCGasFee(GAS_COST.add(keeperReward.div(tx.gasprice)), tx.gasprice).add(
      applyRatio(amountIn, _fee)
    );
  }

  struct LoanParams {
    address to;
    address asset;
    uint256 nonce;
    uint256 amount;
    address module;
    address underwriter;
    bytes data;
    uint256 minOut;
    uint256 _mintAmount;
    uint256 gasDiff;
  }

  function toTypedDataHash(LoanParams memory params) internal view returns (bytes32 result) {
    bytes32 digest = _hashTypedDataV4(
      keccak256(
        abi.encode(
          keccak256(
            "TransferRequest(address asset,uint256 amount,address underwriter,address module,uint256 nonce,bytes data)"
          ),
          params.asset,
          params.amount,
          params.underwriter,
          params.module,
          params.nonce,
          keccak256(params.data)
        )
      )
    );
    return digest;
  }

  function repay(
    address underwriter,
    address to,
    address asset,
    uint256 amount,
    uint256 actualAmount,
    uint256 nonce,
    address module,
    bytes32 nHash,
    bytes memory data,
    bytes memory signature
  ) public returns (uint256 amountOut) {
    require(msg.data.length <= 516, "too much calldata");
    uint256 _gasBefore = gasleft();
    LoanParams memory params;
    {
      require(module == renbtc, "!approved-module");
      params = LoanParams({
        to: to,
        asset: asset,
        amount: amount,
        nonce: nonce,
        module: module,
        underwriter: underwriter,
        data: data,
        minOut: 1,
        _mintAmount: 0,
        gasDiff: computeCalldataGasDiff()
      });
      if (data.length > 0) (params.minOut) = abi.decode(data, (uint256));
    }
    bytes32 digest = toTypedDataHash(params);

    params._mintAmount = IGateway(btcGateway).mint(
      keccak256(abi.encode(params.to, params.nonce, params.module, params.data)),
      actualAmount,
      nHash,
      signature
    );
    {
      amountOut = deductMintFee(params._mintAmount, 1);
    }
    {
      IERC20(module).safeTransfer(to, amountOut);
    }
    {
      tx.origin.transfer(
        Math.min(
          _gasBefore.sub(gasleft()).add(REPAY_GAS_DIFF).add(params.gasDiff).mul(tx.gasprice).add(keeperReward),
          address(this).balance
        )
      );
    }
  }

  function computeBurnNonce(BurnLocals memory params) internal view returns (uint256 result) {
    result = uint256(
      keccak256(
        abi.encodePacked(params.asset, params.amount, params.deadline, params.nonce, params.data, params.destination)
      )
    );
    while (result < block.timestamp) {
      // negligible probability of this
      result = uint256(keccak256(abi.encodePacked(result)));
    }
  }

  function computeERC20PermitDigest(bytes32 domainSeparator, BurnLocals memory params)
    internal
    view
    returns (bytes32 result)
  {
    result = keccak256(
      abi.encodePacked(
        "\x19\x01",
        domainSeparator,
        keccak256(abi.encode(PERMIT_TYPEHASH, params.to, address(this), params.nonce, computeBurnNonce(params), true))
      )
    );
  }

  struct BurnLocals {
    address to;
    address asset;
    uint256 amount;
    uint256 deadline;
    uint256 nonce;
    bytes data;
    uint256 minOut;
    uint256 burnNonce;
    uint256 gasBefore;
    uint256 gasDiff;
    uint8 v;
    bytes32 r;
    bytes32 s;
    bytes destination;
    bytes signature;
  }

  function burn(
    address to,
    address asset,
    uint256 amount,
    uint256 deadline,
    bytes memory data,
    bytes memory destination,
    bytes memory signature
  ) public returns (uint256 amountToBurn) {
    require(msg.data.length <= 580, "too much calldata");
    BurnLocals memory params = BurnLocals({
      to: to,
      asset: asset,
      amount: amount,
      deadline: deadline,
      data: data,
      nonce: 0,
      burnNonce: 0,
      v: uint8(0),
      r: bytes32(0),
      s: bytes32(0),
      destination: destination,
      signature: signature,
      gasBefore: gasleft(),
      minOut: 1,
      gasDiff: 0
    });
    {
      params.gasDiff = computeCalldataGasDiff();
      if (params.data.length > 0) (params.minOut) = abi.decode(params.data, (uint256));
    }
    require(block.timestamp < params.deadline, "!deadline");

    if (params.asset == renbtc) {
      {
        params.nonce = IERC2612Permit(params.asset).nonces(params.to);
        params.burnNonce = computeBurnNonce(params);
      }
      {
        (params.v, params.r, params.s) = SplitSignatureLib.splitSignature(params.signature);
        IERC2612Permit(params.asset).permit(
          params.to,
          address(this),
          params.nonce,
          params.burnNonce,
          true,
          params.v,
          params.r,
          params.s
        );
      }
      {
        IERC20(params.asset).transferFrom(params.to, address(this), params.amount);
      }
      amountToBurn = deductBurnFee(params.amount, 1);
    } else revert("!supported-asset");
    {
      IGateway(btcGateway).burn(params.destination, amountToBurn);
    }
    {
      tx.origin.transfer(
        Math.min(
          params.gasBefore.sub(gasleft()).add(BURN_GAS_DIFF).add(params.gasDiff).mul(tx.gasprice).add(keeperReward),
          address(this).balance
        )
      );
    }
  }

  function fallbackMint(
    address underwriter,
    address to,
    address asset,
    uint256 amount,
    uint256 actualAmount,
    uint256 nonce,
    address module,
    bytes32 nHash,
    bytes memory data,
    bytes memory signature
  ) public {
    LoanParams memory params = LoanParams({
      to: to,
      asset: asset,
      amount: amount,
      nonce: nonce,
      module: module,
      underwriter: underwriter,
      data: data,
      minOut: 1,
      _mintAmount: 0,
      gasDiff: 0
    });
    bytes32 digest = toTypedDataHash(params);
    uint256 _actualAmount = IGateway(btcGateway).mint(
      keccak256(abi.encode(params.to, params.nonce, params.module, params.data)),
      actualAmount,
      nHash,
      signature
    );
    IERC20(asset).safeTransfer(to, _actualAmount);
  }
}

File 110 of 140 : RenZECController.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0;
pragma abicoder v2;

import { IUniswapV2Router02 } from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import { ISwapRouter } from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import { UniswapV2Library } from "../libraries/UniswapV2Library.sol";
import { ZeroLib } from "../libraries/ZeroLib.sol";
import { IERC2612Permit } from "../interfaces/IERC2612Permit.sol";
import { SplitSignatureLib } from "../libraries/SplitSignatureLib.sol";
import { IWETH } from "../interfaces/IWETH.sol";
import { IGateway } from "../interfaces/IGateway.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Math } from "@openzeppelin/contracts/math/Math.sol";
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { ECDSA } from "@openzeppelin/contracts/cryptography/ECDSA.sol";
import { EIP712Upgradeable } from "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol";

contract RenZECController is EIP712Upgradeable {
  using SafeERC20 for IERC20;
  using SafeMath for *;
  uint256 public fee;
  address public governance;
  address public strategist;

  address constant router = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
  address constant renzec = 0x1C5db575E2Ff833E46a2E9864C22F4B22E0B37C2;
  address constant zecGateway = 0xc3BbD5aDb611dd74eCa6123F05B18acc886e122D;
  address constant routerv3 = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
  address constant factory = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
  address constant weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
  address constant usdt = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
  address constant usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
  address constant quoter = 0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6;
  uint24 constant uniswapv3Fee = 500;
  uint256 public governanceFee;
  bytes32 constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
  uint256 constant GAS_COST = uint256(36e4);
  bytes32 constant LOCK_SLOT = keccak256("upgrade-v1");
  uint256 constant ETH_RESERVE = uint256(5 ether);
  uint256 internal renzecForOneETHPrice;
  uint256 internal burnFee;
  uint256 public keeperReward;
  uint256 public constant REPAY_GAS_DIFF = 41510;
  uint256 public constant BURN_GAS_DIFF = 41118;
  bytes32 internal PERMIT_DOMAIN_SEPARATOR_USDT;
  mapping(address => uint256) public noncesUsdt;

  function setStrategist(address _strategist) public {
    require(msg.sender == governance, "!governance");
    strategist = _strategist;
  }

  function setGovernance(address _governance) public {
    require(msg.sender == governance, "!governance");
    governance = _governance;
  }

  function computeCalldataGasDiff() internal pure returns (uint256 diff) {
    if (true) return 0; // TODO: implement exact gas metering
    // EVM charges less for zero bytes, we must compute the offset for refund
    // TODO make this efficient
    uint256 sz;
    assembly {
      sz := calldatasize()
    }
    diff = sz.mul(uint256(68));
    bytes memory slice;
    for (uint256 i = 0; i < sz; i += 0x20) {
      uint256 word;
      assembly {
        word := calldataload(i)
      }
      for (uint256 i = 0; i < 256 && ((uint256(~0) << i) & word) != 0; i += 8) {
        if ((word >> i) & 0xff != 0) diff -= 64;
      }
    }
  }

  function getChainId() internal pure returns (uint256 result) {
    assembly {
      result := chainid()
    }
  }

  function setParameters(
    uint256 _governanceFee,
    uint256 _fee,
    uint256 _burnFee,
    uint256 _keeperReward
  ) public {
    require(governance == msg.sender, "!governance");
    governanceFee = _governanceFee;
    fee = _fee;
    burnFee = _burnFee;
    keeperReward = _keeperReward;
  }

  function initialize(address _governance, address _strategist) public initializer {
    fee = uint256(25e14);
    burnFee = uint256(4e15);
    governanceFee = uint256(5e17);
    governance = _governance;
    strategist = _strategist;
    keeperReward = uint256(1 ether).div(1000);
    IERC20(weth).safeApprove(routerv3, ~uint256(0) >> 2);
    IERC20(usdc).safeApprove(routerv3, ~uint256(0) >> 2);
    IERC20(usdt).safeApprove(routerv3, ~uint256(0) >> 2);
    IERC20(weth).safeApprove(router, ~uint256(0) >> 2);
    IERC20(renzec).safeApprove(router, ~uint256(0) >> 2);
    PERMIT_DOMAIN_SEPARATOR_USDT = keccak256(
      abi.encode(
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
        keccak256("USDT"),
        keccak256("1"),
        getChainId(),
        usdt
      )
    );
  }

  function postUpgrade() public {
    bool isLocked;
    bytes32 upgradeSlot = LOCK_SLOT;

    assembly {
      isLocked := sload(upgradeSlot)
    }

    require(!isLocked, "already upgraded");
    PERMIT_DOMAIN_SEPARATOR_USDT = keccak256(
      abi.encode(
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
        keccak256("USDT"),
        keccak256("1"),
        getChainId(),
        usdt
      )
    );
    IERC20(usdc).safeApprove(routerv3, ~uint256(0) >> 2);
    IERC20(usdt).safeApprove(routerv3, ~uint256(0) >> 2);

    isLocked = true;
    assembly {
      sstore(upgradeSlot, isLocked)
    }
  }

  function applyRatio(uint256 v, uint256 n) internal pure returns (uint256 result) {
    result = v.mul(n).div(uint256(1 ether));
  }

  function quote() internal {
    (uint256 amountWeth, uint256 amountRenZEC) = UniswapV2Library.getReserves(factory, weth, renzec);
    renzecForOneETHPrice = UniswapV2Library.quote(uint256(1 ether), amountWeth, amountRenZEC);
  }

  function renZECtoETH(
    uint256 minOut,
    uint256 amountIn,
    address out
  ) internal returns (uint256 amountOut) {
    address[] memory path = new address[](2);
    path[0] = renzec;
    path[1] = weth;
    uint256[] memory amounts = new uint256[](2);
    amounts = IUniswapV2Router02(router).swapExactTokensForETH(amountIn, minOut, path, out, block.timestamp + 1);
    return amounts[1];
  }

  function fromETHToRenZEC(uint256 minOut, uint256 amountIn) internal returns (uint256) {
    address[] memory path = new address[](2);
    path[0] = weth;
    path[1] = renzec;
    uint256[] memory amounts = new uint256[](2);
    amounts = IUniswapV2Router02(router).swapExactETHForTokens{ value: amountIn }(
      minOut,
      path,
      address(this),
      block.timestamp + 1
    );
    return amounts[1];
  }

  function fromUSDC(uint256 minOut, uint256 amountIn) internal returns (uint256 amountOut) {
    bytes memory path = abi.encodePacked(usdc, uniswapv3Fee, weth);
    ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({
      recipient: address(this),
      deadline: block.timestamp + 1,
      amountIn: amountIn,
      amountOutMinimum: 1,
      path: path
    });
    amountOut = ISwapRouter(routerv3).exactInput(params);
    IWETH(weth).withdraw(amountOut);
    amountOut = fromETHToRenZEC(minOut, amountOut);
  }

  function fromUSDT(uint256 minOut, uint256 amountIn) internal returns (uint256 amountOut) {
    bytes memory path = abi.encodePacked(usdt, uniswapv3Fee, weth);
    ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({
      recipient: address(this),
      deadline: block.timestamp + 1,
      amountIn: amountIn,
      amountOutMinimum: 1,
      path: path
    });
    amountOut = ISwapRouter(routerv3).exactInput(params);
    IWETH(weth).withdraw(amountOut);
    amountOut = fromETHToRenZEC(minOut, amountOut);
  }

  function toUSDC(
    uint256 minOut,
    uint256 amountIn,
    address out
  ) internal returns (uint256 amountOut) {
    bytes memory path = abi.encodePacked(weth, uniswapv3Fee, usdc);
    amountOut = renZECtoETH(1, amountIn, address(this));
    ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({
      recipient: out,
      deadline: block.timestamp + 1,
      amountIn: amountOut,
      amountOutMinimum: minOut,
      path: path
    });
    amountOut = ISwapRouter(routerv3).exactInput{ value: amountOut }(params);
  }

  function toUSDT(
    uint256 minOut,
    uint256 amountIn,
    address out
  ) internal returns (uint256 amountOut) {
    bytes memory path = abi.encodePacked(weth, uniswapv3Fee, usdt);
    amountOut = renZECtoETH(1, amountIn, address(this));
    ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({
      recipient: out,
      deadline: block.timestamp + 1,
      amountIn: amountOut,
      amountOutMinimum: minOut,
      path: path
    });
    amountOut = ISwapRouter(routerv3).exactInput{ value: amountOut }(params);
  }

  function toETH() internal returns (uint256 amountOut) {
    address[] memory path = new address[](2);
    path[0] = renzec;
    path[1] = weth;
    uint256[] memory amounts = new uint256[](2);
    IUniswapV2Router02(router).swapExactTokensForETH(
      IERC20(renzec).balanceOf(address(this)),
      1,
      path,
      address(this),
      block.timestamp + 1
    );
  }

  receive() external payable {
    // no-op
  }

  function earn() public {
    quote();
    toETH();
    uint256 balance = address(this).balance;
    if (balance > ETH_RESERVE) {
      uint256 output = balance - ETH_RESERVE;
      uint256 toGovernance = applyRatio(output, governanceFee);
      bool success;
      address payable governancePayable = address(uint160(governance));
      (success, ) = governancePayable.call{ value: toGovernance, gas: gasleft() }("");
      require(success, "error sending to governance");
      address payable strategistPayable = address(uint160(strategist));
      (success, ) = strategistPayable.call{ value: output.sub(toGovernance), gas: gasleft() }("");
      require(success, "error sending to strategist");
    }
  }

  function computeRenZECGasFee(uint256 gasCost, uint256 gasPrice) internal view returns (uint256 result) {
    result = gasCost.mul(tx.gasprice).mul(renzecForOneETHPrice).div(uint256(1 ether));
  }

  function deductMintFee(uint256 amountIn, uint256 multiplier) internal view returns (uint256 amount) {
    amount = amountIn.sub(applyFee(amountIn, fee, multiplier));
  }

  function deductBurnFee(uint256 amountIn, uint256 multiplier) internal view returns (uint256 amount) {
    amount = amountIn.sub(applyFee(amountIn, burnFee, multiplier));
  }

  function applyFee(
    uint256 amountIn,
    uint256 _fee,
    uint256 multiplier
  ) internal view returns (uint256 amount) {
    amount = computeRenZECGasFee(GAS_COST.add(keeperReward.div(tx.gasprice)), tx.gasprice).add(
      applyRatio(amountIn, _fee)
    );
  }

  struct LoanParams {
    address to;
    address asset;
    uint256 nonce;
    uint256 amount;
    address module;
    address underwriter;
    bytes data;
    uint256 minOut;
    uint256 _mintAmount;
    uint256 gasDiff;
  }

  function toTypedDataHash(LoanParams memory params) internal view returns (bytes32 result) {
    bytes32 digest = _hashTypedDataV4(
      keccak256(
        abi.encode(
          keccak256(
            "TransferRequest(address asset,uint256 amount,address underwriter,address module,uint256 nonce,bytes data)"
          ),
          params.asset,
          params.amount,
          params.underwriter,
          params.module,
          params.nonce,
          keccak256(params.data)
        )
      )
    );
    return digest;
  }

  function repay(
    address underwriter,
    address to,
    address asset,
    uint256 amount,
    uint256 actualAmount,
    uint256 nonce,
    address module,
    bytes32 nHash,
    bytes memory data,
    bytes memory signature
  ) public returns (uint256 amountOut) {
    require(msg.data.length <= 516, "too much calldata");
    uint256 _gasBefore = gasleft();
    LoanParams memory params;
    {
      require(module == usdc || module == usdt || module == address(0x0) || module == renzec, "!approved-module");
      params = LoanParams({
        to: to,
        asset: asset,
        amount: amount,
        nonce: nonce,
        module: module,
        underwriter: underwriter,
        data: data,
        minOut: 1,
        _mintAmount: 0,
        gasDiff: computeCalldataGasDiff()
      });
      if (data.length > 0) (params.minOut) = abi.decode(data, (uint256));
    }
    bytes32 digest = toTypedDataHash(params);

    params._mintAmount = IGateway(zecGateway).mint(
      keccak256(abi.encode(params.to, params.nonce, params.module, params.data)),
      actualAmount,
      nHash,
      signature
    );

    {
      amountOut = module == address(0x0)
        ? renZECtoETH(params.minOut, deductMintFee(params._mintAmount, 1), to)
        : module == usdc
        ? toUSDC(params.minOut, deductMintFee(params._mintAmount, 1), to)
        : module == usdt
        ? toUSDT(params.minOut, deductMintFee(params._mintAmount, 1), to)
        : deductMintFee(params._mintAmount, 1);
    }
    {
      if (module == renzec) IERC20(module).safeTransfer(to, amountOut);
    }
    {
      tx.origin.transfer(
        Math.min(
          _gasBefore.sub(gasleft()).add(REPAY_GAS_DIFF).add(params.gasDiff).mul(tx.gasprice).add(keeperReward),
          address(this).balance
        )
      );
    }
  }

  function burnETH(uint256 minOut, bytes memory destination) public payable returns (uint256 amountToBurn) {
    amountToBurn = fromETHToRenZEC(minOut, msg.value.sub(applyRatio(msg.value, burnFee)));
    IGateway(zecGateway).burn(destination, amountToBurn);
  }

  function computeBurnNonce(BurnLocals memory params) internal view returns (uint256 result) {
    result = uint256(
      keccak256(
        abi.encodePacked(params.asset, params.amount, params.deadline, params.nonce, params.data, params.destination)
      )
    );
    while (result < block.timestamp) {
      // negligible probability of this
      result = uint256(keccak256(abi.encodePacked(result)));
    }
  }

  function computeERC20PermitDigest(bytes32 domainSeparator, BurnLocals memory params)
    internal
    view
    returns (bytes32 result)
  {
    result = keccak256(
      abi.encodePacked(
        "\x19\x01",
        domainSeparator,
        keccak256(abi.encode(PERMIT_TYPEHASH, params.to, address(this), params.nonce, computeBurnNonce(params), true))
      )
    );
  }

  struct BurnLocals {
    address to;
    address asset;
    uint256 amount;
    uint256 deadline;
    uint256 nonce;
    bytes data;
    uint256 minOut;
    uint256 burnNonce;
    uint256 gasBefore;
    uint256 gasDiff;
    uint8 v;
    bytes32 r;
    bytes32 s;
    bytes destination;
    bytes signature;
  }

  function burn(
    address to,
    address asset,
    uint256 amount,
    uint256 deadline,
    bytes memory data,
    bytes memory destination,
    bytes memory signature
  ) public returns (uint256 amountToBurn) {
    require(msg.data.length <= 580, "too much calldata");
    BurnLocals memory params = BurnLocals({
      to: to,
      asset: asset,
      amount: amount,
      deadline: deadline,
      data: data,
      nonce: 0,
      burnNonce: 0,
      v: uint8(0),
      r: bytes32(0),
      s: bytes32(0),
      destination: destination,
      signature: signature,
      gasBefore: gasleft(),
      minOut: 1,
      gasDiff: 0
    });
    {
      params.gasDiff = computeCalldataGasDiff();
      if (params.data.length > 0) (params.minOut) = abi.decode(params.data, (uint256));
    }
    require(block.timestamp < params.deadline, "!deadline");
    if (params.asset == renzec) {
      {
        params.nonce = IERC2612Permit(params.asset).nonces(params.to);
        params.burnNonce = computeBurnNonce(params);
      }
      {
        (params.v, params.r, params.s) = SplitSignatureLib.splitSignature(params.signature);
        IERC2612Permit(params.asset).permit(
          params.to,
          address(this),
          params.nonce,
          params.burnNonce,
          true,
          params.v,
          params.r,
          params.s
        );
      }
      {
        IERC20(params.asset).transferFrom(params.to, address(this), params.amount);
      }
      amountToBurn = deductBurnFee(params.amount, 1);
    } else if (params.asset == usdc) {
      {
        params.nonce = IERC2612Permit(params.asset).nonces(params.to);
        params.burnNonce = computeBurnNonce(params);
      }
      {
        (params.v, params.r, params.s) = SplitSignatureLib.splitSignature(params.signature);
        IERC2612Permit(params.asset).permit(
          params.to,
          address(this),
          params.amount,
          params.burnNonce,
          params.v,
          params.r,
          params.s
        );
      }
      {
        IERC20(params.asset).safeTransferFrom(params.to, address(this), params.amount);
      }
      amountToBurn = deductBurnFee(fromUSDC(params.minOut, params.amount), 1);
    } else if (params.asset == usdt) {
      params.nonce = noncesUsdt[to];
      noncesUsdt[params.to]++;
      require(
        params.to == ECDSA.recover(computeERC20PermitDigest(PERMIT_DOMAIN_SEPARATOR_USDT, params), params.signature),
        "!signature"
      ); //  usdt does not implement ERC20Permit
      {
        (bool success, ) = params.asset.call(
          abi.encodeWithSelector(IERC20.transferFrom.selector, params.to, address(this), params.amount)
        );
        require(success, "!usdt");
      }
      amountToBurn = deductBurnFee(fromUSDT(params.minOut, params.amount), 1);
    } else revert("!supported-asset");
    {
      IGateway(zecGateway).burn(params.destination, amountToBurn);
    }
    {
      tx.origin.transfer(
        Math.min(
          params.gasBefore.sub(gasleft()).add(BURN_GAS_DIFF).add(params.gasDiff).mul(tx.gasprice).add(keeperReward),
          address(this).balance
        )
      );
    }
  }

  function burnApproved(
    address from,
    address asset,
    uint256 amount,
    uint256 minOut,
    bytes memory destination
  ) public payable returns (uint256 amountToBurn) {
    require(asset == renzec || asset == address(0x0), "!approved-module");
    if (asset != address(0x0)) IERC20(asset).transferFrom(msg.sender, address(this), amount);
    amountToBurn = asset == renzec ? amount : fromETHToRenZEC(minOut, msg.value.sub(applyRatio(msg.value, burnFee)));
    IGateway(zecGateway).burn(destination, amountToBurn);
  }

  function fallbackMint(
    address underwriter,
    address to,
    address asset,
    uint256 amount,
    uint256 actualAmount,
    uint256 nonce,
    address module,
    bytes32 nHash,
    bytes memory data,
    bytes memory signature
  ) public {
    LoanParams memory params = LoanParams({
      to: to,
      asset: asset,
      amount: amount,
      nonce: nonce,
      module: module,
      underwriter: underwriter,
      data: data,
      minOut: 1,
      _mintAmount: 0,
      gasDiff: 0
    });
    bytes32 digest = toTypedDataHash(params);
    uint256 _actualAmount = IGateway(zecGateway).mint(
      keccak256(abi.encode(params.to, params.nonce, params.module, params.data)),
      actualAmount,
      nHash,
      signature
    );
    IERC20(asset).safeTransfer(to, _actualAmount);
  }
}

File 111 of 140 : RenZECControllerDeployer.sol
pragma solidity >=0.6.0;

import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/TransparentUpgradeableProxy.sol";
import { RenZECController } from "./RenZECController.sol";

contract RenZECControllerDeployer {
  event Deployed(address indexed controller);

  constructor() {
    emit Deployed(
      address(
        new TransparentUpgradeableProxy(
          address(new RenZECController()),
          address(0xFF727BDFa7608d7Fd12Cd2cDA1e7736ACbfCdB7B),
          abi.encodeWithSelector(
            RenZECController.initialize.selector,
            address(0x5E9B37149b7d7611bD0Eb070194dDA78EB11EfdC),
            address(0x5E9B37149b7d7611bD0Eb070194dDA78EB11EfdC)
          )
        )
      )
    );
    selfdestruct(msg.sender);
  }
}

File 112 of 140 : BadgerBridgeZeroControllerArb.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0;
pragma abicoder v2;

import { IUniswapV2Router02 } from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import { ISwapRouter } from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import { IQuoter } from "@uniswap/v3-periphery/contracts/interfaces/IQuoter.sol";
import { UniswapV2Library } from "../libraries/UniswapV2Library.sol";
import { ZeroLib } from "../libraries/ZeroLib.sol";
import { IERC2612Permit } from "../interfaces/IERC2612Permit.sol";
import { IRenCrvArbitrum } from "../interfaces/CurvePools/IRenCrvArbitrum.sol";
import { SplitSignatureLib } from "../libraries/SplitSignatureLib.sol";
import { IBadgerSettPeak } from "../interfaces/IBadgerSettPeak.sol";
import { ICurveFi } from "../interfaces/ICurveFi.sol";
import { IGateway } from "../interfaces/IGateway.sol";
import { IWETH9 } from "@uniswap/v3-periphery/contracts/interfaces/external/IWETH9.sol";
import { ICurveETHUInt256 } from "../interfaces/CurvePools/ICurveETHUInt256.sol";
import { ICurveInt128 } from "../interfaces/CurvePools/ICurveInt128.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IyVault } from "../interfaces/IyVault.sol";
import { ISett } from "../interfaces/ISett.sol";
import { Math } from "@openzeppelin/contracts/math/Math.sol";
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { ECDSA } from "@openzeppelin/contracts/cryptography/ECDSA.sol";
import { EIP712Upgradeable } from "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol";

contract BadgerBridgeZeroControllerArb is EIP712Upgradeable {
  using SafeERC20 for IERC20;
  using SafeMath for *;
  uint256 public fee;
  address public governance;
  address public strategist;

  address constant btcGateway = 0x05Cadbf3128BcB7f2b89F3dD55E5B0a036a49e20;
  address constant routerv3 = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
  address constant factory = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
  address constant usdc = 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8;
  address constant weth = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;
  address constant wbtc = 0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f;
  address constant renbtc = 0xDBf31dF14B66535aF65AaC99C32e9eA844e14501;
  address constant renCrv = 0x3E01dD8a5E1fb3481F0F589056b428Fc308AF0Fb;
  address constant threepool = 0x7f90122BF0700F9E7e1F688fe926940E8839F353;
  address constant tricrypto = 0x960ea3e3C7FB317332d990873d354E18d7645590;
  address constant renCrvLp = 0x3E01dD8a5E1fb3481F0F589056b428Fc308AF0Fb;
  address constant bCrvRen = 0x6dEf55d2e18486B9dDfaA075bc4e4EE0B28c1545;
  address constant settPeak = 0x41671BA1abcbA387b9b2B752c205e22e916BE6e3;
  address constant quoter = 0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6;
  address constant ibbtc = 0xc4E15973E6fF2A35cC804c2CF9D2a1b817a8b40F;
  uint24 constant wethWbtcFee = 500;
  uint24 constant usdcWethFee = 500;
  uint256 public governanceFee;
  bytes32 constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
  bytes32 constant LOCK_SLOT = keccak256("upgrade-lock-v2");
  uint256 constant GAS_COST = uint256(48e4);
  uint256 constant IBBTC_GAS_COST = uint256(7e5);
  uint256 constant ETH_RESERVE = uint256(5 ether);
  uint256 internal renbtcForOneETHPrice;
  uint256 internal burnFee;
  uint256 public keeperReward;
  uint256 public constant REPAY_GAS_DIFF = 41510;
  uint256 public constant BURN_GAS_DIFF = 41118;
  mapping(address => uint256) public nonces;
  bytes32 internal PERMIT_DOMAIN_SEPARATOR_WBTC;
  bytes32 internal PERMIT_DOMAIN_SEPARATOR_IBBTC;

  function setStrategist(address _strategist) public {
    require(msg.sender == governance, "!governance");
    strategist = _strategist;
  }

  function setGovernance(address _governance) public {
    require(msg.sender == governance, "!governance");
    governance = _governance;
  }

  function approveUpgrade(bool lock) public {
    bool isLocked;
    bytes32 lock_slot = LOCK_SLOT;

    assembly {
      isLocked := sload(lock_slot)
    }
    require(!isLocked, "cannot run upgrade function");
    assembly {
      sstore(lock_slot, lock)
    }
  }

  function computeCalldataGasDiff() internal pure returns (uint256 diff) {
    if (true) return 0; // TODO: implement exact gas metering
    // EVM charges less for zero bytes, we must compute the offset for refund
    // TODO make this efficient
    uint256 sz;
    assembly {
      sz := calldatasize()
    }
    diff = sz.mul(uint256(68));
    bytes memory slice;
    for (uint256 i = 0; i < sz; i += 0x20) {
      uint256 word;
      assembly {
        word := calldataload(i)
      }
      for (uint256 i = 0; i < 256 && ((uint256(~0) << i) & word) != 0; i += 8) {
        if ((word >> i) & 0xff != 0) diff -= 64;
      }
    }
  }

  function getChainId() internal pure returns (uint256 result) {
    assembly {
      result := chainid()
    }
  }

  function setParameters(
    uint256 _governanceFee,
    uint256 _fee,
    uint256 _burnFee,
    uint256 _keeperReward
  ) public {
    require(governance == msg.sender, "!governance");
    governanceFee = _governanceFee;
    fee = _fee;
    burnFee = _burnFee;
    keeperReward = _keeperReward;
  }

  function initialize(address _governance, address _strategist) public initializer {
    fee = uint256(25e14);
    burnFee = uint256(4e15);
    governanceFee = uint256(5e17);
    governance = _governance;
    strategist = _strategist;
    keeperReward = uint256(1 ether).div(1000);
    //IERC20(renbtc).safeApprove(btcGateway, ~uint256(0) >> 2);
    IERC20(renbtc).safeApprove(renCrv, ~uint256(0) >> 2);
    IERC20(wbtc).safeApprove(renCrv, ~uint256(0) >> 2);
    IERC20(wbtc).safeApprove(tricrypto, ~uint256(0) >> 2);
    IERC20(renCrvLp).safeApprove(bCrvRen, ~uint256(0) >> 2);
    IERC20(wbtc).safeApprove(routerv3, ~uint256(0) >> 2);
    IERC20(usdc).safeApprove(routerv3, ~uint256(0) >> 2);
    //IERC20(bCrvRen).safeApprove(settPeak, ~uint256(0) >> 2);
    PERMIT_DOMAIN_SEPARATOR_WBTC = keccak256(
      abi.encode(
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
        keccak256("WBTC"),
        keccak256("1"),
        getChainId(),
        wbtc
      )
    );
    PERMIT_DOMAIN_SEPARATOR_IBBTC = keccak256(
      abi.encode(
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
        keccak256("ibBTC"),
        keccak256("1"),
        getChainId(),
        ibbtc
      )
    );
  }

  function applyRatio(uint256 v, uint256 n) internal pure returns (uint256 result) {
    result = v.mul(n).div(uint256(1 ether));
  }

  function toWBTC(uint256 amount) internal returns (uint256 amountOut) {
    uint256 amountStart = IERC20(wbtc).balanceOf(address(this));
    IRenCrvArbitrum(renCrv).exchange(1, 0, amount, 1, address(this));
    amountOut = IERC20(wbtc).balanceOf(address(this)).sub(amountStart);
  }

  function toIBBTC(uint256 amountIn) internal returns (uint256 amountOut) {
    uint256[2] memory amounts;
    amounts[0] = amountIn;
    (bool success, ) = renCrv.call(abi.encodeWithSelector(ICurveFi.add_liquidity.selector, amounts, 0));
    require(success, "!curve");
    ISett(bCrvRen).deposit(IERC20(renCrvLp).balanceOf(address(this)));
    amountOut = IBadgerSettPeak(settPeak).mint(0, IERC20(bCrvRen).balanceOf(address(this)), new bytes32[](0));
  }

  function toUSDC(
    uint256 minOut,
    uint256 amountIn,
    address out
  ) internal returns (uint256 amountOut) {
    uint256 wbtcAmountIn = toWBTC(amountIn);
    bytes memory path = abi.encodePacked(wbtc, wethWbtcFee, weth, usdcWethFee, usdc);
    ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({
      recipient: out,
      deadline: block.timestamp + 1,
      amountIn: wbtcAmountIn,
      amountOutMinimum: minOut,
      path: path
    });
    amountOut = ISwapRouter(routerv3).exactInput(params);
  }

  function quote() internal {
    bytes memory path = abi.encodePacked(wbtc, uint24(500), weth);
    uint256 wbtcForEthPrice = IQuoter(quoter).quoteExactInput(path, 1 ether);
    renbtcForOneETHPrice = ICurveInt128(renCrv).get_dy(1, 0, wbtcForEthPrice);
  }

  function renBTCtoETH(
    uint256 minOut,
    uint256 amountIn,
    address out
  ) internal returns (uint256 amountOut) {
    uint256 wbtcAmountOut = toWBTC(amountIn);
    ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
      tokenIn: wbtc,
      tokenOut: weth,
      fee: wethWbtcFee,
      recipient: address(this),
      deadline: block.timestamp + 1,
      amountIn: wbtcAmountOut,
      amountOutMinimum: minOut,
      sqrtPriceLimitX96: 0
    });
    amountOut = ISwapRouter(routerv3).exactInputSingle(params);
    address payable to = address(uint160(out));
    IWETH9(weth).withdraw(amountOut);
    to.transfer(amountOut);
  }

  function fromIBBTC(uint256 amountIn) internal returns (uint256 amountOut) {
    uint256 amountStart = IERC20(renbtc).balanceOf(address(this));
    IBadgerSettPeak(settPeak).redeem(0, amountIn);
    ISett(bCrvRen).withdraw(IERC20(bCrvRen).balanceOf(address(this)));
    (bool success, ) = renCrv.call(
      abi.encodeWithSelector(
        ICurveFi.remove_liquidity_one_coin.selector,
        IERC20(renCrvLp).balanceOf(address(this)),
        0,
        0
      )
    );
    require(success, "!curve");
    amountOut = IERC20(renbtc).balanceOf(address(this)).sub(amountStart);
  }

  function fromUSDC(uint256 minOut, uint256 amountIn) internal returns (uint256 amountOut) {
    bytes memory path = abi.encodePacked(usdc, usdcWethFee, weth, wethWbtcFee, wbtc);
    ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({
      recipient: address(this),
      deadline: block.timestamp + 1,
      amountIn: amountIn,
      amountOutMinimum: minOut,
      path: path
    });
    amountOut = ISwapRouter(routerv3).exactInput(params);
    amountOut = toRenBTC(amountOut);
  }

  function toRenBTC(uint256 amountIn) internal returns (uint256 amountOut) {
    uint256 balanceStart = IERC20(renbtc).balanceOf(address(this));
    IRenCrvArbitrum(renCrv).exchange(0, 1, amountIn, 1, address(this));
    amountOut = IERC20(renbtc).balanceOf(address(this)).sub(balanceStart);
  }

  function fromETHToRenBTC(uint256 minOut, uint256 amountIn) internal returns (uint256 amountOut) {
    ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
      tokenIn: weth,
      tokenOut: wbtc,
      fee: wethWbtcFee,
      recipient: address(this),
      deadline: block.timestamp + 1,
      amountIn: amountIn,
      amountOutMinimum: minOut,
      sqrtPriceLimitX96: 0
    });
    amountOut = ISwapRouter(routerv3).exactInputSingle{ value: amountIn }(params);
    return toRenBTC(amountOut);
  }

  function toETH() internal returns (uint256 amountOut) {
    uint256 wbtcStart = IERC20(wbtc).balanceOf(address(this));

    uint256 amountStart = address(this).balance;
    (bool success, ) = tricrypto.call(
      abi.encodeWithSelector(ICurveETHUInt256.exchange.selector, 1, 2, wbtcStart, 0, true)
    );
    amountOut = address(this).balance.sub(amountStart);
  }

  receive() external payable {
    // no-op
  }

  function earn() public {
    quote();
    toWBTC(IERC20(renbtc).balanceOf(address(this)));
    toETH();
    uint256 balance = address(this).balance;
    if (balance > ETH_RESERVE) {
      uint256 output = balance - ETH_RESERVE;
      uint256 toGovernance = applyRatio(output, governanceFee);
      bool success;
      address payable governancePayable = address(uint160(governance));
      (success, ) = governancePayable.call{ value: toGovernance, gas: gasleft() }("");
      require(success, "error sending to governance");
      address payable strategistPayable = address(uint160(strategist));
      (success, ) = strategistPayable.call{ value: output.sub(toGovernance), gas: gasleft() }("");
      require(success, "error sending to strategist");
    }
  }

  function computeRenBTCGasFee(uint256 gasCost, uint256 gasPrice) internal view returns (uint256 result) {
    result = gasCost.mul(tx.gasprice).mul(renbtcForOneETHPrice).div(uint256(1 ether));
  }

  function deductMintFee(uint256 amountIn, uint256 multiplier) internal view returns (uint256 amount) {
    amount = amountIn.sub(applyFee(amountIn, fee, multiplier));
  }

  function deductIBBTCMintFee(uint256 amountIn, uint256 multiplier) internal view returns (uint256 amount) {
    amount = amountIn.sub(applyIBBTCFee(amountIn, fee, multiplier));
  }

  function deductBurnFee(uint256 amountIn, uint256 multiplier) internal view returns (uint256 amount) {
    amount = amountIn.sub(applyFee(amountIn, burnFee, multiplier));
  }

  function deductIBBTCBurnFee(uint256 amountIn, uint256 multiplier) internal view returns (uint256 amount) {
    amount = amountIn.sub(applyIBBTCFee(amountIn, burnFee, multiplier));
  }

  function applyFee(
    uint256 amountIn,
    uint256 _fee,
    uint256 multiplier
  ) internal view returns (uint256 amount) {
    amount = computeRenBTCGasFee(GAS_COST.add(keeperReward.div(tx.gasprice)), tx.gasprice).add(
      applyRatio(amountIn, _fee)
    );
  }

  function applyIBBTCFee(
    uint256 amountIn,
    uint256 _fee,
    uint256 multiplier
  ) internal view returns (uint256 amount) {
    amount = computeRenBTCGasFee(IBBTC_GAS_COST.add(keeperReward.div(tx.gasprice)), tx.gasprice).add(
      applyRatio(amountIn, _fee)
    );
  }

  struct LoanParams {
    address to;
    address asset;
    uint256 nonce;
    uint256 amount;
    address module;
    address underwriter;
    bytes data;
    uint256 minOut;
    uint256 _mintAmount;
    uint256 gasDiff;
  }

  function toTypedDataHash(LoanParams memory params) internal view returns (bytes32 result) {
    bytes32 digest = _hashTypedDataV4(
      keccak256(
        abi.encode(
          keccak256(
            "TransferRequest(address asset,uint256 amount,address underwriter,address module,uint256 nonce,bytes data)"
          ),
          params.asset,
          params.amount,
          params.underwriter,
          params.module,
          params.nonce,
          keccak256(params.data)
        )
      )
    );
    return digest;
  }

  function repay(
    address underwriter,
    address to,
    address asset,
    uint256 amount,
    uint256 actualAmount,
    uint256 nonce,
    address module,
    bytes32 nHash,
    bytes memory data,
    bytes memory signature
  ) public returns (uint256 amountOut) {
    require(msg.data.length <= 516, "too much calldata");
    uint256 _gasBefore = gasleft();
    LoanParams memory params;
    {
      require(
        module == wbtc || module == usdc || module == ibbtc || module == renbtc || module == address(0x0),
        "!approved-module"
      );
      params = LoanParams({
        to: to,
        asset: asset,
        amount: amount,
        nonce: nonce,
        module: module,
        underwriter: underwriter,
        data: data,
        minOut: 1,
        _mintAmount: 0,
        gasDiff: computeCalldataGasDiff()
      });
      if (data.length > 0) (params.minOut) = abi.decode(data, (uint256));
    }
    bytes32 digest = toTypedDataHash(params);

    params._mintAmount = IGateway(btcGateway).mint(
      keccak256(abi.encode(params.to, params.nonce, params.module, params.data)),
      actualAmount,
      nHash,
      signature
    );
    {
      amountOut = module == wbtc ? toWBTC(deductMintFee(params._mintAmount, 1)) : module == address(0x0)
        ? renBTCtoETH(params.minOut, deductMintFee(params._mintAmount, 1), to)
        : module == usdc
        ? toUSDC(params.minOut, deductMintFee(params._mintAmount, 1), to)
        : module == ibbtc
        ? toIBBTC(deductIBBTCMintFee(params._mintAmount, 3))
        : deductMintFee(params._mintAmount, 1);
    }
    {
      if (module != usdc && module != address(0x0)) IERC20(module).safeTransfer(to, amountOut);
    }
    {
      tx.origin.transfer(
        Math.min(
          _gasBefore.sub(gasleft()).add(REPAY_GAS_DIFF).add(params.gasDiff).mul(tx.gasprice).add(keeperReward),
          address(this).balance
        )
      );
    }
  }

  function computeBurnNonce(BurnLocals memory params) internal view returns (uint256 result) {
    result = uint256(
      keccak256(
        abi.encodePacked(params.asset, params.amount, params.deadline, params.nonce, params.data, params.destination)
      )
    );
    while (result < block.timestamp) {
      // negligible probability of this
      result = uint256(keccak256(abi.encodePacked(result)));
    }
  }

  function computeERC20PermitDigest(bytes32 domainSeparator, BurnLocals memory params)
    internal
    view
    returns (bytes32 result)
  {
    result = keccak256(
      abi.encodePacked(
        "\x19\x01",
        domainSeparator,
        keccak256(abi.encode(PERMIT_TYPEHASH, params.to, address(this), params.nonce, computeBurnNonce(params), true))
      )
    );
  }

  struct BurnLocals {
    address to;
    address asset;
    uint256 amount;
    uint256 deadline;
    uint256 nonce;
    bytes data;
    uint256 minOut;
    uint256 burnNonce;
    uint256 gasBefore;
    uint256 gasDiff;
    uint8 v;
    bytes32 r;
    bytes32 s;
    bytes destination;
    bytes signature;
  }

  function burn(
    address to,
    address asset,
    uint256 amount,
    uint256 deadline,
    bytes memory data,
    bytes memory destination,
    bytes memory signature
  ) public returns (uint256 amountToBurn) {
    require(msg.data.length <= 580, "too much calldata");
    BurnLocals memory params = BurnLocals({
      to: to,
      asset: asset,
      amount: amount,
      deadline: deadline,
      data: data,
      nonce: 0,
      burnNonce: 0,
      v: uint8(0),
      r: bytes32(0),
      s: bytes32(0),
      destination: destination,
      signature: signature,
      gasBefore: gasleft(),
      minOut: 1,
      gasDiff: 0
    });
    {
      params.gasDiff = computeCalldataGasDiff();
      if (params.data.length > 0) (params.minOut) = abi.decode(params.data, (uint256));
    }
    require(block.timestamp < params.deadline, "!deadline");

    if (params.asset == wbtc) {
      params.nonce = nonces[to];
      nonces[params.to]++;
      require(
        params.to == ECDSA.recover(computeERC20PermitDigest(PERMIT_DOMAIN_SEPARATOR_WBTC, params), params.signature),
        "!signature"
      ); //  wbtc does not implement ERC20Permit
      {
        IERC20(params.asset).transferFrom(params.to, address(this), params.amount);
        amountToBurn = toRenBTC(deductBurnFee(params.amount, 1));
      }
    } else if (asset == ibbtc) {
      params.nonce = nonces[to];
      nonces[to]++;
      require(
        params.to == ECDSA.recover(computeERC20PermitDigest(PERMIT_DOMAIN_SEPARATOR_IBBTC, params), params.signature),
        "!signature"
      ); //  wbtc ibbtc do not implement ERC20Permit
      {
        IERC20(params.asset).transferFrom(params.to, address(this), params.amount);
        amountToBurn = deductIBBTCBurnFee(fromIBBTC(params.amount), 3);
      }
    } else if (params.asset == renbtc) {
      {
        params.nonce = IERC2612Permit(params.asset).nonces(params.to);
        params.burnNonce = computeBurnNonce(params);
      }
      {
        (params.v, params.r, params.s) = SplitSignatureLib.splitSignature(params.signature);
        IERC2612Permit(params.asset).permit(
          params.to,
          address(this),
          params.nonce,
          params.burnNonce,
          true,
          params.v,
          params.r,
          params.s
        );
      }
      {
        IERC20(params.asset).transferFrom(params.to, address(this), params.amount);
      }
      amountToBurn = deductBurnFee(params.amount, 1);
    } else if (params.asset == usdc) {
      {
        params.nonce = IERC2612Permit(params.asset).nonces(params.to);
        params.burnNonce = computeBurnNonce(params);
      }
      {
        (params.v, params.r, params.s) = SplitSignatureLib.splitSignature(params.signature);
        IERC2612Permit(params.asset).permit(
          params.to,
          address(this),
          params.amount,
          params.burnNonce,
          params.v,
          params.r,
          params.s
        );
      }
      {
        IERC20(params.asset).transferFrom(params.to, address(this), params.amount);
      }
      amountToBurn = deductBurnFee(fromUSDC(params.minOut, params.amount), 1);
    } else revert("!supported-asset");
    {
      IGateway(btcGateway).burn(params.destination, amountToBurn);
    }
    {
      tx.origin.transfer(
        Math.min(
          params.gasBefore.sub(gasleft()).add(BURN_GAS_DIFF).add(params.gasDiff).mul(tx.gasprice).add(keeperReward),
          address(this).balance
        )
      );
    }
  }

  function burnETH(uint256 minOut, bytes memory destination) public payable returns (uint256 amountToBurn) {
    amountToBurn = fromETHToRenBTC(minOut, msg.value.sub(applyRatio(msg.value, burnFee)));
    IGateway(btcGateway).burn(destination, amountToBurn);
  }

  function burnApproved(
    address from,
    address asset,
    uint256 amount,
    uint256 minOut,
    bytes memory destination
  ) public payable returns (uint256 amountToBurn) {
    require(asset == wbtc || asset == usdc || asset == renbtc || asset == address(0x0), "!approved-module");
    if (asset != address(0x0)) IERC20(asset).transferFrom(msg.sender, address(this), amount);
    amountToBurn = asset == wbtc ? toRenBTC(amount.sub(applyRatio(amount, burnFee))) : asset == usdc
      ? fromUSDC(minOut, amount.sub(applyRatio(amount, burnFee)))
      : asset == renbtc
      ? amount
      : fromETHToRenBTC(minOut, msg.value.sub(applyRatio(msg.value, burnFee)));
    IGateway(btcGateway).burn(destination, amountToBurn);
  }

  function fallbackMint(
    address underwriter,
    address to,
    address asset,
    uint256 amount,
    uint256 actualAmount,
    uint256 nonce,
    address module,
    bytes32 nHash,
    bytes memory data,
    bytes memory signature
  ) public {
    LoanParams memory params = LoanParams({
      to: to,
      asset: asset,
      amount: amount,
      nonce: nonce,
      module: module,
      underwriter: underwriter,
      data: data,
      minOut: 1,
      _mintAmount: 0,
      gasDiff: 0
    });
    bytes32 digest = toTypedDataHash(params);
    uint256 _actualAmount = IGateway(btcGateway).mint(
      keccak256(abi.encode(params.to, params.nonce, params.module, params.data)),
      actualAmount,
      nHash,
      signature
    );
    IERC20(asset).safeTransfer(to, _actualAmount);
  }
}

File 113 of 140 : PolygonConvert.sol
pragma solidity >=0.6.0 <0.8.0;
import { PolygonConvertLib } from "./PolygonConvertLib.sol";
import { IUniswapV2Router02 } from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import { SafeMath } from "oz410/math/SafeMath.sol";
import { IERC20 } from "oz410/token/ERC20/IERC20.sol";
import { SafeERC20 } from "oz410/token/ERC20/SafeERC20.sol";
import { IZeroModule } from "../interfaces/IZeroModule.sol";
import { IController } from "../interfaces/IController.sol";
import { IConverter } from "../interfaces/IConverter.sol";
import { ICurveUInt256 } from "../interfaces/CurvePools/ICurveUInt256.sol";
import { IController } from "../interfaces/IController.sol";
import { ICurveUnderlyingUInt256 } from "../interfaces/CurvePools/ICurveUnderlyingUInt256.sol";
import { ICurveInt128 } from "../interfaces/CurvePools/ICurveInt128.sol";
import { IRenCrvPolygon } from "../interfaces/CurvePools/IRenCrvPolygon.sol";

contract PolygonConvert is IZeroModule {
  using SafeERC20 for *;
  using SafeMath for *;
  mapping(uint256 => PolygonConvertLib.ConvertRecord) public outstanding;
  address public immutable controller;
  address public immutable governance;
  uint256 public blockTimeout;
  address public constant router = 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506;
  address public constant wMatic = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270;
  address public constant weth = 0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619;
  address public constant wbtc = 0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6;
  address public constant override want = 0xDBf31dF14B66535aF65AaC99C32e9eA844e14501;
  address public constant renCrvPolygon = 0xC2d95EEF97Ec6C17551d45e77B590dc1F9117C67;
  address public constant tricryptoPolygon = 0x92215849c439E1f8612b6646060B4E3E5ef822cC;

  modifier onlyController() {
    require(msg.sender == controller, "!controller");
    _;
  }

  constructor(address _controller) {
    controller = _controller;
    governance = IController(_controller).governance();
    IERC20(want).safeApprove(router, ~uint256(0) >> 2);
  }

  function setBlockTimeout(uint256 _ct) public {
    require(msg.sender == governance, "!governance");
    blockTimeout = _ct;
  }

  function isActive(PolygonConvertLib.ConvertRecord storage record) internal view returns (bool) {
    return record.qty != 0 || record.qtyETH != 0;
  }

  function defaultLoan(uint256 _nonce) public {
    require(block.number >= outstanding[_nonce].when + blockTimeout);
    require(isActive(outstanding[_nonce]), "!outstanding");
    uint256 _amountSwappedBack = swapTokensBack(outstanding[_nonce]);
    IERC20(want).safeTransfer(controller, _amountSwappedBack);
    delete outstanding[_nonce];
  }

  function receiveLoan(
    address _to,
    address _asset,
    uint256 _actual,
    uint256 _nonce,
    bytes memory _data
  ) public override onlyController {
    uint256 ratio = abi.decode(_data, (uint256));
    (uint256 amountSwappedETH, uint256 amountSwappedBTC) = swapTokens(_actual, ratio);
    outstanding[_nonce] = PolygonConvertLib.ConvertRecord({
      qty: amountSwappedBTC,
      when: uint64(block.timestamp),
      qtyETH: amountSwappedETH
    });
  }

  function swapTokens(uint256 _amountIn, uint256 _ratio)
    internal
    returns (uint256 amountSwappedETH, uint256 amountSwappedBTC)
  {
    uint256 amountToETH = _ratio.mul(_amountIn).div(uint256(1 ether));
    if (amountToETH != 0) {
      address[] memory path = new address[](2);
      path[0] = want;
      path[1] = wMatic;
      uint256[] memory toMaticResult = IUniswapV2Router02(router).swapExactTokensForETH(
        amountToETH,
        1,
        path,
        address(this),
        block.timestamp + 1
      );
      amountSwappedETH = toMaticResult[1];
      amountSwappedBTC = _amountIn.sub(amountToETH);
    } else {
      amountSwappedBTC = _amountIn;
    }
  }

  receive() external payable {
    //
  }

  function swapTokensBack(PolygonConvertLib.ConvertRecord storage record) internal returns (uint256 amountReturned) {
    uint256 _amountStart = IERC20(wbtc).balanceOf(address(this));
    (bool success, ) = tricryptoPolygon.call{ value: record.qtyETH }(
      abi.encodeWithSelector(ICurveUInt256.exchange.selector, 2, 1, record.qtyETH, 0)
    );
    require(success, "!exchange");
    uint256 wbtcOut = IERC20(wbtc).balanceOf(address(this));
    amountReturned = IRenCrvPolygon(renCrvPolygon).exchange(0, 1, wbtcOut, 0).add(record.qty);
  }

  function repayLoan(
    address _to,
    address _asset,
    uint256 _actualAmount,
    uint256 _nonce,
    bytes memory _data
  ) public override onlyController {
    require(outstanding[_nonce].qty != 0 || outstanding[_nonce].qtyETH != 0, "!outstanding");
    IERC20(want).safeTransfer(_to, outstanding[_nonce].qty);
    address payable to = address(uint160(_to));
    to.transfer(outstanding[_nonce].qtyETH);
    delete outstanding[_nonce];
  }

  function computeReserveRequirement(uint256 _in) external view override returns (uint256) {
    return _in.mul(uint256(1e17)).div(uint256(1 ether));
  }
}

File 114 of 140 : PolygonConvertLib.sol
// SPDX-License-Identifier: MIT

library PolygonConvertLib {
  struct ConvertRecord {
    uint64 when;
    uint256 qtyETH;
    uint256 qty;
  }
}

File 115 of 140 : ICurveUnderlyingUInt256.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.8.0;

interface ICurveUnderlyingUInt256 {
  function get_dy_underlying(
    uint256,
    uint256,
    uint256
  ) external view returns (uint256);

  function exchange_underlying(
    uint256,
    uint256,
    uint256,
    uint256
  ) external returns (uint256);

  function underlying_coins(uint256) external view returns (address);
}

File 116 of 140 : IRenCrvPolygon.sol
interface IRenCrvPolygon {
  function exchange(
    int128 i,
    int128 j,
    uint256 dx,
    uint256 min_dy
  ) external returns (uint256);
}

File 117 of 140 : CurveLib.sol
pragma solidity >=0.6.0 <0.8.0;

import { ICurveInt128 } from "../interfaces/CurvePools/ICurveInt128.sol";
import { ICurveUInt128 } from "../interfaces/CurvePools/ICurveUInt128.sol";

import { ICurveInt256 } from "../interfaces/CurvePools/ICurveInt256.sol";

import { ICurveUInt256 } from "../interfaces/CurvePools/ICurveUInt256.sol";
import { ICurveETHUInt256 } from "../interfaces/CurvePools/ICurveETHUInt256.sol";
import { ICurveUnderlyingUInt128 } from "../interfaces/CurvePools/ICurveUnderlyingUInt128.sol";
import { ICurveUnderlyingUInt256 } from "../interfaces/CurvePools/ICurveUnderlyingUInt256.sol";
import { ICurveUnderlyingInt128 } from "../interfaces/CurvePools/ICurveUnderlyingInt128.sol";
import { ICurveUnderlyingInt256 } from "../interfaces/CurvePools/ICurveUnderlyingInt256.sol";
import { RevertCaptureLib } from "./RevertCaptureLib.sol";

library CurveLib {
  address constant weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
  struct ICurve {
    address pool;
    bool underlying;
    bytes4 coinsSelector;
    bytes4 exchangeSelector;
    bytes4 getDySelector;
    bytes4 coinsUnderlyingSelector;
  }

  function hasWETH(address pool, bytes4 coinsSelector) internal returns (bool) {
    for (uint256 i = 0; ; i++) {
      (bool success, bytes memory result) = pool.staticcall{ gas: 2e5 }(abi.encodePacked(coinsSelector, i));
      if (!success || result.length == 0) return false;
      address coin = abi.decode(result, (address));
      if (coin == weth) return true;
    }
  }

  function coins(ICurve memory curve, uint256 i) internal view returns (address result) {
    (bool success, bytes memory returnData) = curve.pool.staticcall(abi.encodeWithSelector(curve.coinsSelector, i));
    require(success, "!coins");
    (result) = abi.decode(returnData, (address));
  }

  function underlying_coins(ICurve memory curve, uint256 i) internal view returns (address result) {
    (bool success, bytes memory returnData) = curve.pool.staticcall(
      abi.encodeWithSelector(curve.coinsUnderlyingSelector, i)
    );
    require(success, "!underlying_coins");
    (result) = abi.decode(returnData, (address));
  }

  function get_dy(
    ICurve memory curve,
    uint256 i,
    uint256 j,
    uint256 amount
  ) internal view returns (uint256 result) {
    (bool success, bytes memory returnData) = curve.pool.staticcall(
      abi.encodeWithSelector(curve.getDySelector, i, j, amount)
    );
    require(success, "!get_dy");
    (result) = abi.decode(returnData, (uint256));
  }

  function exchange(
    ICurve memory curve,
    uint256 i,
    uint256 j,
    uint256 dx,
    uint256 min_dy
  ) internal {
    (bool success, bytes memory returnData) = curve.pool.call{ gas: gasleft() }(
      abi.encodeWithSelector(curve.exchangeSelector, i, j, dx, min_dy)
    );
    if (!success) revert(RevertCaptureLib.decodeError(returnData));
  }

  function toDynamic(bytes4[4] memory ary) internal pure returns (bytes4[] memory result) {
    result = new bytes4[](ary.length);
    for (uint256 i = 0; i < ary.length; i++) {
      result[i] = ary[i];
    }
  }

  function toDynamic(bytes4[5] memory ary) internal pure returns (bytes4[] memory result) {
    result = new bytes4[](ary.length);
    for (uint256 i = 0; i < ary.length; i++) {
      result[i] = ary[i];
    }
  }

  function testSignatures(
    address target,
    bytes4[] memory signatures,
    bytes memory callData
  ) internal returns (bytes4 result) {
    for (uint256 i = 0; i < signatures.length; i++) {
      (, bytes memory returnData) = target.staticcall(abi.encodePacked(signatures[i], callData));
      if (returnData.length != 0) return signatures[i];
    }
    return bytes4(0x0);
  }

  function testExchangeSignatures(
    address target,
    bytes4[] memory signatures,
    bytes memory callData
  ) internal returns (bytes4 result) {
    for (uint256 i = 0; i < signatures.length; i++) {
      uint256 gasStart = gasleft();
      (bool success, ) = target.call{ gas: 2e5 }(abi.encodePacked(signatures[i], callData));
      uint256 gasUsed = gasStart - gasleft();
      if (gasUsed > 10000) return signatures[i];
    }
    return bytes4(0x0);
  }

  function toBytes(bytes4 sel) internal pure returns (bytes memory result) {
    result = new bytes(4);
    bytes32 selWord = bytes32(sel);
    assembly {
      mstore(add(0x20, result), selWord)
    }
  }

  function duckPool(address pool, bool underlying) internal returns (ICurve memory result) {
    result.pool = pool;
    result.underlying = underlying;
    result.coinsSelector = result.underlying
      ? testSignatures(
        pool,
        toDynamic(
          [
            ICurveUnderlyingInt128.underlying_coins.selector,
            ICurveUnderlyingInt256.underlying_coins.selector,
            ICurveUnderlyingUInt128.underlying_coins.selector,
            ICurveUnderlyingUInt256.underlying_coins.selector
          ]
        ),
        abi.encode(0)
      )
      : testSignatures(
        pool,
        toDynamic(
          [
            ICurveInt128.coins.selector,
            ICurveInt256.coins.selector,
            ICurveUInt128.coins.selector,
            ICurveUInt256.coins.selector
          ]
        ),
        abi.encode(0)
      );
    result.exchangeSelector = result.underlying
      ? testExchangeSignatures(
        pool,
        toDynamic(
          [
            ICurveUnderlyingUInt256.exchange_underlying.selector,
            ICurveUnderlyingInt128.exchange_underlying.selector,
            ICurveUnderlyingInt256.exchange_underlying.selector,
            ICurveUnderlyingUInt128.exchange_underlying.selector
          ]
        ),
        abi.encode(0, 0, 1000000000, type(uint256).max / 0x10, false)
      )
      : testExchangeSignatures(
        pool,
        toDynamic(
          [
            ICurveUInt256.exchange.selector,
            ICurveInt128.exchange.selector,
            ICurveInt256.exchange.selector,
            ICurveUInt128.exchange.selector,
            ICurveETHUInt256.exchange.selector
          ]
        ),
        abi.encode(0, 0, 1000000000, type(uint256).max / 0x10, false)
      );
    if (result.exchangeSelector == bytes4(0x0)) result.exchangeSelector = ICurveUInt256.exchange.selector; //hasWETH(pool, result.coinsSelector) ? ICurveETHUInt256.exchange.selector : ICurveUInt256.exchange.selector;
    result.getDySelector = testSignatures(
      pool,
      toDynamic(
        [
          ICurveInt128.get_dy.selector,
          ICurveInt256.get_dy.selector,
          ICurveUInt128.get_dy.selector,
          ICurveUInt256.get_dy.selector
        ]
      ),
      abi.encode(0, 1, 1000000000)
    );
  }

  function fromSelectors(
    address pool,
    bool underlying,
    bytes4 coinsSelector,
    bytes4 coinsUnderlyingSelector,
    bytes4 exchangeSelector,
    bytes4 getDySelector
  ) internal pure returns (ICurve memory result) {
    result.pool = pool;
    result.coinsSelector = coinsSelector;
    result.coinsUnderlyingSelector = coinsUnderlyingSelector;
    result.exchangeSelector = exchangeSelector;
    result.getDySelector = getDySelector;
  }
}

File 118 of 140 : ICurveUInt128.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.8.0;

interface ICurveUInt128 {
  function get_dy(
    uint128,
    uint128,
    uint256
  ) external view returns (uint256);

  function exchange(
    uint128,
    uint128,
    uint256,
    uint256
  ) external returns (uint256);

  function coins(uint128) external view returns (address);
}

File 119 of 140 : ICurveInt256.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.8.0;

interface ICurveInt256 {
  function get_dy(
    int256,
    int256,
    uint256
  ) external view returns (uint256);

  function exchange(
    int256,
    int256,
    uint256,
    uint256
  ) external returns (uint256);

  function coins(int256) external view returns (address);
}

File 120 of 140 : ICurveUnderlyingUInt128.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.8.0;

interface ICurveUnderlyingUInt128 {
  function get_dy_underlying(
    uint128,
    uint128,
    uint256
  ) external view returns (uint256);

  function exchange_underlying(
    uint128,
    uint128,
    uint256,
    uint256
  ) external returns (uint256);

  function underlying_coins(uint128) external view returns (address);
}

File 121 of 140 : ICurveUnderlyingInt128.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.8.0;

interface ICurveUnderlyingInt128 {
  function get_dy_underlying(
    int128,
    int128,
    uint256
  ) external view returns (uint256);

  function exchange_underlying(
    int128,
    int128,
    uint256,
    uint256
  ) external returns (uint256);

  function underlying_coins(int128) external view returns (address);
}

File 122 of 140 : ICurveUnderlyingInt256.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.8.0;

interface ICurveUnderlyingInt256 {
  function get_dy_underlying(
    int256,
    int256,
    uint256
  ) external view returns (uint256);

  function exchange_underlying(
    int256,
    int256,
    uint256,
    uint256
  ) external returns (uint256);

  function underlying_coins(int256) external view returns (address);
}

File 123 of 140 : RevertCaptureLib.sol
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

import { SliceLib } from "./SliceLib.sol";

library RevertCaptureLib {
  using SliceLib for *;
  uint32 constant REVERT_WITH_REASON_MAGIC = 0x08c379a0; // keccak256("Error(string)")

  function decodeString(bytes memory input) internal pure returns (string memory retval) {
    (retval) = abi.decode(input, (string));
  }

  function decodeError(bytes memory buffer) internal pure returns (string memory) {
    if (buffer.length == 0) return "captured empty revert buffer";
    if (uint32(uint256(bytes32(buffer.toSlice(0, 4).asWord()))) != REVERT_WITH_REASON_MAGIC)
      return "captured a revert error, but it doesn't conform to the standard";
    bytes memory revertMessageEncoded = buffer.toSlice(4).copy();
    if (revertMessageEncoded.length == 0) return "captured empty revert message";
    string memory revertMessage = decodeString(revertMessageEncoded);
    return revertMessage;
  }
}

File 124 of 140 : SliceLib.sol
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

import { MemcpyLib } from "./MemcpyLib.sol";

library SliceLib {
  struct Slice {
    uint256 data;
    uint256 length;
    uint256 offset;
  }

  function toPtr(bytes memory input, uint256 offset) internal pure returns (uint256 data) {
    assembly {
      data := add(input, add(offset, 0x20))
    }
  }

  function toSlice(
    bytes memory input,
    uint256 offset,
    uint256 length
  ) internal pure returns (Slice memory retval) {
    retval.data = toPtr(input, offset);
    retval.length = length;
    retval.offset = offset;
  }

  function toSlice(bytes memory input) internal pure returns (Slice memory) {
    return toSlice(input, 0);
  }

  function toSlice(bytes memory input, uint256 offset) internal pure returns (Slice memory) {
    if (input.length < offset) offset = input.length;
    return toSlice(input, offset, input.length - offset);
  }

  function toSlice(
    Slice memory input,
    uint256 offset,
    uint256 length
  ) internal pure returns (Slice memory) {
    return Slice({ data: input.data + offset, offset: input.offset + offset, length: length });
  }

  function toSlice(Slice memory input, uint256 offset) internal pure returns (Slice memory) {
    return toSlice(input, offset, input.length - offset);
  }

  function toSlice(Slice memory input) internal pure returns (Slice memory) {
    return toSlice(input, 0);
  }

  function maskLastByteOfWordAt(uint256 data) internal pure returns (uint8 lastByte) {
    assembly {
      lastByte := and(mload(data), 0xff)
    }
  }

  function get(Slice memory slice, uint256 index) internal pure returns (bytes1 result) {
    return bytes1(maskLastByteOfWordAt(slice.data - 0x1f + index));
  }

  function setByteAt(uint256 ptr, uint8 value) internal pure {
    assembly {
      mstore8(ptr, value)
    }
  }

  function set(
    Slice memory slice,
    uint256 index,
    uint8 value
  ) internal pure {
    setByteAt(slice.data + index, value);
  }

  function wordAt(uint256 ptr, uint256 length) internal pure returns (bytes32 word) {
    assembly {
      let mask := sub(shl(mul(length, 0x8), 0x1), 0x1)
      word := and(mload(sub(ptr, sub(0x20, length))), mask)
    }
  }

  function asWord(Slice memory slice) internal pure returns (bytes32 word) {
    uint256 data = slice.data;
    uint256 length = slice.length;
    return wordAt(data, length);
  }

  function toDataStart(bytes memory input) internal pure returns (bytes32 start) {
    assembly {
      start := add(input, 0x20)
    }
  }

  function copy(Slice memory slice) internal pure returns (bytes memory retval) {
    uint256 length = slice.length;
    retval = new bytes(length);
    bytes32 src = bytes32(slice.data);
    bytes32 dest = toDataStart(retval);
    MemcpyLib.memcpy(dest, src, length);
  }

  function keccakAt(uint256 data, uint256 length) internal pure returns (bytes32 result) {
    assembly {
      result := keccak256(data, length)
    }
  }

  function toKeccak(Slice memory slice) internal pure returns (bytes32 result) {
    return keccakAt(slice.data, slice.length);
  }
}

File 125 of 140 : MemcpyLib.sol
pragma experimental ABIEncoderV2;
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

library MemcpyLib {
  function memcpy(
    bytes32 dest,
    bytes32 src,
    uint256 len
  ) internal pure {
    assembly {
      for {

      } iszero(lt(len, 0x20)) {
        len := sub(len, 0x20)
      } {
        mstore(dest, mload(src))
        dest := add(dest, 0x20)
        src := add(src, 0x20)
      }
      let mask := sub(shl(mul(sub(32, len), 8), 1), 1)
      mstore(dest, or(and(mload(src), not(mask)), and(mload(dest), mask)))
    }
  }
}

File 126 of 140 : ZeroCurveFactory.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.8.0;
import { ICurvePool } from "../interfaces/ICurvePool.sol";
import { IERC20 } from "oz410/token/ERC20/IERC20.sol";
import { ZeroCurveWrapper } from "./ZeroCurveWrapper.sol";
import { ICurveInt128 } from "../interfaces/CurvePools/ICurveInt128.sol";
import { ICurveInt256 } from "../interfaces/CurvePools/ICurveInt256.sol";
import { ICurveUInt128 } from "../interfaces/CurvePools/ICurveUInt128.sol";
import { ICurveUInt256 } from "../interfaces/CurvePools/ICurveUInt256.sol";
import { ICurveUnderlyingInt128 } from "../interfaces/CurvePools/ICurveUnderlyingInt128.sol";
import { ICurveUnderlyingInt256 } from "../interfaces/CurvePools/ICurveUnderlyingInt256.sol";
import { ICurveUnderlyingUInt128 } from "../interfaces/CurvePools/ICurveUnderlyingUInt128.sol";
import { ICurveUnderlyingUInt256 } from "../interfaces/CurvePools/ICurveUnderlyingUInt256.sol";
import { CurveLib } from "../libraries/CurveLib.sol";

contract ZeroCurveFactory {
  event CreateWrapper(address _wrapper);

  function createWrapper(
    bool _underlying,
    uint256 _tokenInIndex,
    uint256 _tokenOutIndex,
    address _pool
  ) public payable {
    emit CreateWrapper(address(new ZeroCurveWrapper(_tokenInIndex, _tokenOutIndex, _pool, _underlying)));
  }

  fallback() external payable {
    /* no op */
  }
}

File 127 of 140 : ZeroCurveWrapper.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.8.0;

import { IERC20 } from "oz410/token/ERC20/IERC20.sol";
import { SafeERC20 } from "oz410/token/ERC20/SafeERC20.sol";
import { ICurvePool } from "../interfaces/ICurvePool.sol";
import { CurveLib } from "../libraries/CurveLib.sol";
import { SafeMath } from "oz410/math/SafeMath.sol";

contract ZeroCurveWrapper {
  bool public immutable underlying;
  uint256 public immutable tokenInIndex;
  uint256 public immutable tokenOutIndex;
  address public immutable tokenInAddress;
  address public immutable tokenOutAddress;
  address public immutable pool;
  bytes4 public immutable coinsUnderlyingSelector;
  bytes4 public immutable coinsSelector;
  bytes4 public immutable getDySelector;
  bytes4 public immutable exchangeSelector;

  using SafeMath for uint256;
  using SafeERC20 for IERC20;
  using CurveLib for CurveLib.ICurve;

  function getPool() internal view returns (CurveLib.ICurve memory result) {
    result = CurveLib.fromSelectors(
      pool,
      underlying,
      coinsSelector,
      coinsUnderlyingSelector,
      exchangeSelector,
      getDySelector
    );
  }

  constructor(
    uint256 _tokenInIndex,
    uint256 _tokenOutIndex,
    address _pool,
    bool _underlying
  ) {
    underlying = _underlying;
    tokenInIndex = _tokenInIndex;
    tokenOutIndex = _tokenOutIndex;
    pool = _pool;
    CurveLib.ICurve memory curve = CurveLib.duckPool(_pool, _underlying);
    coinsUnderlyingSelector = curve.coinsUnderlyingSelector;
    coinsSelector = curve.coinsSelector;
    exchangeSelector = curve.exchangeSelector;
    getDySelector = curve.getDySelector;
    address _tokenInAddress = tokenInAddress = curve.coins(_tokenInIndex);
    address _tokenOutAddress = tokenOutAddress = curve.coins(_tokenOutIndex);
    IERC20(_tokenInAddress).safeApprove(_pool, type(uint256).max / 2);
  }

  function estimate(uint256 _amount) public returns (uint256 result) {
    result = getPool().get_dy(tokenInIndex, tokenOutIndex, _amount);
  }

  function convert(address _module) external payable returns (uint256 _actualOut) {
    uint256 _balance = IERC20(tokenInAddress).balanceOf(address(this));
    uint256 _startOut = IERC20(tokenOutAddress).balanceOf(address(this));
    getPool().exchange(tokenInIndex, tokenOutIndex, _balance, _balance / 0x10);
    _actualOut = IERC20(tokenOutAddress).balanceOf(address(this)) - _startOut;
    IERC20(tokenOutAddress).safeTransfer(msg.sender, _actualOut);
  }

  receive() external payable {
    /* noop */
  }

  fallback() external payable {
    /* noop */
  }
}

File 128 of 140 : StrategyRenVMAsset.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.5.17 <0.8.0;

import "oz410/token/ERC20/IERC20.sol";
import "oz410/math/SafeMath.sol";
import "oz410/utils/Address.sol";
import "oz410/token/ERC20/SafeERC20.sol";
import { IController } from "../interfaces/IController.sol";

contract StrategyRenVMAsset {
  using SafeERC20 for IERC20;
  using Address for address;
  using SafeMath for uint256;

  address public immutable want;

  address public weth;

  uint256 public performanceFee;
  uint256 public performanceMax;

  uint256 public withdrawalFee;
  uint256 public withdrawalMax;

  address public governance;
  address public controller;
  address public strategist;
  string public getName;

  constructor(
    address _controller,
    address _want,
    string memory _name
  ) {
    governance = msg.sender;
    strategist = msg.sender;
    controller = _controller;
    want = _want;
    getName = _name;
  }

  function setStrategist(address _strategist) external {
    require(msg.sender == governance, "!governance");
    strategist = _strategist;
  }

  function setWithdrawalFee(uint256 _withdrawalFee) external {
    require(msg.sender == governance, "!governance");
    withdrawalFee = _withdrawalFee;
  }

  function setPerformanceFee(uint256 _performanceFee) external {
    require(msg.sender == governance, "!governance");
    performanceFee = _performanceFee;
  }

  function deposit() public {
    uint256 _want = IERC20(want).balanceOf(msg.sender);
    IERC20(want).safeTransferFrom(address(msg.sender), address(this), _want);
  }

  // Controller only function for creating additional rewards from dust
  function withdraw(IERC20 _asset) external returns (uint256 balance) {
    require(msg.sender == controller, "!controller");
    require(want != address(_asset), "want");
    balance = _asset.balanceOf(address(this));
    _asset.safeTransfer(controller, balance);
  }

  function permissionedSend(address _target, uint256 _amount) external {
    require(msg.sender == controller, "!controller");
    IERC20(want).safeTransfer(_target, _amount);
  }

  // Withdraw partial funds, normally used with a vault withdrawal
  function withdraw(uint256 _amount) external {
    require(msg.sender == controller, "!controller");
    uint256 _balance = IERC20(want).balanceOf(address(this));
    if (_balance < _amount) {
      _amount = _withdrawSome(_amount.sub(_balance));
      _amount = _amount.add(_balance);
    }

    uint256 _fee = _amount.mul(withdrawalFee).div(withdrawalMax);

    IERC20(want).safeTransfer(IController(controller).rewards(), _fee);
    address _vault = IController(controller).vaults(address(want));
    require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds

    IERC20(want).safeTransfer(_vault, _amount.sub(_fee));
  }

  // Withdraw all funds, normally used when migrating strategies
  function withdrawAll() external returns (uint256 balance) {
    require(msg.sender == controller, "!controller");
    _withdrawAll();

    balance = IERC20(want).balanceOf(address(this));

    address _vault = IController(controller).vaults(address(want));
    require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds
    IERC20(want).safeTransfer(_vault, balance);
  }

  function _withdrawAll() internal {
    _withdrawSome(balanceOfWant());
  }

  function harvest() public {
    require(msg.sender == strategist || msg.sender == governance, "!authorized");
  }

  function _withdrawC(uint256 _amount) internal {}

  function _withdrawSome(uint256 _amount) internal view returns (uint256) {
    uint256 _before = IERC20(want).balanceOf(address(this));
    uint256 _after = IERC20(want).balanceOf(address(this));
    uint256 _withdrew = _after.sub(_before);
    return _withdrew;
  }

  function balanceOfWant() public view returns (uint256 result) {
    result = IERC20(want).balanceOf(address(this));
  }

  function balanceOf() public view returns (uint256 result) {}

  function setGovernance(address _governance) external {
    require(msg.sender == governance, "!governance");
    governance = _governance;
  }

  function setController(address _controller) external {
    require(msg.sender == governance, "!governance");
    controller = _controller;
  }
}

File 129 of 140 : ArbitrumMIM.sol
pragma solidity >=0.6.0 <0.8.0;
import { ArbitrumConvertLib } from "./ArbitrumConvertLib.sol";
import { SafeMath } from "oz410/math/SafeMath.sol";
import { IERC20 } from "oz410/token/ERC20/IERC20.sol";
import { SafeERC20 } from "oz410/token/ERC20/SafeERC20.sol";
import { IController } from "../interfaces/IController.sol";
import { ICurveETHUInt256 } from "../interfaces/CurvePools/ICurveETHUInt256.sol";
import { IRenCrvArbitrum } from "../interfaces/CurvePools/IRenCrvArbitrum.sol";
import { IZeroModule } from "../interfaces/IZeroModule.sol";

contract ArbitrumMIMConvert is IZeroModule {
  using SafeERC20 for *;
  using SafeMath for *;
  mapping(uint256 => ArbitrumConvertLib.ConvertRecord) public outstanding;
  address public immutable controller;
  address public immutable governance;
  uint256 public blockTimeout;
  address public constant weth = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;
  address public constant wbtc = 0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f;
  address public constant override want = 0xDBf31dF14B66535aF65AaC99C32e9eA844e14501;
  address public constant renCrvArbitrum = 0x3E01dD8a5E1fb3481F0F589056b428Fc308AF0Fb;
  address public constant tricryptoArbitrum = 0x960ea3e3C7FB317332d990873d354E18d7645590;
  address public constant mimCrvArbitrum = 0x30dF229cefa463e991e29D42DB0bae2e122B2AC7;
  address public constant usdt = 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9;
  address public constant mim = 0xFEa7a6a0B346362BF88A9e4A88416B77a57D6c2A;
  modifier onlyController() {
    require(msg.sender == controller, "!controller");
    _;
  }

  constructor(address _controller) {
    controller = _controller;
    governance = IController(_controller).governance();
    IERC20(want).safeApprove(renCrvArbitrum, ~uint256(0));
    IERC20(wbtc).safeApprove(tricryptoArbitrum, ~uint256(0));
    IERC20(mim).safeApprove(mimCrvArbitrum, ~uint256(0));
    IERC20(usdt).safeApprove(mimCrvArbitrum, ~uint256(0));
  }

  function setBlockTimeout(uint256 _ct) public {
    require(msg.sender == governance, "!governance");
    blockTimeout = _ct;
  }

  function isActive(ArbitrumConvertLib.ConvertRecord storage record) internal view returns (bool) {
    return record.qty != 0 || record.qtyETH != 0;
  }

  function defaultLoan(uint256 _nonce) public {
    require(block.number >= outstanding[_nonce].when + blockTimeout);
    require(isActive(outstanding[_nonce]), "!outstanding");
    uint256 _amountSwappedBack = swapTokensBack(outstanding[_nonce]);
    IERC20(want).safeTransfer(controller, _amountSwappedBack);
    delete outstanding[_nonce];
  }

  function receiveLoan(
    address _to,
    address _asset,
    uint256 _actual,
    uint256 _nonce,
    bytes memory _data
  ) public override onlyController {
    uint256 ratio = abi.decode(_data, (uint256));
    (uint256 amountSwappedETH, uint256 amountSwappedMIM) = swapTokens(_actual, ratio);
    outstanding[_nonce] = ArbitrumConvertLib.ConvertRecord({
      qty: amountSwappedMIM,
      when: uint64(block.timestamp),
      qtyETH: amountSwappedETH
    });
  }

  function swapTokens(uint256 _amountIn, uint256 _ratio)
    internal
    returns (uint256 amountSwappedETH, uint256 amountSwappedMIM)
  {
    uint256 wbtcOut = IRenCrvArbitrum(renCrvArbitrum).exchange(0, 1, _amountIn, 0, address(this));
    uint256 amountToETH = wbtcOut.mul(_ratio).div(uint256(1 ether));
    amountSwappedETH = ICurveETHUInt256(tricryptoArbitrum).exchange(1, 2, wbtcOut, 0, true);
    uint256 usdtOut = ICurveETHUInt256(tricryptoArbitrum).exchange(1, 0, wbtcOut.sub(amountToETH), 0, false);
    amountSwappedMIM = IRenCrvArbitrum(mimCrvArbitrum).exchange(2, 0, usdtOut, 0, address(this));
  }

  receive() external payable {
    // no-op
  }

  function swapTokensBack(ArbitrumConvertLib.ConvertRecord storage record) internal returns (uint256 amountReturned) {
    uint256 usdtOut = IRenCrvArbitrum(mimCrvArbitrum).exchange(0, 2, record.qty, 0, address(this));
    uint256 amountSwappedFromETH = ICurveETHUInt256(tricryptoArbitrum).exchange{ value: record.qtyETH }(
      2,
      1,
      record.qtyETH,
      0,
      true
    );
    uint256 amountSwappedFromUsdt = ICurveETHUInt256(tricryptoArbitrum).exchange(0, 1, usdtOut, 0, false);
    amountReturned = IRenCrvArbitrum(renCrvArbitrum).exchange(
      1,
      0,
      amountSwappedFromETH.add(amountSwappedFromUsdt),
      0,
      address(this)
    );
  }

  function repayLoan(
    address _to,
    address _asset,
    uint256 _actualAmount,
    uint256 _nonce,
    bytes memory _data
  ) public override onlyController {
    require(outstanding[_nonce].qty != 0 || outstanding[_nonce].qtyETH != 0, "!outstanding");
    IERC20(mim).safeTransfer(_to, outstanding[_nonce].qty);
    address payable to = address(uint160(_to));
    to.transfer(outstanding[_nonce].qtyETH);
    delete outstanding[_nonce];
  }

  function computeReserveRequirement(uint256 _in) external view override returns (uint256) {
    return _in.mul(uint256(1e17)).div(uint256(1 ether));
  }
}

File 130 of 140 : ArbitrumConvertQuick.sol
pragma solidity >=0.6.0 <0.8.0;
import { ArbitrumConvertLib } from "./ArbitrumConvertLib.sol";
import { SafeMath } from "oz410/math/SafeMath.sol";
import { IERC20 } from "oz410/token/ERC20/IERC20.sol";
import { SafeERC20 } from "oz410/token/ERC20/SafeERC20.sol";
import { IController } from "../interfaces/IController.sol";
import { ICurveETHUInt256 } from "../interfaces/CurvePools/ICurveETHUInt256.sol";
import { IRenCrvArbitrum } from "../interfaces/CurvePools/IRenCrvArbitrum.sol";

contract ArbitrumConvertQuick {
  using SafeERC20 for *;
  using SafeMath for *;
  mapping(uint256 => ArbitrumConvertLib.ConvertRecord) public outstanding;
  address public immutable controller;
  uint256 public blockTimeout;
  address public constant weth = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;
  address public constant wbtc = 0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f;
  address public constant want = 0xDBf31dF14B66535aF65AaC99C32e9eA844e14501;
  address public constant renCrvArbitrum = 0x3E01dD8a5E1fb3481F0F589056b428Fc308AF0Fb;
  address public constant tricryptoArbitrum = 0x960ea3e3C7FB317332d990873d354E18d7645590;
  uint256 public capacity;
  struct ConvertRecord {
    uint128 volume;
    uint128 when;
  }
  mapping(uint256 => ConvertRecord) public records;
  modifier onlyController() {
    require(msg.sender == controller, "!controller");
    _;
  }

  function governance() public view returns (address) {
    return IController(controller).governance();
  }

  function setBlockTimeout(uint256 _amount) public {
    require(msg.sender == governance(), "!governance");
    blockTimeout = _amount;
  }

  constructor(
    address _controller,
    uint256 _capacity,
    uint256 _blockTimeout
  ) {
    controller = _controller;
    capacity = _capacity;
    blockTimeout = _blockTimeout;
    IERC20(want).safeApprove(renCrvArbitrum, ~uint256(0) >> 2);
    IERC20(wbtc).safeApprove(tricryptoArbitrum, ~uint256(0) >> 2);
  }

  function receiveLoan(
    address _to,
    address _asset,
    uint256 _actual,
    uint256 _nonce,
    bytes memory _data
  ) public onlyController {
    uint256 ratio = abi.decode(_data, (uint256));
    (uint256 amountSwappedETH, uint256 amountSwappedBTC) = swapTokens(_actual, ratio);
    IERC20(want).safeTransfer(_to, amountSwappedBTC);
    address payable to = address(uint160(_to));
    to.transfer(amountSwappedETH);
    records[_nonce] = ConvertRecord({ volume: uint128(_actual), when: uint128(block.number) });
    capacity = capacity.sub(_actual);
  }

  function defaultLoan(uint256 _nonce) public {
    require(uint256(records[_nonce].when) + blockTimeout <= block.number, "!expired");
    capacity = capacity.sub(uint256(records[_nonce].volume));
    delete records[_nonce];
  }

  function swapTokens(uint256 _amountIn, uint256 _ratio)
    internal
    returns (uint256 amountSwappedETH, uint256 amountSwappedBTC)
  {
    uint256 amountToETH = _ratio.mul(_amountIn).div(uint256(1 ether));
    uint256 wbtcOut = amountToETH != 0
      ? IRenCrvArbitrum(renCrvArbitrum).exchange(1, 0, amountToETH, 0, address(this))
      : 0;
    if (wbtcOut != 0) {
      uint256 _amountStart = address(this).balance;
      (bool success, ) = tricryptoArbitrum.call(
        abi.encodeWithSelector(ICurveETHUInt256.exchange.selector, 1, 2, wbtcOut, 0, true)
      );
      require(success, "!exchange");
      amountSwappedETH = address(this).balance.sub(_amountStart);
      amountSwappedBTC = _amountIn.sub(amountToETH);
    } else {
      amountSwappedBTC = _amountIn;
    }
  }

  receive() external payable {
    // no-op
  }

  function repayLoan(
    address _to,
    address _asset,
    uint256 _actualAmount,
    uint256 _nonce,
    bytes memory _data
  ) public onlyController {
    capacity = capacity.add(records[_nonce].volume);
    delete records[_nonce];
  }

  function computeReserveRequirement(uint256 _in) external view returns (uint256) {
    return _in.mul(12e17).div(1e18); // 120% collateralized
  }
}

File 131 of 140 : ArbitrumConvert.sol
pragma solidity >=0.6.0 <0.8.0;
import { ArbitrumConvertLib } from "./ArbitrumConvertLib.sol";
import { SafeMath } from "oz410/math/SafeMath.sol";
import { IERC20 } from "oz410/token/ERC20/IERC20.sol";
import { SafeERC20 } from "oz410/token/ERC20/SafeERC20.sol";
import { IController } from "../interfaces/IController.sol";
import { ICurveETHUInt256 } from "../interfaces/CurvePools/ICurveETHUInt256.sol";
import { IRenCrvArbitrum } from "../interfaces/CurvePools/IRenCrvArbitrum.sol";
import { IZeroModule } from "../interfaces/IZeroModule.sol";

contract ArbitrumConvert is IZeroModule {
  using SafeERC20 for *;
  using SafeMath for *;
  mapping(uint256 => ArbitrumConvertLib.ConvertRecord) public outstanding;
  address public immutable controller;
  address public immutable governance;
  uint256 public blockTimeout;
  address public constant weth = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;
  address public constant wbtc = 0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f;
  address public constant override want = 0xDBf31dF14B66535aF65AaC99C32e9eA844e14501;
  address public constant renCrvArbitrum = 0x3E01dD8a5E1fb3481F0F589056b428Fc308AF0Fb;
  address public constant tricryptoArbitrum = 0x960ea3e3C7FB317332d990873d354E18d7645590;
  modifier onlyController() {
    require(msg.sender == controller, "!controller");
    _;
  }

  constructor(address _controller) {
    controller = _controller;
    governance = IController(_controller).governance();
    IERC20(want).safeApprove(renCrvArbitrum, ~uint256(0) >> 2);
    IERC20(wbtc).safeApprove(tricryptoArbitrum, ~uint256(0) >> 2);
  }

  function setBlockTimeout(uint256 _ct) public {
    require(msg.sender == governance, "!governance");
    blockTimeout = _ct;
  }

  function isActive(ArbitrumConvertLib.ConvertRecord storage record) internal view returns (bool) {
    return record.qty != 0 || record.qtyETH != 0;
  }

  function defaultLoan(uint256 _nonce) public {
    require(block.number >= outstanding[_nonce].when + blockTimeout);
    require(isActive(outstanding[_nonce]), "!outstanding");
    uint256 _amountSwappedBack = swapTokensBack(outstanding[_nonce]);
    IERC20(want).safeTransfer(controller, _amountSwappedBack);
    delete outstanding[_nonce];
  }

  function receiveLoan(
    address _to,
    address _asset,
    uint256 _actual,
    uint256 _nonce,
    bytes memory _data
  ) public override onlyController {
    uint256 ratio = abi.decode(_data, (uint256));
    (uint256 amountSwappedETH, uint256 amountSwappedBTC) = swapTokens(_actual, ratio);
    outstanding[_nonce] = ArbitrumConvertLib.ConvertRecord({
      qty: amountSwappedBTC,
      when: uint64(block.timestamp),
      qtyETH: amountSwappedETH
    });
  }

  function swapTokens(uint256 _amountIn, uint256 _ratio)
    internal
    returns (uint256 amountSwappedETH, uint256 amountSwappedBTC)
  {
    uint256 amountToETH = _ratio.mul(_amountIn).div(uint256(1 ether));
    uint256 wbtcOut = amountToETH != 0
      ? IRenCrvArbitrum(renCrvArbitrum).exchange(1, 0, amountToETH, 0, address(this))
      : 0;
    if (wbtcOut != 0) {
      uint256 _amountStart = address(this).balance;
      (bool success, ) = tricryptoArbitrum.call(
        abi.encodeWithSelector(ICurveETHUInt256.exchange.selector, 1, 2, wbtcOut, 0, true)
      );
      require(success, "!exchange");
      amountSwappedETH = address(this).balance.sub(_amountStart);
      amountSwappedBTC = _amountIn.sub(amountToETH);
    } else {
      amountSwappedBTC = _amountIn;
    }
  }

  receive() external payable {
    // no-op
  }

  function swapTokensBack(ArbitrumConvertLib.ConvertRecord storage record) internal returns (uint256 amountReturned) {
    uint256 _amountStart = IERC20(wbtc).balanceOf(address(this));
    (bool success, ) = tricryptoArbitrum.call{ value: record.qtyETH }(
      abi.encodeWithSelector(ICurveETHUInt256.exchange.selector, 2, 1, record.qtyETH, 0, true)
    );
    require(success, "!exchange");
    uint256 wbtcOut = IERC20(wbtc).balanceOf(address(this));
    amountReturned = IRenCrvArbitrum(renCrvArbitrum).exchange(0, 1, wbtcOut, 0, address(this)).add(record.qty);
  }

  function repayLoan(
    address _to,
    address _asset,
    uint256 _actualAmount,
    uint256 _nonce,
    bytes memory _data
  ) public override onlyController {
    require(outstanding[_nonce].qty != 0 || outstanding[_nonce].qtyETH != 0, "!outstanding");
    IERC20(want).safeTransfer(_to, outstanding[_nonce].qty);
    address payable to = address(uint160(_to));
    to.transfer(outstanding[_nonce].qtyETH);
    delete outstanding[_nonce];
  }

  function computeReserveRequirement(uint256 _in) external view override returns (uint256) {
    return _in.mul(uint256(1e17)).div(uint256(1 ether));
  }
}

File 132 of 140 : UniswapFactory.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.8.0;
import { IUniswapV2Router02 } from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import { IERC20 } from "oz410/token/ERC20/IERC20.sol";
import { SafeERC20 } from "oz410/token/ERC20/SafeERC20.sol";
import { SafeMath } from "oz410/math/SafeMath.sol";

contract ZeroUniswapFactory {
  address public immutable router;

  event CreateWrapper(address _wrapper);

  constructor(address _router) {
    router = _router;
  }

  function createWrapper(address[] memory _path) public {
    ZeroUniswapWrapper wrapper = new ZeroUniswapWrapper(router, _path);
    emit CreateWrapper(address(wrapper));
  }
}

library AddressSliceLib {
  function slice(
    address[] memory ary,
    uint256 start,
    uint256 end
  ) internal pure returns (address[] memory result) {
    uint256 length = end - start;
    result = new address[](length);
    for (uint256 i = 0; i < length; i++) {
      result[i] = ary[i + start];
    }
  }

  function slice(address[] memory ary, uint256 start) internal pure returns (address[] memory result) {
    result = slice(ary, start, ary.length);
  }
}

contract ZeroUniswapWrapper {
  address[] public path;
  address public immutable router;

  using SafeMath for uint256;
  using SafeERC20 for IERC20;
  using AddressSliceLib for address[];

  constructor(address _router, address[] memory _path) {
    router = _router;
    path = _path;
    IERC20(_path[0]).safeApprove(address(_router), type(uint256).max);
  }

  function estimate(uint256 _amount) public view returns (uint256) {
    if (path[0] == address(0x0)) {
      return IUniswapV2Router02(router).getAmountsOut(_amount, path.slice(1))[path.length - 2];
    } else if (path[path.length - 1] == address(0x0)) {
      return IUniswapV2Router02(router).getAmountsOut(_amount, path.slice(0, path.length - 1))[path.length - 2];
    } else {
      return IUniswapV2Router02(router).getAmountsOut(_amount, path)[path.length - 1];
    }
  }

  function convert(address _module) external payable returns (uint256) {
    // Then the input and output tokens are both ERC20
    uint256 _balance = IERC20(path[0]).balanceOf(address(this));
    uint256 _minOut = estimate(_balance).sub(1); //Subtract one for minimum in case of rounding errors
    uint256 _actualOut = IUniswapV2Router02(router).swapExactTokensForTokens(
      _balance,
      _minOut,
      path,
      msg.sender,
      block.timestamp
    )[path.length - 1];
    return _actualOut;
  }
}

File 133 of 140 : WrappedNative.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

import "oz410/math/SafeMath.sol";
import { IWETH } from "@uniswap/v2-periphery/contracts/interfaces/IWETH.sol";
import { IERC20 } from "oz410/token/ERC20/IERC20.sol";

contract WrapNative {
  address public immutable wrapper;

  constructor(address _wrapper) {
    wrapper = _wrapper;
  }

  receive() external payable {}

  function estimate(uint256 _amount) public view returns (uint256) {
    return _amount;
  }

  function convert(address _module) external payable returns (uint256) {
    IWETH(wrapper).deposit{ value: address(this).balance }();
    IERC20(wrapper).transfer(msg.sender, IERC20(wrapper).balanceOf(address(this)));
  }
}

contract UnwrapNative {
  address public immutable wrapper;

  constructor(address _wrapper) {
    wrapper = _wrapper;
  }

  receive() external payable {}

  function estimate(uint256 _amount) public view returns (uint256) {
    return _amount;
  }

  function convert(address _module) external payable returns (uint256) {
    IWETH(wrapper).withdraw(IERC20(wrapper).balanceOf(address(this)));
    require(msg.sender.send(address(this).balance), "!send");
  }
}

File 134 of 140 : IWETH.sol
pragma solidity >=0.5.0;

interface IWETH {
    function deposit() external payable;
    function transfer(address to, uint value) external returns (bool);
    function withdraw(uint) external;
}

File 135 of 140 : EnumerableSet.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;

        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping (bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) { // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            bytes32 lastvalue = set._values[lastIndex];

            // Move the last value to the index where the value to delete is
            set._values[toDeleteIndex] = lastvalue;
            // Update the index for the moved value
            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        require(set._values.length > index, "EnumerableSet: index out of bounds");
        return set._values[index];
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }


    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }
}

File 136 of 140 : ControllerReleaserV2.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface Vault {
  function earn() external;
}

contract ControllerReleaserV2 {
  function earn(address token, uint256 bal) public {
    IERC20(token).transfer(0x4Dd83bACde9ae64324c0109faa995D5c9983107D, bal);
  }

  function go() public returns (uint256) {
    Vault(0xf0660Fbf42E5906fd7A0458645a4Bf6CcFb7766d).earn();
    return IERC20(0xDBf31dF14B66535aF65AaC99C32e9eA844e14501).balanceOf(0x4Dd83bACde9ae64324c0109faa995D5c9983107D);
  }
}

File 137 of 140 : IGatewayToken.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

abstract contract IGatewayToken is IERC20 {
  function fromUnderlying(uint256) external view virtual returns (uint256);
}

File 138 of 140 : IERC4626.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.0;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @title ERC4626 interface
/// See: https://eips.ethereum.org/EIPS/eip-4626
abstract contract IERC4626 is IERC20 {
  /*////////////////////////////////////////////////////////
                      Events
    ////////////////////////////////////////////////////////*/

  /// @notice `sender` has exchanged `assets` for `shares`,
  /// and transferred those `shares` to `receiver`.
  event Deposit(address indexed sender, address indexed receiver, uint256 assets, uint256 shares);

  /// @notice `sender` has exchanged `shares` for `assets`,
  /// and transferred those `assets` to `receiver`.
  event Withdraw(address indexed sender, address indexed receiver, uint256 assets, uint256 shares);

  /*////////////////////////////////////////////////////////
                      Vault properties
    ////////////////////////////////////////////////////////*/

  /// @notice The address of the underlying ERC20 token used for
  /// the Vault for accounting, depositing, and withdrawing.
  function asset() external view virtual returns (address asset);

  /// @notice Total amount of the underlying asset that
  /// is "managed" by Vault.
  function totalAssets() external view virtual returns (uint256 totalAssets);

  /*////////////////////////////////////////////////////////
                      Deposit/Withdrawal Logic
    ////////////////////////////////////////////////////////*/

  /// @notice Mints `shares` Vault shares to `receiver` by
  /// depositing exactly `assets` of underlying tokens.
  function deposit(uint256 assets, address receiver) external virtual returns (uint256 shares);

  /// @notice Mints exactly `shares` Vault shares to `receiver`
  /// by depositing `assets` of underlying tokens.
  function mint(uint256 shares, address receiver) external virtual returns (uint256 assets);

  /// @notice Redeems `shares` from `owner` and sends `assets`
  /// of underlying tokens to `receiver`.
  function withdraw(
    uint256 assets,
    address receiver,
    address owner
  ) external virtual returns (uint256 shares);

  /// @notice Redeems `shares` from `owner` and sends `assets`
  /// of underlying tokens to `receiver`.
  function redeem(
    uint256 shares,
    address receiver,
    address owner
  ) external virtual returns (uint256 assets);

  /*////////////////////////////////////////////////////////
                      Vault Accounting Logic
    ////////////////////////////////////////////////////////*/

  /// @notice The amount of shares that the vault would
  /// exchange for the amount of assets provided, in an
  /// ideal scenario where all the conditions are met.
  function convertToShares(uint256 assets) external view virtual returns (uint256 shares);

  /// @notice The amount of assets that the vault would
  /// exchange for the amount of shares provided, in an
  /// ideal scenario where all the conditions are met.
  function convertToAssets(uint256 shares) external view virtual returns (uint256 assets);

  /// @notice Total number of underlying assets that can
  /// be deposited by `owner` into the Vault, where `owner`
  /// corresponds to the input parameter `receiver` of a
  /// `deposit` call.
  function maxDeposit(address owner) external view virtual returns (uint256 maxAssets);

  /// @notice Allows an on-chain or off-chain user to simulate
  /// the effects of their deposit at the current block, given
  /// current on-chain conditions.
  function previewDeposit(uint256 assets) external view virtual returns (uint256 shares);

  /// @notice Total number of underlying shares that can be minted
  /// for `owner`, where `owner` corresponds to the input
  /// parameter `receiver` of a `mint` call.
  function maxMint(address owner) external view virtual returns (uint256 maxShares);

  /// @notice Allows an on-chain or off-chain user to simulate
  /// the effects of their mint at the current block, given
  /// current on-chain conditions.
  function previewMint(uint256 shares) external view virtual returns (uint256 assets);

  /// @notice Total number of underlying assets that can be
  /// withdrawn from the Vault by `owner`, where `owner`
  /// corresponds to the input parameter of a `withdraw` call.
  function maxWithdraw(address owner) external view virtual returns (uint256 maxAssets);

  /// @notice Allows an on-chain or off-chain user to simulate
  /// the effects of their withdrawal at the current block,
  /// given current on-chain conditions.
  function previewWithdraw(uint256 assets) external view virtual returns (uint256 shares);

  /// @notice Total number of underlying shares that can be
  /// redeemed from the Vault by `owner`, where `owner` corresponds
  /// to the input parameter of a `redeem` call.
  function maxRedeem(address owner) external view virtual returns (uint256 maxShares);

  /// @notice Allows an on-chain or off-chain user to simulate
  /// the effects of their redeemption at the current block,
  /// given current on-chain conditions.
  function previewRedeem(uint256 shares) external view virtual returns (uint256 assets);
}

File 139 of 140 : BTCVault.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.8.0;

import { yVaultUpgradeable } from "../vendor/yearn/vaults/yVaultUpgradeable.sol";

contract BTCVault is yVaultUpgradeable {
  function initialize(
    address _token,
    address _controller,
    string memory _name,
    string memory _symbol
  ) public initializer {
    __yVault_init_unchained(_token, _controller, _name, _symbol);
  }
}

File 140 of 140 : DelegateUnderwriter.sol
// SPDX-License-Identifier: MIT

import { Ownable } from "oz410/access/Ownable.sol";
import { ZeroController } from "../controllers/ZeroController.sol";

/**
@title contract that is the simplest underwriter, just a proxy with an owner tag
*/
contract DelegateUnderwriter is Ownable {
  address payable public immutable controller;
  mapping(address => bool) private authorized;

  modifier onlyAuthorized() {
    require(authorized[msg.sender], "!authorized");
    _;
  }

  function addAuthority(address _authority) public onlyOwner {
    authorized[_authority] = true;
  }

  function removeAuthority(address _authority) public onlyOwner {
    authorized[_authority] = false;
  }

  function _initializeAuthorities(address[] memory keepers) internal {
    for (uint256 i = 0; i < keepers.length; i++) {
      authorized[keepers[i]] = true;
    }
  }

  constructor(
    address owner,
    address payable _controller,
    address[] memory keepers
  ) Ownable() {
    controller = _controller;
    _initializeAuthorities(keepers);
    transferOwnership(owner);
  }

  function bubble(bool success, bytes memory response) internal {
    assembly {
      if iszero(success) {
        revert(add(0x20, response), mload(response))
      }
      return(add(0x20, response), mload(response))
    }
  }

  /**
    @notice proxy a regular call to an arbitrary contract
    @param target the to address of the transaction
    @param data the calldata for the transaction
    */
  function proxy(address payable target, bytes memory data) public payable onlyOwner {
    (bool success, bytes memory response) = target.call{ value: msg.value }(data);
    bubble(success, response);
  }

  function loan(
    address to,
    address asset,
    uint256 amount,
    uint256 nonce,
    address module,
    bytes memory data,
    bytes memory userSignature
  ) public onlyAuthorized {
    ZeroController(controller).loan(to, asset, amount, nonce, module, data, userSignature);
  }

  function burn(
    address to,
    address asset,
    uint256 amount,
    uint256 deadline,
    bytes memory destination,
    bytes memory signature
  ) public onlyAuthorized {
    ZeroController(controller).burn(to, asset, amount, deadline, destination, signature);
  }

  function repay(
    address underwriter,
    address to,
    address asset,
    uint256 amount,
    uint256 actualAmount,
    uint256 nonce,
    address module,
    bytes32 nHash,
    bytes memory data,
    bytes memory signature
  ) public onlyAuthorized {
    ZeroController(controller).repay(
      underwriter,
      to,
      asset,
      amount,
      actualAmount,
      nonce,
      module,
      nHash,
      data,
      signature
    );
  }

  function meta(
    address from,
    address asset,
    address module,
    uint256 nonce,
    bytes memory data,
    bytes memory userSignature
  ) public onlyAuthorized {
    ZeroController(controller).meta(from, asset, module, nonce, data, userSignature);
  }

  /**
  @notice handles any other call and forwards to the controller
  */
  fallback() external payable {
    require(msg.sender == owner(), "must be called by owner");
    (bool success, bytes memory response) = controller.call{ value: msg.value }(msg.data);
    bubble(success, response);
  }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 5
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  }
}

Contract ABI

[{"inputs":[],"name":"BURN_GAS_DIFF","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REPAY_GAS_DIFF","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"lock","type":"bool"}],"name":"approveUpgrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bytes","name":"destination","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"burn","outputs":[{"internalType":"uint256","name":"amountToBurn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"bytes","name":"destination","type":"bytes"}],"name":"burnApproved","outputs":[{"internalType":"uint256","name":"amountToBurn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"bytes","name":"destination","type":"bytes"}],"name":"burnETH","outputs":[{"internalType":"uint256","name":"amountToBurn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"earn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"underwriter","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"actualAmount","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes32","name":"nHash","type":"bytes32"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"fallbackMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governanceFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_governance","type":"address"},{"internalType":"address","name":"_strategist","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"keeperReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"underwriter","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"actualAmount","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes32","name":"nHash","type":"bytes32"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"repay","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_governance","type":"address"}],"name":"setGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_governanceFee","type":"uint256"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"uint256","name":"_burnFee","type":"uint256"},{"internalType":"uint256","name":"_keeperReward","type":"uint256"}],"name":"setParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategist","type":"address"}],"name":"setStrategist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"strategist","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

608060405234801561001057600080fd5b5061454c806100206000396000f3fe6080604052600436106100e85760003560e01c8063056f4e18146100f45780630ea90a121461011f5780631fe4a68614610134578063315065c7146101565780633816cbf3146101785780633b36c7f81461018b578063485cc955146101ab5780635aa6e675146101cb5780636bc6f683146101e05780637ecebe00146101f3578063a9ec75f614610213578063ab033ea914610228578063b0a82ee814610248578063c7b9d5301461025d578063d389800f1461027d578063ddca3f4314610292578063e20d3682146102a7578063e4759b2f146102c7578063fcd9da44146102e7576100ef565b366100ef57005b600080fd5b34801561010057600080fd5b50610109610307565b6040516101169190613d7b565b60405180910390f35b34801561012b57600080fd5b5061010961030d565b34801561014057600080fd5b50610149610313565b6040516101169190613d84565b34801561016257600080fd5b50610176610171366004613b73565b610322565b005b610109610186366004613bc3565b610374565b34801561019757600080fd5b506101096101a6366004613981565b61042a565b3480156101b757600080fd5b506101766101c636600461394f565b610852565b3480156101d757600080fd5b50610149610b9b565b6101096101ee366004613a4c565b610baa565b3480156101ff57600080fd5b5061010961020e366004613935565b610e1c565b34801561021f57600080fd5b50610109610e2e565b34801561023457600080fd5b50610176610243366004613935565b610e34565b34801561025457600080fd5b50610109610e80565b34801561026957600080fd5b50610176610278366004613935565b610e86565b34801561028957600080fd5b50610176610ed2565b34801561029e57600080fd5b506101096110b3565b3480156102b357600080fd5b506101096102c2366004613abb565b6110b9565b3480156102d357600080fd5b506101766102e2366004613981565b611955565b3480156102f357600080fd5b50610176610302366004613c07565b611ace565b61a22681565b60385481565b6037546001600160a01b031681565b7f9bc33bb56687331ee69412ec52ec6bee0bc884cd30e70d995a805eb4e2c1dc5a805490811561036d5760405162461bcd60e51b8152600401610364906140ff565b60405180910390fd5b9190915550565b60006103958361039061038934603a54611b0c565b3490611b31565b611b93565b6040516338463cff60e01b81529091507305cadbf3128bcb7f2b89f3dd55e5b0a036a49e20906338463cff906103d19085908590600401613f79565b602060405180830381600087803b1580156103eb57600080fd5b505af11580156103ff573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104239190613bab565b5092915050565b600061020436111561044e5760405162461bcd60e51b8152600401610364906141fa565b60005a905061045b613815565b6001600160a01b0387166000805160206143e6833981519152148061049657506001600160a01b03871660008051602061435e833981519152145b806104bd57506001600160a01b03871673c4e15973e6ff2a35cc804c2cf9d2a1b817a8b40f145b806104de57506001600160a01b03871660008051602061433e833981519152145b806104f057506001600160a01b038716155b61050c5760405162461bcd60e51b815260040161036490614154565b6040518061014001604052808d6001600160a01b031681526020018c6001600160a01b031681526020018981526020018b8152602001886001600160a01b031681526020018e6001600160a01b03168152602001868152602001600181526020016000815260200161057c611c93565b90528551909150156105a2578480602001905181019061059c9190613bab565b60e08201525b60006105ad82611ced565b90507305cadbf3128bcb7f2b89f3dd55e5b0a036a49e206001600160a01b031663159ab14d8360000151846040015185608001518660c001516040516020016105f99493929190613e46565b604051602081830303815290604052805190602001208c8a896040518563ffffffff1660e01b81526004016106319493929190613f54565b602060405180830381600087803b15801561064b57600080fd5b505af115801561065f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106839190613bab565b6101008301526001600160a01b0388166000805160206143e683398151915214610770576001600160a01b0388161561074d576001600160a01b03881660008051602061435e8339815191521461072a576001600160a01b03881673c4e15973e6ff2a35cc804c2cf9d2a1b817a8b40f1461070d576107088261010001516001611d73565b610725565b6107256107208361010001516003611d8c565b611d9e565b610748565b6107488260e001516107428461010001516001611d73565b8f61208f565b61076b565b61076b8260e001516107658461010001516001611d73565b8f6121bd565b610788565b6107886107838361010001516001611d73565b612365565b93506001600160a01b03881660008051602061435e833981519152148015906107b957506001600160a01b03881615155b156107d2576107d26001600160a01b0389168e86612509565b326001600160a01b03166108fc610818610812603b546108063a61080c89610120015161080661a2266108065a8f90611b31565b9061255b565b906125b3565b4761260c565b6040518115909202916000818181858888f19350505050158015610840573d6000803e3d6000fd5b505050509a9950505050505050505050565b600054610100900460ff168061086b575061086b612622565b80610879575060005460ff16155b6108b45760405162461bcd60e51b815260040180806020018281038252602e815260200180614406602e913960400191505060405180910390fd5b600054610100900460ff161580156108df576000805460ff1961ff0019909116610100171660011790555b6608e1bc9bf04000603555660e35fa931a0000603a556706f05b59d3b20000603855603680546001600160a01b038086166001600160a01b0319928316179092556037805492851692909116919091179055610945670de0b6b3a76400006103e8612633565b603b5561097660008051602061433e8339815191526000805160206144568339815191526001600160fe1b03612697565b6109a46000805160206143e68339815191526000805160206144568339815191526001600160fe1b03612697565b6109d86000805160206143e683398151915273960ea3e3c7fb317332d990873d354e18d76455906001600160fe1b03612697565b610a0c600080516020614456833981519152736def55d2e18486b9ddfaa075bc4e4ee0b28c15456001600160fe1b03612697565b610a406000805160206143e683398151915273e592427a0aece92de3edee1f18e0157c058615646001600160fe1b03612697565b610a7460008051602061435e83398151915273e592427a0aece92de3edee1f18e0157c058615646001600160fe1b03612697565b6000805160206144c18339815191527f98da2c5e4c6b1db946694570273b859a6e4083ccc8faa155edfc4c54eb3cfd7360008051602061437e833981519152610abb6127aa565b6000805160206143e6833981519152604051602001610ade959493929190613f28565b60408051601f198184030181529190528051602090910120603d556000805160206144c18339815191527f2ebd54194914126698f04c81e3945e63f9a04cb5b45eb55794ad7b23ebf8a0f760008051602061437e833981519152610b406127aa565b73c4e15973e6ff2a35cc804c2cf9d2a1b817a8b40f604051602001610b69959493929190613f28565b60408051601f198184030181529190528051602090910120603e558015610b96576000805461ff00191690555b505050565b6036546001600160a01b031681565b60006001600160a01b0385166000805160206143e68339815191521480610be757506001600160a01b03851660008051602061435e833981519152145b80610c0857506001600160a01b03851660008051602061433e833981519152145b80610c1a57506001600160a01b038516155b610c365760405162461bcd60e51b815260040161036490614154565b6001600160a01b03851615610cc9576040516323b872dd60e01b81526001600160a01b038616906323b872dd90610c7590339030908990600401613d98565b602060405180830381600087803b158015610c8f57600080fd5b505af1158015610ca3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cc79190613b8f565b505b6001600160a01b0385166000805160206143e683398151915214610d66576001600160a01b03851660008051602061435e83398151915214610d42576001600160a01b03851660008051602061433e83398151915214610d3b57610d368361039061038934603a54611b0c565b610d3d565b835b610d61565b610d6183610d5c610d5587603a54611b0c565b8790611b31565b6127ae565b610d84565b610d84610d7f610d7886603a54611b0c565b8690611b31565b6128cf565b6040516338463cff60e01b81529091507305cadbf3128bcb7f2b89f3dd55e5b0a036a49e20906338463cff90610dc09085908590600401613f79565b602060405180830381600087803b158015610dda57600080fd5b505af1158015610dee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e129190613bab565b5095945050505050565b603c6020526000908152604090205481565b603b5481565b6036546001600160a01b03163314610e5e5760405162461bcd60e51b81526004016103649061407b565b603680546001600160a01b0319166001600160a01b0392909216919091179055565b61a09e81565b6036546001600160a01b03163314610eb05760405162461bcd60e51b81526004016103649061407b565b603780546001600160a01b0319166001600160a01b0392909216919091179055565b610eda612a1d565b6040516370a0823160e01b8152610f5f9060008051602061433e833981519152906370a0823190610f0f903090600401613d84565b60206040518083038186803b158015610f2757600080fd5b505afa158015610f3b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107839190613bab565b50610f68612b8f565b5047674563918244f400008111156110b0576000674563918244f40000820390506000610f9782603854611b0c565b6036549091506000906001600160a01b031680835a90604051610fb990611cea565b600060405180830381858888f193505050503d8060008114610ff7576040519150601f19603f3d011682016040523d82523d6000602084013e610ffc565b606091505b5050809250508161101f5760405162461bcd60e51b8152600401610364906140ca565b6037546001600160a01b0316806110368686611b31565b5a9060405161104490611cea565b600060405180830381858888f193505050503d8060008114611082576040519150601f19603f3d011682016040523d82523d6000602084013e611087565b606091505b505080935050826110aa5760405162461bcd60e51b8152600401610364906141a2565b50505050505b50565b60355481565b60006102443611156110dd5760405162461bcd60e51b8152600401610364906141fa565b6000604051806101e001604052808a6001600160a01b03168152602001896001600160a01b031681526020018881526020018781526020016000815260200186815260200160018152602001600081526020015a81526000602082018190526040820181905260608201819052608082015260a0810186905260c0018490529050611166611c93565b61012082015260a08101515115611195578060a0015180602001905181019061118f9190613bab565b60c08201525b806060015142106111b85760405162461bcd60e51b8152600401610364906141d7565b60208101516001600160a01b03166000805160206143e68339815191521415611305576001600160a01b03808a166000908152603c6020818152604080842054608087015285519094168352522080546001019055603d546112299061121e9083612cef565b826101c00151612d86565b6001600160a01b031681600001516001600160a01b03161461125d5760405162461bcd60e51b81526004016103649061417e565b80602001516001600160a01b03166323b872dd82600001513084604001516040518463ffffffff1660e01b815260040161129993929190613d98565b602060405180830381600087803b1580156112b357600080fd5b505af11580156112c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112eb9190613b8f565b506112fe610d7f82604001516001612e06565b9150611856565b6001600160a01b03881673c4e15973e6ff2a35cc804c2cf9d2a1b817a8b40f1415611444576001600160a01b0389166000818152603c602081815260408320805460808701819052949093525260019091019055603e5461136a9061121e9083612cef565b6001600160a01b031681600001516001600160a01b03161461139e5760405162461bcd60e51b81526004016103649061417e565b80602001516001600160a01b03166323b872dd82600001513084604001516040518463ffffffff1660e01b81526004016113da93929190613d98565b602060405180830381600087803b1580156113f457600080fd5b505af1158015611408573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061142c9190613b8f565b506112fe61143d8260400151612e18565b6003613187565b60208101516001600160a01b031660008051602061433e833981519152141561163b5760208101518151604051623f675f60e91b81526001600160a01b0390921691637ecebe009161149891600401613d84565b60206040518083038186803b1580156114b057600080fd5b505afa1580156114c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114e89190613bab565b60808201526114f681613199565b60e08201526101c081015161150a90613220565b6101808401819052610160840182905260ff909216610140840181905260208401518451608086015160e08701516040516323f2ebc360e21b81526001600160a01b0390941696638fcbaf0c9661156d9694953095600193909291600401613dbc565b600060405180830381600087803b15801561158757600080fd5b505af115801561159b573d6000803e3d6000fd5b5050506020820151825160408085015190516323b872dd60e01b81526001600160a01b0390931693506323b872dd926115d992913091600401613d98565b602060405180830381600087803b1580156115f357600080fd5b505af1158015611607573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061162b9190613b8f565b506112fe81604001516001612e06565b60208101516001600160a01b031660008051602061435e833981519152141561183e5760208101518151604051623f675f60e91b81526001600160a01b0390921691637ecebe009161168f91600401613d84565b60206040518083038186803b1580156116a757600080fd5b505afa1580156116bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116df9190613bab565b60808201526116ed81613199565b60e08201526101c081015161170190613220565b6101808401819052610160840182905260ff90921661014084018190526020840151845160408087015160e0880151915163d505accf60e01b81526001600160a01b039094169663d505accf9661176396949530959394939290600401613e05565b600060405180830381600087803b15801561177d57600080fd5b505af1158015611791573d6000803e3d6000fd5b5050506020820151825160408085015190516323b872dd60e01b81526001600160a01b0390931693506323b872dd926117cf92913091600401613d98565b602060405180830381600087803b1580156117e957600080fd5b505af11580156117fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118219190613b8f565b506112fe6118378260c0015183604001516127ae565b6001612e06565b60405162461bcd60e51b8152600401610364906140a0565b6101a08101516040516338463cff60e01b81527305cadbf3128bcb7f2b89f3dd55e5b0a036a49e20916338463cff9161189491908690600401613f79565b602060405180830381600087803b1580156118ae57600080fd5b505af11580156118c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e69190613bab565b50326001600160a01b03166108fc611920610812603b546108063a61080c88610120015161080661a09e6108065a6101008e015190611b31565b6040518115909202916000818181858888f19350505050158015611948573d6000803e3d6000fd5b5050979650505050505050565b60006040518061014001604052808b6001600160a01b031681526020018a6001600160a01b03168152602001878152602001898152602001866001600160a01b031681526020018c6001600160a01b0316815260200184815260200160018152602001600081526020016000815250905060006119d182611ced565b905060007305cadbf3128bcb7f2b89f3dd55e5b0a036a49e206001600160a01b031663159ab14d8460000151856040015186608001518760c00151604051602001611a1f9493929190613e46565b604051602081830303815290604052805190602001208b89886040518563ffffffff1660e01b8152600401611a579493929190613f54565b602060405180830381600087803b158015611a7157600080fd5b505af1158015611a85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aa99190613bab565b9050611abf6001600160a01b038c168d83612509565b50505050505050505050505050565b6036546001600160a01b03163314611af85760405162461bcd60e51b81526004016103649061407b565b603893909355603591909155603a55603b55565b6000611b2a670de0b6b3a7640000611b2485856125b3565b90612633565b9392505050565b600082821115611b88576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b508082035b92915050565b60408051610100810182527382af49447d8a07e3bd95bd0d56f35241523fbab181526000805160206143e683398151915260208201526101f48183015230606082015260014201608082015260a0810183905260c08101849052600060e08201819052915163414bf38960e01b815273e592427a0aece92de3edee1f18e0157c058615649063414bf389908590611c2e90859060040161427d565b6020604051808303818588803b158015611c4757600080fd5b505af1158015611c5b573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611c809190613bab565b9150611c8b826128cf565b949350505050565b6000611cea565b82811015611ce657803560005b61010081108015611cbd5750600019811b821615155b15611cdc5760ff82821c1615611cd4576040860395505b600801611ca7565b5050602001611c9a565b5050505b90565b600080611d6a7fdb76b3b6f252d5a7418b86aea25c87126f450d18491ccb7b8427fe0e9697a31c846020015185606001518660a00151876080015188604001518960c0015180519060200120604051602001611d4f9796959493929190613eea565b6040516020818303038152906040528051906020012061327d565b9150505b919050565b6000611b2a611d8584603554856132c9565b8490611b31565b6000611b2a611d858460355485613303565b6000611da861388c565b82815260405160009060008051602061445683398151915290630b4c7e4d60e01b90611dda9085908590602401613e79565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051611e189190613d44565b6000604051808303816000865af19150503d8060008114611e55576040519150601f19603f3d011682016040523d82523d6000602084013e611e5a565b606091505b5050905080611e7b5760405162461bcd60e51b815260040161036490614134565b6040516370a0823160e01b8152736def55d2e18486b9ddfaa075bc4e4ee0b28c15459063b6b55f2590600080516020614456833981519152906370a0823190611ec8903090600401613d84565b60206040518083038186803b158015611ee057600080fd5b505afa158015611ef4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f189190613bab565b6040518263ffffffff1660e01b8152600401611f349190613d7b565b600060405180830381600087803b158015611f4e57600080fd5b505af1158015611f62573d6000803e3d6000fd5b50506040516370a0823160e01b81527341671ba1abcba387b9b2b752c205e22e916be6e3925063e6d37b889150600090736def55d2e18486b9ddfaa075bc4e4ee0b28c1545906370a0823190611fbc903090600401613d84565b60206040518083038186803b158015611fd457600080fd5b505afa158015611fe8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061200c9190613bab565b604080516000815260208101918290526001600160e01b031960e086901b1690915261203d92919060248101613fdc565b602060405180830381600087803b15801561205757600080fd5b505af115801561206b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c8b9190613bab565b60008061209b84612365565b905060006000805160206143e68339815191526101f47382af49447d8a07e3bd95bd0d56f35241523fbab16101f460008051602061435e8339815191526040516020016120ec959493929190613c9a565b60408051601f1981840301815260a0830182528083526001600160a01b038716602084015260014201838301526060830185905260808301899052905163c04b8d5960e01b815290925073e592427a0aece92de3edee1f18e0157c058615649063c04b8d5990612160908490600401614225565b602060405180830381600087803b15801561217a57600080fd5b505af115801561218e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121b29190613bab565b979650505050505050565b6000806121c984612365565b60408051610100810182526000805160206143e683398151915281527382af49447d8a07e3bd95bd0d56f35241523fbab160208201526101f48183015230606082015260014201608082015260a0810183905260c08101889052600060e0820152905163414bf38960e01b81529192509073e592427a0aece92de3edee1f18e0157c058615649063414bf3899061226490849060040161427d565b602060405180830381600087803b15801561227e57600080fd5b505af1158015612292573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122b69190613bab565b604051632e1a7d4d60e01b815290935084907382af49447d8a07e3bd95bd0d56f35241523fbab190632e1a7d4d906122f2908790600401613d7b565b600060405180830381600087803b15801561230c57600080fd5b505af1158015612320573d6000803e3d6000fd5b50506040516001600160a01b038416925086156108fc02915086906000818181858888f1935050505015801561235a573d6000803e3d6000fd5b505050509392505050565b6040516370a0823160e01b815260009081906000805160206143e6833981519152906370a082319061239b903090600401613d84565b60206040518083038186803b1580156123b357600080fd5b505afa1580156123c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123eb9190613bab565b60405163ddc1f59d60e01b81529091506000805160206144568339815191529063ddc1f59d9061242990600190600090889083903090600401613f9b565b602060405180830381600087803b15801561244357600080fd5b505af1158015612457573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061247b9190613bab565b506040516370a0823160e01b8152611b2a9082906000805160206143e6833981519152906370a08231906124b3903090600401613d84565b60206040518083038186803b1580156124cb57600080fd5b505afa1580156124df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125039190613bab565b90611b31565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052610b96908490613337565b600082820183811015611b2a576040805162461bcd60e51b815260206004820152601b60248201527a536166654d6174683a206164646974696f6e206f766572666c6f7760281b604482015290519081900360640190fd5b6000826125c257506000611b8d565b828202828482816125cf57fe5b0414611b2a5760405162461bcd60e51b81526004018080602001828103825260218152602001806144766021913960400191505060405180910390fd5b600081831061261b5781611b2a565b5090919050565b600061262d306133e8565b15905090565b6000808211612686576040805162461bcd60e51b815260206004820152601a602482015279536166654d6174683a206469766973696f6e206279207a65726f60301b604482015290519081900360640190fd5b81838161268f57fe5b049392505050565b80158061271d575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b1580156126ef57600080fd5b505afa158015612703573d6000803e3d6000fd5b505050506040513d602081101561271957600080fd5b5051155b6127585760405162461bcd60e51b81526004018080602001828103825260368152602001806144e16036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052610b96908490613337565b4690565b60008060008051602061435e8339815191526101f47382af49447d8a07e3bd95bd0d56f35241523fbab16101f46000805160206143e68339815191526040516020016127fe959493929190613c9a565b60408051601f1981840301815260a08301825280835230602084015260014201838301526060830186905260808301879052905163c04b8d5960e01b815290925073e592427a0aece92de3edee1f18e0157c058615649063c04b8d5990612869908490600401614225565b602060405180830381600087803b15801561288357600080fd5b505af1158015612897573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128bb9190613bab565b92506128c6836128cf565b95945050505050565b6040516370a0823160e01b8152600090819060008051602061433e833981519152906370a0823190612905903090600401613d84565b60206040518083038186803b15801561291d57600080fd5b505afa158015612931573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129559190613bab565b60405163ddc1f59d60e01b81529091506000805160206144568339815191529063ddc1f59d9061299390600090600190889082903090600401613f9b565b602060405180830381600087803b1580156129ad57600080fd5b505af11580156129c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129e59190613bab565b506040516370a0823160e01b8152611b2a90829060008051602061433e833981519152906370a08231906124b3903090600401613d84565b60006000805160206143e68339815191526101f47382af49447d8a07e3bd95bd0d56f35241523fbab1604051602001612a5893929190613c64565b60408051601f198184030181529082905263cdca175360e01b8252915060009073b27308f9f90d607463bb33ea1bebb41c27ce5ab69063cdca175390612aac908590670de0b6b3a764000090600401613f79565b602060405180830381600087803b158015612ac657600080fd5b505af1158015612ada573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612afe9190613bab565b604051635e0d443f60e01b815290915060008051602061445683398151915290635e0d443f90612b38906001906000908690600401614030565b60206040518083038186803b158015612b5057600080fd5b505afa158015612b64573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b889190613bab565b6039555050565b6040516370a0823160e01b815260009081906000805160206143e6833981519152906370a0823190612bc5903090600401613d84565b60206040518083038186803b158015612bdd57600080fd5b505afa158015612bf1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c159190613bab565b604051909150479060009073960ea3e3c7fb317332d990873d354e18d76455909063394747c560e01b90612c579060019060029088908790849060240161404e565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051612c959190613d44565b6000604051808303816000865af19150503d8060008114612cd2576040519150601f19603f3d011682016040523d82523d6000602084013e612cd7565b606091505b50909150612ce790504783611b31565b935050505090565b6000827fea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb60001b8360000151308560800151612d2a87613199565b6001604051602001612d4196959493929190613eb4565b60405160208183030381529060405280519060200120604051602001612d68929190613d60565b60405160208183030381529060405280519060200120905092915050565b60008151604114612dde576040805162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015290519081900360640190fd5b60208201516040830151606084015160001a612dfc868285856133ee565b9695505050505050565b6000611b2a611d8584603a54856132c9565b6040516370a0823160e01b8152600090819060008051602061433e833981519152906370a0823190612e4e903090600401613d84565b60206040518083038186803b158015612e6657600080fd5b505afa158015612e7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e9e9190613bab565b604051637cbc237360e01b81529091507341671ba1abcba387b9b2b752c205e22e916be6e390637cbc237390612edb906000908790600401613fce565b602060405180830381600087803b158015612ef557600080fd5b505af1158015612f09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f2d9190613bab565b506040516370a0823160e01b8152736def55d2e18486b9ddfaa075bc4e4ee0b28c154590632e1a7d4d9082906370a0823190612f6d903090600401613d84565b60206040518083038186803b158015612f8557600080fd5b505afa158015612f99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fbd9190613bab565b6040518263ffffffff1660e01b8152600401612fd99190613d7b565b600060405180830381600087803b158015612ff357600080fd5b505af1158015613007573d6000803e3d6000fd5b50506040516370a0823160e01b8152600092506000805160206144568339815191529150630d2680e960e11b9082906370a082319061304a903090600401613d84565b60206040518083038186803b15801561306257600080fd5b505afa158015613076573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061309a9190613bab565b6000806040516024016130af939291906142e6565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516130ed9190613d44565b6000604051808303816000865af19150503d806000811461312a576040519150601f19603f3d011682016040523d82523d6000602084013e61312f565b606091505b50509050806131505760405162461bcd60e51b815260040161036490614134565b6040516370a0823160e01b8152611c8b90839060008051602061433e833981519152906370a08231906124b3903090600401613d84565b6000611b2a611d8584603a5485613303565b6020808201516040808401516060850151608086015160a08701516101a088015194516000976131cb97969101613ce7565b6040516020818303038152906040528051906020012060001c90505b42811015611d6e57806040516020016132009190613d7b565b6040516020818303038152906040528051906020012060001c90506131e7565b600080600083516041141561324b5750505060208101516040820151606083015160001a9190613276565b835160401415613276575050506020810151604082015160ff81901c601b0191906001600160ff1b03165b9193909250565b6000613287613554565b82604051602001808061190160f01b81525060020183815260200182815260200192505050604051602081830303815290604052805190602001209050919050565b6000611c8b6132d88585611b0c565b6108066132fd6132f33a603b5461263390919063ffffffff16565b620753009061255b565b3a613582565b6000611c8b6133128585611b0c565b6108066132fd61332d3a603b5461263390919063ffffffff16565b620aae609061255b565b600061338c826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166135a99092919063ffffffff16565b805190915015610b96578080602001905160208110156133ab57600080fd5b5051610b965760405162461bcd60e51b815260040180806020018281038252602a815260200180614497602a913960400191505060405180910390fd5b3b151590565b60006fa2a8918ca85bafe22016d0b997e4df60600160ff1b038211156134455760405162461bcd60e51b815260040180806020018281038252602281526020018061439e6022913960400191505060405180910390fd5b8360ff16601b148061345a57508360ff16601c145b6134955760405162461bcd60e51b81526004018080602001828103825260228152602001806144346022913960400191505060405180910390fd5b600060018686868660405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156134f1573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166128c6576040805162461bcd60e51b815260206004820152601860248201527745434453413a20696e76616c6964207369676e617475726560401b604482015290519081900360640190fd5b600061357d6000805160206144c18339815191526135706135b8565b6135786135be565b6135c4565b905090565b6000611b2a670de0b6b3a7640000611b2460395461080c3a886125b390919063ffffffff16565b6060611c8b8484600085613626565b60015490565b60025490565b60008383836135d16127aa565b3060405160200180868152602001858152602001848152602001838152602001826001600160a01b03168152602001955050505050506040516020818303038152906040528051906020012090509392505050565b6060824710156136675760405162461bcd60e51b81526004018080602001828103825260268152602001806143c06026913960400191505060405180910390fd5b613670856133e8565b6136c1576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600080866001600160a01b031685876040518082805190602001908083835b602083106136ff5780518252601f1990920191602091820191016136e0565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114613761576040519150601f19603f3d011682016040523d82523d6000602084013e613766565b606091505b50915091506121b282828660608315613780575081611b2a565b8251156137905782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156137da5781810151838201526020016137c2565b50505050905090810190601f1680156138075780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b60405180610140016040528060006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200160006001600160a01b0316815260200160006001600160a01b03168152602001606081526020016000815260200160008152602001600081525090565b60405180604001604052806002906020820280368337509192915050565b80356001600160a01b0381168114611d6e57600080fd5b600082601f8301126138d1578081fd5b81356001600160401b03808211156138e557fe5b604051601f8301601f19168101602001828111828210171561390357fe5b60405282815284830160200186101561391a578384fd5b82602086016020830137918201602001929092529392505050565b600060208284031215613946578081fd5b611b2a826138aa565b60008060408385031215613961578081fd5b61396a836138aa565b9150613978602084016138aa565b90509250929050565b6000806000806000806000806000806101408b8d0312156139a0578586fd5b6139a98b6138aa565b99506139b760208c016138aa565b98506139c560408c016138aa565b975060608b0135965060808b0135955060a08b013594506139e860c08c016138aa565b935060e08b013592506101008b01356001600160401b0380821115613a0b578384fd5b613a178e838f016138c1565b93506101208d0135915080821115613a2d578283fd5b50613a3a8d828e016138c1565b9150509295989b9194979a5092959850565b600080600080600060a08688031215613a63578081fd5b613a6c866138aa565b9450613a7a602087016138aa565b9350604086013592506060860135915060808601356001600160401b03811115613aa2578182fd5b613aae888289016138c1565b9150509295509295909350565b600080600080600080600060e0888a031215613ad5578283fd5b613ade886138aa565b9650613aec602089016138aa565b9550604088013594506060880135935060808801356001600160401b0380821115613b15578485fd5b613b218b838c016138c1565b945060a08a0135915080821115613b36578384fd5b613b428b838c016138c1565b935060c08a0135915080821115613b57578283fd5b50613b648a828b016138c1565b91505092959891949750929550565b600060208284031215613b84578081fd5b8135611b2a8161432f565b600060208284031215613ba0578081fd5b8151611b2a8161432f565b600060208284031215613bbc578081fd5b5051919050565b60008060408385031215613bd5578182fd5b8235915060208301356001600160401b03811115613bf1578182fd5b613bfd858286016138c1565b9150509250929050565b60008060008060808587031215613c1c578384fd5b5050823594602084013594506040840135936060013592509050565b60008151808452613c508160208601602086016142ff565b601f01601f19169290920160200192915050565b606093841b6001600160601b0319908116825260e89390931b6001600160e81b0319166014820152921b166017820152602b0190565b6001600160601b0319606096871b811682526001600160e81b031960e896871b8116601484015294871b811660178301529290941b909216602b840152921b909116602e82015260420190565b600060018060601b03198860601b1682528660148301528560348301528460548301528351613d1d8160748501602088016142ff565b835190830190613d348160748401602088016142ff565b0160740198975050505050505050565b60008251613d568184602087016142ff565b9190910192915050565b61190160f01b81526002810192909252602282015260420190565b90815260200190565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b039889168152969097166020870152604086019490945260608501929092521515608084015260ff1660a083015260c082015260e08101919091526101000190565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b6001600160a01b0385811682526020820185905283166040820152608060608201819052600090612dfc90830184613c38565b60608101818460005b6002811015613ea1578151835260209283019290910190600101613e82565b50505060ff831660408301529392505050565b9586526001600160a01b03948516602087015292909316604085015260608401526080830191909152151560a082015260c00190565b9687526001600160a01b03958616602088015260408701949094529184166060860152909216608084015260a083019190915260c082015260e00190565b9485526020850193909352604084019190915260608301526001600160a01b0316608082015260a00190565b600085825284602083015283604083015260806060830152612dfc6080830184613c38565b600060408252613f8c6040830185613c38565b90508260208301529392505050565b600f95860b81529390940b6020840152604083019190915260608201526001600160a01b03909116608082015260a00190565b918252602082015260400190565b60006060820185835260208581850152606060408501528185518084526080860191508287019350845b8181101561402257845183529383019391830191600101614006565b509098975050505050505050565b600f93840b81529190920b6020820152604081019190915260600190565b60ff9586168152938516602085015260408401929092529092166060820152901515608082015260a00190565b6020808252600b908201526a21676f7665726e616e636560a81b604082015260600190565b60208082526010908201526f085cdd5c1c1bdc9d19590b585cdcd95d60821b604082015260600190565b6020808252601b908201527a6572726f722073656e64696e6720746f20676f7665726e616e636560281b604082015260600190565b6020808252601b908201527a31b0b73737ba10393ab7103ab833b930b23290333ab731ba34b7b760291b604082015260600190565b60208082526006908201526521637572766560d01b604082015260600190565b60208082526010908201526f21617070726f7665642d6d6f64756c6560801b604082015260600190565b6020808252600a9082015269217369676e617475726560b01b604082015260600190565b6020808252601b908201527a195c9c9bdc881cd95b991a5b99c81d1bc81cdd1c985d1959da5cdd602a1b604082015260600190565b60208082526009908201526821646561646c696e6560b81b604082015260600190565b602080825260119082015270746f6f206d7563682063616c6c6461746160781b604082015260600190565b600060208252825160a0602084015261424160c0840182613c38565b905060018060a01b0360208501511660408401526040840151606084015260608401516080840152608084015160a08401528091505092915050565b81516001600160a01b03908116825260208084015182169083015260408084015162ffffff16908301526060808401518216908301526080808401519083015260a0838101519083015260c0808401519083015260e09283015116918101919091526101000190565b92835260ff918216602084015216604082015260600190565b60005b8381101561431a578181015183820152602001614302565b83811115614329576000848401525b50505050565b80151581146110b057600080fdfe000000000000000000000000dbf31df14b66535af65aac99c32e9ea844e14501000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8c89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc645434453413a20696e76616c6964207369676e6174757265202773272076616c7565416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c0000000000000000000000002f2a2543b76a4166549f7aab2e75bef0aefc5b0f496e697469616c697a61626c653a20636f6e747261637420697320616c726561647920696e697469616c697a656445434453413a20696e76616c6964207369676e6174757265202776272076616c75650000000000000000000000003e01dd8a5e1fb3481f0f589056b428fc308af0fb536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565648b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e6365a2646970667358221220129449c27cb9daa9f4831846147512f75dc89ad5c3f4ce4fcb2f8dc2a5f4be2e64736f6c63430007060033

Deployed ByteCode Sourcemap

1680:20992:35:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3488:46;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;3044:28;;;;;;;;;;;;;:::i;1849:25::-;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;4020:264::-;;;;;;;;;;-1:-1:-1;4020:264:35;;;;;:::i;:::-;;:::i;:::-;;20873:259;;;;;;:::i;:::-;;:::i;14357:1921::-;;;;;;;;;;-1:-1:-1;14357:1921:35;;;;;:::i;:::-;;:::i;5345:1303::-;;;;;;;;;;-1:-1:-1;5345:1303:35;;;;;:::i;:::-;;:::i;1820:25::-;;;;;;;;;;;;;:::i;21136:731::-;;;;;;:::i;:::-;;:::i;3587:41::-;;;;;;;;;;-1:-1:-1;3587:41:35;;;;;:::i;:::-;;:::i;3457:27::-;;;;;;;;;;;;;:::i;3876:140::-;;;;;;;;;;-1:-1:-1;3876:140:35;;;;;:::i;:::-;;:::i;3538:45::-;;;;;;;;;;;;;:::i;3732:140::-;;;;;;;;;;-1:-1:-1;3732:140:35;;;;;:::i;:::-;;:::i;11360:761::-;;;;;;;;;;;;;:::i;1798:18::-;;;;;;;;;;;;;:::i;17387:3482::-;;;;;;;;;;-1:-1:-1;17387:3482:35;;;;;:::i;:::-;;:::i;21871:799::-;;;;;;;;;;-1:-1:-1;21871:799:35;;;;;:::i;:::-;;:::i;5043:298::-;;;;;;;;;;-1:-1:-1;5043:298:35;;;;;:::i;:::-;;:::i;3488:46::-;3529:5;3488:46;:::o;3044:28::-;;;;:::o;1849:25::-;;;-1:-1:-1;;;;;1849:25:35;;:::o;4020:264::-;3210:28;4152:16;;;4187:9;;4179:49;;;;-1:-1:-1;;;4179:49:35;;;;;;;:::i;:::-;;;;;;;;;4251:23;;;;-1:-1:-1;4243:37:35:o;20873:259::-;20956:20;20999:70;21015:6;21023:45;21037:30;21048:9;21059:7;;21037:10;:30::i;:::-;21023:9;;:13;:45::i;:::-;20999:15;:70::i;:::-;21075:52;;-1:-1:-1;;;21075:52:35;;20984:85;;-1:-1:-1;1909:42:35;;21075:25;;:52;;21101:11;;20984:85;;21075:52;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;20873:259;;;;:::o;14357:1921::-;14607:17;14659:3;14640:8;:22;;14632:52;;;;-1:-1:-1;;;14632:52:35;;;;;;;:::i;:::-;14690:18;14711:9;14690:30;;14726:24;;:::i;:::-;-1:-1:-1;;;;;14781:14:35;;-1:-1:-1;;;;;;;;;;;14781:14:35;;:32;;-1:-1:-1;;;;;;14799:14:35;;-1:-1:-1;;;;;;;;;;;14799:14:35;14781:32;:51;;;-1:-1:-1;;;;;;14817:15:35;;2924:42;14817:15;14781:51;:71;;;-1:-1:-1;;;;;;14836:16:35;;-1:-1:-1;;;;;;;;;;;14836:16:35;14781:71;:97;;;-1:-1:-1;;;;;;14856:22:35;;;14781:97;14764:150;;;;-1:-1:-1;;;14764:150:35;;;;;;;:::i;:::-;14931:268;;;;;;;;14956:2;-1:-1:-1;;;;;14931:268:35;;;;;14975:5;-1:-1:-1;;;;;14931:268:35;;;;;15021:5;14931:268;;;;14998:6;14931:268;;;;15044:6;-1:-1:-1;;;;;14931:268:35;;;;;15073:11;-1:-1:-1;;;;;14931:268:35;;;;;15100:4;14931:268;;;;15122:1;14931:268;;;;15146:1;14931:268;;;;15166:24;:22;:24::i;:::-;14931:268;;15211:11;;14922:277;;-1:-1:-1;15211:15:35;15207:66;;15257:4;15246:27;;;;;;;;;;;;:::i;:::-;15229:13;;;15228:45;15207:66;15285:14;15302:23;15318:6;15302:15;:23::i;:::-;15285:40;;1909:42;-1:-1:-1;;;;;15353:25:35;;15407:6;:9;;;15418:6;:12;;;15432:6;:13;;;15447:6;:11;;;15396:63;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;15386:74;;;;;;15468:12;15488:5;15501:9;15353:163;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;15332:18;;;:184;-1:-1:-1;;;;;15542:14:35;;-1:-1:-1;;;;;;;;;;;15542:14:35;:398;;-1:-1:-1;;;;;15606:22:35;;;:334;;-1:-1:-1;;;;;15718:14:35;;-1:-1:-1;;;;;;;;;;;15718:14:35;:222;;-1:-1:-1;;;;;15817:15:35;;2924:42;15817:15;:123;;15904:36;15918:6;:18;;;15938:1;15904:13;:36::i;:::-;15817:123;;;15843:50;15851:41;15870:6;:18;;;15890:1;15851:18;:41::i;:::-;15843:7;:50::i;:::-;15718:222;;;15743:63;15750:6;:13;;;15765:36;15779:6;:18;;;15799:1;15765:13;:36::i;:::-;15803:2;15743:6;:63::i;:::-;15606:334;;;15639:68;15651:6;:13;;;15666:36;15680:6;:18;;;15700:1;15666:13;:36::i;:::-;15704:2;15639:11;:68::i;:::-;15542:398;;;15559:44;15566:36;15580:6;:18;;;15600:1;15566:13;:36::i;:::-;15559:6;:44::i;:::-;15530:410;-1:-1:-1;;;;;;15964:14:35;;-1:-1:-1;;;;;;;;;;;15964:14:35;;;;:40;;-1:-1:-1;;;;;;15982:22:35;;;;15964:40;15960:88;;;16006:42;-1:-1:-1;;;;;16006:27:35;;16034:2;16038:9;16006:27;:42::i;:::-;16068:9;-1:-1:-1;;;;;16068:18:35;:199;16096:163;16116:100;16203:12;;16116:82;16186:11;16116:65;16166:6;:14;;;16116:45;3529:5;16116:25;16131:9;16116:10;;:14;:25::i;:::-;:29;;:45::i;:65::-;:69;;:82::i;:100::-;16228:21;16096:8;:163::i;:::-;16068:199;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14357:1921;;;;;;;;;;;;;;;:::o;5345:1303::-;1512:13:3;;;;;;;;:33;;;1529:16;:14;:16::i;:::-;1512:50;;;-1:-1:-1;1550:12:3;;;;1549:13;1512:50;1504:109;;;;-1:-1:-1;;;1504:109:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1624:19;1647:13;;;;;;1646:14;1670:98;;;;1704:13;:20;;-1:-1:-1;;;;1704:20:3;;;;;1738:19;1720:4;1738:19;;;1670:98;5446:5:35::1;5432:3;:20:::0;5476:4:::1;5458:7;:23:::0;5511:4:::1;5487:13;:29:::0;5522:10:::1;:24:::0;;-1:-1:-1;;;;;5522:24:35;;::::1;-1:-1:-1::0;;;;;;5522:24:35;;::::1;;::::0;;;5552:10:::1;:24:::0;;;;::::1;::::0;;;::::1;::::0;;;::::1;::::0;;5597:26:::1;5605:7;5618:4;5597:20;:26::i;:::-;5582:12;:41:::0;5693:52:::1;-1:-1:-1::0;;;;;;;;;;;;;;;;;;;;;;;;;;;5693:26:35::1;:52::i;:::-;5751:50;-1:-1:-1::0;;;;;;;;;;;;;;;;;;;;;;;;;;;5751:24:35::1;:50::i;:::-;5807:53;-1:-1:-1::0;;;;;;;;;;;2560:42:35::1;-1:-1:-1::0;;;;;5807:24:35::1;:53::i;:::-;5866:55;-1:-1:-1::0;;;;;;;;;;;2707:42:35::1;-1:-1:-1::0;;;;;5866:28:35::1;:55::i;:::-;5927:52;-1:-1:-1::0;;;;;;;;;;;1983:42:35::1;-1:-1:-1::0;;;;;5927:24:35::1;:52::i;:::-;5985;-1:-1:-1::0;;;;;;;;;;;1983:42:35::1;-1:-1:-1::0;;;;;5985:24:35::1;:52::i;:::-;-1:-1:-1::0;;;;;;;;;;;6279:17:35::1;-1:-1:-1::0;;;;;;;;;;;6330:12:35::1;:10;:12::i;:::-;-1:-1:-1::0;;;;;;;;;;;6154:210:35::1;;;;;;;;;;;;:::i;:::-;;::::0;;-1:-1:-1;;6154:210:35;;::::1;::::0;;;;;;6137:233;;6154:210:::1;6137:233:::0;;::::1;::::0;6106:28:::1;:264:::0;-1:-1:-1;;;;;;;;;;;6550:18:35::1;-1:-1:-1::0;;;;;;;;;;;6602:12:35::1;:10;:12::i;:::-;2924:42;6425:212;;;;;;;;;;;;:::i;:::-;;::::0;;-1:-1:-1;;6425:212:35;;::::1;::::0;;;;;;6408:235;;6425:212:::1;6408:235:::0;;::::1;::::0;6376:29:::1;:267:::0;1790:66:3;;;;1840:5;1824:21;;-1:-1:-1;;1824:21:3;;;1790:66;5345:1303:35;;;:::o;1820:25::-;;;-1:-1:-1;;;;;1820:25:35;;:::o;21136:731::-;21293:20;-1:-1:-1;;;;;21329:13:35;;-1:-1:-1;;;;;;;;;;;21329:13:35;;:30;;-1:-1:-1;;;;;;21346:13:35;;-1:-1:-1;;;;;;;;;;;21346:13:35;21329:30;:49;;;-1:-1:-1;;;;;;21363:15:35;;-1:-1:-1;;;;;;;;;;;21363:15:35;21329:49;:74;;;-1:-1:-1;;;;;;21382:21:35;;;21329:74;21321:103;;;;-1:-1:-1;;;21321:103:35;;;;;;;:::i;:::-;-1:-1:-1;;;;;21434:21:35;;;21430:88;;21457:61;;-1:-1:-1;;;21457:61:35;;-1:-1:-1;;;;;21457:26:35;;;;;:61;;21484:10;;21504:4;;21511:6;;21457:61;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;21430:88;-1:-1:-1;;;;;21539:13:35;;-1:-1:-1;;;;;;;;;;;21539:13:35;:265;;-1:-1:-1;;;;;21607:13:35;;-1:-1:-1;;;;;;;;;;;21607:13:35;:197;;-1:-1:-1;;;;;21695:15:35;;-1:-1:-1;;;;;;;;;;;21695:15:35;:109;;21734:70;21750:6;21758:45;21772:30;21783:9;21794:7;;21772:10;:30::i;21734:70::-;21695:109;;;21719:6;21695:109;21607:197;;;21629:57;21638:6;21646:39;21657:27;21668:6;21676:7;;21657:10;:27::i;:::-;21646:6;;:10;:39::i;:::-;21629:8;:57::i;:::-;21539:265;;;21555:49;21564:39;21575:27;21586:6;21594:7;;21575:10;:27::i;:::-;21564:6;;:10;:39::i;:::-;21555:8;:49::i;:::-;21810:52;;-1:-1:-1;;;21810:52:35;;21524:280;;-1:-1:-1;1909:42:35;;21810:25;;:52;;21836:11;;21524:280;;21810:52;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;21136:731;;;;;;;:::o;3587:41::-;;;;;;;;;;;;;:::o;3457:27::-;;;;:::o;3876:140::-;3955:10;;-1:-1:-1;;;;;3955:10:35;3941;:24;3933:48;;;;-1:-1:-1;;;3933:48:35;;;;;;;:::i;:::-;3987:10;:24;;-1:-1:-1;;;;;;3987:24:35;-1:-1:-1;;;;;3987:24:35;;;;;;;;;;3876:140::o;3538:45::-;3578:5;3538:45;:::o;3732:140::-;3811:10;;-1:-1:-1;;;;;3811:10:35;3797;:24;3789:48;;;;-1:-1:-1;;;3789:48:35;;;;;;;:::i;:::-;3843:10;:24;;-1:-1:-1;;;;;;3843:24:35;-1:-1:-1;;;;;3843:24:35;;;;;;;;;;3732:140::o;11360:761::-;11389:7;:5;:7::i;:::-;11409:39;;-1:-1:-1;;;11409:39:35;;11402:47;;-1:-1:-1;;;;;;;;;;;2338:42:35;11409:24;;:39;;11442:4;;11409:39;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;11402:47::-;;11455:7;:5;:7::i;:::-;-1:-1:-1;11486:21:35;3376:7;11517:21;;11513:604;;;11548:14;3376:7;11565;:21;11548:38;;11594:20;11617:33;11628:6;11636:13;;11617:10;:33::i;:::-;11730:10;;11594:56;;-1:-1:-1;11658:12:35;;-1:-1:-1;;;;;11730:10:35;;11594:56;11814:9;11764:65;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;11750:79;;;;;11845:7;11837:47;;;;-1:-1:-1;;;11837:47:35;;;;;;;:::i;:::-;11944:10;;-1:-1:-1;;;;;11944:10:35;;12009:24;:6;12020:12;12009:10;:24::i;:::-;12040:9;11978:77;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;11964:91;;;;;12071:7;12063:47;;;;-1:-1:-1;;;12063:47:35;;;;;;;:::i;:::-;11513:604;;;;;;11360:761;:::o;1798:18::-;;;;:::o;17387:3482::-;17579:20;17634:3;17615:8;:22;;17607:52;;;;-1:-1:-1;;;17607:52:35;;;;;;;:::i;:::-;17665:24;17692:338;;;;;;;;17715:2;-1:-1:-1;;;;;17692:338:35;;;;;17732:5;-1:-1:-1;;;;;17692:338:35;;;;;17753:6;17692:338;;;;17777:8;17692:338;;;;17818:1;17692:338;;;;17799:4;17692:338;;;;18004:1;17692:338;;;;17838:1;17692:338;;;;17979:9;17692:338;;18022:1;17692:338;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;17665:365;-1:-1:-1;18061:24:35;:22;:24::i;:::-;18044:14;;;:41;18097:11;;;;:18;:22;18093:80;;18150:6;:11;;;18139:34;;;;;;;;;;;;:::i;:::-;18122:13;;;18121:52;18093:80;18211:6;:15;;;18193;:33;18185:55;;;;-1:-1:-1;;;18185:55:35;;;;;;;:::i;:::-;18251:12;;;;-1:-1:-1;;;;;18251:20:35;-1:-1:-1;;;;;;;;;;;18251:20:35;18247:2314;;;-1:-1:-1;;;;;18296:10:35;;;;;;;:6;:10;;;;;;;;;18281:12;;;:25;18321:9;;18314:17;;;;;;;:19;;;;;;18410:28;;18371:95;;18385:62;;18281:6;18385:24;:62::i;:::-;18449:6;:16;;;18371:13;:95::i;:::-;-1:-1:-1;;;;;18358:108:35;:6;:9;;;-1:-1:-1;;;;;18358:108:35;;18341:155;;;;-1:-1:-1;;;18341:155:35;;;;;;;:::i;:::-;18561:6;:12;;;-1:-1:-1;;;;;18554:33:35;;18588:6;:9;;;18607:4;18614:6;:13;;;18554:74;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;18653:41;18662:31;18676:6;:13;;;18691:1;18662:13;:31::i;18653:41::-;18638:56;;18247:2314;;;-1:-1:-1;;;;;18719:14:35;;2924:42;18719:14;18715:1846;;;-1:-1:-1;;;;;18758:10:35;;;;;;:6;:10;;;;;;;;;18743:12;;;:25;;;18776:10;;;;;:12;;;;;;18865:29;;18826:96;;18840:63;;18743:6;18840:24;:63::i;18826:96::-;-1:-1:-1;;;;;18813:109:35;:6;:9;;;-1:-1:-1;;;;;18813:109:35;;18796:156;;;;-1:-1:-1;;;18796:156:35;;;;;;;:::i;:::-;19021:6;:12;;;-1:-1:-1;;;;;19014:33:35;;19048:6;:9;;;19067:4;19074:6;:13;;;19014:74;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;19113:47;19132:24;19142:6;:13;;;19132:9;:24::i;:::-;19158:1;19113:18;:47::i;18715:1846::-;19185:12;;;;-1:-1:-1;;;;;19185:22:35;-1:-1:-1;;;;;;;;;;;19185:22:35;19181:1380;;;19257:12;;;;19278:9;;19242:46;;-1:-1:-1;;;19242:46:35;;-1:-1:-1;;;;;19242:35:35;;;;;;:46;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;19227:12;;;:61;19317:24;19227:6;19317:16;:24::i;:::-;19298:16;;;:43;19433:16;;;;19400:50;;:32;:50::i;:::-;19388:8;;;19367:83;;;19378:8;;;19367:83;;;;;;;19368:8;;;19367:83;;;-1:-1:-1;19475:12:35;;;19507:9;;19553:12;;;;19577:16;;;;-1:-1:-1;19460:219:35;-1:-1:-1;;;19460:219:35;;-1:-1:-1;;;;;19460:35:35;;;;;;:219;;19507:9;;19536:4;;19605;;19367:83;;;19460:219;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;19712:12:35;;;;19739:9;;19765:13;;;;;19705:74;;-1:-1:-1;;;19705:74:35;;-1:-1:-1;;;;;19705:33:35;;;;-1:-1:-1;19705:33:35;;:74;;19739:9;19758:4;;19705:74;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;19810:31;19824:6;:13;;;19839:1;19810:13;:31::i;19181:1380::-;19858:12;;;;-1:-1:-1;;;;;19858:20:35;-1:-1:-1;;;;;;;;;;;19858:20:35;19854:707;;;19928:12;;;;19949:9;;19913:46;;-1:-1:-1;;;19913:46:35;;-1:-1:-1;;;;;19913:35:35;;;;;;:46;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;19898:12;;;:61;19988:24;19898:6;19988:16;:24::i;:::-;19969:16;;;:43;20104:16;;;;20071:50;;:32;:50::i;:::-;20059:8;;;20038:83;;;20049:8;;;20038:83;;;;;;;20039:8;;;20038:83;;;-1:-1:-1;20146:12:35;;;20178:9;;-1:-1:-1;20224:13:35;;;;20249:16;;;;20131:204;;-1:-1:-1;;;20131:204:35;;-1:-1:-1;;;;;20131:35:35;;;;;;:204;;20178:9;;20207:4;;20224:13;;20249:16;20038:83;;20131:204;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;20368:12:35;;;;20395:9;;20421:13;;;;;20361:74;;-1:-1:-1;;;20361:74:35;;-1:-1:-1;;;;;20361:33:35;;;;-1:-1:-1;20361:33:35;;:74;;20395:9;20414:4;;20361:74;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;20466:56;20480:38;20489:6;:13;;;20504:6;:13;;;20480:8;:38::i;:::-;20520:1;20466:13;:56::i;19854:707::-;20535:26;;-1:-1:-1;;;20535:26:35;;;;;;;:::i;19854:707::-;20601:18;;;;20575:59;;-1:-1:-1;;;20575:59:35;;1909:42;;20575:25;;:59;;20601:18;20621:12;;20575:59;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;20654:9;-1:-1:-1;;;;;20654:18:35;:204;20682:168;20702:105;20794:12;;20702:87;20777:11;20702:70;20757:6;:14;;;20702:50;3578:5;20702:31;20723:9;20702:16;;;;;:20;:31::i;20682:168::-;20654:204;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;17387:3482;;;;;;;;;;:::o;21871:799::-;22125:24;22152:223;;;;;;;;22175:2;-1:-1:-1;;;;;22152:223:35;;;;;22192:5;-1:-1:-1;;;;;22152:223:35;;;;;22234:5;22152:223;;;;22213:6;22152:223;;;;22255:6;-1:-1:-1;;;;;22152:223:35;;;;;22282:11;-1:-1:-1;;;;;22152:223:35;;;;;22307:4;22152:223;;;;22327:1;22152:223;;;;22349:1;22152:223;;;;22367:1;22152:223;;;22125:250;;22381:14;22398:23;22414:6;22398:15;:23::i;:::-;22381:40;;22427:21;1909:42;-1:-1:-1;;;;;22451:25:35;;22505:6;:9;;;22516:6;:12;;;22530:6;:13;;;22545:6;:11;;;22494:63;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;22484:74;;;;;;22566:12;22586:5;22599:9;22451:163;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;22427:187;-1:-1:-1;22620:45:35;-1:-1:-1;;;;;22620:26:35;;22647:2;22427:187;22620:26;:45::i;:::-;21871:799;;;;;;;;;;;;;:::o;5043:298::-;5186:10;;-1:-1:-1;;;;;5186:10:35;5200;5186:24;5178:48;;;;-1:-1:-1;;;5178:48:35;;;;;;;:::i;:::-;5232:13;:30;;;;5268:3;:10;;;;5284:7;:18;5308:12;:28;5043:298::o;6652:131::-;6717:14;6748:30;6769:7;6748:8;:1;6754;6748:5;:8::i;:::-;:12;;:30::i;:::-;6739:39;6652:131;-1:-1:-1;;;6652:131:35:o;3136:155:12:-;3194:7;3226:1;3221;:6;;3213:49;;;;;-1:-1:-1;;;3213:49:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;3279:5:12;;;3136:155;;;;;:::o;10401:537:35:-;10554:264;;;;;;;;2196:42;10554:264;;-1:-1:-1;;;;;;;;;;;10554:264:35;;;;3000:3;10554:264;;;;10683:4;10554:264;;;;10724:1;10706:15;:19;10554:264;;;;;;;;;;;;;;;;10478:17;10554:264;;;;;;10836:65;;-1:-1:-1;;;10836:65:35;;1983:42;;10836:38;;10743:8;;10836:65;;10554:264;;10836:65;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;10824:77;;10914:19;10923:9;10914:8;:19::i;:::-;10907:26;10401:537;-1:-1:-1;;;;10401:537:35:o;4288:634::-;4382:1;4375:8;;4657:261;4681:2;4677:1;:6;4657:261;;;4751:15;;4704:12;4781:131;4805:3;4801:1;:7;:43;;;;-1:-1:-1;;;4814:16:35;;4813:25;;4812:32;;4801:43;4781:131;;;4882:4;4869:9;;;4868:18;:23;4864:39;;4901:2;4893:10;;;;4864:39;4851:1;4846:6;4781:131;;;-1:-1:-1;;4690:4:35;4685:9;4657:261;;;;4288:634;;;;:::o;13823:530::-;13897:14;13919;13936:393;14001:142;14155:6;:12;;;14179:6;:13;;;14204:6;:18;;;14234:6;:13;;;14259:6;:12;;;14293:6;:11;;;14283:22;;;;;;13979:336;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;13960:363;;;;;;13936:16;:393::i;:::-;13919:410;-1:-1:-1;;13823:530:35;;;;:::o;12324:169::-;12408:14;12439:49;12452:35;12461:8;12471:3;;12476:10;12452:8;:35::i;:::-;12439:8;;:12;:49::i;12497:179::-;12586:14;12617:54;12630:40;12644:8;12654:3;;12659:10;12630:13;:40::i;7072:454::-;7125:17;7150:25;;:::i;:::-;7181:21;;;7239:67;;7189:1;;-1:-1:-1;;;;;;;;;;;2410:42:35;-1:-1:-1;;;7262:31:35;7239:67;;7181:7;;7189:1;;7239:67;;;:::i;:::-;;;;-1:-1:-1;;7239:67:35;;;;;;;;;;;;;;-1:-1:-1;;;;;7239:67:35;-1:-1:-1;;;;;;7239:67:35;;;;;;;;;;7227:80;;;;7239:67;7227:80;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7208:99;;;7321:7;7313:26;;;;-1:-1:-1;;;7313:26:35;;;;;;;:::i;:::-;7368:41;;-1:-1:-1;;;7368:41:35;;2707:42;;7345:22;;-1:-1:-1;;;;;;;;;;;2634:42:35;7368:26;;:41;;7403:4;;7368:41;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;7345:65;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;7462:40:35;;-1:-1:-1;;;7462:40:35;;2781:42;;-1:-1:-1;7428:30:35;;-1:-1:-1;7459:1:35;;2707:42;;7462:25;;:40;;7496:4;;7462:40;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;7504:16;;;7518:1;7504:16;;;;;;;;;-1:-1:-1;;;;;;7428:93:35;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;7530:536::-;7627:17;7652:20;7675:16;7682:8;7675:6;:16::i;:::-;7652:39;;7697:17;-1:-1:-1;;;;;;;;;;;3000:3:35;2196:42;3037:3;-1:-1:-1;;;;;;;;;;;7717:60:35;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;7717:60:35;;;;;;7828:175;;;;;;;;-1:-1:-1;;;;;7828:175:35;;7717:60;7828:175;;;7915:1;7897:15;:19;7828:175;;;;;;;;;;;;;;;;8021:40;;-1:-1:-1;;;8021:40:35;;7717:60;;-1:-1:-1;1983:42:35;;8021:32;;:40;;7717:60;;8021:40;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;8009:52;7530:536;-1:-1:-1;;;;;;;7530:536:35:o;8329:676::-;8431:17;8456:21;8480:16;8487:8;8480:6;:16::i;:::-;8553:269;;;;;;;;-1:-1:-1;;;;;;;;;;;8553:269:35;;2196:42;8553:269;;;;3000:3;8553:269;;;;8682:4;8553:269;;;;8723:1;8705:15;:19;8553:269;;;;;;;;;;;;;;;;8502:48;8553:269;;;;8840:46;;-1:-1:-1;;;8840:46:35;;8456:40;;-1:-1:-1;8553:269:35;1983:42;;8840:38;;:46;;8553:269;;8840:46;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;8940:32;;-1:-1:-1;;;8940:32:35;;8828:58;;-1:-1:-1;8929:3:35;;2196:42;;8940:21;;:32;;8828:58;;8940:32;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;8978:22:35;;-1:-1:-1;;;;;8978:11:35;;;-1:-1:-1;8978:22:35;;;;;-1:-1:-1;8990:9:35;;8978:22;;;;8990:9;8978:11;:22;;;;;;;;;;;;;;;;;;;;;8329:676;;;;;;;;:::o;6787:281::-;6884:37;;-1:-1:-1;;;6884:37:35;;6837:17;;;;-1:-1:-1;;;;;;;;;;;2266:42:35;6884:22;;:37;;6915:4;;6884:37;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;6927:64;;-1:-1:-1;;;6927:64:35;;6862:59;;-1:-1:-1;;;;;;;;;;;;2410:42:35;6927:32;;:64;;6960:1;;6963;;6966:6;;6960:1;;6985:4;;6927:64;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;7009:37:35;;-1:-1:-1;;;7009:37:35;;:54;;7051:11;;-1:-1:-1;;;;;;;;;;;2266:42:35;7009:22;;:37;;7040:4;;7009:37;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:41;;:54::i;704:175:19:-;813:58;;;-1:-1:-1;;;;;813:58:19;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;813:58:19;-1:-1:-1;;;813:58:19;;;786:86;;806:5;;786:19;:86::i;2690:175:12:-;2748:7;2779:5;;;2802:6;;;;2794:46;;;;;-1:-1:-1;;;2794:46:12;;;;;;;;;;;;-1:-1:-1;;;2794:46:12;;;;;;;;;;;;;;3538:215;3596:7;3619:6;3615:20;;-1:-1:-1;3634:1:12;3627:8;;3615:20;3657:5;;;3661:1;3657;:5;:1;3680:5;;;;;:10;3672:56;;;;-1:-1:-1;;;3672:56:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;399:104:11;457:7;487:1;483;:5;:13;;495:1;483:13;;;-1:-1:-1;491:1:11;;399:104;-1:-1:-1;399:104:11:o;1952:123:3:-;2000:4;2024:44;2062:4;2024:29;:44::i;:::-;2023:45;2016:52;;1952:123;:::o;4217:150:12:-;4275:7;4306:1;4302;:5;4294:44;;;;;-1:-1:-1;;;4294:44:12;;;;;;;;;;;;-1:-1:-1;;;4294:44:12;;;;;;;;;;;;;;;4359:1;4355;:5;;;;;;;4217:150;-1:-1:-1;;;4217:150:12:o;1348:613:19:-;1713:10;;;1712:62;;-1:-1:-1;1729:39:19;;;-1:-1:-1;;;1729:39:19;;1753:4;1729:39;;;;-1:-1:-1;;;;;1729:39:19;;;;;;;;;:15;;;;;;:39;;;;;;;;;;;;;;;:15;:39;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1729:39:19;:44;1712:62;1704:150;;;;-1:-1:-1;;;1704:150:19;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1891:62;;;-1:-1:-1;;;;;1891:62:19;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1891:62:19;-1:-1:-1;;;1891:62:19;;;1864:90;;1884:5;;1864:19;:90::i;4926:113:35:-;5020:9;;5002:33::o;9593:507::-;9663:17;9688;-1:-1:-1;;;;;;;;;;;3037:3:35;2196:42;3000:3;-1:-1:-1;;;;;;;;;;;9708:60:35;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;9708:60:35;;;;;;9819:181;;;;;;;;9875:4;9708:60;9819:181;;;9916:1;9898:15;:19;9819:181;;;;;;;;;;;;;;;;10018:40;;-1:-1:-1;;;10018:40:35;;9708:60;;-1:-1:-1;1983:42:35;;10018:32;;:40;;9708:60;;10018:40;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;10006:52;;10076:19;10085:9;10076:8;:19::i;:::-;10064:31;9593:507;-1:-1:-1;;;;;9593:507:35:o;10104:293::-;10206:39;;-1:-1:-1;;;10206:39:35;;10158:17;;;;-1:-1:-1;;;;;;;;;;;2338:42:35;10206:24;;:39;;10239:4;;10206:39;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;10251:66;;-1:-1:-1;;;10251:66:35;;10183:62;;-1:-1:-1;;;;;;;;;;;;2410:42:35;10251:32;;:66;;10284:1;;10287;;10290:8;;10287:1;;10311:4;;10251:66;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;10335:39:35;;-1:-1:-1;;;10335:39:35;;:57;;10379:12;;-1:-1:-1;;;;;;;;;;;2338:42:35;10335:24;;:39;;10368:4;;10335:39;;;:::i;8070:255::-;8102:17;-1:-1:-1;;;;;;;;;;;8152:3:35;2196:42;8122:41;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;8122:41:35;;;;;;;;;;-1:-1:-1;;;8195:46:35;;8122:41;-1:-1:-1;8169:23:35;;2853:42;;8195:31;;:46;;8122:41;;8233:7;;8195:46;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;8270:50;;-1:-1:-1;;;8270:50:35;;8169:72;;-1:-1:-1;;;;;;;;;;;;2410:42:35;8270:27;;:50;;8298:1;;8301;;8169:72;;8270:50;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;8247:20;:73;-1:-1:-1;;8070:255:35:o;10942:365::-;11022:37;;-1:-1:-1;;;11022:37:35;;10977:17;;;;-1:-1:-1;;;;;;;;;;;2266:42:35;11022:22;;:37;;11053:4;;11022:37;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;11156:84;;11002:57;;-1:-1:-1;11088:21:35;;11066:19;;2560:42;;-1:-1:-1;;;11179:34:35;11156:84;;11215:1;;11218;;11002:57;;11066:19;;11215:1;;11156:84;;;:::i;:::-;;;;-1:-1:-1;;11156:84:35;;;;;;;;;;;;;;-1:-1:-1;;;;;11156:84:35;-1:-1:-1;;;;;;11156:84:35;;;;;;;;;;11134:112;;;;11156:84;11134:112;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;11115:131:35;;-1:-1:-1;11264:38:35;;-1:-1:-1;11264:21:35;11290:11;11264:25;:38::i;:::-;11252:50;;10942:365;;;;:::o;16700:370::-;16820:14;16916:15;3111:66;16962:15;;16979:6;:9;;;16998:4;17005:6;:12;;;17019:24;17036:6;17019:16;:24::i;:::-;17045:4;16951:99;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;16941:110;;;;;;16870:189;;;;;;;;;:::i;:::-;;;;;;;;;;;;;16853:212;;;;;;16844:221;;16700:370;;;;:::o;1072:740:9:-;1150:7;1211:9;:16;1231:2;1211:22;1207:94;;1249:41;;;-1:-1:-1;;;1249:41:9;;;;;;;;;;;;;;;;;;;;;;;;;;;1207:94;1651:4;1636:20;;1630:27;1696:4;1681:20;;1675:27;1749:4;1734:20;;1728:27;1367:9;1720:36;1783:22;1791:4;1720:36;1630:27;1675;1783:7;:22::i;:::-;1776:29;1072:740;-1:-1:-1;;;;;;1072:740:9:o;12680:173:35:-;12764:14;12795:53;12808:39;12817:8;12827:7;;12836:10;12808:8;:39::i;9009:580::-;9111:39;;-1:-1:-1;;;9111:39:35;;9064:17;;;;-1:-1:-1;;;;;;;;;;;2338:42:35;9111:24;;:39;;9144:4;;9111:39;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9156:45;;-1:-1:-1;;;9156:45:35;;9089:61;;-1:-1:-1;2781:42:35;;9156:32;;:45;;9189:1;;9192:8;;9156:45;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;9231:40:35;;-1:-1:-1;;;9231:40:35;;2707:42;;9207:23;;2707:42;;9231:25;;:40;;9265:4;;9231:40;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9207:65;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;9401:41:35;;-1:-1:-1;;;9401:41:35;;9279:12;;-1:-1:-1;;;;;;;;;;;;2410:42:35;-1:-1:-1;;;;9348:43:35;2410:42;;9401:26;;:41;;9436:4;;9401:41;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9452:1;9463;9316:156;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;9316:156:35;;;;;;;;;;;;;;-1:-1:-1;;;;;9316:156:35;-1:-1:-1;;;;;;9316:156:35;;;;;;;;;;9297:181;;;;9316:156;9297:181;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9278:200;;;9492:7;9484:26;;;;-1:-1:-1;;;9484:26:35;;;;;;;:::i;:::-;9528:39;;-1:-1:-1;;;9528:39:35;;:56;;9572:11;;-1:-1:-1;;;;;;;;;;;2338:42:35;9528:24;;:39;;9561:4;;9528:39;;;:::i;12857:183::-;12946:14;12977:58;12990:44;13004:8;13014:7;;13023:10;12990:13;:44::i;16282:414::-;16439:12;;;;;16453:13;;;;;16468:15;;;;16485:12;;;;16499:11;;;;16512:18;;;;16422:109;;16357:14;;16422:109;;16439:12;16512:18;16422:109;;:::i;:::-;;;;;;;;;;;;;16403:136;;;;;;16388:157;;16379:166;;16551:141;16567:15;16558:6;:24;16551:141;;;16676:6;16659:24;;;;;;;;:::i;:::-;;;;;;;;;;;;;16649:35;;;;;;16641:44;;16632:53;;16551:141;;89:619:88;179:7;194:9;211;239;:16;259:2;239:22;235:469;;;-1:-1:-1;;;316:4:88;301:20;;295:27;357:4;342:20;;336:27;406:4;391:20;;385:27;382:1;377:36;;295:27;280:141;;;437:9;:16;457:2;437:22;433:271;;;-1:-1:-1;;;514:4:88;499:20;;493:27;560:4;545:20;;539:27;677:3;673:12;;;687:2;669:21;;493:27;-1:-1:-1;;;;;580:75:88;478:220;89:619;;;;;:::o;3813:183:1:-;3890:7;3955:20;:18;:20::i;:::-;3977:10;3926:62;;;;;;-1:-1:-1;;;3926:62:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3916:73;;;;;;3909:80;;3813:183;;;:::o;13044:266:35:-;13153:14;13184:121;13273:26;13284:8;13294:4;13273:10;:26::i;:::-;13184:77;13204:43;13217:29;13234:11;13217:12;;:16;;:29;;;;:::i;:::-;3278:4;;13204:12;:43::i;:::-;13249:11;13184:19;:77::i;13314:277::-;13428:14;13459:127;13554:26;13565:8;13575:4;13554:10;:26::i;:::-;13459:83;13479:49;13498:29;13515:11;13498:12;;:16;;:29;;;;:::i;:::-;3329:3;;13479:18;:49::i;2967:751:19:-;3386:23;3412:69;3440:4;3412:69;;;;;;;;;;;;;;;;;3420:5;-1:-1:-1;;;;;3412:27:19;;;:69;;;;;:::i;:::-;3495:17;;3386:95;;-1:-1:-1;3495:21:19;3491:221;;3635:10;3624:30;;;;;;;;;;;;;;;-1:-1:-1;3624:30:19;3616:85;;;;-1:-1:-1;;;3616:85:19;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;729:413:6;1089:20;1127:8;;;729:413::o;1960:1414:9:-;2045:7;-1:-1:-1;;;;;2946:80:9;;;2938:127;;;;-1:-1:-1;;;2938:127:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3083:1;:7;;3088:2;3083:7;:18;;;;3094:1;:7;;3099:2;3094:7;3083:18;3075:65;;;;-1:-1:-1;;;3075:65:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3235:14;3252:24;3262:4;3268:1;3271;3274;3252:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;3252:24:9;;-1:-1:-1;;3252:24:9;;;-1:-1:-1;;;;;;;3294:20:9;;3286:57;;;;;-1:-1:-1;;;3286:57:9;;;;;;;;;;;;-1:-1:-1;;;3286:57:9;;;;;;;;;;;;;;2695:160:1;2748:7;2774:74;-1:-1:-1;;;;;;;;;;;2808:17:1;:15;:17::i;:::-;2827:20;:18;:20::i;:::-;2774:21;:74::i;:::-;2767:81;;2695:160;:::o;12125:195:35:-;12212:14;12243:72;12306:7;12243:50;12272:20;;12243:24;12255:11;12243:7;:11;;:24;;;;:::i;3581:193:20:-;3684:12;3715:52;3737:6;3745:4;3751:1;3754:12;3715:21;:52::i;4558:103:1:-;4642:12;;4558:103;:::o;4900:109::-;4987:15;;4900:109;:::o;2861:327::-;2963:7;3040:8;3066:4;3088:7;3113:13;:11;:13::i;:::-;3152:4;3012:159;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;3012:159:1;;;;;;;;;;;;;;;;;;;;;;;;2989:192;;;;;;2982:199;;2861:327;;;;;:::o;4608:523:20:-;4735:12;4792:5;4767:21;:30;;4759:81;;;;-1:-1:-1;;;4759:81:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4858:18;4869:6;4858:10;:18::i;:::-;4850:60;;;;;-1:-1:-1;;;4850:60:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;4981:12;4995:23;5022:6;-1:-1:-1;;;;;5022:11:20;5042:5;5050:4;5022:33;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5022:33:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4980:75;;;;5072:52;5090:7;5099:10;5111:12;7206;7234:7;7230:580;;;-1:-1:-1;7264:10:20;7257:17;;7230:580;7375:17;;:21;7371:429;;7633:10;7627:17;7693:15;7680:10;7676:2;7672:19;7665:44;7582:145;7772:12;7765:20;;-1:-1:-1;;;7765:20:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;:::o;14:175:140:-;84:20;;-1:-1:-1;;;;;133:31:140;;123:42;;113:2;;179:1;176;169:12;194:694;;291:3;284:4;276:6;272:17;268:27;258:2;;313:5;306;299:20;258:2;340:20;;-1:-1:-1;;;;;409:10:140;;;406:2;;;422:9;406:2;462;456:9;531:2;512:13;;-1:-1:-1;;508:27:140;496:40;;538:4;492:51;558:18;;;578:22;;;555:46;552:2;;;604:9;552:2;631;624:22;655:18;;;692:15;;;709:4;688:26;685:35;-1:-1:-1;682:2:140;;;737:5;730;723:20;682:2;805;798:4;790:6;786:17;779:4;771:6;767:17;754:54;828:15;;;845:4;824:26;817:41;;;;832:6;248:640;-1:-1:-1;;;248:640:140:o;893:198::-;;1005:2;993:9;984:7;980:23;976:32;973:2;;;1026:6;1018;1011:22;973:2;1054:31;1075:9;1054:31;:::i;1096:274::-;;;1225:2;1213:9;1204:7;1200:23;1196:32;1193:2;;;1246:6;1238;1231:22;1193:2;1274:31;1295:9;1274:31;:::i;:::-;1264:41;;1324:40;1360:2;1349:9;1345:18;1324:40;:::i;:::-;1314:50;;1183:187;;;;;:::o;1375:1156::-;;;;;;;;;;;1658:3;1646:9;1637:7;1633:23;1629:33;1626:2;;;1680:6;1672;1665:22;1626:2;1708:31;1729:9;1708:31;:::i;:::-;1698:41;;1758:40;1794:2;1783:9;1779:18;1758:40;:::i;:::-;1748:50;;1817:40;1853:2;1842:9;1838:18;1817:40;:::i;:::-;1807:50;;1904:2;1893:9;1889:18;1876:32;1866:42;;1955:3;1944:9;1940:19;1927:33;1917:43;;2007:3;1996:9;1992:19;1979:33;1969:43;;2031:41;2067:3;2056:9;2052:19;2031:41;:::i;:::-;2021:51;-1:-1:-1;2119:3:140;2104:19;;2091:33;;-1:-1:-1;2175:3:140;2160:19;;2147:33;-1:-1:-1;;;;;2229:14:140;;;2226:2;;;2261:6;2253;2246:22;2226:2;2289:51;2332:7;2323:6;2312:9;2308:22;2289:51;:::i;:::-;2279:61;;2393:3;2382:9;2378:19;2365:33;2349:49;;2423:2;2413:8;2410:16;2407:2;;;2444:6;2436;2429:22;2407:2;;2472:53;2517:7;2506:8;2495:9;2491:24;2472:53;:::i;:::-;2462:63;;;1616:915;;;;;;;;;;;;;:::o;2536:632::-;;;;;;2725:3;2713:9;2704:7;2700:23;2696:33;2693:2;;;2747:6;2739;2732:22;2693:2;2775:31;2796:9;2775:31;:::i;:::-;2765:41;;2825:40;2861:2;2850:9;2846:18;2825:40;:::i;:::-;2815:50;-1:-1:-1;2912:2:140;2897:18;;2884:32;;-1:-1:-1;2963:2:140;2948:18;;2935:32;;-1:-1:-1;3018:3:140;3003:19;;2990:33;-1:-1:-1;;;;;3035:30:140;;3032:2;;;3083:6;3075;3068:22;3032:2;3111:51;3154:7;3145:6;3134:9;3130:22;3111:51;:::i;:::-;3101:61;;;2683:485;;;;;;;;:::o;3173:1075::-;;;;;;;;3414:3;3402:9;3393:7;3389:23;3385:33;3382:2;;;3436:6;3428;3421:22;3382:2;3464:31;3485:9;3464:31;:::i;:::-;3454:41;;3514:40;3550:2;3539:9;3535:18;3514:40;:::i;:::-;3504:50;-1:-1:-1;3601:2:140;3586:18;;3573:32;;-1:-1:-1;3652:2:140;3637:18;;3624:32;;-1:-1:-1;3707:3:140;3692:19;;3679:33;-1:-1:-1;;;;;3761:14:140;;;3758:2;;;3793:6;3785;3778:22;3758:2;3821:51;3864:7;3855:6;3844:9;3840:22;3821:51;:::i;:::-;3811:61;;3925:3;3914:9;3910:19;3897:33;3881:49;;3955:2;3945:8;3942:16;3939:2;;;3976:6;3968;3961:22;3939:2;4004:53;4049:7;4038:8;4027:9;4023:24;4004:53;:::i;:::-;3994:63;;4110:3;4099:9;4095:19;4082:33;4066:49;;4140:2;4130:8;4127:16;4124:2;;;4161:6;4153;4146:22;4124:2;;4189:53;4234:7;4223:8;4212:9;4208:24;4189:53;:::i;:::-;4179:63;;;3372:876;;;;;;;;;;:::o;4253:253::-;;4362:2;4350:9;4341:7;4337:23;4333:32;4330:2;;;4383:6;4375;4368:22;4330:2;4427:9;4414:23;4446:30;4470:5;4446:30;:::i;4511:257::-;;4631:2;4619:9;4610:7;4606:23;4602:32;4599:2;;;4652:6;4644;4637:22;4599:2;4689:9;4683:16;4708:30;4732:5;4708:30;:::i;4773:194::-;;4896:2;4884:9;4875:7;4871:23;4867:32;4864:2;;;4917:6;4909;4902:22;4864:2;-1:-1:-1;4945:16:140;;4854:113;-1:-1:-1;4854:113:140:o;4972:410::-;;;5110:2;5098:9;5089:7;5085:23;5081:32;5078:2;;;5131:6;5123;5116:22;5078:2;5159:23;;;-1:-1:-1;5233:2:140;5218:18;;5205:32;-1:-1:-1;;;;;5249:30:140;;5246:2;;;5297:6;5289;5282:22;5246:2;5325:51;5368:7;5359:6;5348:9;5344:22;5325:51;:::i;:::-;5315:61;;;5068:314;;;;;:::o;5387:395::-;;;;;5550:3;5538:9;5529:7;5525:23;5521:33;5518:2;;;5572:6;5564;5557:22;5518:2;-1:-1:-1;;5600:23:140;;;5670:2;5655:18;;5642:32;;-1:-1:-1;5721:2:140;5706:18;;5693:32;;5772:2;5757:18;5744:32;;-1:-1:-1;5508:274:140;-1:-1:-1;5508:274:140:o;5787:259::-;;5868:5;5862:12;5895:6;5890:3;5883:19;5911:63;5967:6;5960:4;5955:3;5951:14;5944:4;5937:5;5933:16;5911:63;:::i;:::-;6028:2;6007:15;-1:-1:-1;;6003:29:140;5994:39;;;;6035:4;5990:50;;5838:208;-1:-1:-1;;5838:208:140:o;6051:423::-;6256:2;6292:15;;;-1:-1:-1;;;;;;6288:24:140;;;6276:37;;6369:3;6347:16;;;;-1:-1:-1;;;;;;6343:41:140;6338:2;6329:12;;6322:63;6419:15;;6415:24;6410:2;6401:12;;6394:46;6465:2;6456:12;;6224:250::o;6479:609::-;-1:-1:-1;;;;;;6738:2:140;6774:15;;;6770:24;;6758:37;;-1:-1:-1;;;;;;6818:3:140;6866:16;;;6862:25;;6857:2;6848:12;;6841:47;6922:15;;;6918:24;;6913:2;6904:12;;6897:46;6977:16;;;;6973:25;;;6968:2;6959:12;;6952:47;7033:15;;7029:24;;;7024:2;7015:12;;7008:46;7079:2;7070:12;;6706:382::o;7093:777::-;;7451:1;7447;7443:2;7439:10;7435:18;7431:23;7422:6;7418:2;7414:15;7410:45;7405:3;7398:58;7486:6;7481:2;7476:3;7472:12;7465:28;7523:6;7518:2;7513:3;7509:12;7502:28;7560:6;7555:2;7550:3;7546:12;7539:28;7596:6;7590:13;7612:63;7668:6;7662:3;7657;7653:13;7646:4;7638:6;7634:17;7612:63;:::i;:::-;7735:13;;7694:16;;;;7757:64;7735:13;7806:3;7798:12;;7791:4;7779:17;;7757:64;:::i;:::-;7841:17;7860:3;7837:27;;7388:482;-1:-1:-1;;;;;;;;7388:482:140:o;7875:274::-;;8042:6;8036:13;8058:53;8104:6;8099:3;8092:4;8084:6;8080:17;8058:53;:::i;:::-;8127:16;;;;;8012:137;-1:-1:-1;;8012:137:140:o;8154:392::-;-1:-1:-1;;;8412:27:140;;8464:1;8455:11;;8448:27;;;;8500:2;8491:12;;8484:28;8537:2;8528:12;;8402:144::o;8761:182::-;8890:19;;;8934:2;8925:12;;8880:63::o;8948:203::-;-1:-1:-1;;;;;9112:32:140;;;;9094:51;;9082:2;9067:18;;9049:102::o;9372:391::-;-1:-1:-1;;;;;9646:15:140;;;9628:34;;9698:15;;;;9693:2;9678:18;;9671:43;9745:2;9730:18;;9723:34;;;;9578:2;9563:18;;9545:218::o;10156:760::-;-1:-1:-1;;;;;10553:15:140;;;10535:34;;10605:15;;;;10600:2;10585:18;;10578:43;10652:2;10637:18;;10630:34;;;;10695:2;10680:18;;10673:34;;;;10751:14;10744:22;10738:3;10723:19;;10716:51;10816:4;10804:17;10515:3;10783:19;;10776:46;10853:3;10838:19;;10831:35;10897:3;10882:19;;10875:35;;;;10484:3;10469:19;;10451:465::o;10921:678::-;-1:-1:-1;;;;;11296:15:140;;;11278:34;;11348:15;;;;11343:2;11328:18;;11321:43;11395:2;11380:18;;11373:34;;;;11438:2;11423:18;;11416:34;;;;11499:4;11487:17;11481:3;11466:19;;11459:46;11258:3;11521:19;;11514:35;11580:3;11565:19;;11558:35;;;;11227:3;11212:19;;11194:405::o;11604:490::-;-1:-1:-1;;;;;11873:15:140;;;11855:34;;11920:2;11905:18;;11898:34;;;11968:15;;11963:2;11948:18;;11941:43;12020:3;12015:2;12000:18;;11993:31;;;11604:490;;12041:47;;12068:19;;12060:6;12041:47;:::i;12099:582::-;12313:2;12298:18;;12302:9;12393:6;12099:582;12427:194;12441:4;12438:1;12435:11;12427:194;;;12500:13;;12488:26;;12537:4;12561:12;;;;12596:15;;;;12461:1;12454:9;12427:194;;;12431:3;;;12669:4;12661:6;12657:17;12652:2;12641:9;12637:18;12630:45;12280:401;;;;;:::o;12686:617::-;12983:25;;;-1:-1:-1;;;;;13082:15:140;;;13077:2;13062:18;;13055:43;13134:15;;;;13129:2;13114:18;;13107:43;13181:2;13166:18;;13159:34;13224:3;13209:19;;13202:35;;;;13281:14;13274:22;13035:3;13253:19;;13246:51;12970:3;12955:19;;12937:366::o;13308:672::-;13623:25;;;-1:-1:-1;;;;;13722:15:140;;;13717:2;13702:18;;13695:43;13769:2;13754:18;;13747:34;;;;13817:15;;;13812:2;13797:18;;13790:43;13870:15;;;13864:3;13849:19;;13842:44;13675:3;13902:19;;13895:35;;;;13961:3;13946:19;;13939:35;13610:3;13595:19;;13577:403::o;13985:489::-;14244:25;;;14300:2;14285:18;;14278:34;;;;14343:2;14328:18;;14321:34;;;;14386:2;14371:18;;14364:34;-1:-1:-1;;;;;14435:32:140;14429:3;14414:19;;14407:61;14231:3;14216:19;;14198:276::o;14479:434::-;;14710:6;14699:9;14692:25;14753:6;14748:2;14737:9;14733:18;14726:34;14796:6;14791:2;14780:9;14776:18;14769:34;14839:3;14834:2;14823:9;14819:18;14812:31;14860:47;14902:3;14891:9;14887:19;14879:6;14860:47;:::i;14918:316::-;;15119:2;15108:9;15101:21;15139:46;15181:2;15170:9;15166:18;15158:6;15139:46;:::i;:::-;15131:54;;15221:6;15216:2;15205:9;15201:18;15194:34;15091:143;;;;;:::o;15534:551::-;15852:2;15841:22;;;15823:41;;15900:22;;;;15895:2;15880:18;;15873:50;15954:2;15939:18;;15932:34;;;;15997:2;15982:18;;15975:34;-1:-1:-1;;;;;16046:32:140;;;16040:3;16025:19;;16018:61;15810:3;15795:19;;15777:308::o;16090:256::-;16272:25;;;16328:2;16313:18;;16306:34;16260:2;16245:18;;16227:119::o;16351:786::-;;16605:2;16594:9;16590:18;16635:6;16624:9;16617:25;16661:2;16699:6;16694:2;16683:9;16679:18;16672:34;16742:2;16737;16726:9;16722:18;16715:30;16765:6;16800;16794:13;16831:6;16823;16816:22;16869:3;16858:9;16854:19;16847:26;;16908:2;16900:6;16896:15;16882:29;;16929:4;16942:169;16956:6;16953:1;16950:13;16942:169;;;17017:13;;17005:26;;17086:15;;;;17051:12;;;;16978:1;16971:9;16942:169;;;-1:-1:-1;17128:3:140;;16566:571;-1:-1:-1;;;;;;;;16566:571:140:o;17142:365::-;17387:2;17376:22;;;17358:41;;17435:22;;;;17430:2;17415:18;;17408:50;17489:2;17474:18;;17467:34;;;;17346:2;17331:18;;17313:194::o;18068:524::-;18369:4;18357:17;;;18339:36;;18411:17;;;18406:2;18391:18;;18384:45;18460:2;18445:18;;18438:34;;;;18508:17;;;18503:2;18488:18;;18481:45;18570:14;;18563:22;18557:3;18542:19;;18535:51;18326:3;18311:19;;18293:299::o;18597:335::-;18799:2;18781:21;;;18838:2;18818:18;;;18811:30;-1:-1:-1;;;18872:2:140;18857:18;;18850:41;18923:2;18908:18;;18771:161::o;18937:340::-;19139:2;19121:21;;;19178:2;19158:18;;;19151:30;-1:-1:-1;;;19212:2:140;19197:18;;19190:46;19268:2;19253:18;;19111:166::o;19282:351::-;19484:2;19466:21;;;19523:2;19503:18;;;19496:30;-1:-1:-1;;;19557:2:140;19542:18;;19535:57;19624:2;19609:18;;19456:177::o;19638:351::-;19840:2;19822:21;;;19879:2;19859:18;;;19852:30;-1:-1:-1;;;19913:2:140;19898:18;;19891:57;19980:2;19965:18;;19812:177::o;19994:329::-;20196:2;20178:21;;;20235:1;20215:18;;;20208:29;-1:-1:-1;;;20268:2:140;20253:18;;20246:36;20314:2;20299:18;;20168:155::o;20328:340::-;20530:2;20512:21;;;20569:2;20549:18;;;20542:30;-1:-1:-1;;;20603:2:140;20588:18;;20581:46;20659:2;20644:18;;20502:166::o;20673:334::-;20875:2;20857:21;;;20914:2;20894:18;;;20887:30;-1:-1:-1;;;20948:2:140;20933:18;;20926:40;20998:2;20983:18;;20847:160::o;21012:351::-;21214:2;21196:21;;;21253:2;21233:18;;;21226:30;-1:-1:-1;;;21287:2:140;21272:18;;21265:57;21354:2;21339:18;;21186:177::o;21368:332::-;21570:2;21552:21;;;21609:1;21589:18;;;21582:29;-1:-1:-1;;;21642:2:140;21627:18;;21620:39;21691:2;21676:18;;21542:158::o;21705:341::-;21907:2;21889:21;;;21946:2;21926:18;;;21919:30;-1:-1:-1;;;21980:2:140;21965:18;;21958:47;22037:2;22022:18;;21879:167::o;22051:654::-;;22248:2;22237:9;22230:21;22286:6;22280:13;22329:4;22324:2;22313:9;22309:18;22302:32;22357:53;22405:3;22394:9;22390:19;22376:12;22357:53;:::i;:::-;22343:67;;22491:1;22487;22482:3;22478:11;22474:19;22468:2;22460:6;22456:15;22450:22;22446:48;22441:2;22430:9;22426:18;22419:76;22549:2;22541:6;22537:15;22531:22;22526:2;22515:9;22511:18;22504:50;22609:2;22601:6;22597:15;22591:22;22585:3;22574:9;22570:19;22563:51;22670:3;22662:6;22658:16;22652:23;22645:4;22634:9;22630:20;22623:53;22693:6;22685:14;;;22220:485;;;;:::o;22710:795::-;22997:13;;-1:-1:-1;;;;;22993:22:140;;;22975:41;;23076:4;23064:17;;;23058:24;23054:33;;23032:20;;;23025:63;23148:4;23136:17;;;23130:24;23156:8;23126:39;23104:20;;;23097:69;23226:4;23214:17;;;23208:24;23204:33;;23182:20;;;23175:63;23294:4;23282:17;;;23276:24;23254:20;;;23247:54;22955:3;23345:17;;;23339:24;23317:20;;;23310:54;23420:4;23408:17;;;23402:24;23380:20;;;23373:54;23487:4;23475:17;;;23469:24;23465:33;23443:20;;;23436:63;;;;22924:3;22909:19;;22891:614::o;23692:353::-;23906:25;;;23979:4;23967:17;;;23962:2;23947:18;;23940:45;24021:17;24016:2;24001:18;;23994:45;23894:2;23879:18;;23861:184::o;24050:258::-;24122:1;24132:113;24146:6;24143:1;24140:13;24132:113;;;24222:11;;;24216:18;24203:11;;;24196:39;24168:2;24161:10;24132:113;;;24263:6;24260:1;24257:13;24254:2;;;24298:1;24289:6;24284:3;24280:16;24273:27;24254:2;;24103:205;;;:::o;24313:120::-;24401:5;24394:13;24387:21;24380:5;24377:32;24367:2;;24423:1;24420;24413:12

Metadata Hash

129449c27cb9daa9f4831846147512f75dc89ad5c3f4ce4fcb2f8dc2a5f4be2e
Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.