ETH Price: $2,616.59 (-3.69%)

Transaction Decoder

Block:
14776121 at May-14-2022 09:53:41 PM +UTC
Transaction Fee:
0.005447011529199636 ETH $14.25
Gas Used:
133,617 Gas / 40.765857108 Gwei

Emitted Events:

37 XRUNE.Sent( operator=[Receiver] Bridge, from=[Receiver] Bridge, to=[Sender] 0xe0e3d9e170ca8375b96bdda0b87e56958b046999, amount=163231018916000000000000, data=0x, operatorData=0x )
38 XRUNE.Transfer( from=[Receiver] Bridge, to=[Sender] 0xe0e3d9e170ca8375b96bdda0b87e56958b046999, value=163231018916000000000000 )
39 Bridge.Received( recipient=[Sender] 0xe0e3d9e170ca8375b96bdda0b87e56958b046999, token=XRUNE, amount=163231018916000000000000, lockId=1873656951796753942752800333796070008, source=System.Byte[] )

Account State Difference:

  Address   Before After State Difference Code
(viabtc)
2.147073934515870689 Eth2.147407977015870689 Eth0.0003340425
0x69fa0feE...D3D2Ce71c
0x93746538...0E15bd74b
0xe0e3d9e1...58B046999
0.10003437030053317 Eth
Nonce: 4
0.094587358771333534 Eth
Nonce: 5
0.005447011529199636

Execution Trace

