ETH Price: $2,438.08 (-4.55%)

Transaction Decoder

Block:
18888176 at Dec-29-2023 02:10:23 AM +UTC
Transaction Fee:
0.003396108776329722 ETH $8.28
Gas Used:
173,721 Gas / 19.549212682 Gwei

Emitted Events:

254 Dai.Transfer( src=[Receiver] EternalStorageProxy, dst=[Sender] 0xb63a5c5710f06e111fa14ac82da2183c5102b504, wad=50000000000000000000 )
255 EternalStorageProxy.0x4ab7d581336d92edbea22636a613e8e76c99ac7f91137c1523db38dbfb3bf329( 0x4ab7d581336d92edbea22636a613e8e76c99ac7f91137c1523db38dbfb3bf329, 000000000000000000000000b63a5c5710f06e111fa14ac82da2183c5102b504, 000000000000000000000000000000000000000000000002b5e3af16b1880000, 7c724c734b50f5f4b831eea75d5969935cfe5c23a1637112abfb64741bc57e61 )

Account State Difference:

  Address   Before After State Difference Code
0x4aa42145...788045016
(Gnosis Chain: xDai Bridge)
0x6B175474...495271d0F
(beaverbuild)
18.158451055988044754 Eth18.158468428088044754 Eth0.0000173721
0xb63a5C57...C5102B504
0.25419362429404901 Eth
Nonce: 2437
0.250797515517719288 Eth
Nonce: 2438
0.003396108776329722

Execution Trace

