ETH Price: $3,147.26 (+5.67%)

Contract

0x21Fa95485f4571A3a0d0c396561cF4D8D13D445d
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x61016060117048722021-01-22 10:52:121387 days ago1611312732IN
 Create: SushiswapSpellV1
0 ETH0.234676262

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
132534022021-09-19 2:08:461148 days ago1632017326
0x21Fa9548...8D13D445d
0.59065063 ETH
132534022021-09-19 2:08:461148 days ago1632017326
0x21Fa9548...8D13D445d
0.59065063 ETH
128055792021-07-11 10:48:081217 days ago1626000488
0x21Fa9548...8D13D445d
0.03256126 ETH
128055792021-07-11 10:48:081217 days ago1626000488
0x21Fa9548...8D13D445d
0.03256126 ETH
127255102021-06-28 23:32:211230 days ago1624923141
0x21Fa9548...8D13D445d
0.65335195 ETH
127255102021-06-28 23:32:211230 days ago1624923141
0x21Fa9548...8D13D445d
0.65335195 ETH
126842242021-06-22 13:16:271236 days ago1624367787
0x21Fa9548...8D13D445d
12.93005057 ETH
126842242021-06-22 13:16:271236 days ago1624367787
0x21Fa9548...8D13D445d
12.93005057 ETH
126786672021-06-21 16:19:531237 days ago1624292393
0x21Fa9548...8D13D445d
0.4539742 ETH
126786672021-06-21 16:19:531237 days ago1624292393
0x21Fa9548...8D13D445d
0.4539742 ETH
126716372021-06-20 14:06:481238 days ago1624198008
0x21Fa9548...8D13D445d
2.25478067 ETH
126716372021-06-20 14:06:481238 days ago1624198008
0x21Fa9548...8D13D445d
2.25478067 ETH
126488012021-06-17 0:50:051242 days ago1623891005
0x21Fa9548...8D13D445d
1.39719047 ETH
126488012021-06-17 0:50:051242 days ago1623891005
0x21Fa9548...8D13D445d
1.39719047 ETH
126176602021-06-12 4:43:281246 days ago1623473008
0x21Fa9548...8D13D445d
0.19946719 ETH
126176602021-06-12 4:43:281246 days ago1623473008
0x21Fa9548...8D13D445d
0.19946719 ETH
126165022021-06-12 0:31:421247 days ago1623457902
0x21Fa9548...8D13D445d
0.26230018 ETH
126165022021-06-12 0:31:421247 days ago1623457902
0x21Fa9548...8D13D445d
0.26230018 ETH
126140752021-06-11 15:32:591247 days ago1623425579
0x21Fa9548...8D13D445d
0.15527843 ETH
126140752021-06-11 15:32:591247 days ago1623425579
0x21Fa9548...8D13D445d
0.15527843 ETH
125693362021-06-04 17:21:121254 days ago1622827272
0x21Fa9548...8D13D445d
3.04719694 ETH
125693362021-06-04 17:21:121254 days ago1622827272
0x21Fa9548...8D13D445d
3.04719694 ETH
125212132021-05-28 6:26:271261 days ago1622183187
0x21Fa9548...8D13D445d
1.34369864 ETH
125212132021-05-28 6:26:271261 days ago1622183187
0x21Fa9548...8D13D445d
1.34369864 ETH
125194732021-05-28 0:05:131262 days ago1622160313
0x21Fa9548...8D13D445d
51.43409482 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SushiswapSpellV1

Compiler Version
v0.6.12+commit.27d51765

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license
File 1 of 72 : SushiswapSpellV1.sol
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

import 'OpenZeppelin/[email protected]/contracts/token/ERC20/IERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/math/SafeMath.sol';

import './BasicSpell.sol';
import '../utils/HomoraMath.sol';
import '../../interfaces/IUniswapV2Factory.sol';
import '../../interfaces/IUniswapV2Router02.sol';
import '../../interfaces/IUniswapV2Pair.sol';
import '../../interfaces/IWMasterChef.sol';

contract SushiswapSpellV1 is BasicSpell {
  using SafeMath for uint;
  using HomoraMath for uint;

  IUniswapV2Factory public immutable factory;
  IUniswapV2Router02 public immutable router;

  mapping(address => mapping(address => address)) public pairs;

  IWMasterChef public immutable wmasterchef;

  address public immutable sushi;

  constructor(
    IBank _bank,
    address _werc20,
    IUniswapV2Router02 _router,
    address _wmasterchef
  ) public BasicSpell(_bank, _werc20, _router.WETH()) {
    router = _router;
    factory = IUniswapV2Factory(_router.factory());
    wmasterchef = IWMasterChef(_wmasterchef);
    IWMasterChef(_wmasterchef).setApprovalForAll(address(_bank), true);
    sushi = address(IWMasterChef(_wmasterchef).sushi());
  }

  function getPair(address tokenA, address tokenB) public returns (address) {
    address lp = pairs[tokenA][tokenB];
    if (lp == address(0)) {
      lp = factory.getPair(tokenA, tokenB);
      require(lp != address(0), 'no lp token');
      ensureApprove(tokenA, address(router));
      ensureApprove(tokenB, address(router));
      ensureApprove(lp, address(router));
      pairs[tokenA][tokenB] = lp;
      pairs[tokenB][tokenA] = lp;
    }
    return lp;
  }

  /// @dev Compute optimal deposit amount
  /// @param amtA amount of token A desired to deposit
  /// @param amtB amount of token B desired to deposit
  /// @param resA amount of token A in reserve
  /// @param resB amount of token B in reserve
  function optimalDeposit(
    uint amtA,
    uint amtB,
    uint resA,
    uint resB
  ) internal pure returns (uint swapAmt, bool isReversed) {
    if (amtA.mul(resB) >= amtB.mul(resA)) {
      swapAmt = _optimalDepositA(amtA, amtB, resA, resB);
      isReversed = false;
    } else {
      swapAmt = _optimalDepositA(amtB, amtA, resB, resA);
      isReversed = true;
    }
  }

  /// @dev Compute optimal deposit amount helper.
  /// @param amtA amount of token A desired to deposit
  /// @param amtB amount of token B desired to deposit
  /// @param resA amount of token A in reserve
  /// @param resB amount of token B in reserve
  /// Formula: https://blog.alphafinance.io/byot/
  function _optimalDepositA(
    uint amtA,
    uint amtB,
    uint resA,
    uint resB
  ) internal pure returns (uint) {
    require(amtA.mul(resB) >= amtB.mul(resA), 'Reversed');
    uint a = 997;
    uint b = uint(1997).mul(resA);
    uint _c = (amtA.mul(resB)).sub(amtB.mul(resA));
    uint c = _c.mul(1000).div(amtB.add(resB)).mul(resA);
    uint d = a.mul(c).mul(4);
    uint e = HomoraMath.sqrt(b.mul(b).add(d));
    uint numerator = e.sub(b);
    uint denominator = a.mul(2);
    return numerator.div(denominator);
  }

  struct Amounts {
    uint amtAUser;
    uint amtBUser;
    uint amtLPUser;
    uint amtABorrow;
    uint amtBBorrow;
    uint amtLPBorrow;
    uint amtAMin;
    uint amtBMin;
  }

  function addLiquidityInternal(
    address tokenA,
    address tokenB,
    Amounts calldata amt
  ) internal {
    address lp = getPair(tokenA, tokenB);

    // 1. Get user input amounts
    doTransmitETH();
    doTransmit(tokenA, amt.amtAUser);
    doTransmit(tokenB, amt.amtBUser);
    doTransmit(lp, amt.amtLPUser);

    // 2. Borrow specified amounts
    doBorrow(tokenA, amt.amtABorrow);
    doBorrow(tokenB, amt.amtBBorrow);
    doBorrow(lp, amt.amtLPBorrow);

    // 3. Calculate optimal swap amount
    uint swapAmt;
    bool isReversed;
    {
      uint amtA = IERC20(tokenA).balanceOf(address(this));
      uint amtB = IERC20(tokenB).balanceOf(address(this));
      uint resA;
      uint resB;
      if (IUniswapV2Pair(lp).token0() == tokenA) {
        (resA, resB, ) = IUniswapV2Pair(lp).getReserves();
      } else {
        (resB, resA, ) = IUniswapV2Pair(lp).getReserves();
      }
      (swapAmt, isReversed) = optimalDeposit(amtA, amtB, resA, resB);
    }

    // 4. Swap optimal amount
    {
      address[] memory path = new address[](2);
      (path[0], path[1]) = isReversed ? (tokenB, tokenA) : (tokenA, tokenB);
      router.swapExactTokensForTokens(swapAmt, 0, path, address(this), now);
    }

    // 5. Add liquidity
    router.addLiquidity(
      tokenA,
      tokenB,
      IERC20(tokenA).balanceOf(address(this)),
      IERC20(tokenB).balanceOf(address(this)),
      amt.amtAMin,
      amt.amtBMin,
      address(this),
      now
    );
  }

  function addLiquidityWERC20(
    address tokenA,
    address tokenB,
    Amounts calldata amt
  ) external payable {
    address lp = getPair(tokenA, tokenB);
    // 1-5. add liquidity
    addLiquidityInternal(tokenA, tokenB, amt);

    // 6. Put collateral
    doPutCollateral(lp, IERC20(lp).balanceOf(address(this)));

    // 7. Refund leftovers to users
    doRefundETH();
    doRefund(tokenA);
    doRefund(tokenB);
  }

  function addLiquidityWMasterChef(
    address tokenA,
    address tokenB,
    Amounts calldata amt,
    uint pid
  ) external payable {
    address lp = getPair(tokenA, tokenB);
    (address lpToken, , , ) = wmasterchef.chef().poolInfo(pid);
    require(lpToken == lp, 'incorrect lp token');

    // 1-5. add liquidity
    addLiquidityInternal(tokenA, tokenB, amt);

    // 6. Take out collateral
    uint positionId = bank.POSITION_ID();
    (, , uint collId, uint collSize) = bank.getPositionInfo(positionId);
    if (collSize > 0) {
      (uint decodedPid, ) = wmasterchef.decodeId(collId);
      require(pid == decodedPid, 'incorrect pid');
      bank.takeCollateral(address(wmasterchef), collId, collSize);
      wmasterchef.burn(collId, collSize);
    }

    // 7. Put collateral
    ensureApprove(lp, address(wmasterchef));
    uint amount = IERC20(lp).balanceOf(address(this));
    uint id = wmasterchef.mint(pid, amount);
    bank.putCollateral(address(wmasterchef), id, amount);

    // 8. Refund leftovers to users
    doRefundETH();
    doRefund(tokenA);
    doRefund(tokenB);

    // 9. Refund sushi
    doRefund(sushi);
  }

  struct RepayAmounts {
    uint amtLPTake;
    uint amtLPWithdraw;
    uint amtARepay;
    uint amtBRepay;
    uint amtLPRepay;
    uint amtAMin;
    uint amtBMin;
  }

  function removeLiquidityInternal(
    address tokenA,
    address tokenB,
    RepayAmounts calldata amt
  ) internal {
    address lp = getPair(tokenA, tokenB);
    uint positionId = bank.POSITION_ID();

    uint amtARepay = amt.amtARepay;
    uint amtBRepay = amt.amtBRepay;
    uint amtLPRepay = amt.amtLPRepay;

    // 2. Compute repay amount if MAX_INT is supplied (max debt)
    if (amtARepay == uint(-1)) {
      amtARepay = bank.borrowBalanceCurrent(positionId, tokenA);
    }
    if (amtBRepay == uint(-1)) {
      amtBRepay = bank.borrowBalanceCurrent(positionId, tokenB);
    }
    if (amtLPRepay == uint(-1)) {
      amtLPRepay = bank.borrowBalanceCurrent(positionId, lp);
    }

    // 3. Compute amount to actually remove
    uint amtLPToRemove = IERC20(lp).balanceOf(address(this)).sub(amt.amtLPWithdraw);

    // 4. Remove liquidity
    (uint amtA, uint amtB) =
      router.removeLiquidity(tokenA, tokenB, amtLPToRemove, 0, 0, address(this), now);

    // 5. MinimizeTrading
    uint amtADesired = amtARepay.add(amt.amtAMin);
    uint amtBDesired = amtBRepay.add(amt.amtBMin);

    if (amtA < amtADesired && amtB >= amtBDesired) {
      address[] memory path = new address[](2);
      (path[0], path[1]) = (tokenB, tokenA);
      router.swapTokensForExactTokens(
        amtADesired.sub(amtA),
        amtB.sub(amtBDesired),
        path,
        address(this),
        now
      );
    } else if (amtA >= amtADesired && amtB < amtBDesired) {
      address[] memory path = new address[](2);
      (path[0], path[1]) = (tokenA, tokenB);
      router.swapTokensForExactTokens(
        amtBDesired.sub(amtB),
        amtA.sub(amtADesired),
        path,
        address(this),
        now
      );
    }

    // 6. Repay
    doRepay(tokenA, amtARepay);
    doRepay(tokenB, amtBRepay);
    doRepay(lp, amtLPRepay);

    // 7. Slippage control
    require(IERC20(tokenA).balanceOf(address(this)) >= amt.amtAMin);
    require(IERC20(tokenB).balanceOf(address(this)) >= amt.amtBMin);
    require(IERC20(lp).balanceOf(address(this)) >= amt.amtLPWithdraw);

    // 8. Refund leftover
    doRefundETH();
    doRefund(tokenA);
    doRefund(tokenB);
    doRefund(lp);
  }

  function removeLiquidityWERC20(
    address tokenA,
    address tokenB,
    RepayAmounts calldata amt
  ) external {
    address lp = getPair(tokenA, tokenB);

    // 1. Take out collateral
    doTakeCollateral(lp, amt.amtLPTake);

    // 2-8. remove liquidity
    removeLiquidityInternal(tokenA, tokenB, amt);
  }

  function removeLiquidityWMasterChef(
    address tokenA,
    address tokenB,
    RepayAmounts calldata amt
  ) external {
    address lp = getPair(tokenA, tokenB);
    uint positionId = bank.POSITION_ID();
    (, address collToken, uint collId, ) = bank.getPositionInfo(positionId);
    require(IWMasterChef(collToken).getUnderlyingToken(collId) == lp, 'incorrect underlying');

    // 1. Take out collateral
    bank.takeCollateral(address(wmasterchef), collId, amt.amtLPTake);
    wmasterchef.burn(collId, amt.amtLPTake);

    // 2-8. remove liquidity
    removeLiquidityInternal(tokenA, tokenB, amt);

    // 9. Refund sushi
    doRefund(sushi);
  }

  function harvestWMasterChef() external {
    uint positionId = bank.POSITION_ID();
    (, , uint collId, ) = bank.getPositionInfo(positionId);
    (uint pid, ) = wmasterchef.decodeId(collId);
    address lp = wmasterchef.getUnderlyingToken(collId);

    // 1. Take out collateral
    bank.takeCollateral(address(wmasterchef), collId, uint(-1));
    wmasterchef.burn(collId, uint(-1));

    // 2. put collateral
    uint amount = IERC20(lp).balanceOf(address(this));
    ensureApprove(lp, address(wmasterchef));
    uint id = wmasterchef.mint(pid, amount);
    bank.putCollateral(address(wmasterchef), id, amount);

    // 3. Refund sushi
    doRefund(sushi);
  }
}

File 2 of 72 : SafeBox.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/token/ERC20/ERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/token/ERC20/IERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/token/ERC20/SafeERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/cryptography/MerkleProof.sol';
import 'OpenZeppelin/[email protected]/contracts/math/SafeMath.sol';
import 'OpenZeppelin/[email protected]/contracts/utils/ReentrancyGuard.sol';
import './Governable.sol';
import '../interfaces/ICErc20.sol';

contract SafeBox is Governable, ERC20, ReentrancyGuard {
  using SafeMath for uint;
  using SafeERC20 for IERC20;
  event Claim(address user, uint amount);

  ICErc20 public immutable cToken;
  IERC20 public immutable uToken;

  address public relayer;
  bytes32 public root;
  mapping(address => uint) public claimed;

  constructor(
    ICErc20 _cToken,
    string memory _name,
    string memory _symbol
  ) public ERC20(_name, _symbol) {
    IERC20 _uToken = IERC20(_cToken.underlying());
    __Governable__init();
    cToken = _cToken;
    uToken = _uToken;
    relayer = msg.sender;
    _uToken.approve(address(_cToken), uint(-1));
  }

  function setRelayer(address _relayer) external onlyGov {
    relayer = _relayer;
  }

  function updateRoot(bytes32 _root) external {
    require(msg.sender == relayer || msg.sender == governor, '!relayer');
    root = _root;
  }

  function deposit(uint amount) external nonReentrant {
    uint uBalanceBefore = uToken.balanceOf(address(this));
    uToken.safeTransferFrom(msg.sender, address(this), amount);
    uint uBalanceAfter = uToken.balanceOf(address(this));
    uint cBalanceBefore = cToken.balanceOf(address(this));
    require(cToken.mint(uBalanceAfter.sub(uBalanceBefore)) == 0, '!mint');
    uint cBalanceAfter = cToken.balanceOf(address(this));
    _mint(msg.sender, cBalanceAfter.sub(cBalanceBefore));
  }

  function withdraw(uint amount) public nonReentrant {
    _burn(msg.sender, amount);
    uint uBalanceBefore = uToken.balanceOf(address(this));
    require(cToken.redeem(amount) == 0, '!redeem');
    uint uBalanceAfter = uToken.balanceOf(address(this));
    uToken.safeTransfer(msg.sender, uBalanceAfter.sub(uBalanceBefore));
  }

  function claim(uint totalReward, bytes32[] memory proof) public nonReentrant {
    bytes32 leaf = keccak256(abi.encodePacked(msg.sender, totalReward));
    require(MerkleProof.verify(proof, root, leaf), '!proof');
    uint send = totalReward.sub(claimed[msg.sender]);
    claimed[msg.sender] = totalReward;
    uToken.safeTransfer(msg.sender, send);
    emit Claim(msg.sender, send);
  }

  function adminClaim(uint amount) external onlyGov {
    uToken.safeTransfer(msg.sender, amount);
  }

  function claimAndWithdraw(
    uint claimAmount,
    bytes32[] memory proof,
    uint withdrawAmount
  ) external {
    claim(claimAmount, proof);
    withdraw(withdrawAmount);
  }
}

File 3 of 72 : Governable.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/proxy/Initializable.sol';

contract Governable is Initializable {
  address public governor; // The current governor.
  address public pendingGovernor; // The address pending to become the governor once accepted.

  modifier onlyGov() {
    require(msg.sender == governor, 'not the governor');
    _;
  }

  /// @dev Initialize the bank smart contract, using msg.sender as the first governor.
  function __Governable__init() internal initializer {
    governor = msg.sender;
    pendingGovernor = address(0);
  }

  /// @dev Set the pending governor, which will be the governor once accepted.
  /// @param _pendingGovernor The address to become the pending governor.
  function setPendingGovernor(address _pendingGovernor) external onlyGov {
    pendingGovernor = _pendingGovernor;
  }

  /// @dev Accept to become the new governor. Must be called by the pending governor.
  function acceptGovernor() external {
    require(msg.sender == pendingGovernor, 'not the pending governor');
    pendingGovernor = address(0);
    governor = msg.sender;
  }
}

File 4 of 72 : ICErc20.sol
pragma solidity 0.6.12;

interface ICErc20 {
  function underlying() external returns (address);

  function mint(uint mintAmount) external returns (uint);

  function redeem(uint redeemTokens) external returns (uint);

  function balanceOf(address user) external view returns (uint);

  function borrowBalanceCurrent(address account) external returns (uint);

  function borrowBalanceStored(address account) external view returns (uint);

  function borrow(uint borrowAmount) external returns (uint);

  function repayBorrow(uint repayAmount) external returns (uint);
}

File 5 of 72 : WERC20.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/token/ERC1155/ERC1155.sol';
import 'OpenZeppelin/[email protected]/contracts/token/ERC20/IERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/token/ERC20/SafeERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/utils/ReentrancyGuard.sol';

import '../../interfaces/IWERC20.sol';

contract WERC20 is ERC1155('WERC20'), ReentrancyGuard, IWERC20 {
  using SafeERC20 for IERC20;

  /// @dev Return the underlying ERC-20 for the given ERC-1155 token id.
  function getUnderlyingToken(uint id) external view override returns (address) {
    address token = address(id);
    require(uint(token) == id, 'id overflow');
    return token;
  }

  /// @dev Return the conversion rate from ERC-1155 to ERC-20, multiplied by 2**112.
  function getUnderlyingRate(uint) external view override returns (uint) {
    return 2**112;
  }

  /// @dev Return the underlying ERC20 balance for the user.
  function balanceOfERC20(address token, address user) external view override returns (uint) {
    return balanceOf(user, uint(token));
  }

  /// @dev Mint ERC1155 token for the given ERC20 token.
  function mint(address token, uint amount) external override nonReentrant {
    uint balanceBefore = IERC20(token).balanceOf(address(this));
    IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
    uint balanceAfter = IERC20(token).balanceOf(address(this));
    _mint(msg.sender, uint(token), balanceAfter.sub(balanceBefore), '');
  }

  /// @dev Burn ERC1155 token to redeem ERC20 token back.
  function burn(address token, uint amount) external override nonReentrant {
    _burn(msg.sender, uint(token), amount);
    IERC20(token).safeTransfer(msg.sender, amount);
  }
}

File 6 of 72 : IWERC20.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/token/ERC1155/IERC1155.sol';

import './IERC20Wrapper.sol';

interface IWERC20 is IERC1155, IERC20Wrapper {
  /// @dev Return the underlying ERC20 balance for the user.
  function balanceOfERC20(address token, address user) external view returns (uint);

  /// @dev Mint ERC1155 token for the given ERC20 token.
  function mint(address token, uint amount) external;

  /// @dev Burn ERC1155 token to redeem ERC20 token back.
  function burn(address token, uint amount) external;
}

File 7 of 72 : IERC20Wrapper.sol
pragma solidity 0.6.12;

interface IERC20Wrapper {
  /// @dev Return the underlying ERC-20 for the given ERC-1155 token id.
  function getUnderlyingToken(uint id) external view returns (address);

  /// @dev Return the conversion rate from ERC-1155 to ERC-20, multiplied by 2**112.
  function getUnderlyingRate(uint id) external view returns (uint);
}

File 8 of 72 : WStakingRewards.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/token/ERC1155/ERC1155.sol';
import 'OpenZeppelin/[email protected]/contracts/token/ERC20/IERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/token/ERC20/SafeERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/utils/ReentrancyGuard.sol';

import '../utils/HomoraMath.sol';
import '../../interfaces/IERC20Wrapper.sol';
import '../../interfaces/IStakingRewards.sol';

contract WStakingRewards is ERC1155('WStakingRewards'), ReentrancyGuard, IERC20Wrapper {
  using SafeMath for uint;
  using HomoraMath for uint;
  using SafeERC20 for IERC20;

  address public immutable staking;
  address public immutable underlying;
  address public immutable reward;

  constructor(
    address _staking,
    address _underlying,
    address _reward
  ) public {
    staking = _staking;
    underlying = _underlying;
    reward = _reward;
    IERC20(_underlying).approve(_staking, uint(-1));
  }

  function getUnderlyingToken(uint) external view override returns (address) {
    return underlying;
  }

  function getUnderlyingRate(uint) external view override returns (uint) {
    return 2**112;
  }

  function mint(uint amount) external nonReentrant returns (uint) {
    IERC20(underlying).safeTransferFrom(msg.sender, address(this), amount);
    IStakingRewards(staking).stake(amount);
    uint rewardPerToken = IStakingRewards(staking).rewardPerToken();
    _mint(msg.sender, rewardPerToken, amount, '');
    return rewardPerToken;
  }

  function burn(uint id, uint amount) external nonReentrant returns (uint) {
    if (amount == uint(-1)) {
      amount = balanceOf(msg.sender, id);
    }
    _burn(msg.sender, id, amount);
    IStakingRewards(staking).withdraw(amount);
    IStakingRewards(staking).getReward();
    IERC20(underlying).safeTransfer(msg.sender, amount);
    uint stRewardPerToken = id;
    uint enRewardPerToken = IStakingRewards(staking).rewardPerToken();
    uint stReward = stRewardPerToken.mul(amount).divCeil(1e18);
    uint enReward = enRewardPerToken.mul(amount).div(1e18);
    if (enReward > stReward) {
      IERC20(reward).safeTransfer(msg.sender, enReward.sub(stReward));
    }
    return enRewardPerToken;
  }
}

File 9 of 72 : HomoraMath.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/math/SafeMath.sol';

library HomoraMath {
  using SafeMath for uint;

  function divCeil(uint lhs, uint rhs) internal pure returns (uint) {
    return lhs.add(rhs).sub(1) / rhs;
  }

  function fmul(uint lhs, uint rhs) internal pure returns (uint) {
    return lhs.mul(rhs) / (2**112);
  }

  function fdiv(uint lhs, uint rhs) internal pure returns (uint) {
    return lhs.mul(2**112) / rhs;
  }

  // implementation from https://github.com/Uniswap/uniswap-lib/commit/99f3f28770640ba1bb1ff460ac7c5292fb8291a0
  // original implementation: https://github.com/abdk-consulting/abdk-libraries-solidity/blob/master/ABDKMath64x64.sol#L687
  function sqrt(uint x) internal pure returns (uint) {
    if (x == 0) return 0;
    uint xx = x;
    uint r = 1;

    if (xx >= 0x100000000000000000000000000000000) {
      xx >>= 128;
      r <<= 64;
    }

    if (xx >= 0x10000000000000000) {
      xx >>= 64;
      r <<= 32;
    }
    if (xx >= 0x100000000) {
      xx >>= 32;
      r <<= 16;
    }
    if (xx >= 0x10000) {
      xx >>= 16;
      r <<= 8;
    }
    if (xx >= 0x100) {
      xx >>= 8;
      r <<= 4;
    }
    if (xx >= 0x10) {
      xx >>= 4;
      r <<= 2;
    }
    if (xx >= 0x8) {
      r <<= 1;
    }

    r = (r + x / r) >> 1;
    r = (r + x / r) >> 1;
    r = (r + x / r) >> 1;
    r = (r + x / r) >> 1;
    r = (r + x / r) >> 1;
    r = (r + x / r) >> 1;
    r = (r + x / r) >> 1; // Seven iterations should be enough
    uint r1 = x / r;
    return (r < r1 ? r : r1);
  }
}

File 10 of 72 : IStakingRewards.sol
pragma solidity 0.6.12;

interface IStakingRewards {
  function rewardPerToken() external view returns (uint);

  function stake(uint amount) external;

  function withdraw(uint amount) external;

  function getReward() external;
}

File 11 of 72 : WLiquidityGauge.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/token/ERC1155/ERC1155.sol';
import 'OpenZeppelin/[email protected]/contracts/token/ERC20/IERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/token/ERC20/SafeERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/utils/ReentrancyGuard.sol';

import '../Governable.sol';
import '../utils/HomoraMath.sol';
import '../../interfaces/IERC20Wrapper.sol';
import '../../interfaces/ICurveRegistry.sol';
import '../../interfaces/ILiquidityGauge.sol';

interface ILiquidityGaugeMinter {
  function mint(address gauge) external;
}

contract WLiquidityGauge is ERC1155('WLiquidityGauge'), ReentrancyGuard, IERC20Wrapper, Governable {
  using SafeMath for uint;
  using HomoraMath for uint;
  using SafeERC20 for IERC20;

  struct GaugeInfo {
    ILiquidityGauge impl;
    uint accCrvPerShare;
  }

  ICurveRegistry public immutable registry;
  IERC20 public immutable crv;
  mapping(uint => mapping(uint => GaugeInfo)) public gauges;

  constructor(ICurveRegistry _registry, IERC20 _crv) public {
    __Governable__init();
    registry = _registry;
    crv = _crv;
  }

  function encodeId(
    uint pid,
    uint gid,
    uint crvPerShare
  ) public pure returns (uint id) {
    require(pid < (1 << 8), 'bad pid');
    require(gid < (1 << 8), 'bad gid');
    require(crvPerShare < (1 << 240), 'bad crv per share');
    return (pid << 248) | (gid << 240) | crvPerShare;
  }

  function decodeId(uint id)
    public
    pure
    returns (
      uint pid,
      uint gid,
      uint crvPerShare
    )
  {
    pid = id >> 248; // First 8 bits
    gid = (id >> 240) & (255); // Next 8 bits
    crvPerShare = id & ((1 << 240) - 1); // Last 240 bits
  }

  function getUnderlyingToken(uint id) external view override returns (address) {
    (uint pid, uint gid, ) = decodeId(id);
    ILiquidityGauge impl = gauges[pid][gid].impl;
    require(address(impl) != address(0), 'no gauge');
    return impl.lp_token();
  }

  /// @dev Return the conversion rate from ERC-1155 to ERC-20, multiplied by 2**112.
  function getUnderlyingRate(uint) external view override returns (uint) {
    return 2**112;
  }

  function registerGauge(uint pid, uint gid) external onlyGov {
    require(address(gauges[pid][gid].impl) == address(0), 'gauge already exists');
    address pool = registry.pool_list(pid);
    require(pool != address(0), 'no pool');
    (address[10] memory _gauges, ) = registry.get_gauges(pool);
    address gauge = _gauges[gid];
    require(gauge != address(0), 'no gauge');
    IERC20 lpToken = IERC20(ILiquidityGauge(gauge).lp_token());
    lpToken.approve(gauge, 0);
    lpToken.approve(gauge, uint(-1));
    gauges[pid][gid] = GaugeInfo({impl: ILiquidityGauge(gauge), accCrvPerShare: 0});
  }

  function mint(
    uint pid,
    uint gid,
    uint amount
  ) external nonReentrant returns (uint) {
    GaugeInfo storage gauge = gauges[pid][gid];
    ILiquidityGauge impl = gauge.impl;
    require(address(impl) != address(0), 'gauge not registered');
    mintCrv(gauge);
    IERC20 lpToken = IERC20(impl.lp_token());
    lpToken.safeTransferFrom(msg.sender, address(this), amount);
    impl.deposit(amount);
    uint id = encodeId(pid, gid, gauge.accCrvPerShare);
    _mint(msg.sender, id, amount, '');
    return id;
  }

  function burn(uint id, uint amount) external nonReentrant returns (uint) {
    if (amount == uint(-1)) {
      amount = balanceOf(msg.sender, id);
    }
    (uint pid, uint gid, uint stCrvPerShare) = decodeId(id);
    _burn(msg.sender, id, amount);
    GaugeInfo storage gauge = gauges[pid][gid];
    ILiquidityGauge impl = gauge.impl;
    require(address(impl) != address(0), 'gauge not registered');
    mintCrv(gauge);
    impl.withdraw(amount);
    IERC20(impl.lp_token()).safeTransfer(msg.sender, amount);
    uint stCrv = stCrvPerShare.mul(amount).divCeil(1e18);
    uint enCrv = gauge.accCrvPerShare.mul(amount).div(1e18);
    if (enCrv > stCrv) {
      crv.safeTransfer(msg.sender, enCrv.sub(stCrv));
    }
    return pid;
  }

  function mintCrv(GaugeInfo storage gauge) internal {
    ILiquidityGauge impl = gauge.impl;
    uint balanceBefore = crv.balanceOf(address(this));
    ILiquidityGaugeMinter(impl.minter()).mint(address(impl));
    uint balanceAfter = crv.balanceOf(address(this));
    uint gain = balanceAfter.sub(balanceBefore);
    uint supply = impl.balanceOf(address(this));
    if (gain > 0 && supply > 0) {
      gauge.accCrvPerShare = gauge.accCrvPerShare.add(gain.mul(1e18).div(supply));
    }
  }
}

File 12 of 72 : ICurveRegistry.sol
pragma solidity 0.6.12;

interface ICurveRegistry {
  function get_n_coins(address lp) external view returns (uint);

  function pool_list(uint id) external view returns (address);

  function get_coins(address pool) external view returns (address[8] memory);

  function get_gauges(address pool) external view returns (address[10] memory, uint128[10] memory);

  function get_lp_token(address pool) external view returns (address);

  function get_pool_from_lp_token(address lp) external view returns (address);
}

File 13 of 72 : ILiquidityGauge.sol
pragma solidity 0.6.12;

interface ILiquidityGauge {
  function minter() external view returns (address);

  function crv_token() external view returns (address);

  function lp_token() external view returns (address);

  function balanceOf(address addr) external view returns (uint);

  function deposit(uint value) external;

  function withdraw(uint value) external;
}

File 14 of 72 : WMasterChef.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/token/ERC1155/ERC1155.sol';
import 'OpenZeppelin/[email protected]/contracts/token/ERC20/IERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/token/ERC20/SafeERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/utils/ReentrancyGuard.sol';

import '../utils/HomoraMath.sol';
import '../../interfaces/IERC20Wrapper.sol';
import '../../interfaces/IMasterChef.sol';

contract WMasterChef is ERC1155('WMasterChef'), ReentrancyGuard, IERC20Wrapper {
  using SafeMath for uint;
  using HomoraMath for uint;
  using SafeERC20 for IERC20;

  IMasterChef public immutable chef;
  IERC20 public immutable sushi;

  constructor(IMasterChef _chef) public {
    chef = _chef;
    sushi = IERC20(_chef.sushi());
  }

  function encodeId(uint pid, uint sushiPerShare) public pure returns (uint id) {
    require(pid < (1 << 16), 'bad pid');
    require(sushiPerShare < (1 << 240), 'bad sushi per share');
    return (pid << 240) | sushiPerShare;
  }

  function decodeId(uint id) public pure returns (uint pid, uint sushiPerShare) {
    pid = id >> 240; // First 16 bits
    sushiPerShare = id & ((1 << 240) - 1); // Last 240 bits
  }

  /// @dev Return the underlying ERC-20 for the given ERC-1155 token id.
  function getUnderlyingToken(uint id) external view override returns (address) {
    (uint pid, ) = decodeId(id);
    (address lpToken, , , ) = chef.poolInfo(pid);
    return lpToken;
  }

  /// @dev Return the conversion rate from ERC-1155 to ERC-20, multiplied by 2**112.
  function getUnderlyingRate(uint) external view override returns (uint) {
    return 2**112;
  }

  /// @dev Mint ERC1155 token for the given pool id.
  /// @return The token id that got minted.
  function mint(uint pid, uint amount) external nonReentrant returns (uint) {
    (address lpToken, , , ) = chef.poolInfo(pid);
    IERC20(lpToken).safeTransferFrom(msg.sender, address(this), amount);
    if (IERC20(lpToken).allowance(address(this), address(chef)) != uint(-1)) {
      // We only need to do this once per pool, as LP token's allowance won't decrease if it's -1.
      IERC20(lpToken).approve(address(chef), uint(-1));
    }
    chef.deposit(pid, amount);
    (, , , uint sushiPerShare) = chef.poolInfo(pid);
    uint id = encodeId(pid, sushiPerShare);
    _mint(msg.sender, id, amount, '');
    return id;
  }

  /// @dev Burn ERC1155 token to redeem LP ERC20 token back plus SUSHI rewards.
  /// @return The pool id that that you received LP token back.
  function burn(uint id, uint amount) external nonReentrant returns (uint) {
    if (amount == uint(-1)) {
      amount = balanceOf(msg.sender, id);
    }
    (uint pid, uint stSushiPerShare) = decodeId(id);
    _burn(msg.sender, id, amount);
    chef.withdraw(pid, amount);
    (address lpToken, , , uint enSushiPerShare) = chef.poolInfo(pid);
    IERC20(lpToken).safeTransfer(msg.sender, amount);
    uint stSushi = stSushiPerShare.mul(amount).divCeil(1e12);
    uint enSushi = enSushiPerShare.mul(amount).div(1e12);
    if (enSushi > stSushi) {
      sushi.safeTransfer(msg.sender, enSushi.sub(stSushi));
    }
    return pid;
  }

  /// @dev Burn ERC1155 token to redeem LP ERC20 token back without taking SUSHI rewards.
  /// @return The pool id that that you received LP token back.
  function emergencyBurn(uint id, uint amount) external nonReentrant returns (uint) {
    (uint pid, ) = decodeId(id);
    _burn(msg.sender, id, amount);
    chef.withdraw(pid, amount);
    (address lpToken, , , ) = chef.poolInfo(pid);
    IERC20(lpToken).safeTransfer(msg.sender, amount);
    return pid;
  }
}

File 15 of 72 : IMasterChef.sol
pragma solidity 0.6.12;

interface IMasterChef {
  function sushi() external view returns (address);

  function poolInfo(uint pid)
    external
    view
    returns (
      address lpToken,
      uint allocPoint,
      uint lastRewardBlock,
      uint accSushiPerShare
    );

  function deposit(uint pid, uint amount) external;

  function withdraw(uint pid, uint amount) external;
}

File 16 of 72 : UniswapV2SpellV1.sol
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

import 'OpenZeppelin/[email protected]/contracts/token/ERC20/IERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/math/SafeMath.sol';

import './BasicSpell.sol';
import '../utils/HomoraMath.sol';
import '../../interfaces/IUniswapV2Factory.sol';
import '../../interfaces/IUniswapV2Router02.sol';
import '../../interfaces/IUniswapV2Pair.sol';
import '../../interfaces/IWStakingRewards.sol';

