ETH Price: $1,895.36 (-1.10%)

Transaction Decoder

Block:
12777685 at Jul-07-2021 02:40:26 AM +UTC
Transaction Fee:
0.007156654 ETH $13.56
Gas Used:
188,333 Gas / 38 Gwei

Emitted Events:

300 SmoothLovePotion.Transfer( _from=[Receiver] MainchainGatewayProxy, _to=[Sender] 0x851751f982e3e319c4635a813cb9c628df5a87b7, _value=5616 )
301 MainchainGatewayProxy.TokenWithdrew( _withdrawId=114335, _owner=[Sender] 0x851751f982e3e319c4635a813cb9c628df5a87b7, _tokenAddress=SmoothLovePotion, _tokenNumber=5616 )

Account State Difference:

  Address   Before After State Difference Code
0x1A2a1c93...a9DD454F2
(Axie Infinity: Ronin Bridge)
0x851751F9...8Df5A87b7
0.047537687761082914 Eth
Nonce: 32
0.040381033761082914 Eth
Nonce: 33
0.007156654
0xCC8Fa225...5D6cAAa25
(Ethermine)
2,023.521793066300835822 Eth2,023.528949720300835822 Eth0.007156654

Execution Trace

MainchainGatewayProxy.993e1c42( )
  • MainchainGatewayManager.withdrawERC20For( _withdrawalId=114335, _user=0x851751F982e3E319c4635A813cB9C628Df5A87b7, _token=0xCC8Fa225D80b9c7D42F96e9570156c65D6cAAa25, _amount=5616, _signatures=0x01688084FA0CAB46383D6347DCECB8B63B58F451266DF9EC8AEA9B757B02AEAE957B9A9421C7495C7B42F68933699D271226AB86D8A6BBC1A38567A01816412C561B01592A7B439F0B9A6C49FFF7E334F7EB111CBE69DF5F30B1B2D4A6F5E57CD77FA27E33958D4BD13F30E339C209F59724648985E06EEDDAA64FDBF529ADE5F2EAB41B01327833430DD41D0F9FEC7A78AB037FF9A27D7EE6EF470946DEC6F953F2B32E04264A0624F3F7F0DA51D89F720CDE3F69AA96A7BD89348393ADF0F156E3A6D7061C )
    • Registry.isTokenMapped( _token=0xCC8Fa225D80b9c7D42F96e9570156c65D6cAAa25, _standard=20, _isMainchain=True ) => ( True )
    • Registry.STATICCALL( )
    • Registry.getContract( _name=VALIDATOR ) => ( _address=0x42B19dca30fd612B1757682C074497847F2B57e0 )
    • Null: 0x000...001.8b0f2355( )
    • MainchainValidator.isValidator( _addr=0x11360EaCDEDd59bC433aFad4fc8f0417d1fbEbab ) => ( True )
    • Null: 0x000...001.8b0f2355( )
    • MainchainValidator.isValidator( _addr=0x70bB1FB41C8C42F6ddd53a708E2B82209495e455 ) => ( True )
    • Null: 0x000...001.8b0f2355( )
    • MainchainValidator.isValidator( _addr=0xf224beFf587362A88D859e899D0d80C080E1e812 ) => ( True )
    • MainchainValidator.checkThreshold( _voteCount=3 ) => ( True )
    • Registry.STATICCALL( )
    • Registry.getContract( _name=WETH_TOKEN ) => ( _address=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 )
    • SmoothLovePotion.balanceOf( 0x1A2a1c938CE3eC39b6D47113c7955bAa9DD454F2 ) => ( 191132474 )
    • SmoothLovePotion.transfer( _to=0x851751F982e3E319c4635A813cB9C628Df5A87b7, _value=5616 ) => ( _success=True )
      File 1 of 5: MainchainGatewayProxy
      // File: @axie/contract-library/contracts/access/HasAdmin.sol
      
      pragma solidity ^0.5.2;
      
      
      contract HasAdmin {
        event AdminChanged(address indexed _oldAdmin, address indexed _newAdmin);
        event AdminRemoved(address indexed _oldAdmin);
      
        address public admin;
      
        modifier onlyAdmin {
          require(msg.sender == admin);
          _;
        }
      
        constructor() internal {
          admin = msg.sender;
          emit AdminChanged(address(0), admin);
        }
      
        function changeAdmin(address _newAdmin) external onlyAdmin {
          require(_newAdmin != address(0));
          emit AdminChanged(admin, _newAdmin);
          admin = _newAdmin;
        }
      
        function removeAdmin() external onlyAdmin {
          emit AdminRemoved(admin);
          admin = address(0);
        }
      }
      
      // File: @axie/contract-library/contracts/proxy/ProxyStorage.sol
      
      pragma solidity ^0.5.2;
      
      /**
       * @title ProxyStorage
       * @dev Store the address of logic contact that the proxy should forward to.
       */
      contract ProxyStorage is HasAdmin {
        address internal _proxyTo;
      }
      
      // File: @axie/contract-library/contracts/proxy/Proxy.sol
      
      pragma solidity ^0.5.2;
      
      
      /**
       * @title Proxy
       * @dev Gives the possibility to delegate any call to a foreign implementation.
       */
      contract Proxy is ProxyStorage {
      
        event ProxyUpdated(address indexed _new, address indexed _old);
      
        constructor(address _proxyTo) public {
          updateProxyTo(_proxyTo);
        }
      
        /**
        * @dev Tells the address of the implementation where every call will be delegated.
        * @return address of the implementation to which it will be delegated
        */
        function implementation() public view returns (address) {
          return _proxyTo;
        }
      
        /**
        * @dev See more at: https://eips.ethereum.org/EIPS/eip-897
        * @return type of proxy - always upgradable
        */
        function proxyType() external pure returns (uint256) {
            // Upgradeable proxy
            return 2;
        }
      
        /**
        * @dev Fallback function allowing to perform a delegatecall to the given implementation.
        * This function will return whatever the implementation call returns
        */
        function () payable external {
          address _impl = implementation();
          require(_impl != address(0));
      
          assembly {
            let ptr := mload(0x40)
            calldatacopy(ptr, 0, calldatasize)
            let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
            let size := returndatasize
            returndatacopy(ptr, 0, size)
      
            switch result
            case 0 { revert(ptr, size) }
            default { return(ptr, size) }
          }
        }
      
        function updateProxyTo(address _newProxyTo) public onlyAdmin {
          require(_newProxyTo != address(0x0));
      
          _proxyTo = _newProxyTo;
          emit ProxyUpdated(_newProxyTo, _proxyTo);
        }
      }
      
      // File: @axie/contract-library/contracts/lifecycle/Pausable.sol
      
      pragma solidity ^0.5.2;
      
      
      
      contract Pausable is HasAdmin {
        event Paused();
        event Unpaused();
      
        bool public paused;
      
        modifier whenNotPaused() {
          require(!paused);
          _;
        }
      
        modifier whenPaused() {
          require(paused);
          _;
        }
      
        function pause() public onlyAdmin whenNotPaused {
          paused = true;
          emit Paused();
        }
      
        function unpause() public onlyAdmin whenPaused {
          paused = false;
          emit Unpaused();
        }
      }
      
      // File: @axie/contract-library/contracts/math/SafeMath.sol
      
      pragma solidity ^0.5.2;
      
      
      library SafeMath {
        function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
          c = a + b;
          require(c >= a);
        }
      
        function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {
          require(b <= a);
          return a - b;
        }
      
        function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
          if (a == 0) {
            return 0;
          }
      
          c = a * b;
          require(c / a == b);
        }
      
        function div(uint256 a, uint256 b) internal pure returns (uint256 c) {
          // Since Solidity automatically asserts when dividing by 0,
          // but we only need it to revert.
          require(b > 0);
          return a / b;
        }
      
        function mod(uint256 a, uint256 b) internal pure returns (uint256 c) {
          // Same reason as `div`.
          require(b > 0);
          return a % b;
        }
      
        function ceilingDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
          return add(div(a, b), mod(a, b) > 0 ? 1 : 0);
        }
      
        function subU64(uint64 a, uint64 b) internal pure returns (uint64 c) {
          require(b <= a);
          return a - b;
        }
      
        function addU8(uint8 a, uint8 b) internal pure returns (uint8 c) {
          c = a + b;
          require(c >= a);
        }
      }
      
      // File: contracts/chain/common/IValidator.sol
      
      pragma solidity ^0.5.17;
      
      
      contract IValidator {
        event ValidatorAdded(uint256 indexed _id, address indexed _validator);
        event ValidatorRemoved(uint256 indexed _id, address indexed _validator);
        event ThresholdUpdated(
          uint256 indexed _id,
          uint256 indexed _numerator,
          uint256 indexed _denominator,
          uint256 _previousNumerator,
          uint256 _previousDenominator
        );
      
        function isValidator(address _addr) public view returns (bool);
        function getValidators() public view returns (address[] memory _validators);
      
        function checkThreshold(uint256 _voteCount) public view returns (bool);
      }
      
      // File: contracts/chain/common/Validator.sol
      
      pragma solidity ^0.5.17;
      
      
      
      
      contract Validator is IValidator {
        using SafeMath for uint256;
      
        mapping(address => bool) validatorMap;
        address[] public validators;
        uint256 public validatorCount;
      
        uint256 public num;
        uint256 public denom;
      
        constructor(address[] memory _validators, uint256 _num, uint256 _denom)
          public
        {
          validators = _validators;
          validatorCount = _validators.length;
      
          for (uint256 _i = 0; _i < validatorCount; _i++) {
            address _validator = _validators[_i];
            validatorMap[_validator] = true;
          }
      
          num = _num;
          denom = _denom;
        }
      
        function isValidator(address _addr)
          public
          view
          returns (bool)
        {
          return validatorMap[_addr];
        }
      
        function getValidators()
          public
          view
          returns (address[] memory _validators)
        {
          _validators = validators;
        }
      
        function checkThreshold(uint256 _voteCount)
          public
          view
          returns (bool)
        {
          return _voteCount.mul(denom) >= num.mul(validatorCount);
        }
      
        function _addValidator(uint256 _id, address _validator)
          internal
        {
          require(!validatorMap[_validator]);
      
          validators.push(_validator);
          validatorMap[_validator] = true;
          validatorCount++;
      
          emit ValidatorAdded(_id, _validator);
        }
      
        function _removeValidator(uint256 _id, address _validator)
          internal
        {
          require(isValidator(_validator));
      
          uint256 _index;
          for (uint256 _i = 0; _i < validatorCount; _i++) {
            if (validators[_i] == _validator) {
              _index = _i;
              break;
            }
          }
      
          validatorMap[_validator] = false;
          validators[_index] = validators[validatorCount - 1];
          validators.pop();
      
          validatorCount--;
      
          emit ValidatorRemoved(_id, _validator);
        }
      
        function _updateQuorum(uint256 _id, uint256 _numerator, uint256 _denominator)
          internal
        {
          require(_numerator <= _denominator);
          uint256 _previousNumerator = num;
          uint256 _previousDenominator = denom;
      
          num = _numerator;
          denom = _denominator;
      
          emit ThresholdUpdated(
            _id,
            _numerator,
            _denominator,
            _previousNumerator,
            _previousDenominator
          );
        }
      }
      
      // File: contracts/chain/mainchain/MainchainValidator.sol
      
      pragma solidity ^0.5.17;
      
      
      
      
      /**
       * @title Validator
       * @dev Simple validator contract
       */
      contract MainchainValidator is Validator, HasAdmin {
        uint256 nonce;
      
        constructor(
          address[] memory _validators,
          uint256 _num,
          uint256 _denom
        ) Validator(_validators, _num, _denom) public {
        }
      
        function addValidators(address[] calldata _validators) external onlyAdmin {
          for (uint256 _i; _i < _validators.length; ++_i) {
            _addValidator(nonce++, _validators[_i]);
          }
        }
      
        function removeValidator(address _validator) external onlyAdmin {
          _removeValidator(nonce++, _validator);
        }
      
        function updateQuorum(uint256 _numerator, uint256 _denominator) external onlyAdmin {
          _updateQuorum(nonce++, _numerator, _denominator);
        }
      }
      
      // File: contracts/chain/common/Registry.sol
      
      pragma solidity ^0.5.17;
      
      
      
      contract Registry is HasAdmin {
      
        event ContractAddressUpdated(
          string indexed _name,
          bytes32 indexed _code,
          address indexed _newAddress
        );
      
        event TokenMapped(
          address indexed _mainchainToken,
          address indexed _sidechainToken,
          uint32 _standard
        );
      
        string public constant GATEWAY = "GATEWAY";
        string public constant WETH_TOKEN = "WETH_TOKEN";
        string public constant VALIDATOR = "VALIDATOR";
        string public constant ACKNOWLEDGEMENT = "ACKNOWLEDGEMENT";
      
        struct TokenMapping {
          address mainchainToken;
          address sidechainToken;
          uint32 standard; // 20, 721 or any other standards
        }
      
        mapping(bytes32 => address) public contractAddresses;
        mapping(address => TokenMapping) public mainchainMap;
        mapping(address => TokenMapping) public sidechainMap;
      
        function getContract(string calldata _name)
          external
          view
          returns (address _address)
        {
          bytes32 _code = getCode(_name);
          _address = contractAddresses[_code];
          require(_address != address(0));
        }
      
        function isTokenMapped(address _token, uint32 _standard, bool _isMainchain)
          external
          view
          returns (bool)
        {
          TokenMapping memory _mapping = _getTokenMapping(_token, _isMainchain);
      
          return _mapping.mainchainToken != address(0) &&
            _mapping.sidechainToken != address(0) &&
            _mapping.standard == _standard;
        }
      
        function updateContract(string calldata _name, address _newAddress)
          external
          onlyAdmin
        {
          bytes32 _code = getCode(_name);
          contractAddresses[_code] = _newAddress;
      
          emit ContractAddressUpdated(_name, _code, _newAddress);
        }
      
        function mapToken(address _mainchainToken, address _sidechainToken, uint32 _standard)
          external
          onlyAdmin
        {
          TokenMapping memory _map = TokenMapping(
            _mainchainToken,
            _sidechainToken,
            _standard
          );
      
          mainchainMap[_mainchainToken] = _map;
          sidechainMap[_sidechainToken] = _map;
      
          emit TokenMapped(
            _mainchainToken,
            _sidechainToken,
            _standard
          );
        }
      
        function clearMapToken(address _mainchainToken, address _sidechainToken)
          external
          onlyAdmin
        {
          TokenMapping storage _mainchainMap = mainchainMap[_mainchainToken];
          _clearMapEntry(_mainchainMap);
      
          TokenMapping storage _sidechainMap = sidechainMap[_sidechainToken];
          _clearMapEntry(_sidechainMap);
        }
      
        function getMappedToken(
          address _token,
          bool _isMainchain
        )
          external
          view
        returns (
          address _mainchainToken,
          address _sidechainToken,
          uint32 _standard
        )
        {
          TokenMapping memory _mapping = _getTokenMapping(_token, _isMainchain);
          _mainchainToken = _mapping.mainchainToken;
          _sidechainToken = _mapping.sidechainToken;
          _standard = _mapping.standard;
        }
      
        function getCode(string memory _name)
          public
          pure
          returns (bytes32)
        {
          return keccak256(abi.encodePacked(_name));
        }
      
        function _getTokenMapping(
          address _token,
          bool isMainchain
        )
          internal
          view
          returns (TokenMapping memory _mapping)
        {
          if (isMainchain) {
            _mapping = mainchainMap[_token];
          } else {
            _mapping = sidechainMap[_token];
          }
        }
      
        function _clearMapEntry(TokenMapping storage _entry)
          internal
        {
          _entry.mainchainToken = address(0);
          _entry.sidechainToken = address(0);
          _entry.standard = 0;
        }
      }
      
      // File: contracts/chain/mainchain/MainchainGatewayStorage.sol
      
      pragma solidity ^0.5.17;
      
      
      
      
      
      
      
      /**
       * @title GatewayStorage
       * @dev Storage of deposit and withdraw information.
       */
      contract MainchainGatewayStorage is ProxyStorage, Pausable {
      
        event TokenDeposited(
          uint256 indexed _depositId,
          address indexed _owner,
          address indexed _tokenAddress,
          address _sidechainAddress,
          uint32  _standard,
          uint256 _tokenNumber // ERC-20 amount or ERC721 tokenId
        );
      
        event TokenWithdrew(
          uint256 indexed _withdrawId,
          address indexed _owner,
          address indexed _tokenAddress,
          uint256 _tokenNumber
        );
      
        struct DepositEntry {
          address owner;
          address tokenAddress;
          address sidechainAddress;
          uint32  standard;
          uint256 tokenNumber;
        }
      
        struct WithdrawalEntry {
          address owner;
          address tokenAddress;
          uint256 tokenNumber;
        }
      
        Registry public registry;
      
        uint256 public depositCount;
        DepositEntry[] public deposits;
        mapping(uint256 => WithdrawalEntry) public withdrawals;
      
        function updateRegistry(address _registry) external onlyAdmin {
          registry = Registry(_registry);
        }
      }
      
      // File: contracts/chain/mainchain/MainchainGatewayProxy.sol
      
      pragma solidity ^0.5.17;
      
      
      
      
      
      
      contract MainchainGatewayProxy is Proxy, MainchainGatewayStorage {
        constructor(address _proxyTo, address _registry)
          public
          Proxy(_proxyTo)
        {
          registry = Registry(_registry);
        }
      }

      File 2 of 5: SmoothLovePotion
      // File: math/SafeMath.sol
      
      pragma solidity 0.5.17;
      
      
      library SafeMath {
        function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
          c = a + b;
          require(c >= a, "SafeMath: addition overflow");
        }
      
        function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {
          require(b <= a, "SafeMath: subtraction overflow");
          return a - b;
        }
      
        function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
          if (a == 0) {
            return 0;
          }
      
          c = a * b;
          require(c / a == b, "SafeMath: multiplication overflow");
        }
      
        function div(uint256 a, uint256 b) internal pure returns (uint256 c) {
          // Since Solidity automatically asserts when dividing by 0,
          // but we only need it to revert.
          require(b > 0, "SafeMath: division by zero");
          return a / b;
        }
      
        function mod(uint256 a, uint256 b) internal pure returns (uint256 c) {
          // Same reason as `div`.
          require(b > 0, "SafeMath: modulo by zero");
          return a % b;
        }
      }
      
      // File: token/erc20/IERC20.sol
      
      pragma solidity 0.5.17;
      
      
      interface IERC20 {
        event Transfer(address indexed _from, address indexed _to, uint256 _value);
        event Approval(address indexed _owner, address indexed _spender, uint256 _value);
      
        function totalSupply() external view returns (uint256 _supply);
        function balanceOf(address _owner) external view returns (uint256 _balance);
      
        function approve(address _spender, uint256 _value) external returns (bool _success);
        function allowance(address _owner, address _spender) external view returns (uint256 _value);
      
        function transfer(address _to, uint256 _value) external returns (bool _success);
        function transferFrom(address _from, address _to, uint256 _value) external returns (bool _success);
      }
      
      // File: token/erc20/ERC20.sol
      
      pragma solidity 0.5.17;
      
      
      
      
      contract ERC20 is IERC20 {
        using SafeMath for uint256;
      
        uint256 public totalSupply;
        mapping (address => uint256) public balanceOf;
        mapping (address => mapping (address => uint256)) internal _allowance;
      
        function approve(address _spender, uint256 _value) public returns (bool) {
          _approve(msg.sender, _spender, _value);
          return true;
        }
      
        function allowance(address _owner, address _spender) public view returns (uint256) {
          return _allowance[_owner][_spender];
        }
      
        function increaseAllowance(address _spender, uint256 _value) public returns (bool) {
          _approve(msg.sender, _spender, _allowance[msg.sender][_spender].add(_value));
          return true;
        }
      
        function decreaseAllowance(address _spender, uint256 _value) public returns (bool) {
          _approve(msg.sender, _spender, _allowance[msg.sender][_spender].sub(_value));
          return true;
        }
      
        function transfer(address _to, uint256 _value) public returns (bool _success) {
          _transfer(msg.sender, _to, _value);
          return true;
        }
      
        function transferFrom(address _from, address _to, uint256 _value) public returns (bool _success) {
          _transfer(_from, _to, _value);
          _approve(_from, msg.sender, _allowance[_from][msg.sender].sub(_value));
          return true;
        }
      
        function _approve(address _owner, address _spender, uint256 _amount) internal {
          require(_owner != address(0), "ERC20: approve from the zero address");
          require(_spender != address(0), "ERC20: approve to the zero address");
      
          _allowance[_owner][_spender] = _amount;
          emit Approval(_owner, _spender, _amount);
        }
      
        function _transfer(address _from, address _to, uint256 _value) internal {
          require(_from != address(0), "ERC20: transfer from the zero address");
          require(_to != address(0), "ERC20: transfer to the zero address");
          require(_to != address(this), "ERC20: transfer to this contract address");
      
          balanceOf[_from] = balanceOf[_from].sub(_value);
          balanceOf[_to] = balanceOf[_to].add(_value);
          emit Transfer(_from, _to, _value);
        }
      }
      
      // File: token/erc20/IERC20Detailed.sol
      
      pragma solidity 0.5.17;
      
      
      interface IERC20Detailed {
        function name() external view returns (string memory _name);
        function symbol() external view returns (string memory _symbol);
        function decimals() external view returns (uint8 _decimals);
      }
      
      // File: token/erc20/ERC20Detailed.sol
      
      pragma solidity 0.5.17;
      
      
      
      
      contract ERC20Detailed is ERC20, IERC20Detailed {
        string public name;
        string public symbol;
        uint8 public decimals;
      
        constructor(string memory _name, string memory _symbol, uint8 _decimals) public {
          name = _name;
          symbol = _symbol;
          decimals = _decimals;
        }
      }
      
      // File: token/erc20/ERC20GatewayWhitelist.sol
      
      pragma solidity 0.5.17;
      
      
      contract ERC20GatewayWhitelist is ERC20 {
        address public mainchainGateway;
      
        function allowance(address _owner, address _spender)
          public
          view
          returns (uint256 _value)
        {
          if (_spender == mainchainGateway) return uint256(-1);
      
          return _allowance[_owner][_spender];
        }
      
        function transferFrom(
          address _from,
          address _to,
          uint256 _value
        )
          public
          returns (bool _success)
        {
          if (allowance(_from, msg.sender) != uint256(-1)) {
            super._approve(_from, msg.sender, _allowance[_from][msg.sender].sub(_value));
          }
      
          _transfer(_from, _to, _value);
          return true;
        }
      
        function _setGateway(address _mainchainGateway) internal {
          require(
            _mainchainGateway != address(0),
            "ERC20GatewayWhitelist: setting gateway to the zero address"
          );
          mainchainGateway = _mainchainGateway;
        }
      }
      
      // File: access/HasAdmin.sol
      
      pragma solidity 0.5.17;
      
      
      contract HasAdmin {
        event AdminChanged(address indexed _oldAdmin, address indexed _newAdmin);
        event AdminRemoved(address indexed _oldAdmin);
      
        address public admin;
      
        modifier onlyAdmin {
          require(msg.sender == admin, "HasAdmin: not admin");
          _;
        }
      
        constructor() internal {
          admin = msg.sender;
          emit AdminChanged(address(0), admin);
        }
      
        function changeAdmin(address _newAdmin) external onlyAdmin {
          require(_newAdmin != address(0), "HasAdmin: new admin is the zero address");
          emit AdminChanged(admin, _newAdmin);
          admin = _newAdmin;
        }
      
        function removeAdmin() external onlyAdmin {
          emit AdminRemoved(admin);
          admin = address(0);
        }
      }
      
      // File: access/HasMinters.sol
      
      pragma solidity 0.5.17;
      
      
      
      contract HasMinters is HasAdmin {
        event MinterAdded(address indexed _minter);
        event MinterRemoved(address indexed _minter);
      
        address[] public minters;
        mapping (address => bool) public minter;
      
        modifier onlyMinter {
          require(minter[msg.sender]);
          _;
        }
      
        function addMinters(address[] memory _addedMinters) public onlyAdmin {
          address _minter;
      
          for (uint256 i = 0; i < _addedMinters.length; i++) {
            _minter = _addedMinters[i];
      
            if (!minter[_minter]) {
              minters.push(_minter);
              minter[_minter] = true;
              emit MinterAdded(_minter);
            }
          }
        }
      
        function removeMinters(address[] memory _removedMinters) public onlyAdmin {
          address _minter;
      
          for (uint256 i = 0; i < _removedMinters.length; i++) {
            _minter = _removedMinters[i];
      
            if (minter[_minter]) {
              minter[_minter] = false;
              emit MinterRemoved(_minter);
            }
          }
      
          uint256 i = 0;
      
          while (i < minters.length) {
            _minter = minters[i];
      
            if (!minter[_minter]) {
              minters[i] = minters[minters.length - 1];
              delete minters[minters.length - 1];
              minters.length--;
            } else {
              i++;
            }
          }
        }
      
        function isMinter(address _addr) public view returns (bool) {
          return minter[_addr];
        }
      }
      
      // File: token/erc20/ERC20Mintable.sol
      
      pragma solidity 0.5.17;
      
      
      
      
      contract ERC20Mintable is HasMinters, ERC20 {
        function mint(address _to, uint256 _value) public onlyMinter returns (bool _success) {
          return _mint(_to, _value);
        }
      
        function _mint(address _to, uint256 _value) internal returns (bool success) {
          totalSupply = totalSupply.add(_value);
          balanceOf[_to] = balanceOf[_to].add(_value);
          emit Transfer(address(0), _to, _value);
          return true;
        }
      }
      
      // File: SmoothLovePotion.sol
      
      pragma solidity 0.5.17;
      
      
      
      
      
      contract SmoothLovePotion is ERC20Detailed, ERC20Mintable, ERC20GatewayWhitelist {
        constructor(address _mainchainGateway)
          public
          ERC20Detailed("Smooth Love Potion", "SLP", 0)
        {
          _setGateway(_mainchainGateway);
      
          address[] memory _minters = new address[](1);
          _minters[0] = _mainchainGateway;
          addMinters(_minters);
        }
      }

      File 3 of 5: MainchainGatewayManager
      // File: @axie/contract-library/contracts/cryptography/ECVerify.sol
      
      pragma solidity ^0.5.2;
      
      
      library ECVerify {
      
        enum SignatureMode {
          EIP712,
          GETH,
          TREZOR
        }
      
        function recover(bytes32 _hash, bytes memory _signature) internal pure returns (address _signer) {
          return recover(_hash, _signature, 0);
        }
      
        // solium-disable-next-line security/no-assign-params
        function recover(bytes32 _hash, bytes memory _signature, uint256 _index) internal pure returns (address _signer) {
          require(_signature.length >= _index + 66);
      
          SignatureMode _mode = SignatureMode(uint8(_signature[_index]));
          bytes32 _r;
          bytes32 _s;
          uint8 _v;
      
          // solium-disable-next-line security/no-inline-assembly
          assembly {
            _r := mload(add(_signature, add(_index, 33)))
            _s := mload(add(_signature, add(_index, 65)))
            _v := and(255, mload(add(_signature, add(_index, 66))))
          }
      
          if (_v < 27) {
            _v += 27;
          }
      
          require(_v == 27 || _v == 28);
      
          if (_mode == SignatureMode.GETH) {
            _hash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _hash));
          } else if (_mode == SignatureMode.TREZOR) {
            _hash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n\x20", _hash));
          }
      
          return ecrecover(_hash, _v, _r, _s);
        }
      
        function ecverify(bytes32 _hash, bytes memory _signature, address _signer) internal pure returns (bool _valid) {
          return _signer == recover(_hash, _signature);
        }
      }
      
      // File: @axie/contract-library/contracts/math/SafeMath.sol
      
      pragma solidity ^0.5.2;
      
      
      library SafeMath {
        function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
          c = a + b;
          require(c >= a);
        }
      
        function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {
          require(b <= a);
          return a - b;
        }
      
        function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
          if (a == 0) {
            return 0;
          }
      
          c = a * b;
          require(c / a == b);
        }
      
        function div(uint256 a, uint256 b) internal pure returns (uint256 c) {
          // Since Solidity automatically asserts when dividing by 0,
          // but we only need it to revert.
          require(b > 0);
          return a / b;
        }
      
        function mod(uint256 a, uint256 b) internal pure returns (uint256 c) {
          // Same reason as `div`.
          require(b > 0);
          return a % b;
        }
      
        function ceilingDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
          return add(div(a, b), mod(a, b) > 0 ? 1 : 0);
        }
      
        function subU64(uint64 a, uint64 b) internal pure returns (uint64 c) {
          require(b <= a);
          return a - b;
        }
      
        function addU8(uint8 a, uint8 b) internal pure returns (uint8 c) {
          c = a + b;
          require(c >= a);
        }
      }
      
      // File: @axie/contract-library/contracts/token/erc20/IERC20.sol
      
      pragma solidity ^0.5.2;
      
      
      interface IERC20 {
        event Transfer(address indexed _from, address indexed _to, uint256 _value);
        event Approval(address indexed _owner, address indexed _spender, uint256 _value);
      
        function totalSupply() external view returns (uint256 _supply);
        function balanceOf(address _owner) external view returns (uint256 _balance);
      
        function approve(address _spender, uint256 _value) external returns (bool _success);
        function allowance(address _owner, address _spender) external view returns (uint256 _value);
      
        function transfer(address _to, uint256 _value) external returns (bool _success);
        function transferFrom(address _from, address _to, uint256 _value) external returns (bool _success);
      }
      
      // File: @axie/contract-library/contracts/token/erc20/IERC20Mintable.sol
      
      pragma solidity ^0.5.2;
      
      interface IERC20Mintable {
        function mint(address _to, uint256 _value) external returns (bool _success);
      }
      
      // File: @axie/contract-library/contracts/token/erc721/IERC721.sol
      
      pragma solidity ^0.5.2;
      
      
      interface IERC721 {
        event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
        event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
        event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
      
        function balanceOf(address _owner) external view returns (uint256 _balance);
        function ownerOf(uint256 _tokenId) external view returns (address _owner);
      
        function approve(address _to, uint256 _tokenId) external;
        function getApproved(uint256 _tokenId) external view returns (address _operator);
      
        function setApprovalForAll(address _operator, bool _approved) external;
        function isApprovedForAll(address _owner, address _operator) external view returns (bool _approved);
      
        function transferFrom(address _from, address _to, uint256 _tokenId) external;
        function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;
        function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;
      }
      
      // File: @axie/contract-library/contracts/token/erc721/IERC721Mintable.sol
      
      pragma solidity ^0.5.2;
      
      
      interface IERC721Mintable {
        function mint(address _to, uint256 _tokenId) external returns (bool);
        function mintNew(address _to) external returns (uint256 _tokenId);
      }
      
      // File: @axie/contract-library/contracts/util/AddressUtils.sol
      
      pragma solidity ^0.5.2;
      
      
      library AddressUtils {
        function toPayable(address _address) internal pure returns (address payable _payable) {
          return address(uint160(_address));
        }
      
        function isContract(address _address) internal view returns (bool _correct) {
          uint256 _size;
          // solium-disable-next-line security/no-inline-assembly
          assembly { _size := extcodesize(_address) }
          return _size > 0;
        }
      }
      
      // File: @axie/contract-library/contracts/token/erc20/ERC20.sol
      
      pragma solidity ^0.5.2;
      
      
      
      
      contract ERC20 is IERC20 {
        using SafeMath for uint256;
      
        uint256 public totalSupply;
        mapping (address => uint256) public balanceOf;
        mapping (address => mapping (address => uint256)) public allowance;
      
        function approve(address _spender, uint256 _value) public returns (bool _success) {
          allowance[msg.sender][_spender] = _value;
          emit Approval(msg.sender, _spender, _value);
          return true;
        }
      
        function transfer(address _to, uint256 _value) public returns (bool _success) {
          require(_to != address(0));
          balanceOf[msg.sender] = balanceOf[msg.sender].sub(_value);
          balanceOf[_to] = balanceOf[_to].add(_value);
          emit Transfer(msg.sender, _to, _value);
          return true;
        }
      
        function transferFrom(address _from, address _to, uint256 _value) public returns (bool _success) {
          require(_to != address(0));
          balanceOf[_from] = balanceOf[_from].sub(_value);
          balanceOf[_to] = balanceOf[_to].add(_value);
          allowance[_from][msg.sender] = allowance[_from][msg.sender].sub(_value);
          emit Transfer(_from, _to, _value);
          return true;
        }
      }
      
      // File: @axie/contract-library/contracts/token/erc20/IERC20Detailed.sol
      
      pragma solidity ^0.5.2;
      
      
      interface IERC20Detailed {
        function name() external view returns (string memory _name);
        function symbol() external view returns (string memory _symbol);
        function decimals() external view returns (uint8 _decimals);
      }
      
      // File: @axie/contract-library/contracts/token/erc20/ERC20Detailed.sol
      
      pragma solidity ^0.5.2;
      
      
      
      
      contract ERC20Detailed is ERC20, IERC20Detailed {
        string public name;
        string public symbol;
        uint8 public decimals;
      
        constructor(string memory _name, string memory _symbol, uint8 _decimals) public {
          name = _name;
          symbol = _symbol;
          decimals = _decimals;
        }
      }
      
      // File: @axie/contract-library/contracts/access/HasAdmin.sol
      
      pragma solidity ^0.5.2;
      
      
      contract HasAdmin {
        event AdminChanged(address indexed _oldAdmin, address indexed _newAdmin);
        event AdminRemoved(address indexed _oldAdmin);
      
        address public admin;
      
        modifier onlyAdmin {
          require(msg.sender == admin);
          _;
        }
      
        constructor() internal {
          admin = msg.sender;
          emit AdminChanged(address(0), admin);
        }
      
        function changeAdmin(address _newAdmin) external onlyAdmin {
          require(_newAdmin != address(0));
          emit AdminChanged(admin, _newAdmin);
          admin = _newAdmin;
        }
      
        function removeAdmin() external onlyAdmin {
          emit AdminRemoved(admin);
          admin = address(0);
        }
      }
      
      // File: @axie/contract-library/contracts/access/HasMinters.sol
      
      pragma solidity ^0.5.2;
      
      
      
      contract HasMinters is HasAdmin {
        event MinterAdded(address indexed _minter);
        event MinterRemoved(address indexed _minter);
      
        address[] public minters;
        mapping (address => bool) public minter;
      
        modifier onlyMinter {
          require(minter[msg.sender]);
          _;
        }
      
        function addMinters(address[] memory _addedMinters) public onlyAdmin {
          address _minter;
      
          for (uint256 i = 0; i < _addedMinters.length; i++) {
            _minter = _addedMinters[i];
      
            if (!minter[_minter]) {
              minters.push(_minter);
              minter[_minter] = true;
              emit MinterAdded(_minter);
            }
          }
        }
      
        function removeMinters(address[] memory _removedMinters) public onlyAdmin {
          address _minter;
      
          for (uint256 i = 0; i < _removedMinters.length; i++) {
            _minter = _removedMinters[i];
      
            if (minter[_minter]) {
              minter[_minter] = false;
              emit MinterRemoved(_minter);
            }
          }
      
          uint256 i = 0;
      
          while (i < minters.length) {
            _minter = minters[i];
      
            if (!minter[_minter]) {
              minters[i] = minters[minters.length - 1];
              delete minters[minters.length - 1];
              minters.length--;
            } else {
              i++;
            }
          }
        }
      
        function isMinter(address _addr) public view returns (bool) {
          return minter[_addr];
        }
      }
      
      // File: @axie/contract-library/contracts/token/erc20/ERC20Mintable.sol
      
      pragma solidity ^0.5.2;
      
      
      
      
      contract ERC20Mintable is HasMinters, ERC20 {
        function mint(address _to, uint256 _value) public onlyMinter returns (bool _success) {
          return _mint(_to, _value);
        }
      
        function _mint(address _to, uint256 _value) internal returns (bool success) {
          totalSupply = totalSupply.add(_value);
          balanceOf[_to] = balanceOf[_to].add(_value);
          emit Transfer(address(0), _to, _value);
          return true;
        }
      }
      
      // File: contracts/chain/mainchain/WETH.sol
      
      pragma solidity ^0.5.17;
      
      
      
      
      contract WETH is ERC20Detailed {
      
        event Deposit(
          address _sender,
          uint256 _value
        );
      
        event Withdrawal(
          address _sender,
          uint256 _value
        );
      
        constructor () ERC20Detailed("Wrapped Ether", "WETH", 18)
          public
        {}
      
        function deposit()
          external
          payable
        {
          balanceOf[msg.sender] += msg.value;
      
          emit Deposit(msg.sender, msg.value);
        }
      
        function withdraw(uint256 _wad)
          external
        {
          require(balanceOf[msg.sender] >= _wad);
          balanceOf[msg.sender] -= _wad;
          msg.sender.transfer(_wad);
      
          emit Withdrawal(msg.sender, _wad);
        }
      }
      
      // File: @axie/contract-library/contracts/proxy/ProxyStorage.sol
      
      pragma solidity ^0.5.2;
      
      /**
       * @title ProxyStorage
       * @dev Store the address of logic contact that the proxy should forward to.
       */
      contract ProxyStorage is HasAdmin {
        address internal _proxyTo;
      }
      
      // File: @axie/contract-library/contracts/lifecycle/Pausable.sol
      
      pragma solidity ^0.5.2;
      
      
      
      contract Pausable is HasAdmin {
        event Paused();
        event Unpaused();
      
        bool public paused;
      
        modifier whenNotPaused() {
          require(!paused);
          _;
        }
      
        modifier whenPaused() {
          require(paused);
          _;
        }
      
        function pause() public onlyAdmin whenNotPaused {
          paused = true;
          emit Paused();
        }
      
        function unpause() public onlyAdmin whenPaused {
          paused = false;
          emit Unpaused();
        }
      }
      
      // File: contracts/chain/common/IValidator.sol
      
      pragma solidity ^0.5.17;
      
      
      contract IValidator {
        event ValidatorAdded(uint256 indexed _id, address indexed _validator);
        event ValidatorRemoved(uint256 indexed _id, address indexed _validator);
        event ThresholdUpdated(
          uint256 indexed _id,
          uint256 indexed _numerator,
          uint256 indexed _denominator,
          uint256 _previousNumerator,
          uint256 _previousDenominator
        );
      
        function isValidator(address _addr) public view returns (bool);
        function getValidators() public view returns (address[] memory _validators);
      
        function checkThreshold(uint256 _voteCount) public view returns (bool);
      }
      
      // File: contracts/chain/common/Validator.sol
      
      pragma solidity ^0.5.17;
      
      
      
      
      contract Validator is IValidator {
        using SafeMath for uint256;
      
        mapping(address => bool) validatorMap;
        address[] public validators;
        uint256 public validatorCount;
      
        uint256 public num;
        uint256 public denom;
      
        constructor(address[] memory _validators, uint256 _num, uint256 _denom)
          public
        {
          validators = _validators;
          validatorCount = _validators.length;
      
          for (uint256 _i = 0; _i < validatorCount; _i++) {
            address _validator = _validators[_i];
            validatorMap[_validator] = true;
          }
      
          num = _num;
          denom = _denom;
        }
      
        function isValidator(address _addr)
          public
          view
          returns (bool)
        {
          return validatorMap[_addr];
        }
      
        function getValidators()
          public
          view
          returns (address[] memory _validators)
        {
          _validators = validators;
        }
      
        function checkThreshold(uint256 _voteCount)
          public
          view
          returns (bool)
        {
          return _voteCount.mul(denom) >= num.mul(validatorCount);
        }
      
        function _addValidator(uint256 _id, address _validator)
          internal
        {
          require(!validatorMap[_validator]);
      
          validators.push(_validator);
          validatorMap[_validator] = true;
          validatorCount++;
      
          emit ValidatorAdded(_id, _validator);
        }
      
        function _removeValidator(uint256 _id, address _validator)
          internal
        {
          require(isValidator(_validator));
      
          uint256 _index;
          for (uint256 _i = 0; _i < validatorCount; _i++) {
            if (validators[_i] == _validator) {
              _index = _i;
              break;
            }
          }
      
          validatorMap[_validator] = false;
          validators[_index] = validators[validatorCount - 1];
          validators.pop();
      
          validatorCount--;
      
          emit ValidatorRemoved(_id, _validator);
        }
      
        function _updateQuorum(uint256 _id, uint256 _numerator, uint256 _denominator)
          internal
        {
          require(_numerator <= _denominator);
          uint256 _previousNumerator = num;
          uint256 _previousDenominator = denom;
      
          num = _numerator;
          denom = _denominator;
      
          emit ThresholdUpdated(
            _id,
            _numerator,
            _denominator,
            _previousNumerator,
            _previousDenominator
          );
        }
      }
      
      // File: contracts/chain/mainchain/MainchainValidator.sol
      
      pragma solidity ^0.5.17;
      
      
      
      
      /**
       * @title Validator
       * @dev Simple validator contract
       */
      contract MainchainValidator is Validator, HasAdmin {
        uint256 nonce;
      
        constructor(
          address[] memory _validators,
          uint256 _num,
          uint256 _denom
        ) Validator(_validators, _num, _denom) public {
        }
      
        function addValidators(address[] calldata _validators) external onlyAdmin {
          for (uint256 _i; _i < _validators.length; ++_i) {
            _addValidator(nonce++, _validators[_i]);
          }
        }
      
        function removeValidator(address _validator) external onlyAdmin {
          _removeValidator(nonce++, _validator);
        }
      
        function updateQuorum(uint256 _numerator, uint256 _denominator) external onlyAdmin {
          _updateQuorum(nonce++, _numerator, _denominator);
        }
      }
      
      // File: contracts/chain/common/Registry.sol
      
      pragma solidity ^0.5.17;
      
      
      
      contract Registry is HasAdmin {
      
        event ContractAddressUpdated(
          string indexed _name,
          bytes32 indexed _code,
          address indexed _newAddress
        );
      
        event TokenMapped(
          address indexed _mainchainToken,
          address indexed _sidechainToken,
          uint32 _standard
        );
      
        string public constant GATEWAY = "GATEWAY";
        string public constant WETH_TOKEN = "WETH_TOKEN";
        string public constant VALIDATOR = "VALIDATOR";
        string public constant ACKNOWLEDGEMENT = "ACKNOWLEDGEMENT";
      
        struct TokenMapping {
          address mainchainToken;
          address sidechainToken;
          uint32 standard; // 20, 721 or any other standards
        }
      
        mapping(bytes32 => address) public contractAddresses;
        mapping(address => TokenMapping) public mainchainMap;
        mapping(address => TokenMapping) public sidechainMap;
      
        function getContract(string calldata _name)
          external
          view
          returns (address _address)
        {
          bytes32 _code = getCode(_name);
          _address = contractAddresses[_code];
          require(_address != address(0));
        }
      
        function isTokenMapped(address _token, uint32 _standard, bool _isMainchain)
          external
          view
          returns (bool)
        {
          TokenMapping memory _mapping = _getTokenMapping(_token, _isMainchain);
      
          return _mapping.mainchainToken != address(0) &&
            _mapping.sidechainToken != address(0) &&
            _mapping.standard == _standard;
        }
      
        function updateContract(string calldata _name, address _newAddress)
          external
          onlyAdmin
        {
          bytes32 _code = getCode(_name);
          contractAddresses[_code] = _newAddress;
      
          emit ContractAddressUpdated(_name, _code, _newAddress);
        }
      
        function mapToken(address _mainchainToken, address _sidechainToken, uint32 _standard)
          external
          onlyAdmin
        {
          TokenMapping memory _map = TokenMapping(
            _mainchainToken,
            _sidechainToken,
            _standard
          );
      
          mainchainMap[_mainchainToken] = _map;
          sidechainMap[_sidechainToken] = _map;
      
          emit TokenMapped(
            _mainchainToken,
            _sidechainToken,
            _standard
          );
        }
      
        function clearMapToken(address _mainchainToken, address _sidechainToken)
          external
          onlyAdmin
        {
          TokenMapping storage _mainchainMap = mainchainMap[_mainchainToken];
          _clearMapEntry(_mainchainMap);
      
          TokenMapping storage _sidechainMap = sidechainMap[_sidechainToken];
          _clearMapEntry(_sidechainMap);
        }
      
        function getMappedToken(
          address _token,
          bool _isMainchain
        )
          external
          view
        returns (
          address _mainchainToken,
          address _sidechainToken,
          uint32 _standard
        )
        {
          TokenMapping memory _mapping = _getTokenMapping(_token, _isMainchain);
          _mainchainToken = _mapping.mainchainToken;
          _sidechainToken = _mapping.sidechainToken;
          _standard = _mapping.standard;
        }
      
        function getCode(string memory _name)
          public
          pure
          returns (bytes32)
        {
          return keccak256(abi.encodePacked(_name));
        }
      
        function _getTokenMapping(
          address _token,
          bool isMainchain
        )
          internal
          view
          returns (TokenMapping memory _mapping)
        {
          if (isMainchain) {
            _mapping = mainchainMap[_token];
          } else {
            _mapping = sidechainMap[_token];
          }
        }
      
        function _clearMapEntry(TokenMapping storage _entry)
          internal
        {
          _entry.mainchainToken = address(0);
          _entry.sidechainToken = address(0);
          _entry.standard = 0;
        }
      }
      
      // File: contracts/chain/mainchain/MainchainGatewayStorage.sol
      
      pragma solidity ^0.5.17;
      
      
      
      
      
      
      
      /**
       * @title GatewayStorage
       * @dev Storage of deposit and withdraw information.
       */
      contract MainchainGatewayStorage is ProxyStorage, Pausable {
      
        event TokenDeposited(
          uint256 indexed _depositId,
          address indexed _owner,
          address indexed _tokenAddress,
          address _sidechainAddress,
          uint32  _standard,
          uint256 _tokenNumber // ERC-20 amount or ERC721 tokenId
        );
      
        event TokenWithdrew(
          uint256 indexed _withdrawId,
          address indexed _owner,
          address indexed _tokenAddress,
          uint256 _tokenNumber
        );
      
        struct DepositEntry {
          address owner;
          address tokenAddress;
          address sidechainAddress;
          uint32  standard;
          uint256 tokenNumber;
        }
      
        struct WithdrawalEntry {
          address owner;
          address tokenAddress;
          uint256 tokenNumber;
        }
      
        Registry public registry;
      
        uint256 public depositCount;
        DepositEntry[] public deposits;
        mapping(uint256 => WithdrawalEntry) public withdrawals;
      
        function updateRegistry(address _registry) external onlyAdmin {
          registry = Registry(_registry);
        }
      }
      
      // File: contracts/chain/mainchain/MainchainGatewayManager.sol
      
      pragma solidity ^0.5.17;
      
      
      
      
      
      
      
      
      
      
      
      /**
       * @title MainchainGatewayManager
       * @dev Logic to handle deposits and withdrawl on Mainchain.
       */
      contract MainchainGatewayManager is MainchainGatewayStorage {
        using AddressUtils for address;
        using SafeMath for uint256;
        using ECVerify for bytes32;
      
        modifier onlyMappedToken(address _token, uint32 _standard) {
          require(
            registry.isTokenMapped(_token, _standard, true),
            "MainchainGatewayManager: Token is not mapped"
          );
          _;
        }
      
        modifier onlyNewWithdrawal(uint256 _withdrawalId) {
          WithdrawalEntry storage _entry = withdrawals[_withdrawalId];
          require(_entry.owner == address(0) && _entry.tokenAddress == address(0));
          _;
        }
      
        // Should be able to withdraw from WETH
        function()
          external
          payable
        {}
      
        function depositEth()
          external
          whenNotPaused
          payable
          returns (uint256)
        {
          return depositEthFor(msg.sender);
        }
      
        function depositERC20(address _token, uint256 _amount)
          external
          whenNotPaused
          returns (uint256)
        {
          return depositERC20For(msg.sender, _token, _amount);
        }
      
        function depositERC721(address _token, uint256 _tokenId)
          external
          whenNotPaused
          returns (uint256)
        {
          return depositERC721For(msg.sender, _token, _tokenId);
        }
      
        function depositEthFor(address _owner)
          public
          whenNotPaused
          payable
          returns (uint256)
        {
          address _weth = registry.getContract(registry.WETH_TOKEN());
          WETH(_weth).deposit.value(msg.value)();
          return _createDepositEntry(_owner, _weth, 20, msg.value);
        }
      
        function depositERC20For(address _user, address _token, uint256 _amount)
          public
          whenNotPaused
          returns (uint256)
        {
          require(
            IERC20(_token).transferFrom(msg.sender, address(this), _amount),
            "MainchainGatewayManager: ERC-20 token transfer failed"
          );
          return _createDepositEntry(_user, _token, 20, _amount);
        }
      
        function depositERC721For(address _user, address _token, uint256 _tokenId)
          public
          whenNotPaused
          returns (uint256)
        {
          IERC721(_token).transferFrom(msg.sender, address(this), _tokenId);
          return _createDepositEntry(_user, _token, 721, _tokenId);
        }
      
        function depositBulkFor(
          address _user,
          address[] memory _tokens,
          uint256[] memory _tokenNumbers
        )
          public
          whenNotPaused
        {
          require(_tokens.length == _tokenNumbers.length);
      
          for (uint256 _i = 0; _i < _tokens.length; _i++) {
            address _token = _tokens[_i];
            uint256 _tokenNumber = _tokenNumbers[_i];
            (,, uint32 _standard) = registry.getMappedToken(_token, true);
      
            if (_standard == 20) {
              depositERC20For(_user, _token, _tokenNumber);
            } else if (_standard == 721) {
              depositERC721For(_user, _token, _tokenNumber);
            } else {
              revert("Token is not mapped or token type not supported");
            }
          }
        }
      
        function withdrawToken(
          uint256 _withdrawalId,
          address _token,
          uint256 _amount,
          bytes memory _signatures
        )
          public
          whenNotPaused
        {
          withdrawTokenFor(
            _withdrawalId,
            msg.sender,
            _token,
            _amount,
            _signatures
          );
        }
      
        function withdrawTokenFor(
          uint256 _withdrawalId,
          address _user,
          address _token,
          uint256 _amount,
          bytes memory _signatures
        )
          public
          whenNotPaused
        {
          (,, uint32 _tokenType) = registry.getMappedToken(_token, true);
      
          if (_tokenType == 20) {
            withdrawERC20For(
              _withdrawalId,
              _user,
              _token,
              _amount,
              _signatures
            );
          } else if (_tokenType == 721) {
            withdrawERC721For(
              _withdrawalId,
              _user,
              _token,
              _amount,
              _signatures
            );
          }
        }
      
        function withdrawERC20(
          uint256 _withdrawalId,
          address _token,
          uint256 _amount,
          bytes memory _signatures
        )
          public
          whenNotPaused
        {
          withdrawERC20For(
            _withdrawalId,
            msg.sender,
            _token,
            _amount,
            _signatures
          );
        }
      
        function withdrawERC20For(
          uint256 _withdrawalId,
          address _user,
          address _token,
          uint256 _amount,
          bytes memory _signatures
        )
          public
          whenNotPaused
          onlyMappedToken(_token, 20)
        {
          bytes32 _hash = keccak256(
            abi.encodePacked(
              "withdrawERC20",
              _withdrawalId,
              _user,
              _token,
              _amount
            )
          );
      
          require(verifySignatures(_hash, _signatures));
      
          if (_token == registry.getContract(registry.WETH_TOKEN())) {
            _withdrawETHFor(_user, _amount);
          } else {
            uint256 _gatewayBalance = IERC20(_token).balanceOf(address(this));
      
            if (_gatewayBalance < _amount) {
              require(
                IERC20Mintable(_token).mint(address(this), _amount.sub(_gatewayBalance)),
                "MainchainGatewayManager: Minting ERC20 token to gateway failed"
              );
            }
      
            require(IERC20(_token).transfer(_user, _amount), "Transfer failed");
          }
      
          _insertWithdrawalEntry(
            _withdrawalId,
            _user,
            _token,
            _amount
          );
        }
      
        function withdrawERC721(
          uint256 _withdrawalId,
          address _token,
          uint256 _tokenId,
          bytes memory _signatures
        )
          public
          whenNotPaused
        {
          withdrawERC721For(
            _withdrawalId,
            msg.sender,
            _token,
            _tokenId,
            _signatures
          );
        }
      
        function withdrawERC721For(
          uint256 _withdrawalId,
          address _user,
          address _token,
          uint256 _tokenId,
          bytes memory _signatures
        )
          public
          whenNotPaused
          onlyMappedToken(_token, 721)
        {
          bytes32 _hash = keccak256(
            abi.encodePacked(
              "withdrawERC721",
              _withdrawalId,
              _user,
              _token,
              _tokenId
            )
          );
      
          require(verifySignatures(_hash, _signatures));
      
          if (!_tryERC721TransferFrom(_token, address(this), _user, _tokenId)) {
            require(
              IERC721Mintable(_token).mint(_user, _tokenId),
              "MainchainGatewayManager: Minting ERC721 token to gateway failed"
            );
          }
      
          _insertWithdrawalEntry(_withdrawalId, _user, _token, _tokenId);
        }
      
        /**
         * @dev returns true if there is enough signatures from validators.
         */
        function verifySignatures(
          bytes32 _hash,
          bytes memory _signatures
        )
          public
          view
          returns (bool)
        {
          uint256 _signatureCount = _signatures.length.div(66);
      
          Validator _validator = Validator(registry.getContract(registry.VALIDATOR()));
          uint256 _validatorCount = 0;
          address _lastSigner = address(0);
      
          for (uint256 i = 0; i < _signatureCount; i++) {
            address _signer = _hash.recover(_signatures, i.mul(66));
            if (_validator.isValidator(_signer)) {
              _validatorCount++;
            }
            // Prevent duplication of signatures
            require(_signer > _lastSigner);
            _lastSigner = _signer;
          }
      
          return _validator.checkThreshold(_validatorCount);
        }
      
        function _createDepositEntry(
          address _owner,
          address _token,
          uint32 _standard,
          uint256 _number
        )
          internal
          onlyMappedToken(_token, _standard)
          returns (uint256 _depositId)
        {
          (,address _sidechainToken, uint32 _tokenStandard) = registry.getMappedToken(_token, true);
          require(_standard == _tokenStandard);
      
          DepositEntry memory _entry = DepositEntry(
            _owner,
            _token,
            _sidechainToken,
            _standard,
            _number
          );
      
          deposits.push(_entry);
          _depositId = depositCount++;
      
          emit TokenDeposited(
            _depositId,
            _owner,
            _token,
            _sidechainToken,
            _standard,
            _number
          );
        }
      
        function _insertWithdrawalEntry(
          uint256 _withdrawalId,
          address _owner,
          address _token,
          uint256 _number
        )
          internal
          onlyNewWithdrawal(_withdrawalId)
        {
          WithdrawalEntry memory _entry = WithdrawalEntry(
            _owner,
            _token,
            _number
          );
      
          withdrawals[_withdrawalId] = _entry;
      
          emit TokenWithdrew(_withdrawalId, _owner, _token, _number);
        }
      
        function _withdrawETHFor(
          address _user,
          uint256 _amount
        )
          internal
        {
          address _weth = registry.getContract(registry.WETH_TOKEN());
          WETH(_weth).withdraw(_amount);
          _user.toPayable().transfer(_amount);
        }
      
        // See more here https://blog.polymath.network/try-catch-in-solidity-handling-the-revert-exception-f53718f76047
        function _tryERC721TransferFrom(
          address _token,
          address _from,
          address _to,
          uint256 _tokenId
        )
          internal
          returns (bool)
        {
          (bool success,) = _token.call(
            abi.encodeWithSelector(
              IERC721(_token).transferFrom.selector, _from, _to, _tokenId
            )
          );
          return success;
        }
      }

      File 4 of 5: Registry
      // File: @axie/contract-library/contracts/access/HasAdmin.sol
      
      pragma solidity ^0.5.2;
      
      
      contract HasAdmin {
        event AdminChanged(address indexed _oldAdmin, address indexed _newAdmin);
        event AdminRemoved(address indexed _oldAdmin);
      
        address public admin;
      
        modifier onlyAdmin {
          require(msg.sender == admin);
          _;
        }
      
        constructor() internal {
          admin = msg.sender;
          emit AdminChanged(address(0), admin);
        }
      
        function changeAdmin(address _newAdmin) external onlyAdmin {
          require(_newAdmin != address(0));
          emit AdminChanged(admin, _newAdmin);
          admin = _newAdmin;
        }
      
        function removeAdmin() external onlyAdmin {
          emit AdminRemoved(admin);
          admin = address(0);
        }
      }
      // File: contracts/chain/common/Registry.sol
      
      pragma solidity ^0.5.17;
      
      
      
      contract Registry is HasAdmin {
      
        event ContractAddressUpdated(
          string indexed _name,
          bytes32 indexed _code,
          address indexed _newAddress
        );
      
        event TokenMapped(
          address indexed _mainchainToken,
          address indexed _sidechainToken,
          uint32 _standard
        );
      
        string public constant GATEWAY = "GATEWAY";
        string public constant WETH_TOKEN = "WETH_TOKEN";
        string public constant VALIDATOR = "VALIDATOR";
        string public constant ACKNOWLEDGEMENT = "ACKNOWLEDGEMENT";
      
        struct TokenMapping {
          address mainchainToken;
          address sidechainToken;
          uint32 standard; // 20, 721 or any other standards
        }
      
        mapping(bytes32 => address) public contractAddresses;
        mapping(address => TokenMapping) public mainchainMap;
        mapping(address => TokenMapping) public sidechainMap;
      
        function getContract(string calldata _name)
          external
          view
          returns (address _address)
        {
          bytes32 _code = getCode(_name);
          _address = contractAddresses[_code];
          require(_address != address(0));
        }
      
        function isTokenMapped(address _token, uint32 _standard, bool _isMainchain)
          external
          view
          returns (bool)
        {
          TokenMapping memory _mapping = _getTokenMapping(_token, _isMainchain);
      
          return _mapping.mainchainToken != address(0) &&
            _mapping.sidechainToken != address(0) &&
            _mapping.standard == _standard;
        }
      
        function updateContract(string calldata _name, address _newAddress)
          external
          onlyAdmin
        {
          bytes32 _code = getCode(_name);
          contractAddresses[_code] = _newAddress;
      
          emit ContractAddressUpdated(_name, _code, _newAddress);
        }
      
        function mapToken(address _mainchainToken, address _sidechainToken, uint32 _standard)
          external
          onlyAdmin
        {
          TokenMapping memory _map = TokenMapping(
            _mainchainToken,
            _sidechainToken,
            _standard
          );
      
          mainchainMap[_mainchainToken] = _map;
          sidechainMap[_sidechainToken] = _map;
      
          emit TokenMapped(
            _mainchainToken,
            _sidechainToken,
            _standard
          );
        }
      
        function clearMapToken(address _mainchainToken, address _sidechainToken)
          external
          onlyAdmin
        {
          TokenMapping storage _mainchainMap = mainchainMap[_mainchainToken];
          _clearMapEntry(_mainchainMap);
      
          TokenMapping storage _sidechainMap = sidechainMap[_sidechainToken];
          _clearMapEntry(_sidechainMap);
        }
      
        function getMappedToken(
          address _token,
          bool _isMainchain
        )
          external
          view
        returns (
          address _mainchainToken,
          address _sidechainToken,
          uint32 _standard
        )
        {
          TokenMapping memory _mapping = _getTokenMapping(_token, _isMainchain);
          _mainchainToken = _mapping.mainchainToken;
          _sidechainToken = _mapping.sidechainToken;
          _standard = _mapping.standard;
        }
      
        function getCode(string memory _name)
          public
          pure
          returns (bytes32)
        {
          return keccak256(abi.encodePacked(_name));
        }
      
        function _getTokenMapping(
          address _token,
          bool isMainchain
        )
          internal
          view
          returns (TokenMapping memory _mapping)
        {
          if (isMainchain) {
            _mapping = mainchainMap[_token];
          } else {
            _mapping = sidechainMap[_token];
          }
        }
      
        function _clearMapEntry(TokenMapping storage _entry)
          internal
        {
          _entry.mainchainToken = address(0);
          _entry.sidechainToken = address(0);
          _entry.standard = 0;
        }
      }

      File 5 of 5: MainchainValidator
      // File: @axie/contract-library/contracts/math/SafeMath.sol
      
      pragma solidity ^0.5.2;
      
      
      library SafeMath {
        function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
          c = a + b;
          require(c >= a);
        }
      
        function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {
          require(b <= a);
          return a - b;
        }
      
        function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
          if (a == 0) {
            return 0;
          }
      
          c = a * b;
          require(c / a == b);
        }
      
        function div(uint256 a, uint256 b) internal pure returns (uint256 c) {
          // Since Solidity automatically asserts when dividing by 0,
          // but we only need it to revert.
          require(b > 0);
          return a / b;
        }
      
        function mod(uint256 a, uint256 b) internal pure returns (uint256 c) {
          // Same reason as `div`.
          require(b > 0);
          return a % b;
        }
      
        function ceilingDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
          return add(div(a, b), mod(a, b) > 0 ? 1 : 0);
        }
      
        function subU64(uint64 a, uint64 b) internal pure returns (uint64 c) {
          require(b <= a);
          return a - b;
        }
      
        function addU8(uint8 a, uint8 b) internal pure returns (uint8 c) {
          c = a + b;
          require(c >= a);
        }
      }
      
      // File: @axie/contract-library/contracts/access/HasAdmin.sol
      
      pragma solidity ^0.5.2;
      
      
      contract HasAdmin {
        event AdminChanged(address indexed _oldAdmin, address indexed _newAdmin);
        event AdminRemoved(address indexed _oldAdmin);
      
        address public admin;
      
        modifier onlyAdmin {
          require(msg.sender == admin);
          _;
        }
      
        constructor() internal {
          admin = msg.sender;
          emit AdminChanged(address(0), admin);
        }
      
        function changeAdmin(address _newAdmin) external onlyAdmin {
          require(_newAdmin != address(0));
          emit AdminChanged(admin, _newAdmin);
          admin = _newAdmin;
        }
      
        function removeAdmin() external onlyAdmin {
          emit AdminRemoved(admin);
          admin = address(0);
        }
      }
      
      // File: contracts/chain/common/IValidator.sol
      
      pragma solidity ^0.5.17;
      
      
      contract IValidator {
        event ValidatorAdded(uint256 indexed _id, address indexed _validator);
        event ValidatorRemoved(uint256 indexed _id, address indexed _validator);
        event ThresholdUpdated(
          uint256 indexed _id,
          uint256 indexed _numerator,
          uint256 indexed _denominator,
          uint256 _previousNumerator,
          uint256 _previousDenominator
        );
      
        function isValidator(address _addr) public view returns (bool);
        function getValidators() public view returns (address[] memory _validators);
      
        function checkThreshold(uint256 _voteCount) public view returns (bool);
      }
      
      // File: contracts/chain/common/Validator.sol
      
      pragma solidity ^0.5.17;
      
      
      
      
      contract Validator is IValidator {
        using SafeMath for uint256;
      
        mapping(address => bool) validatorMap;
        address[] public validators;
        uint256 public validatorCount;
      
        uint256 public num;
        uint256 public denom;
      
        constructor(address[] memory _validators, uint256 _num, uint256 _denom)
          public
        {
          validators = _validators;
          validatorCount = _validators.length;
      
          for (uint256 _i = 0; _i < validatorCount; _i++) {
            address _validator = _validators[_i];
            validatorMap[_validator] = true;
          }
      
          num = _num;
          denom = _denom;
        }
      
        function isValidator(address _addr)
          public
          view
          returns (bool)
        {
          return validatorMap[_addr];
        }
      
        function getValidators()
          public
          view
          returns (address[] memory _validators)
        {
          _validators = validators;
        }
      
        function checkThreshold(uint256 _voteCount)
          public
          view
          returns (bool)
        {
          return _voteCount.mul(denom) >= num.mul(validatorCount);
        }
      
        function _addValidator(uint256 _id, address _validator)
          internal
        {
          require(!validatorMap[_validator]);
      
          validators.push(_validator);
          validatorMap[_validator] = true;
          validatorCount++;
      
          emit ValidatorAdded(_id, _validator);
        }
      
        function _removeValidator(uint256 _id, address _validator)
          internal
        {
          require(isValidator(_validator));
      
          uint256 _index;
          for (uint256 _i = 0; _i < validatorCount; _i++) {
            if (validators[_i] == _validator) {
              _index = _i;
              break;
            }
          }
      
          validatorMap[_validator] = false;
          validators[_index] = validators[validatorCount - 1];
          validators.pop();
      
          validatorCount--;
      
          emit ValidatorRemoved(_id, _validator);
        }
      
        function _updateQuorum(uint256 _id, uint256 _numerator, uint256 _denominator)
          internal
        {
          require(_numerator <= _denominator);
          uint256 _previousNumerator = num;
          uint256 _previousDenominator = denom;
      
          num = _numerator;
          denom = _denominator;
      
          emit ThresholdUpdated(
            _id,
            _numerator,
            _denominator,
            _previousNumerator,
            _previousDenominator
          );
        }
      }
      
      // File: contracts/chain/mainchain/MainchainValidator.sol
      
      pragma solidity ^0.5.17;
      
      
      
      
      /**
       * @title Validator
       * @dev Simple validator contract
       */
      contract MainchainValidator is Validator, HasAdmin {
        uint256 nonce;
      
        constructor(
          address[] memory _validators,
          uint256 _num,
          uint256 _denom
        ) Validator(_validators, _num, _denom) public {
        }
      
        function addValidators(address[] calldata _validators) external onlyAdmin {
          for (uint256 _i; _i < _validators.length; ++_i) {
            _addValidator(nonce++, _validators[_i]);
          }
        }
      
        function removeValidator(address _validator) external onlyAdmin {
          _removeValidator(nonce++, _validator);
        }
      
        function updateQuorum(uint256 _numerator, uint256 _denominator) external onlyAdmin {
          _updateQuorum(nonce++, _numerator, _denominator);
        }
      }