EternalStorageProxy.3f7658fd( )
  • XDaiForeignBridge.executeSignatures( message=0xB63A5C5710F06E111FA14AC82DA2183C5102B504000000000000000000000000000000000000000000000002B5E3AF16B18800007C724C734B50F5F4B831EEA75D5969935CFE5C23A1637112ABFB64741BC57E614AA42145AA6EBF72E164C9BBC74FBD3788045016, signatures=0x041C1C1C1B171CFAF212D33B18C864EA6005A95DC190BD4A1D64548B4E0EE3A5ABAD2DEB25CE5FD87FF12520DA3729219507F4981627BE28CCD3BBFA2330D7639111587B528A10F112B39AB19E78000EA74EEB06DD64DB94A38D4D8DD64BBF77145FD5EA9C05CB1023353CE8F12957D6AC08E7B590E032D89A738A16F8905239A073F4991548FDAD3BD55501DA439EC36615C1A626F9289463558AE4446B08D6C7D45DA449541DAFB5F2337CA2EF349F449C69717BD7D083C6A58FA7A9E101254B9BFAEEDA60FB5C7F68CF7CBD5B8047FF927AB8EB7AAC9CAC564365D8DF262DFF1CD24AC5661D33C122A654DA5385976A1EBB7BB198FD0FC5BEB5D147303372D046183A0E )
    • EternalStorageProxy.CALL( )
      • BridgeValidators.DELEGATECALL( )
      • Null: 0x000...001.42b20b4d( )
      • EternalStorageProxy.facd743b( )
        • BridgeValidators.isValidator( _validator=0x4D1c96B9A49C4469A0b720a22b74b034EDdFe051 ) => ( True )
        • Null: 0x000...001.42b20b4d( )
        • EternalStorageProxy.facd743b( )
          • BridgeValidators.isValidator( _validator=0x97630E2aE609D4104aBdA91F3066C556403182dd ) => ( True )
          • Null: 0x000...001.42b20b4d( )
          • EternalStorageProxy.facd743b( )
            • BridgeValidators.isValidator( _validator=0x587C0d02B40822f15f05301d87c16f6a08AaDDde ) => ( True )
            • Null: 0x000...001.42b20b4d( )
            • EternalStorageProxy.facd743b( )
              • BridgeValidators.isValidator( _validator=0x1312E98995bbCc30fc63Db3cef807e20CDd33dca ) => ( True )
              • Dai.balanceOf( 0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016 ) => ( 1199363858363323394598347 )
              • Dai.transfer( dst=0xb63a5C5710F06e111fa14AC82dA2183C5102B504, wad=50000000000000000000 ) => ( True )
                File 1 of 5: EternalStorageProxy
                pragma solidity 0.4.24;
                
                // File: contracts/upgradeability/EternalStorage.sol
                
                /**
                 * @title EternalStorage
                 * @dev This contract holds all the necessary state variables to carry out the storage of any contract.
                 */
                contract EternalStorage {
                
                    mapping(bytes32 => uint256) internal uintStorage;
                    mapping(bytes32 => string) internal stringStorage;
                    mapping(bytes32 => address) internal addressStorage;
                    mapping(bytes32 => bytes) internal bytesStorage;
                    mapping(bytes32 => bool) internal boolStorage;
                    mapping(bytes32 => int256) internal intStorage;
                
                }
                
                // File: contracts/upgradeability/UpgradeabilityOwnerStorage.sol
                
                /**
                 * @title UpgradeabilityOwnerStorage
                 * @dev This contract keeps track of the upgradeability owner
                 */
                contract UpgradeabilityOwnerStorage {
                    // Owner of the contract
                    address private _upgradeabilityOwner;
                
                    /**
                    * @dev Tells the address of the owner
                    * @return the address of the owner
                    */
                    function upgradeabilityOwner() public view returns (address) {
                        return _upgradeabilityOwner;
                    }
                
                    /**
                    * @dev Sets the address of the owner
                    */
                    function setUpgradeabilityOwner(address newUpgradeabilityOwner) internal {
                        _upgradeabilityOwner = newUpgradeabilityOwner;
                    }
                }
                
                // File: contracts/upgradeability/Proxy.sol
                
                /**
                 * @title Proxy
                 * @dev Gives the possibility to delegate any call to a foreign implementation.
                 */
                contract Proxy {
                
                  /**
                  * @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);
                
                  /**
                  * @dev Fallback function allowing to perform a delegatecall to the given implementation.
                  * This function will return whatever the implementation call returns
                  */
                    function () payable public {
                        address _impl = implementation();
                        require(_impl != address(0));
                        assembly {
                            /*
                                0x40 is the "free memory slot", meaning a pointer to next slot of empty memory. mload(0x40)
                                loads the data in the free memory slot, so `ptr` is a pointer to the next slot of empty
                                memory. It's needed because we're going to write the return data of delegatecall to the
                                free memory slot.
                            */
                            let ptr := mload(0x40)
                            /*
                                `calldatacopy` is copy calldatasize bytes from calldata
                                First argument is the destination to which data is copied(ptr)
                                Second argument specifies the start position of the copied data.
                                    Since calldata is sort of its own unique location in memory,
                                    0 doesn't refer to 0 in memory or 0 in storage - it just refers to the zeroth byte of calldata.
                                    That's always going to be the zeroth byte of the function selector.
                                Third argument, calldatasize, specifies how much data will be copied.
                                    calldata is naturally calldatasize bytes long (same thing as msg.data.length)
                            */
                            calldatacopy(ptr, 0, calldatasize)
                            /*
                                delegatecall params explained:
                                gas: the amount of gas to provide for the call. `gas` is an Opcode that gives
                                    us the amount of gas still available to execution
                
                                _impl: address of the contract to delegate to
                
                                ptr: to pass copied data
                
                                calldatasize: loads the size of `bytes memory data`, same as msg.data.length
                
                                0, 0: These are for the `out` and `outsize` params. Because the output could be dynamic,
                                        these are set to 0, 0 so the output data will not be written to memory. The output
                                        data will be read using `returndatasize` and `returdatacopy` instead.
                
                                result: This will be 0 if the call fails and 1 if it succeeds
                            */
                            let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
                            /*
                
                            */
                            /*
                                ptr current points to the value stored at 0x40,
                                because we assigned it like ptr := mload(0x40).
                                Because we use 0x40 as a free memory pointer,
                                we want to make sure that the next time we want to allocate memory,
                                we aren't overwriting anything important.
                                So, by adding ptr and returndatasize,
                                we get a memory location beyond the end of the data we will be copying to ptr.
                                We place this in at 0x40, and any reads from 0x40 will now read from free memory
                            */
                            mstore(0x40, add(ptr, returndatasize))
                            /*
                                `returndatacopy` is an Opcode that copies the last return data to a slot. `ptr` is the
                                    slot it will copy to, 0 means copy from the beginning of the return data, and size is
                                    the amount of data to copy.
                                `returndatasize` is an Opcode that gives us the size of the last return data. In this case, that is the size of the data returned from delegatecall
                            */
                            returndatacopy(ptr, 0, returndatasize)
                
                            /*
                                if `result` is 0, revert.
                                if `result` is 1, return `size` amount of data from `ptr`. This is the data that was
                                copied to `ptr` from the delegatecall return data
                            */
                            switch result
                            case 0 { revert(ptr, returndatasize) }
                            default { return(ptr, returndatasize) }
                        }
                    }
                }
                
                // File: contracts/upgradeability/UpgradeabilityStorage.sol
                
                /**
                 * @title UpgradeabilityStorage
                 * @dev This contract holds all the necessary state variables to support the upgrade functionality
                 */
                contract UpgradeabilityStorage {
                    // Version name of the current implementation
                    uint256 internal _version;
                
                    // Address of the current implementation
                    address internal _implementation;
                
                    /**
                    * @dev Tells the version name of the current implementation
                    * @return string representing the name of the current version
                    */
                    function version() public view returns (uint256) {
                        return _version;
                    }
                
                    /**
                    * @dev Tells the address of the current implementation
                    * @return address of the current implementation
                    */
                    function implementation() public view returns (address) {
                        return _implementation;
                    }
                }
                
                // File: contracts/upgradeability/UpgradeabilityProxy.sol
                
                /**
                 * @title UpgradeabilityProxy
                 * @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded
                 */
                contract UpgradeabilityProxy is Proxy, UpgradeabilityStorage {
                    /**
                    * @dev This event will be emitted every time the implementation gets upgraded
                    * @param version representing the version name of the upgraded implementation
                    * @param implementation representing the address of the upgraded implementation
                    */
                    event Upgraded(uint256 version, address indexed implementation);
                
                    /**
                    * @dev Upgrades the implementation address
                    * @param version representing the version name of the new implementation to be set
                    * @param implementation representing the address of the new implementation to be set
                    */
                    function _upgradeTo(uint256 version, address implementation) internal {
                        require(_implementation != implementation);
                        require(version > _version);
                        _version = version;
                        _implementation = implementation;
                        emit Upgraded(version, implementation);
                    }
                }
                
                // File: contracts/upgradeability/OwnedUpgradeabilityProxy.sol
                
                /**
                 * @title OwnedUpgradeabilityProxy
                 * @dev This contract combines an upgradeability proxy with basic authorization control functionalities
                 */
                contract OwnedUpgradeabilityProxy is UpgradeabilityOwnerStorage, UpgradeabilityProxy {
                  /**
                  * @dev Event to show ownership has been transferred
                  * @param previousOwner representing the address of the previous owner
                  * @param newOwner representing the address of the new owner
                  */
                    event ProxyOwnershipTransferred(address previousOwner, address newOwner);
                
                    /**
                    * @dev the constructor sets the original owner of the contract to the sender account.
                    */
                    constructor() public {
                        setUpgradeabilityOwner(msg.sender);
                    }
                
                    /**
                    * @dev Throws if called by any account other than the owner.
                    */
                    modifier onlyProxyOwner() {
                        require(msg.sender == proxyOwner());
                        _;
                    }
                
                    /**
                    * @dev Tells the address of the proxy owner
                    * @return the address of the proxy owner
                    */
                    function proxyOwner() public view returns (address) {
                        return upgradeabilityOwner();
                    }
                
                    /**
                    * @dev Allows the current owner to transfer control of the contract to a newOwner.
                    * @param newOwner The address to transfer ownership to.
                    */
                    function transferProxyOwnership(address newOwner) public onlyProxyOwner {
                        require(newOwner != address(0));
                        emit ProxyOwnershipTransferred(proxyOwner(), newOwner);
                        setUpgradeabilityOwner(newOwner);
                    }
                
                    /**
                    * @dev Allows the upgradeability owner to upgrade the current version of the proxy.
                    * @param version representing the version name of the new implementation to be set.
                    * @param implementation representing the address of the new implementation to be set.
                    */
                    function upgradeTo(uint256 version, address implementation) public onlyProxyOwner {
                        _upgradeTo(version, implementation);
                    }
                
                    /**
                    * @dev Allows the upgradeability owner to upgrade the current version of the proxy and call the new implementation
                    * to initialize whatever is needed through a low level call.
                    * @param version representing the version name of the new implementation to be set.
                    * @param implementation representing the address of the new implementation to be set.
                    * @param data represents the msg.data to bet sent in the low level call. This parameter may include the function
                    * signature of the implementation to be called with the needed payload
                    */
                    function upgradeToAndCall(uint256 version, address implementation, bytes data) payable public onlyProxyOwner {
                        upgradeTo(version, implementation);
                        require(address(this).call.value(msg.value)(data));
                    }
                }
                
                // File: contracts/upgradeability/EternalStorageProxy.sol
                
                /**
                 * @title EternalStorageProxy
                 * @dev This proxy holds the storage of the token contract and delegates every call to the current implementation set.
                 * Besides, it allows to upgrade the token's behaviour towards further implementations, and provides basic
                 * authorization control functionalities
                 */
                contract EternalStorageProxy is OwnedUpgradeabilityProxy, EternalStorage {}

                File 2 of 5: Dai
                // hevm: flattened sources of /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol
                pragma solidity =0.5.12;
                
                ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/lib.sol
                // This program is free software: you can redistribute it and/or modify
                // it under the terms of the GNU General Public License as published by
                // the Free Software Foundation, either version 3 of the License, or
                // (at your option) any later version.
                
                // This program is distributed in the hope that it will be useful,
                // but WITHOUT ANY WARRANTY; without even the implied warranty of
                // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                // GNU General Public License for more details.
                
                // You should have received a copy of the GNU General Public License
                // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                
                /* pragma solidity 0.5.12; */
                
                contract LibNote {
                    event LogNote(
                        bytes4   indexed  sig,
                        address  indexed  usr,
                        bytes32  indexed  arg1,
                        bytes32  indexed  arg2,
                        bytes             data
                    ) anonymous;
                
                    modifier note {
                        _;
                        assembly {
                            // log an 'anonymous' event with a constant 6 words of calldata
                            // and four indexed topics: selector, caller, arg1 and arg2
                            let mark := msize                         // end of memory ensures zero
                            mstore(0x40, add(mark, 288))              // update free memory pointer
                            mstore(mark, 0x20)                        // bytes type data offset
                            mstore(add(mark, 0x20), 224)              // bytes size (padded)
                            calldatacopy(add(mark, 0x40), 0, 224)     // bytes payload
                            log4(mark, 288,                           // calldata
                                 shl(224, shr(224, calldataload(0))), // msg.sig
                                 caller,                              // msg.sender
                                 calldataload(4),                     // arg1
                                 calldataload(36)                     // arg2
                                )
                        }
                    }
                }
                
                ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol
                // Copyright (C) 2017, 2018, 2019 dbrock, rain, mrchico
                
                // This program is free software: you can redistribute it and/or modify
                // it under the terms of the GNU Affero General Public License as published by
                // the Free Software Foundation, either version 3 of the License, or
                // (at your option) any later version.
                //
                // This program is distributed in the hope that it will be useful,
                // but WITHOUT ANY WARRANTY; without even the implied warranty of
                // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                // GNU Affero General Public License for more details.
                //
                // You should have received a copy of the GNU Affero General Public License
                // along with this program.  If not, see <https://www.gnu.org/licenses/>.
                
                /* pragma solidity 0.5.12; */
                
                /* import "./lib.sol"; */
                
                contract Dai is LibNote {
                    // --- Auth ---
                    mapping (address => uint) public wards;
                    function rely(address guy) external note auth { wards[guy] = 1; }
                    function deny(address guy) external note auth { wards[guy] = 0; }
                    modifier auth {
                        require(wards[msg.sender] == 1, "Dai/not-authorized");
                        _;
                    }
                
                    // --- ERC20 Data ---
                    string  public constant name     = "Dai Stablecoin";
                    string  public constant symbol   = "DAI";
                    string  public constant version  = "1";
                    uint8   public constant decimals = 18;
                    uint256 public totalSupply;
                
                    mapping (address => uint)                      public balanceOf;
                    mapping (address => mapping (address => uint)) public allowance;
                    mapping (address => uint)                      public nonces;
                
                    event Approval(address indexed src, address indexed guy, uint wad);
                    event Transfer(address indexed src, address indexed dst, uint wad);
                
                    // --- Math ---
                    function add(uint x, uint y) internal pure returns (uint z) {
                        require((z = x + y) >= x);
                    }
                    function sub(uint x, uint y) internal pure returns (uint z) {
                        require((z = x - y) <= x);
                    }
                
                    // --- EIP712 niceties ---
                    bytes32 public DOMAIN_SEPARATOR;
                    // bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)");
                    bytes32 public constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
                
                    constructor(uint256 chainId_) public {
                        wards[msg.sender] = 1;
                        DOMAIN_SEPARATOR = keccak256(abi.encode(
                            keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                            keccak256(bytes(name)),
                            keccak256(bytes(version)),
                            chainId_,
                            address(this)
                        ));
                    }
                
                    // --- Token ---
                    function transfer(address dst, uint wad) external returns (bool) {
                        return transferFrom(msg.sender, dst, wad);
                    }
                    function transferFrom(address src, address dst, uint wad)
                        public returns (bool)
                    {
                        require(balanceOf[src] >= wad, "Dai/insufficient-balance");
                        if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
                            require(allowance[src][msg.sender] >= wad, "Dai/insufficient-allowance");
                            allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad);
                        }
                        balanceOf[src] = sub(balanceOf[src], wad);
                        balanceOf[dst] = add(balanceOf[dst], wad);
                        emit Transfer(src, dst, wad);
                        return true;
                    }
                    function mint(address usr, uint wad) external auth {
                        balanceOf[usr] = add(balanceOf[usr], wad);
                        totalSupply    = add(totalSupply, wad);
                        emit Transfer(address(0), usr, wad);
                    }
                    function burn(address usr, uint wad) external {
                        require(balanceOf[usr] >= wad, "Dai/insufficient-balance");
                        if (usr != msg.sender && allowance[usr][msg.sender] != uint(-1)) {
                            require(allowance[usr][msg.sender] >= wad, "Dai/insufficient-allowance");
                            allowance[usr][msg.sender] = sub(allowance[usr][msg.sender], wad);
                        }
                        balanceOf[usr] = sub(balanceOf[usr], wad);
                        totalSupply    = sub(totalSupply, wad);
                        emit Transfer(usr, address(0), wad);
                    }
                    function approve(address usr, uint wad) external returns (bool) {
                        allowance[msg.sender][usr] = wad;
                        emit Approval(msg.sender, usr, wad);
                        return true;
                    }
                
                    // --- Alias ---
                    function push(address usr, uint wad) external {
                        transferFrom(msg.sender, usr, wad);
                    }
                    function pull(address usr, uint wad) external {
                        transferFrom(usr, msg.sender, wad);
                    }
                    function move(address src, address dst, uint wad) external {
                        transferFrom(src, dst, wad);
                    }
                
                    // --- Approve by signature ---
                    function permit(address holder, address spender, uint256 nonce, uint256 expiry,
                                    bool allowed, uint8 v, bytes32 r, bytes32 s) external
                    {
                        bytes32 digest =
                            keccak256(abi.encodePacked(
                                "\x19\x01",
                                DOMAIN_SEPARATOR,
                                keccak256(abi.encode(PERMIT_TYPEHASH,
                                                     holder,
                                                     spender,
                                                     nonce,
                                                     expiry,
                                                     allowed))
                        ));
                
                        require(holder != address(0), "Dai/invalid-address-0");
                        require(holder == ecrecover(digest, v, r, s), "Dai/invalid-permit");
                        require(expiry == 0 || now <= expiry, "Dai/permit-expired");
                        require(nonce == nonces[holder]++, "Dai/invalid-nonce");
                        uint wad = allowed ? uint(-1) : 0;
                        allowance[holder][spender] = wad;
                        emit Approval(holder, spender, wad);
                    }
                }

                File 3 of 5: XDaiForeignBridge
                pragma solidity 0.4.24;
                import "./ForeignBridgeErcToNative.sol";
                import "./SavingsDaiConnector.sol";
                import "../GSNForeignERC20Bridge.sol";
                contract XDaiForeignBridge is ForeignBridgeErcToNative, SavingsDaiConnector, GSNForeignERC20Bridge {
                    function initialize(
                        address _validatorContract,
                        address _erc20token,
                        uint256 _requiredBlockConfirmations,
                        uint256 _gasPrice,
                        uint256[3] _dailyLimitMaxPerTxMinPerTxArray, // [ 0 = _dailyLimit, 1 = _maxPerTx, 2 = _minPerTx ]
                        uint256[2] _homeDailyLimitHomeMaxPerTxArray, //[ 0 = _homeDailyLimit, 1 = _homeMaxPerTx ]
                        address _owner,
                        int256 _decimalShift,
                        address _bridgeOnOtherSide
                    ) external onlyRelevantSender returns (bool) {
                        require(!isInitialized());
                        require(AddressUtils.isContract(_validatorContract));
                        require(_erc20token == address(daiToken()));
                        require(_decimalShift == 0);
                        addressStorage[VALIDATOR_CONTRACT] = _validatorContract;
                        uintStorage[DEPLOYED_AT_BLOCK] = block.number;
                        _setRequiredBlockConfirmations(_requiredBlockConfirmations);
                        _setGasPrice(_gasPrice);
                        _setLimits(_dailyLimitMaxPerTxMinPerTxArray);
                        _setExecutionLimits(_homeDailyLimitHomeMaxPerTxArray);
                        _setOwner(_owner);
                        _setBridgeContractOnOtherSide(_bridgeOnOtherSide);
                        setInitialize();
                        return isInitialized();
                    }
                    function erc20token() public view returns (ERC20) {
                        return daiToken();
                    }
                    /**
                     * @dev Withdraws DAI from sDAI vault to the bridge up to min cash threshold
                     */
                    function refillBridge() external {
                        uint256 currentBalance = daiToken().balanceOf(address(this));
                        uint256 minThreshold = minCashThreshold(address(daiToken()));
                        require(currentBalance < minThreshold, "Bridge is Filled");
                        uint256 withdrawAmount = minThreshold - currentBalance;
                        _withdraw(address(daiToken()), withdrawAmount);
                    }
                    /**
                     * @dev Invests the DAI into the sDAI Vault.
                     */
                    function investDai() external {
                        invest(address(daiToken()));
                    }
                    /**
                     * @dev Withdraws the erc20 tokens or native coins from this contract.
                     * @param _token address of the claimed token or address(0) for native coins.
                     * @param _to address of the tokens/coins receiver.
                     */
                    function claimTokens(address _token, address _to) external onlyIfUpgradeabilityOwner {
                        // Since bridged tokens are locked at this contract, it is not allowed to claim them with the use of claimTokens function
                        address bridgedToken = address(daiToken());
                        require(_token != address(bridgedToken), "Can't claim DAI");
                        require(_token != address(sDaiToken()) || !isInterestEnabled(bridgedToken), "Can't claim sDAI");
                        claimValues(_token, _to);
                    }
                    function onExecuteMessage(
                        address _recipient,
                        uint256 _amount,
                        bytes32 /*_txHash*/
                    ) internal returns (bool) {
                        addTotalExecutedPerDay(getCurrentDay(), _amount);
                        ERC20 token = daiToken();
                        ensureEnoughTokens(token, _amount);
                        return token.transfer(_recipient, _amount);
                    }
                    function onExecuteMessageGSN(address recipient, uint256 amount, uint256 fee) internal returns (bool) {
                        ensureEnoughTokens(daiToken(), amount);
                        return super.onExecuteMessageGSN(recipient, amount, fee);
                    }
                    function ensureEnoughTokens(ERC20 token, uint256 amount) internal {
                        uint256 currentBalance = token.balanceOf(address(this));
                        if (currentBalance < amount) {
                            uint256 withdrawAmount = (amount - currentBalance).add(minCashThreshold(address(token)));
                            _withdraw(address(token), withdrawAmount);
                        }
                    }
                }
                pragma solidity 0.4.24;
                import "../ERC20Bridge.sol";
                import "../OtherSideBridgeStorage.sol";
                contract ForeignBridgeErcToNative is ERC20Bridge, OtherSideBridgeStorage {
                    function initialize(
                        address _validatorContract,
                        address _erc20token,
                        uint256 _requiredBlockConfirmations,
                        uint256 _gasPrice,
                        uint256[3] _dailyLimitMaxPerTxMinPerTxArray, // [ 0 = _dailyLimit, 1 = _maxPerTx, 2 = _minPerTx ]
                        uint256[2] _homeDailyLimitHomeMaxPerTxArray, //[ 0 = _homeDailyLimit, 1 = _homeMaxPerTx ]
                        address _owner,
                        int256 _decimalShift,
                        address _bridgeOnOtherSide
                    ) external onlyRelevantSender returns (bool) {
                        require(!isInitialized());
                        require(AddressUtils.isContract(_validatorContract));
                        addressStorage[VALIDATOR_CONTRACT] = _validatorContract;
                        setErc20token(_erc20token);
                        uintStorage[DEPLOYED_AT_BLOCK] = block.number;
                        _setRequiredBlockConfirmations(_requiredBlockConfirmations);
                        _setGasPrice(_gasPrice);
                        _setLimits(_dailyLimitMaxPerTxMinPerTxArray);
                        _setExecutionLimits(_homeDailyLimitHomeMaxPerTxArray);
                        _setDecimalShift(_decimalShift);
                        _setOwner(_owner);
                        _setBridgeContractOnOtherSide(_bridgeOnOtherSide);
                        setInitialize();
                        return isInitialized();
                    }
                    function getBridgeMode() external pure returns (bytes4 _data) {
                        return 0x18762d46; // bytes4(keccak256(abi.encodePacked("erc-to-native-core")))
                    }
                    /**
                     * @dev Withdraws the erc20 tokens or native coins from this contract.
                     * @param _token address of the claimed token or address(0) for native coins.
                     * @param _to address of the tokens/coins receiver.
                     */
                    function claimTokens(address _token, address _to) external onlyIfUpgradeabilityOwner {
                        // Since bridged tokens are locked at this contract, it is not allowed to claim them with the use of claimTokens function
                        require(_token != address(erc20token()), "Bridged Token is disallowed");
                        claimValues(_token, _to);
                    }
                    function onExecuteMessage(
                        address _recipient,
                        uint256 _amount,
                        bytes32 /*_txHash*/
                    ) internal returns (bool) {
                        addTotalExecutedPerDay(getCurrentDay(), _amount);
                        return erc20token().transfer(_recipient, _unshiftValue(_amount));
                    }
                    function onFailedMessage(address, uint256, bytes32) internal {
                        revert();
                    }
                    function relayTokens(address _receiver, uint256 _amount) public {
                        require(_receiver != bridgeContractOnOtherSide(), "Relayed to Bridge address");
                        require(_receiver != address(0), "Relayed to Null address");
                        require(_receiver != address(this), "Relayed to this address");
                        require(_amount > 0, "Relayed zero funds");
                        require(withinLimit(_amount), "Exceeds bridge daily limit");
                        addTotalSpentPerDay(getCurrentDay(), _amount);
                        erc20token().transferFrom(msg.sender, address(this), _amount);
                        emit UserRequestForAffirmation(_receiver, _amount);
                    }
                }
                pragma solidity 0.4.24;
                import "./InterestConnector.sol";
                import "../../interfaces/ISavingsDai.sol";
                /**
                 * @title SavingsDaiConnector
                 * @dev This contract allows to partially deposit locked Dai tokens into the Maker DSR using the sDAI ERC4626 vault. 
                 * @dev This must never be deployed standalone and only as an interface to interact with the SavingsDAI from the InterestConnector
                 */
                contract SavingsDaiConnector is InterestConnector {
                    /**
                     * @dev Tells the address of the DAI token in the Ethereum Mainnet.
                     */
                    function daiToken() public pure returns (ERC20) {
                        return ERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
                    }
                    /**
                     * @dev Tells the address of the sDAI token in the Ethereum Mainnet.
                     */
                    function sDaiToken() public pure returns (ISavingsDai) {
                        return ISavingsDai(0x83F20F44975D03b1b09e64809B757c47f942BEeA);
                    }
                    /**
                     * @dev Tells the current earned interest amount.
                     * @param _token address of the underlying token contract.
                     * @return total amount of interest that can be withdrawn now.
                     */
                    function interestAmount(address _token) public view returns (uint256) {
                        require(_token == address(daiToken()), "Not DAI");
                        uint256 underlyingBalance = sDaiToken().maxWithdraw(address(this));
                        // 1 DAI is reserved for possible truncation/round errors
                        uint256 invested = investedAmount(_token) + 1 ether;
                        return underlyingBalance > invested ? underlyingBalance - invested : 0;
                    }
                    /**
                     * @dev Tells if interest earning is supported for the specific token contract.
                     * @param _token address of the token contract.
                     * @return true, if interest earning is supported.
                     */
                    function _isInterestSupported(address _token) internal pure returns (bool) {
                        return _token == address(daiToken());
                    }
                    /**
                     * @dev Invests the given amount of DAI to the sDAI Vault.
                     * Deposits _amount of _token into the sDAI vault.
                     * @param _token address of the token contract.
                     * @param _amount amount of tokens to invest.
                     */
                    function _invest(address _token, uint256 _amount) internal {
                        (_token);
                        daiToken().approve(address(sDaiToken()), _amount);
                        require(sDaiToken().deposit(_amount, address(this)) > 0, "Failed to deposit");
                    }
                    /**
                     * @dev Withdraws at least the given amount of DAI from the sDAI vault contract.
                     * Withdraws the _amount of _token from the sDAI vault.
                     * @param _token address of the token contract.
                     * @param _amount minimal amount of tokens to withdraw.
                     */
                    function _withdrawTokens(address _token, uint256 _amount) internal {
                        (_token);
                        require(sDaiToken().withdraw(_amount, address(this), address(this)) > 0, "Failed to withdraw");
                    }
                    /**
                     * @dev Previews a withdraw of the given amount of DAI from the sDAI vault contract.
                     * Previews withdrawing the _amount of _token from the sDAI vault.
                     * @param _token address of the token contract.
                     * @param _amount minimal amount of tokens to withdraw.
                     */
                    function previewWithdraw(address _token, uint256 _amount) public view returns(uint256){
                        (_token);
                        return sDaiToken().previewWithdraw(_amount);
                    }
                }
                pragma solidity 0.4.24;
                import "./BasicForeignBridge.sol";
                import "./ERC20Bridge.sol";
                import "../gsn/BaseRelayRecipient.sol";
                import "../gsn/interfaces/IKnowForwarderAddress.sol";
                contract GSNForeignERC20Bridge is BasicForeignBridge, ERC20Bridge, BaseRelayRecipient, IKnowForwarderAddress {
                    bytes32 internal constant PAYMASTER = 0xfefcc139ed357999ed60c6a013947328d52e7d9751e93fd0274a2bfae5cbcb12; // keccak256(abi.encodePacked("paymaster"))
                    bytes32 internal constant TRUSTED_FORWARDER = 0x222cb212229f0f9bcd249029717af6845ea3d3a84f22b54e5744ac25ef224c92; // keccak256(abi.encodePacked("trustedForwarder"))
                    function versionRecipient() external view returns (string memory) {
                        return "1.0.1";
                    }
                    function getTrustedForwarder() external view returns (address) {
                        return addressStorage[TRUSTED_FORWARDER];
                    }
                    function setTrustedForwarder(address _trustedForwarder) public onlyOwner {
                        addressStorage[TRUSTED_FORWARDER] = _trustedForwarder;
                    }
                    function isTrustedForwarder(address forwarder) public view returns (bool) {
                        return forwarder == addressStorage[TRUSTED_FORWARDER];
                    }
                    function setPayMaster(address _paymaster) public onlyOwner {
                        addressStorage[PAYMASTER] = _paymaster;
                    }
                    /**
                    * @param message same as in `executeSignatures`
                    * @param signatures same as in `executeSignatures`
                    * @param maxTokensFee maximum amount of foreign tokens that user allows to take
                    * as a commission
                    */
                    function executeSignaturesGSN(bytes message, bytes signatures, uint256 maxTokensFee) external {
                        // Allow only forwarder calls
                        require(isTrustedForwarder(msg.sender), "invalid forwarder");
                        Message.hasEnoughValidSignatures(message, signatures, validatorContract(), false);
                        address recipient;
                        uint256 amount;
                        bytes32 txHash;
                        address contractAddress;
                        (recipient, amount, txHash, contractAddress) = Message.parseMessage(message);
                        if (withinExecutionLimit(amount)) {
                            require(maxTokensFee <= amount);
                            require(contractAddress == address(this));
                            require(!relayedMessages(txHash));
                            setRelayedMessages(txHash, true);
                            require(onExecuteMessageGSN(recipient, amount, maxTokensFee));
                            emit RelayedMessage(recipient, amount, txHash);
                        } else {
                            onFailedMessage(recipient, amount, txHash);
                        }
                    }
                    function onExecuteMessageGSN(address recipient, uint256 amount, uint256 fee) internal returns (bool) {
                        addTotalExecutedPerDay(getCurrentDay(), amount);
                        // Send maxTokensFee to paymaster
                        ERC20 token = erc20token();
                        bool first = token.transfer(addressStorage[PAYMASTER], fee);
                        bool second = token.transfer(recipient, amount - fee);
                        return first && second;
                    }
                }
                pragma solidity 0.4.24;
                import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
                import "openzeppelin-solidity/contracts/AddressUtils.sol";
                import "./BasicForeignBridge.sol";
                contract ERC20Bridge is BasicForeignBridge {
                    bytes32 internal constant ERC20_TOKEN = 0x15d63b18dbc21bf4438b7972d80076747e1d93c4f87552fe498c90cbde51665e; // keccak256(abi.encodePacked("erc20token"))
                    function erc20token() public view returns (ERC20) {
                        return ERC20(addressStorage[ERC20_TOKEN]);
                    }
                    function setErc20token(address _token) internal {
                        require(AddressUtils.isContract(_token));
                        addressStorage[ERC20_TOKEN] = _token;
                    }
                    function relayTokens(address _receiver, uint256 _amount) public {
                        require(_receiver != address(0), "Receiver can't be Null");
                        require(_receiver != address(this), "Receiver can't be the Bridge");
                        require(_amount > 0, "Relayed zero tokens");
                        require(withinLimit(_amount), "Relayed above limit");
                        addTotalSpentPerDay(getCurrentDay(), _amount);
                        erc20token().transferFrom(msg.sender, address(this), _amount);
                        emit UserRequestForAffirmation(_receiver, _amount);
                    }
                    function _relayInterest(address _receiver, uint256 _amount) internal {
                        require(_receiver != address(0), "Receiver can't be Null");
                        require(_receiver != address(this), "Receiver can't be the Bridge");
                        require(_amount > 0, "Relayed zero tokens");
                        require(withinLimit(_amount), "Relayed above limit");
                        addTotalSpentPerDay(getCurrentDay(), _amount);
                        emit UserRequestForAffirmation(_receiver, _amount);
                    }
                }
                pragma solidity 0.4.24;
                import "../upgradeability/EternalStorage.sol";
                contract OtherSideBridgeStorage is EternalStorage {
                    bytes32 internal constant BRIDGE_CONTRACT = 0x71483949fe7a14d16644d63320f24d10cf1d60abecc30cc677a340e82b699dd2; // keccak256(abi.encodePacked("bridgeOnOtherSide"))
                    function _setBridgeContractOnOtherSide(address _bridgeContract) internal {
                        require(_bridgeContract != address(0));
                        addressStorage[BRIDGE_CONTRACT] = _bridgeContract;
                    }
                    function bridgeContractOnOtherSide() internal view returns (address) {
                        return addressStorage[BRIDGE_CONTRACT];
                    }
                }
                pragma solidity 0.4.24;
                import "../Ownable.sol";
                import "../ERC20Bridge.sol";
                import "../../interfaces/IInterestReceiver.sol";
                /**
                 * @title InterestConnector
                 * @dev This contract gives an abstract way of receiving interest on locked tokens.
                 */
                contract InterestConnector is Ownable, ERC20Bridge {
                    event PaidInterest(address indexed token, address to, uint256 value);
                    /**
                     * @dev Throws if interest is bearing not enabled.
                     * @param token address, for which interest should be enabled.
                     */
                    modifier interestEnabled(address token) {
                        require(isInterestEnabled(token), "Interest not Enabled");
                        /* solcov ignore next */
                        _;
                    }
                    /**
                     * @dev Ensures that caller is an EOA.
                     * Functions with such modifier cannot be called from other contract (as well as from GSN-like approaches)
                     */
                    modifier onlyEOA() {
                        // solhint-disable-next-line avoid-tx-origin
                        require(msg.sender == tx.origin, "Not EOA");
                        /* solcov ignore next */
                        _;
                    }
                    /**
                     * @dev Tells if interest earning was enabled for particular token.
                     * @return true, if interest bearing  is enabled.
                     */
                    function isInterestEnabled(address _token) public view returns (bool) {
                        return boolStorage[keccak256(abi.encodePacked("interestEnabled", _token))];
                    }
                    /**
                     * @dev Initializes interest receiving functionality.
                     * Only owner can call this method.
                     * @param _token address of the token for interest earning.
                     * @param _minCashThreshold minimum amount of underlying tokens that are not invested.
                     * @param _minInterestPaid minimum amount of interest that can be paid in a single call.
                     */
                    function initializeInterest(
                        address _token,
                        uint256 _minCashThreshold,
                        uint256 _minInterestPaid,
                        address _interestReceiver
                    ) external onlyOwner {
                        require(_isInterestSupported(_token), "Token not supported");
                        require(!isInterestEnabled(_token), "Interest already enabled");
                        _setInterestEnabled(_token, true);
                        _setMinCashThreshold(_token, _minCashThreshold);
                        _setMinInterestPaid(_token, _minInterestPaid);
                        _setInterestReceiver(_token, _interestReceiver);
                    }
                    /**
                     * @dev Sets minimum amount of tokens that cannot be invested.
                     * Only owner can call this method.
                     * @param _token address of the token contract.
                     * @param _minCashThreshold minimum amount of underlying tokens that are not invested.
                     */
                    function setMinCashThreshold(address _token, uint256 _minCashThreshold) external onlyOwner {
                        _setMinCashThreshold(_token, _minCashThreshold);
                    }
                    /**
                     * @dev Tells minimum amount of tokens that are not being invested.
                     * @param _token address of the invested token contract.
                     * @return amount of tokens.
                     */
                    function minCashThreshold(address _token) public view returns (uint256) {
                        return uintStorage[keccak256(abi.encodePacked("minCashThreshold", _token))];
                    }
                    /**
                     * @dev Sets lower limit for the paid interest amount.
                     * Only owner can call this method.
                     * @param _token address of the token contract.
                     * @param _minInterestPaid minimum amount of interest paid in a single call.
                     */
                    function setMinInterestPaid(address _token, uint256 _minInterestPaid) external onlyOwner {
                        _setMinInterestPaid(_token, _minInterestPaid);
                    }
                    /**
                     * @dev Tells minimum amount of paid interest in a single call.
                     * @param _token address of the invested token contract.
                     * @return paid interest minimum limit.
                     */
                    function minInterestPaid(address _token) public view returns (uint256) {
                        return uintStorage[keccak256(abi.encodePacked("minInterestPaid", _token))];
                    }
                    /**
                     * @dev Internal function that disables interest for locked funds.
                     * Only owner can call this method.
                     * @param _token of token to disable interest for.
                     */
                    function disableInterest(address _token) external onlyOwner {
                        _withdraw(_token, uint256(-1));
                        _setInterestEnabled(_token, false);
                    }
                    /**
                     * @dev Tells configured address of the interest receiver.
                     * @param _token address of the invested token contract.
                     * @return address of the interest receiver.
                     */
                    function interestReceiver(address _token) public view returns (address) {
                        return addressStorage[keccak256(abi.encodePacked("interestReceiver", _token))];
                    }
                    /**
                     * Updates the interest receiver address.
                     * Only owner can call this method.
                     * @param _token address of the invested token contract.
                     * @param _receiver new receiver address.
                     */
                    function setInterestReceiver(address _token, address _receiver) external onlyOwner {
                        _setInterestReceiver(_token, _receiver);
                    }
                    /**
                     * @dev Pays collected interest for the specific underlying token to _reveicer contract on Gnosis Chain 
                     *      and reinvests amount claimed.
                     * Requires interest for the given token to be enabled.
                     * @param _token address of the token contract.
                     */
                    function payInterest(address _token, uint256 _amount) external interestEnabled(_token) {
                        require(_token == address(erc20token()), "Not bridge Token");
                        uint256 claimable = interestAmount(_token);
                        uint256 interest = (_amount < claimable) ? _amount : claimable;
                        require(interest >= minInterestPaid(_token), "Collectable interest too low");
                        _setInvestedAmount(_token, investedAmount(_token).add(interest));
                        address receiver = interestReceiver(_token);
                        _relayInterest(receiver, interest);
                        emit PaidInterest(_token, receiver, interest);
                    }
                    /**
                     * @dev Tells the amount of underlying tokens that are currently invested.
                     * @param _token address of the token contract.
                     * @return amount of underlying tokens.
                     */
                    function investedAmount(address _token) public view returns (uint256) {
                        return uintStorage[keccak256(abi.encodePacked("investedAmount", _token))];
                    }
                    /**
                     * @dev Invests all excess tokens.
                     * Requires interest for the given token to be enabled.
                     * @param _token address of the token contract considered.
                     */
                    function invest(address _token) public interestEnabled(_token) {
                        uint256 balance = _selfBalance(_token);
                        uint256 minCash = minCashThreshold(_token);
                        require(balance > minCash, "Balance too Low");
                        uint256 amount = balance - minCash;
                        _setInvestedAmount(_token, investedAmount(_token).add(amount));
                        _invest(_token, amount);
                    }
                    /**
                     * @dev Internal function for transferring interest. Deprecated 
                     * Calls a callback on the receiver, if it is a contract.
                     * @param _token address of the underlying token contract.
                     * @param _amount amount of collected tokens that should be sent.
                     */
                    function _transferInterest(address _token, uint256 _amount) internal {
                        address receiver = interestReceiver(_token);
                        require(receiver != address(0), "Receiver can't be Null");
                        ERC20(_token).transfer(receiver, _amount);
                        if (AddressUtils.isContract(receiver)) {
                            IInterestReceiver(receiver).onInterestReceived(_token);
                        }
                        emit PaidInterest(_token, receiver, _amount);
                    }
                    /**
                     * @dev Internal function for setting interest enabled flag for some token.
                     * @param _token address of the token contract.
                     * @param _enabled true to enable interest earning, false to disable.
                     */
                    function _setInterestEnabled(address _token, bool _enabled) internal {
                        boolStorage[keccak256(abi.encodePacked("interestEnabled", _token))] = _enabled;
                    }
                    /**
                     * @dev Internal function for setting the amount of underlying tokens that are currently invested.
                     * @param _token address of the token contract.
                     * @param _amount new amount of invested tokens.
                     */
                    function _setInvestedAmount(address _token, uint256 _amount) internal {
                        uintStorage[keccak256(abi.encodePacked("investedAmount", _token))] = _amount;
                    }
                    /**
                     * @dev Internal function for withdrawing some amount of the invested tokens.
                     * Reverts if given amount cannot be withdrawn.
                     * @param _token address of the token contract withdrawn.
                     * @param _amount amount of requested tokens to be withdrawn.
                     */
                    function _withdraw(address _token, uint256 _amount) internal {
                        uint256 invested = investedAmount(_token);
                        uint256 withdrawal = _amount > invested ? invested : _amount;
                        if (withdrawal == 0) return;
                        uint256 redeemed = _safeWithdrawTokens(_token, withdrawal);
                        _setInvestedAmount(_token, invested > redeemed ? invested - redeemed : 0);
                    }
                    /**
                     * @dev Internal function for safe withdrawal of invested tokens.
                     * Reverts if given amount cannot be withdrawn.
                     * Additionally verifies that at least _amount of tokens were withdrawn.
                     * @param _token address of the token contract withdrawn.
                     * @param _amount amount of requested tokens to be withdrawn.
                     */
                    function _safeWithdrawTokens(address _token, uint256 _amount) private returns (uint256) {
                        uint256 balance = _selfBalance(_token);
                        _withdrawTokens(_token, _amount);
                        uint256 redeemed = _selfBalance(_token) - balance;
                        require(redeemed >= _amount, "Withdrawn less than Amount");
                        return redeemed;
                    }
                    /**
                     * @dev Internal function for setting minimum amount of tokens that cannot be invested.
                     * @param _token address of the token contract.
                     * @param _minCashThreshold minimum amount of underlying tokens that are not invested.
                     */
                    function _setMinCashThreshold(address _token, uint256 _minCashThreshold) internal {
                        uintStorage[keccak256(abi.encodePacked("minCashThreshold", _token))] = _minCashThreshold;
                    }
                    /**
                     * @dev Internal function for setting lower limit for paid interest amount. Must be lower than DAILY_LIMIT and MAX_PER_TX
                     * @param _token address of the token contract.
                     * @param _minInterestPaid minimum amount of interest paid in a single call.
                     */
                    function _setMinInterestPaid(address _token, uint256 _minInterestPaid) internal {
                        uintStorage[keccak256(abi.encodePacked("minInterestPaid", _token))] = _minInterestPaid;
                    }
                    /**
                     * @dev Internal function for setting interest receiver address.
                     * @param _token address of the invested token contract.
                     * @param _receiver address of the interest receiver.
                     */
                    function _setInterestReceiver(address _token, address _receiver) internal {
                        require(_receiver != address(this), "Receiver can't be the Bridge");
                        addressStorage[keccak256(abi.encodePacked("interestReceiver", _token))] = _receiver;
                    }
                    /**
                     * @dev Tells this contract balance of some specific token contract
                     * @param _token address of the token contract.
                     * @return contract balance.
                     */
                    function _selfBalance(address _token) internal view returns (uint256) {
                        return ERC20(_token).balanceOf(address(this));
                    }
                    function _isInterestSupported(address _token) internal pure returns (bool);
                    function _invest(address _token, uint256 _amount) internal;
                    function _withdrawTokens(address _token, uint256 _amount) internal;
                    function previewWithdraw(address _token, uint256 _amount) public view returns (uint256);
                    function interestAmount(address _token) public view returns (uint256);
                }
                // SPDX-License-Identifier: AGPL-3.0-or-later
                // Copyright (C) 2021-2022 Dai Foundation
                //
                // This program is free software: you can redistribute it and/or modify
                // it under the terms of the GNU Affero General Public License as published by
                // the Free Software Foundation, either version 3 of the License, or
                // (at your option) any later version.
                //
                // This program is distributed in the hope that it will be useful,
                // but WITHOUT ANY WARRANTY; without even the implied warranty of
                // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                // GNU Affero General Public License for more details.
                //
                // You should have received a copy of the GNU Affero General Public License
                // along with this program.  If not, see <https://www.gnu.org/licenses/>.
                pragma solidity 0.4.24;
                interface ISavingsDai {
                    function totalSupply() external view returns (uint256);
                    function balanceOf(address) external view returns (uint256);
                    function allowance(address, address) external view returns (uint256);
                    function approve(address, uint256) external returns (bool);
                    function transfer(address, uint256) external returns (bool);
                    function transferFrom(address, address, uint256) external returns (bool);
                    function name() external view returns (string memory);
                    function symbol() external view returns (string memory);
                    function version() external view returns (string memory);
                    function decimals() external view returns (uint8);
                    function deploymentChainId() external view returns (uint256);
                    function PERMIT_TYPEHASH() external view returns (bytes32);
                    function DOMAIN_SEPARATOR() external view returns (bytes32);
                    function nonces(address) external view returns (uint256);
                    function vat() external view returns (address);
                    function daiJoin() external view returns (address);
                    function dai() external view returns (address);
                    function pot() external view returns (address);
                    function increaseAllowance(address, uint256) external returns (bool);
                    function decreaseAllowance(address, uint256) external returns (bool);
                    function asset() external view returns (address);
                    function totalAssets() external view returns (uint256);
                    function convertToShares(uint256) external view returns (uint256);
                    function convertToAssets(uint256) external view returns (uint256);
                    function maxDeposit(address) external view returns (uint256);
                    function previewDeposit(uint256) external view returns (uint256);
                    function deposit(uint256, address) external returns (uint256);
                    function deposit(uint256, address, uint16) external returns (uint256);
                    function maxMint(address) external view returns (uint256);
                    function previewMint(uint256) external view returns (uint256);
                    function mint(uint256, address) external returns (uint256);
                    function mint(uint256, address, uint16) external returns (uint256);
                    function maxWithdraw(address) external view returns (uint256);
                    function previewWithdraw(uint256) external view returns (uint256);
                    function withdraw(uint256, address, address) external returns (uint256);
                    function maxRedeem(address) external view returns (uint256);
                    function previewRedeem(uint256) external view returns (uint256);
                    function redeem(uint256, address, address) external returns (uint256);
                    function permit(address, address, uint256, uint256, bytes) external;
                    function permit(address, address, uint256, uint256, uint8, bytes32, bytes32) external;
                }
                pragma solidity 0.4.24;
                import "../upgradeability/EternalStorage.sol";
                import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
                import "./Validatable.sol";
                import "../libraries/Message.sol";
                import "./MessageRelay.sol";
                import "./BasicBridge.sol";
                import "./BasicTokenBridge.sol";
                import "./MessageRelay.sol";
                contract BasicForeignBridge is EternalStorage, Validatable, BasicBridge, BasicTokenBridge, MessageRelay {
                    /// triggered when relay of deposit from HomeBridge is complete
                    event RelayedMessage(address recipient, uint256 value, bytes32 transactionHash);
                    event UserRequestForAffirmation(address recipient, uint256 value);
                    /**
                    * @dev Validates provided signatures and relays a given message
                    * @param message bytes to be relayed
                    * @param signatures bytes blob with signatures to be validated
                    */
                    function executeSignatures(bytes message, bytes signatures) external {
                        Message.hasEnoughValidSignatures(message, signatures, validatorContract(), false);
                        address recipient;
                        uint256 amount;
                        bytes32 txHash;
                        address contractAddress;
                        (recipient, amount, txHash, contractAddress) = Message.parseMessage(message);
                        if (withinExecutionLimit(amount)) {
                            require(contractAddress == address(this));
                            require(!relayedMessages(txHash));
                            setRelayedMessages(txHash, true);
                            require(onExecuteMessage(recipient, amount, txHash));
                            emit RelayedMessage(recipient, amount, txHash);
                        } else {
                            onFailedMessage(recipient, amount, txHash);
                        }
                    }
                    /**
                    * @dev Internal function for updating fallback gas price value.
                    * @param _gasPrice new value for the gas price, zero gas price is not allowed.
                    */
                    function _setGasPrice(uint256 _gasPrice) internal {
                        require(_gasPrice > 0);
                        super._setGasPrice(_gasPrice);
                    }
                    /* solcov ignore next */
                    function onExecuteMessage(address, uint256, bytes32) internal returns (bool);
                    /* solcov ignore next */
                    function onFailedMessage(address, uint256, bytes32) internal;
                }
                // SPDX-License-Identifier:MIT
                // solhint-disable no-inline-assembly
                pragma solidity 0.4.24;
                import "./interfaces/IRelayRecipient.sol";
                /**
                 * A base contract to be inherited by any contract that want to receive relayed transactions
                 * A subclass must use "_msgSender()" instead of "msg.sender"
                 */
                contract BaseRelayRecipient is IRelayRecipient {
                    /**
                     * return the sender of this call.
                     * if the call came through our trusted forwarder, return the original sender.
                     * otherwise, return `msg.sender`.
                     * should be used in the contract anywhere instead of msg.sender
                     */
                    function _msgSender() internal view returns (address ret) {
                        if (msg.data.length >= 24 && isTrustedForwarder(msg.sender)) {
                            // At this point we know that the sender is a trusted forwarder,
                            // so we trust that the last bytes of msg.data are the verified sender address.
                            // extract sender address from the end of msg.data
                            assembly {
                                ret := shr(96, calldataload(sub(calldatasize, 20)))
                            }
                        } else {
                            return msg.sender;
                        }
                    }
                    /**
                     * return the msg.data of this call.
                     * if the call came through our trusted forwarder, then the real sender was appended as the last 20 bytes
                     * of the msg.data - so this method will strip those 20 bytes off.
                     * otherwise, return `msg.data`
                     * should be used in the contract instead of msg.data, where the difference matters (e.g. when explicitly
                     * signing or hashing the
                     */
                    function _msgData() internal view returns (bytes memory ret) {
                        if (msg.data.length >= 24 && isTrustedForwarder(msg.sender)) {
                            // At this point we know that the sender is a trusted forwarder,
                            // we copy the msg.data , except the last 20 bytes (and update the total length)
                            assembly {
                                let ptr := mload(0x40)
                                // copy only size-20 bytes
                                let size := sub(calldatasize, 20)
                                // structure RLP data as <offset> <length> <bytes>
                                mstore(ptr, 0x20)
                                mstore(add(ptr, 32), size)
                                calldatacopy(add(ptr, 64), 0, size)
                                return(ptr, add(size, 64))
                            }
                        } else {
                            return msg.data;
                        }
                    }
                }
                // SPDX-License-Identifier:MIT
                pragma solidity 0.4.24;
                interface IKnowForwarderAddress {
                    /**
                     * return the forwarder we trust to forward relayed transactions to us.
                     * the forwarder is required to verify the sender's signature, and verify
                     * the call is not a replay.
                     */
                    function getTrustedForwarder() external view returns (address);
                }
                pragma solidity ^0.4.24;
                import "./ERC20Basic.sol";
                /**
                 * @title ERC20 interface
                 * @dev see https://github.com/ethereum/EIPs/issues/20
                 */
                contract ERC20 is ERC20Basic {
                  function allowance(address _owner, address _spender)
                    public view returns (uint256);
                  function transferFrom(address _from, address _to, uint256 _value)
                    public returns (bool);
                  function approve(address _spender, uint256 _value) public returns (bool);
                  event Approval(
                    address indexed owner,
                    address indexed spender,
                    uint256 value
                  );
                }
                pragma solidity ^0.4.24;
                /**
                 * Utility library of inline functions on addresses
                 */
                library AddressUtils {
                  /**
                   * Returns whether the target address is a contract
                   * @dev This function will return false if invoked during the constructor of a contract,
                   * as the code is not actually created until after the constructor finishes.
                   * @param _addr address to check
                   * @return whether the target address is a contract
                   */
                  function isContract(address _addr) internal view returns (bool) {
                    uint256 size;
                    // XXX Currently there is no better way to check if there is a contract in an address
                    // than to check the size of the code at that address.
                    // See https://ethereum.stackexchange.com/a/14016/36603
                    // for more details about how this works.
                    // TODO Check this again before the Serenity release, because all addresses will be
                    // contracts then.
                    // solium-disable-next-line security/no-inline-assembly
                    assembly { size := extcodesize(_addr) }
                    return size > 0;
                  }
                }
                pragma solidity 0.4.24;
                /**
                 * @title EternalStorage
                 * @dev This contract holds all the necessary state variables to carry out the storage of any contract.
                 */
                contract EternalStorage {
                    mapping(bytes32 => uint256) internal uintStorage;
                    mapping(bytes32 => string) internal stringStorage;
                    mapping(bytes32 => address) internal addressStorage;
                    mapping(bytes32 => bytes) internal bytesStorage;
                    mapping(bytes32 => bool) internal boolStorage;
                    mapping(bytes32 => int256) internal intStorage;
                }
                pragma solidity 0.4.24;
                import "../upgradeability/EternalStorage.sol";
                import "../interfaces/IUpgradeabilityOwnerStorage.sol";
                /**
                 * @title Ownable
                 * @dev This contract has an owner address providing basic authorization control
                 */
                contract Ownable is EternalStorage {
                    bytes4 internal constant UPGRADEABILITY_OWNER = 0x6fde8202; // upgradeabilityOwner()
                    /**
                    * @dev Event to show ownership has been transferred
                    * @param previousOwner representing the address of the previous owner
                    * @param newOwner representing the address of the new owner
                    */
                    event OwnershipTransferred(address previousOwner, address newOwner);
                    /**
                    * @dev Throws if called by any account other than the owner.
                    */
                    modifier onlyOwner() {
                        require(msg.sender == owner());
                        /* solcov ignore next */
                        _;
                    }
                    /**
                    * @dev Throws if called by any account other than contract itself or owner.
                    */
                    modifier onlyRelevantSender() {
                        // proxy owner if used through proxy, address(0) otherwise
                        require(
                            !address(this).call(abi.encodeWithSelector(UPGRADEABILITY_OWNER)) || // covers usage without calling through storage proxy
                                msg.sender == IUpgradeabilityOwnerStorage(this).upgradeabilityOwner() || // covers usage through regular proxy calls
                                msg.sender == address(this) // covers calls through upgradeAndCall proxy method
                        );
                        /* solcov ignore next */
                        _;
                    }
                    bytes32 internal constant OWNER = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0; // keccak256(abi.encodePacked("owner"))
                    /**
                    * @dev Tells the address of the owner
                    * @return the address of the owner
                    */
                    function owner() public view returns (address) {
                        return addressStorage[OWNER];
                    }
                    /**
                    * @dev Allows the current owner to transfer control of the contract to a newOwner.
                    * @param newOwner the address to transfer ownership to.
                    */
                    function transferOwnership(address newOwner) external onlyOwner {
                        _setOwner(newOwner);
                    }
                    /**
                    * @dev Sets a new owner address
                    */
                    function _setOwner(address newOwner) internal {
                        require(newOwner != address(0));
                        emit OwnershipTransferred(owner(), newOwner);
                        addressStorage[OWNER] = newOwner;
                    }
                }
                pragma solidity 0.4.24;
                interface IInterestReceiver {
                    function onInterestReceived(address _token) external;
                }
                pragma solidity 0.4.24;
                import "../interfaces/IBridgeValidators.sol";
                import "../upgradeability/EternalStorage.sol";
                import "./ValidatorStorage.sol";
                contract Validatable is EternalStorage, ValidatorStorage {
                    function validatorContract() public view returns (IBridgeValidators) {
                        return IBridgeValidators(addressStorage[VALIDATOR_CONTRACT]);
                    }
                    modifier onlyValidator() {
                        require(validatorContract().isValidator(msg.sender));
                        /* solcov ignore next */
                        _;
                    }
                    function requiredSignatures() public view returns (uint256) {
                        return validatorContract().requiredSignatures();
                    }
                }
                pragma solidity 0.4.24;
                import "../interfaces/IBridgeValidators.sol";
                library Message {
                    function addressArrayContains(address[] array, address value) internal pure returns (bool) {
                        for (uint256 i = 0; i < array.length; i++) {
                            if (array[i] == value) {
                                return true;
                            }
                        }
                        return false;
                    }
                    // layout of message :: bytes:
                    // offset  0: 32 bytes :: uint256 - message length
                    // offset 32: 20 bytes :: address - recipient address
                    // offset 52: 32 bytes :: uint256 - value
                    // offset 84: 32 bytes :: bytes32 - transaction hash
                    // offset 116: 20 bytes :: address - contract address to prevent double spending
                    // mload always reads 32 bytes.
                    // so we can and have to start reading recipient at offset 20 instead of 32.
                    // if we were to read at 32 the address would contain part of value and be corrupted.
                    // when reading from offset 20 mload will read 12 bytes (most of them zeros) followed
                    // by the 20 recipient address bytes and correctly convert it into an address.
                    // this saves some storage/gas over the alternative solution
                    // which is padding address to 32 bytes and reading recipient at offset 32.
                    // for more details see discussion in:
                    // https://github.com/paritytech/parity-bridge/issues/61
                    function parseMessage(bytes message)
                        internal
                        pure
                        returns (address recipient, uint256 amount, bytes32 txHash, address contractAddress)
                    {
                        require(isMessageValid(message));
                        assembly {
                            recipient := mload(add(message, 20))
                            amount := mload(add(message, 52))
                            txHash := mload(add(message, 84))
                            contractAddress := mload(add(message, 104))
                        }
                    }
                    function isMessageValid(bytes _msg) internal pure returns (bool) {
                        return _msg.length == requiredMessageLength();
                    }
                    function requiredMessageLength() internal pure returns (uint256) {
                        return 104;
                    }
                    function recoverAddressFromSignedMessage(bytes signature, bytes message, bool isAMBMessage)
                        internal
                        pure
                        returns (address)
                    {
                        require(signature.length == 65);
                        bytes32 r;
                        bytes32 s;
                        bytes1 v;
                        assembly {
                            r := mload(add(signature, 0x20))
                            s := mload(add(signature, 0x40))
                            v := mload(add(signature, 0x60))
                        }
                        require(uint8(v) == 27 || uint8(v) == 28);
                        require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0);
                        return ecrecover(hashMessage(message, isAMBMessage), uint8(v), r, s);
                    }
                    function hashMessage(bytes message, bool isAMBMessage) internal pure returns (bytes32) {
                        bytes memory prefix = "\\x19Ethereum Signed Message:\
                ";
                        if (isAMBMessage) {
                            return keccak256(abi.encodePacked(prefix, uintToString(message.length), message));
                        } else {
                            string memory msgLength = "104";
                            return keccak256(abi.encodePacked(prefix, msgLength, message));
                        }
                    }
                    /**
                    * @dev Validates provided signatures, only first requiredSignatures() number
                    * of signatures are going to be validated, these signatures should be from different validators.
                    * @param _message bytes message used to generate signatures
                    * @param _signatures bytes blob with signatures to be validated.
                    * First byte X is a number of signatures in a blob,
                    * next X bytes are v components of signatures,
                    * next 32 * X bytes are r components of signatures,
                    * next 32 * X bytes are s components of signatures.
                    * @param _validatorContract contract, which conforms to the IBridgeValidators interface,
                    * where info about current validators and required signatures is stored.
                    * @param isAMBMessage true if _message is an AMB message with arbitrary length.
                    */
                    function hasEnoughValidSignatures(
                        bytes _message,
                        bytes _signatures,
                        IBridgeValidators _validatorContract,
                        bool isAMBMessage
                    ) internal view {
                        require(isAMBMessage || isMessageValid(_message));
                        uint256 requiredSignatures = _validatorContract.requiredSignatures();
                        uint256 amount;
                        assembly {
                            amount := and(mload(add(_signatures, 1)), 0xff)
                        }
                        require(amount >= requiredSignatures);
                        bytes32 hash = hashMessage(_message, isAMBMessage);
                        address[] memory encounteredAddresses = new address[](requiredSignatures);
                        for (uint256 i = 0; i < requiredSignatures; i++) {
                            uint8 v;
                            bytes32 r;
                            bytes32 s;
                            uint256 posr = 33 + amount + 32 * i;
                            uint256 poss = posr + 32 * amount;
                            assembly {
                                v := mload(add(_signatures, add(2, i)))
                                r := mload(add(_signatures, posr))
                                s := mload(add(_signatures, poss))
                            }
                            address recoveredAddress = ecrecover(hash, v, r, s);
                            require(_validatorContract.isValidator(recoveredAddress));
                            require(!addressArrayContains(encounteredAddresses, recoveredAddress));
                            encounteredAddresses[i] = recoveredAddress;
                        }
                    }
                    function uintToString(uint256 i) internal pure returns (string) {
                        if (i == 0) return "0";
                        uint256 j = i;
                        uint256 length;
                        while (j != 0) {
                            length++;
                            j /= 10;
                        }
                        bytes memory bstr = new bytes(length);
                        uint256 k = length - 1;
                        while (i != 0) {
                            bstr[k--] = bytes1(48 + (i % 10));
                            i /= 10;
                        }
                        return string(bstr);
                    }
                }
                pragma solidity 0.4.24;
                import "../upgradeability/EternalStorage.sol";
                contract MessageRelay is EternalStorage {
                    function relayedMessages(bytes32 _txHash) public view returns (bool) {
                        return boolStorage[keccak256(abi.encodePacked("relayedMessages", _txHash))];
                    }
                    function setRelayedMessages(bytes32 _txHash, bool _status) internal {
                        boolStorage[keccak256(abi.encodePacked("relayedMessages", _txHash))] = _status;
                    }
                }
                pragma solidity 0.4.24;
                import "./Upgradeable.sol";
                import "./InitializableBridge.sol";
                import "openzeppelin-solidity/contracts/AddressUtils.sol";
                import "./Validatable.sol";
                import "./Ownable.sol";
                import "./Claimable.sol";
                import "./VersionableBridge.sol";
                import "./DecimalShiftBridge.sol";
                contract BasicBridge is
                    InitializableBridge,
                    Validatable,
                    Ownable,
                    Upgradeable,
                    Claimable,
                    VersionableBridge,
                    DecimalShiftBridge
                {
                    event GasPriceChanged(uint256 gasPrice);
                    event RequiredBlockConfirmationChanged(uint256 requiredBlockConfirmations);
                    bytes32 internal constant GAS_PRICE = 0x55b3774520b5993024893d303890baa4e84b1244a43c60034d1ced2d3cf2b04b; // keccak256(abi.encodePacked("gasPrice"))
                    bytes32 internal constant REQUIRED_BLOCK_CONFIRMATIONS = 0x916daedf6915000ff68ced2f0b6773fe6f2582237f92c3c95bb4d79407230071; // keccak256(abi.encodePacked("requiredBlockConfirmations"))
                    /**
                    * @dev Public setter for fallback gas price value. Only bridge owner can call this method.
                    * @param _gasPrice new value for the gas price.
                    */
                    function setGasPrice(uint256 _gasPrice) external onlyOwner {
                        _setGasPrice(_gasPrice);
                    }
                    function gasPrice() external view returns (uint256) {
                        return uintStorage[GAS_PRICE];
                    }
                    function setRequiredBlockConfirmations(uint256 _blockConfirmations) external onlyOwner {
                        _setRequiredBlockConfirmations(_blockConfirmations);
                    }
                    function _setRequiredBlockConfirmations(uint256 _blockConfirmations) internal {
                        require(_blockConfirmations > 0);
                        uintStorage[REQUIRED_BLOCK_CONFIRMATIONS] = _blockConfirmations;
                        emit RequiredBlockConfirmationChanged(_blockConfirmations);
                    }
                    function requiredBlockConfirmations() external view returns (uint256) {
                        return uintStorage[REQUIRED_BLOCK_CONFIRMATIONS];
                    }
                    /**
                    * @dev Internal function for updating fallback gas price value.
                    * @param _gasPrice new value for the gas price, zero gas price is allowed.
                    */
                    function _setGasPrice(uint256 _gasPrice) internal {
                        uintStorage[GAS_PRICE] = _gasPrice;
                        emit GasPriceChanged(_gasPrice);
                    }
                }
                pragma solidity 0.4.24;
                import "openzeppelin-solidity/contracts/math/SafeMath.sol";
                import "../upgradeability/EternalStorage.sol";
                import "./DecimalShiftBridge.sol";
                import "./Ownable.sol";
                contract BasicTokenBridge is EternalStorage, Ownable, DecimalShiftBridge {
                    using SafeMath for uint256;
                    event DailyLimitChanged(uint256 newLimit);
                    event ExecutionDailyLimitChanged(uint256 newLimit);
                    bytes32 internal constant MIN_PER_TX = 0xbbb088c505d18e049d114c7c91f11724e69c55ad6c5397e2b929e68b41fa05d1; // keccak256(abi.encodePacked("minPerTx"))
                    bytes32 internal constant MAX_PER_TX = 0x0f8803acad17c63ee38bf2de71e1888bc7a079a6f73658e274b08018bea4e29c; // keccak256(abi.encodePacked("maxPerTx"))
                    bytes32 internal constant DAILY_LIMIT = 0x4a6a899679f26b73530d8cf1001e83b6f7702e04b6fdb98f3c62dc7e47e041a5; // keccak256(abi.encodePacked("dailyLimit"))
                    bytes32 internal constant EXECUTION_MAX_PER_TX = 0xc0ed44c192c86d1cc1ba51340b032c2766b4a2b0041031de13c46dd7104888d5; // keccak256(abi.encodePacked("executionMaxPerTx"))
                    bytes32 internal constant EXECUTION_DAILY_LIMIT = 0x21dbcab260e413c20dc13c28b7db95e2b423d1135f42bb8b7d5214a92270d237; // keccak256(abi.encodePacked("executionDailyLimit"))
                    function totalSpentPerDay(uint256 _day) public view returns (uint256) {
                        return uintStorage[keccak256(abi.encodePacked("totalSpentPerDay", _day))];
                    }
                    function totalExecutedPerDay(uint256 _day) public view returns (uint256) {
                        return uintStorage[keccak256(abi.encodePacked("totalExecutedPerDay", _day))];
                    }
                    function dailyLimit() public view returns (uint256) {
                        return uintStorage[DAILY_LIMIT];
                    }
                    function executionDailyLimit() public view returns (uint256) {
                        return uintStorage[EXECUTION_DAILY_LIMIT];
                    }
                    function maxPerTx() public view returns (uint256) {
                        return uintStorage[MAX_PER_TX];
                    }
                    function executionMaxPerTx() public view returns (uint256) {
                        return uintStorage[EXECUTION_MAX_PER_TX];
                    }
                    function minPerTx() public view returns (uint256) {
                        return uintStorage[MIN_PER_TX];
                    }
                    function withinLimit(uint256 _amount) public view returns (bool) {
                        uint256 nextLimit = totalSpentPerDay(getCurrentDay()).add(_amount);
                        return dailyLimit() >= nextLimit && _amount <= maxPerTx() && _amount >= minPerTx();
                    }
                    function withinExecutionLimit(uint256 _amount) public view returns (bool) {
                        uint256 nextLimit = totalExecutedPerDay(getCurrentDay()).add(_amount);
                        return executionDailyLimit() >= nextLimit && _amount <= executionMaxPerTx();
                    }
                    function getCurrentDay() public view returns (uint256) {
                        return now / 1 days;
                    }
                    function addTotalSpentPerDay(uint256 _day, uint256 _value) internal {
                        uintStorage[keccak256(abi.encodePacked("totalSpentPerDay", _day))] = totalSpentPerDay(_day).add(_value);
                    }
                    function addTotalExecutedPerDay(uint256 _day, uint256 _value) internal {
                        uintStorage[keccak256(abi.encodePacked("totalExecutedPerDay", _day))] = totalExecutedPerDay(_day).add(_value);
                    }
                    function setDailyLimit(uint256 _dailyLimit) external onlyOwner {
                        require(_dailyLimit > maxPerTx() || _dailyLimit == 0);
                        uintStorage[DAILY_LIMIT] = _dailyLimit;
                        emit DailyLimitChanged(_dailyLimit);
                    }
                    function setExecutionDailyLimit(uint256 _dailyLimit) external onlyOwner {
                        require(_dailyLimit > executionMaxPerTx() || _dailyLimit == 0);
                        uintStorage[EXECUTION_DAILY_LIMIT] = _dailyLimit;
                        emit ExecutionDailyLimitChanged(_dailyLimit);
                    }
                    function setExecutionMaxPerTx(uint256 _maxPerTx) external onlyOwner {
                        require(_maxPerTx < executionDailyLimit());
                        uintStorage[EXECUTION_MAX_PER_TX] = _maxPerTx;
                    }
                    function setMaxPerTx(uint256 _maxPerTx) external onlyOwner {
                        require(_maxPerTx == 0 || (_maxPerTx > minPerTx() && _maxPerTx < dailyLimit()));
                        uintStorage[MAX_PER_TX] = _maxPerTx;
                    }
                    function setMinPerTx(uint256 _minPerTx) external onlyOwner {
                        require(_minPerTx > 0 && _minPerTx < dailyLimit() && _minPerTx < maxPerTx());
                        uintStorage[MIN_PER_TX] = _minPerTx;
                    }
                    /**
                    * @dev Retrieves maximum available bridge amount per one transaction taking into account maxPerTx() and dailyLimit() parameters.
                    * @return minimum of maxPerTx parameter and remaining daily quota.
                    */
                    function maxAvailablePerTx() public view returns (uint256) {
                        uint256 _maxPerTx = maxPerTx();
                        uint256 _dailyLimit = dailyLimit();
                        uint256 _spent = totalSpentPerDay(getCurrentDay());
                        uint256 _remainingOutOfDaily = _dailyLimit > _spent ? _dailyLimit - _spent : 0;
                        return _maxPerTx < _remainingOutOfDaily ? _maxPerTx : _remainingOutOfDaily;
                    }
                    function _setLimits(uint256[3] _limits) internal {
                        require(
                            _limits[2] > 0 && // minPerTx > 0
                                _limits[1] > _limits[2] && // maxPerTx > minPerTx
                                _limits[0] > _limits[1] // dailyLimit > maxPerTx
                        );
                        uintStorage[DAILY_LIMIT] = _limits[0];
                        uintStorage[MAX_PER_TX] = _limits[1];
                        uintStorage[MIN_PER_TX] = _limits[2];
                        emit DailyLimitChanged(_limits[0]);
                    }
                    function _setExecutionLimits(uint256[2] _limits) internal {
                        require(_limits[1] < _limits[0]); // foreignMaxPerTx < foreignDailyLimit
                        uintStorage[EXECUTION_DAILY_LIMIT] = _limits[0];
                        uintStorage[EXECUTION_MAX_PER_TX] = _limits[1];
                        emit ExecutionDailyLimitChanged(_limits[0]);
                    }
                }
                // SPDX-License-Identifier:MIT
                pragma solidity 0.4.24;
                /**
                 * a contract must implement this interface in order to support relayed transaction.
                 * It is better to inherit the BaseRelayRecipient as its implementation.
                 */
                contract IRelayRecipient {
                    /**
                     * return if the forwarder is trusted to forward relayed transactions to us.
                     * the forwarder is required to verify the sender's signature, and verify
                     * the call is not a replay.
                     */
                    function isTrustedForwarder(address forwarder) public view returns (bool);
                    /**
                     * return the sender of this call.
                     * if the call came through our trusted forwarder, then the real sender is appended as the last 20 bytes
                     * of the msg.data.
                     * otherwise, return `msg.sender`
                     * should be used in the contract anywhere instead of msg.sender
                     */
                    function _msgSender() internal view returns (address);
                    /**
                     * return the msg.data of this call.
                     * if the call came through our trusted forwarder, then the real sender was appended as the last 20 bytes
                     * of the msg.data - so this method will strip those 20 bytes off.
                     * otherwise, return `msg.data`
                     * should be used in the contract instead of msg.data, where the difference matters (e.g. when explicitly
                     * signing or hashing the
                     */
                    function _msgData() internal view returns (bytes memory);
                    function versionRecipient() external view returns (string memory);
                }
                pragma solidity ^0.4.24;
                /**
                 * @title ERC20Basic
                 * @dev Simpler version of ERC20 interface
                 * See https://github.com/ethereum/EIPs/issues/179
                 */
                contract ERC20Basic {
                  function totalSupply() public view returns (uint256);
                  function balanceOf(address _who) public view returns (uint256);
                  function transfer(address _to, uint256 _value) public returns (bool);
                  event Transfer(address indexed from, address indexed to, uint256 value);
                }
                pragma solidity 0.4.24;
                interface IUpgradeabilityOwnerStorage {
                    function upgradeabilityOwner() external view returns (address);
                }
                pragma solidity 0.4.24;
                interface IBridgeValidators {
                    function isValidator(address _validator) external view returns (bool);
                    function requiredSignatures() external view returns (uint256);
                    function owner() external view returns (address);
                }
                pragma solidity 0.4.24;
                contract ValidatorStorage {
                    bytes32 internal constant VALIDATOR_CONTRACT = 0x5a74bb7e202fb8e4bf311841c7d64ec19df195fee77d7e7ae749b27921b6ddfe; // keccak256(abi.encodePacked("validatorContract"))
                }
                pragma solidity 0.4.24;
                import "../interfaces/IUpgradeabilityOwnerStorage.sol";
                contract Upgradeable {
                    // Avoid using onlyUpgradeabilityOwner name to prevent issues with implementation from proxy contract
                    modifier onlyIfUpgradeabilityOwner() {
                        require(msg.sender == IUpgradeabilityOwnerStorage(this).upgradeabilityOwner());
                        /* solcov ignore next */
                        _;
                    }
                }
                pragma solidity 0.4.24;
                import "./Initializable.sol";
                contract InitializableBridge is Initializable {
                    bytes32 internal constant DEPLOYED_AT_BLOCK = 0xb120ceec05576ad0c710bc6e85f1768535e27554458f05dcbb5c65b8c7a749b0; // keccak256(abi.encodePacked("deployedAtBlock"))
                    function deployedAtBlock() external view returns (uint256) {
                        return uintStorage[DEPLOYED_AT_BLOCK];
                    }
                }
                pragma solidity 0.4.24;
                import "../libraries/Address.sol";
                import "../libraries/SafeERC20.sol";
                /**
                 * @title Claimable
                 * @dev Implementation of the claiming utils that can be useful for withdrawing accidentally sent tokens that are not used in bridge operations.
                 */
                contract Claimable {
                    using SafeERC20 for address;
                    /**
                     * Throws if a given address is equal to address(0)
                     */
                    modifier validAddress(address _to) {
                        require(_to != address(0));
                        /* solcov ignore next */
                        _;
                    }
                    /**
                     * @dev Withdraws the erc20 tokens or native coins from this contract.
                     * Caller should additionally check that the claimed token is not a part of bridge operations (i.e. that token != erc20token()).
                     * @param _token address of the claimed token or address(0) for native coins.
                     * @param _to address of the tokens/coins receiver.
                     */
                    function claimValues(address _token, address _to) internal validAddress(_to) {
                        if (_token == address(0)) {
                            claimNativeCoins(_to);
                        } else {
                            claimErc20Tokens(_token, _to);
                        }
                    }
                    /**
                     * @dev Internal function for withdrawing all native coins from the contract.
                     * @param _to address of the coins receiver.
                     */
                    function claimNativeCoins(address _to) internal {
                        uint256 value = address(this).balance;
                        Address.safeSendValue(_to, value);
                    }
                    /**
                     * @dev Internal function for withdrawing all tokens of ssome particular ERC20 contract from this contract.
                     * @param _token address of the claimed ERC20 token.
                     * @param _to address of the tokens receiver.
                     */
                    function claimErc20Tokens(address _token, address _to) internal {
                        ERC20Basic token = ERC20Basic(_token);
                        uint256 balance = token.balanceOf(this);
                        _token.safeTransfer(_to, balance);
                    }
                }
                pragma solidity 0.4.24;
                contract VersionableBridge {
                    function getBridgeInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) {
                        return (6, 1, 0);
                    }
                    /* solcov ignore next */
                    function getBridgeMode() external pure returns (bytes4);
                }
                pragma solidity 0.4.24;
                import "../upgradeability/EternalStorage.sol";
                import "openzeppelin-solidity/contracts/math/SafeMath.sol";
                contract DecimalShiftBridge is EternalStorage {
                    using SafeMath for uint256;
                    bytes32 internal constant DECIMAL_SHIFT = 0x1e8ecaafaddea96ed9ac6d2642dcdfe1bebe58a930b1085842d8fc122b371ee5; // keccak256(abi.encodePacked("decimalShift"))
                    /**
                    * @dev Internal function for setting the decimal shift for bridge operations.
                    * Decimal shift can be positive, negative, or equal to zero.
                    * It has the following meaning: N tokens in the foreign chain are equivalent to N * pow(10, shift) tokens on the home side.
                    * @param _shift new value of decimal shift.
                    */
                    function _setDecimalShift(int256 _shift) internal {
                        // since 1 wei * 10**77 > 2**255, it does not make any sense to use higher values
                        require(_shift > -77 && _shift < 77);
                        uintStorage[DECIMAL_SHIFT] = uint256(_shift);
                    }
                    /**
                    * @dev Returns the value of foreign-to-home decimal shift.
                    * @return decimal shift.
                    */
                    function decimalShift() public view returns (int256) {
                        return int256(uintStorage[DECIMAL_SHIFT]);
                    }
                    /**
                    * @dev Converts the amount of home tokens into the equivalent amount of foreign tokens.
                    * @param _value amount of home tokens.
                    * @return equivalent amount of foreign tokens.
                    */
                    function _unshiftValue(uint256 _value) internal view returns (uint256) {
                        return _shiftUint(_value, -decimalShift());
                    }
                    /**
                    * @dev Converts the amount of foreign tokens into the equivalent amount of home tokens.
                    * @param _value amount of foreign tokens.
                    * @return equivalent amount of home tokens.
                    */
                    function _shiftValue(uint256 _value) internal view returns (uint256) {
                        return _shiftUint(_value, decimalShift());
                    }
                    /**
                    * @dev Calculates _value * pow(10, _shift).
                    * @param _value amount of tokens.
                    * @param _shift decimal shift to apply.
                    * @return shifted value.
                    */
                    function _shiftUint(uint256 _value, int256 _shift) private pure returns (uint256) {
                        if (_shift == 0) {
                            return _value;
                        }
                        if (_shift > 0) {
                            return _value.mul(10**uint256(_shift));
                        }
                        return _value.div(10**uint256(-_shift));
                    }
                }
                pragma solidity ^0.4.24;
                /**
                 * @title SafeMath
                 * @dev Math operations with safety checks that throw on error
                 */
                library SafeMath {
                  /**
                  * @dev Multiplies two numbers, throws on overflow.
                  */
                  function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
                    // Gas optimization: this is cheaper than asserting 'a' not being zero, but the
                    // benefit is lost if 'b' is also tested.
                    // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                    if (_a == 0) {
                      return 0;
                    }
                    c = _a * _b;
                    assert(c / _a == _b);
                    return c;
                  }
                  /**
                  * @dev Integer division of two numbers, truncating the quotient.
                  */
                  function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
                    // assert(_b > 0); // Solidity automatically throws when dividing by 0
                    // uint256 c = _a / _b;
                    // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
                    return _a / _b;
                  }
                  /**
                  * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
                  */
                  function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
                    assert(_b <= _a);
                    return _a - _b;
                  }
                  /**
                  * @dev Adds two numbers, throws on overflow.
                  */
                  function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
                    c = _a + _b;
                    assert(c >= _a);
                    return c;
                  }
                }
                pragma solidity 0.4.24;
                import "../upgradeability/EternalStorage.sol";
                contract Initializable is EternalStorage {
                    bytes32 internal constant INITIALIZED = 0x0a6f646cd611241d8073675e00d1a1ff700fbf1b53fcf473de56d1e6e4b714ba; // keccak256(abi.encodePacked("isInitialized"))
                    function setInitialize() internal {
                        boolStorage[INITIALIZED] = true;
                    }
                    function isInitialized() public view returns (bool) {
                        return boolStorage[INITIALIZED];
                    }
                }
                pragma solidity 0.4.24;
                import "../upgradeable_contracts/Sacrifice.sol";
                /**
                 * @title Address
                 * @dev Helper methods for Address type.
                 */
                library Address {
                    /**
                    * @dev Try to send native tokens to the address. If it fails, it will force the transfer by creating a selfdestruct contract
                    * @param _receiver address that will receive the native tokens
                    * @param _value the amount of native tokens to send
                    */
                    function safeSendValue(address _receiver, uint256 _value) internal {
                        if (!_receiver.send(_value)) {
                            (new Sacrifice).value(_value)(_receiver);
                        }
                    }
                }
                pragma solidity 0.4.24;
                import "openzeppelin-solidity/contracts/math/SafeMath.sol";
                import "../interfaces/ERC677.sol";
                /**
                 * @title SafeERC20
                 * @dev Helper methods for safe token transfers.
                 * Functions perform additional checks to be sure that token transfer really happened.
                 */
                library SafeERC20 {
                    using SafeMath for uint256;
                    /**
                    * @dev Same as ERC20.transfer(address,uint256) but with extra consistency checks.
                    * @param _token address of the token contract
                    * @param _to address of the receiver
                    * @param _value amount of tokens to send
                    */
                    function safeTransfer(address _token, address _to, uint256 _value) internal {
                        LegacyERC20(_token).transfer(_to, _value);
                        assembly {
                            if returndatasize {
                                returndatacopy(0, 0, 32)
                                if iszero(mload(0)) {
                                    revert(0, 0)
                                }
                            }
                        }
                    }
                    /**
                    * @dev Same as ERC20.transferFrom(address,address,uint256) but with extra consistency checks.
                    * @param _token address of the token contract
                    * @param _from address of the sender
                    * @param _value amount of tokens to send
                    */
                    function safeTransferFrom(address _token, address _from, uint256 _value) internal {
                        LegacyERC20(_token).transferFrom(_from, address(this), _value);
                        assembly {
                            if returndatasize {
                                returndatacopy(0, 0, 32)
                                if iszero(mload(0)) {
                                    revert(0, 0)
                                }
                            }
                        }
                    }
                }
                pragma solidity 0.4.24;
                contract Sacrifice {
                    constructor(address _recipient) public payable {
                        selfdestruct(_recipient);
                    }
                }
                pragma solidity 0.4.24;
                import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
                contract ERC677 is ERC20 {
                    event Transfer(address indexed from, address indexed to, uint256 value, bytes data);
                    function transferAndCall(address, uint256, bytes) external returns (bool);
                    function increaseAllowance(address spender, uint256 addedValue) public returns (bool);
                    function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool);
                }
                contract LegacyERC20 {
                    function transfer(address _spender, uint256 _value) public; // returns (bool);
                    function transferFrom(address _owner, address _spender, uint256 _value) public; // returns (bool);
                }
                

                File 4 of 5: EternalStorageProxy
                pragma solidity 0.4.24;
                
                // File: contracts/upgradeability/EternalStorage.sol
                
                /**
                 * @title EternalStorage
                 * @dev This contract holds all the necessary state variables to carry out the storage of any contract.
                 */
                contract EternalStorage {
                
                    mapping(bytes32 => uint256) internal uintStorage;
                    mapping(bytes32 => string) internal stringStorage;
                    mapping(bytes32 => address) internal addressStorage;
                    mapping(bytes32 => bytes) internal bytesStorage;
                    mapping(bytes32 => bool) internal boolStorage;
                    mapping(bytes32 => int256) internal intStorage;
                
                }
                
                // File: contracts/upgradeability/UpgradeabilityOwnerStorage.sol
                
                /**
                 * @title UpgradeabilityOwnerStorage
                 * @dev This contract keeps track of the upgradeability owner
                 */
                contract UpgradeabilityOwnerStorage {
                    // Owner of the contract
                    address private _upgradeabilityOwner;
                
                    /**
                    * @dev Tells the address of the owner
                    * @return the address of the owner
                    */
                    function upgradeabilityOwner() public view returns (address) {
                        return _upgradeabilityOwner;
                    }
                
                    /**
                    * @dev Sets the address of the owner
                    */
                    function setUpgradeabilityOwner(address newUpgradeabilityOwner) internal {
                        _upgradeabilityOwner = newUpgradeabilityOwner;
                    }
                }
                
                // File: contracts/upgradeability/Proxy.sol
                
                /**
                 * @title Proxy
                 * @dev Gives the possibility to delegate any call to a foreign implementation.
                 */
                contract Proxy {
                
                  /**
                  * @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);
                
                  /**
                  * @dev Fallback function allowing to perform a delegatecall to the given implementation.
                  * This function will return whatever the implementation call returns
                  */
                    function () payable public {
                        address _impl = implementation();
                        require(_impl != address(0));
                        assembly {
                            /*
                                0x40 is the "free memory slot", meaning a pointer to next slot of empty memory. mload(0x40)
                                loads the data in the free memory slot, so `ptr` is a pointer to the next slot of empty
                                memory. It's needed because we're going to write the return data of delegatecall to the
                                free memory slot.
                            */
                            let ptr := mload(0x40)
                            /*
                                `calldatacopy` is copy calldatasize bytes from calldata
                                First argument is the destination to which data is copied(ptr)
                                Second argument specifies the start position of the copied data.
                                    Since calldata is sort of its own unique location in memory,
                                    0 doesn't refer to 0 in memory or 0 in storage - it just refers to the zeroth byte of calldata.
                                    That's always going to be the zeroth byte of the function selector.
                                Third argument, calldatasize, specifies how much data will be copied.
                                    calldata is naturally calldatasize bytes long (same thing as msg.data.length)
                            */
                            calldatacopy(ptr, 0, calldatasize)
                            /*
                                delegatecall params explained:
                                gas: the amount of gas to provide for the call. `gas` is an Opcode that gives
                                    us the amount of gas still available to execution
                
                                _impl: address of the contract to delegate to
                
                                ptr: to pass copied data
                
                                calldatasize: loads the size of `bytes memory data`, same as msg.data.length
                
                                0, 0: These are for the `out` and `outsize` params. Because the output could be dynamic,
                                        these are set to 0, 0 so the output data will not be written to memory. The output
                                        data will be read using `returndatasize` and `returdatacopy` instead.
                
                                result: This will be 0 if the call fails and 1 if it succeeds
                            */
                            let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
                            /*
                
                            */
                            /*
                                ptr current points to the value stored at 0x40,
                                because we assigned it like ptr := mload(0x40).
                                Because we use 0x40 as a free memory pointer,
                                we want to make sure that the next time we want to allocate memory,
                                we aren't overwriting anything important.
                                So, by adding ptr and returndatasize,
                                we get a memory location beyond the end of the data we will be copying to ptr.
                                We place this in at 0x40, and any reads from 0x40 will now read from free memory
                            */
                            mstore(0x40, add(ptr, returndatasize))
                            /*
                                `returndatacopy` is an Opcode that copies the last return data to a slot. `ptr` is the
                                    slot it will copy to, 0 means copy from the beginning of the return data, and size is
                                    the amount of data to copy.
                                `returndatasize` is an Opcode that gives us the size of the last return data. In this case, that is the size of the data returned from delegatecall
                            */
                            returndatacopy(ptr, 0, returndatasize)
                
                            /*
                                if `result` is 0, revert.
                                if `result` is 1, return `size` amount of data from `ptr`. This is the data that was
                                copied to `ptr` from the delegatecall return data
                            */
                            switch result
                            case 0 { revert(ptr, returndatasize) }
                            default { return(ptr, returndatasize) }
                        }
                    }
                }
                
                // File: contracts/upgradeability/UpgradeabilityStorage.sol
                
                /**
                 * @title UpgradeabilityStorage
                 * @dev This contract holds all the necessary state variables to support the upgrade functionality
                 */
                contract UpgradeabilityStorage {
                    // Version name of the current implementation
                    uint256 internal _version;
                
                    // Address of the current implementation
                    address internal _implementation;
                
                    /**
                    * @dev Tells the version name of the current implementation
                    * @return string representing the name of the current version
                    */
                    function version() public view returns (uint256) {
                        return _version;
                    }
                
                    /**
                    * @dev Tells the address of the current implementation
                    * @return address of the current implementation
                    */
                    function implementation() public view returns (address) {
                        return _implementation;
                    }
                }
                
                // File: contracts/upgradeability/UpgradeabilityProxy.sol
                
                /**
                 * @title UpgradeabilityProxy
                 * @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded
                 */
                contract UpgradeabilityProxy is Proxy, UpgradeabilityStorage {
                    /**
                    * @dev This event will be emitted every time the implementation gets upgraded
                    * @param version representing the version name of the upgraded implementation
                    * @param implementation representing the address of the upgraded implementation
                    */
                    event Upgraded(uint256 version, address indexed implementation);
                
                    /**
                    * @dev Upgrades the implementation address
                    * @param version representing the version name of the new implementation to be set
                    * @param implementation representing the address of the new implementation to be set
                    */
                    function _upgradeTo(uint256 version, address implementation) internal {
                        require(_implementation != implementation);
                        require(version > _version);
                        _version = version;
                        _implementation = implementation;
                        emit Upgraded(version, implementation);
                    }
                }
                
                // File: contracts/upgradeability/OwnedUpgradeabilityProxy.sol
                
                /**
                 * @title OwnedUpgradeabilityProxy
                 * @dev This contract combines an upgradeability proxy with basic authorization control functionalities
                 */
                contract OwnedUpgradeabilityProxy is UpgradeabilityOwnerStorage, UpgradeabilityProxy {
                  /**
                  * @dev Event to show ownership has been transferred
                  * @param previousOwner representing the address of the previous owner
                  * @param newOwner representing the address of the new owner
                  */
                    event ProxyOwnershipTransferred(address previousOwner, address newOwner);
                
                    /**
                    * @dev the constructor sets the original owner of the contract to the sender account.
                    */
                    constructor() public {
                        setUpgradeabilityOwner(msg.sender);
                    }
                
                    /**
                    * @dev Throws if called by any account other than the owner.
                    */
                    modifier onlyProxyOwner() {
                        require(msg.sender == proxyOwner());
                        _;
                    }
                
                    /**
                    * @dev Tells the address of the proxy owner
                    * @return the address of the proxy owner
                    */
                    function proxyOwner() public view returns (address) {
                        return upgradeabilityOwner();
                    }
                
                    /**
                    * @dev Allows the current owner to transfer control of the contract to a newOwner.
                    * @param newOwner The address to transfer ownership to.
                    */
                    function transferProxyOwnership(address newOwner) public onlyProxyOwner {
                        require(newOwner != address(0));
                        emit ProxyOwnershipTransferred(proxyOwner(), newOwner);
                        setUpgradeabilityOwner(newOwner);
                    }
                
                    /**
                    * @dev Allows the upgradeability owner to upgrade the current version of the proxy.
                    * @param version representing the version name of the new implementation to be set.
                    * @param implementation representing the address of the new implementation to be set.
                    */
                    function upgradeTo(uint256 version, address implementation) public onlyProxyOwner {
                        _upgradeTo(version, implementation);
                    }
                
                    /**
                    * @dev Allows the upgradeability owner to upgrade the current version of the proxy and call the new implementation
                    * to initialize whatever is needed through a low level call.
                    * @param version representing the version name of the new implementation to be set.
                    * @param implementation representing the address of the new implementation to be set.
                    * @param data represents the msg.data to bet sent in the low level call. This parameter may include the function
                    * signature of the implementation to be called with the needed payload
                    */
                    function upgradeToAndCall(uint256 version, address implementation, bytes data) payable public onlyProxyOwner {
                        upgradeTo(version, implementation);
                        require(address(this).call.value(msg.value)(data));
                    }
                }
                
                // File: contracts/upgradeability/EternalStorageProxy.sol
                
                /**
                 * @title EternalStorageProxy
                 * @dev This proxy holds the storage of the token contract and delegates every call to the current implementation set.
                 * Besides, it allows to upgrade the token's behaviour towards further implementations, and provides basic
                 * authorization control functionalities
                 */
                contract EternalStorageProxy is OwnedUpgradeabilityProxy, EternalStorage {}

                File 5 of 5: BridgeValidators
                // File: contracts/upgradeability/EternalStorage.sol
                
                pragma solidity 0.4.24;
                
                /**
                 * @title EternalStorage
                 * @dev This contract holds all the necessary state variables to carry out the storage of any contract.
                 */
                contract EternalStorage {
                    mapping(bytes32 => uint256) internal uintStorage;
                    mapping(bytes32 => string) internal stringStorage;
                    mapping(bytes32 => address) internal addressStorage;
                    mapping(bytes32 => bytes) internal bytesStorage;
                    mapping(bytes32 => bool) internal boolStorage;
                    mapping(bytes32 => int256) internal intStorage;
                
                }
                
                // File: contracts/upgradeable_contracts/Ownable.sol
                
                pragma solidity 0.4.24;
                
                
                /**
                 * @title Ownable
                 * @dev This contract has an owner address providing basic authorization control
                 */
                contract Ownable is EternalStorage {
                    /**
                    * @dev Event to show ownership has been transferred
                    * @param previousOwner representing the address of the previous owner
                    * @param newOwner representing the address of the new owner
                    */
                    event OwnershipTransferred(address previousOwner, address newOwner);
                
                    /**
                    * @dev Throws if called by any account other than the owner.
                    */
                    modifier onlyOwner() {
                        require(msg.sender == owner());
                        /* solcov ignore next */
                        _;
                    }
                
                    bytes32 internal constant OWNER = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0; // keccak256(abi.encodePacked("owner"))
                
                    /**
                    * @dev Tells the address of the owner
                    * @return the address of the owner
                    */
                    function owner() public view returns (address) {
                        return addressStorage[OWNER];
                    }
                
                    /**
                    * @dev Allows the current owner to transfer control of the contract to a newOwner.
                    * @param newOwner the address to transfer ownership to.
                    */
                    function transferOwnership(address newOwner) external onlyOwner {
                        require(newOwner != address(0));
                        setOwner(newOwner);
                    }
                
                    /**
                    * @dev Sets a new owner address
                    */
                    function setOwner(address newOwner) internal {
                        emit OwnershipTransferred(owner(), newOwner);
                        addressStorage[OWNER] = newOwner;
                    }
                }
                
                // File: openzeppelin-solidity/contracts/math/SafeMath.sol
                
                pragma solidity ^0.4.24;
                
                
                /**
                 * @title SafeMath
                 * @dev Math operations with safety checks that throw on error
                 */
                library SafeMath {
                
                  /**
                  * @dev Multiplies two numbers, throws on overflow.
                  */
                  function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
                    // Gas optimization: this is cheaper than asserting 'a' not being zero, but the
                    // benefit is lost if 'b' is also tested.
                    // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                    if (_a == 0) {
                      return 0;
                    }
                
                    c = _a * _b;
                    assert(c / _a == _b);
                    return c;
                  }
                
                  /**
                  * @dev Integer division of two numbers, truncating the quotient.
                  */
                  function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
                    // assert(_b > 0); // Solidity automatically throws when dividing by 0
                    // uint256 c = _a / _b;
                    // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
                    return _a / _b;
                  }
                
                  /**
                  * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
                  */
                  function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
                    assert(_b <= _a);
                    return _a - _b;
                  }
                
                  /**
                  * @dev Adds two numbers, throws on overflow.
                  */
                  function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
                    c = _a + _b;
                    assert(c >= _a);
                    return c;
                  }
                }
                
                // File: contracts/upgradeable_contracts/Initializable.sol
                
                pragma solidity 0.4.24;
                
                
                contract Initializable is EternalStorage {
                    bytes32 internal constant INITIALIZED = 0x0a6f646cd611241d8073675e00d1a1ff700fbf1b53fcf473de56d1e6e4b714ba; // keccak256(abi.encodePacked("isInitialized"))
                
                    function setInitialize() internal {
                        boolStorage[INITIALIZED] = true;
                    }
                
                    function isInitialized() public view returns (bool) {
                        return boolStorage[INITIALIZED];
                    }
                }
                
                // File: contracts/upgradeable_contracts/InitializableBridge.sol
                
                pragma solidity 0.4.24;
                
                
                contract InitializableBridge is Initializable {
                    bytes32 internal constant DEPLOYED_AT_BLOCK = 0xb120ceec05576ad0c710bc6e85f1768535e27554458f05dcbb5c65b8c7a749b0; // keccak256(abi.encodePacked("deployedAtBlock"))
                
                    function deployedAtBlock() external view returns (uint256) {
                        return uintStorage[DEPLOYED_AT_BLOCK];
                    }
                }
                
                // File: contracts/upgradeable_contracts/BaseBridgeValidators.sol
                
                pragma solidity 0.4.24;
                
                
                
                
                contract BaseBridgeValidators is InitializableBridge, Ownable {
                    using SafeMath for uint256;
                
                    address public constant F_ADDR = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF;
                    uint256 internal constant MAX_VALIDATORS = 100;
                    bytes32 internal constant REQUIRED_SIGNATURES = 0xd18ea17c351d6834a0e568067fb71804d2a588d5e26d60f792b1c724b1bd53b1; // keccak256(abi.encodePacked("requiredSignatures"))
                    bytes32 internal constant VALIDATOR_COUNT = 0x8656d603d9f985c3483946a92789d52202f49736384ba131cb92f62c4c1aa082; // keccak256(abi.encodePacked("validatorCount"))
                
                    event ValidatorAdded(address indexed validator);
                    event ValidatorRemoved(address indexed validator);
                    event RequiredSignaturesChanged(uint256 requiredSignatures);
                
                    function setRequiredSignatures(uint256 _requiredSignatures) external onlyOwner {
                        require(validatorCount() >= _requiredSignatures);
                        require(_requiredSignatures != 0);
                        uintStorage[REQUIRED_SIGNATURES] = _requiredSignatures;
                        emit RequiredSignaturesChanged(_requiredSignatures);
                    }
                
                    function getBridgeValidatorsInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) {
                        return (2, 3, 0);
                    }
                
                    function validatorList() external view returns (address[]) {
                        address[] memory list = new address[](validatorCount());
                        uint256 counter = 0;
                        address nextValidator = getNextValidator(F_ADDR);
                        require(nextValidator != address(0));
                
                        while (nextValidator != F_ADDR) {
                            list[counter] = nextValidator;
                            nextValidator = getNextValidator(nextValidator);
                            counter++;
                
                            require(nextValidator != address(0));
                        }
                
                        return list;
                    }
                
                    function _addValidator(address _validator) internal {
                        require(_validator != address(0) && _validator != F_ADDR);
                        require(!isValidator(_validator));
                
                        address firstValidator = getNextValidator(F_ADDR);
                        require(firstValidator != address(0));
                        setNextValidator(_validator, firstValidator);
                        setNextValidator(F_ADDR, _validator);
                        setValidatorCount(validatorCount().add(1));
                    }
                
                    function _removeValidator(address _validator) internal {
                        require(validatorCount() > requiredSignatures());
                        require(isValidator(_validator));
                        address validatorsNext = getNextValidator(_validator);
                        address index = F_ADDR;
                        address next = getNextValidator(index);
                        require(next != address(0));
                
                        while (next != _validator) {
                            index = next;
                            next = getNextValidator(index);
                
                            require(next != F_ADDR && next != address(0));
                        }
                
                        setNextValidator(index, validatorsNext);
                        deleteItemFromAddressStorage("validatorsList", _validator);
                        setValidatorCount(validatorCount().sub(1));
                    }
                
                    function requiredSignatures() public view returns (uint256) {
                        return uintStorage[REQUIRED_SIGNATURES];
                    }
                
                    function validatorCount() public view returns (uint256) {
                        return uintStorage[VALIDATOR_COUNT];
                    }
                
                    function isValidator(address _validator) public view returns (bool) {
                        return _validator != F_ADDR && getNextValidator(_validator) != address(0);
                    }
                
                    function getNextValidator(address _address) public view returns (address) {
                        return addressStorage[keccak256(abi.encodePacked("validatorsList", _address))];
                    }
                
                    function deleteItemFromAddressStorage(string _mapName, address _address) internal {
                        delete addressStorage[keccak256(abi.encodePacked(_mapName, _address))];
                    }
                
                    function setValidatorCount(uint256 _validatorCount) internal {
                        uintStorage[VALIDATOR_COUNT] = _validatorCount;
                    }
                
                    function setNextValidator(address _prevValidator, address _validator) internal {
                        addressStorage[keccak256(abi.encodePacked("validatorsList", _prevValidator))] = _validator;
                    }
                
                    function isValidatorDuty(address _validator) external view returns (bool) {
                        uint256 counter = 0;
                        address next = getNextValidator(F_ADDR);
                        require(next != address(0));
                
                        while (next != F_ADDR) {
                            if (next == _validator) {
                                return (block.number % validatorCount() == counter);
                            }
                
                            next = getNextValidator(next);
                            counter++;
                
                            require(next != address(0));
                        }
                
                        return false;
                    }
                }
                
                // File: contracts/upgradeable_contracts/BridgeValidators.sol
                
                pragma solidity 0.4.24;
                
                
                contract BridgeValidators is BaseBridgeValidators {
                    function initialize(uint256 _requiredSignatures, address[] _initialValidators, address _owner)
                        external
                        returns (bool)
                    {
                        require(!isInitialized());
                        require(_owner != address(0));
                        setOwner(_owner);
                        require(_requiredSignatures != 0);
                        require(_initialValidators.length >= _requiredSignatures);
                        require(_initialValidators.length <= MAX_VALIDATORS);
                
                        for (uint256 i = 0; i < _initialValidators.length; i++) {
                            require(_initialValidators[i] != address(0) && _initialValidators[i] != F_ADDR);
                            require(!isValidator(_initialValidators[i]));
                
                            if (i == 0) {
                                setNextValidator(F_ADDR, _initialValidators[i]);
                                if (_initialValidators.length == 1) {
                                    setNextValidator(_initialValidators[i], F_ADDR);
                                }
                            } else if (i == _initialValidators.length - 1) {
                                setNextValidator(_initialValidators[i - 1], _initialValidators[i]);
                                setNextValidator(_initialValidators[i], F_ADDR);
                            } else {
                                setNextValidator(_initialValidators[i - 1], _initialValidators[i]);
                            }
                
                            emit ValidatorAdded(_initialValidators[i]);
                        }
                
                        setValidatorCount(_initialValidators.length);
                        uintStorage[REQUIRED_SIGNATURES] = _requiredSignatures;
                        uintStorage[DEPLOYED_AT_BLOCK] = block.number;
                        setInitialize();
                        emit RequiredSignaturesChanged(_requiredSignatures);
                
                        return isInitialized();
                    }
                
                    function addValidator(address _validator) external onlyOwner {
                        _addValidator(_validator);
                        emit ValidatorAdded(_validator);
                    }
                
                    function removeValidator(address _validator) external onlyOwner {
                        _removeValidator(_validator);
                        emit ValidatorRemoved(_validator);
                    }
                
                    function upgradeToV230() public {
                        bytes32 upgradeStorage = 0x2e8a0420c66b3c19df2ac1cd4ffadb5637da547c6fd49388c7fe28dc8c13a8dd; // keccak256(abi.encodePacked('isUpgradedToV230'))
                        require(!boolStorage[upgradeStorage]);
                        address validator1 = 0x9ad83402C19Af24F76afa1930a2B2EEC2F47A8C5;
                        address validator2 = 0x4D1c96B9A49C4469A0b720a22b74b034EDdFe051;
                        address validator3 = 0x491FC792e78CDadd7D31446Bb7bDDef876a69AD6;
                        address validator4 = 0xc073C8E5ED9Aa11CF6776C69b3e13b259Ba9F506;
                
                        setNextValidator(F_ADDR, validator1);
                        setNextValidator(validator1, validator2);
                        setNextValidator(validator2, validator3);
                        setNextValidator(validator3, validator4);
                        setNextValidator(validator4, F_ADDR);
                        boolStorage[upgradeStorage] = true;
                    }
                }