contract UniswapV2SpellV1 is BasicSpell {
  using SafeMath for uint;
  using HomoraMath for uint;

  IUniswapV2Factory public immutable factory;
  IUniswapV2Router02 public immutable router;

  mapping(address => mapping(address => address)) public pairs;

  constructor(
    IBank _bank,
    address _werc20,
    IUniswapV2Router02 _router
  ) public BasicSpell(_bank, _werc20, _router.WETH()) {
    router = _router;
    factory = IUniswapV2Factory(_router.factory());
  }

  function getPair(address tokenA, address tokenB) public returns (address) {
    address lp = pairs[tokenA][tokenB];
    if (lp == address(0)) {
      lp = factory.getPair(tokenA, tokenB);
      require(lp != address(0), 'no lp token');
      ensureApprove(tokenA, address(router));
      ensureApprove(tokenB, address(router));
      ensureApprove(lp, address(router));
      pairs[tokenA][tokenB] = lp;
      pairs[tokenB][tokenA] = lp;
    }
    return lp;
  }

  /// @dev Compute optimal deposit amount
  /// @param amtA amount of token A desired to deposit
  /// @param amtB amount of token B desired to deposit
  /// @param resA amount of token A in reserve
  /// @param resB amount of token B in reserve
  function optimalDeposit(
    uint amtA,
    uint amtB,
    uint resA,
    uint resB
  ) internal pure returns (uint swapAmt, bool isReversed) {
    if (amtA.mul(resB) >= amtB.mul(resA)) {
      swapAmt = _optimalDepositA(amtA, amtB, resA, resB);
      isReversed = false;
    } else {
      swapAmt = _optimalDepositA(amtB, amtA, resB, resA);
      isReversed = true;
    }
  }

  /// @dev Compute optimal deposit amount helper.
  /// @param amtA amount of token A desired to deposit
  /// @param amtB amount of token B desired to deposit
  /// @param resA amount of token A in reserve
  /// @param resB amount of token B in reserve
  /// Formula: https://blog.alphafinance.io/byot/
  function _optimalDepositA(
    uint amtA,
    uint amtB,
    uint resA,
    uint resB
  ) internal pure returns (uint) {
    require(amtA.mul(resB) >= amtB.mul(resA), 'Reversed');
    uint a = 997;
    uint b = uint(1997).mul(resA);
    uint _c = (amtA.mul(resB)).sub(amtB.mul(resA));
    uint c = _c.mul(1000).div(amtB.add(resB)).mul(resA);
    uint d = a.mul(c).mul(4);
    uint e = HomoraMath.sqrt(b.mul(b).add(d));
    uint numerator = e.sub(b);
    uint denominator = a.mul(2);
    return numerator.div(denominator);
  }

  struct Amounts {
    uint amtAUser;
    uint amtBUser;
    uint amtLPUser;
    uint amtABorrow;
    uint amtBBorrow;
    uint amtLPBorrow;
    uint amtAMin;
    uint amtBMin;
  }

  function addLiquidityInternal(
    address tokenA,
    address tokenB,
    Amounts calldata amt
  ) internal {
    address lp = getPair(tokenA, tokenB);

    // 1. Get user input amounts
    doTransmitETH();
    doTransmit(tokenA, amt.amtAUser);
    doTransmit(tokenB, amt.amtBUser);
    doTransmit(lp, amt.amtLPUser);

    // 2. Borrow specified amounts
    doBorrow(tokenA, amt.amtABorrow);
    doBorrow(tokenB, amt.amtBBorrow);
    doBorrow(lp, amt.amtLPBorrow);

    // 3. Calculate optimal swap amount
    uint swapAmt;
    bool isReversed;
    {
      uint amtA = IERC20(tokenA).balanceOf(address(this));
      uint amtB = IERC20(tokenB).balanceOf(address(this));
      uint resA;
      uint resB;
      if (IUniswapV2Pair(lp).token0() == tokenA) {
        (resA, resB, ) = IUniswapV2Pair(lp).getReserves();
      } else {
        (resB, resA, ) = IUniswapV2Pair(lp).getReserves();
      }
      (swapAmt, isReversed) = optimalDeposit(amtA, amtB, resA, resB);
    }

    // 4. Swap optimal amount
    {
      address[] memory path = new address[](2);
      (path[0], path[1]) = isReversed ? (tokenB, tokenA) : (tokenA, tokenB);
      router.swapExactTokensForTokens(swapAmt, 0, path, address(this), now);
    }

    // 5. Add liquidity
    router.addLiquidity(
      tokenA,
      tokenB,
      IERC20(tokenA).balanceOf(address(this)),
      IERC20(tokenB).balanceOf(address(this)),
      amt.amtAMin,
      amt.amtBMin,
      address(this),
      now
    );
  }

  function addLiquidityWERC20(
    address tokenA,
    address tokenB,
    Amounts calldata amt
  ) external payable {
    address lp = getPair(tokenA, tokenB);
    // 1-5. add liquidity
    addLiquidityInternal(tokenA, tokenB, amt);

    // 6. Put collateral
    doPutCollateral(lp, IERC20(lp).balanceOf(address(this)));

    // 7. Refund leftovers to users
    doRefundETH();
    doRefund(tokenA);
    doRefund(tokenB);
  }

  function addLiquidityWStakingRewards(
    address tokenA,
    address tokenB,
    Amounts calldata amt,
    address wstaking
  ) external payable {
    address lp = getPair(tokenA, tokenB);
    address reward = IWStakingRewards(wstaking).reward();

    // 1-5. add liquidity
    addLiquidityInternal(tokenA, tokenB, amt);

    // 6. Take out collateral
    uint positionId = bank.POSITION_ID();
    (, address collToken, uint collId, uint collSize) = bank.getPositionInfo(positionId);
    if (collSize > 0) {
      require(IWStakingRewards(collToken).getUnderlyingToken(collId) == lp, 'incorrect underlying');
      bank.takeCollateral(wstaking, collId, collSize);
      IWStakingRewards(wstaking).burn(collId, collSize);
    }

    // 7. Put collateral
    ensureApprove(lp, wstaking);
    uint amount = IERC20(lp).balanceOf(address(this));
    uint id = IWStakingRewards(wstaking).mint(amount);
    if (!IWStakingRewards(wstaking).isApprovedForAll(address(this), address(bank))) {
      IWStakingRewards(wstaking).setApprovalForAll(address(bank), true);
    }
    bank.putCollateral(address(wstaking), id, amount);

    // 8. Refund leftovers to users
    doRefundETH();
    doRefund(tokenA);
    doRefund(tokenB);

    // 9. Refund reward
    doRefund(reward);
  }

  struct RepayAmounts {
    uint amtLPTake;
    uint amtLPWithdraw;
    uint amtARepay;
    uint amtBRepay;
    uint amtLPRepay;
    uint amtAMin;
    uint amtBMin;
  }

  function removeLiquidityInternal(
    address tokenA,
    address tokenB,
    RepayAmounts calldata amt
  ) internal {
    address lp = getPair(tokenA, tokenB);
    uint positionId = bank.POSITION_ID();

    uint amtARepay = amt.amtARepay;
    uint amtBRepay = amt.amtBRepay;
    uint amtLPRepay = amt.amtLPRepay;

    // 2. Compute repay amount if MAX_INT is supplied (max debt)
    if (amtARepay == uint(-1)) {
      amtARepay = bank.borrowBalanceCurrent(positionId, tokenA);
    }
    if (amtBRepay == uint(-1)) {
      amtBRepay = bank.borrowBalanceCurrent(positionId, tokenB);
    }
    if (amtLPRepay == uint(-1)) {
      amtLPRepay = bank.borrowBalanceCurrent(positionId, lp);
    }

    // 3. Compute amount to actually remove
    uint amtLPToRemove = IERC20(lp).balanceOf(address(this)).sub(amt.amtLPWithdraw);

    // 4. Remove liquidity
    (uint amtA, uint amtB) =
      router.removeLiquidity(tokenA, tokenB, amtLPToRemove, 0, 0, address(this), now);

    // 5. MinimizeTrading
    uint amtADesired = amtARepay.add(amt.amtAMin);
    uint amtBDesired = amtBRepay.add(amt.amtBMin);

    if (amtA < amtADesired && amtB >= amtBDesired) {
      address[] memory path = new address[](2);
      (path[0], path[1]) = (tokenB, tokenA);
      router.swapTokensForExactTokens(
        amtADesired.sub(amtA),
        amtB.sub(amtBDesired),
        path,
        address(this),
        now
      );
    } else if (amtA >= amtADesired && amtB < amtBDesired) {
      address[] memory path = new address[](2);
      (path[0], path[1]) = (tokenA, tokenB);
      router.swapTokensForExactTokens(
        amtBDesired.sub(amtB),
        amtA.sub(amtADesired),
        path,
        address(this),
        now
      );
    }

    // 6. Repay
    doRepay(tokenA, amtARepay);
    doRepay(tokenB, amtBRepay);
    doRepay(lp, amtLPRepay);

    // 7. Slippage control
    require(IERC20(tokenA).balanceOf(address(this)) >= amt.amtAMin);
    require(IERC20(tokenB).balanceOf(address(this)) >= amt.amtBMin);
    require(IERC20(lp).balanceOf(address(this)) >= amt.amtLPWithdraw);

    // 8. Refund leftover
    doRefundETH();
    doRefund(tokenA);
    doRefund(tokenB);
    doRefund(lp);
  }

  function removeLiquidityWERC20(
    address tokenA,
    address tokenB,
    RepayAmounts calldata amt
  ) external {
    address lp = getPair(tokenA, tokenB);

    // 1. Take out collateral
    doTakeCollateral(lp, amt.amtLPTake);

    // 2-8. remove liquidity
    removeLiquidityInternal(tokenA, tokenB, amt);
  }

  function removeLiquidityWStakingRewards(
    address tokenA,
    address tokenB,
    RepayAmounts calldata amt,
    address wstaking
  ) external {
    address lp = getPair(tokenA, tokenB);
    uint positionId = bank.POSITION_ID();
    (, address collToken, uint collId, ) = bank.getPositionInfo(positionId);
    address reward = IWStakingRewards(wstaking).reward();

    // 1. Take out collateral
    require(IWStakingRewards(collToken).getUnderlyingToken(collId) == lp, 'incorrect underlying');
    bank.takeCollateral(wstaking, collId, amt.amtLPTake);
    IWStakingRewards(wstaking).burn(collId, amt.amtLPTake);

    // 2-8. remove liquidity
    removeLiquidityInternal(tokenA, tokenB, amt);

    // 9. Refund reward
    doRefund(reward);
  }

  function harvestWStakingRewards(address wstaking) external {
    address reward = IWStakingRewards(wstaking).reward();
    uint positionId = bank.POSITION_ID();
    (, , uint collId, ) = bank.getPositionInfo(positionId);
    address lp = IWStakingRewards(wstaking).getUnderlyingToken(collId);

    // 1. Take out collateral
    bank.takeCollateral(wstaking, collId, uint(-1));
    IWStakingRewards(wstaking).burn(collId, uint(-1));

    // 2. put collateral
    uint amount = IERC20(lp).balanceOf(address(this));
    ensureApprove(lp, wstaking);
    uint id = IWStakingRewards(wstaking).mint(amount);
    bank.putCollateral(wstaking, id, amount);

    // 3. Refund reward
    doRefund(reward);
  }
}

File 17 of 72 : BasicSpell.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/token/ERC20/IERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/token/ERC20/SafeERC20.sol';

import '../utils/ERC1155NaiveReceiver.sol';
import '../../interfaces/IBank.sol';
import '../../interfaces/IWERC20.sol';
import '../../interfaces/IWETH.sol';

contract BasicSpell is ERC1155NaiveReceiver {
  using SafeERC20 for IERC20;

  IBank public immutable bank;
  IWERC20 public immutable werc20;
  address public immutable weth;

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

  constructor(
    IBank _bank,
    address _werc20,
    address _weth
  ) public {
    bank = _bank;
    werc20 = IWERC20(_werc20);
    weth = _weth;
    ensureApprove(_weth, address(_bank));
    IWERC20(_werc20).setApprovalForAll(address(_bank), true);
  }

  /// @dev Ensure that the spell approve the given spender to spend all of its tokens.
  /// @param token The token to approve.
  /// @param spender The spender to allow spending.
  /// NOTE: This is safe because spell is never built to hold fund custody.
  function ensureApprove(address token, address spender) public {
    if (!approved[token][spender]) {
      IERC20(token).safeApprove(spender, uint(-1));
      approved[token][spender] = true;
    }
  }

  /// @dev Internal call to convert msg.value ETH to WETH inside the contract.
  function doTransmitETH() internal {
    if (msg.value > 0) {
      IWETH(weth).deposit{value: msg.value}();
    }
  }

  /// @dev Internal call to transmit tokens from the bank if amount is positive.
  /// @param token The token to perform the transmit action.
  /// @param amount The amount to transmit.
  function doTransmit(address token, uint amount) internal {
    if (amount > 0) {
      bank.transmit(token, amount);
    }
  }

  /// @dev Internal call to refund tokens to the current bank executor.
  /// @param token The token to perform the refund action.
  function doRefund(address token) internal {
    uint balance = IERC20(token).balanceOf(address(this));
    if (balance > 0) {
      IERC20(token).safeTransfer(bank.EXECUTOR(), balance);
    }
  }

  /// @dev Internal call to refund all WETH to the current executor as native ETH.
  function doRefundETH() internal {
    uint balance = IWETH(weth).balanceOf(address(this));
    if (balance > 0) {
      IWETH(weth).withdraw(balance);
      (bool success, ) = bank.EXECUTOR().call{value: balance}(new bytes(0));
      require(success, 'refund ETH failed');
    }
  }

  /// @dev Internal call to borrow tokens from the bank on behalf of the current executor.
  /// @param token The token to borrow from the bank.
  /// @param amount The amount to borrow.
  function doBorrow(address token, uint amount) internal {
    if (amount > 0) {
      bank.borrow(token, amount);
    }
  }

  /// @dev Internal call to repay tokens to the bank on behalf of the current executor.
  /// @param token The token to repay to the bank.
  /// @param amount The amount to repay.
  function doRepay(address token, uint amount) internal {
    if (amount > 0) {
      ensureApprove(token, address(bank));
      bank.repay(token, amount);
    }
  }

  /// @dev Internal call to put collateral tokens to the bank.
  /// @param token The token to put to the bank.
  /// @param amount The amount to put to the bank.
  function doPutCollateral(address token, uint amount) internal {
    if (amount > 0) {
      ensureApprove(token, address(werc20));
      werc20.mint(token, amount);
      bank.putCollateral(address(werc20), uint(token), amount);
    }
  }

  /// @dev Internal call to take collateral tokens from the bank.
  /// @param token The token to take back.
  /// @param amount The amount to take back.
  function doTakeCollateral(address token, uint amount) internal {
    if (amount > 0) {
      if (amount == uint(-1)) {
        (, , , amount) = bank.getPositionInfo(bank.POSITION_ID());
      }
      bank.takeCollateral(address(werc20), uint(token), amount);
      werc20.burn(token, amount);
    }
  }

  receive() external payable {
    require(msg.sender == weth, 'ETH must come from WETH');
  }
}

File 18 of 72 : ERC1155NaiveReceiver.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/token/ERC1155/ERC1155Receiver.sol';
import 'OpenZeppelin/[email protected]/contracts/token/ERC1155/IERC1155Receiver.sol';

contract ERC1155NaiveReceiver is ERC1155Receiver {
  function onERC1155Received(
    address operator,
    address from,
    uint id,
    uint value,
    bytes calldata data
  ) external override returns (bytes4) {
    return this.onERC1155Received.selector;
  }

  function onERC1155BatchReceived(
    address operator,
    address from,
    uint[] calldata ids,
    uint[] calldata values,
    bytes calldata data
  ) external override returns (bytes4) {
    return this.onERC1155BatchReceived.selector;
  }
}

File 19 of 72 : IBank.sol
pragma solidity 0.6.12;

interface IBank {
  /// The governor adds a new bank gets added to the system.
  event AddBank(address token, address cToken);
  /// The governor sets the address of the oracle smart contract.
  event SetOracle(address oracle);
  /// The governor sets the basis point fee of the bank.
  event SetFeeBps(uint feeBps);
  /// The governor withdraw tokens from the reserve of a bank.
  event WithdrawReserve(address user, address token, uint amount);
  /// Someone borrows tokens from a bank via a spell caller.
  event Borrow(uint positionId, address caller, address token, uint amount, uint share);
  /// Someone repays tokens to a bank via a spell caller.
  event Repay(uint positionId, address caller, address token, uint amount, uint share);
  /// Someone puts tokens as collateral via a spell caller.
  event PutCollateral(uint positionId, address caller, address token, uint id, uint amount);
  /// Someone takes tokens from collateral via a spell caller.
  event TakeCollateral(uint positionId, address caller, address token, uint id, uint amount);
  /// Someone calls liquidatation on a position, paying debt and taking collateral tokens.
  event Liquidate(
    uint positionId,
    address liquidator,
    address debtToken,
    uint amount,
    uint share,
    uint bounty
  );

  /// @dev Return the current position while under execution.
  function POSITION_ID() external view returns (uint);

  /// @dev Return the current target while under execution.
  function SPELL() external view returns (address);

  /// @dev Return the current executor (the owner of the current position).
  function EXECUTOR() external view returns (address);

  /// @dev Return bank information for the given token.
  function getBankInfo(address token)
    external
    view
    returns (
      bool isListed,
      address cToken,
      uint reserve,
      uint totalDebt,
      uint totalShare
    );

  /// @dev Return position information for the given position id.
  function getPositionInfo(uint positionId)
    external
    view
    returns (
      address owner,
      address collToken,
      uint collId,
      uint collateralSize
    );

  /// @dev Return the borrow balance for given positon and token without trigger interest accrual.
  function borrowBalanceStored(uint positionId, address token) external view returns (uint);

  /// @dev Trigger interest accrual and return the current borrow balance.
  function borrowBalanceCurrent(uint positionId, address token) external returns (uint);

  /// @dev Borrow tokens from the bank.
  function borrow(address token, uint amount) external;

  /// @dev Repays tokens to the bank.
  function repay(address token, uint amountCall) external;

  /// @dev Transmit user assets to the spell.
  function transmit(address token, uint amount) external;

  /// @dev Put more collateral for users.
  function putCollateral(
    address collToken,
    uint collId,
    uint amountCall
  ) external;

  /// @dev Take some collateral back.
  function takeCollateral(
    address collToken,
    uint collId,
    uint amount
  ) external;

  /// @dev Liquidate a position.
  function liquidate(
    uint positionId,
    address debtToken,
    uint amountCall
  ) external;

  function getBorrowETHValue(uint positionId) external view returns (uint);

  function accrue(address token) external;

  function nextPositionId() external view returns (uint);
}

File 20 of 72 : IWETH.sol
pragma solidity 0.6.12;

interface IWETH {
  function balanceOf(address user) external returns (uint);

  function approve(address to, uint value) external returns (bool);

  function transfer(address to, uint value) external returns (bool);

  function deposit() external payable;

  function withdraw(uint) external;
}

File 21 of 72 : IUniswapV2Factory.sol
pragma solidity >=0.5.0;

// https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/interfaces/IUniswapV2Factory.sol

interface IUniswapV2Factory {
  event PairCreated(address indexed token0, address indexed token1, address pair, uint);

  function feeTo() external view returns (address);

  function feeToSetter() external view returns (address);

  function getPair(address tokenA, address tokenB) external view returns (address pair);

  function allPairs(uint) external view returns (address pair);

  function allPairsLength() external view returns (uint);

  function createPair(address tokenA, address tokenB) external returns (address pair);

  function setFeeTo(address) external;

  function setFeeToSetter(address) external;
}

File 22 of 72 : IUniswapV2Router02.sol
pragma solidity >=0.6.2;

// https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/interfaces/IUniswapV2Router02.sol

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 23 of 72 : IUniswapV2Router01.sol
pragma solidity >=0.6.2;

// https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/interfaces/IUniswapV2Router01.sol

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

// https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/interfaces/IUniswapV2Pair.sol

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 72 : IWStakingRewards.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/token/ERC1155/IERC1155.sol';

import './IERC20Wrapper.sol';

interface IWStakingRewards is IERC1155, IERC20Wrapper {
  /// @dev Mint ERC1155 token for the given ERC20 token.
  function mint(uint amount) external returns (uint id);

  /// @dev Burn ERC1155 token to redeem ERC20 token back.
  function burn(uint id, uint amount) external returns (uint);

  function reward() external returns (address);
}

File 26 of 72 : CurveSpellV1.sol
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

import 'OpenZeppelin/[email protected]/contracts/token/ERC20/IERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/math/SafeMath.sol';

import './BasicSpell.sol';
import '../utils/HomoraMath.sol';
import '../../interfaces/ICurvePool.sol';
import '../../interfaces/ICurveRegistry.sol';
import '../../interfaces/IWLiquidityGauge.sol';
import '../../interfaces/IWERC20.sol';

contract CurveSpellV1 is BasicSpell {
  using SafeMath for uint;
  using HomoraMath for uint;

  ICurveRegistry public immutable registry;
  IWLiquidityGauge public immutable wgauge;
  address public immutable crv;
  mapping(address => address[]) public ulTokens; // lpToken -> underlying token array
  mapping(address => address) public poolOf; // lpToken -> pool

  constructor(
    IBank _bank,
    address _werc20,
    address _weth,
    address _wgauge
  ) public BasicSpell(_bank, _werc20, _weth) {
    wgauge = IWLiquidityGauge(_wgauge);
    IWLiquidityGauge(_wgauge).setApprovalForAll(address(_bank), true);
    registry = IWLiquidityGauge(_wgauge).registry();
    crv = address(IWLiquidityGauge(_wgauge).crv());
  }

  /// @dev Return pool address given LP token and update pool info if not exist.
  /// @param lp LP token to find the corresponding pool.
  function getPool(address lp) public returns (address) {
    address pool = poolOf[lp];
    if (pool == address(0)) {
      require(lp != address(0), 'no lp token');
      pool = registry.get_pool_from_lp_token(lp);
      require(pool != address(0), 'no corresponding pool for lp token');
      poolOf[lp] = pool;
      uint n = registry.get_n_coins(pool);
      address[8] memory tokens = registry.get_coins(pool);
      ulTokens[lp] = new address[](n);
      for (uint i = 0; i < n; i++) {
        ulTokens[lp][i] = tokens[i];
      }
    }
    return pool;
  }

  function ensureApproveN(address lp, uint n) public {
    require(ulTokens[lp].length == n, 'incorrect pool length');
    address pool = poolOf[lp];
    address[] memory tokens = ulTokens[lp];
    for (uint idx = 0; idx < n; idx++) {
      ensureApprove(tokens[idx], pool);
    }
  }

  /// @dev add liquidity for pools with 2 underlying tokens
  function addLiquidity2(
    address lp,
    uint[2] calldata amtsUser,
    uint amtLPUser,
    uint[2] calldata amtsBorrow,
    uint amtLPBorrow,
    uint minLPMint,
    uint pid,
    uint gid
  ) external {
    address pool = getPool(lp);
    require(ulTokens[lp].length == 2, 'incorrect pool length');
    require(wgauge.getUnderlyingToken(wgauge.encodeId(pid, gid, 0)) == lp, 'incorrect underlying');
    address[] memory tokens = ulTokens[lp];

    // 0. Take out collateral
    uint positionId = bank.POSITION_ID();
    (, , uint collId, uint collSize) = bank.getPositionInfo(positionId);
    if (collSize > 0) {
      (uint decodedPid, uint decodedGid, ) = wgauge.decodeId(collId);
      require(decodedPid == pid && decodedGid == gid, 'incorrect coll id');
      bank.takeCollateral(address(wgauge), collId, collSize);
      wgauge.burn(collId, collSize);
    }

    // 1. Ensure approve 2 underlying tokens
    ensureApproveN(lp, 2);

    // 2. Get user input amounts
    for (uint i = 0; i < 2; i++) doTransmit(tokens[i], amtsUser[i]);
    doTransmit(lp, amtLPUser);

    // 3. Borrow specified amounts
    for (uint i = 0; i < 2; i++) doBorrow(tokens[i], amtsBorrow[i]);
    doBorrow(lp, amtLPBorrow);

    // 4. add liquidity
    uint[2] memory suppliedAmts;
    for (uint i = 0; i < 2; i++) {
      suppliedAmts[i] = IERC20(tokens[i]).balanceOf(address(this));
    }
    ICurvePool(pool).add_liquidity(suppliedAmts, minLPMint);

    // 5. Put collateral
    uint amount = IERC20(lp).balanceOf(address(this));
    ensureApprove(lp, address(wgauge));
    uint id = wgauge.mint(pid, gid, amount);
    bank.putCollateral(address(wgauge), id, amount);

    // 6. Refund
    for (uint i = 0; i < 2; i++) doRefund(tokens[i]);

    // 7. Refund crv
    doRefund(crv);
  }

  /// @dev add liquidity for pools with 3 underlying tokens
  function addLiquidity3(
    address lp,
    uint[3] calldata amtsUser,
    uint amtLPUser,
    uint[3] calldata amtsBorrow,
    uint amtLPBorrow,
    uint minLPMint,
    uint pid,
    uint gid
  ) external {
    address pool = getPool(lp);
    require(ulTokens[lp].length == 3, 'incorrect pool length');
    require(wgauge.getUnderlyingToken(wgauge.encodeId(pid, gid, 0)) == lp, 'incorrect underlying');
    address[] memory tokens = ulTokens[lp];

    // 0. take out collateral
    uint positionId = bank.POSITION_ID();
    (, , uint collId, uint collSize) = bank.getPositionInfo(positionId);
    if (collSize > 0) {
      (uint decodedPid, uint decodedGid, ) = wgauge.decodeId(collId);
      require(decodedPid == pid && decodedGid == gid, 'incorrect coll id');
      bank.takeCollateral(address(wgauge), collId, collSize);
      wgauge.burn(collId, collSize);
    }

    // 1. Ensure approve 3 underlying tokens
    ensureApproveN(lp, 3);

    // 2. Get user input amounts
    for (uint i = 0; i < 3; i++) doTransmit(tokens[i], amtsUser[i]);
    doTransmit(lp, amtLPUser);

    // 3. Borrow specified amounts
    for (uint i = 0; i < 3; i++) doBorrow(tokens[i], amtsBorrow[i]);
    doBorrow(lp, amtLPBorrow);

    // 4. add liquidity
    uint[3] memory suppliedAmts;
    for (uint i = 0; i < 3; i++) {
      suppliedAmts[i] = IERC20(tokens[i]).balanceOf(address(this));
    }
    ICurvePool(pool).add_liquidity(suppliedAmts, minLPMint);

    // 5. put collateral
    uint amount = IERC20(lp).balanceOf(address(this));
    ensureApprove(lp, address(wgauge));
    uint id = wgauge.mint(pid, gid, amount);
    bank.putCollateral(address(wgauge), id, amount);

    // 6. Refund
    for (uint i = 0; i < 3; i++) doRefund(tokens[i]);

    // 7. Refund crv
    doRefund(crv);
  }

  /// @dev add liquidity for pools with 4 underlying tokens
  function addLiquidity4(
    address lp,
    uint[4] calldata amtsUser,
    uint amtLPUser,
    uint[4] calldata amtsBorrow,
    uint amtLPBorrow,
    uint minLPMint,
    uint pid,
    uint gid
  ) external {
    address pool = getPool(lp);
    require(ulTokens[lp].length == 4, 'incorrect pool length');
    require(wgauge.getUnderlyingToken(wgauge.encodeId(pid, gid, 0)) == lp, 'incorrect underlying');
    address[] memory tokens = ulTokens[lp];

    // 0. Take out collateral
    uint positionId = bank.POSITION_ID();
    (, , uint collId, uint collSize) = bank.getPositionInfo(positionId);
    if (collSize > 0) {
      (uint decodedPid, uint decodedGid, ) = wgauge.decodeId(collId);
      require(decodedPid == pid && decodedGid == gid, 'incorrect coll id');
      bank.takeCollateral(address(wgauge), collId, collSize);
      wgauge.burn(collId, collSize);
    }

    // 1. Ensure approve 4 underlying tokens
    ensureApproveN(lp, 4);

    // 2. Get user input amounts
    for (uint i = 0; i < 4; i++) doTransmit(tokens[i], amtsUser[i]);
    doTransmit(lp, amtLPUser);

    // 3. Borrow specified amounts
    for (uint i = 0; i < 4; i++) doBorrow(tokens[i], amtsBorrow[i]);
    doBorrow(lp, amtLPBorrow);

    // 4. add liquidity
    uint[4] memory suppliedAmts;
    for (uint i = 0; i < 4; i++) {
      suppliedAmts[i] = IERC20(tokens[i]).balanceOf(address(this));
    }
    ICurvePool(pool).add_liquidity(suppliedAmts, minLPMint);

    // 5. Put collateral
    uint amount = IERC20(lp).balanceOf(address(this));
    ensureApprove(lp, address(wgauge));
    uint id = wgauge.mint(pid, gid, amount);
    bank.putCollateral(address(wgauge), id, amount);

    // 6. Refund
    for (uint i = 0; i < 4; i++) doRefund(tokens[i]);

    // 7. Refund crv
    doRefund(crv);
  }

  function removeLiquidity2(
    address lp,
    uint amtLPTake,
    uint amtLPWithdraw,
    uint[2] calldata amtsRepay,
    uint amtLPRepay,
    uint[2] calldata amtsMin
  ) external {
    address pool = getPool(lp);
    uint positionId = bank.POSITION_ID();
    (, address collToken, uint collId, ) = bank.getPositionInfo(positionId);
    require(IWLiquidityGauge(collToken).getUnderlyingToken(collId) == lp, 'incorrect underlying');
    address[] memory tokens = ulTokens[lp];

    // 0. Ensure approve
    ensureApproveN(lp, 2);

    // 1. Compute repay amount if MAX_INT is supplied (max debt)
    uint[2] memory actualAmtsRepay;
    for (uint i = 0; i < 2; i++) {
      actualAmtsRepay[i] = amtsRepay[i] == uint(-1)
        ? bank.borrowBalanceCurrent(positionId, tokens[i])
        : amtsRepay[i];
    }
    uint[2] memory amtsDesired;
    for (uint i = 0; i < 2; i++) {
      amtsDesired[i] = actualAmtsRepay[i].add(amtsMin[i]); // repay amt + slippage control
    }

    // 2. Take out collateral
    bank.takeCollateral(address(wgauge), collId, amtLPTake);
    wgauge.burn(collId, amtLPTake);

    // 3. Compute amount to actually remove. Remove to repay just enough
    uint amtLPToRemove;
    if (amtsDesired[0] > 0 || amtsDesired[1] > 0) {
      amtLPToRemove = IERC20(lp).balanceOf(address(this)).sub(amtLPWithdraw);
      ICurvePool(pool).remove_liquidity_imbalance(amtsDesired, amtLPToRemove);
    }

    // 4. Compute leftover amount to remove. Remove balancedly.
    amtLPToRemove = IERC20(lp).balanceOf(address(this)).sub(amtLPWithdraw);
    uint[2] memory mins;
    ICurvePool(pool).remove_liquidity(amtLPToRemove, mins);

    // 5. Repay
    for (uint i = 0; i < 2; i++) {
      doRepay(tokens[i], actualAmtsRepay[i]);
    }
    doRepay(lp, amtLPRepay);

    // 6. Refund
    for (uint i = 0; i < 2; i++) {
      doRefund(tokens[i]);
    }
    doRefund(lp);

    // 7. Refund crv
    doRefund(crv);
  }

  function removeLiquidity3(
    address lp,
    uint amtLPTake,
    uint amtLPWithdraw,
    uint[3] calldata amtsRepay,
    uint amtLPRepay,
    uint[3] calldata amtsMin
  ) external {
    address pool = getPool(lp);
    uint positionId = bank.POSITION_ID();
    (, address collToken, uint collId, ) = bank.getPositionInfo(positionId);
    require(IWLiquidityGauge(collToken).getUnderlyingToken(collId) == lp, 'incorrect underlying');
    address[] memory tokens = ulTokens[lp];

    // 0. Ensure approve
    ensureApproveN(lp, 3);

    // 1. Compute repay amount if MAX_INT is supplied (max debt)
    uint[3] memory actualAmtsRepay;
    for (uint i = 0; i < 3; i++) {
      actualAmtsRepay[i] = amtsRepay[i] == uint(-1)
        ? bank.borrowBalanceCurrent(positionId, tokens[i])
        : amtsRepay[i];
    }
    uint[3] memory amtsDesired;
    for (uint i = 0; i < 3; i++) {
      amtsDesired[i] = actualAmtsRepay[i].add(amtsMin[i]); // repay amt + slippage control
    }

    // 2. Take out collateral
    bank.takeCollateral(address(wgauge), collId, amtLPTake);
    wgauge.burn(collId, amtLPTake);

    // 3. Compute amount to actually remove. Remove to repay just enough
    uint amtLPToRemove;
    if (amtsDesired[0] > 0 || amtsDesired[1] > 0 || amtsDesired[2] > 0) {
      amtLPToRemove = IERC20(lp).balanceOf(address(this)).sub(amtLPWithdraw);
      ICurvePool(pool).remove_liquidity_imbalance(amtsDesired, amtLPToRemove);
    }

    // 4. Compute leftover amount to remove. Remove balancedly.
    amtLPToRemove = IERC20(lp).balanceOf(address(this)).sub(amtLPWithdraw);
    uint[3] memory mins;
    ICurvePool(pool).remove_liquidity(amtLPToRemove, mins);

    // 5. Repay
    for (uint i = 0; i < 3; i++) {
      doRepay(tokens[i], actualAmtsRepay[i]);
    }
    doRepay(lp, amtLPRepay);

    // 6. Refund
    for (uint i = 0; i < 3; i++) {
      doRefund(tokens[i]);
    }
    doRefund(lp);

    // 7. Refund crv
    doRefund(crv);
  }

  function removeLiquidity4(
    address lp,
    uint amtLPTake,
    uint amtLPWithdraw,
    uint[4] calldata amtsRepay,
    uint amtLPRepay,
    uint[4] calldata amtsMin
  ) external {
    address pool = getPool(lp);
    uint positionId = bank.POSITION_ID();
    (, address collToken, uint collId, ) = bank.getPositionInfo(positionId);
    require(IWLiquidityGauge(collToken).getUnderlyingToken(collId) == lp, 'incorrect underlying');
    address[] memory tokens = ulTokens[lp];

    // 0. Ensure approve
    ensureApproveN(lp, 4);

    // 1. Compute repay amount if MAX_INT is supplied (max debt)
    uint[4] memory actualAmtsRepay;
    for (uint i = 0; i < 4; i++) {
      actualAmtsRepay[i] = amtsRepay[i] == uint(-1)
        ? bank.borrowBalanceCurrent(positionId, tokens[i])
        : amtsRepay[i];
    }
    uint[4] memory amtsDesired;
    for (uint i = 0; i < 4; i++) {
      amtsDesired[i] = actualAmtsRepay[i].add(amtsMin[i]); // repay amt + slippage control
    }

    // 2. Take out collateral
    bank.takeCollateral(address(wgauge), collId, amtLPTake);
    wgauge.burn(collId, amtLPTake);

    // 3. Compute amount to actually remove. Remove to repay just enough
    uint amtLPToRemove;
    if (amtsDesired[0] > 0 || amtsDesired[1] > 0 || amtsDesired[2] > 0 || amtsDesired[3] > 0) {
      amtLPToRemove = IERC20(lp).balanceOf(address(this)).sub(amtLPWithdraw);
      ICurvePool(pool).remove_liquidity_imbalance(amtsDesired, amtLPToRemove);
    }

    // 4. Compute leftover amount to remove. Remove balancedly.
    amtLPToRemove = IERC20(lp).balanceOf(address(this)).sub(amtLPWithdraw);
    uint[4] memory mins;
    ICurvePool(pool).remove_liquidity(amtLPToRemove, mins);

    // 5. Repay
    for (uint i = 0; i < 4; i++) {
      doRepay(tokens[i], actualAmtsRepay[i]);
    }
    doRepay(lp, amtLPRepay);

    // 6. Refund
    for (uint i = 0; i < 4; i++) {
      doRefund(tokens[i]);
    }
    doRefund(lp);

    // 7. Refund crv
    doRefund(crv);
  }

  function harvest() external {
    uint positionId = bank.POSITION_ID();
    (, , uint collId, uint collSize) = bank.getPositionInfo(positionId);
    (uint pid, uint gid, ) = wgauge.decodeId(collId);
    address lp = wgauge.getUnderlyingToken(collId);

    // 1. Take out collateral
    bank.takeCollateral(address(wgauge), collId, collSize);
    wgauge.burn(collId, collSize);

    // 2. Put collateral
    uint amount = IERC20(lp).balanceOf(address(this));
    ensureApprove(lp, address(wgauge));
    uint id = wgauge.mint(pid, gid, amount);
    bank.putCollateral(address(wgauge), id, amount);

    // 3. Refund crv
    doRefund(crv);
  }
}

File 27 of 72 : ICurvePool.sol
pragma solidity 0.6.12;

interface ICurvePool {
  function add_liquidity(uint[2] calldata, uint) external;

  function add_liquidity(uint[3] calldata, uint) external;

  function add_liquidity(uint[4] calldata, uint) external;

  function remove_liquidity(uint, uint[2] calldata) external;

  function remove_liquidity(uint, uint[3] calldata) external;

  function remove_liquidity(uint, uint[4] calldata) external;

  function remove_liquidity_imbalance(uint[2] calldata, uint) external;

  function remove_liquidity_imbalance(uint[3] calldata, uint) external;

  function remove_liquidity_imbalance(uint[4] calldata, uint) external;

  function remove_liquidity_one_coin(
    uint,
    int128,
    uint
  ) external;

  function get_virtual_price() external view returns (uint);
}

File 28 of 72 : IWLiquidityGauge.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/token/ERC1155/IERC1155.sol';
import 'OpenZeppelin/[email protected]/contracts/token/ERC20/IERC20.sol';

