ETH Price: $2,427.18 (+0.48%)

Transaction Decoder

Block:
18317405 at Oct-10-2023 03:35:11 AM +UTC
Transaction Fee:
0.001762008 ETH $4.28
Gas Used:
146,834 Gas / 12 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x142a774E...5acB646D0
(beaverbuild)
9.11843528692171196 Eth9.119440018462865794 Eth0.001004731541153834
0x955bdf89...C6ECEe025
0.0125 Eth
Nonce: 77
0.010737992 Eth
Nonce: 78
0.001762008

Execution Trace

SaitaRealtyV2.transfer( recipient=0x75e89d5979E4f6Fba9F97c104c2F0AFB3F1dcB88, amount=4545942850000000 ) => ( True )
  • SaitaSwapRouter.STATICCALL( )
  • SaitaSwapRouter.getAmountsOut( amountIn=8202337318629023, path=[0x142a774E8b52550E88E196CedD7A5835acB646D0, 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, 0xdAC17F958D2ee523a2206206994597C13D831ec7] ) => ( amounts=[8202337318629023, 754965955492352244, 655673520] )
    • SaitaSwapPair.STATICCALL( )
    • SaitaSwapPair.STATICCALL( )
      transfer[SaitaRealtyV2 (ln:192)]
      File 1 of 4: SaitaRealtyV2
      // SPDX-License-Identifier: NOLICENSE
      pragma solidity ^0.8.10;
      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);
      }
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
              return msg.data;
          }
      }
      abstract contract Ownable is Context {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          constructor() {
              _setOwner(_msgSender());
          }
          function owner() public view virtual returns (address) {
              return _owner;
          }
          modifier onlyOwner() {
              require(owner() == _msgSender(), "Ownable: caller is not the owner");
              _;
          }
          function renounceOwnership() public virtual onlyOwner {
              _setOwner(address(0));
          }
          function transferOwnership(address newOwner) public virtual onlyOwner {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              _setOwner(newOwner);
          }
          function _setOwner(address newOwner) internal {
              address oldOwner = _owner;
              _owner = newOwner;
              emit OwnershipTransferred(oldOwner, newOwner);
          }
      }
      interface IFactory{
              function createPair(address tokenA, address tokenB) external returns (address pair);
      }
      interface IRouter {
          function factory() external pure returns (address);
          function WETH() external pure returns (address);
          function addTreasuryETH(
              address token,
              uint amountTokenDesired,
              uint amountTokenMin,
              uint amountETHMin,
              address to,
              uint deadline
          ) external payable returns (uint amountToken, uint amountETH, uint treasury);
          function swapExactTokensForETHSupportingFeeOnTransferTokens(
              uint amountIn,
              uint amountOutMin,
              address[] calldata path,
              address to,
              uint deadline) external;
          function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
      }
      contract SaitaRealtyV2 is IERC20, Ownable {
          mapping(address => uint256) private _rOwned;
          mapping(address => uint256) private _tOwned;
          mapping(address => mapping(address => uint256)) private _allowances;
          mapping(address => bool) private _isExcludedFromFee;
          mapping(address => bool) private _isExcluded;
          mapping(address => bool) private _isBot;
          mapping(address => bool) private _isPair;
          address[] private _excluded;
          
          bool private swapping;
          IRouter public router;
          address public pair;
          uint8 private constant _decimals = 9;
          uint256 private constant MAX = ~uint256(0);
          uint256 private _tTotal = 12e10 * 10**_decimals;
          uint256 private _rTotal = (MAX - (MAX % _tTotal));
          
          uint256 public swapTokensAtAmount = 1_000 * 10 ** 6;
          uint256 public maxTxAmount = 100_000_000_000 * 10**_decimals;
          
          // Anti Dump //
          mapping (address => uint256) public _lastTrade;
          bool public coolDownEnabled = true;
          uint256 public coolDownTime = 30 seconds;
          address public capitalAddress = 0x22D5c2837FFB86392C81D3Be0aDe307F81AF10C1;
          address public marketingAddress = 0x2084f438b1EFf6Bd5FbdE57215eaB741CAC7aDb7;
          address public burnAddress = 0x000000000000000000000000000000000000dEaD;
          address public USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
          string private constant _name = "SaitaRealtyV2";
          string private constant _symbol = "SRLTY";
          struct Taxes {
            uint256 reflection;
            uint256 capital;
            uint256 marketing;
            uint256 burn;
            uint256 treasury;
          }
          Taxes private taxes = Taxes(10,10,10,10,50);
          struct TotFeesPaidStruct {
              uint256 reflection;
              uint256 capital;
              uint256 marketing;
              uint256 burn;
              uint256 treasury;
          }
          TotFeesPaidStruct public totFeesPaid;
          struct valuesFromGetValues{
            uint256 rAmount;
            uint256 rTransferAmount;
            uint256 rReflection;
            uint256 rCapital;
            uint256 rMarketing;
            uint256 rBurn;
            uint256 rTreasury;
            uint256 tTransferAmount;
            uint256 tReflection;
            uint256 tCapital;
            uint256 tMarketing;
            uint256 tBurn;
            uint256 tTreasury;
          }
          
          struct splitETHStruct{
              uint256 capital;
              uint256 marketing;
          }
          splitETHStruct private splitETH = splitETHStruct(40,10);
          struct ETHAmountStruct{
              uint256 capital;
              uint256 marketing;
          }
          ETHAmountStruct public ETHAmount;
          event FeesChanged();
          modifier lockTheSwap {
              swapping = true;
              _;
              swapping = false;
          }
          modifier addressValidation(address _addr) {
              require(_addr != address(0), 'SaitaRealty: Zero address');
              _;
          }
          constructor (address routerAddress, address owner_) {
              IRouter _router = IRouter(routerAddress);
              address _pair = IFactory(_router.factory())
                  .createPair(address(this), _router.WETH());
              router = _router;
              pair = _pair;
              
              addPair(pair);
          
              excludeFromReward(pair);
              _setOwner(owner_);
              _rOwned[owner()] = _rTotal;
              _isExcludedFromFee[owner()] = true;
              _isExcludedFromFee[address(this)] = true;
              _isExcludedFromFee[capitalAddress] = true;
              _isExcludedFromFee[burnAddress] = true;
              _isExcludedFromFee[marketingAddress] = true;
              emit Transfer(address(0), owner(), _tTotal);
          }
          function name() public pure returns (string memory) {
              return _name;
          }
          function symbol() public pure returns (string memory) {
              return _symbol;
          }
          function decimals() public pure returns (uint8) {
              return _decimals;
          }
          function totalSupply() public view override returns (uint256) {
              return _tTotal;
          }
          function balanceOf(address account) public view override returns (uint256) {
              if (_isExcluded[account]) return _tOwned[account];
              return tokenFromReflection(_rOwned[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 virtual override returns (bool) {
              uint256 currentAllowance = _allowances[sender][_msgSender()];
              require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
              _transfer(sender, recipient, amount);
              _approve(sender, _msgSender(), currentAllowance - amount);
              return true;
          }
          function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
              _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
              return true;
          }
          function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
              uint256 currentAllowance = _allowances[_msgSender()][spender];
              require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
              _approve(_msgSender(), spender, currentAllowance - subtractedValue);
              return true;
          }
          function isExcludedFromReward(address account) public view returns (bool) {
              return _isExcluded[account];
          }
          function tokenFromReflection(uint256 rAmount) public view returns(uint256) {
              require(rAmount <= _rTotal, "Amount must be less than total reflections");
              uint256 currentRate =  _getRate();
              return rAmount/currentRate;
          }
          function excludeFromReward(address account) public onlyOwner {
              require(!_isExcluded[account], "Account is already excluded");
              require(_excluded.length <= 200, "Invalid length");
              require(account != owner(), "Owner cannot be excluded");
              if(_rOwned[account] > 0) {
                  _tOwned[account] = tokenFromReflection(_rOwned[account]);
              }
              _isExcluded[account] = true;
              _excluded.push(account);
          }
          function includeInReward(address account) external onlyOwner() {
              require(_isExcluded[account], "Account is not excluded");
              for (uint256 i = 0; i < _excluded.length; i++) {
                  if (_excluded[i] == account) {
                      _excluded[i] = _excluded[_excluded.length - 1];
                      _tOwned[account] = 0;
                      _isExcluded[account] = false;
                      _excluded.pop();
                      break;
                  }
              }
          }
          function excludeFromFee(address account) public onlyOwner {
              _isExcludedFromFee[account] = true;
          }
          function includeInFee(address account) public onlyOwner {
              _isExcludedFromFee[account] = false;
          }
          function isExcludedFromFee(address account) public view returns(bool) {
              return _isExcludedFromFee[account];
          }
          function addPair(address _pair) public onlyOwner {
              _isPair[_pair] = true;
          }
          function removePair(address _pair) public onlyOwner {
              _isPair[_pair] = false;
          }
          function isPair(address account) public view returns(bool){
              return _isPair[account];
          }
          function setTaxes(uint256 _reflection, uint256 _capital, uint256 _marketing, uint256 _burn, uint256 _treasury) public onlyOwner {
              taxes.reflection = _reflection;
              taxes.capital = _capital;
              taxes.marketing = _marketing;
              taxes.burn = _burn;
              taxes.treasury = _treasury;
              emit FeesChanged();
          }
          function setSplitETH(uint256 _capital, uint256 _marketing) public onlyOwner {
              splitETH.capital = _capital;
              splitETH.marketing = _marketing;
              emit FeesChanged();
          }
          function _reflectReflection(uint256 rReflection, uint256 tReflection) private {
              _rTotal -=rReflection;
              totFeesPaid.reflection += tReflection;
          }
          function _takeTreasury(uint256 rTreasury, uint256 tTreasury) private {
              totFeesPaid.treasury += tTreasury;
              if(_isExcluded[address(this)]) _tOwned[address(this)] += tTreasury;
              _rOwned[address(this)] += rTreasury;
          }
          function _takeCapital(uint256 rCapital, uint256 tCapital) private {
              totFeesPaid.capital += tCapital;
              if(_isExcluded[capitalAddress]) _tOwned[capitalAddress] += tCapital;
              _rOwned[capitalAddress] +=rCapital;
          }
          
          function _takeMarketing(uint256 rMarketing, uint256 tMarketing) private{
              totFeesPaid.marketing += tMarketing;
              if(_isExcluded[marketingAddress]) _tOwned[marketingAddress] += tMarketing;
              _rOwned[marketingAddress] += rMarketing;
          }
          function _takeBurn(uint256 rBurn, uint256 tBurn) private {
              totFeesPaid.burn += tBurn;
              if(_isExcluded[marketingAddress])_tOwned[burnAddress] += tBurn;
              _rOwned[burnAddress] += rBurn;
          }
          function _getValues(uint256 tAmount, uint8 takeFee) private  returns (valuesFromGetValues memory to_return) {
              to_return = _getTValues(tAmount, takeFee);
              (to_return.rAmount, to_return.rTransferAmount, to_return.rReflection, to_return.rCapital,to_return.rMarketing, to_return.rBurn, to_return.rTreasury) = _getRValues(to_return, tAmount, takeFee, _getRate());
              return to_return;
          }
          function _getTValues(uint256 tAmount, uint8 takeFee) private returns (valuesFromGetValues memory s) {
              if(takeFee == 0) {
                s.tTransferAmount = tAmount;
                return s;
              } else if(takeFee == 1){
                  s.tReflection = (tAmount*taxes.reflection)/1000;
                  s.tCapital = (tAmount*taxes.capital)/1000;
                  s.tMarketing = tAmount*taxes.marketing/1000;
                  s.tBurn = tAmount*taxes.burn/1000;
                  s.tTreasury = tAmount*taxes.treasury/1000;
                  ETHAmount.capital += s.tTreasury*splitETH.capital/taxes.treasury;
                  ETHAmount.marketing += s.tTreasury*splitETH.marketing/taxes.treasury;
                  s.tTransferAmount = tAmount-s.tReflection-s.tCapital-s.tTreasury-s.tMarketing-s.tBurn;
                  return s;
              } else {
                  s.tReflection = tAmount*taxes.reflection/1000;
                  s.tMarketing = tAmount*taxes.marketing/1000;
                  s.tBurn = tAmount*taxes.burn/1000;
                  s.tTreasury = tAmount*splitETH.marketing/1000;
                  ETHAmount.marketing += s.tTreasury;
                  s.tTransferAmount = tAmount-s.tReflection-s.tTreasury-s.tMarketing-s.tBurn;
                  return s;
              }
              
          }
          function _getRValues(valuesFromGetValues memory s, uint256 tAmount, uint8 takeFee, uint256 currentRate) private pure returns (uint256 rAmount, uint256 rTransferAmount, uint256 rReflection,uint256 rCapital,uint256 rMarketing,uint256 rBurn,uint256 rTreasury) {
              rAmount = tAmount*currentRate;
              if(takeFee == 0) {
                return(rAmount, rAmount, 0,0,0,0,0);
              }else if(takeFee == 1){
                  rReflection = s.tReflection*currentRate;
                  rCapital = s.tCapital*currentRate;
                  rTreasury = s.tTreasury*currentRate;
                  rMarketing = s.tMarketing*currentRate;
                  rBurn = s.tBurn*currentRate;
                  rTransferAmount =  rAmount-rReflection-rCapital-rTreasury-rMarketing-rBurn;
                  return (rAmount, rTransferAmount, rReflection,rCapital,rMarketing,rBurn,rTreasury);
              }
              else{
                  rReflection = s.tReflection*currentRate;
                  rTreasury = s.tTreasury*currentRate;
                  rMarketing = s.tMarketing*currentRate;
                  rBurn = s.tBurn*currentRate;
                  rTransferAmount =  rAmount-rReflection-rTreasury-rMarketing-rBurn;
                  return (rAmount, rTransferAmount, rReflection,0,rMarketing,rBurn,rTreasury);
              }
          }
          function _getRate() private view returns(uint256) {
              (uint256 rSupply, uint256 tSupply) = _getCurrentSupply();
              return rSupply/tSupply;
          }
          function _getCurrentSupply() private view returns(uint256, uint256) {
              uint256 rSupply = _rTotal;
              uint256 tSupply = _tTotal;
              for (uint256 i = 0; i < _excluded.length; i++) {
                  if (_rOwned[_excluded[i]] > rSupply || _tOwned[_excluded[i]] > tSupply) return (_rTotal, _tTotal);
                  rSupply = rSupply-_rOwned[_excluded[i]];
                  tSupply = tSupply-_tOwned[_excluded[i]];
              }
              if (rSupply < _rTotal/_tTotal) return (_rTotal, _tTotal);
              return (rSupply, tSupply);
          }
          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, "Zero amount");
              require(amount <= balanceOf(from),"Insufficient balance");
              require(!_isBot[from] && !_isBot[to], "You are a bot");
              require(amount <= maxTxAmount ,"Amount is exceeding maxTxAmount");
              if (coolDownEnabled) {
                  uint256 timePassed = block.timestamp - _lastTrade[from];
                  require(timePassed > coolDownTime, "You must wait coolDownTime");
              }
              
              if(!_isExcludedFromFee[from] && !_isExcludedFromFee[to] && !swapping) {//check this !swapping
                  if(_isPair[from] || _isPair[to]) {
                      _tokenTransfer(from, to, amount, 1);
                  } else {
                      _tokenTransfer(from, to, amount, 2);
                  }
              } else {
                  _tokenTransfer(from, to, amount, 0);
              }
              _lastTrade[from] = block.timestamp;
              
              if(!swapping && from != pair && to != pair && !_isExcludedFromFee[from] && !_isExcludedFromFee[to]){
                  address[] memory path = new address[](3);
                      path[0] = address(this);
                      path[1] = router.WETH();
                      path[2] = USDT;
                  uint _amount = router.getAmountsOut(balanceOf(address(this)), path)[2];
                  if(_amount >= swapTokensAtAmount) swapTokensForETH(balanceOf(address(this)));
              }
          }
          //this method is responsible for taking all fee, if takeFee is true
          function _tokenTransfer(address sender, address recipient, uint256 tAmount, uint8 takeFee) private {
              valuesFromGetValues memory s = _getValues(tAmount, takeFee);
              if (_isExcluded[sender] ) {  //from excluded
                      _tOwned[sender] = _tOwned[sender] - tAmount;
              }
              if (_isExcluded[recipient]) { //to excluded
                      _tOwned[recipient] = _tOwned[recipient] + s.tTransferAmount;
              }
              _rOwned[sender] = _rOwned[sender]-s.rAmount;
              _rOwned[recipient] = _rOwned[recipient]+s.rTransferAmount;
              
              if(s.rReflection > 0 || s.tReflection > 0) _reflectReflection(s.rReflection, s.tReflection);
              if(s.rTreasury > 0 || s.tTreasury > 0) {
                  _takeTreasury(s.rTreasury,s.tTreasury);
              }
              if(s.rCapital > 0 || s.tCapital > 0){
                  _takeCapital(s.rCapital, s.tCapital);
                  emit Transfer(sender, capitalAddress, s.tMarketing);
              }
              if(s.rMarketing > 0 || s.tMarketing > 0){
                  _takeMarketing(s.rMarketing, s.tMarketing);
                  emit Transfer(sender, marketingAddress, s.tMarketing);
              }
              if(s.rBurn > 0 || s.tBurn > 0){
                  _takeBurn(s.rBurn, s.tBurn);
                  emit Transfer(sender, burnAddress, s.tBurn);
              }
              
              emit Transfer(sender, recipient, s.tTransferAmount);
              if(s.tTreasury > 0){
              emit Transfer(sender, address(this), s.tTreasury);
              }
          }
          function swapTokensForETH(uint256 tokenAmount) private lockTheSwap {
              // generate the uniswap pair path of token -> weth
              address[] memory path = new address[](2);
                      path[0] = address(this);
                      path[1] = router.WETH();
              _approve(address(this), address(router), tokenAmount);
              // make the swap
              router.swapExactTokensForETHSupportingFeeOnTransferTokens(
                  tokenAmount,
                  0, // accept any amount of ETH
                  path,
                  address(this),
                  block.timestamp
              );
              (bool success, ) = capitalAddress.call{value: (ETHAmount.capital * address(this).balance)/tokenAmount}("");
              require(success, 'ETH_TRANSFER_FAILED');
              ETHAmount.capital = 0;
              (success, ) = marketingAddress.call{value: (ETHAmount.marketing * address(this).balance)/tokenAmount}("");
              require(success, 'ETH_TRANSFER_FAILED');
              ETHAmount.marketing = 0;
          }
          function updateCapitalWallet(address newWallet) external onlyOwner addressValidation(newWallet) {
              require(capitalAddress != newWallet, 'SaitaRealty: Wallet already set');
              capitalAddress = newWallet;
              _isExcludedFromFee[capitalAddress];
          }
          function updateBurnWallet(address newWallet) external onlyOwner addressValidation(newWallet) {
              require(burnAddress != newWallet, 'SaitaRealty: Wallet already set');
              burnAddress = newWallet;
              _isExcludedFromFee[burnAddress];
          }
          function updateMarketingWallet(address newWallet) external onlyOwner addressValidation(newWallet) {
              require(marketingAddress != newWallet, 'SaitaRealty: Wallet already set');
              marketingAddress = newWallet;
              _isExcludedFromFee[marketingAddress];
          }
          function updateStableCoin(address _usdt) external onlyOwner  addressValidation(_usdt) {
              require(USDT != _usdt, 'SaitaRealty: Wallet already set');
              USDT = _usdt;
          }
          function updateMaxTxAmt(uint256 amount) external onlyOwner {
              require(amount >= 100);
              maxTxAmount = amount * 10**_decimals;
          }
          function updateSwapTokensAtAmount(uint256 amount) external onlyOwner {
              require(amount > 0);
              swapTokensAtAmount = amount * 10**6;
          }
          function updateCoolDownSettings(bool _enabled, uint256 _timeInSeconds) external onlyOwner{
              coolDownEnabled = _enabled;
              coolDownTime = _timeInSeconds * 1 seconds;
          }
          function setAntibot(address account, bool state) external onlyOwner{
              require(_isBot[account] != state, 'SaitaRealty: Value already set');
              _isBot[account] = state;
          }
          
          function bulkAntiBot(address[] memory accounts, bool state) external onlyOwner {
              require(accounts.length <= 100, "SaitaRealty: Invalid");
              for(uint256 i = 0; i < accounts.length; i++){
                  _isBot[accounts[i]] = state;
              }
          }
          
          function updateRouterAndPair(address newRouter, address newPair) external onlyOwner {
              router = IRouter(newRouter);
              pair = newPair;
              addPair(pair);
          }
          
          function isBot(address account) public view returns(bool){
              return _isBot[account];
          }
          
          function airdropTokens(address[] memory recipients, uint256[] memory amounts) external onlyOwner {
              require(recipients.length == amounts.length,"Invalid size");
               address sender = msg.sender;
               for(uint256 i; i<recipients.length; i++){
                  address recipient = recipients[i];
                  uint256 rAmount = amounts[i]*_getRate();
                  _rOwned[sender] = _rOwned[sender]- rAmount;
                  _rOwned[recipient] = _rOwned[recipient] + rAmount;
                  emit Transfer(sender, recipient, amounts[i]);
               }
              }
          //Use this in case ETH are sent to the contract by mistake
          function rescueETH(uint256 weiAmount) external onlyOwner{
              require(address(this).balance >= weiAmount, "insufficient ETH balance");
              payable(owner()).transfer(weiAmount);
          }
          
          // Function to allow admin to claim *other* ERC20 tokens sent to this contract (by mistake)
          // Owner cannot transfer out catecoin from this smart contract
          function rescueAnyERC20Tokens(address _tokenAddr, address _to, uint _amount) public onlyOwner {
              IERC20(_tokenAddr).transfer(_to, _amount);
          }
          receive() external payable {
          }
      }

      File 2 of 4: SaitaSwapRouter
      // SPDX-License-Identifier: GPL-3.0-or-later
      pragma solidity >=0.6.0;
      // 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,
              uint256 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::safeApprove: approve failed'
              );
          }
          function safeTransfer(
              address token,
              address to,
              uint256 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::safeTransfer: transfer failed'
              );
          }
          function safeTransferFrom(
              address token,
              address from,
              address to,
              uint256 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::transferFrom: transferFrom failed'
              );
          }
          function safeTransferETH(address to, uint256 value) internal {
              (bool success, ) = to.call{value: value}(new bytes(0));
              require(success, 'TransferHelper::safeTransferETH: ETH transfer failed');
          }
      }
      // SPDX-License-Identifier: MIT
      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);
      }
      pragma solidity >=0.5.0;
      interface ISaitaSwapFactory {
          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;
      }pragma solidity >=0.5.0;
      interface ISaitaSwapPair {
          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;
      }pragma solidity >=0.6.2;
      interface ISaitaSwapRouter01 {
          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);
      }pragma solidity >=0.6.2;
      import './ISaitaSwapRouter01.sol';
      interface ISaitaSwapRouter02 is ISaitaSwapRouter01 {
          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;
      }// SPDX-License-Identifier: MIT
      pragma solidity >=0.5.0;
      interface IWETH {
          function deposit() external payable;
          function transfer(address to, uint value) external returns (bool);
          function withdraw(uint) external;
      }pragma solidity =0.6.6;
      // 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');
          }
      }pragma solidity =0.6.6;
      import "../interfaces/ISaitaSwapPair.sol";
      import "./SafeMath.sol";
      library SaitaSwapLibrary {
          using SafeMath for uint256;
          // returns sorted token addresses, used to handle return values from pairs sorted in this order
          function sortTokens(address tokenA, address tokenB)
              internal
              pure
              returns (address token0, address token1)
          {
              require(tokenA != tokenB, "SaitaSwapLibrary: IDENTICAL_ADDRESSES");
              (token0, token1) = tokenA < tokenB
                  ? (tokenA, tokenB)
                  : (tokenB, tokenA);
              require(token0 != address(0), "SaitaSwapLibrary: ZERO_ADDRESS");
          }
          // calculates the CREATE2 address for a pair without making any external calls
          function pairFor(
              address factory,
              address tokenA,
              address tokenB
          ) internal pure returns (address pair) {
              (address token0, address token1) = sortTokens(tokenA, tokenB);
              pair = address(
                  (uint256(
                      keccak256(
                          abi.encodePacked(
                              hex"ff",
                              factory,
                              keccak256(abi.encodePacked(token0, token1)),
                              hex"ce30f06d03390218a95a12e2bb31c744b142f1064ccdfd037850b4194bfbe741" // init code hash
                          )
                      )
                  ))
              );
          }
          // fetches and sorts the reserves for a pair
          function getReserves(
              address factory,
              address tokenA,
              address tokenB
          ) internal view returns (uint256 reserveA, uint256 reserveB) {
              (address token0, ) = sortTokens(tokenA, tokenB);
              (uint256 reserve0, uint256 reserve1, ) = ISaitaSwapPair(
                  pairFor(factory, tokenA, tokenB)
              ).getReserves();
              (reserveA, reserveB) = tokenA == token0
                  ? (reserve0, reserve1)
                  : (reserve1, reserve0);
          }
          // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
          function quote(
              uint256 amountA,
              uint256 reserveA,
              uint256 reserveB
          ) internal pure returns (uint256 amountB) {
              require(amountA > 0, "SaitaSwapLibrary: INSUFFICIENT_AMOUNT");
              require(
                  reserveA > 0 && reserveB > 0,
                  "SaitaSwapLibrary: INSUFFICIENT_LIQUIDITY"
              );
              amountB = amountA.mul(reserveB) / reserveA;
          }
          // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
          function getAmountOut(
              uint256 amountIn,
              uint256 reserveIn,
              uint256 reserveOut
          ) internal pure returns (uint256 amountOut) {
              require(amountIn > 0, "SaitaSwapLibrary: INSUFFICIENT_INPUT_AMOUNT");
              require(
                  reserveIn > 0 && reserveOut > 0,
                  "SaitaSwapLibrary: INSUFFICIENT_LIQUIDITY"
              );
              uint256 amountInWithFee = amountIn.mul(997);
              uint256 numerator = amountInWithFee.mul(reserveOut);
              uint256 denominator = reserveIn.mul(1000).add(amountInWithFee);
              amountOut = numerator / denominator;
          }
          // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
          function getAmountIn(
              uint256 amountOut,
              uint256 reserveIn,
              uint256 reserveOut
          ) internal pure returns (uint256 amountIn) {
              require(amountOut > 0, "SaitaSwapLibrary: INSUFFICIENT_OUTPUT_AMOUNT");
              require(
                  reserveIn > 0 && reserveOut > 0,
                  "SaitaSwapLibrary: INSUFFICIENT_LIQUIDITY"
              );
              uint256 numerator = reserveIn.mul(amountOut).mul(1000);
              uint256 denominator = reserveOut.sub(amountOut).mul(997);
              amountIn = (numerator / denominator).add(1);
          }
          // performs chained getAmountOut calculations on any number of pairs
          function getAmountsOut(
              address factory,
              uint256 amountIn,
              address[] memory path
          ) internal view returns (uint256[] memory amounts) {
              require(path.length >= 2, "SaitaSwapLibrary: INVALID_PATH");
              amounts = new uint256[](path.length);
              amounts[0] = amountIn;
              for (uint256 i; i < path.length - 1; i++) {
                  (uint256 reserveIn, uint256 reserveOut) = getReserves(
                      factory,
                      path[i],
                      path[i + 1]
                  );
                  amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
              }
          }
          // performs chained getAmountIn calculations on any number of pairs
          function getAmountsIn(
              address factory,
              uint256 amountOut,
              address[] memory path
          ) internal view returns (uint256[] memory amounts) {
              require(path.length >= 2, "SaitaSwapLibrary: INVALID_PATH");
              amounts = new uint256[](path.length);
              amounts[amounts.length - 1] = amountOut;
              for (uint256 i = path.length - 1; i > 0; i--) {
                  (uint256 reserveIn, uint256 reserveOut) = getReserves(
                      factory,
                      path[i - 1],
                      path[i]
                  );
                  amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
              }
          }
      }// SPDX-License-Identifier: GPL-3.0
      pragma solidity =0.6.6;
      import "./interfaces/ISaitaSwapFactory.sol";
      import "@uniswap/lib/contracts/libraries/TransferHelper.sol";
      import "./interfaces/ISaitaSwapRouter02.sol";
      import "./libraries/SaitaSwapLibrary.sol";
      import "./libraries/SafeMath.sol";
      import "./interfaces/IERC20.sol";
      import "./interfaces/IWETH.sol";
      contract SaitaSwapRouter is ISaitaSwapRouter02 {
          using SafeMath for uint;
          address public immutable override factory;
          address public immutable override WETH;
          modifier ensure(uint deadline) {
              require(deadline >= block.timestamp, "SaitaSwapRouter: 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 (ISaitaSwapFactory(factory).getPair(tokenA, tokenB) == address(0)) {
                  ISaitaSwapFactory(factory).createPair(tokenA, tokenB);
              }
              (uint reserveA, uint reserveB) = SaitaSwapLibrary.getReserves(factory, tokenA, tokenB);
              if (reserveA == 0 && reserveB == 0) {
                  (amountA, amountB) = (amountADesired, amountBDesired);
              } else {
                  uint amountBOptimal = SaitaSwapLibrary.quote(amountADesired, reserveA, reserveB);
                  if (amountBOptimal <= amountBDesired) {
                      require(amountBOptimal >= amountBMin, "SaitaSwapRouter: INSUFFICIENT_B_AMOUNT");
                      (amountA, amountB) = (amountADesired, amountBOptimal);
                  } else {
                      uint amountAOptimal = SaitaSwapLibrary.quote(amountBDesired, reserveB, reserveA);
                      assert(amountAOptimal <= amountADesired);
                      require(amountAOptimal >= amountAMin, "SaitaSwapRouter: 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 = SaitaSwapLibrary.pairFor(factory, tokenA, tokenB);
              TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
              TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
              liquidity = ISaitaSwapPair(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 = SaitaSwapLibrary.pairFor(factory, token, WETH);
              TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);
              IWETH(WETH).deposit{value: amountETH}();
              assert(IWETH(WETH).transfer(pair, amountETH));
              liquidity = ISaitaSwapPair(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 = SaitaSwapLibrary.pairFor(factory, tokenA, tokenB);
              ISaitaSwapPair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair
              (uint amount0, uint amount1) = ISaitaSwapPair(pair).burn(to);
              (address token0,) = SaitaSwapLibrary.sortTokens(tokenA, tokenB);
              (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
              require(amountA >= amountAMin, "SaitaSwapRouter: INSUFFICIENT_A_AMOUNT");
              require(amountB >= amountBMin, "SaitaSwapRouter: 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 = SaitaSwapLibrary.pairFor(factory, tokenA, tokenB);
              uint value = approveMax ? uint(-1) : liquidity;
              ISaitaSwapPair(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 = SaitaSwapLibrary.pairFor(factory, token, WETH);
              uint value = approveMax ? uint(-1) : liquidity;
              ISaitaSwapPair(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 = SaitaSwapLibrary.pairFor(factory, token, WETH);
              uint value = approveMax ? uint(-1) : liquidity;
              ISaitaSwapPair(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,) = SaitaSwapLibrary.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 ? SaitaSwapLibrary.pairFor(factory, output, path[i + 2]) : _to;
                  ISaitaSwapPair(SaitaSwapLibrary.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 = SaitaSwapLibrary.getAmountsOut(factory, amountIn, path);
              require(amounts[amounts.length - 1] >= amountOutMin, "SaitaSwapRouter: INSUFFICIENT_OUTPUT_AMOUNT");
              TransferHelper.safeTransferFrom(
                  path[0], msg.sender, SaitaSwapLibrary.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 = SaitaSwapLibrary.getAmountsIn(factory, amountOut, path);
              require(amounts[0] <= amountInMax, "SaitaSwapRouter: EXCESSIVE_INPUT_AMOUNT");
              TransferHelper.safeTransferFrom(
                  path[0], msg.sender, SaitaSwapLibrary.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, "SaitaSwapRouter: INVALID_PATH");
              amounts = SaitaSwapLibrary.getAmountsOut(factory, msg.value, path);
              require(amounts[amounts.length - 1] >= amountOutMin, "SaitaSwapRouter: INSUFFICIENT_OUTPUT_AMOUNT");
              IWETH(WETH).deposit{value: amounts[0]}();
              assert(IWETH(WETH).transfer(SaitaSwapLibrary.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, "SaitaSwapRouter: INVALID_PATH");
              amounts = SaitaSwapLibrary.getAmountsIn(factory, amountOut, path);
              require(amounts[0] <= amountInMax, "SaitaSwapRouter: EXCESSIVE_INPUT_AMOUNT");
              TransferHelper.safeTransferFrom(
                  path[0], msg.sender, SaitaSwapLibrary.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, "SaitaSwapRouter: INVALID_PATH");
              amounts = SaitaSwapLibrary.getAmountsOut(factory, amountIn, path);
              require(amounts[amounts.length - 1] >= amountOutMin, "SaitaSwapRouter: INSUFFICIENT_OUTPUT_AMOUNT");
              TransferHelper.safeTransferFrom(
                  path[0], msg.sender, SaitaSwapLibrary.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, "SaitaSwapRouter: INVALID_PATH");
              amounts = SaitaSwapLibrary.getAmountsIn(factory, amountOut, path);
              require(amounts[0] <= msg.value, "SaitaSwapRouter: EXCESSIVE_INPUT_AMOUNT");
              IWETH(WETH).deposit{value: amounts[0]}();
              assert(IWETH(WETH).transfer(SaitaSwapLibrary.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,) = SaitaSwapLibrary.sortTokens(input, output);
                  ISaitaSwapPair pair = ISaitaSwapPair(SaitaSwapLibrary.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 = SaitaSwapLibrary.getAmountOut(amountInput, reserveInput, reserveOutput);
                  }
                  (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOutput) : (amountOutput, uint(0));
                  address to = i < path.length - 2 ? SaitaSwapLibrary.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, SaitaSwapLibrary.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,
                  "SaitaSwapRouter: INSUFFICIENT_OUTPUT_AMOUNT"
              );
          }
          function swapExactETHForTokensSupportingFeeOnTransferTokens(
              uint amountOutMin,
              address[] calldata path,
              address to,
              uint deadline
          )
              external
              virtual
              override
              payable
              ensure(deadline)
          {
              require(path[0] == WETH, "SaitaSwapRouter: INVALID_PATH");
              uint amountIn = msg.value;
              IWETH(WETH).deposit{value: amountIn}();
              assert(IWETH(WETH).transfer(SaitaSwapLibrary.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,
                  "SaitaSwapRouter: 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, "SaitaSwapRouter: INVALID_PATH");
              TransferHelper.safeTransferFrom(
                  path[0], msg.sender, SaitaSwapLibrary.pairFor(factory, path[0], path[1]), amountIn
              );
              _swapSupportingFeeOnTransferTokens(path, address(this));
              uint amountOut = IERC20(WETH).balanceOf(address(this));
              require(amountOut >= amountOutMin, "SaitaSwapRouter: 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 SaitaSwapLibrary.quote(amountA, reserveA, reserveB);
          }
          function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut)
              public
              pure
              virtual
              override
              returns (uint amountOut)
          {
              return SaitaSwapLibrary.getAmountOut(amountIn, reserveIn, reserveOut);
          }
          function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut)
              public
              pure
              virtual
              override
              returns (uint amountIn)
          {
              return SaitaSwapLibrary.getAmountIn(amountOut, reserveIn, reserveOut);
          }
          function getAmountsOut(uint amountIn, address[] memory path)
              public
              view
              virtual
              override
              returns (uint[] memory amounts)
          {
              return SaitaSwapLibrary.getAmountsOut(factory, amountIn, path);
          }
          function getAmountsIn(uint amountOut, address[] memory path)
              public
              view
              virtual
              override
              returns (uint[] memory amounts)
          {
              return SaitaSwapLibrary.getAmountsIn(factory, amountOut, path);
          }
      }

      File 3 of 4: SaitaSwapPair
      // SPDX-License-Identifier: MIT
      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);
      }
      pragma solidity >=0.5.0;
      interface ISaitaSwapCallee {
          function SaitaSwapCall(address sender, uint amount0, uint amount1, bytes calldata data) external;
      }pragma solidity >=0.5.0;
      interface ISaitaSwapERC20 {
          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;
      }pragma solidity >=0.5.0;
      interface ISaitaSwapFactory {
          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;
      }pragma solidity >=0.5.0;
      interface ISaitaSwapPair {
          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;
      }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;
              }
          }
      }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');
          }
      }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);
          }
      }// SPDX-License-Identifier: UNLICENSED
      pragma solidity =0.5.16;
      import './interfaces/ISaitaSwapERC20.sol';
      import './libraries/SafeMath.sol';
      contract SaitaSwapERC20 is ISaitaSwapERC20 {
          using SafeMath for uint;
          string public constant name = 'SaitaSwap LPs';
          string public constant symbol = 'slp';
          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);
          
          modifier ReChain{
              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)
                  )
              );
              _;
          }
          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 ReChain {
              require(deadline >= block.timestamp, 'SaitaSwap: 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, 'SaitaSwap: INVALID_SIGNATURE');
              _approve(owner, spender, value);
          }
      }// SPDX-License-Identifier: GPL-3.0
      pragma solidity =0.5.16;
      import './interfaces/ISaitaSwapPair.sol';
      import './SaitaSwapERC20.sol';
      import './libraries/Math.sol';
      import './libraries/UQ112x112.sol';
      import './interfaces/IERC20.sol';
      import './interfaces/ISaitaSwapFactory.sol';
      import './interfaces/ISaitaSwapCallee.sol';
      contract SaitaSwapPair is ISaitaSwapPair, SaitaSwapERC20 {
          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;
          
          uint public SWAP_FEE_NUMERATOR=2;
          uint public SWAP_FEE_DENOMINATOR=1000;
        
          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, 'SaitaSwap: 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))), 'SaitaSwap: 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, 'SaitaSwap: 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), 'SaitaSwap: 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 the growth in sqrt(k) .
      function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
              address feeTo = ISaitaSwapFactory(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 = 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, 'SaitaSwap: 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, 'SaitaSwap: 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, 'SaitaSwap: INSUFFICIENT_OUTPUT_AMOUNT');
              (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
              require(amount0Out < _reserve0 && amount1Out < _reserve1, 'SaitaSwap: 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, 'SaitaSwap: 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) ISaitaSwapCallee(to).SaitaSwapCall(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, 'SaitaSwap: INSUFFICIENT_INPUT_AMOUNT');
              { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
              uint balance0Adjusted = balance0.mul(SWAP_FEE_DENOMINATOR).sub(amount0In.mul(SWAP_FEE_NUMERATOR));
              uint balance1Adjusted = balance1.mul(SWAP_FEE_DENOMINATOR).sub(amount1In.mul(SWAP_FEE_NUMERATOR));
              require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(SWAP_FEE_DENOMINATOR**SWAP_FEE_NUMERATOR), 'SaitaSwap: K');
              }
              _update(balance0, balance1, _reserve0, _reserve1);
              emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
          }
          // force balances to match reserves
          function skim(address to) external lock {
              address _token0 = token0; // gas savings
              address _token1 = token1; // gas savings
              _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
              _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
          }
          // force reserves to match balances
          function sync() external lock {
              _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
          }
      }

      File 4 of 4: SaitaSwapPair
      // SPDX-License-Identifier: MIT
      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);
      }
      pragma solidity >=0.5.0;
      interface ISaitaSwapCallee {
          function SaitaSwapCall(address sender, uint amount0, uint amount1, bytes calldata data) external;
      }pragma solidity >=0.5.0;
      interface ISaitaSwapERC20 {
          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;
      }pragma solidity >=0.5.0;
      interface ISaitaSwapFactory {
          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;
      }pragma solidity >=0.5.0;
      interface ISaitaSwapPair {
          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;
      }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;
              }
          }
      }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');
          }
      }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);
          }
      }// SPDX-License-Identifier: UNLICENSED
      pragma solidity =0.5.16;
      import './interfaces/ISaitaSwapERC20.sol';
      import './libraries/SafeMath.sol';
      contract SaitaSwapERC20 is ISaitaSwapERC20 {
          using SafeMath for uint;
          string public constant name = 'SaitaSwap LPs';
          string public constant symbol = 'slp';
          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);
          
          modifier ReChain{
              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)
                  )
              );
              _;
          }
          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 ReChain {
              require(deadline >= block.timestamp, 'SaitaSwap: 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, 'SaitaSwap: INVALID_SIGNATURE');
              _approve(owner, spender, value);
          }
      }// SPDX-License-Identifier: GPL-3.0
      pragma solidity =0.5.16;
      import './interfaces/ISaitaSwapPair.sol';
      import './SaitaSwapERC20.sol';
      import './libraries/Math.sol';
      import './libraries/UQ112x112.sol';
      import './interfaces/IERC20.sol';
      import './interfaces/ISaitaSwapFactory.sol';
      import './interfaces/ISaitaSwapCallee.sol';
      contract SaitaSwapPair is ISaitaSwapPair, SaitaSwapERC20 {
          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;
          
          uint public SWAP_FEE_NUMERATOR=2;
          uint public SWAP_FEE_DENOMINATOR=1000;
        
          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, 'SaitaSwap: 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))), 'SaitaSwap: 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, 'SaitaSwap: 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), 'SaitaSwap: 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 the growth in sqrt(k) .
      function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
              address feeTo = ISaitaSwapFactory(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 = 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, 'SaitaSwap: 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, 'SaitaSwap: 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, 'SaitaSwap: INSUFFICIENT_OUTPUT_AMOUNT');
              (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
              require(amount0Out < _reserve0 && amount1Out < _reserve1, 'SaitaSwap: 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, 'SaitaSwap: 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) ISaitaSwapCallee(to).SaitaSwapCall(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, 'SaitaSwap: INSUFFICIENT_INPUT_AMOUNT');
              { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
              uint balance0Adjusted = balance0.mul(SWAP_FEE_DENOMINATOR).sub(amount0In.mul(SWAP_FEE_NUMERATOR));
              uint balance1Adjusted = balance1.mul(SWAP_FEE_DENOMINATOR).sub(amount1In.mul(SWAP_FEE_NUMERATOR));
              require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(SWAP_FEE_DENOMINATOR**SWAP_FEE_NUMERATOR), 'SaitaSwap: 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);
          }
      }