Bridge.unlock( lockId=1873656951796753942752800333796070008, recipient=0xe0e3d9e170CA8375b96BddA0b87e56958B046999, amount=163231018916000, lockSource=System.Byte[], tokenSource=System.Byte[], tokenSourceAddress=69FA0FEE221AD11012BAB0FDB45D444D3D2CE71C000000000000000000000000, signature=0xB9BAA038EEA66D8C80E0D79988D4CABA8C397D562D48D706E20F517B5424D47316F401219747D90E1973311131C0D64EFBFFA67CD913854023E404AE7EC5A1651B )
  • 0x93746538d4519c809827205bd1c2c7a0e15bd74b.7281efa6( )
    • Null: 0x000...001.5496ae2c( )
    • XRUNE.transfer( recipient=0xe0e3d9e170CA8375b96BddA0b87e56958B046999, amount=163231018916000000000000 ) => ( True )
      • ERC1820Registry.getInterfaceImplementer( _addr=0xBBbD1BbB4f9b936C3604906D7592A644071dE884, _interfaceHash=29DDB589B1FB5FC7CF394961C1ADF5F8C6454761ADF795E67FE149F658ABE895 ) => ( 0x0000000000000000000000000000000000000000 )
      • ERC1820Registry.getInterfaceImplementer( _addr=0xe0e3d9e170CA8375b96BddA0b87e56958B046999, _interfaceHash=B281FC8C12954D22544DB45DE3159A39272895B169A852B314F9CC762E44C53B ) => ( 0x0000000000000000000000000000000000000000 )
        File 1 of 3: Bridge
        /**
         *Submitted for verification at BscScan.com on 2022-10-20
        */
        
        // SPDX-License-Identifier: MIT
        
        pragma solidity ^0.8.0;
        pragma experimental ABIEncoderV2;
        
        /**
         * @dev Interface of the ERC20 standard as defined in the EIP.
         */
        interface IERC20 {
            /**
             * @dev Returns the amount of tokens in existence.
             */
            function totalSupply() external view returns (uint256);
        
            /**
             * @dev Returns the amount of tokens owned by `account`.
             */
            function balanceOf(address account) external view returns (uint256);
        
            /**
             * @dev Moves `amount` tokens from the caller's account to `recipient`.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * Emits a {Transfer} event.
             */
            function transfer(address recipient, uint256 amount) external returns (bool);
        
            /**
             * @dev Returns the remaining number of tokens that `spender` will be
             * allowed to spend on behalf of `owner` through {transferFrom}. This is
             * zero by default.
             *
             * This value changes when {approve} or {transferFrom} are called.
             */
            function allowance(address owner, address spender) external view returns (uint256);
        
            /**
             * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * IMPORTANT: Beware that changing an allowance with this method brings the risk
             * that someone may use both the old and the new allowance by unfortunate
             * transaction ordering. One possible solution to mitigate this race
             * condition is to first reduce the spender's allowance to 0 and set the
             * desired value afterwards:
             * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
             *
             * Emits an {Approval} event.
             */
            function approve(address spender, uint256 amount) external returns (bool);
        
            /**
             * @dev Moves `amount` tokens from `sender` to `recipient` using the
             * allowance mechanism. `amount` is then deducted from the caller's
             * allowance.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * Emits a {Transfer} event.
             */
            function transferFrom(
                address sender,
                address recipient,
                uint256 amount
            ) external returns (bool);
        
            /**
             * @dev Emitted when `value` tokens are moved from one account (`from`) to
             * another (`to`).
             *
             * Note that `value` may be zero.
             */
            event Transfer(address indexed from, address indexed to, uint256 value);
        
            /**
             * @dev Emitted when the allowance of a `spender` for an `owner` is set by
             * a call to {approve}. `value` is the new allowance.
             */
            event Approval(address indexed owner, address indexed spender, uint256 value);
        }
        
        /**
         * @dev Collection of functions related to the address type
         */
        library Address {
            /**
             * @dev Returns true if `account` is a contract.
             *
             * [IMPORTANT]
             * ====
             * It is unsafe to assume that an address for which this function returns
             * false is an externally-owned account (EOA) and not a contract.
             *
             * Among others, `isContract` will return false for the following
             * types of addresses:
             *
             *  - an externally-owned account
             *  - a contract in construction
             *  - an address where a contract will be created
             *  - an address where a contract lived, but was destroyed
             * ====
             */
            function isContract(address account) internal view returns (bool) {
                // This method relies on extcodesize, which returns 0 for contracts in
                // construction, since the code is only stored at the end of the
                // constructor execution.
        
                uint256 size;
                assembly {
                    size := extcodesize(account)
                }
                return size > 0;
            }
        
            /**
             * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
             * `recipient`, forwarding all available gas and reverting on errors.
             *
             * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
             * of certain opcodes, possibly making contracts go over the 2300 gas limit
             * imposed by `transfer`, making them unable to receive funds via
             * `transfer`. {sendValue} removes this limitation.
             *
             * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
             *
             * IMPORTANT: because control is transferred to `recipient`, care must be
             * taken to not create reentrancy vulnerabilities. Consider using
             * {ReentrancyGuard} or the
             * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
             */
            function sendValue(address payable recipient, uint256 amount) internal {
                require(address(this).balance >= amount, "Address: insufficient balance");
        
                (bool success, ) = recipient.call{value: amount}("");
                require(success, "Address: unable to send value, recipient may have reverted");
            }
        
            /**
             * @dev Performs a Solidity function call using a low level `call`. A
             * plain `call` is an unsafe replacement for a function call: use this
             * function instead.
             *
             * If `target` reverts with a revert reason, it is bubbled up by this
             * function (like regular Solidity function calls).
             *
             * Returns the raw returned data. To convert to the expected return value,
             * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
             *
             * Requirements:
             *
             * - `target` must be a contract.
             * - calling `target` with `data` must not revert.
             *
             * _Available since v3.1._
             */
            function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                return functionCall(target, data, "Address: low-level call failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
             * `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCall(
                address target,
                bytes memory data,
                string memory errorMessage
            ) internal returns (bytes memory) {
                return functionCallWithValue(target, data, 0, errorMessage);
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but also transferring `value` wei to `target`.
             *
             * Requirements:
             *
             * - the calling contract must have an ETH balance of at least `value`.
             * - the called Solidity function must be `payable`.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(
                address target,
                bytes memory data,
                uint256 value
            ) internal returns (bytes memory) {
                return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
             * with `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(
                address target,
                bytes memory data,
                uint256 value,
                string memory errorMessage
            ) internal returns (bytes memory) {
                require(address(this).balance >= value, "Address: insufficient balance for call");
                require(isContract(target), "Address: call to non-contract");
        
                (bool success, bytes memory returndata) = target.call{value: value}(data);
                return verifyCallResult(success, returndata, errorMessage);
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a static call.
             *
             * _Available since v3.3._
             */
            function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                return functionStaticCall(target, data, "Address: low-level static call failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
             * but performing a static call.
             *
             * _Available since v3.3._
             */
            function functionStaticCall(
                address target,
                bytes memory data,
                string memory errorMessage
            ) internal view returns (bytes memory) {
                require(isContract(target), "Address: static call to non-contract");
        
                (bool success, bytes memory returndata) = target.staticcall(data);
                return verifyCallResult(success, returndata, errorMessage);
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a delegate call.
             *
             * _Available since v3.4._
             */
            function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                return functionDelegateCall(target, data, "Address: low-level delegate call failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
             * but performing a delegate call.
             *
             * _Available since v3.4._
             */
            function functionDelegateCall(
                address target,
                bytes memory data,
                string memory errorMessage
            ) internal returns (bytes memory) {
                require(isContract(target), "Address: delegate call to non-contract");
        
                (bool success, bytes memory returndata) = target.delegatecall(data);
                return verifyCallResult(success, returndata, errorMessage);
            }
        
            /**
             * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
             * revert reason using the provided one.
             *
             * _Available since v4.3._
             */
            function verifyCallResult(
                bool success,
                bytes memory returndata,
                string memory errorMessage
            ) internal pure returns (bytes memory) {
                if (success) {
                    return returndata;
                } else {
                    // Look for revert reason and bubble it up if present
                    if (returndata.length > 0) {
                        // The easiest way to bubble the revert reason is using memory via assembly
        
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert(errorMessage);
                    }
                }
            }
        }
        
        /**
         * @title SafeERC20
         * @dev Wrappers around ERC20 operations that throw on failure (when the token
         * contract returns false). Tokens that return no value (and instead revert or
         * throw on failure) are also supported, non-reverting calls are assumed to be
         * successful.
         * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
         * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
         */
        library SafeERC20 {
            using Address for address;
        
            function safeTransfer(
                IERC20 token,
                address to,
                uint256 value
            ) internal {
                _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
            }
        
            function safeTransferFrom(
                IERC20 token,
                address from,
                address to,
                uint256 value
            ) internal {
                _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
            }
        
            /**
             * @dev Deprecated. This function has issues similar to the ones found in
             * {IERC20-approve}, and its usage is discouraged.
             *
             * Whenever possible, use {safeIncreaseAllowance} and
             * {safeDecreaseAllowance} instead.
             */
            function safeApprove(
                IERC20 token,
                address spender,
                uint256 value
            ) internal {
                // safeApprove should only be called when setting an initial allowance,
                // or when resetting it to zero. To increase and decrease it, use
                // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                require(
                    (value == 0) || (token.allowance(address(this), spender) == 0),
                    "SafeERC20: approve from non-zero to non-zero allowance"
                );
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
            }
        
            function safeIncreaseAllowance(
                IERC20 token,
                address spender,
                uint256 value
            ) internal {
                uint256 newAllowance = token.allowance(address(this), spender) + value;
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
            }
        
            function safeDecreaseAllowance(
                IERC20 token,
                address spender,
                uint256 value
            ) internal {
                unchecked {
                    uint256 oldAllowance = token.allowance(address(this), spender);
                    require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                    uint256 newAllowance = oldAllowance - value;
                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                }
            }
        
            /**
             * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
             * on the return value: the return value is optional (but if data is returned, it must not be false).
             * @param token The token targeted by the call.
             * @param data The call data (encoded using abi.encode or one of its variants).
             */
            function _callOptionalReturn(IERC20 token, bytes memory data) private {
                // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                // the target address contains contract code and also asserts for success in the low-level call.
        
                bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                if (returndata.length > 0) {
                    // Return data is optional
                    require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                }
            }
        }
        
        /**
         * @dev Interface for the optional metadata functions from the ERC20 standard.
         *
         * _Available since v4.1._
         */
        interface IERC20Metadata is IERC20 {
            /**
             * @dev Returns the name of the token.
             */
            function name() external view returns (string memory);
        
            /**
             * @dev Returns the symbol of the token.
             */
            function symbol() external view returns (string memory);
        
            /**
             * @dev Returns the decimals places of the token.
             */
            function decimals() external view returns (uint8);
        }
        
        /**
         * @dev Provides information about the current execution context, including the
         * sender of the transaction and its data. While these are generally available
         * via msg.sender and msg.data, they should not be accessed in such a direct
         * manner, since when dealing with meta-transactions the account sending and
         * paying for execution may not be the actual sender (as far as an application
         * is concerned).
         *
         * This contract is only required for intermediate, library-like contracts.
         */
        abstract contract Context {
            function _msgSender() internal view virtual returns (address) {
                return msg.sender;
            }
        
            function _msgData() internal view virtual returns (bytes calldata) {
                return msg.data;
            }
        }
        
        /**
         * @dev Implementation of the {IERC20} interface.
         *
         * This implementation is agnostic to the way tokens are created. This means
         * that a supply mechanism has to be added in a derived contract using {_mint}.
         * For a generic mechanism see {ERC20PresetMinterPauser}.
         *
         * TIP: For a detailed writeup see our guide
         * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
         * to implement supply mechanisms].
         *
         * We have followed general OpenZeppelin Contracts guidelines: functions revert
         * instead returning `false` on failure. This behavior is nonetheless
         * conventional and does not conflict with the expectations of ERC20
         * applications.
         *
         * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
         * This allows applications to reconstruct the allowance for all accounts just
         * by listening to said events. Other implementations of the EIP may not emit
         * these events, as it isn't required by the specification.
         *
         * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
         * functions have been added to mitigate the well-known issues around setting
         * allowances. See {IERC20-approve}.
         */
        contract ERC20 is Context, IERC20, IERC20Metadata {
            mapping(address => uint256) private _balances;
        
            mapping(address => mapping(address => uint256)) private _allowances;
        
            uint256 private _totalSupply;
        
            string private _name;
            string private _symbol;
        
            /**
             * @dev Sets the values for {name} and {symbol}.
             *
             * The default value of {decimals} is 18. To select a different value for
             * {decimals} you should overload it.
             *
             * All two of these values are immutable: they can only be set once during
             * construction.
             */
            constructor(string memory name_, string memory symbol_) {
                _name = name_;
                _symbol = symbol_;
            }
        
            /**
             * @dev Returns the name of the token.
             */
            function name() public view virtual override returns (string memory) {
                return _name;
            }
        
            /**
             * @dev Returns the symbol of the token, usually a shorter version of the
             * name.
             */
            function symbol() public view virtual override returns (string memory) {
                return _symbol;
            }
        
            /**
             * @dev Returns the number of decimals used to get its user representation.
             * For example, if `decimals` equals `2`, a balance of `505` tokens should
             * be displayed to a user as `5.05` (`505 / 10 ** 2`).
             *
             * Tokens usually opt for a value of 18, imitating the relationship between
             * Ether and Wei. This is the value {ERC20} uses, unless this function is
             * overridden;
             *
             * NOTE: This information is only used for _display_ purposes: it in
             * no way affects any of the arithmetic of the contract, including
             * {IERC20-balanceOf} and {IERC20-transfer}.
             */
            function decimals() public view virtual override returns (uint8) {
                return 18;
            }
        
            /**
             * @dev See {IERC20-totalSupply}.
             */
            function totalSupply() public view virtual override returns (uint256) {
                return _totalSupply;
            }
        
            /**
             * @dev See {IERC20-balanceOf}.
             */
            function balanceOf(address account) public view virtual override returns (uint256) {
                return _balances[account];
            }
        
            /**
             * @dev See {IERC20-transfer}.
             *
             * Requirements:
             *
             * - `recipient` cannot be the zero address.
             * - the caller must have a balance of at least `amount`.
             */
            function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                _transfer(_msgSender(), recipient, amount);
                return true;
            }
        
            /**
             * @dev See {IERC20-allowance}.
             */
            function allowance(address owner, address spender) public view virtual override returns (uint256) {
                return _allowances[owner][spender];
            }
        
            /**
             * @dev See {IERC20-approve}.
             *
             * Requirements:
             *
             * - `spender` cannot be the zero address.
             */
            function approve(address spender, uint256 amount) public virtual override returns (bool) {
                _approve(_msgSender(), spender, amount);
                return true;
            }
        
            /**
             * @dev See {IERC20-transferFrom}.
             *
             * Emits an {Approval} event indicating the updated allowance. This is not
             * required by the EIP. See the note at the beginning of {ERC20}.
             *
             * Requirements:
             *
             * - `sender` and `recipient` cannot be the zero address.
             * - `sender` must have a balance of at least `amount`.
             * - the caller must have allowance for ``sender``'s tokens of at least
             * `amount`.
             */
            function transferFrom(
                address sender,
                address recipient,
                uint256 amount
            ) public virtual override returns (bool) {
                _transfer(sender, recipient, amount);
        
                uint256 currentAllowance = _allowances[sender][_msgSender()];
                require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
                unchecked {
                    _approve(sender, _msgSender(), currentAllowance - amount);
                }
        
                return true;
            }
        
            /**
             * @dev Atomically increases the allowance granted to `spender` by the caller.
             *
             * This is an alternative to {approve} that can be used as a mitigation for
             * problems described in {IERC20-approve}.
             *
             * Emits an {Approval} event indicating the updated allowance.
             *
             * Requirements:
             *
             * - `spender` cannot be the zero address.
             */
            function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
                return true;
            }
        
            /**
             * @dev Atomically decreases the allowance granted to `spender` by the caller.
             *
             * This is an alternative to {approve} that can be used as a mitigation for
             * problems described in {IERC20-approve}.
             *
             * Emits an {Approval} event indicating the updated allowance.
             *
             * Requirements:
             *
             * - `spender` cannot be the zero address.
             * - `spender` must have allowance for the caller of at least
             * `subtractedValue`.
             */
            function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                uint256 currentAllowance = _allowances[_msgSender()][spender];
                require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
                unchecked {
                    _approve(_msgSender(), spender, currentAllowance - subtractedValue);
                }
        
                return true;
            }
        
            /**
             * @dev Moves `amount` of tokens from `sender` to `recipient`.
             *
             * This internal function is equivalent to {transfer}, and can be used to
             * e.g. implement automatic token fees, slashing mechanisms, etc.
             *
             * Emits a {Transfer} event.
             *
             * Requirements:
             *
             * - `sender` cannot be the zero address.
             * - `recipient` cannot be the zero address.
             * - `sender` must have a balance of at least `amount`.
             */
            function _transfer(
                address sender,
                address recipient,
                uint256 amount
            ) internal virtual {
                require(sender != address(0), "ERC20: transfer from the zero address");
                require(recipient != address(0), "ERC20: transfer to the zero address");
        
                _beforeTokenTransfer(sender, recipient, amount);
        
                uint256 senderBalance = _balances[sender];
                require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
                unchecked {
                    _balances[sender] = senderBalance - amount;
                }
                _balances[recipient] += amount;
        
                emit Transfer(sender, recipient, amount);
        
                _afterTokenTransfer(sender, recipient, amount);
            }
        
            /** @dev Creates `amount` tokens and assigns them to `account`, increasing
             * the total supply.
             *
             * Emits a {Transfer} event with `from` set to the zero address.
             *
             * Requirements:
             *
             * - `account` cannot be the zero address.
             */
            function _mint(address account, uint256 amount) internal virtual {
                require(account != address(0), "ERC20: mint to the zero address");
        
                _beforeTokenTransfer(address(0), account, amount);
        
                _totalSupply += amount;
                _balances[account] += amount;
                emit Transfer(address(0), account, amount);
        
                _afterTokenTransfer(address(0), account, amount);
            }
        
            /**
             * @dev Destroys `amount` tokens from `account`, reducing the
             * total supply.
             *
             * Emits a {Transfer} event with `to` set to the zero address.
             *
             * Requirements:
             *
             * - `account` cannot be the zero address.
             * - `account` must have at least `amount` tokens.
             */
            function _burn(address account, uint256 amount) internal virtual {
                require(account != address(0), "ERC20: burn from the zero address");
        
                _beforeTokenTransfer(account, address(0), amount);
        
                uint256 accountBalance = _balances[account];
                require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
                unchecked {
                    _balances[account] = accountBalance - amount;
                }
                _totalSupply -= amount;
        
                emit Transfer(account, address(0), amount);
        
                _afterTokenTransfer(account, address(0), amount);
            }
        
            /**
             * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
             *
             * This internal function is equivalent to `approve`, and can be used to
             * e.g. set automatic allowances for certain subsystems, etc.
             *
             * Emits an {Approval} event.
             *
             * Requirements:
             *
             * - `owner` cannot be the zero address.
             * - `spender` cannot be the zero address.
             */
            function _approve(
                address owner,
                address spender,
                uint256 amount
            ) internal virtual {
                require(owner != address(0), "ERC20: approve from the zero address");
                require(spender != address(0), "ERC20: approve to the zero address");
        
                _allowances[owner][spender] = amount;
                emit Approval(owner, spender, amount);
            }
        
            /**
             * @dev Hook that is called before any transfer of tokens. This includes
             * minting and burning.
             *
             * Calling conditions:
             *
             * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
             * will be transferred to `to`.
             * - when `from` is zero, `amount` tokens will be minted for `to`.
             * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
             * - `from` and `to` are never both zero.
             *
             * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
             */
            function _beforeTokenTransfer(
                address from,
                address to,
                uint256 amount
            ) internal virtual {}
        
            /**
             * @dev Hook that is called after any transfer of tokens. This includes
             * minting and burning.
             *
             * Calling conditions:
             *
             * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
             * has been transferred to `to`.
             * - when `from` is zero, `amount` tokens have been minted for `to`.
             * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
             * - `from` and `to` are never both zero.
             *
             * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
             */
            function _afterTokenTransfer(
                address from,
                address to,
                uint256 amount
            ) internal virtual {}
        }
        
        /**
         * @dev Contract module which provides a basic access control mechanism, where
         * there is an account (an owner) that can be granted exclusive access to
         * specific functions.
         *
         * By default, the owner account will be the one that deploys the contract. This
         * can later be changed with {transferOwnership}.
         *
         * This module is used through inheritance. It will make available the modifier
         * `onlyOwner`, which can be applied to your functions to restrict their use to
         * the owner.
         */
        abstract contract Ownable is Context {
            address private _owner;
        
            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        
            /**
             * @dev Initializes the contract setting the deployer as the initial owner.
             */
            constructor() {
                _setOwner(_msgSender());
            }
        
            /**
             * @dev Returns the address of the current owner.
             */
            function owner() public view virtual returns (address) {
                return _owner;
            }
        
            /**
             * @dev Throws if called by any account other than the owner.
             */
            modifier onlyOwner() {
                require(owner() == _msgSender(), "Ownable: caller is not the owner");
                _;
            }
        
            /**
             * @dev Leaves the contract without owner. It will not be possible to call
             * `onlyOwner` functions anymore. Can only be called by the current owner.
             *
             * NOTE: Renouncing ownership will leave the contract without an owner,
             * thereby removing any functionality that is only available to the owner.
             */
            function renounceOwnership() public virtual onlyOwner {
                _setOwner(address(0));
            }
        
            /**
             * @dev Transfers ownership of the contract to a new account (`newOwner`).
             * Can only be called by the current owner.
             */
            function transferOwnership(address newOwner) public virtual onlyOwner {
                require(newOwner != address(0), "Ownable: new owner is the zero address");
                _setOwner(newOwner);
            }
        
            function _setOwner(address newOwner) private {
                address oldOwner = _owner;
                _owner = newOwner;
                emit OwnershipTransferred(oldOwner, newOwner);
            }
        }
        
        /**
         * @dev External interface of AccessControl declared to support ERC165 detection.
         */
        interface IAccessControl {
            /**
             * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
             *
             * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
             * {RoleAdminChanged} not being emitted signaling this.
             *
             * _Available since v3.1._
             */
            event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
        
            /**
             * @dev Emitted when `account` is granted `role`.
             *
             * `sender` is the account that originated the contract call, an admin role
             * bearer except when using {AccessControl-_setupRole}.
             */
            event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
        
            /**
             * @dev Emitted when `account` is revoked `role`.
             *
             * `sender` is the account that originated the contract call:
             *   - if using `revokeRole`, it is the admin role bearer
             *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
             */
            event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
        
            /**
             * @dev Returns `true` if `account` has been granted `role`.
             */
            function hasRole(bytes32 role, address account) external view returns (bool);
        
            /**
             * @dev Returns the admin role that controls `role`. See {grantRole} and
             * {revokeRole}.
             *
             * To change a role's admin, use {AccessControl-_setRoleAdmin}.
             */
            function getRoleAdmin(bytes32 role) external view returns (bytes32);
        
            /**
             * @dev Grants `role` to `account`.
             *
             * If `account` had not been already granted `role`, emits a {RoleGranted}
             * event.
             *
             * Requirements:
             *
             * - the caller must have ``role``'s admin role.
             */
            function grantRole(bytes32 role, address account) external;
        
            /**
             * @dev Revokes `role` from `account`.
             *
             * If `account` had been granted `role`, emits a {RoleRevoked} event.
             *
             * Requirements:
             *
             * - the caller must have ``role``'s admin role.
             */
            function revokeRole(bytes32 role, address account) external;
        
            /**
             * @dev Revokes `role` from the calling account.
             *
             * Roles are often managed via {grantRole} and {revokeRole}: this function's
             * purpose is to provide a mechanism for accounts to lose their privileges
             * if they are compromised (such as when a trusted device is misplaced).
             *
             * If the calling account had been granted `role`, emits a {RoleRevoked}
             * event.
             *
             * Requirements:
             *
             * - the caller must be `account`.
             */
            function renounceRole(bytes32 role, address account) external;
        }
        
        /**
         * @dev String operations.
         */
        library Strings {
            bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
        
            /**
             * @dev Converts a `uint256` to its ASCII `string` decimal representation.
             */
            function toString(uint256 value) internal pure returns (string memory) {
                // Inspired by OraclizeAPI's implementation - MIT licence
                // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
        
                if (value == 0) {
                    return "0";
                }
                uint256 temp = value;
                uint256 digits;
                while (temp != 0) {
                    digits++;
                    temp /= 10;
                }
                bytes memory buffer = new bytes(digits);
                while (value != 0) {
                    digits -= 1;
                    buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                    value /= 10;
                }
                return string(buffer);
            }
        
            /**
             * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
             */
            function toHexString(uint256 value) internal pure returns (string memory) {
                if (value == 0) {
                    return "0x00";
                }
                uint256 temp = value;
                uint256 length = 0;
                while (temp != 0) {
                    length++;
                    temp >>= 8;
                }
                return toHexString(value, length);
            }
        
            /**
             * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
             */
            function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                bytes memory buffer = new bytes(2 * length + 2);
                buffer[0] = "0";
                buffer[1] = "x";
                for (uint256 i = 2 * length + 1; i > 1; --i) {
                    buffer[i] = _HEX_SYMBOLS[value & 0xf];
                    value >>= 4;
                }
                require(value == 0, "Strings: hex length insufficient");
                return string(buffer);
            }
        }
        
        /**
         * @dev Interface of the ERC165 standard, as defined in the
         * https://eips.ethereum.org/EIPS/eip-165[EIP].
         *
         * Implementers can declare support of contract interfaces, which can then be
         * queried by others ({ERC165Checker}).
         *
         * For an implementation, see {ERC165}.
         */
        interface IERC165 {
            /**
             * @dev Returns true if this contract implements the interface defined by
             * `interfaceId`. See the corresponding
             * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
             * to learn more about how these ids are created.
             *
             * This function call must use less than 30 000 gas.
             */
            function supportsInterface(bytes4 interfaceId) external view returns (bool);
        }
        
        /**
         * @dev Implementation of the {IERC165} interface.
         *
         * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
         * for the additional interface id that will be supported. For example:
         *
         * ```solidity
         * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
         *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
         * }
         * ```
         *
         * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
         */
        abstract contract ERC165 is IERC165 {
            /**
             * @dev See {IERC165-supportsInterface}.
             */
            function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                return interfaceId == type(IERC165).interfaceId;
            }
        }
        /**
         * @dev Contract module that allows children to implement role-based access
         * control mechanisms. This is a lightweight version that doesn't allow enumerating role
         * members except through off-chain means by accessing the contract event logs. Some
         * applications may benefit from on-chain enumerability, for those cases see
         * {AccessControlEnumerable}.
         *
         * Roles are referred to by their `bytes32` identifier. These should be exposed
         * in the external API and be unique. The best way to achieve this is by
         * using `public constant` hash digests:
         *
         * ```
         * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
         * ```
         *
         * Roles can be used to represent a set of permissions. To restrict access to a
         * function call, use {hasRole}:
         *
         * ```
         * function foo() public {
         *     require(hasRole(MY_ROLE, msg.sender));
         *     ...
         * }
         * ```
         *
         * Roles can be granted and revoked dynamically via the {grantRole} and
         * {revokeRole} functions. Each role has an associated admin role, and only
         * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
         *
         * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
         * that only accounts with this role will be able to grant or revoke other
         * roles. More complex role relationships can be created by using
         * {_setRoleAdmin}.
         *
         * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
         * grant and revoke this role. Extra precautions should be taken to secure
         * accounts that have been granted it.
         */
        abstract contract AccessControl is Context, IAccessControl, ERC165 {
            struct RoleData {
                mapping(address => bool) members;
                bytes32 adminRole;
            }
        
            mapping(bytes32 => RoleData) private _roles;
        
            bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
        
            /**
             * @dev Modifier that checks that an account has a specific role. Reverts
             * with a standardized message including the required role.
             *
             * The format of the revert reason is given by the following regular expression:
             *
             *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
             *
             * _Available since v4.1._
             */
            modifier onlyRole(bytes32 role) {
                _checkRole(role, _msgSender());
                _;
            }
        
            /**
             * @dev See {IERC165-supportsInterface}.
             */
            function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
            }
        
            /**
             * @dev Returns `true` if `account` has been granted `role`.
             */
            function hasRole(bytes32 role, address account) public view override returns (bool) {
                return _roles[role].members[account];
            }
        
            /**
             * @dev Revert with a standard message if `account` is missing `role`.
             *
             * The format of the revert reason is given by the following regular expression:
             *
             *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
             */
            function _checkRole(bytes32 role, address account) internal view {
                if (!hasRole(role, account)) {
                    revert(
                        string(
                            abi.encodePacked(
                                "AccessControl: account ",
                                Strings.toHexString(uint160(account), 20),
                                " is missing role ",
                                Strings.toHexString(uint256(role), 32)
                            )
                        )
                    );
                }
            }
        
            /**
             * @dev Returns the admin role that controls `role`. See {grantRole} and
             * {revokeRole}.
             *
             * To change a role's admin, use {_setRoleAdmin}.
             */
            function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
                return _roles[role].adminRole;
            }
        
            /**
             * @dev Grants `role` to `account`.
             *
             * If `account` had not been already granted `role`, emits a {RoleGranted}
             * event.
             *
             * Requirements:
             *
             * - the caller must have ``role``'s admin role.
             */
            function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                _grantRole(role, account);
            }
        
            /**
             * @dev Revokes `role` from `account`.
             *
             * If `account` had been granted `role`, emits a {RoleRevoked} event.
             *
             * Requirements:
             *
             * - the caller must have ``role``'s admin role.
             */
            function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                _revokeRole(role, account);
            }
        
            /**
             * @dev Revokes `role` from the calling account.
             *
             * Roles are often managed via {grantRole} and {revokeRole}: this function's
             * purpose is to provide a mechanism for accounts to lose their privileges
             * if they are compromised (such as when a trusted device is misplaced).
             *
             * If the calling account had been granted `role`, emits a {RoleRevoked}
             * event.
             *
             * Requirements:
             *
             * - the caller must be `account`.
             */
            function renounceRole(bytes32 role, address account) public virtual override {
                require(account == _msgSender(), "AccessControl: can only renounce roles for self");
        
                _revokeRole(role, account);
            }
        
            /**
             * @dev Grants `role` to `account`.
             *
             * If `account` had not been already granted `role`, emits a {RoleGranted}
             * event. Note that unlike {grantRole}, this function doesn't perform any
             * checks on the calling account.
             *
             * [WARNING]
             * ====
             * This function should only be called from the constructor when setting
             * up the initial roles for the system.
             *
             * Using this function in any other way is effectively circumventing the admin
             * system imposed by {AccessControl}.
             * ====
             */
            function _setupRole(bytes32 role, address account) internal virtual {
                _grantRole(role, account);
            }
        
            /**
             * @dev Sets `adminRole` as ``role``'s admin role.
             *
             * Emits a {RoleAdminChanged} event.
             */
            function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                bytes32 previousAdminRole = getRoleAdmin(role);
                _roles[role].adminRole = adminRole;
                emit RoleAdminChanged(role, previousAdminRole, adminRole);
            }
        
            function _grantRole(bytes32 role, address account) private {
                if (!hasRole(role, account)) {
                    _roles[role].members[account] = true;
                    emit RoleGranted(role, account, _msgSender());
                }
            }
        
            function _revokeRole(bytes32 role, address account) private {
                if (hasRole(role, account)) {
                    _roles[role].members[account] = false;
                    emit RoleRevoked(role, account, _msgSender());
                }
            }
        }
        
        contract WrappedToken is Context, Ownable, ERC20 {
            uint8 private _decimals;
            bytes4 public source;
            bytes32 public sourceAddress;
        
            constructor(
                bytes4 source_,
                bytes32 sourceAddress_,
                uint8 decimals_,
                string memory name,
                string memory symbol
            ) ERC20(name, symbol) {
                source = source_;
                sourceAddress = sourceAddress_;
                _decimals = decimals_;
            }
        
            function decimals() public view virtual override returns (uint8) {
                return _decimals;
            }
        
            function mint(address to, uint256 amount) public virtual onlyOwner {
                _mint(to, amount);
            }
        
            function burn(address from, uint256 amount) public virtual onlyOwner {
                _burn(from, amount);
            }
        }
        
        /**
         * @dev Standard math utilities missing in the Solidity language.
         */
        library Math {
            /**
             * @dev Returns the largest of two numbers.
             */
            function max(uint256 a, uint256 b) internal pure returns (uint256) {
                return a >= b ? a : b;
            }
        
            /**
             * @dev Returns the smallest of two numbers.
             */
            function min(uint256 a, uint256 b) internal pure returns (uint256) {
                return a < b ? a : b;
            }
        
            /**
             * @dev Returns the average of two numbers. The result is rounded towards
             * zero.
             */
            function average(uint256 a, uint256 b) internal pure returns (uint256) {
                // (a + b) / 2 can overflow.
                return (a & b) + (a ^ b) / 2;
            }
        
            /**
             * @dev Returns the ceiling of the division of two numbers.
             *
             * This differs from standard division with `/` in that it rounds up instead
             * of rounding down.
             */
            function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                // (a + b - 1) / b can overflow on addition, so we distribute.
                return a / b + (a % b == 0 ? 0 : 1);
            }
        }
        // This contract handles swapping to and from xABR, Allbridge's staking token.
        contract Staking is ERC20("xABR", "xABR"){
            IERC20 public ABR;
        
            // Define the ABR token contract
            constructor(IERC20 _ABR) {
                ABR = _ABR;
            }
        
            // Locks ABR and mints xABR
            function deposit(uint256 _amount) public {
                // Gets the amount of ABR locked in the contract
                uint256 totalABR = ABR.balanceOf(address(this));
                // Gets the amount of xABR in existence
                uint256 totalShares = totalSupply();
                // If no xABR exists, mint it 1:1 to the amount put in
                if (totalShares == 0 || totalABR == 0) {
                    _mint(msg.sender, _amount);
                }
                // Calculate and mint the amount of xABR the ABR is worth. The ratio will change overtime, 
                // as xABR is burned/minted and ABR deposited + gained from fees / withdrawn.
                else {
                    uint256 what = _amount * totalShares / totalABR;
                    _mint(msg.sender, what);
                }
                // Lock the ABR in the contract
                ABR.transferFrom(msg.sender, address(this), _amount);
            }
        
            // Unlocks the staked + gained ABR and burns xABR
            function withdraw(uint256 _share) public {
                // Gets the amount of xABR in existence
                uint256 totalShares = totalSupply();
                // Calculates the amount of ABR the xABR is worth
                uint256 what = _share * ABR.balanceOf(address(this)) / totalShares;
                _burn(msg.sender, _share);
                ABR.transfer(msg.sender, what);
            }
        }
        
        contract FeeOracle is Ownable {
            using Math for uint256;
        
            // tokenAddress => mintFee
            mapping(address => uint256) public minFee;
        
            // poolId => multiplier
            uint256 public feeMultiplier;
            uint256 public baseFeeRateBP;
        
            uint256 public constant BP = 10000;
        
            IERC20 public xABR;
        
            constructor(IERC20 xABR_, uint256 baseFeeRateBP_, uint256 feeMultiplier_) {
                xABR = xABR_;
                baseFeeRateBP = baseFeeRateBP_;
                feeMultiplier = feeMultiplier_;
            }
        
            function setFeeMultiplier(uint256 multiplier) public onlyOwner {
                feeMultiplier = multiplier;
            }
        
            function setMinFee(address token, uint256 _minFee) public onlyOwner {
                minFee[token] = _minFee;
            }
        
            function setBaseFeeRate(uint256 baseFeeRateBP_) public onlyOwner {
                baseFeeRateBP = baseFeeRateBP_;
            }
        
            // Fourth argument is destination
            function fee(address token, address sender, uint256 amount, bytes4) public view returns (uint256) {
                uint256 _minFee = minFee[token];
                if (xABR.totalSupply() == 0 || baseFeeRateBP == 0 || amount == 0) {
                    return _minFee;
                }
                uint256 userShareBP = xABR.balanceOf(sender) * feeMultiplier * BP / xABR.totalSupply();
        
                uint256 result = (amount * BP) / (userShareBP + (BP * BP / baseFeeRateBP));
                if (_minFee > 0 && result < _minFee) {
                    return _minFee;
                } else {
                    return result;
                }
            }
        
        }
        
        interface IValidator {
            function createLock(
                uint128 lockId,
                address sender,
                bytes32 recipient,
                uint256 amount,
                bytes4 destination,
                bytes4 tokenSource,
                bytes32 tokenSourceAddress
            ) external;
        
            function createUnlock(
                uint128 lockId,
                address recipient,
                uint256 amount,
                bytes4 lockSource,
                bytes4 tokenSource,
                bytes32 tokenSourceAddress,
                bytes calldata signature
            ) external;
        }
        
        interface IWrappedTokenV0 {
            function changeAuthority(address newAuthority) external;
            function mint(address account, uint256 amount) external;
            function burn(address account, uint256 amount) external;
        }
        
        contract Bridge is AccessControl {
            using SafeERC20 for IERC20;
        
            bytes32 public constant TOKEN_MANAGER = keccak256("TOKEN_MANAGER");
            bytes32 public constant BRIDGE_MANAGER = keccak256("BRIDGE_MANAGER");
            bytes32 public constant STOP_MANAGER = keccak256("STOP_MANAGER");
        
            bool active;
        
            enum TokenType {
                Base,
                Native,
                WrappedV0,
                Wrapped
            }
        
            enum TokenStatus {
                Disabled,
                Enabled
            }
        
            uint256 private constant SYSTEM_PRECISION = 9;
        
            event Sent(
                bytes4 tokenSource,
                bytes32 tokenSourceAddress,
                address sender,
                bytes32 indexed recipient,
                uint256 amount,
                uint128 indexed lockId,
                bytes4 destination
            );
            event Received(address indexed recipient, address token, uint256 amount, uint128 indexed lockId, bytes4 source);
        
            // Validator contract address
            address public validator;
        
            // Address to collect fee
            address public feeCollector;
        
            // Fee manager address
            address public feeOracle;
        
            // Fee manager address
            address public unlockSigner;
        
            // Structure for token info
            struct TokenInfo {
                bytes4 tokenSource;
                bytes32 tokenSourceAddress;
                uint8 precision;
                TokenType tokenType;
                TokenStatus tokenStatus;
            }
        
            // Map to get token info by its address
            mapping(address => TokenInfo) public tokenInfos;
        
            // Structure for getting tokenAddress by tokenSource and tokenSourceAddress
            // tokenSource => tokenSourceAddress => nativeAddress
            mapping(bytes4 => mapping(bytes32 => address)) public tokenSourceMap;
        
            modifier isActive() {
                require(active, "Bridge: is not active");
                _;
            }
        
            constructor(
                address feeCollector_,
                address admin_,
                address validator_,
                address feeOracle_,
                address unlockSigner_
            ) {
                feeCollector = feeCollector_;
                validator = validator_;
                feeOracle = feeOracle_;
                _setupRole(DEFAULT_ADMIN_ROLE, admin_);
                unlockSigner = unlockSigner_;
                active = false;
            }
        
            // Method to lock tokens
            function lock(
                uint128 lockId,
                address tokenAddress,
                bytes32 recipient,
                bytes4 destination,
                uint256 amount
            ) external isActive {
                (uint256 amountToLock, uint256 fee, TokenInfo memory tokenInfo) = _createLock(
                    lockId,
                    tokenAddress,
                    amount,
                    recipient,
                    destination
                );
        
                require(tokenInfo.tokenStatus == TokenStatus.Enabled, "Bridge: disabled token");
        
                if (tokenInfo.tokenType == TokenType.Native) {
                    // If token is native - transfer tokens from user to contract
                    IERC20(tokenAddress).safeTransferFrom(
                        msg.sender,
                        address(this),
                        amountToLock
                    );
                } else if (tokenInfo.tokenType == TokenType.Wrapped) {
                    // If wrapped then butn the token
                    WrappedToken(tokenAddress).burn(msg.sender, amountToLock);
                } else if (tokenInfo.tokenType == TokenType.WrappedV0) {
                    // Legacy wrapped tokens burn
                    IWrappedTokenV0(tokenAddress).burn(msg.sender, amountToLock);
                } else {
                    revert("Bridge: invalid token type");
                }
        
                if (fee > 0) {
                    // If there is fee - transfer it to fee collector address
                    IERC20(tokenAddress).safeTransferFrom(
                        msg.sender,
                        feeCollector,
                        fee
                    );
                }
            }
        
            function lockBase(
                uint128 lockId, 
                address wrappedBaseTokenAddress, 
                bytes32 recipient, 
                bytes4 destination) external payable isActive {
                (, uint256 fee, TokenInfo memory tokenInfo) = _createLock(
                    lockId,
                    wrappedBaseTokenAddress,
                    msg.value,
                    recipient,
                    destination
                );
        
                require(tokenInfo.tokenStatus == TokenStatus.Enabled, "Bridge: disabled token");
                require(tokenInfo.tokenType == TokenType.Base, "Bridge: invalid token type");
        
                if (fee > 0) {
                    // If there is fee - transfer ETH to fee collector address
                    payable(feeCollector).transfer(fee);
                }
            }
        
            // Method unlock funds. Amount has to be in system precision
            function unlock(
                uint128 lockId,
                address recipient, uint256 amount,
                bytes4 lockSource, bytes4 tokenSource,
                bytes32 tokenSourceAddress,
                bytes calldata signature) external isActive {
                // Create message hash and validate the signature
                IValidator(validator).createUnlock(
                        lockId,
                        recipient,
                        amount,
                        lockSource,
                        tokenSource,
                        tokenSourceAddress,
                        signature);
        
                // Mark lock as received
                address tokenAddress = tokenSourceMap[tokenSource][tokenSourceAddress];
                require(tokenAddress != address(0), "Bridge: unsupported token");
                TokenInfo memory tokenInfo = tokenInfos[tokenAddress];
        
                // Transform amount form system to token precision
                uint256 amountWithTokenPrecision = fromSystemPrecision(amount, tokenInfo.precision);
                uint256 fee = 0;
                if (msg.sender == unlockSigner) {
                    fee = FeeOracle(feeOracle).minFee(tokenAddress);
                    require(amountWithTokenPrecision > fee, "Bridge: amount too small");
                    amountWithTokenPrecision = amountWithTokenPrecision - fee;
                }
        
                if (tokenInfo.tokenType == TokenType.Base) {
                    // If token is WETH - transfer ETH
                    payable(recipient).transfer(amountWithTokenPrecision);
                    if (fee > 0) {
                        payable(feeCollector).transfer(fee);
                    }
                } else if (tokenInfo.tokenType == TokenType.Native) {
                    // If token is native - transfer the token
                    IERC20(tokenAddress).safeTransfer(recipient, amountWithTokenPrecision);
                    if (fee > 0) {
                        IERC20(tokenAddress).safeTransfer(feeCollector, fee);
                    }
                } else if (tokenInfo.tokenType == TokenType.Wrapped) {
                    // Else token is wrapped - mint tokens to the user
                    WrappedToken(tokenAddress).mint(recipient, amountWithTokenPrecision);
                    if (fee > 0) {
                        WrappedToken(tokenAddress).mint(feeCollector, fee);
                    }
                } else if (tokenInfo.tokenType == TokenType.WrappedV0) {
                    // Legacy wrapped token
                    IWrappedTokenV0(tokenAddress).mint(recipient, amountWithTokenPrecision);
                    if (fee > 0) {
                        IWrappedTokenV0(tokenAddress).mint(feeCollector, fee);
                    }
                }
        
                emit Received(recipient, tokenAddress, amountWithTokenPrecision, lockId, lockSource);
            }
        
            // Method to add token that already exist in the current blockchain
            // Fee has to be in system precision
            // If token is wrapped, but it was deployed manually, isManualWrapped must be true
            function addToken(
                bytes4 tokenSource, 
                bytes32 tokenSourceAddress, 
                address nativeTokenAddress, 
                TokenType tokenType) external onlyRole(TOKEN_MANAGER) {
                require(
                    tokenInfos[nativeTokenAddress].tokenSourceAddress == bytes32(0) &&
                    tokenSourceMap[tokenSource][tokenSourceAddress] == address(0), "Bridge: exists");
                uint8 precision = ERC20(nativeTokenAddress).decimals();
        
                tokenSourceMap[tokenSource][tokenSourceAddress] = nativeTokenAddress;
                tokenInfos[nativeTokenAddress] = TokenInfo(
                    tokenSource, 
                    tokenSourceAddress, 
                    precision, 
                    tokenType, 
                    TokenStatus.Enabled);
            }
        
            // Method to remove token from lists
            function removeToken(
                bytes4 tokenSource, 
                bytes32 tokenSourceAddress, 
                address newAuthority) external onlyRole(TOKEN_MANAGER) {
                require(newAuthority != address(0), "Bridge: zero address authority");
                address tokenAddress = tokenSourceMap[tokenSource][tokenSourceAddress];
                require(tokenAddress != address(0), "Bridge: token not found");
                TokenInfo memory tokenInfo = tokenInfos[tokenAddress];
        
                if (tokenInfo.tokenType == TokenType.Base && address(this).balance > 0) {
                    payable(newAuthority).transfer(address(this).balance);
                }
        
                uint256 tokenBalance = IERC20(tokenAddress).balanceOf(address(this));
                if (tokenBalance > 0) {
                    IERC20(tokenAddress).safeTransfer(newAuthority, tokenBalance);
                }
        
                if (tokenInfo.tokenType == TokenType.Wrapped) {
                    WrappedToken(tokenAddress).transferOwnership(newAuthority);
                } else if (tokenInfo.tokenType == TokenType.WrappedV0) {
                    IWrappedTokenV0(tokenAddress).changeAuthority(newAuthority);
                }
        
                delete tokenInfos[tokenAddress];
                delete tokenSourceMap[tokenSource][tokenSourceAddress];
            }
        
            function setFeeOracle(address _feeOracle) external onlyRole(TOKEN_MANAGER) {
                feeOracle = _feeOracle;
            }
        
            function setFeeCollector(address _feeCollector) external onlyRole(TOKEN_MANAGER) {
                feeCollector = _feeCollector;
            }
        
            function setValidator(address _validator ) external onlyRole(BRIDGE_MANAGER) {
                validator = _validator;
            }
        
            function setUnlockSigner(address _unlockSigner ) external onlyRole(BRIDGE_MANAGER) {
                unlockSigner = _unlockSigner;
            }
        
            function setTokenStatus(address tokenAddress, TokenStatus status)  external onlyRole(TOKEN_MANAGER) {
                require(tokenInfos[tokenAddress].tokenSourceAddress != bytes32(0), "Bridge: unsupported token");
                tokenInfos[tokenAddress].tokenStatus = status;
            }
        
            function startBridge() external onlyRole(BRIDGE_MANAGER) {
                active = true;
            }
        
            function stopBridge() external onlyRole(STOP_MANAGER) {
                active = false;
            }
        
            // Private method to validate lock, create lock record, and emmit the event
            // Method returns amount to lock and token info structure
            function _createLock(
                uint128 lockId,
                address tokenAddress,
                uint256 amount,
                bytes32 recipient,
                bytes4 destination
            ) private returns (uint256, uint256, TokenInfo memory) {
                require(amount > 0, "Bridge: amount is 0");
                TokenInfo memory tokenInfo = tokenInfos[tokenAddress];
                require(
                    tokenInfo.tokenSourceAddress != bytes32(0),
                    "Bridge: unsupported token"
                );
        
                uint256 fee = FeeOracle(feeOracle).fee(tokenAddress, msg.sender, amount, destination);
        
                require(amount > fee, "Bridge: amount too small");
        
                // Amount to lock is amount without fee
                uint256 amountToLock = amount - fee;
        
                // Create and add lock structure to the locks list
                IValidator(validator).createLock(
                    lockId,
                    msg.sender,
                    recipient,
                    toSystemPrecision(amountToLock, tokenInfo.precision),
                    destination,
                    tokenInfo.tokenSource,
                    tokenInfo.tokenSourceAddress
                );
        
                emit Sent(
                    tokenInfo.tokenSource,
                    tokenInfo.tokenSourceAddress,
                    msg.sender,
                    recipient,
                    amountToLock,
                    lockId,
                    destination
                );
                return (amountToLock, fee, tokenInfo);
            }
        
            // Convert amount from token precision to system precision
            function toSystemPrecision(uint256 amount, uint8 precision)
                private
                pure
                returns (uint256)
            {
                if (precision > SYSTEM_PRECISION) {
                    return amount / (10**(precision - SYSTEM_PRECISION));
                } else if (precision < SYSTEM_PRECISION) {
                    return amount * (10**(SYSTEM_PRECISION - precision));
                } else {
                    return amount;
                }
            }
        
            // Convert amount from system precision to token precision
            function fromSystemPrecision(uint256 amount, uint8 precision)
                private
                pure
                returns (uint256)
            {
                if (precision > SYSTEM_PRECISION) {
                    return amount * (10**(precision - SYSTEM_PRECISION));
                } else if (precision < SYSTEM_PRECISION) {
                    return amount / (10**(SYSTEM_PRECISION - precision));
                } else {
                    return amount;
                }
            }
        }

        File 2 of 3: XRUNE
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.4;
        import "./ERC677.sol";
        import "./ERC777Permit.sol";
        import "./utils/Ownable.sol";
        import "@openzeppelin/contracts/token/ERC777/ERC777.sol";
        contract XRUNE is ERC777, ERC777Permit, ERC677, Ownable {
            uint public constant ERA_SECONDS = 86400;
            uint public constant MAX_SUPPLY = 1000000000 ether;
            uint public nextEra = 1622433600; // 2021-05-31
            uint public curve = 1024;
            bool public emitting = false;
            address public reserve = address(0);
            event NewEra(uint256 time, uint256 emission);
            constructor(address owner) public ERC777("XRUNE Token", "XRUNE", new address[](0)) ERC777Permit("XRUNE") Ownable(owner) {
                _mint(owner, MAX_SUPPLY / 2, "", "");
            }
            function setCurve(uint _curve) public onlyOwner {
                require(_curve > 0 && _curve < 10000, "curve needs to be between 0 and 10000");
                curve = _curve;
            }
            function toggleEmitting() public onlyOwner {
                emitting = !emitting;
            }
            function setReserve(address _reserve) public onlyOwner {
                reserve = _reserve;
            }
            function setNextEra(uint next) public onlyOwner {
                // solhint-disable-next-line not-rely-on-time
                require(next > nextEra && next > block.timestamp, "next era needs to be in the future");
                nextEra = next;
            }
            function _beforeTokenTransfer(address operator, address from, address to, uint amount) internal virtual override {
                super._beforeTokenTransfer(operator, from, to, amount);
                require(to != address(this), "!self");
                dailyEmit();
            }
            function dailyEmit() public {
                // solhint-disable-next-line not-rely-on-time
                if ((block.timestamp >= nextEra) && emitting && reserve != address(0)) {
                    uint _emission = dailyEmission();
                    emit NewEra(nextEra, _emission);
                    nextEra = nextEra + ERA_SECONDS;
                    _mint(reserve, _emission, "", "");
                }
            }
            function dailyEmission() public view returns (uint) {
                return (MAX_SUPPLY - totalSupply()) / curve;
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        import "./interfaces/IERC677.sol";
        import "./interfaces/IERC677Receiver.sol";
        import "@openzeppelin/contracts/token/ERC777/ERC777.sol";
        abstract contract ERC677 is ERC777, IERC677 {
          /**
          * @dev transfer token to a contract address with additional data if the recipient is a contact.
          * @param _to The address to transfer to.
          * @param _value The amount to be transferred.
          * @param _data The extra data to be passed to the receiving contract.
          */
          function transferAndCall(address _to, uint _value, bytes calldata _data)
            public
            override
            returns (bool success)
          {
            super.transfer(_to, _value);
            emit TransferWithData(msg.sender, _to, _value, _data);
            if (isContract(_to)) {
              contractFallback(_to, _value, _data);
            }
            return true;
          }
          function contractFallback(address _to, uint _value, bytes calldata _data)
            private
          {
            IERC677Receiver receiver = IERC677Receiver(_to);
            receiver.onTokenTransfer(msg.sender, _value, _data);
          }
          function isContract(address _addr)
            private
            view
            returns (bool hasCode)
          {
            uint length;
            assembly { length := extcodesize(_addr) }
            return length > 0;
          }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.4;
        // From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/draft-ERC20Permit.sol
        import "./interfaces/IERC20Permit.sol";
        import "@openzeppelin/contracts/token/ERC777/ERC777.sol";
        import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
        import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
        import "@openzeppelin/contracts/utils/Counters.sol";
        /**
         * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
         * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
         *
         * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
         * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
         * need to send a transaction, and thus is not required to hold Ether at all.
         *
         * _Available since v3.4._
         */
        abstract contract ERC777Permit is ERC777, IERC20Permit, EIP712 {
            using Counters for Counters.Counter;
            mapping (address => Counters.Counter) private _nonces;
            // solhint-disable-next-line var-name-mixedcase
            bytes32 private immutable _PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
            /**
             * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
             *
             * It's a good idea to use the same `name` that is defined as the ERC20 token name.
             */
            constructor(string memory name) EIP712(name, "1") {
            }
            /**
             * @dev See {IERC20Permit-permit}.
             */
            function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual override {
                // solhint-disable-next-line not-rely-on-time
                require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
                bytes32 structHash = keccak256(
                    abi.encode(
                        _PERMIT_TYPEHASH,
                        owner,
                        spender,
                        value,
                        _useNonce(owner),
                        deadline
                    )
                );
                bytes32 hash = _hashTypedDataV4(structHash);
                address signer = ECDSA.recover(hash, v, r, s);
                require(signer == owner, "ERC20Permit: invalid signature");
                _approve(owner, spender, value);
            }
            /**
             * @dev See {IERC20Permit-nonces}.
             */
            function nonces(address owner) public view virtual override returns (uint256) {
                return _nonces[owner].current();
            }
            /**
             * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
             */
            // solhint-disable-next-line func-name-mixedcase
            function DOMAIN_SEPARATOR() external view override returns (bytes32) {
                return _domainSeparatorV4();
            }
            /**
             * @dev "Consume a nonce": return the current value and increment.
             *
             * _Available since v4.1._
             */
            function _useNonce(address owner) internal virtual returns (uint256 current) {
                Counters.Counter storage nonce = _nonces[owner];
                current = nonce.current();
                nonce.increment();
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        import "@openzeppelin/contracts/utils/Context.sol";
        /**
         * @dev Contract module which provides a basic access control mechanism, where
         * there is an account (an owner) that can be granted exclusive access to
         * specific functions.
         *
         * By default, the owner account will be the one that deploys the contract. This
         * can later be changed with {transferOwnership}.
         *
         * This module is used through inheritance. It will make available the modifier
         * `onlyOwner`, which can be applied to your functions to restrict their use to
         * the owner.
         */
        abstract contract Ownable is Context {
            address private _owner;
            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
            /**
             * @dev Initializes the contract setting the deployer as the initial owner.
             */
            constructor (address owner) {
                _owner = owner;
                emit OwnershipTransferred(address(0), owner);
            }
            /**
             * @dev Returns the address of the current owner.
             */
            function owner() public view virtual returns (address) {
                return _owner;
            }
            /**
             * @dev Throws if called by any account other than the owner.
             */
            modifier onlyOwner() {
                require(owner() == _msgSender(), "Ownable: caller is not the owner");
                _;
            }
            /**
             * @dev Leaves the contract without owner. It will not be possible to call
             * `onlyOwner` functions anymore. Can only be called by the current owner.
             *
             * NOTE: Renouncing ownership will leave the contract without an owner,
             * thereby removing any functionality that is only available to the owner.
             */
            function renounceOwnership() public virtual onlyOwner {
                emit OwnershipTransferred(_owner, address(0));
                _owner = address(0);
            }
            /**
             * @dev Transfers ownership of the contract to a new account (`newOwner`).
             * Can only be called by the current owner.
             */
            function transferOwnership(address newOwner) public virtual onlyOwner {
                require(newOwner != address(0), "Ownable: new owner is the zero address");
                emit OwnershipTransferred(_owner, newOwner);
                _owner = newOwner;
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        import "./IERC777.sol";
        import "./IERC777Recipient.sol";
        import "./IERC777Sender.sol";
        import "../ERC20/IERC20.sol";
        import "../../utils/Address.sol";
        import "../../utils/Context.sol";
        import "../../utils/introspection/IERC1820Registry.sol";
        /**
         * @dev Implementation of the {IERC777} interface.
         *
         * This implementation is agnostic to the way tokens are created. This means
         * that a supply mechanism has to be added in a derived contract using {_mint}.
         *
         * Support for ERC20 is included in this contract, as specified by the EIP: both
         * the ERC777 and ERC20 interfaces can be safely used when interacting with it.
         * Both {IERC777-Sent} and {IERC20-Transfer} events are emitted on token
         * movements.
         *
         * Additionally, the {IERC777-granularity} value is hard-coded to `1`, meaning that there
         * are no special restrictions in the amount of tokens that created, moved, or
         * destroyed. This makes integration with ERC20 applications seamless.
         */
        contract ERC777 is Context, IERC777, IERC20 {
            using Address for address;
            IERC1820Registry constant internal _ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
            mapping(address => uint256) private _balances;
            uint256 private _totalSupply;
            string private _name;
            string private _symbol;
            bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH = keccak256("ERC777TokensSender");
            bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient");
            // This isn't ever read from - it's only used to respond to the defaultOperators query.
            address[] private _defaultOperatorsArray;
            // Immutable, but accounts may revoke them (tracked in __revokedDefaultOperators).
            mapping(address => bool) private _defaultOperators;
            // For each account, a mapping of its operators and revoked default operators.
            mapping(address => mapping(address => bool)) private _operators;
            mapping(address => mapping(address => bool)) private _revokedDefaultOperators;
            // ERC20-allowances
            mapping (address => mapping (address => uint256)) private _allowances;
            /**
             * @dev `defaultOperators` may be an empty array.
             */
            constructor(
                string memory name_,
                string memory symbol_,
                address[] memory defaultOperators_
            ) {
                _name = name_;
                _symbol = symbol_;
                _defaultOperatorsArray = defaultOperators_;
                for (uint256 i = 0; i < defaultOperators_.length; i++) {
                    _defaultOperators[defaultOperators_[i]] = true;
                }
                // register interfaces
                _ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC777Token"), address(this));
                _ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC20Token"), address(this));
            }
            /**
             * @dev See {IERC777-name}.
             */
            function name() public view virtual override returns (string memory) {
                return _name;
            }
            /**
             * @dev See {IERC777-symbol}.
             */
            function symbol() public view virtual override returns (string memory) {
                return _symbol;
            }
            /**
             * @dev See {ERC20-decimals}.
             *
             * Always returns 18, as per the
             * [ERC777 EIP](https://eips.ethereum.org/EIPS/eip-777#backward-compatibility).
             */
            function decimals() public pure virtual returns (uint8) {
                return 18;
            }
            /**
             * @dev See {IERC777-granularity}.
             *
             * This implementation always returns `1`.
             */
            function granularity() public view virtual override returns (uint256) {
                return 1;
            }
            /**
             * @dev See {IERC777-totalSupply}.
             */
            function totalSupply() public view virtual override(IERC20, IERC777) returns (uint256) {
                return _totalSupply;
            }
            /**
             * @dev Returns the amount of tokens owned by an account (`tokenHolder`).
             */
            function balanceOf(address tokenHolder) public view virtual override(IERC20, IERC777) returns (uint256) {
                return _balances[tokenHolder];
            }
            /**
             * @dev See {IERC777-send}.
             *
             * Also emits a {IERC20-Transfer} event for ERC20 compatibility.
             */
            function send(address recipient, uint256 amount, bytes memory data) public virtual override  {
                _send(_msgSender(), recipient, amount, data, "", true);
            }
            /**
             * @dev See {IERC20-transfer}.
             *
             * Unlike `send`, `recipient` is _not_ required to implement the {IERC777Recipient}
             * interface if it is a contract.
             *
             * Also emits a {Sent} event.
             */
            function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                require(recipient != address(0), "ERC777: transfer to the zero address");
                address from = _msgSender();
                _callTokensToSend(from, from, recipient, amount, "", "");
                _move(from, from, recipient, amount, "", "");
                _callTokensReceived(from, from, recipient, amount, "", "", false);
                return true;
            }
            /**
             * @dev See {IERC777-burn}.
             *
             * Also emits a {IERC20-Transfer} event for ERC20 compatibility.
             */
            function burn(uint256 amount, bytes memory data) public virtual override  {
                _burn(_msgSender(), amount, data, "");
            }
            /**
             * @dev See {IERC777-isOperatorFor}.
             */
            function isOperatorFor(address operator, address tokenHolder) public view virtual override returns (bool) {
                return operator == tokenHolder ||
                    (_defaultOperators[operator] && !_revokedDefaultOperators[tokenHolder][operator]) ||
                    _operators[tokenHolder][operator];
            }
            /**
             * @dev See {IERC777-authorizeOperator}.
             */
            function authorizeOperator(address operator) public virtual override  {
                require(_msgSender() != operator, "ERC777: authorizing self as operator");
                if (_defaultOperators[operator]) {
                    delete _revokedDefaultOperators[_msgSender()][operator];
                } else {
                    _operators[_msgSender()][operator] = true;
                }
                emit AuthorizedOperator(operator, _msgSender());
            }
            /**
             * @dev See {IERC777-revokeOperator}.
             */
            function revokeOperator(address operator) public virtual override  {
                require(operator != _msgSender(), "ERC777: revoking self as operator");
                if (_defaultOperators[operator]) {
                    _revokedDefaultOperators[_msgSender()][operator] = true;
                } else {
                    delete _operators[_msgSender()][operator];
                }
                emit RevokedOperator(operator, _msgSender());
            }
            /**
             * @dev See {IERC777-defaultOperators}.
             */
            function defaultOperators() public view virtual override returns (address[] memory) {
                return _defaultOperatorsArray;
            }
            /**
             * @dev See {IERC777-operatorSend}.
             *
             * Emits {Sent} and {IERC20-Transfer} events.
             */
            function operatorSend(
                address sender,
                address recipient,
                uint256 amount,
                bytes memory data,
                bytes memory operatorData
            )
                public
                virtual
                override
            {
                require(isOperatorFor(_msgSender(), sender), "ERC777: caller is not an operator for holder");
                _send(sender, recipient, amount, data, operatorData, true);
            }
            /**
             * @dev See {IERC777-operatorBurn}.
             *
             * Emits {Burned} and {IERC20-Transfer} events.
             */
            function operatorBurn(address account, uint256 amount, bytes memory data, bytes memory operatorData) public virtual override {
                require(isOperatorFor(_msgSender(), account), "ERC777: caller is not an operator for holder");
                _burn(account, amount, data, operatorData);
            }
            /**
             * @dev See {IERC20-allowance}.
             *
             * Note that operator and allowance concepts are orthogonal: operators may
             * not have allowance, and accounts with allowance may not be operators
             * themselves.
             */
            function allowance(address holder, address spender) public view virtual override returns (uint256) {
                return _allowances[holder][spender];
            }
            /**
             * @dev See {IERC20-approve}.
             *
             * Note that accounts cannot have allowance issued by their operators.
             */
            function approve(address spender, uint256 value) public virtual override returns (bool) {
                address holder = _msgSender();
                _approve(holder, spender, value);
                return true;
            }
           /**
            * @dev See {IERC20-transferFrom}.
            *
            * Note that operator and allowance concepts are orthogonal: operators cannot
            * call `transferFrom` (unless they have allowance), and accounts with
            * allowance cannot call `operatorSend` (unless they are operators).
            *
            * Emits {Sent}, {IERC20-Transfer} and {IERC20-Approval} events.
            */
            function transferFrom(address holder, address recipient, uint256 amount) public virtual override returns (bool) {
                require(recipient != address(0), "ERC777: transfer to the zero address");
                require(holder != address(0), "ERC777: transfer from the zero address");
                address spender = _msgSender();
                _callTokensToSend(spender, holder, recipient, amount, "", "");
                _move(spender, holder, recipient, amount, "", "");
                uint256 currentAllowance = _allowances[holder][spender];
                require(currentAllowance >= amount, "ERC777: transfer amount exceeds allowance");
                _approve(holder, spender, currentAllowance - amount);
                _callTokensReceived(spender, holder, recipient, amount, "", "", false);
                return true;
            }
            /**
             * @dev Creates `amount` tokens and assigns them to `account`, increasing
             * the total supply.
             *
             * If a send hook is registered for `account`, the corresponding function
             * will be called with `operator`, `data` and `operatorData`.
             *
             * See {IERC777Sender} and {IERC777Recipient}.
             *
             * Emits {Minted} and {IERC20-Transfer} events.
             *
             * Requirements
             *
             * - `account` cannot be the zero address.
             * - if `account` is a contract, it must implement the {IERC777Recipient}
             * interface.
             */
            function _mint(
                address account,
                uint256 amount,
                bytes memory userData,
                bytes memory operatorData
            )
                internal
                virtual
            {
                _mint(account, amount, userData, operatorData, true);
            }
            /**
             * @dev Creates `amount` tokens and assigns them to `account`, increasing
             * the total supply.
             *
             * If `requireReceptionAck` is set to true, and if a send hook is
             * registered for `account`, the corresponding function will be called with
             * `operator`, `data` and `operatorData`.
             *
             * See {IERC777Sender} and {IERC777Recipient}.
             *
             * Emits {Minted} and {IERC20-Transfer} events.
             *
             * Requirements
             *
             * - `account` cannot be the zero address.
             * - if `account` is a contract, it must implement the {IERC777Recipient}
             * interface.
             */
            function _mint(
                address account,
                uint256 amount,
                bytes memory userData,
                bytes memory operatorData,
                bool requireReceptionAck
            )
                internal
                virtual
            {
                require(account != address(0), "ERC777: mint to the zero address");
                address operator = _msgSender();
                _beforeTokenTransfer(operator, address(0), account, amount);
                // Update state variables
                _totalSupply += amount;
                _balances[account] += amount;
                _callTokensReceived(operator, address(0), account, amount, userData, operatorData, requireReceptionAck);
                emit Minted(operator, account, amount, userData, operatorData);
                emit Transfer(address(0), account, amount);
            }
            /**
             * @dev Send tokens
             * @param from address token holder address
             * @param to address recipient address
             * @param amount uint256 amount of tokens to transfer
             * @param userData bytes extra information provided by the token holder (if any)
             * @param operatorData bytes extra information provided by the operator (if any)
             * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient
             */
            function _send(
                address from,
                address to,
                uint256 amount,
                bytes memory userData,
                bytes memory operatorData,
                bool requireReceptionAck
            )
                internal
                virtual
            {
                require(from != address(0), "ERC777: send from the zero address");
                require(to != address(0), "ERC777: send to the zero address");
                address operator = _msgSender();
                _callTokensToSend(operator, from, to, amount, userData, operatorData);
                _move(operator, from, to, amount, userData, operatorData);
                _callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck);
            }
            /**
             * @dev Burn tokens
             * @param from address token holder address
             * @param amount uint256 amount of tokens to burn
             * @param data bytes extra information provided by the token holder
             * @param operatorData bytes extra information provided by the operator (if any)
             */
            function _burn(
                address from,
                uint256 amount,
                bytes memory data,
                bytes memory operatorData
            )
                internal
                virtual
            {
                require(from != address(0), "ERC777: burn from the zero address");
                address operator = _msgSender();
                _callTokensToSend(operator, from, address(0), amount, data, operatorData);
                _beforeTokenTransfer(operator, from, address(0), amount);
                // Update state variables
                uint256 fromBalance = _balances[from];
                require(fromBalance >= amount, "ERC777: burn amount exceeds balance");
                _balances[from] = fromBalance - amount;
                _totalSupply -= amount;
                emit Burned(operator, from, amount, data, operatorData);
                emit Transfer(from, address(0), amount);
            }
            function _move(
                address operator,
                address from,
                address to,
                uint256 amount,
                bytes memory userData,
                bytes memory operatorData
            )
                private
            {
                _beforeTokenTransfer(operator, from, to, amount);
                uint256 fromBalance = _balances[from];
                require(fromBalance >= amount, "ERC777: transfer amount exceeds balance");
                _balances[from] = fromBalance - amount;
                _balances[to] += amount;
                emit Sent(operator, from, to, amount, userData, operatorData);
                emit Transfer(from, to, amount);
            }
            /**
             * @dev See {ERC20-_approve}.
             *
             * Note that accounts cannot have allowance issued by their operators.
             */
            function _approve(address holder, address spender, uint256 value) internal {
                require(holder != address(0), "ERC777: approve from the zero address");
                require(spender != address(0), "ERC777: approve to the zero address");
                _allowances[holder][spender] = value;
                emit Approval(holder, spender, value);
            }
            /**
             * @dev Call from.tokensToSend() if the interface is registered
             * @param operator address operator requesting the transfer
             * @param from address token holder address
             * @param to address recipient address
             * @param amount uint256 amount of tokens to transfer
             * @param userData bytes extra information provided by the token holder (if any)
             * @param operatorData bytes extra information provided by the operator (if any)
             */
            function _callTokensToSend(
                address operator,
                address from,
                address to,
                uint256 amount,
                bytes memory userData,
                bytes memory operatorData
            )
                private
            {
                address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(from, _TOKENS_SENDER_INTERFACE_HASH);
                if (implementer != address(0)) {
                    IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData);
                }
            }
            /**
             * @dev Call to.tokensReceived() if the interface is registered. Reverts if the recipient is a contract but
             * tokensReceived() was not registered for the recipient
             * @param operator address operator requesting the transfer
             * @param from address token holder address
             * @param to address recipient address
             * @param amount uint256 amount of tokens to transfer
             * @param userData bytes extra information provided by the token holder (if any)
             * @param operatorData bytes extra information provided by the operator (if any)
             * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient
             */
            function _callTokensReceived(
                address operator,
                address from,
                address to,
                uint256 amount,
                bytes memory userData,
                bytes memory operatorData,
                bool requireReceptionAck
            )
                private
            {
                address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(to, _TOKENS_RECIPIENT_INTERFACE_HASH);
                if (implementer != address(0)) {
                    IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData);
                } else if (requireReceptionAck) {
                    require(!to.isContract(), "ERC777: token recipient contract has no implementer for ERC777TokensRecipient");
                }
            }
            /**
             * @dev Hook that is called before any token transfer. This includes
             * calls to {send}, {transfer}, {operatorSend}, minting and burning.
             *
             * Calling conditions:
             *
             * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
             * will be to transferred to `to`.
             * - when `from` is zero, `amount` tokens will be minted for `to`.
             * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
             * - `from` and `to` are never both zero.
             *
             * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
             */
            function _beforeTokenTransfer(address operator, address from, address to, uint256 amount) internal virtual { }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        interface IERC677 {
          function transferAndCall(address to, uint value, bytes calldata data) external returns (bool success);
          event TransferWithData(address indexed from, address indexed to, uint value, bytes data);
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        interface IERC677Receiver {
          function onTokenTransfer(address _sender, uint _value, bytes calldata _data) external;
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        /**
         * @dev Interface of the ERC777Token standard as defined in the EIP.
         *
         * This contract uses the
         * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let
         * token holders and recipients react to token movements by using setting implementers
         * for the associated interfaces in said registry. See {IERC1820Registry} and
         * {ERC1820Implementer}.
         */
        interface IERC777 {
            /**
             * @dev Returns the name of the token.
             */
            function name() external view returns (string memory);
            /**
             * @dev Returns the symbol of the token, usually a shorter version of the
             * name.
             */
            function symbol() external view returns (string memory);
            /**
             * @dev Returns the smallest part of the token that is not divisible. This
             * means all token operations (creation, movement and destruction) must have
             * amounts that are a multiple of this number.
             *
             * For most token contracts, this value will equal 1.
             */
            function granularity() external view returns (uint256);
            /**
             * @dev Returns the amount of tokens in existence.
             */
            function totalSupply() external view returns (uint256);
            /**
             * @dev Returns the amount of tokens owned by an account (`owner`).
             */
            function balanceOf(address owner) external view returns (uint256);
            /**
             * @dev Moves `amount` tokens from the caller's account to `recipient`.
             *
             * If send or receive hooks are registered for the caller and `recipient`,
             * the corresponding functions will be called with `data` and empty
             * `operatorData`. See {IERC777Sender} and {IERC777Recipient}.
             *
             * Emits a {Sent} event.
             *
             * Requirements
             *
             * - the caller must have at least `amount` tokens.
             * - `recipient` cannot be the zero address.
             * - if `recipient` is a contract, it must implement the {IERC777Recipient}
             * interface.
             */
            function send(address recipient, uint256 amount, bytes calldata data) external;
            /**
             * @dev Destroys `amount` tokens from the caller's account, reducing the
             * total supply.
             *
             * If a send hook is registered for the caller, the corresponding function
             * will be called with `data` and empty `operatorData`. See {IERC777Sender}.
             *
             * Emits a {Burned} event.
             *
             * Requirements
             *
             * - the caller must have at least `amount` tokens.
             */
            function burn(uint256 amount, bytes calldata data) external;
            /**
             * @dev Returns true if an account is an operator of `tokenHolder`.
             * Operators can send and burn tokens on behalf of their owners. All
             * accounts are their own operator.
             *
             * See {operatorSend} and {operatorBurn}.
             */
            function isOperatorFor(address operator, address tokenHolder) external view returns (bool);
            /**
             * @dev Make an account an operator of the caller.
             *
             * See {isOperatorFor}.
             *
             * Emits an {AuthorizedOperator} event.
             *
             * Requirements
             *
             * - `operator` cannot be calling address.
             */
            function authorizeOperator(address operator) external;
            /**
             * @dev Revoke an account's operator status for the caller.
             *
             * See {isOperatorFor} and {defaultOperators}.
             *
             * Emits a {RevokedOperator} event.
             *
             * Requirements
             *
             * - `operator` cannot be calling address.
             */
            function revokeOperator(address operator) external;
            /**
             * @dev Returns the list of default operators. These accounts are operators
             * for all token holders, even if {authorizeOperator} was never called on
             * them.
             *
             * This list is immutable, but individual holders may revoke these via
             * {revokeOperator}, in which case {isOperatorFor} will return false.
             */
            function defaultOperators() external view returns (address[] memory);
            /**
             * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must
             * be an operator of `sender`.
             *
             * If send or receive hooks are registered for `sender` and `recipient`,
             * the corresponding functions will be called with `data` and
             * `operatorData`. See {IERC777Sender} and {IERC777Recipient}.
             *
             * Emits a {Sent} event.
             *
             * Requirements
             *
             * - `sender` cannot be the zero address.
             * - `sender` must have at least `amount` tokens.
             * - the caller must be an operator for `sender`.
             * - `recipient` cannot be the zero address.
             * - if `recipient` is a contract, it must implement the {IERC777Recipient}
             * interface.
             */
            function operatorSend(
                address sender,
                address recipient,
                uint256 amount,
                bytes calldata data,
                bytes calldata operatorData
            ) external;
            /**
             * @dev Destroys `amount` tokens from `account`, reducing the total supply.
             * The caller must be an operator of `account`.
             *
             * If a send hook is registered for `account`, the corresponding function
             * will be called with `data` and `operatorData`. See {IERC777Sender}.
             *
             * Emits a {Burned} event.
             *
             * Requirements
             *
             * - `account` cannot be the zero address.
             * - `account` must have at least `amount` tokens.
             * - the caller must be an operator for `account`.
             */
            function operatorBurn(
                address account,
                uint256 amount,
                bytes calldata data,
                bytes calldata operatorData
            ) external;
            event Sent(
                address indexed operator,
                address indexed from,
                address indexed to,
                uint256 amount,
                bytes data,
                bytes operatorData
            );
            event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);
            event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);
            event AuthorizedOperator(address indexed operator, address indexed tokenHolder);
            event RevokedOperator(address indexed operator, address indexed tokenHolder);
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        /**
         * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP.
         *
         * Accounts can be notified of {IERC777} tokens being sent to them by having a
         * contract implement this interface (contract holders can be their own
         * implementer) and registering it on the
         * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry].
         *
         * See {IERC1820Registry} and {ERC1820Implementer}.
         */
        interface IERC777Recipient {
            /**
             * @dev Called by an {IERC777} token contract whenever tokens are being
             * moved or created into a registered account (`to`). The type of operation
             * is conveyed by `from` being the zero address or not.
             *
             * This call occurs _after_ the token contract's state is updated, so
             * {IERC777-balanceOf}, etc., can be used to query the post-operation state.
             *
             * This function may revert to prevent the operation from being executed.
             */
            function tokensReceived(
                address operator,
                address from,
                address to,
                uint256 amount,
                bytes calldata userData,
                bytes calldata operatorData
            ) external;
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        /**
         * @dev Interface of the ERC777TokensSender standard as defined in the EIP.
         *
         * {IERC777} Token holders can be notified of operations performed on their
         * tokens by having a contract implement this interface (contract holders can be
         *  their own implementer) and registering it on the
         * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry].
         *
         * See {IERC1820Registry} and {ERC1820Implementer}.
         */
        interface IERC777Sender {
            /**
             * @dev Called by an {IERC777} token contract whenever a registered holder's
             * (`from`) tokens are about to be moved or destroyed. The type of operation
             * is conveyed by `to` being the zero address or not.
             *
             * This call occurs _before_ the token contract's state is updated, so
             * {IERC777-balanceOf}, etc., can be used to query the pre-operation state.
             *
             * This function may revert to prevent the operation from being executed.
             */
            function tokensToSend(
                address operator,
                address from,
                address to,
                uint256 amount,
                bytes calldata userData,
                bytes calldata operatorData
            ) external;
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        /**
         * @dev Interface of the ERC20 standard as defined in the EIP.
         */
        interface IERC20 {
            /**
             * @dev Returns the amount of tokens in existence.
             */
            function totalSupply() external view returns (uint256);
            /**
             * @dev Returns the amount of tokens owned by `account`.
             */
            function balanceOf(address account) external view returns (uint256);
            /**
             * @dev Moves `amount` tokens from the caller's account to `recipient`.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * Emits a {Transfer} event.
             */
            function transfer(address recipient, uint256 amount) external returns (bool);
            /**
             * @dev Returns the remaining number of tokens that `spender` will be
             * allowed to spend on behalf of `owner` through {transferFrom}. This is
             * zero by default.
             *
             * This value changes when {approve} or {transferFrom} are called.
             */
            function allowance(address owner, address spender) external view returns (uint256);
            /**
             * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * IMPORTANT: Beware that changing an allowance with this method brings the risk
             * that someone may use both the old and the new allowance by unfortunate
             * transaction ordering. One possible solution to mitigate this race
             * condition is to first reduce the spender's allowance to 0 and set the
             * desired value afterwards:
             * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
             *
             * Emits an {Approval} event.
             */
            function approve(address spender, uint256 amount) external returns (bool);
            /**
             * @dev Moves `amount` tokens from `sender` to `recipient` using the
             * allowance mechanism. `amount` is then deducted from the caller's
             * allowance.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * Emits a {Transfer} event.
             */
            function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
            /**
             * @dev Emitted when `value` tokens are moved from one account (`from`) to
             * another (`to`).
             *
             * Note that `value` may be zero.
             */
            event Transfer(address indexed from, address indexed to, uint256 value);
            /**
             * @dev Emitted when the allowance of a `spender` for an `owner` is set by
             * a call to {approve}. `value` is the new allowance.
             */
            event Approval(address indexed owner, address indexed spender, uint256 value);
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        /**
         * @dev Collection of functions related to the address type
         */
        library Address {
            /**
             * @dev Returns true if `account` is a contract.
             *
             * [IMPORTANT]
             * ====
             * It is unsafe to assume that an address for which this function returns
             * false is an externally-owned account (EOA) and not a contract.
             *
             * Among others, `isContract` will return false for the following
             * types of addresses:
             *
             *  - an externally-owned account
             *  - a contract in construction
             *  - an address where a contract will be created
             *  - an address where a contract lived, but was destroyed
             * ====
             */
            function isContract(address account) internal view returns (bool) {
                // This method relies on extcodesize, which returns 0 for contracts in
                // construction, since the code is only stored at the end of the
                // constructor execution.
                uint256 size;
                // solhint-disable-next-line no-inline-assembly
                assembly { size := extcodesize(account) }
                return size > 0;
            }
            /**
             * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
             * `recipient`, forwarding all available gas and reverting on errors.
             *
             * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
             * of certain opcodes, possibly making contracts go over the 2300 gas limit
             * imposed by `transfer`, making them unable to receive funds via
             * `transfer`. {sendValue} removes this limitation.
             *
             * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
             *
             * IMPORTANT: because control is transferred to `recipient`, care must be
             * taken to not create reentrancy vulnerabilities. Consider using
             * {ReentrancyGuard} or the
             * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
             */
            function sendValue(address payable recipient, uint256 amount) internal {
                require(address(this).balance >= amount, "Address: insufficient balance");
                // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                (bool success, ) = recipient.call{ value: amount }("");
                require(success, "Address: unable to send value, recipient may have reverted");
            }
            /**
             * @dev Performs a Solidity function call using a low level `call`. A
             * plain`call` is an unsafe replacement for a function call: use this
             * function instead.
             *
             * If `target` reverts with a revert reason, it is bubbled up by this
             * function (like regular Solidity function calls).
             *
             * Returns the raw returned data. To convert to the expected return value,
             * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
             *
             * Requirements:
             *
             * - `target` must be a contract.
             * - calling `target` with `data` must not revert.
             *
             * _Available since v3.1._
             */
            function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCall(target, data, "Address: low-level call failed");
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
             * `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                return functionCallWithValue(target, data, 0, errorMessage);
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but also transferring `value` wei to `target`.
             *
             * Requirements:
             *
             * - the calling contract must have an ETH balance of at least `value`.
             * - the called Solidity function must be `payable`.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
            }
            /**
             * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
             * with `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                require(address(this).balance >= value, "Address: insufficient balance for call");
                require(isContract(target), "Address: call to non-contract");
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.call{ value: value }(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a static call.
             *
             * _Available since v3.3._
             */
            function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                return functionStaticCall(target, data, "Address: low-level static call failed");
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
             * but performing a static call.
             *
             * _Available since v3.3._
             */
            function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                require(isContract(target), "Address: static call to non-contract");
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.staticcall(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a delegate call.
             *
             * _Available since v3.4._
             */
            function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                return functionDelegateCall(target, data, "Address: low-level delegate call failed");
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
             * but performing a delegate call.
             *
             * _Available since v3.4._
             */
            function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                require(isContract(target), "Address: delegate call to non-contract");
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.delegatecall(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
            function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                if (success) {
                    return returndata;
                } else {
                    // Look for revert reason and bubble it up if present
                    if (returndata.length > 0) {
                        // The easiest way to bubble the revert reason is using memory via assembly
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert(errorMessage);
                    }
                }
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        /*
         * @dev Provides information about the current execution context, including the
         * sender of the transaction and its data. While these are generally available
         * via msg.sender and msg.data, they should not be accessed in such a direct
         * manner, since when dealing with meta-transactions the account sending and
         * paying for execution may not be the actual sender (as far as an application
         * is concerned).
         *
         * This contract is only required for intermediate, library-like contracts.
         */
        abstract contract Context {
            function _msgSender() internal view virtual returns (address) {
                return msg.sender;
            }
            function _msgData() internal view virtual returns (bytes calldata) {
                this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                return msg.data;
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        /**
         * @dev Interface of the global ERC1820 Registry, as defined in the
         * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register
         * implementers for interfaces in this registry, as well as query support.
         *
         * Implementers may be shared by multiple accounts, and can also implement more
         * than a single interface for each account. Contracts can implement interfaces
         * for themselves, but externally-owned accounts (EOA) must delegate this to a
         * contract.
         *
         * {IERC165} interfaces can also be queried via the registry.
         *
         * For an in-depth explanation and source code analysis, see the EIP text.
         */
        interface IERC1820Registry {
            /**
             * @dev Sets `newManager` as the manager for `account`. A manager of an
             * account is able to set interface implementers for it.
             *
             * By default, each account is its own manager. Passing a value of `0x0` in
             * `newManager` will reset the manager to this initial state.
             *
             * Emits a {ManagerChanged} event.
             *
             * Requirements:
             *
             * - the caller must be the current manager for `account`.
             */
            function setManager(address account, address newManager) external;
            /**
             * @dev Returns the manager for `account`.
             *
             * See {setManager}.
             */
            function getManager(address account) external view returns (address);
            /**
             * @dev Sets the `implementer` contract as ``account``'s implementer for
             * `interfaceHash`.
             *
             * `account` being the zero address is an alias for the caller's address.
             * The zero address can also be used in `implementer` to remove an old one.
             *
             * See {interfaceHash} to learn how these are created.
             *
             * Emits an {InterfaceImplementerSet} event.
             *
             * Requirements:
             *
             * - the caller must be the current manager for `account`.
             * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not
             * end in 28 zeroes).
             * - `implementer` must implement {IERC1820Implementer} and return true when
             * queried for support, unless `implementer` is the caller. See
             * {IERC1820Implementer-canImplementInterfaceForAddress}.
             */
            function setInterfaceImplementer(address account, bytes32 _interfaceHash, address implementer) external;
            /**
             * @dev Returns the implementer of `interfaceHash` for `account`. If no such
             * implementer is registered, returns the zero address.
             *
             * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28
             * zeroes), `account` will be queried for support of it.
             *
             * `account` being the zero address is an alias for the caller's address.
             */
            function getInterfaceImplementer(address account, bytes32 _interfaceHash) external view returns (address);
            /**
             * @dev Returns the interface hash for an `interfaceName`, as defined in the
             * corresponding
             * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP].
             */
            function interfaceHash(string calldata interfaceName) external pure returns (bytes32);
            /**
             * @notice Updates the cache with whether the contract implements an ERC165 interface or not.
             * @param account Address of the contract for which to update the cache.
             * @param interfaceId ERC165 interface for which to update the cache.
             */
            function updateERC165Cache(address account, bytes4 interfaceId) external;
            /**
             * @notice Checks whether a contract implements an ERC165 interface or not.
             * If the result is not cached a direct lookup on the contract address is performed.
             * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
             * {updateERC165Cache} with the contract address.
             * @param account Address of the contract to check.
             * @param interfaceId ERC165 interface to check.
             * @return True if `account` implements `interfaceId`, false otherwise.
             */
            function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool);
            /**
             * @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
             * @param account Address of the contract to check.
             * @param interfaceId ERC165 interface to check.
             * @return True if `account` implements `interfaceId`, false otherwise.
             */
            function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool);
            event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer);
            event ManagerChanged(address indexed account, address indexed newManager);
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.4;
        // From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/draft-IERC20Permit.sol
        /**
         * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
         * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
         *
         * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
         * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
         * need to send a transaction, and thus is not required to hold Ether at all.
         */
        interface IERC20Permit {
            /**
             * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
             * given ``owner``'s signed approval.
             *
             * IMPORTANT: The same issues {IERC20-approve} has related to transaction
             * ordering also apply here.
             *
             * Emits an {Approval} event.
             *
             * Requirements:
             *
             * - `spender` cannot be the zero address.
             * - `deadline` must be a timestamp in the future.
             * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
             * over the EIP712-formatted function arguments.
             * - the signature must use ``owner``'s current nonce (see {nonces}).
             *
             * For more information on the signature format, see the
             * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
             * section].
             */
            function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
            /**
             * @dev Returns the current nonce for `owner`. This value must be
             * included whenever a signature is generated for {permit}.
             *
             * Every successful call to {permit} increases ``owner``'s nonce by one. This
             * prevents a signature from being used multiple times.
             */
            function nonces(address owner) external view returns (uint256);
            /**
             * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
             */
            // solhint-disable-next-line func-name-mixedcase
            function DOMAIN_SEPARATOR() external view returns (bytes32);
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        import "./ECDSA.sol";
        /**
         * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
         *
         * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
         * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
         * they need in their contracts using a combination of `abi.encode` and `keccak256`.
         *
         * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
         * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
         * ({_hashTypedDataV4}).
         *
         * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
         * the chain id to protect against replay attacks on an eventual fork of the chain.
         *
         * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
         * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
         *
         * _Available since v3.4._
         */
        abstract contract EIP712 {
            /* solhint-disable var-name-mixedcase */
            // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
            // invalidate the cached domain separator if the chain id changes.
            bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
            uint256 private immutable _CACHED_CHAIN_ID;
            bytes32 private immutable _HASHED_NAME;
            bytes32 private immutable _HASHED_VERSION;
            bytes32 private immutable _TYPE_HASH;
            /* solhint-enable var-name-mixedcase */
            /**
             * @dev Initializes the domain separator and parameter caches.
             *
             * The meaning of `name` and `version` is specified in
             * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
             *
             * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
             * - `version`: the current major version of the signing domain.
             *
             * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
             * contract upgrade].
             */
            constructor(string memory name, string memory version) {
                bytes32 hashedName = keccak256(bytes(name));
                bytes32 hashedVersion = keccak256(bytes(version));
                bytes32 typeHash = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
                _HASHED_NAME = hashedName;
                _HASHED_VERSION = hashedVersion;
                _CACHED_CHAIN_ID = block.chainid;
                _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
                _TYPE_HASH = typeHash;
            }
            /**
             * @dev Returns the domain separator for the current chain.
             */
            function _domainSeparatorV4() internal view returns (bytes32) {
                if (block.chainid == _CACHED_CHAIN_ID) {
                    return _CACHED_DOMAIN_SEPARATOR;
                } else {
                    return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
                }
            }
            function _buildDomainSeparator(bytes32 typeHash, bytes32 name, bytes32 version) private view returns (bytes32) {
                return keccak256(
                    abi.encode(
                        typeHash,
                        name,
                        version,
                        block.chainid,
                        address(this)
                    )
                );
            }
            /**
             * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
             * function returns the hash of the fully encoded EIP712 message for this domain.
             *
             * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
             *
             * ```solidity
             * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
             *     keccak256("Mail(address to,string contents)"),
             *     mailTo,
             *     keccak256(bytes(mailContents))
             * )));
             * address signer = ECDSA.recover(digest, signature);
             * ```
             */
            function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
                return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        /**
         * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
         *
         * These functions can be used to verify that a message was signed by the holder
         * of the private keys of a given address.
         */
        library ECDSA {
            /**
             * @dev Returns the address that signed a hashed message (`hash`) with
             * `signature`. This address can then be used for verification purposes.
             *
             * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
             * this function rejects them by requiring the `s` value to be in the lower
             * half order, and the `v` value to be either 27 or 28.
             *
             * IMPORTANT: `hash` _must_ be the result of a hash operation for the
             * verification to be secure: it is possible to craft signatures that
             * recover to arbitrary addresses for non-hashed data. A safe way to ensure
             * this is by receiving a hash of the original message (which may otherwise
             * be too long), and then calling {toEthSignedMessageHash} on it.
             */
            function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
                // Divide the signature in r, s and v variables
                bytes32 r;
                bytes32 s;
                uint8 v;
                // Check the signature length
                // - case 65: r,s,v signature (standard)
                // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
                if (signature.length == 65) {
                    // ecrecover takes the signature parameters, and the only way to get them
                    // currently is to use assembly.
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        r := mload(add(signature, 0x20))
                        s := mload(add(signature, 0x40))
                        v := byte(0, mload(add(signature, 0x60)))
                    }
                } else if (signature.length == 64) {
                    // ecrecover takes the signature parameters, and the only way to get them
                    // currently is to use assembly.
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        let vs := mload(add(signature, 0x40))
                        r := mload(add(signature, 0x20))
                        s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
                        v := add(shr(255, vs), 27)
                    }
                } else {
                    revert("ECDSA: invalid signature length");
                }
                return recover(hash, v, r, s);
            }
            /**
             * @dev Overload of {ECDSA-recover} that receives the `v`,
             * `r` and `s` signature fields separately.
             */
            function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
                // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
                // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                //
                // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                // these malleable signatures as well.
                require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
                require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
                // If the signature is valid (and not malleable), return the signer address
                address signer = ecrecover(hash, v, r, s);
                require(signer != address(0), "ECDSA: invalid signature");
                return signer;
            }
            /**
             * @dev Returns an Ethereum Signed Message, created from a `hash`. This
             * produces hash corresponding to the one signed with the
             * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
             * JSON-RPC method as part of EIP-191.
             *
             * See {recover}.
             */
            function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
                // 32 is the length in bytes of hash,
                // enforced by the type signature above
                return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
        32", hash));
            }
            /**
             * @dev Returns an Ethereum Signed Typed Data, created from a
             * `domainSeparator` and a `structHash`. This produces hash corresponding
             * to the one signed with the
             * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
             * JSON-RPC method as part of EIP-712.
             *
             * See {recover}.
             */
            function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
                return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        /**
         * @title Counters
         * @author Matt Condon (@shrugs)
         * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
         * of elements in a mapping, issuing ERC721 ids, or counting request ids.
         *
         * Include with `using Counters for Counters.Counter;`
         */
        library Counters {
            struct Counter {
                // This variable should never be directly accessed by users of the library: interactions must be restricted to
                // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
                // this feature: see https://github.com/ethereum/solidity/issues/4637
                uint256 _value; // default: 0
            }
            function current(Counter storage counter) internal view returns (uint256) {
                return counter._value;
            }
            function increment(Counter storage counter) internal {
                unchecked {
                    counter._value += 1;
                }
            }
            function decrement(Counter storage counter) internal {
                uint256 value = counter._value;
                require(value > 0, "Counter: decrement overflow");
                unchecked {
                    counter._value = value - 1;
                }
            }
        }
        

        File 3 of 3: ERC1820Registry
        /* ERC1820 Pseudo-introspection Registry Contract
         * This standard defines a universal registry smart contract where any address (contract or regular account) can
         * register which interface it supports and which smart contract is responsible for its implementation.
         *
         * Written in 2019 by Jordi Baylina and Jacques Dafflon
         *
         * To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to
         * this software to the public domain worldwide. This software is distributed without any warranty.
         *
         * You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see
         * <http://creativecommons.org/publicdomain/zero/1.0/>.
         *
         *    ███████╗██████╗  ██████╗ ██╗ █████╗ ██████╗  ██████╗
         *    ██╔════╝██╔══██╗██╔════╝███║██╔══██╗╚════██╗██╔═████╗
         *    █████╗  ██████╔╝██║     ╚██║╚█████╔╝ █████╔╝██║██╔██║
         *    ██╔══╝  ██╔══██╗██║      ██║██╔══██╗██╔═══╝ ████╔╝██║
         *    ███████╗██║  ██║╚██████╗ ██║╚█████╔╝███████╗╚██████╔╝
         *    ╚══════╝╚═╝  ╚═╝ ╚═════╝ ╚═╝ ╚════╝ ╚══════╝ ╚═════╝
         *
         *    ██████╗ ███████╗ ██████╗ ██╗███████╗████████╗██████╗ ██╗   ██╗
         *    ██╔══██╗██╔════╝██╔════╝ ██║██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝
         *    ██████╔╝█████╗  ██║  ███╗██║███████╗   ██║   ██████╔╝ ╚████╔╝
         *    ██╔══██╗██╔══╝  ██║   ██║██║╚════██║   ██║   ██╔══██╗  ╚██╔╝
         *    ██║  ██║███████╗╚██████╔╝██║███████║   ██║   ██║  ██║   ██║
         *    ╚═╝  ╚═╝╚══════╝ ╚═════╝ ╚═╝╚══════╝   ╚═╝   ╚═╝  ╚═╝   ╚═╝
         *
         */
        pragma solidity 0.5.3;
        // IV is value needed to have a vanity address starting with '0x1820'.
        // IV: 53759
        
        /// @dev The interface a contract MUST implement if it is the implementer of
        /// some (other) interface for any address other than itself.
        interface ERC1820ImplementerInterface {
            /// @notice Indicates whether the contract implements the interface 'interfaceHash' for the address 'addr' or not.
            /// @param interfaceHash keccak256 hash of the name of the interface
            /// @param addr Address for which the contract will implement the interface
            /// @return ERC1820_ACCEPT_MAGIC only if the contract implements 'interfaceHash' for the address 'addr'.
            function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32);
        }
        
        
        /// @title ERC1820 Pseudo-introspection Registry Contract
        /// @author Jordi Baylina and Jacques Dafflon
        /// @notice This contract is the official implementation of the ERC1820 Registry.
        /// @notice For more details, see https://eips.ethereum.org/EIPS/eip-1820
        contract ERC1820Registry {
            /// @notice ERC165 Invalid ID.
            bytes4 constant internal INVALID_ID = 0xffffffff;
            /// @notice Method ID for the ERC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`).
            bytes4 constant internal ERC165ID = 0x01ffc9a7;
            /// @notice Magic value which is returned if a contract implements an interface on behalf of some other address.
            bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC"));
        
            /// @notice mapping from addresses and interface hashes to their implementers.
            mapping(address => mapping(bytes32 => address)) internal interfaces;
            /// @notice mapping from addresses to their manager.
            mapping(address => address) internal managers;
            /// @notice flag for each address and erc165 interface to indicate if it is cached.
            mapping(address => mapping(bytes4 => bool)) internal erc165Cached;
        
            /// @notice Indicates a contract is the 'implementer' of 'interfaceHash' for 'addr'.
            event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);
            /// @notice Indicates 'newManager' is the address of the new manager for 'addr'.
            event ManagerChanged(address indexed addr, address indexed newManager);
        
            /// @notice Query if an address implements an interface and through which contract.
            /// @param _addr Address being queried for the implementer of an interface.
            /// (If '_addr' is the zero address then 'msg.sender' is assumed.)
            /// @param _interfaceHash Keccak256 hash of the name of the interface as a string.
            /// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient' interface.
            /// @return The address of the contract which implements the interface '_interfaceHash' for '_addr'
            /// or '0' if '_addr' did not register an implementer for this interface.
            function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) {
                address addr = _addr == address(0) ? msg.sender : _addr;
                if (isERC165Interface(_interfaceHash)) {
                    bytes4 erc165InterfaceHash = bytes4(_interfaceHash);
                    return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : address(0);
                }
                return interfaces[addr][_interfaceHash];
            }
        
            /// @notice Sets the contract which implements a specific interface for an address.
            /// Only the manager defined for that address can set it.
            /// (Each address is the manager for itself until it sets a new manager.)
            /// @param _addr Address for which to set the interface.
            /// (If '_addr' is the zero address then 'msg.sender' is assumed.)
            /// @param _interfaceHash Keccak256 hash of the name of the interface as a string.
            /// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient' interface.
            /// @param _implementer Contract address implementing '_interfaceHash' for '_addr'.
            function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {
                address addr = _addr == address(0) ? msg.sender : _addr;
                require(getManager(addr) == msg.sender, "Not the manager");
        
                require(!isERC165Interface(_interfaceHash), "Must not be an ERC165 hash");
                if (_implementer != address(0) && _implementer != msg.sender) {
                    require(
                        ERC1820ImplementerInterface(_implementer)
                            .canImplementInterfaceForAddress(_interfaceHash, addr) == ERC1820_ACCEPT_MAGIC,
                        "Does not implement the interface"
                    );
                }
                interfaces[addr][_interfaceHash] = _implementer;
                emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);
            }
        
            /// @notice Sets '_newManager' as manager for '_addr'.
            /// The new manager will be able to call 'setInterfaceImplementer' for '_addr'.
            /// @param _addr Address for which to set the new manager.
            /// @param _newManager Address of the new manager for 'addr'. (Pass '0x0' to reset the manager to '_addr'.)
            function setManager(address _addr, address _newManager) external {
                require(getManager(_addr) == msg.sender, "Not the manager");
                managers[_addr] = _newManager == _addr ? address(0) : _newManager;
                emit ManagerChanged(_addr, _newManager);
            }
        
            /// @notice Get the manager of an address.
            /// @param _addr Address for which to return the manager.
            /// @return Address of the manager for a given address.
            function getManager(address _addr) public view returns(address) {
                // By default the manager of an address is the same address
                if (managers[_addr] == address(0)) {
                    return _addr;
                } else {
                    return managers[_addr];
                }
            }
        
            /// @notice Compute the keccak256 hash of an interface given its name.
            /// @param _interfaceName Name of the interface.
            /// @return The keccak256 hash of an interface name.
            function interfaceHash(string calldata _interfaceName) external pure returns(bytes32) {
                return keccak256(abi.encodePacked(_interfaceName));
            }
        
            /* --- ERC165 Related Functions --- */
            /* --- Developed in collaboration with William Entriken. --- */
        
            /// @notice Updates the cache with whether the contract implements an ERC165 interface or not.
            /// @param _contract Address of the contract for which to update the cache.
            /// @param _interfaceId ERC165 interface for which to update the cache.
            function updateERC165Cache(address _contract, bytes4 _interfaceId) external {
                interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(
                    _contract, _interfaceId) ? _contract : address(0);
                erc165Cached[_contract][_interfaceId] = true;
            }
        
            /// @notice Checks whether a contract implements an ERC165 interface or not.
            //  If the result is not cached a direct lookup on the contract address is performed.
            //  If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
            //  'updateERC165Cache' with the contract address.
            /// @param _contract Address of the contract to check.
            /// @param _interfaceId ERC165 interface to check.
            /// @return True if '_contract' implements '_interfaceId', false otherwise.
            function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) {
                if (!erc165Cached[_contract][_interfaceId]) {
                    return implementsERC165InterfaceNoCache(_contract, _interfaceId);
                }
                return interfaces[_contract][_interfaceId] == _contract;
            }
        
            /// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
            /// @param _contract Address of the contract to check.
            /// @param _interfaceId ERC165 interface to check.
            /// @return True if '_contract' implements '_interfaceId', false otherwise.
            function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) {
                uint256 success;
                uint256 result;
        
                (success, result) = noThrowCall(_contract, ERC165ID);
                if (success == 0 || result == 0) {
                    return false;
                }
        
                (success, result) = noThrowCall(_contract, INVALID_ID);
                if (success == 0 || result != 0) {
                    return false;
                }
        
                (success, result) = noThrowCall(_contract, _interfaceId);
                if (success == 1 && result == 1) {
                    return true;
                }
                return false;
            }
        
            /// @notice Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not.
            /// @param _interfaceHash The hash to check.
            /// @return True if '_interfaceHash' is an ERC165 interface (ending with 28 zeroes), false otherwise.
            function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {
                return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;
            }
        
            /// @dev Make a call on a contract without throwing if the function does not exist.
            function noThrowCall(address _contract, bytes4 _interfaceId)
                internal view returns (uint256 success, uint256 result)
            {
                bytes4 erc165ID = ERC165ID;
        
                assembly {
                    let x := mload(0x40)               // Find empty storage location using "free memory pointer"
                    mstore(x, erc165ID)                // Place signature at beginning of empty storage
                    mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature
        
                    success := staticcall(
                        30000,                         // 30k gas
                        _contract,                     // To addr
                        x,                             // Inputs are stored at location x
                        0x24,                          // Inputs are 36 (4 + 32) bytes long
                        x,                             // Store output over input (saves space)
                        0x20                           // Outputs are 32 bytes long
                    )
        
                    result := mload(x)                 // Load the result
                }
            }
        }