import './IERC20Wrapper.sol';
import './ICurveRegistry.sol';
import './ILiquidityGauge.sol';

interface IWLiquidityGauge is IERC1155, IERC20Wrapper {
  /// @dev Mint ERC1155 token for the given ERC20 token.
  function mint(
    uint pid,
    uint gid,
    uint amount
  ) external returns (uint id);

  /// @dev Burn ERC1155 token to redeem ERC20 token back.
  function burn(uint id, uint amount) external returns (uint pid);

  function crv() external returns (IERC20);

  function registry() external returns (ICurveRegistry);

  function encodeId(
    uint,
    uint,
    uint
  ) external pure returns (uint);

  function decodeId(uint id)
    external
    pure
    returns (
      uint,
      uint,
      uint
    );
}

File 29 of 72 : IWMasterChef.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/token/ERC1155/IERC1155.sol';
import 'OpenZeppelin/[email protected]/contracts/token/ERC20/IERC20.sol';

import './IERC20Wrapper.sol';
import './IMasterChef.sol';

interface IWMasterChef is IERC1155, IERC20Wrapper {
  /// @dev Mint ERC1155 token for the given ERC20 token.
  function mint(uint pid, uint amount) external returns (uint id);

  /// @dev Burn ERC1155 token to redeem ERC20 token back.
  function burn(uint id, uint amount) external returns (uint pid);

  function sushi() external returns (IERC20);

  function decodeId(uint id) external pure returns (uint, uint);

  function chef() external view returns (IMasterChef);
}

File 30 of 72 : BalancerSpellV1.sol
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

import 'OpenZeppelin/[email protected]/contracts/token/ERC20/IERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/math/SafeMath.sol';

import './BasicSpell.sol';
import '../utils/HomoraMath.sol';
import '../../interfaces/IBalancerPool.sol';
import '../../interfaces/IWStakingRewards.sol';

contract BalancerSpellV1 is BasicSpell {
  using SafeMath for uint;
  using HomoraMath for uint;

  mapping(address => address[2]) pairs; // mapping from lp token to underlying token (only pairs)

  constructor(
    IBank _bank,
    address _werc20,
    address _weth
  ) public BasicSpell(_bank, _werc20, _weth) {}

  function getPair(address lp) public returns (address tokenA, address tokenB) {
    address[2] memory ulTokens = pairs[lp];
    tokenA = ulTokens[0];
    tokenB = ulTokens[1];
    if (tokenA == address(0) || tokenB == address(0)) {
      address[] memory tokens = IBalancerPool(lp).getFinalTokens();
      require(tokens.length == 2, 'underlying tokens not 2');
      tokenA = tokens[0];
      tokenB = tokens[1];
      ensureApprove(tokenA, lp);
      ensureApprove(tokenB, lp);
    }
  }

  struct Amounts {
    uint amtAUser;
    uint amtBUser;
    uint amtLPUser;
    uint amtABorrow;
    uint amtBBorrow;
    uint amtLPBorrow;
    uint amtLPDesired;
  }

  function addLiquidityInternal(address lp, Amounts calldata amt) internal {
    (address tokenA, address tokenB) = getPair(lp);

    // 1. Get user input amounts
    doTransmitETH();
    doTransmit(tokenA, amt.amtAUser);
    doTransmit(tokenB, amt.amtBUser);
    doTransmit(lp, amt.amtLPUser);

    // 2. Borrow specified amounts
    doBorrow(tokenA, amt.amtABorrow);
    doBorrow(tokenB, amt.amtBBorrow);
    doBorrow(lp, amt.amtLPBorrow);

    // 3.1 Add Liquidity using equal value two side to minimize swap fee
    uint[] memory maxAmountsIn = new uint[](2);
    maxAmountsIn[0] = amt.amtAUser.add(amt.amtABorrow);
    maxAmountsIn[1] = amt.amtBUser.add(amt.amtBBorrow);
    uint totalLPSupply = IBalancerPool(lp).totalSupply();
    uint poolAmountFromA =
      maxAmountsIn[0].mul(1e18).div(IBalancerPool(lp).getBalance(tokenA)).mul(totalLPSupply).div(
        1e18
      ); // compute in reverse order of how Balancer's `joinPool` computes tokenAmountIn
    uint poolAmountFromB =
      maxAmountsIn[1].mul(1e18).div(IBalancerPool(lp).getBalance(tokenB)).mul(totalLPSupply).div(
        1e18
      ); // compute in reverse order of how Balancer's `joinPool` computes tokenAmountIn

    uint poolAmountOut = poolAmountFromA > poolAmountFromB ? poolAmountFromB : poolAmountFromA;
    if (poolAmountOut > 0) IBalancerPool(lp).joinPool(poolAmountOut, maxAmountsIn);

    // 3.2 Add Liquidity leftover for each token
    uint ABal = IERC20(tokenA).balanceOf(address(this));
    uint BBal = IERC20(tokenB).balanceOf(address(this));
    if (ABal > 0) IBalancerPool(lp).joinswapExternAmountIn(tokenA, ABal, 0);
    if (BBal > 0) IBalancerPool(lp).joinswapExternAmountIn(tokenB, BBal, 0);

    // 4. Slippage control
    uint lpBalance = IERC20(lp).balanceOf(address(this));
    require(lpBalance >= amt.amtLPDesired, 'lp desired not met');
  }

  /// @dev Add liquidity to Balancer pool (with 2 underlying tokens)
  function addLiquidityWERC20(address lp, Amounts calldata amt) external payable {
    // 1-4. add liquidity
    addLiquidityInternal(lp, amt);

    // 5. Put collateral
    doPutCollateral(lp, IERC20(lp).balanceOf(address(this)));

    // 6. Refund leftovers to users
    (address tokenA, address tokenB) = getPair(lp);
    doRefundETH();
    doRefund(tokenA);
    doRefund(tokenB);
  }

  /// @dev Add liquidity to Balancer pool (with 2 underlying tokens)
  function addLiquidityWStakingRewards(
    address lp,
    Amounts calldata amt,
    address wstaking
  ) external payable {
    // 1-4. add liquidity
    addLiquidityInternal(lp, amt);

    // 5. Take out collateral
    uint positionId = bank.POSITION_ID();
    (, address collToken, uint collId, uint collSize) = bank.getPositionInfo(positionId);
    if (collSize > 0) {
      require(IWStakingRewards(collToken).getUnderlyingToken(collId) == lp, 'incorrect underlying');
      bank.takeCollateral(wstaking, collId, collSize);
      IWStakingRewards(wstaking).burn(collId, collSize);
    }

    // 6. Put collateral
    ensureApprove(lp, wstaking);
    uint amount = IERC20(lp).balanceOf(address(this));
    uint id = IWStakingRewards(wstaking).mint(amount);
    if (!IWStakingRewards(wstaking).isApprovedForAll(address(this), address(bank))) {
      IWStakingRewards(wstaking).setApprovalForAll(address(bank), true);
    }
    bank.putCollateral(address(wstaking), id, amount);

    // 7. Refund leftovers to users
    (address tokenA, address tokenB) = getPair(lp);
    doRefundETH();
    doRefund(tokenA);
    doRefund(tokenB);

    // 8. Refund reward
    doRefund(IWStakingRewards(wstaking).reward());
  }

  struct RepayAmounts {
    uint amtLPTake;
    uint amtLPWithdraw;
    uint amtARepay;
    uint amtBRepay;
    uint amtLPRepay;
    uint amtAMin;
    uint amtBMin;
  }

  function removeLiquidityInternal(address lp, RepayAmounts calldata amt) internal {
    (address tokenA, address tokenB) = getPair(lp);
    uint amtARepay = amt.amtARepay;
    uint amtBRepay = amt.amtBRepay;
    uint amtLPRepay = amt.amtLPRepay;

    // 2. Compute repay amount if MAX_INT is supplied (max debt)
    {
      uint positionId = bank.POSITION_ID();
      if (amtARepay == uint(-1)) {
        amtARepay = bank.borrowBalanceCurrent(positionId, tokenA);
      }
      if (amtBRepay == uint(-1)) {
        amtBRepay = bank.borrowBalanceCurrent(positionId, tokenB);
      }
      if (amtLPRepay == uint(-1)) {
        amtLPRepay = bank.borrowBalanceCurrent(positionId, lp);
      }
    }

    // 3.1 Remove liquidity 2 sides
    uint amtLPToRemove = IERC20(lp).balanceOf(address(this)).sub(amt.amtLPWithdraw);

    uint[] memory minAmountsOut = new uint[](2);
    IBalancerPool(lp).exitPool(amtLPToRemove, minAmountsOut);

    // 3.2 Minimize trading
    uint amtADesired = amtARepay.add(amt.amtAMin);
    uint amtBDesired = amtBRepay.add(amt.amtBMin);

    uint amtA = IERC20(tokenA).balanceOf(address(this));
    uint amtB = IERC20(tokenB).balanceOf(address(this));

    if (amtA < amtADesired && amtB >= amtBDesired) {
      IBalancerPool(lp).swapExactAmountOut(
        tokenB,
        amtB.sub(amtBDesired),
        tokenA,
        amtADesired.sub(amtA),
        uint(-1)
      );
    } else if (amtA >= amtADesired && amtB < amtBDesired) {
      IBalancerPool(lp).swapExactAmountOut(
        tokenA,
        amtA.sub(amtADesired),
        tokenB,
        amtBDesired.sub(amtB),
        uint(-1)
      );
    }

    // 4. Repay
    doRepay(tokenA, amtARepay);
    doRepay(tokenB, amtBRepay);
    doRepay(lp, amtLPRepay);

    // 5. Slippage control
    require(IERC20(tokenA).balanceOf(address(this)) >= amt.amtAMin);
    require(IERC20(tokenB).balanceOf(address(this)) >= amt.amtBMin);
    require(IERC20(lp).balanceOf(address(this)) >= amt.amtLPWithdraw);

    // 6. Refund leftover
    doRefundETH();
    doRefund(tokenA);
    doRefund(tokenB);
    doRefund(lp);
  }

  function removeLiquidityWERC20(address lp, RepayAmounts calldata amt) external {
    // 1. Take out collateral
    doTakeCollateral(lp, amt.amtLPTake);

    // 2-6. remove liquidity
    removeLiquidityInternal(lp, amt);
  }

  function removeLiquidityWStakingRewards(
    address lp,
    RepayAmounts calldata amt,
    address wstaking
  ) external {
    uint positionId = bank.POSITION_ID();
    (, address collToken, uint collId, ) = bank.getPositionInfo(positionId);

    // 1. Take out collateral
    require(IWStakingRewards(collToken).getUnderlyingToken(collId) == lp, 'incorrect underlying');
    bank.takeCollateral(wstaking, collId, amt.amtLPTake);
    IWStakingRewards(wstaking).burn(collId, amt.amtLPTake);

    // 2-6. remove liquidity
    removeLiquidityInternal(lp, amt);

    // 7. Refund reward
    doRefund(IWStakingRewards(wstaking).reward());
  }

  function harvestWStakingRewards(address wstaking) external {
    uint positionId = bank.POSITION_ID();
    (, , uint collId, ) = bank.getPositionInfo(positionId);
    address lp = IWStakingRewards(wstaking).getUnderlyingToken(collId);

    // 1. Take out collateral
    bank.takeCollateral(wstaking, collId, uint(-1));
    IWStakingRewards(wstaking).burn(collId, uint(-1));

    // 2. put collateral
    uint amount = IERC20(lp).balanceOf(address(this));
    ensureApprove(lp, wstaking);
    uint id = IWStakingRewards(wstaking).mint(amount);
    bank.putCollateral(wstaking, id, amount);

    // 3. Refund reward
    doRefund(IWStakingRewards(wstaking).reward());
  }
}

File 31 of 72 : IBalancerPool.sol
pragma solidity 0.6.12;

interface IBalancerPool {
  function getFinalTokens() external view returns (address[] memory);

  function getNormalizedWeight(address token) external view returns (uint);

  function getSwapFee() external view returns (uint);

  function getNumTokens() external view returns (uint);

  function getBalance(address token) external view returns (uint);

  function totalSupply() external view returns (uint);

  function joinPool(uint poolAmountOut, uint[] calldata maxAmountsIn) external;

  function swapExactAmountOut(
    address tokenIn,
    uint maxAmountIn,
    address tokenOut,
    uint tokenAmountOut,
    uint maxPrice
  ) external returns (uint tokenAmountIn, uint spotPriceAfter);

  function joinswapExternAmountIn(
    address tokenIn,
    uint tokenAmountIn,
    uint minPoolAmountOut
  ) external returns (uint poolAmountOut);

  function exitPool(uint poolAmoutnIn, uint[] calldata minAmountsOut) external;

  function exitswapExternAmountOut(
    address tokenOut,
    uint tokenAmountOut,
    uint maxPoolAmountIn
  ) external returns (uint poolAmountIn);
}

File 32 of 72 : HouseHoldSpell.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/token/ERC20/IERC20.sol';

import './BasicSpell.sol';
import '../../interfaces/IBank.sol';
import '../../interfaces/IWETH.sol';

contract HouseHoldSpell is BasicSpell {
  constructor(
    IBank _bank,
    address _werc20,
    address _weth
  ) public BasicSpell(_bank, _werc20, _weth) {}

  function borrowETH(uint amount) external {
    doBorrow(weth, amount);
    doRefundETH();
  }

  function borrow(address token, uint amount) external {
    doBorrow(token, amount);
    doRefund(token);
  }

  function repayETH(uint amount) external payable {
    doTransmitETH();
    doRepay(weth, amount);
    doRefundETH();
  }

  function repay(address token, uint amount) external {
    doTransmit(token, amount);
    doRepay(token, IERC20(token).balanceOf(address(this)));
  }

  function putCollateral(address token, uint amount) external {
    doTransmit(token, amount);
    doPutCollateral(token, IERC20(token).balanceOf(address(this)));
  }

  function takeCollateral(address token, uint amount) external {
    doTakeCollateral(token, amount);
    doRefund(token);
  }
}

File 33 of 72 : MockUniswapV2Factory.sol
pragma solidity 0.6.12;

interface MockUniswapV2FactoryIUniswapV2Factory {
  event PairCreated(address indexed token0, address indexed token1, address pair, uint);

  function feeTo() external view returns (address);

  function feeToSetter() external view returns (address);

  function getPair(address tokenA, address tokenB) external view returns (address pair);

  function allPairs(uint) external view returns (address pair);

  function allPairsLength() external view returns (uint);

  function createPair(address tokenA, address tokenB) external returns (address pair);

  function setFeeTo(address) external;

  function setFeeToSetter(address) external;
}

interface MockUniswapV2FactoryIUniswapV2Pair {
  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;
}

interface MockUniswapV2FactoryIUniswapV2ERC20 {
  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;
}

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

  function name() external view returns (string memory);

  function symbol() external view returns (string memory);

  function decimals() external view returns (uint8);

  function totalSupply() external view returns (uint);

  function balanceOf(address owner) external view returns (uint);

  function allowance(address owner, address spender) external view returns (uint);

  function approve(address spender, uint value) external returns (bool);

  function transfer(address to, uint value) external returns (bool);

  function transferFrom(
    address from,
    address to,
    uint value
  ) external returns (bool);
}

interface IUniswapV2Callee {
  function uniswapV2Call(
    address sender,
    uint amount0,
    uint amount1,
    bytes calldata data
  ) external;
}

contract UniswapV2ERC20 {
  using MockUniswapV2FactorySafeMath for uint;

  string public constant name = 'Uniswap V2';
  string public constant symbol = 'UNI-V2';
  uint8 public constant decimals = 18;
  uint public totalSupply;
  mapping(address => uint) public balanceOf;
  mapping(address => mapping(address => uint)) public allowance;

  bytes32 public DOMAIN_SEPARATOR;
  // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
  bytes32 public constant PERMIT_TYPEHASH =
    0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
  mapping(address => uint) public nonces;

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

  constructor() public {
    uint chainId;
    assembly {
      chainId := chainid()
    }
    DOMAIN_SEPARATOR = keccak256(
      abi.encode(
        keccak256(
          'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'
        ),
        keccak256(bytes(name)),
        keccak256(bytes('1')),
        chainId,
        address(this)
      )
    );
  }

  function _mint(address to, uint value) internal {
    totalSupply = totalSupply.add(value);
    balanceOf[to] = balanceOf[to].add(value);
    emit Transfer(address(0), to, value);
  }

  function _burn(address from, uint value) internal {
    balanceOf[from] = balanceOf[from].sub(value);
    totalSupply = totalSupply.sub(value);
    emit Transfer(from, address(0), value);
  }

  function _approve(
    address owner,
    address spender,
    uint value
  ) private {
    allowance[owner][spender] = value;
    emit Approval(owner, spender, value);
  }

  function _transfer(
    address from,
    address to,
    uint value
  ) private {
    balanceOf[from] = balanceOf[from].sub(value);
    balanceOf[to] = balanceOf[to].add(value);
    emit Transfer(from, to, value);
  }

  function approve(address spender, uint value) external returns (bool) {
    _approve(msg.sender, spender, value);
    return true;
  }

  function transfer(address to, uint value) external returns (bool) {
    _transfer(msg.sender, to, value);
    return true;
  }

  function transferFrom(
    address from,
    address to,
    uint value
  ) external returns (bool) {
    if (allowance[from][msg.sender] != uint(-1)) {
      allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
    }
    _transfer(from, to, value);
    return true;
  }

  function permit(
    address owner,
    address spender,
    uint value,
    uint deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external {
    require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
    bytes32 digest =
      keccak256(
        abi.encodePacked(
          '\x19\x01',
          DOMAIN_SEPARATOR,
          keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
        )
      );
    address recoveredAddress = ecrecover(digest, v, r, s);
    require(
      recoveredAddress != address(0) && recoveredAddress == owner,
      'UniswapV2: INVALID_SIGNATURE'
    );
    _approve(owner, spender, value);
  }
}

contract MockUniswapV2FactoryUniswapV2Pair is UniswapV2ERC20 {
  using MockUniswapV2FactorySafeMath for uint;
  using UQ112x112 for uint224;

  uint public constant MINIMUM_LIQUIDITY = 10**3;
  bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));

  address public factory;
  address public token0;
  address public token1;

  uint112 private reserve0; // uses single storage slot, accessible via getReserves
  uint112 private reserve1; // uses single storage slot, accessible via getReserves
  uint32 private blockTimestampLast; // uses single storage slot, accessible via getReserves

  uint public price0CumulativeLast;
  uint public price1CumulativeLast;
  uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event

  uint private unlocked = 1;
  modifier lock() {
    require(unlocked == 1, 'UniswapV2: LOCKED');
    unlocked = 0;
    _;
    unlocked = 1;
  }

  function getReserves()
    public
    view
    returns (
      uint112 _reserve0,
      uint112 _reserve1,
      uint32 _blockTimestampLast
    )
  {
    _reserve0 = reserve0;
    _reserve1 = reserve1;
    _blockTimestampLast = blockTimestampLast;
  }

  function _safeTransfer(
    address token,
    address to,
    uint value
  ) private {
    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
    require(
      success && (data.length == 0 || abi.decode(data, (bool))),
      'UniswapV2: TRANSFER_FAILED'
    );
  }

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

  constructor() public {
    factory = msg.sender;
  }

  // called once by the factory at time of deployment
  function initialize(address _token0, address _token1) external {
    require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
    token0 = _token0;
    token1 = _token1;
  }

  // update reserves and, on the first call per block, price accumulators
  function _update(
    uint balance0,
    uint balance1,
    uint112 _reserve0,
    uint112 _reserve1
  ) private {
    require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
    uint32 blockTimestamp = uint32(block.timestamp % 2**32);
    uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
    if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
      // * never overflows, and + overflow is desired
      price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
      price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
    }
    reserve0 = uint112(balance0);
    reserve1 = uint112(balance1);
    blockTimestampLast = blockTimestamp;
    emit Sync(reserve0, reserve1);
  }

  // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
  function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
    address feeTo = MockUniswapV2FactoryIUniswapV2Factory(factory).feeTo();
    feeOn = feeTo != address(0);
    uint _kLast = kLast; // gas savings
    if (feeOn) {
      if (_kLast != 0) {
        uint rootK = MockUniswapV2FactoryMah.sqrt(uint(_reserve0).mul(_reserve1));
        uint rootKLast = MockUniswapV2FactoryMah.sqrt(_kLast);
        if (rootK > rootKLast) {
          uint numerator = totalSupply.mul(rootK.sub(rootKLast));
          uint denominator = rootK.mul(5).add(rootKLast);
          uint liquidity = numerator / denominator;
          if (liquidity > 0) _mint(feeTo, liquidity);
        }
      }
    } else if (_kLast != 0) {
      kLast = 0;
    }
  }

  // this low-level function should be called from a contract which performs important safety checks
  function mint(address to) external lock returns (uint liquidity) {
    (uint112 _reserve0, uint112 _reserve1, ) = getReserves(); // gas savings
    uint balance0 = MockUniswapV2FactoryIERC20(token0).balanceOf(address(this));
    uint balance1 = MockUniswapV2FactoryIERC20(token1).balanceOf(address(this));
    uint amount0 = balance0.sub(_reserve0);
    uint amount1 = balance1.sub(_reserve1);

    bool feeOn = _mintFee(_reserve0, _reserve1);
    uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
    if (_totalSupply == 0) {
      liquidity = MockUniswapV2FactoryMah.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
      _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
    } else {
      liquidity = MockUniswapV2FactoryMah.min(
        amount0.mul(_totalSupply) / _reserve0,
        amount1.mul(_totalSupply) / _reserve1
      );
    }
    require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
    _mint(to, liquidity);

    _update(balance0, balance1, _reserve0, _reserve1);
    if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
    emit Mint(msg.sender, amount0, amount1);
  }

  // this low-level function should be called from a contract which performs important safety checks
  function burn(address to) external lock returns (uint amount0, uint amount1) {
    (uint112 _reserve0, uint112 _reserve1, ) = getReserves(); // gas savings
    address _token0 = token0; // gas savings
    address _token1 = token1; // gas savings
    uint balance0 = MockUniswapV2FactoryIERC20(_token0).balanceOf(address(this));
    uint balance1 = MockUniswapV2FactoryIERC20(_token1).balanceOf(address(this));
    uint liquidity = balanceOf[address(this)];

    bool feeOn = _mintFee(_reserve0, _reserve1);
    uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
    amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
    amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
    require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
    _burn(address(this), liquidity);
    _safeTransfer(_token0, to, amount0);
    _safeTransfer(_token1, to, amount1);
    balance0 = MockUniswapV2FactoryIERC20(_token0).balanceOf(address(this));
    balance1 = MockUniswapV2FactoryIERC20(_token1).balanceOf(address(this));

    _update(balance0, balance1, _reserve0, _reserve1);
    if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
    emit Burn(msg.sender, amount0, amount1, to);
  }

  // this low-level function should be called from a contract which performs important safety checks
  function swap(
    uint amount0Out,
    uint amount1Out,
    address to,
    bytes calldata data
  ) external lock {
    require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
    (uint112 _reserve0, uint112 _reserve1, ) = getReserves(); // gas savings
    require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');

    uint balance0;
    uint balance1;
    {
      // scope for _token{0,1}, avoids stack too deep errors
      address _token0 = token0;
      address _token1 = token1;
      require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
      if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
      if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
      if (data.length > 0)
        IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
      balance0 = MockUniswapV2FactoryIERC20(_token0).balanceOf(address(this));
      balance1 = MockUniswapV2FactoryIERC20(_token1).balanceOf(address(this));
    }
    uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
    uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
    require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
    {
      // scope for reserve{0,1}Adjusted, avoids stack too deep errors
      uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
      uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
      require(
        balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2),
        'UniswapV2: K'
      );
    }

    _update(balance0, balance1, _reserve0, _reserve1);
    emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
  }

  // force balances to match reserves
  function skim(address to) external lock {
    address _token0 = token0; // gas savings
    address _token1 = token1; // gas savings
    _safeTransfer(
      _token0,
      to,
      MockUniswapV2FactoryIERC20(_token0).balanceOf(address(this)).sub(reserve0)
    );
    _safeTransfer(
      _token1,
      to,
      MockUniswapV2FactoryIERC20(_token1).balanceOf(address(this)).sub(reserve1)
    );
  }

  // force reserves to match balances
  function sync() external lock {
    _update(
      MockUniswapV2FactoryIERC20(token0).balanceOf(address(this)),
      MockUniswapV2FactoryIERC20(token1).balanceOf(address(this)),
      reserve0,
      reserve1
    );
  }
}

contract MockUniswapV2Factory {
  address public feeTo;
  address public feeToSetter;

  mapping(address => mapping(address => address)) public getPair;
  address[] public allPairs;

  event PairCreated(address indexed token0, address indexed token1, address pair, uint);

  constructor(address _feeToSetter) public {
    feeToSetter = _feeToSetter;
  }

  function allPairsLength() external view returns (uint) {
    return allPairs.length;
  }

  function createPair(address tokenA, address tokenB) external returns (address pair) {
    require(tokenA != tokenB, 'UniswapV2: IDENTICAL_ADDRESSES');
    (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
    require(token0 != address(0), 'UniswapV2: ZERO_ADDRESS');
    require(getPair[token0][token1] == address(0), 'UniswapV2: PAIR_EXISTS'); // single check is sufficient
    bytes memory bytecode = type(MockUniswapV2FactoryUniswapV2Pair).creationCode;
    bytes32 salt = keccak256(abi.encodePacked(token0, token1));
    assembly {
      pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
    }
    MockUniswapV2FactoryIUniswapV2Pair(pair).initialize(token0, token1);
    getPair[token0][token1] = pair;
    getPair[token1][token0] = pair; // populate mapping in the reverse direction
    allPairs.push(pair);
    emit PairCreated(token0, token1, pair, allPairs.length);
  }

  function setFeeTo(address _feeTo) external {
    require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
    feeTo = _feeTo;
  }

  function setFeeToSetter(address _feeToSetter) external {
    require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
    feeToSetter = _feeToSetter;
  }
}

// a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)

library MockUniswapV2FactorySafeMath {
  function add(uint x, uint y) internal pure returns (uint z) {
    require((z = x + y) >= x, 'ds-math-add-overflow');
  }

  function sub(uint x, uint y) internal pure returns (uint z) {
    require((z = x - y) <= x, 'ds-math-sub-underflow');
  }

  function mul(uint x, uint y) internal pure returns (uint z) {
    require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
  }
}

// a library for performing various math operations

library MockUniswapV2FactoryMah {
  function min(uint x, uint y) internal pure returns (uint z) {
    z = x < y ? x : y;
  }

  // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
  function sqrt(uint y) internal pure returns (uint z) {
    if (y > 3) {
      z = y;
      uint x = y / 2 + 1;
      while (x < z) {
        z = x;
        x = (y / x + x) / 2;
      }
    } else if (y != 0) {
      z = 1;
    }
  }
}

// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))

// range: [0, 2**112 - 1]
// resolution: 1 / 2**112

library UQ112x112 {
  uint224 constant Q112 = 2**112;

  // encode a uint112 as a UQ112x112
  function encode(uint112 y) internal pure returns (uint224 z) {
    z = uint224(y) * Q112; // never overflows
  }

  // divide a UQ112x112 by a uint112, returning a UQ112x112
  function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
    z = x / uint224(y);
  }
}

File 34 of 72 : MockERC20.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/token/ERC20/ERC20.sol';

contract MockERC20 is ERC20 {
  constructor(
    string memory name,
    string memory symbol,
    uint8 decimals
  ) public ERC20(name, symbol) {
    _setupDecimals(decimals);
  }

  function mint(address to, uint amount) public {
    _mint(to, amount);
  }
}

File 35 of 72 : MockCErc20_2.sol
pragma solidity 0.6.12;

// import 'OpenZeppelin/[email protected]/contracts/token/ERC20/IERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/token/ERC20/SafeERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/math/SafeMath.sol';

import '../../interfaces/ICErc20_2.sol';

contract MockCErc20_2 is ICErc20_2 {
  using SafeMath for uint;
  using SafeERC20 for IERC20;

  IERC20 public token;
  uint public mintRate = 1e18;
  uint public totalSupply = 0;
  mapping(address => uint) public override balanceOf;

  constructor(IERC20 _token) public {
    token = _token;
  }

  function setMintRate(uint _mintRate) external override {
    mintRate = _mintRate;
  }

  function underlying() external override returns (address) {
    return address(token);
  }

  function mint(uint mintAmount) external override returns (uint) {
    uint amountIn = mintAmount.mul(mintRate).div(1e18);
    IERC20(token).safeTransferFrom(msg.sender, address(this), amountIn);
    totalSupply = totalSupply.add(mintAmount);
    balanceOf[msg.sender] = balanceOf[msg.sender].add(mintAmount);
    return 0;
  }

  function redeem(uint redeemAmount) external override returns (uint) {
    uint amountOut = redeemAmount.mul(1e18).div(mintRate);
    IERC20(token).safeTransfer(msg.sender, amountOut);
    totalSupply = totalSupply.sub(redeemAmount);
    balanceOf[msg.sender] = balanceOf[msg.sender].sub(redeemAmount);
    return 0;
  }
}

File 36 of 72 : ICErc20_2.sol
pragma solidity 0.6.12;

interface ICErc20_2 {
  function underlying() external returns (address);

  function mint(uint mintAmount) external returns (uint);

  function redeem(uint redeemTokens) external returns (uint);

  function balanceOf(address user) external view returns (uint);

  function setMintRate(uint mintRate) external;
}

File 37 of 72 : MockUniswapV2Router02.sol
pragma solidity 0.6.12;

interface MockUniswapV2Router02IUniswapV2Factory {
  event PairCreated(address indexed token0, address indexed token1, address pair, uint);

  function feeTo() external view returns (address);

  function feeToSetter() external view returns (address);

  function getPair(address tokenA, address tokenB) external view returns (address pair);

  function allPairs(uint) external view returns (address pair);

  function allPairsLength() external view returns (uint);

  function createPair(address tokenA, address tokenB) external returns (address pair);

  function setFeeTo(address) external;

  function setFeeToSetter(address) external;
}

interface MockUniswapV2Router02IUniswapV2Pair {
  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;
}

interface MockUniswapV2Router02IUniswapV2Router01 {
  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);
}

interface MockUniswapV2Router02IUniswapV2Router02 is MockUniswapV2Router02IUniswapV2Router01 {
  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;
}

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

  function name() external view returns (string memory);

  function symbol() external view returns (string memory);

  function decimals() external view returns (uint8);

  function totalSupply() external view returns (uint);

  function balanceOf(address owner) external view returns (uint);

  function allowance(address owner, address spender) external view returns (uint);

  function approve(address spender, uint value) external returns (bool);

  function transfer(address to, uint value) external returns (bool);

  function transferFrom(
    address from,
    address to,
    uint value
  ) external returns (bool);
}

interface MockUniswapV2Router02IWETH {
  function deposit() external payable;

  function transfer(address to, uint value) external returns (bool);

  function withdraw(uint) external;
}

