ETH Price: $2,419.65 (-2.85%)

Transaction Decoder

Block:
17432285 at Jun-08-2023 01:17:35 AM +UTC
Transaction Fee:
0.001276746371966793 ETH $3.09
Gas Used:
70,629 Gas / 18.076800917 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x391AD1e4...d20952810
5.90809619468465674 Eth
Nonce: 922
5.906819448312689947 Eth
Nonce: 923
0.001276746371966793
(eth-builder)
32.117416696956532795 Eth32.117437885656532795 Eth0.0000211887

Execution Trace

0x77174578e370c9402fe2930a3d80c777ac77cb81.d80aea15( )
  • JIANGLI.balanceOf( account=0x8138B5BD26031878705D514b23b06735D993E536 ) => ( 0 )
  • ETH 0.22 UniswapV2Router02.swapETHForExactTokens( amountOut=2000000000000, path=[0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, 0x5a2cC1a9cD9a8639D5AA6bd96C7DEF72A740771F], to=0x8138B5BD26031878705D514b23b06735D993E536, deadline=1686187055 )
    • UniswapV2Pair.STATICCALL( )
      File 1 of 3: JIANGLI
      //TG: https://t.me/Jiangli_ETH
      //WEB: https://Jiangli.io
      //TWITTER: https://twitter.com/JiangliEth
      
      pragma solidity ^0.8.19;
      // SPDX-License-Identifier: Unlicensed
      interface IERC20 {
      
          function totalSupply() external view returns (uint256);
      
          function balanceOf(address account) external view returns (uint256);
      
          function transfer(address recipient, uint256 amount) external returns (bool);
      
          function allowance(address owner, address spender) external view returns (uint256);
      
          function approve(address spender, uint256 amount) external returns (bool);
      
          function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
      
          event Transfer(address indexed from, address indexed to, uint256 value);
      
          event Approval(address indexed owner, address indexed spender, uint256 value);
      }
      
      
      library SafeMathInt {
          int256 private constant MIN_INT256 = int256(1) << 255;
          int256 private constant MAX_INT256 = ~(int256(1) << 255);
      
          function mul(int256 a, int256 b) internal pure returns (int256) {
              int256 c = a * b;
              // Detect overflow when multiplying MIN_INT256 with -1
              require(c != MIN_INT256 || (a & MIN_INT256) != (b & MIN_INT256));
              require((b == 0) || (c / b == a));
              return c;
          }
      
          function div(int256 a, int256 b) internal pure returns (int256) {
              // Prevent overflow when dividing MIN_INT256 by -1
              require(b != - 1 || a != MIN_INT256);
              // Solidity already throws when dividing by 0.
              return a / b;
          }
      
          function sub(int256 a, int256 b) internal pure returns (int256) {
              int256 c = a - b;
              require((b >= 0 && c <= a) || (b < 0 && c > a));
              return c;
          }
      
          function add(int256 a, int256 b) internal pure returns (int256) {
              int256 c = a + b;
              require((b >= 0 && c >= a) || (b < 0 && c < a));
              return c;
          }
      
          function abs(int256 a) internal pure returns (int256) {
              require(a != MIN_INT256);
              return a < 0 ? - a : a;
          }
      
          function toUint256Safe(int256 a) internal pure returns (uint256) {
              require(a >= 0);
              return uint256(a);
          }
      }
      
      library SafeMathUint {
          function toInt256Safe(uint256 a) internal pure returns (int256) {
              int256 b = int256(a);
              require(b >= 0);
              return b;
          }
      }
      
      library SafeMath {
      
          function add(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 c = a + b;
              require(c >= a, "SafeMath: addition overflow");
      
              return c;
          }
      
          function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              return sub(a, b, "SafeMath: subtraction overflow");
          }
      
          function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b <= a, errorMessage);
              uint256 c = a - b;
      
              return c;
          }
      
          function mul(uint256 a, uint256 b) internal pure returns (uint256) {
              if (a == 0) {
                  return 0;
              }
      
              uint256 c = a * b;
              require(c / a == b, "SafeMath: multiplication overflow");
      
              return c;
          }
      
          function div(uint256 a, uint256 b) internal pure returns (uint256) {
              return div(a, b, "SafeMath: division by zero");
          }
      
          function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b > 0, errorMessage);
              uint256 c = a / b;
      
              return c;
          }
      
          function mod(uint256 a, uint256 b) internal pure returns (uint256) {
              return mod(a, b, "SafeMath: modulo by zero");
          }
      
          function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
          }
      }
      
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
      
          function _msgData() internal view virtual returns (bytes memory) {
              this;
              return msg.data;
          }
      }
      
      library Address {
      
          function isContract(address account) internal view returns (bool) {
              bytes32 codehash;
              bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
              // solhint-disable-next-line no-inline-assembly
              assembly {codehash := extcodehash(account)}
              return (codehash != accountHash && codehash != 0x0);
          }
      
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
      
              (bool success,) = recipient.call{value : amount}("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
      
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCall(target, data, "Address: low-level call failed");
          }
      
          function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              return _functionCallWithValue(target, data, 0, errorMessage);
          }
      
          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");
          }
      
          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");
      
          
              (bool success, bytes memory returndata) = target.call{value : weiValue}(data);
              if (success) {
                  return returndata;
              } else {
      
                  if (returndata.length > 0) {
       
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      
      contract Ownable is Context {
          address private _owner;
      
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
      
          constructor () {
              address msgSender = _msgSender();
              _owner = msgSender;
              emit OwnershipTransferred(address(0), msgSender);
          }
      
          function owner() public view returns (address) {
              return _owner;
          }
      
          modifier onlyOwner() {
              require(_owner == _msgSender(), "Ownable: caller is not the owner");
              _;
          }
      
          function renounceOwnership() public virtual onlyOwner {
              emit OwnershipTransferred(_owner, address(0));
              _owner = address(0);
          }
      
          function transferOwnership(address newOwner) public virtual onlyOwner {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              emit OwnershipTransferred(_owner, newOwner);
              _owner = newOwner;
          }
      
      }
      
      interface IUniswapV2Factory {
          function createPair(address tokenA, address tokenB) external returns (address pair);
      }
      
      interface IUniswapV2Router02 {
          function swapExactTokensForETHSupportingFeeOnTransferTokens(
              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 factory() external pure returns (address);
      
          function WETH() external pure returns (address);
      
          function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
          function getAmountsIn(uint amountOut, address[] memory path) external view returns (uint[] memory amounts);
      }
      
      
      contract JIANGLI is Context, IERC20, Ownable {
          using SafeMath for uint256;
          using Address for address;
      
          event HolderBuySell(address holder, string actionType, uint256 ethAmount, uint256 ethBalance);
          
          event SwapAndLiquifyEnabledUpdated(bool enabled);
          event SwapAndLiquify(
              uint256 tokensSwapped,
              uint256 ethReceived,
              uint256 tokensIntoLiqudity
          );
      
          modifier lockTheSwap {
              inSwapAndLiquify = true;
              _;
              inSwapAndLiquify = false;
          }
          IUniswapV2Router02 public uniswapV2Router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
          address public uniswapV2Pair = address(0);
          mapping(address => uint256) private _balances;
          mapping(address => mapping(address => uint256)) private _allowances;
          mapping(address => bool) private botWallets;
          mapping(address => bool) private _isExcludedFromFee;
          mapping(address => bool) private _isExcludedFromRewards;
          string private _name = "JIANGLI";
          string private _symbol = "JIANGLI";
          uint8 private _decimals = 9;
          uint256 private _tTotal = 100_000 * 10 ** _decimals;
          bool inSwapAndLiquify;
          bool public swapAndLiquifyEnabled = true;
          uint256 public ethPriceToSwap = .4 ether;
          uint256 public highSellFeeSwapAmount = 3 ether;
          uint256 public _maxWalletAmount = 2000 * 10 ** _decimals;
          address public jTreasuryAddress = 0x9FEa6FF2FadB0Bad098754489cBB1Acc2F468A80;
          address developmentAddress = 0xee093e11A09E3C4B9667860F83f46346FA8B75BE;
          address public deadWallet = address(0xdead);
          uint256 public gasForProcessing = 50000;
          event ProcessedDividendTracker(uint256 iterations, uint256 claims, uint256 lastProcessedIndex, bool indexed automatic, uint256 gas, address indexed processor);
          event SendDividends(uint256 EthAmount);
          IterableMapping private holderBalanceMap = new IterableMapping();
          
          struct Distribution {
              uint256 jTreasury;
              uint256 development;
              uint256 jiangliDividend;
          }
      
          struct TaxFees {
              uint256 buyFee;
              uint256 sellFee;
              uint256 highSellFee;
          }
      
          TaxFees public taxFees;
          DividendTracker public dividendTracker;
          Distribution public distribution = Distribution(50,50,0);
      
          constructor () {
              _balances[_msgSender()] = _tTotal;
              _isExcludedFromFee[owner()] = true;
              _isExcludedFromRewards[owner()] = true;
              _isExcludedFromRewards[deadWallet] = true;
              uniswapV2Pair = IUniswapV2Factory(uniswapV2Router.factory()).createPair(address(this), uniswapV2Router.WETH());
              _isExcludedFromRewards[uniswapV2Pair] = true;
              taxFees = TaxFees(30, 35, 35);
              emit Transfer(address(0), _msgSender(), _tTotal);
          }
      
          function name() public view returns (string memory) {
              return _name;
          }
      
          function symbol() public view returns (string memory) {
              return _symbol;
          }
      
          function decimals() public view returns (uint8) {
              return _decimals;
          }
      
          function totalSupply() public view override returns (uint256) {
              return _tTotal;
          }
      
          function balanceOf(address account) public view override returns (uint256) {
              return _balances[account];
          }
      
          function transfer(address recipient, uint256 amount) public override returns (bool) {
              _transfer(_msgSender(), recipient, amount);
              return true;
          }
      
          function allowance(address owner, address spender) public view override returns (uint256) {
              return _allowances[owner][spender];
          }
      
          function approve(address spender, uint256 amount) public override returns (bool) {
              _approve(_msgSender(), spender, amount);
              return true;
          }
      
          function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
              _transfer(sender, recipient, amount);
              _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
              return true;
          }
      
          function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
              _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
              return true;
          }
      
          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;
          }
      
          function ethHolderBalance(address account) public view returns (uint) {
              return holderBalanceMap.get(account);
          }
          function setMaxWalletAmount(uint256 maxWalletAmount) external onlyOwner() {
              _maxWalletAmount = maxWalletAmount * 10 ** 9;
          }
      
          function excludeIncludeFromFee(address[] calldata addresses, bool isExcludeFromFee) public onlyOwner {
              addRemoveFee(addresses, isExcludeFromFee);
          }
      
          function excludeIncludeFromRewards(address[] calldata addresses, bool isExcluded) public onlyOwner {
              addRemoveRewards(addresses, isExcluded);
          }
      
          function isExcludedFromRewards(address addr) public view returns (bool) {
              return _isExcludedFromRewards[addr];
          }
      
          function addRemoveRewards(address[] calldata addresses, bool flag) private {
              for (uint256 i = 0; i < addresses.length; i++) {
                  address addr = addresses[i];
                  _isExcludedFromRewards[addr] = flag;
              }
          }
      
          function setEthPriceToSwap(uint256 ethPriceToSwap_) external onlyOwner {
              ethPriceToSwap = ethPriceToSwap_;
          }
      
          function setHighSellFeeSwapAmount(uint256 ethAmount) external onlyOwner {
              highSellFeeSwapAmount = ethAmount;
          }
      
          function addRemoveFee(address[] calldata addresses, bool flag) private {
              for (uint256 i = 0; i < addresses.length; i++) {
                  address addr = addresses[i];
                  _isExcludedFromFee[addr] = flag;
              }
          }
      
          function setTaxFees(uint256 buyFee, uint256 sellFee, uint256 highSellFee) external onlyOwner {
              taxFees.buyFee = buyFee;
              taxFees.sellFee = sellFee;
              taxFees.highSellFee = highSellFee;
          }
      
          function isAddressBlocked(address addr) public view returns (bool) {
              return botWallets[addr];
          }
      
          function blockAddresses(address[] memory addresses) external onlyOwner() {
              blockUnblockAddress(addresses, true);
          }
      
          function unblockAddresses(address[] memory addresses) external onlyOwner() {
              blockUnblockAddress(addresses, false);
          }
      
          function blockUnblockAddress(address[] memory addresses, bool doBlock) private {
              for (uint256 i = 0; i < addresses.length; i++) {
                  address addr = addresses[i];
                  if (doBlock) {
                      botWallets[addr] = true;
                  } else {
                      delete botWallets[addr];
                  }
              }
          }
      
          function setSwapAndLiquifyEnabled(bool _enabled) public onlyOwner {
              swapAndLiquifyEnabled = _enabled;
              emit SwapAndLiquifyEnabledUpdated(_enabled);
          }
      
          receive() external payable {}
      
          function getTokenAmountByEthPrice() public view returns (uint256)  {
              address[] memory path = new address[](2);
              path[0] = uniswapV2Router.WETH();
              path[1] = address(this);
              return uniswapV2Router.getAmountsOut(ethPriceToSwap, path)[1];
          }
      
          function isExcludedFromFee(address account) public view returns (bool) {
              return _isExcludedFromFee[account];
          }
      
          function _approve(address owner, address spender, uint256 amount) private {
              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);
          }
      
          function _transfer(address from, address to, uint256 amount) private {
              require(from != address(0), "ERC20: transfer from the zero address");
              require(to != address(0), "ERC20: transfer to the zero address");
              require(amount > 0, "Transfer amount must be greater than zero");
              bool takeFees = !_isExcludedFromFee[from] && !_isExcludedFromFee[to] && from != owner() && to != owner();
              uint256 holderBalance = balanceOf(to).add(amount);
              uint256 taxAmount = 0;
              //block the bots, but allow them to transfer to dead wallet if they are blocked
              if (from != owner() && to != owner() && to != deadWallet && from != address(this) && to != address(this)) {
                  require(!botWallets[from] && !botWallets[to], "bots are not allowed to sell or transfer tokens");
      
                  if (from == uniswapV2Pair) {
                      require(holderBalance <= _maxWalletAmount, "Wallet cannot exceed max Wallet limit");
                      taxAmount = takeFees ? amount.mul(taxFees.buyFee).div(100) :  0;
                      uint ethBuy = getEthValueFromTokens(amount);
                      uint newBalance = holderBalanceMap.get(to).add(ethBuy);
                      holderBalanceMap.set(to, newBalance);
                      emit HolderBuySell(to, "BUY", ethBuy,  newBalance);
                  }
                  if (from != uniswapV2Pair && to == uniswapV2Pair) {
                      taxAmount = takeFees ? amount.mul(taxFees.sellFee).div(100) : 0;
                      uint ethSell = getEthValueFromTokens(amount);
                      if(taxAmount > 0 && ethSell > highSellFeeSwapAmount) {
                          taxAmount = taxFees.highSellFee;
                      }
                      int val = int(holderBalanceMap.get(from)) - int(ethSell);
                      uint256 newBalance = val <= 0 ? 0 : uint256(val);
                      holderBalanceMap.set(from, newBalance);
                      emit HolderBuySell(from, "SELL", ethSell,  newBalance);
                      swapTokens();
                  }
                  if (from != uniswapV2Pair && to != uniswapV2Pair) {
                      require(holderBalance <= _maxWalletAmount, "Wallet cannot exceed max Wallet limit");
                  }
      
                  try dividendTracker.setTokenBalance(from) {} catch{}
                  try dividendTracker.setTokenBalance(to) {} catch{}
                  try dividendTracker.process(gasForProcessing) returns (uint256 iterations, uint256 claims, uint256 lastProcessedIndex) {
                      emit ProcessedDividendTracker(iterations, claims, lastProcessedIndex, true, gasForProcessing, tx.origin);
                  }catch {}
              }
              uint256 transferAmount = amount.sub(taxAmount);
              _balances[from] = _balances[from].sub(amount);
              _balances[to] = _balances[to].add(transferAmount);
              _balances[address(this)] = _balances[address(this)].add(taxAmount);
              emit Transfer(from, to, amount);
          }
      
          function airDrops(address[] calldata holders, uint256[] calldata amounts) external onlyOwner {
              require(holders.length == amounts.length, "Holders and amounts must be the same count");
              address from = _msgSender();
              for(uint256 i=0; i < holders.length; i++) {
                  address to = holders[i];
                  uint256 amount = amounts[i];
                  _balances[from] = _balances[from].sub(amount);
                  _balances[to] = _balances[to].add(amount);
                  emit Transfer(from, to, amount);
              }
          }
      
          function swapTokens() private {
              uint256 contractTokenBalance = balanceOf(address(this));
              if (contractTokenBalance > 0) {
                  uint256 tokenAmount = getTokenAmountByEthPrice();
                  if (contractTokenBalance >= tokenAmount && !inSwapAndLiquify && swapAndLiquifyEnabled) {
                      //send eth to wallets investment and dev
                      swapTokensForEth(tokenAmount);
                      distributeShares();
                  }
              }
          }
      
          function getEthValueFromTokens(uint tokenAmount) public view returns (uint)  {
              address[] memory path = new address[](2);
              path[0] = uniswapV2Router.WETH();
              path[1] = address(this);
              return uniswapV2Router.getAmountsIn(tokenAmount, path)[0];
          }
      
          function updateGasForProcessing(uint256 newValue) public onlyOwner {
              require(newValue != gasForProcessing, "Cannot update gasForProcessing to same value");
              gasForProcessing = newValue;
          }
      
          function distributeShares() private lockTheSwap {
              uint256 ethBalance = address(this).balance;
              uint256 jTreasury = ethBalance.mul(distribution.jTreasury).div(100);
              uint256 development = ethBalance.mul(distribution.development).div(100);
              uint256 jiangliDividend = ethBalance.mul(distribution.jiangliDividend).div(100);
              
              payable(jTreasuryAddress).transfer(jTreasury);
              payable(developmentAddress).transfer(development);
              sendEthDividends(jiangliDividend);
          }
      
          function manualSwap() external {
              uint256 contractTokenBalance = balanceOf(address(this));
              if (contractTokenBalance > 0) {
                  if (!inSwapAndLiquify) {
                      swapTokensForEth(contractTokenBalance);
                      distributeShares();
                  }
              }
          }
      
          function setDistribution(uint256 jTreasury, uint256 development, uint256 jiangliDividend) external onlyOwner {
              distribution.jTreasury = jTreasury;
              distribution.development = development;
              distribution.jiangliDividend = jiangliDividend;
          }
      
          function setDividendTracker(address dividendContractAddress) external onlyOwner {
              dividendTracker = DividendTracker(payable(dividendContractAddress));
          }
      
          function sendEthDividends(uint256 dividends) private {
              (bool success,) = address(dividendTracker).call{value : dividends}("");
              if (success) {
                  emit SendDividends(dividends);
              }
          }
      
          function removeEthFromContract() external onlyOwner {
              uint256 ethBalance = address(this).balance;
              payable(owner()).transfer(ethBalance);
          }
      
          function swapTokensForEth(uint256 tokenAmount) private {
              // generate the uniswap pair path of token -> weth
              address[] memory path = new address[](2);
              path[0] = address(this);
              path[1] = uniswapV2Router.WETH();
              _approve(address(this), address(uniswapV2Router), tokenAmount);
              // make the swap
              uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
                  tokenAmount,
                  0, // accept any amount of ETH
                  path,
                  address(this),
                  block.timestamp
              );
          }
      }
      
      contract IterableMapping {
          // Iterable mapping from address to uint;
          struct Map {
              address[] keys;
              mapping(address => uint) values;
              mapping(address => uint) indexOf;
              mapping(address => bool) inserted;
          }
      
          Map private map;
      
          function get(address key) public view returns (uint) {
              return map.values[key];
          }
      
          function keyExists(address key) public view returns (bool) {
              return (getIndexOfKey(key) != - 1);
          }
      
          function getIndexOfKey(address key) public view returns (int) {
              if (!map.inserted[key]) {
                  return - 1;
              }
              return int(map.indexOf[key]);
          }
      
          function getKeyAtIndex(uint index) public view returns (address) {
              return map.keys[index];
          }
      
          function size() public view returns (uint) {
              return map.keys.length;
          }
      
          function set(address key, uint val) public {
              if (map.inserted[key]) {
                  map.values[key] = val;
              } else {
                  map.inserted[key] = true;
                  map.values[key] = val;
                  map.indexOf[key] = map.keys.length;
                  map.keys.push(key);
              }
          }
      
          function remove(address key) public {
              if (!map.inserted[key]) {
                  return;
              }
              delete map.inserted[key];
              delete map.values[key];
              uint index = map.indexOf[key];
              uint lastIndex = map.keys.length - 1;
              address lastKey = map.keys[lastIndex];
              map.indexOf[lastKey] = index;
              delete map.indexOf[key];
              map.keys[index] = lastKey;
              map.keys.pop();
          }
      }
      
      contract DividendTracker is IERC20, Context, Ownable {
          using SafeMath for uint256;
          using SafeMathUint for uint256;
          using SafeMathInt for int256;
          uint256 constant internal magnitude = 2 ** 128;
          uint256 internal magnifiedDividendPerShare;
          mapping(address => int256) internal magnifiedDividendCorrections;
          mapping(address => uint256) internal withdrawnDividends;
          mapping(address => uint256) internal claimedDividends;
          mapping(address => uint256) private _balances;
          mapping(address => mapping(address => uint256)) private _allowances;
          uint256 private _totalSupply;
          string private _name = "JIANGLI REWARDS";
          string private _symbol = "JIANGLIREWARDS";
          uint8 private _decimals = 9;
          uint256 public totalDividendsDistributed;
          IterableMapping private tokenHoldersMap = new IterableMapping();
          JIANGLI private jiangli;
      
          event updateBalance(address addr, uint256 amount);
          event DividendsDistributed(address indexed from, uint256 weiAmount);
          event DividendWithdrawn(address indexed to, uint256 weiAmount);
      
          uint256 public lastProcessedIndex;
          mapping(address => uint256) public lastClaimTimes;
          uint256 public claimWait = 3600;
      
          event ExcludeFromDividends(address indexed account);
          event ClaimWaitUpdated(uint256 indexed newValue, uint256 indexed oldValue);
          event Claim(address indexed account, uint256 amount, bool indexed automatic);
          IUniswapV2Router02 uniswapV2Router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
          IERC20 public jiangliToken = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7); //USDT
      
          struct EthDividendTiers {
              uint tier7;
              uint tier6;
              uint tier5;
              uint tier4;
              uint tier3;
              uint tier2;
              uint tier1;
          }
      
          struct TierLevels {
              uint level1;
              uint level2;
              uint level3;
              uint level4;
              uint level5;
              uint level6;
              uint level7;
      
          }
          EthDividendTiers public ethDividendTiers;
          TierLevels public tierLevels;
          constructor() {
      
              ethDividendTiers = EthDividendTiers(
                  8 ether,
                  4 ether,
                  2 ether,
                  1 ether,
                  .5 ether,
                  .25 ether,
                  .1 ether);
      
              tierLevels = TierLevels(
                  1 ether,
                  2 ether,
                  3 ether,
                  4 ether,
                  5 ether,
                  6 ether,
                  8 ether);
          }
      
          function name() public view returns (string memory) {
              return _name;
          }
      
          function symbol() public view returns (string memory) {
              return _symbol;
          }
      
          function decimals() public view returns (uint8) {
              return _decimals;
          }
      
          function totalSupply() public view override returns (uint256) {
              return _totalSupply;
          }
      
          function balanceOf(address account) public view virtual override returns (uint256) {
              return _balances[account];
          }
      
          function updateTierLevels( TierLevels memory _tierLevels) external onlyOwner {
              tierLevels = _tierLevels;
      
          }
      
          function updateEthDividendTier(EthDividendTiers memory _dividendTiers) external onlyOwner {
              ethDividendTiers = _dividendTiers;
          }
      
          function getEthTier(uint256 amount) public view returns (uint, string memory) {
              uint tierLevel = 0;
              string memory tier = "Not Eligible";
              if(amount >= ethDividendTiers.tier1) {
                  tierLevel = .1 ether;
                  tier = "Fortune Seeker";
              } 
              if(amount >= ethDividendTiers.tier2) {
                  tierLevel = .25 ether;
                  tier = "Abundance Enthusiast";
              } 
              if(amount >= ethDividendTiers.tier3) {
                  tierLevel = .5 ether;
                  tier = "Prosperity Advocate";
              } 
              if(amount >= ethDividendTiers.tier4) {
                  tierLevel = 1 ether;
                  tier = "Weahlth Architect";
              } 
              if(amount >= ethDividendTiers.tier5) {
                  tierLevel = 2 ether;
                  tier = "Bounty Seeker";
              } 
              if(amount >= ethDividendTiers.tier6) {
                  tierLevel = 4 ether;
                  tier = "Virtuous Benefactor";
              } 
              if(amount >= ethDividendTiers.tier7) {
                  tierLevel = 8 ether;
                  tier = "Sumpreme Luminary";
              } 
              return (tierLevel, tier);
          }
      
          function transfer(address, uint256) public pure returns (bool) {
              require(false, "No transfers allowed in dividend tracker");
              return true;
          }
      
          function transferFrom(address, address, uint256) public pure override returns (bool) {
              require(false, "No transfers allowed in dividend tracker");
              return true;
          }
      
          function allowance(address owner, address spender) public view override returns (uint256) {
              return _allowances[owner][spender];
          }
      
          function approve(address spender, uint256 amount) public virtual override returns (bool) {
              _approve(_msgSender(), spender, amount);
              return true;
          }
      
          function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
              _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
              return true;
          }
      
          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;
          }
      
          function _approve(address owner, address spender, uint256 amount) private {
              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);
          }
      
          function setTokenBalance(address account) public {
              uint256 balance = jiangli.ethHolderBalance(account);
              if (!jiangli.isExcludedFromRewards(account)) {
                  (uint tierLevel,) = getEthTier(balance);
                  if (tierLevel > 0) {
                      _setBalance(account, tierLevel);
                      tokenHoldersMap.set(account, tierLevel);
                  }
                  else {
                      _setBalance(account, 0);
                      tokenHoldersMap.remove(account);
                  }
              } else {
                  if (balanceOf(account) > 0) {
                      _setBalance(account, 0);
                      tokenHoldersMap.remove(account);
                  }
              }
              processAccount(payable(account), true);
          }
      
          function updateTokenBalances(address[] memory accounts) external {
              uint256 index = 0;
              while (index < accounts.length) {
                  setTokenBalance(accounts[index]);
                  index += 1;
              }
          }
      
          function _mint(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: mint to the zero address");
              _totalSupply = _totalSupply.add(amount);
              _balances[account] = _balances[account].add(amount);
              emit Transfer(address(0), account, amount);
              magnifiedDividendCorrections[account] = magnifiedDividendCorrections[account]
              .sub((magnifiedDividendPerShare.mul(amount)).toInt256Safe());
          }
      
          function _burn(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: burn from the zero address");
      
              _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
              _totalSupply = _totalSupply.sub(amount);
              emit Transfer(account, address(0), amount);
      
              magnifiedDividendCorrections[account] = magnifiedDividendCorrections[account]
              .add((magnifiedDividendPerShare.mul(amount)).toInt256Safe());
          }
      
          receive() external payable {
              distributeDividends();
          }
      
          function setERC20Contract(address contractAddr) external onlyOwner {
              jiangli = JIANGLI(payable(contractAddr));
          }
      
          function excludeFromDividends(address account) external onlyOwner {
              _setBalance(account, 0);
              tokenHoldersMap.remove(account);
              emit ExcludeFromDividends(account);
          }
      
          function distributeDividends() public payable {
              require(totalSupply() > 0);
              uint256 initialBalance = jiangliToken.balanceOf(address(this));
              swapEthForJIANGLI(msg.value);
              uint256 newBalance = jiangliToken.balanceOf(address(this)).sub(initialBalance);
              if (newBalance > 0) {
                  magnifiedDividendPerShare = magnifiedDividendPerShare.add(
                      (newBalance).mul(magnitude) / totalSupply()
                  );
                  emit DividendsDistributed(msg.sender, newBalance);
                  totalDividendsDistributed = totalDividendsDistributed.add(newBalance);
              }
          }
      
          function swapEthForJIANGLI(uint256 ethAmount) public {
              // generate the uniswap pair path of weth -> eth
              address[] memory path = new address[](2);
              path[0] = uniswapV2Router.WETH();
              path[1] = address(jiangliToken);
      
              // make the swap
              uniswapV2Router.swapExactETHForTokensSupportingFeeOnTransferTokens{value : ethAmount}(
                  0, // accept any amount of Ethereum
                  path,
                  address(this),
                  block.timestamp
              );
          }
      
      
          function withdrawDividend() public virtual {
              _withdrawDividendOfUser(payable(msg.sender));
          }
      
          function _withdrawDividendOfUser(address payable user) internal returns (uint256) {
              uint256 _withdrawableDividend = withdrawableDividendOf(user);
              if (_withdrawableDividend > 0) {
                  withdrawnDividends[user] = withdrawnDividends[user].add(_withdrawableDividend);
                  emit DividendWithdrawn(user, _withdrawableDividend);
                  jiangliToken.transfer(user, _withdrawableDividend);
                  return _withdrawableDividend;
              }
              return 0;
          }
      
          function dividendOf(address _owner) public view returns (uint256) {
              return withdrawableDividendOf(_owner);
          }
      
          function withdrawableDividendOf(address _owner) public view returns (uint256) {
              return accumulativeDividendOf(_owner).sub(withdrawnDividends[_owner]);
          }
      
          function withdrawnDividendOf(address _owner) public view returns (uint256) {
              return withdrawnDividends[_owner];
          }
      
          function accumulativeDividendOf(address _owner) public view returns (uint256) {
              return magnifiedDividendPerShare.mul(balanceOf(_owner)).toInt256Safe()
              .add(magnifiedDividendCorrections[_owner]).toUint256Safe() / magnitude;
          }
      
      
          function updateClaimWait(uint256 newClaimWait) external onlyOwner {
              require(newClaimWait >= 3600 && newClaimWait <= 86400, "ClaimWait must be updated to between 1 and 24 hours");
              require(newClaimWait != claimWait, "Cannot update claimWait to same value");
              emit ClaimWaitUpdated(newClaimWait, claimWait);
              claimWait = newClaimWait;
          }
      
          function getLastProcessedIndex() external view returns (uint256) {
              return lastProcessedIndex;
          }
      
          function getNumberOfTokenHolders() external view returns (uint256) {
              return tokenHoldersMap.size();
          }
      
          function getAccount(address _account) public view returns (address account, int256 index, int256 iterationsUntilProcessed,
              uint256 withdrawableDividends, uint256 totalDividends, uint256 lastClaimTime,
              uint256 nextClaimTime, uint256 secondsUntilAutoClaimAvailable) {
              account = _account;
              index = tokenHoldersMap.getIndexOfKey(account);
              iterationsUntilProcessed = - 1;
              if (index >= 0) {
                  if (uint256(index) > lastProcessedIndex) {
                      iterationsUntilProcessed = index.sub(int256(lastProcessedIndex));
                  }
                  else {
                      uint256 processesUntilEndOfArray = tokenHoldersMap.size() > lastProcessedIndex ?
                      tokenHoldersMap.size().sub(lastProcessedIndex) : 0;
                      iterationsUntilProcessed = index.add(int256(processesUntilEndOfArray));
                  }
              }
              withdrawableDividends = withdrawableDividendOf(account);
              totalDividends = accumulativeDividendOf(account);
              lastClaimTime = lastClaimTimes[account];
              nextClaimTime = lastClaimTime > 0 ? lastClaimTime.add(claimWait) : 0;
              secondsUntilAutoClaimAvailable = nextClaimTime > block.timestamp ? nextClaimTime.sub(block.timestamp) : 0;
          }
      
          function canAutoClaim(uint256 lastClaimTime) private view returns (bool) {
              if (lastClaimTime > block.timestamp) {
                  return false;
              }
              return block.timestamp.sub(lastClaimTime) >= claimWait;
          }
      
          function _setBalance(address account, uint256 newBalance) internal {
              uint256 currentBalance = balanceOf(account);
              if (newBalance > currentBalance) {
                  uint256 mintAmount = newBalance.sub(currentBalance);
                  _mint(account, mintAmount);
              } else if (newBalance < currentBalance) {
                  uint256 burnAmount = currentBalance.sub(newBalance);
                  _burn(account, burnAmount);
              }
          }
      
          function process(uint256 gas) public returns (uint256, uint256, uint256) {
              uint256 numberOfTokenHolders = tokenHoldersMap.size();
      
              if (numberOfTokenHolders == 0) {
                  return (0, 0, lastProcessedIndex);
              }
              uint256 _lastProcessedIndex = lastProcessedIndex;
              uint256 gasUsed = 0;
              uint256 gasLeft = gasleft();
              uint256 iterations = 0;
              uint256 claims = 0;
              while (gasUsed < gas && iterations < numberOfTokenHolders) {
                  _lastProcessedIndex++;
                  if (_lastProcessedIndex >= tokenHoldersMap.size()) {
                      _lastProcessedIndex = 0;
                  }
                  address account = tokenHoldersMap.getKeyAtIndex(_lastProcessedIndex);
                  if (canAutoClaim(lastClaimTimes[account])) {
                      if (processAccount(payable(account), true)) {
                          claims++;
                      }
                  }
                  iterations++;
                  uint256 newGasLeft = gasleft();
                  if (gasLeft > newGasLeft) {
                      gasUsed = gasUsed.add(gasLeft.sub(newGasLeft));
                  }
                  gasLeft = newGasLeft;
              }
              lastProcessedIndex = _lastProcessedIndex;
              return (iterations, claims, lastProcessedIndex);
          }
      
          function processAccountByDeployer(address payable account, bool automatic) external onlyOwner {
              processAccount(account, automatic);
          }
      
          function airDropJIANGLI(address[] calldata holders, uint256[] calldata amounts) external onlyOwner {
              require(holders.length == amounts.length, "Holders and amounts must be the same count");
              for(uint256 i=0; i < holders.length; i++) {
                  address to = holders[i];
                  uint256 jiangliAmount = amounts[i];
                  jiangliToken.transfer(to, jiangliAmount);
              }
          }
          function totalDividendClaimed(address account) public view returns (uint256) {
              return claimedDividends[account];
          }
      
          function processAccount(address payable account, bool automatic) private returns (bool) {
              uint256 amount = _withdrawDividendOfUser(account);
              if (amount > 0) {
                  uint256 totalClaimed = claimedDividends[account];
                  claimedDividends[account] = amount.add(totalClaimed);
                  lastClaimTimes[account] = block.timestamp;
                  emit Claim(account, amount, automatic);
                  return true;
              }
              return false;
          }
      
          //This should never be used, but available in case of unforseen issues
          function sendEthBack() external onlyOwner {
              uint256 ethBalance = address(this).balance;
              payable(owner()).transfer(ethBalance);
          }
      
          //This should never be used, but available in case of unforseen issues
          function sendJIANGLIBack() external onlyOwner {
              uint256 jiangliBalance = jiangliToken.balanceOf(address(this));
              jiangliToken.transfer(owner(), jiangliBalance);
          }
      
      }

      File 2 of 3: UniswapV2Router02
      pragma solidity =0.6.6;
      
      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;
      }
      
      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;
      }
      
      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);
      }
      
      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;
      }
      
      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 IWETH {
          function deposit() external payable;
          function transfer(address to, uint value) external returns (bool);
          function withdraw(uint) external;
      }
      
      contract UniswapV2Router02 is IUniswapV2Router02 {
          using SafeMath 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 (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {
                  IUniswapV2Factory(factory).createPair(tokenA, tokenB);
              }
              (uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
              if (reserveA == 0 && reserveB == 0) {
                  (amountA, amountB) = (amountADesired, amountBDesired);
              } else {
                  uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB);
                  if (amountBOptimal <= amountBDesired) {
                      require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
                      (amountA, amountB) = (amountADesired, amountBOptimal);
                  } else {
                      uint amountAOptimal = UniswapV2Library.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 = UniswapV2Library.pairFor(factory, tokenA, tokenB);
              TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
              TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
              liquidity = IUniswapV2Pair(pair).mint(to);
          }
          function addLiquidityETH(
              address token,
              uint amountTokenDesired,
              uint amountTokenMin,
              uint amountETHMin,
              address to,
              uint deadline
          ) external virtual override payable ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) {
              (amountToken, amountETH) = _addLiquidity(
                  token,
                  WETH,
                  amountTokenDesired,
                  msg.value,
                  amountTokenMin,
                  amountETHMin
              );
              address pair = UniswapV2Library.pairFor(factory, token, WETH);
              TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);
              IWETH(WETH).deposit{value: amountETH}();
              assert(IWETH(WETH).transfer(pair, amountETH));
              liquidity = IUniswapV2Pair(pair).mint(to);
              // refund dust eth, if any
              if (msg.value > amountETH) TransferHelper.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 = UniswapV2Library.pairFor(factory, tokenA, tokenB);
              IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair
              (uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);
              (address token0,) = UniswapV2Library.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
              );
              TransferHelper.safeTransfer(token, to, amountToken);
              IWETH(WETH).withdraw(amountETH);
              TransferHelper.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 = UniswapV2Library.pairFor(factory, tokenA, tokenB);
              uint value = approveMax ? uint(-1) : liquidity;
              IUniswapV2Pair(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 = UniswapV2Library.pairFor(factory, token, WETH);
              uint value = approveMax ? uint(-1) : liquidity;
              IUniswapV2Pair(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
              );
              TransferHelper.safeTransfer(token, to, IERC20(token).balanceOf(address(this)));
              IWETH(WETH).withdraw(amountETH);
              TransferHelper.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 = UniswapV2Library.pairFor(factory, token, WETH);
              uint value = approveMax ? uint(-1) : liquidity;
              IUniswapV2Pair(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,) = UniswapV2Library.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 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;
                  IUniswapV2Pair(UniswapV2Library.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 = UniswapV2Library.getAmountsOut(factory, amountIn, path);
              require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
              TransferHelper.safeTransferFrom(
                  path[0], msg.sender, UniswapV2Library.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 = UniswapV2Library.getAmountsIn(factory, amountOut, path);
              require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
              TransferHelper.safeTransferFrom(
                  path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
              );
              _swap(amounts, path, to);
          }
          function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
              external
              virtual
              override
              payable
              ensure(deadline)
              returns (uint[] memory amounts)
          {
              require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
              amounts = UniswapV2Library.getAmountsOut(factory, msg.value, path);
              require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
              IWETH(WETH).deposit{value: amounts[0]}();
              assert(IWETH(WETH).transfer(UniswapV2Library.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 = UniswapV2Library.getAmountsIn(factory, amountOut, path);
              require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
              TransferHelper.safeTransferFrom(
                  path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
              );
              _swap(amounts, path, address(this));
              IWETH(WETH).withdraw(amounts[amounts.length - 1]);
              TransferHelper.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 = UniswapV2Library.getAmountsOut(factory, amountIn, path);
              require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
              TransferHelper.safeTransferFrom(
                  path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
              );
              _swap(amounts, path, address(this));
              IWETH(WETH).withdraw(amounts[amounts.length - 1]);
              TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
          }
          function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
              external
              virtual
              override
              payable
              ensure(deadline)
              returns (uint[] memory amounts)
          {
              require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
              amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
              require(amounts[0] <= msg.value, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
              IWETH(WETH).deposit{value: amounts[0]}();
              assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
              _swap(amounts, path, to);
              // refund dust eth, if any
              if (msg.value > amounts[0]) TransferHelper.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,) = UniswapV2Library.sortTokens(input, output);
                  IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.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 = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput);
                  }
                  (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOutput) : (amountOutput, uint(0));
                  address to = i < path.length - 2 ? UniswapV2Library.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) {
              TransferHelper.safeTransferFrom(
                  path[0], msg.sender, UniswapV2Library.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
              virtual
              override
              payable
              ensure(deadline)
          {
              require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
              uint amountIn = msg.value;
              IWETH(WETH).deposit{value: amountIn}();
              assert(IWETH(WETH).transfer(UniswapV2Library.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');
              TransferHelper.safeTransferFrom(
                  path[0], msg.sender, UniswapV2Library.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');
              IWETH(WETH).withdraw(amountOut);
              TransferHelper.safeTransferETH(to, amountOut);
          }
      
          // **** LIBRARY FUNCTIONS ****
          function quote(uint amountA, uint reserveA, uint reserveB) public pure virtual override returns (uint amountB) {
              return UniswapV2Library.quote(amountA, reserveA, reserveB);
          }
      
          function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut)
              public
              pure
              virtual
              override
              returns (uint amountOut)
          {
              return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut);
          }
      
          function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut)
              public
              pure
              virtual
              override
              returns (uint amountIn)
          {
              return UniswapV2Library.getAmountIn(amountOut, reserveIn, reserveOut);
          }
      
          function getAmountsOut(uint amountIn, address[] memory path)
              public
              view
              virtual
              override
              returns (uint[] memory amounts)
          {
              return UniswapV2Library.getAmountsOut(factory, amountIn, path);
          }
      
          function getAmountsIn(uint amountOut, address[] memory path)
              public
              view
              virtual
              override
              returns (uint[] memory amounts)
          {
              return UniswapV2Library.getAmountsIn(factory, amountOut, path);
          }
      }
      
      // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
      
      library SafeMath {
          function add(uint x, uint y) internal pure returns (uint z) {
              require((z = x + y) >= x, 'ds-math-add-overflow');
          }
      
          function sub(uint x, uint y) internal pure returns (uint z) {
              require((z = x - y) <= x, 'ds-math-sub-underflow');
          }
      
          function mul(uint x, uint y) internal pure returns (uint z) {
              require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
          }
      }
      
      library UniswapV2Library {
          using SafeMath 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, 'UniswapV2Library: IDENTICAL_ADDRESSES');
              (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
              require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS');
          }
      
          // calculates the CREATE2 address for a pair without making any external calls
          function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
              (address token0, address token1) = sortTokens(tokenA, tokenB);
              pair = address(uint(keccak256(abi.encodePacked(
                      hex'ff',
                      factory,
                      keccak256(abi.encodePacked(token0, token1)),
                      hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash
                  ))));
          }
      
          // fetches and sorts the reserves for a pair
          function getReserves(address factory, address tokenA, address tokenB) internal view returns (uint reserveA, uint reserveB) {
              (address token0,) = sortTokens(tokenA, tokenB);
              (uint reserve0, uint reserve1,) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();
              (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
          }
      
          // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
          function quote(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) {
              require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
              require(reserveA > 0 && reserveB > 0, 'UniswapV2Library: 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, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
              require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: 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, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
              require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: 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, 'UniswapV2Library: 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, 'UniswapV2Library: 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 TransferHelper {
          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))), 'TransferHelper: 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))), 'TransferHelper: 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))), 'TransferHelper: TRANSFER_FROM_FAILED');
          }
      
          function safeTransferETH(address to, uint value) internal {
              (bool success,) = to.call{value:value}(new bytes(0));
              require(success, 'TransferHelper: ETH_TRANSFER_FAILED');
          }
      }

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