contract MockUniswapV2Router02 is MockUniswapV2Router02IUniswapV2Router02 {
  using MockUniswapV2Router02SafeMath for uint;

  address public immutable override factory;
  address public immutable override WETH;

  modifier ensure(uint deadline) {
    require(deadline >= block.timestamp, 'UniswapV2Router: EXPIRED');
    _;
  }

  constructor(address _factory, address _WETH) public {
    factory = _factory;
    WETH = _WETH;
  }

  receive() external payable {
    assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract
  }

  // **** ADD LIQUIDITY ****
  function _addLiquidity(
    address tokenA,
    address tokenB,
    uint amountADesired,
    uint amountBDesired,
    uint amountAMin,
    uint amountBMin
  ) internal virtual returns (uint amountA, uint amountB) {
    // create the pair if it doesn't exist yet
    if (MockUniswapV2Router02IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {
      MockUniswapV2Router02IUniswapV2Factory(factory).createPair(tokenA, tokenB);
    }
    (uint reserveA, uint reserveB) =
      MockUniswapV2Router02UniswapV2Library.getReserves(factory, tokenA, tokenB);
    if (reserveA == 0 && reserveB == 0) {
      (amountA, amountB) = (amountADesired, amountBDesired);
    } else {
      uint amountBOptimal =
        MockUniswapV2Router02UniswapV2Library.quote(amountADesired, reserveA, reserveB);
      if (amountBOptimal <= amountBDesired) {
        require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
        (amountA, amountB) = (amountADesired, amountBOptimal);
      } else {
        uint amountAOptimal =
          MockUniswapV2Router02UniswapV2Library.quote(amountBDesired, reserveB, reserveA);
        assert(amountAOptimal <= amountADesired);
        require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
        (amountA, amountB) = (amountAOptimal, amountBDesired);
      }
    }
  }

  function addLiquidity(
    address tokenA,
    address tokenB,
    uint amountADesired,
    uint amountBDesired,
    uint amountAMin,
    uint amountBMin,
    address to,
    uint deadline
  )
    external
    virtual
    override
    ensure(deadline)
    returns (
      uint amountA,
      uint amountB,
      uint liquidity
    )
  {
    (amountA, amountB) = _addLiquidity(
      tokenA,
      tokenB,
      amountADesired,
      amountBDesired,
      amountAMin,
      amountBMin
    );
    address pair = MockUniswapV2Router02UniswapV2Library.pairFor(factory, tokenA, tokenB);
    MockUniswapV2Router02TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
    MockUniswapV2Router02TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
    liquidity = MockUniswapV2Router02IUniswapV2Pair(pair).mint(to);
  }

  function addLiquidityETH(
    address token,
    uint amountTokenDesired,
    uint amountTokenMin,
    uint amountETHMin,
    address to,
    uint deadline
  )
    external
    payable
    virtual
    override
    ensure(deadline)
    returns (
      uint amountToken,
      uint amountETH,
      uint liquidity
    )
  {
    (amountToken, amountETH) = _addLiquidity(
      token,
      WETH,
      amountTokenDesired,
      msg.value,
      amountTokenMin,
      amountETHMin
    );
    address pair = MockUniswapV2Router02UniswapV2Library.pairFor(factory, token, WETH);
    MockUniswapV2Router02TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);
    MockUniswapV2Router02IWETH(WETH).deposit{value: amountETH}();
    assert(MockUniswapV2Router02IWETH(WETH).transfer(pair, amountETH));
    liquidity = MockUniswapV2Router02IUniswapV2Pair(pair).mint(to);
    // refund dust eth, if any
    if (msg.value > amountETH)
      MockUniswapV2Router02TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);
  }

  // **** REMOVE LIQUIDITY ****
  function removeLiquidity(
    address tokenA,
    address tokenB,
    uint liquidity,
    uint amountAMin,
    uint amountBMin,
    address to,
    uint deadline
  ) public virtual override ensure(deadline) returns (uint amountA, uint amountB) {
    address pair = MockUniswapV2Router02UniswapV2Library.pairFor(factory, tokenA, tokenB);
    MockUniswapV2Router02IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair
    (uint amount0, uint amount1) = MockUniswapV2Router02IUniswapV2Pair(pair).burn(to);
    (address token0, ) = MockUniswapV2Router02UniswapV2Library.sortTokens(tokenA, tokenB);
    (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
    require(amountA >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
    require(amountB >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
  }

  function removeLiquidityETH(
    address token,
    uint liquidity,
    uint amountTokenMin,
    uint amountETHMin,
    address to,
    uint deadline
  ) public virtual override ensure(deadline) returns (uint amountToken, uint amountETH) {
    (amountToken, amountETH) = removeLiquidity(
      token,
      WETH,
      liquidity,
      amountTokenMin,
      amountETHMin,
      address(this),
      deadline
    );
    MockUniswapV2Router02TransferHelper.safeTransfer(token, to, amountToken);
    MockUniswapV2Router02IWETH(WETH).withdraw(amountETH);
    MockUniswapV2Router02TransferHelper.safeTransferETH(to, 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 virtual override returns (uint amountA, uint amountB) {
    address pair = MockUniswapV2Router02UniswapV2Library.pairFor(factory, tokenA, tokenB);
    uint value = approveMax ? uint(-1) : liquidity;
    MockUniswapV2Router02IUniswapV2Pair(pair).permit(
      msg.sender,
      address(this),
      value,
      deadline,
      v,
      r,
      s
    );
    (amountA, amountB) = removeLiquidity(
      tokenA,
      tokenB,
      liquidity,
      amountAMin,
      amountBMin,
      to,
      deadline
    );
  }

  function removeLiquidityETHWithPermit(
    address token,
    uint liquidity,
    uint amountTokenMin,
    uint amountETHMin,
    address to,
    uint deadline,
    bool approveMax,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external virtual override returns (uint amountToken, uint amountETH) {
    address pair = MockUniswapV2Router02UniswapV2Library.pairFor(factory, token, WETH);
    uint value = approveMax ? uint(-1) : liquidity;
    MockUniswapV2Router02IUniswapV2Pair(pair).permit(
      msg.sender,
      address(this),
      value,
      deadline,
      v,
      r,
      s
    );
    (amountToken, amountETH) = removeLiquidityETH(
      token,
      liquidity,
      amountTokenMin,
      amountETHMin,
      to,
      deadline
    );
  }

  // **** REMOVE LIQUIDITY (supporting fee-on-transfer tokens) ****
  function removeLiquidityETHSupportingFeeOnTransferTokens(
    address token,
    uint liquidity,
    uint amountTokenMin,
    uint amountETHMin,
    address to,
    uint deadline
  ) public virtual override ensure(deadline) returns (uint amountETH) {
    (, amountETH) = removeLiquidity(
      token,
      WETH,
      liquidity,
      amountTokenMin,
      amountETHMin,
      address(this),
      deadline
    );
    MockUniswapV2Router02TransferHelper.safeTransfer(
      token,
      to,
      IERC20(token).balanceOf(address(this))
    );
    MockUniswapV2Router02IWETH(WETH).withdraw(amountETH);
    MockUniswapV2Router02TransferHelper.safeTransferETH(to, amountETH);
  }

  function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
    address token,
    uint liquidity,
    uint amountTokenMin,
    uint amountETHMin,
    address to,
    uint deadline,
    bool approveMax,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external virtual override returns (uint amountETH) {
    address pair = MockUniswapV2Router02UniswapV2Library.pairFor(factory, token, WETH);
    uint value = approveMax ? uint(-1) : liquidity;
    MockUniswapV2Router02IUniswapV2Pair(pair).permit(
      msg.sender,
      address(this),
      value,
      deadline,
      v,
      r,
      s
    );
    amountETH = removeLiquidityETHSupportingFeeOnTransferTokens(
      token,
      liquidity,
      amountTokenMin,
      amountETHMin,
      to,
      deadline
    );
  }

  // **** SWAP ****
  // requires the initial amount to have already been sent to the first pair
  function _swap(
    uint[] memory amounts,
    address[] memory path,
    address _to
  ) internal virtual {
    for (uint i; i < path.length - 1; i++) {
      (address input, address output) = (path[i], path[i + 1]);
      (address token0, ) = MockUniswapV2Router02UniswapV2Library.sortTokens(input, output);
      uint amountOut = amounts[i + 1];
      (uint amount0Out, uint amount1Out) =
        input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));
      address to =
        i < path.length - 2
          ? MockUniswapV2Router02UniswapV2Library.pairFor(factory, output, path[i + 2])
          : _to;
      MockUniswapV2Router02IUniswapV2Pair(
        MockUniswapV2Router02UniswapV2Library.pairFor(factory, input, output)
      )
        .swap(amount0Out, amount1Out, to, new bytes(0));
    }
  }

  function swapExactTokensForTokens(
    uint amountIn,
    uint amountOutMin,
    address[] calldata path,
    address to,
    uint deadline
  ) external virtual override ensure(deadline) returns (uint[] memory amounts) {
    amounts = MockUniswapV2Router02UniswapV2Library.getAmountsOut(factory, amountIn, path);
    require(
      amounts[amounts.length - 1] >= amountOutMin,
      'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'
    );
    MockUniswapV2Router02TransferHelper.safeTransferFrom(
      path[0],
      msg.sender,
      MockUniswapV2Router02UniswapV2Library.pairFor(factory, path[0], path[1]),
      amounts[0]
    );
    _swap(amounts, path, to);
  }

  function swapTokensForExactTokens(
    uint amountOut,
    uint amountInMax,
    address[] calldata path,
    address to,
    uint deadline
  ) external virtual override ensure(deadline) returns (uint[] memory amounts) {
    amounts = MockUniswapV2Router02UniswapV2Library.getAmountsIn(factory, amountOut, path);
    require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
    MockUniswapV2Router02TransferHelper.safeTransferFrom(
      path[0],
      msg.sender,
      MockUniswapV2Router02UniswapV2Library.pairFor(factory, path[0], path[1]),
      amounts[0]
    );
    _swap(amounts, path, to);
  }

  function swapExactETHForTokens(
    uint amountOutMin,
    address[] calldata path,
    address to,
    uint deadline
  ) external payable virtual override ensure(deadline) returns (uint[] memory amounts) {
    require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
    amounts = MockUniswapV2Router02UniswapV2Library.getAmountsOut(factory, msg.value, path);
    require(
      amounts[amounts.length - 1] >= amountOutMin,
      'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'
    );
    MockUniswapV2Router02IWETH(WETH).deposit{value: amounts[0]}();
    assert(
      MockUniswapV2Router02IWETH(WETH).transfer(
        MockUniswapV2Router02UniswapV2Library.pairFor(factory, path[0], path[1]),
        amounts[0]
      )
    );
    _swap(amounts, path, to);
  }

  function swapTokensForExactETH(
    uint amountOut,
    uint amountInMax,
    address[] calldata path,
    address to,
    uint deadline
  ) external virtual override ensure(deadline) returns (uint[] memory amounts) {
    require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
    amounts = MockUniswapV2Router02UniswapV2Library.getAmountsIn(factory, amountOut, path);
    require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
    MockUniswapV2Router02TransferHelper.safeTransferFrom(
      path[0],
      msg.sender,
      MockUniswapV2Router02UniswapV2Library.pairFor(factory, path[0], path[1]),
      amounts[0]
    );
    _swap(amounts, path, address(this));
    MockUniswapV2Router02IWETH(WETH).withdraw(amounts[amounts.length - 1]);
    MockUniswapV2Router02TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
  }

  function swapExactTokensForETH(
    uint amountIn,
    uint amountOutMin,
    address[] calldata path,
    address to,
    uint deadline
  ) external virtual override ensure(deadline) returns (uint[] memory amounts) {
    require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
    amounts = MockUniswapV2Router02UniswapV2Library.getAmountsOut(factory, amountIn, path);
    require(
      amounts[amounts.length - 1] >= amountOutMin,
      'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'
    );
    MockUniswapV2Router02TransferHelper.safeTransferFrom(
      path[0],
      msg.sender,
      MockUniswapV2Router02UniswapV2Library.pairFor(factory, path[0], path[1]),
      amounts[0]
    );
    _swap(amounts, path, address(this));
    MockUniswapV2Router02IWETH(WETH).withdraw(amounts[amounts.length - 1]);
    MockUniswapV2Router02TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
  }

  function swapETHForExactTokens(
    uint amountOut,
    address[] calldata path,
    address to,
    uint deadline
  ) external payable virtual override ensure(deadline) returns (uint[] memory amounts) {
    require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
    amounts = MockUniswapV2Router02UniswapV2Library.getAmountsIn(factory, amountOut, path);
    require(amounts[0] <= msg.value, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
    MockUniswapV2Router02IWETH(WETH).deposit{value: amounts[0]}();
    assert(
      MockUniswapV2Router02IWETH(WETH).transfer(
        MockUniswapV2Router02UniswapV2Library.pairFor(factory, path[0], path[1]),
        amounts[0]
      )
    );
    _swap(amounts, path, to);
    // refund dust eth, if any
    if (msg.value > amounts[0])
      MockUniswapV2Router02TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);
  }

  // **** SWAP (supporting fee-on-transfer tokens) ****
  // requires the initial amount to have already been sent to the first pair
  function _swapSupportingFeeOnTransferTokens(address[] memory path, address _to) internal virtual {
    for (uint i; i < path.length - 1; i++) {
      (address input, address output) = (path[i], path[i + 1]);
      (address token0, ) = MockUniswapV2Router02UniswapV2Library.sortTokens(input, output);
      MockUniswapV2Router02IUniswapV2Pair pair =
        MockUniswapV2Router02IUniswapV2Pair(
          MockUniswapV2Router02UniswapV2Library.pairFor(factory, input, output)
        );
      uint amountInput;
      uint amountOutput;
      {
        // scope to avoid stack too deep errors
        (uint reserve0, uint reserve1, ) = pair.getReserves();
        (uint reserveInput, uint reserveOutput) =
          input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
        amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput);
        amountOutput = MockUniswapV2Router02UniswapV2Library.getAmountOut(
          amountInput,
          reserveInput,
          reserveOutput
        );
      }
      (uint amount0Out, uint amount1Out) =
        input == token0 ? (uint(0), amountOutput) : (amountOutput, uint(0));
      address to =
        i < path.length - 2
          ? MockUniswapV2Router02UniswapV2Library.pairFor(factory, output, path[i + 2])
          : _to;
      pair.swap(amount0Out, amount1Out, to, new bytes(0));
    }
  }

  function swapExactTokensForTokensSupportingFeeOnTransferTokens(
    uint amountIn,
    uint amountOutMin,
    address[] calldata path,
    address to,
    uint deadline
  ) external virtual override ensure(deadline) {
    MockUniswapV2Router02TransferHelper.safeTransferFrom(
      path[0],
      msg.sender,
      MockUniswapV2Router02UniswapV2Library.pairFor(factory, path[0], path[1]),
      amountIn
    );
    uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
    _swapSupportingFeeOnTransferTokens(path, to);
    require(
      IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin,
      'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'
    );
  }

  function swapExactETHForTokensSupportingFeeOnTransferTokens(
    uint amountOutMin,
    address[] calldata path,
    address to,
    uint deadline
  ) external payable virtual override ensure(deadline) {
    require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
    uint amountIn = msg.value;
    MockUniswapV2Router02IWETH(WETH).deposit{value: amountIn}();
    assert(
      MockUniswapV2Router02IWETH(WETH).transfer(
        MockUniswapV2Router02UniswapV2Library.pairFor(factory, path[0], path[1]),
        amountIn
      )
    );
    uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
    _swapSupportingFeeOnTransferTokens(path, to);
    require(
      IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin,
      'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'
    );
  }

  function swapExactTokensForETHSupportingFeeOnTransferTokens(
    uint amountIn,
    uint amountOutMin,
    address[] calldata path,
    address to,
    uint deadline
  ) external virtual override ensure(deadline) {
    require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
    MockUniswapV2Router02TransferHelper.safeTransferFrom(
      path[0],
      msg.sender,
      MockUniswapV2Router02UniswapV2Library.pairFor(factory, path[0], path[1]),
      amountIn
    );
    _swapSupportingFeeOnTransferTokens(path, address(this));
    uint amountOut = IERC20(WETH).balanceOf(address(this));
    require(amountOut >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
    MockUniswapV2Router02IWETH(WETH).withdraw(amountOut);
    MockUniswapV2Router02TransferHelper.safeTransferETH(to, amountOut);
  }

  // **** LIBRARY FUNCTIONS ****
  function quote(
    uint amountA,
    uint reserveA,
    uint reserveB
  ) public pure virtual override returns (uint amountB) {
    return MockUniswapV2Router02UniswapV2Library.quote(amountA, reserveA, reserveB);
  }

  function getAmountOut(
    uint amountIn,
    uint reserveIn,
    uint reserveOut
  ) public pure virtual override returns (uint amountOut) {
    return MockUniswapV2Router02UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut);
  }

  function getAmountIn(
    uint amountOut,
    uint reserveIn,
    uint reserveOut
  ) public pure virtual override returns (uint amountIn) {
    return MockUniswapV2Router02UniswapV2Library.getAmountIn(amountOut, reserveIn, reserveOut);
  }

  function getAmountsOut(uint amountIn, address[] memory path)
    public
    view
    virtual
    override
    returns (uint[] memory amounts)
  {
    return MockUniswapV2Router02UniswapV2Library.getAmountsOut(factory, amountIn, path);
  }

  function getAmountsIn(uint amountOut, address[] memory path)
    public
    view
    virtual
    override
    returns (uint[] memory amounts)
  {
    return MockUniswapV2Router02UniswapV2Library.getAmountsIn(factory, amountOut, path);
  }
}

// a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)

library MockUniswapV2Router02SafeMath {
  function add(uint x, uint y) internal pure returns (uint z) {
    require((z = x + y) >= x, 'ds-math-add-overflow');
  }

  function sub(uint x, uint y) internal pure returns (uint z) {
    require((z = x - y) <= x, 'ds-math-sub-underflow');
  }

  function mul(uint x, uint y) internal pure returns (uint z) {
    require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
  }
}

library MockUniswapV2Router02UniswapV2Library {
  using MockUniswapV2Router02SafeMath for uint;

  // 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, 'MockUniswapV2Router02UniswapV2Library: IDENTICAL_ADDRESSES');
    (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
    require(token0 != address(0), 'MockUniswapV2Router02UniswapV2Library: ZERO_ADDRESS');
  }

  // calculates the CREATE2 address for a pair without making any external calls
  function pairFor(
    address factory,
    address tokenA,
    address tokenB
  ) internal view returns (address pair) {
    return MockUniswapV2Router02IUniswapV2Factory(factory).getPair(tokenA, tokenB);
  }

  // fetches and sorts the reserves for a pair
  function getReserves(
    address factory,
    address tokenA,
    address tokenB
  ) internal view returns (uint reserveA, uint reserveB) {
    (address token0, ) = sortTokens(tokenA, tokenB);
    (uint reserve0, uint reserve1, ) =
      MockUniswapV2Router02IUniswapV2Pair(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(
    uint amountA,
    uint reserveA,
    uint reserveB
  ) internal pure returns (uint amountB) {
    require(amountA > 0, 'MockUniswapV2Router02UniswapV2Library: INSUFFICIENT_AMOUNT');
    require(
      reserveA > 0 && reserveB > 0,
      'MockUniswapV2Router02UniswapV2Library: 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(
    uint amountIn,
    uint reserveIn,
    uint reserveOut
  ) internal pure returns (uint amountOut) {
    require(amountIn > 0, 'MockUniswapV2Router02UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
    require(
      reserveIn > 0 && reserveOut > 0,
      'MockUniswapV2Router02UniswapV2Library: INSUFFICIENT_LIQUIDITY'
    );
    uint amountInWithFee = amountIn.mul(997);
    uint numerator = amountInWithFee.mul(reserveOut);
    uint 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(
    uint amountOut,
    uint reserveIn,
    uint reserveOut
  ) internal pure returns (uint amountIn) {
    require(amountOut > 0, 'MockUniswapV2Router02UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
    require(
      reserveIn > 0 && reserveOut > 0,
      'MockUniswapV2Router02UniswapV2Library: INSUFFICIENT_LIQUIDITY'
    );
    uint numerator = reserveIn.mul(amountOut).mul(1000);
    uint denominator = reserveOut.sub(amountOut).mul(997);
    amountIn = (numerator / denominator).add(1);
  }

  // performs chained getAmountOut calculations on any number of pairs
  function getAmountsOut(
    address factory,
    uint amountIn,
    address[] memory path
  ) internal view returns (uint[] memory amounts) {
    require(path.length >= 2, 'MockUniswapV2Router02UniswapV2Library: INVALID_PATH');
    amounts = new uint[](path.length);
    amounts[0] = amountIn;
    for (uint i; i < path.length - 1; i++) {
      (uint reserveIn, uint 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,
    uint amountOut,
    address[] memory path
  ) internal view returns (uint[] memory amounts) {
    require(path.length >= 2, 'MockUniswapV2Router02UniswapV2Library: INVALID_PATH');
    amounts = new uint[](path.length);
    amounts[amounts.length - 1] = amountOut;
    for (uint i = path.length - 1; i > 0; i--) {
      (uint reserveIn, uint reserveOut) = getReserves(factory, path[i - 1], path[i]);
      amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
    }
  }
}

// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library MockUniswapV2Router02TransferHelper {
  function safeApprove(
    address token,
    address to,
    uint value
  ) internal {
    // bytes4(keccak256(bytes('approve(address,uint256)')));
    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
    require(
      success && (data.length == 0 || abi.decode(data, (bool))),
      'MockUniswapV2Router02TransferHelper: APPROVE_FAILED'
    );
  }

  function safeTransfer(
    address token,
    address to,
    uint value
  ) internal {
    // bytes4(keccak256(bytes('transfer(address,uint256)')));
    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
    require(
      success && (data.length == 0 || abi.decode(data, (bool))),
      'MockUniswapV2Router02TransferHelper: TRANSFER_FAILED'
    );
  }

  function safeTransferFrom(
    address token,
    address from,
    address to,
    uint value
  ) internal {
    // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
    (bool success, bytes memory data) =
      token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
    require(
      success && (data.length == 0 || abi.decode(data, (bool))),
      'MockUniswapV2Router02TransferHelper: TRANSFER_FROM_FAILED'
    );
  }

  function safeTransferETH(address to, uint value) internal {
    (bool success, ) = to.call{value: value}(new bytes(0));
    require(success, 'MockUniswapV2Router02TransferHelper: ETH_TRANSFER_FAILED');
  }
}

File 38 of 72 : MockCErc20.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/token/ERC20/IERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/math/SafeMath.sol';

import '../../interfaces/ICErc20.sol';

contract MockCErc20 is ICErc20 {
  using SafeMath for uint;

  IERC20 public token;
  uint public interestPerYear = 10e16; // 10% per year

  mapping(address => uint) public borrows;
  mapping(address => uint) public lastBlock;

  constructor(IERC20 _token) public {
    token = _token;
  }

  function underlying() external override returns (address) {
    return address(token);
  }

  function mint(uint mintAmount) external override returns (uint) {
    // Not implemented
    return 0;
  }

  function redeem(uint redeemTokens) external override returns (uint) {
    // Not implemented
    return 0;
  }

  function balanceOf(address user) external view override returns (uint) {
    // Not implemented
    return 0;
  }

  function borrowBalanceCurrent(address account) public override returns (uint) {
    uint timePast = now - lastBlock[account];
    if (timePast > 0) {
      uint interest = borrows[account].mul(interestPerYear).div(100e16).mul(timePast).div(365 days);
      borrows[account] = borrows[account].add(interest);
      lastBlock[account] = now;
    }
    return borrows[account];
  }

  function borrowBalanceStored(address account) external view override returns (uint) {
    return borrows[account];
  }

  function borrow(uint borrowAmount) external override returns (uint) {
    borrowBalanceCurrent(msg.sender);
    token.transfer(msg.sender, borrowAmount);
    borrows[msg.sender] = borrows[msg.sender].add(borrowAmount);
    return 0;
  }

  function repayBorrow(uint repayAmount) external override returns (uint) {
    borrowBalanceCurrent(msg.sender);
    token.transferFrom(msg.sender, address(this), repayAmount);
    borrows[msg.sender] = borrows[msg.sender].sub(repayAmount);
    return 0;
  }
}

File 39 of 72 : MockWETH.sol
pragma solidity 0.6.12;

contract MockWETH {
  string public name = 'Wrapped Ether';
  string public symbol = 'WETH';
  uint8 public decimals = 18;

  event Approval(address indexed src, address indexed guy, uint wad);
  event Transfer(address indexed src, address indexed dst, uint wad);
  event Deposit(address indexed dst, uint wad);
  event Withdrawal(address indexed src, uint wad);

  mapping(address => uint) public balanceOf;
  mapping(address => mapping(address => uint)) public allowance;

  receive() external payable {
    deposit();
  }

  function deposit() public payable {
    balanceOf[msg.sender] += msg.value;
    emit Deposit(msg.sender, msg.value);
  }

  function withdraw(uint wad) public {
    require(balanceOf[msg.sender] >= wad);
    balanceOf[msg.sender] -= wad;
    msg.sender.transfer(wad);
    emit Withdrawal(msg.sender, wad);
  }

  function totalSupply() public view returns (uint) {
    return address(this).balance;
  }

  function approve(address guy, uint wad) public returns (bool) {
    allowance[msg.sender][guy] = wad;
    emit Approval(msg.sender, guy, wad);
    return true;
  }

  function transfer(address dst, uint wad) public returns (bool) {
    return transferFrom(msg.sender, dst, wad);
  }

  function transferFrom(
    address src,
    address dst,
    uint wad
  ) public returns (bool) {
    require(balanceOf[src] >= wad);

    if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
      require(allowance[src][msg.sender] >= wad);
      allowance[src][msg.sender] -= wad;
    }

    balanceOf[src] -= wad;
    balanceOf[dst] += wad;

    emit Transfer(src, dst, wad);

    return true;
  }
}

File 40 of 72 : UsingBaseOracle.sol
pragma solidity 0.6.12;

import '../../interfaces/IBaseOracle.sol';

contract UsingBaseOracle {
  IBaseOracle public immutable base;

  constructor(IBaseOracle _base) public {
    base = _base;
  }
}

File 41 of 72 : IBaseOracle.sol
pragma solidity 0.6.12;

interface IBaseOracle {
  /// @dev Return the value of the given input as ETH per unit, multiplied by 2**112.
  /// @param token The ERC-20 token to check the value.
  function getETHPx(address token) external view returns (uint);
}

File 42 of 72 : SimpleOracle.sol
pragma solidity 0.6.12;

import '../Governable.sol';
import '../../interfaces/IBaseOracle.sol';

contract SimpleOracle is IBaseOracle, Governable {
  mapping(address => uint) public prices; // Mapping from token to price in ETH (times 2**112).

  /// The governor sets oracle price for a token.
  event SetETHPx(address token, uint px);

  /// @dev Create the contract and initialize the first governor.
  constructor() public {
    __Governable__init();
  }

  /// @dev Return the value of the given input as ETH per unit, multiplied by 2**112.
  /// @param token The ERC-20 token to check the value.
  function getETHPx(address token) external view override returns (uint) {
    uint px = prices[token];
    require(px != 0, 'no px');
    return px;
  }

  /// @dev Set the prices of the given token addresses.
  /// @param tokens The token addresses to set the prices.
  /// @param pxs The price data points, representing token value in ETH times 2**112.
  function setETHPx(address[] memory tokens, uint[] memory pxs) external onlyGov {
    require(tokens.length == pxs.length, 'inconsistent length');
    for (uint idx = 0; idx < tokens.length; idx++) {
      prices[tokens[idx]] = pxs[idx];
      emit SetETHPx(tokens[idx], pxs[idx]);
    }
  }
}

File 43 of 72 : UniswapV2Oracle.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/math/SafeMath.sol';

import './UsingBaseOracle.sol';
import '../utils/HomoraMath.sol';
import '../../interfaces/IBaseOracle.sol';
import '../../interfaces/IUniswapV2Pair.sol';

contract UniswapV2Oracle is UsingBaseOracle, IBaseOracle {
  using SafeMath for uint;
  using HomoraMath for uint;

  constructor(IBaseOracle _base) public UsingBaseOracle(_base) {}

  /// @dev Return the value of the given input as ETH per unit, multiplied by 2**112.
  /// @param pair The Uniswap pair to check the value.
  function getETHPx(address pair) external view override returns (uint) {
    address token0 = IUniswapV2Pair(pair).token0();
    address token1 = IUniswapV2Pair(pair).token1();
    uint totalSupply = IUniswapV2Pair(pair).totalSupply();
    (uint r0, uint r1, ) = IUniswapV2Pair(pair).getReserves();
    uint sqrtK = HomoraMath.sqrt(r0.mul(r1)).fdiv(totalSupply); // in 2**112
    uint px0 = base.getETHPx(token0);
    uint px1 = base.getETHPx(token1);
    return sqrtK.mul(2).mul(HomoraMath.sqrt(px0)).div(2**56).mul(HomoraMath.sqrt(px1)).div(2**56);
  }
}

File 44 of 72 : BalancerPairOracle.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/math/SafeMath.sol';

import './UsingBaseOracle.sol';
import '../utils/BNum.sol';
import '../../interfaces/IBaseOracle.sol';
import '../../interfaces/IBalancerPool.sol';

contract BalancerPairOracle is UsingBaseOracle, IBaseOracle, BNum {
  using SafeMath for uint;

  constructor(IBaseOracle _base) public UsingBaseOracle(_base) {}

  /// @dev Return fair reserve amounts given spot reserves, weights, and fair prices.
  /// @param resA Reserve of the first asset
  /// @param resB Reserev of the second asset
  /// @param wA Weight of the first asset
  /// @param wB Weight of the second asset
  /// @param pxA Fair price of the first asset
  /// @param pxB Fair price of the second asset
  function computeFairReserves(
    uint resA,
    uint resB,
    uint wA,
    uint wB,
    uint pxA,
    uint pxB
  ) internal pure returns (uint fairResA, uint fairResB) {
    uint r0 = bdiv(resA, resB);
    uint r1 = bdiv(bmul(wA, pxB), bmul(wB, pxA));
    // fairResA = resA * (r1 / r0) ^ wB
    // fairResB = resB * (r0 / r1) ^ wA
    if (r0 > r1) {
      uint ratio = bdiv(r1, r0);
      fairResA = bmul(resA, bpow(ratio, wB));
      fairResB = bdiv(resB, bpow(ratio, wA));
    } else {
      uint ratio = bdiv(r0, r1);
      fairResA = bdiv(resA, bpow(ratio, wB));
      fairResB = bmul(resB, bpow(ratio, wA));
    }
  }

  /// @dev Return the value of the given input as ETH per unit, multiplied by 2**112.
  /// @param token The ERC-20 token to check the value.
  function getETHPx(address token) external view override returns (uint) {
    IBalancerPool pool = IBalancerPool(token);
    require(pool.getNumTokens() == 2, 'num tokens must be 2');
    address[] memory tokens = pool.getFinalTokens();
    address tokenA = tokens[0];
    address tokenB = tokens[1];
    uint pxA = base.getETHPx(tokenA);
    uint pxB = base.getETHPx(tokenB);
    (uint fairResA, uint fairResB) =
      computeFairReserves(
        pool.getBalance(tokenA),
        pool.getBalance(tokenB),
        pool.getNormalizedWeight(tokenA),
        pool.getNormalizedWeight(tokenB),
        pxA,
        pxB
      );
    return fairResA.mul(pxA).add(fairResB.mul(pxB)).div(pool.totalSupply());
  }
}

File 45 of 72 : BNum.sol
// https://github.com/balancer-labs/balancer-core/blob/master/contracts/BNum.sol

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity 0.6.12;

import './BConst.sol';

contract BNum is BConst {
  function btoi(uint a) internal pure returns (uint) {
    return a / BONE;
  }

  function bfloor(uint a) internal pure returns (uint) {
    return btoi(a) * BONE;
  }

  function badd(uint a, uint b) internal pure returns (uint) {
    uint c = a + b;
    require(c >= a, 'ERR_ADD_OVERFLOW');
    return c;
  }

  function bsub(uint a, uint b) internal pure returns (uint) {
    (uint c, bool flag) = bsubSign(a, b);
    require(!flag, 'ERR_SUB_UNDERFLOW');
    return c;
  }

  function bsubSign(uint a, uint b) internal pure returns (uint, bool) {
    if (a >= b) {
      return (a - b, false);
    } else {
      return (b - a, true);
    }
  }

  function bmul(uint a, uint b) internal pure returns (uint) {
    uint c0 = a * b;
    require(a == 0 || c0 / a == b, 'ERR_MUL_OVERFLOW');
    uint c1 = c0 + (BONE / 2);
    require(c1 >= c0, 'ERR_MUL_OVERFLOW');
    uint c2 = c1 / BONE;
    return c2;
  }

  function bdiv(uint a, uint b) internal pure returns (uint) {
    require(b != 0, 'ERR_DIV_ZERO');
    uint c0 = a * BONE;
    require(a == 0 || c0 / a == BONE, 'ERR_DIV_INTERNAL'); // bmul overflow
    uint c1 = c0 + (b / 2);
    require(c1 >= c0, 'ERR_DIV_INTERNAL'); //  badd require
    uint c2 = c1 / b;
    return c2;
  }

  // DSMath.wpow
  function bpowi(uint a, uint n) internal pure returns (uint) {
    uint z = n % 2 != 0 ? a : BONE;

    for (n /= 2; n != 0; n /= 2) {
      a = bmul(a, a);

      if (n % 2 != 0) {
        z = bmul(z, a);
      }
    }
    return z;
  }

  // Compute b^(e.w) by splitting it into (b^e)*(b^0.w).
  // Use `bpowi` for `b^e` and `bpowK` for k iterations
  // of approximation of b^0.w
  function bpow(uint base, uint exp) internal pure returns (uint) {
    require(base >= MIN_BPOW_BASE, 'ERR_BPOW_BASE_TOO_LOW');
    require(base <= MAX_BPOW_BASE, 'ERR_BPOW_BASE_TOO_HIGH');

    uint whole = bfloor(exp);
    uint remain = bsub(exp, whole);

    uint wholePow = bpowi(base, btoi(whole));

    if (remain == 0) {
      return wholePow;
    }

    uint partialResult = bpowApprox(base, remain, BPOW_PRECISION);
    return bmul(wholePow, partialResult);
  }

  function bpowApprox(
    uint base,
    uint exp,
    uint precision
  ) internal pure returns (uint) {
    // term 0:
    uint a = exp;
    (uint x, bool xneg) = bsubSign(base, BONE);
    uint term = BONE;
    uint sum = term;
    bool negative = false;

    // term(k) = numer / denom
    //         = (product(a - i - 1, i=1-->k) * x^k) / (k!)
    // each iteration, multiply previous term by (a-(k-1)) * x / k
    // continue until term is less than precision
    for (uint i = 1; term >= precision; i++) {
      uint bigK = i * BONE;
      (uint c, bool cneg) = bsubSign(a, bsub(bigK, BONE));
      term = bmul(term, bmul(c, x));
      term = bdiv(term, bigK);
      if (term == 0) break;

      if (xneg) negative = !negative;
      if (cneg) negative = !negative;
      if (negative) {
        sum = bsub(sum, term);
      } else {
        sum = badd(sum, term);
      }
    }

    return sum;
  }
}

File 46 of 72 : BConst.sol
// https://github.com/balancer-labs/balancer-core/blob/master/contracts/BConst.sol

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity 0.6.12;

contract BConst {
  uint public constant BONE = 10**18;

  uint public constant MIN_BOUND_TOKENS = 2;
  uint public constant MAX_BOUND_TOKENS = 8;

  uint public constant MIN_FEE = BONE / 10**6;
  uint public constant MAX_FEE = BONE / 10;
  uint public constant EXIT_FEE = 0;

  uint public constant MIN_WEIGHT = BONE;
  uint public constant MAX_WEIGHT = BONE * 50;
  uint public constant MAX_TOTAL_WEIGHT = BONE * 50;
  uint public constant MIN_BALANCE = BONE / 10**12;

  uint public constant INIT_POOL_SUPPLY = BONE * 100;

  uint public constant MIN_BPOW_BASE = 1 wei;
  uint public constant MAX_BPOW_BASE = (2 * BONE) - 1 wei;
  uint public constant BPOW_PRECISION = BONE / 10**10;

  uint public constant MAX_IN_RATIO = BONE / 2;
  uint public constant MAX_OUT_RATIO = (BONE / 3) + 1 wei;
}

File 47 of 72 : CurveOracle.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/math/SafeMath.sol';

import './UsingBaseOracle.sol';
import '../../interfaces/IBaseOracle.sol';
import '../../interfaces/ICurvePool.sol';
import '../../interfaces/ICurveRegistry.sol';

interface IERC20Decimal {
  function decimals() external view returns (uint8);
}

contract CurveOracle is UsingBaseOracle, IBaseOracle {
  using SafeMath for uint;

  ICurveRegistry public immutable registry;

  struct UnderlyingToken {
    uint8 decimals; // token decimals
    address token; // token address
  }

  mapping(address => UnderlyingToken[]) public ulTokens; // lpToken -> underlying tokens array
  mapping(address => address) public poolOf; // lpToken -> pool

  constructor(IBaseOracle _base, ICurveRegistry _registry) public UsingBaseOracle(_base) {
    registry = _registry;
  }

  /// @dev Register the pool given LP token address and set the pool info.
  /// @param lp LP token to find the corresponding pool.
  function registerPool(address lp) external {
    address pool = poolOf[lp];
    require(pool == address(0), 'lp is already registered');
    pool = registry.get_pool_from_lp_token(lp);
    require(pool != address(0), 'no corresponding pool for lp token');
    poolOf[lp] = pool;
    uint n = registry.get_n_coins(pool);
    address[8] memory tokens = registry.get_coins(pool);
    for (uint i = 0; i < n; i++) {
      ulTokens[lp].push(
        UnderlyingToken({token: tokens[i], decimals: IERC20Decimal(tokens[i]).decimals()})
      );
    }
  }

  /// @dev Return the value of the given input as ETH per unit, multiplied by 2**112.
  /// @param lp The ERC-20 LP token to check the value.
  function getETHPx(address lp) external view override returns (uint) {
    address pool = poolOf[lp];
    require(pool != address(0), 'lp is not registered');
    UnderlyingToken[] memory tokens = ulTokens[lp];
    uint minPx = uint(-1);
    uint n = tokens.length;
    for (uint idx = 0; idx < n; idx++) {
      UnderlyingToken memory ulToken = tokens[idx];
      uint tokenPx = base.getETHPx(ulToken.token);
      if (ulToken.decimals < 18) tokenPx = tokenPx.div(10**(18 - uint(ulToken.decimals)));
      if (ulToken.decimals > 18) tokenPx = tokenPx.mul(10**(uint(ulToken.decimals) - 18));
      if (tokenPx < minPx) minPx = tokenPx;
    }
    require(minPx != uint(-1), 'no min px');
    return minPx.mul(ICurvePool(pool).get_virtual_price()).div(1e18);
  }
}

File 48 of 72 : ERC20KP3ROracle.sol
pragma solidity 0.6.12;

import './BaseKP3ROracle.sol';
import '../../interfaces/IBaseOracle.sol';
import '../../interfaces/IKeep3rV1Oracle.sol';
import '../../interfaces/IUniswapV2Factory.sol';

contract ERC20KP3ROracle is IBaseOracle, BaseKP3ROracle {
  constructor(IKeep3rV1Oracle _kp3r) public BaseKP3ROracle(_kp3r) {}

  /// @dev Return the value of the given input as ETH per unit, multiplied by 2**112.
  /// @param token The ERC-20 token to check the value.
  function getETHPx(address token) external view override returns (uint) {
    if (token == weth || token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {
      return 2**112;
    }
    address pair = IUniswapV2Factory(factory).getPair(token, weth);
    if (token < weth) {
      return price0TWAP(pair);
    } else {
      return price1TWAP(pair);
    }
  }
}

File 49 of 72 : BaseKP3ROracle.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/proxy/Initializable.sol';

import '../../interfaces/IKeep3rV1Oracle.sol';
import '../../interfaces/IUniswapV2Pair.sol';

contract BaseKP3ROracle is Initializable {
  uint public constant MIN_TWAP_TIME = 15 minutes;
  uint public constant MAX_TWAP_TIME = 60 minutes;

  IKeep3rV1Oracle public immutable kp3r;
  address public immutable factory;
  address public immutable weth;

  constructor(IKeep3rV1Oracle _kp3r) public {
    kp3r = _kp3r;
    factory = _kp3r.factory();
    weth = _kp3r.WETH();
  }

  /// @dev Return the TWAP value price0. Revert if TWAP time range is not within the threshold.
  /// @param pair The pair to query for price0.
  function price0TWAP(address pair) public view returns (uint) {
    uint length = kp3r.observationLength(pair);
    require(length > 0, 'no length-1 observation');
    (uint lastTime, uint lastPx0Cumu, ) = kp3r.observations(pair, length - 1);
    if (lastTime > now - MIN_TWAP_TIME) {
      require(length > 1, 'no length-2 observation');
      (lastTime, lastPx0Cumu, ) = kp3r.observations(pair, length - 2);
    }
    uint elapsedTime = now - lastTime;
    require(elapsedTime >= MIN_TWAP_TIME && elapsedTime <= MAX_TWAP_TIME, 'bad TWAP time');
    uint currPx0Cumu = currentPx0Cumu(pair);
    return (currPx0Cumu - lastPx0Cumu) / (now - lastTime); // overflow is desired
  }

  /// @dev Return the TWAP value price1. Revert if TWAP time range is not within the threshold.
  /// @param pair The pair to query for price1.
  function price1TWAP(address pair) public view returns (uint) {
    uint length = kp3r.observationLength(pair);
    require(length > 0, 'no length-1 observation');
    (uint lastTime, , uint lastPx1Cumu) = kp3r.observations(pair, length - 1);
    if (lastTime > now - MIN_TWAP_TIME) {
      require(length > 1, 'no length-2 observation');
      (lastTime, , lastPx1Cumu) = kp3r.observations(pair, length - 2);
    }
    uint elapsedTime = now - lastTime;
    require(elapsedTime >= MIN_TWAP_TIME && elapsedTime <= MAX_TWAP_TIME, 'bad TWAP time');
    uint currPx1Cumu = currentPx1Cumu(pair);
    return (currPx1Cumu - lastPx1Cumu) / (now - lastTime); // overflow is desired
  }

  /// @dev Return the current price0 cumulative value on uniswap.
  /// @param pair The uniswap pair to query for price0 cumulative value.
  function currentPx0Cumu(address pair) public view returns (uint px0Cumu) {
    uint32 currTime = uint32(now);
    px0Cumu = IUniswapV2Pair(pair).price0CumulativeLast();
    (uint reserve0, uint reserve1, uint32 lastTime) = IUniswapV2Pair(pair).getReserves();
    if (lastTime != now) {
      uint32 timeElapsed = currTime - lastTime; // overflow is desired
      px0Cumu += uint((reserve1 << 112) / reserve0) * timeElapsed; // overflow is desired
    }
  }

  /// @dev Return the current price1 cumulative value on uniswap.
  /// @param pair The uniswap pair to query for price1 cumulative value.
  function currentPx1Cumu(address pair) public view returns (uint px1Cumu) {
    uint32 currTime = uint32(now);
    px1Cumu = IUniswapV2Pair(pair).price1CumulativeLast();
    (uint reserve0, uint reserve1, uint32 lastTime) = IUniswapV2Pair(pair).getReserves();
    if (lastTime != currTime) {
      uint32 timeElapsed = currTime - lastTime; // overflow is desired
      px1Cumu += uint((reserve0 << 112) / reserve1) * timeElapsed; // overflow is desired
    }
  }
}

File 50 of 72 : IKeep3rV1Oracle.sol
pragma solidity 0.6.12;

abstract contract IKeep3rV1Oracle {
  struct Observation {
    uint timestamp;
    uint price0Cumulative;
    uint price1Cumulative;
  }

  function WETH() external pure virtual returns (address);

  function factory() external pure virtual returns (address);

  mapping(address => Observation[]) public observations;

  function observationLength(address pair) external view virtual returns (uint);
}

File 51 of 72 : ProxyOracle.sol
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

import 'OpenZeppelin/[email protected]/contracts/math/SafeMath.sol';

import '../Governable.sol';
import '../../interfaces/IOracle.sol';
import '../../interfaces/IBaseOracle.sol';
import '../../interfaces/IERC20Wrapper.sol';

contract ProxyOracle is IOracle, Governable {
  using SafeMath for uint;

  /// The governor sets oracle information for a token.
  event SetOracle(address token, Oracle info);
  /// The governor unsets oracle information for a token.
  event UnsetOracle(address token);
  /// The governor sets token whitelist for an ERC1155 token.
  event SetWhitelist(address token, bool ok);

  struct Oracle {
    uint16 borrowFactor; // The borrow factor for this token, multiplied by 1e4.
    uint16 collateralFactor; // The collateral factor for this token, multiplied by 1e4.
    uint16 liqIncentive; // The liquidation incentive, multiplied by 1e4.
  }

  IBaseOracle public immutable source;
  mapping(address => Oracle) public oracles; // Mapping from token address to oracle info.
  mapping(address => bool) public whitelistERC1155;

  /// @dev Create the contract and initialize the first governor.
  constructor(IBaseOracle _source) public {
    source = _source;
    __Governable__init();
  }

  /// @dev Set oracle information for the given list of token addresses.
  function setOracles(address[] memory tokens, Oracle[] memory info) external onlyGov {
    require(tokens.length == info.length, 'inconsistent length');
    for (uint idx = 0; idx < tokens.length; idx++) {
      require(info[idx].borrowFactor >= 10000, 'borrow factor must be at least 100%');
      require(info[idx].collateralFactor <= 10000, 'collateral factor must be at most 100%');
      require(info[idx].liqIncentive >= 10000, 'incentive must be at least 100%');
      require(info[idx].liqIncentive <= 20000, 'incentive must be at most 200%');
      oracles[tokens[idx]] = info[idx];
      emit SetOracle(tokens[idx], info[idx]);
    }
  }

  function unsetOracles(address[] memory tokens) external onlyGov {
    for (uint idx = 0; idx < tokens.length; idx++) {
      oracles[tokens[idx]] = Oracle(0, 0, 0);
      emit UnsetOracle(tokens[idx]);
    }
  }

  /// @dev Set whitelist status for the given list of token addresses.
  function setWhitelistERC1155(address[] memory tokens, bool ok) external onlyGov {
    for (uint idx = 0; idx < tokens.length; idx++) {
      whitelistERC1155[tokens[idx]] = ok;
      emit SetWhitelist(tokens[idx], ok);
    }
  }

  /// @dev Return whether the oracle supports evaluating collateral value of the given token.
  function support(address token, uint id) external view override returns (bool) {
    if (!whitelistERC1155[token]) return false;
    address tokenUnderlying = IERC20Wrapper(token).getUnderlyingToken(id);
    return oracles[tokenUnderlying].liqIncentive != 0;
  }

  /// @dev Return the amount of token out as liquidation reward for liquidating token in.
  function convertForLiquidation(
    address tokenIn,
    address tokenOut,
    uint tokenOutId,
    uint amountIn
  ) external view override returns (uint) {
    require(whitelistERC1155[tokenOut], 'bad token');
    address tokenOutUnderlying = IERC20Wrapper(tokenOut).getUnderlyingToken(tokenOutId);
    uint rateUnderlying = IERC20Wrapper(tokenOut).getUnderlyingRate(tokenOutId);
    Oracle memory oracleIn = oracles[tokenIn];
    Oracle memory oracleOut = oracles[tokenOutUnderlying];
    require(oracleIn.liqIncentive != 0, 'bad underlying in');
    require(oracleOut.liqIncentive != 0, 'bad underlying out');
    uint pxIn = source.getETHPx(tokenIn);
    uint pxOut = source.getETHPx(tokenOutUnderlying);
    uint amountOut = amountIn.mul(pxIn).div(pxOut);
    amountOut = amountOut.mul(2**112).div(rateUnderlying);
    return amountOut.mul(oracleIn.liqIncentive).mul(oracleOut.liqIncentive).div(10000 * 10000);
  }

  /// @dev Return the value of the given input as ETH for collateral purpose.
  function asETHCollateral(
    address token,
    uint id,
    uint amount,
    address owner
  ) external view override returns (uint) {
    require(whitelistERC1155[token], 'bad token');
    address tokenUnderlying = IERC20Wrapper(token).getUnderlyingToken(id);
    uint rateUnderlying = IERC20Wrapper(token).getUnderlyingRate(id);
    uint amountUnderlying = amount.mul(rateUnderlying).div(2**112);
    Oracle memory oracle = oracles[tokenUnderlying];
    require(oracle.liqIncentive != 0, 'bad underlying collateral');
    uint ethValue = source.getETHPx(tokenUnderlying).mul(amountUnderlying).div(2**112);
    return ethValue.mul(oracle.collateralFactor).div(10000);
  }

  /// @dev Return the value of the given input as ETH for borrow purpose.
  function asETHBorrow(
    address token,
    uint amount,
    address owner
  ) external view override returns (uint) {
    Oracle memory oracle = oracles[token];
    require(oracle.liqIncentive != 0, 'bad underlying borrow');
    uint ethValue = source.getETHPx(token).mul(amount).div(2**112);
    return ethValue.mul(oracle.borrowFactor).div(10000);
  }
}

File 52 of 72 : IOracle.sol
pragma solidity 0.6.12;

interface IOracle {
  /// @dev Return whether the oracle supports evaluating collateral value of the given address.
  /// @param token The ERC-1155 token to check the acceptence.
  /// @param id The token id to check the acceptance.
  function support(address token, uint id) external view returns (bool);

  /// @dev Return the amount of token out as liquidation reward for liquidating token in.
  /// @param tokenIn The ERC-20 token that gets liquidated.
  /// @param tokenOut The ERC-1155 token to pay as reward.
  /// @param tokenOutId The id of the token to pay as reward.
  /// @param amountIn The amount of liquidating tokens.
  function convertForLiquidation(
    address tokenIn,
    address tokenOut,
    uint tokenOutId,
    uint amountIn
  ) external view returns (uint);

  /// @dev Return the value of the given input as ETH for collateral purpose.
  /// @param token The ERC-1155 token to check the value.
  /// @param id The id of the token to check the value.
  /// @param amount The amount of tokens to check the value.
  /// @param owner The owner of the token to check for collateral credit.
  function asETHCollateral(
    address token,
    uint id,
    uint amount,
    address owner
  ) external view returns (uint);

  /// @dev Return the value of the given input as ETH for borrow purpose.
  /// @param token The ERC-20 token to check the value.
  /// @param amount The amount of tokens to check the value.
  /// @param owner The owner of the token to check for borrow credit.
  function asETHBorrow(
    address token,
    uint amount,
    address owner
  ) external view returns (uint);
}

File 53 of 72 : CoreOracle.sol
pragma solidity 0.6.12;

import '../../interfaces/IBaseOracle.sol';
import '../Governable.sol';

contract CoreOracle is IBaseOracle, Governable {
  event SetRoute(address token, address route);
  mapping(address => address) public routes;

  constructor() public {
    __Governable__init();
  }

  function setRoute(address[] calldata tokens, address[] calldata targets) external onlyGov {
    require(tokens.length == targets.length, 'inconsistent length');
    for (uint idx = 0; idx < tokens.length; idx++) {
      routes[tokens[idx]] = targets[idx];
      emit SetRoute(tokens[idx], targets[idx]);
    }
  }

  function getETHPx(address token) external view override returns (uint) {
    uint px = IBaseOracle(routes[token]).getETHPx(token);
    require(px != 0, 'no px');
    return px;
  }
}

File 54 of 72 : HomoraBank.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/token/ERC20/IERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/token/ERC20/SafeERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/token/ERC1155/IERC1155.sol';
import 'OpenZeppelin/[email protected]/contracts/math/SafeMath.sol';
import 'OpenZeppelin/[email protected]/contracts/math/Math.sol';
import 'OpenZeppelin/[email protected]/contracts/proxy/Initializable.sol';

import './Governable.sol';
import './utils/ERC1155NaiveReceiver.sol';
import '../interfaces/IBank.sol';
import '../interfaces/ICErc20.sol';
import '../interfaces/IOracle.sol';

contract HomoraCaster {
  /// @dev Call to the target using the given data.
  /// @param target The address target to call.
  /// @param data The data used in the call.
  function cast(address target, bytes calldata data) external payable {
    (bool ok, bytes memory returndata) = target.call{value: msg.value}(data);
    if (!ok) {
      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('bad cast call');
      }
    }
  }
}

contract HomoraBank is Initializable, Governable, ERC1155NaiveReceiver, IBank {
  using SafeMath for uint;
  using SafeERC20 for IERC20;

  uint private constant _NOT_ENTERED = 1;
  uint private constant _ENTERED = 2;
  uint private constant _NO_ID = uint(-1);
  address private constant _NO_ADDRESS = address(1);

  struct Bank {
    bool isListed; // Whether this market exists.
    uint8 index; // Reverse look up index for this bank.
    address cToken; // The CToken to draw liquidity from.
    uint reserve; // The reserve portion allocated to Homora protocol.
    uint pendingReserve; // The pending reserve portion waiting to be resolve.
    uint totalDebt; // The last recorded total debt since last action.
    uint totalShare; // The total debt share count across all open positions.
  }

  struct Position {
    address owner; // The owner of this position.
    address collToken; // The ERC1155 token used as collateral for this position.
    uint collId; // The token id used as collateral.
    uint collateralSize; // The size of collateral token for this position.
    uint debtMap; // Bitmap of nonzero debt. i^th bit is set iff debt share of i^th bank is nonzero.
    mapping(address => uint) debtShareOf; // The debt share for each token.
  }

  uint public _GENERAL_LOCK; // TEMPORARY: re-entrancy lock guard.
  uint public _IN_EXEC_LOCK; // TEMPORARY: exec lock guard.
  uint public override POSITION_ID; // TEMPORARY: position ID currently under execution.
  address public override SPELL; // TEMPORARY: spell currently under execution.

  address public caster; // The caster address for untrusted execution.
  IOracle public oracle; // The oracle address for determining prices.
  uint public feeBps; // The fee collected as protocol reserve in basis point from interest.
  uint public override nextPositionId; // Next available position ID, starting from 1 (see initialize).

  address[] public allBanks; // The list of all listed banks.
  mapping(address => Bank) public banks; // Mapping from token to bank data.
  mapping(address => bool) public cTokenInBank; // Mapping from cToken to its existence in bank.
  mapping(uint => Position) public positions; // Mapping from position ID to position data.

  /// @dev Reentrancy lock guard.
  modifier lock() {
    require(_GENERAL_LOCK == _NOT_ENTERED, 'general lock');
    _GENERAL_LOCK = _ENTERED;
    _;
    _GENERAL_LOCK = _NOT_ENTERED;
  }

  /// @dev Ensure that the function is called from within the execution scope.
  modifier inExec() {
    require(POSITION_ID != _NO_ID, 'not within execution');
    require(SPELL == msg.sender, 'not from spell');
    require(_IN_EXEC_LOCK == _NOT_ENTERED, 'in exec lock');
    _IN_EXEC_LOCK = _ENTERED;
    _;
    _IN_EXEC_LOCK = _NOT_ENTERED;
  }

  /// @dev Ensure that the interest rate of the given token is accrued.
  modifier poke(address token) {
    accrue(token);
    _;
  }

  /// @dev Initialize the bank smart contract, using msg.sender as the first governor.
  /// @param _oracle The oracle smart contract address.
  /// @param _feeBps The fee collected to Homora bank.
  function initialize(IOracle _oracle, uint _feeBps) external initializer {
    __Governable__init();
    _GENERAL_LOCK = _NOT_ENTERED;
    _IN_EXEC_LOCK = _NOT_ENTERED;
    POSITION_ID = _NO_ID;
    SPELL = _NO_ADDRESS;
    caster = address(new HomoraCaster());
    oracle = _oracle;
    require(address(_oracle) != address(0), 'bad oracle address');
    feeBps = _feeBps;
    nextPositionId = 1;
    emit SetOracle(address(_oracle));
    emit SetFeeBps(_feeBps);
  }

  /// @dev Return the current executor (the owner of the current position).
  function EXECUTOR() external view override returns (address) {
    uint positionId = POSITION_ID;
    require(positionId != _NO_ID, 'not under execution');
    return positions[positionId].owner;
  }

  /// @dev Trigger interest accrual for the given bank.
  /// @param token The underlying token to trigger the interest accrual.
  function accrue(address token) public override {
    Bank storage bank = banks[token];
    require(bank.isListed, 'bank not exists');
    uint totalDebt = bank.totalDebt;
    uint debt = ICErc20(bank.cToken).borrowBalanceCurrent(address(this));
    if (debt > totalDebt) {
      uint fee = debt.sub(totalDebt).mul(feeBps).div(10000);
      bank.totalDebt = debt;
      bank.pendingReserve = bank.pendingReserve.add(fee);
    } else if (totalDebt != debt) {
      // We should never reach here because CREAMv2 does not support *repayBorrowBehalf*
      // functionality. We set bank.totalDebt = debt nonetheless to ensure consistency. But do
      // note that if *repayBorrowBehalf* exists, an attacker can maliciously deflate debt
      // share value and potentially make this contract stop working due to math overflow.
      bank.totalDebt = debt;
    }
  }

  /// @dev Convenient function to trigger interest accrual for a list of banks.
  /// @param tokens The list of banks to trigger interest accrual.
  function accrueAll(address[] memory tokens) external {
    for (uint idx = 0; idx < tokens.length; idx++) {
      accrue(tokens[idx]);
    }
  }

  /// @dev Trigger reserve resolve by borrowing the pending amount for reserve.
  /// @param token The underlying token to trigger reserve resolve.
  function resolveReserve(address token) public lock poke(token) {
    Bank storage bank = banks[token];
    require(bank.isListed, 'bank not exists');
    uint pendingReserve = bank.pendingReserve;
    bank.pendingReserve = 0;
    bank.reserve = bank.reserve.add(doBorrow(token, pendingReserve));
  }

  /// @dev Convenient function to trigger reserve resolve for the list of banks.
  /// @param tokens The list of banks to trigger reserve resolve.
  function resolveReserveAll(address[] memory tokens) external {
    for (uint idx = 0; idx < tokens.length; idx++) {
      resolveReserve(tokens[idx]);
    }
  }

  /// @dev Return the borrow balance for given positon and token without trigger interest accrual.
  /// @param positionId The position to query for borrow balance.
  /// @param token The token to query for borrow balance.
  function borrowBalanceStored(uint positionId, address token) public view override returns (uint) {
    uint totalDebt = banks[token].totalDebt;
    uint totalShare = banks[token].totalShare;
    uint share = positions[positionId].debtShareOf[token];
    if (share == 0 || totalDebt == 0) {
      return 0;
    } else {
      return share.mul(totalDebt).div(totalShare);
    }
  }

  /// @dev Trigger interest accrual and return the current borrow balance.
  /// @param positionId The position to query for borrow balance.
  /// @param token The token to query for borrow balance.
  function borrowBalanceCurrent(uint positionId, address token) external override returns (uint) {
    accrue(token);
    return borrowBalanceStored(positionId, token);
  }

  /// @dev Return bank information for the given token.
  /// @param token The token address to query for bank information.
  function getBankInfo(address token)
    external
    view
    override
    returns (
      bool isListed,
      address cToken,
      uint reserve,
      uint totalDebt,
      uint totalShare
    )
  {
    Bank storage bank = banks[token];
    return (bank.isListed, bank.cToken, bank.reserve, bank.totalDebt, bank.totalShare);
  }

  /// @dev Return position information for the given position id.
  /// @param positionId The position id to query for position information.
  function getPositionInfo(uint positionId)
    external
    view
    override
    returns (
      address owner,
      address collToken,
      uint collId,
      uint collateralSize
    )
  {
    Position storage pos = positions[positionId];
    return (pos.owner, pos.collToken, pos.collId, pos.collateralSize);
  }

  /// @dev Return the debt share of the given bank token for the given position id.
  function getPositionDebtShareOf(uint positionId, address token) external view returns (uint) {
    return positions[positionId].debtShareOf[token];
  }

  /// @dev Return the list of all debts for the given position id.
  function getPositionDebts(uint positionId)
    external
    view
    returns (address[] memory tokens, uint[] memory debts)
  {
    Position storage pos = positions[positionId];
    uint count = 0;
    uint bitMap = pos.debtMap;
    while (bitMap > 0) {
      if ((bitMap & 1) != 0) {
        count++;
      }
      bitMap >>= 1;
    }
    tokens = new address[](count);
    debts = new uint[](count);
    bitMap = pos.debtMap;
    count = 0;
    uint idx = 0;
    while (bitMap > 0) {
      if ((bitMap & 1) != 0) {
        address token = allBanks[idx];
        Bank storage bank = banks[token];
        tokens[count] = token;
        debts[count] = pos.debtShareOf[token].mul(bank.totalDebt).div(bank.totalShare);
        count++;
      }
      idx++;
      bitMap >>= 1;
    }
  }

  /// @dev Return the total collateral value of the given position in ETH.
  /// @param positionId The position ID to query for the collateral value.
  function getCollateralETHValue(uint positionId) public view returns (uint) {
    Position storage pos = positions[positionId];
    uint size = pos.collateralSize;
    if (size == 0) {
      return 0;
    } else {
      require(pos.collToken != address(0), 'bad collateral token');
      return oracle.asETHCollateral(pos.collToken, pos.collId, size, pos.owner);
    }
  }

  /// @dev Return the total borrow value of the given position in ETH.
  /// @param positionId The position ID to query for the borrow value.
  function getBorrowETHValue(uint positionId) public view override returns (uint) {
    uint value = 0;
    Position storage pos = positions[positionId];
    address owner = pos.owner;
    uint bitMap = pos.debtMap;
    uint idx = 0;
    while (bitMap > 0) {
      if ((bitMap & 1) != 0) {
        address token = allBanks[idx];
        uint share = pos.debtShareOf[token];
        Bank storage bank = banks[token];
        uint debt = share.mul(bank.totalDebt).div(bank.totalShare);
        value = value.add(oracle.asETHBorrow(token, debt, owner));
      }
      idx++;
      bitMap >>= 1;
    }
    return value;
  }

  /// @dev Add a new bank to the ecosystem.
  /// @param token The underlying token for the bank.
  /// @param cToken The address of the cToken smart contract.
  function addBank(address token, address cToken) external onlyGov {
    Bank storage bank = banks[token];
    require(!cTokenInBank[cToken], 'cToken already exists');
    require(!bank.isListed, 'bank already exists');
    cTokenInBank[cToken] = true;
    bank.isListed = true;
    require(allBanks.length < 256, 'reach bank limit');
    bank.index = uint8(allBanks.length);
    bank.cToken = cToken;
    IERC20(token).safeApprove(cToken, 0);
    IERC20(token).safeApprove(cToken, uint(-1));
    allBanks.push(token);
    emit AddBank(token, cToken);
  }

  /// @dev Set the oracle smart contract address.
  /// @param _oracle The new oracle smart contract address.
  function setOracle(IOracle _oracle) external onlyGov {
    oracle = _oracle;
    emit SetOracle(address(_oracle));
  }

  /// @dev Set the fee bps value that Homora bank charges.
  /// @param _feeBps The new fee bps value.
  function setFeeBps(uint _feeBps) external onlyGov {
    require(_feeBps <= 10000, 'fee too high');
    feeBps = _feeBps;
    emit SetFeeBps(_feeBps);
  }

  /// @dev Withdraw the reserve portion of the bank.
  /// @param amount The amount of tokens to withdraw.
  function withdrawReserve(address token, uint amount) external onlyGov lock {
    Bank storage bank = banks[token];
    require(bank.isListed, 'bank not exists');
    bank.reserve = bank.reserve.sub(amount);
    IERC20(token).safeTransfer(msg.sender, amount);
    emit WithdrawReserve(msg.sender, token, amount);
  }

  /// @dev Liquidate a position. Pay debt for its owner and take the collateral.
  /// @param positionId The position ID to liquidate.
  /// @param debtToken The debt token to repay.
  /// @param amountCall The amount to repay when doing transferFrom call.
  function liquidate(
    uint positionId,
    address debtToken,
    uint amountCall
  ) external override lock poke(debtToken) {
    uint collateralValue = getCollateralETHValue(positionId);
    uint borrowValue = getBorrowETHValue(positionId);
    require(collateralValue < borrowValue, 'position still healthy');
    Position storage pos = positions[positionId];
    (uint amountPaid, uint share) = repayInternal(positionId, debtToken, amountCall);
    require(pos.collToken != address(0), 'bad collateral token');
    uint bounty =
      Math.min(
        oracle.convertForLiquidation(debtToken, pos.collToken, pos.collId, amountPaid),
        pos.collateralSize
      );
    pos.collateralSize = pos.collateralSize.sub(bounty);
    IERC1155(pos.collToken).safeTransferFrom(address(this), msg.sender, pos.collId, bounty, '');
    emit Liquidate(positionId, msg.sender, debtToken, amountPaid, share, bounty);
  }

  /// @dev Execute the action via HomoraCaster, calling its function with the supplied data.
  /// @param positionId The position ID to execute the action, or zero for new position.
  /// @param spell The target spell to invoke the execution via HomoraCaster.
  /// @param data Extra data to pass to the target for the execution.
  function execute(
    uint positionId,
    address spell,
    bytes memory data
  ) external payable lock returns (uint) {
    if (positionId == 0) {
      positionId = nextPositionId++;
      positions[positionId].owner = msg.sender;
    } else {
      require(positionId < nextPositionId, 'position id not exists');
      require(msg.sender == positions[positionId].owner, 'not position owner');
    }
    POSITION_ID = positionId;
    SPELL = spell;
    HomoraCaster(caster).cast{value: msg.value}(spell, data);
    uint collateralValue = getCollateralETHValue(positionId);
    uint borrowValue = getBorrowETHValue(positionId);
    require(collateralValue >= borrowValue, 'insufficient collateral');
    POSITION_ID = _NO_ID;
    SPELL = _NO_ADDRESS;
    return positionId;
  }

  /// @dev Borrow tokens from that bank. Must only be called while under execution.
  /// @param token The token to borrow from the bank.
  /// @param amount The amount of tokens to borrow.
  function borrow(address token, uint amount) external override inExec poke(token) {
    Bank storage bank = banks[token];
    require(bank.isListed, 'bank not exists');
    Position storage pos = positions[POSITION_ID];
    uint totalShare = bank.totalShare;
    uint totalDebt = bank.totalDebt;
    uint share = totalShare == 0 ? amount : amount.mul(totalShare).div(totalDebt);
    bank.totalShare = bank.totalShare.add(share);
    uint newShare = pos.debtShareOf[token].add(share);
    pos.debtShareOf[token] = newShare;
    if (newShare > 0) {
      pos.debtMap |= (1 << uint(bank.index));
    }
    IERC20(token).safeTransfer(msg.sender, doBorrow(token, amount));
    emit Borrow(POSITION_ID, msg.sender, token, amount, share);
  }

  /// @dev Repay tokens to the bank. Must only be called while under execution.
  /// @param token The token to repay to the bank.
  /// @param amountCall The amount of tokens to repay via transferFrom.
  function repay(address token, uint amountCall) external override inExec poke(token) {
    (uint amount, uint share) = repayInternal(POSITION_ID, token, amountCall);
    emit Repay(POSITION_ID, msg.sender, token, amount, share);
  }

  /// @dev Perform repay action. Return the amount actually taken and the debt share reduced.
  /// @param positionId The position ID to repay the debt.
  /// @param token The bank token to pay the debt.
  /// @param amountCall The amount to repay by calling transferFrom, or -1 for debt size.
  function repayInternal(
    uint positionId,
    address token,
    uint amountCall
  ) internal returns (uint, uint) {
    Bank storage bank = banks[token];
    require(bank.isListed, 'bank not exists');
    Position storage pos = positions[positionId];
    uint totalShare = bank.totalShare;
    uint totalDebt = bank.totalDebt;
    uint oldShare = pos.debtShareOf[token];
    uint oldDebt = oldShare.mul(totalDebt).div(totalShare);
    if (amountCall == uint(-1)) {
      amountCall = oldDebt;
    }
    uint paid = doRepay(token, doERC20TransferIn(token, amountCall));
    require(paid <= oldDebt, 'paid exceeds debt'); // prevent share overflow attack
    uint lessShare = paid == oldDebt ? oldShare : paid.mul(totalShare).div(totalDebt);
    bank.totalShare = totalShare.sub(lessShare);
    uint newShare = oldShare.sub(lessShare);
    pos.debtShareOf[token] = newShare;
    if (newShare == 0) {
      pos.debtMap &= ~(1 << uint(bank.index));
    }
    return (paid, lessShare);
  }

  /// @dev Transmit user assets to the caller, so users only need to approve Bank for spending.
  /// @param token The token to transfer from user to the caller.
  /// @param amount The amount to transfer.
  function transmit(address token, uint amount) external override inExec {
    Position storage pos = positions[POSITION_ID];
    IERC20(token).safeTransferFrom(pos.owner, msg.sender, amount);
  }

  /// @dev Put more collateral for users. Must only be called during execution.
  /// @param collToken The ERC1155 token to collateral.
  /// @param collId The token id to collateral.
  /// @param amountCall The amount of tokens to put via transferFrom.
  function putCollateral(
    address collToken,
    uint collId,
    uint amountCall
  ) external override inExec {
    Position storage pos = positions[POSITION_ID];
    if (pos.collToken != collToken || pos.collId != collId) {
      require(oracle.support(collToken, collId), 'collateral not supported');
      require(pos.collateralSize == 0, 'another type of collateral already exists');
      pos.collToken = collToken;
      pos.collId = collId;
    }
    uint amount = doERC1155TransferIn(collToken, collId, amountCall);
    pos.collateralSize = pos.collateralSize.add(amount);
    emit PutCollateral(POSITION_ID, msg.sender, collToken, collId, amount);
  }

  /// @dev Take some collateral back. Must only be called during execution.
  /// @param collToken The ERC1155 token to take back.
  /// @param collId The token id to take back.
  /// @param amount The amount of tokens to take back via transfer.
  function takeCollateral(
    address collToken,
    uint collId,
    uint amount
  ) external override inExec {
    Position storage pos = positions[POSITION_ID];
    require(collToken == pos.collToken, 'invalid collateral token');
    require(collId == pos.collId, 'invalid collateral token');
    if (amount == uint(-1)) {
      amount = pos.collateralSize;
    }
    pos.collateralSize = pos.collateralSize.sub(amount);
    IERC1155(collToken).safeTransferFrom(address(this), msg.sender, collId, amount, '');
    emit TakeCollateral(POSITION_ID, msg.sender, collToken, collId, amount);
  }

  /// @dev Internal function to perform borrow from the bank and return the amount received.
  /// @param token The token to perform borrow action.
  /// @param amountCall The amount use in the transferFrom call.
  /// NOTE: Caller must ensure that cToken interest was already accrued up to this block.
  function doBorrow(address token, uint amountCall) internal returns (uint) {
    Bank storage bank = banks[token]; // assume the input is already sanity checked.
    uint balanceBefore = IERC20(token).balanceOf(address(this));
    require(ICErc20(bank.cToken).borrow(amountCall) == 0, 'bad borrow');
    uint balanceAfter = IERC20(token).balanceOf(address(this));
    bank.totalDebt = bank.totalDebt.add(amountCall);
    return balanceAfter.sub(balanceBefore);
  }

  /// @dev Internal function to perform repay to the bank and return the amount actually repaid.
  /// @param token The token to perform repay action.
  /// @param amountCall The amount to use in the repay call.
  /// NOTE: Caller must ensure that cToken interest was already accrued up to this block.
  function doRepay(address token, uint amountCall) internal returns (uint) {
    Bank storage bank = banks[token]; // assume the input is already sanity checked.
    ICErc20 cToken = ICErc20(bank.cToken);
    uint oldDebt = bank.totalDebt;
    require(cToken.repayBorrow(amountCall) == 0, 'bad repay');
    uint newDebt = cToken.borrowBalanceStored(address(this));
    bank.totalDebt = newDebt;
    return oldDebt.sub(newDebt);
  }

  /// @dev Internal function to perform ERC20 transfer in and return amount actually received.
  /// @param token The token to perform transferFrom action.
  /// @param amountCall The amount use in the transferFrom call.
  function doERC20TransferIn(address token, uint amountCall) internal returns (uint) {
    uint balanceBefore = IERC20(token).balanceOf(address(this));
    IERC20(token).safeTransferFrom(msg.sender, address(this), amountCall);
    uint balanceAfter = IERC20(token).balanceOf(address(this));
    return balanceAfter.sub(balanceBefore);
  }

  /// @dev Internal function to perform ERC1155 transfer in and return amount actually received.
  /// @param token The token to perform transferFrom action.
  /// @param id The id to perform transferFrom action.
  /// @param amountCall The amount use in the transferFrom call.
  function doERC1155TransferIn(
    address token,
    uint id,
    uint amountCall
  ) internal returns (uint) {
    uint balanceBefore = IERC1155(token).balanceOf(address(this), id);
    IERC1155(token).safeTransferFrom(msg.sender, address(this), id, amountCall, '');
    uint balanceAfter = IERC1155(token).balanceOf(address(this), id);
    return balanceAfter.sub(balanceBefore);
  }
}

File 55 of 72 : SafeBoxETH.sol
pragma solidity 0.6.12;

import 'OpenZeppelin/[email protected]/contracts/token/ERC20/ERC20.sol';
import 'OpenZeppelin/[email protected]/contracts/cryptography/MerkleProof.sol';
import 'OpenZeppelin/[email protected]/contracts/math/SafeMath.sol';
import 'OpenZeppelin/[email protected]/contracts/utils/ReentrancyGuard.sol';
import './Governable.sol';
import '../interfaces/ICErc20.sol';
import '../interfaces/IWETH.sol';

contract SafeBoxETH is Governable, ERC20, ReentrancyGuard {
  using SafeMath for uint;
  event Claim(address user, uint amount);

  ICErc20 public immutable cToken;
  IWETH public immutable weth;

  address public relayer;
  bytes32 public root;
  mapping(address => uint) public claimed;

  constructor(
    ICErc20 _cToken,
    string memory _name,
    string memory _symbol
  ) public ERC20(_name, _symbol) {
    IWETH _weth = IWETH(_cToken.underlying());
    __Governable__init();
    cToken = _cToken;
    weth = _weth;
    relayer = msg.sender;
    _weth.approve(address(_cToken), uint(-1));
  }

  function setRelayer(address _relayer) external onlyGov {
    relayer = _relayer;
  }

  function updateRoot(bytes32 _root) external {
    require(msg.sender == relayer || msg.sender == governor, '!relayer');
    root = _root;
  }

  function deposit() external payable nonReentrant {
    weth.deposit{value: msg.value}();
    uint cBalanceBefore = cToken.balanceOf(address(this));
    require(cToken.mint(msg.value) == 0, '!mint');
    uint cBalanceAfter = cToken.balanceOf(address(this));
    _mint(msg.sender, cBalanceAfter.sub(cBalanceBefore));
  }

  function withdraw(uint amount) public nonReentrant {
    _burn(msg.sender, amount);
    uint wethBalanceBefore = weth.balanceOf(address(this));
    require(cToken.redeem(amount) == 0, '!redeem');
    uint wethBalanceAfter = weth.balanceOf(address(this));
    uint wethAmount = wethBalanceAfter.sub(wethBalanceBefore);
    weth.withdraw(wethAmount);
    (bool success, ) = msg.sender.call{value: wethAmount}(new bytes(0));
    require(success, '!withdraw');
  }

  function claim(uint totalReward, bytes32[] memory proof) public nonReentrant {
    bytes32 leaf = keccak256(abi.encodePacked(msg.sender, totalReward));
    require(MerkleProof.verify(proof, root, leaf), '!proof');
    uint send = totalReward.sub(claimed[msg.sender]);
    claimed[msg.sender] = totalReward;
    weth.withdraw(send);
    (bool success, ) = msg.sender.call{value: send}(new bytes(0));
    require(success, '!claim');
    emit Claim(msg.sender, send);
  }

  function adminClaim(uint amount) external onlyGov {
    weth.withdraw(amount);
    (bool success, ) = msg.sender.call{value: amount}(new bytes(0));
    require(success, '!adminClaim');
  }

  function claimAndWithdraw(
    uint claimAmount,
    bytes32[] memory proof,
    uint withdrawAmount
  ) external {
    claim(claimAmount, proof);
    withdraw(withdrawAmount);
  }

  receive() external payable {
    require(msg.sender == address(weth), '!weth');
  }
}

File 56 of 72 : ERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

import "../../GSN/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.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;
    using Address for address;

    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 returns (string memory) {
        return _name;
    }

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

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view 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 {
        _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 57 of 72 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.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 58 of 72 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.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 59 of 72 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

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

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

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

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

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

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

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

File 60 of 72 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.2;

/**
 * @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 in 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");
        return _functionCallWithValue(target, data, value, errorMessage);
    }

    function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
        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 61 of 72 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.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 62 of 72 : MerkleProof.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.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 63 of 72 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor () internal {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 64 of 72 : Initializable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.4.24 <0.7.0;


/**
 * @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) {
        // extcodesize checks the size of the code stored in an address, and
        // address returns the current address. Since the code is still not
        // deployed when running a constructor, any checks on its code size will
        // yield zero, making it an effective way to detect if a contract is
        // under construction or not.
        address self = address(this);
        uint256 cs;
        // solhint-disable-next-line no-inline-assembly
        assembly { cs := extcodesize(self) }
        return cs == 0;
    }
}

File 65 of 72 : ERC1155.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

import "./IERC1155.sol";
import "./IERC1155MetadataURI.sol";
import "./IERC1155Receiver.sol";
import "../../GSN/Context.sol";
import "../../introspection/ERC165.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";

/**
 *
 * @dev Implementation of the basic standard multi-token.
 * See https://eips.ethereum.org/EIPS/eip-1155
 * Originally based on code by Enjin: https://github.com/enjin/erc-1155
 *
 * _Available since v3.1._
 */
contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
    using SafeMath for uint256;
    using Address for address;

    // Mapping from token ID to account balances
    mapping (uint256 => mapping(address => uint256)) private _balances;

    // Mapping from account to operator approvals
    mapping (address => mapping(address => bool)) private _operatorApprovals;

    // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
    string private _uri;

    /*
     *     bytes4(keccak256('balanceOf(address,uint256)')) == 0x00fdd58e
     *     bytes4(keccak256('balanceOfBatch(address[],uint256[])')) == 0x4e1273f4
     *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
     *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
     *     bytes4(keccak256('safeTransferFrom(address,address,uint256,uint256,bytes)')) == 0xf242432a
     *     bytes4(keccak256('safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)')) == 0x2eb2c2d6
     *
     *     => 0x00fdd58e ^ 0x4e1273f4 ^ 0xa22cb465 ^
     *        0xe985e9c5 ^ 0xf242432a ^ 0x2eb2c2d6 == 0xd9b67a26
     */
    bytes4 private constant _INTERFACE_ID_ERC1155 = 0xd9b67a26;

    /*
     *     bytes4(keccak256('uri(uint256)')) == 0x0e89341c
     */
    bytes4 private constant _INTERFACE_ID_ERC1155_METADATA_URI = 0x0e89341c;

    /**
     * @dev See {_setURI}.
     */
    constructor (string memory uri) public {
        _setURI(uri);

        // register the supported interfaces to conform to ERC1155 via ERC165
        _registerInterface(_INTERFACE_ID_ERC1155);

        // register the supported interfaces to conform to ERC1155MetadataURI via ERC165
        _registerInterface(_INTERFACE_ID_ERC1155_METADATA_URI);
    }

    /**
     * @dev See {IERC1155MetadataURI-uri}.
     *
     * This implementation returns the same URI for *all* token types. It relies
     * on the token type ID substitution mechanism
     * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
     *
     * Clients calling this function must replace the `\{id\}` substring with the
     * actual token type ID.
     */
    function uri(uint256) external view override returns (string memory) {
        return _uri;
    }

    /**
     * @dev See {IERC1155-balanceOf}.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) public view override returns (uint256) {
        require(account != address(0), "ERC1155: balance query for the zero address");
        return _balances[id][account];
    }

    /**
     * @dev See {IERC1155-balanceOfBatch}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(
        address[] memory accounts,
        uint256[] memory ids
    )
        public
        view
        override
        returns (uint256[] memory)
    {
        require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");

        uint256[] memory batchBalances = new uint256[](accounts.length);

        for (uint256 i = 0; i < accounts.length; ++i) {
            require(accounts[i] != address(0), "ERC1155: batch balance query for the zero address");
            batchBalances[i] = _balances[ids[i]][accounts[i]];
        }

        return batchBalances;
    }

    /**
     * @dev See {IERC1155-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        require(_msgSender() != operator, "ERC1155: setting approval status for self");

        _operatorApprovals[_msgSender()][operator] = approved;
        emit ApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC1155-isApprovedForAll}.
     */
    function isApprovedForAll(address account, address operator) public view override returns (bool) {
        return _operatorApprovals[account][operator];
    }

    /**
     * @dev See {IERC1155-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    )
        public
        virtual
        override
    {
        require(to != address(0), "ERC1155: transfer to the zero address");
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155: caller is not owner nor approved"
        );

        address operator = _msgSender();

        _beforeTokenTransfer(operator, from, to, _asSingletonArray(id), _asSingletonArray(amount), data);

        _balances[id][from] = _balances[id][from].sub(amount, "ERC1155: insufficient balance for transfer");
        _balances[id][to] = _balances[id][to].add(amount);

        emit TransferSingle(operator, from, to, id, amount);

        _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
    }

    /**
     * @dev See {IERC1155-safeBatchTransferFrom}.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    )
        public
        virtual
        override
    {
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
        require(to != address(0), "ERC1155: transfer to the zero address");
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155: transfer caller is not owner nor approved"
        );

        address operator = _msgSender();

        _beforeTokenTransfer(operator, from, to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            _balances[id][from] = _balances[id][from].sub(
                amount,
                "ERC1155: insufficient balance for transfer"
            );
            _balances[id][to] = _balances[id][to].add(amount);
        }

        emit TransferBatch(operator, from, to, ids, amounts);

        _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
    }

    /**
     * @dev Sets a new URI for all token types, by relying on the token type ID
     * substitution mechanism
     * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
     *
     * By this mechanism, any occurrence of the `\{id\}` substring in either the
     * URI or any of the amounts in the JSON file at said URI will be replaced by
     * clients with the token type ID.
     *
     * For example, the `https://token-cdn-domain/\{id\}.json` URI would be
     * interpreted by clients as
     * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
     * for token type ID 0x4cce0.
     *
     * See {uri}.
     *
     * Because these URIs cannot be meaningfully represented by the {URI} event,
     * this function emits no events.
     */
    function _setURI(string memory newuri) internal virtual {
        _uri = newuri;
    }

    /**
     * @dev Creates `amount` tokens of token type `id`, and assigns them to `account`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function _mint(address account, uint256 id, uint256 amount, bytes memory data) internal virtual {
        require(account != address(0), "ERC1155: mint to the zero address");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, address(0), account, _asSingletonArray(id), _asSingletonArray(amount), data);

        _balances[id][account] = _balances[id][account].add(amount);
        emit TransferSingle(operator, address(0), account, id, amount);

        _doSafeTransferAcceptanceCheck(operator, address(0), account, id, amount, data);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

        for (uint i = 0; i < ids.length; i++) {
            _balances[ids[i]][to] = amounts[i].add(_balances[ids[i]][to]);
        }

        emit TransferBatch(operator, address(0), to, ids, amounts);

        _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
    }

    /**
     * @dev Destroys `amount` tokens of token type `id` from `account`
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens of token type `id`.
     */
    function _burn(address account, uint256 id, uint256 amount) internal virtual {
        require(account != address(0), "ERC1155: burn from the zero address");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, account, address(0), _asSingletonArray(id), _asSingletonArray(amount), "");

        _balances[id][account] = _balances[id][account].sub(
            amount,
            "ERC1155: burn amount exceeds balance"
        );

        emit TransferSingle(operator, account, address(0), id, amount);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     */
    function _burnBatch(address account, uint256[] memory ids, uint256[] memory amounts) internal virtual {
        require(account != address(0), "ERC1155: burn from the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, account, address(0), ids, amounts, "");

        for (uint i = 0; i < ids.length; i++) {
            _balances[ids[i]][account] = _balances[ids[i]][account].sub(
                amounts[i],
                "ERC1155: burn amount exceeds balance"
            );
        }

        emit TransferBatch(operator, account, address(0), ids, amounts);
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning, as well as batched variants.
     *
     * The same hook is called on both single and batched variants. For single
     * transfers, the length of the `id` and `amount` arrays will be 1.
     *
     * Calling conditions (for each `id` and `amount` pair):
     *
     * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * of token type `id` will be  transferred to `to`.
     * - When `from` is zero, `amount` tokens of token type `id` will be minted
     * for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
     * will be burned.
     * - `from` and `to` are never both zero.
     * - `ids` and `amounts` have the same, non-zero length.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    )
        internal virtual
    { }

    function _doSafeTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    )
        private
    {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                if (response != IERC1155Receiver(to).onERC1155Received.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non ERC1155Receiver implementer");
            }
        }
    }

    function _doSafeBatchTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    )
        private
    {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (bytes4 response) {
                if (response != IERC1155Receiver(to).onERC1155BatchReceived.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non ERC1155Receiver implementer");
            }
        }
    }

    function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
        uint256[] memory array = new uint256[](1);
        array[0] = element;

        return array;
    }
}

File 66 of 72 : IERC1155.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.2;

import "../../introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155 is IERC165 {
    /**
     * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
     */
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    /**
     * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
     * transfers.
     */
    event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);

    /**
     * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
     * `approved`.
     */
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    /**
     * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
     *
     * If an {URI} event was emitted for `id`, the standard
     * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
     * returned by {IERC1155MetadataURI-uri}.
     */
    event URI(string value, uint256 indexed id);

    /**
     * @dev Returns the amount of tokens of token type `id` owned by `account`.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) external view returns (uint256);

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);

    /**
     * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
     *
     * Emits an {ApprovalForAll} event.
     *
     * Requirements:
     *
     * - `operator` cannot be the caller.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address account, address operator) external view returns (bool);

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;
}

File 67 of 72 : IERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.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 68 of 72 : IERC1155MetadataURI.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.2;

import "./IERC1155.sol";

/**
 * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
 * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155MetadataURI is IERC1155 {
    /**
     * @dev Returns the URI for token type `id`.
     *
     * If the `\{id\}` substring is present in the URI, it must be replaced by
     * clients with the actual token type ID.
     */
    function uri(uint256 id) external view returns (string memory);
}

File 69 of 72 : IERC1155Receiver.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

import "../../introspection/IERC165.sol";

/**
 * _Available since v3.1._
 */
interface IERC1155Receiver is IERC165 {

    /**
        @dev Handles the receipt of a single ERC1155 token type. This function is
        called at the end of a `safeTransferFrom` after the balance has been updated.
        To accept the transfer, this must return
        `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
        (i.e. 0xf23a6e61, or its own function selector).
        @param operator The address which initiated the transfer (i.e. msg.sender)
        @param from The address which previously owned the token
        @param id The ID of the token being transferred
        @param value The amount of tokens being transferred
        @param data Additional data with no specified format
        @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
    */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    )
        external
        returns(bytes4);

    /**
        @dev Handles the receipt of a multiple ERC1155 token types. This function
        is called at the end of a `safeBatchTransferFrom` after the balances have
        been updated. To accept the transfer(s), this must return
        `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
        (i.e. 0xbc197c81, or its own function selector).
        @param operator The address which initiated the batch transfer (i.e. msg.sender)
        @param from The address which previously owned the token
        @param ids An array containing ids of each token being transferred (order and length must match values array)
        @param values An array containing amounts of each token being transferred (order and length must match ids array)
        @param data Additional data with no specified format
        @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
    */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    )
        external
        returns(bytes4);
}

File 70 of 72 : ERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts may inherit from this and call {_registerInterface} to declare
 * their support of an interface.
 */
contract ERC165 is IERC165 {
    /*
     * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
     */
    bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;

    /**
     * @dev Mapping of interface ids to whether or not it's supported.
     */
    mapping(bytes4 => bool) private _supportedInterfaces;

    constructor () internal {
        // Derived contracts need only register support for their own interfaces,
        // we register support for ERC165 itself here
        _registerInterface(_INTERFACE_ID_ERC165);
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     *
     * Time complexity O(1), guaranteed to always use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
        return _supportedInterfaces[interfaceId];
    }

    /**
     * @dev Registers the contract as an implementer of the interface defined by
     * `interfaceId`. Support of the actual ERC165 interface is automatic and
     * registering its interface id is not required.
     *
     * See {IERC165-supportsInterface}.
     *
     * Requirements:
     *
     * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
     */
    function _registerInterface(bytes4 interfaceId) internal virtual {
        require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
        _supportedInterfaces[interfaceId] = true;
    }
}

File 71 of 72 : ERC1155Receiver.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

import "./IERC1155Receiver.sol";
import "../../introspection/ERC165.sol";

/**
 * @dev _Available since v3.1._
 */
abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
    constructor() public {
        _registerInterface(
            ERC1155Receiver(0).onERC1155Received.selector ^
            ERC1155Receiver(0).onERC1155BatchReceived.selector
        );
    }
}

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

pragma solidity ^0.6.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);
    }
}

Settings
{
  "metadata": {
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IBank","name":"_bank","type":"address"},{"internalType":"address","name":"_werc20","type":"address"},{"internalType":"contract IUniswapV2Router02","name":"_router","type":"address"},{"internalType":"address","name":"_wmasterchef","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"components":[{"internalType":"uint256","name":"amtAUser","type":"uint256"},{"internalType":"uint256","name":"amtBUser","type":"uint256"},{"internalType":"uint256","name":"amtLPUser","type":"uint256"},{"internalType":"uint256","name":"amtABorrow","type":"uint256"},{"internalType":"uint256","name":"amtBBorrow","type":"uint256"},{"internalType":"uint256","name":"amtLPBorrow","type":"uint256"},{"internalType":"uint256","name":"amtAMin","type":"uint256"},{"internalType":"uint256","name":"amtBMin","type":"uint256"}],"internalType":"struct SushiswapSpellV1.Amounts","name":"amt","type":"tuple"}],"name":"addLiquidityWERC20","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"components":[{"internalType":"uint256","name":"amtAUser","type":"uint256"},{"internalType":"uint256","name":"amtBUser","type":"uint256"},{"internalType":"uint256","name":"amtLPUser","type":"uint256"},{"internalType":"uint256","name":"amtABorrow","type":"uint256"},{"internalType":"uint256","name":"amtBBorrow","type":"uint256"},{"internalType":"uint256","name":"amtLPBorrow","type":"uint256"},{"internalType":"uint256","name":"amtAMin","type":"uint256"},{"internalType":"uint256","name":"amtBMin","type":"uint256"}],"internalType":"struct SushiswapSpellV1.Amounts","name":"amt","type":"tuple"},{"internalType":"uint256","name":"pid","type":"uint256"}],"name":"addLiquidityWMasterChef","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"approved","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bank","outputs":[{"internalType":"contract IBank","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"ensureApprove","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"contract IUniswapV2Factory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"name":"getPair","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"harvestWMasterChef","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"pairs","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"components":[{"internalType":"uint256","name":"amtLPTake","type":"uint256"},{"internalType":"uint256","name":"amtLPWithdraw","type":"uint256"},{"internalType":"uint256","name":"amtARepay","type":"uint256"},{"internalType":"uint256","name":"amtBRepay","type":"uint256"},{"internalType":"uint256","name":"amtLPRepay","type":"uint256"},{"internalType":"uint256","name":"amtAMin","type":"uint256"},{"internalType":"uint256","name":"amtBMin","type":"uint256"}],"internalType":"struct SushiswapSpellV1.RepayAmounts","name":"amt","type":"tuple"}],"name":"removeLiquidityWERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"components":[{"internalType":"uint256","name":"amtLPTake","type":"uint256"},{"internalType":"uint256","name":"amtLPWithdraw","type":"uint256"},{"internalType":"uint256","name":"amtARepay","type":"uint256"},{"internalType":"uint256","name":"amtBRepay","type":"uint256"},{"internalType":"uint256","name":"amtLPRepay","type":"uint256"},{"internalType":"uint256","name":"amtAMin","type":"uint256"},{"internalType":"uint256","name":"amtBMin","type":"uint256"}],"internalType":"struct SushiswapSpellV1.RepayAmounts","name":"amt","type":"tuple"}],"name":"removeLiquidityWMasterChef","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"router","outputs":[{"internalType":"contract IUniswapV2Router02","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sushi","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"werc20","outputs":[{"internalType":"contract IWERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"wmasterchef","outputs":[{"internalType":"contract IWMasterChef","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

6101606040523480156200001257600080fd5b5060405162004b7438038062004b748339810160408190526200003591620006d7565b8383836001600160a01b031663ad5c46486040518163ffffffff1660e01b815260040160206040518083038186803b1580156200007157600080fd5b505afa15801562000086573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000ac91906200068f565b620000be6301ffc9a760e01b6200030d565b620000d0630271189760e51b6200030d565b6001600160601b0319606084811b821660805283811b821660a05282901b1660c052620000fe818462000368565b60405163a22cb46560e01b81526001600160a01b0383169063a22cb465906200012f9086906001906004016200078f565b600060405180830381600087803b1580156200014a57600080fd5b505af11580156200015f573d6000803e3d6000fd5b50505050505050816001600160a01b0316610100816001600160a01b031660601b81525050816001600160a01b031663c45a01556040518163ffffffff1660e01b815260040160206040518083038186803b158015620001be57600080fd5b505afa158015620001d3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001f991906200068f565b6001600160601b0319606091821b811660e0529082901b166101205260405163a22cb46560e01b81526001600160a01b0382169063a22cb46590620002469087906001906004016200078f565b600060405180830381600087803b1580156200026157600080fd5b505af115801562000276573d6000803e3d6000fd5b50505050806001600160a01b0316630a0879036040518163ffffffff1660e01b8152600401602060405180830381600087803b158015620002b657600080fd5b505af1158015620002cb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002f191906200068f565b60601b6001600160601b03191661014052506200095992505050565b6001600160e01b03198082161415620003435760405162461bcd60e51b81526004016200033a90620007f8565b60405180910390fd5b6001600160e01b0319166000908152602081905260409020805460ff19166001179055565b6001600160a01b0380831660009081526001602090815260408083209385168352929052205460ff16620003f057620003bd81600019846001600160a01b0316620003f460201b6200188b179092919060201c565b6001600160a01b0380831660009081526001602081815260408084209486168452939052919020805460ff191690911790555b5050565b801580620004835750604051636eb1769f60e11b81526001600160a01b0384169063dd62ed3e906200042d903090869060040162000775565b60206040518083038186803b1580156200044657600080fd5b505afa1580156200045b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200048191906200073e565b155b620004a25760405162461bcd60e51b81526004016200033a90620008b0565b620004fd8363095ea7b360e01b8484604051602401620004c4929190620007aa565b60408051808303601f190181529190526020810180516001600160e01b0319939093166001600160e01b03938416179052906200050216565b505050565b60606200055e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166200059e60201b6200198a179092919060201c565b805190915015620004fd57808060200190518101906200057f9190620006b5565b620004fd5760405162461bcd60e51b81526004016200033a9062000866565b6060620005af8484600085620005b7565b949350505050565b6060620005c48562000689565b620005e35760405162461bcd60e51b81526004016200033a906200082f565b60006060866001600160a01b0316858760405162000602919062000757565b60006040518083038185875af1925050503d806000811462000641576040519150601f19603f3d011682016040523d82523d6000602084013e62000646565b606091505b509150915081156200065c579150620005af9050565b8051156200066d5780518082602001fd5b8360405162461bcd60e51b81526004016200033a9190620007c3565b3b151590565b600060208284031215620006a1578081fd5b8151620006ae8162000940565b9392505050565b600060208284031215620006c7578081fd5b81518015158114620006ae578182fd5b60008060008060808587031215620006ed578283fd5b8451620006fa8162000940565b60208601519094506200070d8162000940565b6040860151909350620007208162000940565b6060860151909250620007338162000940565b939692955090935050565b60006020828403121562000750578081fd5b5051919050565b600082516200076b8184602087016200090d565b9190910192915050565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039290921682521515602082015260400190565b6001600160a01b03929092168252602082015260400190565b6000602082528251806020840152620007e48160408501602087016200090d565b601f01601f19169190910160400192915050565b6020808252601c908201527f4552433136353a20696e76616c696420696e7465726661636520696400000000604082015260600190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b60208082526036908201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60408201527f20746f206e6f6e2d7a65726f20616c6c6f77616e636500000000000000000000606082015260800190565b60005b838110156200092a57818101518382015260200162000910565b838111156200093a576000848401525b50505050565b6001600160a01b03811681146200095657600080fd5b50565b60805160601c60a05160601c60c05160601c60e05160601c6101005160601c6101205160601c6101405160601c61407c62000af8600039806103b852806109e05280610de752806115ba525080610564528061060452806106e5528061075a528061086f52806108ad528061097d5280610a7c5280610cd65280610d4a5280610f1652806111be52806112a8528061131b52806113af5280611458528061153d5250806116e65280611710528061173a52806118695280611f4f528061209552806121c3528061294852806129e2525080610e2a528061163152508061012852806104065280612cd15280612d77528061315f525080610a0e5280611b285280611b9c5280612ba15280612bdc5280612c8a52508061042c52806104c152806106b852806109505280610a585280610ab05280610b465280610ca9528061106552806110fb528061127b528061151052806119b352806119e25280611afa5280611c155280611cd65280611d835280611e3052806124fb5280612c5a5280612de252806130c6528061310152806131f15280613245525061407c6000f3fe6080604052600436106101185760003560e01c806395723b1c116100a0578063e6a4390511610064578063e6a43905146102fe578063f23a6e611461031e578063f4b160451461033e578063f4d1a3b81461035e578063f887ea401461037e57610170565b806395723b1c14610276578063bc197c8114610296578063c45a0155146102c3578063cc9b1880146102d8578063e07d904e146102eb57610170565b806340a65ad2116100e757806340a65ad2146102025780635741229c1461021757806369454b861461022c57806376cdb03b1461024c57806386adcc791461026157610170565b806301ffc9a7146101755780630a087903146101ab5780631387d96d146101cd5780633fc8cef3146101ed57610170565b3661017057336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461016e5760405162461bcd60e51b815260040161016590613d79565b60405180910390fd5b005b600080fd5b34801561018157600080fd5b50610195610190366004613a3f565b610393565b6040516101a29190613c73565b60405180910390f35b3480156101b757600080fd5b506101c06103b6565b6040516101a29190613b82565b3480156101d957600080fd5b5061016e6101e8366004613839565b6103da565b3480156101f957600080fd5b506101c0610404565b34801561020e57600080fd5b5061016e610428565b34801561022357600080fd5b506101c0610a0c565b34801561023857600080fd5b506101c06102473660046136ad565b610a30565b34801561025857600080fd5b506101c0610a56565b34801561026d57600080fd5b506101c0610a7a565b34801561028257600080fd5b5061016e610291366004613839565b610a9e565b3480156102a257600080fd5b506102b66102b13660046136e5565b610e14565b6040516101a29190613c7e565b3480156102cf57600080fd5b506101c0610e28565b61016e6102e63660046137a0565b610e4c565b61016e6102f93660046137e8565b610f04565b34801561030a57600080fd5b506101c06103193660046136ad565b6115eb565b34801561032a57600080fd5b506102b66103393660046138d2565b6117bb565b34801561034a57600080fd5b506101956103593660046136ad565b6117cd565b34801561036a57600080fd5b5061016e6103793660046136ad565b6117ed565b34801561038a57600080fd5b506101c0611867565b6001600160e01b0319811660009081526020819052604090205460ff165b919050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60006103e684846115eb565b90506103f38183356119a1565b6103fe848484611c03565b50505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d7ac71ff6040518163ffffffff1660e01b815260040160206040518083038186803b15801561048357600080fd5b505afa158015610497573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104bb9190613abb565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663519f5099836040518263ffffffff1660e01b815260040161050b9190613f3f565b60806040518083038186803b15801561052357600080fd5b505afa158015610537573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061055b919061388b565b509250505060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663dc20c6fa836040518263ffffffff1660e01b81526004016105ae9190613f3f565b604080518083038186803b1580156105c557600080fd5b505afa1580156105d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105fd9190613ad3565b50905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a4775772846040518263ffffffff1660e01b815260040161064e9190613f3f565b60206040518083038186803b15801561066657600080fd5b505afa15801561067a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061069e9190613691565b604051630d1d697560e31b81529091506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906368eb4ba890610713907f000000000000000000000000000000000000000000000000000000000000000090879060001990600401613c52565b600060405180830381600087803b15801561072d57600080fd5b505af1158015610741573d6000803e3d6000fd5b505060405163b390c0ab60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016925063b390c0ab915061079590869060001990600401613f9b565b602060405180830381600087803b1580156107af57600080fd5b505af11580156107c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107e79190613abb565b506040516370a0823160e01b81526000906001600160a01b038316906370a0823190610817903090600401613b82565b60206040518083038186803b15801561082f57600080fd5b505afa158015610843573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108679190613abb565b9050610893827f00000000000000000000000000000000000000000000000000000000000000006117ed565b604051630d9778e560e11b81526000906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690631b2ef1ca906108e49087908690600401613f9b565b602060405180830381600087803b1580156108fe57600080fd5b505af1158015610912573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109369190613abb565b60405163314568d960e01b81529091506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063314568d9906109a9907f00000000000000000000000000000000000000000000000000000000000000009085908790600401613c52565b600060405180830381600087803b1580156109c357600080fd5b505af11580156109d7573d6000803e3d6000fd5b50505050610a047f000000000000000000000000000000000000000000000000000000000000000061246f565b505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60026020908152600092835260408084209091529082529020546001600160a01b031681565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000610aaa84846115eb565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d7ac71ff6040518163ffffffff1660e01b815260040160206040518083038186803b158015610b0757600080fd5b505afa158015610b1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3f9190613abb565b90506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663519f5099846040518263ffffffff1660e01b8152600401610b909190613f3f565b60806040518083038186803b158015610ba857600080fd5b505afa158015610bbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be0919061388b565b509250925050836001600160a01b0316826001600160a01b031663a4775772836040518263ffffffff1660e01b8152600401610c1c9190613f3f565b60206040518083038186803b158015610c3457600080fd5b505afa158015610c48573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c6c9190613691565b6001600160a01b031614610c925760405162461bcd60e51b815260040161016590613e71565b604051630d1d697560e31b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906368eb4ba890610d03907f00000000000000000000000000000000000000000000000000000000000000009085908a3590600401613c52565b600060405180830381600087803b158015610d1d57600080fd5b505af1158015610d31573d6000803e3d6000fd5b505060405163b390c0ab60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016925063b390c0ab9150610d84908490893590600401613f9b565b602060405180830381600087803b158015610d9e57600080fd5b505af1158015610db2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd69190613abb565b50610de2878787611c03565b610e0b7f000000000000000000000000000000000000000000000000000000000000000061246f565b50505050505050565b63bc197c8160e01b98975050505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000610e5884846115eb565b9050610e6584848461259b565b610eea81826001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401610e959190613b82565b60206040518083038186803b158015610ead57600080fd5b505afa158015610ec1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ee59190613abb565b612b95565b610ef2612cb7565b610efb8461246f565b6103fe8361246f565b6000610f1085856115eb565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316631fc8bc5d6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f6d57600080fd5b505afa158015610f81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fa59190613691565b6001600160a01b0316631526fe27846040518263ffffffff1660e01b8152600401610fd09190613f3f565b60806040518083038186803b158015610fe857600080fd5b505afa158015610ffc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611020919061394c565b5050509050816001600160a01b0316816001600160a01b0316146110565760405162461bcd60e51b815260040161016590613d4d565b61106186868661259b565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d7ac71ff6040518163ffffffff1660e01b815260040160206040518083038186803b1580156110bc57600080fd5b505afa1580156110d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110f49190613abb565b90506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663519f5099846040518263ffffffff1660e01b81526004016111459190613f3f565b60806040518083038186803b15801561115d57600080fd5b505afa158015611171573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611195919061388b565b93509350505060008111156113a957604051636e10637d60e11b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063dc20c6fa906111f3908690600401613f3f565b604080518083038186803b15801561120a57600080fd5b505afa15801561121e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112429190613ad3565b5090508087146112645760405162461bcd60e51b815260040161016590613e13565b604051630d1d697560e31b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906368eb4ba8906112d4907f00000000000000000000000000000000000000000000000000000000000000009087908790600401613c52565b600060405180830381600087803b1580156112ee57600080fd5b505af1158015611302573d6000803e3d6000fd5b505060405163b390c0ab60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016925063b390c0ab91506113549086908690600401613f9b565b602060405180830381600087803b15801561136e57600080fd5b505af1158015611382573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113a69190613abb565b50505b6113d3857f00000000000000000000000000000000000000000000000000000000000000006117ed565b6040516370a0823160e01b81526000906001600160a01b038716906370a0823190611402903090600401613b82565b60206040518083038186803b15801561141a57600080fd5b505afa15801561142e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114529190613abb565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316631b2ef1ca89846040518363ffffffff1660e01b81526004016114a4929190613f9b565b602060405180830381600087803b1580156114be57600080fd5b505af11580156114d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114f69190613abb565b60405163314568d960e01b81529091506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063314568d990611569907f00000000000000000000000000000000000000000000000000000000000000009085908790600401613c52565b600060405180830381600087803b15801561158357600080fd5b505af1158015611597573d6000803e3d6000fd5b505050506115a3612cb7565b6115ac8b61246f565b6115b58a61246f565b6115de7f000000000000000000000000000000000000000000000000000000000000000061246f565b5050505050505050505050565b6001600160a01b0380831660009081526002602090815260408083208585168452909152812054909116806117b25760405163e6a4390560e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063e6a43905906116689087908790600401613b96565b60206040518083038186803b15801561168057600080fd5b505afa158015611694573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116b89190613691565b90506001600160a01b0381166116e05760405162461bcd60e51b815260040161016590613d28565b61170a847f00000000000000000000000000000000000000000000000000000000000000006117ed565b611734837f00000000000000000000000000000000000000000000000000000000000000006117ed565b61175e817f00000000000000000000000000000000000000000000000000000000000000006117ed565b6001600160a01b0380851660008181526002602081815260408084208987168552825280842080549688166001600160a01b0319978816811790915592825280842094845293905291902080549092161790555b90505b92915050565b63f23a6e6160e01b9695505050505050565b600160209081526000928352604080842090915290825290205460ff1681565b6001600160a01b0380831660009081526001602090815260408083209385168352929052205460ff16611863576118306001600160a01b0383168260001961188b565b6001600160a01b0380831660009081526001602081815260408084209486168452939052919020805460ff191690911790555b5050565b7f000000000000000000000000000000000000000000000000000000000000000081565b8015806119135750604051636eb1769f60e11b81526001600160a01b0384169063dd62ed3e906118c19030908690600401613b96565b60206040518083038186803b1580156118d957600080fd5b505afa1580156118ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119119190613abb565b155b61192f5760405162461bcd60e51b815260040161016590613ee9565b6119858363095ea7b360e01b848460405160240161194e929190613c39565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612f00565b505050565b60606119998484600085612f8f565b949350505050565b801561186357600019811415611ae3577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663519f50997f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d7ac71ff6040518163ffffffff1660e01b815260040160206040518083038186803b158015611a3957600080fd5b505afa158015611a4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a719190613abb565b6040518263ffffffff1660e01b8152600401611a8d9190613f3f565b60806040518083038186803b158015611aa557600080fd5b505afa158015611ab9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611add919061388b565b93505050505b604051630d1d697560e31b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116916368eb4ba891611b55917f0000000000000000000000000000000000000000000000000000000000000000918716908690600401613c52565b600060405180830381600087803b158015611b6f57600080fd5b505af1158015611b83573d6000803e3d6000fd5b5050604051632770a7eb60e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169250639dc29fac9150611bd59085908590600401613c39565b600060405180830381600087803b158015611bef57600080fd5b505af1158015610a04573d6000803e3d6000fd5b6000611c0f84846115eb565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d7ac71ff6040518163ffffffff1660e01b815260040160206040518083038186803b158015611c6c57600080fd5b505afa158015611c80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ca49190613abb565b9050604083013560608401356080850135600019831415611d62576040516320a8bee760e21b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906382a2fb9c90611d0d9087908c90600401613f48565b602060405180830381600087803b158015611d2757600080fd5b505af1158015611d3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d5f9190613abb565b92505b600019821415611e0f576040516320a8bee760e21b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906382a2fb9c90611dba9087908b90600401613f48565b602060405180830381600087803b158015611dd457600080fd5b505af1158015611de8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e0c9190613abb565b91505b600019811415611ebc576040516320a8bee760e21b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906382a2fb9c90611e679087908990600401613f48565b602060405180830381600087803b158015611e8157600080fd5b505af1158015611e95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eb99190613abb565b90505b6000611f488760200135876001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401611ef29190613b82565b60206040518083038186803b158015611f0a57600080fd5b505afa158015611f1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f429190613abb565b90613053565b90506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663baa2abde8c8c8660008030426040518863ffffffff1660e01b8152600401611fa69796959493929190613bb0565b6040805180830381600087803b158015611fbf57600080fd5b505af1158015611fd3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ff79190613ad3565b9092509050600061200c8760a08c0135613095565b9050600061201e8760c08d0135613095565b9050818410801561202f5750808310155b1561214e5760408051600280825260608083018452926020830190803683370190505090508c8e8260008151811061206357fe5b602002602001018360018151811061207757fe5b6001600160a01b0393841660209182029290920101529181169091527f000000000000000000000000000000000000000000000000000000000000000016638803dbee6120c48588613053565b6120ce8786613053565b8430426040518663ffffffff1660e01b81526004016120f1959493929190613f5f565b600060405180830381600087803b15801561210b57600080fd5b505af115801561211f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612147919081019061398a565b5050612278565b81841015801561215d57508083105b156122785760408051600280825260608083018452926020830190803683370190505090508d8d8260008151811061219157fe5b60200260200101836001815181106121a557fe5b6001600160a01b0393841660209182029290920101529181169091527f000000000000000000000000000000000000000000000000000000000000000016638803dbee6121f28487613053565b6121fc8887613053565b8430426040518663ffffffff1660e01b815260040161221f959493929190613f5f565b600060405180830381600087803b15801561223957600080fd5b505af115801561224d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612275919081019061398a565b50505b6122828d896130ba565b61228c8c886130ba565b6122968a876130ba565b6040516370a0823160e01b815260a08c0135906001600160a01b038f16906370a08231906122c8903090600401613b82565b60206040518083038186803b1580156122e057600080fd5b505afa1580156122f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123189190613abb565b101561232357600080fd5b6040516370a0823160e01b815260c08c0135906001600160a01b038e16906370a0823190612355903090600401613b82565b60206040518083038186803b15801561236d57600080fd5b505afa158015612381573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123a59190613abb565b10156123b057600080fd5b6040516370a0823160e01b815260208c0135906001600160a01b038c16906370a08231906123e2903090600401613b82565b60206040518083038186803b1580156123fa57600080fd5b505afa15801561240e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124329190613abb565b101561243d57600080fd5b612445612cb7565b61244e8d61246f565b6124578c61246f565b6124608a61246f565b50505050505050505050505050565b6040516370a0823160e01b81526000906001600160a01b038316906370a082319061249e903090600401613b82565b60206040518083038186803b1580156124b657600080fd5b505afa1580156124ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124ee9190613abb565b90508015611863576118637f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663630dc7cb6040518163ffffffff1660e01b815260040160206040518083038186803b15801561255257600080fd5b505afa158015612566573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061258a9190613691565b6001600160a01b0384169083613138565b60006125a784846115eb565b90506125b1613157565b6125bc8483356131d4565b6125ca8383602001356131d4565b6125d88183604001356131d4565b6125e6848360600135613228565b6125f4838360800135613228565b612602818360a00135613228565b6000806000866001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016126339190613b82565b60206040518083038186803b15801561264b57600080fd5b505afa15801561265f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126839190613abb565b90506000866001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016126b39190613b82565b60206040518083038186803b1580156126cb57600080fd5b505afa1580156126df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127039190613abb565b9050600080896001600160a01b0316876001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b15801561274b57600080fd5b505afa15801561275f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127839190613691565b6001600160a01b0316141561281957866001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b1580156127cb57600080fd5b505afa1580156127df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128039190613a67565b506001600160701b03918216935016905061289c565b866001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561285257600080fd5b505afa158015612866573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061288a9190613a67565b506001600160701b0390811693501690505b6128a88484848461327c565b90965094506060935060029250600091506128c09050565b506040519080825280602002602001820160405280156128ea578160200160208202803683370190505b509050816128f95786866128fc565b85875b8260008151811061290957fe5b602002602001018360018151811061291d57fe5b6001600160a01b0393841660209182029290920101529181169091526040516338ed173960e01b81527f0000000000000000000000000000000000000000000000000000000000000000909116906338ed173990612988908690600090869030904290600401613f5f565b600060405180830381600087803b1580156129a257600080fd5b505af11580156129b6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526129de919081019061398a565b50507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663e8e337008787896001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401612a3d9190613b82565b60206040518083038186803b158015612a5557600080fd5b505afa158015612a69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a8d9190613abb565b6040516370a0823160e01b81526001600160a01b038b16906370a0823190612ab9903090600401613b82565b60206040518083038186803b158015612ad157600080fd5b505afa158015612ae5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b099190613abb565b8960c001358a60e0013530426040518963ffffffff1660e01b8152600401612b38989796959493929190613bf0565b606060405180830381600087803b158015612b5257600080fd5b505af1158015612b66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b8a9190613af6565b505050505050505050565b801561186357612bc5827f00000000000000000000000000000000000000000000000000000000000000006117ed565b6040516340c10f1960e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906340c10f1990612c139085908590600401613c39565b600060405180830381600087803b158015612c2d57600080fd5b505af1158015612c41573d6000803e3d6000fd5b505060405163314568d960e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116935063314568d99250611bd5917f0000000000000000000000000000000000000000000000000000000000000000918716908690600401613c52565b6040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190612d06903090600401613b82565b602060405180830381600087803b158015612d2057600080fd5b505af1158015612d34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d589190613abb565b90508015612efd57604051632e1a7d4d60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632e1a7d4d90612dac908490600401613f3f565b600060405180830381600087803b158015612dc657600080fd5b505af1158015612dda573d6000803e3d6000fd5b5050505060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663630dc7cb6040518163ffffffff1660e01b815260040160206040518083038186803b158015612e3957600080fd5b505afa158015612e4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e719190613691565b604080516000815260208101918290526001600160a01b0392909216918491612e9a9190613b66565b60006040518083038185875af1925050503d8060008114612ed7576040519150601f19603f3d011682016040523d82523d6000602084013e612edc565b606091505b50509050806118635760405162461bcd60e51b815260040161016590613cc6565b50565b6060612f55826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661198a9092919063ffffffff16565b8051909150156119855780806020019051810190612f739190613a1f565b6119855760405162461bcd60e51b815260040161016590613e9f565b6060612f9a856132cb565b612fb65760405162461bcd60e51b815260040161016590613e3a565b60006060866001600160a01b03168587604051612fd39190613b66565b60006040518083038185875af1925050503d8060008114613010576040519150601f19603f3d011682016040523d82523d6000602084013e613015565b606091505b509150915081156130295791506119999050565b8051156130395780518082602001fd5b8360405162461bcd60e51b81526004016101659190613c93565b60006117b283836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506132d1565b6000828201838110156117b25760405162461bcd60e51b815260040161016590613cf1565b8015611863576130ea827f00000000000000000000000000000000000000000000000000000000000000006117ed565b604051630450cfaf60e31b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906322867d7890611bd59085908590600401613c39565b6119858363a9059cbb60e01b848460405160240161194e929190613c39565b34156131d2577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156131b857600080fd5b505af11580156131cc573d6000803e3d6000fd5b50505050505b565b80156118635760405163246b5de160e11b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906348d6bbc290611bd59085908590600401613c39565b801561186357604051634b8a352960e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690634b8a352990611bd59085908590600401613c39565b60008061328985856132fd565b61329387856132fd565b106132af576132a486868686613337565b9150600090506132c2565b6132bb85878587613337565b9150600190505b94509492505050565b3b151590565b600081848411156132f55760405162461bcd60e51b81526004016101659190613c93565b505050900390565b60008261330c575060006117b5565b8282028284828161331957fe5b04146117b25760405162461bcd60e51b815260040161016590613db0565b600061334384846132fd565b61334d86846132fd565b101561336b5760405162461bcd60e51b815260040161016590613df1565b6103e5600061337c6107cd866132fd565b9050600061339761338d88886132fd565b611f428a886132fd565b905060006133c4876133be6133ac8b8a613095565b6133b8866103e86132fd565b90613431565b906132fd565b905060006133d760046133be87856132fd565b905060006133f76133f2836133ec88806132fd565b90613095565b613473565b905060006134058287613053565b905060006134148860026132fd565b90506134208282613431565b9d9c50505050505050505050505050565b60006117b283836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506135b9565b600081613482575060006103b1565b816001600160801b821061349b5760809190911c9060401b5b6801000000000000000082106134b65760409190911c9060201b5b64010000000082106134cd5760209190911c9060101b5b6201000082106134e25760109190911c9060081b5b61010082106134f65760089190911c9060041b5b601082106135095760049190911c9060021b5b600882106135155760011b5b600181858161352057fe5b048201901c9050600181858161353257fe5b048201901c9050600181858161354457fe5b048201901c9050600181858161355657fe5b048201901c9050600181858161356857fe5b048201901c9050600181858161357a57fe5b048201901c9050600181858161358c57fe5b048201901c9050600081858161359e57fe5b0490508082106135ae57806135b0565b815b95945050505050565b600081836135da5760405162461bcd60e51b81526004016101659190613c93565b5060008385816135e657fe5b0495945050505050565b60008083601f840112613601578182fd5b50813567ffffffffffffffff811115613618578182fd5b602083019150836020808302850101111561363257600080fd5b9250929050565b60008083601f84011261364a578182fd5b50813567ffffffffffffffff811115613661578182fd5b60208301915083602082850101111561363257600080fd5b6000610100828403121561368b578081fd5b50919050565b6000602082840312156136a2578081fd5b81516117b28161401c565b600080604083850312156136bf578081fd5b82356136ca8161401c565b915060208301356136da8161401c565b809150509250929050565b60008060008060008060008060a0898b031215613700578384fd5b883561370b8161401c565b9750602089013561371b8161401c565b9650604089013567ffffffffffffffff80821115613737578586fd5b6137438c838d016135f0565b909850965060608b013591508082111561375b578586fd5b6137678c838d016135f0565b909650945060808b013591508082111561377f578384fd5b5061378c8b828c01613639565b999c989b5096995094979396929594505050565b600080600061014084860312156137b5578283fd5b83356137c08161401c565b925060208401356137d08161401c565b91506137df8560408601613679565b90509250925092565b60008060008061016085870312156137fe578384fd5b84356138098161401c565b935060208501356138198161401c565b92506138288660408701613679565b939692955092936101400135925050565b600080600083850361012081121561384f578384fd5b843561385a8161401c565b9350602085013561386a8161401c565b925060e0603f198201121561387d578182fd5b506040840190509250925092565b600080600080608085870312156138a0578384fd5b84516138ab8161401c565b60208601519094506138bc8161401c565b6040860151606090960151949790965092505050565b60008060008060008060a087890312156138ea578182fd5b86356138f58161401c565b955060208701356139058161401c565b94506040870135935060608701359250608087013567ffffffffffffffff81111561392e578283fd5b61393a89828a01613639565b979a9699509497509295939492505050565b60008060008060808587031215613961578182fd5b845161396c8161401c565b60208601516040870151606090970151919890975090945092505050565b6000602080838503121561399c578182fd5b825167ffffffffffffffff8111156139b2578283fd5b8301601f810185136139c2578283fd5b80516139d56139d082613fd0565b613fa9565b81815283810190838501858402850186018910156139f1578687fd5b8694505b83851015613a135780518352600194909401939185019185016139f5565b50979650505050505050565b600060208284031215613a30578081fd5b815180151581146117b2578182fd5b600060208284031215613a50578081fd5b81356001600160e01b0319811681146117b2578182fd5b600080600060608486031215613a7b578081fd5b8351613a8681614031565b6020850151909350613a9781614031565b604085015190925063ffffffff81168114613ab0578182fd5b809150509250925092565b600060208284031215613acc578081fd5b5051919050565b60008060408385031215613ae5578182fd5b505080516020909101519092909150565b600080600060608486031215613b0a578081fd5b8351925060208401519150604084015190509250925092565b6000815180845260208085019450808401835b83811015613b5b5781516001600160a01b031687529582019590820190600101613b36565b509495945050505050565b60008251613b78818460208701613ff0565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039788168152958716602087015260408601949094526060850192909252608084015290921660a082015260c081019190915260e00190565b6001600160a01b039889168152968816602088015260408701959095526060860193909352608085019190915260a084015290921660c082015260e08101919091526101000190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b039390931683526020830191909152604082015260600190565b901515815260200190565b6001600160e01b031991909116815260200190565b6000602082528251806020840152613cb2816040850160208701613ff0565b601f01601f19169190910160400192915050565b6020808252601190820152701c99599d5b99081155120819985a5b1959607a1b604082015260600190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b6020808252600b908201526a3737903638103a37b5b2b760a91b604082015260600190565b60208082526012908201527134b731b7b93932b1ba103638103a37b5b2b760711b604082015260600190565b60208082526017908201527f455448206d75737420636f6d652066726f6d2057455448000000000000000000604082015260600190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b60208082526008908201526714995d995c9cd95960c21b604082015260600190565b6020808252600d908201526c1a5b98dbdc9c9958dd081c1a59609a1b604082015260600190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b602080825260149082015273696e636f727265637420756e6465726c79696e6760601b604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b60208082526036908201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60408201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b606082015260800190565b90815260200190565b9182526001600160a01b0316602082015260400190565b600086825285602083015260a06040830152613f7e60a0830186613b23565b6001600160a01b0394909416606083015250608001529392505050565b918252602082015260400190565b60405181810167ffffffffffffffff81118282101715613fc857600080fd5b604052919050565b600067ffffffffffffffff821115613fe6578081fd5b5060209081020190565b60005b8381101561400b578181015183820152602001613ff3565b838111156103fe5750506000910152565b6001600160a01b0381168114612efd57600080fd5b6001600160701b0381168114612efd57600080fdfea26469706673582212202cc665dcacfc29171dd8e76b08827b74a5e701ef6bd4e33f774cba5f4e542d0164736f6c634300060c00330000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb000000000000000000000000e28d9df7718b0b5ba69e01073fe82254a9ed2f98000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c68

Deployed Bytecode

0x6080604052600436106101185760003560e01c806395723b1c116100a0578063e6a4390511610064578063e6a43905146102fe578063f23a6e611461031e578063f4b160451461033e578063f4d1a3b81461035e578063f887ea401461037e57610170565b806395723b1c14610276578063bc197c8114610296578063c45a0155146102c3578063cc9b1880146102d8578063e07d904e146102eb57610170565b806340a65ad2116100e757806340a65ad2146102025780635741229c1461021757806369454b861461022c57806376cdb03b1461024c57806386adcc791461026157610170565b806301ffc9a7146101755780630a087903146101ab5780631387d96d146101cd5780633fc8cef3146101ed57610170565b3661017057336001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2161461016e5760405162461bcd60e51b815260040161016590613d79565b60405180910390fd5b005b600080fd5b34801561018157600080fd5b50610195610190366004613a3f565b610393565b6040516101a29190613c73565b60405180910390f35b3480156101b757600080fd5b506101c06103b6565b6040516101a29190613b82565b3480156101d957600080fd5b5061016e6101e8366004613839565b6103da565b3480156101f957600080fd5b506101c0610404565b34801561020e57600080fd5b5061016e610428565b34801561022357600080fd5b506101c0610a0c565b34801561023857600080fd5b506101c06102473660046136ad565b610a30565b34801561025857600080fd5b506101c0610a56565b34801561026d57600080fd5b506101c0610a7a565b34801561028257600080fd5b5061016e610291366004613839565b610a9e565b3480156102a257600080fd5b506102b66102b13660046136e5565b610e14565b6040516101a29190613c7e565b3480156102cf57600080fd5b506101c0610e28565b61016e6102e63660046137a0565b610e4c565b61016e6102f93660046137e8565b610f04565b34801561030a57600080fd5b506101c06103193660046136ad565b6115eb565b34801561032a57600080fd5b506102b66103393660046138d2565b6117bb565b34801561034a57600080fd5b506101956103593660046136ad565b6117cd565b34801561036a57600080fd5b5061016e6103793660046136ad565b6117ed565b34801561038a57600080fd5b506101c0611867565b6001600160e01b0319811660009081526020819052604090205460ff165b919050565b7f0000000000000000000000006b3595068778dd592e39a122f4f5a5cf09c90fe281565b60006103e684846115eb565b90506103f38183356119a1565b6103fe848484611c03565b50505050565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b60007f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb6001600160a01b031663d7ac71ff6040518163ffffffff1660e01b815260040160206040518083038186803b15801561048357600080fd5b505afa158015610497573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104bb9190613abb565b905060007f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb6001600160a01b031663519f5099836040518263ffffffff1660e01b815260040161050b9190613f3f565b60806040518083038186803b15801561052357600080fd5b505afa158015610537573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061055b919061388b565b509250505060007f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c686001600160a01b031663dc20c6fa836040518263ffffffff1660e01b81526004016105ae9190613f3f565b604080518083038186803b1580156105c557600080fd5b505afa1580156105d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105fd9190613ad3565b50905060007f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c686001600160a01b031663a4775772846040518263ffffffff1660e01b815260040161064e9190613f3f565b60206040518083038186803b15801561066657600080fd5b505afa15801561067a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061069e9190613691565b604051630d1d697560e31b81529091506001600160a01b037f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb16906368eb4ba890610713907f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c6890879060001990600401613c52565b600060405180830381600087803b15801561072d57600080fd5b505af1158015610741573d6000803e3d6000fd5b505060405163b390c0ab60e01b81526001600160a01b037f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c6816925063b390c0ab915061079590869060001990600401613f9b565b602060405180830381600087803b1580156107af57600080fd5b505af11580156107c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107e79190613abb565b506040516370a0823160e01b81526000906001600160a01b038316906370a0823190610817903090600401613b82565b60206040518083038186803b15801561082f57600080fd5b505afa158015610843573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108679190613abb565b9050610893827f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c686117ed565b604051630d9778e560e11b81526000906001600160a01b037f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c681690631b2ef1ca906108e49087908690600401613f9b565b602060405180830381600087803b1580156108fe57600080fd5b505af1158015610912573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109369190613abb565b60405163314568d960e01b81529091506001600160a01b037f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb169063314568d9906109a9907f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c689085908790600401613c52565b600060405180830381600087803b1580156109c357600080fd5b505af11580156109d7573d6000803e3d6000fd5b50505050610a047f0000000000000000000000006b3595068778dd592e39a122f4f5a5cf09c90fe261246f565b505050505050565b7f000000000000000000000000e28d9df7718b0b5ba69e01073fe82254a9ed2f9881565b60026020908152600092835260408084209091529082529020546001600160a01b031681565b7f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb81565b7f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c6881565b6000610aaa84846115eb565b905060007f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb6001600160a01b031663d7ac71ff6040518163ffffffff1660e01b815260040160206040518083038186803b158015610b0757600080fd5b505afa158015610b1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3f9190613abb565b90506000807f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb6001600160a01b031663519f5099846040518263ffffffff1660e01b8152600401610b909190613f3f565b60806040518083038186803b158015610ba857600080fd5b505afa158015610bbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be0919061388b565b509250925050836001600160a01b0316826001600160a01b031663a4775772836040518263ffffffff1660e01b8152600401610c1c9190613f3f565b60206040518083038186803b158015610c3457600080fd5b505afa158015610c48573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c6c9190613691565b6001600160a01b031614610c925760405162461bcd60e51b815260040161016590613e71565b604051630d1d697560e31b81526001600160a01b037f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb16906368eb4ba890610d03907f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c689085908a3590600401613c52565b600060405180830381600087803b158015610d1d57600080fd5b505af1158015610d31573d6000803e3d6000fd5b505060405163b390c0ab60e01b81526001600160a01b037f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c6816925063b390c0ab9150610d84908490893590600401613f9b565b602060405180830381600087803b158015610d9e57600080fd5b505af1158015610db2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd69190613abb565b50610de2878787611c03565b610e0b7f0000000000000000000000006b3595068778dd592e39a122f4f5a5cf09c90fe261246f565b50505050505050565b63bc197c8160e01b98975050505050505050565b7f000000000000000000000000c0aee478e3658e2610c5f7a4a2e1777ce9e4f2ac81565b6000610e5884846115eb565b9050610e6584848461259b565b610eea81826001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401610e959190613b82565b60206040518083038186803b158015610ead57600080fd5b505afa158015610ec1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ee59190613abb565b612b95565b610ef2612cb7565b610efb8461246f565b6103fe8361246f565b6000610f1085856115eb565b905060007f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c686001600160a01b0316631fc8bc5d6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f6d57600080fd5b505afa158015610f81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fa59190613691565b6001600160a01b0316631526fe27846040518263ffffffff1660e01b8152600401610fd09190613f3f565b60806040518083038186803b158015610fe857600080fd5b505afa158015610ffc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611020919061394c565b5050509050816001600160a01b0316816001600160a01b0316146110565760405162461bcd60e51b815260040161016590613d4d565b61106186868661259b565b60007f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb6001600160a01b031663d7ac71ff6040518163ffffffff1660e01b815260040160206040518083038186803b1580156110bc57600080fd5b505afa1580156110d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110f49190613abb565b90506000807f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb6001600160a01b031663519f5099846040518263ffffffff1660e01b81526004016111459190613f3f565b60806040518083038186803b15801561115d57600080fd5b505afa158015611171573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611195919061388b565b93509350505060008111156113a957604051636e10637d60e11b81526000906001600160a01b037f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c68169063dc20c6fa906111f3908690600401613f3f565b604080518083038186803b15801561120a57600080fd5b505afa15801561121e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112429190613ad3565b5090508087146112645760405162461bcd60e51b815260040161016590613e13565b604051630d1d697560e31b81526001600160a01b037f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb16906368eb4ba8906112d4907f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c689087908790600401613c52565b600060405180830381600087803b1580156112ee57600080fd5b505af1158015611302573d6000803e3d6000fd5b505060405163b390c0ab60e01b81526001600160a01b037f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c6816925063b390c0ab91506113549086908690600401613f9b565b602060405180830381600087803b15801561136e57600080fd5b505af1158015611382573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113a69190613abb565b50505b6113d3857f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c686117ed565b6040516370a0823160e01b81526000906001600160a01b038716906370a0823190611402903090600401613b82565b60206040518083038186803b15801561141a57600080fd5b505afa15801561142e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114529190613abb565b905060007f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c686001600160a01b0316631b2ef1ca89846040518363ffffffff1660e01b81526004016114a4929190613f9b565b602060405180830381600087803b1580156114be57600080fd5b505af11580156114d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114f69190613abb565b60405163314568d960e01b81529091506001600160a01b037f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb169063314568d990611569907f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c689085908790600401613c52565b600060405180830381600087803b15801561158357600080fd5b505af1158015611597573d6000803e3d6000fd5b505050506115a3612cb7565b6115ac8b61246f565b6115b58a61246f565b6115de7f0000000000000000000000006b3595068778dd592e39a122f4f5a5cf09c90fe261246f565b5050505050505050505050565b6001600160a01b0380831660009081526002602090815260408083208585168452909152812054909116806117b25760405163e6a4390560e01b81526001600160a01b037f000000000000000000000000c0aee478e3658e2610c5f7a4a2e1777ce9e4f2ac169063e6a43905906116689087908790600401613b96565b60206040518083038186803b15801561168057600080fd5b505afa158015611694573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116b89190613691565b90506001600160a01b0381166116e05760405162461bcd60e51b815260040161016590613d28565b61170a847f000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f6117ed565b611734837f000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f6117ed565b61175e817f000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f6117ed565b6001600160a01b0380851660008181526002602081815260408084208987168552825280842080549688166001600160a01b0319978816811790915592825280842094845293905291902080549092161790555b90505b92915050565b63f23a6e6160e01b9695505050505050565b600160209081526000928352604080842090915290825290205460ff1681565b6001600160a01b0380831660009081526001602090815260408083209385168352929052205460ff16611863576118306001600160a01b0383168260001961188b565b6001600160a01b0380831660009081526001602081815260408084209486168452939052919020805460ff191690911790555b5050565b7f000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f81565b8015806119135750604051636eb1769f60e11b81526001600160a01b0384169063dd62ed3e906118c19030908690600401613b96565b60206040518083038186803b1580156118d957600080fd5b505afa1580156118ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119119190613abb565b155b61192f5760405162461bcd60e51b815260040161016590613ee9565b6119858363095ea7b360e01b848460405160240161194e929190613c39565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612f00565b505050565b60606119998484600085612f8f565b949350505050565b801561186357600019811415611ae3577f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb6001600160a01b031663519f50997f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb6001600160a01b031663d7ac71ff6040518163ffffffff1660e01b815260040160206040518083038186803b158015611a3957600080fd5b505afa158015611a4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a719190613abb565b6040518263ffffffff1660e01b8152600401611a8d9190613f3f565b60806040518083038186803b158015611aa557600080fd5b505afa158015611ab9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611add919061388b565b93505050505b604051630d1d697560e31b81526001600160a01b037f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb8116916368eb4ba891611b55917f000000000000000000000000e28d9df7718b0b5ba69e01073fe82254a9ed2f98918716908690600401613c52565b600060405180830381600087803b158015611b6f57600080fd5b505af1158015611b83573d6000803e3d6000fd5b5050604051632770a7eb60e21b81526001600160a01b037f000000000000000000000000e28d9df7718b0b5ba69e01073fe82254a9ed2f98169250639dc29fac9150611bd59085908590600401613c39565b600060405180830381600087803b158015611bef57600080fd5b505af1158015610a04573d6000803e3d6000fd5b6000611c0f84846115eb565b905060007f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb6001600160a01b031663d7ac71ff6040518163ffffffff1660e01b815260040160206040518083038186803b158015611c6c57600080fd5b505afa158015611c80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ca49190613abb565b9050604083013560608401356080850135600019831415611d62576040516320a8bee760e21b81526001600160a01b037f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb16906382a2fb9c90611d0d9087908c90600401613f48565b602060405180830381600087803b158015611d2757600080fd5b505af1158015611d3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d5f9190613abb565b92505b600019821415611e0f576040516320a8bee760e21b81526001600160a01b037f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb16906382a2fb9c90611dba9087908b90600401613f48565b602060405180830381600087803b158015611dd457600080fd5b505af1158015611de8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e0c9190613abb565b91505b600019811415611ebc576040516320a8bee760e21b81526001600160a01b037f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb16906382a2fb9c90611e679087908990600401613f48565b602060405180830381600087803b158015611e8157600080fd5b505af1158015611e95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eb99190613abb565b90505b6000611f488760200135876001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401611ef29190613b82565b60206040518083038186803b158015611f0a57600080fd5b505afa158015611f1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f429190613abb565b90613053565b90506000807f000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f6001600160a01b031663baa2abde8c8c8660008030426040518863ffffffff1660e01b8152600401611fa69796959493929190613bb0565b6040805180830381600087803b158015611fbf57600080fd5b505af1158015611fd3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ff79190613ad3565b9092509050600061200c8760a08c0135613095565b9050600061201e8760c08d0135613095565b9050818410801561202f5750808310155b1561214e5760408051600280825260608083018452926020830190803683370190505090508c8e8260008151811061206357fe5b602002602001018360018151811061207757fe5b6001600160a01b0393841660209182029290920101529181169091527f000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f16638803dbee6120c48588613053565b6120ce8786613053565b8430426040518663ffffffff1660e01b81526004016120f1959493929190613f5f565b600060405180830381600087803b15801561210b57600080fd5b505af115801561211f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612147919081019061398a565b5050612278565b81841015801561215d57508083105b156122785760408051600280825260608083018452926020830190803683370190505090508d8d8260008151811061219157fe5b60200260200101836001815181106121a557fe5b6001600160a01b0393841660209182029290920101529181169091527f000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f16638803dbee6121f28487613053565b6121fc8887613053565b8430426040518663ffffffff1660e01b815260040161221f959493929190613f5f565b600060405180830381600087803b15801561223957600080fd5b505af115801561224d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612275919081019061398a565b50505b6122828d896130ba565b61228c8c886130ba565b6122968a876130ba565b6040516370a0823160e01b815260a08c0135906001600160a01b038f16906370a08231906122c8903090600401613b82565b60206040518083038186803b1580156122e057600080fd5b505afa1580156122f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123189190613abb565b101561232357600080fd5b6040516370a0823160e01b815260c08c0135906001600160a01b038e16906370a0823190612355903090600401613b82565b60206040518083038186803b15801561236d57600080fd5b505afa158015612381573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123a59190613abb565b10156123b057600080fd5b6040516370a0823160e01b815260208c0135906001600160a01b038c16906370a08231906123e2903090600401613b82565b60206040518083038186803b1580156123fa57600080fd5b505afa15801561240e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124329190613abb565b101561243d57600080fd5b612445612cb7565b61244e8d61246f565b6124578c61246f565b6124608a61246f565b50505050505050505050505050565b6040516370a0823160e01b81526000906001600160a01b038316906370a082319061249e903090600401613b82565b60206040518083038186803b1580156124b657600080fd5b505afa1580156124ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124ee9190613abb565b90508015611863576118637f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb6001600160a01b031663630dc7cb6040518163ffffffff1660e01b815260040160206040518083038186803b15801561255257600080fd5b505afa158015612566573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061258a9190613691565b6001600160a01b0384169083613138565b60006125a784846115eb565b90506125b1613157565b6125bc8483356131d4565b6125ca8383602001356131d4565b6125d88183604001356131d4565b6125e6848360600135613228565b6125f4838360800135613228565b612602818360a00135613228565b6000806000866001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016126339190613b82565b60206040518083038186803b15801561264b57600080fd5b505afa15801561265f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126839190613abb565b90506000866001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016126b39190613b82565b60206040518083038186803b1580156126cb57600080fd5b505afa1580156126df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127039190613abb565b9050600080896001600160a01b0316876001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b15801561274b57600080fd5b505afa15801561275f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127839190613691565b6001600160a01b0316141561281957866001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b1580156127cb57600080fd5b505afa1580156127df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128039190613a67565b506001600160701b03918216935016905061289c565b866001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561285257600080fd5b505afa158015612866573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061288a9190613a67565b506001600160701b0390811693501690505b6128a88484848461327c565b90965094506060935060029250600091506128c09050565b506040519080825280602002602001820160405280156128ea578160200160208202803683370190505b509050816128f95786866128fc565b85875b8260008151811061290957fe5b602002602001018360018151811061291d57fe5b6001600160a01b0393841660209182029290920101529181169091526040516338ed173960e01b81527f000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f909116906338ed173990612988908690600090869030904290600401613f5f565b600060405180830381600087803b1580156129a257600080fd5b505af11580156129b6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526129de919081019061398a565b50507f000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f6001600160a01b031663e8e337008787896001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401612a3d9190613b82565b60206040518083038186803b158015612a5557600080fd5b505afa158015612a69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a8d9190613abb565b6040516370a0823160e01b81526001600160a01b038b16906370a0823190612ab9903090600401613b82565b60206040518083038186803b158015612ad157600080fd5b505afa158015612ae5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b099190613abb565b8960c001358a60e0013530426040518963ffffffff1660e01b8152600401612b38989796959493929190613bf0565b606060405180830381600087803b158015612b5257600080fd5b505af1158015612b66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b8a9190613af6565b505050505050505050565b801561186357612bc5827f000000000000000000000000e28d9df7718b0b5ba69e01073fe82254a9ed2f986117ed565b6040516340c10f1960e01b81526001600160a01b037f000000000000000000000000e28d9df7718b0b5ba69e01073fe82254a9ed2f9816906340c10f1990612c139085908590600401613c39565b600060405180830381600087803b158015612c2d57600080fd5b505af1158015612c41573d6000803e3d6000fd5b505060405163314568d960e01b81526001600160a01b037f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb8116935063314568d99250611bd5917f000000000000000000000000e28d9df7718b0b5ba69e01073fe82254a9ed2f98918716908690600401613c52565b6040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a0823190612d06903090600401613b82565b602060405180830381600087803b158015612d2057600080fd5b505af1158015612d34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d589190613abb565b90508015612efd57604051632e1a7d4d60e01b81526001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21690632e1a7d4d90612dac908490600401613f3f565b600060405180830381600087803b158015612dc657600080fd5b505af1158015612dda573d6000803e3d6000fd5b5050505060007f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb6001600160a01b031663630dc7cb6040518163ffffffff1660e01b815260040160206040518083038186803b158015612e3957600080fd5b505afa158015612e4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e719190613691565b604080516000815260208101918290526001600160a01b0392909216918491612e9a9190613b66565b60006040518083038185875af1925050503d8060008114612ed7576040519150601f19603f3d011682016040523d82523d6000602084013e612edc565b606091505b50509050806118635760405162461bcd60e51b815260040161016590613cc6565b50565b6060612f55826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661198a9092919063ffffffff16565b8051909150156119855780806020019051810190612f739190613a1f565b6119855760405162461bcd60e51b815260040161016590613e9f565b6060612f9a856132cb565b612fb65760405162461bcd60e51b815260040161016590613e3a565b60006060866001600160a01b03168587604051612fd39190613b66565b60006040518083038185875af1925050503d8060008114613010576040519150601f19603f3d011682016040523d82523d6000602084013e613015565b606091505b509150915081156130295791506119999050565b8051156130395780518082602001fd5b8360405162461bcd60e51b81526004016101659190613c93565b60006117b283836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506132d1565b6000828201838110156117b25760405162461bcd60e51b815260040161016590613cf1565b8015611863576130ea827f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb6117ed565b604051630450cfaf60e31b81526001600160a01b037f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb16906322867d7890611bd59085908590600401613c39565b6119858363a9059cbb60e01b848460405160240161194e929190613c39565b34156131d2577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156131b857600080fd5b505af11580156131cc573d6000803e3d6000fd5b50505050505b565b80156118635760405163246b5de160e11b81526001600160a01b037f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb16906348d6bbc290611bd59085908590600401613c39565b801561186357604051634b8a352960e01b81526001600160a01b037f0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb1690634b8a352990611bd59085908590600401613c39565b60008061328985856132fd565b61329387856132fd565b106132af576132a486868686613337565b9150600090506132c2565b6132bb85878587613337565b9150600190505b94509492505050565b3b151590565b600081848411156132f55760405162461bcd60e51b81526004016101659190613c93565b505050900390565b60008261330c575060006117b5565b8282028284828161331957fe5b04146117b25760405162461bcd60e51b815260040161016590613db0565b600061334384846132fd565b61334d86846132fd565b101561336b5760405162461bcd60e51b815260040161016590613df1565b6103e5600061337c6107cd866132fd565b9050600061339761338d88886132fd565b611f428a886132fd565b905060006133c4876133be6133ac8b8a613095565b6133b8866103e86132fd565b90613431565b906132fd565b905060006133d760046133be87856132fd565b905060006133f76133f2836133ec88806132fd565b90613095565b613473565b905060006134058287613053565b905060006134148860026132fd565b90506134208282613431565b9d9c50505050505050505050505050565b60006117b283836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506135b9565b600081613482575060006103b1565b816001600160801b821061349b5760809190911c9060401b5b6801000000000000000082106134b65760409190911c9060201b5b64010000000082106134cd5760209190911c9060101b5b6201000082106134e25760109190911c9060081b5b61010082106134f65760089190911c9060041b5b601082106135095760049190911c9060021b5b600882106135155760011b5b600181858161352057fe5b048201901c9050600181858161353257fe5b048201901c9050600181858161354457fe5b048201901c9050600181858161355657fe5b048201901c9050600181858161356857fe5b048201901c9050600181858161357a57fe5b048201901c9050600181858161358c57fe5b048201901c9050600081858161359e57fe5b0490508082106135ae57806135b0565b815b95945050505050565b600081836135da5760405162461bcd60e51b81526004016101659190613c93565b5060008385816135e657fe5b0495945050505050565b60008083601f840112613601578182fd5b50813567ffffffffffffffff811115613618578182fd5b602083019150836020808302850101111561363257600080fd5b9250929050565b60008083601f84011261364a578182fd5b50813567ffffffffffffffff811115613661578182fd5b60208301915083602082850101111561363257600080fd5b6000610100828403121561368b578081fd5b50919050565b6000602082840312156136a2578081fd5b81516117b28161401c565b600080604083850312156136bf578081fd5b82356136ca8161401c565b915060208301356136da8161401c565b809150509250929050565b60008060008060008060008060a0898b031215613700578384fd5b883561370b8161401c565b9750602089013561371b8161401c565b9650604089013567ffffffffffffffff80821115613737578586fd5b6137438c838d016135f0565b909850965060608b013591508082111561375b578586fd5b6137678c838d016135f0565b909650945060808b013591508082111561377f578384fd5b5061378c8b828c01613639565b999c989b5096995094979396929594505050565b600080600061014084860312156137b5578283fd5b83356137c08161401c565b925060208401356137d08161401c565b91506137df8560408601613679565b90509250925092565b60008060008061016085870312156137fe578384fd5b84356138098161401c565b935060208501356138198161401c565b92506138288660408701613679565b939692955092936101400135925050565b600080600083850361012081121561384f578384fd5b843561385a8161401c565b9350602085013561386a8161401c565b925060e0603f198201121561387d578182fd5b506040840190509250925092565b600080600080608085870312156138a0578384fd5b84516138ab8161401c565b60208601519094506138bc8161401c565b6040860151606090960151949790965092505050565b60008060008060008060a087890312156138ea578182fd5b86356138f58161401c565b955060208701356139058161401c565b94506040870135935060608701359250608087013567ffffffffffffffff81111561392e578283fd5b61393a89828a01613639565b979a9699509497509295939492505050565b60008060008060808587031215613961578182fd5b845161396c8161401c565b60208601516040870151606090970151919890975090945092505050565b6000602080838503121561399c578182fd5b825167ffffffffffffffff8111156139b2578283fd5b8301601f810185136139c2578283fd5b80516139d56139d082613fd0565b613fa9565b81815283810190838501858402850186018910156139f1578687fd5b8694505b83851015613a135780518352600194909401939185019185016139f5565b50979650505050505050565b600060208284031215613a30578081fd5b815180151581146117b2578182fd5b600060208284031215613a50578081fd5b81356001600160e01b0319811681146117b2578182fd5b600080600060608486031215613a7b578081fd5b8351613a8681614031565b6020850151909350613a9781614031565b604085015190925063ffffffff81168114613ab0578182fd5b809150509250925092565b600060208284031215613acc578081fd5b5051919050565b60008060408385031215613ae5578182fd5b505080516020909101519092909150565b600080600060608486031215613b0a578081fd5b8351925060208401519150604084015190509250925092565b6000815180845260208085019450808401835b83811015613b5b5781516001600160a01b031687529582019590820190600101613b36565b509495945050505050565b60008251613b78818460208701613ff0565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039788168152958716602087015260408601949094526060850192909252608084015290921660a082015260c081019190915260e00190565b6001600160a01b039889168152968816602088015260408701959095526060860193909352608085019190915260a084015290921660c082015260e08101919091526101000190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b039390931683526020830191909152604082015260600190565b901515815260200190565b6001600160e01b031991909116815260200190565b6000602082528251806020840152613cb2816040850160208701613ff0565b601f01601f19169190910160400192915050565b6020808252601190820152701c99599d5b99081155120819985a5b1959607a1b604082015260600190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b6020808252600b908201526a3737903638103a37b5b2b760a91b604082015260600190565b60208082526012908201527134b731b7b93932b1ba103638103a37b5b2b760711b604082015260600190565b60208082526017908201527f455448206d75737420636f6d652066726f6d2057455448000000000000000000604082015260600190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b60208082526008908201526714995d995c9cd95960c21b604082015260600190565b6020808252600d908201526c1a5b98dbdc9c9958dd081c1a59609a1b604082015260600190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b602080825260149082015273696e636f727265637420756e6465726c79696e6760601b604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b60208082526036908201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60408201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b606082015260800190565b90815260200190565b9182526001600160a01b0316602082015260400190565b600086825285602083015260a06040830152613f7e60a0830186613b23565b6001600160a01b0394909416606083015250608001529392505050565b918252602082015260400190565b60405181810167ffffffffffffffff81118282101715613fc857600080fd5b604052919050565b600067ffffffffffffffff821115613fe6578081fd5b5060209081020190565b60005b8381101561400b578181015183820152602001613ff3565b838111156103fe5750506000910152565b6001600160a01b0381168114612efd57600080fd5b6001600160701b0381168114612efd57600080fdfea26469706673582212202cc665dcacfc29171dd8e76b08827b74a5e701ef6bd4e33f774cba5f4e542d0164736f6c634300060c0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb000000000000000000000000e28d9df7718b0b5ba69e01073fe82254a9ed2f98000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f000000000000000000000000373ae78a14577682591e088f2e78ef1417612c68

-----Decoded View---------------
Arg [0] : _bank (address): 0x5f5Cd91070960D13ee549C9CC47e7a4Cd00457bb
Arg [1] : _werc20 (address): 0xe28D9dF7718b0b5Ba69E01073fE82254a9eD2F98
Arg [2] : _router (address): 0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F
Arg [3] : _wmasterchef (address): 0x373ae78a14577682591E088F2E78EF1417612c68

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 0000000000000000000000005f5cd91070960d13ee549c9cc47e7a4cd00457bb
Arg [1] : 000000000000000000000000e28d9df7718b0b5ba69e01073fe82254a9ed2f98
Arg [2] : 000000000000000000000000d9e1ce17f2641f24ae83637ab66a2cca9c378b9f
Arg [3] : 000000000000000000000000373ae78a14577682591e088f2e78ef1417612c68


Deployed Bytecode Sourcemap

476:9895:23:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4103:10:20;-1:-1:-1;;;;;4117:4:20;4103:18;;4095:54;;;;-1:-1:-1;;;4095:54:20;;;;;;;:::i;:::-;;;;;;;;;476:9895:23;;;;;948:140:57;;;;;;;;;;-1:-1:-1;948:140:57;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;781:30:23;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;8733:314::-;;;;;;;;;;-1:-1:-1;8733:314:23;;;;;:::i;:::-;;:::i;502:29:20:-;;;;;;;;;;;;;:::i;9707:662:23:-;;;;;;;;;;;;;:::i;467:31:20:-;;;;;;;;;;;;;:::i;670:60:23:-;;;;;;;;;;-1:-1:-1;670:60:23;;;;;:::i;:::-;;:::i;436:27:20:-;;;;;;;;;;;;;:::i;735:41:23:-;;;;;;;;;;;;;:::i;9051:652::-;;;;;;;;;;-1:-1:-1;9051:652:23;;;;;:::i;:::-;;:::i;485:243:27:-;;;;;;;;;;-1:-1:-1;485:243:27;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;577:42:23:-;;;;;;;;;;;;;:::i;4816:423::-;;;;;;:::i;:::-;;:::i;5243:1137::-;;;;;;:::i;:::-;;:::i;1236:462::-;;;;;;;;;;-1:-1:-1;1236:462:23;;;;;:::i;:::-;;:::i;272:209:27:-;;;;;;;;;;-1:-1:-1;272:209:27;;;;;:::i;:::-;;:::i;536:60:20:-;;;;;;;;;;-1:-1:-1;536:60:20;;;;;:::i;:::-;;:::i;1117:201::-;;;;;;;;;;-1:-1:-1;1117:201:20;;;;;:::i;:::-;;:::i;623:42:23:-;;;;;;;;;;;;;:::i;948:140:57:-;-1:-1:-1;;;;;;1048:33:57;;1025:4;1048:33;;;;;;;;;;;;;948:140;;;;:::o;781:30:23:-;;;:::o;8733:314::-;8854:10;8867:23;8875:6;8883;8867:7;:23::i;:::-;8854:36;-1:-1:-1;8927:35:23;8854:36;8948:13;;8927:16;:35::i;:::-;8998:44;9022:6;9030;9038:3;8998:23;:44::i;:::-;8733:314;;;;:::o;502:29:20:-;;;:::o;9707:662:23:-;9752:15;9770:4;-1:-1:-1;;;;;9770:16:23;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9752:36;;9799:11;9816:4;-1:-1:-1;;;;;9816:20:23;;9837:10;9816:32;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9794:54;;;;;9855:8;9869:11;-1:-1:-1;;;;;9869:20:23;;9890:6;9869:28;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9854:43;;;9903:10;9916:11;-1:-1:-1;;;;;9916:30:23;;9947:6;9916:38;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9991:59;;-1:-1:-1;;;9991:59:23;;9903:51;;-1:-1:-1;;;;;;9991:4:23;:19;;;;:59;;10019:11;;10033:6;;-1:-1:-1;;10046:2:23;9991:59;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;10056:34:23;;-1:-1:-1;;;10056:34:23;;-1:-1:-1;;;;;10056:11:23;:16;;-1:-1:-1;10056:16:23;;-1:-1:-1;10056:34:23;;10073:6;;-1:-1:-1;;10086:2:23;10056:34;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;10136:35:23;;-1:-1:-1;;;10136:35:23;;10122:11;;-1:-1:-1;;;;;10136:20:23;;;;;:35;;10165:4;;10136:35;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;10122:49;;10177:39;10191:2;10203:11;10177:13;:39::i;:::-;10232:29;;-1:-1:-1;;;10232:29:23;;10222:7;;-1:-1:-1;;;;;10232:11:23;:16;;;;:29;;10249:3;;10254:6;;10232:29;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;10267:52;;-1:-1:-1;;;10267:52:23;;10222:39;;-1:-1:-1;;;;;;10267:4:23;:18;;;;:52;;10294:11;;10222:39;;10312:6;;10267:52;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10349:15;10358:5;10349:8;:15::i;:::-;9707:662;;;;;;:::o;467:31:20:-;;;:::o;670:60:23:-;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;670:60:23;;:::o;436:27:20:-;;;:::o;735:41:23:-;;;:::o;9051:652::-;9177:10;9190:23;9198:6;9206;9190:7;:23::i;:::-;9177:36;;9219:15;9237:4;-1:-1:-1;;;;;9237:16:23;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9219:36;;9264:17;9283:11;9300:4;-1:-1:-1;;;;;9300:20:23;;9321:10;9300:32;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9261:71;;;;;;9400:2;-1:-1:-1;;;;;9346:56:23;9359:9;-1:-1:-1;;;;;9346:42:23;;9389:6;9346:50;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;9346:56:23;;9338:89;;;;-1:-1:-1;;;9338:89:23;;;;;;;:::i;:::-;9464:64;;-1:-1:-1;;;9464:64:23;;-1:-1:-1;;;;;9464:4:23;:19;;;;:64;;9492:11;;9506:6;;9514:13;;;9464:64;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;9534:39:23;;-1:-1:-1;;;9534:39:23;;-1:-1:-1;;;;;9534:11:23;:16;;-1:-1:-1;9534:16:23;;-1:-1:-1;9534:39:23;;9551:6;;9559:13;;;9534:39;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;9609:44;9633:6;9641;9649:3;9609:23;:44::i;:::-;9683:15;9692:5;9683:8;:15::i;:::-;9051:652;;;;;;;:::o;485:243:27:-;-1:-1:-1;;;485:243:27;;;;;;;;;;:::o;577:42:23:-;;;:::o;4816:423::-;4937:10;4950:23;4958:6;4966;4950:7;:23::i;:::-;4937:36;;5005:41;5026:6;5034;5042:3;5005:20;:41::i;:::-;5078:56;5094:2;5105;-1:-1:-1;;;;;5098:20:23;;5127:4;5098:35;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;5078:15;:56::i;:::-;5177:13;:11;:13::i;:::-;5196:16;5205:6;5196:8;:16::i;:::-;5218;5227:6;5218:8;:16::i;5243:1137::-;5383:10;5396:23;5404:6;5412;5396:7;:23::i;:::-;5383:36;;5426:15;5451:11;-1:-1:-1;;;;;5451:16:23;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;5451:27:23;;5479:3;5451:32;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;5425:58;;;;;5508:2;-1:-1:-1;;;;;5497:13:23;:7;-1:-1:-1;;;;;5497:13:23;;5489:44;;;;-1:-1:-1;;;5489:44:23;;;;;;;:::i;:::-;5566:41;5587:6;5595;5603:3;5566:20;:41::i;:::-;5644:15;5662:4;-1:-1:-1;;;;;5662:16:23;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;5644:36;;5691:11;5704:13;5721:4;-1:-1:-1;;;;;5721:20:23;;5742:10;5721:32;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;5686:67;;;;;;5774:1;5763:8;:12;5759:243;;;5807:28;;-1:-1:-1;;;5807:28:23;;5786:15;;-1:-1:-1;;;;;5807:11:23;:20;;;;:28;;5828:6;;5807:28;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;5785:50;;;5858:10;5851:3;:17;5843:43;;;;-1:-1:-1;;;5843:43:23;;;;;;;:::i;:::-;5894:59;;-1:-1:-1;;;5894:59:23;;-1:-1:-1;;;;;5894:4:23;:19;;;;:59;;5922:11;;5936:6;;5944:8;;5894:59;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5961:34:23;;-1:-1:-1;;;5961:34:23;;-1:-1:-1;;;;;5961:11:23;:16;;-1:-1:-1;5961:16:23;;-1:-1:-1;5961:34:23;;5978:6;;5986:8;;5961:34;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;5759:243;;6033:39;6047:2;6059:11;6033:13;:39::i;:::-;6092:35;;-1:-1:-1;;;6092:35:23;;6078:11;;-1:-1:-1;;;;;6092:20:23;;;;;:35;;6121:4;;6092:35;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;6078:49;;6133:7;6143:11;-1:-1:-1;;;;;6143:16:23;;6160:3;6165:6;6143:29;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;6178:52;;-1:-1:-1;;;6178:52:23;;6133:39;;-1:-1:-1;;;;;;6178:4:23;:18;;;;:52;;6205:11;;6133:39;;6223:6;;6178:52;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6273:13;:11;:13::i;:::-;6292:16;6301:6;6292:8;:16::i;:::-;6314;6323:6;6314:8;:16::i;:::-;6360:15;6369:5;6360:8;:15::i;:::-;5243:1137;;;;;;;;;;;:::o;1236:462::-;-1:-1:-1;;;;;1329:13:23;;;1301:7;1329:13;;;:5;:13;;;;;;;;:21;;;;;;;;;;;1301:7;;1329:21;1360:16;1356:323;;1391:31;;-1:-1:-1;;;1391:31:23;;-1:-1:-1;;;;;1391:7:23;:15;;;;:31;;1407:6;;1415;;1391:31;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;1386:36;-1:-1:-1;;;;;;1438:16:23;;1430:40;;;;-1:-1:-1;;;1430:40:23;;;;;;;:::i;:::-;1478:38;1492:6;1508;1478:13;:38::i;:::-;1524;1538:6;1554;1524:13;:38::i;:::-;1570:34;1584:2;1596:6;1570:13;:34::i;:::-;-1:-1:-1;;;;;1612:13:23;;;;;;;:5;:13;;;;;;;;:21;;;;;;;;;;:26;;;;;-1:-1:-1;;;;;;1612:26:23;;;;;;;;1646:13;;;;;;:21;;;;;;;;;:26;;;;;;;;1356:323;1691:2;-1:-1:-1;1236:462:23;;;;;:::o;272:209:27:-;-1:-1:-1;;;272:209:27;;;;;;;;:::o;536:60:20:-;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;1117:201::-;-1:-1:-1;;;;;1190:15:20;;;;;;;:8;:15;;;;;;;;:24;;;;;;;;;;;;1185:129;;1224:44;-1:-1:-1;;;;;1224:25:20;;1250:7;-1:-1:-1;;1224:25:20;:44::i;:::-;-1:-1:-1;;;;;1276:15:20;;;;;;;1303:4;1276:15;;;;;;;;:24;;;;;;;;;;;:31;;-1:-1:-1;;1276:31:20;;;;;;1185:129;1117:201;;:::o;623:42:23:-;;;:::o;1340:613:69:-;1705:10;;;1704:62;;-1:-1:-1;1721:39:69;;-1:-1:-1;;;1721:39:69;;-1:-1:-1;;;;;1721:15:69;;;;;:39;;1745:4;;1752:7;;1721:39;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:44;1704:62;1696:150;;;;-1:-1:-1;;;1696:150:69;;;;;;;:::i;:::-;1856:90;1876:5;1906:22;;;1930:7;1939:5;1883:62;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;1883:62:69;;;;;;;;;;;;;;-1:-1:-1;;;;;1883:62:69;-1:-1:-1;;;;;;1883:62:69;;;;;;;;;;1856:19;:90::i;:::-;1340:613;;;:::o;3573:194:70:-;3676:12;3707:53;3730:6;3738:4;3744:1;3747:12;3707:22;:53::i;:::-;3700:60;3573:194;-1:-1:-1;;;;3573:194:70:o;3756:302:20:-;3829:10;;3825:229;;-1:-1:-1;;3853:6:20;:18;3849:100;;;3900:4;-1:-1:-1;;;;;3900:20:20;;3921:4;-1:-1:-1;;;;;3921:16:20;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3900:40;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3883:57;-1:-1:-1;;;;3849:100:20;3956:57;;-1:-1:-1;;;3956:57:20;;-1:-1:-1;;;;;3956:4:20;:19;;;;;:57;;3984:6;;3993:11;;;4006:6;;3956:57;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4021:26:20;;-1:-1:-1;;;4021:26:20;;-1:-1:-1;;;;;4021:6:20;:11;;-1:-1:-1;4021:11:20;;-1:-1:-1;4021:26:20;;4033:5;;4040:6;;4021:26;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6554:2175:23;6677:10;6690:23;6698:6;6706;6690:7;:23::i;:::-;6677:36;;6719:15;6737:4;-1:-1:-1;;;;;6737:16:23;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;6719:36;-1:-1:-1;6779:13:23;;;;6815;;;;6852:14;;;;-1:-1:-1;;6942:21:23;;6938:99;;;6985:45;;-1:-1:-1;;;6985:45:23;;-1:-1:-1;;;;;6985:4:23;:25;;;;:45;;7011:10;;7023:6;;6985:45;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;6973:57;;6938:99;-1:-1:-1;;7046:9:23;:21;7042:99;;;7089:45;;-1:-1:-1;;;7089:45:23;;-1:-1:-1;;;;;7089:4:23;:25;;;;:45;;7115:10;;7127:6;;7089:45;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;7077:57;;7042:99;-1:-1:-1;;7150:10:23;:22;7146:97;;;7195:41;;-1:-1:-1;;;7195:41:23;;-1:-1:-1;;;;;7195:4:23;:25;;;;:41;;7221:10;;7233:2;;7195:41;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;7182:54;;7146:97;7293:18;7314:58;7354:3;:17;;;7321:2;-1:-1:-1;;;;;7314:20:23;;7343:4;7314:35;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:39;;:58::i;:::-;7293:79;;7407:9;7418;7437:6;-1:-1:-1;;;;;7437:22:23;;7460:6;7468;7476:13;7491:1;7494;7505:4;7512:3;7437:79;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;7406:110;;-1:-1:-1;7406:110:23;-1:-1:-1;7549:16:23;7568:26;:9;7582:11;;;;7568:13;:26::i;:::-;7549:45;-1:-1:-1;7600:16:23;7619:26;:9;7633:11;;;;7619:13;:26::i;:::-;7600:45;;7663:11;7656:4;:18;:41;;;;;7686:11;7678:4;:19;;7656:41;7652:618;;;7731:16;;;7745:1;7731:16;;;7707:21;7731:16;;;;;7707:21;7731:16;;;;;;;;;;-1:-1:-1;7731:16:23;7707:40;;7777:6;7785;7756:4;7761:1;7756:7;;;;;;;;;;;;;7765:4;7770:1;7765:7;;;;;;;;-1:-1:-1;;;;;7755:37:23;;;7765:7;;;;;;;;;7755:37;;;;;;;7800:6;:31;;7841:21;:11;7857:4;7841:15;:21::i;:::-;7872;:4;7881:11;7872:8;:21::i;:::-;7903:4;7925;7940:3;7800:151;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;7800:151:23;;;;;;;;;;;;:::i;:::-;;7652:618;;;;7976:11;7968:4;:19;;:41;;;;;7998:11;7991:4;:18;7968:41;7964:306;;;8043:16;;;8057:1;8043:16;;;8019:21;8043:16;;;;;8019:21;8043:16;;;;;;;;;;-1:-1:-1;8043:16:23;8019:40;;8089:6;8097;8068:4;8073:1;8068:7;;;;;;;;;;;;;8077:4;8082:1;8077:7;;;;;;;;-1:-1:-1;;;;;8067:37:23;;;8077:7;;;;;;;;;8067:37;;;;;;;8112:6;:31;;8153:21;:11;8169:4;8153:15;:21::i;:::-;8184;:4;8193:11;8184:8;:21::i;:::-;8215:4;8237;8252:3;8112:151;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;8112:151:23;;;;;;;;;;;;:::i;:::-;;7964:306;;8292:26;8300:6;8308:9;8292:7;:26::i;:::-;8324;8332:6;8340:9;8324:7;:26::i;:::-;8356:23;8364:2;8368:10;8356:7;:23::i;:::-;8421:39;;-1:-1:-1;;;8421:39:23;;8464:11;;;;;-1:-1:-1;;;;;8421:24:23;;;;;:39;;8454:4;;8421:39;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:54;;8413:63;;;;;;8490:39;;-1:-1:-1;;;8490:39:23;;8533:11;;;;;-1:-1:-1;;;;;8490:24:23;;;;;:39;;8523:4;;8490:39;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:54;;8482:63;;;;;;8559:35;;-1:-1:-1;;;8559:35:23;;8598:17;;;;;-1:-1:-1;;;;;8559:20:23;;;;;:35;;8588:4;;8559:35;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:56;;8551:65;;;;;;8649:13;:11;:13::i;:::-;8668:16;8677:6;8668:8;:16::i;:::-;8690;8699:6;8690:8;:16::i;:::-;8712:12;8721:2;8712:8;:12::i;:::-;6554:2175;;;;;;;;;;;;;:::o;1969:195:20:-;2032:38;;-1:-1:-1;;;2032:38:20;;2017:12;;-1:-1:-1;;;;;2032:23:20;;;;;:38;;2064:4;;2032:38;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;2017:53;-1:-1:-1;2080:11:20;;2076:84;;2101:52;2128:4;-1:-1:-1;;;;;2128:13:20;;:15;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;2101:26:20;;;2145:7;2101:26;:52::i;3344:1468:23:-;3459:10;3472:23;3480:6;3488;3472:7;:23::i;:::-;3459:36;;3535:15;:13;:15::i;:::-;3556:32;3567:6;3575:12;;3556:10;:32::i;:::-;3594;3605:6;3613:3;:12;;;3594:10;:32::i;:::-;3632:29;3643:2;3647:3;:13;;;3632:10;:29::i;:::-;3703:32;3712:6;3720:3;:14;;;3703:8;:32::i;:::-;3741;3750:6;3758:3;:14;;;3741:8;:32::i;:::-;3779:29;3788:2;3792:3;:15;;;3779:8;:29::i;:::-;3855:12;3873:15;3902:9;3921:6;-1:-1:-1;;;;;3914:24:23;;3947:4;3914:39;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3902:51;;3961:9;3980:6;-1:-1:-1;;;;;3973:24:23;;4006:4;3973:39;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3961:51;;4020:9;4037;4089:6;-1:-1:-1;;;;;4058:37:23;4073:2;-1:-1:-1;;;;;4058:25:23;;:27;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;4058:37:23;;4054:185;;;4139:2;-1:-1:-1;;;;;4124:30:23;;:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;;4107:49:23;;;;-1:-1:-1;4107:49:23;;-1:-1:-1;4054:185:23;;;4213:2;-1:-1:-1;;;;;4198:30:23;;:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;;4181:49:23;;;;-1:-1:-1;4181:49:23;;-1:-1:-1;4054:185:23;4270:38;4285:4;4291;4297;4303;4270:14;:38::i;:::-;4246:62;;-1:-1:-1;4246:62:23;-1:-1:-1;4359:21:23;;-1:-1:-1;4397:1:23;;-1:-1:-1;4383:16:23;;-1:-1:-1;4383:16:23;;-1:-1:-1;4383:16:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;4383:16:23;;4359:40;;4428:10;:48;;4461:6;4469;4428:48;;;4442:6;4450;4428:48;4408:4;4413:1;4408:7;;;;;;;;;;;;;4417:4;4422:1;4417:7;;;;;;;;-1:-1:-1;;;;;4407:69:23;;;4417:7;;;;;;;;;4407:69;;;;;;;4484;;-1:-1:-1;;;4484:69:23;;:6;:31;;;;;;:69;;4516:7;;4525:1;;4528:4;;4542;;4549:3;;4484:69;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4484:69:23;;;;;;;;;;;;:::i;:::-;;3344:1468;4590:6;-1:-1:-1;;;;;4590:19:23;;4617:6;4631;4652;-1:-1:-1;;;;;4645:24:23;;4678:4;4645:39;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4692;;-1:-1:-1;;;4692:39:23;;-1:-1:-1;;;;;4692:24:23;;;;;:39;;4725:4;;4692:39;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4739:3;:11;;;4758:3;:11;;;4785:4;4798:3;4590:217;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;3344:1468;;;;;;:::o;3360:238:20:-;3432:10;;3428:166;;3452:37;3466:5;3481:6;3452:13;:37::i;:::-;3497:26;;-1:-1:-1;;;3497:26:20;;-1:-1:-1;;;;;3497:6:20;:11;;;;:26;;3509:5;;3516:6;;3497:26;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;3531:56:20;;-1:-1:-1;;;3531:56:20;;-1:-1:-1;;;;;3531:4:20;:18;;;-1:-1:-1;3531:18:20;;-1:-1:-1;3531:56:20;;3558:6;;3567:11;;;3580:6;;3531:56;;;:::i;2251:282::-;2304:36;;-1:-1:-1;;;2304:36:20;;2289:12;;-1:-1:-1;;;;;2310:4:20;2304:21;;;;:36;;2334:4;;2304:36;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;2289:51;-1:-1:-1;2350:11:20;;2346:183;;2371:29;;-1:-1:-1;;;2371:29:20;;-1:-1:-1;;;;;2377:4:20;2371:20;;;;:29;;2392:7;;2371:29;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2409:12;2427:4;-1:-1:-1;;;;;2427:13:20;;:15;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;2464:12;;;2474:1;2464:12;;;;;;;;;-1:-1:-1;;;;;2427:20:20;;;;;2455:7;;2427:50;;2464:12;2427:50;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2408:69;;;2493:7;2485:37;;;;-1:-1:-1;;;2485:37:20;;;;;;;:::i;2346:183::-;2251:282;:::o;2959:751:69:-;3378:23;3404:69;3432:4;3404:69;;;;;;;;;;;;;;;;;3412:5;-1:-1:-1;;;;;3404:27:69;;;:69;;;;;:::i;:::-;3487:17;;3378:95;;-1:-1:-1;3487:21:69;3483:221;;3627:10;3616:30;;;;;;;;;;;;:::i;:::-;3608:85;;;;-1:-1:-1;;;3608:85:69;;;;;;;:::i;4920:958:70:-;5050:12;5082:18;5093:6;5082:10;:18::i;:::-;5074:60;;;;-1:-1:-1;;;5074:60:70;;;;;;;:::i;:::-;5205:12;5219:23;5246:6;-1:-1:-1;;;;;5246:11:70;5266:8;5277:4;5246:36;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5204:78;;;;5296:7;5292:580;;;5326:10;-1:-1:-1;5319:17:70;;-1:-1:-1;5319:17:70;5292:580;5437:17;;:21;5433:429;;5695:10;5689:17;5755:15;5742:10;5738:2;5734:19;5727:44;5644:145;5834:12;5827:20;;-1:-1:-1;;;5827:20:70;;;;;;;;:::i;1321:134:60:-;1379:7;1405:43;1409:1;1412;1405:43;;;;;;;;;;;;;;;;;:3;:43::i;874:176::-;932:7;963:5;;;986:6;;;;978:46;;;;-1:-1:-1;;;978:46:60;;;;;;;:::i;3030:163:20:-;3094:10;;3090:99;;3114:35;3128:5;3143:4;3114:13;:35::i;:::-;3157:25;;-1:-1:-1;;;3157:25:20;;-1:-1:-1;;;;;3157:4:20;:10;;;;:25;;3168:5;;3175:6;;3157:25;;;:::i;696:175:69:-;778:86;798:5;828:23;;;853:2;857:5;805:58;;;;;;;;;:::i;1401:117:20:-;1445:9;:13;1441:73;;1474:4;-1:-1:-1;;;;;1468:19:20;;1495:9;1468:39;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1441:73;1401:117::o;1708:126::-;1775:10;;1771:59;;1795:28;;-1:-1:-1;;;1795:28:20;;-1:-1:-1;;;;;1795:4:20;:13;;;;:28;;1809:5;;1816:6;;1795:28;;;:::i;2724:122::-;2789:10;;2785:57;;2809:26;;-1:-1:-1;;;2809:26:20;;-1:-1:-1;;;;;2809:4:20;:11;;;;:26;;2821:5;;2828:6;;2809:26;;;:::i;1948:377:23:-;2059:12;;2118:14;:4;2127;2118:8;:14::i;:::-;2100;:4;2109;2100:8;:14::i;:::-;:32;2096:225;;2152:40;2169:4;2175;2181;2187;2152:16;:40::i;:::-;2142:50;;2213:5;2200:18;;2096:225;;;2249:40;2266:4;2272;2278;2284;2249:16;:40::i;:::-;2239:50;;2310:4;2297:17;;2096:225;1948:377;;;;;;;:::o;718:413:70:-;1078:20;1116:8;;;718:413::o;1746:187:60:-;1832:7;1867:12;1859:6;;;;1851:29;;;;-1:-1:-1;;;1851:29:60;;;;;;;;:::i;:::-;-1:-1:-1;;;1902:5:60;;;1746:187::o;2180:459::-;2238:7;2479:6;2475:45;;-1:-1:-1;2508:1:60;2501:8;;2475:45;2542:5;;;2546:1;2542;:5;:1;2565:5;;;;;:10;2557:56;;;;-1:-1:-1;;;2557:56:60;;;;;;;:::i;2633:525:23:-;2746:4;2784:14;:4;2793;2784:8;:14::i;:::-;2766;:4;2775;2766:8;:14::i;:::-;:32;;2758:53;;;;-1:-1:-1;;;2758:53:23;;;;;;;:::i;:::-;2826:3;2817:6;2844:20;2849:4;2859;2844:14;:20::i;:::-;2835:29;-1:-1:-1;2870:7:23;2880:36;2901:14;:4;2910;2901:8;:14::i;:::-;2881;:4;2890;2881:8;:14::i;2880:36::-;2870:46;-1:-1:-1;2922:6:23;2931:42;2968:4;2931:32;2948:14;:4;2957;2948:8;:14::i;:::-;2931:12;:2;2938:4;2931:6;:12::i;:::-;:16;;:32::i;:::-;:36;;:42::i;:::-;2922:51;-1:-1:-1;2979:6:23;2988:15;3001:1;2988:8;:1;2922:51;2988:5;:8::i;:15::-;2979:24;-1:-1:-1;3009:6:23;3018:32;3034:15;2979:24;3034:8;3040:1;;3034:5;:8::i;:::-;:12;;:15::i;:::-;3018;:32::i;:::-;3009:41;-1:-1:-1;3056:14:23;3073:8;3009:41;3079:1;3073:5;:8::i;:::-;3056:25;-1:-1:-1;3087:16:23;3106:8;:1;3112;3106:5;:8::i;:::-;3087:27;-1:-1:-1;3127:26:23;:9;3087:27;3127:13;:26::i;:::-;3120:33;2633:525;-1:-1:-1;;;;;;;;;;;;;2633:525:23:o;3101:130:60:-;3159:7;3185:39;3189:1;3192;3185:39;;;;;;;;;;;;;;;;;:3;:39::i;720:849:28:-;765:4;781:6;777:20;;-1:-1:-1;796:1:28;789:8;;777:20;813:1;829;-1:-1:-1;;;841:41:28;;837:88;;899:3;892:10;;;;;916:2;910:8;837:88;941:19;935:2;:25;931:71;;977:2;970:9;;;;;993:2;987:8;931:71;1017:11;1011:2;:17;1007:63;;1045:2;1038:9;;;;;1061:2;1055:8;1007:63;1085:7;1079:2;:13;1075:58;;1109:2;1102:9;;;;;1125:1;1119:7;1075:58;1148:5;1142:2;:11;1138:55;;1170:1;1163:8;;;;;1185:1;1179:7;1138:55;1208:4;1202:2;:10;1198:54;;1229:1;1222:8;;;;;1244:1;1238:7;1198:54;1267:3;1261:2;:9;1257:37;;1286:1;1280:7;1257:37;1319:1;1313;1309;:5;;;;;;1305:1;:9;1304:16;;1300:20;;1345:1;1339;1335;:5;;;;;;1331:1;:9;1330:16;;1326:20;;1371:1;1365;1361;:5;;;;;;1357:1;:9;1356:16;;1352:20;;1397:1;1391;1387;:5;;;;;;1383:1;:9;1382:16;;1378:20;;1423:1;1417;1413;:5;;;;;;1409:1;:9;1408:16;;1404:20;;1449:1;1443;1439;:5;;;;;;1435:1;:9;1434:16;;1430:20;;1475:1;1469;1465;:5;;;;;;1461:1;:9;1460:16;;1456:20;;1519:7;1533:1;1529;:5;;;;;;1519:15;;1552:2;1548:1;:6;:15;;1561:2;1548:15;;;1557:1;1548:15;1540:24;720:849;-1:-1:-1;;;;;720:849:28:o;3713:272:60:-;3799:7;3833:12;3826:5;3818:28;;;;-1:-1:-1;;;3818:28:60;;;;;;;;:::i;:::-;;3856:9;3872:1;3868;:5;;;;;;;3713:272;-1:-1:-1;;;;;3713:272:60:o;301:352:-1:-;;;431:3;424:4;416:6;412:17;408:27;398:2;;-1:-1;;439:12;398:2;-1:-1;469:20;;509:18;498:30;;495:2;;;-1:-1;;531:12;495:2;575:4;567:6;563:17;551:29;;626:3;575:4;;610:6;606:17;567:6;592:32;;589:41;586:2;;;643:1;;633:12;586:2;391:262;;;;;:::o;1693:336::-;;;1807:3;1800:4;1792:6;1788:17;1784:27;1774:2;;-1:-1;;1815:12;1774:2;-1:-1;1845:20;;1885:18;1874:30;;1871:2;;;-1:-1;;1907:12;1871:2;1951:4;1943:6;1939:17;1927:29;;2002:3;1951:4;1982:17;1943:6;1968:32;;1965:41;1962:2;;;2019:1;;2009:12;2260:160;;2372:3;2363:6;2358:3;2354:16;2350:26;2347:2;;;-1:-1;;2379:12;2347:2;-1:-1;2399:15;2340:80;-1:-1;2340:80::o;3202:263::-;;3317:2;3305:9;3296:7;3292:23;3288:32;3285:2;;;-1:-1;;3323:12;3285:2;226:6;220:13;238:33;265:5;238:33;:::i;3472:366::-;;;3593:2;3581:9;3572:7;3568:23;3564:32;3561:2;;;-1:-1;;3599:12;3561:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;3651:63;-1:-1;3751:2;3790:22;;72:20;97:33;72:20;97:33;:::i;:::-;3759:63;;;;3555:283;;;;;:::o;3845:1179::-;;;;;;;;;4106:3;4094:9;4085:7;4081:23;4077:33;4074:2;;;-1:-1;;4113:12;4074:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;4165:63;-1:-1;4265:2;4304:22;;72:20;97:33;72:20;97:33;:::i;:::-;4273:63;-1:-1;4401:2;4386:18;;4373:32;4425:18;4414:30;;;4411:2;;;-1:-1;;4447:12;4411:2;4485:80;4557:7;4548:6;4537:9;4533:22;4485:80;:::i;:::-;4467:98;;-1:-1;4467:98;-1:-1;4630:2;4615:18;;4602:32;;-1:-1;4643:30;;;4640:2;;;-1:-1;;4676:12;4640:2;4714:80;4786:7;4777:6;4766:9;4762:22;4714:80;:::i;:::-;4696:98;;-1:-1;4696:98;-1:-1;4859:3;4844:19;;4831:33;;-1:-1;4873:30;;;4870:2;;;-1:-1;;4906:12;4870:2;;4944:64;5000:7;4991:6;4980:9;4976:22;4944:64;:::i;:::-;4068:956;;;;-1:-1;4068:956;;-1:-1;4068:956;;;;;;4926:82;-1:-1;;;4068:956::o;5031:548::-;;;;5197:3;5185:9;5176:7;5172:23;5168:33;5165:2;;;-1:-1;;5204:12;5165:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;5256:63;-1:-1;5356:2;5395:22;;72:20;97:33;72:20;97:33;:::i;:::-;5364:63;-1:-1;5482:81;5555:7;5464:2;5531:22;;5482:81;:::i;:::-;5472:91;;5159:420;;;;;:::o;5586:674::-;;;;;5769:3;5757:9;5748:7;5744:23;5740:33;5737:2;;;-1:-1;;5776:12;5737:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;5828:63;-1:-1;5928:2;5967:22;;72:20;97:33;72:20;97:33;:::i;:::-;5936:63;-1:-1;6054:81;6127:7;6036:2;6103:22;;6054:81;:::i;:::-;5731:529;;;;-1:-1;6044:91;;6172:3;6212:22;2852:20;;-1:-1;;5731:529::o;6267:558::-;;;;6426:9;6417:7;6413:23;6438:3;6413:23;6409:33;6406:2;;;-1:-1;;6445:12;6406:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;6497:63;-1:-1;6597:2;6636:22;;72:20;97:33;72:20;97:33;:::i;:::-;6605:63;-1:-1;2589:3;-1:-1;;2571:16;;2567:26;2564:2;;;-1:-1;;2596:12;2564:2;;6705;6781:9;6777:22;6713:96;;6400:425;;;;;:::o;6832:672::-;;;;;6998:3;6986:9;6977:7;6973:23;6969:33;6966:2;;;-1:-1;;7005:12;6966:2;226:6;220:13;238:33;265:5;238:33;:::i;:::-;7168:2;7218:22;;220:13;7057:74;;-1:-1;238:33;220:13;238:33;:::i;:::-;7287:2;7337:22;;3000:13;7406:2;7456:22;;;3000:13;6960:544;;7176:74;;-1:-1;6960:544;-1:-1;;;6960:544::o;7511:867::-;;;;;;;7702:3;7690:9;7681:7;7677:23;7673:33;7670:2;;;-1:-1;;7709:12;7670:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;7761:63;-1:-1;7861:2;7900:22;;72:20;97:33;72:20;97:33;:::i;:::-;7869:63;-1:-1;7969:2;8008:22;;2852:20;;-1:-1;8077:2;8116:22;;2852:20;;-1:-1;8213:3;8198:19;;8185:33;8238:18;8227:30;;8224:2;;;-1:-1;;8260:12;8224:2;8298:64;8354:7;8345:6;8334:9;8330:22;8298:64;:::i;:::-;7664:714;;;;-1:-1;7664:714;;-1:-1;7664:714;;8280:82;;7664:714;-1:-1;;;7664:714::o;8385:672::-;;;;;8551:3;8539:9;8530:7;8526:23;8522:33;8519:2;;;-1:-1;;8558:12;8519:2;226:6;220:13;238:33;265:5;238:33;:::i;:::-;8721:2;8771:22;;3000:13;8840:2;8890:22;;3000:13;8959:2;9009:22;;;3000:13;8610:74;;3000:13;;-1:-1;3000:13;;-1:-1;8513:544;-1:-1;;;8513:544::o;9064:392::-;;9204:2;;9192:9;9183:7;9179:23;9175:32;9172:2;;;-1:-1;;9210:12;9172:2;9261:17;9255:24;9299:18;9291:6;9288:30;9285:2;;;-1:-1;;9321:12;9285:2;9408:22;;800:4;788:17;;784:27;-1:-1;774:2;;-1:-1;;815:12;774:2;855:6;849:13;877:80;892:64;949:6;892:64;:::i;:::-;877:80;:::i;:::-;985:21;;;1042:14;;;;1017:17;;;1131;;;1122:27;;;;1119:36;-1:-1;1116:2;;;-1:-1;;1158:12;1116:2;-1:-1;1184:10;;1178:217;1203:6;1200:1;1197:13;1178:217;;;3000:13;;1271:61;;1225:1;1218:9;;;;;1346:14;;;;1374;;1178:217;;;-1:-1;9341:99;9166:290;-1:-1;;;;;;;9166:290::o;9463:257::-;;9575:2;9563:9;9554:7;9550:23;9546:32;9543:2;;;-1:-1;;9581:12;9543:2;1490:6;1484:13;38470:5;35125:13;35118:21;38448:5;38445:32;38435:2;;-1:-1;;38481:12;9727:239;;9830:2;9818:9;9809:7;9805:23;9801:32;9798:2;;;-1:-1;;9836:12;9798:2;1610:20;;-1:-1;;;;;;35212:78;;38565:34;;38555:2;;-1:-1;;38603:12;10285:533;;;;10433:2;10421:9;10412:7;10408:23;10404:32;10401:2;;;-1:-1;;10439:12;10401:2;2728:6;2722:13;2740:33;2767:5;2740:33;:::i;:::-;10602:2;10652:22;;2722:13;10491:74;;-1:-1;2740:33;2722:13;2740:33;:::i;:::-;10721:2;10770:22;;3140:13;10610:74;;-1:-1;35816:10;35805:22;;39101:34;;39091:2;;-1:-1;;39139:12;39091:2;10729:73;;;;10395:423;;;;;:::o;10825:263::-;;10940:2;10928:9;10919:7;10915:23;10911:32;10908:2;;;-1:-1;;10946:12;10908:2;-1:-1;3000:13;;10902:186;-1:-1;10902:186::o;11095:399::-;;;11227:2;11215:9;11206:7;11202:23;11198:32;11195:2;;;-1:-1;;11233:12;11195:2;-1:-1;;3000:13;;11396:2;11446:22;;;3000:13;;;;;-1:-1;11189:305::o;11501:535::-;;;;11650:2;11638:9;11629:7;11625:23;11621:32;11618:2;;;-1:-1;;11656:12;11618:2;3006:6;3000:13;11708:74;;11819:2;11873:9;11869:22;3000:13;11827:74;;11938:2;11992:9;11988:22;3000:13;11946:74;;11612:424;;;;;:::o;12635:690::-;;12828:5;34036:12;34580:6;34575:3;34568:19;34617:4;;34612:3;34608:14;12840:93;;34617:4;13004:5;33890:14;-1:-1;13043:260;13068:6;13065:1;13062:13;13043:260;;;13129:13;;-1:-1;;;;;35599:54;12435:37;;12197:14;;;;34423;;;;509:18;13083:9;13043:260;;;-1:-1;13309:10;;12759:566;-1:-1;;;;;12759:566::o;19543:271::-;;13721:5;34036:12;13832:52;13877:6;13872:3;13865:4;13858:5;13854:16;13832:52;:::i;:::-;13896:16;;;;;19677:137;-1:-1;;19677:137::o;19821:222::-;-1:-1;;;;;35599:54;;;;12435:37;;19948:2;19933:18;;19919:124::o;20295:333::-;-1:-1;;;;;35599:54;;;12435:37;;35599:54;;20614:2;20599:18;;12435:37;20450:2;20435:18;;20421:207::o;20635:940::-;-1:-1;;;;;35599:54;;;12435:37;;35599:54;;;21119:2;21104:18;;12435:37;21202:2;21187:18;;19494:37;;;;21293:2;21278:18;;14886:58;;;;21384:3;21369:19;;14886:58;35599:54;;;35610:42;21461:19;;12304:58;21560:3;21545:19;;19494:37;;;;20954:3;20939:19;;20925:650::o;21582:1020::-;-1:-1;;;;;35599:54;;;12435:37;;35599:54;;;22078:2;22063:18;;12435:37;22161:2;22146:18;;19494:37;;;;22244:2;22229:18;;19494:37;;;;22327:3;22312:19;;19494:37;;;;35610:42;22396:19;;19494:37;35599:54;;;22503:3;22488:19;;12304:58;22587:3;22572:19;;19494:37;;;;21913:3;21898:19;;21884:718::o;22609:333::-;-1:-1;;;;;35599:54;;;;12435:37;;22928:2;22913:18;;19494:37;22764:2;22749:18;;22735:207::o;22949:444::-;-1:-1;;;;;35599:54;;;;12435:37;;23296:2;23281:18;;19494:37;;;;23379:2;23364:18;;19494:37;23132:2;23117:18;;23103:290::o;23400:210::-;35125:13;;35118:21;13398:34;;23521:2;23506:18;;23492:118::o;23617:218::-;-1:-1;;;;;;35212:78;;;;13513:36;;23742:2;23727:18;;23713:122::o;25205:310::-;;25352:2;25373:17;25366:47;15101:5;34036:12;34580:6;25352:2;25341:9;25337:18;34568:19;15195:52;15240:6;34608:14;25341:9;34608:14;25352:2;15221:5;15217:16;15195:52;:::i;:::-;38244:7;38228:14;-1:-1;;38224:28;15259:39;;;;34608:14;15259:39;;25323:192;-1:-1;;25323:192::o;25522:416::-;25722:2;25736:47;;;15535:2;25707:18;;;34568:19;-1:-1;;;34608:14;;;15551:40;15610:12;;;25693:245::o;25945:416::-;26145:2;26159:47;;;15861:2;26130:18;;;34568:19;15897:29;34608:14;;;15877:50;15946:12;;;26116:245::o;26368:416::-;26568:2;26582:47;;;16197:2;26553:18;;;34568:19;-1:-1;;;34608:14;;;16213:34;16266:12;;;26539:245::o;26791:416::-;26991:2;27005:47;;;16517:2;26976:18;;;34568:19;-1:-1;;;34608:14;;;16533:41;16593:12;;;26962:245::o;27214:416::-;27414:2;27428:47;;;16844:2;27399:18;;;34568:19;16880:25;34608:14;;;16860:46;16925:12;;;27385:245::o;27637:416::-;27837:2;27851:47;;;17176:2;27822:18;;;34568:19;17212:34;34608:14;;;17192:55;-1:-1;;;17267:12;;;17260:25;17304:12;;;27808:245::o;28060:416::-;28260:2;28274:47;;;17555:1;28245:18;;;34568:19;-1:-1;;;34608:14;;;17570:31;17620:12;;;28231:245::o;28483:416::-;28683:2;28697:47;;;17871:2;28668:18;;;34568:19;-1:-1;;;34608:14;;;17887:36;17942:12;;;28654:245::o;28906:416::-;29106:2;29120:47;;;18193:2;29091:18;;;34568:19;18229:31;34608:14;;;18209:52;18280:12;;;29077:245::o;29329:416::-;29529:2;29543:47;;;18531:2;29514:18;;;34568:19;-1:-1;;;34608:14;;;18547:43;18609:12;;;29500:245::o;29752:416::-;29952:2;29966:47;;;18860:2;29937:18;;;34568:19;18896:34;34608:14;;;18876:55;-1:-1;;;18951:12;;;18944:34;18997:12;;;29923:245::o;30175:416::-;30375:2;30389:47;;;19248:2;30360:18;;;34568:19;19284:34;34608:14;;;19264:55;-1:-1;;;19339:12;;;19332:46;19397:12;;;30346:245::o;30598:222::-;19494:37;;;30725:2;30710:18;;30696:124::o;30827:333::-;19494:37;;;-1:-1;;;;;35599:54;31146:2;31131:18;;12435:37;30982:2;30967:18;;30953:207::o;31167:848::-;;19524:5;19501:3;19494:37;37604:24;31645:2;31634:9;31630:18;14886:58;31472:3;31682:2;31671:9;31667:18;31660:48;31722:108;31472:3;31461:9;31457:19;31816:6;31722:108;:::i;:::-;-1:-1;;;;;35599:54;;;;31917:2;31902:18;;12304:58;-1:-1;32000:3;31985:19;19494:37;31714:116;31443:572;-1:-1;;;31443:572::o;32022:333::-;19494:37;;;32341:2;32326:18;;19494:37;32177:2;32162:18;;32148:207::o;33201:256::-;33263:2;33257:9;33289:17;;;33364:18;33349:34;;33385:22;;;33346:62;33343:2;;;33421:1;;33411:12;33343:2;33263;33430:22;33241:216;;-1:-1;33241:216::o;33464:304::-;;33623:18;33615:6;33612:30;33609:2;;;-1:-1;;33645:12;33609:2;-1:-1;33690:4;33678:17;;;33743:15;;33546:222::o;37884:268::-;37949:1;37956:101;37970:6;37967:1;37964:13;37956:101;;;38037:11;;;38031:18;38018:11;;;38011:39;37992:2;37985:10;37956:101;;;38072:6;38069:1;38066:13;38063:2;;;-1:-1;;37949:1;38119:16;;38112:27;37933:219::o;38265:117::-;-1:-1;;;;;35599:54;;38324:35;;38314:2;;38373:1;;38363:12;38795:117;-1:-1;;;;;38882:5;35483:42;38857:5;38854:35;38844:2;;38903:1;;38893:12

Swarm Source

ipfs://2cc665dcacfc29171dd8e76b08827b74a5e701ef6bd4e33f774cba5f4e542d01

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.